diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f131747fa..26a38482a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -216,9 +216,19 @@ jobs: # Start the corpus immediately beside a serial side queue. One shared # worker budget covers both processes; file and case shard axes stay # separate so every fixture still runs exactly once in each lane. + # The tests run in a transient scope capped below the runner's 16 GB, so + # the runner itself keeps memory: past 14 GB of RAM the scope swaps, as + # the whole runner did before (a sanitized shard peaks near 16 GB), and + # when RAM and swap run out the kernel kills the scope's largest process. + # OOMPolicy=continue leaves the rest of the scope running, so vitest + # reports the killed worker and the job log names the test. - name: Tests (corpus and integration suites) run: | ulimit -c 0 + if [ -d /run/systemd/system ]; then + exec sudo --preserve-env systemd-run --scope --quiet -p MemoryMax=14G -p OOMPolicy=continue \ + --uid="$(id -u)" --gid="$(id -g)" -- env PATH="$PATH" HOME="$HOME" node scripts/ci-test.mjs + fi node scripts/ci-test.mjs tests_macos: @@ -524,7 +534,7 @@ jobs: if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }} name: test (tsgo and tsc-ts give the same IR) runs-on: ubuntu-24.04 - timeout-minutes: 30 + timeout-minutes: 60 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index ec0b27dbf..612df47b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,51 @@ All notable changes to scriptc will be documented in this file. +## 0.4.4 + +### Highlights + +- **Fixes for 0.4.3:** + - Unchanged rebuilds hit the frontend cache again. An unchanged rebuild of a 23-module program takes 93 ms instead of 1.26 s, and of tsc-ts (dev) 1.6 s instead of 58.2 s. + - The native compiler builds programs with several typed-array accesses at one masked index in a loop again, including tsc-ts's release build, which failed with SC3004. + - `x!` returned from a function, used as an arrow function's result, or passed as an argument keeps null or undefined when the destination type holds it, as JavaScript does. `x!` operands of `+`, `+=` and other conversions that hold undefined or null convert as Node does. + - Effect 4 programs whose generators yield clients built by other generators run again. + - The cache for untyped property access in programs that declare classes is reverted, fixing a crash in Effect 4 observability programs. +- **Faster:** + - Number compares and typed-array indexes use the exact integer of sums like `i + 1`: a heap sift-down over an `Int32Array` runs 17.5% faster. + - glibc's per-thread heaps use huge pages: the VS Code type-check runs 3.4% faster. + - Mid-size executables (over 1 MiB of LLVM text) build in at least eight partitions: the LLVM backend phase takes 34 to 74% less time for three.js programs of 1.4 to 7.8 MB. + - JavaScript class instances are boxed at their exact class, and stores into nested string-keyed dictionaries keep class method calls direct. + - `--target-cpu` selects an x86-64 microarchitecture level. +- **Node.js parity:** + - X25519 keys and `crypto.diffieHellman`; `existsSync` and `fs.promises` accept `file:` URLs; `Buffer.from` ranges over ArrayBuffers, the deprecated `Buffer()` constructor and `Buffer` as a value. + - Dates are objects with identity; `Promise.all` over different entry types settles on Node's turns; completion callbacks run in their call's AsyncLocalStorage context. + - TypedArray methods run on typed arrays held as untyped values, and unions of typed-array classes compile. Borrowed `TypedArray.prototype.set` calls compile and throw Node's TypeError for other targets. + - Split CommonJS `require` bindings, `__`-prefixed exports, string-key destructuring of `any` values, and record key order for values JavaScript built. +- **npm packages:** zod 4 and `@hono/zod-validator` values are projected at the declaration boundary. + + + +## 0.4.3 + +### Highlights + +- **Faster checking and smaller builds with tsc-ts 0.0.9:** scriptc checks with tsc-ts 0.0.9, and builds reuse the bundled declaration files' diagnostics instead of checking them again. Small IR builds are 10.6 to 16.0% faster and small executable builds 5.5 to 7.6% faster, with identical output. A native build of tsc-ts with this release checks 0.4 to 6.6% faster than with 0.4.2 and peaks 4 to 16% lower. +- **Integer arrays and loops:** + - Number arrays that only hold int32 values use 4-byte slots in locals, across direct calls and in module globals, and loop-carried locals written from their elements stay int32. A workload that walks arrays of cursors runs 54.5% faster. + - Loops with two counters closing in on each other (`for (let i = lo, j = hi; i < j; i++, j--)`) and element copies through `a[i]!` run without per-element checks, and two-counter `while` loops run on integer counters. + - Loops over typed arrays and charAt scans are versioned on their bounds, and ASCII strings are scanned as bytes. +- **Less work for untyped and dynamic code:** + - String-keyed record reads under `??` and `!` no longer allocate: a dictionary-walking workload makes no union boxes instead of 363,359 and runs 12 to 15% faster. + - Checked-dynamic method calls dispatch on method ids, observed class methods call natively while their slot is unchanged, and calls on bag-tracked class instances skip building their views. +- **Node.js parity:** + - `crypto.getCipherInfo`, AES-CCM, AES key wrap, the legacy `createCipheriv` modes, `checkPrime`/`generatePrime`, and Node's argument checks for `generateKeyPair`. + - Streams: `construct`, `writev`, `decodeStrings` and `defaultEncoding`, including `writev` on `Transform` and `PassThrough`. + - `FinalizationRegistry`, `new.target`, `String.prototype.at` on untyped values, `Error.stackTraceLimit`, `spawnSync`'s `env` option, and class constructors called through `call`/`apply` throw Node's TypeError. + - Records enumerate in the key order their object literal wrote, and objects converting to a union with an empty-record arm keep every field. +- **npm packages:** zod 3 compiles natively and zod 4's core runs, minified packages such as marked compile, chalk 5 and hot-shots 8 build from built-in shims, and `@hono/node-server` 2.x is built in. User code types npm package declarations at a checked, identity-preserving boundary. +- **Builds:** the cached build is reused after whitespace and comment edits, function bodies are emitted on forked workers, and the LLVM emitter reads its analyses from one recorded walk of each module. + ## 0.4.2 ### Highlights @@ -159,8 +204,6 @@ All notable changes to scriptc will be documented in this file. - **The same program compiles to the same executable wherever it sits on disk** and whatever order its functions lower in, and edits outside a declaration do not rename the helpers and closures inside it. - **Libraries that use untyped byte arrays** link. - - ## 0.4.1 ### Highlights diff --git a/benchmarks/runtime/workloads/index-cursors.json b/benchmarks/runtime/workloads/index-cursors.json new file mode 100644 index 000000000..24f2a263c --- /dev/null +++ b/benchmarks/runtime/workloads/index-cursors.json @@ -0,0 +1,6 @@ +{ + "entry": "workloads/index-cursors.ts", + "args": [ + "20000" + ] +} diff --git a/benchmarks/runtime/workloads/index-cursors.ts b/benchmarks/runtime/workloads/index-cursors.ts new file mode 100644 index 000000000..b65ded46b --- /dev/null +++ b/benchmarks/runtime/workloads/index-cursors.ts @@ -0,0 +1,25 @@ +// Arrays of indices walked by loop-carried cursors: each step reads the +// next index from the array and relinks the visited entries, so the +// cursor and every stored value are small integers read back from the +// array itself. +const N = 4096; + +function relink(rounds: number): number { + const next: number[] = []; + for (let i = 0; i < N; i++) next.push(i + 1 < N ? i + 1 : 0); + let at = next[0]!; + let total = 0; + for (let r = 0; r < rounds; r++) { + for (let s = 0; s < N; s++) { + const t = next[at]!; + next[at] = next[t]!; + next[t] = at; + at = t; + total = (total + at) | 0; + } + } + return total; +} + +const rounds = Number(process.argv[2] ?? "1"); +console.log("relink", relink(rounds)); diff --git a/docs/content/docs/cli.mdx b/docs/content/docs/cli.mdx index 8b0353620..9fa5e9447 100644 --- a/docs/content/docs/cli.mdx +++ b/docs/content/docs/cli.mdx @@ -135,6 +135,9 @@ With `--sparse`, the profile leaves out the functions the runs never reached. Th
--detached-exit
On Linux, when the program calls process.exit(), hand the release of its memory to a helper process so the parent sees the exit sooner. The kernel otherwise frees the whole address space before the exit is reported, which takes tens of milliseconds per gigabyte. Opt in only where the trade-offs are acceptable: for up to about 100 ms after the exit, the helper keeps the executable mapped (overwriting the binary in place fails with "text file busy"), its memory stays charged to the cgroup, and the teardown CPU time no longer appears in the parent's resource usage. Valid only for executable builds; it has no effect on other platforms.
+
--target-cpu <baseline|x86-64-v2|x86-64-v3|x86-64-v4|native>
+
On x86-64 targets, compile the program's code for an x86-64 microarchitecture level. The default, baseline, runs on any x86-64 CPU. A higher level lets LLVM use more instructions, and the executable then runs only on CPUs that support that level. native uses the highest level the build machine supports. Valid for executable, assembly, and object output on x86-64 targets. See Target CPU.
+
--profile-generate <pattern>
On Linux, build an instrumented executable for profile-guided optimization. Each run writes a raw profile to the path pattern, where %p expands to the process ID; the LLVM_PROFILE_FILE environment variable overrides the pattern at run time. A relative pattern resolves from the current working directory. Instrumented programs run slower. Valid only for executable builds with --optimization=release or speed.
@@ -228,6 +231,43 @@ $ scriptc build app.ts --optimization=speed --profile-use app.profdata Train on workloads like the ones the program will run: code the training never reached is optimized as without a profile. The profile can be committed and reused as the program changes. Functions whose control flow changed since the profile was collected lose their profile, and the rest keep theirs, so regenerate it when much of the program has changed. [Build parallelism](/docs/how-it-works#build-parallelism) describes what a profile adds to build time. +## Target CPU + +By default, x86-64 executables run on any x86-64 CPU, so the compiler uses only the baseline instruction set (SSE2). `--target-cpu` compiles the program's code for a higher x86-64 microarchitecture level. Each level includes the ones below it: + + + + + + + + + + + + + + + + + + + + + + +
LevelAdds
x86-64-v2SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT
x86-64-v3AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE
x86-64-v4AVX-512 (F, BW, CD, DQ, VL)
+ +```console +$ scriptc build app.ts --target-cpu=x86-64-v3 -o app +``` + +Loops over typed arrays and numbers gain the most, because LLVM vectorizes them with wider registers. Some loops run slower at a higher level, so measure the program on the machines it will run on. + +An executable built for a level runs only on CPUs that support that level. On an older CPU, it crashes with an illegal-instruction signal (`SIGILL`) when it reaches an instruction the CPU lacks. A named level is portable to every machine of that level. `native` selects the highest level the build machine supports, so use it only for executables that run on the machine that built them or on identical machines. + +The level applies to the program's own code. The runtime pack keeps its baseline code. Builds for different levels never share cached artifacts, and `baseline` builds are the same as builds without the option. + ## LLVM backend scriptc generates LLVM IR and compiles it to native code. Executable builds link the result with the matching precompiled runtime pack. Use --emit=llvm to inspect the generated code. diff --git a/docs/content/docs/how-it-works.mdx b/docs/content/docs/how-it-works.mdx index f1b64948d..e017267d7 100644 --- a/docs/content/docs/how-it-works.mdx +++ b/docs/content/docs/how-it-works.mdx @@ -42,17 +42,16 @@ Parsing, type checking, lowering, and LLVM IR generation run one after another o ### Partitions -The LLVM helper divides an executable's LLVM IR into partitions by its size: one partition per 4 MiB of LLVM text, rounded up to a power of two, and at most 64. `--emit=llvm` writes this text, so its file size shows how many partitions a program gets. +The LLVM helper divides an executable's LLVM IR into partitions by its size. Up to 1 MiB of LLVM text is one partition, and up to 2 MiB is four. Larger programs get one partition per 4 MiB of LLVM text, rounded up to a power of two, and at least 8 and at most 64. `--emit=llvm` writes this text, so its file size shows how many partitions a program gets. - - - - + + + @@ -63,7 +62,7 @@ The count depends only on the size of the LLVM text, so a program has the same p These builds are not divided into partitions and compile on one thread: `--emit=asm`, `--emit=obj`, and `--print=native-link-info` output, library builds (`--lib`; `dev` library builds are compiled in pieces, one after another), and `--sanitize` builds, which compile the LLVM IR with the C compiler. -The helper compiles partitions on a pool with one thread per hardware thread of the host, so an executable with two partitions uses at most two cores in the backend. A program of one partition is optimized and compiled on one thread. +The helper compiles partitions on a pool with one thread per hardware thread of the host, so an executable with four partitions uses at most four cores in the backend. A program of one partition is optimized and compiled on one thread. How partitions are compiled depends on `--optimization`: diff --git a/docs/content/limitations/buffer-construction.mdx b/docs/content/limitations/buffer-construction.mdx index aca0c74ca..c88e01336 100644 --- a/docs/content/limitations/buffer-construction.mdx +++ b/docs/content/limitations/buffer-construction.mdx @@ -1,3 +1,8 @@ `Buffer.from` accepts untyped strings, byte arrays, arrays, and plain array-like or Buffer JSON objects. Encoding arguments must be supported literals. +`Buffer.from(arrayBuffer, byteOffset, length)` is a view of the ArrayBuffer or SharedArrayBuffer, with Node's offset and length checks. Custom input `valueOf` hooks and opaque class or native-handle inputs throw an unsupported-operation error. +The deprecated `Buffer()` and `new Buffer()` take up to three arguments, without spreads, and follow Node: the first call made outside `node_modules` reports DEP0005 on the next tick, a number allocates zero-filled bytes, and other arguments convert as `Buffer.from` does. +In JavaScript, `Buffer` read as a value (the global or the buffer module's `Buffer`) is one function with Node's statics and `poolSize`. +Calling or constructing it through a stored value, reading its `prototype`, and its `copyBytesFrom` and `of` statics throw an unsupported-operation error. +`Buffer.prototype` members read as values are functions that run on Buffer receivers; inherited and symbol-keyed members throw an unsupported-operation error. diff --git a/docs/content/limitations/builtin-apis/asynclocalstorage.mdx b/docs/content/limitations/builtin-apis/asynclocalstorage.mdx index 341994faa..ce77d57f5 100644 --- a/docs/content/limitations/builtin-apis/asynclocalstorage.mdx +++ b/docs/content/limitations/builtin-apis/asynclocalstorage.mdx @@ -1,2 +1,12 @@ - **AsyncLocalStorage:** timer, interval, `setImmediate`, `process.nextTick` and `queueMicrotask` callbacks run with the stores that were active when they were scheduled, as in Node. - Completion callbacks of callback-style I/O APIs, such as `fs.readFile` and `crypto.randomBytes`, run with no store, where Node runs them with the stores that were active when the operation started. + The completion callbacks of `fs`, `crypto` and `zlib` callback APIs, `dns.lookup`, `child_process.execFile`, `server.listen`, `net.connect`, `http.request`, `http.get` and `dgram` run with the stores that were active at the call, as in Node. + Events of `net`, `tls`, `http` and `https` sockets, servers, requests and responses run with the stores Node runs them with. + A socket's or client request's events see the context of the call that created it, and a server's events and its requests' and responses' events see the context of its `listen()` call. + Socket `write` and `end` callbacks, `'finish'` and `server.close(callback)` see the context of their call. + Stream events and callbacks run with the stores of the code that triggers them, as in Node. + Stream steps that Node defers with `process.nextTick` see the context that scheduled them, and `stream.finished` and `stream.pipeline` callbacks see the context of their call. + `fs` read and write streams and `zlib` streams run their events and callbacks in the context that created them. + Events of `child_process` children, their standard streams and their IPC channels see the context of the `spawn()` or `fork()` call; stdin's `'finish'` and `send()` callbacks see the context of their call. + A client `http2` session's events see the context of its `http2.connect()` call, and a client stream's events the context of its `request()` call. + An `http2` server's `'session'` and `'stream'` events and the compatibility API's request handler see the context of its `listen()` call; a server stream's own events, and its compatibility request's and response's, run with no store, as in Node. + `AsyncResource` has no lowering yet. diff --git a/docs/content/limitations/builtin-apis/compression.mdx b/docs/content/limitations/builtin-apis/compression.mdx index 36cec0bc4..83b0fd683 100644 --- a/docs/content/limitations/builtin-apis/compression.mdx +++ b/docs/content/limitations/builtin-apis/compression.mdx @@ -4,4 +4,5 @@ The `Deflate`, `Inflate`, `Gzip`, `Gunzip`, `DeflateRaw`, `InflateRaw`, and `Unzip` streams, from `createGzip()` and the other factories or `new zlib.Gzip()`, accept Node's options and support writes, piping, `pipeline`, async iteration, `flush`, `params`, `close`, `reset`, and `bytesWritten`. Each output chunk is its own Buffer rather than a slice of a shared chunk-size buffer. `zlib.constants` members and their module-level copies read as Node's values. - `util.inspect`, `instanceof zlib.Gzip`, and Brotli and Zstd compression are unsupported. + The Brotli one-shots `brotliCompress` and `brotliDecompress` and their `Sync` forms, and the `BrotliCompress` and `BrotliDecompress` streams, accept Node's options, including `params` and `dictionary`, and produce the same bytes as Node. + `util.inspect` and Zstd compression are unsupported. diff --git a/docs/content/limitations/builtin-apis/crypto.mdx b/docs/content/limitations/builtin-apis/crypto.mdx index e4b555806..329aa6809 100644 --- a/docs/content/limitations/builtin-apis/crypto.mdx +++ b/docs/content/limitations/builtin-apis/crypto.mdx @@ -27,20 +27,22 @@ A call on a DiffieHellman value whose arguments no @types/node overload accepts, or whose output encoding is typed `any`, is refused at compile time, as are its `Object.prototype` methods; other property reads, and property writes, throw a coded SC2020 error. `Object.keys()` and `in` do not see a DiffieHellman's own `verifyError` property, and `JSON.stringify()` and `instanceof` are refused. After `createDiffieHellman(primeLength)` throws `Invalid DH parameters`, the next OpenSSL error Node throws can list the failed generation's errors in `opensslErrorStack`; errors in a compiled binary list only their own. - `crypto.diffieHellman`, which takes DH, EC, or X25519 KeyObjects, and the `DiffieHellman` and `DiffieHellmanGroup` constructors are not available. + `crypto.diffieHellman` computes ECDH and X25519 secrets from EC and X25519 keys, given as KeyObjects or in any form `createPrivateKey` and `createPublicKey` read, with Node's errors; DH keys have no KeyObjects here, and the `DiffieHellman` and `DiffieHellmanGroup` constructors are not available. `checkPrime`, `checkPrimeSync`, `generatePrime`, and `generatePrimeSync` support bigint, ArrayBuffer, typed-array, Buffer, and DataView values and the `checks`, `safe`, `add`, `rem`, and `bigint` options on native targets, with Node's errors; the callback forms compute before they return and call back in a later macrotask. Candidates and sizes longer than 262144 bits throw a coded SC2020 error, and wasm32-wasi builds are refused. Node calls their callbacks with `undefined` as the error, which a callback whose error parameter is typed `Error | null`, as @types/node declares it, receives as `null`. - RSA, EC, and Ed25519 private and public KeyObjects come from `generateKeyPairSync`, `generateKeyPair`, `createPrivateKey`, and `createPublicKey`, on native targets. + RSA, EC, Ed25519, and X25519 private and public KeyObjects come from `generateKeyPairSync`, `generateKeyPair`, `createPrivateKey`, and `createPublicKey`, on native targets. `generateKeyPairSync` and `generateKeyPair` check their arguments in Node's order, with Node's errors, including the OpenSSL errors for RSA modulus lengths below 512 or above 2147483647 bits and for public exponents of 0, 1, or an even number, which `generateKeyPair` passes to its callback. - RSA keys generate with even modulus lengths from 1024 to 8192 bits and public exponents up to 2147483647; other lengths and exponents that Node accepts, the `raw-public`, `raw-private`, and `raw-seed` encodings, and `paramEncoding: 'explicit'` throw a coded SC2020 error. + RSA keys generate with even modulus lengths from 1024 to 8192 bits and public exponents up to 2147483647; other lengths and exponents that Node accepts, and `paramEncoding: 'explicit'`, throw a coded SC2020 error. The EC curves are prime256v1 (P-256), secp384r1 (P-384), secp521r1 (P-521), and secp256k1. - Keys read and write PEM and DER as PKCS#1, SEC1, PKCS#8, and SubjectPublicKeyInfo, and JWK (Ed25519 keys use PKCS#8, SubjectPublicKeyInfo, and JWK); `createPublicKey` also takes a private key or an X.509 certificate. + Keys read and write PEM and DER as PKCS#1, SEC1, PKCS#8, and SubjectPublicKeyInfo, and JWK (Ed25519 and X25519 keys use PKCS#8, SubjectPublicKeyInfo, and JWK); `createPublicKey` also takes a private key or an X.509 certificate. + EC, Ed25519, and X25519 keys also read and write the `raw-public` and `raw-private` formats, including compressed EC points. + Raw X448, Ed448, and post-quantum keys, EC scalars of 0 or at least the curve order, and the EC point at infinity, which Node accepts, throw a coded SC2020 error. They support `type`, `asymmetricKeyType`, `asymmetricKeyDetails`, `export()`, `equals()`, and `instanceof KeyObject`. `crypto.sign`, `crypto.verify`, `createSign`, and `createVerify` support RSA PKCS#1 v1.5 and PSS padding and ECDSA with DER and IEEE P1363 encodings over SHA-1 and SHA-2 digests, and Ed25519 through `crypto.sign` and `crypto.verify` with a `null` or `undefined` algorithm. Ed25519 signing, verification, and private keys link when a string literal in the program names `ed25519` (in any case); without one they throw a coded SC2020 error, while Ed25519 public keys still import and export. Sign and Verify values accumulate `update()` data and are not writable streams. - Encrypted private keys (`passphrase`, `cipher`), the other key types (`rsa-pss`, `dsa`, `ed448`, `x25519`, and the rest), and wasm32-wasi builds throw a coded SC2020 error or are refused. + Encrypted private keys (`passphrase`, `cipher`), the other key types (`rsa-pss`, `dsa`, `ed448`, `x448`, and the rest), and wasm32-wasi builds throw a coded SC2020 error or are refused. Comparing keys of different types with `equals()` answers false; Node also leaves an OpenSSL error queued, which a later key operation can report. `publicEncrypt`/`privateDecrypt` remain unsupported. The WebCrypto `crypto` global, which node:crypto also exports as `webcrypto`, supports `randomUUID()` and `getRandomValues()`, also read as values from untyped JavaScript. diff --git a/docs/content/limitations/builtin-apis/date.mdx b/docs/content/limitations/builtin-apis/date.mdx index ac8bf9115..89266b745 100644 --- a/docs/content/limitations/builtin-apis/date.mdx +++ b/docs/content/limitations/builtin-apis/date.mdx @@ -1,5 +1,6 @@ -- **Date:** Date unions, spread constructor arguments, setters, statically typed TypeScript identity comparisons, thrown Date values, and locale formatters are unsupported. - Zero-argument, single number/string, and local-time year/month field constructors, storage and passing, `getTime`, `valueOf`, `toISOString`, `toUTCString`, `toJSON` (a `string | null`: null for an invalid date, as in Node), `toString`, `toDateString`, `toTimeString`, `String(date)` and template literal conversion, calendar getters, `getTimezoneOffset`, and single-string `Date.parse` compile. - JavaScript Dates retain identity through checked storage and support Date-copy construction and JSON serialization. - A TypeScript Date converted to `unknown` becomes a Date with the same time value; each conversion creates a new Date, so two `unknown` values converted from one TypeScript Date are not `===`. +- **Date:** Spread constructor arguments, setters, thrown Date values, and locale formatters are unsupported. + Dates are objects: `===` compares identity, `new Date(date)` makes a new Date, and Dates compile in arrays (`includes`, `indexOf`, `lastIndexOf`, `join`, and default `sort` by `toString()` text), in `Date | undefined` and `Date | null` slots, as Map and Set keys and values, and in records, tuples, class fields, closures, promises and generators. + Unions of a Date with other non-nullish types are unsupported. + Zero-argument, single number/string, and local-time year/month field constructors, `getTime`, `valueOf`, `toISOString`, `toUTCString`, `toJSON` (a `string | null`: null for an invalid date, as in Node), `toString`, `toDateString`, `toTimeString`, `String(date)` and template literal conversion, relational comparison, calendar getters, `getTimezoneOffset`, and single-string `Date.parse` compile. + A Date converted to `unknown` is the same Date object. See [Dates and timezones](#dates-and-timezones) for timezone data. diff --git a/docs/content/limitations/builtin-apis/filesystem-url-paths.mdx b/docs/content/limitations/builtin-apis/filesystem-url-paths.mdx index d5079ed5a..9b3580647 100644 --- a/docs/content/limitations/builtin-apis/filesystem-url-paths.mdx +++ b/docs/content/limitations/builtin-apis/filesystem-url-paths.mdx @@ -1,3 +1,3 @@ -- **Filesystem URL paths:** Synchronous `fs` functions accept `file:` URL paths and convert them as `fileURLToPath` does. - `existsSync` and the `fs.promises` functions refuse URL paths; pass `fileURLToPath(url)` instead. - A URL path after a string path, or with options that are not literals, is refused, because Node checks those arguments before it converts the URL. +- **Filesystem URL paths:** `fs` and `fs.promises` functions accept `file:` URL paths and convert them as `fileURLToPath` does; `existsSync` answers `false` and the `fs.promises` functions reject for a URL that does not convert. + A URL path with options that are not literals is refused where Node checks those options before it converts the URL, except a `readFileSync` encoding. + `fs.promises` functions also refuse a string path before a URL path; pass `fileURLToPath(url)` instead. diff --git a/docs/content/limitations/builtin-apis/streams-and-readline.mdx b/docs/content/limitations/builtin-apis/streams-and-readline.mdx index 45a784742..f9d69e3db 100644 --- a/docs/content/limitations/builtin-apis/streams-and-readline.mdx +++ b/docs/content/limitations/builtin-apis/streams-and-readline.mdx @@ -27,7 +27,8 @@ A `Duplex.from` body function may take the `{ signal }` argument, which is aborted with the error the Duplex is destroyed with. `stream.compose` accepts streams and async generator functions in any position, an async function as the last stage, and an array, a string, or a generator as the first stage; one stage is `Duplex.from` of it. A web `ReadableStream` is read as an async iterable by `Readable.from` and as the first stage of `compose` or `pipeline`, and through its reader by `Readable.fromWeb` (with `objectMode`, `highWaterMark`, `encoding`, and `signal` in an options object literal) and `Duplex.from`. - A web stream in a later compose stage, `Readable.toWeb`, and web `WritableStream` and `TransformStream` interop are unsupported. + `new TransformStream()` without arguments builds the identity transform: its readable side is a web `ReadableStream`, its writable side supports `getWriter`, `abort`, `close`, and `locked`, and the writer supports `write`, `close`, `abort`, `releaseLock`, `ready`, `closed`, and `desiredSize`, with Node's backpressure (a write completes when the readable side reads its chunk). + A web stream in a later compose stage, `Readable.toWeb`, a `TransformStream` with a transformer or queuing strategies, the `WritableStream` constructor, `pipeTo`, and `pipeThrough` are unsupported. The `ERR_INVALID_ARG_VALUE` message for a stream that is no longer readable or writable names the stream's class instead of printing the stream. `readable.compose(stage, { signal })` is `stream.compose(readable, stage)` with the signal added; the options are an object literal whose `signal` is a variable or a property read. `pipeline` from `node:stream/promises` accepts a trailing `{ signal, end }` object, with or without function stages, and `finished` accepts `readable`, `writable`, `error`, `cleanup`, and `signal` options, with boolean literals for the flags. diff --git a/docs/content/limitations/builtin-apis/text-encoding.mdx b/docs/content/limitations/builtin-apis/text-encoding.mdx index bffd24424..e20cfd943 100644 --- a/docs/content/limitations/builtin-apis/text-encoding.mdx +++ b/docs/content/limitations/builtin-apis/text-encoding.mdx @@ -1,6 +1,7 @@ - **Text encoding:** `TextEncoder` and `TextDecoder` can be stored in fields, arguments, return values, arrays, and closures. Encoding supports UTF-8, including `encodeInto` into a Uint8Array or Buffer view. Codec properties expose the canonical encoding and decoder flags. + Codecs held in `any` or `unknown` storage keep their identity and streaming state, and dynamic code can call `encode`, `encodeInto` and `decode`, with options objects, and read their properties. Decoding accepts recognized WHATWG labels, including runtime labels and omitted or undefined encodings. Literal constructor options support `fatal` and `ignoreBOM`; literal decode options support streaming UTF-8, UTF-16, and single-byte encodings. Other legacy streaming encodings, runtime options objects, inspection, JSON conversion, and module-scope declarations using `var` or lacking an initializer are unsupported. diff --git a/docs/content/limitations/builtin-apis/url.mdx b/docs/content/limitations/builtin-apis/url.mdx index 6345e2616..c9eee85b3 100644 --- a/docs/content/limitations/builtin-apis/url.mdx +++ b/docs/content/limitations/builtin-apis/url.mdx @@ -1,4 +1,4 @@ -- **URL:** Non-ASCII or percent-escaped hosts are unsupported. +- **URL:** Hosts of special URLs that hold non-ASCII text, or percent-escapes that decode to it, throw `ERR_INVALID_URL`, and `xn--` labels are kept without Punycode validation. Absolute inputs and relative inputs with runtime string or URL bases, including optional bases, compile. `URL.canParse`, `URL.parse`, `toString()`, and `toJSON()` compile; the factories use the same parser as construction, returning `false` or `null` for rejected inputs while preserving conversion errors. `href`, `protocol`, `username`, `password`, `host`, `hostname`, `port`, `pathname`, `search`, and `hash` support assignment in native code. @@ -6,4 +6,3 @@ Native URL assignments require statically known property keys; `+=` accepts primitive operands, while other assignment operators and object operands for `+=` are refused. `origin` is read-only. `searchParams` is a live view whose identity survives query and URL replacement. - Opaque paths retain their input bytes without WHATWG C0 encoding. diff --git a/docs/content/limitations/record-identity-and-property-order.mdx b/docs/content/limitations/record-identity-and-property-order.mdx index cc16f74d5..dd180cb7b 100644 --- a/docs/content/limitations/record-identity-and-property-order.mdx +++ b/docs/content/limitations/record-identity-and-property-order.mdx @@ -4,6 +4,8 @@ Empty record layouts are also snapshots because they have no property storage. `scriptc coverage` and `scriptc build` report `SC6001` where a copied value is then written or compared, whether the record was passed as an argument, stored in a typed binding, asserted with `as`, assigned to a typed binding later, or stored as an element of a typed array literal. Typed records with declared fields, indexed records, and arrays retain identity and mutations through native checked storage. +A record or class instance converted to a string-keyed dictionary type such as `Record`, directly or through a `{}`, `object` or `unknown` binding or parameter, keeps its identity when the program writes through, compares or inspects values of that dictionary type. +Every dictionary type with the same value type then uses checked dynamic storage, which is slower than a native dictionary. Class instances retain identity at their exact type or a base class, and at any interface or object type that a class instance is passed, stored, returned, or asserted as when the program could tell a copy from the instance: values of such a type are checked views of the original object, so writes, identity comparisons, Set and Map keys, `util.inspect`, and `JSON.stringify` through them reach the instance, as in Node.js. These types use checked dynamic storage, which is slower than native record fields, for all of their values, including plain objects. A type whose values are only read field by field, from fields the class does not write after construction and does not implement as accessors, stays a native record and holds a copy. diff --git a/docs/content/limitations/types-and-class-definitions/symbol-fields.mdx b/docs/content/limitations/types-and-class-definitions/symbol-fields.mdx index baff4d190..45659b493 100644 --- a/docs/content/limitations/types-and-class-definitions/symbol-fields.mdx +++ b/docs/content/limitations/types-and-class-definitions/symbol-fields.mdx @@ -1,4 +1,5 @@ - **Symbol fields:** Instance fields require a stable module-level `Symbol()` or `Symbol.for()` binding with a literal description or constant string binding. + A `[Symbol.toStringTag]` field or getter with a string value sets the tag that `String()`, `Object.prototype.toString` and `util.inspect` print; fields keyed by other well-known symbols are unsupported. Reassigned keys, runtime-computed descriptions, distinct symbols with the same description in one class, and copying symbol fields with rest bindings or `Object.assign` are unsupported. JavaScript object spreads copy represented own symbol fields. Equal registry keys share a field across modules. diff --git a/docs/content/limitations/untyped-values.mdx b/docs/content/limitations/untyped-values.mdx index 6a881aec3..05245a396 100644 --- a/docs/content/limitations/untyped-values.mdx +++ b/docs/content/limitations/untyped-values.mdx @@ -15,3 +15,6 @@ JavaScript argument and return conversions preserve native array references; oth Supported operations on `unknown` include truthiness, `typeof` narrowing, property access, `+`, `switch`, and `throw`. Cast before using other operations. + +In JavaScript, a call whose untyped argument could select an overload with another result type returns a checked value, whichever overload the type checker picks, and so do the unannotated bindings and functions that hold or return it. +In TypeScript, an `any` argument keeps the overload the type checker picks, and a value that selects another overload at runtime throws a `TypeError` where the result is used. diff --git a/docs/package.json b/docs/package.json index 3c2a950fd..51d74d0a1 100644 --- a/docs/package.json +++ b/docs/package.json @@ -10,9 +10,10 @@ "build": "fumadocs-mdx && next build", "start": "next start", "typecheck": "fumadocs-mdx && next typegen && tsc --noEmit", + "test:unit": "node --test test/compatibility-matrix.test.mjs", "test:routes": "node scripts/test-routes.mjs", "compat:check": "pnpm --dir .. node-compat:check", - "check": "pnpm compat:check && pnpm typecheck && pnpm build && pnpm test:routes" + "check": "pnpm compat:check && pnpm typecheck && pnpm test:unit && pnpm build && pnpm test:routes" }, "dependencies": { "@vercel/geistdocs": "2.7.8", diff --git a/docs/src/components/node-compatibility-matrix.tsx b/docs/src/components/node-compatibility-matrix.tsx index 095bcdaf2..af2435668 100644 --- a/docs/src/components/node-compatibility-matrix.tsx +++ b/docs/src/components/node-compatibility-matrix.tsx @@ -4,52 +4,28 @@ // empty after an edit. // @refresh reset -import { useEffect, useMemo, useState } from "react"; +import { memo, startTransition, useCallback, useDeferredValue, useEffect, useId, useMemo, useState, type FocusEvent, type KeyboardEvent, type ReactNode } from "react"; +import { + buildTree, + expandableIds, + filterTree, + flattenTree, + nodeCount, + rowMatcher, + searchTexts, + statusCounts, + statuses, + type NodeStability, + type Row, + type Snapshot, + type Status, + type Verification, +} from "@/lib/compatibility-tree"; // Set by next.config.mjs from the committed artifact. const artifactVersion = process.env.COMPATIBILITY_ARTIFACT_VERSION; const rowCount = Number(process.env.COMPATIBILITY_ROW_COUNT); -type Status = - | "supported" - | "partial" - | "refused" - | "not-implemented" - | "by-design" - | "unreviewed" - | "not-applicable"; -type VerificationBasis = - | "test-backed" - | "explicit-refusal" - | "verified-absence" - | "declared-gap" - | "registry-gap" - | "architectural-policy" - | "unreviewed" - | "not-applicable" - | "derived"; -type Verification = { label: string; detail: string }; -type Tier = { status: Status; detail: string; verification: VerificationBasis }; -type NodeStability = { index: string; level: number; text: string; inherited: boolean } | null; -type Row = { - id: string; - chapter: string; - kind: string; - name: string; - signature: string; - label: string; - apiSymbol: string; - depth: number; - scope: "api" | "documentation" | "metadata" | "configuration"; - anchor: string; - nodeStability: NodeStability; - static: Tier; -}; -type Chapter = { slug: string; title: string; entries: number; apiEntries: number }; -type Snapshot = { nodeVersion: string; verificationBases: Record; chapters: Chapter[]; rows: Row[] }; -type TreeNode = { row: Row; children: TreeNode[] }; -type VisibleNode = { node: TreeNode; level: number }; - const labels: Record = { supported: "Supported", partial: "Partial", @@ -70,7 +46,6 @@ const styles: Record = { "not-applicable": "border-gray-alpha-400 bg-background-200 text-gray-700", }; -const statuses = ["supported", "partial", "not-implemented", "refused", "by-design", "unreviewed", "not-applicable"] as const; const stabilityStyles: Record = { 0: "border-red-500/40 bg-red-100 text-red-900 dark:bg-red-200", 1: "border-amber-500/50 bg-amber-100 text-amber-900 dark:bg-amber-200", @@ -78,114 +53,83 @@ const stabilityStyles: Record = { 3: "border-gray-alpha-500 bg-gray-alpha-100 text-gray-900", }; -function StatusBadge({ status, detail, verification }: { status: Status; detail?: string; verification?: Verification }) { - const tooltip = verification ? `${detail ?? ""}${detail ? "\n\n" : ""}Verification: ${verification.label}. ${verification.detail}` : detail; +/** A badge's tooltip, mounted only while the badge is hovered or has + * keyboard focus (the :focus-visible rule the CSS-only tooltip used), and + * named by aria-describedby while open. Hover and focus are tracked apart, + * so moving the pointer away keeps a keyboard-focused badge's tooltip. + * Escape dismisses it until the next hover or focus. */ +function BadgeTooltip({ tooltipClass, children, badge }: { tooltipClass: string; children: ReactNode; badge: ReactNode }) { + const [hovered, setHovered] = useState(false); + const [focused, setFocused] = useState(false); + const open = hovered || focused; + const id = useId(); + const enter = useCallback(() => setHovered(true), []); + const leave = useCallback(() => setHovered(false), []); + const focus = useCallback((event: FocusEvent) => { + if (event.currentTarget.matches(":focus-visible")) setFocused(true); + }, []); + const blur = useCallback(() => setFocused(false), []); + const key = useCallback((event: KeyboardEvent) => { + if (event.key !== "Escape") return; + setHovered(false); + setFocused(false); + }, []); return ( - - - {labels[status]} - - {tooltip ? ( - - {detail ? {detail} : null} - {verification ? Verification: {verification.label}. {verification.detail} : null} + + {badge} + {open ? ( + + {children} ) : null} ); } -function StabilityBadge({ stability }: { stability: NodeStability }) { +export const StatusBadge = memo(function StatusBadge({ status, detail, verification }: { status: Status; detail?: string; verification?: Verification }) { + const tooltip = verification ? `${detail ?? ""}${detail ? "\n\n" : ""}Verification: ${verification.label}. ${verification.detail}` : detail; + const badge = ( + + {labels[status]} + + ); + if (!tooltip) return {badge}; + return ( + + {detail ? {detail} : null} + {verification ? Verification: {verification.label}. {verification.detail} : null} + + ); +}); + +export const StabilityBadge = memo(function StabilityBadge({ stability }: { stability: NodeStability }) { if (stability === null) return —; const detail = `Node.js Stability ${stability.index}: ${stability.text}${stability.inherited ? " (inherited from the nearest parent section)" : ""}`; return ( - - - {stability.index} - - - {detail} - - - ); -} - -function statusCounts(rows: Row[], tier: "static") { - return rows.reduce>( - (counts, row) => { - if (row.scope !== "api" || row.depth === 0) return counts; - counts[row[tier].status] += 1; - return counts; - }, - { - supported: 0, - partial: 0, - refused: 0, - "not-implemented": 0, - "by-design": 0, - unreviewed: 0, - "not-applicable": 0, - }, - ); -} - -/** Node's all.json is preorder with an explicit heading depth. */ -function buildTree(rows: Row[]): TreeNode[] { - const roots: TreeNode[] = []; - const stack: TreeNode[] = []; - for (const row of rows) { - const node: TreeNode = { row, children: [] }; - while (stack.length > row.depth) stack.pop(); - const parent = stack[row.depth - 1]; - if (row.depth === 0 || parent === undefined) roots.push(node); - else parent.children.push(node); - stack[row.depth] = node; - stack.length = row.depth + 1; - } - return roots; -} - -function filterTree(nodes: TreeNode[], matches: (row: Row) => boolean): TreeNode[] { - const filtered: TreeNode[] = []; - for (const node of nodes) { - const children = filterTree(node.children, matches); - if (matches(node.row) || children.length > 0) filtered.push({ row: node.row, children }); - } - return filtered; -} - -function flattenTree(nodes: TreeNode[], expanded: Set, forceExpanded: boolean): VisibleNode[] { - const visible: VisibleNode[] = []; - const visit = (items: TreeNode[], level: number) => { - for (const node of items) { - visible.push({ node, level }); - if (node.children.length > 0 && (forceExpanded || expanded.has(node.row.id))) { - visit(node.children, level + 1); + + {stability.index} + } - } - }; - visit(nodes, 1); - return visible; -} - -function nodeCount(nodes: TreeNode[]): number { - return nodes.reduce((count, node) => count + 1 + nodeCount(node.children), 0); -} - -function expandableIds(nodes: TreeNode[]): Set { - const ids = new Set(); - const visit = (items: TreeNode[]) => { - for (const node of items) { - if (node.children.length > 0) ids.add(node.row.id); - visit(node.children); - } - }; - visit(nodes); - return ids; -} + > + {detail} + + ); +}); function Chevron({ open }: { open: boolean }) { return ( @@ -195,6 +139,47 @@ function Chevron({ open }: { open: boolean }) { ); } +type MatrixRowProps = { + row: Row; + level: number; + hasChildren: boolean; + open: boolean; + filtering: boolean; + displayName: string; + href: string; + verification: Verification | undefined; + onToggle: (id: string) => void; +}; + +/** One table row. Memoized on its own values, so a filter or expansion + * change re-renders only the rows whose values changed. */ +const MatrixRow = memo(function MatrixRow({ row, level, hasChildren, open, filtering, displayName, href, verification, onToggle }: MatrixRowProps) { + const isRoot = level === 1; + return ( + + + + + + ); +}); + export function NodeCompatibilityMatrix() { const [snapshot, setSnapshot] = useState(null); const [loadError, setLoadError] = useState(false); @@ -202,15 +187,16 @@ export function NodeCompatibilityMatrix() { const [staticFilter, setStaticFilter] = useState<"all" | Status>("all"); const [expanded, setExpanded] = useState>(() => new Set()); - const rows = snapshot?.rows ?? []; - const chapters = snapshot?.chapters ?? []; + const rows = useMemo(() => snapshot?.rows ?? [], [snapshot]); + const chapters = useMemo(() => snapshot?.chapters ?? [], [snapshot]); const verificationBases = snapshot?.verificationBases; const apiRowCount = useMemo(() => rows.filter((row) => row.scope === "api").length, [rows]); const chapterTitles = useMemo(() => new Map(chapters.map((item) => [item.slug, item.title])), [chapters]); const base = `https://nodejs.org/docs/v${snapshot?.nodeVersion ?? "24.22.0"}/api/`; - const staticCounts = useMemo(() => statusCounts(rows, "static"), [rows]); + const staticCounts = useMemo(() => statusCounts(rows), [rows]); const visibleStatuses = useMemo(() => statuses.filter((status) => staticCounts[status] > 0), [staticCounts]); const tree = useMemo(() => buildTree(rows), [rows]); + const texts = useMemo(() => searchTexts(rows, chapterTitles), [rows, chapterTitles]); useEffect(() => { const controller = new AbortController(); @@ -231,36 +217,23 @@ export function NodeCompatibilityMatrix() { return () => controller.abort(); }, []); - const needle = query.trim().toLocaleLowerCase(); - const hasResultFilter = needle !== "" || staticFilter !== "all"; - const rowMatches = (row: Row) => { - if (staticFilter !== "all" && row.static.status !== staticFilter) return false; - if (!needle) return true; - return [chapterTitles.get(row.chapter), row.kind, row.name, row.signature, row.apiSymbol, row.static.detail] - .join(" ") - .toLocaleLowerCase() - .includes(needle); - }; + // The search field and the status select update at once; the table + // follows the deferred values, so input stays responsive while the rows + // re-render. + const deferredQuery = useDeferredValue(query); + const deferredStatic = useDeferredValue(staticFilter); + const needle = deferredQuery.trim().toLocaleLowerCase(); + const hasResultFilter = needle !== "" || deferredStatic !== "all"; - const resultTree = useMemo( - () => hasResultFilter ? filterTree(tree, rowMatches) : tree, - // rowMatches closes over these filter values. - // eslint-disable-next-line react-hooks/exhaustive-deps - [hasResultFilter, needle, staticFilter, tree], + const result = useMemo( + () => hasResultFilter ? filterTree(tree, rowMatcher(texts, needle, deferredStatic)) : { nodes: tree, matched: nodeCount(tree) }, + [hasResultFilter, needle, deferredStatic, texts, tree], ); const visible = useMemo( - () => flattenTree(resultTree, expanded, hasResultFilter), - [expanded, hasResultFilter, resultTree], - ); - const matchedCount = useMemo( - () => hasResultFilter ? tree.reduce((count, node) => { - const visit = (item: TreeNode): number => (rowMatches(item.row) ? 1 : 0) + item.children.reduce((sum, child) => sum + visit(child), 0); - return count + visit(node); - }, 0) : nodeCount(tree), - // rowMatches closes over these filter values. - // eslint-disable-next-line react-hooks/exhaustive-deps - [hasResultFilter, needle, staticFilter, tree], + () => flattenTree(result.nodes, expanded, hasResultFilter), + [expanded, hasResultFilter, result], ); + const matchedCount = result.matched; const changeQuery = (value: string) => setQuery(value); const resetFilters = () => { @@ -268,12 +241,12 @@ export function NodeCompatibilityMatrix() { setStaticFilter("all"); setExpanded(new Set()); }; - const toggle = (id: string) => setExpanded((current) => { + const toggle = useCallback((id: string) => setExpanded((current) => { const next = new Set(current); if (next.has(id)) next.delete(id); else next.add(id); return next; - }); + }), []); useEffect(() => { if (!snapshot) return; @@ -316,8 +289,8 @@ export function NodeCompatibilityMatrix() { {visible.length.toLocaleString()} visible · {matchedCount.toLocaleString()} {hasResultFilter ? "matching" : "API"} rows {!hasResultFilter ? ( - - + + ) : null} @@ -335,33 +308,19 @@ export function NodeCompatibilityMatrix() { {visible.map(({ node, level }) => { const row = node.row; - const hasChildren = node.children.length > 0; - const open = hasResultFilter || expanded.has(row.id); - const href = `${base}${row.chapter}.html${row.anchor ? `#${row.anchor}` : ""}`; - const isRoot = level === 1; - const displayName = isRoot ? chapterTitles.get(row.chapter) ?? row.label : row.label; return ( - - - - - + 0} + open={hasResultFilter || expanded.has(row.id)} + filtering={hasResultFilter} + displayName={level === 1 ? chapterTitles.get(row.chapter) ?? row.label : row.label} + href={`${base}${row.chapter}.html${row.anchor ? `#${row.anchor}` : ""}`} + verification={verificationBases?.[row.static.verification]} + onToggle={toggle} + /> ); })} diff --git a/docs/src/generated/node-v24-compatibility.json b/docs/src/generated/node-v24-compatibility.json index c3f1835ab..2475cd122 100644 --- a/docs/src/generated/node-v24-compatibility.json +++ b/docs/src/generated/node-v24-compatibility.json @@ -11476,9 +11476,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -18268,9 +18268,9 @@ "anchor": "err_brotli_invalid_param", "nodeStability": null, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -35971,9 +35971,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -71509,9 +71509,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -81657,9 +81657,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented static async-iteration subset.", + "verification": "test-backed" } }, { @@ -84302,9 +84302,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "BrotliCompress and BrotliDecompress are Transform streams over the brotli version Node bundles, with Node's parameters, dictionaries, flush, reset, error codes, and option validation; like Node's, they have no params().", + "verification": "test-backed" } }, { @@ -84325,9 +84325,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "BrotliCompress and BrotliDecompress are Transform streams over the brotli version Node bundles, with Node's parameters, dictionaries, flush, reset, error codes, and option validation; like Node's, they have no params().", + "verification": "test-backed" } }, { @@ -84349,7 +84349,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84372,7 +84372,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84395,7 +84395,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84418,7 +84418,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84441,7 +84441,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84464,7 +84464,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84487,7 +84487,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84510,7 +84510,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84533,7 +84533,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84556,7 +84556,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84579,7 +84579,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84602,7 +84602,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84625,7 +84625,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -84716,9 +84716,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -84739,9 +84739,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -84969,9 +84969,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -84992,9 +84992,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -85015,9 +85015,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -85038,9 +85038,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -85499,7 +85499,7 @@ }, "static": { "status": "partial", - "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported.", + "detail": "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported.", "verification": "test-backed" } }, @@ -85521,9 +85521,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "BrotliCompress and BrotliDecompress are Transform streams over the brotli version Node bundles, with Node's parameters, dictionaries, flush, reset, error codes, and option validation; like Node's, they have no params().", + "verification": "test-backed" } }, { @@ -85567,9 +85567,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -85590,9 +85590,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -85613,9 +85613,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -85636,9 +85636,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { diff --git a/docs/src/lib/compatibility-tree.ts b/docs/src/lib/compatibility-tree.ts new file mode 100644 index 000000000..4044bbc8d --- /dev/null +++ b/docs/src/lib/compatibility-tree.ts @@ -0,0 +1,157 @@ +// The compatibility matrix's data model: the ledger rows as a tree, and the +// search and filter passes over it. Kept free of React so the passes can be +// tested directly. + +export type Status = + | "supported" + | "partial" + | "refused" + | "not-implemented" + | "by-design" + | "unreviewed" + | "not-applicable"; +export type VerificationBasis = + | "test-backed" + | "explicit-refusal" + | "verified-absence" + | "declared-gap" + | "registry-gap" + | "architectural-policy" + | "unreviewed" + | "not-applicable" + | "derived"; +export type Verification = { label: string; detail: string }; +export type Tier = { status: Status; detail: string; verification: VerificationBasis }; +export type NodeStability = { index: string; level: number; text: string; inherited: boolean } | null; +export type Row = { + id: string; + chapter: string; + kind: string; + name: string; + signature: string; + label: string; + apiSymbol: string; + depth: number; + scope: "api" | "documentation" | "metadata" | "configuration"; + anchor: string; + nodeStability: NodeStability; + static: Tier; +}; +export type Chapter = { slug: string; title: string; entries: number; apiEntries: number }; +export type Snapshot = { + nodeVersion: string; + verificationBases: Record; + chapters: Chapter[]; + rows: Row[]; +}; +export type TreeNode = { row: Row; children: TreeNode[] }; +export type VisibleNode = { node: TreeNode; level: number }; + +export const statuses = ["supported", "partial", "not-implemented", "refused", "by-design", "unreviewed", "not-applicable"] as const; + +export function statusCounts(rows: Row[]): Record { + return rows.reduce>( + (counts, row) => { + if (row.scope !== "api" || row.depth === 0) return counts; + counts[row.static.status] += 1; + return counts; + }, + { + supported: 0, + partial: 0, + refused: 0, + "not-implemented": 0, + "by-design": 0, + unreviewed: 0, + "not-applicable": 0, + }, + ); +} + +/** Node's all.json is preorder with an explicit heading depth. */ +export function buildTree(rows: Row[]): TreeNode[] { + const roots: TreeNode[] = []; + const stack: TreeNode[] = []; + for (const row of rows) { + const node: TreeNode = { row, children: [] }; + while (stack.length > row.depth) stack.pop(); + const parent = stack[row.depth - 1]; + if (row.depth === 0 || parent === undefined) roots.push(node); + else parent.children.push(node); + stack[row.depth] = node; + stack.length = row.depth + 1; + } + return roots; +} + +/** The lowercase text a search matches against, built once per row: the + * chapter title, kind, name, signature, API symbol and support detail. */ +export function searchTexts(rows: Row[], chapterTitles: ReadonlyMap): Map { + return new Map( + rows.map((row) => [ + row.id, + [chapterTitles.get(row.chapter), row.kind, row.name, row.signature, row.apiSymbol, row.static.detail] + .join(" ") + .toLocaleLowerCase(), + ]), + ); +} + +/** The rows a status filter and a lowercase search needle keep. */ +export function rowMatcher( + texts: ReadonlyMap, + needle: string, + status: "all" | Status, +): (row: Row) => boolean { + return (row) => { + if (status !== "all" && row.static.status !== status) return false; + return needle === "" || (texts.get(row.id) ?? "").includes(needle); + }; +} + +/** The matching rows with their ancestors, and the number of matching + * rows, in one pass. */ +export function filterTree(nodes: TreeNode[], matches: (row: Row) => boolean): { nodes: TreeNode[]; matched: number } { + let matched = 0; + const visit = (items: TreeNode[]): TreeNode[] => { + const filtered: TreeNode[] = []; + for (const node of items) { + const children = visit(node.children); + const own = matches(node.row); + if (own) matched += 1; + if (own || children.length > 0) filtered.push({ row: node.row, children }); + } + return filtered; + }; + return { nodes: visit(nodes), matched }; +} + +export function flattenTree(nodes: TreeNode[], expanded: ReadonlySet, forceExpanded: boolean): VisibleNode[] { + const visible: VisibleNode[] = []; + const visit = (items: TreeNode[], level: number) => { + for (const node of items) { + visible.push({ node, level }); + if (node.children.length > 0 && (forceExpanded || expanded.has(node.row.id))) { + visit(node.children, level + 1); + } + } + }; + visit(nodes, 1); + return visible; +} + +export function nodeCount(nodes: TreeNode[]): number { + return nodes.reduce((count, node) => count + 1 + nodeCount(node.children), 0); +} + +export function expandableIds(nodes: TreeNode[]): Set { + const ids = new Set(); + const visit = (items: TreeNode[]) => { + for (const node of items) { + if (node.children.length > 0) ids.add(node.row.id); + visit(node.children); + } + }; + visit(nodes); + return ids; +} diff --git a/docs/test/compatibility-matrix.test.mjs b/docs/test/compatibility-matrix.test.mjs new file mode 100644 index 000000000..abd3df945 --- /dev/null +++ b/docs/test/compatibility-matrix.test.mjs @@ -0,0 +1,115 @@ +import assert from "node:assert/strict"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { test } from "node:test"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import ts from "typescript"; +import { + buildTree, + expandableIds, + filterTree, + flattenTree, + nodeCount, + rowMatcher, + searchTexts, + statusCounts, +} from "../src/lib/compatibility-tree.ts"; + +const row = (id, depth, label, status, detail = "") => ({ + id, + chapter: "zlib", + kind: depth === 0 ? "module" : "method", + name: label, + signature: label, + label, + apiSymbol: label, + depth, + scope: "api", + anchor: "", + nodeStability: null, + static: { status, detail, verification: "test-backed" }, +}); +const rows = [ + row("root", 0, "zlib", "partial"), + row("a", 1, "zlib.gzipSync()", "supported", "Lowered with Node's options"), + row("b", 1, "zlib.Gzip", "partial"), + row("b1", 2, "gzip.flush()", "supported"), + row("c", 1, "zlib.zstdCompress()", "not-implemented"), +]; +const titles = new Map([["zlib", "Zlib"]]); + +test("the tree follows the rows' heading depths", () => { + const tree = buildTree(rows); + assert.equal(tree.length, 1); + assert.deepEqual(tree[0].children.map((node) => node.row.id), ["a", "b", "c"]); + assert.deepEqual(tree[0].children[1].children.map((node) => node.row.id), ["b1"]); + assert.equal(nodeCount(tree), 5); + assert.deepEqual([...expandableIds(tree)].sort(), ["b", "root"]); +}); + +test("filtering keeps matching rows with their ancestors and counts the matches once", () => { + const tree = buildTree(rows); + const texts = searchTexts(rows, titles); + const byText = filterTree(tree, rowMatcher(texts, "flush", "all")); + assert.equal(byText.matched, 1); + assert.deepEqual(flattenTree(byText.nodes, new Set(), true).map(({ node, level }) => `${node.row.id}@${level}`), ["root@1", "b@2", "b1@3"]); + const byDetail = filterTree(tree, rowMatcher(texts, "node's options", "all")); + assert.deepEqual(flattenTree(byDetail.nodes, new Set(), true).map(({ node }) => node.row.id), ["root", "a"]); + const byStatus = filterTree(tree, rowMatcher(texts, "", "supported")); + assert.equal(byStatus.matched, 2); + const both = filterTree(tree, rowMatcher(texts, "gzip", "partial")); + assert.equal(both.matched, 1); + assert.equal(filterTree(tree, rowMatcher(texts, "brotli", "all")).nodes.length, 0); +}); + +test("collapsed rows hide their children until expanded", () => { + const tree = buildTree(rows); + assert.deepEqual(flattenTree(tree, new Set(), false).map(({ node }) => node.row.id), ["root"]); + assert.deepEqual(flattenTree(tree, new Set(["root"]), false).map(({ node }) => node.row.id), ["root", "a", "b", "c"]); + assert.deepEqual(flattenTree(tree, new Set(["root", "b"]), false).map(({ node }) => node.row.id), ["root", "a", "b", "b1", "c"]); +}); + +test("status counts cover API rows below the chapters", () => { + const counts = statusCounts(rows); + assert.equal(counts.supported, 2); + assert.equal(counts.partial, 1); + assert.equal(counts["not-implemented"], 1); +}); + +/** The matrix component, compiled for Node from its TSX source. */ +async function loadMatrix() { + const docs = new URL("../", import.meta.url); + const source = await readFile(new URL("src/components/node-compatibility-matrix.tsx", docs), "utf8"); + const { outputText } = ts.transpileModule(source, { + compilerOptions: { jsx: ts.JsxEmit.ReactJSX, module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 }, + }); + const lib = pathToFileURL(fileURLToPath(new URL("src/lib/compatibility-tree.ts", docs))).href; + const dir = new URL("node_modules/.cache/compatibility-matrix-test/", docs); + await mkdir(dir, { recursive: true }); + const file = new URL("node-compatibility-matrix.mjs", dir); + await writeFile(file, outputText.replace('"@/lib/compatibility-tree"', JSON.stringify(lib))); + return import(file.href); +} + +test("badges render without their tooltips until hovered or focused", async () => { + const { StatusBadge, StabilityBadge } = await loadMatrix(); + const status = renderToStaticMarkup( + createElement(StatusBadge, { + status: "partial", + detail: "Lowered with Node's options", + verification: { label: "Test-backed", detail: "Corpus programs cover it." }, + }), + ); + assert.ok(status.includes('tabindex="0"'), status); + assert.ok(status.includes('title="Lowered with Node's options'), status); + assert.ok(!status.includes('role="tooltip"'), status); + assert.ok(!status.includes("aria-describedby"), status); + const stability = renderToStaticMarkup( + createElement(StabilityBadge, { stability: { index: "2", level: 2, text: "Stable", inherited: false } }), + ); + assert.ok(stability.includes('tabindex="0"'), stability); + assert.ok(!stability.includes('role="tooltip"'), stability); + const legend = renderToStaticMarkup(createElement(StatusBadge, { status: "supported" })); + assert.ok(!legend.includes("tabindex"), legend); +}); diff --git a/internal/compatibility/generated/node-v24-internal.json b/internal/compatibility/generated/node-v24-internal.json index ba8692b49..cb06685f8 100644 --- a/internal/compatibility/generated/node-v24-internal.json +++ b/internal/compatibility/generated/node-v24-internal.json @@ -1387,7 +1387,20 @@ "evidence": "derived:descendants", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "chapter" @@ -1437,7 +1450,20 @@ "evidence": "compiler-dedicated:AsyncLocalStorage", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "exact" @@ -1510,7 +1536,20 @@ "evidence": "compiler-dedicated:asyncLocalStorage.disable", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "exact" @@ -1537,7 +1576,20 @@ "evidence": "compiler-dedicated:asyncLocalStorage.getStore", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "exact" @@ -1564,7 +1616,20 @@ "evidence": "compiler-dedicated:asyncLocalStorage.enterWith", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "exact" @@ -1591,7 +1656,20 @@ "evidence": "compiler-dedicated:asyncLocalStorage.run", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "exact" @@ -1618,7 +1696,20 @@ "evidence": "compiler-dedicated:asyncLocalStorage.exit", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "exact" @@ -1922,6 +2013,19 @@ "tests": [ "tests/corpus/2213-async-local-storage.cjs", "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs", "tests/harness/node-test.test.ts" ] }, @@ -2484,7 +2588,20 @@ "evidence": "compiler-dedicated:AsyncLocalStorage", "tests": [ "tests/corpus/2213-async-local-storage.cjs", - "tests/corpus/5815-als-callback-context.ts" + "tests/corpus/5815-als-callback-context.ts", + "tests/corpus/7110-als-completion-callbacks.ts", + "tests/corpus/7111-als-bind-snapshot-options.ts", + "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", + "tests/corpus/7119-als-net-event-context.mjs", + "tests/corpus/7380-als-http-event-context.mjs", + "tests/corpus/7383-als-stream-events.ts", + "tests/corpus/7384-als-stream-events.mjs", + "tests/corpus/7385-als-child-process-events.ts", + "tests/corpus/7386-als-child-fork-events/main.mjs", + "tests/corpus/7387-als-http2-events.mjs", + "tests/corpus/7388-als-http2-compat-events.mjs", + "tests/corpus/7389-als-http2-secure-events.mjs", + "tests/corpus/7391-als-response-end-and-finished.mjs" ] }, "anchorSource": "exact" @@ -20228,8 +20345,19 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.crypto.diffieHellman" + "status": "partial", + "evidence": "surface-manifest:node-builtin.crypto.diffieHellman", + "tests": [ + "tests/corpus/1358-crypto-random.ts", + "tests/corpus/1534-crypto-hash-chain.ts", + "tests/corpus/2556-crypto-introspection.ts", + "tests/corpus/2890-crypto-static-utility.ts", + "tests/harness/npm-static.test.ts", + "tests/corpus/node24-crypto-sha2.ts", + "tests/corpus/node24-crypto-kdf.ts", + "tests/corpus/node24-crypto-kdf-errors.ts", + "tests/corpus/node24-crypto-kdf-options.ts" + ] }, "anchorSource": "exact" }, @@ -32551,6 +32679,7 @@ "tests": [ "tests/corpus/6702-ecdh-errors.js", "tests/corpus/6708-dh-errors.js", + "tests/corpus/7714-zlib-brotli-compress.cjs", "tests/corpus/caught-system-error-fields.ts", "tests/corpus/error-stack-frames.ts", "tests/corpus/error-stack-limit.ts", @@ -33343,8 +33472,11 @@ "anchor": "err_brotli_invalid_param", "nodeStability": null, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:errors" + "status": "partial", + "evidence": "compiler-feature:errors.ERR_BROTLI_INVALID_PARAM", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "exact" }, @@ -52194,6 +52326,8 @@ "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs", "tests/corpus/6331-jose-root-entry/main.ts", + "tests/corpus/7743-transform-stream-identity.ts", + "tests/corpus/7747-text-codec-untyped-slot.js", "tests/corpus/text-codec-values/main.ts", "tests/corpus/webassembly-api.mjs", "tests/corpus/webassembly-dylink/main.mjs", @@ -53346,7 +53480,8 @@ "tests": [ "tests/corpus/1423-text-codec.ts", "tests/corpus/text-codec-values/main.ts", - "tests/corpus/3133-text-decoder-streaming.ts" + "tests/corpus/3133-text-decoder-streaming.ts", + "tests/corpus/7747-text-codec-untyped-slot.js" ] }, "anchorSource": "exact" @@ -53397,7 +53532,8 @@ "tests": [ "tests/corpus/1423-text-codec.ts", "tests/corpus/text-codec-values/main.ts", - "tests/corpus/3133-text-decoder-streaming.ts" + "tests/corpus/3133-text-decoder-streaming.ts", + "tests/corpus/7747-text-codec-untyped-slot.js" ] }, "anchorSource": "exact" @@ -53443,8 +53579,11 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:globals" + "status": "partial", + "evidence": "compiler-feature:globals.TransformStream", + "tests": [ + "tests/corpus/7743-transform-stream-identity.ts" + ] }, "anchorSource": "exact" }, @@ -99348,6 +99487,7 @@ "evidence": "derived:descendants", "tests": [ "packages/runtime/test/url-legacy.test.ts", + "tests/corpus/1794-searchparams-url-live.ts", "tests/corpus/5220-url-parse-warnings.ts", "tests/corpus/5221-url-parse-shapes.ts", "tests/corpus/5222-url-parse-hosts.ts", @@ -100606,8 +100746,11 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:url" + "status": "partial", + "evidence": "surface-manifest:node-builtin.url.urlToHttpOptions", + "tests": [ + "tests/corpus/1794-searchparams-url-live.ts" + ] }, "anchorSource": "exact" }, @@ -101277,6 +101420,7 @@ "tests/corpus/5835-util-format-integer-specifiers.ts", "tests/corpus/5884-inspect-layout-options.ts", "tests/corpus/6428-promisify-callback-functions.ts", + "tests/corpus/7747-text-codec-untyped-slot.js", "tests/corpus/node24-deep-equality-properties.js", "tests/corpus/node24-deep-equality.cjs", "tests/corpus/node24-deep-equality.ts", @@ -101805,7 +101949,8 @@ "tests/corpus/text-codec-values/main.ts", "tests/fixtures/node-types/text-codecs.mts", "tests/harness/project-config.test.ts", - "tests/corpus/3133-text-decoder-streaming.ts" + "tests/corpus/3133-text-decoder-streaming.ts", + "tests/corpus/7747-text-codec-untyped-slot.js" ] }, "anchorSource": "exact" @@ -101926,7 +102071,8 @@ "tests/corpus/text-codec-values/main.ts", "tests/fixtures/node-types/text-codecs.mts", "tests/harness/project-config.test.ts", - "tests/corpus/3133-text-decoder-streaming.ts" + "tests/corpus/3133-text-decoder-streaming.ts", + "tests/corpus/7747-text-codec-untyped-slot.js" ] }, "anchorSource": "exact" @@ -102036,7 +102182,8 @@ "tests/corpus/text-codec-values/main.ts", "tests/fixtures/node-types/text-codecs.mts", "tests/harness/project-config.test.ts", - "tests/corpus/3133-text-decoder-streaming.ts" + "tests/corpus/3133-text-decoder-streaming.ts", + "tests/corpus/7747-text-codec-untyped-slot.js" ] }, "anchorSource": "exact" @@ -102065,7 +102212,8 @@ "tests/corpus/text-codec-values/main.ts", "tests/fixtures/node-types/text-codecs.mts", "tests/harness/project-config.test.ts", - "tests/corpus/3133-text-decoder-streaming.ts" + "tests/corpus/3133-text-decoder-streaming.ts", + "tests/corpus/7747-text-codec-untyped-slot.js" ] }, "anchorSource": "exact" @@ -111483,6 +111631,7 @@ "tests/corpus/2630-stream-consumers-errors.ts", "tests/corpus/2862-web-readable-async-iteration.ts", "tests/corpus/4875-stream-consumers-array-buffer.ts", + "tests/corpus/7743-transform-stream-identity.ts", "tests/corpus/stream-web-readable.ts", "tests/corpus/stream-web-require.cjs", "tests/harness/fetch-conformance.test.ts" @@ -112957,8 +113106,11 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:webstreams" + "status": "partial", + "evidence": "compiler-feature:webstreams.TransformStream", + "tests": [ + "tests/corpus/7743-transform-stream-identity.ts" + ] }, "anchorSource": "exact" }, @@ -115612,7 +115764,10 @@ "tests/corpus/5377-zlib-stream-backpressure.cjs", "tests/corpus/5378-zlib-sync-options.cjs", "tests/corpus/5379-zlib-callback-options.ts", - "tests/corpus/5415-zlib-dynamic-inputs.cjs" + "tests/corpus/5415-zlib-dynamic-inputs.cjs", + "tests/corpus/7714-zlib-brotli-compress.cjs", + "tests/corpus/7715-zlib-brotli-decompress.cjs", + "tests/corpus/7716-zlib-brotli.ts" ] }, "anchorSource": "chapter" @@ -116095,8 +116250,11 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.BrotliCompress" + "status": "partial", + "evidence": "compiler-feature:zlib.zlib.BrotliCompress", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "exact" }, @@ -116118,8 +116276,12 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.BrotliDecompress" + "status": "partial", + "evidence": "compiler-feature:zlib.zlib.BrotliDecompress", + "tests": [ + "tests/corpus/7715-zlib-brotli-decompress.cjs", + "tests/corpus/7716-zlib-brotli.ts" + ] }, "anchorSource": "exact" }, @@ -116637,8 +116799,11 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.createBrotliCompress" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.createBrotliCompress", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "exact" }, @@ -116660,8 +116825,12 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.createBrotliDecompress" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.createBrotliDecompress", + "tests": [ + "tests/corpus/7715-zlib-brotli-decompress.cjs", + "tests/corpus/7716-zlib-brotli.ts" + ] }, "anchorSource": "exact" }, @@ -116967,8 +117136,11 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliCompress" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliCompress", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "exact" }, @@ -116990,8 +117162,11 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliCompressSync" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliCompressSync", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "exact" }, @@ -117013,8 +117188,12 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliDecompress" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliDecompress", + "tests": [ + "tests/corpus/7715-zlib-brotli-decompress.cjs", + "tests/corpus/7716-zlib-brotli.ts" + ] }, "anchorSource": "exact" }, @@ -117036,8 +117215,12 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliDecompressSync" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliDecompressSync", + "tests": [ + "tests/corpus/7715-zlib-brotli-decompress.cjs", + "tests/corpus/7716-zlib-brotli.ts" + ] }, "anchorSource": "exact" }, @@ -118054,8 +118237,11 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:zlib" + "status": "partial", + "evidence": "compiler-feature:zlib.BrotliOptions", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "exact" }, @@ -118123,8 +118309,11 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliCompress" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliCompress", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "ancestor" }, @@ -118146,8 +118335,11 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliCompressSync" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliCompressSync", + "tests": [ + "tests/corpus/7714-zlib-brotli-compress.cjs" + ] }, "anchorSource": "ancestor" }, @@ -118169,8 +118361,12 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliDecompress" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliDecompress", + "tests": [ + "tests/corpus/7715-zlib-brotli-decompress.cjs", + "tests/corpus/7716-zlib-brotli.ts" + ] }, "anchorSource": "ancestor" }, @@ -118192,8 +118388,12 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.zlib.brotliDecompressSync" + "status": "partial", + "evidence": "surface-manifest:node-builtin.zlib.brotliDecompressSync", + "tests": [ + "tests/corpus/7715-zlib-brotli-decompress.cjs", + "tests/corpus/7716-zlib-brotli.ts" + ] }, "anchorSource": "ancestor" }, diff --git a/internal/compatibility/src/generate.mjs b/internal/compatibility/src/generate.mjs index 662c0abd6..1b598fbd3 100644 --- a/internal/compatibility/src/generate.mjs +++ b/internal/compatibility/src/generate.mjs @@ -652,7 +652,14 @@ function publicDetail(tier) { source, ) ) { - return "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Brotli and Zstd streams remain unsupported."; + return "Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, and Unzip are Transform streams with Node's output chunking, flush, params, reset, error codes, and option validation; Zstd streams remain unsupported."; + } + if ( + /^compiler-feature:zlib\.(?:zlib\.(?:BrotliCompress|BrotliDecompress)|BrotliOptions)$/.test( + source, + ) + ) { + return "BrotliCompress and BrotliDecompress are Transform streams over the brotli version Node bundles, with Node's parameters, dictionaries, flush, reset, error codes, and option validation; like Node's, they have no params()."; } if (source === "compiler-feature:zlib.zlib.constants") { return "Each zlib.constants member and its module-level copy reads as Node's value; the constants object itself is not a runtime value."; diff --git a/internal/compatibility/static-support.json b/internal/compatibility/static-support.json index 100882c1a..89992231e 100644 --- a/internal/compatibility/static-support.json +++ b/internal/compatibility/static-support.json @@ -190,6 +190,12 @@ "node-builtin.util": ["tests/corpus/2440-console-inspect-args.ts", "tests/corpus/2912-promisify-fs-readfile.ts"], "node-builtin.worker_threads": ["tests/corpus/957-builtins-namespace.ts", "tests/corpus/native-worker-messages.ts", "tests/corpus/native-worker-references.ts"], "node-builtin.zlib": ["tests/corpus/1404-zlib-crypto-bytes.ts", "tests/corpus/2921-zlib-callbacks.ts", "tests/corpus/3115-zlib-static-compression-levels.ts", "tests/corpus/5370-zlib-stream-roundtrip.ts", "tests/corpus/5371-zlib-stream-chunking.cjs", "tests/corpus/5373-zlib-stream-options.cjs", "tests/corpus/5378-zlib-sync-options.cjs", "tests/corpus/5379-zlib-callback-options.ts", "tests/corpus/5415-zlib-dynamic-inputs.cjs"], + "node-builtin.zlib.brotliCompressSync": ["tests/corpus/7714-zlib-brotli-compress.cjs"], + "node-builtin.zlib.brotliCompress": ["tests/corpus/7714-zlib-brotli-compress.cjs"], + "node-builtin.zlib.createBrotliCompress": ["tests/corpus/7714-zlib-brotli-compress.cjs"], + "node-builtin.zlib.brotliDecompressSync": ["tests/corpus/7715-zlib-brotli-decompress.cjs", "tests/corpus/7716-zlib-brotli.ts"], + "node-builtin.zlib.brotliDecompress": ["tests/corpus/7715-zlib-brotli-decompress.cjs", "tests/corpus/7716-zlib-brotli.ts"], + "node-builtin.zlib.createBrotliDecompress": ["tests/corpus/7715-zlib-brotli-decompress.cjs", "tests/corpus/7716-zlib-brotli.ts"], "stdlib.fetch": ["tests/harness/fetch-conformance.test.ts"], "stdlib.map": ["tests/corpus/typed-collection-iterators.ts", "tests/corpus/typed-collection-iterator-references.ts", "tests/corpus/537-map-iter-drains.ts"], "stdlib.set": ["tests/corpus/typed-collection-iterators.ts", "tests/corpus/typed-collection-iterator-references.ts", "tests/corpus/checked-collection-iterators.ts"], @@ -332,11 +338,11 @@ { "chapter": "buffer", "signatures": ["`buf.readBigInt64BE([offset])`", "`buf.readBigInt64LE([offset])`", "`buf.readBigUInt64BE([offset])`", "`buf.readBigUInt64LE([offset])`", "`buf.writeBigInt64BE(value[, offset])`", "`buf.writeBigInt64LE(value[, offset])`", "`buf.writeBigUInt64BE(value[, offset])`", "`buf.writeBigUInt64LE(value[, offset])`"], "status": "supported", "evidence": ["tests/corpus/2902-bigint-bytes.ts", "tests/corpus/2904-bigint-errors.ts"] }, { "chapter": "environment_variables", "symbols": ["export"], "status": "not-applicable" }, { "chapter": "globals", "symbols": ["fetch"], "status": "partial", "evidence": ["tests/harness/fetch-conformance.test.ts"] }, - { "chapter": "globals", "signatures": ["Class: `TextEncoder`", "Class: `TextDecoder`"], "status": "partial", "evidence": ["tests/corpus/1423-text-codec.ts", "tests/corpus/text-codec-values/main.ts", "tests/corpus/3133-text-decoder-streaming.ts"] }, + { "chapter": "globals", "signatures": ["Class: `TextEncoder`", "Class: `TextDecoder`"], "status": "partial", "evidence": ["tests/corpus/1423-text-codec.ts", "tests/corpus/text-codec-values/main.ts", "tests/corpus/3133-text-decoder-streaming.ts", "tests/corpus/7747-text-codec-untyped-slot.js"] }, { "chapter": "globals", "signatures": ["Class: `Blob`", "Class: `File`"], "status": "partial", "evidence": ["tests/corpus/5285-blob-construct.ts", "tests/corpus/5286-blob-readers.ts", "tests/corpus/5287-blob-inspect.ts", "tests/corpus/5288-blob-validation.js", "tests/corpus/5290-blob-dynamic.js"] }, { "chapter": "buffer", "signatures": ["Class: `Blob`", "Class: `File`"], "status": "partial", "evidence": ["tests/corpus/5285-blob-construct.ts", "tests/corpus/5288-blob-validation.js", "tests/fixtures/node-types/blob-file.ts"] }, { "chapter": "buffer", "signatures": ["`blob.arrayBuffer()`", "`blob.bytes()`", "`blob.text()`", "`blob.slice([start[, end[, type]]])`", "`blob.size`", "`type` Type: {string}", "`name` Type: {string}", "`lastModified` Type: {number}"], "status": "supported", "evidence": ["tests/corpus/5285-blob-construct.ts", "tests/corpus/5286-blob-readers.ts", "tests/corpus/5288-blob-validation.js", "tests/corpus/5289-blob-read-order.ts", "tests/corpus/5290-blob-dynamic.js", "tests/fixtures/node-types/blob-file.ts"] }, - { "chapter": "util", "signatures": ["Class: `util.TextEncoder`", "Class: `util.TextDecoder`", "`textEncoder.encode([input])`", "`textDecoder.decode([input[, options]])`"], "status": "partial", "evidence": ["tests/corpus/text-codec-values/main.ts", "tests/fixtures/node-types/text-codecs.mts", "tests/harness/project-config.test.ts", "tests/corpus/3133-text-decoder-streaming.ts"] }, + { "chapter": "util", "signatures": ["Class: `util.TextEncoder`", "Class: `util.TextDecoder`", "`textEncoder.encode([input])`", "`textDecoder.decode([input[, options]])`"], "status": "partial", "evidence": ["tests/corpus/text-codec-values/main.ts", "tests/fixtures/node-types/text-codecs.mts", "tests/harness/project-config.test.ts", "tests/corpus/3133-text-decoder-streaming.ts", "tests/corpus/7747-text-codec-untyped-slot.js"] }, { "chapter": "modules", "symbols": ["__dirname", "__filename", "exports", "require", "module.require", "main", "filename"], "status": "partial", "evidence": ["tests/corpus/1612-cjs-module-globals.cjs", "tests/corpus/1596-cjs-modules/main.js", "tests/corpus/1629-require-main-filename.cjs", "tests/corpus/2830-cjs-require-self-name/main.cjs", "tests/corpus/2880-cjs-module-graph/main.cjs", "tests/corpus/2923-cjs-factory-export/main.cjs"] }, { "chapter": "modules", "symbols": ["require.resolve", "require.resolve.paths"], "status": "partial", "evidence": ["tests/corpus/2858-import-meta-resolve/main.mjs", "tests/corpus/2860-require-resolve/main.cjs"] }, { "chapter": "modules", "symbols": ["cache", "children", "id", "isPreloading", "loaded", "parent", "path", "paths", "Module"], "status": "partial", "evidence": ["tests/corpus/2880-cjs-module-graph/main.cjs"] }, @@ -404,10 +410,15 @@ { "chapter": "util", "signatures": ["`util.promisify(original)`"], "status": "partial", "evidence": ["tests/corpus/1473-promisify-execfile.ts", "tests/corpus/1573-promisify-execfile-env-spread.ts", "tests/corpus/2912-promisify-fs-readfile.ts", "tests/corpus/5625-promisify-exec.ts", "tests/corpus/5626-promisify-forms.ts", "tests/fixtures/node-types/promisify-fs.ts", "tests/corpus/5628-promisify-timers.ts", "tests/fixtures/node-types/promisify-timers.ts", "tests/corpus/5629-promisify-stream.ts", "tests/corpus/5630-promisify-zlib.ts", "tests/fixtures/node-types/promisify-zlib.ts", "tests/corpus/5631-promisify-crypto.ts", "tests/fixtures/node-types/promisify-crypto.ts", "tests/corpus/5632-promisify-dns.ts", "tests/fixtures/node-types/promisify-dns.ts", "tests/corpus/6428-promisify-callback-functions.ts", "tests/fixtures/node-types/promisify-callback-functions.ts", "tests/harness/project-config.test.ts"] }, { "chapter": "typescript", "symbols": ["type"], "status": "supported", "evidence": ["tests/corpus/2702-type-import-link-static/main.ts", "tests/corpus/2703-type-import-link-static-reexport/main.ts", "tests/corpus/2704-type-import-link-static-success/main.ts", "tests/corpus/2707-type-import-link-static-default-reexport/main.ts", "tests/harness/errors.test.ts"] }, { "chapter": "zlib", "signatures": ["Class: `zlib.Deflate`", "Class: `zlib.Inflate`", "Class: `zlib.Gzip`", "Class: `zlib.Gunzip`", "Class: `zlib.DeflateRaw`", "Class: `zlib.InflateRaw`", "Class: `zlib.Unzip`", "Class: `zlib.ZlibBase`", "`zlib.close([callback])`", "`zlib.flush([kind, ]callback)`", "`zlib.params(level, strategy, callback)`", "`zlib.reset()`", "`bytesWritten` Type: {number}", "Class: `Options`"], "status": "partial", "evidence": ["tests/corpus/5370-zlib-stream-roundtrip.ts", "tests/corpus/5371-zlib-stream-chunking.cjs", "tests/corpus/5372-zlib-stream-errors.cjs", "tests/corpus/5373-zlib-stream-options.cjs", "tests/corpus/5374-zlib-stream-flush-params.ts", "tests/corpus/5375-zlib-stream-promises.ts", "tests/corpus/5377-zlib-stream-backpressure.cjs"] }, + { "chapter": "zlib", "signatures": ["Class: `zlib.BrotliCompress`", "Class: `BrotliOptions`"], "status": "partial", "evidence": ["tests/corpus/7714-zlib-brotli-compress.cjs"] }, + { "chapter": "errors", "symbols": ["ERR_BROTLI_INVALID_PARAM"], "status": "partial", "evidence": ["tests/corpus/7714-zlib-brotli-compress.cjs"] }, + { "chapter": "zlib", "signatures": ["Class: `zlib.BrotliDecompress`"], "status": "partial", "evidence": ["tests/corpus/7715-zlib-brotli-decompress.cjs", "tests/corpus/7716-zlib-brotli.ts"] }, { "chapter": "zlib", "signatures": ["`zlib.constants`"], "status": "partial", "evidence": ["tests/corpus/5376-zlib-constants.cjs", "tests/corpus/5374-zlib-stream-flush-params.ts"] }, { "chapter": "dns", "symbols": ["dns.lookup", "dns.lookupService", "dns.getServers", "dns.getDefaultResultOrder", "dns.setDefaultResultOrder"], "status": "partial", "evidence": ["tests/corpus/5648-dns-lookup-forms.ts", "tests/corpus/5649-dns-service-servers.ts", "tests/corpus/5650-dns-lookup-errors-untyped.cjs"] }, { "chapter": "dns", "symbols": ["dnsPromises.lookup", "dnsPromises.lookupService", "dnsPromises.getDefaultResultOrder"], "status": "partial", "evidence": ["tests/corpus/dns-promises-lookup.ts", "tests/corpus/dns-promises-untyped.cjs"] }, - { "chapter": "webstreams", "symbols": ["ReadableStream"], "status": "partial", "evidence": ["tests/corpus/stream-web-readable.ts", "tests/corpus/stream-web-require.cjs", "tests/corpus/2862-web-readable-async-iteration.ts"] } + { "chapter": "webstreams", "symbols": ["ReadableStream"], "status": "partial", "evidence": ["tests/corpus/stream-web-readable.ts", "tests/corpus/stream-web-require.cjs", "tests/corpus/2862-web-readable-async-iteration.ts"] }, + { "chapter": "webstreams", "symbols": ["TransformStream"], "status": "partial", "evidence": ["tests/corpus/7743-transform-stream-identity.ts"] }, + { "chapter": "globals", "signatures": ["Class: `TransformStream`"], "status": "partial", "evidence": ["tests/corpus/7743-transform-stream-identity.ts"] } ], "dedicated": [ { "symbols": ["assert.doesNotThrow", "assert.doesNotReject"], "status": "partial", "evidence": ["tests/corpus/node24-no-error-assertions.ts", "tests/corpus/node24-no-error-assertions.cjs", "tests/corpus/node24-no-error-assertions-coercion.js", "tests/fixtures/node-types/no-error-assertions.ts", "tests/harness/project-config.test.ts"] }, @@ -439,7 +450,7 @@ { "symbols": ["crypto.hkdf", "crypto.hkdfSync", "crypto.scrypt", "crypto.scryptSync"], "status": "partial", "evidence": ["tests/corpus/node24-crypto-kdf.ts", "tests/corpus/node24-crypto-kdf-errors.ts", "tests/corpus/node24-crypto-kdf-options.ts", "tests/corpus/5438-crypto-kdf-digests.ts"] }, { "symbols": ["EventEmitter", "emitter.on", "emitter.addListener", "emitter.once", "emitter.prependListener", "emitter.prependOnceListener", "emitter.off", "emitter.removeListener", "emitter.removeAllListeners", "emitter.emit", "emitter.listenerCount", "emitter.listeners", "emitter.rawListeners", "emitter.eventNames", "emitter.setMaxListeners", "emitter.getMaxListeners", "events.once"], "status": "partial", "evidence": ["tests/corpus/1677-emitter-listeners.ts", "tests/corpus/2620-ee-override-once-order.ts"] }, { "symbols": ["events.getEventListeners", "events.getMaxListeners", "events.listenerCount", "events.setMaxListeners", "events.defaultMaxListeners"], "status": "partial", "evidence": ["tests/corpus/5885-events-max-listener-helpers.ts", "tests/corpus/5846-events-esm-default-max.ts", "tests/corpus/5847-events-cjs-default-max.cjs"] }, - { "symbols": ["AsyncLocalStorage", "asyncLocalStorage.run", "asyncLocalStorage.exit", "asyncLocalStorage.getStore", "asyncLocalStorage.enterWith", "asyncLocalStorage.disable"], "status": "partial", "evidence": ["tests/corpus/2213-async-local-storage.cjs", "tests/corpus/5815-als-callback-context.ts"] }, + { "symbols": ["AsyncLocalStorage", "asyncLocalStorage.run", "asyncLocalStorage.exit", "asyncLocalStorage.getStore", "asyncLocalStorage.enterWith", "asyncLocalStorage.disable", "asyncLocalStorage.name", "AsyncLocalStorage.bind", "AsyncLocalStorage.snapshot"], "status": "partial", "evidence": ["tests/corpus/2213-async-local-storage.cjs", "tests/corpus/5815-als-callback-context.ts", "tests/corpus/7110-als-completion-callbacks.ts", "tests/corpus/7111-als-bind-snapshot-options.ts", "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", "tests/corpus/7119-als-net-event-context.mjs", "tests/corpus/7380-als-http-event-context.mjs", "tests/corpus/7383-als-stream-events.ts", "tests/corpus/7384-als-stream-events.mjs", "tests/corpus/7385-als-child-process-events.ts", "tests/corpus/7386-als-child-fork-events/main.mjs", "tests/corpus/7387-als-http2-events.mjs", "tests/corpus/7388-als-http2-compat-events.mjs", "tests/corpus/7389-als-http2-secure-events.mjs", "tests/corpus/7391-als-response-end-and-finished.mjs"] }, { "symbols": ["StringDecoder", "stringDecoder.write", "stringDecoder.end"], "status": "partial", "evidence": ["tests/corpus/1662-string-decoder-encodings.ts", "tests/corpus/node24-string-decoder.ts", "tests/corpus/5882-string-decoder-runtime-encoding.ts"] }, { "symbols": ["setImmediate", "setInterval", "setTimeout", "clearImmediate", "clearInterval", "clearTimeout", "timersPromises.setTimeout", "timersPromises.setImmediate", "timersPromises.setInterval", "immediate.hasRef", "immediate.ref", "immediate.unref", "timeout.hasRef", "timeout.ref", "timeout.unref", "timeout.refresh"], "status": "partial", "evidence": ["tests/corpus/1804-timers-module.ts", "tests/corpus/1805-timer-callback-args.ts", "tests/corpus/2093-timers-promises.ts", "tests/corpus/2852-timers-promises-interval.ts", "tests/corpus/5830-timers-promises-signal.ts"] }, { "symbols": ["timersPromises.scheduler.wait", "timersPromises.scheduler.yield"], "status": "partial", "evidence": ["tests/corpus/5839-timers-promises-scheduler.ts", "tests/corpus/5889-timers-scheduler-signal.ts"] }, diff --git a/packages/cli-darwin-arm64/package.json b/packages/cli-darwin-arm64/package.json index 23584b60a..bac01cc44 100644 --- a/packages/cli-darwin-arm64/package.json +++ b/packages/cli-darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/cli-darwin-arm64", - "version": "0.4.2", + "version": "0.4.4", "description": "Native scriptc compiler for darwin-arm64", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli-darwin-x64/package.json b/packages/cli-darwin-x64/package.json index d9e6dafc6..6c2fb3123 100644 --- a/packages/cli-darwin-x64/package.json +++ b/packages/cli-darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/cli-darwin-x64", - "version": "0.4.2", + "version": "0.4.4", "description": "Native scriptc compiler for darwin-x64", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli-linux-arm64-gnu/package.json b/packages/cli-linux-arm64-gnu/package.json index 5b8cb4db2..289d8d6bd 100644 --- a/packages/cli-linux-arm64-gnu/package.json +++ b/packages/cli-linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/cli-linux-arm64-gnu", - "version": "0.4.2", + "version": "0.4.4", "description": "Native scriptc compiler for linux-arm64-gnu", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli-linux-arm64-musl/package.json b/packages/cli-linux-arm64-musl/package.json index 0901602ec..3a601ca33 100644 --- a/packages/cli-linux-arm64-musl/package.json +++ b/packages/cli-linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/cli-linux-arm64-musl", - "version": "0.4.2", + "version": "0.4.4", "description": "Native scriptc compiler for linux-arm64-musl", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli-linux-x64-gnu/package.json b/packages/cli-linux-x64-gnu/package.json index f01478aad..8f6b1aa5e 100644 --- a/packages/cli-linux-x64-gnu/package.json +++ b/packages/cli-linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/cli-linux-x64-gnu", - "version": "0.4.2", + "version": "0.4.4", "description": "Native scriptc compiler for linux-x64-gnu", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli-linux-x64-musl/package.json b/packages/cli-linux-x64-musl/package.json index a6986a66a..ff0d1d686 100644 --- a/packages/cli-linux-x64-musl/package.json +++ b/packages/cli-linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/cli-linux-x64-musl", - "version": "0.4.2", + "version": "0.4.4", "description": "Native scriptc compiler for linux-x64-musl", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli-win32-x64-msvc/package.json b/packages/cli-win32-x64-msvc/package.json index 6b03811b8..e94ba31df 100644 --- a/packages/cli-win32-x64-msvc/package.json +++ b/packages/cli-win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/cli-win32-x64-msvc", - "version": "0.4.2", + "version": "0.4.4", "description": "Native scriptc compiler for win32-x64-msvc", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli/package.json b/packages/cli/package.json index 034747c29..26cf10f07 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "scriptc", - "version": "0.4.2", + "version": "0.4.4", "description": "Compile ordinary TypeScript and JavaScript to small, fast native executables and WebAssembly modules that run without Node.js", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/cli/src/bootstrap.ts b/packages/cli/src/bootstrap.ts index 709bd1124..127a11064 100644 --- a/packages/cli/src/bootstrap.ts +++ b/packages/cli/src/bootstrap.ts @@ -70,6 +70,8 @@ async function tryFastPath(): Promise { values["profile-generate"] !== undefined || values["profile-use"] !== undefined || values.sparse || + // The full compiler resolves `native` against the build host. + values["target-cpu"] !== undefined || (values["external-types"] ?? []).length > 0 || // The full CLI validates the Node island policy flags. hasNodeIslandFlags(values) @@ -217,6 +219,7 @@ async function coverageFastPath( values.emit !== undefined || values.strip || values["detached-exit"] || + values["target-cpu"] !== undefined || values.lib || values.profile !== undefined || values.ffi !== undefined || diff --git a/packages/cli/test/target-cpu.test.ts b/packages/cli/test/target-cpu.test.ts new file mode 100644 index 000000000..ca5243564 --- /dev/null +++ b/packages/cli/test/target-cpu.test.ts @@ -0,0 +1,100 @@ +import { execFile } from "node:child_process"; +import { createRequire } from "node:module"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { promisify } from "node:util"; +import { expect, test } from "vitest"; +import { hostTargetCpu } from "../../compiler/src/backend/target-cpu.js"; + +const execFileAsync = promisify(execFile); +const require = createRequire(import.meta.url); +const repoRoot = join(import.meta.dirname, "../../.."); +const cliEntry = join(repoRoot, "packages/cli/src/main.ts"); +const bootstrapEntry = join(repoRoot, "packages/cli/dist/bootstrap.js"); +const tsxLoader = join(dirname(require.resolve("tsx/package.json")), "dist/loader.mjs"); +const host = hostTargetCpu(); +const runsV3 = host === "x86-64-v3" || host === "x86-64-v4"; + +const program = `const a = new Int32Array(4096); +for (let i = 0; i < a.length; i++) a[i] = (i * 2654435761) | 0; +let s = 0; +for (let r = 0; r < 64; r++) for (let i = 0; i < a.length; i++) s = (s + a[i]!) | 0; +console.log(s); +`; + +test.runIf(process.platform === "linux" && process.arch === "x64")( + "--target-cpu compiles the program for the level without mixing cache variants", + async () => { + const dir = await mkdtemp(join("/tmp", "scriptc-target-cpu-")); + const entry = join(dir, "main.ts"); + const outPath = join(dir, "program"); + const llvmPath = join(dir, "main.ll"); + const env = { ...process.env, SCRIPTC_CACHE_DIR: join(dir, "cache") }; + const build = async (args: string[], bootstrap = false): Promise => { + await execFileAsync( + process.execPath, + [ + ...(bootstrap ? [bootstrapEntry] : ["--import", tsxLoader, cliEntry]), + "build", + entry, + "-o", + outPath, + ...args, + ], + { env, maxBuffer: 4 * 1024 * 1024 }, + ); + return readFile(llvmPath, "utf8"); + }; + try { + await writeFile(entry, program); + const expected = `${(await execFileAsync(process.execPath, [entry])).stdout}`; + const baseline = await build([]); + expect(baseline).not.toContain('"target-cpu"'); + expect(await build(["--target-cpu=baseline"])).toBe(baseline); + const v3 = await build(["--target-cpu=x86-64-v3"]); + expect(v3).toMatch(/^attributes #0 = \{ sanitize_address "target-cpu"="x86-64-v3" \}$/m); + if (runsV3) expect((await execFileAsync(outPath)).stdout).toBe(expected); + // The bootstrap's cached route builds without a level. + expect(await build([], true)).toBe(baseline); + expect((await execFileAsync(outPath)).stdout).toBe(expected); + await expect(build(["--target-cpu=haswell"])).rejects.toThrow(/unknown target CPU "haswell"/); + } finally { + await rm(dir, { recursive: true, force: true }); + } + }, + 240_000, +); + +test("--target-cpu levels are refused for arm64 and WASI targets", async () => { + const dir = await mkdtemp(join("/tmp", "scriptc-target-cpu-refusal-")); + const entry = join(dir, "main.ts"); + try { + await writeFile(entry, program); + for (const target of ["aarch64-unknown-linux-gnu", "wasm32-wasi"]) { + for (const kind of ["llvm", "asm", "obj", "exe"]) { + const build = execFileAsync( + process.execPath, + [ + "--import", + tsxLoader, + cliEntry, + "build", + entry, + `--emit=${kind}`, + "-o", + join(dir, `out-${kind}`), + "--target-cpu=x86-64-v3", + ], + { + env: { ...process.env, SCRIPTC_TARGET: target, SCRIPTC_CACHE_DIR: join(dir, "cache") }, + }, + ); + await expect(build, `${target} ${kind}`).rejects.toThrow( + /--target-cpu=x86-64-v3 requires an x86-64 target/, + ); + } + } + } finally { + await rm(dir, { recursive: true, force: true }); + } +}, 240_000); diff --git a/packages/compiler/ambient/scriptc-node-fallback.d.ts b/packages/compiler/ambient/scriptc-node-fallback.d.ts index d7abe70df..1fa787e35 100644 --- a/packages/compiler/ambient/scriptc-node-fallback.d.ts +++ b/packages/compiler/ambient/scriptc-node-fallback.d.ts @@ -917,6 +917,14 @@ interface Buffer< readBigUint64LE(offset?: number): bigint; } interface BufferConstructor { + /** @deprecated since v10.0.0 - Use `Buffer.from(string[, encoding])` instead. */ + new (str: string, encoding?: BufferEncoding): Buffer; + /** @deprecated since v10.0.0 - Use `Buffer.alloc()` instead. */ + new (size: number): Buffer; + /** @deprecated since v10.0.0 - Use `Buffer.from(array)` instead. */ + new (array: ArrayLike): Buffer; + /** @deprecated since v10.0.0 - Use `Buffer.from(arrayBuffer[, byteOffset[, length]])` instead. */ + new (arrayBuffer: ArrayBuffer | SharedArrayBuffer): Buffer; from(data: string, encoding?: BufferEncoding): Buffer; /* The ArrayBuffer form is the VIEW construction: x.buffer with an * optional byte offset/length shares x's storage (the subarray rule). */ @@ -1106,6 +1114,34 @@ declare var ReadableStream: { from(iterable: string): ReadableStream; readonly prototype: ReadableStream; }; +/* The writable side of a TransformStream and its writer. The + * WritableStream constructor has no native lowering; writable streams come + * from `new TransformStream()`. */ +interface WritableStreamDefaultWriter { + readonly closed: Promise; + readonly desiredSize: number | null; + readonly ready: Promise; + abort(reason?: unknown): Promise; + close(): Promise; + releaseLock(): void; + write(chunk?: W): Promise; +} +interface WritableStream { + readonly locked: boolean; + abort(reason?: unknown): Promise; + close(): Promise; + getWriter(): WritableStreamDefaultWriter; +} +/* The identity transform: chunks written to `writable` are read from + * `readable`, and a write completes when its chunk is read. */ +interface TransformStream { + readonly readable: ReadableStream; + readonly writable: WritableStream; +} +declare var TransformStream: { + new (): TransformStream; + readonly prototype: TransformStream; +}; /* fetch, typed as the native JSON + readable-stream slice: one URL, an * optional init with the members CLI requests carry, and a Response @@ -1554,7 +1590,7 @@ declare module "node:fs" { export function writeFileSync(path: string | URL, data: string, encoding: "utf8" | "utf-8"): void; export function writeFileSync(path: string | URL, data: string | Uint8Array): void; export function appendFileSync(path: string | URL, data: string | Uint8Array): void; - export function existsSync(path: string): boolean; + export function existsSync(path: string | URL): boolean; export function mkdirSync(path: string | URL): void; export function mkdirSync( path: string | URL, @@ -2195,7 +2231,7 @@ declare module "fs/promises" { stat(): Promise; [Symbol.asyncDispose](): Promise; } - export function open(path: string, flags?: string, mode?: number): Promise; + export function open(path: string | URL, flags?: string, mode?: number): Promise; export function link( existingPath: string | Uint8Array | URL, newPath: string | Uint8Array | URL, @@ -2232,14 +2268,14 @@ declare module "fs/promises" { atime: number | string | Date, mtime: number | string | Date, ): Promise; - export function readFile(path: string, encoding: "utf8" | "utf-8"): Promise; + export function readFile(path: string | URL, encoding: "utf8" | "utf-8"): Promise; export function readFile( - path: string, + path: string | URL, options: BufferEncoding | { encoding: BufferEncoding }, ): Promise; - export function readFile(path: string): Promise; + export function readFile(path: string | URL): Promise; export function writeFile( - path: string, + path: string | URL, data: string, options?: { mode?: number; @@ -2248,14 +2284,18 @@ declare module "fs/promises" { [option: string]: unknown; }, ): Promise; - export function writeFile(path: string, data: string, encoding: "utf8" | "utf-8"): Promise; + export function writeFile( + path: string | URL, + data: string, + encoding: "utf8" | "utf-8", + ): Promise; export function mkdir( - path: string, + path: string | URL, options?: { recursive?: boolean; mode?: number }, ): Promise; - export function readdir(path: string): Promise; + export function readdir(path: string | URL): Promise; export function readdir( - path: string, + path: string | URL, options: { encoding?: "utf8" | "utf-8"; withFileTypes: true; @@ -2272,12 +2312,12 @@ declare module "fs/promises" { pattern: string | readonly string[], options: import("node:fs").GlobOptionsWithFileTypes, ): AsyncIterableIterator; - export function rm(path: string): Promise; + export function rm(path: string | URL): Promise; export function rm( - path: string, + path: string | URL, options: { recursive?: boolean; force?: boolean; maxRetries?: number; retryDelay?: number }, ): Promise; - export function stat(path: string): Promise; + export function stat(path: string | URL): Promise; export function statfs( path: string | Uint8Array | URL, options?: import("node:fs").StatFsOptions & { bigint?: false }, @@ -2290,21 +2330,21 @@ declare module "fs/promises" { path: string | Uint8Array | URL, options?: import("node:fs").StatFsOptions, ): Promise; - export function realpath(path: string): Promise; - export function lstat(path: string): Promise; - export function unlink(path: string): Promise; - export function chmod(path: string, mode: number): Promise; - export function chown(path: string, uid: number, gid: number): Promise; - export function lchown(path: string, uid: number, gid: number): Promise; - export function access(path: string, mode?: number): Promise; - export function truncate(path: string, len?: number): Promise; - export function appendFile(path: string, data: string | Uint8Array): Promise; - export function copyFile(src: string, dest: string): Promise; - export function mkdtemp(prefix: string): Promise; - export function rmdir(path: string): Promise; - export function rename(oldPath: string, newPath: string): Promise; + export function realpath(path: string | URL): Promise; + export function lstat(path: string | URL): Promise; + export function unlink(path: string | URL): Promise; + export function chmod(path: string | URL, mode: number): Promise; + export function chown(path: string | URL, uid: number, gid: number): Promise; + export function lchown(path: string | URL, uid: number, gid: number): Promise; + export function access(path: string | URL, mode?: number): Promise; + export function truncate(path: string | URL, len?: number): Promise; + export function appendFile(path: string | URL, data: string | Uint8Array): Promise; + export function copyFile(src: string | URL, dest: string | URL): Promise; + export function mkdtemp(prefix: string | URL): Promise; + export function rmdir(path: string | URL): Promise; + export function rename(oldPath: string | URL, newPath: string | URL): Promise; export function opendir( - path: string, + path: string | URL, options?: import("node:fs").OpenDirOptions, ): Promise; } @@ -2756,8 +2796,9 @@ declare var URLSearchParams: { * slashes, non-empty host); pathToFileURL resolves the path and percent- * encodes it into a file: URL. The legacy API: parse() answers a Url * object, format() serializes a Url-shaped object, a string, or a URL, - * resolve() resolves one URL string against another, and the domain pair - * converts a hostname to and from its Punycode form. */ + * resolve() resolves one URL string against another, the domain pair + * converts a hostname to and from its Punycode form, and urlToHttpOptions + * builds http.request options from a URL. */ declare module "url" { export type URL = globalThis.URL; export type URLSearchParams = globalThis.URLSearchParams; @@ -2825,6 +2866,19 @@ declare module "url" { export function resolve(from: string, to: string): string; export function domainToASCII(domain: string): string; export function domainToUnicode(domain: string): string; + /** urlToHttpOptions's null-prototype request options. */ + export interface UrlHttpOptions { + protocol: string; + hostname: string; + hash: string; + search: string; + pathname: string; + path: string; + href: string; + port?: number; + auth?: string; + } + export function urlToHttpOptions(url: URL): UrlHttpOptions; } declare module "node:url" { export * from "url"; @@ -2964,6 +3018,24 @@ declare module "crypto" { options: ED25519KeyPairOptions<"der", "der">, ): KeyPairSyncResult; export function generateKeyPairSync(type: "ed25519", options?: {}): KeyPairKeyObjectResult; + export interface X25519KeyPairOptions { + publicKeyEncoding: { type: "spki"; format: PubF }; + privateKeyEncoding: { type: "pkcs8"; format: PrivF }; + } + export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"pem", "pem">, + ): KeyPairSyncResult; + export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"der", "der">, + ): KeyPairSyncResult; + export function generateKeyPairSync(type: "x25519", options?: {}): KeyPairKeyObjectResult; + export function generateKeyPair( + type: "x25519", + options: {} | undefined, + callback: (err: Error | null, publicKey: KeyObject, privateKey: KeyObject) => void, + ): void; export function generateKeyPair( type: "ed25519", options: {} | undefined, @@ -2992,6 +3064,11 @@ declare module "crypto" { export interface VerifyKeyObjectInput extends SigningOptions { key: KeyObject; } + export function diffieHellman(options: { privateKey: KeyObject; publicKey: KeyObject }): Buffer; + export function diffieHellman( + options: { privateKey: KeyObject; publicKey: KeyObject }, + callback: (err: Error | null, secret: Buffer) => void, + ): void; export function sign( algorithm: string | null | undefined, data: ArrayBufferView, @@ -4795,9 +4872,9 @@ declare module "node:readline/promises" { * inputs with Node's default options; libz links only into zlib-using * binaries. Callback forms use the executable worker pool. Options stay * declared so reached uses receive a named SC2020 refusal. The Deflate, - * Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw and Unzip streams are - * runtime Transform streams; Brotli and Zstd remain outside the lowered - * surface. */ + * Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, Unzip, BrotliCompress + * and BrotliDecompress streams are runtime Transform streams; Zstd remains + * outside the lowered surface. */ declare module "zlib" { import { Transform } from "stream"; export interface ZlibOptions { @@ -4854,50 +4931,66 @@ declare module "zlib" { ): void; export function unzipSync(data: string | Uint8Array, options?: ZlibOptions): Buffer; export function crc32(data: string | Uint8Array, value?: number): number; - export function brotliCompressSync(data: string | Uint8Array): Buffer; - export function brotliDecompressSync(data: Uint8Array): Buffer; - /* Brotli needs a codec the static runtime does not vendor: these are - * declared so their uses receive the named refusal. */ + /* Brotli over the vendored brotli 1.2.0 (scr_brotli.c): `params` keys + * are the numeric BROTLI_PARAM_* and BROTLI_DECODER_PARAM_* constants. */ + export interface BrotliOptions { + [option: string]: unknown; + } export function brotliCompress(data: string | Uint8Array, callback: ZlibCallback): void; - export function brotliDecompress(data: Uint8Array, callback: ZlibCallback): void; - export function createBrotliCompress(options?: ZlibOptions): Transform; - export function createBrotliDecompress(options?: ZlibOptions): Transform; - export class BrotliCompress extends Transform { - constructor(options?: ZlibOptions); - } - export class BrotliDecompress extends Transform { - constructor(options?: ZlibOptions); - } + export function brotliCompress( + data: string | Uint8Array, + options: BrotliOptions, + callback: ZlibCallback, + ): void; + export function brotliCompressSync(data: string | Uint8Array, options?: BrotliOptions): Buffer; + export function brotliDecompress(data: string | Uint8Array, callback: ZlibCallback): void; + export function brotliDecompress( + data: string | Uint8Array, + options: BrotliOptions, + callback: ZlibCallback, + ): void; + export function brotliDecompressSync(data: string | Uint8Array, options?: BrotliOptions): Buffer; /* The stream classes: runtime Transform streams over the zlib engine * (scr_zlib_stream.c). createGzip(options) and new Gzip(options) build - * the same stream; options are validated at runtime with Node's rules. */ + * the same stream; options are validated at runtime with Node's rules. + * Brotli streams have no params(), as in Node. */ class ZlibBase extends Transform { readonly bytesWritten: number; flush(kind?: number, callback?: () => void): void; flush(callback: () => void): void; - params(level: number, strategy: number, callback?: () => void): void; close(callback?: () => void): void; reset(): void; } - export class Deflate extends ZlibBase { + class Zlib extends ZlibBase { + params(level: number, strategy: number, callback?: () => void): void; + } + export class BrotliCompress extends ZlibBase { + constructor(options?: BrotliOptions); + } + export class BrotliDecompress extends ZlibBase { + constructor(options?: BrotliOptions); + } + export function createBrotliCompress(options?: BrotliOptions): BrotliCompress; + export function createBrotliDecompress(options?: BrotliOptions): BrotliDecompress; + export class Deflate extends Zlib { constructor(options?: ZlibOptions); } - export class Inflate extends ZlibBase { + export class Inflate extends Zlib { constructor(options?: ZlibOptions); } - export class Gzip extends ZlibBase { + export class Gzip extends Zlib { constructor(options?: ZlibOptions); } - export class Gunzip extends ZlibBase { + export class Gunzip extends Zlib { constructor(options?: ZlibOptions); } - export class DeflateRaw extends ZlibBase { + export class DeflateRaw extends Zlib { constructor(options?: ZlibOptions); } - export class InflateRaw extends ZlibBase { + export class InflateRaw extends Zlib { constructor(options?: ZlibOptions); } - export class Unzip extends ZlibBase { + export class Unzip extends Zlib { constructor(options?: ZlibOptions); } export function createDeflate(options?: ZlibOptions): Deflate; @@ -7207,9 +7300,18 @@ declare module "node:tty" { declare module "async_hooks" { /* AsyncLocalStorage: fiber-carried context snapshots — run/getStore/ * exit/enterWith/disable lower onto the runtime's active-slot machinery - * (als.* libCalls); values cross as dyn values. */ + * (als.* libCalls); values cross as dyn values. bind and snapshot bind + * functions to the active context; the options carry the store's + * default value and name. */ + export interface AsyncLocalStorageOptions { + defaultValue?: any; + name?: string | undefined; + } export class AsyncLocalStorage { - constructor(); + constructor(options?: AsyncLocalStorageOptions); + static bind any>(fn: Func): Func; + static snapshot(): (fn: (...args: TArgs) => R, ...args: TArgs) => R; + readonly name: string; run(store: T, callback: (...args: any[]) => R, ...args: any[]): R; exit(callback: (...args: any[]) => R, ...args: any[]): R; getStore(): T | undefined; diff --git a/packages/compiler/package.json b/packages/compiler/package.json index a358ae545..570c0a1a5 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/compiler", - "version": "0.4.2", + "version": "0.4.4", "description": "The scriptc compiler — TypeScript/JavaScript frontend, typed IR, LLVM backend", "license": "Apache-2.0", "homepage": "https://scriptc.dev", @@ -40,7 +40,7 @@ }, "dependencies": { "@scriptc/runtime": "workspace:*", - "tsc-ts": "0.0.8", + "tsc-ts": "0.0.9", "typescript": "7.0.2", "typescript5": "npm:typescript@5.9.3" }, diff --git a/packages/compiler/src/backend/executable-features.ts b/packages/compiler/src/backend/executable-features.ts index 32318d53e..2afb887ea 100644 --- a/packages/compiler/src/backend/executable-features.ts +++ b/packages/compiler/src/backend/executable-features.ts @@ -20,6 +20,7 @@ export function executableLinkFeatures(mod: IrModule): NativeLinkFeatures { ...(features.warningReplace ? { warningReplace: true } : {}), ...(features.stdioExtended ? { stdioExtended: true } : {}), zlib: features.zlib, + ...(features.brotli ? { brotli: true } : {}), assert: features.assert, inspect: features.inspect, dynInvoke: features.dynInvoke, @@ -50,6 +51,7 @@ export function executableLinkFeatures(mod: IrModule): NativeLinkFeatures { dgram: features.dgram, dnsPromises: features.dnsPromises, dgramDyn: features.dgramDyn, + textCodecDyn: features.textCodecDyn, watch: features.fsWatch, foreignFfi: hasForeignFfiCallback(mod.ffiImports ?? []), nodeTest: features.nodeTest, diff --git a/packages/compiler/src/backend/library-pack.ts b/packages/compiler/src/backend/library-pack.ts index e9f52f7a0..79be316db 100644 --- a/packages/compiler/src/backend/library-pack.ts +++ b/packages/compiler/src/backend/library-pack.ts @@ -54,6 +54,7 @@ export async function compilePackedLibrary( dgram: false, dnsPromises: false, dgramDyn: false, + textCodecDyn: false, watch: false, foreignFfi: false, nodeTest: false, diff --git a/packages/compiler/src/backend/llvm/alloc.ts b/packages/compiler/src/backend/llvm/alloc.ts index aa6345dab..2efeb96ac 100644 --- a/packages/compiler/src/backend/llvm/alloc.ts +++ b/packages/compiler/src/backend/llvm/alloc.ts @@ -459,18 +459,72 @@ export type FreeObservers = "weak" | "weak-or-source"; * type's disposals), and the block — `scr_cyc_free` for collector-headered * objects, `scr_rt_free` otherwise. Labels use the `sf.` prefix; the lines * end in an open block. + * + * With `keep` (an alloca of ptr holding NULL; inline allocation paths only), + * a block that would go onto the calling thread's free list is stored there + * instead, for a teardown segment (emitSegmentFree); a block the system or + * another arena owns is still freed. */ export function emitObjectFree( host: AllocHost, cyc: boolean, observers: FreeObservers = "weak", + keep: string | null = null, ): string[] { - const lines = threadLocalState(host, "sf", freeLines(host, cyc, observers)); + const lines = threadLocalState(host, "sf", freeLines(host, cyc, observers, keep)); if (host.objectAudit === false) return lines.filter((line) => line !== FREE_NOTE); host.declare(`declare void @scr_obj_free_note()`); return lines; } +/** Lines that put the blocks `first` to `last` (linked through their first + * words; one block when they are equal) of the class at reservation offset + * `off` on the calling thread's free list. */ +function freeListPushLines(prefix: string, off: string, first: string, last: string): string[] { + return [ + ` %${prefix}.shp = getelementptr inbounds i8, ptr @scr_sa, i64 ${SA_SHIFT}`, + ` %${prefix}.sh32 = load i32, ptr %${prefix}.shp`, + ` %${prefix}.sh = zext i32 %${prefix}.sh32 to i64`, + ` %${prefix}.c = lshr i64 ${off}, %${prefix}.sh`, + ` %${prefix}.c8 = shl i64 %${prefix}.c, 3`, + ` %${prefix}.fo = add i64 %${prefix}.c8, ${SA_FREE}`, + ` %${prefix}.fp = getelementptr inbounds i8, ptr @scr_sa, i64 %${prefix}.fo`, + ` %${prefix}.head = load ptr, ptr %${prefix}.fp`, + ` store ptr %${prefix}.head, ptr ${last}`, + ` store ptr ${first}, ptr %${prefix}.fp`, + ]; +} + +/** + * Lines that free a teardown segment: the blocks `first` to `last` (ptr + * values, both NULL for an empty segment), linked through their first words, + * which emitObjectFree kept. Every block of a segment is in the calling + * thread's arena and of one size class, so the segment goes onto that + * class's free list in one step, and the next allocations of the class take + * its blocks in segment order. Labels use `prefix`; the lines end in the + * open block `${prefix}.done`. + */ +export function emitSegmentFree( + host: AllocHost, + prefix: string, + first: string, + last: string, +): string[] { + declareState(host); + const lines = [ + ` %${prefix}.none = icmp eq ptr ${first}, null`, + ` br i1 %${prefix}.none, label %${prefix}.done, label %${prefix}.push`, + `${prefix}.push:`, + ` %${prefix}.base = load i64, ptr @scr_sa`, + ` %${prefix}.bi = ptrtoint ptr ${first} to i64`, + ` %${prefix}.off = sub i64 %${prefix}.bi, %${prefix}.base`, + ...freeListPushLines(prefix, `%${prefix}.off`, first, last), + ` br label %${prefix}.done`, + `${prefix}.done:`, + ]; + return threadLocalState(host, prefix, lines); +} + /** Lines that leave in `%.obs` whether the program can observe a * disposal of this type (folds to a constant). */ function observedLines(prefix: string, observers: FreeObservers): string[] { @@ -482,8 +536,14 @@ function observedLines(prefix: string, observers: FreeObservers): string[] { ]; } -function freeLines(host: AllocHost, cyc: boolean, observers: FreeObservers): string[] { +function freeLines( + host: AllocHost, + cyc: boolean, + observers: FreeObservers, + keep: string | null, +): string[] { if (host.inlineAlloc !== true) { + if (keep !== null) throw new Error("a kept block needs the inline allocation paths"); if (cyc) { host.declare(`declare void @scr_cyc_free(ptr)`); return [` call void @scr_obj_free_note()`, ` call void @scr_cyc_free(ptr %o)`]; @@ -548,16 +608,9 @@ function freeLines(host: AllocHost, cyc: boolean, observers: FreeObservers): str ` %sf.inx = call i1 @llvm.expect.i1(i1 %sf.in, i1 true)`, ` br i1 %sf.inx, label %sf.push, label %sf.sys`, `sf.push:`, - ` %sf.shp = getelementptr inbounds i8, ptr @scr_sa, i64 ${SA_SHIFT}`, - ` %sf.sh32 = load i32, ptr %sf.shp`, - ` %sf.sh = zext i32 %sf.sh32 to i64`, - ` %sf.c = lshr i64 %sf.off, %sf.sh`, - ` %sf.c8 = shl i64 %sf.c, 3`, - ` %sf.fo = add i64 %sf.c8, ${SA_FREE}`, - ` %sf.fp = getelementptr inbounds i8, ptr @scr_sa, i64 %sf.fo`, - ` %sf.head = load ptr, ptr %sf.fp`, - ` store ptr %sf.head, ptr ${blk}`, - ` store ptr ${blk}, ptr %sf.fp`, + ...(keep !== null + ? [` store ptr ${blk}, ptr ${keep}`] + : freeListPushLines("sf", "%sf.off", blk, blk)), ` br label %sf.done`, `sf.sys: ; outside the reservation, or the allocator is compiled out`, ` call void @scr_obj_free_note()`, diff --git a/packages/compiler/src/backend/llvm/bytes-alias.ts b/packages/compiler/src/backend/llvm/bytes-alias.ts index 9aaeaa6c7..d43620fd9 100644 --- a/packages/compiler/src/backend/llvm/bytes-alias.ts +++ b/packages/compiler/src/backend/llvm/bytes-alias.ts @@ -7,9 +7,15 @@ import type { LlvmEmitterContext } from "./expr-context.js"; * buffer. Neither overlaps the other, a native field, array storage or a * module global's slot, so element stores leave header loads and global * receiver slots loop-invariant. Untagged accesses and calls still alias - * every category. */ + * every category. + * + * Header loads are also `!invariant.load`: no store or call between the + * construction of a view and its release changes them. Without it, a data + * pointer load ahead of a bounds branch is sunk into the in-bounds block, + * and LICM keeps a conditionally executed load inside the loop. */ export function bytesHeaderAlias(host: LlvmEmitterContext): string { - return host.memoryAliasAttachment("bytes:header"); + const tag = host.memoryAliasAttachment("bytes:header"); + return tag === "" ? "" : `${tag}, !invariant.load !{}`; } export function bytesElementAlias(host: LlvmEmitterContext): string { diff --git a/packages/compiler/src/backend/llvm/bytes-loop-versions.test.ts b/packages/compiler/src/backend/llvm/bytes-loop-versions.test.ts index cee91c66d..aef39ef75 100644 --- a/packages/compiler/src/backend/llvm/bytes-loop-versions.test.ts +++ b/packages/compiler/src/backend/llvm/bytes-loop-versions.test.ts @@ -11,7 +11,7 @@ import { type IrType, } from "../../ir/ir.js"; import { analyzeIntegerRanges } from "../../ir/integer-ranges.js"; -import { matchIntegerCountedForLoop } from "../../ir/integer-loops.js"; +import { matchIntegerCountedForLoop, matchIntegerPairedForLoop } from "../../ir/integer-loops.js"; import { findInitializedByteLoopBindings } from "./byte-windows.js"; import { matchBytesLoopVersion, type BytesLoopVersion } from "./bytes-loop-versions.js"; import { emitLlvmModule } from "./emitter.js"; @@ -249,6 +249,11 @@ test("emitted loops version the body and tag typed-array memory", () => { const elements = /store double [^\n]*!tbaa !(\d+)/.exec(body)![1]; const header = /load ptr, ptr [^\n]*!tbaa !(\d+)/.exec(body)![1]; expect(elements).not.toBe(header); + // Header loads are invariant for the view's lifetime; element accesses + // are not. + expect(body).toMatch(/load ptr, ptr [^\n]*, !tbaa !\d+, !invariant\.load !\{\}$/m); + expect(body).toMatch(/load i64, ptr [^\n]*, !tbaa !\d+, !invariant\.load !\{\}$/m); + expect(body).not.toMatch(/(load double|store double)[^\n]*invariant\.load/); }); test("loops to a typed array's length version the other typed arrays they index", () => { @@ -380,7 +385,10 @@ test("while loop counters below a bound version their accesses", () => { ]); const declared = (loop: IrStmt): IrFunction => fn([{ kind: "varDecl", localId: "i", init: ref("k"), loc }, loop]); - const versionOf = (loop: IrStmt & { kind: "while" }): BytesLoopVersion | null => { + const versionOf = ( + loop: IrStmt & { kind: "while" }, + workers = false, + ): BytesLoopVersion | null => { const f = declared(loop); const ranges = analyzeIntegerRanges(f); return matchBytesLoopVersion(loop, null, { @@ -391,7 +399,7 @@ test("while loop counters below a bound version their accesses", () => { initialized: findInitializedByteLoopBindings(f).get(loop) ?? new Set(), proven: new Set(), scalarStrings: new Set(), - workers: false, + workers, owned: new Set(), }); }; @@ -408,6 +416,11 @@ test("while loop counters below a bound version their accesses", () => { // An access after the counter advances in the same pass stays checked. const advanced = whileLoop([step, set(ref("out", f64s), ref("i"), num(0))]); expect(versionOf(advanced)).toBeNull(); + // In a worker program, a while loop versions for its counter's accesses, + // and not for thread-owned receivers alone. + expect(versionOf(copy, true)!.ownedReceivers).toHaveLength(2); + const job = whileLoop([set(ref("out", f64s), ref("k"), get(ref("src", f64s), ref("k"))), step]); + expect(versionOf(job, true)).toBeNull(); const mod: IrModule = { irVersion: 17, sourceFile: loc.file, @@ -618,3 +631,78 @@ test("while loop counters below a string's length version its reads", () => { expect(version.bounded.map((c) => c.slack)).toEqual([0]); expect(version.fixed).toEqual([]); }); + +test("a two-counter loop versions the typed arrays it indexes at either counter", () => { + // for (let i = 0, j = k; i < j; i++, j--) { const t = out[i]; out[i] = out[j]; out[j] = t; } + const loop: IrStmt & { kind: "for" } = { + kind: "for", + init: { + kind: "block", + body: [ + { kind: "varDecl", localId: "i", init: num(0), loc }, + { kind: "varDecl", localId: "j", init: ref("k"), loc }, + ], + loc, + }, + cond: { kind: "bin", op: "<", left: ref("i"), right: ref("j"), type: BOOL, loc }, + update: { + kind: "block", + body: [ + { kind: "assign", localId: "i", value: bin("+", ref("i"), num(1)), loc }, + { kind: "assign", localId: "j", value: bin("-", ref("j"), num(1)), loc }, + ], + loc, + }, + body: [ + { kind: "varDecl", localId: "t", init: get(ref("out", f64s), ref("i")), loc }, + set(ref("out", f64s), ref("i"), get(ref("out", f64s), ref("j"))), + set(ref("out", f64s), ref("j"), ref("t")), + // Another index stays checked. + set(ref("src", f64s), bin("+", ref("i"), num(1)), num(0)), + ], + loc, + }; + const kernel = fn([loop], [local("t", F64)]); + const ranges = analyzeIntegerRanges(kernel); + const locals = new Map(kernel.locals.map((l) => [l.id, l])); + const paired = matchIntegerPairedForLoop(loop, locals, ranges)!; + expect(paired).not.toBeNull(); + const version = matchBytesLoopVersion( + loop, + null, + { + locals, + invariantGlobals: new Set(), + captured: new Set(), + ranges, + initialized: findInitializedByteLoopBindings(kernel).get(loop) ?? new Set(), + proven: new Set(), + workers: false, + owned: new Set(), + scalarStrings: new Set(), + }, + paired, + )!; + expect(version.paired?.loop).toBe(paired); + expect(version.paired?.receivers.map((r) => version.receivers[r]!.ref)).toEqual([ + ref("out", f64s), + ]); + expect(version.bounds.size).toBe(4); + const mod: IrModule = { + irVersion: 17, + sourceFile: loc.file, + entry: "__main", + functions: [ + { name: "__main", params: [], returnType: VOID, locals: [], body: [], loc }, + kernel, + ], + }; + const body = /^define internal [^\n]*@sc_bf_kernel[^]*?^}/m.exec(emitLlvmModule(mod))![0]; + const guard = body.slice(0, body.search(/^bytes\.version\.(fast|slow)\d+:$/m)); + expect(guard).toMatch(/icmp sge i64 %\S+, 0/); + expect(guard).toMatch(/icmp slt i64 %\S+, %\S+/); + const fast = /^bytes\.version\.fast\d+:$[^]*?^bytes\.version\.done\d+:$/m.exec(body)![0]; + // The fast copy keeps only the `src[i + 1]` check, and no second guard. + expect(fast.match(/bytes\.index\.invalid\d+:/g)).toHaveLength(1); + expect(fast).not.toContain("counted.fast"); +}); diff --git a/packages/compiler/src/backend/llvm/bytes-loop-versions.ts b/packages/compiler/src/backend/llvm/bytes-loop-versions.ts index 704b6723a..1b51deee1 100644 --- a/packages/compiler/src/backend/llvm/bytes-loop-versions.ts +++ b/packages/compiler/src/backend/llvm/bytes-loop-versions.ts @@ -1,11 +1,15 @@ import { F64, type IrExpr, type IrLocal, type IrStmt } from "../../ir/ir.js"; import type { IntegerRange, IntegerRanges } from "../../ir/integer-ranges.js"; -import type { IntegerCountedForLoop } from "../../ir/integer-loops.js"; +import type { IntegerCountedForLoop, IntegerPairedForLoop } from "../../ir/integer-loops.js"; import type { LlvmEmitterContext } from "./expr-context.js"; import { byteNumberAccess } from "../../ir/byte-numbers.js"; import { everyExprChild, everyStmtChild } from "../../ir/traverse.js"; import { exactInteger, widenInteger } from "./integer-values.js"; -import { emitCountedLoopLimit, emitCountedLoopSafety } from "./counted-loops.js"; +import { + emitCountedLoopLimit, + emitCountedLoopSafety, + emitExactIntegerTest, +} from "./counted-loops.js"; import { emitBytesHeaderLength, emitBytesSharedStorage } from "./expr-bytes.js"; import { f64Lit } from "./common.js"; import { emitStringAsciiFlag, emitStringByteLength } from "./string-reads.js"; @@ -79,6 +83,10 @@ export interface BytesLoopVersion { guarded: ReadonlySet; /** The counted loop whose iteration space the affine checks cover. */ counted: IntegerCountedForLoop | null; + /** A two-counter loop (`for (let i = lo, j = hi; i < j; i++, j--)`) and + * the receivers it indexes at either counter. Both counters stay between + * the two starts, so the guard requires `lo >= 0` and `hi < length`. */ + paired: { loop: IntegerPairedForLoop; receivers: number[] } | null; /** The guard also requires every counter value within [-bound, bound]. */ counterBound: number | null; /** Integer ranges of affine index operands that hold in the unchecked @@ -153,6 +161,7 @@ export function matchBytesLoopVersion( loop: IrStmt & { kind: "for" | "while" }, counted: IntegerCountedForLoop | null, facts: BytesLoopFacts, + paired: IntegerPairedForLoop | null = null, ): BytesLoopVersion | null { const written = new Set(); const temps = new Map(); @@ -438,6 +447,9 @@ export function matchBytesLoopVersion( string, { check: BoundedCheck; nodes: { node: Node; access: WhileAccess }[] } >(); + const pairedCounters = + paired !== null && counted === null ? new Set([paired.low.localId, paired.high.localId]) : null; + const pairedNodes = new Map(); const access = (node: Node, array: IrExpr, index: IrExpr, width: number): void => { const ref = root(array); if (!ref) return; @@ -448,6 +460,20 @@ export function matchBytesLoopVersion( else owned.set(receiver, [node]); } if (facts.proven.has(node)) return; + // An element at either counter of a two-counter loop. + if ( + pairedCounters !== null && + width === 1 && + ref.type.kind === "bytes" && + index.kind === "varRef" && + pairedCounters.has(index.localId) + ) { + const receiver = receiverOf(ref); + const nodes = pairedNodes.get(receiver); + if (nodes) nodes.push(node); + else pairedNodes.set(receiver, [node]); + return; + } const shape = counter !== null ? affine(index) : null; if (shape && shape.counter !== 0) { const operands = new Set(); @@ -543,13 +569,23 @@ export function matchBytesLoopVersion( Math.max(0, MAX_CHECKS - fixedKept.length - affineKept.length), ); const ownedKept = [...owned.entries()].slice(0, MAX_CHECKS); + const pairedKept = [...pairedNodes.entries()].slice( + 0, + Math.max(0, MAX_CHECKS - fixedKept.length - affineKept.length - boundedKept.length), + ); if ( fixedKept.length === 0 && affineKept.length === 0 && boundedKept.length === 0 && - ownedKept.length === 0 + ownedKept.length === 0 && + pairedKept.length === 0 ) return null; + // A while loop versions only for accesses at its counters. Its other + // checks (thread-owned receivers, static indices) save a test or two per + // pass, while a second copy of its body doubles the calls it makes, which + // can keep a worker's job loop from inlining the function that runs a job. + if (loop.kind === "while" && boundedKept.length === 0) return null; const bounds = new Set(); const ownedNodes = new Set(); const used = new Set(); @@ -616,6 +652,10 @@ export function matchBytesLoopVersion( if (access.index !== access.ref) narrow(access.index, { min: 0, max: SAFE }); } } + for (const [receiver, nodes] of pairedKept) { + used.add(receiver); + for (const node of nodes) bounds.add(node); + } const counterChecks: CounterCheck[] = []; for (const [id, min] of counterMins) { const info = whileAccesses!.counters.get(id)!; @@ -658,6 +698,10 @@ export function matchBytesLoopVersion( counters: counterChecks, guarded, counted: affineKept.length > 0 ? counted : null, + paired: + paired !== null && pairedKept.length > 0 + ? { loop: paired, receivers: pairedKept.map(([receiver]) => renumber.get(receiver)!) } + : null, counterBound: needsBound ? bound : null, ranges, }; @@ -1143,6 +1187,25 @@ export function emitBytesLoopVersionGuard( conditions.push(both); } } + if (version.paired !== null) { + // Both counters stay within [lo, hi] (exact integers below 2^53): every + // access at either one is in bounds when `lo >= 0` and `hi < length`. + const { loop } = version.paired; + const [low, high] = [loop.low.start, loop.high.start].map((expr) => { + const tested = emitExactIntegerTest(host, host.emitExpr(expr).name, -SAFE, SAFE); + conditions.push(tested.ok); + return tested.integer; + }); + const nonNegative = B.tmp(); + B.line(`${nonNegative} = icmp sge i64 ${low}, 0`); + conditions.push(nonNegative); + for (const receiver of version.paired.receivers) { + // Lengths are at most 2^53 elements: a signed comparison is exact. + const below = B.tmp(); + B.line(`${below} = icmp slt i64 ${high}, ${lengths[receiver]}`); + conditions.push(below); + } + } let valid = "true"; for (const cond of conditions) { if (valid === "true") valid = cond; @@ -1156,39 +1219,6 @@ export function emitBytesLoopVersionGuard( else B.condBr(valid, fast, slow); } -/** Tests that a double is an exact integer within [min, max] (not -0; NaN - * fails the ordered comparisons) and gives its i64. The integer is frozen, - * so it is a fixed value even when the test fails. */ -function emitExactIntegerTest( - host: LlvmEmitterContext, - value: string, - min: number, - max: number, -): { ok: string; integer: string } { - const B = host.B; - const low = B.tmp(), - high = B.tmp(), - inRange = B.tmp(), - raw = B.tmp(), - integer = B.tmp(), - back = B.tmp(), - bits = B.tmp(), - backBits = B.tmp(), - exact = B.tmp(), - ok = B.tmp(); - B.line(`${low} = fcmp oge double ${value}, ${f64Lit(min)}`); - B.line(`${high} = fcmp ole double ${value}, ${f64Lit(max)}`); - B.line(`${inRange} = and i1 ${low}, ${high}`); - B.line(`${raw} = fptosi double ${value} to i64`); - B.line(`${integer} = freeze i64 ${raw}`); - B.line(`${back} = sitofp i64 ${integer} to double`); - B.line(`${bits} = bitcast double ${value} to i64`); - B.line(`${backBits} = bitcast double ${back} to i64`); - B.line(`${exact} = icmp eq i64 ${bits}, ${backBits}`); - B.line(`${ok} = and i1 ${inRange}, ${exact}`); - return { ok, integer }; -} - function scaleCounter(host: LlvmEmitterContext, value: string, scale: number): string { if (scale === 1) return value; const scaled = host.B.tmp(); diff --git a/packages/compiler/src/backend/llvm/call-lifetimes.test.ts b/packages/compiler/src/backend/llvm/call-lifetimes.test.ts index 90909a024..5c40bb4ce 100644 --- a/packages/compiler/src/backend/llvm/call-lifetimes.test.ts +++ b/packages/compiler/src/backend/llvm/call-lifetimes.test.ts @@ -760,3 +760,154 @@ test("a parameter stored as its last use takes the owned convention", () => { const passed = ctor([{ kind: "exprStmt", expr: call("use", [ref("item", cell)]), loc }]); expect(analyze(passed).borrowed.get("%Box.constructor")).toEqual(new Set([0, 1])); }); + +test("a wrap into a pointer union forwards the wrapped binding to the callee's parameter", () => { + const wrap = (id: string): IrExpr => ({ + kind: "unionWrap", + unionId: "optional", + tag: 0, + value: ref(id, record), + type: optional, + loc, + }); + const leaf = helper("leaf"); + const outer = helper("outer"); + outer.params[0]!.type = outer.locals[0]!.type = record; + outer.locals.push(local("cell", record)); + outer.body = [ + { + kind: "varDecl", + localId: "cell", + init: { kind: "recordLit", fields: [], type: record, loc }, + loc, + }, + { kind: "exprStmt", expr: call("leaf", [wrap("value")]), loc }, + ret(call("leaf", [wrap("cell")])), + ]; + const functions = new Map([leaf, outer].map((fn) => [fn.name, fn])); + const analyzeWith = (pointer: boolean) => + analyzeCallLifetimes( + functions, + undefined, + undefined, + undefined, + undefined, + undefined, + () => pointer, + ); + // A boxed union builds a new box around the payload: a whole-value use. + expect(analyzeWith(false).parameters.get("outer")).toBeUndefined(); + expect(analyzeWith(false).locals.get("outer")).toEqual(new Set()); + // A pointer union passes the payload pointer itself. + expect(analyzeWith(true).parameters.get("outer")).toEqual(new Set([0])); + expect(analyzeWith(true).locals.get("outer")).toEqual(new Set(["cell"])); + // A callee that keeps its parameter keeps the wrapped binding too. + leaf.body.push(ret(ref("value"))); + expect(analyzeWith(true).parameters.get("outer")).toBeUndefined(); + expect(analyzeWith(true).locals.get("outer")).toEqual(new Set()); +}); + +test("a pointer-wrapped binding needs a callee that keeps the narrowed payload in place", () => { + const wrapCell: IrExpr = { + kind: "unionWrap", + unionId: "optional", + tag: 0, + value: ref("cell", record), + type: optional, + loc, + }; + const outer = helper("outer", []); + outer.locals.push(local("cell", record)); + outer.body = [ + { + kind: "varDecl", + localId: "cell", + init: { kind: "recordLit", fields: [], type: record, loc }, + loc, + }, + ret(call("leaf", [wrapCell])), + ]; + const read = (value: IrExpr): IrExpr => ({ + kind: "recordGet", + obj: value, + shapeId: "cell", + field: "x", + type: F64, + loc, + }); + const keeps = (leaf: IrFunction, ...more: IrFunction[]): boolean => { + const result = analyzeCallLifetimes( + new Map([outer, leaf, ...more].map((fn) => [fn.name, fn])), + (className) => className === "Node", + undefined, + undefined, + undefined, + undefined, + () => true, + ); + return result.locals.get("outer")?.has("cell") !== true; + }; + const leafWith = (body: IrStmt[]): IrFunction => ({ ...helper("leaf"), body }); + // Tag tests and field reads of the narrowed payload leave it in place. + expect(keeps(leafWith([{ kind: "exprStmt", expr: tag("value"), loc }]))).toBe(false); + expect(keeps(leafWith([ret(read(narrow("value")))]))).toBe(false); + // So do field reads through a checked narrowing, which throws on the + // other arm (`value.x` of a `T | undefined` parameter). + const checked: IrExpr = { + kind: "ternary", + cond: tag("value"), + then: { kind: "libCall", fn: "error.nodeThrow", args: [], type: record, loc }, + else_: narrow("value"), + type: record, + loc, + }; + expect(keeps(leafWith([ret(read(checked))]))).toBe(false); + // Returning it keeps the payload. + const returnsChecked = leafWith([ret(checked)]); + returnsChecked.returnType = record; + expect(keeps(returnsChecked)).toBe(true); + // Returning the narrowed payload, or a narrowed nullish read, keeps it, + // although the union itself is only projected. + const returned = leafWith([ret(narrow("value"))]); + returned.returnType = record; + expect(analyze(returned).parameters.get("leaf")).toEqual(new Set([0])); + expect(keeps(returned)).toBe(true); + const defaulted = leafWith([ + ret({ kind: "nullish", left: ref("value"), right: ref("other", record), type: record, loc }), + ]); + defaulted.locals.push(local("other", record)); + expect(keeps(defaulted)).toBe(true); + // So does a nullable-pointer field store of the union. + const self: IrType = { kind: "object", className: "Node" }; + const stored = leafWith([ + { + kind: "fieldSet", + obj: ref("this", self), + className: "Node", + field: "next", + value: ref("value"), + loc, + }, + ]); + stored.params = [{ localId: "value", name: "value", type: optional }]; + stored.locals.push(local("this", self)); + expect(keeps(stored)).toBe(true); + // A narrowed payload passed on, or the union passed on, is in place when + // the next callee keeps it in place. + const inner = (body: IrStmt[], type: IrType = record): IrFunction => { + const fn = helper("inner"); + fn.params[0]!.type = fn.locals[0]!.type = type; + fn.body = body; + return fn; + }; + const passNarrowed = leafWith([ret(call("inner", [narrow("value")]))]); + expect(keeps(passNarrowed, inner([ret(read(ref("value", record)))]))).toBe(false); + const returnsRecord = inner([ret(ref("value", record))]); + returnsRecord.returnType = record; + expect(keeps(passNarrowed, returnsRecord)).toBe(true); + const passUnion = leafWith([ret(call("inner", [ref("value")]))]); + expect(keeps(passUnion, inner([ret(read(narrow("value")))], optional))).toBe(false); + const innerReturned = inner([ret(narrow("value"))], optional); + innerReturned.returnType = record; + expect(keeps(passUnion, innerReturned)).toBe(true); +}); diff --git a/packages/compiler/src/backend/llvm/call-lifetimes.ts b/packages/compiler/src/backend/llvm/call-lifetimes.ts index db5bdb574..becd24805 100644 --- a/packages/compiler/src/backend/llvm/call-lifetimes.ts +++ b/packages/compiler/src/backend/llvm/call-lifetimes.ts @@ -14,6 +14,11 @@ import { storedLastUseRead } from "./local-extents.js"; interface ForwardedUse { callee: string; index: number; + /** The binding reached the call inside a nullable-pointer union + * (PointerWrapTest): the callee's parameter holds the binding itself as + * its payload, so the callee must keep the payload, not only the union, + * in place. */ + wrapped: boolean; } interface Uses { @@ -21,6 +26,13 @@ interface Uses { written: Set; declarations: Map; forwards: Map; + /** Union bindings whose payload a use may keep: a narrowed read that is + * not projected or passed on, a narrowed nullish read, a store into a + * nullable-pointer field. The union itself can still be only projected. */ + payloadKept: Set; + /** Narrowed payloads of union bindings passed directly as call arguments: + * the payload stays in place if the callee's parameter is proved. */ + payloadForwards: Map; } interface Parameter { @@ -99,19 +111,55 @@ const NO_INLINE_FIELDS: InlineFieldTest = { record: () => false, classField: () * reads the record and keeps no reference to it. */ export type RecordDynCopyTest = (shapeId: string) => boolean; +/** Wraps into a nullable-pointer union whose value is the payload pointer + * itself (LlEmitter.borrowableNullableWrap): the wrapped binding reaches the + * callee's parameter unchanged. */ +export type PointerWrapTest = (wrap: IrExpr & { kind: "unionWrap" }) => boolean; + function collectUses( fn: IrFunction, nullableField: NullableFieldTest, registerReturn: boolean, inlineField: InlineFieldTest, recordDynCopy: RecordDynCopyTest, + pointerWrap: PointerWrapTest, ): Uses { const uses: Uses = { invalid: new Set(), written: new Set(), declarations: new Map(), forwards: new Map(), + payloadKept: new Set(), + payloadForwards: new Map(), }; + const forward = ( + table: Map, + localId: string, + use: ForwardedUse, + ): void => { + let forwards = table.get(localId); + if (!forwards) table.set(localId, (forwards = [])); + forwards.push(use); + }; + /** The union binding `x` whose payload `value` is: `unionNarrow(x)`, or a + * checked narrowing `unionIsTag(x) ? : unionNarrow(x)` (either + * order), whose other branch is visited as any use. */ + function narrowedBinding(value: IrExpr): string | null { + if (value.kind === "unionNarrow") + return value.value.kind === "varRef" ? value.value.localId : null; + if ( + value.kind !== "ternary" || + value.cond.kind !== "unionIsTag" || + value.cond.value.kind !== "varRef" + ) + return null; + const id = value.cond.value.localId; + if (value.else_.kind === "libCall" && narrowedBinding(value.then) === id) + return expr(value.else_) ? id : null; + if (value.then.kind === "libCall" && narrowedBinding(value.else_) === id) + return expr(value.then) ? id : null; + return null; + } const aliases: { alias: string; source: string }[] = []; const stringInput = (value: IrExpr): boolean => { if (value.kind === "varRef" && value.type.kind === "string") return true; @@ -145,6 +193,12 @@ function collectUses( if (borrowsMapReadInputs(node)) return expr(node.receiver) && node.args.every(stringInput); break; case "unionNarrow": + // The payload may escape; the union itself does not. + if (node.value.kind === "varRef") { + uses.payloadKept.add(node.value.localId); + return true; + } + break; case "unionIsTag": if (node.value.kind === "varRef") return true; break; @@ -161,12 +215,16 @@ function collectUses( node.left.kind === "varRef" && node.left.type.kind === "union" && node.type.kind !== "union" - ) + ) { + uses.payloadKept.add(node.left.localId); return expr(node.right); + } break; case "fieldGet": case "recordGet": - if (node.obj.kind === "varRef") return true; + // A field read of a binding, or of a union binding's narrowed + // payload, keeps neither. + if (node.obj.kind === "varRef" || narrowedBinding(node.obj) !== null) return true; break; case "dynFrom": // A copied record converts field by field; a live capsule keeps it. @@ -200,13 +258,19 @@ function collectUses( return node.args.every(expr); case "call": node.args.forEach((arg, index) => { - if (arg.kind !== "varRef") { + const narrowed = narrowedBinding(arg); + if (narrowed !== null) { + forward(uses.payloadForwards, narrowed, { callee: node.callee, index, wrapped: false }); + return; + } + const wrapped = + arg.kind === "unionWrap" && arg.value.kind === "varRef" && pointerWrap(arg); + const value = wrapped ? arg.value : arg; + if (value.kind !== "varRef") { expr(arg); return; } - let forwards = uses.forwards.get(arg.localId); - if (!forwards) uses.forwards.set(arg.localId, (forwards = [])); - forwards.push({ callee: node.callee, index }); + forward(uses.forwards, value.localId, { callee: node.callee, index, wrapped }); }); return true; case "assignExpr": @@ -250,8 +314,11 @@ function collectUses( node.value.kind === "varRef" && node.value.type.kind === "union" && nullableField(node.className, node.field) - ) + ) { + // The field retains the payload. + uses.payloadKept.add(node.value.localId); return expr(node.obj); + } const stored = fieldStore( node.obj, node.value, @@ -345,10 +412,14 @@ export function analyzeCallLifetimes( registerReturns: ReadonlySet = new Set(), inlineField: InlineFieldTest = NO_INLINE_FIELDS, recordDynCopy: RecordDynCopyTest = () => false, + pointerWrap: PointerWrapTest = () => false, ): CallLifetimes { const usesByFunction = new Map(); const lazyCaptures = new LazyCaptures(functions); const nodes = new Map(); + /** Union parameters that keep their payload in place too, which a + * pointer-wrapped argument needs (ForwardedUse.wrapped). */ + const payloadNodes = new Map(); const unsafe: Parameter[] = []; const consumed: { fn: string; index: number }[] = []; const result: CallLifetimes = { @@ -361,7 +432,7 @@ export function analyzeCallLifetimes( }; for (const fn of functions.values()) { if (fn.generator !== undefined && fn.classCaptures === undefined) { - const uses = collectUses(fn, nullableField, false, inlineField, recordDynCopy); + const uses = collectUses(fn, nullableField, false, inlineField, recordDynCopy, pointerWrap); const locals = new Map(fn.locals.map((local) => [local.id, local])); const stable = new Set(); for (const param of fn.params) { @@ -386,6 +457,7 @@ export function analyzeCallLifetimes( registerReturns.has(fn.name), inlineField, recordDynCopy, + pointerWrap, ); usesByFunction.set(fn.name, uses); // Class-capturing bodies get only local facts (computed below from @@ -460,23 +532,50 @@ export function analyzeCallLifetimes( return node; }), ); + payloadNodes.set( + fn.name, + fn.params.map((param) => { + if (param.type.kind !== "union") return undefined; + const node: Parameter = { safe: !uses.payloadKept.has(param.localId), callers: new Set() }; + if (!node.safe) unsafe.push(node); + return node; + }), + ); } + /** The fact a forwarded use needs from the callee's parameter. */ + const target = (use: ForwardedUse): Parameter | undefined => + use.wrapped ? payloadNodes.get(use.callee)?.[use.index] : nodes.get(use.callee)?.[use.index]; for (const fn of functions.values()) { const params = nodes.get(fn.name); const uses = usesByFunction.get(fn.name); if (!params || !uses) continue; - fn.params.forEach((param, index) => { - const node = params[index]!; + const depend = (node: Parameter, callees: (Parameter | undefined)[]): void => { if (!node.safe) return; - for (const use of uses.forwards.get(param.localId) ?? []) { - const callee = nodes.get(use.callee)?.[use.index]; + for (const callee of callees) { if (!callee) { node.safe = false; unsafe.push(node); - break; + return; } callee.callers.add(node); } + }; + fn.params.forEach((param, index) => { + const node = params[index]!; + const forwards = uses.forwards.get(param.localId) ?? []; + depend(node, forwards.map(target)); + const payload = payloadNodes.get(fn.name)?.[index]; + if (!payload) return; + // The payload stays in place when the union does, when every callee + // the union reaches keeps its payload too, and when every callee its + // narrowed payload reaches proves that parameter. + depend(payload, [ + node, + ...forwards.map((use) => payloadNodes.get(use.callee)?.[use.index]), + ...(uses.payloadForwards.get(param.localId) ?? []).map( + (use) => nodes.get(use.callee)?.[use.index], + ), + ]); }); } for (let i = 0; i < unsafe.length; i++) { @@ -508,7 +607,7 @@ export function analyzeCallLifetimes( ) continue; const forwards = uses.forwards.get(local.id) ?? []; - if (forwards.every((use) => result.parameters.get(use.callee)?.has(use.index) === true)) { + if (forwards.every((use) => target(use)?.safe === true)) { projected.add(local.id); if (eligible(local) && !uses.written.has(local.id)) safe.add(local.id); } diff --git a/packages/compiler/src/backend/llvm/classes.test.ts b/packages/compiler/src/backend/llvm/classes.test.ts index bee954afb..f472eec60 100644 --- a/packages/compiler/src/backend/llvm/classes.test.ts +++ b/packages/compiler/src/backend/llvm/classes.test.ts @@ -77,6 +77,65 @@ test("leaf layouts keep direct teardown while recursive layouts share the depth } }); +test("teardowns of types with children of their own type free their blocks as one segment", () => { + const linked = { kind: "object" as const, className: "Linked" }; + const mod: IrModule = { + irVersion: 17, + sourceFile: loc.file, + entry: "main", + functions: [{ name: "main", params: [], returnType: VOID, locals: [], body: [], loc }], + classes: [ + { + name: "Linked", + fields: [ + { name: "next", type: linked }, + { name: "other", type: linked }, + ], + methods: [], + loc, + }, + ], + records: [{ id: "branch", fields: [{ name: "next", type: linked }] }], + }; + const define = (llvm: string, name: string): string => + new RegExp(`^define internal [^@]*@${name}\\([^]*?^}`, "m").exec(llvm)?.[0] ?? ""; + const linkedDestroy = `${mangleClassRelease("Linked")}_destroy`; + const branchDestroy = `${mangleRecordRelease("branch")}_destroy`; + for (const workers of [false, true]) { + const llvm = emitLlvmModule({ ...mod, workers }, { pointerBits: 64 }); + // Linked's teardown keeps its block and its children's in a segment: it + // releases its own block first and places it by address against its + // first child, a child torn down directly appends to the segment, one + // past the direct chain appends through the runtime, and the chain's + // first teardown frees the segment onto the free list in one step. + const seg = define(llvm, `${linkedDestroy}_s`); + expect(seg.startsWith("define internal ptr"), "segment form").toBe(true); + expect(seg).toMatch( + /store ptr %(?:sf\.blk|o), ptr %sg\.own[^]*call ptr @sc_release_Linked_destroy_s\(/, + ); + // The children's releases have a copy for each place of the own block. + for (const copy of ["ta", "tb"]) + expect(seg).toContain( + `call ptr @scr_rc_destroy_seg(ptr %v0, ptr @${linkedDestroy}_sc, ptr @${linkedDestroy}, ptr %${copy}0.bt)`, + ); + expect(seg).not.toContain("call void @scr_rc_destroy("); + expect(seg).toContain("icmp ult ptr %sg.o, %sg.r1"); + const first = define(llvm, `${linkedDestroy}_d`); + expect(first).toContain(`call ptr @${linkedDestroy}_s(ptr %o, i32 %d, ptr %head)`); + expect(first).toContain("store ptr %first, ptr %sgf.fp"); + expect(first.includes("llvm.threadlocal.address"), "thread-local state").toBe(workers); + expect(define(llvm, linkedDestroy)).toContain(`call void @${linkedDestroy}_d(ptr %o, i32 0)`); + // A record whose child is another type frees its own block as before. + expect(define(llvm, `${branchDestroy}_s`)).toBe(""); + expect(define(llvm, branchDestroy)).toContain(`call void @${mangleClassRelease("Linked")}(`); + } + // Without the inline allocation paths (32-bit targets) a teardown frees + // through the runtime, block by block. + const llvm32 = emitLlvmModule(mod, { pointerBits: 32 }); + expect(define(llvm32, `${linkedDestroy}_s`)).toBe(""); + expect(define(llvm32, `${linkedDestroy}_d`)).toContain(`call void @${linkedDestroy}_d(`); +}); + test("releases split into an inlinable fast path and an out-of-line remainder", () => { // An array of the class itself makes the hierarchy cycle-capable. const kids = { kind: "array" as const, elem: { kind: "object" as const, className: "Node" } }; diff --git a/packages/compiler/src/backend/llvm/classes.ts b/packages/compiler/src/backend/llvm/classes.ts index 8f179828d..699755157 100644 --- a/packages/compiler/src/backend/llvm/classes.ts +++ b/packages/compiler/src/backend/llvm/classes.ts @@ -43,6 +43,9 @@ import { destroyDefinitions, directChildReleaseLines, directDestroyTarget, + segmentPrologueLines, + segmentTeardownLines, + segmentedTeardown, usedDirectTargets, type DirectDestroy, type RegionHooks, @@ -1082,25 +1085,62 @@ export function emitClassShapes( bounded && (directTargets.has(`object:${cls.name}`) || refFields.some((f) => directDestroyTarget(host, f.type, directTargets) !== null)); + // The teardown's own symbol, and whether it keeps a segment (shapes.ts + // segmentedTeardown). + const destroy = `${meta.hierarchy ? mangleClassReleaseDirect(cls.name) : mangleClassRelease(cls.name)}_destroy`; + const segmented = + chained && + !meta.compact && + runtimePrefix === null && + !isStreamRooted && + !meta.threadSlot && + segmentedTeardown( + host, + destroy, + refFields.map((f) => f.type), + directTargets, + ); // The field-releasing teardown body shared by both release shapes // (the public one on standalone classes, the DIRECT one on hierarchy // members). Runs at rc == 0. const teardown = (lines: string[]): void => { - refFields.forEach((f, t) => { - lines.push( - ` %f${t} = getelementptr inbounds %${struct}, ptr %o, i64 0, i32 ${f.index}`, - ` %v${t} = load ptr, ptr %f${t}`, - ); + const release = (prefix: string, f: (typeof refFields)[number], t: number): string[] => { const direct = bounded ? directDestroyTarget(host, f.type, directTargets) : null; - if (direct) - lines.push( - ...directChildReleaseLines(host, `td${t}`, `%v${t}`, direct, llvmCommentText(f.name)), - ); - else - lines.push( + if (direct === null) + return [ ` call void ${releaseSym(host, f.type)}(ptr %v${t}) ; ${llvmCommentText(f.name)}`, - ); - }); + ]; + const own = segmented && direct.destroy === destroy; + return directChildReleaseLines( + host, + `${prefix}${t}`, + `%v${t}`, + direct, + llvmCommentText(f.name), + own, + ); + }; + const loads = refFields.map((f, t) => [ + ` %f${t} = getelementptr inbounds %${struct}, ptr %o, i64 0, i32 ${f.index}`, + ` %v${t} = load ptr, ptr %f${t}`, + ]); + if (segmented) { + // A segmented teardown loads its fields, releases its own block, and + // then releases the fields (shapes.ts segmentTeardownLines). + const ownType = refFields.flatMap((f, t) => + directDestroyTarget(host, f.type, directTargets)?.destroy === destroy ? [`%v${t}`] : [], + ); + lines.push( + ...segmentPrologueLines(), + ...loads.flat(), + ...emitObjectFree(host, traced, "weak", "%sg.own"), + ...segmentTeardownLines(ownType, (prefix) => + refFields.flatMap((f, t) => release(prefix, f, t)), + ), + ); + return; + } + refFields.forEach((f, t) => lines.push(...loads[t]!, ...release("td", f, t))); if (runtimePrefix) lines.push(...regCall("td", runtimePrefix.drop, "")); if (isStreamRooted) lines.push(...stCall("tds", "scr_stream_st_release", "")); if (meta.threadSlot) lines.push(...slotForgetLines(host, struct, meta, "tdw")); @@ -1182,10 +1222,12 @@ export function emitClassShapes( ]; if (traced) reld.push(...cycleDeadLines(host, "teardown")); if (bounded) { - const destroy = `${mangleClassReleaseDirect(cls.name)}_destroy`; const destroyBody: string[] = []; teardown(destroyBody); - defs.push(...destroyDefinitions(destroy, destroyBody, chained), ``); + defs.push( + ...destroyDefinitions(destroy, destroyBody, chained, segmented ? host : null), + ``, + ); host.declare(`declare void @scr_rc_destroy(ptr, ptr)`); reld.push(` call void @scr_rc_destroy(ptr %o, ptr @${destroy})`); } else teardown(reld); @@ -1207,6 +1249,7 @@ export function emitClassShapes( bounded, lifetime, chained, + segmented, ), ``, ); @@ -1367,18 +1410,18 @@ export function emitClassShapes( * statics plus their construct thunks — allocate, run the constructor over * a +1 `this`, and hand the remaining +1 out. No pending check is needed: * in-tier constructors are throw-free by construction. - * `classObjs` maps className → { nameSym } registered during body emission - * (the literal interned while the table was open). */ + * `classObjs` maps className → { nameSym, baseNameSym } registered during + * body emission (the literals interned while the table was open). */ export function emitClassObjDefs( host: ClassHost, metaMap: Map, - classObjs: Map, + classObjs: Map, fnByName: Map, llType: (t: IrType) => string, threadLocal = false, ): string[] { const out: string[] = []; - for (const [className, { nameSym }] of classObjs) { + for (const [className, { nameSym, baseNameSym }] of classObjs) { const meta = metaMap.get(className); if (!meta) throw new InternalCompilerError( @@ -1404,7 +1447,7 @@ export function emitClassObjDefs( const ctorArgs = params.map((p, i) => `${llType(p.type)} %a${i}`); const classObj = `@${mangleClassObj(className)} = internal ${threadLocal ? "thread_local " : ""}global %ScrClassObj ` + - `{ ${host.sizeType} -1, ${host.sizeType} ${intervalMeta.pre}, ${host.sizeType} ${intervalMeta.post}, ptr @${mangleCtorThunk(className)}, ptr ${nameSym}, ${host.sizeType} 0, ${host.sizeType} ${meta.def.jsLength ?? (meta.def.runtime ? 1 : 0)}, ptr null, ptr null } ; class ${className}`; + `{ ${host.sizeType} -1, ${host.sizeType} ${intervalMeta.pre}, ${host.sizeType} ${intervalMeta.post}, ptr @${mangleCtorThunk(className)}, ptr ${nameSym}, ${host.sizeType} 0, ${host.sizeType} ${meta.def.jsLength ?? (meta.def.runtime ? 1 : 0)}, ptr null, ptr null, ptr ${baseNameSym ?? "null"}, ${host.sizeType} ${meta.def.ownStaticFields ? 1 : 0} } ; class ${className}`; if (meta.def.runtime) { // A runtime-provided class: %C.construct allocates through the // runtime and returns the instance. diff --git a/packages/compiler/src/backend/llvm/common.ts b/packages/compiler/src/backend/llvm/common.ts index ac4e28708..cca1b603b 100644 --- a/packages/compiler/src/backend/llvm/common.ts +++ b/packages/compiler/src/backend/llvm/common.ts @@ -29,6 +29,9 @@ export const SCR_ARR_FIELD = { * (SCR_ARR_STATES in scr_array.h). */ export const SCR_ARR_STATES = 16; +/** The `spare` flag of a frozen %ScrArr (SCR_ARR_FROZEN in scr_array.h). */ +export const SCR_ARR_FROZEN = 32; + /** Loads the i32 `len` or `cap` of a %ScrArr at `ptr` into `dest` as a * size-type count. */ export function loadArrCount( diff --git a/packages/compiler/src/backend/llvm/counted-loops.ts b/packages/compiler/src/backend/llvm/counted-loops.ts index ca2dd6b92..1ff4c6580 100644 --- a/packages/compiler/src/backend/llvm/counted-loops.ts +++ b/packages/compiler/src/backend/llvm/counted-loops.ts @@ -151,3 +151,36 @@ function emitExactStart(host: LlvmEmitterContext, expr: IrExpr): string { B.line(`${ok} = and i1 ${inRange}, ${exact}`); return ok; } + +/** Tests that a double is an exact integer within [min, max] (not -0; NaN + * fails the ordered comparisons) and gives its i64. The integer is frozen, + * so it is a fixed value even when the test fails. */ +export function emitExactIntegerTest( + host: LlvmEmitterContext, + value: string, + min: number, + max: number, +): { ok: string; integer: string } { + const B = host.B; + const low = B.tmp(), + high = B.tmp(), + inRange = B.tmp(), + raw = B.tmp(), + integer = B.tmp(), + back = B.tmp(), + bits = B.tmp(), + backBits = B.tmp(), + exact = B.tmp(), + ok = B.tmp(); + B.line(`${low} = fcmp oge double ${value}, ${f64Lit(min)}`); + B.line(`${high} = fcmp ole double ${value}, ${f64Lit(max)}`); + B.line(`${inRange} = and i1 ${low}, ${high}`); + B.line(`${raw} = fptosi double ${value} to i64`); + B.line(`${integer} = freeze i64 ${raw}`); + B.line(`${back} = sitofp i64 ${integer} to double`); + B.line(`${bits} = bitcast double ${value} to i64`); + B.line(`${backBits} = bitcast double ${back} to i64`); + B.line(`${exact} = icmp eq i64 ${bits}, ${backBits}`); + B.line(`${ok} = and i1 ${inRange}, ${exact}`); + return { ok, integer }; +} diff --git a/packages/compiler/src/backend/llvm/debug-info.ts b/packages/compiler/src/backend/llvm/debug-info.ts index 4e37ee5bc..3a75c6263 100644 --- a/packages/compiler/src/backend/llvm/debug-info.ts +++ b/packages/compiler/src/backend/llvm/debug-info.ts @@ -139,7 +139,6 @@ export class LlvmDebugInfo { let id: string; switch (type.kind) { case "f64": - case "date": case "procStream": id = this.add( `!DIBasicType(name: ${quoted(type.kind === "f64" ? "number" : type.kind)}, size: 64, encoding: DW_ATE_float)`, @@ -190,7 +189,7 @@ export class LlvmDebugInfo { ? [] : [ this.add( - `!DIDerivedType(tag: DW_TAG_member, name: "arm${i}", baseType: ${this.type(arm)}, size: ${arm.kind === "bool" ? 8 : arm.kind === "f64" || arm.kind === "date" ? 64 : this.pointerBits})`, + `!DIDerivedType(tag: DW_TAG_member, name: "arm${i}", baseType: ${this.type(arm)}, size: ${arm.kind === "bool" ? 8 : arm.kind === "f64" ? 64 : this.pointerBits})`, ), ], ); @@ -239,9 +238,7 @@ export class LlvmDebugInfo { // Scalar forward captures use an array-backed TDZ box. Expose that // exceptional representation opaquely rather than reporting a bad value. const opaqueBox = - local.boxed && - local.tdz && - (local.type.kind === "f64" || local.type.kind === "bool" || local.type.kind === "date"); + local.boxed && local.tdz && (local.type.kind === "f64" || local.type.kind === "bool"); const type = opaqueBox ? this.pointer( this.add( diff --git a/packages/compiler/src/backend/llvm/dense-array-access.ts b/packages/compiler/src/backend/llvm/dense-array-access.ts index 95612e57c..df37d9c58 100644 --- a/packages/compiler/src/backend/llvm/dense-array-access.ts +++ b/packages/compiler/src/backend/llvm/dense-array-access.ts @@ -505,8 +505,12 @@ export function emitDenseArrayState( indexExpr: IrExpr | undefined, kind: "state" | "has", width: number, + /** The index is a dense array loop counter its entry test proved below + * the hole-free prefix (provenDenseIndex): the slot is a present value. */ + proven = false, ): string { const B = host.B; + if (proven) return kind === "state" ? f64Lit(SCR_ARR_VALUE) : "true"; const fn = kind === "state" ? "scr_arr_state" : "scr_arr_has"; const ty = kind === "state" ? "double" : "i1"; host.declare(`declare ${kind === "state" ? "double" : "zeroext i1"} @${fn}(ptr, double)`); diff --git a/packages/compiler/src/backend/llvm/dense-array-loops.test.ts b/packages/compiler/src/backend/llvm/dense-array-loops.test.ts index f7a4bc892..a88c38575 100644 --- a/packages/compiler/src/backend/llvm/dense-array-loops.test.ts +++ b/packages/compiler/src/backend/llvm/dense-array-loops.test.ts @@ -1,9 +1,10 @@ import { expect, test } from "vitest"; import { BOOL, F64, arrayOf, type IrExpr, type IrLocal, type IrStmt } from "../../ir/ir.js"; -import type { IntegerCountedForLoop } from "../../ir/integer-loops.js"; +import type { IntegerCountedForLoop, IntegerPairedForLoop } from "../../ir/integer-loops.js"; import { countedDenseShape, matchDenseArrayLoop, + pairedDenseShape, type DenseLoopShape, } from "./dense-array-loops.js"; @@ -42,6 +43,8 @@ const locals = new Map([ ["a", { id: "a", name: "a", type: ARR, mutable: false }], ["b", { id: "b", name: "b", type: ARR, mutable: true }], ["c", { id: "c", name: "c", type: ARR, mutable: false }], + ["j", { id: "j", name: "j", type: F64, mutable: true }], + ["t", { id: "t", name: "t", type: F64, mutable: false }], ]); /** for (let i = 0; i < a.length; i++) body */ @@ -193,3 +196,100 @@ test("a loop to another invariant limit covers each array whose length it keeps" expect([...matchLimited(resizing, new Set(["b"]))!.arrays.keys()]).toEqual(["a"]); expect(matchLimited(resizing)).toBeNull(); }); + +test("a two-counter loop covers the arrays indexed at either counter", () => { + // for (let i = 1, j = k - 1; i < j; i++, j--) { t = a[i]; a[i] = a[j]; a[j] = t; } + const paired: IntegerPairedForLoop = { + low: { localId: "i", start: num(1), step: 1, startGuarded: false }, + high: { localId: "j", start: ref("k"), step: -1, startGuarded: true }, + inclusive: false, + guarded: true, + range: { min: -Number.MAX_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER }, + }; + const pairedShape = pairedDenseShape(paired); + const matchPaired = (body: IrStmt[]) => + matchDenseArrayLoop(loop(body), pairedShape, locals, new Set(), new Set(["a", "b"])); + const swap: IrStmt[] = [ + { kind: "varDecl", localId: "t", init: read("a"), loc }, + write("a", read("a", ref("j"))), + write("a", ref("t"), ref("j")), + ]; + const dense = matchPaired(swap); + expect([...dense!.arrays.keys()]).toEqual(["a"]); + expect(dense!.pair).toEqual({ high: "j", lowStart: num(1), highStart: ref("k") }); + expect(dense!.range).toEqual({ min: 0, max: 2147483646 }); + // Stores at any other index may extend the array. + expect(matchPaired([...swap, write("a", num(0), ref("k"))])).toBeNull(); +}); + +test("a present-undefined store is covered only where it stores a covered read's missing arm", () => { + // const v = b[i] (as number | undefined); + // if (v is undefined) c[i] = undefined; else c[i] = v is number ? v : throw; + const unionType = { kind: "union" as const, unionId: "u" }; + const v: IrExpr = { kind: "varRef", localId: "v", type: unionType, loc }; + const isTag = (tag: number): IrExpr => ({ + kind: "unionIsTag", + unionId: "u", + tag, + negated: false, + value: v, + type: BOOL, + loc, + }); + const reader = (array: string, index = ref("i")): IrExpr => ({ + kind: "call", + callee: "%arr.idxOr.f64", + args: [arr(array), index], + type: unionType, + loc, + }); + const undefinedStore: IrStmt = { kind: "arraySetUndefined", arr: arr("c"), index: ref("i"), loc }; + const guarded = (array: string): IrStmt[] => [ + { kind: "varDecl", localId: "v", init: reader(array), loc }, + { + kind: "if", + cond: isTag(1), + then: [undefinedStore], + else_: [ + write("c", { + kind: "ternary", + cond: isTag(0), + then: { kind: "unionNarrow", unionId: "u", tag: 0, value: v, type: F64, loc }, + else_: { + kind: "libCall", + fn: "error.nodeThrow", + args: [num(1), { kind: "strLit", value: "", type: { kind: "string" }, loc }], + type: F64, + loc, + }, + type: F64, + loc, + }), + ], + loc, + }, + ]; + const withV = new Map([ + ...locals, + ["v", { id: "v", name: "v", type: unionType, mutable: false }], + ]); + const matchStores = (body: IrStmt[], initialized: string[]) => + matchDenseArrayLoop(loop(body), shape, withV, new Set(), new Set(initialized)); + const dense = matchStores(guarded("b"), ["a", "b", "c"]); + expect([...dense!.arrays.keys()].sort()).toEqual(["b", "c"]); + // A read of an array the entry test does not cover may miss in the fast + // copy, and its undefined store would run there. + expect(matchStores(guarded("b"), ["a", "c"])).toBeNull(); + // A present undefined stored on its own leaves out its array and every + // array the other name may hold. + const alone: IrStmt[] = [{ kind: "exprStmt", expr: read("b"), loc }, undefinedStore]; + expect(matchStores(alone, ["a", "b", "c"])).toBeNull(); + const separate = matchDenseArrayLoop( + loop(alone), + shape, + withV, + new Set(["c"]), + new Set(["a", "b", "c"]), + ); + expect([...separate!.arrays.keys()]).toEqual(["b"]); +}); diff --git a/packages/compiler/src/backend/llvm/dense-array-loops.ts b/packages/compiler/src/backend/llvm/dense-array-loops.ts index 184ff1353..b806939b5 100644 --- a/packages/compiler/src/backend/llvm/dense-array-loops.ts +++ b/packages/compiler/src/backend/llvm/dense-array-loops.ts @@ -20,16 +20,23 @@ * A counted loop with another invariant limit, such as `j < n` after * `const n = a.length` or `j < 5`, is covered the same way: the test bounds * the loop's exclusive limit by 2^31 - 1 and by each indexed array's - * `filled`, and the loop must provably never change any of their lengths. */ + * `filled`, and the loop must provably never change any of their lengths. + * + * A two-counter loop closing in on itself, such as + * `for (let i = 1, j = k - 1; i < j; i++, j--)`, indexes only between its + * low counter's start and its high counter's: the low counter only rises + * and stays below the high one, which only falls. Its test bounds the low + * start below by 0 and the high start by 2^31 - 2 and below each indexed + * array's `filled`, both starts being exact integers. */ import type { IrExpr, IrLocal, IrStmt } from "../../ir/ir.js"; import type { IntegerRange } from "../../ir/integer-ranges.js"; import { preservesArrayLength } from "../../ir/array-length-stability.js"; import { everyExprChild, everyStmtChild } from "../../ir/traverse.js"; -import type { IntegerCountedForLoop } from "../../ir/integer-loops.js"; +import type { IntegerCountedForLoop, IntegerPairedForLoop } from "../../ir/integer-loops.js"; import type { LlvmEmitterContext } from "./expr-context.js"; import { loadArrCount, SCR_ARR_FIELD } from "./common.js"; -import { emitCountedLoopLimit } from "./counted-loops.js"; +import { emitCountedLoopLimit, emitExactIntegerTest } from "./counted-loops.js"; export const INT32_MAX = 2147483647; @@ -47,6 +54,17 @@ export interface DenseArrayLoop { arrays: ReadonlyMap; /** The counter's range on every pass of the fast copy. */ range: IntegerRange; + /** A two-counter loop's high counter and both starts, when the counter + * is its low one (`length` and `limit` are then null). */ + pair: DensePair | null; +} + +/** The high counter of a two-counter dense loop, and the starts its entry + * test bounds. */ +export interface DensePair { + high: string; + lowStart: IrExpr; + highStart: IrExpr; } /** The fast copy duplicates the loop; keep the duplicated code small. */ @@ -62,6 +80,8 @@ export interface DenseLoopShape { /** The counter's proven range on every pass of the ordinary loop; the * fast copy keeps its minimum, which must be at least 0. */ range: IntegerRange; + /** A two-counter loop's high counter and starts. */ + pair?: DensePair; } /** `length.length - c` (c an integer literal, 0 when absent). */ @@ -107,6 +127,124 @@ export function countedDenseShape( return { counter: counted.localId, length: start.receiver, range: counted.range }; } +/** The shape of a two-counter loop whose low counter rises and high + * counter falls: both stay between the two starts, which the entry test + * bounds, so the fast copy's counters lie in [0, 2^31 - 2]. */ +export function pairedDenseShape(loop: IntegerPairedForLoop): DenseLoopShape { + return { + counter: loop.low.localId, + length: null, + range: { min: 0, max: INT32_MAX - 1 }, + pair: { high: loop.high.localId, lowStart: loop.low.start, highStart: loop.high.start }, + }; +} + +interface ElementRead { + arr: IrExpr; + index: IrExpr; +} + +/** The array and index of a call of an element-reader helper + * (`%arr.idxOr.*`, ir/int32-arrays.ts isElementReader): `a[i]` as + * `number | undefined`. */ +function elementReaderCall(e: IrExpr): ElementRead | null { + if (e.kind !== "call" || !e.callee.startsWith("%arr.idxOr.") || e.args.length !== 2) return null; + return { arr: e.args[0]!, index: e.args[1]! }; +} + +/** The array and index of an element read as `T | undefined`: an + * element-reader call, or its body inlined for an int32 array + * (`arrayState(a, i) === 1 ? wrap(a[i]) : wrap(undefined)`). */ +function elementRead(e: IrExpr): ElementRead | null { + const call = elementReaderCall(e); + if (call) return call; + if (e.kind !== "ternary") return null; + const cond = e.cond; + if (cond.kind !== "bin" || cond.op !== "===") return null; + const state = cond.left, + present = cond.right, + wrapped = e.then, + missing = e.else_; + if (state.kind !== "arrayState" || present.kind !== "numLit" || present.value !== 1) return null; + if (wrapped.kind !== "unionWrap" || missing.kind !== "unionWrap") return null; + const value = wrapped.value; + if (value.kind !== "arrayGet" || missing.value.kind !== "unitLit") return null; + const same = (x: IrExpr, y: IrExpr): boolean => + x.kind === "varRef" && y.kind === "varRef" && x.localId === y.localId; + return same(state.arr, value.arr) && same(state.index, value.index) + ? { arr: state.arr, index: state.index } + : null; +} + +/** The present-undefined stores of `a[i] = b[j]!` as lowering spells it, + * with the element read each one stores the missing arm of: + * + * const v = %arr.idxOr(b, j); + * if (v is undefined) a[i] = undefined; else a[i] = v is number ? v : throw; + * + * The read's local is declared once in the same list as the `if`, and only + * read after it. */ +function missingElementStores(body: IrStmt[]): Map { + const result = new Map(); + const writes = new Map(); + const count = (id: string): void => void writes.set(id, (writes.get(id) ?? 0) + 1); + const expr = (e: IrExpr): boolean => { + if (e.kind === "assignExpr" || e.kind === "incDec") count(e.localId); + return everyExprChild(e, expr, stmt); + }; + const stmt = (s: IrStmt): boolean => { + if (s.kind === "varDecl" || s.kind === "assign") count(s.localId); + return everyStmtChild(s, expr, stmt); + }; + body.every(stmt); + const list = (stmts: IrStmt[]): void => { + const reads = new Map(); + for (const s of stmts) { + if (s.kind === "varDecl" && s.init && writes.get(s.localId) === 1) { + const read = elementRead(s.init); + if (read) reads.set(s.localId, read); + } + if (s.kind === "if") { + const store = missingArmStore(s, reads); + if (store) result.set(store.stmt, store.read); + } + // Nested loops keep a loop out of the fast copy (matchDenseArrayLoop), + // and other statement lists leave their stores unmatched. + if (s.kind === "block") list(s.body); + if (s.kind === "if") { + list(s.then); + if (s.else_) list(s.else_); + } + } + }; + list(body); + return result; +} + +/** The present-undefined store of `if (v is undefined) a[i] = undefined; + * else a[i] = v is number ? v : throw;`, with the read `v` holds. */ +function missingArmStore( + s: IrStmt & { kind: "if" }, + reads: ReadonlyMap, +): { stmt: IrStmt; read: ElementRead } | null { + const cond = s.cond; + if (cond.kind !== "unionIsTag" || cond.negated || cond.value.kind !== "varRef") return null; + const local = cond.value.localId; + const read = reads.get(local); + if (!read || s.then.length !== 1 || s.else_?.length !== 1) return null; + const store = s.then[0]!, + other = s.else_[0]!; + if (store.kind !== "arraySetUndefined" || other.kind !== "arraySet") return null; + const value = other.value; + if (value.kind !== "ternary") return null; + const test = value.cond, + narrowed = value.then; + if (test.kind !== "unionIsTag" || test.negated || test.tag === cond.tag) return null; + if (test.value.kind !== "varRef" || test.value.localId !== local) return null; + if (narrowed.kind !== "unionNarrow" || narrowed.tag !== test.tag) return null; + return narrowed.type.kind === "f64" ? { stmt: store, read } : null; +} + /** Match the fast copy of a loop of the given shape. `initialized` holds the * bindings definitely assigned when the loop starts; `unaliased` the local * arrays no other binding holds. */ @@ -118,6 +256,8 @@ export function matchDenseArrayLoop( initialized: ReadonlySet, ): DenseArrayLoop | null { const { counter, length } = shape; + const high = shape.pair?.high ?? null; + const isCounter = (id: string): boolean => id === counter || id === high; if (shape.range.min < 0 || !loop.cond) return null; let nodes = 0, nested = false; @@ -126,13 +266,21 @@ export function matchDenseArrayLoop( // element write. const disturbed = new Set(); const access = (arr: IrExpr, index: IrExpr): void => { - if (arr.kind === "varRef" && index.kind === "varRef" && index.localId === counter) + if (arr.kind === "varRef" && index.kind === "varRef" && isCounter(index.localId)) candidates.set(arr.localId, arr); }; + const missing = missingElementStores(loop.body); + // The element reads behind the guarded present-undefined stores. + const guards: ElementRead[] = []; + // The arrays of the other present-undefined stores. The fast copy runs + // them, and the slot each one writes leaves the hole-free prefix. + const undefinedStores: IrExpr[] = []; const expr = (e: IrExpr): boolean => { if (++nodes > MAX_LOOP_NODES) return false; if (e.kind === "assignExpr" || e.kind === "incDec") disturbed.add(e.localId); if (e.kind === "arrayGet") access(e.arr, e.index); + const reader = elementReaderCall(e); + if (reader) access(reader.arr, reader.index); if (e.kind === "arrIntrinsic") { if (e.method === "getNumber" && e.args[0]) access(e.receiver, e.args[0]); else if (e.method !== "length" && e.receiver.kind === "varRef") @@ -147,9 +295,21 @@ export function matchDenseArrayLoop( if (s.kind === "assign" || s.kind === "varDecl") disturbed.add(s.localId); if (s.kind === "arraySet") { access(s.arr, s.index); - if (s.arr.kind === "varRef" && (s.index.kind !== "varRef" || s.index.localId !== counter)) + if (s.arr.kind === "varRef" && (s.index.kind !== "varRef" || !isCounter(s.index.localId))) disturbed.add(s.arr.localId); } + if (s.kind === "arraySetUndefined") { + // A present undefined at a counter only where it stores a missing + // element read (`a[i] = b[j]!`): the fast copy reads a present value + // there, so the store never runs in it. + const read = missing.get(s); + if (read === undefined || s.index.kind !== "varRef" || !isCounter(s.index.localId)) { + undefinedStores.push(s.arr); + } else { + access(s.arr, s.index); + guards.push(read); + } + } return everyStmtChild(s, expr, stmt); }; if (!loop.body.every(stmt) || !expr(loop.cond) || nested) return null; @@ -164,6 +324,15 @@ export function matchDenseArrayLoop( ) ) return null; + // A store through another name, such as `const b = a` or a field, lands + // in `a` unless one of the two bindings is a local array no other binding + // holds. + const mayStoreUndefined = (id: string): boolean => + undefinedStores.some((arr) => + arr.kind === "varRef" + ? arr.localId === id || (!unaliased.has(id) && !unaliased.has(arr.localId)) + : !unaliased.has(id), + ); const arrays = new Map(); for (const [id, ref] of candidates) { const local = locals.get(id); @@ -173,6 +342,7 @@ export function matchDenseArrayLoop( local.boxed || local.tdz || disturbed.has(id) || + mayStoreUndefined(id) || (id !== length?.localId && !initialized.has(id)) ) continue; @@ -180,18 +350,33 @@ export function matchDenseArrayLoop( // once: the loop must not change any of their lengths. if ( length === null && - !preservesArrayLength(nodesOfLoop, { receiver: id, inRange: counter }, locals, unaliased) + !preservesArrayLength( + nodesOfLoop, + { receiver: id, inRange: counter, inRangeHigh: high }, + locals, + unaliased, + ) ) continue; arrays.set(id, ref); } if (arrays.size === 0) return null; + // Each guarded store's read must be one the fast copy proves present. + for (const { arr, index } of guards) + if ( + arr.kind !== "varRef" || + !arrays.has(arr.localId) || + index.kind !== "varRef" || + !isCounter(index.localId) + ) + return null; return { counter, length, limit: length === null ? (shape.limit ?? null) : null, arrays, range: { min: shape.range.min, max: Math.min(shape.range.max, INT32_MAX - 1) }, + pair: shape.pair ?? null, }; } @@ -207,6 +392,7 @@ export function emitDenseLoopGuard(host: LlvmEmitterContext, loop: DenseArrayLoo host.markMemoryPointer(ptr, "array:header"); return ptr; }; + if (loop.pair !== null) return emitPairedDenseGuard(host, loop, loop.pair, header); if (loop.length === null) { // A limit at or below the start runs no pass, and passes the test. const limit = emitCountedLoopLimit(host, loop.limit!); @@ -254,3 +440,36 @@ export function emitDenseLoopGuard(host: LlvmEmitterContext, loop: DenseArrayLoo } return valid; } + +/** A two-counter loop's entry test: both starts are exact integers, the + * low one at least 0 and the high one at most 2^31 - 2 and below every + * indexed array's hole-free prefix bound. The starts are stable values + * (matchIntegerPairedForLoop), so evaluating them here again is unobservable. */ +function emitPairedDenseGuard( + host: LlvmEmitterContext, + loop: DenseArrayLoop, + pair: DensePair, + header: (array: string, field: number) => string, +): string { + const B = host.B; + const start = (expr: IrExpr, min: number, max: number) => + emitExactIntegerTest(host, host.emitExpr(expr).name, min, max); + const low = start(pair.lowStart, 0, INT32_MAX - 1); + const high = start(pair.highStart, -1, INT32_MAX - 1); + let valid = B.tmp(); + B.line(`${valid} = and i1 ${low.ok}, ${high.ok}`); + for (const ref of loop.arrays.values()) { + const array = host.emitStableReceiver(ref, []); + const filledPtr = header(array.name, SCR_ARR_FIELD.filled), + filled = B.tmp(), + wide = B.tmp(), + covered = B.tmp(), + both = B.tmp(); + B.line(`${filled} = load i32, ptr ${filledPtr}${host.fieldAliasAttachment(filledPtr)}`); + B.line(`${wide} = zext i32 ${filled} to i64`); + B.line(`${covered} = icmp slt i64 ${high.integer}, ${wide}`); + B.line(`${both} = and i1 ${valid}, ${covered}`); + valid = both; + } + return valid; +} diff --git a/packages/compiler/src/backend/llvm/dyn-handle-tags.test.ts b/packages/compiler/src/backend/llvm/dyn-handle-tags.test.ts index c015b85d0..8c3e5de80 100644 --- a/packages/compiler/src/backend/llvm/dyn-handle-tags.test.ts +++ b/packages/compiler/src/backend/llvm/dyn-handle-tags.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from "node:fs"; import { expect, test } from "vitest"; import { DYN_HANDLE_KINDS } from "../../ir/ir.js"; -import { DYN_HANDLE_TAG_NUM } from "./dyn.js"; +import { DYN_HANDLE_TAG_NUM, TEXT_CODEC_TAG_NUM } from "./dyn.js"; // SCR_DYNH_TAGS in scr_dyn.h lists the ScrDynHandleTag values by name, each // with an explicit value, so changes that add tags edit different lines. The @@ -39,3 +39,9 @@ test("the backend's handle tag numbers match scr_dyn.h", () => { expect(values.get(tag!), `${kind} (${tag})`).toBe(num); } }); + +test("the backend's codec handle tag numbers match scr_dyn.h", () => { + const values = new Map(tags.map((tag) => [tag.name, tag.value])); + expect(values.get("SCR_DYNH_TEXT_DECODER")).toBe(TEXT_CODEC_TAG_NUM.TextDecoder); + expect(values.get("SCR_DYNH_TEXT_ENCODER")).toBe(TEXT_CODEC_TAG_NUM.TextEncoder); +}); diff --git a/packages/compiler/src/backend/llvm/dyn.ts b/packages/compiler/src/backend/llvm/dyn.ts index 86626d304..6d02daa8b 100644 --- a/packages/compiler/src/backend/llvm/dyn.ts +++ b/packages/compiler/src/backend/llvm/dyn.ts @@ -43,6 +43,8 @@ import { isDynTypedRefType, isRefCounted, isUnitType, + recordTextCodecClass, + textCodecRecordClass, typeKey, } from "../../ir/ir.js"; import { dynDesc, streamTypedRefEligible } from "../../ir/analysis.js"; @@ -94,6 +96,17 @@ export const DYN_HANDLE_TAG_NUM: Record = { cryptoHmac: 53, }; +/** The handle tags of TextEncoder and TextDecoder records boxed as dyn + * (scr_dyn.h; scr_text_codec_dyn.c boxes and unboxes them). */ +export const TEXT_CODEC_TAG_NUM: Record<"TextEncoder" | "TextDecoder", number> = { + TextDecoder: 76, + TextEncoder: 77, +}; + +/** The decoder record fields scr_text_decoder_to_dyn reads, in its + * parameter order. */ +const TEXT_DECODER_FIELDS = ["%TextDecoder", "%fatal", "%ignoreBOM", "%state"] as const; + /** Handle kinds whose runtime unit boxes them through its own function, * which installs the tag's ops on first use (scr_crypto.h). Programs that * never box one of these values do not link its dynamic members. */ @@ -456,7 +469,7 @@ export class LlDyn { /** The value LLVM type of a dynCheck result / toDyn operand for `t`. */ private valTy(t: IrType): string { - return t.kind === "f64" || t.kind === "date" || t.kind === "procStream" + return t.kind === "f64" || t.kind === "procStream" ? "double" : t.kind === "bool" ? "i1" @@ -927,6 +940,21 @@ export class LlDyn { const name = `sc_dm_${this.dynMatchers.size}`; this.dynMatchers.set(matcherKey, name); this.host.journalAllocated?.(name, "dyn.dynMatchHelper", [t, preserveRefs]); + const codec = textCodecRecordClass(t, (id) => this.host.recordsById.get(id)); + if (codec !== null) { + // A codec matches only its own handle box. + const B = new BlockBuilder(); + const matched = this.handleTagMatches(B, TEXT_CODEC_TAG_NUM[codec]); + B.terminate(`ret i1 ${matched}`); + this.defs.push( + `define internal zeroext i1 @${name}(ptr %d) ${FN_ATTRS} { ; matches ${key}`, + B.render(), + `}`, + ``, + ); + this.host.journalBuilt?.(); + return name; + } const recursive = this.isRecursiveShape(t); const wrapped = recursive || this.holdsRecursiveShape(t); const B = new BlockBuilder(); @@ -951,7 +979,10 @@ export class LlDyn { const lPlain = B.newLabel("dm.tr.plain"); B.condBr(capsule, lCapsule, lPlain); B.startBlock(lCapsule); - if (preserveRefs) { + // A class target matches only capsules of that class (tested above): + // a materialized view is a plain object, which never carries a class + // brand, so materializing one cannot change the answer. + if (preserveRefs || (t.kind === "object" && t.className !== "%Error")) { B.terminate(`ret i1 false`); } else { const materialized = B.tmp(); @@ -1535,6 +1566,19 @@ export class LlDyn { return descriptor; } + /** `record.sourceSpread(built, value)`: the built record keeps value's + * kept-source order over its own declared names. */ + emitRecordSourceSpread(B: BlockBuilder, built: string, value: string, shapeId: string): void { + const host = this.host; + const shape = host.recordsById.get(shapeId)!; + this.keepsRecordSources = true; + const names = this.recordNameTable(shapeId, shape); + host.declare(`declare void @scr_record_source_spread(ptr, ptr, ptr, ptr, ${host.sizeType})`); + B.line( + `call void @scr_record_source_spread(ptr ${built}, ptr ${value}, ptr ${names.names}, ptr ${names.lens}, ${host.sizeType} ${shape.fields.length})`, + ); + } + /** The declared-name tables scr_record_source_note scans: parallel * constant arrays of field-name bytes and byte lengths in declaration * order (the order the record serializes), interned per shape. Internal @@ -1643,6 +1687,24 @@ export class LlDyn { this.host.journalBuilt?.(); return name; } + const codec = textCodecRecordClass(t, (id) => host.recordsById.get(id)); + if (codec !== null) { + // The record the codec's handle box holds (+1), or the path TypeError. + host.declare(`declare ptr @scr_text_codec_from_dyn(ptr, i1 zeroext, ptr, ptr)`); + const checked = B.tmp(); + B.line( + `${checked} = call ptr @scr_text_codec_from_dyn(ptr %d, i1 ${codec === "TextDecoder"}, ptr %path, ptr ${want})`, + ); + B.terminate(`ret ptr ${checked}`); + this.defs.push( + `define internal ptr @${name}(ptr %d, ptr %path) ${FN_ATTRS} {`, + B.render(), + `}`, + ``, + ); + this.host.journalBuilt?.(); + return name; + } if (isRefCounted(t) && t.kind !== "dyn") { host.declare(`declare ptr @scr_dyn_typed_ref_unbox(ptr)`); if (t.kind === "object" && t.className === "%Error") { @@ -1895,10 +1957,10 @@ export class LlDyn { }; switch (t.kind) { case "date": { - host.declare(`declare double @scr_dyn_native_date_value(ptr)`); + host.declare(`declare ptr @scr_dyn_native_date_check(ptr, ptr)`); const r = B.tmp(); - B.line(`${r} = call double @scr_dyn_native_date_value(ptr %d)`); - B.terminate(`ret double ${r}`); + B.line(`${r} = call ptr @scr_dyn_native_date_check(ptr %d, ptr %path)`); + B.terminate(`ret ptr ${r}`); break; } case "url": @@ -2671,6 +2733,77 @@ export class LlDyn { return name; } + /** `i1`: %d is a handle box with tag `tag`. */ + private handleTagMatches(B: BlockBuilder, tag: number): string { + const kd = this.kindOf(B, "%d"); + const isHandle = B.tmp(); + B.line(`${isHandle} = icmp eq i32 ${kd}, ${DYN_KIND.HANDLE}`); + const lHandle = B.newLabel("dm.handle"); + const lDone = B.newLabel("dm.handle.done"); + const entry = B.currentLabel(); + B.condBr(isHandle, lHandle, lDone); + B.startBlock(lHandle); + const tagPtr = B.tmp(); + const loaded = B.tmp(); + B.line( + `${tagPtr} = getelementptr inbounds i8, ptr %d, i64 ${this.abiOffset(24, 20)} ; ->v.handle.tag`, + ); + B.line(`${loaded} = load i32, ptr ${tagPtr}`); + const same = B.tmp(); + B.line(`${same} = icmp eq i32 ${loaded}, ${tag}`); + B.br(lDone); + B.startBlock(lDone); + const matched = B.tmp(); + B.line(`${matched} = phi i1 [ false, %${entry} ], [ ${same}, %${lHandle} ]`); + return matched; + } + + /** A TextEncoder or TextDecoder record boxes as its handle (+1), which + * holds the record by reference. A decoder passes the byte offsets of the + * fields its boxed decode reads. */ + private textCodecToDyn( + B: BlockBuilder, + t: IrType & { kind: "record" }, + shape: IrRecordShape, + codec: "TextEncoder" | "TextDecoder", + ): void { + const host = this.host; + const rc = vAdapters(host, t); + const r = B.tmp(); + if (codec === "TextEncoder") { + host.declare(`declare ptr @scr_text_encoder_to_dyn(ptr, ptr, ptr)`); + B.line( + `${r} = call ptr @scr_text_encoder_to_dyn(ptr %v, ptr ${rc.retain}, ptr ${rc.release})`, + ); + } else { + const struct = mangleRecordStruct(t.shapeId); + const base = host.nullableUnions.fieldBase(t.shapeId); + const start = B.tmp(); + B.line(`${start} = ptrtoint ptr %v to ${this.S}`); + const offsets = TEXT_DECODER_FIELDS.map((field) => { + const index = shape.fields.findIndex((f) => f.name === field); + if (index < 0) + throw new InternalCompilerError(`llvm emitter bug: TextDecoder record lacks ${field}`); + const p = B.tmp(); + const at = B.tmp(); + const offset = B.tmp(); + B.line( + `${p} = getelementptr inbounds %${struct}, ptr %v, i64 0, i32 ${index + base} ; .${llvmCommentText(field)}`, + ); + B.line(`${at} = ptrtoint ptr ${p} to ${this.S}`); + B.line(`${offset} = sub ${this.S} ${at}, ${start}`); + return `${this.S} ${offset}`; + }); + host.declare( + `declare ptr @scr_text_decoder_to_dyn(ptr, ptr, ptr, ${this.S}, ${this.S}, ${this.S}, ${this.S})`, + ); + B.line( + `${r} = call ptr @scr_text_decoder_to_dyn(ptr %v, ptr ${rc.retain}, ptr ${rc.release}, ${offsets.join(", ")})`, + ); + } + B.terminate(`ret ptr ${r}`); + } + /* ── toDynHelper (walkers.ts, ported) ─────────────────────────── */ /** `sc_td_( v) -> ptr` — build a fresh dyn value from a @@ -2688,11 +2821,10 @@ export class LlDyn { let sourceAccessor: { name: string; release: string } | null = null; switch (t.kind) { case "date": { - // A Date handle holding the same time value; native Dates are - // immutable values, and so is the handle. - host.declare(`declare ptr @scr_dyn_native_date_from_ms(double)`); + // A Date handle holding the same Date object. + host.declare(`declare ptr @scr_dyn_native_date(ptr)`); const r = B.tmp(); - B.line(`${r} = call ptr @scr_dyn_native_date_from_ms(double %v)`); + B.line(`${r} = call ptr @scr_dyn_native_date(ptr %v)`); B.terminate(`ret ptr ${r}`); break; } @@ -2846,6 +2978,11 @@ export class LlDyn { const shape = host.recordsById.get(t.shapeId); if (!shape) throw new InternalCompilerError(`llvm emitter bug: to-dyn of unknown shape ${t.shapeId}`); + const codec = recordTextCodecClass(shape); + if (codec !== null) { + this.textCodecToDyn(B, t, shape, codec); + break; + } const struct = mangleRecordStruct(t.shapeId); const base = host.nullableUnions.fieldBase(t.shapeId); const fieldIndex = new Map(shape.fields.map((f, i) => [f.name, i + base])); diff --git a/packages/compiler/src/backend/llvm/emit-function.ts b/packages/compiler/src/backend/llvm/emit-function.ts index b6df7bcca..8f306035a 100644 --- a/packages/compiler/src/backend/llvm/emit-function.ts +++ b/packages/compiler/src/backend/llvm/emit-function.ts @@ -304,11 +304,7 @@ export function emitFunction(emitter: LlEmitter, fn: IrFunction): string { // ScrGen owns the frame), so its OUT slot address is fixed. let out: string | null = null; const yieldKind = fn.generator?.yieldT.kind; - if ( - !emitter.wasi && - fn.async !== true && - (yieldKind === "f64" || yieldKind === "date" || yieldKind === "bool") - ) { + if (!emitter.wasi && fn.async !== true && (yieldKind === "f64" || yieldKind === "bool")) { emitter.declare(`declare ptr @scr_gen_of_fiber(ptr)`); const gen = B.tmp(); B.line(`${gen} = call ptr @scr_gen_of_fiber(ptr ${self})`); @@ -704,7 +700,6 @@ export function emitGenScaffolding(emitter: LlEmitter): string[] { tr.push(` br label %done ; void body: the done value is undefined (NONE)`); break; case "f64": - case "date": emitter.declare(`declare void @scr_gen_out_f64(ptr, double)`); tr.push(` call void @scr_gen_out_f64(ptr %g, double %r)`, ` br label %done`); break; @@ -1053,7 +1048,6 @@ export function emitAsyncAwaitCall( emitter.declare(`declare void @scr_await_void(ptr)`); return ` call void @scr_await_void(ptr ${promise})`; case "f64": - case "date": emitter.declare(`declare double @scr_await_f64(ptr)`); return ` ${dest} = call double @scr_await_f64(ptr ${promise})`; case "bool": @@ -1108,7 +1102,7 @@ export function emitAsyncAwaitCall( const kind = ret.kind === "void" ? "void" - : ret.kind === "f64" || ret.kind === "date" + : ret.kind === "f64" ? "f64" : ret.kind === "bool" ? "bool" @@ -1187,7 +1181,6 @@ export function emitWasiFulfill(emitter: LlEmitter, v: LlValue | null): void { case "void": return; case "f64": - case "date": emitter.declare(`declare void @scr_gen_out_f64(ptr, double)`); emitter.B.line(`call void @scr_gen_out_f64(ptr ${gen}, double ${v!.name})`); return; @@ -1212,7 +1205,6 @@ export function emitWasiFulfill(emitter: LlEmitter, v: LlValue | null): void { emitter.B.line(`call void @scr_promise_fulfill_void(ptr ${pr})`); break; case "f64": - case "date": emitter.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`); emitter.B.line(`call void @scr_promise_fulfill_f64(ptr ${pr}, double ${v!.name})`); break; diff --git a/packages/compiler/src/backend/llvm/emit-module.ts b/packages/compiler/src/backend/llvm/emit-module.ts index 16a1b1601..5aa18bf9f 100644 --- a/packages/compiler/src/backend/llvm/emit-module.ts +++ b/packages/compiler/src/backend/llvm/emit-module.ts @@ -64,6 +64,7 @@ import { literalEqualityDefinition } from "./string-literal-equality.js"; import { STRING_READ_HELPERS, stringScanDecl } from "./string-reads.js"; import { emitLibraryIdentityLines } from "../library-identity-markers.js"; import { InternalCompilerError } from "../../errors.js"; +import { targetCpuAttribute } from "../target-cpu.js"; /** The function bodies emitted in `chunks` contiguous ranges, each by its * own emitter (LlEmitter.chunkEmitter), then merged in function order: the @@ -191,6 +192,20 @@ function readRange(dir: string, range: number): { result: ChunkResult; texts: st } } +/** The attribute groups of the emitted functions: #0 for every function, + * #1 for coroutine bodies and #2 for out-of-line record clones. Each carries + * the build's `--target-cpu` level. */ +function functionAttributes(emitter: LlEmitter, hasNoInlineRecordClone: boolean): string[] { + const cpu = targetCpuAttribute(emitter.targetCpu); + return [ + `attributes #0 = { sanitize_address${cpu} }`, + ...(emitter.usesCoroutines + ? [`attributes #1 = { sanitize_address presplitcoroutine${cpu} }`] + : []), + ...(hasNoInlineRecordClone ? [`attributes #2 = { noinline sanitize_address${cpu} }`] : []), + ]; +} + export function emitModuleParts(emitter: LlEmitter): string[] { // Function bodies first (the literal/unit/fn-value tables fill as they // emit), then the file assembles around them — the runtime ABI’s order. @@ -631,7 +646,7 @@ export function emitModuleParts(emitter: LlEmitter): string[] { // class-object shape { rc, pre, post, ctor, name } — field reads on // builtin errors and classval loads GEP through these. `%ScrError = type { ${emitter.sizeType}, ptr, ptr, ptr, ptr, ptr, i8, i8, i8, i8, i8, ptr, ptr }`, - `%ScrClassObj = type { ${emitter.sizeType}, ${emitter.sizeType}, ${emitter.sizeType}, ptr, ptr, ${emitter.sizeType}, ${emitter.sizeType}, ptr, ptr }`, + `%ScrClassObj = type { ${emitter.sizeType}, ${emitter.sizeType}, ${emitter.sizeType}, ptr, ptr, ${emitter.sizeType}, ${emitter.sizeType}, ptr, ptr, ptr, ${emitter.sizeType} }`, // The runtime emitter prefix { rc, vt, reg, cls } — user subclasses // embed it (classes.ts), and bare-emitter GEPs address through it. `%ScrEmitter = type { ${emitter.sizeType}, ptr, ptr, ptr }`, @@ -972,9 +987,7 @@ export function emitModuleParts(emitter: LlEmitter): string[] { // symbols specified by the library IR instead. for (const line of emitter.emitLibDefs(globals, globalReleaseLines, stamps)) out.push(line); out.push(...emitter.inlineRcTail(flushedDecls)); - out.push(`attributes #0 = { sanitize_address }`); - if (emitter.usesCoroutines) out.push(`attributes #1 = { sanitize_address presplitcoroutine }`); - if (hasNoInlineRecordClone) out.push(`attributes #2 = { noinline sanitize_address }`); + out.push(...functionAttributes(emitter, hasNoInlineRecordClone)); out.push(``); out.push(...emitter.fieldAliasMetadata()); if (emitter.debug !== null) out.push(emitter.debug.render()); @@ -1122,9 +1135,7 @@ export function emitModuleParts(emitter: LlEmitter): string[] { // sanitize_address is inert under the plain pipeline; the sanitized // lane's -fsanitize=address link activates instrumentation over the // emitted functions too (the runtime TUs get theirs from clang). - `attributes #0 = { sanitize_address }`, - ...(emitter.usesCoroutines ? [`attributes #1 = { sanitize_address presplitcoroutine }`] : []), - ...(hasNoInlineRecordClone ? [`attributes #2 = { noinline sanitize_address }`] : []), + ...functionAttributes(emitter, hasNoInlineRecordClone), ``, ); if (emitter.mod.workers) { diff --git a/packages/compiler/src/backend/llvm/emit-statements.ts b/packages/compiler/src/backend/llvm/emit-statements.ts index c2846a9d4..55e31219c 100644 --- a/packages/compiler/src/backend/llvm/emit-statements.ts +++ b/packages/compiler/src/backend/llvm/emit-statements.ts @@ -52,13 +52,19 @@ import { matchIntegerPairedForLoop, } from "../../ir/integer-loops.js"; import { emitByteWindowGuard, matchByteWindow } from "./byte-windows.js"; -import { countedDenseShape, emitDenseLoopGuard, matchDenseArrayLoop } from "./dense-array-loops.js"; +import { + countedDenseShape, + emitDenseLoopGuard, + matchDenseArrayLoop, + pairedDenseShape, +} from "./dense-array-loops.js"; import { emitCountedLoopLimit, emitCountedLoopSafety, emitPairedLoopSafety, } from "./counted-loops.js"; import { emitBytesLoopVersionGuard, type BytesLoopVersion } from "./bytes-loop-versions.js"; +import { bytesHeaderAlias } from "./bytes-alias.js"; import { mangleLocal } from "../mangle.js"; import { LlvmUnsupportedError } from "./unsupported.js"; import type { LlValue } from "./expr-context.js"; @@ -966,7 +972,9 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void { } : countedLoop && !countedLoop.guarded && countedLoop.followers.length === 0 ? countedDenseShape(s, countedLoop) - : null; + : pairedLoop + ? pairedDenseShape(pairedLoop) + : null; const dense = denseShape && !window ? matchDenseArrayLoop( @@ -1043,15 +1051,17 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void { LOOPS.of(emitter).unaliasedArrays, ) : countedLoop; - const version = versionable ? emitter.matchBytesLoopVersion(s, versionCounted) : null; + const version = versionable + ? emitter.matchBytesLoopVersion(s, versionCounted, versionCounted ? null : pairedLoop) + : null; if (version) { emitVersionedLoop(emitter, s, version); break; } let countedJoin: string | null = null; if ( - (countedLoop?.guarded && !LOOPS.of(emitter).provenCountedGuards.has(s)) || - pairedLoop?.guarded + (countedLoop?.guarded || pairedLoop?.guarded) && + !LOOPS.of(emitter).provenCountedGuards.has(s) ) { const safe = countedLoop ? emitCountedLoopSafety(emitter, countedLoop) @@ -1144,11 +1154,15 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void { B.line(`${start} = fptosi double ${value.name} to i64`); B.line(`store i64 ${start}, ptr ${slot}`); pairedSlots.push(slot); + const dense = LOOPS.of(emitter).denseLoop; emitter.integerLoopBindings.set(counter.localId, { slot, type: "i64", signed: true, - range: pairedLoop.range, + range: + dense?.pair && dense.counter === pairedLoop.low.localId + ? dense.range + : pairedLoop.range, }); } } else if (s.init) { @@ -1218,10 +1232,7 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void { } B.line(`${inBounds} = icmp ult i32 ${narrow}, ${len}`); } else { - emitter.markMemoryPointer(lenPtr, "bytes:header"); - B.line( - `${len} = load ${emitter.sizeType}, ptr ${lenPtr}${emitter.fieldAliasAttachment(lenPtr)}`, - ); + B.line(`${len} = load ${emitter.sizeType}, ptr ${lenPtr}${bytesHeaderAlias(emitter)}`); B.line(`${inBounds} = icmp ult ${emitter.sizeType} ${index}, ${len}`); } B.condBr(inBounds, lb, exit); @@ -2085,7 +2096,8 @@ function emitVersionedLoop( if (version.owned.size > 0) emitter.bytesOwned = new Set([...originalOwned, ...version.owned]); if (version.ranges.size > 0) emitter.integerRanges = new Map([...originalRanges, ...version.ranges]); - if (version.counted?.guarded) LOOPS.of(emitter).provenCountedGuards.add(s); + if (version.counted?.guarded || version.paired?.loop.guarded) + LOOPS.of(emitter).provenCountedGuards.add(s); emitter.emitStmt(s); B.br(done); } finally { diff --git a/packages/compiler/src/backend/llvm/emitter.test.ts b/packages/compiler/src/backend/llvm/emitter.test.ts index 4793663aa..876476cc1 100644 --- a/packages/compiler/src/backend/llvm/emitter.test.ts +++ b/packages/compiler/src/backend/llvm/emitter.test.ts @@ -1,7 +1,6 @@ import { expect, test } from "vitest"; import { BOOL, - DATE_T, F64, PROCSTREAM_T, STRING, @@ -202,6 +201,16 @@ test("detached exit defines the runtime opt-in symbol only when requested", () = expect(detached.replace("@scr_detached_exit = constant i8 1\n", "")).toBe(plain); }); +test("a target CPU level reaches every function attribute group and nothing else", () => { + const module = moduleFor([call()]); + const plain = emitLlvmModule(module); + expect(plain).not.toContain("target-cpu"); + const v3 = emitLlvmModule(module, { targetCpu: "x86-64-v3" }); + expect(v3).toMatch(/^attributes #0 = \{ sanitize_address "target-cpu"="x86-64-v3" \}$/m); + expect(v3.replaceAll(' "target-cpu"="x86-64-v3"', "")).toBe(plain); + expect(emitLlvmModule(module, { targetCpu: "baseline" })).toBe(plain); +}); + test("inline RC inlines every fast path at speed and keeps box releases as calls in release", () => { for (const boxed of [false, true]) { const module = moduleFor(Array.from({ length: 4 }, call), { boxed }); @@ -287,7 +296,7 @@ test("shared exceptional exits preserve the function's scalar return ABI", () => }); test("exceptional exits use each scalar kind's LLVM return type", () => { - for (const type of [F64, DATE_T, BOOL, PROCSTREAM_T]) { + for (const type of [F64, BOOL, PROCSTREAM_T]) { for (const ownedLocals of [false, true]) { const module = moduleFor([call(), call(), { kind: "throw", value: literal("failed"), loc }]); const work = module.functions[1]!; @@ -499,6 +508,27 @@ test("class brand probes compare the program's key constant before the key text" const named = work(probe("dyn.typedRefIs"), dyn); expect(named).toContain("; ->v.typed_ref.type_key"); expect(named).toContain("@scr_dyn_typed_ref_is_key"); + // Another key pointer is rejected on its length and last eight bytes + // ("Example" plus the prefix's ':') before the out-of-line comparison. + expect(named).toContain("; ->v.typed_ref.type_key_len"); + expect(named).toContain("and i32 %t"); + expect(named).toMatch(/icmp eq i32 %t\d+, 14\b/); + const tail = Buffer.from("object:Example").readBigInt64LE(6); + expect(named).toContain(`icmp eq i64 %t`); + expect(named).toContain(`, ${tail}`); + const shortKey: IrExpr = { kind: "strLit", value: "o:E", type: STRING, loc: receiverLoc }; + const short = work( + { + kind: "libCall", + fn: "dyn.typedRefIs", + args: [ref(dyn), shortKey], + type: BOOL, + loc: receiverLoc, + }, + dyn, + ); + expect(short).toMatch(/icmp eq i32 %t\d+, 3\b/); + expect(short).not.toContain("load i64"); // The key-pointer test answers false for capsules boxed elsewhere. const exact = work(probe("dyn.typedRefKeyIs"), dyn); expect(exact).toContain("; ->v.typed_ref.type_key"); diff --git a/packages/compiler/src/backend/llvm/emitter.ts b/packages/compiler/src/backend/llvm/emitter.ts index 050c3fb90..6760f3b74 100644 --- a/packages/compiler/src/backend/llvm/emitter.ts +++ b/packages/compiler/src/backend/llvm/emitter.ts @@ -1,3 +1,4 @@ +import type { TargetCpu } from "../target-cpu.js"; import { relieveHeapPressure } from "../../heap-pressure.js"; import { wasmBoundsChecks } from "./wasm-modules.js"; import { jsTruthy } from "./jsval.js"; @@ -60,7 +61,7 @@ import { captureFreeFunctions, observesStacks } from "../stack-observations.js"; import { findInitializedByteLoopBindings } from "./byte-windows.js"; import { matchBytesLoopVersion } from "./bytes-loop-versions.js"; import { globalSlotAlias } from "./bytes-alias.js"; -import type { IntegerCountedForLoop } from "../../ir/integer-loops.js"; +import type { IntegerCountedForLoop, IntegerPairedForLoop } from "../../ir/integer-loops.js"; import { materializeSplitPiece, type SplitSpan, @@ -68,7 +69,7 @@ import { type StringSplit, } from "./split-loops.js"; import { scalarizeNumericRecords } from "../../ir/scalar-records.js"; -import { addRecordResultEntries } from "./record-results.js"; +import { addRecordResultEntries, resultEntryName } from "./record-results.js"; import { specializeCallbackCalls } from "../../ir/callback-specialization.js"; import { type ChunkResult, @@ -82,6 +83,7 @@ import { versionLoopBoundParameters } from "../../ir/parameter-versions.js"; import { allocatedLiteral, fillAllocationLength, + int32ArrayKey, selectInt32Arrays, } from "../../ir/int32-arrays.js"; import { everyExpr, everyExprChild, everyStmtChild, everyStmtList } from "../../ir/traverse.js"; @@ -185,7 +187,7 @@ import { } from "./string-slices.js"; import { LazyCaptures } from "./lazy-captures.js"; import { StackCaptures } from "./stack-captures.js"; -import { emitFieldAbsentTest, f64Lit, SCR_ARR_FIELD } from "./common.js"; +import { emitFieldAbsentTest, f64Lit, SCR_ARR_FIELD, SCR_ARR_FROZEN } from "./common.js"; import { ffiExtendsNarrowIntegers } from "../targets.js"; import { emitLiteralExpr, @@ -481,6 +483,10 @@ export interface LlvmTargetOptions { * store the instance's address over 8 in 32 bits (classes.ts * compactRefLoadLines). Elsewhere they keep 8-byte pointers. */ lowRegion?: boolean; + /** The x86-64 level the program's code may require (`--target-cpu`): + * every emitted function carries it as its `target-cpu`. Unset, code + * runs on any x86-64 CPU. */ + targetCpu?: TargetCpu; } /** LlvmTargetOptions.lowRegion for an executable: Linux targets without @@ -510,6 +516,15 @@ function provenInt32Arrays(mod: IrModule): ReadonlySet { return new Set([...slots.int32Arrays, ...slots.checkedInt32Arrays]); } +/** The functions whose calls the backend may emit as calls of their + * result-slot entries (record-results.ts). */ +function resultEntryTargets(mod: IrModule): ReadonlySet { + const names = new Set(mod.functions.map((fn) => fn.name)); + const targets = new Set(); + for (const name of names) if (names.has(resultEntryName(name))) targets.add(name); + return targets; +} + /** The emitter for `input` after the module passes every emission runs. */ function moduleEmitter(input: IrModule, options: LlvmTargetOptions): LlEmitter { // Facts are keyed by node identity: emit the same module whether it was @@ -545,6 +560,7 @@ function moduleEmitter(input: IrModule, options: LlvmTargetOptions): LlEmitter { ), ), provenInt32Arrays, + resultEntryTargets, ) : mod, options, @@ -781,7 +797,7 @@ export class LlEmitter { everyStmtList(fn.body, { stmt: (s) => { if (s.kind !== "varDecl" && s.kind !== "assign") return true; - const key = `${fn.name}\0${s.localId}`; + const key = int32ArrayKey(fn.name, s.localId); const checked = checkedInt32Arrays.has(key); if (!checked && !int32Arrays.has(key)) return true; const value = s.kind === "varDecl" ? s.init : s.value; @@ -799,15 +815,16 @@ export class LlEmitter { }); } - /** Whether `arr` is one of the current function's int32 arrays - * (ir/int32-arrays.ts, kept by the int32 slot analysis). */ + /** Whether `arr` reads one of the int32 arrays (ir/int32-arrays.ts, kept + * by the int32 slot analysis): a local or parameter of the current + * function, or a module global. */ int32Array(arr: IrExpr): boolean { if (arr.kind !== "varRef") return false; if (this.int32KindArray(arr)) return true; if (this.int32Slots.int32Arrays.size === 0) return false; return ( this.int32ArrayTemps.has(arr.localId) || - this.int32Slots.int32Arrays.has(`${this.currentFunctionName}\0${arr.localId}`) + this.int32Slots.int32Arrays.has(int32ArrayKey(this.currentFunctionName, arr.localId)) ); } @@ -974,8 +991,8 @@ export class LlEmitter { } /** Whether an element access at `index` of `arr` lies in the hole-free - * prefix: the counter of a dense array loop's fast copy indexing one of - * the arrays its entry test covered. */ + * prefix: a counter of a dense array loop's fast copy indexing one of the + * arrays its entry test covered. */ provenDenseIndex(arr: IrExpr, index: IrExpr): boolean { const loop = LOOPS.of(this).denseLoop; return ( @@ -983,8 +1000,8 @@ export class LlEmitter { arr.kind === "varRef" && loop.arrays.has(arr.localId) && index.kind === "varRef" && - index.localId === loop.counter && - this.integerLoopBindings.has(loop.counter) + (index.localId === loop.counter || index.localId === loop.pair?.high) && + this.integerLoopBindings.has(index.localId) ); } @@ -1036,6 +1053,7 @@ export class LlEmitter { readonly emitLibraryIdentity: boolean; readonly runtimeAbiMarker: boolean; readonly detachedExit: boolean; + readonly targetCpu: TargetCpu | undefined; readonly wasmBoundsChecks: boolean; /** Interned string literals: UTF-8 text → { symbol, byte length } — * first-use order, the runtime ABI’s determinism discipline. */ @@ -1313,7 +1331,7 @@ export class LlEmitter { /** Class objects (classes as first-class values): className → the * interned .name literal ref — registered during body emission, the * statics and construct thunks assemble around the bodies. */ - readonly classObjs = new Map(); + readonly classObjs = new Map(); private readonly unionFieldReadGroups = new Map(); /** Preorder intervals of the runtime error classes under THIS module's * class-forest numbering (main() stamps scr_error_vts with them, exactly @@ -1618,6 +1636,7 @@ export class LlEmitter { this.emitLibraryIdentity = options.emitLibraryIdentity !== false; this.runtimeAbiMarker = options.runtimeAbiMarker === true; this.detachedExit = options.detachedExit === true; + this.targetCpu = options.targetCpu; this.wasmBoundsChecks = options.wasmBoundsChecks === true || wasmBoundsChecks({ @@ -1762,6 +1781,7 @@ export class LlEmitter { const shape = this.recordsById.get(shapeId); return shape !== undefined && !recordDynKeepsIdentity(shape); }, + (wrap) => this.borrowableNullableWrap(wrap) === "ref", ); this.lentStringParameters = this.debug === null ? findLentStringParameters(this.fnByName, this.callLifetimes) : new Map(); @@ -2032,7 +2052,6 @@ export class LlEmitter { if (POINTER_KINDS.has(t.kind)) return "ptr"; switch (t.kind) { case "f64": - case "date": return "double"; case "bool": return "i1"; @@ -2165,7 +2184,6 @@ export class LlEmitter { this.declare(`declare void @scr_promise_fulfill_void(ptr)`); return [` call void @scr_promise_fulfill_void(ptr ${promise})`]; case "f64": - case "date": this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`); return [` call void @scr_promise_fulfill_f64(ptr ${promise}, double %r)`]; case "bool": @@ -2395,7 +2413,7 @@ export class LlEmitter { return { name: "", type }; } const t = B.tmp(); - if (type.kind === "f64" || type.kind === "date") { + if (type.kind === "f64") { this.declare(`declare double @scr_coro_direct_f64(ptr)`); B.line(`${t} = call double @scr_coro_direct_f64(ptr ${record})`); } else if (type.kind === "bool") { @@ -3791,8 +3809,17 @@ export class LlEmitter { /** A union value's tag (i32): the box's tag word, or the NULL test of a * nullable union's pointer. */ + /** A box's tag and payload words are written only where the box is + * built (runtime constructors and untagged stack stores), so their loads + * carry their own alias tags: element and field stores cannot change + * them, and loops keep them hoisted. */ unionTag(uName: string, unionId: string): string { - return emitUnionTag(this.B, this.nullableUnions.get(unionId), uName); + return emitUnionTag( + this.B, + this.nullableUnions.get(unionId), + uName, + this.memoryAliasAttachment("union.tag"), + ); } /** Guard for helpers that build or read a union's `%ScrUnion` box @@ -3808,7 +3835,12 @@ export class LlEmitter { /** The BORROWED payload pointer of a ref arm (scr_union_peek inlined — * the runtime's is a static inline); a nullable union is its payload. */ unionPeek(uName: string, unionId: string): string { - return emitUnionPeek(this.B, this.nullableUnions.get(unionId), uName); + return emitUnionPeek( + this.B, + this.nullableUnions.get(unionId), + uName, + this.memoryAliasAttachment("union.payload"), + ); } /** Emits `switch` over a union's tag with one block per arm; each arm @@ -4037,7 +4069,12 @@ export class LlEmitter { classObjSym(className: string): string { if (!this.classObjs.has(className)) { const meta = this.classMetaOf(className); - this.classObjs.set(className, { nameSym: this.internLiteral(classJsName(meta.def)) }); + // util.inspect prints `[class C extends B]` with the base's name. + const baseName = meta.base ? classJsName(meta.base.def) : ""; + this.classObjs.set(className, { + nameSym: this.internLiteral(classJsName(meta.def)), + baseNameSym: baseName ? this.internLiteral(baseName) : null, + }); } this.journal?.entry("classObj", className, this.classObjs.get(className)); return mangleClassObj(className); @@ -4251,6 +4288,7 @@ export class LlEmitter { matchBytesLoopVersion( s: IrStmt & { kind: "for" | "while" }, counted: IntegerCountedForLoop | null, + paired: IntegerPairedForLoop | null = null, ): ReturnType { const fn = LOOPS.of(this).bytesLoopFunction; if (!fn) return null; @@ -4260,17 +4298,22 @@ export class LlEmitter { const invariantGlobals = new Set(this.stableGlobals); for (const global of this.initializerBindings.get(fn.name) ?? []) invariantGlobals.add(global.id); - return matchBytesLoopVersion(s, counted, { - locals: this.currentLocals, - invariantGlobals, - captured: this.captureIds, - ranges: this.integerRanges, - initialized, - proven: this.bytesBounds, - workers: this.mod.workers === true, - owned: this.bytesOwned, - scalarStrings: new Set(this.stringSlices.keys()), - }); + return matchBytesLoopVersion( + s, + counted, + { + locals: this.currentLocals, + invariantGlobals, + captured: this.captureIds, + ranges: this.integerRanges, + initialized, + proven: this.bytesBounds, + workers: this.mod.workers === true, + owned: this.bytesOwned, + scalarStrings: new Set(this.stringSlices.keys()), + }, + paired, + ); } /** The alias tag of a whole-slot load of a binding (module globals only). */ @@ -4897,7 +4940,7 @@ export class LlEmitter { return; } case "classObj": - addEntry(this.classObjs, key, value as { nameSym: string }); + addEntry(this.classObjs, key, value as { nameSym: string; baseNameSym: string | null }); return; case "retainWrapper": addEntry(this.nullableUnions.retainWrappers, key, value as string); @@ -6279,6 +6322,41 @@ export class LlEmitter { return ok; } + /** Branches on "the array `arr` is frozen" (SCR_ARR_FROZEN in its storage + * flags, scr_array.h), or published too when `published`; like + * emitPublishedTest, leaves the builder in the cold block and answers the + * continuation label. */ + private emitFrozenArrayTest(arr: string, published: boolean): string { + const B = this.B; + const sparep = B.tmp(), + spare = B.tmp(), + bit = B.tmp(); + let frozen = B.tmp(); + B.line( + `${sparep} = getelementptr inbounds %ScrArr, ptr ${arr}, i32 0, i32 ${SCR_ARR_FIELD.spare}`, + ); + B.line(`${spare} = load i16, ptr ${sparep}`); + B.line(`${bit} = and i16 ${spare}, ${SCR_ARR_FROZEN}`); + B.line(`${frozen} = icmp ne i16 ${bit}, 0`); + if (published) { + const rc = B.tmp(), + immortal = B.tmp(), + either = B.tmp(); + B.line(`${rc} = load ${this.sizeType}, ptr ${arr}`); + B.line(`${immortal} = icmp eq ${this.sizeType} ${rc}, -1`); + B.line(`${either} = or i1 ${frozen}, ${immortal}`); + frozen = either; + } + this.declare(`declare i1 @llvm.expect.i1(i1, i1)`); + const expected = B.tmp(); + B.line(`${expected} = call i1 @llvm.expect.i1(i1 ${frozen}, i1 false)`); + const cold = B.newLabel("arr.frozen"), + ok = B.newLabel("arr.ok"); + B.condBr(expected, cold, ok); + B.startBlock(cold); + return ok; + } + private publishedNamesSym: string | null = null; /** The class-name table the hierarchy field guards index by preorder. */ @@ -6343,15 +6421,29 @@ export class LlEmitter { op: number, index = f64Lit(0), count = f64Lit(0), + third: string | null = null, ): void { - if (this.publishedTypes?.containerGuarded(arrType) !== true) return; + const published = this.publishedTypes; + if (published?.containerGuarded(arrType) !== true) return; const B = this.B; - const ok = this.emitPublishedTest(arr); - this.declare(`declare zeroext i1 @scr_throw_published_array(i32, ptr, double, double)`); + const guards = published.arrayGuards(arrType); + const ok = guards.frozen + ? this.emitFrozenArrayTest(arr, guards.published) + : this.emitPublishedTest(arr); const threw = B.tmp(); - B.line( - `${threw} = call zeroext i1 @scr_throw_published_array(i32 ${op}, ptr ${arr}, double ${index}, double ${count})`, - ); + if (third === null) { + this.declare(`declare zeroext i1 @scr_throw_published_array(i32, ptr, double, double)`); + B.line( + `${threw} = call zeroext i1 @scr_throw_published_array(i32 ${op}, ptr ${arr}, double ${index}, double ${count})`, + ); + } else { + this.declare( + `declare zeroext i1 @scr_throw_published_array_range(i32, ptr, double, double, double)`, + ); + B.line( + `${threw} = call zeroext i1 @scr_throw_published_array_range(i32 ${op}, ptr ${arr}, double ${index}, double ${count}, double ${third})`, + ); + } const unwind = B.newLabel("pub.throw"); B.condBr(threw, unwind, ok); B.startBlock(unwind); diff --git a/packages/compiler/src/backend/llvm/expr-async.ts b/packages/compiler/src/backend/llvm/expr-async.ts index b5d765b16..ee9ff2983 100644 --- a/packages/compiler/src/backend/llvm/expr-async.ts +++ b/packages/compiler/src/backend/llvm/expr-async.ts @@ -184,7 +184,7 @@ export function emitIntrinsicExpr(host: LlvmEmitterContext, e: ExprOf<"intrinsic } const v = host.emitExpr(e.args[0]!); const t = e.args[0]!.type; - if (t.kind === "f64" || t.kind === "date") { + if (t.kind === "f64") { host.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`); B.line(`call void @scr_promise_fulfill_f64(ptr ${p}, double ${v.name})`); } else if (t.kind === "bool") { @@ -500,7 +500,7 @@ export function emitAsyncExpr( host.declare(`declare void @scr_async_gen_hop_done()`); B.line(`call void @scr_async_gen_hop_done()`); } - if (yt.kind === "f64" || yt.kind === "date") { + if (yt.kind === "f64") { if (host.currentCoro !== null) { const coro = host.currentCoro; if (coro.out !== null) host.emitGenSlotStore(coro.out, "f64", v.name); @@ -582,7 +582,7 @@ export function emitAsyncExpr( return { name: "", type: e.type }; } const t = B.tmp(); - if (e.type.kind === "f64" || e.type.kind === "date") { + if (e.type.kind === "f64") { host.declare(`declare double @scr_gen_take_in_f64()`); B.line(`${t} = call double @scr_gen_take_in_f64()`); return { name: t, type: e.type }; @@ -710,12 +710,12 @@ export function emitAsyncExpr( throw new InternalCompilerError(`llvm emitter bug: genResume ${e.mode} on async`); const gb = emitBorrowedInput(host, e.gen); if (e.mode === "value") { - if (!host.wasi && (e.type.kind === "f64" || e.type.kind === "date")) + if (!host.wasi && e.type.kind === "f64") return { name: host.emitGenSlotTake(host.genOutSlot(gb.name), "f64"), type: e.type }; if (!host.wasi && e.type.kind === "bool") return { name: host.emitGenSlotTake(host.genOutSlot(gb.name), "bool"), type: e.type }; const t = B.tmp(); - if (e.type.kind === "f64" || e.type.kind === "date") { + if (e.type.kind === "f64") { host.declare(`declare double @scr_gen_take_out_f64(ptr)`); B.line(`${t} = call double @scr_gen_take_out_f64(ptr ${gb.name})`); return { name: t, type: e.type }; @@ -752,7 +752,7 @@ export function emitAsyncExpr( store(a.name, e.arg!.type); }; const parkIn = (name: string, t: IrType): void => { - if (t.kind === "f64" || t.kind === "date") { + if (t.kind === "f64") { host.declare(`declare void @scr_gen_in_f64(ptr, double)`); B.line(`call void @scr_gen_in_f64(ptr ${g.name}, double ${name})`); } else if (t.kind === "bool") { @@ -787,7 +787,7 @@ export function emitAsyncExpr( const a = host.emitExpr(e.arg); const t = e.arg.type; if (isRefCounted(t)) host.toHeap(a); - if (t.kind === "f64" || t.kind === "date") { + if (t.kind === "f64") { host.declare(`declare ptr @scr_async_gen_next_f64(ptr, double)`); call = `call ptr @scr_async_gen_next_f64(ptr ${g.name}, double ${a.name})`; } else if (t.kind === "bool") { @@ -806,7 +806,7 @@ export function emitAsyncExpr( const a = host.emitExpr(e.arg); const t = e.arg.type; if (isRefCounted(t)) host.toHeap(a); - if (t.kind === "f64" || t.kind === "date") { + if (t.kind === "f64") { host.declare(`declare ptr @scr_async_gen_return_f64(ptr, double)`); call = `call ptr @scr_async_gen_return_f64(ptr ${g.name}, double ${a.name})`; } else if (t.kind === "bool") { @@ -849,7 +849,7 @@ export function emitAsyncExpr( B.line(`call void @scr_gen_ret_none(ptr ${g.name})`); } else { sendArg((name, t) => { - if (t.kind === "f64" || t.kind === "date") { + if (t.kind === "f64") { host.declare(`declare void @scr_gen_ret_f64(ptr, double)`); B.line(`call void @scr_gen_ret_f64(ptr ${g.name}, double ${name})`); } else if (t.kind === "bool") { @@ -960,7 +960,7 @@ export function emitAsyncExpr( return { name: "", type: e.type }; } const t = B.tmp(); - if (e.type.kind === "f64" || e.type.kind === "date") { + if (e.type.kind === "f64") { const f64Await = host.awaitResultFn("f64"); host.declare(`declare double @${f64Await}(ptr)`); B.line(`${t} = call double @${f64Await}(ptr ${pr.name})`); @@ -1050,7 +1050,7 @@ export function emitAsyncExpr( const peek = host.unionPeek(u.name, def.id); if (host.coroAwait) host.emitCoroAwait(peek); let awaited: LlValue; - if (inner.kind === "f64" || inner.kind === "date") { + if (inner.kind === "f64") { const f64Await = host.awaitResultFn("f64"); host.declare(`declare double @${f64Await}(ptr)`); const x = B.tmp(); @@ -1136,7 +1136,6 @@ export function emitAsyncExpr( let resolve: string; const kindNums: Partial> = { f64: 0, - date: 0, bool: 1, string: 2, void: 3, @@ -1191,7 +1190,6 @@ export function emitAsyncExpr( B.line(`${p} = call ptr @scr_promise_new()`); const kindNums: Partial> = { f64: 0, - date: 0, bool: 1, string: 2, void: 3, diff --git a/packages/compiler/src/backend/llvm/expr-bytes.ts b/packages/compiler/src/backend/llvm/expr-bytes.ts index 41ddf2b9d..23025d00c 100644 --- a/packages/compiler/src/backend/llvm/expr-bytes.ts +++ b/packages/compiler/src/backend/llvm/expr-bytes.ts @@ -4,7 +4,7 @@ import { emitBorrowedInput } from "./borrowed-inputs.js"; import { BYTES_ELEMENT_SIZE, F64, type IrBytesElem, type IrExpr } from "../../ir/ir.js"; import type { LlvmEmitterContext, LlValue } from "./expr-context.js"; import { F64_INF, exactIndexLines, f64Lit, sizeToDouble } from "./common.js"; -import { exactInteger, widenInteger } from "./integer-values.js"; +import { alwaysExactInteger, exactInteger } from "./integer-values.js"; import { byteNumberAccess } from "../../ir/byte-numbers.js"; import { emitByteNumber } from "./byte-numbers.js"; import { integerOrNaNIndex, integerOrNaNRange } from "./wrapping-sums.js"; @@ -86,17 +86,18 @@ export function emitBytesIndex( ): string { const B = host.B; const index = value.name; - const integer = exactInteger(host, value, expr); - if (inBounds && integer) { - const wide = widenInteger(host, integer); - if (host.sizeType === "i64") return wide; + // The exact integer, or a wide view that always equals the double (such + // as `i + 1` of a speculated counter). + const exact = alwaysExactInteger(host, value, exactInteger(host, value, expr)); + if (inBounds && exact !== null) { + if (host.sizeType === "i64") return exact; const narrow = B.tmp(); - B.line(`${narrow} = trunc i64 ${wide} to ${host.sizeType}`); + B.line(`${narrow} = trunc i64 ${exact} to ${host.sizeType}`); return narrow; } const len = emitBytesHeaderLength(host, receiver); - if (integer) { - const wideIndex = widenInteger(host, integer); + if (exact !== null) { + const wideIndex = exact; const wideLength = host.sizeType === "i64" ? len : B.tmp(); if (host.sizeType !== "i64") B.line(`${wideLength} = zext ${host.sizeType} ${len} to i64`); const inRange = B.tmp(); @@ -936,6 +937,23 @@ export function emitBytesIntrinsic( B.line(`${t} = fcmp one double ${idx.name}, ${f64Lit(-1)}`); return { name: t, type: e.type }; } + case "indexOfElem": + case "lastIndexOfElem": + return call( + "scr_bytes_index_of_elem", + "double (ptr, double, double, i1 zeroext)", + `ptr ${r.name}, double ${args[0]!.name}, double ${args[1]!.name}, i1 ${method === "lastIndexOfElem"}`, + false, + false, + ); + case "includesElem": + return call( + "scr_bytes_includes_elem", + "zeroext i1 (ptr, double, double)", + `ptr ${r.name}, double ${args[0]!.name}, double ${args[1]!.name}`, + false, + false, + ); case "fillElem": // Per-element TypedArray fill (non-u8): slice-style index // defaults, never throws; the receiver comes back +1. diff --git a/packages/compiler/src/backend/llvm/expr-calls.ts b/packages/compiler/src/backend/llvm/expr-calls.ts index a4e7cde2c..6ff52619e 100644 --- a/packages/compiler/src/backend/llvm/expr-calls.ts +++ b/packages/compiler/src/backend/llvm/expr-calls.ts @@ -44,6 +44,7 @@ import { LlvmUnsupportedError } from "./unsupported.js"; import type { LlvmEmitterContext, ExprOf, LlValue } from "./expr-context.js"; import { f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl } from "./common.js"; import { canStackUnion, emitStackUnion } from "./stack-unions.js"; +import { emitStackRecordKeyGet, scalarRecordKeyGet } from "./expr-records.js"; import { emitCallArrayRead } from "./local-array-reads.js"; import { emitScalarOptional, isScalarOptionalSource } from "./local-union-storage.js"; import { emitStackMapRead } from "./map-read-lifetimes.js"; @@ -166,6 +167,9 @@ export function emitCallExpr( return { name: host.emitOwnedNullableStack(a).box, type: a.type }; // An inline union field read copies its tag and bits. if (host.inlineFieldGet(a)) return host.emitReadReceiver(a); + // A scalar keyed record read writes its tag and bits. + if (a.kind === "recordKeyGet" && scalarRecordKeyGet(host, a)) + return { name: emitStackRecordKeyGet(host, a), type: a.type }; } if (inputs[index]) return host.emitReadReceiver(a); } diff --git a/packages/compiler/src/backend/llvm/expr-containers.ts b/packages/compiler/src/backend/llvm/expr-containers.ts index 1ba5da818..279b8c044 100644 --- a/packages/compiler/src/backend/llvm/expr-containers.ts +++ b/packages/compiler/src/backend/llvm/expr-containers.ts @@ -257,6 +257,16 @@ function emitDiscarded(host: LlvmEmitterContext, e: IrExpr): void { const start = host.emitExpr(e.args[0]!); const count = e.args[1] ? host.emitExpr(e.args[1]).name : F64_INF; const values = items.map((item) => host.emitExpr(item)); + // Before the values move into the buffer: a throw releases them with the + // frame. + host.emitPublishedArrayGuard( + r.name, + e.receiver.type, + 10, + start.name, + count, + f64Lit(values.length), + ); if (acc === "ref") values.forEach((value) => host.toHeap(value)); let buffer = "null"; if (values.length > 0) { @@ -904,6 +914,12 @@ export function emitArrIntrinsic( B.line(`${t} = call ptr @${helper}(ptr ${r.name}, ptr ${sep.name})`); return host.own({ name: t, type: e.type }); } + if (elem.kind === "date") { + host.declare(`declare ptr @scr_date_arr_join(ptr, ptr)`); + const t = B.tmp(); + B.line(`${t} = call ptr @scr_date_arr_join(ptr ${r.name}, ptr ${sep.name})`); + return host.own({ name: t, type: e.type }); + } host.declare(`declare ptr @scr_arr_join(ptr, ptr)`); const t = B.tmp(); B.line(`${t} = call ptr @scr_arr_join(ptr ${r.name}, ptr ${sep.name})`); @@ -928,6 +944,16 @@ export function emitArrIntrinsic( case "sortPrimitive": case "toSortedPrimitive": { if (e.method === "sortPrimitive") host.emitPublishedArrayGuard(r.name, e.receiver.type, 7); + if (elem.kind === "date") { + // Dates order by their toString() text. + host.declare(`declare ptr @scr_arr_sort_by_text(ptr, i1 zeroext, ptr)`); + host.declare(`declare ptr @scr_date_object_to_string(ptr)`); + const sorted = B.tmp(); + B.line( + `${sorted} = call ptr @scr_arr_sort_by_text(ptr ${r.name}, i1 ${e.method === "toSortedPrimitive" ? 1 : 0}, ptr @scr_date_object_to_string)`, + ); + return host.own({ name: sorted, type: e.type }); + } host.declare(`declare ptr @scr_arr_sort_primitive(ptr, i1 zeroext)`); const result = B.tmp(); B.line( @@ -978,7 +1004,25 @@ export function emitArrIntrinsic( case "fill": case "fillUndefined": { const args = e.args.map((arg) => host.emitExpr(arg)); - host.emitPublishedArrayGuard(r.name, e.receiver.type, 7); + // fill's range is its last two arguments; copyWithin's target, + // start and end are its three. + if (e.method === "copyWithin") + host.emitPublishedArrayGuard( + r.name, + e.receiver.type, + 9, + args[0]!.name, + args[1]!.name, + args[2]!.name, + ); + else + host.emitPublishedArrayGuard( + r.name, + e.receiver.type, + 8, + args[args.length - 2]!.name, + args[args.length - 1]!.name, + ); if (host.checkedFills.has(e)) { // A new int32 array with checked stores, filled with a value that // may not be an int32: widen it first then. @@ -1042,7 +1086,7 @@ export function emitArrIntrinsic( // removes to the end (+Infinity, the slice convention). const start = host.emitExpr(e.args[0]!); const cnt = e.args[1] ? host.emitExpr(e.args[1]).name : F64_INF; - host.emitPublishedArrayGuard(r.name, e.receiver.type, 7); + host.emitPublishedArrayGuard(r.name, e.receiver.type, 10, start.name, cnt, f64Lit(0)); host.declare(`declare ptr @scr_arr_splice(ptr, double, double)`); const t = B.tmp(); B.line(`${t} = call ptr @scr_arr_splice(ptr ${r.name}, double ${start.name}, double ${cnt})`); @@ -1052,7 +1096,12 @@ export function emitArrIntrinsic( const start = host.emitExpr(e.args[0]!); const count = host.emitExpr(e.args[1]!); const items = emitBorrowedInput(host, e.args[2]!); - host.emitPublishedArrayGuard(r.name, e.receiver.type, 7); + if (host.publishedTypes?.containerGuarded(e.receiver.type) === true) { + host.declare(`declare double @scr_arr_len(ptr)`); + const n = B.tmp(); + B.line(`${n} = call double @scr_arr_len(ptr ${items.name})`); + host.emitPublishedArrayGuard(r.name, e.receiver.type, 10, start.name, count.name, n); + } host.declare(`declare ptr @scr_arr_splice_insert(ptr, double, double, ptr)`); const t = B.tmp(); B.line( diff --git a/packages/compiler/src/backend/llvm/expr-dynamic.ts b/packages/compiler/src/backend/llvm/expr-dynamic.ts index 07a538184..26905a7ef 100644 --- a/packages/compiler/src/backend/llvm/expr-dynamic.ts +++ b/packages/compiler/src/backend/llvm/expr-dynamic.ts @@ -18,6 +18,7 @@ import { type IrExpr, type IrType, isDynTypedRefType, + textCodecRecordClass, isRefCounted, isUnitType, typeEquals, @@ -86,7 +87,10 @@ export function emitDynamicExpr( // Bytes already box their shared mutable storage directly. A second // capsule would split identity between checked and native views. const errorRef = v.type.kind === "object" && host.shapeHost.isErrorClass(v.type.className); - if (!errorRef && ((e.liveRef && v.type.kind !== "bytes") || identityRef)) { + // A TextEncoder or TextDecoder record boxes as its own handle, which + // keeps the record's identity (toDynHelper). + const codecRef = textCodecRecordClass(v.type, (id) => host.recordsById.get(id)) !== null; + if (!errorRef && !codecRef && ((e.liveRef && v.type.kind !== "bytes") || identityRef)) { if (v.type.kind === "union") { const adapter = host.liveDynUnionRefAdapter(v.type); const boxed = B.tmp(); diff --git a/packages/compiler/src/backend/llvm/expr-primitives.ts b/packages/compiler/src/backend/llvm/expr-primitives.ts index 1eec6a965..ccbe3edcc 100644 --- a/packages/compiler/src/backend/llvm/expr-primitives.ts +++ b/packages/compiler/src/backend/llvm/expr-primitives.ts @@ -13,6 +13,7 @@ import { emitStringInputs } from "./string-lifetimes.js"; import { emitStringParts, numberPart, stringParts } from "./string-construction.js"; import { emitBorrowedInputs } from "./borrowed-inputs.js"; import { + alwaysExactInteger, exactInteger, int32Number, integerNumber, @@ -45,6 +46,17 @@ import { type ArrayAccess, } from "./dense-array-access.js"; import { emitWrappingSum, markWrappingOperands } from "./wrapping-sums.js"; +import { emitPowerOfTwoQuotient, isPowerOfTwoCall } from "./lib-filesystem.js"; + +/** The signed integer predicates of the number compares. */ +const INTEGER_COMPARES: Record = { + "<": "slt", + "<=": "sle", + ">": "sgt", + ">=": "sge", + "===": "eq", + "!==": "ne", +}; export function emitLiteralExpr( host: LlvmEmitterContext, @@ -318,15 +330,7 @@ export function emitOperatorExpr( if (integer) return integer; } if (cmp[e.op] !== undefined) { - const integerCmp: Record = { - "<": "slt", - "<=": "sle", - ">": "sgt", - ">=": "sge", - "===": "eq", - "!==": "ne", - }; - B.line(`${t} = icmp ${integerCmp[e.op]} i64 ${left}, ${right}`); + B.line(`${t} = icmp ${INTEGER_COMPARES[e.op]} i64 ${left}, ${right}`); return { name: t, type: e.type }; } } @@ -345,6 +349,8 @@ export function emitOperatorExpr( : emitCheckedRemainder(host, l, r, li, ri); if (checked) return checked; } + if (e.op === "/" && isPowerOfTwoCall(e.right)) + return { name: emitPowerOfTwoQuotient(host, l.name, r.name), type: e.type }; if (arith[e.op] !== undefined) { B.line(`${t} = ${arith[e.op]} double ${l.name}, ${r.name}`); const uint32 = emitWrappingSum(host, e, l, r, li, ri); @@ -356,7 +362,17 @@ export function emitOperatorExpr( if (wideInteger) value.wideInteger = wideInteger; return value; } - } else B.line(`${t} = fcmp ${cmp[e.op]} double ${l.name}, ${r.name}`); + } else { + // An operand with only a wide view, such as `i + 1` of a speculated + // counter: compare the integers when both always equal the doubles. + const left = alwaysExactInteger(host, l, li), + right = left === null ? null : alwaysExactInteger(host, r, ri); + if (left !== null && right !== null) { + B.line(`${t} = icmp ${INTEGER_COMPARES[e.op]} i64 ${left}, ${right}`); + return { name: t, type: e.type }; + } + B.line(`${t} = fcmp ${cmp[e.op]} double ${l.name}, ${r.name}`); + } } else if (bit[e.op] !== undefined) { if (e.left.type.kind !== "f64" || e.right.type.kind !== "f64") { throw new LlvmUnsupportedError( @@ -1076,9 +1092,10 @@ export function emitContainerExpr( const idx = host.emitExpr(e.index); if (e.arr.type.kind !== "array") throw new InternalCompilerError("llvm emitter bug: arrayState on non-array"); + const proven = host.provenDenseIndex(e.arr, e.index); const t = host.checkedInt32Array(e.arr) ? host.emitByElementKind(arr.name, "double", (kind) => - emitDenseArrayState(host, arr.name, idx, e.index, "state", slotWidth(kind)), + emitDenseArrayState(host, arr.name, idx, e.index, "state", slotWidth(kind), proven), )! : emitDenseArrayState( host, @@ -1087,6 +1104,7 @@ export function emitContainerExpr( e.index, "state", host.int32Array(e.arr) ? slotWidth("i32") : denseSlotWidth(host, e.arr.type.elem), + proven, ); return { name: t, type: e.type }; } diff --git a/packages/compiler/src/backend/llvm/expr-records.ts b/packages/compiler/src/backend/llvm/expr-records.ts index bca728e9d..59ab625e5 100644 --- a/packages/compiler/src/backend/llvm/expr-records.ts +++ b/packages/compiler/src/backend/llvm/expr-records.ts @@ -7,6 +7,7 @@ import type { LlvmEmitterContext, LlValue } from "./expr-context.js"; import { f64Lit, llvmCommentText } from "./common.js"; import { borrowableInputs, emitBorrowedInputs, emitBorrowedInput } from "./borrowed-inputs.js"; import { regexLiteralTemplate } from "./expr-primitives.js"; +import { isScalarUnionDef, loadInlineUnion, presentBits } from "./inline-unions.js"; import { preservesRegexInputs } from "./reference-effects.js"; /** Widen one tagged read. The source is borrowed unless the overflow map @@ -365,6 +366,114 @@ export function emitRecordKeyGet( return host.own({ name: t, type: e.type }); } +/** A keyed read whose result is a scalar union (`r[k]` on a + * `Record`, at `number | undefined`) and whose every hit + * is a number or boolean arm of it, or an inline field of the same union. + * Projections (`??`, `!`, tag tests) read it as a private stack box, so + * the read allocates nothing. */ +export function scalarRecordKeyGet(host: LlvmEmitterContext, e: IrExpr): boolean { + if (e.kind !== "recordKeyGet" || e.missNaN === true || e.type.kind !== "union") return false; + const resultType = e.type; + const def = host.unionsById.get(resultType.unionId); + if (!isScalarUnionDef(def) || undefinedArmTag(resultType, host.unionsById) < 0) return false; + const isArm = (t: IrType): boolean => + (t.kind === "f64" || t.kind === "bool") && def!.arms.some((a) => typeEquals(a, t)); + const shape = host.recordShape(e.shapeId); + if (e.overflowOnly !== true) { + for (const f of shape.fields) { + const inline = host.recordFieldInline(e.shapeId, f.name); + if (!isArm(f.type) && !(inline && typeEquals(f.type, resultType))) return false; + } + } + return shape.indexValue === undefined || isArm(shape.indexValue); +} + +/** A scalarRecordKeyGet read as a private stack box: the tag and bits of + * the declared field or overflow entry the key names, or the undefined arm + * for a miss. The box must never reach an RC entry point. */ +export function emitStackRecordKeyGet( + host: LlvmEmitterContext, + e: IrExpr & { kind: "recordKeyGet" }, +): string { + const B = host.B; + if (e.type.kind !== "union") + throw new InternalCompilerError("llvm emitter bug: stack keyed read of a non-union"); + const resultType = e.type; + const def = host.unionsById.get(resultType.unionId)!; + const armTag = (t: IrType): number => def.arms.findIndex((a) => typeEquals(a, t)); + const obj = host.emitExpr(e.obj); + const key = host.emitExpr(e.key); + const box = B.slot(); + B.entryAllocas.push(`${box} = alloca %ScrUnion`); + const tagPtr = B.tmp(), + bitsPtr = B.tmp(); + B.line(`store ${host.sizeType} -1, ptr ${box}`); + B.line(`${tagPtr} = getelementptr inbounds %ScrUnion, ptr ${box}, i64 0, i32 1`); + B.line(`${bitsPtr} = getelementptr inbounds %ScrUnion, ptr ${box}, i64 0, i32 5`); + const join = B.newLabel("rks.j"); + const storeScalar = (t: IrType, value: string): void => { + const bits = B.tmp(); + if (t.kind === "f64") B.line(`${bits} = bitcast double ${value} to i64`); + else B.line(`${bits} = zext i1 ${value} to i64`); + B.line(`store i32 ${armTag(t)}, ptr ${tagPtr}`); + B.line(`store i64 ${bits}, ptr ${bitsPtr}`); + }; + const shape = host.recordShape(e.shapeId); + for (const f of e.overflowOnly === true ? [] : shape.fields) { + const hit = B.tmp(); + B.line( + `${hit} = call zeroext i1 ${host.literalEquals(f.name)}(ptr ${key.name}) ; ${llvmCommentText(f.name)}`, + ); + const lh = B.newLabel("rks.h"); + const ln = B.newLabel("rks.n"); + B.condBr(hit, lh, ln); + B.startBlock(lh); + const { ptr, type, inline } = host.recordFieldPtr(obj.name, e.shapeId, f.name); + if (inline) { + const { tag, bits } = loadInlineUnion(B, ptr); + const present = presentBits(B, tag, bits, undefinedArmTag(type, host.unionsById)); + B.line(`store i32 ${tag}, ptr ${tagPtr}`); + B.line(`store i64 ${present}, ptr ${bitsPtr}`); + } else { + storeScalar(type, host.loadRecordField(ptr, type, false)); + } + B.br(join); + B.startBlock(ln); + } + const iv = shape.indexValue; + if (iv) { + const ovf = host.recordOvfPtr(obj.name, e.shapeId); + const outTy = iv.kind === "f64" ? "double" : "i8"; + const outSlot = B.slot(); + B.entryAllocas.push(`${outSlot} = alloca ${outTy}`); + B.line(`store ${outTy} ${iv.kind === "f64" ? f64Lit(0) : "0"}, ptr ${outSlot}`); + const getter = `scr_map_get_str_${iv.kind === "f64" ? "f64" : "bool"}`; + host.declare(`declare zeroext i1 @${getter}(ptr, ptr, ptr)`); + const found = B.tmp(); + B.line(`${found} = call zeroext i1 @${getter}(ptr ${ovf}, ptr ${key.name}, ptr ${outSlot})`); + const lh = B.newLabel("rks.h"); + const ln = B.newLabel("rks.n"); + B.condBr(found, lh, ln); + B.startBlock(lh); + const raw = B.tmp(); + B.line(`${raw} = load ${outTy}, ptr ${outSlot}`); + let value = raw; + if (iv.kind === "bool") { + value = B.tmp(); + B.line(`${value} = trunc i8 ${raw} to i1`); + } + storeScalar(iv, value); + B.br(join); + B.startBlock(ln); + } + // A miss is the undefined arm. + B.line(`store i32 ${undefinedArmTag(resultType, host.unionsById)}, ptr ${tagPtr}`); + B.line(`store i64 0, ptr ${bitsPtr}`); + B.br(join); + B.startBlock(join); + return box; +} + export function keyedRecordReadInto( host: LlvmEmitterContext, slot: string, diff --git a/packages/compiler/src/backend/llvm/expr-stream-bridges.ts b/packages/compiler/src/backend/llvm/expr-stream-bridges.ts index aee7be180..675932d47 100644 --- a/packages/compiler/src/backend/llvm/expr-stream-bridges.ts +++ b/packages/compiler/src/backend/llvm/expr-stream-bridges.ts @@ -16,9 +16,11 @@ import { KEY_ORDER_FIELD, shapeKeyOrders, isDynTypedRefType, + textCodecRecordClass, isRefCounted, shapeHasAccessorSlots, typeKey, + type IrClassDef, } from "../../ir/ir.js"; import { mangleFunction, mangleGlobal, mangleRecordStruct } from "../mangle.js"; import { BlockBuilder } from "./blocks.js"; @@ -121,12 +123,7 @@ export function dynPromiseAdapter(host: LlvmEmitterContext, inner: IrType): stri if (inner.kind === "void") { host.declare(`declare void @scr_promise_fulfill_void(ptr)`); B.line(`call void @scr_promise_fulfill_void(ptr %dst)`); - } else if ( - inner.kind === "f64" || - inner.kind === "date" || - inner.kind === "procStream" || - inner.kind === "bool" - ) { + } else if (inner.kind === "f64" || inner.kind === "procStream" || inner.kind === "bool") { const fn = inner.kind !== "bool" ? "scr_promise_fulfill_f64" : "scr_promise_fulfill_bool"; host.declare( `declare void @${fn}(ptr, ${host.llType(inner)}${inner.kind === "bool" ? " zeroext" : ""})`, @@ -276,9 +273,7 @@ export function streamTypedRefCommitAdapter( ); }); } - const symbols = new Map( - (meta.def.symbolFields ?? []).map((symbol) => [symbol.field, symbol.globalId]), - ); + const symbols = new Map((meta.def.symbolFields ?? []).map((symbol) => [symbol.field, symbol])); const fields = meta.def.fields.filter( (field) => (isClassOwnEnumerableFieldName(field.name) || symbols.has(field.name)) && @@ -298,13 +293,15 @@ export function streamTypedRefCommitAdapter( const inline = host.inlineUnions.classField(t.className, field.name); const fieldTy = int32 ? "i32" : llFieldType(field.type, inline); const checkTy = host.llType(field.type); - const symbolGlobal = symbols.get(field.name); - if (symbolGlobal) { + const symbolKey = symbols.get(field.name); + if (symbolKey) { host.declare(`declare ptr @scr_dyn_symbol_key_get(ptr, ptr, i1 zeroext)`); host.declare(`declare void @scr_dyn_release_v(ptr)`); + const keyLines = symbolKeyLines(host, symbolKey, `f${index}_keyraw`); lines.push( - ` %f${index}_keyraw = load ptr, ptr @${mangleGlobal(symbolGlobal)}`, + ...keyLines.load, ` %f${index}_key = call ptr @${host.dyn.toDynHelper(SYMBOL_T)}(ptr %f${index}_keyraw)`, + ...keyLines.release, ); if (field.type.kind === "symbol") { // A base constructor can mutate the instance before this @@ -332,7 +329,7 @@ export function streamTypedRefCommitAdapter( lines.push( ` %${raw} = call ptr @scr_dyn_obj_get(ptr %d, ptr ${host.cstr(field.name)}, ${host.sizeType} ${Buffer.byteLength(field.name, "utf8")})`, ); - if (meta.def.tracksOwnFields && !symbolGlobal) + if (meta.def.tracksOwnFields && !symbolKey) lines.push( ` %f${index}_present = icmp ne ptr %${raw}, null`, ` br i1 %f${index}_present, label %f${index}_read, label %${after}`, @@ -343,7 +340,7 @@ export function streamTypedRefCommitAdapter( ` %${undef} = call ptr @scr_dyn_undefined()`, ` %${input} = select i1 %${missing}, ptr %${undef}, ptr %${raw}`, ` %${next} = call ${field.type.kind === "bool" ? "zeroext " : ""}${checkTy} @${host.dyn.dynCheckHelper(field.type)}(ptr %${input}, ptr null)`, - ...(symbolGlobal ? [` call void @scr_dyn_release_v(ptr %${raw})`] : []), + ...(symbolKey ? [` call void @scr_dyn_release_v(ptr %${raw})`] : []), ...host.pendingTestLines(`%${pending}`), ` br i1 %${pending}, label %done, label %${store}`, `${store}:`, @@ -498,16 +495,18 @@ export function streamTypedRefCommitAdapter( ? `declare void @scr_dyn_symbol_key_placeholder(ptr, ptr)` : `declare void @scr_dyn_key_delete_computed(ptr, ptr, i1 zeroext)`, ); - [...symbols.values()].forEach((globalId, index) => + [...symbols.values()].forEach((symbol, index) => { + const keyLines = symbolKeyLines(host, symbol, `bag_symraw${index}`); lines.push( - ` %bag_symraw${index} = load ptr, ptr @${mangleGlobal(globalId)}`, + ...keyLines.load, ` %bag_sym${index} = call ptr @${host.dyn.toDynHelper(SYMBOL_T)}(ptr %bag_symraw${index})`, + ...keyLines.release, meta.def.tracksOwnFields ? ` call void @scr_dyn_symbol_key_placeholder(ptr %bag, ptr %bag_sym${index})` : ` call void @scr_dyn_key_delete_computed(ptr %bag, ptr %bag_sym${index}, i1 zeroext false)`, ` call void @scr_dyn_release_v(ptr %bag_sym${index})`, - ), - ); + ); + }); const { index } = classFieldIndex(meta, DYN_CLASS_PROPERTIES); lines.push( ` %bag_ptr = getelementptr inbounds %${classStructSym(t.className)}, ptr %target, i64 0, i32 ${index}`, @@ -597,12 +596,21 @@ export function liveDynUnionRefAdapter( if (!union) { throw new InternalCompilerError(`llvm emitter bug: live dyn ref of unknown union ${t.unionId}`); } + // Codec records box as their own handles, by reference, through toDynHelper. + const codecArm = (arm: IrType): boolean => + textCodecRecordClass(arm, (id) => host.recordsById.get(id)) !== null; const mutableArms = union.arms .map((arm, tag) => ({ arm, tag })) .filter( - ({ arm }) => arm.kind !== "bytes" && (streamTypedRefEligible(arm) || isDynTypedRefType(arm)), + ({ arm }) => + arm.kind !== "bytes" && + !codecArm(arm) && + (streamTypedRefEligible(arm) || isDynTypedRefType(arm)), ); - if (mutableArms.length === 0 && !union.arms.some((arm) => arm.kind === "bytes")) { + if ( + mutableArms.length === 0 && + !union.arms.some((arm) => arm.kind === "bytes" || codecArm(arm)) + ) { throw new InternalCompilerError(`llvm emitter bug: live dyn ref of immutable union ${key}`); } @@ -695,6 +703,27 @@ export function liveDynUnionRefAdapter( return sym; } +/** The raw ScrSym of a class's symbol field key into `%name`: a module + * key is borrowed from its global, a well-known one comes from the + * runtime's registry (+1, released by the `release` lines once the key + * value holds it). */ +function symbolKeyLines( + host: LlvmEmitterContext, + symbol: NonNullable[number], + name: string, +): { load: string[]; release: string[] } { + if ("globalId" in symbol) + return { load: [` %${name} = load ptr, ptr @${mangleGlobal(symbol.globalId)}`], release: [] }; + host.declare("declare ptr @scr_sym_well_known(ptr)"); + host.declare("declare void @scr_sym_release(ptr)"); + return { + load: [ + ` %${name} = call ptr @scr_sym_well_known(ptr ${host.internLiteral(symbol.wellKnown)})`, + ], + release: [` call void @scr_sym_release(ptr %${name})`], + }; +} + export function streamTypedRefBoxValue( host: LlvmEmitterContext, B: BlockBuilder, @@ -712,7 +741,13 @@ export function streamTypedRefBoxValue( B.line(`${boxed} = call ptr @${host.liveDynUnionRefAdapter(t)}(ptr ${value})`); return boxed; } - if (t.kind === "bytes" || (!streamTypedRefEligible(t) && !isDynTypedRefType(t))) { + // A codec record boxes as its own handle, by reference (toDynHelper), as + // it does outside a live view. + if ( + t.kind === "bytes" || + textCodecRecordClass(t, (id) => host.recordsById.get(id)) !== null || + (!streamTypedRefEligible(t) && !isDynTypedRefType(t)) + ) { const valueTy = host.llType(t); B.line(`${boxed} = call ptr @${host.dyn.toDynHelper(t)}(${valueTy} ${value})`); return boxed; @@ -913,8 +948,10 @@ export function streamTypedRefMaterializeAdapter( B.line(`${fieldValue} = sitofp i32 ${raw} to double`); } else if (inline) fieldValue = loadStructField(B, host.sizeType, fieldPtr, field.type, true); else B.line(`${fieldValue} = load ${llFieldType(field.type, false)}, ptr ${fieldPtr}`); - B.line(`${rawKey} = load ptr, ptr @${mangleGlobal(symbol.globalId)}`); + const keyLines = symbolKeyLines(host, symbol, rawKey.slice(1)); + for (const line of keyLines.load) B.line(line.trim()); const key = host.streamTypedRefBoxValue(B, SYMBOL_T, rawKey); + for (const line of keyLines.release) B.line(line.trim()); const afterSymbol = meta.def.tracksOwnFields || field.type.kind === "symbol" ? B.newLabel("symbol.after") @@ -982,6 +1019,28 @@ export function streamTypedRefMaterializeAdapter( : `call void @scr_error_dyn_place_name(ptr ${out}, ptr ${host.cstr(nameAfter)}, ${host.sizeType} ${Buffer.byteLength(nameAfter, "utf8")})`, ); } + // A JavaScript Error subclass's property bag records, once a dynamic + // write has committed the view, where `name` and `message` were created + // (zod's error factory assigns `name` before it defines `message`); the + // slots hold their values. A `name` the class's construction sets is + // already where errorNameAfter places it among the fields. + if ( + meta.def.errorBagOrder && + meta.root.def.name === "%Error" && + meta.def.fields.some((field) => field.name === DYN_CLASS_PROPERTIES) + ) { + host.declare(`declare void @scr_error_dyn_follow_bag(ptr, ptr, ptr, i1)`); + const { index } = classFieldIndex(meta, DYN_CLASS_PROPERTIES); + const slot = B.tmp(), + bag = B.tmp(); + B.line( + `${slot} = getelementptr inbounds %${classStructSym(t.className)}, ptr %p, i64 0, i32 ${index}`, + ); + B.line(`${bag} = load ptr, ptr ${slot}`); + B.line( + `call void @scr_error_dyn_follow_bag(ptr ${out}, ptr ${bag}, ptr %p, i1 ${nameAfter === undefined ? "false" : "true"})`, + ); + } // A frozen, sealed or non-extensible instance's view says so. if (withAttributes) { host.declare(`declare void @scr_dyn_class_view_attributes(ptr, double)`); @@ -1457,9 +1516,26 @@ function liveClassOps(host: LlvmEmitterContext, snapshot: string, meta: LlClassM // The constructor's name, as util.inspect prints before the fields. const jsName = meta.def.jsName ?? meta.def.name.replace(/^%/, ""); const className = jsName === "" ? "null" : host.internLiteral(jsName); + // Its Symbol.toStringTag getter's string, for Object.prototype.toString + // and util.inspect. + let stringTag = "null"; + if (meta.def.stringTagHelper) { + // The runtime lends the instance; the helper takes its own reference. + stringTag = `@${snapshot}_ctag`; + const retain = vAdapters(host.shapeHost, { kind: "object", className: meta.def.name }).retain; + host.resolveThunkDefs.push( + `define internal ptr ${stringTag}(ptr %p) ${FN_ATTRS} { ; Symbol.toStringTag of ${meta.def.name}`, + `entry:`, + ` %owned = call ptr ${retain}(ptr %p)`, + ` %tag = call ptr @${mangleFunction(meta.def.stringTagHelper)}(ptr %owned)`, + ` ret ptr %tag`, + `}`, + ``, + ); + } host.resolveThunkDefs.push( `@${names} = internal constant [${members.length} x ptr] [${literals.join(", ")}]`, - `@${ops} = internal constant { ptr, ${host.sizeType}, ptr, ptr } { ptr ${exact}, ${host.sizeType} ${members.length}, ptr @${names}, ptr ${className} }`, + `@${ops} = internal constant { ptr, ${host.sizeType}, ptr, ptr, ptr } { ptr ${exact}, ${host.sizeType} ${members.length}, ptr @${names}, ptr ${className}, ptr ${stringTag} }`, ``, ); } diff --git a/packages/compiler/src/backend/llvm/integer-values.ts b/packages/compiler/src/backend/llvm/integer-values.ts index ddbcdd8da..90954f4c6 100644 --- a/packages/compiler/src/backend/llvm/integer-values.ts +++ b/packages/compiler/src/backend/llvm/integer-values.ts @@ -65,6 +65,22 @@ function magnitude(range: IntegerRange): number { return Math.max(Math.abs(range.min), Math.abs(range.max)); } +/** The bounds of a sum and a product of two magnitude bounds, as doubles. + * Below 2^53 the double result is the integer; above it the result is past + * every exactness limit either way, and rounding moves it far less than the + * margin from WIDE_BOUND to the i64 range. A result of exactly 2^53 may + * stand for 2^53 + 1 (2^53 - 1 + 2 rounds to 2^53), so it moves to the next + * double up unless the sum is exactly 2^53. */ +function sumBound(a: number, b: number): number { + const sum = a + b; + return sum === EXACT_DOUBLES && EXACT_DOUBLES - a !== b ? sum + 2 : sum; +} + +function productBound(a: number, b: number): number { + const product = a * b; + return product === EXACT_DOUBLES ? product + 2 : product; +} + /** The wide view of `l op r` for `+`, `-` and `*` when the exact operands * have no proven result range (their product or sum may pass 2^53). * @@ -98,14 +114,14 @@ export function wideArithmetic( if (op === "*") { if (lv.below !== Infinity || rv.below !== Infinity) return null; below = EXACT_DOUBLES; - bound = lv.bound * rv.bound; + bound = productBound(lv.bound, rv.bound); } else if (lv.below === Infinity && rv.below === Infinity) { below = EXACT_DOUBLES; - bound = lv.bound + rv.bound; + bound = sumBound(lv.bound, rv.bound); } else if (lv.below === Infinity || rv.below === Infinity) { const [exact, wide] = lv.below === Infinity ? [lv, rv] : [rv, lv]; below = Math.min(wide.below - exact.bound, EXACT_DOUBLES); - bound = exact.bound + wide.bound; + bound = sumBound(exact.bound, wide.bound); } else return null; if (!(bound <= WIDE_BOUND) || below < INDEX_LIMIT) return null; const operand = (value: LlValue, integer: LlInteger | null): string => @@ -134,6 +150,20 @@ export function indexInteger( return wide && wide.below >= INDEX_LIMIT ? wide.name : null; } +/** An i64 equal to a value's double wherever the value can be: its exact + * integer, or a wide view whose magnitude bound is within the magnitude below + * which the view is exact. Every integer of magnitude at most 2^53 is a + * double, so a bound equal to that limit is exact too. Null otherwise. */ +export function alwaysExactInteger( + host: LlvmEmitterContext, + value: LlValue, + integer: LlInteger | null, +): string | null { + if (integer) return widenInteger(host, integer); + const wide = value.wideInteger; + return wide && wide.bound <= wide.below ? wide.name : null; +} + export function widenInteger(host: LlvmEmitterContext, value: LlInteger): string { if (value.type === "i64") return value.name; const wide = host.B.tmp(); diff --git a/packages/compiler/src/backend/llvm/layouts.ts b/packages/compiler/src/backend/llvm/layouts.ts index 8e1f0a4c1..5c426e910 100644 --- a/packages/compiler/src/backend/llvm/layouts.ts +++ b/packages/compiler/src/backend/llvm/layouts.ts @@ -17,7 +17,7 @@ export function emitLlvmLayouts( host: ClassHost, mod: IrModule, classes: Map, - classObjects: Map, + classObjects: Map, functions: Map, llType: (type: IrType) => string, nullable?: NullableRefFields, diff --git a/packages/compiler/src/backend/llvm/lib-dispatch.ts b/packages/compiler/src/backend/llvm/lib-dispatch.ts index eaf8f59f6..9cd11feb1 100644 --- a/packages/compiler/src/backend/llvm/lib-dispatch.ts +++ b/packages/compiler/src/backend/llvm/lib-dispatch.ts @@ -5,11 +5,12 @@ import { emitTlsLibCall } from "./lib-tls.js"; import { emitHttp2LibCall } from "./lib-http2.js"; import { emitDatagramLibCall } from "./lib-datagram.js"; import { emitTestLibCall } from "./lib-test.js"; +import { FN_ATTRS, arrNewCall, traceArg, vAdapters } from "./shapes.js"; /* Focused LLVM library-call emission extracted from emitter.ts. */ import { InternalCompilerError } from "../../errors.js"; import { MAY_THROW_LIB_FNS } from "../../ir/builtin-effects.js"; -import type { IrExpr, IrType } from "../../ir/ir.js"; +import { F64, type IrExpr, type IrType } from "../../ir/ir.js"; import { LlvmUnsupportedError } from "./unsupported.js"; import { emitJsLibCall } from "./jsval.js"; import type { LlvmEmitterContext, LibCallExpr, LibCallPrefix, LlValue } from "./expr-context.js"; @@ -25,6 +26,9 @@ import { rawBytes } from "./lib-abi.js"; import { emitInlineAtomicsOp } from "./expr-bytes.js"; import { regexLiteralCaptures } from "./regex-matchers.js"; +/** ScrDyn.v.typed_ref.type_key_len: the low 27 bits of its bit-field word. */ +const TYPE_KEY_LEN_MASK = (1 << 27) - 1; + export function emitAssertInspectLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue { const B = host.B; if (e.fn === "assert.shapeStr" || e.fn === "assert.shapeRe") { @@ -236,6 +240,42 @@ export function emitGenericLibCall(host: LlvmEmitterContext, e: LibCallExpr): Ll host.emitPendingCheck(); return output; } + if (e.fn === "promise.allTuple") return emitPromiseAllTuple(host, e); + if (e.fn === "promise.fulfilledValue") { + // The payload of a fulfilled promise (borrowed operand): the scalar + // kinds that lower to a double (numbers, Dates, process streams) and + // booleans by value, strings and references +1. + const promise = host.emitExpr(e.args[0]!); + const out = B.tmp(); + const ty = host.llType(e.type); + if (ty === "double") { + host.declare(`declare double @scr_promise_payload_f64(ptr)`); + B.line(`${out} = call double @scr_promise_payload_f64(ptr ${promise.name})`); + return { name: out, type: e.type }; + } + if (ty === "i1") { + host.declare(`declare zeroext i1 @scr_promise_payload_bool(ptr)`); + B.line(`${out} = call zeroext i1 @scr_promise_payload_bool(ptr ${promise.name})`); + return { name: out, type: e.type }; + } + if (e.type.kind === "string") { + host.declare(`declare ptr @scr_promise_payload_str(ptr)`); + B.line(`${out} = call ptr @scr_promise_payload_str(ptr ${promise.name}) ; +1`); + return host.own({ name: out, type: e.type }); + } + host.declare(`declare ptr @scr_promise_payload_ref(ptr)`); + B.line(`${out} = call ptr @scr_promise_payload_ref(ptr ${promise.name}) ; +1`); + return host.own({ name: out, type: e.type }); + } + if (e.fn === "record.sourceSpread") { + const built = host.emitReadReceiver(e.args[0]!); + const value = host.emitReadReceiver(e.args[1]!); + const type = e.args[0]!.type; + if (type.kind !== "record") + throw new InternalCompilerError("llvm emitter bug: record.sourceSpread of a non-record"); + host.dyn.emitRecordSourceSpread(B, built.name, value.name, type.shapeId); + return { name: "", type: e.type }; + } if (e.fn === "dyn.typedRefIs" || e.fn === "dyn.typedRefKeyIs") { const literal = e.args[1]!.kind === "strLit" ? e.args[1]!.value : null; if (e.fn === "dyn.typedRefKeyIs" && literal === null) @@ -281,6 +321,38 @@ export function emitGenericLibCall(host: LlvmEmitterContext, e: LibCallExpr): Ll B.line(`store i1 true, ptr ${slot}`); B.condBr(same, done, compare); B.startBlock(compare); + // A capsule with another key pointer usually belongs to another + // class: reject it on its key length and last eight bytes before + // the out-of-line name comparison, so a class table's chain of + // probes costs a few instructions per class it skips. + B.line(`store i1 false, ptr ${slot}`); + const bytes = Buffer.from(literal, "utf8"); + const lenSlot = B.tmp(), + lenWord = B.tmp(), + len = B.tmp(), + lenEq = B.tmp(); + B.line( + `${lenSlot} = getelementptr inbounds i8, ptr ${value.name}, i64 ${host.abiOffset(88, 52)} ; ->v.typed_ref.type_key_len`, + ); + B.line(`${lenWord} = load i32, ptr ${lenSlot}`); + B.line(`${len} = and i32 ${lenWord}, ${TYPE_KEY_LEN_MASK}`); + B.line(`${lenEq} = icmp eq i32 ${len}, ${bytes.length}`); + const full = B.newLabel("brand.full"); + if (bytes.length >= 8) { + const tail = B.newLabel("brand.tail"); + B.condBr(lenEq, tail, done); + B.startBlock(tail); + const tailPtr = B.tmp(), + word = B.tmp(), + wordEq = B.tmp(); + B.line(`${tailPtr} = getelementptr inbounds i8, ptr ${keyPtr}, i64 ${bytes.length - 8}`); + B.line(`${word} = load i64, ptr ${tailPtr}, align 1`); + B.line(`${wordEq} = icmp eq i64 ${word}, ${bytes.readBigInt64LE(bytes.length - 8)}`); + B.condBr(wordEq, full, done); + } else { + B.condBr(lenEq, full, done); + } + B.startBlock(full); } } if (e.fn === "dyn.typedRefIs") { @@ -594,16 +666,66 @@ export function emitGenericLibCall(host: LlvmEmitterContext, e: LibCallExpr): Ll ? "scr_stream_dyn_async_iterator_open" : LIB_FN_SYMS[e.fn]); if (sym === undefined) throw new LlvmUnsupportedError(`libCall:${e.fn}`, e.loc); + // The runtime's Date functions take and answer time values: a Date + // operand passes its time value, read at once from the borrowed object, + // and a Date result is a new Date object. const args = READ_ONLY_STRING_LIB_FNS.has(e.fn) ? emitBorrowedInputs(host, e.args) : e.args.map((a) => - borrowsJsonInputs(e.fn) || borrowsIteratorInputs(e.fn) - ? emitBorrowedInput(host, a) - : host.emitExpr(a), + a.type.kind === "date" + ? { name: emitDateTimeArg(host, a), type: F64 } + : borrowsJsonInputs(e.fn) || borrowsIteratorInputs(e.fn) + ? emitBorrowedInput(host, a) + : host.emitExpr(a), ); + // getTime and valueOf answer the time value the argument already is. + if ((e.fn === "date.getTime" || e.fn === "date.valueOf") && e.args[0]?.type.kind === "date") + return { name: args[0]!.name, type: e.type }; // The deep comparisons' number test: computed here, with no runtime call. if (e.fn === "assert.sameValue") return { name: emitSameValueF64(host, args[0]!.name, args[1]!.name), type: e.type }; + const callArgs = args; + if (e.type.kind === "date") { + const ms = emitSymbolLibCall(host, { ...e, type: F64 }, sym, callArgs); + host.declare(`declare ptr @scr_date_new(double)`); + const date = B.tmp(); + B.line(`${date} = call ptr @scr_date_new(double ${ms.name})`); + return host.own({ name: date, type: e.type }); + } + return emitSymbolLibCall(host, e, sym, callArgs); +} + +/** The time value of a Date argument. A Date that a runtime Date function + * produces for this argument alone (`new Date(ms).getTime()`, a + * time-value local's read) passes the function's time value and creates + * no Date object. */ +function emitDateTimeArg(host: LlvmEmitterContext, date: IrExpr): string { + // A time-value local's read is its time value itself. + if (date.kind === "libCall" && date.fn === "date.fromTime") + return host.emitExpr(date.args[0]!).name; + if (date.kind === "libCall" && date.fn.startsWith("date.")) + return emitGenericLibCall(host, { ...date, type: F64 }).name; + return emitDateTime(host, emitBorrowedInput(host, date).name); +} + +/** A Date object's time value (ScrDate.ms). */ +function emitDateTime(host: LlvmEmitterContext, date: string): string { + const B = host.B; + const field = B.tmp(); + const ms = B.tmp(); + B.line(`${field} = getelementptr inbounds i8, ptr ${date}, i64 8 ; ->ms`); + B.line(`${ms} = load double, ptr ${field}`); + return ms; +} + +/** The call of runtime function `sym` with evaluated arguments. */ +function emitSymbolLibCall( + host: LlvmEmitterContext, + e: LibCallExpr, + sym: string, + args: readonly LlValue[], +): LlValue { + const B = host.B; const argDecls = args.map((a) => { const ty = host.llType(a.type); return ty === "i1" ? "i1 zeroext" : ty; @@ -805,6 +927,19 @@ export function emitLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue { return { name: t, type: e.type }; } if (e.fn === "bytes.absent") return host.own({ name: "null", type: e.type }); + if (e.fn === "record.isFrozen") { + // The frozen bit of the record's rc word (scr_rc.h SCR_RC_FROZEN) is + // its second-highest bit; an immortal word sets the highest one too. + const value = host.emitReadReceiver(e.args[0]!); + const S = host.sizeType; + const rc = host.B.tmp(), + top = host.B.tmp(), + t = host.B.tmp(); + host.B.line(`${rc} = load ${S}, ptr ${value.name}`); + host.B.line(`${top} = lshr ${S} ${rc}, ${S === "i64" ? 62 : 30}`); + host.B.line(`${t} = icmp eq ${S} ${top}, 1`); + return { name: t, type: e.type }; + } const prefix = e.fn.slice(0, e.fn.indexOf(".")) as LibCallPrefix; // events.once builds typed listener adapters beside the emitter calls. if (e.fn === "events.once" || e.fn === "events.onceSignal") @@ -913,10 +1048,12 @@ export function emitLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue { case "blob": case "wasm": case "array": + case "collection": case "record": case "disposable": case "punycode": case "constants": + case "promise": return host.emitGenericLibCall(e); case "test": return emitTestLibCall(host, e); @@ -931,3 +1068,63 @@ export function emitLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue { } } } + +/** Promise.all's tuple form (promise.allTuple): the combinator over the + * entries stores each fulfilled entry promise, and a settlement adapter on + * the combinator calls the builder with them and fulfills the result with + * the tuple inside the combinator's settlement; a rejection settles the + * result the same way. The combinator itself counts as handled. */ +function emitPromiseAllTuple(host: LlvmEmitterContext, e: LibCallExpr): LlValue { + const B = host.B; + const build = e.args[1]!; + if (build.kind !== "closure" || e.type.kind !== "promise") + throw new InternalCompilerError("llvm emitter bug: promise.allTuple shape"); + const entriesT = e.args[0]!.type; + const entries = host.emitExpr(e.args[0]!); + const tuple = e.type.inner; + const key = `allTuple:${build.fnName}`; + let adapter = host.resolveThunks.get(key); + if (adapter === undefined) { + adapter = `sc_all_tuple_${host.resolveThunks.size}`; + host.resolveThunks.set(key, adapter); + const rc = vAdapters(host.shapeHost, tuple); + host.declare(`declare ptr @scr_promise_payload_ref(ptr)`); + host.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`); + host.resolveThunkDefs.push( + `define internal void @${adapter}(ptr %dst, ptr %src) ${FN_ATTRS} { ; Promise.all tuple`, + `entry:`, + ` %entries = call ptr @scr_promise_payload_ref(ptr %src) ; +1, moves into the builder`, + ` %tuple = call ptr @${host.callTarget(build.fnName)}(ptr %entries)`, + ` call void @scr_promise_fulfill_ref(ptr %dst, ptr %tuple, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(host.shapeHost, tuple)})`, + ` ret void`, + `}`, + ``, + ); + } + host.declare(`declare ptr @scr_promise_all(ptr, ptr, ptr)`); + host.declare(`declare void @scr_promise_all_store_promise(ptr, double, ptr)`); + host.declare(`declare ptr @scr_promise_new()`); + host.declare(`declare void @scr_promise_race_add(ptr, ptr, ptr)`); + host.declare(`declare void @scr_promise_mark_handled(ptr)`); + host.declare(`declare void @scr_promise_release(ptr)`); + host.declare(`declare double @scr_arr_len(ptr)`); + if (entriesT.kind !== "array") + throw new InternalCompilerError("llvm emitter bug: promise.allTuple entries"); + const len = B.tmp(); + const cap = B.tmp(); + B.line(`${len} = call double @scr_arr_len(ptr ${entries.name})`); + B.line(`${cap} = fptoui double ${len} to ${host.sizeType}`); + const values = B.tmp(); + B.line(`${values} = ${arrNewCall(host.shapeHost, entriesT.elem, cap)}`); + host.own({ name: values, type: entriesT }); + const all = B.tmp(); + B.line( + `${all} = call ptr @scr_promise_all(ptr ${entries.name}, ptr ${values}, ptr @scr_promise_all_store_promise)`, + ); + B.line(`call void @scr_promise_mark_handled(ptr ${all})`); + const result = B.tmp(); + B.line(`${result} = call ptr @scr_promise_new()`); + B.line(`call void @scr_promise_race_add(ptr ${result}, ptr ${all}, ptr @${adapter})`); + B.line(`call void @scr_promise_release(ptr ${all})`); + return host.own({ name: result, type: e.type }); +} diff --git a/packages/compiler/src/backend/llvm/lib-filesystem.ts b/packages/compiler/src/backend/llvm/lib-filesystem.ts index 7d9be632d..f0b766936 100644 --- a/packages/compiler/src/backend/llvm/lib-filesystem.ts +++ b/packages/compiler/src/backend/llvm/lib-filesystem.ts @@ -1,7 +1,7 @@ /* Focused LLVM library-call emission extracted from emitter.ts. */ import { InternalCompilerError } from "../../errors.js"; import { undefinedArmTag } from "../../ir/analysis.js"; -import { DYN, F64, STATS_T, STRING } from "../../ir/ir.js"; +import { DYN, F64, STATS_T, STRING, type IrExpr } from "../../ir/ir.js"; import { mangleRecordNew } from "../mangle.js"; import { arrNewCall, traceArg, vAdapters } from "./shapes.js"; import type { LlvmEmitterContext, LibCallExpr, LlValue } from "./expr-context.js"; @@ -1027,6 +1027,57 @@ function emitPowerOfTwo(host: LlvmEmitterContext, n: string): string { return out; } +/** `Math.pow(2, n)`, the divisor emitPowerOfTwoQuotient takes. */ +export function isPowerOfTwoCall(e: IrExpr): boolean { + return ( + e.kind === "libCall" && + e.fn === "math.pow" && + e.args.length === 2 && + e.args[0]!.kind === "numLit" && + e.args[0]!.value === 2 + ); +} + +/** `left / divisor` for a divisor that is usually a power of two + * (`x / Math.pow(2, n)`). A positive power of two from 2^-1022 to 2^1022 + * has a reciprocal that is a power of two too, whose bits are + * 0x7FE0000000000000 minus the divisor's, and the product with it is the + * quotient for every `left`: both are `left` scaled by the same power of + * two, rounded once. Any other divisor divides. */ +export function emitPowerOfTwoQuotient( + host: LlvmEmitterContext, + left: string, + divisor: string, +): string { + const B = host.B; + const bits = B.tmp(), + mantissa = B.tmp(), + plain = B.tmp(), + biased = B.tmp(), + offset = B.tmp(), + normal = B.tmp(), + power = B.tmp(), + inverseBits = B.tmp(), + inverse = B.tmp(), + scaled = B.tmp(), + divided = B.tmp(), + out = B.tmp(); + B.line(`${bits} = bitcast double ${divisor} to i64`); + B.line(`${mantissa} = and i64 ${bits}, ${2n ** 52n - 1n}`); + B.line(`${plain} = icmp eq i64 ${mantissa}, 0`); + // Biased exponents 1 to 2045 with a clear sign bit. + B.line(`${biased} = lshr i64 ${bits}, 52`); + B.line(`${offset} = sub i64 ${biased}, 1`); + B.line(`${normal} = icmp ult i64 ${offset}, 2045`); + B.line(`${power} = and i1 ${plain}, ${normal}`); + B.line(`${inverseBits} = sub i64 ${0x7fe0000000000000n}, ${bits}`); + B.line(`${inverse} = bitcast i64 ${inverseBits} to double`); + B.line(`${scaled} = fmul double ${left}, ${inverse}`); + B.line(`${divided} = fdiv double ${left}, ${divisor}`); + B.line(`${out} = select i1 ${power}, double ${scaled}, double ${divided}`); + return out; +} + export function emitPrimitiveLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue { const B = host.B; if (e.fn === "math.min" || e.fn === "math.max") { diff --git a/packages/compiler/src/backend/llvm/lib-process.ts b/packages/compiler/src/backend/llvm/lib-process.ts index 3e8a9bff5..1a3973b07 100644 --- a/packages/compiler/src/backend/llvm/lib-process.ts +++ b/packages/compiler/src/backend/llvm/lib-process.ts @@ -1,10 +1,11 @@ /* Focused LLVM library-call emission extracted from emitter.ts. */ import { InternalCompilerError } from "../../errors.js"; import { undefinedArmTag } from "../../ir/analysis.js"; -import { F64, RUNTIME_ERROR_CLASSES, STRING } from "../../ir/ir.js"; +import { F64, type IrType, RUNTIME_ERROR_CLASSES, STRING, typeKey } from "../../ir/ir.js"; import type { LlvmEmitterContext, LibCallExpr, LlValue } from "./expr-context.js"; import { f64Lit } from "./common.js"; import { mockTimerSym } from "./lib-shared.js"; +import { FN_ATTRS } from "./shapes.js"; export function emitChildProcessLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue { const B = host.B; @@ -460,9 +461,58 @@ export function emitAsyncContextLibCall(host: LlvmEmitterContext, e: LibCallExpr for (const a of e.args) host.emitExpr(a); return { name: "", type: e.type }; } + if (e.fn === "als.bindCallback") { + // The closure moves into the bound closure, whose entry is the + // per-signature thunk; with nothing to bind the runtime answers the + // closure itself. + if (e.type.kind !== "func") + throw new InternalCompilerError("llvm emitter bug: als.bindCallback on non-func"); + const cb = host.emitExpr(e.args[0]!); + const always = host.emitExpr(e.args[1]!); + host.toHeap(cb); + const thunk = boundCallbackThunk(host, e.type); + host.declare("declare ptr @scr_als_bind_closure(ptr, ptr, i1 zeroext)"); + const t = B.tmp(); + B.line( + `${t} = call ptr @scr_als_bind_closure(ptr ${cb.name}, ptr @${thunk}, i1 ${always.name})`, + ); + return host.own({ name: t, type: e.type }); + } return host.emitGenericLibCall(e); } +/** The entry of a callback bound to an AsyncLocalStorage context, one per + * callback signature: enters the captured context, calls the target with + * the same arguments (the callValue ABI) and restores the caller's + * context, also when the target leaves an exception pending. */ +function boundCallbackThunk(host: LlvmEmitterContext, cbT: IrType & { kind: "func" }): string { + const key = `alsbound:${typeKey(cbT)}`; + let sym = host.resolveThunks.get(key); + if (sym) return sym; + sym = `sc_alsbound_${host.resolveThunks.size}`; + host.resolveThunks.set(key, sym); + host.declare("declare ptr @scr_als_bound_target(ptr)"); + host.declare("declare ptr @scr_als_bound_enter(ptr)"); + host.declare("declare void @scr_als_bound_leave(ptr, ptr)"); + const ret = host.llType(cbT.ret); + const params = cbT.params.map((p, i) => `${host.llType(p)} %a${i}`); + const call = `call ${ret} %fn(${["ptr %cb", ...params].join(", ")})`; + host.resolveThunkDefs.push( + `define internal ${ret} @${sym}(${["ptr %self", ...params].join(", ")}) ${FN_ATTRS} { ; AsyncLocalStorage-bound callback ${typeKey(cbT)}`, + `entry:`, + ` %cb = call ptr @scr_als_bound_target(ptr %self)`, + ` %prev = call ptr @scr_als_bound_enter(ptr %self)`, + ` %fnp = getelementptr inbounds %ScrClosure, ptr %cb, i64 0, i32 1`, + ` %fn = load ptr, ptr %fnp`, + ret === "void" ? ` ${call}` : ` %r = ${call}`, + ` call void @scr_als_bound_leave(ptr %prev, ptr %cb)`, + ret === "void" ? ` ret void` : ` ret ${ret} %r`, + `}`, + ``, + ); + return sym; +} + export function emitProcessLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue { const B = host.B; if (e.fn === "process.forkTarget") { diff --git a/packages/compiler/src/backend/llvm/lib-shared.ts b/packages/compiler/src/backend/llvm/lib-shared.ts index af32c6936..7a16c37e8 100644 --- a/packages/compiler/src/backend/llvm/lib-shared.ts +++ b/packages/compiler/src/backend/llvm/lib-shared.ts @@ -333,6 +333,7 @@ export const LIB_FN_SYMS: Record = { "date.parse": "scr_date_parse", "date.getTime": "scr_date_get_time", "date.valueOf": "scr_date_get_time", + "date.fromTime": "scr_date_get_time", "date.parseGetTime": "scr_date_parse", "date.utc": "scr_date_utc", "date.newLocal": "scr_date_new_local", @@ -454,6 +455,7 @@ export const LIB_FN_SYMS: Record = { "buffer.poolFromDyn": "scr_buffer_from_dyn_pooled", "buffer.poolFromDynRef": "scr_buffer_from_dyn_ref_pooled", "buffer.poolFromDynOffset": "scr_buffer_from_dyn_offset_pooled", + "buffer.poolFromDynRange": "scr_buffer_from_dyn_range_pooled", "buffer.poolSize": "scr_buffer_pool_size", "buffer.setPoolSize": "scr_buffer_set_pool_size", "buffer.byteLenDyn": "scr_buffer_byte_length_dyn", @@ -479,6 +481,12 @@ export const LIB_FN_SYMS: Record = { "bytes.equalsChk": "scr_bytes_equals_chk", "bytes.compareChk": "scr_bytes_compare_chk", "buffer.newStringFail": "scr_buffer_new_string_fail", + "buffer.deprecation": "scr_buffer_deprecation", + "buffer.deprecationCount": "scr_buffer_deprecation_count", + "buffer.constructor": "scr_buffer_constructor_value", + "buffer.installValue": "scr_buffer_install_value", + "buffer.staticValue": "scr_buffer_static_value", + "buffer.prototypeMember": "scr_buffer_prototype_member", "fs.toUnixTimestamp": "scr_fs_to_unix_timestamp", "fs.existsChk": "scr_fs_exists_async", "fs.mkdtempSyncChk": "scr_fs_mkdtemp_sync_chk", @@ -560,12 +568,14 @@ export const LIB_FN_SYMS: Record = { "text.decoderEncoding": "scr_text_decoder_encoding", "text.decoderName": "scr_text_decoder_name", "text.encodeInto": "scr_text_encode_into", + "text.codecIs": "scr_text_codec_is", "buffer.isAscii": "scr_buffer_is_ascii", "buffer.isUtf8": "scr_buffer_is_utf8", "buffer.transcode": "scr_buffer_transcode", "zlib.deflateSync": "scr_zlib_deflate", "zlib.inflateSync": "scr_zlib_inflate", "zlib.crc32": "scr_zlib_crc32", + "zlib.brotliInstall": "scr_brotli_install", // The CA-store unit. get/set throw (an unknown type name, a // certificate-free set) and take the generic path's pending check. "tlsca.root": "scr_tls_ca_root", @@ -649,6 +659,7 @@ export const LIB_FN_SYMS: Record = { "process.stderrWriteBytes": "scr_process_stderr_write_bytes", "insp.buffer": "scr_insp_buffer", "insp.typedArray": "scr_insp_typed_array", + "insp.bytes": "scr_insp_bytes", // util.inspect (scr_inspect.c): all args borrowed; string results +1; // the begin/entry/key/moreItems/end accumulator drives the emitted // container walks. None of these throw. @@ -667,6 +678,8 @@ export const LIB_FN_SYMS: Record = { "insp.moreItems": "scr_insp_more_items", "insp.end": "scr_insp_end", "insp.nestedBegin": "scr_insp_nested_begin", + "insp.mark": "scr_insp_mark", + "insp.unwind": "scr_insp_unwind", "insp.nestedEnd": "scr_insp_nested_end", // Circular references over cycle-capable composites (Node's // seen/circular machinery — none of these throw; refWrap borrows its @@ -677,6 +690,7 @@ export const LIB_FN_SYMS: Record = { "insp.circular": "scr_insp_circular", // Undeclared source properties kept beside a checked record (scr_json.c). "record.sourceHas": "scr_record_source_has", + "record.sourceSpread": "scr_record_source_spread", // WHATWG URL + URLSearchParams (scr_url.c / scr_url_params.c): // constructions +1; url.new, the fileURLToPath pair, the win32 // pathToFileURL flavor, and sp.fromPairs throw catchably (may-throw). @@ -701,6 +715,9 @@ export const LIB_FN_SYMS: Record = { "url.hash": "scr_url_hash", "url.searchParams": "scr_url_search_params", "url.fileURLToPathUrl": "scr_url_to_path", + "url.fileURLToPathValue": "scr_url_to_path_value", + "fs.checkPath": "scr_fs_check_path", + "fs.checkReadEncoding": "scr_fs_check_read_encoding", "url.fileURLToPathChecked": "scr_url_checked_to_path", "url.fileURLToPathOptions": "scr_url_checked_to_path_options", "url.fileURLToPathBuffer": "scr_url_checked_to_path_buffer", @@ -718,6 +735,7 @@ export const LIB_FN_SYMS: Record = { "punycode.ucs2Encode": "scr_str_from_code_point", "punycode.warn": "scr_punycode_warn", "url.domainToUnicode": "scr_url_domain_to_unicode", + "url.toHttpOptions": "scr_url_to_http_options_dyn", "url.pathToFileURL": "scr_url_from_path", "url.pathToFileURLPlatform": "scr_url_from_path_platform", "url.pathToFileURLWin32": "scr_url_from_path", @@ -958,6 +976,7 @@ export const LIB_FN_SYMS: Record = { "fetch.streamIs": "scr_fetch_stream_is", "zlib.streamIs": "scr_zlib_stream_is", "fetch.streamNew": "scr_fetch_stream_new", + "fetch.transformNew": "scr_fetch_transform_new", "fetch.streamFrom": "scr_fetch_stream_from", "fetch.readerRead": "scr_fetch_reader_read", "json.parse": "scr_json_parse", @@ -1019,6 +1038,12 @@ export const LIB_FN_SYMS: Record = { "class.descriptorHasValue": "scr_class_descriptor_has_value", "class.throwReadOnly": "scr_class_throw_read_only", "class.throwNotExtensible": "scr_class_throw_not_extensible", + "record.freeze": "scr_record_freeze", + "record.throwFrozen": "scr_record_throw_frozen", + "array.freeze": "scr_arr_freeze", + "array.isFrozen": "scr_arr_is_frozen", + "collection.preventExtensions": "scr_map_prevent_extensions", + "collection.isExtensible": "scr_map_is_extensible", "dyn.nativeSetNew": "scr_dyn_native_set_new", "dyn.nativeSetIs": "scr_dyn_native_set_is", "dyn.nativeMapIs": "scr_dyn_native_map_is", @@ -1042,6 +1067,7 @@ export const LIB_FN_SYMS: Record = { "dyn.generatorThis": "scr_gen_receiver", "insp.dyn": "scr_insp_dyn", "insp.dynS": "scr_insp_dyn_s", + "insp.dynHidden": "scr_insp_dyn_hidden", "fs.readFileSyncDyn": "scr_fs_read_file_sync_dyn", "fs.readFileEncoded": "scr_fs_read_file_encoded", "fs.readFdEncoded": "scr_fs_read_fd_encoded", @@ -1061,6 +1087,7 @@ export const LIB_FN_SYMS: Record = { "crypto.generateKeyPairSync": "scr_crypto_generate_key_pair_sync", "crypto.generateKeyPairCb": "scr_crypto_generate_key_pair_async", "crypto.signOneshot": "scr_crypto_sign_oneshot", + "crypto.diffieHellman": "scr_crypto_diffie_hellman", "crypto.verifyOneshot": "scr_crypto_verify_oneshot", "crypto.createSign": "scr_crypto_create_sign", "crypto.createVerify": "scr_crypto_create_verify", @@ -1478,6 +1505,10 @@ export const LIB_FN_SYMS: Record = { "als.exitRun": "scr_als_exit_run", "als.enterWith": "scr_als_enter_with", "als.disable": "scr_als_disable", + "als.newOptions": "scr_als_new_options", + "als.name": "scr_als_name", + "als.bindCallbackDyn": "scr_als_bind_dyn", + "als.snapshot": "scr_als_snapshot", "dc.chanBindStore": "scr_dc_chan_bind_store", "dc.chanUnbindStore": "scr_dc_chan_unbind_store", "dc.chanRunStores": "scr_dc_chan_run_stores", @@ -1641,6 +1672,7 @@ export function mockTimerSym( export const USES_TIMERS_LIB_FNS = new Set([ "crypto.generateKeyPairCb", + "crypto.diffieHellman", "crypto.checkPrimeCb", "crypto.generatePrimeCb", "process.emitWarning", @@ -1666,6 +1698,8 @@ export const USES_TIMERS_LIB_FNS = new Set([ "url.legacyParse", "url.legacyFormat", "url.legacyResolve", + // The deprecated Buffer constructor reports DEP0005 on the next tick. + "buffer.deprecation", // The wrappers schedule warnings and callbacks on the tick queue. "util.deprecate", "util.callbackify", @@ -1691,6 +1725,7 @@ export const USES_TIMERS_LIB_FNS = new Set([ "fetch.abortAny", "fetch.streamNew", "fetch.streamFrom", + "fetch.transformNew", "readable.new", // fs.ReadStream / fs.WriteStream open, read and write on the worker pool. "fs.createReadStream", diff --git a/packages/compiler/src/backend/llvm/local-array-reads.ts b/packages/compiler/src/backend/llvm/local-array-reads.ts index 1c540295a..80ed2c440 100644 --- a/packages/compiler/src/backend/llvm/local-array-reads.ts +++ b/packages/compiler/src/backend/llvm/local-array-reads.ts @@ -797,7 +797,33 @@ export function emitLocalArrayRead( ); return null; }); - else if (inline) + else if ( + inline && + loopIndex !== null && + read.element.kind === "f64" && + host.provenDenseIndex(read.array, read.index) + ) { + // In a dense array loop's fast copy, the entry test proved the counter + // below the array's hole-free prefix: the slot holds a present value, + // and the read has no missing arm. + const dataPtr = B.tmp(), + data = B.tmp(), + valuePtr = B.tmp(), + value = B.tmp(), + width = int32 ? "i32" : "i64"; + B.line( + `${dataPtr} = getelementptr inbounds %ScrArr, ptr ${array.name}, i32 0, i32 ${SCR_ARR_FIELD.data}`, + ); + host.markMemoryPointer(dataPtr, "array:header"); + B.line(`${data} = load ptr, ptr ${dataPtr}${host.fieldAliasAttachment(dataPtr)}`); + B.line( + `${valuePtr} = getelementptr inbounds ${width}, ptr ${data}, ${host.sizeType} ${loopIndex}`, + ); + host.markMemoryPointer(valuePtr, "array:elements"); + B.line(`${value} = load ${width}, ptr ${valuePtr}${host.fieldAliasAttachment(valuePtr)}`); + storeValue(value); + B.br(join); + } else if (inline) emitDenseReferenceArrayRead( host, array, diff --git a/packages/compiler/src/backend/llvm/publish.ts b/packages/compiler/src/backend/llvm/publish.ts index 60270adda..62fdd75ec 100644 --- a/packages/compiler/src/backend/llvm/publish.ts +++ b/packages/compiler/src/backend/llvm/publish.ts @@ -596,10 +596,10 @@ export class PublishWalkers { } } -/** Unpublishable types stored as scalars (dates are time values, process - * streams file descriptors): no pointer to push, and always a value. */ +/** Unpublishable types stored as scalars (process streams are file + * descriptors): no pointer to push, and always a value. */ function scalarRefusal(t: IrType): boolean { - return t.kind === "date" || t.kind === "procStream"; + return t.kind === "procStream"; } /** The Node.js implementation's refusal for a value of type `t`. */ diff --git a/packages/compiler/src/backend/llvm/receivers.ts b/packages/compiler/src/backend/llvm/receivers.ts index 5e1077ee5..2fe9dd66b 100644 --- a/packages/compiler/src/backend/llvm/receivers.ts +++ b/packages/compiler/src/backend/llvm/receivers.ts @@ -18,6 +18,7 @@ import { canStackUnion, emitStackUnion } from "./stack-unions.js"; import { emitDowncastGuard, emitUnionNarrowGuard } from "./narrow-guards.js"; import { emitScalarOptional, isScalarOptionalSource } from "./local-union-storage.js"; import { borrowableInputs } from "./borrowed-inputs.js"; +import { emitStackRecordKeyGet, scalarRecordKeyGet } from "./expr-records.js"; import { type WalkBorrowHost, analyzeWalks } from "./walk-borrows.js"; /** Read a receiver whose value is consumed immediately by a field or tag @@ -95,6 +96,8 @@ export function emitReadReceiver(emitter: LlEmitter, e: IrExpr): LlValue { const narrowed = narrow ? emitCheckedNarrowReceiver(emitter, narrow) : null; if (narrowed) return narrowed; if (canStackUnion(e, emitter.boxedUnionsById)) return emitStackUnion(emitter, e).value; + if (e.kind === "recordKeyGet" && scalarRecordKeyGet(emitter, e)) + return { name: emitStackRecordKeyGet(emitter, e), type: e.type }; if (e.kind === "unionNarrow") { const union = emitter.emitUnionProjection(e.value); emitUnionNarrowGuard(emitter, union.name, e.unionId, e.tag); @@ -410,6 +413,7 @@ export function isStackUnionSource(emitter: LlEmitter, e: IrExpr): boolean { emitter.optionalArrayReads.get(e) !== null || isScalarOptionalSource(e, emitter.unionSources) || emitter.scalarResultCall(e) || - emitter.inlineFieldGet(e) + emitter.inlineFieldGet(e) || + scalarRecordKeyGet(emitter, e) ); } diff --git a/packages/compiler/src/backend/llvm/reference-effects.ts b/packages/compiler/src/backend/llvm/reference-effects.ts index 6ebe1222b..43a9176d5 100644 --- a/packages/compiler/src/backend/llvm/reference-effects.ts +++ b/packages/compiler/src/backend/llvm/reference-effects.ts @@ -181,6 +181,9 @@ function expressionPreservesEdges( case "bytesIntrinsic": return ( e.method === "get" || + e.method === "indexOfElem" || + e.method === "lastIndexOfElem" || + e.method === "includesElem" || e.method === "length" || e.method === "byteLength" || e.method === "byteOffset" || diff --git a/packages/compiler/src/backend/llvm/shapes.test.ts b/packages/compiler/src/backend/llvm/shapes.test.ts index f7327a688..7af16ab83 100644 --- a/packages/compiler/src/backend/llvm/shapes.test.ts +++ b/packages/compiler/src/backend/llvm/shapes.test.ts @@ -14,6 +14,7 @@ import { type ShapeHost, vAdapters, } from "./shapes.js"; +import { mangleRecordRelease } from "../mangle.js"; import { NullableUnions } from "./nullable-unions.js"; import { InlineUnionFields } from "./inline-unions.js"; @@ -315,6 +316,42 @@ describe("emitted object releases", () => { root.indexOf("call void @scr_cyc_on_release(ptr %o)"), ); }); + + test("freezable records count their references without the frozen bit", () => { + const mod: IrModule = { + irVersion: 17, + sourceFile: "frozen.ts", + entry: "main", + functions: [], + records: [ + { id: "plain", fields: [{ name: "n", type: F64 }] }, + { id: "frozen", fields: [{ name: "n", type: F64 }], freezable: true }, + ], + }; + const body = (defs: string, name: string): string => + new RegExp(`^define internal void @${name}\\([^]*?^}`, "m").exec(defs)?.[0] ?? ""; + for (const [pointerBits, notFrozen, sharedBelow] of [ + [64, "-4611686018427387905", "-4611686018427387907"], + [32, "-1073741825", "-1073741827"], + ] as const) { + const { host } = declarationHost(pointerBits); + const defs = emitRecordShapes({ ...host, objectAudit: false }, mod).defs.join("\n"); + const S = pointerBits === 64 ? "i64" : "i32"; + const plain = body(defs, mangleRecordRelease("plain")); + expect(plain).toContain(`%shared = icmp ult ${S} %rcm2, -3`); + expect(plain).not.toContain("%refs"); + // The immortal word's count is ~FROZEN, which the compare sends to the + // immortal test, like counts 0 and 1. + const fast = body(defs, mangleRecordRelease("frozen")); + expect(fast).toContain(`%refs = and ${S} %rc, ${notFrozen}`); + expect(fast).toContain(`%rcm2 = sub ${S} %refs, 2`); + expect(fast).toContain(`%shared = icmp ult ${S} %rcm2, ${sharedBelow}`); + expect(fast).toContain(`%imm = icmp eq ${S} %rc, -1`); + const slow = body(defs, `${mangleRecordRelease("frozen")}_slow`); + expect(slow).toContain(`%refs = and ${S} %n, ${notFrozen}`); + expect(slow).toContain(`%dead = icmp eq ${S} %refs, 0`); + } + }); }); describe("live-object audit notes", () => { diff --git a/packages/compiler/src/backend/llvm/shapes.ts b/packages/compiler/src/backend/llvm/shapes.ts index d13f80a78..b3f5084d3 100644 --- a/packages/compiler/src/backend/llvm/shapes.ts +++ b/packages/compiler/src/backend/llvm/shapes.ts @@ -34,6 +34,7 @@ import { cycTypeDescriptor, emitObjectAlloc, emitObjectFree, + emitSegmentFree, CYC_APPEND, cycleAppendDecls, cycleAppendHelper, @@ -1099,6 +1100,7 @@ export function arrNewCall( elem.kind === "stats" || // fs.Stats snapshots: scr_stats_* adapters, no trace elem.kind === "dyn" || // native collection seeds/drains: traced REF elem.kind === "regex" || // RegExp values: scr_regex_* adapters, no trace (no refs inside) + elem.kind === "date" || // Date objects: scr_date_* adapters, no trace (no refs inside) (elem.kind === "array" && traceAdapter(host, elem) !== null); if (!useRef) { if (small) { @@ -1162,7 +1164,6 @@ const SCR_ARR_OPS_RELEASE_NOTHING = 2; export function boxNewCall(host: ShapeHost, t: IrType): string { const plain: Partial> = { f64: 0, - date: 0, procStream: 0, bool: 1, string: 2, @@ -1187,11 +1188,7 @@ export function boxNewCall(host: ShapeHost, t: IrType): string { /** Box accessor suffix (boxAccess): scalars unboxed, ref kinds pointers. */ export function boxAccess(t: IrType): "f64" | "bool" | "ref" { - return t.kind === "f64" || t.kind === "date" || t.kind === "procStream" - ? "f64" - : t.kind === "bool" - ? "bool" - : "ref"; + return t.kind === "f64" || t.kind === "procStream" ? "f64" : t.kind === "bool" ? "bool" : "ref"; } /* ── record shapes ────────────────────────────────────────────────────── */ @@ -1207,7 +1204,6 @@ export function llFieldType( if (POINTER_KINDS.has(t.kind)) return "ptr"; switch (t.kind) { case "f64": - case "date": case "procStream": return "double"; case "bool": @@ -1405,17 +1401,23 @@ export interface DirectDestroy { /** The release of child `value` (a loaded field, possibly NULL) inside a * teardown whose chain length is `%d`: the child's own release for a shared * or immortal reference, its teardown for the last one. Labels use `tag`; - * the lines end in the open block `${tag}.next`. */ + * the lines end in the open block `${tag}.next`. + * + * With `segment` (the child has the teardown's own type, in a segmented + * teardown; segmentPrologueLines), the child's teardown appends its blocks + * to the teardown's segment. */ export function directChildReleaseLines( host: ShapeHost, tag: string, value: string, target: DirectDestroy, comment: string, + segment = false, ): string[] { const S = host.sizeType; const C = target.count ?? S; - host.declare(`declare void @scr_rc_destroy(ptr, ptr)`); + if (segment) host.declare(`declare ptr @scr_rc_destroy_seg(ptr, ptr, ptr, ptr)`); + else host.declare(`declare void @scr_rc_destroy(ptr, ptr)`); const lines = [ ` %${tag}.nn = icmp ne ptr ${value}, null`, ` br i1 %${tag}.nn, label %${tag}.chk, label %${tag}.next`, @@ -1447,16 +1449,139 @@ export function directChildReleaseLines( ` br i1 %${tag}.short, label %${tag}.direct, label %${tag}.bounded`, `${tag}.direct:`, ` %${tag}.d = add i32 %d, 1`, - ` call void @${target.destroy}_d(ptr ${value}, i32 %${tag}.d)`, + ); + if (!segment) { + lines.push( + ` call void @${target.destroy}_d(ptr ${value}, i32 %${tag}.d)`, + ` br label %${tag}.next`, + `${tag}.bounded:`, + ` call void @scr_rc_destroy(ptr ${value}, ptr @${target.destroy})`, + ` br label %${tag}.next`, + `${tag}.next:`, + ); + return lines; + } + lines.push( + ` %${tag}.t = load ptr, ptr %sg.t`, + ` %${tag}.dt = call ptr @${target.destroy}_s(ptr ${value}, i32 %${tag}.d, ptr %${tag}.t)`, + ` store ptr %${tag}.dt, ptr %sg.t`, ` br label %${tag}.next`, `${tag}.bounded:`, - ` call void @scr_rc_destroy(ptr ${value}, ptr @${target.destroy})`, + ` %${tag}.bt = load ptr, ptr %sg.t`, + ` %${tag}.rt = call ptr @scr_rc_destroy_seg(ptr ${value}, ptr @${target.destroy}_sc, ptr @${target.destroy}, ptr %${tag}.bt)`, + ` store ptr %${tag}.rt, ptr %sg.t`, ` br label %${tag}.next`, `${tag}.next:`, ); return lines; } +/* Teardown segments. A typed teardown that frees each block onto its free + * list as it goes frees a tree in walk order, and the free lists, which are + * LIFO, hand the blocks to the next allocations in the reverse order: a tree + * built again in the same order as the last (each generation of a structure + * a loop rebuilds) is laid out mirrored, and every walk over it, the next + * teardown included, runs through memory backwards, which defeats the + * hardware prefetchers. The runtime frees large dead sets of the cycle + * collector in address order for the same reason (scr_mem_order_for_free). + * + * So the teardown of a type with children of its own type (the `_s` form, + * segmentedTeardown) keeps the blocks it frees in a segment, linked through + * their first words, which it builds in order through a tail pointer: the + * `_s` form takes the segment's last link (the first word of its last block, + * or the head slot of an empty segment) and returns the new one. A node + * first releases its own block (emitObjectFree, keeping it), then appends it + * ahead of its children's blocks when its address is the lower, behind them + * otherwise (segmentTeardownLines), and the children append theirs in field + * order, so a structure + * built parent first (object literals allocate before their fields) or + * children first (constructor arguments) comes back in ascending order. The + * chain's first teardown (`_d`) frees the whole segment onto the class's + * free list in one step (emitSegmentFree), and the walks over the next + * generation run forward. A teardown past DIRECT_DESTROY_CHAIN appends + * through scr_rc_destroy_seg, under the same depth budget as + * scr_rc_destroy. Blocks outside the calling thread's arena are freed as + * before; one type's blocks are all of one class. */ + +/** Whether the teardown of a type whose reference fields have `fieldTypes` + * (with direct targets `targets`) and whose own teardown is `destroy` keeps + * a segment: a child field has its own type, and the inline allocation paths + * are in use. */ +export function segmentedTeardown( + host: ShapeHost, + destroy: string, + fieldTypes: readonly IrType[], + targets: ReadonlyMap, +): boolean { + if (host.inlineAlloc !== true) return false; + return fieldTypes.some((t) => directDestroyTarget(host, t, targets)?.destroy === destroy); +} + +/** The first lines of a segmented teardown, in its entry block: the + * segment's last link (`%sg.t`, from the `%tail` argument) and the kept own + * block (`%sg.own`, for emitObjectFree). */ +export function segmentPrologueLines(): string[] { + return [ + ` %sg.t = alloca ptr`, + ` %sg.own = alloca ptr`, + ` store ptr %tail, ptr %sg.t`, + ` store ptr null, ptr %sg.own`, + ]; +} + +/** The rest of a segmented teardown after emitObjectFree kept (or freed) + * its own block: the block goes ahead of its children's when its address is + * below the first non-NULL one of `children` (loaded fields of the + * teardown's own type, which stand for their blocks) or there is none, and + * behind them otherwise; then the children's releases (`releases`, given a + * label prefix), and the segment's last link is returned. Each order has its + * own copy of the releases, so neither keeps the decision, or the block once + * it is appended, across the children's teardowns. */ +export function segmentTeardownLines( + children: readonly string[], + releases: (prefix: string) => string[], +): string[] { + const lines = [` %sg.o = load ptr, ptr %sg.own`, ` %sg.kept = icmp ne ptr %sg.o, null`]; + let ref = "null"; + [...children].reverse().forEach((child, i) => { + lines.push( + ` %sg.nn${i} = icmp ne ptr ${child}, null`, + ` %sg.r${i} = select i1 %sg.nn${i}, ptr ${child}, ptr ${ref}`, + ); + ref = `%sg.r${i}`; + }); + lines.push( + ` %sg.leaf = icmp eq ptr ${ref}, null`, + ` %sg.lower = icmp ult ptr %sg.o, ${ref}`, + ` %sg.first = or i1 %sg.leaf, %sg.lower`, + ` %sg.ahead = and i1 %sg.kept, %sg.first`, + ` br i1 %sg.ahead, label %sg.front, label %sg.back`, + `sg.front:`, + ...segmentAppendLines("a"), + ...releases("ta"), + ` br label %sg.ret`, + `sg.back:`, + ...releases("tb"), + ` %sg.keep = icmp ne ptr %sg.o, null`, + ` br i1 %sg.keep, label %sg.last, label %sg.ret`, + `sg.last:`, + ...segmentAppendLines("l"), + ` br label %sg.ret`, + `sg.ret:`, + ` %sg.tr = load ptr, ptr %sg.t`, + ` ret ptr %sg.tr`, + ); + return lines; +} + +function segmentAppendLines(tag: string): string[] { + return [ + ` %sg.t${tag} = load ptr, ptr %sg.t`, + ` store ptr %sg.o, ptr %sg.t${tag}`, + ` store ptr %sg.o, ptr %sg.t`, + ]; +} + /** The direct-teardown target of a reference of `type`, if `targets` (keyed * `record:` / `object:`) has one. Nullable unions hold the arm's * pointer, NULL or an immortal sentinel, which the child release handles. */ @@ -1499,12 +1624,42 @@ export function usedDirectTargets( /** The teardown definitions for `destroy`: with `chained`, the body takes * the chain length `%d` (directChildReleaseLines) and the runtime's - * one-argument entry starts a chain. */ + * one-argument entry starts a chain. With `segment` (the host, for a + * segmented teardown), the body is a segment teardown's + * (segmentPrologueLines to segmentTeardownLines), defined as `_s`, with + * `_sc` for scr_rc_destroy_seg, and `_d` starts a segment and frees it. */ export function destroyDefinitions( destroy: string, body: readonly string[], chained: boolean, + segment: ShapeHost | null = null, ): string[] { + if (segment !== null) + return [ + `define internal ptr @${destroy}_s(ptr %o, i32 %d, ptr %tail) ${FN_ATTRS} {`, + `entry:`, + ...body, + `}`, + `define internal ptr @${destroy}_sc(ptr %o, ptr %tail) ${FN_ATTRS} {`, + `entry:`, + ` %t = call ptr @${destroy}_s(ptr %o, i32 0, ptr %tail)`, + ` ret ptr %t`, + `}`, + `define internal void @${destroy}_d(ptr %o, i32 %d) ${FN_ATTRS} {`, + `entry:`, + ` %head = alloca ptr`, + ` store ptr null, ptr %head`, + ` %last = call ptr @${destroy}_s(ptr %o, i32 %d, ptr %head)`, + ` %first = load ptr, ptr %head`, + ...emitSegmentFree(segment, "sgf", "%first", "%last"), + ` ret void`, + `}`, + `define internal void @${destroy}(ptr %o) ${FN_ATTRS} {`, + `entry:`, + ` call void @${destroy}_d(ptr %o, i32 0)`, + ` ret void`, + `}`, + ]; if (!chained) return [ `define internal void @${destroy}(ptr %o) ${FN_ATTRS} {`, @@ -1527,6 +1682,17 @@ export function destroyDefinitions( ]; } +/** The frozen bit of a freezable record's rc word (scr_rc.h SCR_RC_FROZEN): + * bit 62 on 64-bit targets, bit 30 on 32-bit ones. */ +function recordFrozenBit(sizeType: RcCount): bigint { + return 1n << (sizeType === "i64" ? 62n : 30n); +} + +/** `value` as a signed LLVM integer literal of `sizeType`. */ +function sizeLiteral(sizeType: RcCount, value: bigint): string { + return BigInt.asIntN(sizeType === "i64" ? 64 : 32, value).toString(); +} + /** Common NULL/immortal/decrement skeleton for ordinary object releases. * `freeBody` owns the zero-ref teardown and must leave the current block at * its end; traced objects also get the possible-cycle-root branch. @@ -1543,13 +1709,17 @@ export function releaseBody( bounded = true, immortal = false, chained = false, + segmented = false, + frozen = false, ): string[] { const S = host.sizeType; if (bounded) host.declare(`declare void @scr_rc_destroy(ptr, ptr)`); const destroy = `${fnName}_destroy`; + // A freezable record's count is the word without its frozen bit. + const notFrozen = sizeLiteral(S, ~recordFrozenBit(S)); const lines = [ - ...(bounded ? destroyDefinitions(destroy, freeBody, chained) : []), - ...releaseFastPath(host, fnName, `${fnName}_slow`, traced, comment, immortal), + ...(bounded ? destroyDefinitions(destroy, freeBody, chained, segmented ? host : null) : []), + ...releaseFastPath(host, fnName, `${fnName}_slow`, traced, comment, immortal, S, frozen), `define internal void @${fnName}_slow(ptr %o) noinline ${FN_ATTRS} {${comment ? ` ; ${comment}` : ""}`, `entry:`, ` %isnull = icmp eq ptr %o, null`, @@ -1561,7 +1731,9 @@ export function releaseBody( `dec:`, ` %n = sub ${S} %rc, 1`, ` store ${S} %n, ptr %o`, - ` %dead = icmp eq ${S} %n, 0`, + ...(frozen + ? [` %refs = and ${S} %n, ${notFrozen}`, ` %dead = icmp eq ${S} %refs, 0`] + : [` %dead = icmp eq ${S} %n, 0`]), ` br i1 %dead, label %free, label %${traced ? "root" : "done"}`, `free:`, ...(traced ? cycleDeadLines(host, "teardown") : []), @@ -1591,9 +1763,14 @@ export function releaseFastPath( comment = "", immortal = false, count: RcCount = host.sizeType, + frozen = false, ): string[] { const S = count; if (immortal) return immortalRcBody(host, fnName, "void", comment); + // A freezable record compares its count without the frozen bit: an + // immortal word's count is then ~FROZEN, which the same compare sends to + // `slow`. + const notFrozen = ~recordFrozenBit(S); return [ `define internal void @${fnName}(ptr %o) ${FN_ATTRS} {${comment ? ` ; ${comment} (fast path)` : ""}`, `entry:`, @@ -1601,8 +1778,13 @@ export function releaseFastPath( ` br i1 %isnull, label %done, label %check`, `check:`, ` %rc = load ${S}, ptr %o`, - ` %rcm2 = sub ${S} %rc, 2`, - ` %shared = icmp ult ${S} %rcm2, -3`, + ...(frozen + ? [ + ` %refs = and ${S} %rc, ${sizeLiteral(S, notFrozen)}`, + ` %rcm2 = sub ${S} %refs, 2`, + ` %shared = icmp ult ${S} %rcm2, ${sizeLiteral(S, notFrozen - 2n)}`, + ] + : [` %rcm2 = sub ${S} %rc, 2`, ` %shared = icmp ult ${S} %rcm2, -3`]), ` br i1 %shared, label %dec, label %slow`, `dec:`, ` %n = sub ${S} %rc, 1`, @@ -1780,26 +1962,58 @@ export function emitRecordShapes( const chained = directTargets.has(`record:${shape.id}`) || refMembers.some((m) => directDestroyTarget(host, m.type, directTargets) !== null); - for (const m of refMembers) { - freeBody.push( - ` %f${t} = getelementptr inbounds %${struct}, ptr %o, i64 0, i32 ${m.index}`, - ` %v${t} = load ptr, ptr %f${t}`, + const destroy = `${mangleRecordRelease(shape.id)}_destroy`; + const bounded = refMembers.some((member) => needsBoundedRelease(member.type)); + const segmented = + bounded && + segmentedTeardown( + host, + destroy, + refMembers.map((m) => m.type), + directTargets, ); - const direct = directDestroyTarget(host, m.type, directTargets); - if (direct) { - freeBody.push( - ...directChildReleaseLines(host, `td${t}`, `%v${t}`, direct, llvmCommentText(m.name)), - ); - } else { - freeBody.push( - ` call void ${releaseSym(host, m.type)}(ptr %v${t}) ; ${llvmCommentText(m.name)}`, - ); - } - t++; - } // Closed layouts can carry a kept record source (record-sources.ts). const observers = recordMayKeepSource(shape) ? "weak-or-source" : "weak"; - freeBody.push(...emitObjectFree(host, traced, observers)); + // A segmented teardown loads its fields, releases its own block, and + // then releases the fields (segmentTeardownLines). + const loads: string[] = []; + const ownType: string[] = []; + const release = (prefix: string, m: (typeof refMembers)[number], i: number): string[] => { + const direct = directDestroyTarget(host, m.type, directTargets); + if (direct === null) + return [` call void ${releaseSym(host, m.type)}(ptr %v${i}) ; ${llvmCommentText(m.name)}`]; + const own = segmented && direct.destroy === destroy; + return directChildReleaseLines( + host, + `${prefix}${i}`, + `%v${i}`, + direct, + llvmCommentText(m.name), + own, + ); + }; + for (const m of refMembers) { + const fieldLines = [ + ` %f${t} = getelementptr inbounds %${struct}, ptr %o, i64 0, i32 ${m.index}`, + ` %v${t} = load ptr, ptr %f${t}`, + ]; + if (segmented) { + loads.push(...fieldLines); + if (directDestroyTarget(host, m.type, directTargets)?.destroy === destroy) + ownType.push(`%v${t}`); + } else freeBody.push(...fieldLines, ...release("td", m, t)); + t++; + } + if (segmented) + freeBody.push( + ...segmentPrologueLines(), + ...loads, + ...emitObjectFree(host, traced, observers, "%sg.own"), + ...segmentTeardownLines(ownType, (prefix) => + refMembers.flatMap((m, i) => release(prefix, m, i)), + ), + ); + else freeBody.push(...emitObjectFree(host, traced, observers)); defs.push( ...releaseBody( host, @@ -1807,9 +2021,11 @@ export function emitRecordShapes( traced, freeBody, "", - refMembers.some((member) => needsBoundedRelease(member.type)), + bounded, false, chained, + segmented, + shape.freezable === true, ), ``, ); diff --git a/packages/compiler/src/backend/llvm/stack-captures.ts b/packages/compiler/src/backend/llvm/stack-captures.ts index f0812b7b0..40e1a0428 100644 --- a/packages/compiler/src/backend/llvm/stack-captures.ts +++ b/packages/compiler/src/backend/llvm/stack-captures.ts @@ -105,13 +105,7 @@ export class StackCaptures { // sites, including duplicated finally bodies and versioned loops. if (!this.locals.has(id) || this.remaining < 48) return null; const ref = isRefCounted(type); - if ( - !ref && - type.kind !== "f64" && - type.kind !== "date" && - type.kind !== "procStream" && - type.kind !== "bool" - ) + if (!ref && type.kind !== "f64" && type.kind !== "procStream" && type.kind !== "bool") return null; this.remaining -= 48; const B = host.B; diff --git a/packages/compiler/src/backend/llvm/stack-objects.ts b/packages/compiler/src/backend/llvm/stack-objects.ts index c7a58a7ea..ae2b904db 100644 --- a/packages/compiler/src/backend/llvm/stack-objects.ts +++ b/packages/compiler/src/backend/llvm/stack-objects.ts @@ -44,8 +44,9 @@ export type StackObjectFieldOwner = (fields: readonly StackObjectField[]) => voi * standalone scalar class whose constructor never lets `this` escape * (record-results.ts). The caller proves that every use of the value only * projects it. Record fields may own references; the frame releases them - * (stackObjectFields). Published shapes keep heap storage: their field - * guards treat the immortal header as a frozen object. */ + * (stackObjectFields). Published and freezable shapes keep heap storage: + * their field guards treat the immortal header as a frozen object, and a + * frozen record keeps its frozen bit in a counted header. */ export function canStackObject(host: LlvmEmitterContext, e: IrExpr): e is StackObjectSource { // A ThreadSlots key keeps its slot index for its lifetime and has its // values released when it is freed: it lives on the heap. @@ -53,6 +54,8 @@ export function canStackObject(host: LlvmEmitterContext, e: IrExpr): e is StackO return false; if (e.type.kind === "record" && host.publishedTypes?.recordGuarded(e.type.shapeId) === true) return false; + if (e.type.kind === "record" && host.recordsById.get(e.type.shapeId)?.freezable === true) + return false; // A producer with a result-slot entry builds its fresh result in the // caller's storage. if (e.kind === "call") return host.fnByName.has(resultEntryName(e.callee)); diff --git a/packages/compiler/src/backend/llvm/union-repr.ts b/packages/compiler/src/backend/llvm/union-repr.ts index 959f9e51e..8c6fb4c85 100644 --- a/packages/compiler/src/backend/llvm/union-repr.ts +++ b/packages/compiler/src/backend/llvm/union-repr.ts @@ -8,8 +8,14 @@ import type { BlockBuilder } from "./blocks.js"; import { NULLABLE_NULL, shapeTagFn, type PointerUnion } from "./nullable-unions.js"; -/** The i32 tag of a union value. */ -export function emitUnionTag(B: BlockBuilder, nullable: PointerUnion | null, u: string): string { +/** The i32 tag of a union value. `alias` is the tag word's alias + * attachment (", !tbaa !N"), when the caller has one. */ +export function emitUnionTag( + B: BlockBuilder, + nullable: PointerUnion | null, + u: string, + alias = "", +): string { const t = B.tmp(); if (nullable?.repr === "shapes") { B.line(`${t} = call i32 ${shapeTagFn(nullable.unionId)}(ptr ${u})`); @@ -31,7 +37,7 @@ export function emitUnionTag(B: BlockBuilder, nullable: PointerUnion | null, u: } const p = B.tmp(); B.line(`${p} = getelementptr inbounds %ScrUnion, ptr ${u}, i64 0, i32 1`); - B.line(`${t} = load i32, ptr ${p}`); + B.line(`${t} = load i32, ptr ${p}${alias}`); return t; } @@ -81,12 +87,17 @@ export function emitNullableIsTag( } /** The BORROWED payload pointer of a reference arm. */ -export function emitUnionPeek(B: BlockBuilder, nullable: PointerUnion | null, u: string): string { +export function emitUnionPeek( + B: BlockBuilder, + nullable: PointerUnion | null, + u: string, + alias = "", +): string { if (nullable) return u; const p = B.tmp(); const t = B.tmp(); B.line(`${p} = getelementptr inbounds %ScrUnion, ptr ${u}, i64 0, i32 5`); - B.line(`${t} = load ptr, ptr ${p}`); + B.line(`${t} = load ptr, ptr ${p}${alias}`); return t; } diff --git a/packages/compiler/src/backend/llvm/walkers.ts b/packages/compiler/src/backend/llvm/walkers.ts index 331ea2199..58df3e845 100644 --- a/packages/compiler/src/backend/llvm/walkers.ts +++ b/packages/compiler/src/backend/llvm/walkers.ts @@ -234,6 +234,10 @@ export class LlWalkers { this.host.declare(`declare void @scr_jb_put_json_str(ptr, ptr)`); B.line(`call void @scr_jb_put_json_str(ptr %b, ptr %v)`); break; + case "date": + this.host.declare(`declare void @scr_jb_put_date(ptr, ptr)`); + B.line(`call void @scr_jb_put_date(ptr %b, ptr %v) ; toJSON(): ISO text, null if invalid`); + break; case "dyn": // A checked-dynamic value serializes itself (runtime walker). // Bare stringify of dyn stays frontend-fenced, and overflow @@ -927,6 +931,17 @@ export class LlWalkers { case "string": this.putScrStr(B, buf, this.unionPeek(B, u, unionId)); break; + case "date": { + host.declare(`declare ptr @scr_date_object_to_string(ptr)`); + host.declare(`declare void @scr_str_release(ptr)`); + const text = B.tmp(); + B.line( + `${text} = call ptr @scr_date_object_to_string(ptr ${this.unionPeek(B, u, unionId)})`, + ); + this.putScrStr(B, buf, text); + B.line(`call void @scr_str_release(ptr ${text})`); + break; + } case "f64": { host.declare(`declare double @scr_union_get_f64(ptr)`); host.declare(`declare void @scr_jb_put_number(ptr, double)`); diff --git a/packages/compiler/src/backend/native-codegen-core.ts b/packages/compiler/src/backend/native-codegen-core.ts index 5b9437cde..dd17466eb 100644 --- a/packages/compiler/src/backend/native-codegen-core.ts +++ b/packages/compiler/src/backend/native-codegen-core.ts @@ -6,19 +6,29 @@ export const NATIVE_CODEGEN_LLVM_VERSION = "22.1.8"; export type NativeCodegenOutputKind = "asm" | "obj"; -/** LLVM text per partition, and the most partitions one executable uses. */ +/** LLVM text that stays one module, LLVM text per partition, the fewest + * partitions (MIN_PROGRAM_PARTITIONS, of at least MIN_PARTITION_BYTES each) + * a divided module gets, and the most one executable uses. */ +const SINGLE_PARTITION_BYTES = 1024 * 1024; const PROGRAM_PARTITION_BYTES = 4 * 1024 * 1024; +const MIN_PARTITION_BYTES = 512 * 1024; +const MIN_PROGRAM_PARTITIONS = 8; const MAX_PROGRAM_PARTITIONS = 64; -/** Executable objects this large are divided into partitions that the helper - * simplifies, optimizes and compiles concurrently, importing small functions - * across partitions for inlining, and reuses individually across builds. The +/** Executable objects larger than SINGLE_PARTITION_BYTES are divided into + * partitions that the helper simplifies, optimizes and compiles concurrently, + * importing small functions across partitions for inlining, and reuses + * individually across builds. A divided module gets at least + * MIN_PROGRAM_PARTITIONS so that mid-size programs use several cores. The * count is a power of two that depends only on the module's size, on every * target, so artifacts never vary with the host and ordinary edits keep every * function in its partition. */ export function nativeProgramPartitions(llvmBytes: number): number { - const required = Math.ceil(llvmBytes / PROGRAM_PARTITION_BYTES); - if (required < 2) return 1; + if (llvmBytes <= SINGLE_PARTITION_BYTES) return 1; + const required = Math.max( + Math.ceil(llvmBytes / PROGRAM_PARTITION_BYTES), + Math.min(MIN_PROGRAM_PARTITIONS, Math.ceil(llvmBytes / MIN_PARTITION_BYTES)), + ); return Math.min(MAX_PROGRAM_PARTITIONS, 2 ** Math.ceil(Math.log2(required))); } diff --git a/packages/compiler/src/backend/native-codegen.test.ts b/packages/compiler/src/backend/native-codegen.test.ts index 545711a60..5463b868e 100644 --- a/packages/compiler/src/backend/native-codegen.test.ts +++ b/packages/compiler/src/backend/native-codegen.test.ts @@ -352,11 +352,13 @@ test("sanitized native artifacts fail before helper resolution", async () => { test("program partitions depend only on module size", () => { const megabyte = 1024 * 1024; - expect(nativeProgramPartitions(4 * megabyte)).toBe(1); - expect(nativeProgramPartitions(4 * megabyte + 1)).toBe(2); + expect(nativeProgramPartitions(megabyte)).toBe(1); + expect(nativeProgramPartitions(megabyte + 1)).toBe(4); + expect(nativeProgramPartitions(2 * megabyte)).toBe(4); + expect(nativeProgramPartitions(2 * megabyte + 1)).toBe(8); + expect(nativeProgramPartitions(32 * megabyte)).toBe(8); // Powers of two keep every function in its partition across small edits. - expect(nativeProgramPartitions(13 * megabyte)).toBe(4); - expect(nativeProgramPartitions(16 * megabyte)).toBe(4); + expect(nativeProgramPartitions(32 * megabyte + 1)).toBe(16); expect(nativeProgramPartitions(100 * megabyte)).toBe(32); expect(nativeProgramPartitions(1024 * megabyte)).toBe(64); expect(nativePartitionPaths("/build.d/program.o", 3)).toEqual([ diff --git a/packages/compiler/src/backend/native-link-info-core.ts b/packages/compiler/src/backend/native-link-info-core.ts index e018037fd..1e9994422 100644 --- a/packages/compiler/src/backend/native-link-info-core.ts +++ b/packages/compiler/src/backend/native-link-info-core.ts @@ -35,6 +35,8 @@ export interface NativeLinkFeatures { * or stdio 'resize' events: links the SCR_STDIO_EXTENDED events unit. */ stdioExtended?: boolean; zlib: boolean; + /** node:zlib's Brotli forms: scr_brotli.c and the vendored brotli archive. */ + brotli?: boolean; assert: boolean; inspect: boolean; dynInvoke: boolean; @@ -81,6 +83,9 @@ export interface NativeLinkFeatures { dnsPromises: boolean; /** dgram sockets as untyped values: scr_dgram_dyn.c. */ dgramDyn: boolean; + /** TextEncoder and TextDecoder records as untyped values: + * scr_text_codec_dyn.c. */ + textCodecDyn: boolean; watch: boolean; foreignFfi: boolean; nodeTest: boolean; diff --git a/packages/compiler/src/backend/native/contracts.ts b/packages/compiler/src/backend/native/contracts.ts index 61de82e72..40bb2352b 100644 --- a/packages/compiler/src/backend/native/contracts.ts +++ b/packages/compiler/src/backend/native/contracts.ts @@ -85,6 +85,9 @@ export interface CcOptions { * compares round-trips and fixed-blob inflation, never raw deflate * output. */ zlib?: boolean; + /** The program uses node:zlib's Brotli forms (with zlib): compiles + * scr_brotli.c and links the vendored brotli archive. */ + brotli?: boolean; /** The program uses node:assert (index.ts detects assert.* libCalls on * the IR): compiles scr_assert.c — the zlib gating precedent, so * assert-free binaries keep their exact size class. scr_regex.c calls @@ -220,6 +223,10 @@ export interface CcOptions { * scr_dgram_dyn.c over the dgram unit, so typed dgram binaries keep * their exact link line. */ dgramDyn?: boolean; + /** A TextEncoder or TextDecoder record converts to or from an untyped + * value: compiles scr_text_codec_dyn.c, so typed codec binaries keep + * their exact link line. */ + textCodecDyn?: boolean; /** The program uses fs.watch (moduleUsesFsWatch on the IR): compiles * scr_watch.c into the binary — the net gating precedent, so watch-free diff --git a/packages/compiler/src/backend/native/executable.ts b/packages/compiler/src/backend/native/executable.ts index 233b9cac2..ceb162bcc 100644 --- a/packages/compiler/src/backend/native/executable.ts +++ b/packages/compiler/src/backend/native/executable.ts @@ -82,15 +82,18 @@ import { currentVendorCacheBuildIdentity, vendorBuildCacheRoot, tlsArchivePath, + brotliArchivePath, lreObjectPaths, zlibObjectPaths, stageVendorInputs, ensureTlsArchive, + ensureBrotliArchive, ensureLreObjects, ensureZlibObjects, vendorEngineDir, vendorZlibDir, vendorTlsDir, + vendorBrotliDir, } from "./vendor.js"; import { execFileAsync, subprocessFailureDetail, CcCompileError } from "./process.js"; import { implicitLinkerFingerprint } from "./linker-fingerprint.js"; @@ -119,6 +122,7 @@ interface RuntimeUnitContext { lreObjects: readonly string[]; zlibObjects: readonly string[]; tlsArchive: string | null; + brotliArchive: string | null; } /** An optional runtime input: runtime sources compiled when `when` holds, @@ -222,6 +226,18 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [ : [] : [], ), + // Brotli (scr_brotli.c) plugs into the zlib engine and links the + // vendored brotli archive. + unitArgs((u) => + u.brotliArchive + ? [ + "-I", + join(vendorBrotliDir(), "include"), + u.rt(join(u.rtDir, "scr_brotli.c")), + u.brotliArchive, + ] + : [], + ), unit((u) => u.opts.events, "scr_events.c", "scr_readline.c"), // readline's terminal mode: rl.terminal sets the regex unit it // measures with. @@ -302,6 +318,7 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [ unit((u) => u.opts.dgram, "scr_dgram.c"), unit((u) => u.opts.dgram && u.opts.dnsPromises, "scr_dns_promises.c"), unit((u) => u.opts.dgram && u.opts.dgramDyn, "scr_dgram_dyn.c"), + unit((u) => u.opts.textCodecDyn, "scr_text_codec_dyn.c"), unit((u) => u.opts.watch, "scr_watch.c"), unit((u) => u.opts.foreignFfi, "scr_ffi_queue.c"), unit((u) => u.opts.nodeTest, "scr_test.c"), @@ -397,6 +414,7 @@ export async function compileCInternal( const dh = opts.dh ?? false; const pki = opts.pki ?? false; const mbedtls = tls || cipher || ecdh || dh || pki; + const brotli = (opts.zlib ?? false) && opts.brotli === true; const driver = resolveCc(); const darwinDebugSymbols = needsDarwinDebugSymbols( targetPlatform(driver), @@ -636,6 +654,9 @@ export async function compileCInternal( let tlsArchive = mbedtls ? tlsArchivePath(sanitize, driver, vendorBuildIdentity, vendorCacheRoot) : null; + let brotliArchive = brotli + ? brotliArchivePath(sanitize, driver, vendorBuildIdentity, vendorCacheRoot) + : null; // ours would collide; see scr_regex.c) — the standalone objects are for // static builds only. @@ -673,6 +694,23 @@ export async function compileCInternal( : await stageVendorInputs(materialize, join(stageRoot, "tls")); tlsArchive = paths[0]!; } + if (brotli) { + const cachedArchive = brotliArchivePath( + sanitize, + driver, + vendorBuildIdentity, + materializeCacheRoot, + ); + protectCachedArtifact(cacheWarmPaths, cachedArchive); + const materialize = async (): Promise => [ + await ensureBrotliArchive(sanitize, driver, vendorBuildIdentity, materializeCacheRoot), + ]; + const paths = + stageRoot === undefined + ? await materialize() + : await stageVendorInputs(materialize, join(stageRoot, "brotli")); + brotliArchive = paths[0]!; + } if (regex) { for (const object of lreObjectPaths( sanitize, @@ -759,6 +797,7 @@ export async function compileCInternal( lreObjects, zlibObjects, tlsArchive, + brotliArchive, }), // A .ll program TU (the LLVM backend) deliberately carries no target // triple (byte-stable output; clang supplies the host/SCRIPTC_TARGET diff --git a/packages/compiler/src/backend/native/runtime-inputs.ts b/packages/compiler/src/backend/native/runtime-inputs.ts index 6fb51fd32..c7c5bc32b 100644 --- a/packages/compiler/src/backend/native/runtime-inputs.ts +++ b/packages/compiler/src/backend/native/runtime-inputs.ts @@ -3,7 +3,7 @@ import { createRequire } from "node:module"; import { readdir, readFile, realpath, stat } from "node:fs/promises"; import { basename, dirname, extname, join, relative, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { MBEDTLS_VERSION, QJS_COMMIT, ZLIB_VERSION } from "../vendor-inputs.js"; +import { BROTLI_VERSION, MBEDTLS_VERSION, QJS_COMMIT, ZLIB_VERSION } from "../vendor-inputs.js"; import { stableTestMemo } from "./session.js"; const implementationPath = fileURLToPath(import.meta.url); @@ -134,6 +134,7 @@ async function runtimeFingerprintFresh(rtDir: string): Promise { .update(QJS_COMMIT) .update(MBEDTLS_VERSION) .update(ZLIB_VERSION) + .update(BROTLI_VERSION) .update("\0backend-recipe\0"); for (const path of NATIVE_RECIPE_IMPLEMENTATION_PATHS) { h.update(basename(path)) @@ -164,6 +165,7 @@ async function runtimeFingerprintInputGroups( { label: "quickjs-ng", dir: join(rtDir, "..", "vendor", "quickjs-ng") }, { label: "mbedtls", dir: join(rtDir, "..", "vendor", "mbedtls") }, { label: "zlib", dir: join(rtDir, "..", "vendor", "zlib") }, + { label: "brotli", dir: join(rtDir, "..", "vendor", "brotli", "c") }, { label: "curl", dir: join(rtDir, "..", "vendor", "curl") }, ].map(async (group) => { const names = (await nativeSourceFiles(group.dir, true)) diff --git a/packages/compiler/src/backend/native/vendor.ts b/packages/compiler/src/backend/native/vendor.ts index cf7b3a68d..b6084d5c4 100644 --- a/packages/compiler/src/backend/native/vendor.ts +++ b/packages/compiler/src/backend/native/vendor.ts @@ -6,6 +6,9 @@ import { resolvedToolIdentity } from "./tool-identity.js"; export const { vendorEngineDir, vendorTlsDir, + vendorBrotliDir, + brotliArchivePath, + ensureBrotliArchive, vendorBuildCacheRoot, vendorCacheTargetFlavor, vendorCacheBuildIdentity, diff --git a/packages/compiler/src/backend/publication.test.ts b/packages/compiler/src/backend/publication.test.ts index 149ea4bb9..7b3e5f5cc 100644 --- a/packages/compiler/src/backend/publication.test.ts +++ b/packages/compiler/src/backend/publication.test.ts @@ -85,3 +85,34 @@ test("published types close over fields, containers and whole hierarchies", () = computePublishedTypes({ ...withStores, functions: withStores.functions.slice(1) }), ).toBeNull(); }); + +test("freezable array types carry the array write guard without a publish", () => { + const numbers = arrayOf(F64); + const push: IrStmt = { + kind: "exprStmt", + expr: { + kind: "arrIntrinsic", + method: "push", + receiver: ref("a", numbers), + args: [{ kind: "numLit", value: 1, type: F64, loc }], + type: F64, + loc, + }, + loc, + }; + const mod: IrModule = { + irVersion: 17, + sourceFile: loc.file, + entry: "main", + functions: [fn("main", []), fn("pushNumber", [push])], + freezableArrays: ["array"], + }; + const guarded = computePublishedTypes(mod)!; + expect(guarded.containerGuarded(numbers)).toBe(true); + expect(guarded.arrayGuards(numbers)).toEqual({ published: false, frozen: true }); + expect(guarded.containerGuarded(arrayOf(STRING))).toBe(false); + // A frozen array's push throws, so the function may throw. + expect(computeMayThrow(mod).fns.has("pushNumber")).toBe(true); + const { freezableArrays: _frozen, ...plain } = mod; + expect(computePublishedTypes(plain)).toBeNull(); +}); diff --git a/packages/compiler/src/backend/publication.ts b/packages/compiler/src/backend/publication.ts index 473a0cba4..38e429a47 100644 --- a/packages/compiler/src/backend/publication.ts +++ b/packages/compiler/src/backend/publication.ts @@ -46,6 +46,10 @@ export class PublishedTypes { readonly records = new Set(); /** typeKeys of array, map and set types. */ readonly containers = new Set(); + /** typeKeys of the array types the program may freeze + * (IrModule.freezableArrays): a frozen array refuses writes like a + * published one, so their stores carry the same guard. */ + readonly frozenArrays = new Set(); /** Constructor stores into a `this` nothing else can reach yet * (immutability.ts): never a published target, so never guarded. */ privateStores: ReadonlySet = new Set(); @@ -64,10 +68,16 @@ export class PublishedTypes { } containerGuarded(t: IrType): boolean { - return ( - (t.kind === "array" || t.kind === "map" || t.kind === "set") && - this.containers.has(typeKey(t)) - ); + if (t.kind !== "array" && t.kind !== "map" && t.kind !== "set") return false; + const key = typeKey(t); + return this.containers.has(key) || this.frozenArrays.has(key); + } + + /** Whether an array of type `t` may be published, and whether it may be + * frozen: the two states its write guard tests. */ + arrayGuards(t: IrType): { published: boolean; frozen: boolean } { + const key = typeKey(t); + return { published: this.containers.has(key), frozen: this.frozenArrays.has(key) }; } } @@ -87,7 +97,13 @@ export function computePublishedTypes(mod: IrModule, nodes?: IrNodeIndex): Publi ) roots.push(node.type); }); - if (roots.length === 0) return null; + const frozenArrays = mod.freezableArrays ?? []; + if (roots.length === 0 && frozenArrays.length === 0) return null; + if (roots.length === 0) { + const frozen = new PublishedTypes(); + for (const key of frozenArrays) frozen.frozenArrays.add(key); + return frozen; + } const classes = new Map((mod.classes ?? []).map((c) => [c.name, c])); const children = new Map(); @@ -100,6 +116,7 @@ export function computePublishedTypes(mod: IrModule, nodes?: IrNodeIndex): Publi const records = new Map((mod.records ?? []).map((r) => [r.id, r])); const unions = new Map((mod.unions ?? []).map((u) => [u.id, u])); const out = new PublishedTypes(); + for (const key of frozenArrays) out.frozenArrays.add(key); out.privateStores = computeConstructionFacts(mod, nodes).privateStores; const seen = new Set(); const stack = [...roots]; diff --git a/packages/compiler/src/backend/runtime-pack-core.ts b/packages/compiler/src/backend/runtime-pack-core.ts index 75953be59..49165d8df 100644 --- a/packages/compiler/src/backend/runtime-pack-core.ts +++ b/packages/compiler/src/backend/runtime-pack-core.ts @@ -34,7 +34,7 @@ interface RuntimePackUnit { } interface RuntimePackArchive extends RuntimePackArtifact { - id: "quickjs" | "libregexp" | "zlib" | "mbedtls"; + id: "quickjs" | "libregexp" | "zlib" | "brotli" | "mbedtls"; predicate: RuntimePredicate; } @@ -93,6 +93,7 @@ export interface RuntimeFeatureSet extends NativeLinkFeatures { builtinValues: boolean; warningReplace: boolean; stdioExtended: boolean; + brotli: boolean; nativeFetch: boolean; netEffective: boolean; httpEffective: boolean; @@ -291,6 +292,7 @@ export function effectiveRuntimeFeatures( builtinValues: features.builtinValues === true, warningReplace: features.warningReplace === true, stdioExtended: features.stdioExtended === true, + brotli: features.brotli === true, nativeFetch, netEffective, httpEffective, diff --git a/packages/compiler/src/backend/runtime-pack-targets.test.ts b/packages/compiler/src/backend/runtime-pack-targets.test.ts index 44c3d6290..9846e8d89 100644 --- a/packages/compiler/src/backend/runtime-pack-targets.test.ts +++ b/packages/compiler/src/backend/runtime-pack-targets.test.ts @@ -118,6 +118,7 @@ const features = { dgram: false, dnsPromises: false, dgramDyn: false, + textCodecDyn: false, watch: false, foreignFfi: false, nodeTest: false, diff --git a/packages/compiler/src/backend/runtime-pack.test.ts b/packages/compiler/src/backend/runtime-pack.test.ts index e3bc999a7..e2b24f8ca 100644 --- a/packages/compiler/src/backend/runtime-pack.test.ts +++ b/packages/compiler/src/backend/runtime-pack.test.ts @@ -54,6 +54,7 @@ const BASE: NativeLinkFeatures = { dgram: false, dnsPromises: false, dgramDyn: false, + textCodecDyn: false, watch: false, foreignFfi: false, nodeTest: false, diff --git a/packages/compiler/src/backend/target-cpu.test.ts b/packages/compiler/src/backend/target-cpu.test.ts new file mode 100644 index 000000000..428caee32 --- /dev/null +++ b/packages/compiler/src/backend/target-cpu.test.ts @@ -0,0 +1,51 @@ +import { expect, test } from "vitest"; +import { + isTargetCpuOption, + targetCpuAttribute, + targetCpuField, + targetCpuKeyParts, + targetCpuTargetRefusal, + x86Level, +} from "./target-cpu.js"; + +const V2 = ["cx16", "lahf_lm", "popcnt", "pni", "sse4_1", "sse4_2", "ssse3"]; +const V3 = ["avx", "avx2", "bmi1", "bmi2", "f16c", "fma", "abm", "movbe", "xsave"]; +const V4 = ["avx512f", "avx512bw", "avx512cd", "avx512dq", "avx512vl"]; +const level = (flags: string[]) => x86Level((flag) => flags.includes(flag)); + +test("a host's level is the highest one whose features and lower levels' features it has", () => { + expect(level([])).toBe("baseline"); + expect(level(V2)).toBe("x86-64-v2"); + expect(level([...V2, ...V3])).toBe("x86-64-v3"); + expect(level([...V2, ...V3, ...V4])).toBe("x86-64-v4"); + // AVX-512 without BMI2 is still v2. + expect(level([...V2, ...V3.filter((f) => f !== "bmi2"), ...V4])).toBe("x86-64-v2"); + expect(level([...V3, ...V4])).toBe("baseline"); +}); + +test("the baseline adds no key part, attribute or option field", () => { + for (const cpu of [undefined, "baseline"] as const) { + expect(targetCpuKeyParts(cpu)).toEqual([]); + expect(targetCpuAttribute(cpu)).toBe(""); + expect(targetCpuField(cpu)).toEqual({}); + expect(targetCpuTargetRefusal(cpu, "linux", "arm64")).toBeNull(); + } + expect(targetCpuKeyParts("x86-64-v3")).toEqual(["target-cpu-x86-64-v3"]); + expect(targetCpuAttribute("x86-64-v4")).toBe(' "target-cpu"="x86-64-v4"'); + expect(targetCpuField("x86-64-v2")).toEqual({ targetCpu: "x86-64-v2" }); +}); + +test("a level is refused for targets that are not x86-64", () => { + expect(targetCpuTargetRefusal("x86-64-v3", "linux", "x64")).toBeNull(); + expect(targetCpuTargetRefusal("x86-64-v3", "darwin", "arm64")).toBe( + "--target-cpu=x86-64-v3 requires an x86-64 target", + ); + expect(targetCpuTargetRefusal("x86-64-v3", "wasi", "x64")).not.toBeNull(); +}); + +test("the option accepts the levels and native", () => { + for (const value of ["baseline", "x86-64-v2", "x86-64-v3", "x86-64-v4", "native"]) + expect(isTargetCpuOption(value)).toBe(true); + for (const value of ["x86-64", "haswell", "", undefined]) + expect(isTargetCpuOption(value)).toBe(false); +}); diff --git a/packages/compiler/src/backend/target-cpu.ts b/packages/compiler/src/backend/target-cpu.ts new file mode 100644 index 000000000..51db87bbb --- /dev/null +++ b/packages/compiler/src/backend/target-cpu.ts @@ -0,0 +1,118 @@ +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; + +/** The x86-64 microarchitecture levels an executable may require + * (`--target-cpu`). `baseline` is the x86-64 baseline every release build + * targets; the other levels are LLVM's `x86-64-v2` to `x86-64-v4`. */ +export type TargetCpu = "baseline" | "x86-64-v2" | "x86-64-v3" | "x86-64-v4"; + +/** The values `--target-cpu` accepts: a level, or `native` for the highest + * level the build host supports. */ +export const TARGET_CPU_OPTIONS = ["baseline", "x86-64-v2", "x86-64-v3", "x86-64-v4", "native"]; + +export function isTargetCpuOption(value: unknown): value is TargetCpu | "native" { + return typeof value === "string" && TARGET_CPU_OPTIONS.includes(value); +} + +/** The CPU features each level adds to the one below it, as Linux names + * them in /proc/cpuinfo (`pni` is SSE3, `abm` includes LZCNT). */ +const LEVEL_FLAGS: readonly [TargetCpu, readonly string[]][] = [ + ["x86-64-v2", ["cx16", "lahf_lm", "popcnt", "pni", "sse4_1", "sse4_2", "ssse3"]], + ["x86-64-v3", ["avx", "avx2", "bmi1", "bmi2", "f16c", "fma", "abm", "movbe", "xsave"]], + ["x86-64-v4", ["avx512f", "avx512bw", "avx512cd", "avx512dq", "avx512vl"]], +]; + +/** The macOS `machdep.cpu` spellings of the same features. */ +const DARWIN_FLAGS: Readonly> = { + cx16: "CX16", + lahf_lm: "LAHF", + popcnt: "POPCNT", + pni: "SSE3", + sse4_1: "SSE4.1", + sse4_2: "SSE4.2", + ssse3: "SSSE3", + avx: "AVX1.0", + avx2: "AVX2", + bmi1: "BMI1", + bmi2: "BMI2", + f16c: "F16C", + fma: "FMA", + abm: "LZCNT", + movbe: "MOVBE", + xsave: "XSAVE", + avx512f: "AVX512F", + avx512bw: "AVX512BW", + avx512cd: "AVX512CD", + avx512dq: "AVX512DQ", + avx512vl: "AVX512VL", +}; + +/** The highest level whose features (and every lower level's) are all in + * `has`. */ +export function x86Level(has: (flag: string) => boolean): TargetCpu { + let level: TargetCpu = "baseline"; + for (const [next, flags] of LEVEL_FLAGS) { + if (!flags.every(has)) break; + level = next; + } + return level; +} + +/** The highest level the build host supports, or null when the host is not + * an x86-64 Linux or macOS machine. */ +export function hostTargetCpu( + platform: string = process.platform, + arch: string = process.arch, +): TargetCpu | null { + if (arch !== "x64") return null; + try { + if (platform === "linux") { + const info = readFileSync("/proc/cpuinfo", "utf8"); + const line = /^flags\s*:(.*)$/m.exec(info); + if (!line) return null; + const flags = new Set(line[1]!.trim().split(/\s+/)); + return x86Level((flag) => flags.has(flag)); + } + if (platform === "darwin") { + const text = execFileSync( + "sysctl", + ["-n", "machdep.cpu.features", "machdep.cpu.leaf7_features", "machdep.cpu.extfeatures"], + { encoding: "utf8" }, + ); + const flags = new Set(text.trim().split(/\s+/)); + return x86Level((flag) => flags.has(DARWIN_FLAGS[flag]!)); + } + } catch { + return null; + } + return null; +} + +/** Cache-key parts for a target level; empty for the baseline so existing + * keys are unchanged. */ +export function targetCpuKeyParts(cpu: TargetCpu | undefined): string[] { + return cpu === undefined || cpu === "baseline" ? [] : [`target-cpu-${cpu}`]; +} + +/** The function attribute that makes LLVM generate code for the level, or + * "" for the baseline. */ +export function targetCpuAttribute(cpu: TargetCpu | undefined): string { + return cpu === undefined || cpu === "baseline" ? "" : ` "target-cpu"="${cpu}"`; +} + +/** The options field for a level: omitted for the baseline. */ +export function targetCpuField(cpu: TargetCpu | undefined): { targetCpu?: TargetCpu } { + return cpu === undefined || cpu === "baseline" ? {} : { targetCpu: cpu }; +} + +/** The error for a level on a target that is not x86-64, or null. */ +export function targetCpuTargetRefusal( + cpu: TargetCpu | undefined, + platform: string, + architecture: string, +): string | null { + if (cpu === undefined || cpu === "baseline") return null; + if (architecture !== "x64" || platform === "wasi") + return `--target-cpu=${cpu} requires an x86-64 target`; + return null; +} diff --git a/packages/compiler/src/backend/vendor-archives.ts b/packages/compiler/src/backend/vendor-archives.ts index daa7e5eaa..dbcaf2d36 100644 --- a/packages/compiler/src/backend/vendor-archives.ts +++ b/packages/compiler/src/backend/vendor-archives.ts @@ -18,6 +18,7 @@ import { QJS_COMMIT, MBEDTLS_VERSION, ZLIB_VERSION, + BROTLI_VERSION, LRE_SOURCES, ZLIB_SOURCES, } from "./vendor-inputs.js"; @@ -25,6 +26,7 @@ export { QJS_COMMIT, MBEDTLS_VERSION, ZLIB_VERSION, + BROTLI_VERSION, LRE_SOURCES, ZLIB_SOURCES, } from "./vendor-inputs.js"; @@ -418,9 +420,98 @@ export function createVendorArchives(context: VendorArchiveContext) { return archive; } + function vendorBrotliDir(): string { + return join(runtimeSrcDir(), "..", "vendor", "brotli", "c"); + } + + function brotliArchivePath( + sanitize: boolean, + driver: CcDriver, + buildIdentity: string, + cacheRoot: string = vendorBuildCacheRoot(), + ): string { + const flavor = `${sanitize ? "asan" : "plain"}-${vendorCacheTargetFlavor(driver)}-${buildIdentity}`; + return join(cacheRoot, `brotli-${BROTLI_VERSION}-${flavor}`, "libbrotli.a"); + } + + /** The brotli archive for one flavor, compiled lazily on the first + * Brotli-using build and cached under + * /vendor/brotli----/libbrotli.a, + * with the mbedTLS archive's recipe: per-TU compiles of c/common, c/dec + * and c/enc against c/include, then one `ar rcs`. dec/ and enc/ share + * file names, so each object is named after its path (dec-static_init.o). */ + async function ensureBrotliArchive( + sanitize: boolean, + driver: CcDriver, + buildIdentity: string, + cacheRoot: string = vendorBuildCacheRoot(), + ): Promise { + const flavor = `${sanitize ? "asan" : "plain"}-${vendorCacheTargetFlavor(driver)}-${buildIdentity}`; + const compileArgv = isZigDriver(driver) ? driver.argv : ["clang"]; + const arArgv = isZigDriver(driver) ? [...driver.argv.slice(0, 1), "ar"] : ["ar"]; + const vendor = vendorBrotliDir(); + const archive = brotliArchivePath(sanitize, driver, buildIdentity, cacheRoot); + if (await validVendorArtifact(archive)) return archive; + + await mkdir(cacheRoot, { recursive: true }); + const buildDir = await mkdtemp(join(tmpdir(), `scriptc-vendor-brotli-${flavor}-`)); + try { + const sources = ( + await Promise.all( + ["common", "dec", "enc"].map(async (dir) => + (await readdir(join(vendor, dir))) + .filter((name) => !name.startsWith(".") && name.endsWith(".c")) + .map((name) => `${dir}/${name}`), + ), + ) + ) + .flat() + .sort(); + const object = (src: string): string => + join(buildDir, `${src.replaceAll("/", "-").replace(/\.c$/, "")}.o`); + const cflags = [ + "-std=c11", + ...driver.targetArgs, + ...(sanitize ? ["-O1", "-fsanitize=address"] : ["-Os"]), + "-I", + join(vendor, "include"), + ]; + const width = availableParallelism(); + for (let i = 0; i < sources.length; i += width) { + await Promise.all( + sources + .slice(i, i + width) + .map((src) => + execFileAsync(compileArgv[0] ?? "clang", [ + ...compileArgv.slice(1), + ...cflags, + "-c", + join(vendor, src), + "-o", + object(src), + ]), + ), + ); + } + await execFileAsync(arArgv[0] ?? "ar", [ + ...arArgv.slice(1), + "rcs", + join(buildDir, "libbrotli.a"), + ...sources.map(object), + ]); + await publishVendorArtifact(join(buildDir, "libbrotli.a"), archive); + } finally { + await rm(buildDir, { recursive: true, force: true }); + } + return archive; + } + return { vendorEngineDir, vendorTlsDir, + vendorBrotliDir, + brotliArchivePath, + ensureBrotliArchive, vendorBuildCacheRoot, vendorCacheTargetFlavor, vendorCacheBuildIdentity, diff --git a/packages/compiler/src/backend/vendor-inputs.ts b/packages/compiler/src/backend/vendor-inputs.ts index 3d2710fa0..a9d13e2f0 100644 --- a/packages/compiler/src/backend/vendor-inputs.ts +++ b/packages/compiler/src/backend/vendor-inputs.ts @@ -1,7 +1,9 @@ -/** The pinned QuickJS, mbedTLS, and zlib inputs used by native recipes. */ +/** The pinned QuickJS, mbedTLS, zlib, and brotli inputs used by native recipes. */ export const QJS_COMMIT = "3c8f3d68953955950074c41c6e4d999562ae82a7"; export const MBEDTLS_VERSION = "3.6.7"; export const ZLIB_VERSION = "1.3.1"; +/** The brotli release Node v24.22.0 bundles (packages/runtime/vendor/brotli). */ +export const BROTLI_VERSION = "1.2.0"; /** Exact translation-unit membership shared by cache builds and external * source-pack recipes. */ export const LRE_SOURCES = ["libregexp.c", "libunicode.c"] as const; diff --git a/packages/compiler/src/backend/warm-cache.ts b/packages/compiler/src/backend/warm-cache.ts index b48cf6739..02acb5462 100644 --- a/packages/compiler/src/backend/warm-cache.ts +++ b/packages/compiler/src/backend/warm-cache.ts @@ -61,6 +61,7 @@ export async function warmNativeCaches( dgram: false, dnsPromises: false, dgramDyn: false, + textCodecDyn: false, watch: false, foreignFfi: false, nodeTest: false, diff --git a/packages/compiler/src/cli/command.ts b/packages/compiler/src/cli/command.ts index a660d339a..608b97b67 100644 --- a/packages/compiler/src/cli/command.ts +++ b/packages/compiler/src/cli/command.ts @@ -22,6 +22,13 @@ import { buildEnvelope } from "../diagnostics/envelope.js"; import { renderConfigWarnings, renderWarnings } from "../diagnostics/render.js"; import { isPerformanceCode, renderPerformanceWarnings } from "../diagnostics/dynamic-tier.js"; import { isNativeOptimization } from "../backend/optimization.js"; +import { + hostTargetCpu, + isTargetCpuOption, + TARGET_CPU_OPTIONS, + targetCpuField, + type TargetCpu, +} from "../backend/target-cpu.js"; import { setProvenanceSources } from "../frontend/provenance-registry.js"; import type { CliHost, NativeCacheWarmProfile } from "./host.js"; import { resolveOutputOptions } from "./output-options.js"; @@ -103,6 +110,7 @@ async function main(args: string[], host: CliHost): Promise { values.strip || values["windows-subsystem"] !== undefined || values["detached-exit"] || + values["target-cpu"] !== undefined || values["profile-generate"] !== undefined || values["profile-use"] !== undefined || values.sparse || @@ -162,6 +170,7 @@ async function main(args: string[], host: CliHost): Promise { values.sanitize || values["windows-subsystem"] !== undefined || values["detached-exit"] || + values["target-cpu"] !== undefined || values["profile-generate"] !== undefined || values["profile-use"] !== undefined || (values["npm-static"] ?? []).length > 0 || @@ -219,6 +228,7 @@ async function main(args: string[], host: CliHost): Promise { values.ffi !== undefined || values["windows-subsystem"] !== undefined || values["detached-exit"] || + values["target-cpu"] !== undefined || values["profile-generate"] !== undefined || values["profile-use"] !== undefined || (values["npm-static"] ?? []).length > 0 || @@ -228,7 +238,7 @@ async function main(args: string[], host: CliHost): Promise { hasNodeIslandFlags(values) ) { fail( - "scriptc build --lib takes no --backend/--emit/--print/--optimization/--strip/--windows-subsystem/--detached-exit/--profile-generate/--profile-use/--npm-static/--ffi/--external-types/--tier/--explain-dynamic: the profile pins the emission and optimization, npm imports are judged automatically, outbound FFI belongs to executable builds, external type mappings belong to coverage, and library artifacts are static-tier only", + "scriptc build --lib takes no --backend/--emit/--print/--optimization/--strip/--windows-subsystem/--detached-exit/--target-cpu/--profile-generate/--profile-use/--npm-static/--ffi/--external-types/--tier/--explain-dynamic: the profile pins the emission and optimization, npm imports are judged automatically, outbound FFI belongs to executable builds, external type mappings belong to coverage, and library artifacts are static-tier only", ); } const profilePath = resolve(profileArg); @@ -271,6 +281,9 @@ async function main(args: string[], host: CliHost): Promise { if (command === "coverage" && values.strip) { fail(`--strip is a build/run option\n\n${USAGE}`); } + if (command === "coverage" && values["target-cpu"] !== undefined) { + fail(`--target-cpu is a build/run option\n\n${USAGE}`); + } if (command === "coverage" && values["detached-exit"]) { fail(`--detached-exit is a build/run option\n\n${USAGE}`); } @@ -424,6 +437,19 @@ async function main(args: string[], host: CliHost): Promise { if (windowsSubsystem !== undefined && command === "coverage") { fail(`--windows-subsystem is only supported for executable builds\n\n${USAGE}`); } + const targetCpuOption = values["target-cpu"]; + if (targetCpuOption !== undefined && !isTargetCpuOption(targetCpuOption)) { + fail( + `unknown target CPU "${targetCpuOption}" (supported: ${TARGET_CPU_OPTIONS.join(", ")})\n\n${USAGE}`, + ); + } + let targetCpu: TargetCpu | undefined = targetCpuOption === "native" ? undefined : targetCpuOption; + if (targetCpuOption === "native") { + const level = hostTargetCpu(); + if (level === null) + fail(`--target-cpu=native needs an x86-64 Linux or macOS build host; name a level instead`); + targetCpu = level; + } const output = command === "coverage" ? null @@ -557,6 +583,7 @@ async function main(args: string[], host: CliHost): Promise { ...(values.strip ? { strip: true } : {}), ...(windowsSubsystem !== undefined ? { windowsSubsystem } : {}), ...(values["detached-exit"] ? { detachedExit: true } : {}), + ...targetCpuField(targetCpu), // The instrumented program resolves a relative pattern against its own // working directory; resolve it here, where the user wrote it. ...(values["profile-generate"] === undefined diff --git a/packages/compiler/src/cli/usage.ts b/packages/compiler/src/cli/usage.ts index 113b11fe5..f8b6a4869 100644 --- a/packages/compiler/src/cli/usage.ts +++ b/packages/compiler/src/cli/usage.ts @@ -56,6 +56,12 @@ Options: a helper process so the parent sees the exit sooner. The helper keeps the executable mapped and its memory charged for up to ~100 ms after exit (opt in; --emit=exe only) + --target-cpu + x86-64: compile the program's code for a CPU level + (default: baseline, which runs on any x86-64 CPU). The + executable then requires that level: v3 adds AVX2, BMI2 + and FMA, v4 adds AVX-512. native uses the build host's + level. The runtime keeps its baseline code --profile-generate Linux: instrument the executable for profile-guided optimization. Each run writes a raw profile to the @@ -147,6 +153,7 @@ export const CLI_OPTIONS = { strip: { type: "boolean", default: false }, "windows-subsystem": { type: "string" }, "detached-exit": { type: "boolean", default: false }, + "target-cpu": { type: "string" }, "profile-generate": { type: "string" }, "profile-use": { type: "string" }, sparse: { type: "boolean", default: false }, diff --git a/packages/compiler/src/compat/fetch-profile.ts b/packages/compiler/src/compat/fetch-profile.ts index c4d1e3f50..ff7cedb22 100644 --- a/packages/compiler/src/compat/fetch-profile.ts +++ b/packages/compiler/src/compat/fetch-profile.ts @@ -900,7 +900,7 @@ export const NODE24_FETCH_COMPAT_PROFILE = { { name: "WritableStream/TransformStream", reason: - "native support currently covers readable fetch bodies; general Web Streams graphs are unsupported", + "native support covers readable fetch bodies and the identity `new TransformStream()`; the WritableStream constructor, transformers, and general Web Streams graphs are unsupported", }, { name: "Readable byte/BYOB streams", diff --git a/packages/compiler/src/compile-types.ts b/packages/compiler/src/compile-types.ts index 39fea3acf..607fe6a51 100644 --- a/packages/compiler/src/compile-types.ts +++ b/packages/compiler/src/compile-types.ts @@ -1,3 +1,4 @@ +import type { TargetCpu } from "./backend/target-cpu.js"; import type { WindowsSubsystem } from "./backend/targets.js"; import type { NativeLinkInfo } from "./backend/native-link-info.js"; import type { NativeOptimization } from "./backend/optimization.js"; @@ -37,6 +38,12 @@ export interface CompileBaseOptions { * executable mapped and the memory charged for up to ~100 ms after exit. * Executables only; no effect on other platforms. */ detachedExit?: boolean; + /** The x86-64 microarchitecture level the executable's code may require + * (`x86-64-v2` to `x86-64-v4`). The program's functions are compiled for + * that level, and the executable then runs only on CPUs that support it. + * Unset or `baseline`, the code runs on any x86-64 CPU. x86-64 targets + * only; the runtime pack keeps its baseline code. */ + targetCpu?: TargetCpu; /** Instrument the executable for profile-guided optimization. Each run * writes a raw profile to this path pattern (`%p` expands to the process * id; `LLVM_PROFILE_FILE` overrides the pattern at run time). Linux diff --git a/packages/compiler/src/diagnostics/diagnostic.ts b/packages/compiler/src/diagnostics/diagnostic.ts index bb5efba31..0da37f690 100644 --- a/packages/compiler/src/diagnostics/diagnostic.ts +++ b/packages/compiler/src/diagnostics/diagnostic.ts @@ -737,7 +737,7 @@ export function genericSignatureTypeDiag(typeText: string, loc: SrcLoc): ScrDiag export function indexSignatureTypeDiag(typeText: string, loc: SrcLoc): ScrDiagnostic { return { code: "SC2006", - message: `values of type '${typeText}' cannot be compiled: this index signature is outside the supported shape (string or number keys; values limited to numbers, strings, booleans, records, classes, arrays, unions, functions, Maps, Sets, RegExps, Promises, or 'unknown')`, + message: `values of type '${typeText}' cannot be compiled: this index signature is outside the supported shape (string or number keys; values limited to numbers, strings, booleans, records, classes, arrays, unions, functions, Maps, Sets, RegExps, Dates, Promises, or 'unknown')`, loc, milestone: "M2", hint: "string- and number-keyed index signatures over the supported value types compile directly; symbol keys and dual signatures with unequal value types have no lowering", diff --git a/packages/compiler/src/executable/executable-cache.test.ts b/packages/compiler/src/executable/executable-cache.test.ts index 5cf8c6588..e1235d8ac 100644 --- a/packages/compiler/src/executable/executable-cache.test.ts +++ b/packages/compiler/src/executable/executable-cache.test.ts @@ -58,6 +58,7 @@ const native: EarlyExecutableNativeFeatures = { dgram: false, dnsPromises: false, dgramDyn: false, + textCodecDyn: false, watch: false, foreignFfi: false, nodeTest: false, @@ -235,6 +236,8 @@ test("early executable keys isolate compile modes, paths, implementation, checke { ...f.options, optimization: "dev" }, { ...f.options, optimization: "speed" }, { ...f.options, detachedExit: true }, + { ...f.options, targetCpu: "x86-64-v3" }, + { ...f.options, targetCpu: "x86-64-v4" }, { ...f.options, profile: "profile-generate:/tmp/%p.profraw" }, { ...f.options, profile: "profile-use:digest" }, { ...f.options, checker: "tsc-ts-executable-identity" }, diff --git a/packages/compiler/src/executable/executable-cache.ts b/packages/compiler/src/executable/executable-cache.ts index 7b7febc06..754c20648 100644 --- a/packages/compiler/src/executable/executable-cache.ts +++ b/packages/compiler/src/executable/executable-cache.ts @@ -18,6 +18,7 @@ import { compilerReleaseVersion } from "../library/sidecar.js"; import { nativeArtifactDependenciesStillMatch } from "../backend/native/artifact-stamps.js"; import { type NativeArtifactDependency } from "../backend/native/contracts.js"; import { optimizationKeyParts } from "../backend/optimization.js"; +import { type TargetCpu, targetCpuKeyParts } from "../backend/target-cpu.js"; import { privateSiblingPath } from "../backend/build-cache.js"; import type { CompilerImplementationDependency } from "../library/compiler-self-identity.js"; import { @@ -62,6 +63,7 @@ export interface EarlyExecutableNativeFeatures { warningReplace?: boolean; stdioExtended?: boolean; zlib: boolean; + brotli?: boolean; assert: boolean; inspect: boolean; dynInvoke: boolean; @@ -92,6 +94,7 @@ export interface EarlyExecutableNativeFeatures { dgram: boolean; dnsPromises: boolean; dgramDyn: boolean; + textCodecDyn: boolean; watch: boolean; foreignFfi: boolean; nodeTest: boolean; @@ -138,6 +141,8 @@ export interface EarlyExecutableCacheOptions { windowsSubsystem?: "gui"; /** Omitted unless --detached-exit is set. */ detachedExit?: true; + /** Omitted for the x86-64 baseline (`--target-cpu`). */ + targetCpu?: TargetCpu; /** Omitted without --profile-generate/--profile-use; otherwise the * pattern, or the digest of the profile's bytes. */ profile?: string; @@ -235,6 +240,7 @@ const BOOLEAN_NATIVE_KEYS = [ "dgram", "dnsPromises", "dgramDyn", + "textCodecDyn", "watch", "foreignFfi", "nodeTest", @@ -279,6 +285,7 @@ function cacheKey(options: EarlyExecutableCacheOptions): string { ...(options.strip ? ["strip"] : []), ...(options.windowsSubsystem === "gui" ? ["windows-subsystem-gui"] : []), ...(options.detachedExit ? ["detached-exit"] : []), + ...targetCpuKeyParts(options.targetCpu), ...(options.profile !== undefined ? [options.profile] : []), ...(options.checker !== undefined ? [`checker:${options.checker}`] : []), ...(options.youngNursery ? ["young-nursery"] : []), @@ -321,6 +328,7 @@ function routeKey(options: Omit(); + /** While set, printing registers no recursive alias (projectable()). */ + private dry = false; + constructor(private readonly checker: ts.TypeChecker) {} + /** Whether a type projects, without naming it in the generated file. */ + projectable(type: ts.Type): boolean { + if (type.flags & ANYISH) return false; + const was = this.dry; + this.dry = true; + try { + const text = this.print(type, 0); + return text !== null && text !== "unknown"; + } finally { + this.dry = was; + } + } + /** The alias naming a projectable type, or null. Top-level any/unknown * never projects. */ project(type: ts.Type): string | null { @@ -212,7 +228,12 @@ class TypePrinter { if (f & ts.TypeFlags.Null) return "null"; if (f & ts.TypeFlags.Undefined) return "undefined"; if (f & ts.TypeFlags.EnumLiteral || f & ts.TypeFlags.Enum) return null; - if (f & ts.TypeFlags.BooleanLiteral) return type.intrinsicName === "true" ? "true" : "false"; + // A boolean literal type carries its value (`intrinsicName` is not + // set for it by every checker facade). + if (f & ts.TypeFlags.BooleanLiteral) + return (type as ts.BooleanLiteralType).value === true || type.intrinsicName === "true" + ? "true" + : "false"; if (f & ts.TypeFlags.StringLiteral) return JSON.stringify(String(type.value)); if (f & ts.TypeFlags.NumberLiteral) { const v = Number(type.value); @@ -341,7 +362,7 @@ class TypePrinter { if (props.length > 0 && readonlyCount === props.length) return null; const text = `{ ${members.join("; ")} }`; if (frame.name !== null) { - this.declarations.push(`type ${frame.name} = ${text};`); + if (!this.dry) this.declarations.push(`type ${frame.name} = ${text};`); return frame.name; } return text; @@ -517,9 +538,17 @@ export function planDeclProjection(input: DeclProjectionInput): DeclProjection | const declSf = input.declProgram.getSourceFile(file); if (jsSf === undefined || declSf === undefined || jsSf.text !== declSf.text) continue; const fileEdits: Edit[] = []; - /** Candidate expressions (by js node) with their alias. */ - const exprCandidates = new Map(); + /** Candidate expressions (by js node) with their declared type, named + * only for the casts that survive suppression. */ + const exprCandidates = new Map(); const exprOrder: ts.Node[] = []; + /** Reads whose projectable receiver the JavaScript world types `any`: + * kept only when nothing types the receiver. */ + const deferred = new Set(); + /** Package calls whose declared type does not project. */ + const unprojected = new Set(); + /** The declaration-world twin of every walked node. */ + const twins = new Map(); const considerType = (jsNode: ts.Node, declNode: ts.Node): boolean => { if ( @@ -603,8 +632,18 @@ export function planDeclProjection(input: DeclProjectionInput): DeclProjection | } else if (ts.isPropertyAccessExpression(d) || ts.isElementAccessExpression(d)) { const recvDecl = typeAt(declChecker, d.expression); const recvJs = typeAt(jsChecker, (jsNode as ts.PropertyAccessExpression).expression); - const recvProjectable = recvDecl !== null && printer.project(recvDecl) !== null; + const recvProjectable = recvDecl !== null && printer.projectable(recvDecl); produced = !recvProjectable || (recvJs !== null && !isAnyish(recvJs)); + // A projectable receiver the JavaScript world types `any` is + // usually typed by another candidate. One that is a package value + // nothing projects stays `any`, such as a safeParse result narrowed + // to its success arm (the whole result is not data: its error arm + // is a class): the read crosses here instead, if the suppression + // below confirms the receiver stays untyped. + if (!produced) { + produced = true; + deferred.add(jsNode); + } } else if (ts.isAwaitExpression(d)) { const inner = unwrapTransparent(d.expression); produced = @@ -633,6 +672,12 @@ export function planDeclProjection(input: DeclProjectionInput): DeclProjection | declaredInPackage(resolveAlias(raw)); } if (!produced) return; + if (isCall && !deferred.has(jsNode)) { + // Recorded before the contextual-type test below: a package value + // whose type does not project stays `any` wherever it flows. + const t = typeAt(declChecker, d); + if (t === null || !printer.projectable(t)) unprojected.add(jsNode); + } // A consumer that takes anything needs no conversion. try { const ctx = declChecker.getContextualType(d); @@ -641,10 +686,8 @@ export function planDeclProjection(input: DeclProjectionInput): DeclProjection | // no contextual type } const declType = typeAt(declChecker, d); - if (declType === null) return; - const alias = printer.project(declType); - if (alias === null) return; - exprCandidates.set(jsNode, alias); + if (declType === null || !printer.projectable(declType)) return; + exprCandidates.set(jsNode, declType); exprOrder.push(jsNode); }; @@ -703,6 +746,7 @@ export function planDeclProjection(input: DeclProjectionInput): DeclProjection | const walk = (jsNode: ts.Node, declNode: ts.Node): void => { if (jsNode.kind !== declNode.kind) return; + twins.set(jsNode, declNode); if (ts.isTypeNode(jsNode) || ts.isExpressionWithTypeArguments(jsNode)) { if (considerType(jsNode, declNode)) return; } else { @@ -749,21 +793,67 @@ export function planDeclProjection(input: DeclProjectionInput): DeclProjection | result = willBeTyped(decl.initializer, depth + 1); else if (ts.isParameter(decl) && annotated.has(decl)) result = true; else if (ts.isBindingElement(decl)) result = false; + else if ( + (ts.isVariableDeclaration(decl) || ts.isParameter(decl)) && + decl.type !== undefined + ) + result = annotationWillBeTyped(decl.type); } symbolTyped.set(sym, result); return result; } return false; }; + /** An annotation in this file whose declaration-world type projects: + * after the rewrite it names the projected structure (a package type + * is replaced by its alias, a user alias of one has its right-hand + * side replaced). */ + const annotationWillBeTyped = (type: ts.TypeNode): boolean => { + const twin = twins.get(type); + if (twin === undefined) return false; + let declType: ts.Type | null; + try { + declType = declChecker.getTypeFromTypeNode(twin as ts.TypeNode); + } catch { + return false; + } + return declType !== null && printer.projectable(declType); + }; const receiverOf = (e: ts.Node): ts.Node | null => { if (ts.isPropertyAccessExpression(e) || ts.isElementAccessExpression(e)) return e.expression; if (ts.isIdentifier(e)) return null; return null; }; const kept: ts.Node[] = []; + /** A receiver that stays `any` after the rewrite: a package call whose + * type does not project, or a user const bound to one. */ + const staysUntyped = (e: ts.Node): boolean => { + const u = unwrapTransparent(e as ts.Expression); + if (unprojected.has(u)) return true; + if (!ts.isIdentifier(u)) return false; + let sym: ts.Symbol | undefined; + try { + sym = jsChecker.getSymbolAtLocation(u); + } catch { + return false; + } + const decls = sym?.declarations ?? []; + if (decls.length !== 1) return false; + const decl = decls[0]!; + return ( + ts.isVariableDeclaration(decl) && + decl.type === undefined && + decl.initializer !== undefined && + decl.parent !== undefined && + ts.isVariableDeclarationList(decl.parent) && + (decl.parent.flags & ts.NodeFlags.Const) !== 0 && + unprojected.has(unwrapTransparent(decl.initializer)) + ); + }; for (const e of exprOrder) { const recv = receiverOf(e); if (recv !== null && willBeTyped(recv)) continue; + if (deferred.has(e) && (recv === null || !staysUntyped(recv))) continue; // An identifier candidate that is a user binding never gets here // (only package imports are candidates). kept.push(e); @@ -800,7 +890,8 @@ export function planDeclProjection(input: DeclProjectionInput): DeclProjection | ) continue; } - const alias = exprCandidates.get(e)!; + const alias = printer.project(exprCandidates.get(e)!); + if (alias === null) continue; const start = ts.tokenStartOfNode(e); const stmt = e.parent; let prefix = "("; diff --git a/packages/compiler/src/frontend/lowering/array-values.ts b/packages/compiler/src/frontend/lowering/array-values.ts index 314cdfd24..f191ad942 100644 --- a/packages/compiler/src/frontend/lowering/array-values.ts +++ b/packages/compiler/src/frontend/lowering/array-values.ts @@ -15,6 +15,7 @@ import { typeKey, unionContainerArmsOk, } from "../../ir/ir.js"; +import { everyExpr } from "../../ir/traverse.js"; import type { Lowerer } from "./lowerer.js"; import type * as ts from "../ts7/adapter.js"; @@ -193,6 +194,81 @@ export function unionArrayValueRead( return result; } +/** Expression kinds that evaluate without running other code or writing + * a binding. */ +const PURE_KINDS = new Set([ + "varRef", + "numLit", + "strLit", + "boolLit", + "unitLit", + "bin", + "unary", + "logical", + "ternary", + "toBool", + "fieldGet", + "recordGet", + "arrayGet", + "arrayState", + "arrayHas", + "unionIsTag", + "unionNarrow", + "unionWrap", +]); + +/** The locals evaluating `value` may assign, and whether it may run other + * code (anything but the pure kinds and the read-only element helpers), + * which can assign locals and fields through closures. */ +function valueEffects(lowerer: Lowerer, value: IrExpr): { writes: Set; opaque: boolean } { + const readers = new Set(); + for (const [key, name] of lowerer.arrHofHelpers) if (key.startsWith("idxOr:")) readers.add(name); + const writes = new Set(); + let opaque = false; + everyExpr(value, { + expr: (e) => { + if (e.kind === "assignExpr" || e.kind === "incDec") writes.add(e.localId); + else if (e.kind === "call" ? !readers.has(e.callee) : !PURE_KINDS.has(e.kind)) opaque = true; + return true; + }, + stmt: (s) => { + if (s.kind === "varDecl" || s.kind === "assign") writes.add(s.localId); + else opaque = true; + return true; + }, + }); + return { writes, opaque }; +} + +/** Whether evaluating `e` after `value` gives what it gives before it: a + * literal, or a local and field read `value` cannot change. */ +function unaffectedBy(e: IrExpr, effects: { writes: Set; opaque: boolean }): boolean { + switch (e.kind) { + case "numLit": + case "strLit": + return true; + case "varRef": + return !effects.writes.has(e.localId); + case "bin": + return unaffectedBy(e.left, effects) && unaffectedBy(e.right, effects); + case "fieldGet": + case "recordGet": + return !effects.opaque && unaffectedBy(e.obj, effects); + default: + return false; + } +} + +/** Whether `e` reads a `let` or `var` binding, which code the value runs + * can assign through a closure. */ +function readsMutableLocal(lowerer: Lowerer, e: IrExpr): boolean { + return !everyExpr(e, { + expr: (x) => + x.kind !== "varRef" || lowerer.ctx.locals.find((l) => l.id === x.localId)?.mutable === false, + stmt: () => true, + }); +} + /** Store a stabilized value without changing the array's element ABI. */ export function arrayValueStore( lowerer: Lowerer, @@ -210,10 +286,31 @@ export function arrayValueStore( // owned union temporary to release on every path. const unionId = value.type.unionId; let stable = value; - let prefix: IrStmt[] = []; + const prefix: IrStmt[] = []; + // The stores below evaluate the receiver and the index after the value. + // JavaScript evaluates them first (`out[n++] = src[n]` reads src[1]), so + // either may need its value held before the value runs. + if (value.kind !== "varRef") { + const valueFx = valueEffects(lowerer, value); + const hold = (e: IrExpr, name: string, effects: typeof valueFx): IrExpr => { + if (unaffectedBy(e, effects) && !(effects.opaque && readsMutableLocal(lowerer, e))) + return e; + const temp = lowerer.declareHiddenLocal(name, e.type); + prefix.push({ kind: "varDecl", localId: temp.id, init: e, loc }); + return varRef(temp.id, temp.type, loc); + }; + // The receiver runs before the index and the value, so the index's + // effects can change it too (`a[(a = b, 0)] = c[0]!`). + const indexFx = valueEffects(lowerer, index); + arr = hold(arr, "%arrayTarget", { + writes: new Set([...valueFx.writes, ...indexFx.writes]), + opaque: valueFx.opaque || indexFx.opaque, + }); + index = hold(index, "%arrayIndex", valueFx); + } if (value.kind !== "varRef") { const temp = lowerer.declareHiddenLocal("%arrayValue", value.type); - prefix = [{ kind: "varDecl", localId: temp.id, init: value, loc }]; + prefix.push({ kind: "varDecl", localId: temp.id, init: value, loc }); stable = varRef(temp.id, temp.type, loc); } const write: IrStmt = { diff --git a/packages/compiler/src/frontend/lowering/builtins/async-context.ts b/packages/compiler/src/frontend/lowering/builtins/async-context.ts index 18bd93a86..a130df59b 100644 --- a/packages/compiler/src/frontend/lowering/builtins/async-context.ts +++ b/packages/compiler/src/frontend/lowering/builtins/async-context.ts @@ -2,7 +2,7 @@ import { isBuiltinClassInstance } from "./receiver-types.js"; import { dynUndefinedExpr } from "../../../ir/build.js"; import * as ts from "../../ts7/adapter.js"; import { type Lowerer } from "../lowerer.js"; -import { locOf } from "../../program.js"; +import { isJsSourceFile, locOf } from "../../program.js"; import { BOOL, DYN, @@ -184,16 +184,89 @@ export function lowerDiagnosticsChannelMethodCall( ); } +/** Whether `node` names the AsyncLocalStorage class itself. */ +function isAsyncLocalStorageClass(lowerer: Lowerer, node: ts.Expression): boolean { + if (!ts.isIdentifier(node)) return false; + const symbol = lowerer.resolveValueSymbol(node); + return ( + symbol !== null && + lowerer.isStdlibSymbol(symbol) && + ts.symbolName(symbol) === "AsyncLocalStorage" + ); +} + +/** The static AsyncLocalStorage members: bind(fn) answers fn bound to the + * active context (its own type for a typed function; a dyn function in + * JavaScript files, where the bound function's name and receiver are + * observable, and for dyn values); snapshot() answers a dyn function + * (cb, ...args) that runs cb in the context active at the snapshot. */ +function lowerAsyncLocalStorageStaticCall( + lowerer: Lowerer, + call: ts.CallExpression, + access: ts.PropertyAccessExpression, +): IrExpr { + const name = access.name.text; + const loc = locOf(call); + const always: IrExpr = { kind: "boolLit", value: true, type: BOOL, loc }; + if (name === "bind" && call.arguments.nodes.length === 1) { + const arg = call.arguments.nodes[0]!; + if (!isJsSourceFile(ts.getSourceFileOfNode(call))) { + const fn = lowerer.lowerExpr(arg); + if (fn.type.kind === "func") + return { kind: "libCall", fn: "als.bindCallback", args: [fn, always], type: fn.type, loc }; + } + return asyncLocalStorageResult(lowerer, call, { + kind: "libCall", + fn: "als.bindCallbackDyn", + args: [lowerDiagnosticsMessage(lowerer, arg), always], + type: DYN, + loc, + }); + } + if (name === "snapshot" && call.arguments.nodes.length === 0) + return { kind: "libCall", fn: "als.snapshot", args: [], type: DYN, loc }; + lowerer.noLowering( + `AsyncLocalStorage.${name}`, + call, + "AsyncLocalStorage.bind(fn) and AsyncLocalStorage.snapshot() are the supported static members", + lowerer.checker.getSymbolAtLocation(access.name), + ); +} + +/** new AsyncLocalStorage(options): the options object as a dyn value + * (plain property literals build it member by member). */ +export function lowerAsyncLocalStorageOptions(lowerer: Lowerer, node: ts.Expression): IrExpr { + return lowerTracingHandlers(lowerer, node); +} + +/** `.name` on AsyncLocalStorage receivers: the constructor's name option + * (als.name), converted to the property's type. Null otherwise. */ +export function lowerAsyncLocalStorageProperty( + lowerer: Lowerer, + access: ts.PropertyAccessExpression, +): IrExpr | null { + if (access.questionDotToken || access.name.text !== "name") return null; + if (!isBuiltinClassInstance(lowerer, access.expression, "AsyncLocalStorage")) return null; + if (!lowerer.isStdlibMember(access)) return null; + const loc = locOf(access); + const receiver = lowerer.lowerExprExpecting(access.expression, F64); + const value: IrExpr = { kind: "libCall", fn: "als.name", args: [receiver], type: DYN, loc }; + const expected = lowerer.mapTypeOf(lowerer.typeOf(access)); + return !expected || expected.kind === "dyn" ? value : lowerer.coerceInto(access, value, expected); +} + /** Method calls on AsyncLocalStorage receivers: run(store, fn, ...args), * exit(fn, ...args), getStore(), enterWith(store), disable() — over the - * f64 store handle (als.* libCalls; values cross as dyn values). Null - * for non-ALS receivers. */ + * f64 store handle (als.* libCalls; values cross as dyn values) — and the + * static bind/snapshot members. Null for other receivers. */ export function lowerAsyncLocalStorageMethodCall( lowerer: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression, ): IrExpr | null { if (call.questionDotToken || access.questionDotToken) return null; + if (isAsyncLocalStorageClass(lowerer, access.expression)) + return lowerAsyncLocalStorageStaticCall(lowerer, call, access); if (!isBuiltinClassInstance(lowerer, access.expression, "AsyncLocalStorage")) return null; if (!lowerer.isStdlibMember(access)) return null; const name = access.name.text; diff --git a/packages/compiler/src/frontend/lowering/builtins/compression.ts b/packages/compiler/src/frontend/lowering/builtins/compression.ts index 717e09933..1e4d7364c 100644 --- a/packages/compiler/src/frontend/lowering/builtins/compression.ts +++ b/packages/compiler/src/frontend/lowering/builtins/compression.ts @@ -13,6 +13,7 @@ import { funcOf, type IrExpr, type IrLibFn, + type IrStmt, type IrType, type SrcLoc, STRING, @@ -40,6 +41,36 @@ const ZLIB_CALLBACK_FNS: Readonly> = { unzip: "zlib.unzipCb", }; +/* Node's BROTLI_DECODE and BROTLI_ENCODE modes. */ +const BROTLI_DECODE = 8; +const BROTLI_ENCODE = 9; + +/** The Brotli one-shots: every form takes the options entries. */ +const BROTLI_SYNC_MEMBERS: ReadonlySet = new Set([ + "brotliCompressSync", + "brotliDecompressSync", +]); +const BROTLI_CALLBACK_MEMBERS: ReadonlySet = new Set([ + "brotliCompress", + "brotliDecompress", +]); + +/** A Brotli mode connects the codec (scr_brotli.c) to the zlib engine + * before any module initializer runs. */ +function noteBrotliMode(lowerer: Lowerer, mode: number, loc: SrcLoc): void { + if (mode !== BROTLI_DECODE && mode !== BROTLI_ENCODE) return; + const installs = (stmt: IrStmt): boolean => + stmt.kind === "exprStmt" && + stmt.expr.kind === "libCall" && + stmt.expr.fn === "zlib.brotliInstall"; + if (lowerer.mainPrelude.some(installs)) return; + lowerer.mainPrelude.push({ + kind: "exprStmt", + expr: { kind: "libCall", fn: "zlib.brotliInstall", args: [], type: VOID, loc }, + loc, + }); +} + const ZLIB_STREAM_CLASS = "%Transform"; const ZLIB_STREAM_T: IrType = { kind: "object", className: ZLIB_STREAM_CLASS }; @@ -115,6 +146,7 @@ export function lowerZlibStreamNew( } const options = args[0] !== undefined ? zlibOptionsValue(lowerer, args[0], what, loc) : dynUndefinedExpr(loc); + noteBrotliMode(lowerer, mode, loc); return { kind: "libCall", fn: "zlib.streamNew", @@ -154,8 +186,8 @@ function isZlibFallbackDecl(lowerer: Lowerer, d: ts.Node): boolean { return false; } -/** True when `access` names a member the zlib stream base class declares - * (bytesWritten, flush, params, close, reset). */ +/** True when `access` names a member the zlib stream base classes declare + * (bytesWritten, flush, close and reset on ZlibBase; params on Zlib). */ export function isZlibStreamMember(lowerer: Lowerer, access: ts.PropertyAccessExpression): boolean { const symbol = lowerer.checker.getSymbolAtLocation(access.name); if (!symbol) return false; @@ -163,7 +195,7 @@ export function isZlibStreamMember(lowerer: Lowerer, access: ts.PropertyAccessEx (d) => d.parent !== undefined && ts.isClassDeclaration(d.parent) && - d.parent.name?.text === "ZlibBase" && + (d.parent.name?.text === "ZlibBase" || d.parent.name?.text === "Zlib") && isZlibFallbackDecl(lowerer, d.parent), ); } @@ -281,12 +313,17 @@ const ZLIB_ONE_SHOT_MODES: Readonly> = { deflateRaw: 5, inflateRaw: 6, unzip: 7, + brotliDecompressSync: BROTLI_DECODE, + brotliCompressSync: BROTLI_ENCODE, + brotliDecompress: BROTLI_DECODE, + brotliCompress: BROTLI_ENCODE, }; /** True for a zlib callback one-shot util.promisify converts (deflate, - * inflate, deflateRaw, inflateRaw, gzip, gunzip and unzip). */ + * inflate, deflateRaw, inflateRaw, gzip, gunzip, unzip, brotliCompress and + * brotliDecompress). */ export function isPromisifiedZlibMember(member: string): boolean { - return ZLIB_CALLBACK_FNS[member] !== undefined; + return ZLIB_CALLBACK_FNS[member] !== undefined || BROTLI_CALLBACK_MEMBERS.has(member); } /** A call through `promisify(zlib.gzip)` and the other callback one-shots: @@ -311,6 +348,7 @@ export function lowerPromisifiedZlibCall( ); } const input = zlibInput(lowerer, expr.arguments.nodes[0]!, loc); + noteBrotliMode(lowerer, ZLIB_ONE_SHOT_MODES[member]!, loc); return { kind: "libCall", fn: input.dyn ? "zlib.promiseDyn" : "zlib.promise", @@ -334,6 +372,8 @@ const ZLIB_STREAM_FACTORIES: Readonly> = { createDeflateRaw: 5, createInflateRaw: 6, createUnzip: 7, + createBrotliDecompress: BROTLI_DECODE, + createBrotliCompress: BROTLI_ENCODE, }; export function lowerZlibModuleCall( @@ -356,6 +396,9 @@ export function lowerZlibModuleCall( loc, ); } + if (BROTLI_SYNC_MEMBERS.has(bi.member) || BROTLI_CALLBACK_MEMBERS.has(bi.member)) { + return lowerBrotliOneShot(lowerer, expr, bi.member, loc); + } const syncFn = ZLIB_SYNC_FNS[bi.member]; if (syncFn !== undefined) { if (expr.arguments.nodes.length === 0 || expr.arguments.nodes.length > 2) { @@ -502,6 +545,57 @@ export function lowerZlibModuleCall( throw new InternalCompilerError(`unhandled lowered zlib member ${bi.member}`); } +/** brotliCompressSync(data[, options]), brotliCompress(data[, options], + * callback) and the decompress pair: the options entries with Node's + * BROTLI_ENCODE or BROTLI_DECODE mode, an absent options value passing + * undefined. */ +function lowerBrotliOneShot( + lowerer: Lowerer, + expr: ts.CallExpression, + member: string, + loc: SrcLoc, +): IrExpr { + const args = expr.arguments.nodes; + const mode = ZLIB_ONE_SHOT_MODES[member]!; + const sync = BROTLI_SYNC_MEMBERS.has(member); + if (sync ? args.length === 0 || args.length > 2 : args.length !== 2 && args.length !== 3) { + lowerer.noLowering( + `zlib.${member} with ${args.length} arguments`, + expr, + sync + ? `${member}(data) and ${member}(data, options) are supported` + : `${member}(data, callback) and ${member}(data, options, callback) are supported`, + ); + } + const input = zlibInput(lowerer, args[0]!, loc); + const withOptions = sync ? args.length === 2 : args.length === 3; + const options = withOptions + ? zlibOptionsValue(lowerer, args[1]!, `zlib.${member}`, loc) + : dynUndefinedExpr(loc); + noteBrotliMode(lowerer, mode, loc); + if (sync) { + return { + kind: "libCall", + fn: input.dyn ? "zlib.syncOptsDyn" : "zlib.syncOpts", + args: [numLit(mode, loc), input.value, options], + type: BYTES_U8, + loc, + }; + } + return { + kind: "libCall", + fn: input.dyn ? "zlib.cbOptsDyn" : "zlib.cbOpts", + args: [ + numLit(mode, loc), + input.value, + options, + errorFirstBytesCallback(lowerer, args[withOptions ? 2 : 1]!, "zlib"), + ], + type: VOID, + loc, + }; +} + /** The class name of a zlib stream mode (Node's constructor name). */ export function zlibClassName(mode: number): string { for (const [name, m] of ZLIB_STREAM_MODES) if (m === mode) return name; diff --git a/packages/compiler/src/frontend/lowering/builtins/crypto.ts b/packages/compiler/src/frontend/lowering/builtins/crypto.ts index 051ec18c4..15c057462 100644 --- a/packages/compiler/src/frontend/lowering/builtins/crypto.ts +++ b/packages/compiler/src/frontend/lowering/builtins/crypto.ts @@ -1454,6 +1454,7 @@ function lowerPkiCall( ], sign: ["crypto.signOneshot", 3, 3, "sign(algorithm, data, key)"], verify: ["crypto.verifyOneshot", 4, 4, "verify(algorithm, data, key, signature)"], + diffieHellman: ["crypto.diffieHellman", 1, 2, "diffieHellman(options[, callback])"], createSign: ["crypto.createSign", 1, 1, "createSign(algorithm)"], createVerify: ["crypto.createVerify", 1, 1, "createVerify(algorithm)"], }; @@ -1474,6 +1475,7 @@ function lowerPkiCall( return { kind: "libCall", fn, args: values, type: VOID, loc }; const call: IrExpr = { kind: "libCall", fn, args: values, type: DYN, loc }; const result = lowerer.mapTypeOf(lowerer.typeOf(expr)); - if (result === null || result.kind === "dyn") return call; + // diffieHellman's callback form answers undefined. + if (result === null || result.kind === "dyn" || result.kind === "void") return call; return lowerer.coerceInto(expr, call, result); } diff --git a/packages/compiler/src/frontend/lowering/builtins/dates.ts b/packages/compiler/src/frontend/lowering/builtins/dates.ts index a598f4ee6..0204386ab 100644 --- a/packages/compiler/src/frontend/lowering/builtins/dates.ts +++ b/packages/compiler/src/frontend/lowering/builtins/dates.ts @@ -53,7 +53,13 @@ export function lowerDateCall( call: ts.CallExpression, access: ts.PropertyAccessExpression, ): IrExpr | null { - if (call.questionDotToken || access.questionDotToken) return null; + // An optional chain lowers the call again once it has narrowed the + // receiver (Lowerer.chainHandled). + if ( + (call.questionDotToken && !lowerer.chainHandled.has(call)) || + (access.questionDotToken && !lowerer.chainHandled.has(access)) + ) + return null; const loc = locOf(call); const member = lowerer.stdlibGlobalMember(access, "Date"); if (member === "now" || member === "parse" || member === "UTC") diff --git a/packages/compiler/src/frontend/lowering/builtins/filesystem-calls.ts b/packages/compiler/src/frontend/lowering/builtins/filesystem-calls.ts index d147a6208..0bbc3b3fa 100644 --- a/packages/compiler/src/frontend/lowering/builtins/filesystem-calls.ts +++ b/packages/compiler/src/frontend/lowering/builtins/filesystem-calls.ts @@ -265,7 +265,7 @@ export function lowerFsPromisesOpenCall( "use open(path[, stringFlags[, numericMode]])", ); } - const path = lowerer.lowerExprExpecting(expr.arguments.nodes[0]!, STRING); + const path = lowerFsPath(lowerer, expr.arguments.nodes[0]!); const defaultFlags: IrExpr = { kind: "strLit", value: "r", type: STRING, loc }; const defaultMode: IrExpr = { kind: "numLit", value: 0o666, type: F64, loc }; const flags = expr.arguments.nodes[1] @@ -705,7 +705,7 @@ export function lowerFsPromisesEncodedRead( ) return null; } - const path = lowerer.lowerExprExpecting(expr.arguments.nodes[0]!, STRING); + const path = lowerFsPath(lowerer, expr.arguments.nodes[0]!); const encoding: IrExpr = optionsType.kind === "string" ? lowerer.lowerExprExpecting(options, STRING) @@ -853,7 +853,7 @@ export function lowerFsPromisesMkdirCall( ); } const { recursive, modeNode } = options; - const path = lowerer.lowerExprExpecting(expr.arguments.nodes[0]!, STRING); + const path = lowerFsPath(lowerer, expr.arguments.nodes[0]!); const mode: IrExpr | null = modeNode ? lowerer.lowerExprExpecting(modeNode, F64) : null; const type: IrType = { kind: "promise", inner: VOID }; if (!recursive) { diff --git a/packages/compiler/src/frontend/lowering/builtins/fs-glob.ts b/packages/compiler/src/frontend/lowering/builtins/fs-glob.ts index f988c204d..4d88cd96e 100644 --- a/packages/compiler/src/frontend/lowering/builtins/fs-glob.ts +++ b/packages/compiler/src/frontend/lowering/builtins/fs-glob.ts @@ -22,7 +22,10 @@ import { builtinImportOf } from "./module-bindings.js"; * lib/internal/fs/glob.js). The pattern and options cross as dyn values so * the runtime validates them in Node's order. The result form (strings or * Dirent rows) comes from the call's mapped result type, so withFileTypes - * must be statically known; the runtime checks that the option agrees. */ + * must be statically known; the runtime checks that the option agrees. + * Because of that check, the form follows the checker's resolved overload + * even where an untyped options argument leaves the overload undecided + * (dynamicOverloadChoice). */ /** The interned Dirent record array (type-mapper.ts), or null. */ function direntArray(lowerer: Lowerer, type: IrType | null | undefined): boolean { @@ -67,7 +70,7 @@ export function lowerFsGlobSyncCall( loc: SrcLoc, ): IrExpr { const args = globArgs(lowerer, call, "fs.globSync"); - const callType = lowerer.mapTypeOf(lowerer.typeOf(call)); + const callType = lowerer.mapTypeOf(lowerer.checker.getTypeAtLocation(call)); const types = direntArray(lowerer, callType); if (types) fenceDirentExclude(lowerer, call, "fs.globSync"); if (!types && !(callType?.kind === "array" && callType.elem.kind === "string")) { @@ -132,7 +135,9 @@ export function lowerFspGlobCall(lowerer: Lowerer, call: ts.CallExpression, loc: } const args = globArgs(lowerer, call, "fs/promises glob"); // The async iterator's yielded type (its first type argument). - const yielded = lowerer.checker.getTypeArguments(lowerer.typeOf(call) as ts.TypeReference)[0]; + const yielded = lowerer.checker.getTypeArguments( + lowerer.checker.getTypeAtLocation(call) as ts.TypeReference, + )[0]; const element = yielded ? lowerer.mapTypeOf(yielded) : null; const rows: IrType | null = element ? { kind: "array", elem: element } : null; const types = direntArray(lowerer, rows); diff --git a/packages/compiler/src/frontend/lowering/builtins/fs-paths.ts b/packages/compiler/src/frontend/lowering/builtins/fs-paths.ts index 38583025d..46a575388 100644 --- a/packages/compiler/src/frontend/lowering/builtins/fs-paths.ts +++ b/packages/compiler/src/frontend/lowering/builtins/fs-paths.ts @@ -5,30 +5,40 @@ * conversion (url.fileURLToPathUrl). The later arguments through the last * one with side effects are evaluated into hidden locals before the * conversion, in source order, so they run even when it throws and each - * reads what it reads in Node. The conversion keeps Node's error order when every - * earlier path is a URL too and each argument Node validates before the - * path is absent or a literal that passes that validation; other URL - * forms fence with SC2020, as do the fs.promises functions, whose - * conversion errors are rejections rather than throws, and existsSync, - * which answers false for a URL it cannot convert. */ + * reads what it reads in Node. Before the conversion, an earlier string + * path gets its null-byte check (copyFileSync, renameSync) and a runtime + * readFileSync encoding its name check, which Node runs first. existsSync + * and the fs.promises functions convert to a value instead + * (url.fileURLToPathValue) and answer false or a rejected promise for a + * conversion error. An argument Node validates before the path must + * otherwise be absent or a literal that passes that validation; other + * forms fence with SC2020. */ import * as ts from "../../ts7/adapter.js"; import { type Lowerer } from "../lowerer.js"; import { locOf } from "../../program.js"; import { sideEffectFreeOptionValue } from "../surfaces.js"; import { stripTypeWrappers } from "../typed-arguments.js"; -import { type IrExpr, type IrStmt, STRING, URL_T } from "../../../ir/ir.js"; +import { boolLit, varRef } from "../../../ir/build.js"; +import { BOOL, DYN, type IrExpr, type IrStmt, STRING, URL_T, VOID } from "../../../ir/ir.js"; +import { InternalCompilerError } from "../../../errors.js"; -/** The fs functions whose path parameters accept a URL: the path - * parameter indexes, and whether Node validates the paths before every - * other argument (otherwise the options come first). */ -const FS_URL_PATHS: Readonly< - Record -> = { +interface FsPathRow { + /** The path parameter indexes. */ + readonly paths: readonly number[]; + /** Whether Node validates the paths before every other argument + * (otherwise the options come first). */ + readonly pathsFirst: boolean; + /** The path parameters' names in Node's errors, when there are two. */ + readonly names?: readonly string[]; +} + +/** The fs functions whose path parameters accept a URL. */ +const FS_URL_PATHS: Readonly> = { accessSync: { paths: [0], pathsFirst: true }, appendFileSync: { paths: [0], pathsFirst: false }, chmodSync: { paths: [0], pathsFirst: true }, chownSync: { paths: [0], pathsFirst: true }, - copyFileSync: { paths: [0, 1], pathsFirst: true }, + copyFileSync: { paths: [0, 1], pathsFirst: true, names: ["src", "dest"] }, lchownSync: { paths: [0], pathsFirst: true }, lstatSync: { paths: [0], pathsFirst: true }, mkdirSync: { paths: [0], pathsFirst: false }, @@ -39,7 +49,7 @@ const FS_URL_PATHS: Readonly< readdirSync: { paths: [0], pathsFirst: false }, realpathSync: { paths: [0], pathsFirst: false }, "realpathSync.native": { paths: [0], pathsFirst: false }, - renameSync: { paths: [0, 1], pathsFirst: true }, + renameSync: { paths: [0, 1], pathsFirst: true, names: ["oldPath", "newPath"] }, rmSync: { paths: [0], pathsFirst: false }, rmdirSync: { paths: [0], pathsFirst: true }, statSync: { paths: [0], pathsFirst: true }, @@ -48,43 +58,66 @@ const FS_URL_PATHS: Readonly< writeFileSync: { paths: [0], pathsFirst: false }, }; -/** The fs.promises functions with path parameters, by path indexes. */ -const FSP_PATHS: Readonly> = { - access: [0], - appendFile: [0], - chmod: [0], - chown: [0], - copyFile: [0, 1], - lchown: [0], - lstat: [0], - mkdir: [0], - mkdtemp: [0], - open: [0], - opendir: [0], - readFile: [0], - readdir: [0], - realpath: [0], - rename: [0, 1], - rm: [0], - rmdir: [0], - stat: [0], - truncate: [0], - unlink: [0], - writeFile: [0], +/** The fs.promises functions with path parameters. */ +const FSP_PATHS: Readonly> = { + access: { paths: [0], pathsFirst: true }, + appendFile: { paths: [0], pathsFirst: false }, + chmod: { paths: [0], pathsFirst: true }, + chown: { paths: [0], pathsFirst: true }, + copyFile: { paths: [0, 1], pathsFirst: true }, + lchown: { paths: [0], pathsFirst: true }, + lstat: { paths: [0], pathsFirst: true }, + mkdir: { paths: [0], pathsFirst: false }, + mkdtemp: { paths: [0], pathsFirst: false }, + open: { paths: [0], pathsFirst: true }, + opendir: { paths: [0], pathsFirst: true }, + readFile: { paths: [0], pathsFirst: false }, + readdir: { paths: [0], pathsFirst: false }, + realpath: { paths: [0], pathsFirst: false }, + rename: { paths: [0, 1], pathsFirst: true }, + rm: { paths: [0], pathsFirst: true }, + rmdir: { paths: [0], pathsFirst: true }, + stat: { paths: [0], pathsFirst: true }, + truncate: { paths: [0], pathsFirst: true }, + unlink: { paths: [0], pathsFirst: true }, + writeFile: { paths: [0], pathsFirst: false }, }; +/** What a path argument of an fs call lowers to (Lowerer.fsPathPlans). */ +export type FsPathPlan = + /** The first URL path: the later arguments with effects, the earlier + * string paths to check, and a runtime encoding to check, all before the + * conversion. */ + | { + kind: "convert"; + staged: ts.Expression[]; + checks: { path: ts.Expression; name: string }[]; + encoding: ts.Expression | null; + } + /** A string path that a later URL path checks: the local that holds it, + * once lowered. */ + | { kind: "save"; local: string | null } + /** A URL path whose conversion error becomes false or a rejection: the + * local that holds the conversion's value. */ + | { kind: "value"; local: string }; + function isUrlArgument(lowerer: Lowerer, node: ts.Expression | undefined): boolean { return node !== undefined && lowerer.mapTypeOf(lowerer.typeOf(node))?.kind === "url"; } +function stripParens(node: ts.Expression): ts.Expression { + let e = node; + while (ts.isParenthesizedExpression(e)) e = e.expression; + return e; +} + /** A literal that Node's option validation accepts before it reads the * path: a string (the checker admits only the declared encodings, flags * and type names), a boolean, an integer in [0, 2^31 - 1] (every numeric * option of these functions accepts that range), undefined or null, or an * object literal of those. */ function preValidatedLiteral(node: ts.Expression): boolean { - let e = node; - while (ts.isParenthesizedExpression(e)) e = e.expression; + const e = stripParens(node); if (ts.isStringLiteralLike(e)) return true; if ( e.kind === ts.SyntaxKind.TrueKeyword || @@ -108,39 +141,59 @@ function preValidatedLiteral(node: ts.Expression): boolean { return false; } -/** SC2020 when the call passes a URL path in a form whose conversion - * order would differ from Node's (see the file comment). */ -function checkUrlPaths( +const HINT = "convert the URL first: fileURLToPath(url) from node:url"; + +/** SC2020 for an argument Node validates before the paths that is neither + * absent nor a literal passing that validation, other than a string + * readFileSync encoding (returned: it is checked before the conversion). + * writeFileSync/appendFileSync's data is checked after the path. */ +function optionsBeforePaths( lowerer: Lowerer, - call: ts.CallExpression, - module: string, + args: readonly ts.Expression[], member: string, -): void { - const args = call.arguments.nodes; - const name = module === "fs/promises" ? `fs.promises.${member}` : member; - const hint = "convert the URL first: fileURLToPath(url) from node:url"; - if (module === "fs/promises") { - if ((FSP_PATHS[member] ?? []).some((i) => isUrlArgument(lowerer, args[i]))) - lowerer.noLowering(`${name} with a URL path`, call, hint); - return; + row: FsPathRow, + name: string, +): ts.Expression | null { + if (row.pathsFirst) return null; + const data = /^(writeFile|appendFile)(Sync)?$/.test(member) ? 1 : -1; + let encoding: ts.Expression | null = null; + for (let i = 0; i < args.length; i++) { + const arg = args[i]!; + if (row.paths.includes(i) || i === data || preValidatedLiteral(arg)) continue; + if ( + member === "readFileSync" && + i === 1 && + lowerer.mapTypeOf(lowerer.typeOf(arg))?.kind === "string" + ) { + encoding = stripTypeWrappers(arg); + continue; + } + lowerer.noLowering(`${name} with a URL path and options that are not literals`, arg, HINT); } - if (member === "existsSync" && isUrlArgument(lowerer, args[0])) - lowerer.noLowering(`${name} with a URL path`, call, hint); - const row = FS_URL_PATHS[member]; - if (!row) return; - const first = row.paths.find((i) => isUrlArgument(lowerer, args[i])); - if (first === undefined) return; - if (args.some(ts.isSpreadElement)) - lowerer.noLowering(`${name} with spread arguments and a URL path`, call); - // The runtime checks a string path after the conversion, so a string - // path Node checks first cannot precede the URL. - if (row.paths.some((i) => i < first && args[i] !== undefined)) - lowerer.noLowering(`${name} with a string path before a URL path`, call, hint); - // Every argument from the URL through the last one with effects is - // evaluated ahead of the conversion, in source order: a later argument's - // effect (an assignment, say) must not change what an earlier one read. + return encoding; +} + +/** The arguments after the URL path that are evaluated ahead of the + * conversion: every one through the last with effects or that `force` + * names, in source order. An object or array literal among them is + * SC2020: its members are lowered one by one, so it cannot be evaluated + * ahead of the conversion as one value. */ +function stagedAfter( + lowerer: Lowerer, + args: readonly ts.Expression[], + first: number, + name: string, + force: (e: ts.Expression, i: number) => boolean, +): ts.Expression[] { + // Every argument from the URL through the last one with effects (or that + // the caller forces) is evaluated ahead of the conversion, in source + // order: a later argument's effect (an assignment, say) must not change + // what an earlier one read. let last = first; - for (let i = first + 1; i < args.length; i++) if (!sideEffectFreeOptionValue(args[i]!)) last = i; + for (let i = first + 1; i < args.length; i++) { + const e = stripTypeWrappers(args[i]!); + if (!sideEffectFreeOptionValue(e) || force(e, i)) last = i; + } const staged: ts.Expression[] = []; for (let i = first + 1; i <= last; i++) { // Keyed without type-only wrappers (`satisfies`, `as`, `!`), so a @@ -154,56 +207,190 @@ function checkUrlPaths( ? `${name} with a URL path and a literal before a later argument that has effects` : `${name} with a URL path and a later literal that has effects`, e, - hint, + HINT, ); } staged.push(e); } - if (staged.length > 0) { - lowerer.stagedAfterPath.set(args[first]!, staged); - for (const e of staged) lowerer.stagedArguments.set(e, null); - } - if (row.pathsFirst) return; - // writeFileSync/appendFileSync's data is checked after the path. - const data = member === "writeFileSync" || member === "appendFileSync" ? 1 : -1; - for (let i = 0; i < args.length; i++) { - if (row.paths.includes(i) || i === data || preValidatedLiteral(args[i]!)) continue; - lowerer.noLowering(`${name} with a URL path and options that are not literals`, args[i]!, hint); - } + return staged; } -/** Fences URL paths that cannot convert in Node's order: the check every - * fs and fs.promises call runs before lowering its arguments. */ +/** Plans the URL paths of a synchronous fs call (see the file comment): + * the check every fs call runs before lowering its arguments. */ export function checkFsUrlPaths( lowerer: Lowerer, call: ts.CallExpression, bi: { module: string; member: string }, ): void { - if (bi.module === "fs" || bi.module === "fs/promises") - checkUrlPaths(lowerer, call, bi.module, bi.member); + if (bi.module !== "fs") return; + const row = FS_URL_PATHS[bi.member]; + if (!row) return; + const args = call.arguments.nodes; + const first = row.paths.find((i) => isUrlArgument(lowerer, args[i])); + if (first === undefined || lowerer.fsPathPlans.has(stripParens(args[first]!))) return; + const name = bi.member; + if (args.some(ts.isSpreadElement)) + lowerer.noLowering(`${name} with spread arguments and a URL path`, call); + const checks: { path: ts.Expression; name: string }[] = []; + for (const i of row.paths) { + if (i >= first || args[i] === undefined) continue; + const path = stripParens(args[i]!); + checks.push({ path, name: row.names?.[i] ?? "path" }); + lowerer.fsPathPlans.set(path, { kind: "save", local: null }); + } + const encoding = optionsBeforePaths(lowerer, args, bi.member, row, name); + const staged = stagedAfter(lowerer, args, first, name, (e) => e === encoding); + for (const e of staged) lowerer.stagedArguments.set(e, null); + lowerer.fsPathPlans.set(stripParens(args[first]!), { kind: "convert", staged, checks, encoding }); +} + +/** existsSync and the fs.promises functions with URL paths: each URL + * converts to a value (url.fileURLToPathValue) after the arguments are + * evaluated, and the call runs on the converted paths only when every + * conversion produced a string; otherwise existsSync answers false and an + * fs.promises function a promise rejected with the first conversion's + * error. `lowerCall` lowers the call itself, whose URL paths then read the + * converted values. Null for every other call. */ +export function lowerFallibleFsUrlCall( + lowerer: Lowerer, + call: ts.CallExpression, + bi: { module: string; member: string }, + lowerCall: () => IrExpr, +): IrExpr | null { + const row = + bi.module === "fs/promises" + ? FSP_PATHS[bi.member] + : bi.module === "fs" && bi.member === "existsSync" + ? ({ paths: [0], pathsFirst: true } satisfies FsPathRow) + : undefined; + if (!row) return null; + const args = call.arguments.nodes; + const urls = row.paths.filter((i) => isUrlArgument(lowerer, args[i])); + if (urls.length === 0 || lowerer.fallibleFsCalls.has(call)) return null; + const name = bi.module === "fs/promises" ? `fs.promises.${bi.member}` : bi.member; + if (args.some(ts.isSpreadElement)) + lowerer.noLowering(`${name} with spread arguments and a URL path`, call); + const first = urls[0]!; + // A string path before the URL would be checked in the call itself, + // after the conversion. + if (row.paths.some((i) => i < first && args[i] !== undefined)) + lowerer.noLowering(`${name} with a string path before a URL path`, call, HINT); + if (optionsBeforePaths(lowerer, args, bi.member, row, name) !== null) + lowerer.noLowering(`${name} with a URL path and options that are not literals`, args[1]!, HINT); + const loc = locOf(call); + const stmts: IrStmt[] = []; + const values = new Map(); + const save = (value: IrExpr, label: string, at: ts.Node): string => { + const slot = lowerer.declareHiddenLocal(label, value.type); + stmts.push({ kind: "varDecl", localId: slot.id, init: value, loc: locOf(at) }); + return slot.id; + }; + values.set(first, save(lowerer.lowerExprExpecting(args[first]!, URL_T), "%fsUrl", args[first]!)); + const staged = stagedAfter(lowerer, args, first, name, (_e, i) => urls.includes(i)); + for (const e of staged) { + // A call lowered again (an array-literal element) evaluates the + // argument anew; the slot an earlier lowering saved it in is gone. + lowerer.stagedArguments.delete(e); + const value = lowerer.lowerExpr(e); + const slot = save(value, "%fsArg", e); + lowerer.stagedArguments.set(e, { localId: slot, type: value.type }); + const index = args.findIndex((arg) => stripTypeWrappers(arg) === e); + if (urls.includes(index)) values.set(index, slot); + } + const results: string[] = []; + for (const i of urls) { + const url = varRef(values.get(i)!, URL_T, loc); + const converted: IrExpr = { + kind: "libCall", + fn: "url.fileURLToPathValue", + args: [url], + type: DYN, + loc, + }; + const local = save(converted, "%fsPathValue", args[i]!); + lowerer.fsPathPlans.set(stripParens(args[i]!), { kind: "value", local }); + results.push(local); + } + lowerer.fallibleFsCalls.add(call); + let inner: IrExpr; + try { + inner = lowerCall(); + } finally { + lowerer.fallibleFsCalls.delete(call); + } + let result = inner; + for (let k = results.length - 1; k >= 0; k--) { + const value = varRef(results[k]!, DYN, loc); + const failed: IrExpr = + bi.module === "fs/promises" + ? { kind: "intrinsic", name: "promise.reject", args: [value], type: inner.type, loc } + : boolLit(false, loc); + result = { + kind: "ternary", + cond: { kind: "dynTest", test: "string", value, type: BOOL, loc }, + then: result, + else_: failed, + type: inner.type, + loc, + }; + } + return { kind: "seqExpr", stmts, result, type: inner.type, loc }; } /** An fs path argument as a string: a URL converts with fileURLToPath's * conversion (url.fileURLToPathUrl), which throws Node's errors for other - * schemes, hosts and encoded separators. */ + * schemes, hosts and encoded separators, after the checks its plan names. */ export function lowerFsPath(lowerer: Lowerer, node: ts.Expression): IrExpr { - if (!isUrlArgument(lowerer, node)) return lowerer.lowerExprExpecting(node, STRING); const loc = locOf(node); + const plan = lowerer.fsPathPlans.get(stripParens(node)); + if (plan?.kind === "value") + return { kind: "dynCheck", value: varRef(plan.local, DYN, loc), type: STRING, loc }; + if (!isUrlArgument(lowerer, node)) { + const value = lowerer.lowerExprExpecting(node, STRING); + if (plan?.kind !== "save") return value; + const slot = lowerer.declareHiddenLocal("%fsPath", STRING); + plan.local = slot.id; + return { + kind: "seqExpr", + stmts: [{ kind: "varDecl", localId: slot.id, init: value, loc }], + result: varRef(slot.id, STRING, loc), + type: STRING, + loc, + }; + } const url = lowerer.lowerExprExpecting(node, URL_T); - const staged = lowerer.stagedAfterPath.get(node); - if (staged === undefined) + if (plan?.kind !== "convert") return { kind: "libCall", fn: "url.fileURLToPathUrl", args: [url], type: STRING, loc }; // The URL and the later arguments with effects, in source order, then - // the conversion. + // the checks Node runs first, then the conversion. const saved = lowerer.declareHiddenLocal("%fsUrl", URL_T); const stmts: IrStmt[] = [{ kind: "varDecl", localId: saved.id, init: url, loc }]; - for (const arg of staged) { + for (const arg of plan.staged) { lowerer.stagedArguments.delete(arg); const value = lowerer.lowerExpr(arg); const slot = lowerer.declareHiddenLocal("%fsArg", value.type); stmts.push({ kind: "varDecl", localId: slot.id, init: value, loc: locOf(arg) }); lowerer.stagedArguments.set(arg, { localId: slot.id, type: value.type }); } + const check = (fn: "fs.checkPath" | "fs.checkReadEncoding", args: IrExpr[]): void => { + stmts.push({ kind: "exprStmt", expr: { kind: "libCall", fn, args, type: VOID, loc }, loc }); + }; + for (const { path, name } of plan.checks) { + const saved = lowerer.fsPathPlans.get(path); + if (saved?.kind !== "save" || saved.local === null) + throw new InternalCompilerError("an fs path checked before it was lowered"); + check("fs.checkPath", [ + varRef(saved.local, STRING, loc), + { kind: "strLit", value: name, type: STRING, loc }, + ]); + } + if (plan.encoding !== null) { + const slot = lowerer.stagedArguments.get(plan.encoding); + if (!slot) throw new InternalCompilerError("an fs encoding checked before it was saved"); + check("fs.checkReadEncoding", [ + lowerer.coerceToExpected(varRef(slot.localId, slot.type, loc), STRING), + ]); + } return { kind: "seqExpr", stmts, @@ -219,7 +406,12 @@ export function lowerFsPath(lowerer: Lowerer, node: ts.Expression): IrExpr { }; } -/** Whether `index` is a path parameter of the fs function `member`. */ -export function isFsPathParam(member: string, index: number): boolean { - return FS_URL_PATHS[member]?.paths.includes(index) === true; +/** Whether `index` is a path parameter of the fs or fs.promises function + * `member`. */ +export function isFsPathParam(module: string, member: string, index: number): boolean { + if (module === "fs/promises") return FSP_PATHS[member]?.paths.includes(index) === true; + if (module !== "fs") return false; + return ( + (member === "existsSync" && index === 0) || FS_URL_PATHS[member]?.paths.includes(index) === true + ); } diff --git a/packages/compiler/src/frontend/lowering/builtins/module-calls.ts b/packages/compiler/src/frontend/lowering/builtins/module-calls.ts index e5446291f..2aaa2f80f 100644 --- a/packages/compiler/src/frontend/lowering/builtins/module-calls.ts +++ b/packages/compiler/src/frontend/lowering/builtins/module-calls.ts @@ -25,7 +25,7 @@ import { lowerFsWriteOptionsCall, lowerFsReadPathCall, } from "./filesystem-calls.js"; -import { checkFsUrlPaths, isFsPathParam, lowerFsPath } from "./fs-paths.js"; +import { checkFsUrlPaths, isFsPathParam, lowerFallibleFsUrlCall, lowerFsPath } from "./fs-paths.js"; import { lowerBufferEncodingCall } from "./text-codecs.js"; import { lowerUtilTypeCall } from "./util-types.js"; import { InternalCompilerError } from "../../../errors.js"; @@ -59,6 +59,10 @@ export function lowerBuiltinModuleCall( loc: SrcLoc, ): IrExpr { const name = ts.getTextOfNode(expr.expression); + const fallible = lowerFallibleFsUrlCall(lowerer, expr, bi, () => + lowerBuiltinModuleCall(lowerer, expr, bi, fn, loc), + ); + if (fallible) return fallible; checkFsUrlPaths(lowerer, expr, bi); if (bi.module === "util/types") { return lowerUtilTypeCall(lowerer, expr, bi, loc, name); @@ -258,7 +262,7 @@ export function lowerBuiltinModuleCall( bi.member === "readFile" && expr.arguments.nodes.length === 1 ) { - const path = lowerer.lowerExprExpecting(expr.arguments.nodes[0]!, STRING); + const path = lowerFsPath(lowerer, expr.arguments.nodes[0]!); return { kind: "libCall", fn: "fsp.readFileBytes", @@ -473,7 +477,7 @@ export function lowerBuiltinModuleCall( args = lowerer.completeArgs(expr.arguments.nodes, shapes, loc, expr); } else { args = expr.arguments.nodes.map((a, i) => - bi.module === "fs" && isFsPathParam(bi.member, i) + isFsPathParam(bi.module, bi.member, i) ? lowerFsPath(lowerer, a) : lowerer.lowerExprExpecting(a, fn.params[i]), ); diff --git a/packages/compiler/src/frontend/lowering/builtins/promises.ts b/packages/compiler/src/frontend/lowering/builtins/promises.ts index 129ac73a3..e9d4322ad 100644 --- a/packages/compiler/src/frontend/lowering/builtins/promises.ts +++ b/packages/compiler/src/frontend/lowering/builtins/promises.ts @@ -2,7 +2,7 @@ import { keyedHelperName } from "../stable-names.js"; import { checkedPromiseAll, thenReader } from "../checked-promise-all.js"; import { InternalCompilerError } from "../../../errors.js"; import * as ts from "../../ts7/adapter.js"; -import { newFnCtx, type Lowerer } from "../lowerer.js"; +import type { Lowerer } from "../lowerer.js"; import { varRef } from "../../../ir/build.js"; import { maybePromiseResolve, maybePromiseUnionOf } from "../maybe-promise.js"; import { isJsSourceFile, locOf } from "../../program.js"; @@ -17,31 +17,23 @@ import { UNDEFINED_T, VOID, arrayOf, + funcOf, isUnitType, typeEquals, typeKey, } from "../../../ir/ir.js"; /** `Promise.all([p, q, r])` over DIFFERENT promise types — the checker's - * tuple overload, `Promise<[A, B, C]>`. One interned async helper per - * entry/result signature takes the entries as arguments, awaits the - * runtime combinator over the same promises (each widened to - * Promise, a type-only view), so settlement is Node's exactly: the - * first rejection in settlement order wins and every later one counts as - * handled; then it reads each (now fulfilled) entry and returns the - * tuple. Entries keep their identity — no value converts through the - * checked-dynamic tree. Non-promise entries (numbers, strings, booleans, - * unit values — never thenable) pass straight into their slot; a - * checked-dynamic entry (a JavaScript function's result) joins through - * PromiseResolve: the promise it holds, a promise that adopts a thenable, - * or a promise fulfilled with its plain value. The tuple - * fulfills one microtask turn after the combinator does. Null when the - * result type or an entry has no typed home here. */ -function lowerTuplePromiseAll( + * tuple overload, `Promise<[A, B, C]>`: the result type and tuple shape, + * or null when the result type or an entry has no typed home here. + * Entries are promises, checked-dynamic values (a JavaScript function's + * result) and plain numbers, strings, booleans and unit values, which are + * never thenable. */ +function tuplePromiseAllShape( lowerer: Lowerer, call: ts.CallExpression, - elems: IrExpr[], -): IrExpr | null { + elems: readonly IrExpr[], +): { resultT: IrType & { kind: "promise" }; tuple: IrType & { kind: "record" } } | null { const resultT = lowerer.mapTypeOf(lowerer.typeOf(call)); if (resultT?.kind !== "promise" || resultT.inner.kind !== "record") return null; const shape = lowerer.shapes.get(resultT.inner.shapeId); @@ -53,135 +45,159 @@ function lowerTuplePromiseAll( const settles = (t: IrType): boolean => t.kind === "promise" || t.kind === "dyn"; if (!elems.some((e) => settles(e.type))) return null; if (!elems.every((e) => settles(e.type) || plainValue(e.type))) return null; + return { resultT, tuple: resultT.inner }; +} + +/** `Promise.all([p, q, r])` over different promise types. Each entry + * becomes a promise in input order (a plain value through Promise.resolve, + * a checked-dynamic one through its PromiseResolve view: the promise it + * holds, a promise that adopts a thenable, or a promise fulfilled with the + * value), widened to Promise, a type-only view. The runtime + * combinator over them gives Node's settlement: the first rejection in + * settlement order wins and every later one counts as handled. The + * result settles inside the combinator's own settlement, so it and its + * awaits land on Node's microtask turns; a fulfillment runs one interned + * function per entry/result signature that reads the fulfilled entries + * into the tuple. Entries keep their identity: no value converts through + * the checked-dynamic tree. */ +function lowerTuplePromiseAll( + lowerer: Lowerer, + call: ts.CallExpression, + elems: IrExpr[], +): IrExpr | null { + const plan = tuplePromiseAllShape(lowerer, call, elems); + if (!plan) return null; + const { resultT, tuple } = plan; const loc = locOf(call); - const key = `${elems.map((e) => typeKey(e.type)).join(",")}->${typeKey(resultT.inner)}`; + const voidPromise: IrType & { kind: "promise" } = { kind: "promise", inner: VOID }; + const entriesT = arrayOf(voidPromise); + const dynPromise: IrType = { kind: "promise", inner: DYN }; + // A checked-dynamic entry's PromiseResolve can run a `then` getter, which + // Node runs after the whole array literal evaluated. With such an entry, + // every entry evaluates into a hidden local first. + const staged: IrStmt[] = []; + const values = !elems.some((e) => e.type.kind === "dyn") + ? elems + : elems.map((e): IrExpr => { + if (e.kind === "unitLit") return e; + if (isUnitType(e.type)) { + staged.push({ kind: "exprStmt", expr: e, loc }); + return { + kind: "unitLit", + unit: e.type.kind === "nullT" ? "null" : "undefined", + type: e.type, + loc, + }; + } + const local = lowerer.declareHiddenLocal("%allValue", e.type); + staged.push({ kind: "varDecl", localId: local.id, init: e, loc }); + return varRef(local.id, e.type, loc); + }); + const entries = values.map((e): IrExpr => { + let promise: IrExpr; + if (e.type.kind === "promise") promise = e; + else if (e.type.kind === "dyn") + promise = { + kind: "libCall", + fn: "dyn.promiseResolve", + args: [e, thenReader(lowerer, loc)], + type: dynPromise, + loc, + }; + else if (isUnitType(e.type)) { + // A unit entry's value is its type's one value; the promise only + // takes its place in the combinator. + const resolved: IrExpr = { + kind: "intrinsic", + name: "promise.resolve", + args: [], + type: voidPromise, + loc, + }; + promise = + e.kind === "unitLit" + ? resolved + : { + kind: "seqExpr", + stmts: [{ kind: "exprStmt", expr: e, loc }], + result: resolved, + type: voidPromise, + loc, + }; + } else + promise = { + kind: "intrinsic", + name: "promise.resolve", + args: [e], + type: { kind: "promise", inner: e.type }, + loc, + }; + return promise.type.kind === "promise" && promise.type.inner.kind === "void" + ? promise + : { kind: "promiseVoidWiden", value: promise, type: voidPromise, loc }; + }); + const key = `${elems.map((e) => typeKey(e.type)).join(",")}->${typeKey(tuple)}`; let name = lowerer.arrHofHelpers.get(`promise.allTuple:${key}`); if (name === undefined) { name = keyedHelperName(lowerer, "%promise.allTuple", `promise.allTuple:${key}`); lowerer.arrHofHelpers.set(`promise.allTuple:${key}`, name); - const ctx = newFnCtx(false, null, null, resultT.inner); - ctx.isAsync = true; - lowerer.fnStack.push(ctx); - try { - const params = elems.map((e, i) => ({ - localId: `entry.${i}`, - name: `entry${i}`, - type: e.type, - })); - const ref = (i: number): IrExpr => varRef(`entry.${i}`, elems[i]!.type, loc); - const dynPromise: IrType = { kind: "promise", inner: DYN }; - const body: IrStmt[] = []; - // Each dyn entry's promise view, in entry order: the promise it - // holds, a promise that adopts a thenable's settlement, or a promise - // fulfilled with the plain value (PromiseResolve's rule). - const views = new Map(); - elems.forEach((e, i) => { - if (e.type.kind !== "dyn") return; - const view = lowerer.declareHiddenLocal("%allEntry", dynPromise); - body.push({ - kind: "varDecl", - localId: view.id, - init: { - kind: "libCall", - fn: "dyn.promiseResolve", - args: [ref(i), thenReader(lowerer, loc)], - type: dynPromise, - loc, - }, - loc, - }); - views.set(i, varRef(view.id, dynPromise, loc)); - }); - const widened: IrExpr[] = []; - elems.forEach((e, i) => { - const view = views.get(i); - if (view !== undefined) { - widened.push({ - kind: "promiseVoidWiden", - value: view, - type: { kind: "promise", inner: VOID }, - loc, - }); - return; - } - if (e.type.kind !== "promise") return; - widened.push( - e.type.inner.kind === "void" - ? ref(i) - : { - kind: "promiseVoidWiden", - value: ref(i), - type: { kind: "promise", inner: VOID }, + const shape = lowerer.shapes.get(tuple.shapeId)!; + const entriesRef = varRef("entries.0", entriesT, loc); + // Shape fields are sorted by name ("10" before "2"); a tuple field's + // name is its position. + const fields = shape.fields.map((field) => { + const i = Number(field.name); + const type = elems[i]!.type; + const inner = type.kind === "promise" ? type.inner : type; + const value: IrExpr = + inner.kind === "void" + ? { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc } + : isUnitType(inner) + ? { + kind: "unitLit", + unit: inner.kind === "nullT" ? "null" : "undefined", + type: inner, loc, - }, - ); - }); - const settled: IrExpr = { - kind: "awaitExpr", - value: { - kind: "intrinsic", - name: "promise.all", - args: [ - { - kind: "arrayLit", - elems: widened, - type: arrayOf({ kind: "promise", inner: VOID }), - loc, - }, - ], - type: { kind: "promise", inner: VOID }, - loc, - }, - type: VOID, - loc, - }; - // Shape fields are sorted by name ("10" before "2"); a tuple field's - // name is its position. - const fields = shape.fields.map((field) => { - const i = Number(field.name); - const entry = elems[i]!; - let value: IrExpr; - const view = views.get(i); - if (view !== undefined) { - value = { kind: "awaitExpr", value: view, type: DYN, loc }; - } else if (entry.type.kind === "promise") { - value = - entry.type.inner.kind === "void" - ? { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc } - : { kind: "awaitExpr", value: ref(i), type: entry.type.inner, loc }; - } else { - value = ref(i); - } - return { name: field.name, value: lowerer.coerceToExpected(value, field.type) }; - }); - body.push( - { kind: "exprStmt", expr: settled, loc }, - { - kind: "return", - value: { kind: "recordLit", fields, type: resultT.inner, loc }, - loc, - }, - ); - lowerer.liftedFns.push({ - name, - params, - returnType: resultT.inner, - locals: [ - ...params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })), - ...lowerer.ctx.locals, - ], - body, - async: true, - loc, - }); - } catch (e) { - lowerer.arrHofHelpers.delete(`promise.allTuple:${key}`); - throw e; - } finally { - lowerer.fnStack.pop(); - } + } + : { + kind: "libCall", + fn: "promise.fulfilledValue", + args: [ + { + kind: "arrayGet", + arr: entriesRef, + index: { kind: "numLit", value: i, type: F64, loc }, + type: voidPromise, + loc, + }, + ], + type: inner, + loc, + }; + return { name: field.name, value: lowerer.coerceToExpected(value, field.type) }; + }); + lowerer.liftedFns.push({ + name, + params: [{ localId: "entries.0", name: "entries", type: entriesT }], + returnType: tuple, + locals: [{ id: "entries.0", name: "entries", type: entriesT, mutable: false }], + body: [{ kind: "return", value: { kind: "recordLit", fields, type: tuple, loc }, loc }], + loc, + }); } - return { kind: "call", callee: name, args: elems, type: resultT, loc }; + const all: IrExpr = { + kind: "libCall", + fn: "promise.allTuple", + args: [ + { kind: "arrayLit", elems: entries, type: entriesT, loc }, + { kind: "closure", fnName: name, captures: [], type: funcOf([entriesT], tuple), loc }, + ], + type: resultT, + loc, + }; + return staged.length === 0 + ? all + : { kind: "seqExpr", stmts: staged, result: all, type: resultT, loc }; } /** `Promise.race([...])` on THE Promise global: the entries lower diff --git a/packages/compiler/src/frontend/lowering/call-signatures.ts b/packages/compiler/src/frontend/lowering/call-signatures.ts index 45e6b091c..c7fb8d36e 100644 --- a/packages/compiler/src/frontend/lowering/call-signatures.ts +++ b/packages/compiler/src/frontend/lowering/call-signatures.ts @@ -611,7 +611,6 @@ export function checkDefaultParamBodyType( if ( bodyType.kind === "void" || bodyType.kind === "regex" || - bodyType.kind === "date" || bodyType.kind === "dyn" || isUnitType(bodyType) ) { diff --git a/packages/compiler/src/frontend/lowering/cjs-exports-object.ts b/packages/compiler/src/frontend/lowering/cjs-exports-object.ts index ae8cce4ca..5e5f24e32 100644 --- a/packages/compiler/src/frontend/lowering/cjs-exports-object.ts +++ b/packages/compiler/src/frontend/lowering/cjs-exports-object.ts @@ -381,7 +381,7 @@ function esModuleExportIgnoresThis( depth: number, ): boolean { const moduleSym = lowerer.checker.getSymbolAtLocation(sf); - const exported = moduleSym?.getExports().get(name as ts.__String); + const exported = moduleSym?.getExports().get(ts.escapedSymbolName(name)); if (exported !== undefined) { const target = exported.flags & ts.SymbolFlags.Alias ? lowerer.checker.getAliasedSymbol(exported) : exported; diff --git a/packages/compiler/src/frontend/lowering/cjs-inline-require.ts b/packages/compiler/src/frontend/lowering/cjs-inline-require.ts index a4df8d910..d8893aa72 100644 --- a/packages/compiler/src/frontend/lowering/cjs-inline-require.ts +++ b/packages/compiler/src/frontend/lowering/cjs-inline-require.ts @@ -28,8 +28,10 @@ import { isModuleExportsAccess, locOf, npmStaticDepSf7, + requireBindingEarlyUse7, requireSpecOf, resolveImport, + splitRequireCallOf, } from "../program.js"; import { createRequireCalleeFileOf } from "./builtins/module-bindings.js"; import { isCjsRequireGlobal } from "./lower-node-module.js"; @@ -43,6 +45,332 @@ export interface InlineRequire { dep: ts.SourceFile; } +/** A top-level `var m; m = require("./x")` binding. Node evaluates the + * require at the assignment; the binding is alias plumbing exactly like + * `const m = require("./x")` for every later read. */ +export interface SplitRequireBinding { + decl: ts.VariableDeclaration; + assign: ts.BinaryExpression; + call: ts.CallExpression; + spec: string; + dep: ts.SourceFile; +} + +function symbolOfIdentifierValue(lowerer: Lowerer, ident: ts.Identifier): ts.Symbol | undefined { + const parent = ident.parent; + // A destructuring shorthand target resolves to the property symbol at + // the name, but reads/writes the variable through its value symbol. + if (ts.isShorthandPropertyAssignment(parent) && parent.name === ident) { + return ( + lowerer.checker.getShorthandAssignmentValueSymbol(parent) ?? + lowerer.checker.getSymbolAtLocation(ident) + ); + } + return lowerer.checker.getSymbolAtLocation(ident); +} + +function destructuringContainerOf(node: ts.Node): ts.Node | null { + const parent = node.parent; + if (ts.isShorthandPropertyAssignment(parent) && parent.name === node) return parent; + if (ts.isPropertyAssignment(parent) && parent.initializer === node) return parent; + if (ts.isSpreadElement(parent) && parent.expression === node) return parent; + if (ts.isSpreadAssignment(parent) && parent.expression === node) return parent; + return null; +} + +function writeTargetOf(node: ts.Node): ts.Node | null { + let target = node; + for (;;) { + const parent = target.parent; + if ( + ts.isParenthesizedExpression(parent) || + ts.isArrayLiteralExpression(parent) || + ts.isObjectLiteralExpression(parent) + ) { + target = parent; + continue; + } + const container = destructuringContainerOf(target); + if (container === null) break; + target = container; + } + const parent = target.parent; + if ( + ts.isBinaryExpression(parent) && + parent.left === target && + ts.isAssignmentOperator(parent.operatorToken.kind) + ) + return parent; + if ( + (ts.isPrefixUnaryExpression(parent) || ts.isPostfixUnaryExpression(parent)) && + parent.operand === target && + (parent.operator === ts.SyntaxKind.PlusPlusToken || + parent.operator === ts.SyntaxKind.MinusMinusToken) + ) + return parent; + if ((ts.isForInStatement(parent) || ts.isForOfStatement(parent)) && parent.initializer === target) + return parent; + return null; +} + +/** The split form of a top-level require binding: `var m; m = require("./x")`. + * Exactly one write, at a top-level `m = require("")` statement. + * Null for the ordinary `const m = require("./x")` declaration and for + * every form that needs runtime module selection. */ +export function splitRequireBindingOf( + lowerer: Lowerer, + decl: ts.VariableDeclaration, +): SplitRequireBinding | null { + // Cheap shape gate before the cache: ordinary declarations never enter the + // per-Lowerer map. + if (decl.initializer !== undefined || !ts.isIdentifier(decl.name)) return null; + const list = decl.parent as ts.VariableDeclarationList; + if ((list.flags & ts.NodeFlags.Const) !== 0) return null; + const stmt = list.parent; + if (!ts.isVariableStatement(stmt) || !ts.isSourceFile(stmt.parent)) return null; + const cached = lowerer.cjsSplitRequireBindings.get(decl); + if (cached !== undefined) return cached; + const result = computeSplitRequireBinding(lowerer, decl); + lowerer.cjsSplitRequireBindings.set(decl, result); + return result; +} + +function computeSplitRequireBinding( + lowerer: Lowerer, + decl: ts.VariableDeclaration, +): SplitRequireBinding | null { + const sf = ts.getSourceFileOfNode(decl); + if (!isCjsJsFile(sf, lowerer.program)) return null; + if (!sf.text.includes("require")) return null; + const symbol = lowerer.checker.getSymbolAtLocation(decl.name); + if (symbol === undefined) return null; + // Merged declarations make the binding's initialization order ambiguous; + // keep ordinary storage instead of guessing. + let declarations = 0; + for (const other of symbol.declarations) { + if (!ts.isVariableDeclaration(other) || ++declarations > 1) return null; + } + if (declarations !== 1) return null; + + // Find the one top-level `m = ...` statement first: most uninitialized + // variables never reach that shape, so they avoid the full-file write scan. + let assign: ts.BinaryExpression | null = null; + for (const stmt of sf.statements.nodes) { + if (!ts.isExpressionStatement(stmt)) continue; + const expr = stmt.expression; + if ( + !ts.isBinaryExpression(expr) || + expr.operatorToken.kind !== ts.SyntaxKind.EqualsToken || + !ts.isIdentifier(expr.left) || + expr.left.text !== decl.name.text || + lowerer.checker.getSymbolAtLocation(expr.left) !== symbol + ) + continue; + if (assign !== null) return null; + assign = expr; + } + if (assign === null) return null; + + const req = inlineProgramRequireOf(lowerer, assign.right); + if (req === null) return null; + // Preflight treats the same bindings as require declarations (module edge, + // cycle admission, early-read fence); a binding it does not know keeps + // ordinary storage. + if (splitRequireCallOf(lowerer.program, decl) !== req.call) return null; + + // Any other write makes the value depend on runtime control flow. + let writes = 0; + ts.walkPreorder(sf, (node) => { + if (!ts.isIdentifier(node) || node.text !== decl.name.text) return; + if (symbolOfIdentifierValue(lowerer, node) !== symbol) return; + if (writeTargetOf(node) === null) return; + if (++writes > 1) return "stop"; + }); + if (writes !== 1) return null; + + return { decl, assign, call: req.call, spec: req.spec, dep: req.dep }; +} + +/** The split binding an identifier names, or null. */ +export function splitRequireBindingOfIdent( + lowerer: Lowerer, + ident: ts.Identifier, +): SplitRequireBinding | null { + const symbol = symbolOfIdentifierValue(lowerer, ident); + return symbol === undefined ? null : splitBindingOfSymbol(lowerer, symbol); +} + +/** The exact top-level assignment statement that initializes `expr`, or null. */ +export function splitRequireAssignmentOf( + lowerer: Lowerer, + expr: ts.Expression, +): SplitRequireBinding | null { + if ( + !ts.isBinaryExpression(expr) || + expr.operatorToken.kind !== ts.SyntaxKind.EqualsToken || + !ts.isIdentifier(expr.left) + ) + return null; + const bindingOfIdent = splitRequireBindingOfIdent(lowerer, expr.left); + return bindingOfIdent !== null && bindingOfIdent.assign === expr ? bindingOfIdent : null; +} + +/** The whole-export symbol a module's `module.exports = ` names, + * or null when the module is a table. */ +export function wholeExportSymbolOfModule(lowerer: Lowerer, dep: ts.SourceFile): ts.Symbol | null { + const cached = lowerer.cjsWholeExportSymbols.get(dep); + if (cached !== undefined) return cached; + if (cjsNamespaceModule(lowerer, dep)) { + lowerer.cjsWholeExportSymbols.set(dep, null); + return null; + } + const moduleSymbol = lowerer.checker.getSymbolAtLocation(dep); + let symbol = moduleSymbol?.getExports()?.get("export=" as ts.__String); + if (symbol === undefined) { + lowerer.cjsWholeExportSymbols.set(dep, null); + return null; + } + if (symbol.flags & ts.SymbolFlags.Alias) symbol = lowerer.checker.getAliasedSymbol(symbol); + lowerer.cjsWholeExportSymbols.set(dep, symbol); + return symbol; +} + +function splitBindingOfSymbol(lowerer: Lowerer, symbol: ts.Symbol): SplitRequireBinding | null { + const decl = symbol.declarations.find(ts.isVariableDeclaration); + return decl === undefined ? null : splitRequireBindingOf(lowerer, decl); +} + +/** The target whole-export symbol behind a split-binding symbol. */ +function splitWholeExportSymbolOfSymbol(lowerer: Lowerer, symbol: ts.Symbol): ts.Symbol | null { + const binding = splitBindingOfSymbol(lowerer, symbol); + return binding === null ? null : wholeExportSymbolOfModule(lowerer, binding.dep); +} + +/** True when a CommonJS module has a live member export (`exports.x` or + * `module.exports.x`) after all `module.exports =` replacements. Those + * members need a mutable whole-export root. */ +export function cjsHasLiveMemberExports(lowerer: Lowerer, sf: ts.SourceFile): boolean { + const cached = lowerer.cjsLiveMemberExports.get(sf); + if (cached !== undefined) return cached; + const stmts = sf.statements.nodes; + let lastTable = -1; + for (let i = 0; i < stmts.length; i++) { + if (cjsExportAssignmentOf(stmts[i]!)?.kind === "table") lastTable = i; + } + let live = false; + // Only members attached after the last replacement survive in Node. + if (lastTable >= 0) { + for (let i = lastTable + 1; i < stmts.length; i++) { + const stmt = stmts[i]!; + if (cjsExportAssignmentOf(stmt)?.kind === "member" && cjsExportDiscardReason(stmt) === null) { + live = true; + break; + } + } + } + lowerer.cjsLiveMemberExports.set(sf, live); + return live; +} + +/** The wrapper's export= symbol when a split binding is re-exported whole + * (`module.exports = m`) and that assignment owns storage for later member + * writes. Importers must read the wrapper's storage, not the split + * binding's underlying value. */ +function splitWholeExportStorageSymbolOf(lowerer: Lowerer, symbol: ts.Symbol): ts.Symbol | null { + const binding = splitBindingOfSymbol(lowerer, symbol); + if (binding === null) return null; + if (wholeExportSymbolOfModule(lowerer, binding.dep) === null) return null; + const sf = ts.getSourceFileOfNode(binding.decl); + if (!cjsHasLiveMemberExports(lowerer, sf)) return null; + const whole = cjsWholeExportValueOf(sf); + if (whole === null) return null; + let rhs: ts.Expression = whole.rhs; + while (ts.isParenthesizedExpression(rhs)) rhs = rhs.expression; + if (!ts.isIdentifier(rhs) || lowerer.checker.getSymbolAtLocation(rhs) !== symbol) return null; + const moduleSymbol = lowerer.checker.getSymbolAtLocation(sf); + let exportSymbol = moduleSymbol?.getExports()?.get("export=" as ts.__String); + if (exportSymbol === undefined) return null; + if (exportSymbol.flags & ts.SymbolFlags.Alias) + exportSymbol = lowerer.checker.getAliasedSymbol(exportSymbol); + return exportSymbol; +} + +/** The source file whose `module.exports =` assignment declares `symbol`. */ +function moduleExportSourceFileOf(symbol: ts.Symbol): ts.SourceFile | null { + for (const decl of symbol.declarations) { + if ( + ts.isBinaryExpression(decl) && + isModuleExportsAccess(decl.left) && + ts.isExpressionStatement(decl.parent) && + ts.isSourceFile(decl.parent.parent) + ) + return decl.getSourceFile(); + } + return null; +} + +/** One hop from a wrapper's export= symbol to the value it re-exports + * (`module.exports = require(...)` or `= `), or null. A + * wrapper with live members owns storage for them, so it is not followed. */ +function wholeReexportTargetOf(lowerer: Lowerer, symbol: ts.Symbol): ts.Symbol | null { + const sf = moduleExportSourceFileOf(symbol); + if (sf === null) return null; + // A wrapper with live members owns storage for them; importers must keep + // reading that root rather than the inner value. + if (cjsHasLiveMemberExports(lowerer, sf)) return null; + const whole = cjsWholeExportValueOf(sf); + if (whole === null) return null; + const inner = inlineProgramRequireOf(lowerer, whole.rhs); + if (inner !== null) return wholeExportSymbolOfModule(lowerer, inner.dep); + let rhs: ts.Expression = whole.rhs; + while (ts.isParenthesizedExpression(rhs)) rhs = rhs.expression; + if (!ts.isIdentifier(rhs)) return null; + const rhsSymbol = lowerer.checker.getSymbolAtLocation(rhs); + return rhsSymbol === undefined ? null : splitWholeExportSymbolOfSymbol(lowerer, rhsSymbol); +} + +/** Follow split bindings and whole re-exports to the ultimate value symbol. */ +function followSplitWholeTarget(lowerer: Lowerer, candidate: ts.Symbol | null): ts.Symbol | null { + if (candidate === null) return null; + let symbol = candidate; + const seen = new Set(); + for (let depth = 0; depth < 32 && !seen.has(symbol); depth++) { + seen.add(symbol); + const next = + splitWholeExportSymbolOfSymbol(lowerer, symbol) ?? wholeReexportTargetOf(lowerer, symbol); + if (next === null) break; + symbol = next; + } + return symbol; +} + +/** The target whole-export symbol behind `module.exports = `, + * letting importers of that wrapper resolve to the real value symbol. */ +export function splitWholeExportAliasTargetOf( + lowerer: Lowerer, + symbol: ts.Symbol, +): ts.Symbol | null { + const storage = splitWholeExportStorageSymbolOf(lowerer, symbol); + if (storage !== null) return followSplitWholeTarget(lowerer, storage); + const direct = followSplitWholeTarget(lowerer, splitWholeExportSymbolOfSymbol(lowerer, symbol)); + if (direct !== null) return direct; + return followSplitWholeTarget(lowerer, wholeReexportTargetOf(lowerer, symbol)); +} + +/** The first read of a split binding that code before its assignment can + * reach, using the same reachability model as ordinary require bindings. */ +export function splitRequireEarlyReadOf( + lowerer: Lowerer, + binding: SplitRequireBinding, +): ts.Identifier | null { + const sf = ts.getSourceFileOfNode(binding.decl); + const stmt = binding.assign.parent; + // `binding` was recognized only from a top-level `m = require(...)` + // statement, so the statement index is always present. + const index = sf.statements.nodes.indexOf(stmt as ts.ExpressionStatement); + return requireBindingEarlyUse7(lowerer.program, sf, index, binding.decl, []); +} + /** A `require("")` call of the CommonJS global in a CommonJS JS * file whose target is a compiled program module (relative, aliased, or an * opted-in --npm-static package). Builtins, JSON documents and @@ -108,13 +436,6 @@ export function inlineNamespaceRequireOf( /** `value` evaluated after the required module's run-once %init (and its * module-registry link) at the require's own position. */ export function withRequireInit(lowerer: Lowerer, req: InlineRequire, value: IrExpr): IrExpr { - if (lowerer.inlineRequireCycleCalls.has(req.call)) { - lowerer.unsupported( - "SC1090", - req.call, - `require() of '${req.spec}' while it is still evaluating (a require cycle: Node answers its partial exports, which the static export table does not model)`, - ); - } // Every inline program require is a preflight module edge, so its module // has an initializer; never read its exports without evaluating it. if (!lowerer.initNameOf.has(req.dep)) { diff --git a/packages/compiler/src/frontend/lowering/class-callbacks.ts b/packages/compiler/src/frontend/lowering/class-callbacks.ts index 0bda0068e..3a1c7975d 100644 --- a/packages/compiler/src/frontend/lowering/class-callbacks.ts +++ b/packages/compiler/src/frontend/lowering/class-callbacks.ts @@ -156,6 +156,13 @@ export function classCallbackValue( }; } +/** Notes a checked conversion of a replaceable method's result for the + * key-order witness: the result is a value the method or its replacement + * returns, not a record built from a dynamic value. */ +function methodResult(lowerer: Lowerer, checked: IrExpr, name: string): IrExpr { + return checked.kind === "dynCheck" ? lowerer.keyOrder.methodResult(checked, name) : checked; +} + export function classCallbackCall( lowerer: Lowerer, receiver: IrExpr, @@ -197,7 +204,7 @@ export function classCallbackCall( // generator arm does not box) keeps its native type: the unchanged // method's result stays as it is, and a replacement's dynamic result // takes the checked conversion into that type. - const converted = lowerer.coerceToExpected(call, fallback.type); + const converted = methodResult(lowerer, lowerer.coerceToExpected(call, fallback.type), name); if (!typeEquals(converted.type, fallback.type)) { lowerer.pushDiag( unsupportedDiag( @@ -234,7 +241,9 @@ export function classCallbackCall( loc, }; answer = - fallback.type.kind === "void" ? result : lowerer.coerceToExpected(result, fallback.type); + fallback.type.kind === "void" + ? result + : methodResult(lowerer, lowerer.coerceToExpected(result, fallback.type), name); } // Read after observedSlot built the prototype data and its originals. const originalId = observed ? original?.originalId() : undefined; diff --git a/packages/compiler/src/frontend/lowering/class-dynamic-dispatch.ts b/packages/compiler/src/frontend/lowering/class-dynamic-dispatch.ts index ab3ebb34e..fc6d0a922 100644 --- a/packages/compiler/src/frontend/lowering/class-dynamic-dispatch.ts +++ b/packages/compiler/src/frontend/lowering/class-dynamic-dispatch.ts @@ -1,4 +1,5 @@ import { primitiveConstructorOr } from "./lower-exprs.js"; +import { TO_STRING_TAG_FIELD, wellKnownSymbolOfField } from "./symbol-fields.js"; import { InternalCompilerError } from "../../errors.js"; import { dynUndefinedExpr, helperLoc, nodeThrowExpr, varRef } from "../../ir/build.js"; import { constructedValue } from "./constructor-returns.js"; @@ -26,6 +27,7 @@ import { isUnitType, typeEquals, typeKey, + type IrClassDef, type IrExpr, type IrFunction, type IrParam, @@ -153,6 +155,41 @@ const STREAM_NUM_PROPERTIES = new Set([ const streamProperty = (name: string): boolean => STREAM_BOOL_PROPERTIES.has(name) || STREAM_NUM_PROPERTIES.has(name); +/** `(instance) => string`, the string a class's inherited + * Symbol.toStringTag getter answers, which the runtime reads for + * Object.prototype.toString and util.inspect of an instance seen as + * `unknown`. None for a class without a string getter, or whose own tag + * field shadows it. */ +function stringTagHelper(lowerer: Lowerer, info: ClassInfo, loc: SrcLoc): IrFunction | null { + if (info.fields.has(TO_STRING_TAG_FIELD)) return null; + const getter = findMethodOn(lowerer, info, "get:sym:toStringTag"); + if (getter?.sig.ret.kind !== "string") return null; + const type: IrType = { kind: "object", className: info.def.name }; + const owner = getter.declarer.def.name; + return { + name: `%class.stringTag:${info.def.name}`, + params: [{ localId: "this", name: "this", type }], + locals: [{ id: "this", name: "this", type, mutable: false }], + returnType: STRING, + body: [ + { + kind: "return", + value: accessorCall( + lowerer, + owner, + "get:sym:toStringTag", + lowerer.upcastTo(varRef("this", type, loc), owner), + [], + STRING, + loc, + ), + loc, + }, + ], + loc, + }; +} + function symbolMemberKey( lowerer: Lowerer, info: ClassInfo, @@ -282,6 +319,11 @@ interface PropertyDispatch { fn: IrFunction; classes: Set; reflect?: boolean; + /** The helper's fallthrough statements as created, and the same access + * applied to the receiver itself: what the fallthrough reduces to for a + * value that is neither a class capsule nor a function, whose bag is the + * value. */ + plain?: { tail: readonly IrStmt[]; access: IrStmt[] }; } export function classInstanceOf( @@ -712,6 +754,105 @@ export function classForInHasKey( return { kind: "call", callee: name, args: [value, key], type: BOOL, loc }; } +/** Box class instances at their exact class. `%dyn.class.normalize` turns a + * capsule of a class with subclasses into a capsule of the instance's own + * class before any member table reads it, so every such box was followed + * by a second capsule. Each conversion of a value whose static class has a + * normalization branch now selects the exact class at the conversion, with + * the same tests in the same order (%dyn.class.boxExact:C), and the + * normalization of the resulting capsule falls through. Only JavaScript + * class hierarchies, whose instances live in untyped code, take the exact + * box. */ +export function boxClassInstancesExactly(lowerer: Lowerer, functions: IrFunction[]): void { + const normalize = lowerer.liftedFnNamed("%dyn.class.normalize"); + if (!normalize) return; + // Static class → its constructible descendants in normalization order. + const branches = new Map(); + for (const stmt of normalize.body) { + if (stmt.kind !== "if" || stmt.cond.kind !== "libCall" || stmt.cond.fn !== "dyn.typedRefIs") + continue; + const key = stmt.cond.args[1]; + if (key?.kind !== "strLit" || !key.value.startsWith("object:")) continue; + const children: string[] = []; + for (const inner of stmt.then) { + if (inner.kind !== "if" || inner.cond.kind !== "instanceOf") continue; + children.push(inner.cond.className); + } + // TypeScript hierarchies keep their static-class capsules: their boxes + // are rarer, and an exact box would emit each subclass's checked view. + const className = key.value.slice("object:".length); + const decl = lowerer.classes.get(className)?.decl; + if (!decl || !isJsSourceFile(ts.getSourceFileOfNode(decl))) continue; + if (children.length) branches.set(className, children); + } + if (branches.size === 0) return; + const loc = normalize.loc; + const helpers = new Map(); + const helperFor = (className: string): string => { + const known = helpers.get(className); + if (known !== undefined) return known; + const name = `%dyn.class.boxExact:${className}`; + helpers.set(className, name); + const type: IrType = { kind: "object", className }; + const value = varRef("value", type, loc); + functions.push({ + name, + params: [{ localId: "value", name: "value", type }], + returnType: DYN, + locals: [{ id: "value", name: "value", type, mutable: false }], + body: [ + ...branches.get(className)!.map((child): IrStmt => ({ + kind: "if", + cond: { kind: "instanceOf", value, className: child, type: BOOL, loc }, + then: [ + { + kind: "return", + value: { + kind: "dynFrom", + value: { + kind: "downcast", + value, + type: { kind: "object", className: child }, + loc, + }, + type: DYN, + loc, + }, + loc, + }, + ], + else_: null, + loc, + })), + { kind: "return", value: { kind: "dynFrom", value, type: DYN, loc }, loc }, + ], + loc, + }); + return name; + }; + const rewrite = (fn: IrFunction): void => { + fn.body = transformStmtList(fn.body, { + stmt: (statement) => statement, + expr: (expression) => + expression.kind === "dynFrom" && + expression.value.type.kind === "object" && + branches.has(expression.value.type.className) + ? { + kind: "call", + callee: helperFor(expression.value.type.className), + args: [expression.value], + type: DYN, + loc: expression.loc, + } + : expression, + }); + }; + for (const fn of [...functions]) { + if (fn === normalize || fn.name.startsWith("%dyn.class.boxExact:")) continue; + rewrite(fn); + } +} + /** Record field creation after the dispatch worklist closes, including writes * in generated member tables. Native values stay in their slots; the bag * owns only their property presence, attributes, and insertion position. */ @@ -1271,10 +1412,27 @@ export class ClassDynamicDispatch { const info = lowerer.classes.get(className); if (!info || info.builtinEmitter || info.builtinStream || info.builtinError) continue; if (info.symbolFields && info.def.symbolFields === undefined) { - info.def.symbolFields = [...info.symbolFields].flatMap(([symbol, field]) => { - const global = lowerer.globalsBySymbol.get(symbol); - return global?.type.kind === "symbol" ? [{ field, globalId: global.id }] : []; - }); + info.def.symbolFields = [...info.symbolFields].flatMap( + ([symbol, field]): NonNullable => { + const global = lowerer.globalsBySymbol.get(symbol); + if (global?.type.kind === "symbol") return [{ field, globalId: global.id }]; + const wellKnown = wellKnownSymbolOfField(field); + return wellKnown === null ? [] : [{ field, wellKnown }]; + }, + ); + } + const tagged = (current: ClassInfo): ClassInfo[] => [ + current, + ...current.subclasses.flatMap(tagged), + ]; + for (const current of tagged(info)) { + if (current.def.stringTagHelper !== undefined) continue; + const fn = stringTagHelper(lowerer, current, functions[0]!.loc); + if (!fn) continue; + current.def.stringTagHelper = fn.name; + this.generated.add(fn); + lowerer.liftedFns.push(fn); + changed = true; } if (this.prototypeHelper && !this.prototypeClasses.has(className)) { this.prototypeClasses.add(className); @@ -1629,6 +1787,15 @@ export class ClassDynamicDispatch { if (this.deletedNames === undefined) this.deletedNames = deletedPropertyNames(lowerer.moduleOrder); if (!root.base?.def.runtime && !fixedFieldFamily(root, this.deletedNames)) track(root); + // An Error family's bag records where `name` and `message` were + // created, which the error slots do not. + if (root.base?.def.name === "%Error") { + const order = (current: ClassInfo): void => { + current.def.errorBagOrder = true; + current.subclasses.forEach(order); + }; + order(info); + } } const loc = this.propertyBag.loc; const { receiver, condition } = this.propertyReceiver(plan, loc); @@ -2724,7 +2891,28 @@ export class ClassDynamicDispatch { loc, }); } - dispatch = { name, write: !!write, fn: helper, classes: new Set() }; + const receiverAccess: IrStmt[] = read + ? [{ kind: "return", value: { ...read, value: varRef("p.0", DYN, loc) }, loc }] + : [ + { + kind: "exprStmt", + expr: { + ...write!, + args: [varRef("p.0", DYN, loc), write!.args[1]!, varRef("p.1", DYN, loc)], + }, + loc, + }, + { kind: "return", value: null, loc }, + ]; + dispatch = { + name, + write: !!write, + fn: helper, + classes: new Set(), + ...(name !== "constructor" && name !== "toString" + ? { plain: { tail: [...helper.body], access: receiverAccess } } + : {}), + }; this.properties.set(key, dispatch); this.generated.add(helper); lowerer.liftedFns.push(helper); @@ -3528,10 +3716,29 @@ export class ClassDynamicDispatch { name, lowerer.coerceToExpected(varRef(global.id, global.type, loc), DYN), ); + } else if (wellKnownSymbolOfField(name) !== null) { + // A field keyed by a well-known symbol (Symbol.toStringTag). + symbols.set(name, { + kind: "dynFrom", + type: DYN, + loc, + value: { + kind: "libCall", + fn: "sym.wellKnown", + args: [ + { kind: "strLit", value: wellKnownSymbolOfField(name)!, type: STRING, loc }, + ], + type: SYMBOL_T, + loc, + }, + }); } } for (let owner: ClassInfo | null = info; owner; owner = owner.base) { - for (const name of [...owner.methods.keys()]) + for (const method of [...owner.methods.keys()]) { + // Accessors key by the property they define (`get [Symbol.toStringTag]`). + const name = + method.startsWith("get:") || method.startsWith("set:") ? method.slice(4) : method; if (name.startsWith("sym:")) { symbols.set(name, { kind: "dynFrom", @@ -3546,6 +3753,7 @@ export class ClassDynamicDispatch { }, }); } + } } const branches: IrStmt[] = []; if (!dispatch.write && !dispatch.reflect && !info.def.tracksOwnFields) { @@ -4896,6 +5104,7 @@ export class ClassDynamicDispatch { ); } if (lowerer) this.guardClassTables(lowerer); + if (lowerer) this.guardPropertyHelpers(lowerer); } /** Each tracked class's bag initializer over a typed receiver (`this`), @@ -4985,6 +5194,87 @@ export class ClassDynamicDispatch { } } + /** Fast exits for the generated property helpers: when every class + * branch tests a capsule key or a class key and the fallthrough is still + * the bag access the helper was created with, a value of another kind + * (a plain object, array or primitive) normalizes to itself, has itself + * as its bag, and so gets the plain access directly, without the + * normalization, bag and static-bag calls. */ + private guardPropertyHelpers(lowerer: Lowerer): void { + // Every class table the fallthrough or normalization calls must answer + // a plain value with the value itself: its kind exit is in place. + const exits = (name: string): boolean => { + const helper = lowerer.liftedFnNamed(name); + const first = helper?.body[0]; + const param = helper?.params[0]?.localId; + return ( + param !== undefined && + first?.kind === "if" && + first.cond.kind === "dynTest" && + first.cond.negated === true && + first.then.length === 1 && + first.then[0]!.kind === "return" && + first.then[0]!.value !== null && + returnsParam(first.then[0]!.value, param) + ); + }; + const tables = new Set([ + "%dyn.class.properties", + READ_PROPERTIES, + READ_PROPERTIES_OR_EMPTY, + "%dyn.class.normalize", + "%dyn.class.staticProperties", + ]); + const callsOnlyGuardedTables = (statements: IrStmt[]): boolean => + everyStmtList(statements, { + stmt: () => true, + expr: (expression) => + expression.kind !== "call" || (tables.has(expression.callee) && exits(expression.callee)), + }); + for (const dispatch of this.properties.values()) { + const plain = dispatch.plain; + if (!plain) continue; + const fn = dispatch.fn; + const param = fn.params[0]?.localId; + if (param === undefined) continue; + const head = fn.body.length - plain.tail.length; + if (head < 0 || plain.tail.some((stmt, i) => fn.body[head + i] !== stmt)) continue; + let ok = true; + for (const stmt of fn.body.slice(0, head)) { + if ( + stmt.kind === "assign" && + stmt.localId === param && + stmt.value.kind === "call" && + stmt.value.callee === "%dyn.class.normalize" + ) + continue; + if (stmt.kind === "if" && stmt.else_ === null && branchKind(stmt.cond, param) !== null) + continue; + ok = false; + break; + } + // Class branches never run for a plain value; the rest always does. + if (!ok || !callsOnlyGuardedTables(fn.body.filter((stmt) => stmt.kind !== "if"))) continue; + const loc = fn.loc; + const not = (test: "classCapsule" | "funcKind"): IrExpr => ({ + kind: "dynTest", + test, + negated: true, + value: varRef(param, DYN, loc), + type: BOOL, + loc, + }); + fn.body.unshift({ + kind: "if", + cond: not("classCapsule"), + then: [{ kind: "if", cond: not("funcKind"), then: plain.access, else_: null, loc }], + else_: null, + loc, + }); + delete dispatch.plain; + } + } + private normalizedDispatches(): IrFunction[] { return [ ...(this.propertyBag ? [this.propertyBag] : []), diff --git a/packages/compiler/src/frontend/lowering/class-method-mutations.ts b/packages/compiler/src/frontend/lowering/class-method-mutations.ts index fd23b6db8..b57729181 100644 --- a/packages/compiler/src/frontend/lowering/class-method-mutations.ts +++ b/packages/compiler/src/frontend/lowering/class-method-mutations.ts @@ -46,6 +46,13 @@ export function collectClassMethodMutations( const typedClasses: (ts.ClassDeclaration | ts.ClassExpression)[] = []; const symbolMethods = new Set(); let unknownKey: ts.Node | undefined; + /** Every write with an unknown key: each may install a replacement of any + * method name, and the key-order witness asks what each one installs. */ + const unknownKeySites: ts.Node[] = []; + const markUnknownKey = (site: ts.Node): void => { + unknownKey = site; + unknownKeySites.push(site); + }; let numericKey: ts.Node | undefined; let unknownOwnField = false; // `sym:iterator` for `Symbol.iterator`: the name a class method declared @@ -83,8 +90,8 @@ export function collectClassMethodMutations( for (const key of computedMethods) for (const name of literalValues(lowerer.typeOf(key)) ?? []) methods.add(String(name)); const markName = (name: string, site: ts.Node, arrow = false): void => { - if (name === "prototype" || name === "__proto__") unknownKey = site; - if (methods.has(name)) lowerer.prototypeMethodAccesses.set(name, site); + if (name === "prototype" || name === "__proto__") markUnknownKey(site); + if (methods.has(name)) lowerer.noteMethodAccess(name, site); if (!arrow) receiverFields.add(name); }; const unwrap = (value: ts.Expression): ts.Expression => { @@ -125,11 +132,13 @@ export function collectClassMethodMutations( }; // Whether no function value can inhabit `type`. Primitives qualify, and so // do non-callable object types a function is not assignable to: one that - // requires a property functions lack, or a weak (all-optional) type that + // requires a property functions lack, a weak (all-optional) type that // shares no property with functions (TypeScript's weak-type rule rejects - // callable sources too). - const functionFree = (type: ts.Type): boolean => { - if (type.isUnionType()) return ts.constituentTypes(type).every(functionFree); + // callable sources too), or a dictionary with no properties whose index + // signatures hold function-free values (functions have no index + // signature). `seen` stops recursive dictionary types. + const functionFree = (type: ts.Type, seen: Set = new Set()): boolean => { + if (type.isUnionType()) return ts.constituentTypes(type).every((t) => functionFree(t, seen)); if ( (type.flags & (ts.TypeFlags.StringLike | @@ -149,7 +158,12 @@ export function collectClassMethodMutations( ) return false; const properties = lowerer.checker.getPropertiesOfType(type); - if (properties.length === 0) return false; + if (properties.length === 0) { + if (seen.has(type)) return true; + seen.add(type); + const indexes = lowerer.checker.getIndexInfosOfType(type); + return indexes.length > 0 && indexes.every((index) => functionFree(index.type, seen)); + } const required = properties.filter( (property) => (property.flags & ts.SymbolFlags.Optional) === 0, ); @@ -163,14 +177,32 @@ export function collectClassMethodMutations( // A string index whose values cannot be functions excludes callable // method slots. Class instances have no implicit index signature, so such // a dictionary is only a class view when the class declares that index - // signature, and then every method must be assignable to its value type. - const functionFreeDictionary = (value: ts.Expression): boolean => - lowerer.checker - .getIndexInfosOfType(lowerer.typeOf(value)) + // signature, and then every method must be assignable to its value type, + // or when the dictionary keeps checked identity: it then holds the + // existing objects it receives, class instances included. + // A string-keyed dictionary that keeps checked identity holds the + // existing objects it receives, class instances included, so any write + // through it can replace a method. + const dictionaryView = (value: ts.Expression): boolean => { + const type = lowerer.typeOf(value); + return ( + lowerer.checker.getPropertiesOfType(type).length === 0 && + lowerer.checker + .getIndexInfosOfType(type) + .some((index) => (index.keyType.flags & ts.TypeFlags.StringLike) !== 0) && + lowerer.classImplementedProtocol(type) + ); + }; + const functionFreeDictionary = (value: ts.Expression): boolean => { + const type = lowerer.typeOf(value); + if (lowerer.classImplementedProtocol(type)) return false; + return lowerer.checker + .getIndexInfosOfType(type) .some( (index) => (index.keyType.flags & ts.TypeFlags.StringLike) !== 0 && functionFree(index.type), ); + }; const literalLoopKeys = (key: ts.Expression): (string | number)[] | null => { if (!ts.isIdentifier(key)) return null; const symbol = lowerer.checker.getSymbolAtLocation(key); @@ -394,11 +426,22 @@ export function collectClassMethodMutations( (ts.isElementAccessExpression(base) && ts.isStringLiteralLike(base.argumentExpression) && base.argumentExpression.text === "prototype"); - if (prototype) { - if (ts.isPropertyAccessExpression(node)) - lowerer.prototypeMethodAccesses.set(node.name.text, node); + // `Object.prototype.toString.call(value)` calls the builtin and + // keeps no reference to it, so it replaces nothing. + const builtinCall = + ts.isPropertyAccessExpression(node) && + ts.isPropertyAccessExpression(base) && + ts.isIdentifier(unwrap(base.expression)) && + lowerer.isStdlibGlobal(unwrap(base.expression) as ts.Identifier, "Object") && + ts.isPropertyAccessExpression(node.parent) && + node.parent.expression === node && + (node.parent.name.text === "call" || node.parent.name.text === "apply") && + ts.isCallExpression(node.parent.parent) && + node.parent.parent.expression === node.parent; + if (prototype && !builtinCall) { + if (ts.isPropertyAccessExpression(node)) lowerer.noteMethodAccess(node.name.text, node); else if (ts.isStringLiteralLike(node.argumentExpression)) - lowerer.prototypeMethodAccesses.set(node.argumentExpression.text, node); + lowerer.noteMethodAccess(node.argumentExpression.text, node); else markKey(node.argumentExpression, node); } } @@ -435,7 +478,7 @@ export function collectClassMethodMutations( ? lowerer.checker.getSymbolAtLocation(access.name) : lowerer.checker.getPropertyOfType(lowerer.typeOf(access.expression), name); if (symbol && symbol.declarations.some(ts.isMethodDeclaration)) - lowerer.prototypeMethodAccesses.set(name, access); + lowerer.noteMethodAccess(name, access); } lowerer.checker.prefetchSymbolRoots([...globals, ...reflection]); for (const node of globals) @@ -443,11 +486,12 @@ export function collectClassMethodMutations( (lowerer.isStdlibGlobal(node, "Object") || lowerer.isStdlibGlobal(node, "Reflect")) && (!ts.isPropertyAccessExpression(node.parent) || node.parent.expression !== node) ) - unknownKey = node; + markUnknownKey(node); for (const node of reflection) { const objectMethod = lowerer.stdlibGlobalMember(node, "Object"); const reflectMethod = lowerer.stdlibGlobalMember(node, "Reflect"); - if (objectMethod === "setPrototypeOf" || reflectMethod === "setPrototypeOf") unknownKey = node; + if (objectMethod === "setPrototypeOf" || reflectMethod === "setPrototypeOf") + markUnknownKey(node); const single = objectMethod === "defineProperty" || reflectMethod === "defineProperty" || @@ -457,7 +501,7 @@ export function collectClassMethodMutations( if (!single && !multiple) continue; const call = node.parent; if (!ts.isCallExpression(call) || call.expression !== node) { - unknownKey = node; + markUnknownKey(node); continue; } const receiver = call.arguments.nodes[0]; @@ -571,9 +615,13 @@ export function collectClassMethodMutations( ); for (const { target, site } of propertyWrites) { const name = target.name.text; - if (lowerer.prototypeMethodAccesses.has(name) || functionFree(lowerer.typeOf(target))) continue; - if (!methodOwnerCanInhabit(target.expression, name)) continue; - lowerer.prototypeMethodAccesses.set(name, site); + if ( + !dictionaryView(target.expression) && + (functionFree(lowerer.typeOf(target)) || !methodOwnerCanInhabit(target.expression, name)) + ) + continue; + if (lowerer.prototypeMethodAccesses.has(name)) lowerer.noteMethodSite(name, site); + else lowerer.noteMethodAccess(name, site); } for (const { key, receiver, site } of computedWrites) { // A class method declared with a well-known symbol key is not a write; @@ -611,7 +659,7 @@ export function collectClassMethodMutations( // Numeric writes can replace numeric names but not ordinary names. numericKey = site; } else if (!(flags & ts.TypeFlags.ESSymbolLike)) { - unknownKey = site; + markUnknownKey(site); } } if (numericKey) { @@ -620,7 +668,10 @@ export function collectClassMethodMutations( } } if (unknownKey) { - for (const method of methods) lowerer.prototypeMethodAccesses.set(method, unknownKey); + for (const method of methods) { + lowerer.prototypeMethodAccesses.set(method, unknownKey); + for (const site of unknownKeySites) lowerer.noteMethodSite(method, site); + } for (const method of symbolMethods) lowerer.replacedSymbolMethods.add(method); } if (!unknownKey && !unknownOwnField) diff --git a/packages/compiler/src/frontend/lowering/class-protocols.ts b/packages/compiler/src/frontend/lowering/class-protocols.ts index 76091b13e..00eedb6c0 100644 --- a/packages/compiler/src/frontend/lowering/class-protocols.ts +++ b/packages/compiler/src/frontend/lowering/class-protocols.ts @@ -10,6 +10,9 @@ export class ClassProtocols { private readonly accepted = new Map(); /** What markIdentityFlows learned about the program, while it runs. */ private facts: FlowFacts | null = null; + /** The value types of the string-keyed dictionaries that keep checked + * identity (see markDictionaryViews). */ + private readonly dictionaryViews = new Set(); constructor( private readonly checker: ts.TypeChecker, @@ -323,6 +326,7 @@ export class ClassProtocols { else if (names !== "all") names.add(name); } } + this.markDictionaryViews(sites, sourceNodes, observedNodes, writeNodes); for (let i = 0; i < looseArguments.length; i++) { const type = typeOf(looseNodes[i]!); if (type === undefined) continue; @@ -697,6 +701,163 @@ export class ClassProtocols { return source !== undefined && this.isProgramClassInstance(source); } + /** String-keyed dictionaries (`Record`, `{ [key: string]: V }`) + * that receive an existing record or class instance. Natively a + * dictionary is a key/value record, and converting another object into + * one copies its fields, so writes through the dictionary and identity + * checks would miss the original. When the program writes through, or + * observes, a value of such a dictionary type, every dictionary with that + * value type keeps checked identity instead (records with the same value + * type share one native layout, so values pass between them). A source + * counts when it is an existing object (a binding, member, element or + * `this`, not a fresh literal or call result) whose type is a program + * class instance or a record with declared properties, or a constant + * `{}`, `object` or `unknown` binding initialized with one. */ + private markDictionaryViews( + sites: readonly { + source: ts.Expression; + target: () => ts.Type | undefined; + contextual?: ts.Expression; + }[], + sourceNodes: readonly ts.Expression[], + observedNodes: readonly ts.Expression[], + writeNodes: readonly ts.Expression[], + ): void { + // The calls the sites' arguments belong to, by callee name, built on + // first use. + let calls: Map | null = null; + const callsNamed = (name: string): readonly (ts.CallExpression | ts.NewExpression)[] => { + if (calls === null) { + calls = new Map(); + for (const site of sites) { + const call = site.contextual?.parent; + if (call === undefined || (!ts.isCallExpression(call) && !ts.isNewExpression(call))) + continue; + const callee = call.expression; + const named = ts.isIdentifier(callee) + ? callee.text + : ts.isPropertyAccessExpression(callee) + ? callee.name.text + : undefined; + if (named === undefined) continue; + const list = calls.get(named) ?? []; + if (!list.includes(call)) list.push(call); + calls.set(named, list); + } + } + return calls.get(name) ?? []; + }; + const candidates: { index: number; sourceType: ts.Type }[] = []; + for (let i = 0; i < sites.length; i++) { + const source = sourceNodes[i]!; + if (!isExistingObject(source)) continue; + const sourceType = attempt(() => this.checker.getTypeAtLocation(source)); + if (sourceType !== undefined && this.dictionarySource(source, sourceType, callsNamed)) + candidates.push({ index: i, sourceType }); + } + if (candidates.length === 0) return; + // One batch for the contextual targets of the candidate sites. + attempt(() => + this.checker.prefetchContextualTypes( + candidates.flatMap(({ index }) => { + const contextual = sites[index]!.contextual; + return contextual === undefined ? [] : [contextual]; + }), + ), + ); + const received = new Set(); + for (const { index, sourceType } of candidates) { + const target = attempt(sites[index]!.target); + const parts = + target === undefined ? [] : target.isUnionType() ? ts.constituentTypes(target) : [target]; + for (const part of parts) { + const value = this.dictionaryValueType(part); + // A dictionary of `unknown` or `any` values is checked-dynamic already. + if ( + value !== undefined && + (value.flags & (ts.TypeFlags.Unknown | ts.TypeFlags.Any)) === 0 && + this.dictionaryValueType(sourceType) !== value + ) + received.add(value); + } + } + if (received.size === 0) return; + // The dictionary values the program writes through or observes. + const seen = new Set(); + for (const node of [...writeNodes, ...observedNodes]) { + const type = attempt(() => this.checker.getTypeAtLocation(node)); + const parts = + type === undefined ? [] : type.isUnionType() ? ts.constituentTypes(type) : [type]; + for (const part of parts) { + const value = this.dictionaryValueType(part); + if (value !== undefined) seen.add(value); + } + } + for (const value of received) if (seen.has(value)) this.dictionaryViews.add(value); + } + + /** Whether `source` holds an existing object that a dictionary + * conversion would copy. */ + private dictionarySource( + source: ts.Expression, + type: ts.Type, + callsNamed: (name: string) => readonly (ts.CallExpression | ts.NewExpression)[], + ): boolean { + if (this.isProgramClassInstance(type) || this.isRecordWithFields(type)) return true; + if (!isDynamicView(this.checker, type) || !ts.isIdentifier(source)) return false; + const decl = attempt(() => this.checker.getSymbolAtLocation(source))?.valueDeclaration; + if (decl === undefined) return false; + const holdsObject = (expr: ts.Expression): boolean => { + const held = attempt(() => this.checker.getTypeAtLocation(this.flowSourceNode(expr))); + return ( + held !== undefined && (this.isProgramClassInstance(held) || this.isRecordWithFields(held)) + ); + }; + if (ts.isParameter(decl)) { + // A parameter holds what its callers pass: the calls of the function + // by name whose signature resolves to it. + const fn = decl.parent; + if (fn === undefined || !ts.isFunctionLike(fn)) return false; + const index = fn.parameters.nodes.indexOf(decl); + const name = functionName(fn); + if (name === undefined || index < 0) return false; + return callsNamed(name).some((call) => { + const argument = call.arguments?.nodes[index]; + return ( + argument !== undefined && + !ts.isSpreadElement(argument) && + attempt(() => this.checker.getResolvedSignature(call)?.declaration) === fn && + holdsObject(argument) + ); + }); + } + return ( + ts.isVariableDeclaration(decl) && + decl.initializer !== undefined && + ts.isVariableDeclarationList(decl.parent) && + (decl.parent.flags & ts.NodeFlags.Const) !== 0 && + holdsObject(decl.initializer) + ); + } + + /** A program object type with declared properties that is not a class + * instance, array, tuple, function or library type. */ + private isRecordWithFields(type: ts.Type): boolean { + return this.isStructuralTarget(type) && this.checker.getPropertiesOfType(type).length > 0; + } + + /** The value type of a string-keyed dictionary with no declared + * properties, or undefined for any other type. */ + private dictionaryValueType(type: ts.Type): ts.Type | undefined { + if ((type.flags & ts.TypeFlags.Object) === 0) return undefined; + if (this.checker.isArrayType(type) || this.checker.isTupleType(type)) return undefined; + if (this.checker.getCallSignatures(type).length > 0) return undefined; + if (this.checker.getPropertiesOfType(type).length > 0) return undefined; + return this.checker + .getIndexInfosOfType(type) + .find((index) => (index.keyType.flags & ts.TypeFlags.String) !== 0)?.type; + } + private isProgramClassInstance(type: ts.Type): boolean { const symbol = type.getSymbol(); return ( @@ -822,7 +983,13 @@ export class ClassProtocols { if ((type.flags & ts.TypeFlags.Object) === 0) return false; const previous = this.answers.get(type); if (previous !== undefined) return previous; - const answer = this.protocol(type); + // A dictionary keeps checked identity when the protocols already mark + // it (a literal whose methods read `this`) or when it receives existing + // objects (markDictionaryViews). + const dictionaryValue = this.dictionaryValueType(type); + const answer = + this.protocol(type) || + (dictionaryValue !== undefined && this.dictionaryViews.has(dictionaryValue)); this.answers.set(type, answer); return answer; } @@ -922,6 +1089,44 @@ const IDENTITY_METHODS = new Set([ "includes", ]); +/** An expression that denotes an object that already exists, not a fresh + * literal or call result. */ +function isExistingObject(node: ts.Expression): boolean { + return ( + ts.isIdentifier(node) || + ts.isPropertyAccessExpression(node) || + ts.isElementAccessExpression(node) || + node.kind === ts.SyntaxKind.ThisKeyword + ); +} + +/** `{}`, `object` and `unknown`: types whose values are checked-dynamic + * and keep the original object. */ +function isDynamicView(checker: ts.TypeChecker, type: ts.Type): boolean { + if (type.flags & (ts.TypeFlags.Unknown | ts.TypeFlags.NonPrimitive)) return true; + return ( + (type.flags & ts.TypeFlags.Object) !== 0 && + checker.getPropertiesOfType(type).length === 0 && + checker.getIndexInfosOfType(type).length === 0 && + checker.getCallSignatures(type).length === 0 && + checker.getConstructSignatures(type).length === 0 + ); +} + +/** The name calls use for a function, method or function-valued binding. */ +function functionName(fn: ts.Node): string | undefined { + if ((ts.isFunctionDeclaration(fn) || ts.isMethodDeclaration(fn)) && fn.name !== undefined) + return ts.isIdentifier(fn.name) ? fn.name.text : undefined; + if ( + (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) && + fn.parent !== undefined && + ts.isVariableDeclaration(fn.parent) && + ts.isIdentifier(fn.parent.name) + ) + return fn.parent.name.text; + return undefined; +} + function isNullishLiteral(node: ts.Expression): boolean { while (ts.isParenthesizedExpression(node)) node = node.expression; return ( diff --git a/packages/compiler/src/frontend/lowering/class-to-primitive.ts b/packages/compiler/src/frontend/lowering/class-to-primitive.ts index 0b107ab4e..8807ac5a5 100644 --- a/packages/compiler/src/frontend/lowering/class-to-primitive.ts +++ b/packages/compiler/src/frontend/lowering/class-to-primitive.ts @@ -13,6 +13,7 @@ import * as ts from "../ts7/adapter.js"; import type { Lowerer } from "./lowerer.js"; import { accessorCall, findMethodOn, type ClassInfo } from "./lower-classes.js"; import { isClassCallback } from "./class-callbacks.js"; +import { TO_STRING_TAG_FIELD } from "./symbol-fields.js"; import { isSafeToRepeat } from "./expressions/evaluation-safety.js"; import { DATE_TIME_FIELD, dateRootOf } from "./builtins/date-classes.js"; import { dateValueMethod } from "./builtins/dates.js"; @@ -29,7 +30,15 @@ type ConversionMethod = "sym:toPrimitive" | "valueOf" | "toString"; * Date`, or through Object.prototype.toString ("[object Object]"). */ type Plan = | { method: ConversionMethod; ret: IrType } - | { method: null; date?: "toString" | "valueOf" }; + | { method: null; date?: "toString" | "valueOf"; tag?: ToStringTag }; + +/** Where Object.prototype.toString finds a class's Symbol.toStringTag: a + * string field (`[Symbol.toStringTag] = "LRUCache"`) or a getter that + * returns a string, declared on `owner`. */ +interface ToStringTag { + owner: ClassInfo; + getter: boolean; +} const PRIMITIVE_KINDS = new Set(["f64", "string", "bool", "bigint", "undefinedT", "nullT"]); @@ -52,6 +61,17 @@ export function classToStringConversion( ); } +/** Object.prototype.toString of a program class instance: "[object " + + * its Symbol.toStringTag + "]" (an own tag field, else an inherited + * getter), or "[object Object]". The class's own conversion methods do not + * take part. Null as for classToStringConversion, and for Date subclasses. */ +export function classObjectTag(lowerer: Lowerer, value: IrExpr, node: ts.Node): IrExpr | null { + if (value.type.kind !== "object") return null; + const info = lowerer.classes.get(value.type.className); + if (!info || dateRootOf(info) !== null) return null; + return classConversion(lowerer, value, "tag", node, STRING, (tag) => tag); +} + /** ToNumber(ToPrimitive(value, "number")) of a program class instance * (`Number(x)`): `[Symbol.toPrimitive]("number")`, then `valueOf` and then * `toString`, and NaN for "[object Object]". Null as for @@ -122,7 +142,7 @@ function mayBeBigint(lowerer: Lowerer, type: IrType): boolean { function classConversion( lowerer: Lowerer, value: IrExpr, - hint: ConversionHint, + hint: ConversionHint | "tag", node: ts.Node, resultType: IrType, finish: (primitive: IrExpr) => IrExpr, @@ -131,7 +151,7 @@ function classConversion( const info = lowerer.classes.get(value.type.className); if (!info || !plainProgramClass(info)) return null; const classes = subtree(lowerer, info); - for (const cls of classes) refuseReplacedConversions(lowerer, cls, node); + if (hint !== "tag") for (const cls of classes) refuseReplacedConversions(lowerer, cls, node); const plans = new Map(classes.map((cls) => [cls, planOf(lowerer, cls, hint, node)])); // A class whose plan differs from its base's starts a group; the deepest // groups are tested first, so each instance takes its own class's plan. @@ -164,12 +184,52 @@ function classConversion( }; return finish(dateValueMethod(plan.date, time, loc)!); } + if (plan.method === null && plan.tag) { + // Object.prototype.toString: "[object " + the tag + "]". + const owner = plan.tag.owner.def.name; + const tag: IrExpr = plan.tag.getter + ? accessorCall( + lowerer, + owner, + "get:sym:toStringTag", + lowerer.upcastTo(object, owner), + [], + STRING, + loc, + ) + : { + kind: "fieldGet", + obj: lowerer.upcastTo(object, owner), + className: owner, + field: TO_STRING_TAG_FIELD, + type: STRING, + loc, + }; + return finish({ + kind: "strConcat", + left: { + kind: "strConcat", + left: { kind: "strLit", value: "[object ", type: STRING, loc }, + right: tag, + type: STRING, + loc, + }, + right: { kind: "strLit", value: "]", type: STRING, loc }, + type: STRING, + loc, + }); + } if (plan.method === null) return finish({ kind: "strLit", value: "[object Object]", type: STRING, loc }); const args: IrExpr[] = plan.method === "sym:toPrimitive" ? [{ kind: "strLit", value: hint, type: STRING, loc }] : []; return finish(accessorCall(lowerer, cls.def.name, plan.method, object, args, plan.ret, loc)); }; + // "[object Object]" reads nothing of the instance, but the receiver + // expression still runs, once. + const own = plans.get(info)!; + if (tops.length === 0 && own.method === null && !own.date && !own.tag && !isSafeToRepeat(value)) + prefix.push({ kind: "exprStmt", expr: value, loc }); let result = convert(info, receiver); for (let i = tops.length - 1; i >= 0; i--) { const top = tops[i]!; @@ -228,10 +288,28 @@ function depth(info: ClassInfo): number { } function samePlan(a: Plan, b: Plan): boolean { - return ( - a.method === b.method && - (a.method !== null || b.method !== null || a.date === (b as { date?: string }).date) - ); + if (a.method !== null || b.method !== null) return a.method === b.method; + return a.date === b.date && a.tag?.owner === b.tag?.owner && a.tag?.getter === b.tag?.getter; +} + +/** The class's Symbol.toStringTag, when it has one: the class at the top + * of the chain whose layout holds the tag field (an own property, which + * shadows any getter), or its getter. Other tag types would make + * Object.prototype.toString answer "Object" or the value depending on the + * run, and are refused. */ +function toStringTagOf(lowerer: Lowerer, info: ClassInfo, node: ts.Node): ToStringTag | null { + const type = info.fields.get(TO_STRING_TAG_FIELD); + if (type !== undefined) { + if (type.kind !== "string") refuse(lowerer, node, info, "a non-string Symbol.toStringTag"); + let owner = info; + while (owner.base?.fields.has(TO_STRING_TAG_FIELD)) owner = owner.base; + return { owner, getter: false }; + } + const getter = findMethodOn(lowerer, info, "get:sym:toStringTag"); + if (!getter) return null; + if (getter.sig.ret.kind !== "string") + refuse(lowerer, node, info, "a non-string Symbol.toStringTag"); + return { owner: getter.declarer, getter: true }; } /** A conversion method the class declares or inherits (a field of that @@ -246,7 +324,16 @@ function declared( return found ? { ret: found.sig.ret } : null; } -function planOf(lowerer: Lowerer, info: ClassInfo, hint: ConversionHint, node: ts.Node): Plan { +function planOf( + lowerer: Lowerer, + info: ClassInfo, + hint: ConversionHint | "tag", + node: ts.Node, +): Plan { + if (hint === "tag") { + const tag = toStringTagOf(lowerer, info, node); + return tag ? { method: null, tag } : { method: null }; + } const exotic = declared(lowerer, info, "sym:toPrimitive"); if (exotic) { if (!primitive(lowerer, exotic.ret)) @@ -271,7 +358,8 @@ function planOf(lowerer: Lowerer, info: ClassInfo, hint: ConversionHint, node: t refuse(lowerer, node, info, `a '${method}' method that can return an object`); return { method, ret: found.ret }; } - return { method: null }; + const tag = toStringTagOf(lowerer, info, node); + return tag ? { method: null, tag } : { method: null }; } function primitive(lowerer: Lowerer, type: IrType): boolean { @@ -292,8 +380,6 @@ function refuseReplacedConversions(lowerer: Lowerer, info: ClassInfo, node: ts.N if (lowerer.prototypeMethodAccesses.has(method)) refuse(lowerer, node, info, `a '${method}' the program reaches through a prototype`); } - if (findMethodOn(lowerer, info, "get:sym:toStringTag") || info.fields.has("sym:toStringTag")) - refuse(lowerer, node, info, "a Symbol.toStringTag"); } function refuse(lowerer: Lowerer, node: ts.Node, info: ClassInfo, what: string): never { diff --git a/packages/compiler/src/frontend/lowering/completion-callbacks.ts b/packages/compiler/src/frontend/lowering/completion-callbacks.ts new file mode 100644 index 000000000..1f4013d5e --- /dev/null +++ b/packages/compiler/src/frontend/lowering/completion-callbacks.ts @@ -0,0 +1,155 @@ +/* Completion callbacks in programs that use AsyncLocalStorage. Node runs + * the callback of an operation a callback-style API starts (fs.readFile, + * crypto.randomBytes, zlib.gzip, dns.lookup, server.listen, net.connect, + * http.get, stream.finished, stream.pipeline, ...) with the stores active + * when the call started it. The runtime invokes these callbacks from + * the event loop, where the context is empty, so in programs whose + * scheduling carries contexts (RuntimeFeatures asyncContext) each such + * argument is bound to the active context at the call: als.bindCallback + * for typed closures, als.bindCallbackDyn for dyn functions. Other + * programs are left unchanged. */ +import { BOOL, type IrExpr, type IrModule, type IrStmt } from "../../ir/ir.js"; +import type { IrLibFn } from "../../ir/builtin-signatures.js"; +import { mapExprChildren, mapStmtChildren } from "../../ir/traverse.js"; +import { moduleRuntimeFeatures } from "../../ir/runtime-features.js"; + +/** The argument that is the completion callback: its index; "last" for + * calls whose callback is the final argument when one is passed; or + * "arguments" for the fs callback calls, whose argument is the call's + * dyn argument vector, where the runtime finds the callback. */ +type CallbackPosition = number | "last" | "arguments"; + +const COMPLETION_CALLBACKS: ReadonlyMap = new Map< + IrLibFn, + CallbackPosition +>([ + ["cp.execFile", 2], + ["crypto.generateKeyCb", 2], + ["crypto.hkdfCb", 5], + ["crypto.pbkdf2Cb", 5], + ["crypto.randomBytesCb", 1], + ["crypto.randomFillCb", 4], + ["crypto.randomFillCbDyn", 4], + ["crypto.randomIntCb", 3], + ["crypto.scryptCb", 4], + ["dgram.bindCb", 3], + ["dgram.closeCb", 1], + ["dgram.connectCb", 3], + ["dns.lookupDyn", 2], + ["fs.callbackCall", "arguments"], + ["fs.callbackGlob", "arguments"], + ["fs.renameCb", 2], + ["http.requestAgentCb", "last"], + ["http.requestCb", "last"], + ["http.requestConnCb", "last"], + ["http.requestDynCb", "last"], + ["http.requestUrlCb", "last"], + ["net.connectCb", 2], + ["net.listenCb", 2], + ["net.listenOptsCb", 4], + ["net.listenOptsReusePortCb", 5], + ["net.listenPathCb", 2], + ["process.stderrWriteBytesCb", 2], + ["process.stdoutWriteBytesCb", 2], + // stream.finished and stream.pipeline callbacks, which Node binds to + // the context of the call (eos' and pipeline's AsyncResource.bind). + ["stream.finished", 1], + ["stream.finishedDyn", 1], + ["stream.finishedOpts", "last"], + ["stream.finishedOptsSignal", "last"], + ["stream.pipeline", "last"], + ["stream.pipelineDyn", "last"], + ["stream.pipelineStages", "last"], + ["stream.pipelineStagesDyn", "last"], + ["zlib.cbOpts", 3], + ["zlib.cbOptsDyn", 3], + ["zlib.deflateCb", 1], + ["zlib.deflateRawCb", 1], + ["zlib.gunzipCb", 1], + ["zlib.gzipCb", 1], + ["zlib.inflateCb", 1], + ["zlib.inflateRawCb", 1], + ["zlib.unzipCb", 1], +]); + +/** Completion callbacks that fire from other contexts' code (a stream event + * that another context's call emits), so an empty context at the call + * binds too: the callback then runs with no store, as in Node. */ +const BIND_EMPTY_CONTEXT: ReadonlySet = new Set([ + "stream.finished", + "stream.finishedDyn", + "stream.finishedOpts", + "stream.finishedOptsSignal", + "stream.pipeline", + "stream.pipelineDyn", + "stream.pipelineStages", + "stream.pipelineStagesDyn", +]); + +/** `cb` bound to the context active where it is evaluated. Unless + * `bindEmpty`, an empty context leaves it unbound (`always` false): the + * loop already runs those callbacks with an empty context. */ +function bindToActiveContext(cb: IrExpr, bindEmpty = false): IrExpr { + const always: IrExpr = { kind: "boolLit", value: bindEmpty, type: BOOL, loc: cb.loc }; + if (cb.type.kind === "func") + return { + kind: "libCall", + fn: "als.bindCallback", + args: [cb, always], + type: cb.type, + loc: cb.loc, + }; + if (cb.type.kind === "dyn") + return { + kind: "libCall", + fn: "als.bindCallbackDyn", + args: [cb, always], + type: cb.type, + loc: cb.loc, + }; + return cb; +} + +/** An element of a dyn argument vector that can be the callback: a boxed + * closure binds as a typed closure; any other dyn value that is not a + * boxed primitive binds as a dyn function (non-functions pass through). */ +function bindArgumentElement(e: IrExpr): IrExpr { + if (e.kind === "dynFrom") { + return e.value.type.kind === "func" ? { ...e, value: bindToActiveContext(e.value) } : e; + } + return e.type.kind === "dyn" ? bindToActiveContext(e) : e; +} + +function bindCall(e: IrExpr): IrExpr { + if (e.kind !== "libCall") return e; + const position = COMPLETION_CALLBACKS.get(e.fn); + if (position === undefined) return e; + if (position === "arguments") { + const index = e.fn === "fs.callbackCall" ? 1 : 0; + const vector = e.args[index]; + if (vector === undefined || vector.kind !== "dynArrLit") return e; + const args = e.args.slice(); + args[index] = { + kind: "dynArrLit", + elems: vector.elems.map(bindArgumentElement), + type: vector.type, + loc: vector.loc, + }; + return { ...e, args }; + } + const index = position === "last" ? e.args.length - 1 : position; + const cb = e.args[index]; + if (cb === undefined || (position === "last" && cb.type.kind !== "func")) return e; + const args = e.args.slice(); + args[index] = bindToActiveContext(cb, BIND_EMPTY_CONTEXT.has(e.fn)); + return { ...e, args }; +} + +/** Binds the completion callbacks of `mod` when its scheduling carries + * AsyncLocalStorage contexts. */ +export function bindCompletionCallbacks(mod: IrModule): void { + if (!moduleRuntimeFeatures(mod).asyncContext) return; + const rewriteExpr = (e: IrExpr): IrExpr => bindCall(mapExprChildren(e, rewriteExpr, rewriteStmt)); + const rewriteStmt = (s: IrStmt): IrStmt => mapStmtChildren(s, rewriteExpr, rewriteStmt); + for (const fn of mod.functions) fn.body = fn.body.map(rewriteStmt); +} diff --git a/packages/compiler/src/frontend/lowering/containers/array-callbacks.ts b/packages/compiler/src/frontend/lowering/containers/array-callbacks.ts index 86f214bc1..edf65ab2b 100644 --- a/packages/compiler/src/frontend/lowering/containers/array-callbacks.ts +++ b/packages/compiler/src/frontend/lowering/containers/array-callbacks.ts @@ -2358,10 +2358,11 @@ function reduceHelper( * the left element first. Undefined values sink without reaching the user * comparator. The callback sequence differs from V8's TimSort, so exact * order and count parity are not claimed; results agree for consistent - * comparators. Without a comparator, string, number and boolean arrays use - * native stable ordering: their String conversions cannot call user code. - * Strings compare UTF-16 units; numbers compare their decimal spellings - * ([10, 9, 1] → [1, 10, 9]). Other element types still need a comparator. */ + * comparators. Without a comparator, string, number, boolean and Date + * arrays use native stable ordering: their String conversions cannot call + * user code. Strings compare UTF-16 units; numbers compare their decimal + * spellings ([10, 9, 1] → [1, 10, 9]); Dates compare their toString() text. + * Other element types still need a comparator. */ export function lowerArraySortCall( lowerer: Lowerer, call: ts.CallExpression, @@ -2374,7 +2375,7 @@ export function lowerArraySortCall( const copyFirst = method === "toSorted"; if ( call.arguments.nodes.length === 0 && - (elem.kind === "string" || elem.kind === "f64" || elem.kind === "bool") + (elem.kind === "string" || elem.kind === "f64" || elem.kind === "bool" || elem.kind === "date") ) { return { kind: "arrIntrinsic", @@ -2389,7 +2390,7 @@ export function lowerArraySortCall( lowerer.noLowering( `.${method} with ${call.arguments.nodes.length} arguments`, call, - "default ordering supports string[], number[] and boolean[]; other element types need a comparator", + "default ordering supports string[], number[], boolean[] and Date[]; other element types need a comparator", ); } const receiver = lowerer.lowerExpr(access.expression); diff --git a/packages/compiler/src/frontend/lowering/containers/array-construction.ts b/packages/compiler/src/frontend/lowering/containers/array-construction.ts index 1f10640c0..1ca51f526 100644 --- a/packages/compiler/src/frontend/lowering/containers/array-construction.ts +++ b/packages/compiler/src/frontend/lowering/containers/array-construction.ts @@ -1,4 +1,5 @@ import { keyedHelperName } from "../stable-names.js"; +import { indexedUnionArms, lowerIndexedUnionToArray } from "../indexed-unions.js"; import { generatorDrain } from "../iterator-adapters.js"; import { identityPreservingWidening } from "../coercions/identity.js"; import { liftedFreshSpread } from "../coercions/fresh-spread.js"; @@ -1054,6 +1055,10 @@ export function lowerArrayFromCall( loc, }; } + // A union of typed arrays and number arrays (indexed-unions.ts). + const indexedArms = indexedUnionArms(lowerer, src.type); + if (indexedArms && args.length === 1) + return lowerIndexedUnionToArray(lowerer, src, indexedArms, loc, true); if (src.type.kind === "set" && args.length === 1) { return { kind: "setIntrinsic", diff --git a/packages/compiler/src/frontend/lowering/containers/array-methods.ts b/packages/compiler/src/frontend/lowering/containers/array-methods.ts index 3ab2e84e2..4ef203edd 100644 --- a/packages/compiler/src/frontend/lowering/containers/array-methods.ts +++ b/packages/compiler/src/frontend/lowering/containers/array-methods.ts @@ -1081,14 +1081,25 @@ export function lowerArrayMethodCall( (lowerer.unions .get(elem.unionId) ?.arms.every( - (a) => a.kind === "f64" || a.kind === "string" || a.kind === "bool" || isUnitType(a), + (a) => + a.kind === "f64" || + a.kind === "string" || + a.kind === "bool" || + a.kind === "date" || + isUnitType(a), ) ?? false); - if (elem.kind !== "f64" && elem.kind !== "string" && elem.kind !== "bool" && !joinableUnion) { + if ( + elem.kind !== "f64" && + elem.kind !== "string" && + elem.kind !== "bool" && + elem.kind !== "date" && + !joinableUnion + ) { lowerer.unsupported( "SC1090", call, - "'.join()' on arrays of this element type (number, string, and boolean arrays join — unions of those with undefined/null arms too, the units printing empty like JS)", + "'.join()' on arrays of this element type (number, string, boolean and Date arrays join — unions of those with undefined/null arms too, the units printing empty like JS)", ); } const receiver = lowerer.lowerExpr(access.expression); diff --git a/packages/compiler/src/frontend/lowering/containers/bytes.ts b/packages/compiler/src/frontend/lowering/containers/bytes.ts index 995cb177a..a8890c431 100644 --- a/packages/compiler/src/frontend/lowering/containers/bytes.ts +++ b/packages/compiler/src/frontend/lowering/containers/bytes.ts @@ -1,7 +1,9 @@ import { keyedHelperName } from "../stable-names.js"; -import { boolLit, dynUndefinedExpr, numLit, varRef } from "../../../ir/build.js"; +import { allBytesArms, indexedUnionArms, lowerIndexedUnionMethod } from "../indexed-unions.js"; +import { boolLit, dynUndefinedExpr, nodeThrowExpr, numLit, varRef } from "../../../ir/build.js"; import { BUF_NUM_METHODS } from "./buffer-numeric-methods.js"; import * as ts from "../../ts7/adapter.js"; +import { constituentTypes } from "../../ts7/checker.js"; import { BIGINT_T, BOOL, @@ -15,6 +17,7 @@ import { type IrLibFn, type IrLocal, type IrParam, + type IrFunction, type IrStmt, type IrType, STRING, @@ -30,7 +33,8 @@ import { import { isJsSourceFile, locOf } from "../../program.js"; import { stdlibGlobalNameOf } from "../surfaces.js"; import type { Lowerer } from "../lowerer.js"; -import { newFnCtx, own } from "../lowerer.js"; +import { ENTRY_NAME, newFnCtx, own } from "../lowerer.js"; +import { everyStmt } from "../../../ir/traverse.js"; import { buildBytesSortFn, closureKeyOrder } from "../lower-array-sort.js"; import { isSafeToDiscard } from "../expressions/evaluation-safety.js"; import { lowerDynObjectLiteral } from "../expressions/object-literals.js"; @@ -40,7 +44,7 @@ import { lowerStaticallyUndefinedArgument, } from "../optional-arguments.js"; import { lowerArraySpreadItems, lowerCheckedArrayFrom } from "./array-construction.js"; -import { lowerToNumberArgument } from "../lower-exprs.js"; +import { lowerOptionalNumber, lowerToNumberArgument } from "../lower-exprs.js"; import { lowerBytesIteratorValue } from "../iterator-adapters.js"; import { lowerArrayCallback } from "./callback-arguments.js"; import { callbackSite, findPredicateOk, predicateToBoolean } from "./array-callbacks.js"; @@ -903,6 +907,126 @@ function lowerDataViewNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { return lowerArrayBufferView(lowerer, args, "dv", locOf(expr)); } +/** `receiver.set(src, offset?)` on a lowered typed-array receiver. */ +function bytesSetIntrinsic( + lowerer: Lowerer, + receiver: IrExpr, + argNodes: readonly ts.Expression[], + loc: SrcLoc, +): IrExpr { + const src = lowerer.lowerExpr(argNodes[0]!); + const method = src.type.kind === "bytes" ? "setFrom" : "setFromDyn"; + const args = [src.type.kind === "bytes" ? src : lowerer.coerceInto(argNodes[0]!, src, DYN)]; + if (argNodes.length === 2) + args.push( + lowerOptionalArgument(lowerer, argNodes[1]!, F64, { + kind: "numLit", + value: 0, + type: F64, + loc, + }), + ); + return { kind: "bytesIntrinsic", method, receiver, args, type: VOID, loc }; +} + +/** `Uint8Array.prototype.set.call(target, src, offset?)`, through any typed + * array constructor's prototype (they share %TypedArray%.prototype.set). A + * target the checker types as a typed array or Buffer lowers like + * `target.set(src, offset)`. Any other target, a DataView included, is + * checked at run time: a typed array calls its `set`, and + * anything else throws Node's TypeError "this is not a typed array.", + * after every argument is evaluated. */ +export function lowerBytesPrototypeSetCall( + lowerer: Lowerer, + call: ts.CallExpression, + access: ts.PropertyAccessExpression, +): IrExpr | null { + if ( + call.questionDotToken || + access.questionDotToken || + access.name.text !== "call" || + !ts.isPropertyAccessExpression(access.expression) + ) + return null; + const method = access.expression; + const proto = method.expression; + if ( + method.questionDotToken || + method.name.text !== "set" || + !ts.isPropertyAccessExpression(proto) || + proto.questionDotToken || + proto.name.text !== "prototype" || + !ts.isIdentifier(proto.expression) || + !lowerer.isStdlibMember(method) + ) + return null; + const ctor = lowerer.resolveValueSymbol(proto.expression); + if (!ctor || !lowerer.isStdlibSymbol(ctor) || !own(BYTES_CTORS, ts.symbolName(ctor))) return null; + const argNodes = call.arguments.nodes; + if (argNodes.length < 2 || argNodes.length > 3 || argNodes.some(ts.isSpreadElement)) return null; + const loc = locOf(call); + const target = lowerer.lowerExpr(argNodes[0]!); + // A DataView lowers to the same bytes type, so the checker type decides: + // only a typed array (or a Buffer) takes the method's path. + const targetSymbol = lowerer.typeOf(argNodes[0]!).getSymbol(); + if ( + target.type.kind === "bytes" && + targetSymbol !== undefined && + lowerer.isStdlibSymbol(targetSymbol) && + (own(BYTES_CTORS, ts.symbolName(targetSymbol)) !== undefined || + ts.symbolName(targetSymbol) === "Buffer") + ) + return bytesSetIntrinsic(lowerer, target, argNodes.slice(1), loc); + const slot = (value: IrExpr): { decl: IrStmt; read: IrExpr } => { + const local = lowerer.declareHiddenLocal("%setArg", DYN); + return { + decl: { kind: "varDecl", localId: local.id, init: value, loc }, + read: varRef(local.id, DYN, loc), + }; + }; + const slots = [ + slot(lowerer.coerceInto(argNodes[0]!, target, DYN)), + ...argNodes.slice(1).map((arg) => slot(lowerer.lowerExprExpecting(arg, DYN))), + ]; + const isTypedArray: IrExpr = { + kind: "libCall", + fn: "util.typeIs", + args: [slots[0]!.read, { kind: "strLit", value: "isTypedArray", type: STRING, loc }], + type: BOOL, + loc, + }; + return { + kind: "seqExpr", + stmts: [ + ...slots.map((s) => s.decl), + { + kind: "if", + cond: { kind: "unary", op: "!", operand: isTypedArray, type: BOOL, loc }, + then: [ + { + kind: "exprStmt", + expr: nodeThrowExpr(1, "", "this is not a typed array.", VOID, loc), + loc, + }, + ], + else_: null, + loc, + }, + ], + result: { + kind: "dynInvoke", + recv: slots[0]!.read, + method: "set", + calleeName: ts.getTextOfNode(method), + args: slots.slice(1).map((s) => s.read), + type: DYN, + loc, + }, + type: DYN, + loc, + }; +} + /** Method calls on typed-array/Buffer receivers: slice (copy), subarray * (shared view), toReversed, copyWithin, set(src, * offset?), and the u8-only Buffer surface: toString(enc?) plus the @@ -910,6 +1034,119 @@ function lowerDataViewNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { * variable-width read/writeUIntLE quartet — BUF_NUM_METHODS). Everything * else the lib declares (fill, indexOf, reverse, ...) falls through to * the SC2020 member fence. Null when this isn't a bytes method call. */ +/** The [needle, fromIndex] arguments of TypedArray indexOf, lastIndexOf + * and includes. A present fromIndex converts even when it is undefined + * (NaN, which ToIntegerOrInfinity makes 0); an absent one is 0, or + * +Infinity for lastIndexOf, which starts at the last element. */ +function elementSearchArgs(lowerer: Lowerer, call: ts.CallExpression, name: string): IrExpr[] { + const nodes = call.arguments.nodes; + const loc = locOf(call); + const needle = lowerer.lowerExprExpecting(nodes[0]!, F64); + const from: IrExpr = nodes[1] + ? lowerOptionalNumber(lowerer, lowerer.lowerExpr(nodes[1]), locOf(nodes[1]), nodes[1]) + : ({ + kind: "numLit", + value: name === "lastIndexOf" ? Infinity : 0, + type: F64, + loc, + } satisfies IrExpr); + // An undefined fromIndex is NaN once converted, which starts at 0. + if (from.kind === "unitLit") return [needle, { kind: "numLit", value: NaN, type: F64, loc }]; + if (from.type.kind === "undefinedT" || from.type.kind === "void") + return [ + needle, + { + kind: "seqExpr", + stmts: [{ kind: "exprStmt", expr: from, loc }], + result: { kind: "numLit", value: NaN, type: F64, loc }, + type: F64, + loc, + }, + ]; + if (from.type.kind !== "f64") lowerer.noLowering(`.${name} with a non-number fromIndex`, call); + return [needle, from]; +} + +const ELEMENT_SEARCHES: Readonly> = { + indexOf: "indexOfElem", + lastIndexOf: "lastIndexOfElem", + includes: "includesElem", +}; + +/** Whether a union has a Buffer arm, under any alias name: each arm's own + * symbol names it. */ +function hasBufferArm(type: ts.Type): boolean { + const arms = type.flags & ts.TypeFlags.Union ? constituentTypes(type) : [type]; + return arms.some((arm) => { + const symbol = arm.getSymbol(); + return symbol !== undefined && ts.symbolName(symbol) === "Buffer"; + }); +} + +/** The element-kind-generic typed-array methods on a union of typed + * arrays (indexed-unions.ts): the arguments lower as for one typed array, + * and each arm runs the method on its own receiver. Null for the methods + * that need a narrowing to one kind first. */ +function lowerUnionBytesMethodCall( + lowerer: Lowerer, + call: ts.CallExpression, + access: ts.PropertyAccessExpression, + name: string, +): IrExpr | null { + if (isJsSourceFile(ts.getSourceFileOfNode(call))) return null; + // A Buffer arm's methods differ from a typed array's (slice aliases, fill + // checks its range, searches take strings and wrap numbers), so such a + // union narrows to its arms first. + if (hasBufferArm(lowerer.typeOf(access.expression))) return null; + const nodes = call.arguments.nodes; + if (nodes.some(ts.isSpreadElement)) return null; + const loc = locOf(call); + const num = (value: number): IrExpr => ({ kind: "numLit", value, type: F64, loc }); + const optional = (i: number, fallback: IrExpr, type: IrType = F64): IrExpr => + nodes[i] ? lowerOptionalArgument(lowerer, nodes[i]!, type, fallback) : fallback; + let method: IrBytesIntrinsicMethod; + let args: () => IrExpr[]; + let result: "receiver" | IrType; + if ((name === "slice" || name === "subarray") && nodes.length <= 2) { + method = name; + args = () => [optional(0, num(0)), optional(1, num(Infinity))]; + result = "receiver"; + } else if (name === "fill" && nodes.length <= 3) { + method = "fillElem"; + args = () => [optional(0, num(0)), optional(1, num(0)), optional(2, num(Infinity))]; + result = "receiver"; + } else if (name === "copyWithin" && nodes.length <= 3) { + method = "copyWithin"; + args = () => [optional(0, num(0)), optional(1, num(0)), optional(2, num(Infinity))]; + result = "receiver"; + } else if (name === "toReversed" && nodes.length === 0) { + method = "toReversed"; + args = () => []; + result = "receiver"; + } else if (Object.hasOwn(ELEMENT_SEARCHES, name) && (nodes.length === 1 || nodes.length === 2)) { + method = ELEMENT_SEARCHES[name]!; + args = () => elementSearchArgs(lowerer, call, name); + result = name === "includes" ? BOOL : F64; + } else if (name === "join" && nodes.length <= 1) { + method = "join"; + args = () => [optional(0, { kind: "strLit", value: ",", type: STRING, loc }, STRING)]; + result = STRING; + } else if (name === "set" && (nodes.length === 1 || nodes.length === 2)) { + const source = lowerer.mapTypeOf(lowerer.typeOf(nodes[0]!)); + method = source?.kind === "bytes" ? "setFrom" : "setFromDyn"; + args = () => { + const src = lowerer.lowerExpr(nodes[0]!); + const first = src.type.kind === "bytes" ? src : lowerer.coerceInto(nodes[0]!, src, DYN); + return nodes.length === 2 ? [first, optional(1, num(0))] : [first]; + }; + result = VOID; + } else return null; + const receiver = lowerer.lowerExpr(access.expression); + const arms = indexedUnionArms(lowerer, receiver.type); + if (!arms || !allBytesArms(arms)) return null; + return lowerIndexedUnionMethod(lowerer, receiver, arms, method, args(), result, loc); +} + export function lowerBytesMethodCall( lowerer: Lowerer, call: ts.CallExpression, @@ -918,6 +1155,9 @@ export function lowerBytesMethodCall( if (lowerer.chainBlocked(access, call)) return null; const name = access.name.text; const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(access.expression)); + const unionArms = receiverIr ? indexedUnionArms(lowerer, receiverIr) : null; + if (unionArms && allBytesArms(unionArms) && lowerer.isStdlibMember(access)) + return lowerUnionBytesMethodCall(lowerer, call, access, name); if (receiverIr?.kind !== "bytes") return null; if (!lowerer.isStdlibMember(access)) return null; const loc = locOf(call); @@ -964,6 +1204,29 @@ export function lowerBytesMethodCall( if (value) return value; } } + // TypedArray indexOf / lastIndexOf / includes (the Buffer-declared forms, + // which also take string and Buffer needles, lowered above). + if ( + Object.hasOwn(ELEMENT_SEARCHES, name) && + !lowerer.checker + .getSymbolAtLocation(access.name) + ?.declarations.some( + (d) => ts.isInterfaceDeclaration(d.parent) && d.parent.name.text === "Buffer", + ) + ) { + if (nArgs < 1 || nArgs > 2 || call.arguments.nodes.some(ts.isSpreadElement)) { + lowerer.noLowering(`.${name} with ${nArgs} arguments on typed arrays`, call); + } + const receiver = lowerer.lowerExprExpecting(access.expression, receiverIr); + return { + kind: "bytesIntrinsic", + method: ELEMENT_SEARCHES[name]!, + receiver, + args: elementSearchArgs(lowerer, call, name), + type: name === "includes" ? BOOL : F64, + loc, + }; + } if (name === "toReversed") { if (nArgs !== 0) { lowerer.noLowering(`.toReversed with ${nArgs} arguments on typed arrays`, call); @@ -1106,21 +1369,7 @@ export function lowerBytesMethodCall( lowerer.noLowering(`.set with ${nArgs} arguments on typed arrays`, call); } const receiver = lowerer.lowerExprExpecting(access.expression, receiverIr); - const src = lowerer.lowerExpr(call.arguments.nodes[0]!); - const method = src.type.kind === "bytes" ? "setFrom" : "setFromDyn"; - const args = [ - src.type.kind === "bytes" ? src : lowerer.coerceInto(call.arguments.nodes[0]!, src, DYN), - ]; - if (nArgs === 2) - args.push( - lowerOptionalArgument(lowerer, call.arguments.nodes[1]!, F64, { - kind: "numLit", - value: 0, - type: F64, - loc, - }), - ); - return { kind: "bytesIntrinsic", method, receiver, args, type: VOID, loc }; + return bytesSetIntrinsic(lowerer, receiver, call.arguments.nodes, loc); } if (name === "toString") { // Buffer's toString(encoding?) — utf8 by default. A 0-arg toString @@ -1271,6 +1520,8 @@ export function lowerBytesMethodCall( "the numeric families read/write Buffer bytes", ); } + if (!numericArguments(lowerer, call)) + return dynNumericCall(lowerer, call, access, name, receiverIr, loc); const write = name.startsWith("write"); const required = write ? 1 : 0; // value; offset defaults to 0 if (nArgs < required || nArgs > required + 1) { @@ -1448,6 +1699,39 @@ const BUF_BIGINT_METHODS: Record lowerer.mapTypeOf(lowerer.typeOf(a))?.kind === "f64"); +} + +/** A numeric read/write with an argument that is not a number (a string + * offset from untyped JavaScript): the checked-dynamic Buffer method runs + * Node's argument validation at runtime. */ +function dynNumericCall( + lowerer: Lowerer, + call: ts.CallExpression, + access: ts.PropertyAccessExpression, + name: string, + receiverIr: IrType, + loc: SrcLoc, +): IrExpr { + const receiver = lowerer.lowerExprExpecting(access.expression, receiverIr); + return { + kind: "dynCheck", + value: { + kind: "dynInvoke", + recv: { kind: "dynFrom", value: receiver, type: DYN, loc, liveRef: true }, + method: name, + calleeName: ts.getTextOfNode(access), + args: call.arguments.nodes.map((a) => lowerer.lowerExprExpecting(a, DYN)), + type: DYN, + loc, + }, + type: F64, + loc, + }; +} + /** The variable-width quartet (offset + byteLength) → sign/endian token. */ const BUF_NUM_VAR_METHODS: Record = { readUIntBE: "ube", @@ -1807,7 +2091,12 @@ function knownBufferProducer( * or empty encoding and throws ERR_UNKNOWN_ENCODING for an unknown name; an * ArrayBuffer takes the second argument as its byte offset; other sources * ignore it. */ -function dynBufferFromParts(source: IrExpr, encoding: IrExpr, loc: SrcLoc): IrExpr { +function dynBufferFromParts( + source: IrExpr, + encoding: IrExpr, + loc: SrcLoc, + length?: IrExpr, +): IrExpr { const utf8: IrExpr = { kind: "strLit", value: "utf8", type: STRING, loc }; const isString = (x: IrExpr): IrExpr => ({ kind: "dynTest", @@ -1848,20 +2137,48 @@ function dynBufferFromParts(source: IrExpr, encoding: IrExpr, loc: SrcLoc): IrEx type: BYTES_U8, loc, }, - // An ArrayBuffer takes the second argument as its byte offset - // (scr_buffer_from_dyn_offset_pooled); other values convert with utf8. - else_: { - kind: "libCall", - fn: "buffer.poolFromDynOffset", - args: [source, encoding], - type: BYTES_U8, - loc, - }, + // An ArrayBuffer takes the second argument as its byte offset and the + // third as its length (scr_buffer_from_dyn_range_pooled); other values + // convert with utf8. + else_: length + ? { + kind: "libCall", + fn: "buffer.poolFromDynRange", + args: [source, encoding, length], + type: BYTES_U8, + loc, + } + : { + kind: "libCall", + fn: "buffer.poolFromDynOffset", + args: [source, encoding], + type: BYTES_U8, + loc, + }, type: BYTES_U8, loc, }; } +/** Buffer.from(arrayBuffer, byteOffset[, length]): each argument in order + * as a checked-dynamic value, read by the runtime as Node's + * fromArrayBuffer reads them. */ +function bufferFromArrayBufferRange( + lowerer: Lowerer, + args: readonly ts.Expression[], + loc: SrcLoc, + first?: IrExpr, +): IrExpr { + const operands = [0, 1, 2].map((i) => + i === 0 && first + ? first + : args[i] + ? lowerer.lowerExprExpecting(args[i]!, DYN) + : dynUndefinedExpr(loc), + ); + return { kind: "libCall", fn: "buffer.poolFromDynRange", args: operands, type: BYTES_U8, loc }; +} + /** Lowers `source` and `encNode` (or undefined) into hidden dyn locals, in * source order, and builds the result from their references. */ function withDynBufferArguments( @@ -1909,52 +2226,81 @@ function dynBufferFromRuntimeEncoding( ); } -/** The deprecated `new Buffer(arg[, encoding])` in a dependency's - * JavaScript, through the global or an alias of it (buffer-crc32 and yazl - * keep it as the fallback for runtimes without Buffer.alloc/Buffer.from). - * Node never emits DEP0005 for calls from node_modules, so the construction - * is exactly Node's: a number allocates zero-filled bytes (and throws - * ERR_INVALID_ARG_TYPE beside a string second argument), anything else - * converts as Buffer.from does. Null elsewhere: other callers owe the - * deprecation warning. */ -export function lowerNodeModulesBufferConstruct( +/** The deprecated `Buffer(arg[, encodingOrOffset[, length]])`, called or + * constructed through the global or an alias of it, as Node's Buffer + * function runs it: the arguments evaluate in order, DEP0005 is reported + * on the next tick (once, and never for a call made from node_modules, + * which only counts toward Node's 10000-call check), then a number + * allocates zero-filled bytes (and throws ERR_INVALID_ARG_TYPE beside a + * string second argument) and anything else converts as Buffer.from does. + * Null for spread arguments and more than three. */ +export function lowerBufferConstruct( lowerer: Lowerer, - expr: ts.NewExpression, + expr: ts.NewExpression | ts.CallExpression, ): IrExpr | null { - const sf = expr.getSourceFile(); - if (!isJsSourceFile(sf) || !/[\\/]node_modules[\\/]/.test(sf.fileName)) return null; + if (ts.isCallExpression(expr) && expr.questionDotToken) return null; const args = expr.arguments?.nodes ?? []; - if (args.length < 1 || args.length > 2 || args.some(ts.isSpreadElement)) return null; + if (args.length > 3 || args.some(ts.isSpreadElement)) return null; if (stdlibGlobalNameOf(lowerer, expr.expression) !== "Buffer") return null; const loc = locOf(expr); - const value = withDynBufferArguments( - lowerer, - lowerer.lowerExpr(args[0]!), - args[1], - loc, - (source, encoding) => ({ + const locals = [0, 1, 2].slice(0, Math.max(args.length, 2)).map((i) => { + const local = lowerer.declareHiddenLocal("%bufferArgument", DYN); + const node = args[i]; + const init = node === undefined ? dynUndefinedExpr(loc) : lowerer.lowerExprExpecting(node, DYN); + return { local, init }; + }); + const [source, second, third] = locals.map(({ local }) => varRef(local.id, DYN, loc)) as [ + IrExpr, + IrExpr, + IrExpr | undefined, + ]; + const inNodeModules = /[\\/]node_modules[\\/]/.test(expr.getSourceFile().fileName); + const value: IrExpr = { + kind: "ternary", + cond: { kind: "dynTest", test: "number", value: source, type: BOOL, loc }, + then: { kind: "ternary", - cond: { kind: "dynTest", test: "number", value: source, type: BOOL, loc }, - then: { - kind: "ternary", - cond: { kind: "dynTest", test: "string", value: encoding, type: BOOL, loc }, - then: { kind: "libCall", fn: "buffer.newStringFail", args: [source], type: BYTES_U8, loc }, - else_: { - kind: "libCall", - fn: "buffer.alloc", - args: [{ kind: "dynCheck", value: source, type: F64, loc }], - type: BYTES_U8, - loc, - }, + cond: { kind: "dynTest", test: "string", value: second, type: BOOL, loc }, + then: { kind: "libCall", fn: "buffer.newStringFail", args: [source], type: BYTES_U8, loc }, + else_: { + kind: "libCall", + fn: "buffer.alloc", + args: [{ kind: "dynCheck", value: source, type: F64, loc }], type: BYTES_U8, loc, }, - else_: dynBufferFromParts(source, encoding, loc), type: BYTES_U8, loc, - }), - ); - return { kind: "libCall", fn: "buffer.brand", args: [value], type: BYTES_U8, loc }; + }, + else_: dynBufferFromParts(source, second, loc, third), + type: BYTES_U8, + loc, + }; + return { + kind: "seqExpr", + stmts: [ + ...locals.map(({ local, init }): IrStmt => ({ + kind: "varDecl", + localId: local.id, + init, + loc, + })), + { + kind: "exprStmt", + expr: { + kind: "libCall", + fn: inNodeModules ? "buffer.deprecationCount" : "buffer.deprecation", + args: [], + type: VOID, + loc, + }, + loc, + }, + ], + result: { kind: "libCall", fn: "buffer.brand", args: [value], type: BYTES_U8, loc }, + type: BYTES_U8, + loc, + }; } /** Buffer.from(array or bytes, extra): Node copies an array-like source and @@ -2018,7 +2364,16 @@ function lowerBufferStaticValue( lowerer.typeOf(args[0]!).getSymbol(), ) ) { - return lowerArrayBufferView(lowerer, args, "u8", loc); + // A byte offset or length follows Node's fromArrayBuffer (its + // conversions and ERR_BUFFER_OUT_OF_BOUNDS), not the typed-array view. + if (args.length === 1) return lowerArrayBufferView(lowerer, args, "u8", loc); + return bufferFromArrayBufferRange(lowerer, args, loc); + } + // Buffer.from(value, byteOffset, length) over an untyped value: an + // ArrayBuffer's range, or the two-argument conversion of other values. + if (args.length === 3 && !args.some(ts.isSpreadElement)) { + const first = lowerer.lowerExpr(args[0]!); + if (first.type.kind === "dyn") return bufferFromArrayBufferRange(lowerer, args, loc, first); } // Buffer.from(x.buffer[, byteOffset[, length]]): a u8 VIEW sharing // x's storage (Node shares the ArrayBuffer; length is in BYTES). @@ -2445,11 +2800,101 @@ function lowerBufferStaticValue( const s = lowerer.lowerExprExpecting(args[0]!, STRING); return { kind: "libCall", fn: "buffer.isEncoding", args: [s], type: BOOL, loc }; } + // Any other single argument: Node answers false for non-strings. + if (args.length === 1 && !ts.isSpreadElement(args[0]!)) { + const value = lowerer.declareHiddenLocal("%bufferEncoding", DYN); + const ref = varRef(value.id, DYN, loc); + return { + kind: "seqExpr", + stmts: [ + { + kind: "varDecl", + localId: value.id, + init: lowerer.lowerExprExpecting(args[0]!, DYN), + loc, + }, + ], + result: { + kind: "logical", + op: "&&", + left: { kind: "dynTest", test: "string", value: ref, type: BOOL, loc }, + right: { + kind: "libCall", + fn: "buffer.isEncoding", + args: [{ kind: "dynCheck", value: ref, type: STRING, loc }], + type: BOOL, + loc, + }, + type: BOOL, + loc, + }, + type: BOOL, + loc, + }; + } lowerer.noLowering( "Buffer.isEncoding with this argument shape", call, - "one string-typed argument is the lowered form", + "one argument is the lowered form", ); } return null; // of, copyBytesFrom, ... → the SC2020 member fence } + +/** `Buffer.prototype.` or `Buffer.prototype[key]` read in JavaScript + * (smart-buffer's `typeof Buffer.prototype[method]` probe): the member as + * scr_buffer_prototype_member answers it. Null for other expressions. */ +export function lowerBufferPrototypeMember( + lowerer: Lowerer, + expr: ts.PropertyAccessExpression | ts.ElementAccessExpression, +): IrExpr | null { + if (expr.questionDotToken || !isJsSourceFile(expr.getSourceFile())) return null; + const receiver = expr.expression; + if ( + !ts.isPropertyAccessExpression(receiver) || + receiver.questionDotToken || + receiver.name.text !== "prototype" || + stdlibGlobalNameOf(lowerer, receiver.expression) !== "Buffer" + ) + return null; + const loc = locOf(expr); + const key: IrExpr = ts.isPropertyAccessExpression(expr) + ? { + kind: "dynFrom", + value: { kind: "strLit", value: expr.name.text, type: STRING, loc }, + type: DYN, + loc, + } + : lowerer.lowerExprExpecting(expr.argumentExpression, DYN); + return { kind: "libCall", fn: "buffer.prototypeMember", args: [key], type: DYN, loc }; +} + +const BUFFER_VALUE_FNS: ReadonlySet = new Set([ + "buffer.constructor", + "buffer.staticValue", + "buffer.prototypeMember", +]); + +/** A program that reads the Buffer function's value installs it in %main, + * so a Buffer's `.constructor` answers the same function. */ +export function installBufferValue(functions: IrFunction[]): void { + let found = false; + const visitor = { + expr: (e: IrExpr): boolean => { + if (e.kind === "libCall" && BUFFER_VALUE_FNS.has(e.fn)) found = true; + return !found; + }, + stmt: (): boolean => !found, + }; + for (const fn of functions) { + for (const s of fn.body) everyStmt(s, visitor); + if (found) break; + } + const main = functions.find((fn) => fn.name === ENTRY_NAME); + if (!found || !main) return; + main.body.unshift({ + kind: "exprStmt", + expr: { kind: "libCall", fn: "buffer.installValue", args: [], type: VOID, loc: main.loc }, + loc: main.loc, + }); +} diff --git a/packages/compiler/src/frontend/lowering/cycle-initialization.ts b/packages/compiler/src/frontend/lowering/cycle-initialization.ts index 4b1e74fcf..8c8ce06e6 100644 --- a/packages/compiler/src/frontend/lowering/cycle-initialization.ts +++ b/packages/compiler/src/frontend/lowering/cycle-initialization.ts @@ -707,11 +707,17 @@ function rewriteFunction( * unchecked. */ export function refuseUncheckedCycleReads( lowerer: Lowerer, - classes: readonly { baseValueGlobal?: string; symbolFields?: { globalId: string }[] }[], + classes: readonly { + baseValueGlobal?: string; + symbolFields?: readonly ({ globalId: string } | { wellKnown: string })[]; + }[], ): void { if (lowerer.cycleInitFlags.size === 0) return; for (const cls of classes) { - const ids = [cls.baseValueGlobal, ...(cls.symbolFields ?? []).map((f) => f.globalId)]; + const ids = [ + cls.baseValueGlobal, + ...(cls.symbolFields ?? []).map((f) => ("globalId" in f ? f.globalId : undefined)), + ]; for (const id of ids) { const flag = id === undefined ? undefined : lowerer.cycleInitFlags.get(id); if (flag === undefined) continue; diff --git a/packages/compiler/src/frontend/lowering/date-values.ts b/packages/compiler/src/frontend/lowering/date-values.ts new file mode 100644 index 000000000..ef5eafebe --- /dev/null +++ b/packages/compiler/src/frontend/lowering/date-values.ts @@ -0,0 +1,207 @@ +import * as ts from "../ts7/adapter.js"; +import type { Lowerer } from "./lowerer.js"; + +/** Date methods whose receiver is only read for its time value: a Date + * local used as their receiver can store the time value instead of a Date + * object (builtins/dates.ts lowers them). */ +const TIME_VALUE_METHODS = new Set([ + "getTime", + "valueOf", + "toISOString", + "toJSON", + "toString", + "toDateString", + "toTimeString", + "toUTCString", + "getFullYear", + "getUTCFullYear", + "getMonth", + "getUTCMonth", + "getDate", + "getUTCDate", + "getDay", + "getUTCDay", + "getHours", + "getUTCHours", + "getMinutes", + "getUTCMinutes", + "getSeconds", + "getUTCSeconds", + "getMilliseconds", + "getUTCMilliseconds", + "getTimezoneOffset", +]); + +const RELATIONAL = new Set([ + ts.SyntaxKind.LessThanToken, + ts.SyntaxKind.LessThanEqualsToken, + ts.SyntaxKind.GreaterThanToken, + ts.SyntaxKind.GreaterThanEqualsToken, +]); + +/** True when a local `const` (or a `let` that is never assigned again) + * initialized with `new Date(...)` can keep the Date's time value instead + * of a Date object: no use observes the Date's identity. Every use reads the time value as + * a Date method receiver, a String() or template operand, a relational or + * string `+` operand, or `new Date(d)`'s argument, inside the declaring + * function, or inside the block or loop that scopes a module-level + * binding (no closure captures it). Top-level module bindings keep the + * object. Other uses box the time value into a new Date at the use. */ +export function isTimeValueDateDeclaration( + lowerer: Lowerer, + decl: ts.VariableDeclaration, +): boolean { + const known = lowerer.timeValueDateDecisions.get(decl); + if (known !== undefined) return known; + const answer = decideTimeValueDate(lowerer, decl); + lowerer.timeValueDateDecisions.set(decl, answer); + return answer; +} + +function decideTimeValueDate(lowerer: Lowerer, decl: ts.VariableDeclaration): boolean { + if (!ts.isIdentifier(decl.name) || decl.initializer === undefined) return false; + // Only a Date the declaration creates: another reference to an existing + // Date can mutate it (`const d = a; a.setTime(0)`), and d must see that. + if (!createsDate(lowerer, decl.initializer)) return false; + const sf = decl.getSourceFile(); + if (sf.isDeclarationFile || /\.[cm]?jsx?$/.test(sf.fileName)) return false; + const list = decl.parent; + if (!ts.isVariableDeclarationList(list)) return false; + const isConst = (list.flags & ts.NodeFlags.Const) !== 0; + const isLet = (list.flags & ts.NodeFlags.Let) !== 0; + if (!isConst && !isLet) return false; + if (!ts.isVariableStatement(list.parent) && !ts.isForStatement(list.parent)) return false; + const fn = enclosingFunction(decl); + // The uses to check: the function body, or for a binding inside a block + // or loop at module level (a local of the module's initializer, not a + // module binding) the block or loop that scopes it. + let scope: ts.Node | undefined; + if (fn !== null) scope = fn.body; + else if (!inClassStaticBlock(decl)) scope = moduleBlockScope(decl); + if (scope === undefined) return false; + // The lib Date interface by its symbol first: mapping every candidate + // binding's type would intern its unions before the lowering does. + const typeSymbol = lowerer.typeOf(decl.name).getSymbol(); + if ( + typeSymbol === undefined || + ts.symbolName(typeSymbol) !== "Date" || + !typeSymbol.declarations.some( + (d) => ts.isInterfaceDeclaration(d) && lowerer.isStdlibFile(d.getSourceFile()), + ) + ) + return false; + if (lowerer.mapTypeOf(lowerer.typeOf(decl.name))?.kind !== "date") return false; + const symbol = lowerer.checker.getSymbolAtLocation(decl.name); + if (!symbol) return false; + let ok = true; + const visit = (node: ts.Node): void => { + if (!ok) return; + if (ts.isIdentifier(node) && node !== decl.name && node.text === decl.name.text) { + if (lowerer.checker.getSymbolAtLocation(node) === symbol) { + if (enclosingFunction(node) !== fn || !readsTimeValue(lowerer, node)) ok = false; + } + return; + } + ts.forEachChild(node, visit); + }; + visit(scope); + return ok; +} + +/** True for `new Date(...)` of the standard Date, through parentheses and + * type-only wrappers: a Date no other reference can reach. */ +function createsDate(lowerer: Lowerer, init: ts.Expression): boolean { + let e = init; + while ( + ts.isParenthesizedExpression(e) || + ts.isAsExpression(e) || + ts.isSatisfiesExpression(e) || + ts.isTypeAssertion(e) || + ts.isNonNullExpression(e) + ) + e = e.expression; + return ts.isNewExpression(e) && lowerer.isStdlibGlobal(e.expression, "Date"); +} + +/** The block or for statement that scopes a module-level binding which is + * not a module binding: a declaration in a block or loop body, or in a + * for statement's initializer. Undefined for a top-level declaration. */ +function moduleBlockScope(decl: ts.VariableDeclaration): ts.Node | undefined { + const list = decl.parent; + if (!list || !ts.isVariableDeclarationList(list)) return undefined; + if (ts.isForStatement(list.parent)) return list.parent; + const statement = list.parent; + if (!ts.isVariableStatement(statement) || ts.isSourceFile(statement.parent)) return undefined; + for (let cur: ts.Node | undefined = statement.parent; cur; cur = cur.parent) { + if (ts.isBlock(cur) || ts.isCaseBlock(cur)) return cur; + if (ts.isSourceFile(cur)) return undefined; + } + return undefined; +} + +function inClassStaticBlock(node: ts.Node): boolean { + for (let cur = node.parent; cur; cur = cur.parent) { + if (ts.isClassStaticBlockDeclaration(cur)) return true; + if (ts.isSourceFile(cur)) return false; + } + return false; +} + +function enclosingFunction(node: ts.Node): ts.FunctionLikeDeclaration | null { + for (let cur = node.parent; cur; cur = cur.parent) { + if ( + ts.isFunctionDeclaration(cur) || + ts.isFunctionExpression(cur) || + ts.isArrowFunction(cur) || + ts.isMethodDeclaration(cur) || + ts.isConstructorDeclaration(cur) || + ts.isGetAccessorDeclaration(cur) || + ts.isSetAccessorDeclaration(cur) + ) + return cur; + if (ts.isClassStaticBlockDeclaration(cur) || ts.isSourceFile(cur)) return null; + } + return null; +} + +/** A use of the binding that reads only its time value. */ +function readsTimeValue(lowerer: Lowerer, id: ts.Identifier): boolean { + let node: ts.Node = id; + while (ts.isParenthesizedExpression(node.parent)) node = node.parent; + const parent = node.parent; + if (ts.isPropertyAccessExpression(parent) && parent.expression === node) { + const call = parent.parent; + return ( + !parent.questionDotToken && + ts.isCallExpression(call) && + call.expression === parent && + !call.questionDotToken && + TIME_VALUE_METHODS.has(parent.name.text) && + lowerer.isStdlibMember(parent) + ); + } + if (ts.isTemplateSpan(parent)) return parent.expression === node; + if (ts.isCallExpression(parent)) { + return ( + parent.arguments.nodes.length === 1 && + parent.arguments.nodes[0] === node && + ts.isIdentifier(parent.expression) && + lowerer.isStdlibGlobal(parent.expression, "String") + ); + } + if (ts.isNewExpression(parent)) { + return ( + parent.arguments?.nodes.length === 1 && + parent.arguments.nodes[0] === node && + ts.isIdentifier(parent.expression) && + lowerer.isStdlibGlobal(parent.expression, "Date") + ); + } + if (ts.isBinaryExpression(parent)) { + const other = parent.left === node ? parent.right : parent.left; + const otherKind = lowerer.mapTypeOf(lowerer.typeOf(other))?.kind; + if (RELATIONAL.has(parent.operatorToken.kind)) return otherKind === "date"; + if (parent.operatorToken.kind === ts.SyntaxKind.PlusToken) return otherKind === "string"; + } + return false; +} diff --git a/packages/compiler/src/frontend/lowering/expressions/array-literals.ts b/packages/compiler/src/frontend/lowering/expressions/array-literals.ts index 6c7ce814e..c1b54a417 100644 --- a/packages/compiler/src/frontend/lowering/expressions/array-literals.ts +++ b/packages/compiler/src/frontend/lowering/expressions/array-literals.ts @@ -1,4 +1,5 @@ /* Array literals. */ +import { indexedUnionArms, lowerIndexedUnionToArray } from "../indexed-unions.js"; import * as ts from "../../ts7/adapter.js"; import { type Lowerer, neverTaintedJsType } from "../lowerer.js"; import { notIterableSpelling } from "../iterable-spelling.js"; @@ -504,6 +505,10 @@ export function lowerArrayLiteral( loc: locOf(el), }; } + // A union of typed arrays and number arrays (indexed-unions.ts): + // each arm's elements as a number[]. + const indexedArms = indexedUnionArms(lowerer, src.type); + if (indexedArms) src = lowerIndexedUnionToArray(lowerer, src, indexedArms, locOf(el)); // `[...obj]` over a class whose [Symbol.iterator] is a generator // method spreads the generator that method returns. src = classGeneratorIterable(lowerer, src) ?? src; diff --git a/packages/compiler/src/frontend/lowering/expressions/binary.ts b/packages/compiler/src/frontend/lowering/expressions/binary.ts index 17aef7b85..00a23caf5 100644 --- a/packages/compiler/src/frontend/lowering/expressions/binary.ts +++ b/packages/compiler/src/frontend/lowering/expressions/binary.ts @@ -243,37 +243,38 @@ function lowerLooseNullCompare( // No unit arms: never null-ish (defensive — the fold below). return { kind: "boolLit", value: negated, type: BOOL, loc }; } - const isTag = (tag: number): IrExpr => ({ + const isTag = (tag: number, value: IrExpr): IrExpr => ({ kind: "unionIsTag", unionId: ut.unionId, tag, negated, - value: other, + value, type: BOOL, loc, }); - if (tags.length === 1) return isTag(tags[0]!); + if (tags.length === 1) return isTag(tags[0]!, other); // Both null AND undefined arms: tag-in-set as a short-circuit pair - // (De Morgan for `!=`). The operand IrExpr rides both tests, so the - // backend emits it per test — restricted to re-emittable pure reads; - // anything effectful gets its own actionable fence (the generic - // SC1040 hint would wrongly suggest `== null` as the fix). + // (De Morgan for `!=`). The operand rides both tests, so an operand + // that is not a plain read (`m.get(k)! == null`) is evaluated once + // into a hidden local first. + let value = other; + let prefix: IrStmt[] = []; if (!isSafeToRepeat(other)) { - lowerer.unsupported( - "SC1090", - expr, - `'== null' / '!= null' on a '${lowerer.fmt(ut)}' value that isn't a plain read ` + - `(both unit arms need the operand twice — bind it to a const first)`, - ); + const tmp = lowerer.declareHiddenLocal("%nullish", ut); + value = varRef(tmp.id, ut, loc); + prefix = [{ kind: "varDecl", localId: tmp.id, init: other, loc }]; } - return { + const test: IrExpr = { kind: "logical", op: negated ? "&&" : "||", - left: isTag(tags[0]!), - right: isTag(tags[1]!), + left: isTag(tags[0]!, value), + right: isTag(tags[1]!, value), type: BOOL, loc, }; + return prefix.length === 0 + ? test + : { kind: "seqExpr", stmts: prefix, result: test, type: BOOL, loc }; } if (other.type.kind === "void") { return null; // no static tag to test — keep the fence @@ -764,10 +765,21 @@ export function ensureString(lowerer: Lowerer, e: IrExpr, node: ts.Node): IrExpr (lowerer.unions .get(elem.unionId) ?.arms.every( - (a) => a.kind === "f64" || a.kind === "string" || a.kind === "bool" || isUnitType(a), + (a) => + a.kind === "f64" || + a.kind === "string" || + a.kind === "bool" || + a.kind === "date" || + isUnitType(a), ) ?? false); - if (elem.kind === "f64" || elem.kind === "string" || elem.kind === "bool" || joinableUnion) { + if ( + elem.kind === "f64" || + elem.kind === "string" || + elem.kind === "bool" || + elem.kind === "date" || + joinableUnion + ) { const sep: IrExpr = { kind: "strLit", value: ",", type: STRING, loc: e.loc }; return { kind: "arrIntrinsic", @@ -2195,14 +2207,41 @@ export function lowerBinary(lowerer: Lowerer, expr: ts.BinaryExpression): IrExpr loc, }; if (bothStr) return { kind: "strCmp", op: cmpOp, left, right, type: BOOL, loc }; - // A Date converts to a new checked-dynamic Date on each crossing, so - // comparisons keep their Date fence instead of comparing copies. - if ( - left.type.kind !== "date" && - right.type.kind !== "date" && - lowerer.dynConvertible(left.type) && - lowerer.dynConvertible(right.type) - ) { + // Dates compare by time value, the number hint of ToPrimitive. Both + // operands evaluate before either converts, so a right operand that + // mutates the left Date (`a < (a.setTime(t), b)`) is seen: with such + // a right operand, both Dates bind first and their times read after. + if (left.type.kind === "date" && right.type.kind === "date") { + const time = (d: IrExpr): IrExpr => ({ + kind: "libCall", + fn: "date.getTime", + args: [d], + type: F64, + loc, + }); + if (isSafeToRepeat(right)) + return { kind: "bin", op: cmpOp, left: time(left), right: time(right), type: BOOL, loc }; + const leftDate = lowerer.declareHiddenLocal("%date", left.type); + const rightDate = lowerer.declareHiddenLocal("%date", right.type); + return { + kind: "seqExpr", + stmts: [ + { kind: "varDecl", localId: leftDate.id, init: left, loc }, + { kind: "varDecl", localId: rightDate.id, init: right, loc }, + ], + result: { + kind: "bin", + op: cmpOp, + left: time(varRef(leftDate.id, left.type, loc)), + right: time(varRef(rightDate.id, right.type, loc)), + type: BOOL, + loc, + }, + type: BOOL, + loc, + }; + } + if (lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) { return { kind: "libCall", fn: "dyn.compare", @@ -2416,6 +2455,7 @@ export function lowerStrictEquality( idLeft.type.kind === "map" || idLeft.type.kind === "set" || idLeft.type.kind === "regex" || + idLeft.type.kind === "date" || idLeft.type.kind === "url" || idLeft.type.kind === "object" || idLeft.type.kind === "record" || @@ -2452,14 +2492,7 @@ export function lowerStrictEquality( loc, }; } - // A Date converts to a new checked-dynamic Date on each crossing; its - // identity has no representation, so equality keeps the fence. - if ( - idLeft.type.kind !== "date" && - idRight.type.kind !== "date" && - lowerer.dynConvertible(idLeft.type) && - lowerer.dynConvertible(idRight.type) - ) { + if (lowerer.dynConvertible(idLeft.type) && lowerer.dynConvertible(idRight.type)) { return { kind: "dynScalarEq", left: lowerer.coerceToExpected(idLeft, DYN), diff --git a/packages/compiler/src/frontend/lowering/expressions/element-access.ts b/packages/compiler/src/frontend/lowering/expressions/element-access.ts index 0ea94ec7a..9ac6a04b6 100644 --- a/packages/compiler/src/frontend/lowering/expressions/element-access.ts +++ b/packages/compiler/src/frontend/lowering/expressions/element-access.ts @@ -45,6 +45,7 @@ import { lowerDynMemberAssignment, lowerOptionalArrayIndexRead, lowerOptionalNumber, + assertedReadObservesUndefined, recordKeyResultOk, representedClassFieldTarget, runtimeOptionalElementKey, @@ -71,6 +72,14 @@ import { isClassCallback } from "../class-callbacks.js"; import { isKeyedFieldAccess } from "./keyed-field-targets.js"; import { lowerFieldCompoundValue } from "./updates.js"; import { readsHeldEvolvingArray } from "../lower-stmts.js"; +import { + type IndexedArm, + indexedUnionArms, + lowerIndexedUnionNumberRead, + lowerIndexedUnionRead, + lowerIndexedUnionWrite, +} from "../indexed-unions.js"; +import { lowerBufferPrototypeMember } from "../containers/bytes.js"; /** `a[i]` reads. Only f64 indices into array receivers are modeled; JS * string-key element access (`a["length"]`) and string indexing (`s[0]` @@ -104,6 +113,8 @@ export function lowerElementAccess(lowerer: Lowerer, expr: ts.ElementAccessExpre const en = lowerEnumAccess(lowerer, expr); if (en) return en; } + const bufferMember = lowerBufferPrototypeMember(lowerer, expr); + if (bufferMember) return bufferMember; const cachedModule = lowerRequireCacheElement(lowerer, expr); if (cachedModule) return cachedModule; const namespaceElement = lowerModuleNamespaceElement(lowerer, expr); @@ -299,6 +310,31 @@ export function lowerElementAccess(lowerer: Lowerer, expr: ts.ElementAccessExpre } } + // An element read of a union of typed arrays and number arrays: each + // arm's own read, dispatched on the tag (indexed-unions.ts). + const indexedArms = receiverIr ? indexedUnionArms(lowerer, receiverIr) : null; + if (indexedArms && receiverIr && !isJsSourceFile(ts.getSourceFileOfNode(expr))) { + let recv = lowerer.lowerExpr(expr.expression); + if (recv.type.kind === "dyn") recv = lowerer.coerceInto(expr.expression, recv, receiverIr); + const arms = indexedUnionArms(lowerer, recv.type); + if (arms) { + const index = lowerOptionalNumber( + lowerer, + lowerer.lowerExpr(expr.argumentExpression), + locOf(expr.argumentExpression), + expr.argumentExpression, + ); + if (index.type.kind !== "f64") + lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys"); + if ( + (ts.isNonNullExpression(expr.parent) && expr.parent.expression === expr) || + lowerer.nanCodedReads?.isNumericRead(expr) === true + ) + return lowerIndexedUnionNumberRead(lowerer, recv, arms, index, locOf(expr)); + return lowerIndexedUnionRead(lowerer, recv, arms, index, locOf(expr)); + } + } + // Typed-array element read `b[i]` — like arrayGet with a scalar // result; any invalid index traps (the array discipline; JS would // read undefined — divergence 4's policy). @@ -346,13 +382,18 @@ export function lowerElementAccess(lowerer: Lowerer, expr: ts.ElementAccessExpre if (index.type.kind !== "f64") { lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys"); } - // `t[i]!` asserts presence: the numeric element read (NaN when - // invalid). Every other read answers undefined for an invalid index, - // like Node. + // `t[i]!` performs no runtime check, but its static type is a number: + // it takes the numeric element read (NaN when invalid), except where + // the value reaches a use that tells undefined from NaN (an equality, a + // string conversion, `typeof`, an array element), which reads undefined + // for an invalid index like `t[i]`. Every other read answers undefined + // for an invalid index, like Node. // A read whose every consumer treats undefined like NaN (see // nan-coded-reads.ts) is the same plain numeric read. if ( - (ts.isNonNullExpression(expr.parent) && expr.parent.expression === expr) || + (ts.isNonNullExpression(expr.parent) && + expr.parent.expression === expr && + !assertedReadObservesUndefined(lowerer, expr.parent)) || lowerer.nanCodedReads?.isNumericRead(expr) === true ) { return { @@ -1028,7 +1069,7 @@ function lowerGuardedRecordWrite( const value = stabilized(lowerer, lowerer.lowerExpr(expr.right), "%recordValue", receiver.stmts); const write = lowerer.withExpressionOverride(target.expression, receiver.present, () => lowerer.withExpressionOverride(target.argumentExpression, key, () => - lowerer.withExpressionOverride(expr.right, value, () => lowerElementWrite(lowerer, expr)), + lowerer.withExpressionOverride(expr.right, value, () => lowerer.lowerElementWrite(expr)), ), ); return { @@ -1518,6 +1559,9 @@ export function lowerElementCompoundParts( }); return { expr: { kind: "seqExpr", stmts, result: value, type: DYN, loc }, stmts: null }; } + const indexedArms = indexedUnionArms(lowerer, checked.type); + if (indexedArms && !isJsSourceFile(ts.getSourceFileOfNode(expr))) + return lowerIndexedUnionCompound(lowerer, expr, op, checked, indexedArms); const receiverType = checked.type.kind === "array" || checked.type.kind === "bytes" ? checked.type @@ -1702,6 +1746,54 @@ export function lowerElementCompoundParts( }; } +/** `u[i] op= v` on a union of typed arrays and number arrays: the + * receiver and index evaluate once, the old element reads as a number (NaN + * for an invalid index, as in the typed-array form), and the result is + * stored through the union's element write. */ +function lowerIndexedUnionCompound( + lowerer: Lowerer, + expr: ts.BinaryExpression, + op: CompoundOp, + receiver: IrExpr, + arms: readonly IndexedArm[], +): { expr: IrExpr | null; stmts: IrStmt[] | null } { + const target = expr.left as ts.ElementAccessExpression; + const loc = locOf(expr); + const index = lowerOptionalIndex( + lowerer, + lowerer.lowerExpr(target.argumentExpression), + locOf(target.argumentExpression), + target.argumentExpression, + ); + if (index.type.kind !== "f64") + lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys"); + const receiverLocal = lowerer.declareHiddenLocal("%compoundArray", receiver.type); + const indexLocal = lowerer.declareHiddenLocal("%compoundIndex", F64); + const receiverRef = (): IrExpr => varRef(receiverLocal.id, receiver.type, loc); + const indexRef = (): IrExpr => varRef(indexLocal.id, F64, loc); + const oldValue = lowerIndexedUnionNumberRead(lowerer, receiverRef(), arms, indexRef(), loc); + const rhs = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.right), loc); + if (rhs.type.kind !== "f64") compoundUnsupported(lowerer, expr, op, F64, rhs.type); + const computed: IrExpr = { kind: "bin", op, left: oldValue, right: rhs, type: F64, loc }; + const resultLocal = lowerer.declareHiddenLocal("%compoundResult", F64); + const resultRef = (): IrExpr => varRef(resultLocal.id, F64, loc); + return { + expr: { + kind: "seqExpr", + stmts: [ + { kind: "varDecl", localId: receiverLocal.id, init: receiver, loc }, + { kind: "varDecl", localId: indexLocal.id, init: index, loc }, + { kind: "varDecl", localId: resultLocal.id, init: computed, loc }, + lowerIndexedUnionWrite(lowerer, receiverRef(), arms, indexRef(), resultRef(), loc), + ], + result: resultRef(), + type: F64, + loc, + }, + stmts: null, + }; +} + /** ToString of one stabilized `string[]` element read for `+=`: a missing * element concatenates as "undefined". */ function arrayStringOperand(arr: IrExpr, index: IrExpr, loc: SrcLoc): IrExpr { @@ -2243,6 +2335,26 @@ export function lowerElementWrite(lowerer: Lowerer, expr: ts.BinaryExpression): }; } } + // An element write into a union of typed arrays and number arrays + // (indexed-unions.ts). + const indexedArms = receiverIr ? indexedUnionArms(lowerer, receiverIr) : null; + if (indexedArms && receiverIr && !isJsSourceFile(ts.getSourceFileOfNode(expr))) { + let recv = lowerer.lowerExpr(target.expression); + if (recv.type.kind === "dyn") recv = lowerer.coerceInto(target.expression, recv, receiverIr); + const arms = indexedUnionArms(lowerer, recv.type); + if (arms) { + const index = lowerOptionalNumber( + lowerer, + lowerer.lowerExpr(target.argumentExpression), + locOf(target.argumentExpression), + target.argumentExpression, + ); + if (index.type.kind !== "f64") + lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys"); + const value = lowerer.lowerExprExpecting(expr.right, F64); + return lowerIndexedUnionWrite(lowerer, recv, arms, index, value, locOf(expr)); + } + } if (receiverIr?.kind !== "array") { lowerer.unsupported("SC1090", target, "assignment to non-array elements"); } diff --git a/packages/compiler/src/frontend/lowering/expressions/identifiers.ts b/packages/compiler/src/frontend/lowering/expressions/identifiers.ts index 23be960c8..67869e255 100644 --- a/packages/compiler/src/frontend/lowering/expressions/identifiers.ts +++ b/packages/compiler/src/frontend/lowering/expressions/identifiers.ts @@ -9,6 +9,7 @@ import { type Lowerer, PoisonError } from "../lowerer.js"; import { BOOL, BYTES_ELEMENT_NAME, + DATE_T, DYN, F64, type IrExpr, @@ -24,6 +25,11 @@ import { } from "../../../ir/ir.js"; import { lowerNodeModuleIdentifier } from "../lower-node-module.js"; import { lowerCjsExportsObjectValue } from "../cjs-exports-object.js"; +import { + splitRequireBindingOfIdent, + wholeExportSymbolOfModule, + wholeExportValue, +} from "../cjs-inline-require.js"; import { captureContextArguments } from "../function-context.js"; import { argumentsOwner, typedArgumentsCandidate } from "../typed-arguments.js"; import { @@ -171,6 +177,12 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc: // when the checker types this USE as a single arm, maybeNarrow // bridges the tagged representation with a unionNarrow. const local = lowerer.resolveLocal(expr); + // A Date local holding its time value reads as that Date: only uses + // that read the time value reach here (date-values.ts). + if (local && lowerer.timeValueDateLocals.has(local)) { + const time: IrExpr = { kind: "varRef", localId: local.id, type: F64, loc }; + return { kind: "libCall", fn: "date.fromTime", args: [time], type: DATE_T, loc }; + } if (local) { // A switch can dispatch past a lexical declaration. Keep this // previously refused codec form out until switch-scope TDZ boxes @@ -744,6 +756,20 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc: return { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc }; const nativeAlias = stdlibGlobalNameOf(lowerer, expr); if (nativeAlias === "Buffer" && expr.text !== "Buffer") { + // An alias through a cast of globalThis carries the cast's static type, + // which the identity token below does not have. + const aliasSymbol = identifierReadSymbol(lowerer, expr); + if (aliasSymbol && lowerer.castGlobalAliases.has(aliasSymbol)) { + lowerer.unsupported( + "SC1090", + expr, + "the Buffer constructor read through a cast of globalThis as a value", + "test it in a condition, read its typeof, or call its members", + ); + } + // JavaScript reads the Buffer function's value (builtinGlobalValue). + if (isJsSourceFile(ts.getSourceFileOfNode(expr))) + return { kind: "libCall", fn: "buffer.constructor", args: [], type: DYN, loc }; return { kind: "strLit", value: "[builtin Buffer]", type: STRING, loc }; } if (lowerer.isStdlibGlobal(expr, "fetch")) { @@ -968,6 +994,27 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc: // Client`): the member holds that binding's value. const memberAlias = cjsMemberAliasValueOf(lowerer, expr); if (memberAlias !== null) return lowerer.lowerExpr(memberAlias); + // `var m; m = require("./x")` as a bare value: a whole-export target is + // the exported value; a table target has no first-class module object. + { + const split = splitRequireBindingOfIdent(lowerer, expr); + if (split !== null) { + if (wholeExportSymbolOfModule(lowerer, split.dep) !== null) { + const value = wholeExportValue(lowerer, split.dep, expr); + if (value !== null) return value; + lowerer.unsupported( + "SC1090", + expr, + `the whole export of '${split.spec}' as a value (its 'module.exports =' value has no static representation)`, + ); + } + lowerer.unsupported( + "SC1090", + expr, + `the exports object of '${split.spec}' as a value (its exports compile to a static table: read require(${JSON.stringify(split.spec)}).member directly)`, + ); + } + } // Preflight guarantees no unresolved identifiers; anything else here // is a blocked declaration's binding (the SC2004 cascade) or a // binding form we don't model yet. diff --git a/packages/compiler/src/frontend/lowering/expressions/property-access.ts b/packages/compiler/src/frontend/lowering/expressions/property-access.ts index 7b12297a8..1a4883e5a 100644 --- a/packages/compiler/src/frontend/lowering/expressions/property-access.ts +++ b/packages/compiler/src/frontend/lowering/expressions/property-access.ts @@ -1,4 +1,5 @@ /* Property reads (`a.b`): lowerExprInner's property-access branch, intrinsic and union properties, field reads and targets, and match-group reads. */ +import { indexedUnionArms, lowerIndexedUnionMember } from "../indexed-unions.js"; import { keyedHelperName } from "../stable-names.js"; import { staticallySelected } from "../static-typeof.js"; import * as ts from "../../ts7/adapter.js"; @@ -134,6 +135,7 @@ import { import { expandoMemberRead } from "../lower-expando.js"; import { lowerProtocolPropertyRead } from "../class-protocol-dispatch.js"; import { lowerReadlineProperty } from "../builtins/readline.js"; +import { lowerAsyncLocalStorageProperty } from "../builtins/async-context.js"; import { lowerEventStaticProperty } from "../builtins/numbers.js"; import { lowerStreamProperty, lowerStreamStateProperty, streamSidesOf } from "../lower-stream.js"; import { lowerValueStreamProperty } from "../builtins/hash-stream.js"; @@ -153,6 +155,7 @@ import { lowerStringCodesValue, } from "../lower-builtin-values.js"; import { lowerArrayFromValue } from "../containers/array-construction.js"; +import { lowerBufferPrototypeMember } from "../containers/bytes.js"; import { lowerNetAddressStaticRead } from "../builtins/net-address.js"; import { STATS_TYPE_TESTS } from "../builtins/filesystem.js"; import { arrayValueRead, arrayValueType } from "../array-values.js"; @@ -310,6 +313,10 @@ export function lowerPropertyAccessExpr( // Cloudflare probe) folds like `navigator.userAgent`. const navigator = lowerNavigatorMember(lowerer, expr, NODE_COMPAT_VERSION); if (navigator) return navigator; + { + const member = lowerBufferPrototypeMember(lowerer, expr); + if (member) return member; + } // A dgram socket's method read as a value in JavaScript (`emit: // socket.emit.bind(socket)`): the socket crosses as its checked-dynamic // handle, whose methods are function values that dispatch on their @@ -833,6 +840,8 @@ export function lowerPropertyAccessExpr( // diagnostics_channel Channel receivers — name/hasSubscribers // over the f64 channel handle. lowerer.lowerDiagnosticsChannelProperty(expr) ?? + // AsyncLocalStorage receivers — the name option. + lowerAsyncLocalStorageProperty(lowerer, expr) ?? // readline Interface receivers — the history and option members // over the f64 interface handle. lowerReadlineProperty(lowerer, expr) ?? @@ -1499,6 +1508,25 @@ export function lowerPropertyAccessExpr( ) { return afterReceiver(recvLowered, dynUndefinedExpr(loc), loc); } + // A typed array under a checker `any` (a JS binding its own initializer's + // inference refers to: hono's etag digest `nextChunk`) reads through its + // box, as the method-call twin dispatches. The box shares the native + // storage, so it needs no fresh-result restriction. + if ( + recvLowered.type.kind === "bytes" && + (lowerer.typeOf(expr.expression).flags & ts.TypeFlags.Any) !== 0 + ) { + return lowerer.maybeNarrow( + { + kind: "dynKeyGet", + value: lowerer.coerceToExpected(recvLowered, DYN), + key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, + type: DYN, + loc, + }, + expr, + ); + } lowerer.unsupported( "SC1090", expr, @@ -1769,6 +1797,28 @@ export function lowerIntrinsicProperty( return lowerNumberParserValue(lowerer, numberMember, locOf(expr)); if (lowerer.stdlibGlobalMember(expr, "Buffer") === "poolSize") return { kind: "libCall", fn: "buffer.poolSize", args: [], type: F64, loc: locOf(expr) }; + { + const member = lowerer.stdlibGlobalMember(expr, "Buffer"); + if (member === "name") + return { kind: "strLit", value: "Buffer", type: STRING, loc: locOf(expr) }; + if (member === "length") return numLit(3, locOf(expr)); + // JavaScript reads the statics from the Buffer function's value, so + // each has one identity and Node's name and length. + if ( + member !== null && + BUFFER_VALUE_STATICS.has(member) && + isJsSourceFile(ts.getSourceFileOfNode(expr)) + ) { + const loc = locOf(expr); + return { + kind: "libCall", + fn: "buffer.staticValue", + args: [{ kind: "strLit", value: member, type: STRING, loc }], + type: DYN, + loc, + }; + } + } const netAddressStatic = lowerNetAddressStaticRead(lowerer, expr); if (netAddressStatic) return netAddressStatic; if (lowerer.stdlibGlobalMember(expr, "Buffer") === "isBuffer") @@ -3345,6 +3395,13 @@ export function lowerUnionProperty( loc: locOf(expr), }; } + // A union of typed arrays and number arrays: each arm's own length + // (and, for typed arrays only, byteLength, byteOffset, BYTES_PER_ELEMENT). + const indexedArms = indexedUnionArms(lowerer, value.type); + if (indexedArms) { + const member = lowerIndexedUnionMember(lowerer, value, indexedArms, field, locOf(expr)); + if (member) return member; + } lowerer.unsupported( "SC1090", expr, @@ -4173,6 +4230,8 @@ export function builtinFunctionValue(name: string, loc: SrcLoc): IrExpr { * null when it stays an identity token (JavaScript) or fenced. */ export function builtinGlobalValue(canonical: string, loc: SrcLoc, js = false): IrExpr | null { if (js && GLOBAL_FUNCTION_VALUES.has(canonical)) return builtinFunctionValue(canonical, loc); + if (js && canonical === "Buffer") + return { kind: "libCall", fn: "buffer.constructor", args: [], type: DYN, loc }; if (GLOBAL_CONSTRUCTOR_VALUES.has(canonical)) { return { kind: "libCall", @@ -4300,6 +4359,22 @@ function fenceBuiltinConstructorStaticRead( return null; } +/** The Buffer statics the Buffer function's value holds + * (scr_buffer_constructor_value). */ +const BUFFER_VALUE_STATICS: ReadonlySet = new Set([ + "from", + "copyBytesFrom", + "of", + "alloc", + "allocUnsafe", + "allocUnsafeSlow", + "isBuffer", + "compare", + "isEncoding", + "concat", + "byteLength", +]); + /** Object.prototype members a plain record inherits, read as values. */ const OBJECT_PROTOTYPE_VALUE_READS: ReadonlySet = new Set([ "constructor", diff --git a/packages/compiler/src/frontend/lowering/expressions/relational.ts b/packages/compiler/src/frontend/lowering/expressions/relational.ts index f1590e9cc..1651bf0d9 100644 --- a/packages/compiler/src/frontend/lowering/expressions/relational.ts +++ b/packages/compiler/src/frontend/lowering/expressions/relational.ts @@ -338,6 +338,22 @@ export function lowerInstanceOf(lowerer: Lowerer, expr: ts.BinaryExpression, loc type: BOOL, loc, }; + // A codec in untyped storage is its record's handle box. + const codec = (["TextEncoder", "TextDecoder"] as const).find((name) => + lowerer.isStdlibGlobal(expr.right, name), + ); + if (codec && lowerer.mapTypeOf(lowerer.typeOf(expr.left))?.kind === "dyn") { + return { + kind: "libCall", + fn: "text.codecIs", + args: [ + lowerer.lowerExprExpecting(expr.left, DYN), + { kind: "boolLit", value: codec === "TextDecoder", type: BOOL, loc }, + ], + type: BOOL, + loc, + }; + } if (lowerer.isStdlibGlobal(expr.right, "ReadableStream")) { const value = lowerer.lowerExprExpecting(expr.left, DYN); return { kind: "libCall", fn: "fetch.streamIs", args: [value], type: BOOL, loc }; diff --git a/packages/compiler/src/frontend/lowering/expressions/typeof.ts b/packages/compiler/src/frontend/lowering/expressions/typeof.ts index cc3ac06fb..fbbc068b9 100644 --- a/packages/compiler/src/frontend/lowering/expressions/typeof.ts +++ b/packages/compiler/src/frontend/lowering/expressions/typeof.ts @@ -88,6 +88,12 @@ export function lowerTypeofExpr(lowerer: Lowerer, expr: ts.TypeOfExpression, loc if (builtinClassRefOf(lowerer, expr.expression) !== null) { return { kind: "strLit", value: "function", type: STRING, loc }; } + // The Buffer constructor and its aliases (`const B = globalThis.Buffer`, + // `(globalThis as { Buffer?: B }).Buffer`): a value read of an alias is an + // identity token, so its typeof is decided here. + if (stdlibGlobalNameOf(lowerer, expr.expression) === "Buffer") { + return { kind: "strLit", value: "function", type: STRING, loc }; + } if (ts.isPropertyAccessExpression(expr.expression)) { const member = expr.expression; const prototype = member.expression; diff --git a/packages/compiler/src/frontend/lowering/expressions/union-fields.ts b/packages/compiler/src/frontend/lowering/expressions/union-fields.ts index 6454bc1fd..4f4532a6a 100644 --- a/packages/compiler/src/frontend/lowering/expressions/union-fields.ts +++ b/packages/compiler/src/frontend/lowering/expressions/union-fields.ts @@ -2,6 +2,7 @@ import { nodeThrowExpr, varRef } from "../../../ir/build.js"; import * as ts from "../../ts7/adapter.js"; import type { Lowerer } from "../lowerer.js"; import { narrowStoredClassValue } from "../class-unions.js"; +import { markSloppyRecordWrites } from "../frozen-records.js"; import { BOOL, type IrExpr, @@ -246,6 +247,8 @@ export function lowerUnionFieldWrite( value: slotValue, loc, }; + // A sloppy-mode write to a frozen record leaves it unchanged. + if (write.kind === "recordSet") markSloppyRecordWrites(lowerer, write, access); branch = { kind: "seqExpr", stmts: [write], result, type: result.type, loc }; } else { branch = nodeThrowExpr( diff --git a/packages/compiler/src/frontend/lowering/expressions/updates.ts b/packages/compiler/src/frontend/lowering/expressions/updates.ts index 04ca23a96..bdcd94a44 100644 --- a/packages/compiler/src/frontend/lowering/expressions/updates.ts +++ b/packages/compiler/src/frontend/lowering/expressions/updates.ts @@ -1,4 +1,9 @@ /* Increment, decrement and prefix unary operators, and compound field updates. */ +import { + indexedUnionArms, + lowerIndexedUnionNumberRead, + lowerIndexedUnionWrite, +} from "../indexed-unions.js"; import * as ts from "../../ts7/adapter.js"; import { lowerOptionalIndex, @@ -191,6 +196,10 @@ export function lowerPrefixUnary(lowerer: Lowerer, expr: ts.PrefixUnaryExpressio operatorUnsupported(lowerer, expr, "+", [raw.type]); } case ts.SyntaxKind.ExclamationToken: { + // `!runtimeBuffer` over an alias of the Buffer global: Node always + // defines Buffer (the condition twin is lowerCondition's). + if (lowerer.isStdlibGlobal(expr.operand, "Buffer")) + return { kind: "boolLit", value: false, type: BOOL, loc }; // `!x` is ToBoolean-then-negate: f64/string operands go through toBool. const operand = lowerer.ensureBool( deferredSlotOr( @@ -386,6 +395,39 @@ export function lowerIncDec( loc, })); } + // A union of typed arrays and number arrays (indexed-unions.ts): the + // element read as a number and the union's element write. + const indexedArms = indexedUnionArms(lowerer, raw.type); + if ( + ts.isElementAccessExpression(access) && + indexedArms && + !isJsSourceFile(ts.getSourceFileOfNode(expr)) + ) { + const receiver = save(raw, "%incrementArray"); + const index = save( + lowerOptionalIndex( + lowerer, + lowerer.lowerExpr(access.argumentExpression), + locOf(access.argumentExpression), + access.argumentExpression, + ), + "%incrementIndex", + ); + if (index.type.kind !== "f64") + lowerer.unsupported("SC1090", access, "incrementing non-number array keys"); + return finish( + lowerIndexedUnionNumberRead(lowerer, receiver, indexedArms, index, loc), + (value) => + lowerIndexedUnionWrite( + lowerer, + receiver, + indexedArms, + index, + lowerer.coerceInto(access, value, F64), + loc, + ), + ); + } // A TS member statically typed number on a checked-dynamic receiver (a // class-backed protocol view): the expanded form `o.v = o.v + 1`, its // read checked into number like any other read of the member. diff --git a/packages/compiler/src/frontend/lowering/frozen-records.ts b/packages/compiler/src/frontend/lowering/frozen-records.ts new file mode 100644 index 000000000..30eb9e455 --- /dev/null +++ b/packages/compiler/src/frontend/lowering/frozen-records.ts @@ -0,0 +1,354 @@ +import * as ts from "../ts7/adapter.js"; +import { + BOOL, + VOID, + shapeHasAccessorSlots, + typeKey, + type IrExpr, + type IrFunction, + type IrStmt, + type IrType, + type SrcLoc, +} from "../../ir/ir.js"; +import { numLit, strLit, varRef } from "../../ir/build.js"; +import { everyStmtList, transformStmtList } from "../../ir/traverse.js"; +import type { Lowerer } from "./lowerer.js"; +import { recordHasOwnHelper } from "./lower-calls.js"; +import { isStrictCode } from "./strict-mode.js"; + +/* Object.freeze of records and arrays that other references can still + * write. Only the types a program may freeze pay for it. A record keeps its + * frozen bit in the rc word (scr_rc.h SCR_RC_FROZEN), which its release + * ignores, and every write to it checks the bit first + * (guardFrozenWrites). An array keeps it in its storage flags + * (scr_array.h SCR_ARR_FROZEN), which the backend's array write guards test + * for the array types the module lists (IrModule.freezableArrays). A type is + * freezable when a static Object.freeze names it, or, in a program that + * freezes untyped values, when a value of it or of a type reaching it can be + * held as a live untyped reference. */ + +type RecordType = Extract; +type RecordWrite = Extract; +type FreezeFn = "record.freeze" | "array.freeze" | "collection.preventExtensions"; + +/** Object.freeze of a record or an array, or Object.freeze, seal or + * preventExtensions of a Map or Set: `fn` sets the value's bit, and the + * call answers the value. */ +export function lowerNativeFreeze( + lowerer: Lowerer, + fn: FreezeFn, + target: IrExpr, + loc: SrcLoc, +): IrExpr { + const type = target.type; + const slot = lowerer.declareHiddenLocal("%freezeTarget", type); + const receiver = (): IrExpr => varRef(slot.id, type, loc); + return { + kind: "seqExpr", + stmts: [ + { kind: "varDecl", localId: slot.id, init: target, loc }, + { + kind: "exprStmt", + expr: { kind: "libCall", fn, args: [receiver()], type: VOID, loc }, + loc, + }, + ], + result: receiver(), + type, + loc, + }; +} + +function isRecordWrite(s: IrStmt): s is RecordWrite { + return s.kind === "recordSet" || s.kind === "recordKeySet" || s.kind === "recordKeyDelete"; +} + +/** The key of a record write's source range: strict mode is lexical, so + * every write lowered from the same range shares its mode. */ +function writeKey(loc: SrcLoc): string { + return `${loc.file}\0${loc.start}\0${loc.end}`; +} + +/** Records the record writes of `stmt`, lowered from `node`, as sloppy-mode + * writes when `node` is sloppy-mode code: a write to a frozen record then + * leaves it unchanged instead of throwing. `deletes` says `stmt` is the + * absent-state write of `delete r.f`, which Node refuses only while the + * field is present. */ +export function markSloppyRecordWrites( + lowerer: Lowerer, + stmt: IrStmt, + node: ts.Node, + deletes = false, +): IrStmt { + if (deletes) lowerer.recordFieldDeletes.add(writeKey(stmt.loc)); + if (isStrictCode(node)) return stmt; + everyStmtList([stmt], { + stmt: (s) => { + if (s.kind === "recordSet" || s.kind === "recordKeySet" || s.kind === "recordKeyDelete") + lowerer.sloppyRecordWrites.add(writeKey(s.loc)); + return true; + }, + expr: () => true, + }); + return stmt; +} + +/** Whether a field write's value can be an optional field's absent state, + * which only a field write itself can hold. */ +function holdsAbsentState(e: IrExpr): boolean { + if (e.kind === "fieldAbsent") return true; + return e.kind === "ternary" && (holdsAbsentState(e.then) || holdsAbsentState(e.else_)); +} + +/** The record shapes and array types (typeKeys) whose values the program + * may freeze. */ +function freezableTypes( + lowerer: Lowerer, + functions: IrFunction[], +): { shapes: Set; arrays: Set } { + const shapes = new Set(); + const arrays = new Set(); + const live: IrType[] = []; + let dynFreeze = false; + for (const fn of functions) + everyStmtList(fn.body, { + expr: (e) => { + if (e.kind === "libCall" && e.fn === "record.freeze") { + const target = e.args[0]; + if (target && target.type.kind === "record") shapes.add(target.type.shapeId); + } else if (e.kind === "libCall" && e.fn === "array.freeze") { + const target = e.args[0]; + if (target && target.type.kind === "array") arrays.add(typeKey(target.type)); + } else if (e.kind === "libCall" && e.fn === "dyn.freeze") dynFreeze = true; + else if (e.kind === "dynFrom") live.push(e.value.type); + return true; + }, + stmt: () => true, + }); + if (!dynFreeze) return { shapes, arrays }; + // An untyped reference to a class instance, or to a record or array + // boxed live, reaches the values its fields and elements hold as further + // live references. Class instances also cross into untyped code through + // compiled callbacks, so every class is a root. + const stack = [...live]; + for (const info of lowerer.classes.values()) + for (const field of info.def.fields) stack.push(field.type); + const seen = new Set(); + while (stack.length > 0) { + const type = stack.pop()!; + switch (type.kind) { + case "record": { + if (seen.has(`record:${type.shapeId}`)) break; + seen.add(`record:${type.shapeId}`); + shapes.add(type.shapeId); + const shape = lowerer.shapes.get(type.shapeId); + for (const field of shape?.fields ?? []) stack.push(field.type); + if (shape?.indexValue) stack.push(shape.indexValue); + break; + } + case "union": + if (seen.has(`union:${type.unionId}`)) break; + seen.add(`union:${type.unionId}`); + for (const arm of lowerer.unions.get(type.unionId)?.arms ?? []) stack.push(arm); + break; + case "array": { + const key = typeKey(type); + if (seen.has(key)) break; + seen.add(key); + arrays.add(key); + stack.push(type.elem); + break; + } + case "set": + stack.push(type.elem); + break; + case "map": + stack.push(type.key, type.value); + break; + case "promise": + stack.push(type.inner); + break; + case "generator": + stack.push(type.yieldT, type.retT, type.nextT); + break; + case "func": + for (const param of type.params) stack.push(param); + stack.push(type.ret); + break; + default: + break; + } + } + return { shapes, arrays }; +} + +/** The guarded form of a write to a freezable record: the receiver and the + * operands are evaluated first, in source order, then the frozen bit decides + * between the write and `reject`. */ +function guardedWrite( + receiver: IrExpr, + decls: IrStmt[], + reject: IrStmt[], + store: IrStmt, + loc: SrcLoc, +): IrStmt { + return { + kind: "block", + body: [ + ...decls, + { + kind: "if", + cond: { kind: "libCall", fn: "record.isFrozen", args: [receiver], type: BOOL, loc }, + then: reject, + else_: [store], + loc, + }, + ], + loc, + }; +} + +/** Checks the frozen bit before every write to a record of a freezable + * shape, and lists the freezable array types, whose writes the backend + * guards (IrModule.freezableArrays). A write to a frozen record throws + * Node's TypeError in strict mode code; in sloppy-mode code it leaves the + * record unchanged. Deleting a key the record does not have succeeds either + * way. */ +export function guardFrozenWrites(lowerer: Lowerer, functions: IrFunction[]): void { + const { shapes: freezable, arrays } = freezableTypes(lowerer, functions); + for (const id of freezable) { + const shape = lowerer.shapes.get(id); + if (shape) shape.freezable = true; + } + for (const key of arrays) lowerer.freezableArrays.add(key); + if (freezable.size === 0) return; + const lifted = lowerer.liftedFns.length; + for (const fn of functions) { + const local = (type: IrType, loc: SrcLoc): Extract => { + const id = `%frozenWrite.${fn.locals.length}`; + fn.locals.push({ id, name: id, type, mutable: false }); + return { kind: "varRef", localId: id, type, loc }; + }; + const guarded = new Set(); + fn.body = transformStmtList(fn.body, { + stmt: (statement) => { + if (!isRecordWrite(statement) || guarded.has(statement)) return statement; + if (!freezable.has(statement.shapeId)) return statement; + const sloppy = lowerer.sloppyRecordWrites.has(writeKey(statement.loc)); + const deletes = + statement.kind === "recordSet" && lowerer.recordFieldDeletes.has(writeKey(statement.loc)); + const loc = statement.loc; + const shapeId = statement.shapeId; + const receiver = local(statement.obj.type, loc); + const decls: IrStmt[] = [ + { kind: "varDecl", localId: receiver.localId, init: statement.obj, loc }, + ]; + const owner = strLit( + lowerer.shapes.get(shapeId)?.tuple ? "[object Array]" : "#", + loc, + ); + const thrown = (key: IrExpr, operation: number): IrStmt => ({ + kind: "exprStmt", + expr: { + kind: "libCall", + fn: "record.throwFrozen", + args: [key, owner, numLit(operation, loc)], + type: VOID, + loc, + }, + loc, + }); + if (statement.kind === "recordSet") { + let value = statement.value; + if (!holdsAbsentState(value)) { + const slot = local(value.type, loc); + decls.push({ kind: "varDecl", localId: slot.localId, init: value, loc }); + value = slot; + } + const store: IrStmt = { + kind: "recordSet", + obj: receiver, + shapeId, + field: statement.field, + value, + loc, + }; + guarded.add(store); + const field = strLit(statement.field, loc); + // `delete r.f` of an optional field writes its absent state: Node + // only refuses it when the field is present. + const reject: IrStmt[] = sloppy + ? [] + : deletes + ? [ + { + kind: "if", + cond: lowerer.recordFieldPresent(receiver, shapeId, statement.field, loc), + then: [thrown(field, 2)], + else_: null, + loc, + }, + ] + : [thrown(field, 0)]; + return guardedWrite(receiver, decls, reject, store, loc); + } + const key = local(statement.key.type, loc); + decls.push({ kind: "varDecl", localId: key.localId, init: statement.key, loc }); + const named = key.type.kind === "string"; + const present: IrExpr = named + ? { + kind: "call", + callee: recordHasOwnHelper(lowerer, shapeId, loc), + args: [receiver, key], + type: BOOL, + loc, + } + : { kind: "boolLit", value: true, type: BOOL, loc }; + const text: IrExpr = named ? key : strLit("[key]", loc); + if (statement.kind === "recordKeySet") { + const value = local(statement.value.type, loc); + decls.push({ kind: "varDecl", localId: value.localId, init: statement.value, loc }); + const store: IrStmt = { + kind: "recordKeySet", + obj: receiver, + shapeId, + key, + value, + ...(statement.overflowOnly ? { overflowOnly: true as const } : {}), + loc, + }; + guarded.add(store); + const reject: IrStmt[] = sloppy + ? [] + : [ + { + kind: "if", + cond: present, + then: [thrown(text, 0)], + else_: [thrown(text, 1)], + loc, + }, + ]; + return guardedWrite(receiver, decls, reject, store, loc); + } + const store: IrStmt = { kind: "recordKeyDelete", obj: receiver, shapeId, key, loc }; + guarded.add(store); + const reject: IrStmt[] = sloppy + ? [] + : [{ kind: "if", cond: present, then: [thrown(text, 2)], else_: null, loc }]; + return guardedWrite(receiver, decls, reject, store, loc); + }, + expr: (expression) => expression, + }); + } + // The key-presence helpers the guards call. + for (const helper of lowerer.liftedFns.slice(lifted)) functions.push(helper); +} + +/** Whether a statically typed Object.freeze can freeze a value of `type`: + * a record whose shape has no accessor properties. */ +export function freezableRecordType(lowerer: Lowerer, type: IrType): type is RecordType { + if (type.kind !== "record") return false; + const shape = lowerer.shapes.get(type.shapeId); + return shape !== undefined && !shapeHasAccessorSlots(shape); +} diff --git a/packages/compiler/src/frontend/lowering/function-returns.ts b/packages/compiler/src/frontend/lowering/function-returns.ts index dde109ea7..6d0989e2b 100644 --- a/packages/compiler/src/frontend/lowering/function-returns.ts +++ b/packages/compiler/src/frontend/lowering/function-returns.ts @@ -17,6 +17,7 @@ import { PoisonError, dynFallbackType } from "./lowerer.js"; import { functionCanReturnUndefined } from "../function-completion.js"; import { jsBindingAppendsOnly, jsBindingHasOpenWrites } from "./lower-stmts.js"; import type { FnCtx } from "./function-context.js"; +import { returnsGuessedOverloadValue } from "./overload-choice.js"; /** Keep the conservative completion rule shared by return construction * and inference. Nested control flow may still make completion unreachable. */ @@ -256,6 +257,22 @@ export function declaredReturnType( return decl.modifiers?.nodes.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) ? { kind: "promise", inner: DYN } : DYN; + // A JS function returning a value whose type rests on an undecided + // overload (guessedOverloadValue): the inferred result is the first + // overload's, which the returned value need not have. + if ( + isJsSourceFile(ts.getSourceFileOfNode(decl)) && + !decl.type && + !decl.asteriskToken && + !hasExplicitJsDocReturn(decl) && + mappedReturn !== null && + mappedReturn.kind !== "dyn" && + !(mappedReturn.kind === "promise" && mappedReturn.inner.kind === "dyn") && + returnsGuessedOverloadValue(lowerer, decl) + ) + return decl.modifiers?.nodes.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) + ? { kind: "promise", inner: DYN } + : DYN; // JS inference assigns Number to arithmetic over any, although the // runtime operands can both be BigInts. Preserve that native result. if ( diff --git a/packages/compiler/src/frontend/lowering/indexed-unions.ts b/packages/compiler/src/frontend/lowering/indexed-unions.ts new file mode 100644 index 000000000..774f3aef3 --- /dev/null +++ b/packages/compiler/src/frontend/lowering/indexed-unions.ts @@ -0,0 +1,569 @@ +import { keyedHelperName } from "./stable-names.js"; +import { helperLoc, varRef } from "../../ir/build.js"; +import { + BOOL, + BYTES_ELEMENT_SIZE, + F64, + VOID, + type IrBytesIntrinsicMethod, + type IrExpr, + type IrFunction, + type IrStmt, + type IrType, + type SrcLoc, + typeKey, +} from "../../ir/ir.js"; +import { everyExprChild, everyStmtChild } from "../../ir/traverse.js"; +import { arrayNumberRead, lowerSafeBytesRead, lowerSafeIndexRead } from "./array-values.js"; +import type { Lowerer } from "./lowerer.js"; + +/** Unions of number-indexed containers: typed arrays of any element kind + * (`Uint8Array | Uint16Array | Uint32Array`, as lru-cache picks by size) + * and `number[]` arms. + * + * The union keeps its ordinary tagged representation. Each operation on it + * lowers to a lifted helper per union and operation that tests the tag, + * narrows to the arm and runs the arm's own operation: the inline load or + * store of a typed array's element kind, or the dense read or write of a + * number array. The helpers are small, so LLVM inlines them, and a loop + * over one union value tests a loop-invariant tag. */ + +/** One arm: its tag and its type (bytes, or an array of numbers). */ +export interface IndexedArm { + tag: number; + type: IrType & ({ kind: "bytes" } | { kind: "array" }); +} + +/** The arms of a union whose every arm is a typed array or a `number[]`, + * or null. Unions with any other arm (undefined included) are not + * indexed unions: their reads need a narrowing first. */ +export function indexedUnionArms(lowerer: Lowerer, type: IrType): IndexedArm[] | null { + if (type.kind !== "union") return null; + const arms = lowerer.unions.get(type.unionId)?.arms; + if (!arms || arms.length < 2) return null; + const result: IndexedArm[] = []; + for (const [tag, arm] of arms.entries()) { + if (arm.kind === "bytes") result.push({ tag, type: arm }); + else if (arm.kind === "array" && arm.elem.kind === "f64") result.push({ tag, type: arm }); + else return null; + } + return result; +} + +/** Whether every arm is a typed array (so typed-array-only members such + * as `byteLength` apply). */ +export function allBytesArms(arms: readonly IndexedArm[]): boolean { + return arms.every((arm) => arm.type.kind === "bytes"); +} + +type ArmBody = (receiver: IrExpr, arm: IndexedArm, args: readonly IrExpr[]) => IrStmt[]; + +/** The helper `name(u, ...args)`: for each arm but the last, a tag test + * that runs the arm's statements; the last arm's statements run without a + * test (the union holds one of its arms). */ +function dispatchHelper( + lowerer: Lowerer, + key: string, + prefix: string, + union: IrType, + arms: readonly IndexedArm[], + params: readonly { name: string; type: IrType }[], + returnType: IrType, + body: ArmBody, + loc: SrcLoc, +): string { + const existing = lowerer.arrHofHelpers.get(key); + if (existing) return existing; + const name = keyedHelperName(lowerer, prefix, key); + lowerer.arrHofHelpers.set(key, name); + const at = helperLoc(loc); + if (union.kind !== "union") throw new Error("indexed-union helper over a non-union"); + const u = varRef("u.0", union, at); + const args = params.map((p) => varRef(`${p.name}.0`, p.type, at)); + const stmts: IrStmt[] = []; + arms.forEach((arm, i) => { + const receiver: IrExpr = { + kind: "unionNarrow", + unionId: union.unionId, + tag: arm.tag, + value: u, + type: arm.type, + loc: at, + }; + const armStmts = body(receiver, arm, args); + if (i === arms.length - 1) { + stmts.push(...armStmts); + return; + } + stmts.push({ + kind: "if", + cond: { + kind: "unionIsTag", + unionId: union.unionId, + tag: arm.tag, + negated: false, + value: u, + type: BOOL, + loc: at, + }, + then: armStmts, + else_: null, + loc: at, + }); + }); + const fn: IrFunction = { + name, + params: [ + { localId: "u.0", name: "u", type: union }, + ...params.map((p) => ({ localId: `${p.name}.0`, name: p.name, type: p.type })), + ], + returnType, + locals: [ + { id: "u.0", name: "u", type: union, mutable: false }, + ...params.map((p) => ({ id: `${p.name}.0`, name: p.name, type: p.type, mutable: false })), + ], + body: stmts, + loc: at, + }; + lowerer.liftedFns.push(fn); + return name; +} + +const returning = (value: IrExpr, loc: SrcLoc): IrStmt[] => [{ kind: "return", value, loc }]; + +/** `u[i]` read as an element or undefined (an invalid index), as the + * typed-array and number-array reads answer. */ +export function lowerIndexedUnionRead( + lowerer: Lowerer, + receiver: IrExpr, + arms: readonly IndexedArm[], + index: IrExpr, + loc: SrcLoc, +): IrExpr { + const resultT = lowerer.withUndefinedArm(F64); + const key = `idxUnion:get:${unionKey(receiver.type)}`; + const name = dispatchHelper( + lowerer, + key, + "%union.idxOr", + receiver.type, + arms, + [{ name: "i", type: F64 }], + resultT, + (arm, info, [i]) => + returning( + info.type.kind === "bytes" + ? lowerSafeBytesRead(lowerer, arm, i!, loc) + : lowerSafeIndexRead(lowerer, arm, i!, loc)!, + loc, + ), + loc, + ); + const numeric = indexedUnionNumberHelper(lowerer, receiver.type, arms, loc); + lowerer.indexedUnionNumericTwins.set(name, numeric); + return { kind: "call", callee: name, args: [receiver, index], type: resultT, loc }; +} + +/** `u[i]` read as a number: an invalid index reads as NaN, which is + * ToNumber(undefined). */ +export function lowerIndexedUnionNumberRead( + lowerer: Lowerer, + receiver: IrExpr, + arms: readonly IndexedArm[], + index: IrExpr, + loc: SrcLoc, +): IrExpr { + if (inlineDispatch(receiver, [index])) + return inlineArms(receiver, arms, F64, (arm, info) => numberRead(arm, info, index, loc), loc); + const name = indexedUnionNumberHelper(lowerer, receiver.type, arms, loc); + return { kind: "call", callee: name, args: [receiver, index], type: F64, loc }; +} + +function numberRead(arm: IrExpr, info: IndexedArm, index: IrExpr, loc: SrcLoc): IrExpr { + return info.type.kind === "bytes" + ? { + kind: "bytesIntrinsic", + method: "get", + receiver: arm, + args: [index], + type: F64, + loc, + invalidNaN: true, + } + : arrayNumberRead(arm, index, loc); +} + +function indexedUnionNumberHelper( + lowerer: Lowerer, + union: IrType, + arms: readonly IndexedArm[], + loc: SrcLoc, +): string { + return dispatchHelper( + lowerer, + `idxUnion:num:${unionKey(union)}`, + "%union.getNumber", + union, + arms, + [{ name: "i", type: F64 }], + F64, + (arm, info, [i]) => returning(numberRead(arm, info, i!, loc), loc), + loc, + ); +} + +/** The numeric twin of an indexed-union read helper call, or null. */ +export function indexedUnionNumericTwin(lowerer: Lowerer, call: IrExpr): IrExpr | null { + if (call.kind !== "call") return null; + const twin = lowerer.indexedUnionNumericTwins.get(call.callee); + return twin === undefined ? null : { ...call, callee: twin, type: F64 }; +} + +/** `u[i] = v` for a number `v`: each arm's element store (a typed array + * converts the number to its element kind and ignores an invalid index; a + * number array stores at the index). */ +export function lowerIndexedUnionWrite( + lowerer: Lowerer, + receiver: IrExpr, + arms: readonly IndexedArm[], + index: IrExpr, + value: IrExpr, + loc: SrcLoc, +): IrStmt { + // In place, the value evaluates before the arms read the receiver and + // index: only when it cannot rebind either. + if (inlineDispatch(receiver, [index]) && !rebinds(value, [receiver, index])) { + const union = receiver.type; + const valueLocal = lowerer.declareHiddenLocal("%elementValue", F64); + const v: IrExpr = { kind: "varRef", localId: valueLocal.id, type: F64, loc }; + let chain: IrStmt[] | null = null; + for (let i = arms.length - 1; i >= 0; i--) { + const info = arms[i]!; + const arm: IrExpr = { + kind: "unionNarrow", + unionId: union.kind === "union" ? union.unionId : "", + tag: info.tag, + value: receiver, + type: info.type, + loc, + }; + const store: IrStmt = + info.type.kind === "bytes" + ? { kind: "bytesSet", arr: arm, index, value: v, loc } + : { kind: "arraySet", arr: arm, index, value: v, loc }; + chain = + chain === null + ? [store] + : [ + { + kind: "if", + cond: { + kind: "unionIsTag", + unionId: union.kind === "union" ? union.unionId : "", + tag: info.tag, + negated: false, + value: receiver, + type: BOOL, + loc, + }, + then: [store], + else_: chain, + loc, + }, + ]; + } + return { + kind: "block", + body: [{ kind: "varDecl", localId: valueLocal.id, init: value, loc }, ...chain!], + loc, + }; + } + const name = dispatchHelper( + lowerer, + `idxUnion:set:${unionKey(receiver.type)}`, + "%union.idxSet", + receiver.type, + arms, + [ + { name: "i", type: F64 }, + { name: "v", type: F64 }, + ], + VOID, + (arm, info, [i, v]) => [ + info.type.kind === "bytes" + ? { kind: "bytesSet", arr: arm, index: i!, value: v!, loc } + : { kind: "arraySet", arr: arm, index: i!, value: v!, loc }, + { kind: "return", value: null, loc }, + ], + loc, + ); + return { + kind: "exprStmt", + expr: { kind: "call", callee: name, args: [receiver, index, value], type: VOID, loc }, + loc, + }; +} + +/** The members every arm answers: `length` for all, and `byteLength`, + * `byteOffset` and `BYTES_PER_ELEMENT` when every arm is a typed array. */ +export function lowerIndexedUnionMember( + lowerer: Lowerer, + receiver: IrExpr, + arms: readonly IndexedArm[], + member: string, + loc: SrcLoc, +): IrExpr | null { + const bytesOnly = member !== "length"; + if ( + member !== "length" && + member !== "byteLength" && + member !== "byteOffset" && + member !== "BYTES_PER_ELEMENT" + ) + return null; + if (bytesOnly && !allBytesArms(arms)) return null; + const memberOf = (arm: IrExpr, info: IndexedArm): IrExpr => + info.type.kind === "array" + ? { kind: "arrIntrinsic", method: "length", receiver: arm, args: [], type: F64, loc } + : member === "BYTES_PER_ELEMENT" + ? { kind: "numLit", value: BYTES_ELEMENT_SIZE[info.type.elem], type: F64, loc } + : { kind: "bytesIntrinsic", method: member, receiver: arm, args: [], type: F64, loc }; + if (inlineDispatch(receiver, [])) return inlineArms(receiver, arms, F64, memberOf, loc); + const name = dispatchHelper( + lowerer, + `idxUnion:${member}:${unionKey(receiver.type)}`, + `%union.${member}`, + receiver.type, + arms, + [], + F64, + (arm, info) => returning(memberOf(arm, info), loc), + loc, + ); + return { kind: "call", callee: name, args: [receiver], type: F64, loc }; +} + +function unionKey(type: IrType): string { + return type.kind === "union" ? String(type.unionId) : "?"; +} + +/** A typed-array method on a union whose every arm is a typed array: the + * arguments evaluate once in the caller, and each arm runs the same + * `bytesIntrinsic` on its own receiver. `result` "receiver" is a method + * that answers a typed array of the receiver's kind (subarray, slice, + * fill, ...): each arm wraps its answer back into the union. */ +export function lowerIndexedUnionMethod( + lowerer: Lowerer, + receiver: IrExpr, + arms: readonly IndexedArm[], + method: IrBytesIntrinsicMethod, + args: readonly IrExpr[], + result: "receiver" | IrType, + loc: SrcLoc, +): IrExpr { + const union = receiver.type; + if (union.kind !== "union") throw new Error("indexed-union method over a non-union"); + const resultT = result === "receiver" ? union : result; + const params = args.map((arg, i) => ({ name: `a${i}`, type: arg.type })); + const signature = args.map((arg) => typeKey(arg.type)).join(","); + const name = dispatchHelper( + lowerer, + `idxUnion:${method}(${signature}):${unionKey(union)}`, + `%union.${method}`, + union, + arms, + params, + resultT, + (arm, info, refs) => { + const call: IrExpr = { + kind: "bytesIntrinsic", + method, + receiver: arm, + args: [...refs], + type: result === "receiver" ? info.type : result, + loc, + }; + if (result === "receiver") + return returning( + { + kind: "unionWrap", + unionId: union.unionId, + tag: info.tag, + value: call, + type: union, + loc, + }, + loc, + ); + if (result.kind === "void") + return [ + { kind: "exprStmt", expr: call, loc }, + { kind: "return", value: null, loc }, + ]; + return returning(call, loc); + }, + loc, + ); + return { kind: "call", callee: name, args: [receiver, ...args], type: resultT, loc }; +} + +/** The elements of `u` as a `number[]`: a typed-array arm drains its + * elements into a fresh array. A number-array arm is the array itself for + * consumers that copy it (spreads), or with `fresh` a dense copy whose + * holes read as undefined (Array.from). */ +export function lowerIndexedUnionToArray( + lowerer: Lowerer, + receiver: IrExpr, + arms: readonly IndexedArm[], + loc: SrcLoc, + fresh = false, +): IrExpr { + const arrayT: IrType = { kind: "array", elem: F64 }; + const name = dispatchHelper( + lowerer, + `idxUnion:${fresh ? "from" : "toArray"}:${unionKey(receiver.type)}`, + fresh ? "%union.arrayFrom" : "%union.toArray", + receiver.type, + arms, + [], + arrayT, + (arm, info) => + returning( + info.type.kind === "bytes" + ? { + kind: "bytesIntrinsic", + method: "toArray", + receiver: arm, + args: [], + type: arrayT, + loc, + } + : fresh + ? { + kind: "arrIntrinsic", + method: "reverse", + receiver: { + kind: "arrIntrinsic", + method: "toReversed", + receiver: arm, + args: [], + type: arrayT, + loc, + }, + args: [], + type: arrayT, + loc, + } + : arm, + loc, + ), + loc, + ); + return { kind: "call", callee: name, args: [receiver], type: arrayT, loc }; +} + +/** Whether a dispatch can run in place instead of through a helper call: + * the receiver is a plain read, so testing and narrowing it once per arm + * reads the same value, and the other operands are plain reads or + * literals. The arms' own reads then stay visible to the integer range + * analysis (a loop bounded by `u.length` keeps an integer counter). */ +function inlineDispatch(receiver: IrExpr, operands: readonly IrExpr[]): boolean { + return ( + receiver.kind === "varRef" && operands.every((e) => e.kind === "varRef" || e.kind === "numLit") + ); +} + +/** Expressions that run no program code: reads, literals and operators + * over primitive values. */ +const CODE_FREE_EXPRS = new Set([ + "numLit", + "boolLit", + "strLit", + "unitLit", + "varRef", + "unary", + "logical", + "ternary", + "toBool", + "strEq", + "strCmp", + "arrayGet", + "arrayHas", + "arrayState", + "bytesIntrinsic", + "fieldGet", + "recordGet", + "unionNarrow", + "unionIsTag", + "unionDisc", + "upcast", + "nullish", +]); +const PRIMITIVE_OPERANDS = new Set(["f64", "bool", "string", "bigint"]); + +/** Whether evaluating `e` can rebind one of the plain reads `reads`: by + * assigning it, or by running program code (a call, a getter, a + * conversion) that could assign a captured binding. */ +function rebinds(e: IrExpr, reads: readonly IrExpr[]): boolean { + const ids = new Set(reads.flatMap((r) => (r.kind === "varRef" ? [r.localId] : []))); + let found = false; + const expr = (x: IrExpr): boolean => { + if ((x.kind === "assignExpr" || x.kind === "incDec") && ids.has(x.localId)) found = true; + else if (x.kind === "bin") { + if (!PRIMITIVE_OPERANDS.has(x.left.type.kind) || !PRIMITIVE_OPERANDS.has(x.right.type.kind)) + found = true; + } else if (x.kind === "call") { + // The union dispatch helpers only read and store elements. + if (!x.callee.startsWith("%union.")) found = true; + } else if (!CODE_FREE_EXPRS.has(x.kind)) found = true; + return !found && everyExprChild(x, expr, stmt); + }; + const stmt = (x: IrStmt): boolean => { + if ((x.kind === "assign" || x.kind === "varDecl") && ids.has(x.localId)) found = true; + return !found && everyStmtChild(x, expr, stmt); + }; + expr(e); + return found; +} + +/** The in-place form of a dispatch: a ternary chain over the arms' tags, + * the last arm untested. */ +function inlineArms( + receiver: IrExpr, + arms: readonly IndexedArm[], + type: IrType, + arm: (narrowed: IrExpr, info: IndexedArm) => IrExpr, + loc: SrcLoc, +): IrExpr { + const union = receiver.type; + if (union.kind !== "union") throw new Error("indexed-union dispatch over a non-union"); + const narrowed = (info: IndexedArm): IrExpr => ({ + kind: "unionNarrow", + unionId: union.unionId, + tag: info.tag, + value: receiver, + type: info.type, + loc, + }); + let result = arm(narrowed(arms[arms.length - 1]!), arms[arms.length - 1]!); + for (let i = arms.length - 2; i >= 0; i--) { + const info = arms[i]!; + result = { + kind: "ternary", + cond: { + kind: "unionIsTag", + unionId: union.unionId, + tag: info.tag, + negated: false, + value: receiver, + type: BOOL, + loc, + }, + then: arm(narrowed(info), info), + else_: result, + type, + loc, + }; + } + return result; +} diff --git a/packages/compiler/src/frontend/lowering/iterator-adapters.ts b/packages/compiler/src/frontend/lowering/iterator-adapters.ts index e50652182..9f2b1e31f 100644 --- a/packages/compiler/src/frontend/lowering/iterator-adapters.ts +++ b/packages/compiler/src/frontend/lowering/iterator-adapters.ts @@ -28,11 +28,206 @@ export function iteratorSlotAdapter( expr: IrExpr, expected: IrType & { kind: "generator" }, ): IrExpr | null { - if (expr.type.kind === "dyn") return dynIteratorAdapter(lowerer, expr, expected); + if (expr.type.kind === "dyn") + return ( + collectionIteratorSlotValue(lowerer, expr, expected) ?? + dynIteratorAdapter(lowerer, expr, expected) + ); if (expr.type.kind === "generator") return generatorRetypeAdapter(lowerer, expr, expected); return null; } +/** `m.values()` (or keys/entries, or a Set's) on a typed Map or Set, which + * lowers to a dynamic invocation on the boxed collection, flowing into an + * iterator-typed slot (`values(): IterableIterator { return + * this.rows.values(); }`): an interned generator over the collection that + * reads each entry typed, instead of the checked adapter over the boxed + * native iterator (dynIteratorAdapter), which boxes every value and checks + * it back. It keeps that adapter's protocol: the walk is lazy and live, and + * since the native iterator has no return(), an early exit from a + * consumer's loop parks it on the current entry instead of completing it, + * so a later loop continues with the next entry. Null for any other source + * or a slot whose yield type is not the collection's entry type. */ +function collectionIteratorSlotValue( + lowerer: Lowerer, + expr: IrExpr, + expected: IrType & { kind: "generator" }, +): IrExpr | null { + if (expr.kind !== "dynInvoke" || expr.args.length > 0 || expr.calleeNameValue) return null; + const method = expr.method; + if (method !== "values" && method !== "keys" && method !== "entries") return null; + if (expr.recv.kind !== "dynFrom") return null; + const receiver = expr.recv.value; + const collT = receiver.type; + if (collT.kind !== "map" && collT.kind !== "set") return null; + if (expected.async || expected.yieldT.kind === "void") return null; + if (expected.retT.kind !== "void" && expected.retT.kind !== "dyn") return null; + if (expected.nextT.kind !== "undefinedT" && expected.nextT.kind !== "dyn") return null; + const resultType = genResultRecord( + expected.yieldT, + expected.retT, + lowerer.shapes, + lowerer.unions, + ); + if (!resultType) return null; + const loc = expr.loc; + const key = `collIterSlot:${method}:${typeKey(collT)}:${typeKey(expected)}`; + let name = lowerer.valueHelpers.get(key); + if (!name) { + const coll = varRef("coll.0", collT, loc); + const index = varRef("index.0", F64, loc); + const native = varRef("native.0", DYN, loc); + const resumedFlag = varRef("resumed.0", BOOL, loc); + const keyT = collT.kind === "map" ? collT.key : collT.elem; + const valueT = collT.kind === "map" ? collT.value : collT.elem; + const read = (m: "iterKey" | "iterValue", t: IrType): IrExpr => + collT.kind === "map" + ? { kind: "mapIntrinsic", method: m, receiver: coll, args: [index], type: t, loc } + : { kind: "setIntrinsic", method: "iterKey", receiver: coll, args: [index], type: t, loc }; + // entries() answers [key, value] pairs: only a slot whose yield type is + // that pair as a record of the two fields takes the typed walk; a tuple + // or other pair type keeps the checked adapter. + if (method === "entries") { + const pair = + expected.yieldT.kind === "record" ? lowerer.shapes.get(expected.yieldT.shapeId) : undefined; + const field = (name: string): IrType | undefined => + pair?.fields.find((f) => f.name === name)?.type; + const first = field("0"); + const second = field("1"); + if ( + pair === undefined || + pair.fields.length !== 2 || + first === undefined || + second === undefined || + !typeEquals(first, keyT) || + !typeEquals(second, valueT) + ) + return null; + } + const entry = (): IrExpr => + method === "keys" || (collT.kind === "set" && method === "values") + ? read("iterKey", keyT) + : method === "values" + ? read("iterValue", valueT) + : { + kind: "recordLit", + fields: [ + { name: "0", value: read("iterKey", keyT) }, + { name: "1", value: read("iterValue", valueT) }, + ], + type: expected.yieldT, + loc, + }; + if (!typeEquals(entry().type, expected.yieldT)) return null; + // The entry is read once per step and kept: the parking yield of an + // early exit answers it again rather than rereading a slot the loop may + // have deleted. + const current = varRef("current.0", expected.yieldT, loc); + const suspend = (): IrStmt => ({ + kind: "exprStmt", + expr: { kind: "yieldExpr", value: current, type: resumeType(expected.nextT), loc }, + loc, + }); + const finish: IrStmt = { + kind: "return", + value: expected.retT.kind === "dyn" ? dynUndefinedExpr(loc) : null, + loc, + }; + name = keyedHelperName(lowerer, "%coll.iterSlot", key); + lowerer.valueHelpers.set(key, name); + lowerer.liftedFns.push({ + name, + params: [{ localId: "coll.0", name: "coll", type: collT }], + returnType: expected.retT, + locals: [ + { id: "coll.0", name: "coll", type: collT, mutable: false }, + { id: "native.0", name: "native", type: DYN, mutable: false }, + { id: "index.0", name: "index", type: F64, mutable: true }, + { id: "resumed.0", name: "resumed", type: BOOL, mutable: true }, + { id: "current.0", name: "current", type: expected.yieldT, mutable: false }, + ], + body: [ + { + kind: "varDecl", + localId: "native.0", + init: { + kind: "dynInvoke", + recv: lowerer.coerceToExpected(coll, DYN), + method: "values", + calleeName: collT.kind === "map" ? "map.values" : "set.values", + args: [], + type: DYN, + loc, + }, + loc, + }, + { + kind: "varDecl", + localId: "resumed.0", + init: { kind: "boolLit", value: false, type: BOOL, loc }, + loc, + }, + { + kind: "while", + cond: { kind: "boolLit", value: true, type: BOOL, loc }, + body: [ + { + kind: "assign", + localId: "index.0", + value: { + kind: "libCall", + fn: "dyn.collectionIteratorIndex", + args: [native], + type: F64, + loc, + }, + loc, + }, + { + kind: "if", + cond: { + kind: "bin", + op: "<", + left: index, + right: { kind: "numLit", value: 0, type: F64, loc }, + type: BOOL, + loc, + }, + then: [finish], + else_: null, + loc, + }, + setResumed(false, loc), + { kind: "varDecl", localId: "current.0", init: entry(), loc }, + { + kind: "tryCatch", + tryBody: [suspend(), setResumed(true, loc)], + catchBody: null, + catchLocalId: null, + // An early exit parks on the current entry; the next step + // resumes the walk after it. + finallyBody: [ + { + kind: "if", + cond: { kind: "unary", op: "!", operand: resumedFlag, type: BOOL, loc }, + then: [suspend(), { kind: "continue", loc }], + else_: null, + loc, + }, + ], + loc, + }, + ], + loc, + }, + ], + generator: { yieldT: expected.yieldT, nextT: expected.nextT, resultType, tag: "!" }, + loc, + }); + } + return { kind: "call", callee: name, args: [receiver], type: expected, loc }; +} + /** `resumed.0 = value` — the adapters' early-exit marker around a yield. */ function setResumed(value: boolean, loc: SrcLoc): IrStmt { return { diff --git a/packages/compiler/src/frontend/lowering/js-class-fields.ts b/packages/compiler/src/frontend/lowering/js-class-fields.ts index 2f726ce97..28a7ceeda 100644 --- a/packages/compiler/src/frontend/lowering/js-class-fields.ts +++ b/packages/compiler/src/frontend/lowering/js-class-fields.ts @@ -2,11 +2,19 @@ import * as ts from "../ts7/adapter.js"; import { isClassOwnEnumerableFieldName } from "../../ir/ir.js"; import type { ClassInfo } from "./lower-classes.js"; -/** Reflection that can remove a property or change its attributes. */ -const PROPERTY_REFLECTION = new Set(["deleteProperty", "freeze", "seal", "preventExtensions"]); +/* Object.freeze, Object.seal and Object.preventExtensions do not appear + * here: they never create or remove a property, and a class instance keeps + * its integrity state outside the field layout. A statically typed call + * sets the instance's CLASS_ATTRIBUTES word (class-attributes.ts), which + * TypeScript classes use with the same fixed layouts; through an untyped + * value the runtime refuses the operation on any class instance + * (scr_dyn_class_integrity_fence). Lifting that fence must revisit this. */ -/** `Object.freeze({ ... })` and the like apply to a new literal, which is - * not a class instance. */ +/** Reflection that can remove any property. */ +const PROPERTY_REFLECTION = new Set(["deleteProperty"]); + +/** `Reflect.deleteProperty({ ... }, key)` applies to a new literal, which + * is not a class instance. */ function freshLiteralArgument(callee: ts.PropertyAccessExpression): boolean { const call = callee.parent; if (!ts.isCallExpression(call) || call.expression !== callee) return false; @@ -141,12 +149,35 @@ export function constructionPrefix( return { created, sealed }; } -/** Whether a class declares a getter or setter, which can intercept a - * base constructor's field assignment. */ -function declaresAccessor(info: ClassInfo): boolean { - return [...info.methods.keys()].some( - (name) => name.startsWith("get:") || name.startsWith("set:"), - ); +/** The names of the getters and setters among a class's own methods. */ +export function accessorNames(methods: Iterable): Set { + const names = new Set(); + for (const name of methods) + if (name.startsWith("get:") || name.startsWith("set:")) names.add(name.slice(4)); + return names; +} + +/** Whether one of `accessors` can intercept an assignment the base + * constructions make: a base constructor's `this.name = value` runs a + * subclass accessor of that name instead of creating the field. Only field + * names can reach the created prefix; any other assignment already ends + * it. */ +export function accessorInterceptsBase(accessors: ReadonlySet, base: ClassInfo): boolean { + for (const name of accessors) if (base.fields.has(name)) return true; + return false; +} + +/** Whether an accessor that instances of `info` inherit or declare shares + * a name with one of their fields. Such a field and accessor disagree on + * what the instance's property is; any other accessor lives on the + * prototype beside the fields and never touches the layout. */ +function accessorShadowsField(info: ClassInfo): boolean { + for (let current: ClassInfo | null = info; current; current = current.base) { + if (current.def.runtime) break; + for (const name of accessorNames(current.methods.keys())) + if (info.fields.has(name)) return true; + } + return false; } /** `constructionPrefix` for a collected class and its bases. */ @@ -156,7 +187,8 @@ export function construction(info: ClassInfo): Construction | null { if (info.base) { base = construction(info.base); if (!base) return null; - if (declaresAccessor(info)) base = { created: base.created, sealed: false }; + if (accessorInterceptsBase(accessorNames(info.methods.keys()), info.base)) + base = { created: base.created, sealed: false }; } return constructionPrefix(info.decl, base, (name) => info.fields.has(name)); } @@ -169,7 +201,7 @@ export function construction(info: ClassInfo): Construction | null { export function fixedFieldFamily(root: ClassInfo, deleted: Set | null): boolean { if (deleted === null) return false; const fixed = (info: ClassInfo): boolean => { - if (declaresAccessor(info)) return false; + if (accessorShadowsField(info)) return false; const built = construction(info); if (!built) return false; for (const field of info.fieldOrder) { diff --git a/packages/compiler/src/frontend/lowering/key-order-witness.test.ts b/packages/compiler/src/frontend/lowering/key-order-witness.test.ts index c85902c96..21fe56815 100644 --- a/packages/compiler/src/frontend/lowering/key-order-witness.test.ts +++ b/packages/compiler/src/frontend/lowering/key-order-witness.test.ts @@ -15,6 +15,7 @@ import { type KeyOrderSegment, } from "./key-order-witness.js"; import type { Lowerer } from "./lowerer.js"; +import * as ts from "../ts7/adapter.js"; const loc = { file: "order.ts", start: 0, end: 1 }; /** `number | undefined`: the optional-field slot type. */ @@ -28,7 +29,7 @@ interface Fixture { notes: KeyOrderNotes; record(fields: [string, IrType][], order: string[]): IrType & { kind: "record" }; literal(type: IrType, keys: string[], segments?: KeyOrderSegment[]): IrExpr; - run(body: IrStmt[]): IrFunction; + run(body: IrStmt[], name?: string): IrFunction; order(type: IrType & { kind: "record" }): string[] | undefined; } @@ -40,6 +41,8 @@ function fixture(): Fixture { keyOrder: notes, unions: new Map([["u0", { id: "u0", arms: [F64, { kind: "undefinedT" }] }]]), classes: new Map(), + methodAccessSites: new Map(), + dynConvertible: () => true, armTag: (unionId: string, arm: IrType): number => unionId === "u0" && arm.kind === "undefinedT" ? 1 : -1, recordFieldPresent: (obj: IrExpr, shapeId: string, field: string): IrExpr => ({ @@ -70,8 +73,8 @@ function fixture(): Fixture { segments ?? keys.map((name) => ({ kind: "key", name, definite: true })), ); }; - const run = (body: IrStmt[]): IrFunction => { - const fn: IrFunction = { name: "main", params: [], returnType: F64, locals: [], body, loc }; + const run = (body: IrStmt[], name = "main"): IrFunction => { + const fn: IrFunction = { name, params: [], returnType: F64, locals: [], body, loc }; applyKeyOrderWitnesses(lowerer, [fn]); return fn; }; @@ -700,3 +703,294 @@ test("a spread reads its source's order right after the literal evaluates the so const snapshot = (prologue[2] as IrStmt & { kind: "varDecl" }).localId; expect(orderIndexOf(lit)).toMatchObject({ kind: "varRef", localId: snapshot }); }); + +/** `{ a, b }` with a declared table `["a", "b"]` and a literal `{ b, a }` + * plus `{ ...value, a }` of the same shape, beside `extra`. */ +function selfCopy( + extra: (f: Fixture, ab: IrType & { kind: "record" }) => IrStmt[], + fnName = "main", +): string[] { + const f = fixture(); + const ab = f.record( + [ + ["a", F64], + ["b", F64], + ], + ["a", "b"], + ); + const self: KeyOrderSegment = { kind: "spread", shapeId: ab.shapeId, maybeAbsent: false }; + f.run( + [ + decl("v", f.literal(ab, ["b", "a"])), + observe(f.literal(ab, ["a", "b"], [self, { kind: "key", name: "a", definite: true }])), + ...extra(f, ab), + ], + fnName, + ); + return f.order(ab)!; +} + +test("a literal that starts by spreading its own shape takes the other literals' order", () => { + expect(selfCopy(() => [])).toEqual(["b", "a"]); +}); + +test("a self copy of a shape a checked conversion builds cannot be ordered statically", () => { + const parsed = (ab: IrType): IrExpr => ({ + kind: "dynCheck", + value: ref("j", { kind: "dyn" }), + type: ab, + loc, + }); + expect(selfCopy((_, ab) => [observe(parsed(ab))])).toEqual(["a", "b"]); +}); + +test("a replaceable method's checked result builds no record", () => { + expect( + selfCopy((f, ab) => { + const result: IrExpr = { kind: "dynCheck", value: ref("r", { kind: "dyn" }), type: ab, loc }; + return [observe(f.notes.methodResult(result, "load"))]; + }), + ).toEqual(["b", "a"]); +}); + +test("a replaceable method's checked result builds records when a replacement may return one", () => { + expect( + selfCopy((f, ab) => { + // A site the census cannot see through (a computed write of some + // value, a reflection call) may install a replacement returning a + // record built in any order. + (f.lowerer.methodAccessSites as Map).set("load", [ + { kind: ts.SyntaxKind.CallExpression } as unknown as ts.Node, + ]); + const result: IrExpr = { kind: "dynCheck", value: ref("r", { kind: "dyn" }), type: ab, loc }; + return [observe(f.notes.methodResult(result, "load"))]; + }), + ).toEqual(["a", "b"]); +}); + +/** A native callback taking the shape, boxed for JavaScript. */ +const boxedCallback = (ab: IrType): IrExpr => ({ + kind: "dynFrom", + value: { + kind: "closure", + fnName: "callback", + captures: [], + type: { kind: "func", params: [ab], ret: { kind: "void" } }, + loc, + }, + type: { kind: "dyn" }, + loc, +}); + +test("a native callback boxed for JavaScript receives records JavaScript builds", () => { + expect(selfCopy((_, ab) => [observe(boxedCallback(ab))])).toEqual(["a", "b"]); +}); + +test("a class's prototype slots box its methods without building their parameters", () => { + expect(selfCopy((_, ab) => [observe(boxedCallback(ab))], "%prototype.data.Service")).toEqual([ + "b", + "a", + ]); +}); + +test("a replaceable method's result keeps its witness through an IR rewrite", () => { + expect( + selfCopy((f, ab) => { + const result = f.notes.methodResult( + { + kind: "dynCheck", + value: ref("r", { kind: "dyn" }), + type: ab, + loc, + }, + "load", + ); + const identity = { expr: (e: IrExpr): IrExpr => e, stmt: (st: IrStmt): IrStmt => st }; + observeRecordLiteralRebuilds((from, to) => f.notes.follow(from, to)); + try { + const rebuilt = transformExpr(result, identity); + expect(rebuilt).not.toBe(result); + return [observe(rebuilt)]; + } finally { + observeRecordLiteralRebuilds(null); + } + }), + ).toEqual(["b", "a"]); +}); + +test("a checked conversion of a boxed primitive builds no record", () => { + expect( + selfCopy((_, ab) => { + const missing: IrExpr = { + kind: "dynFrom", + value: { kind: "unitLit", unit: "undefined", type: { kind: "undefinedT" }, loc }, + type: { kind: "dyn" }, + loc, + }; + return [observe({ kind: "dynCheck", value: missing, type: ab, loc })]; + }), + ).toEqual(["b", "a"]); +}); + +test("a checked conversion into a class builds none of its fields' records", () => { + expect( + selfCopy((f, ab) => { + (f.lowerer.classes as Map).set("Holder", { + def: { fields: [{ name: "value", type: ab }] }, + }); + const holder: IrType = { kind: "object", className: "Holder" }; + const checked: IrExpr = { + kind: "dynCheck", + value: ref("h", { kind: "dyn" }), + type: holder, + loc, + }; + return [{ kind: "exprStmt", expr: checked, loc }]; + }), + ).toEqual(["b", "a"]); +}); + +test("a checked function passes its record parameters out, and its callbacks' back in", () => { + const callable = (params: IrType[]): IrType => ({ + kind: "func", + params, + ret: { kind: "string" }, + }); + const checkedAs = (type: IrType): IrStmt => ({ + kind: "exprStmt", + expr: { kind: "dynCheck", value: ref("fn", { kind: "dyn" }), type, loc }, + loc, + }); + expect(selfCopy((_, ab) => [checkedAs(callable([ab]))])).toEqual(["b", "a"]); + // A callback parameter receives values the JavaScript function builds. + expect(selfCopy((_, ab) => [checkedAs(callable([callable([ab])]))])).toEqual(["a", "b"]); + // So does a container parameter, which the function can write into. + expect(selfCopy((_, ab) => [checkedAs(callable([{ kind: "array", elem: ab }]))])).toEqual([ + "a", + "b", + ]); +}); + +test("a generator resume builds only its result record, never the value it carries", () => { + const f = fixture(); + const ab = f.record( + [ + ["a", F64], + ["b", F64], + ], + ["a", "b"], + ); + const result = f.record( + [ + ["done", { kind: "bool" }], + ["value", ab], + ], + ["value", "done"], + ); + const gen: IrType = { + kind: "generator", + yieldT: ab, + retT: { kind: "void" }, + nextT: { kind: "undefinedT" }, + }; + const resume = (mode: "next" | "value", type: IrType): IrStmt => + observe({ kind: "genResume", mode, gen: ref("g", gen), arg: null, type, loc }); + f.run([ + observe(f.literal(ab, ["b", "a"])), + observe(f.literal(result, ["done", "value"])), + resume("value", ab), + resume("next", result), + ]); + expect(f.order(ab)).toEqual(["b", "a"]); + // The result record itself is built in its fixed layout: today's table. + expect(f.order(result)).toEqual(["value", "done"]); +}); + +/** `{ a, b }` built by a checked conversion and by the literal `{ b, a }`, + * copied by `{ ...value, a }` (evaluating the value into a binding, as + * spread literals do), observed. Answers the table and the function. */ +function checkedSelfCopy(): { order: string[]; fn: IrFunction; copy: IrExpr } { + const f = fixture(); + const ab = f.record( + [ + ["a", F64], + ["b", F64], + ], + ["a", "b"], + ); + const value = ref("value", ab) as IrExpr & { kind: "varRef" }; + const self: KeyOrderSegment = { kind: "spread", shapeId: ab.shapeId, maybeAbsent: false, value }; + const literal = f.literal(ab, ["a", "b"], [self, { kind: "key", name: "a", definite: true }]); + const copy: IrExpr = { + kind: "seqExpr", + stmts: [decl("value", { kind: "dynCheck", value: ref("j", { kind: "dyn" }), type: ab, loc })], + result: literal, + type: ab, + loc, + }; + const fn = f.run([observe(f.literal(ab, ["b", "a"])), observe(copy)]); + return { order: f.order(ab)!, fn, copy }; +} + +test("a spread of a value a checked conversion built carries the value's order over", () => { + const { order, fn, copy } = checkedSelfCopy(); + // Values the literal builds enumerate in its order; copies of converted + // values keep the order their record source holds. + expect(order).toEqual(["b", "a"]); + expect(copy).toMatchObject({ + kind: "seqExpr", + stmts: [ + { kind: "varDecl", localId: "value" }, + { kind: "varDecl", localId: "%orderedSpread.0", init: { kind: "recordLit" } }, + { + kind: "exprStmt", + expr: { + kind: "libCall", + fn: "record.sourceSpread", + args: [ + { kind: "varRef", localId: "%orderedSpread.0" }, + { kind: "varRef", localId: "value" }, + ], + }, + }, + ], + result: { kind: "varRef", localId: "%orderedSpread.0" }, + }); + expect(fn.locals.map((local) => local.id)).toEqual(["%orderedSpread.0"]); +}); + +test("a spread of a converted value into a wider shape orders the wider shape", () => { + const f = fixture(); + const ab = f.record( + [ + ["a", F64], + ["b", F64], + ], + ["a", "b"], + ); + const abc = f.record( + [ + ["a", F64], + ["b", F64], + ["c", F64], + ], + ["c", "a", "b"], + ); + const value = ref("value", ab) as IrExpr & { kind: "varRef" }; + const spread: KeyOrderSegment = { + kind: "spread", + shapeId: ab.shapeId, + maybeAbsent: false, + value, + }; + const wide: IrExpr = { + kind: "seqExpr", + stmts: [decl("value", { kind: "dynCheck", value: ref("j", { kind: "dyn" }), type: ab, loc })], + result: f.literal(abc, ["a", "b", "c"], [spread, { kind: "key", name: "c", definite: true }]), + type: abc, + loc, + }; + f.run([observe(f.literal(ab, ["b", "a"])), observe(wide)]); + expect(f.order(ab)).toEqual(["b", "a"]); + expect(f.order(abc)).toEqual(["b", "a", "c"]); +}); diff --git a/packages/compiler/src/frontend/lowering/key-order-witness.ts b/packages/compiler/src/frontend/lowering/key-order-witness.ts index aa84c68c2..a5e635e04 100644 --- a/packages/compiler/src/frontend/lowering/key-order-witness.ts +++ b/packages/compiler/src/frontend/lowering/key-order-witness.ts @@ -28,7 +28,25 @@ * build keeps today's table, since another table would make values * parsed in today's order keep a source view; a shape object literals * also build takes the literals' order. Their order is unknown, so a - * copy out of such a shape cannot be ordered statically; + * copy out of such a shape cannot be ordered statically. A conversion + * builds only the records it can create from a JavaScript value: none + * for a boxed primitive or a class instance (a native instance whose + * fields its own writes fill), none for a checked function's record + * parameters, and none for the checked result of a typed call of a + * method the program may replace on its instance. Without a replacement + * that result is the method's own native value, boxed and checked back + * unchanged; a replacement's result keeps its own order in its record + * source, and only copies out of it follow the chosen table, as they + * followed today's; + * - a literal that starts by spreading a value of its own shape copies + * that value's order. While no checked conversion builds the shape, + * every value has the one chosen order, so the copy adds no witness; + * - a literal that starts by spreading a value of a shape checked + * conversions build (an ordered spread) gives the record it builds the + * value's record source, when the value has one (record.sourceSpread). + * Every value of a shape enumerates in its table or in the order its + * record source holds, so the copy has the value's order either way, + * and the literal resolves as if the value were in its table's order; * - a write to an optional key that may be absent appends it in Node. A * key no construction creates ("late-only") goes after every key a * construction can create; any other moving write goes after every key. @@ -49,6 +67,7 @@ import { shapeHasAccessorSlots, typeEquals, UNDEFINED_T, + VOID, } from "../../ir/ir.js"; import { everyExprChild, @@ -57,7 +76,9 @@ import { transformStmtList, } from "../../ir/traverse.js"; import type { Lowerer } from "./lowerer.js"; +import * as ts from "../ts7/adapter.js"; import { ALWAYS_THROW_LIB_FNS } from "../../ir/builtin-effects.js"; +import { recordMayKeepSource } from "./record-sources.js"; /** The statements a key-order helper runs for record `ref`: `walks[0]`, * its walk over the shape's table, or, for a shape with orderVariants, a @@ -112,6 +133,7 @@ export type KeyOrderSegment = export class KeyOrderNotes { private readonly literals = new Map(); private readonly copies = new Map(); + private readonly methodResults = new Map(); /** Shapes a frontend order surface (keys, values/entries, inspect, * index-signature merges) walks. */ readonly observed = new Set(); @@ -128,13 +150,22 @@ export class KeyOrderNotes { return node; } + /** The checked result of a typed call of method `name`, which the program + * may replace on its instance (class-callbacks.ts classCallbackCall). */ + methodResult(node: IrExpr, name: string): IrExpr { + this.methodResults.set(node, name); + return node; + } + /** A pass rebuilt `from` as `to` (traverse.ts mapExprChildren): the - * rebuilt literal is the same construction, so it keeps the witness. */ + * rebuilt node is the same construction, so it keeps the witness. */ follow(from: IrExpr, to: IrExpr): void { const segments = this.literals.get(from); if (segments !== undefined) this.literals.set(to, segments); const source = this.copies.get(from); if (source !== undefined) this.copies.set(to, source); + const method = this.methodResults.get(from); + if (method !== undefined) this.methodResults.set(to, method); } observe(type: IrType): void { @@ -148,6 +179,11 @@ export class KeyOrderNotes { copyOf(node: IrExpr): string | undefined { return this.copies.get(node); } + + /** The method whose checked result `node` is, if it is one. */ + methodResultOf(node: IrExpr): string | undefined { + return this.methodResults.get(node); + } } const isArrayIndex = (name: string): boolean => { @@ -190,6 +226,14 @@ interface ShapeFacts { type Resolved = { order: string[] } | "unknown"; +/** A literal that starts with `...value`: the sequence that evaluates the + * value into its binding and then builds the literal, and its function. */ +interface LeadingSpread { + wrapper: IrExpr & { kind: "seqExpr" }; + literal: IrExpr & { kind: "recordLit" }; + fn: IrFunction; +} + /** `structuredClone(v)` checked back into v's own type: the copy is built * from v's dyn view, so it creates exactly v's keys in v's order. */ function orderPreservingClone(node: IrExpr & { kind: "dynCheck" }): boolean { @@ -201,6 +245,82 @@ function orderPreservingClone(node: IrExpr & { kind: "dynCheck" }): boolean { ); } +const PRIMITIVE_KINDS = new Set([ + "f64", + "bigint", + "string", + "bool", + "undefinedT", + "nullT", + "symbol", +]); + +/** A checked conversion of a boxed primitive (the `undefined` a dynamic + * construction passes for a missing argument) answers that primitive or + * throws: it builds no record. */ +function primitiveSource(node: IrExpr & { kind: "dynCheck" }): boolean { + return node.value.kind === "dynFrom" && PRIMITIVE_KINDS.has(node.value.value.type.kind); +} + +/** Whether a census site for a method name can only read the method or + * install a replacement whose result holds no object: a read + * (`C.prototype.m`, `obj.m`) outside an assignment target, or an assignment + * of a function expression whose return type is primitive. Other sites (a + * computed write of another value, a reflection call, a property bag) may + * install a replacement that returns a record. */ +function replacesWithoutRecords(lowerer: Lowerer, site: ts.Node): boolean { + const parent = site.parent; + if (ts.isPropertyAccessExpression(site) || ts.isElementAccessExpression(site)) + return !( + parent !== undefined && + ts.isBinaryExpression(parent) && + parent.left === site && + parent.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && + parent.operatorToken.kind <= ts.SyntaxKind.LastAssignment + ); + if (!ts.isBinaryExpression(site) || site.operatorToken.kind !== ts.SyntaxKind.EqualsToken) + return false; + let value = site.right; + while (ts.isParenthesizedExpression(value)) value = value.expression; + if (!ts.isArrowFunction(value) && !ts.isFunctionExpression(value)) return false; + const signatures = lowerer.checker.getCallSignatures(lowerer.typeOf(value)); + return ( + signatures.length > 0 && + signatures.every((signature) => + primitiveOnly(lowerer.checker.getReturnTypeOfSignature(signature)), + ) + ); +} + +/** A compiler helper that boxes a class's own state for JavaScript's reads: + * its prototype data and the property and method dispatch over boxed + * instances. */ +function boxesOwnState(fnName: string): boolean { + return ( + fnName.startsWith("%prototype.data.") || + fnName.startsWith("%dyn.class.") || + fnName.startsWith("%class.") + ); +} + +/** A type whose values are primitives (or void): it holds no record. */ +function primitiveOnly(type: ts.Type): boolean { + if (type.isUnionType()) return ts.constituentTypes(type).every(primitiveOnly); + return ( + (type.flags & + (ts.TypeFlags.StringLike | + ts.TypeFlags.NumberLike | + ts.TypeFlags.BooleanLike | + ts.TypeFlags.BigIntLike | + ts.TypeFlags.ESSymbolLike | + ts.TypeFlags.Null | + ts.TypeFlags.Undefined | + ts.TypeFlags.Void | + ts.TypeFlags.Never)) !== + 0 + ); +} + /** Chooses each in-scope record shape's enumeration table from its * witnesses. Runs once retained reachability has settled the historical * tables and derived metadata, before the order-snapshotting helper bodies @@ -239,35 +359,70 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio /** Every record shape a type can hold, through fields, arms, elements, * callables, promises and class fields. */ /** Each walk shares one `seen` set per result set, so every type graph - * is walked once however many roots reach it. */ - const shapesIn = (type: IrType, out: Set, seen: Set): void => { - const visit = (t: IrType): boolean => { + * is walked once however many roots reach it. + * + * `produced` walks only what a checked conversion into the type can + * build. It answers an existing native instance for a class type (a plain + * object cannot acquire a class brand; only %Error rebuilds), so the + * records the instance's fields hold come from the class's own writes and + * its checked field setters, each a witness of its own. A checked + * function's parameters receive native values: a record parameter is + * passed out, never built, though a callback among them receives values + * built from JavaScript again. `built` false starts the walk at a value + * passed out (a native value boxed for JavaScript); `writes` false leaves + * out what JavaScript could write into the containers passed out, so the + * walk counts only what the functions among them receive. */ + const shapesIn = ( + type: IrType, + out: Set, + seen: Set, + produced = false, + built = true, + writes = true, + ): void => { + const visit = (t: IrType, built: boolean): boolean => { + const mark = built ? "" : "-"; + // A container JavaScript builds converts into fresh native storage, so + // its children are built too. One passed out stays shared: JavaScript + // writes into it (built children) and reads native values out of it, + // such as callbacks whose parameters it then builds (children passed + // out). Those children count in both directions. + const child = (type: IrType): boolean => { + if (built || writes) visit(type, true); + if (!built) visit(type, false); + return true; + }; if (t.kind === "record") { - if (seen.has("r:" + t.shapeId)) return true; - seen.add("r:" + t.shapeId); - out.add(t.shapeId); + if (seen.has(mark + "r:" + t.shapeId)) return true; + seen.add(mark + "r:" + t.shapeId); + if (built) out.add(t.shapeId); const shape = shapes.get(t.shapeId); if (shape) { - for (const f of shape.fields) visit(f.type); - if (shape.indexValue) visit(shape.indexValue); + for (const f of shape.fields) child(f.type); + if (shape.indexValue) child(shape.indexValue); } return true; } if (t.kind === "union") { - if (seen.has("u:" + t.unionId)) return true; - seen.add("u:" + t.unionId); - for (const arm of lowerer.unions.get(t.unionId)?.arms ?? []) visit(arm); + if (seen.has(mark + "u:" + t.unionId)) return true; + seen.add(mark + "u:" + t.unionId); + for (const arm of lowerer.unions.get(t.unionId)?.arms ?? []) visit(arm, built); return true; } if (t.kind === "object") { - if (seen.has("c:" + t.className)) return true; - seen.add("c:" + t.className); - for (const f of lowerer.classes.get(t.className)?.def.fields ?? []) visit(f.type); + if (produced && t.className !== "%Error") return true; + if (seen.has(mark + "c:" + t.className)) return true; + seen.add(mark + "c:" + t.className); + for (const f of lowerer.classes.get(t.className)?.def.fields ?? []) child(f.type); return true; } - return everyTypeChild(t, visit); + if (produced && t.kind === "func") { + for (const param of t.params) visit(param, !built); + return visit(t.ret, built); + } + return everyTypeChild(t, child); }; - visit(type); + visit(type, built); }; const opaqueShapes = new Set(); const opaqueSeen = new Set(); @@ -278,14 +433,29 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio * declaration's order, the order their table holds. */ const declaredShapes = new Set(); const declaredSeen = new Set(); + const boxedSeen = new Set(); + /** The checked result of a replaceable method call while every site that + * can replace the method installs functions that return no record. */ + const unchangedMethodResult = (node: IrExpr): boolean => { + const name = notes.methodResultOf(node); + if (name === undefined) return false; + const sites = lowerer.methodAccessSites.get(name) ?? []; + return sites.every((site) => replacesWithoutRecords(lowerer, site)); + }; const producedBy = (type: IrType, opaque: boolean): void => { if (opaque) shapesIn(type, opaqueShapes, opaqueSeen); - else shapesIn(type, runtimeShapes, runtimeSeen); + else shapesIn(type, runtimeShapes, runtimeSeen, true); }; // ── collection ────────────────────────────────────────────────────── let pos = 0; let fnName = ""; + let currentFn: IrFunction | null = null; + /** Literals that start by spreading a binding, by their witness: where + * each one is built. A literal's notes are shared by its rebuilt copies, + * so `literalSites` counts every literal of each witness. */ + const leadingSpreads = new Map(); + const literalSites = new Map(); const loops: number[] = []; let loopIds = 0; const loopKey = (): string => loops.join("/"); @@ -337,8 +507,10 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio } const segments = notes.literalOf(e); const copy = notes.copyOf(e); - if (segments !== undefined) f.literals.push(segments); - else if (copy !== undefined) f.copies.push({ source: copy, keys: presentKeys }); + if (segments !== undefined) { + f.literals.push(segments); + literalSites.set(segments, (literalSites.get(segments) ?? 0) + 1); + } else if (copy !== undefined) f.copies.push({ source: copy, keys: presentKeys }); else f.pinned = true; return; } @@ -353,9 +525,31 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio case "assignExpr": noteInit(e.localId, e.value); return; + case "seqExpr": { + // A spread literal evaluates its sources into bindings first. + const literal = e.result; + if (literal.kind !== "recordLit" || currentFn === null) return; + const segments = notes.literalOf(literal); + const first = segments?.[0]; + if (segments === undefined || first?.kind !== "spread" || first.value === undefined) return; + const list = leadingSpreads.get(segments) ?? []; + list.push({ wrapper: e, literal, fn: currentFn }); + leadingSpreads.set(segments, list); + return; + } case "jsonStringify": case "dynFrom": observedRoots.push(e.value.type); + // A native value the program hands to JavaScript: the callbacks it + // holds (itself, a field, an element) receive records JavaScript + // builds. The helpers that box the program's own state for + // JavaScript's reads (a class's prototype slots and property + // dispatch) hand out the class's methods, whose arguments the + // program's own calls pass as native values, as a replaceable + // method's result above. Writes through the boxed value are left + // out: reading a boxed value creates no record. + if (!boxesOwnState(fnName)) + shapesIn(e.value.type, runtimeShapes, boxedSeen, true, false, false); return; case "recordKeyGet": observedRoots.push(e.obj.type); @@ -380,15 +574,31 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio case "strIntrinsic": case "regexIntrinsic": case "bytesIntrinsic": - // Generator results ({ done, value }) and Promise.withResolvers - // records are built by the backend in a fixed layout. - case "genResume": + // Promise.withResolvers records are built by the backend in a fixed + // layout. case "promiseWithResolvers": producedBy(e.type, true); return; + case "genResume": { + // A resume builds at most its IteratorResult record ({ done, value } + // in a fixed layout; an async generator's settles a promise of it). + // The value it carries is the generator's own yielded or returned + // value, which the generator's body constructs. The record-free + // drives build nothing: `value` moves that value out, and step, + // close and a record-free delegate answer a boolean or nothing. + if (e.mode === "step" || e.mode === "value" || e.mode === "close") return; + const result = e.type.kind === "promise" ? e.type.inner : e.type; + if (result.kind === "record") opaqueShapes.add(result.shapeId); + return; + } case "dynCheck": if (e.adopt === true) shapesIn(e.type, declaredShapes, declaredSeen); - else if (!orderPreservingClone(e)) producedBy(e.type, false); + // Conversions that build no record: a boxed primitive, and a + // replaceable method's result while no replacement the program can + // install returns a record: then it is the method's own value, + // boxed and checked back unchanged. + else if (!orderPreservingClone(e) && !primitiveSource(e) && !unchangedMethodResult(e)) + producedBy(e.type, false); return; case "dynDestrCheck": case "caughtCheck": @@ -440,6 +650,7 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio }; for (const fn of fns) { fnName = fn.name; + currentFn = fn; for (const st of fn.body) stmt(st); } for (const id of opaqueShapes) { @@ -556,6 +767,61 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio return edges; }; + // ── ordered spreads ───────────────────────────────────────────────── + // A value a checked conversion builds keeps its source's key order in + // its record source when that order is not the table's (record- + // sources.ts), so every value of a shape enumerates in the table's order + // or in the order its record source holds. A literal that starts with + // `...value` creates the value's keys first, in the value's order. When + // the value's shape is one checked conversions build, the literal + // carries that order over: after it builds the record it gives the + // record a source holding the value's order (record.sourceSpread), and + // the keys the value lacks follow in the table's order, which every + // literal sequence of the shape agrees with. Such a literal resolves as + // if its source were in the source's table order, and its own shape + // counts as built at run time too, since its values may now carry a + // source. It qualifies when every value of the source has all of the + // source's keys, both shapes can keep a source, and every later spread + // whose values may carry a source only overwrites the source's keys + // (its own order cannot move them). + const orderedSpreads = new Set(); + const keepsSource = (shapeId: string): boolean => + recordMayKeepSource(lowerer, { kind: "record", shapeId }); + const userKeys = (shapeId: string): string[] => + (shapes.get(shapeId)?.fields ?? []).map((x) => x.name).filter((k) => !k.startsWith("%")); + const ordersSpread = (segments: KeyOrderSegment[], sites: readonly LeadingSpread[]): boolean => { + const first = segments[0]; + if (first?.kind !== "spread" || first.maybeAbsent || first.value === undefined) return false; + const sourceId = first.shapeId; + if (facts.get(sourceId)?.runtime !== true || sites.length !== literalSites.get(segments)) + return false; + const target = sites[0]!.literal.type; + if (target.kind !== "record" || !keepsSource(sourceId) || !keepsSource(target.shapeId)) + return false; + const localId = first.value.localId; + const bound = ({ wrapper, literal }: LeadingSpread): boolean => + typeEquals(literal.type, target) && + wrapper.stmts.some( + (st) => + st.kind === "varDecl" && + st.localId === localId && + st.init !== null && + st.init.type.kind === "record" && + st.init.type.shapeId === sourceId, + ); + if (!sites.every(bound)) return false; + const sourceKeys = new Set(userKeys(sourceId)); + if ([...sourceKeys].some((k) => maybeAbsent(sourceId, k))) return false; + return segments + .slice(1) + .every( + (s) => + s.kind === "key" || + (s.kind === "spread" && + (facts.get(s.shapeId)?.runtime !== true || + userKeys(s.shapeId).every((k) => sourceKeys.has(k)))), + ); + }; // ── witness sequences ─────────────────────────────────────────────── /** The key sequences one literal can create, or null when a spread * source's order is unknown. A key sits at its first occurrence that @@ -565,15 +831,19 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio segments: readonly KeyOrderSegment[], keySet: ReadonlySet, resolved: (id: string) => Resolved, + raw: (id: string) => Resolved, ): string[][] | null => { const occurrences: { key: string; definite: boolean }[] = []; + const ordered = orderedSpreads.has(segments); for (const s of segments) { if (s.kind === "opaque") return null; if (s.kind === "key") { if (keySet.has(s.name)) occurrences.push({ key: s.name, definite: s.definite }); continue; } - const source = resolved(s.shapeId); + // An ordered spread's values without a source enumerate in the + // source's table; the others carry their order over. + const source = ordered && s === segments[0] ? raw(s.shapeId) : resolved(s.shapeId); if (source === "unknown") return null; for (const k of source.order) { if (keySet.has(k)) @@ -649,8 +919,35 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio for (const c of f.copies) deps.add(c.source); return [...deps]; }; + /** `{ ...value, ...rest }` that starts with a spread of a value of the + * literal's own shape holding every key: it creates its keys in the + * value's order, and the later segments only overwrite them. With no + * checked conversion building the shape, every value enumerates in the + * one table this pass chooses, so the copy agrees with any choice and + * adds no edge. */ + const copiesItself = ( + id: string, + f: ShapeFacts, + table: readonly string[], + segments: readonly KeyOrderSegment[], + ): boolean => { + const first = segments[0]; + return ( + first !== undefined && + first.kind === "spread" && + first.shapeId === id && + !first.maybeAbsent && + (!f.runtime || orderedSpreads.has(segments)) && + segments.every((s) => s.kind !== "opaque") && + table.every((k) => !maybeAbsent(id, k)) + ); + }; /** One shape's order given its sources' current orders. */ - const resolveOne = (id: string, current: (source: string) => Resolved): Resolved => { + const resolveOne = ( + id: string, + current: (source: string) => Resolved, + raw: (source: string) => Resolved, + ): Resolved => { const shape = shapes.get(id); if (!inScope(shape)) return "unknown"; const table = tableOf(shape); @@ -666,7 +963,8 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio const edges: [string, string][] = []; if (f.pinned) chain(table, edges); for (const segments of f.literals) { - const sequences = literalSequences(segments, keySet, current); + if (copiesItself(id, f, table, segments)) continue; + const sequences = literalSequences(segments, keySet, current, raw); if (sequences === null) return "unknown"; for (const seq of sequences) chain(seq, edges); } @@ -725,32 +1023,57 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio const known = result.get(id); return known === undefined ? "unknown" : asSource(id, known); }; - for (const scc of sccs) { - const members = new Set(scc); - const guess = new Map(); - for (const id of scc) { - const shape = shapes.get(id); - guess.set(id, inScope(shape) ? { order: tableOf(shape) } : "unknown"); - } - const lookup = (id: string): Resolved => - members.has(id) ? asSource(id, guess.get(id)!) : external(id); - let stable = false; - for (let round = 0; round < scc.length + 3 && !stable; round++) { - stable = true; + const resolveAll = (): void => { + result.clear(); + late.clear(); + for (const scc of sccs) { + const members = new Set(scc); + const guess = new Map(); for (const id of scc) { - const next = resolveOne(id, lookup); - const prev = guess.get(id)!; - const same = - next === "unknown" - ? prev === "unknown" - : prev !== "unknown" && prev.order.join("\0") === next.order.join("\0"); - if (!same) { - stable = false; - guess.set(id, next); + const shape = shapes.get(id); + guess.set(id, inScope(shape) ? { order: tableOf(shape) } : "unknown"); + } + const lookup = (id: string): Resolved => + members.has(id) ? asSource(id, guess.get(id)!) : external(id); + const rawLookup = (id: string): Resolved => + members.has(id) ? guess.get(id)! : (result.get(id) ?? "unknown"); + let stable = false; + for (let round = 0; round < scc.length + 3 && !stable; round++) { + stable = true; + for (const id of scc) { + const next = resolveOne(id, lookup, rawLookup); + const prev = guess.get(id)!; + const same = + next === "unknown" + ? prev === "unknown" + : prev !== "unknown" && prev.order.join("\0") === next.order.join("\0"); + if (!same) { + stable = false; + guess.set(id, next); + } } } + for (const id of scc) result.set(id, stable ? guess.get(id)! : "unknown"); } - for (const id of scc) result.set(id, stable ? guess.get(id)! : "unknown"); + }; + // Ordered spreads join once their source resolves; each one can let its + // own shape resolve, which a later spread of it needs. + resolveAll(); + for (let grew = true; grew;) { + grew = false; + for (const [segments, sites] of leadingSpreads) { + const first = segments[0]; + if (first?.kind !== "spread" || orderedSpreads.has(segments)) continue; + // A self copy resolves with its own shape. + const target = sites[0]!.literal.type; + const source = result.get(first.shapeId) ?? "unknown"; + const self = target.kind === "record" && target.shapeId === first.shapeId; + if ((source === "unknown" && !self) || !ordersSpread(segments, sites)) continue; + orderedSpreads.add(segments); + if (target.kind === "record") factsOf(target.shapeId).runtime = true; + grew = true; + } + if (grew) resolveAll(); } // ── observation ───────────────────────────────────────────────────── @@ -759,6 +1082,19 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio for (const id of notes.observed) shapesIn({ kind: "record", shapeId: id }, observed, observedSeen); for (const root of observedRoots) shapesIn(root, observed, observedSeen); + // An ordered spread's copies of values without a source take the source's + // order from its table, which must then hold the resolved order. + const orderedTarget = (sites: readonly LeadingSpread[]): string | null => { + const target = sites[0]!.literal.type; + return target.kind === "record" && (result.get(target.shapeId) ?? "unknown") !== "unknown" + ? target.shapeId + : null; + }; + for (const [segments, sites] of leadingSpreads) { + const first = segments[0]; + if (orderedSpreads.has(segments) && first?.kind === "spread" && orderedTarget(sites) !== null) + observed.add(first.shapeId); + } // ── apply ─────────────────────────────────────────────────────────── for (const [id, resolved] of result) { @@ -771,6 +1107,46 @@ export function applyKeyOrderWitnesses(lowerer: Lowerer, fns: readonly IrFunctio notes.changed.add(id); } + // Ordered spreads carry their source's order over (see above). + let orderedResults = 0; + const rewritten = new Set(); + for (const [segments, sites] of leadingSpreads) { + const first = segments[0]; + if (!orderedSpreads.has(segments) || first?.kind !== "spread" || first.value === undefined) + continue; + // A shape that did not resolve keeps today's table for every value. + if (orderedTarget(sites) === null) continue; + for (const { wrapper, literal, fn } of sites) { + if (rewritten.has(wrapper)) continue; + rewritten.add(wrapper); + const loc = literal.loc; + const localId = `%orderedSpread.${orderedResults++}`; + fn.locals.push({ id: localId, name: "%orderedSpread", type: literal.type, mutable: false }); + const built: IrExpr = { kind: "varRef", localId, type: literal.type, loc }; + const source: IrExpr = { + kind: "varRef", + localId: first.value.localId, + type: { kind: "record", shapeId: first.shapeId }, + loc, + }; + wrapper.stmts.push( + { kind: "varDecl", localId, init: literal, loc }, + { + kind: "exprStmt", + expr: { + kind: "libCall", + fn: "record.sourceSpread", + args: [built, source], + type: VOID, + loc, + }, + loc, + }, + ); + wrapper.result = { kind: "varRef", localId, type: literal.type, loc }; + } + } + // ── per-instance orders ───────────────────────────────────────────── // A shape whose literals write its keys in more than one order has no // one table. Each value then records which of the orders it was written diff --git a/packages/compiler/src/frontend/lowering/lower-assert.ts b/packages/compiler/src/frontend/lowering/lower-assert.ts index bc51a9382..b996abed7 100644 --- a/packages/compiler/src/frontend/lowering/lower-assert.ts +++ b/packages/compiler/src/frontend/lowering/lower-assert.ts @@ -1355,7 +1355,7 @@ function lowerAssertMatch( lowerer.noLowering(`${surface} with ${expr.arguments.nodes.length} arguments`, expr); } const s = lowerer.lowerExprExpecting(expr.arguments.nodes[0]!, STRING); - const re = lowerer.lowerExpr(expr.arguments.nodes[1]!); + const re = lowerExpectedValue(lowerer, expr.arguments.nodes[1]!); if (re.type.kind !== "regex") { lowerer.noLowering( `${surface} with a non-regex pattern`, @@ -1420,6 +1420,18 @@ function inErrorHierarchy(lowerer: Lowerer, className: string): boolean { return false; } +/** An expected string or regex: a value JavaScript stores checked-dynamic + * (an object literal's field, an untyped binding) converts to the string + * or regex its checker type names, so the comparison takes that arm. */ +function lowerExpectedValue(lowerer: Lowerer, node: ts.Expression): IrExpr { + const value = lowerer.lowerExpr(node); + if (value.type.kind !== "dyn") return value; + const expected = lowerer.mapTypeOf(lowerer.typeOf(node)); + return expected?.kind === "string" || expected?.kind === "regex" + ? lowerer.coerceToExpected(value, expected) + : value; +} + /** Classify the expected argument: string → message, regex value → the * regex test over String(error), an error-class identifier → instanceof, * an object LITERAL over name/message/code (string or regex values) → @@ -1436,7 +1448,7 @@ function classifyThrowsExpected( return { form: "message", msg: m.msg, hasMsg: m.hasMsg }; } if (expectedT?.kind === "regex") { - return { form: "regex", value: lowerer.lowerExpr(node) }; + return { form: "regex", value: lowerExpectedValue(lowerer, node) }; } const sym = ts.isIdentifier(node) ? lowerer.resolveValueSymbol(node) : null; const info = @@ -1509,7 +1521,7 @@ function classifyThrowsExpected( ); } const valueNode = ts.isPropertyAssignment(named) ? named.initializer : named.name; - const value = lowerer.lowerExpr(valueNode); + const value = lowerExpectedValue(lowerer, valueNode); if (value.type.kind === "string") { byId.set(id, { id, kind: "str", value, enameBake: null }); } else if (value.type.kind === "regex") { diff --git a/packages/compiler/src/frontend/lowering/lower-calls.ts b/packages/compiler/src/frontend/lowering/lower-calls.ts index 672801e9e..14565aebc 100644 --- a/packages/compiler/src/frontend/lowering/lower-calls.ts +++ b/packages/compiler/src/frontend/lowering/lower-calls.ts @@ -119,7 +119,11 @@ import { } from "./containers/indexed-objects.js"; import { lowerTupleReadMethodCall } from "./containers/array-methods.js"; import { tryFuseIndexRead } from "./array-values.js"; -import { lowerBytesStaticCall } from "./containers/bytes.js"; +import { + lowerBufferConstruct, + lowerBytesPrototypeSetCall, + lowerBytesStaticCall, +} from "./containers/bytes.js"; import { lowerRegexMethodCall, lowerStringIndexCall, @@ -183,7 +187,7 @@ import { import { isSafeToDiscard } from "./expressions/evaluation-safety.js"; import { tryLowerExpression } from "./expressions/try-lower-expression.js"; import { lowerChildMethodValue, lowerChildPropertyCall } from "./handle-properties.js"; -import { classToNumberConversion } from "./class-to-primitive.js"; +import { classObjectTag, classToNumberConversion } from "./class-to-primitive.js"; import { hasRuntimeStatics } from "./class-runtime-statics.js"; import { structuralReadType } from "./class-unions.js"; import { @@ -245,6 +249,7 @@ import { import { fenceSymbolFieldCopy } from "./symbol-fields.js"; import { lowerClassDataDescriptor, lowerClassDescriptorRead } from "./class-descriptors.js"; import { lowerClassFieldDefine, lowerClassIntegrity } from "./class-attributes.js"; +import { freezableRecordType, lowerNativeFreeze } from "./frozen-records.js"; import { classStaticDataFor } from "./class-static-data.js"; import { lowerUrlFactory } from "./lower-url.js"; import { nullishValueUnitOf } from "./binding-analysis.js"; @@ -255,6 +260,7 @@ import { completeArgs, } from "./call-arguments.js"; import { isThisParameter, type ParamShape } from "./call-signatures.js"; +import { dynamicOverloadChoice } from "./overload-choice.js"; import { lowerStringIteratorValue } from "./iterator-adapters.js"; import { checkedArmExtraction } from "./coercions/unions.js"; import { maybePromiseReturnStmt, promiseArmedUnionOf } from "./maybe-promise.js"; @@ -362,6 +368,13 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr { const codeGeneration = codeGenerationCalleeOf(lowerer, expr.expression); if (codeGeneration !== null) return lowerCodeGenerationCall(lowerer, expr, codeGeneration); } + // The deprecated Buffer(...) call (containers/bytes.ts). The checker + // types it `any`, so the bytes cross as a checked-dynamic value, which + // keeps the Buffer brand for inspection. + { + const buffer = lowerBufferConstruct(lowerer, expr); + if (buffer) return { kind: "dynFrom", value: buffer, type: DYN, loc }; + } // A package shim's runtime intrinsic (a bodiless `declare function` // named scriptcUnixDgram* in a shipped shim): a direct runtime call. if (ts.isIdentifier(expr.expression) && isShimIntrinsicName(expr.expression.text)) { @@ -1578,6 +1591,7 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr { lowerer.lowerTestMethodCall(expr, expr.expression) ?? lowerer.lowerTimeoutMethodCall(expr, expr.expression) ?? lowerArrayPrototypeBorrowCall(lowerer, expr, expr.expression) ?? + lowerBytesPrototypeSetCall(lowerer, expr, expr.expression) ?? lowerObjectOwnPrototypeCall(lowerer, expr, expr.expression) ?? lowerObjectPrototypeCall(lowerer, expr, expr.expression) ?? lowerStringPrototypeCall(lowerer, expr, expr.expression) ?? @@ -3102,6 +3116,10 @@ function lowerObjectPrototypeCall( const immediate = immediateObjectTagReceiver(lowerer, receiverNode); const receiver = immediate?.effect ?? lowerer.lowerExpr(receiverNode); const tag = immediate?.tag ?? objectToStringTag(lowerer, receiver.type); + if (tag === null && immediate === null && call.arguments.nodes.length === 1) { + const classTag = classObjectTag(lowerer, receiver, receiverNode); + if (classTag) return classTag; + } // A generator object answers its tag through its checked capsule. if (tag === null && receiver.type.kind !== "dyn" && receiver.type.kind !== "generator") { lowerer.noLowering( @@ -3755,6 +3773,14 @@ function lowerDynReceiverMethodCall( // 'unknown' slot does. if (checkerUntyped && DYN_HANDLE_KINDS.has(recv.type.kind)) recv = lowerer.coerceToExpected(recv, DYN); + // A typed array whose checker type collapsed to `any` (a JS binding + // referenced in its own initializer's inference — hono's etag digest + // `const nextChunk = new Uint8Array(new ArrayBuffer(f(chunk)))` beside + // `chunk = nextChunk`) still lowered to its native bytes. The typed + // lowerings key on the checker type and decline it, so it boxes by + // reference (the box shares the native storage, so writes through the + // call stay visible) and the call dispatches on the runtime kind. + if (checkerUntyped && recv.type.kind === "bytes") recv = lowerer.coerceToExpected(recv, DYN); // The last-resort retry (boxTypedTemporary — every other lowering // already declined): a checker-`any` receiver whose value is a FRESH // typed result of an earlier step in the chain (`out.split("\n")` @@ -5485,6 +5511,9 @@ function reconcileOverloadReturn( expr: ts.CallExpression | ts.TaggedTemplateExpression, call: IrExpr, ): IrExpr { + // The checker's overload is a guess for a checked-dynamic argument + // another overload accepts: the implementation's value stands. + if (ts.isCallExpression(expr) && dynamicOverloadChoice(lowerer, expr)) return call; const rsig = lowerer.checker.getResolvedSignature(expr); const rdecl = rsig ? rsig.declaration : undefined; // Nominal covariant overrides share the base's return ABI, but retain @@ -7271,7 +7300,7 @@ export function recordKeysArrayCall( * (a key is own exactly when Object.keys would list it — the two share * the test), everything else true. Unmatched keys probe the overflow * map when present, so explicit undefined values remain own keys. */ -function recordHasOwnHelper(lowerer: Lowerer, shapeId: string, loc: SrcLoc): string { +export function recordHasOwnHelper(lowerer: Lowerer, shapeId: string, loc: SrcLoc): string { const key = `obj.hasOwn:${shapeId}`; const existing = lowerer.arrHofHelpers.get(key); if (existing) return existing; @@ -8486,6 +8515,22 @@ function lowerObjectStaticCall( locOf(call), ); if (fnIntegrity) return fnIntegrity; + // A Map or Set has no own properties: freeze, seal and preventExtensions + // all only make it not extensible. + if (value.type.kind === "map" || value.type.kind === "set") { + if (member === "preventExtensions" || member === "seal") + return lowerNativeFreeze(lowerer, "collection.preventExtensions", value, locOf(call)); + const extensible: IrExpr = { + kind: "libCall", + fn: "collection.isExtensible", + args: [value], + type: BOOL, + loc: locOf(call), + }; + return member === "isExtensible" + ? extensible + : { kind: "unary", op: "!", operand: extensible, type: BOOL, loc: locOf(call) }; + } if (member === "preventExtensions" || member === "seal") { const integrity = lowerClassIntegrity(lowerer, call, member, value); if (integrity) return integrity; @@ -8520,10 +8565,44 @@ function lowerObjectStaticCall( // the argument — no alias exists, so the frozen bit is unobservable // (writes through the Readonly result are compile errors, and no // other reference can write). Primitives pass through per ES2015. - // Aliased objects keep a fence: a later write through the original - // reference would need the runtime frozen bit (strict mode throws). + // Aliased records get the frozen bit of their rc word (frozen-records.ts); + // other aliased objects keep a fence: a later write through the original + // reference would need a runtime frozen bit (strict mode throws). if (member === "isFrozen" && call.arguments.nodes.length === 1) { - const value = lowerer.lowerExprExpecting(call.arguments.nodes[0]!, DYN); + const argNode = call.arguments.nodes[0]!; + const probedKind = tryLowerExpression(lowerer, argNode)?.type.kind; + if ( + probedKind === "record" || + probedKind === "array" || + probedKind === "map" || + probedKind === "set" + ) { + const native = lowerer.lowerExpr(argNode); + const loc = locOf(call); + if (native.type.kind === "record" || native.type.kind === "array") + return { + kind: "libCall", + fn: native.type.kind === "record" ? "record.isFrozen" : "array.isFrozen", + args: [native], + type: BOOL, + loc, + }; + if (native.type.kind === "map" || native.type.kind === "set") + return { + kind: "unary", + op: "!", + operand: { + kind: "libCall", + fn: "collection.isExtensible", + args: [native], + type: BOOL, + loc, + }, + type: BOOL, + loc, + }; + } + const value = lowerer.lowerExprExpecting(argNode, DYN); return { kind: "libCall", fn: "dyn.isFrozen", args: [value], type: BOOL, loc: locOf(call) }; } if (member === "freeze") { @@ -8573,6 +8652,12 @@ function lowerObjectStaticCall( if (fnIntegrity) return fnIntegrity; const integrity = lowerClassIntegrity(lowerer, call, "freeze", value); if (integrity) return integrity; + if (freezableRecordType(lowerer, value.type)) + return lowerNativeFreeze(lowerer, "record.freeze", value, locOf(call)); + if (value.type.kind === "array") + return lowerNativeFreeze(lowerer, "array.freeze", value, locOf(call)); + if (value.type.kind === "map" || value.type.kind === "set") + return lowerNativeFreeze(lowerer, "collection.preventExtensions", value, locOf(call)); lowerer.noLowering( "Object.freeze of a possibly-aliased value", call, @@ -8712,11 +8797,13 @@ function lowerObjectStaticCall( const moduleSymbol = source ? lowerer.checker.getSymbolAtLocation(source) : undefined; const names: string[] = []; moduleSymbol?.getExports().forEach((symbol, key) => { + // Internal names (`__export`) start with `__`; an exported name that + // does is keyed with an extra `_` (escapedSymbolName). const name = String(key); - if (name.startsWith("__") || name === "export=") return; + if ((name.startsWith("__") && !name.startsWith("___")) || name === "export=") return; const target = symbol.flags & ts.SymbolFlags.Alias ? lowerer.checker.getAliasedSymbol(symbol) : symbol; - if (target.flags & ts.SymbolFlags.Value) names.push(name); + if (target.flags & ts.SymbolFlags.Value) names.push(ts.symbolName(symbol)); }); names.sort(); const loc = locOf(call); diff --git a/packages/compiler/src/frontend/lowering/lower-classes.ts b/packages/compiler/src/frontend/lowering/lower-classes.ts index 42c72c653..b829b6ddc 100644 --- a/packages/compiler/src/frontend/lowering/lower-classes.ts +++ b/packages/compiler/src/frontend/lowering/lower-classes.ts @@ -1,3 +1,4 @@ +import { lowerAsyncLocalStorageOptions } from "./builtins/async-context.js"; import { DATE_TIME_FIELD, dateRootOf, @@ -46,7 +47,6 @@ import { DATE_T, DYN, F64, - bytesOf, type IrClassDef, type IrExpr, type IrFunction, @@ -115,8 +115,7 @@ import { isCjsRequireGlobal } from "./lower-node-module.js"; import { lowerArrayConstructor } from "./containers/array-construction.js"; import { lowerMapSeedNew, lowerSetSeedNew } from "./containers/collection-methods.js"; import { lowerObjectConstructor } from "./containers/indexed-objects.js"; -import { knownBufEncoding, lowerNodeModulesBufferConstruct } from "./containers/bytes.js"; -import { isSafeToRepeat } from "./expressions/evaluation-safety.js"; +import { knownBufEncoding, lowerBufferConstruct } from "./containers/bytes.js"; import { lowerSearchParamsNew } from "./builtins/url.js"; import { lowerTextCodecNew } from "./builtins/text-codecs.js"; import { @@ -177,7 +176,12 @@ import { hasLexicalPrivateReference, hasStaticThis, rejectStaticThis } from "./s import { lowerUrlNew } from "./lower-url.js"; import { isNativeProxyInitializer, lowerNativeProxy } from "./expressions/native-proxy.js"; import { classStaticDataFor } from "./class-static-data.js"; -import { construction, constructionPrefix } from "./js-class-fields.js"; +import { + accessorInterceptsBase, + accessorNames, + construction, + constructionPrefix, +} from "./js-class-fields.js"; import { lowerObjectFactoryNew } from "./object-factory-new.js"; import { tryLowerExpression } from "./expressions/try-lower-expression.js"; import { lowerInstanceConstructorNew } from "./class-instance-constructor.js"; @@ -1420,7 +1424,7 @@ function undefArmedFieldType(lowerer: Lowerer, p: ts.Symbol): IrType | null { const arms = mapped.kind === "union" ? (lowerer.unions.get(mapped.unionId)?.arms ?? []) : [mapped]; for (const a of arms) { - if (a.kind === "map" || a.kind === "regex" || a.kind === "date" || a.kind === "generator") { + if (a.kind === "map" || a.kind === "regex" || a.kind === "generator") { return null; } byKey.set(typeKey(a), a); @@ -3977,7 +3981,6 @@ export function collectClassShapeInner( declared.kind !== "union" && declared.kind !== "dyn" && declared.kind !== "map" && - declared.kind !== "date" && declared.kind !== "generator" && declared.kind !== "void" && declared.kind !== "caught"; @@ -4114,7 +4117,7 @@ export function collectClassShapeInner( // A subclass field created before anything can observe the instance // keeps its type: super() cannot run code that reads it earlier. const baseConstruction = - base && ![...methods.keys()].some((m) => m.startsWith("get:") || m.startsWith("set:")) + base && !accessorInterceptsBase(accessorNames(methods.keys()), base) ? construction(base) : null; const sealedPrefix = baseConstruction @@ -4147,7 +4150,7 @@ export function collectClassShapeInner( // An own JS callable shadows its prototype declaration. // Keep the method in the prototype and store the assigned // value in the instance property bag. - lowerer.prototypeMethodAccesses.set(name, assign); + lowerer.noteMethodAccess(name, assign); continue; } const sym = lowerer.checker.getSymbolAtLocation(assign); @@ -4229,7 +4232,7 @@ export function collectClassShapeInner( } if (!key && (fields.has(fieldName) || hasAccessor(fieldName))) continue; if (!key && (methods.has(fieldName) || lowerer.findMethodOn(base, fieldName))) { - lowerer.prototypeMethodAccesses.set(fieldName, lhs); + lowerer.noteMethodAccess(fieldName, lhs); continue; } // A key already declared (own or inherited) makes later @@ -4403,6 +4406,14 @@ export function collectClassShapeInner( // INSTANTIATION prints its family's name — JS has one `Box`. jsName: jsNameOverride ?? decl.name?.text ?? "", ...(base ? { base: base.def.name } : {}), + ...(decl.members.nodes.some( + (member) => + ts.isPropertyDeclaration(member) && + !ts.isPrivateIdentifier(member.name) && + member.modifiers?.nodes.some((m) => m.kind === ts.SyntaxKind.StaticKeyword), + ) + ? { ownStaticFields: true as const } + : {}), jsLength: ctor?.parameters?.nodes.findIndex( (parameter) => parameter.initializer || parameter.dotDotDotToken, @@ -8150,7 +8161,10 @@ export function classMemberNameOf(lowerer: Lowerer, name: ts.PropertyName): stri member === "asyncIterator" || member === "dispose" || member === "asyncDispose" || - member === "toPrimitive" + member === "toPrimitive" || + // A toStringTag field keeps its symbol field name; its accessor and + // method forms take the method slot name. + (member === "toStringTag" && !ts.isPropertyDeclaration(name.parent)) ) { return `sym:${member}`; } @@ -9776,9 +9790,9 @@ function isCollectionOrErrorClass(ref: BuiltinClassRef | null): boolean { export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { const loc = locOf(expr); - // A dependency's deprecated `new Buffer(...)` fallback (containers/bytes.ts). + // The deprecated `new Buffer(...)` (containers/bytes.ts). { - const buffer = lowerNodeModulesBufferConstruct(lowerer, expr); + const buffer = lowerBufferConstruct(lowerer, expr); if (buffer) return buffer; } // new Function(...): Node's EvalError under @@ -10418,8 +10432,10 @@ export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { if (arg.type.kind === "string") { return { kind: "libCall", fn: "date.newString", args: [arg], type: DATE_T, loc }; } + // new Date(date): a new Date object with the same time value. if (arg.type.kind === "date") { - return arg; + const time: IrExpr = { kind: "libCall", fn: "date.getTime", args: [arg], type: F64, loc }; + return { kind: "libCall", fn: "date.newMs", args: [time], type: DATE_T, loc }; } lowerer.noLowering( `new Date of '${lowerer.fmt(arg.type)}' values`, @@ -10622,7 +10638,7 @@ export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { expr, `Map values of type '${lowerer.checker.typeToString(targs[1])}' ` + `(Map values must be number, string, boolean, records, class instances, functions, ` + - `typed arrays, arrays, promises, Maps, Sets, or supported unions and checked values)`, + `typed arrays, arrays, promises, Dates, Maps, Sets, or supported unions and checked values)`, ); } lowerer.badType(expr, tsType); @@ -10766,21 +10782,23 @@ export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { lowerer.badType(expr, tsType); } // `new AsyncLocalStorage()` (node:async_hooks): a fresh store id — - // an f64 handle (type-mapper.ts), the Channel story. Construction options - // ({ defaultValue, name }) have no lowering yet. + // an f64 handle (type-mapper.ts), the Channel story. The options form + // ({ defaultValue, name }) passes the options object as a dyn value. if ( symbol !== null && ts.symbolName(symbol) === "AsyncLocalStorage" && lowerer.isStdlibSymbol(symbol) ) { - if ((expr.arguments?.nodes.length ?? 0) > 0) { - lowerer.noLowering( - "new AsyncLocalStorage(options)", - expr, - "the zero-argument constructor is the supported form (defaultValue/name options have no lowering yet)", - ); - } - return { kind: "libCall", fn: "als.new", args: [], type: F64, loc }; + const options = expr.arguments?.nodes[0]; + if (options === undefined) + return { kind: "libCall", fn: "als.new", args: [], type: F64, loc }; + return { + kind: "libCall", + fn: "als.newOptions", + args: [lowerAsyncLocalStorageOptions(lowerer, options)], + type: F64, + loc, + }; } // `new Promise((resolve) => ...)`: the ambient Promise constructor. if (symbol !== null && ts.symbolName(symbol) === "Promise" && lowerer.isStdlibSymbol(symbol)) { @@ -10855,43 +10873,6 @@ export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { // families carry pointed hints: each states WHY no honest static // lowering exists (or what to use instead). if (lowerer.isStdlibSymbol(symbol ?? undefined)) { - // The deprecated `new Buffer(string, encoding?)` ctor's string arm - // with a NON-STRING first argument and a string second: Node - // throws ERR_INVALID_ARG_TYPE synchronously (and DEP0005 never - // fires on this throwing path, so the compiled silence matches). - // Every other new Buffer form keeps the fence below — the - // constructing forms would owe the deprecation warning. - if ( - expr.expression.text === "Buffer" && - expr.arguments?.nodes.length === 2 && - !expr.arguments.nodes.some(ts.isSpreadElement) && - lowerer.mapTypeOf(lowerer.typeOf(expr.arguments.nodes[0]!))?.kind !== "string" && - lowerer.mapTypeOf(lowerer.typeOf(expr.arguments.nodes[1]!))?.kind === "string" - ) { - const first = lowerer.lowerExpr(expr.arguments.nodes[0]!); - if ( - first.kind === "unitLit" || - first.type.kind === "dyn" || - lowerer.dynConvertible(first.type) - ) { - const got: IrExpr = - first.type.kind === "dyn" ? first : { kind: "dynFrom", value: first, type: DYN, loc }; - // The encoding argument still evaluates in Node before the - // throw only via the ctor body's later reads — it does NOT - // observe it before throwing, so dropping it is exact for - // effect-free operands; effectful ones keep the fence. - const enc = lowerer.lowerExpr(expr.arguments.nodes[1]!); - if (enc.kind === "strLit" || isSafeToRepeat(enc)) { - return { - kind: "libCall", - fn: "buffer.newStringFail", - args: [got], - type: bytesOf("u8"), - loc, - }; - } - } - } const ctorHints: Record = { RegExp: "use a regex literal (/pattern/flags) — constructed regexes have no lowering", String: diff --git a/packages/compiler/src/frontend/lowering/lower-console.ts b/packages/compiler/src/frontend/lowering/lower-console.ts index eec308342..b45218bd0 100644 --- a/packages/compiler/src/frontend/lowering/lower-console.ts +++ b/packages/compiler/src/frontend/lowering/lower-console.ts @@ -249,7 +249,8 @@ export function lowerConsoleArg( // inspect at the rest-args depth 2 (formatWithOptions) — scalar // kinds byte-exactly, boxed functions as [Function: name] / // [Function (anonymous)], composites through the dyn walk - // (insp.dyn). Never throws — Node's console.log never does. + // (insp.dyn). A value the inspector does not render throws the + // 'not supported yet' Error before anything is written. if (lowered.type.kind === "dyn") { return { kind: "libCall", diff --git a/packages/compiler/src/frontend/lowering/lower-exprs.ts b/packages/compiler/src/frontend/lowering/lower-exprs.ts index 2bde5f2aa..033f482d1 100644 --- a/packages/compiler/src/frontend/lowering/lower-exprs.ts +++ b/packages/compiler/src/frontend/lowering/lower-exprs.ts @@ -72,7 +72,7 @@ import { import { nsMemberIdentOf } from "./lower-namespaces.js"; import { bindingNeverReassigned } from "./binding-analysis.js"; import { implicitMonoFile } from "./generic-functions.js"; -import { lowerTaggedTemplate } from "./lower-calls.js"; +import { lowerPrimitiveUnionNumber, lowerTaggedTemplate } from "./lower-calls.js"; import { unitOnlyUnion } from "../type-mapper.js"; import { lowerYield } from "./lower-generators.js"; import { unionWideningTags } from "../../ir/analysis.js"; @@ -426,6 +426,12 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr { return inner; const use = runtimeOptionalUseOf(expr); if (runtimeOptionalAssertionErases(lowerer, expr, inner, use)) return inner; + // A destination that holds null or undefined keeps the value, as `!` + // does in JavaScript (`return cache.get(key)!` from a function that + // returns `T | null`); the destination's own coercion re-tags it. + if (assertionDestinationHoldsNullish(lowerer, expr, inner.type)) return inner; + const operand = assertionOperand(lowerer, expr, inner, target ?? null, loc); + if (operand) return operand; if (target && target.kind !== "union" && !typeEquals(target, inner.type)) { const helper = lowerer.narrowedArmHelper(inner.type.unionId, target, loc); if (helper) { @@ -933,6 +939,229 @@ export function assertedElementKeepsUndefined( ); } +/** Whether `node!` is a returned value, an arrow function's body or a call + * argument whose destination type (the return or parameter type) is `any` + * or `unknown` (as for `String(x!)` and `Number(x!)`) or holds null or + * undefined that the union `inner` holds and the assertion would remove. + * Bindings and assignments are left out: the checker narrows them to the + * asserted type for later reads. */ +function assertionDestinationHoldsNullish( + lowerer: Lowerer, + node: ts.NonNullExpression, + inner: IrType & { kind: "union" }, +): boolean { + const holdsNull = lowerer.armTag(inner.unionId, NULL_T) >= 0; + const holdsUndefined = lowerer.armTag(inner.unionId, UNDEFINED_T) >= 0; + if (!holdsNull && !holdsUndefined) return false; + let outer: ts.Expression = node; + while (ts.isParenthesizedExpression(outer.parent)) outer = outer.parent; + const parent = outer.parent; + const destination = + (ts.isReturnStatement(parent) && parent.expression === outer) || + (ts.isArrowFunction(parent) && parent.body === outer) || + ((ts.isCallExpression(parent) || ts.isNewExpression(parent)) && + parent.arguments?.nodes.includes(outer) === true); + if (!destination) return false; + const contextual = lowerer.checker.getContextualType(outer); + if (!contextual) return false; + const nullish = + ts.TypeFlags.Any | + ts.TypeFlags.Unknown | + (holdsNull ? ts.TypeFlags.Null : 0) | + (holdsUndefined && !optionalStdlibParameter(lowerer, parent, outer) + ? ts.TypeFlags.Undefined + : 0); + if ((contextual.flags & ts.TypeFlags.Union) === 0) return (contextual.flags & nullish) !== 0; + return constituentTypes(contextual).some((type) => (type.flags & nullish) !== 0); +} + +/** Whether `argument` of `call` binds an optional parameter of a standard + * library declaration (`decoder.decode(input?)`). The undefined such a + * parameter's type holds comes from its optionality, and the builtin + * lowerings read the argument as the declared type, so `x!` keeps the + * checked narrowing there. */ +function optionalStdlibParameter( + lowerer: Lowerer, + call: ts.Node, + argument: ts.Expression, +): boolean { + if (!ts.isCallExpression(call) && !ts.isNewExpression(call)) return false; + const declaration = lowerer.checker.getResolvedSignature(call)?.declaration; + if (declaration === undefined || !lowerer.isStdlibFile(ts.getSourceFileOfNode(declaration))) + return false; + const index = call.arguments?.nodes.indexOf(argument) ?? -1; + const parameter = declaration.parameters?.nodes[index]; + return ( + parameter !== undefined && + parameter.dotDotDotToken === undefined && + (parameter.questionToken !== undefined || parameter.initializer !== undefined) + ); +} + +/** `x!` as the operand of an operator that converts its operands: an + * arithmetic, bitwise, relational or equality operator, `+`, a compound + * assignment, a unary `-`, `+`, `~` or `!`, a template span, or an argument + * of a Math function. `!` performs no runtime check, + * so a value that still holds undefined or null reaches the operator, which + * converts it as JavaScript does: NaN or 0 in arithmetic, false in a + * comparison, "undefined" or "null" in a string. Returns the operand to + * use, or null where the assertion narrows as usual. A numeric use gets the + * value converted with ToNumber; other uses get the union itself. */ +function assertionOperand( + lowerer: Lowerer, + expr: ts.NonNullExpression, + inner: IrExpr, + target: IrType | null, + loc: SrcLoc, +): IrExpr | null { + if (inner.type.kind !== "union" || target === null || target.kind === "union") return null; + const arms = lowerer.unions.get(inner.type.unionId)?.arms; + if ( + !arms?.some(isUnitType) || + !arms.every( + (arm) => + arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" || isUnitType(arm), + ) + ) + return null; + // Array element reads keep their own handling of a missing element. A + // typed-array read is optional here only where its use tells undefined + // from NaN (assertedReadObservesUndefined). + let operand: ts.Expression = expr.expression; + while (ts.isParenthesizedExpression(operand)) operand = operand.expression; + if (ts.isElementAccessExpression(operand)) { + const kind = lowerer.mapTypeOf(lowerer.typeOf(operand.expression))?.kind; + if (kind === "array") return null; + } + const use = convertingUseOf(lowerer, expr); + if (use === null) return null; + if (use === "value") return inner; + return lowerPrimitiveUnionNumber(lowerer, inner, expr, loc); +} + +/** How the converting use of `x!` (see assertionOperand) reads it: + * "number" where the use applies ToNumber, "value" where the consumer + * lowers the union itself (strings, equality, truthiness), null for other + * uses. */ +function convertingUseOf(lowerer: Lowerer, expr: ts.NonNullExpression): "number" | "value" | null { + let outer: ts.Expression = expr; + while (ts.isParenthesizedExpression(outer.parent)) outer = outer.parent; + const parent = outer.parent; + if (ts.isTemplateSpan(parent)) return parent.expression === outer ? "value" : null; + if (ts.isTypeOfExpression(parent)) return "value"; + if (ts.isPrefixUnaryExpression(parent)) { + if (parent.operator === ts.SyntaxKind.ExclamationToken) return "value"; + return parent.operator === ts.SyntaxKind.MinusToken || + parent.operator === ts.SyntaxKind.PlusToken || + parent.operator === ts.SyntaxKind.TildeToken + ? "number" + : null; + } + if (ts.isCallExpression(parent) && parent.arguments.nodes.includes(outer)) { + const callee = parent.expression; + return ts.isPropertyAccessExpression(callee) && + stdlibGlobalNameOf(lowerer, callee.expression) === "Math" + ? "number" + : null; + } + if (!ts.isBinaryExpression(parent)) return null; + const op = parent.operatorToken.kind; + if (EQUALITY_OPERATORS.has(op)) return "value"; + const other = parent.left === outer ? parent.right : parent.left; + const isString = (node: ts.Expression): boolean => + (lowerer.typeOf(node).flags & ts.TypeFlags.StringLike) !== 0; + // With a string on either side, `+` concatenates and a comparison + // compares strings. `undefined + 1` and `undefined + undefined` are NaN, + // which a string-typed result cannot hold, so `+` takes the union only + // with a string partner that is not itself asserted. A partner that may + // hold a string or a number at run time (`any`, `unknown`) also gets the + // union, and `+` decides between concatenation and addition when it runs. + if (op === ts.SyntaxKind.PlusToken || op === ts.SyntaxKind.PlusEqualsToken) { + if (op === ts.SyntaxKind.PlusEqualsToken && parent.right !== outer) return null; + let partner = other; + while (ts.isParenthesizedExpression(partner)) partner = partner.expression; + if (isString(other)) return ts.isNonNullExpression(partner) ? null : "value"; + if (!numericPartner(lowerer.typeOf(other))) return "value"; + return isString(expr) ? null : "number"; + } + if (RELATIONAL_OPERATORS.has(op)) return isString(other) || isString(expr) ? "value" : "number"; + if (NUMERIC_OPERATORS.has(op)) return "number"; + return NUMERIC_COMPOUNDS.has(op) && parent.right === outer ? "number" : null; +} + +/** A `+` partner whose every value is a number, a boolean, null or + * undefined: `+` adds with it. */ +function numericPartner(type: ts.Type): boolean { + const numeric = + ts.TypeFlags.NumberLike | + ts.TypeFlags.BooleanLike | + ts.TypeFlags.Null | + ts.TypeFlags.Undefined | + ts.TypeFlags.Void; + return (type.flags & ts.TypeFlags.Union) !== 0 + ? constituentTypes(type).every((part) => (part.flags & numeric) !== 0) + : (type.flags & numeric) !== 0; +} + +const EQUALITY_OPERATORS = new Set([ + ts.SyntaxKind.EqualsEqualsEqualsToken, + ts.SyntaxKind.ExclamationEqualsEqualsToken, + ts.SyntaxKind.EqualsEqualsToken, + ts.SyntaxKind.ExclamationEqualsToken, +]); + +const RELATIONAL_OPERATORS = new Set([ + ts.SyntaxKind.LessThanToken, + ts.SyntaxKind.GreaterThanToken, + ts.SyntaxKind.LessThanEqualsToken, + ts.SyntaxKind.GreaterThanEqualsToken, +]); + +const NUMERIC_OPERATORS = new Set([ + ts.SyntaxKind.MinusToken, + ts.SyntaxKind.AsteriskToken, + ts.SyntaxKind.SlashToken, + ts.SyntaxKind.PercentToken, + ts.SyntaxKind.AsteriskAsteriskToken, + ts.SyntaxKind.AmpersandToken, + ts.SyntaxKind.BarToken, + ts.SyntaxKind.CaretToken, + ts.SyntaxKind.LessThanLessThanToken, + ts.SyntaxKind.GreaterThanGreaterThanToken, + ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, +]); + +const NUMERIC_COMPOUNDS = new Set([ + ts.SyntaxKind.MinusEqualsToken, + ts.SyntaxKind.AsteriskEqualsToken, + ts.SyntaxKind.SlashEqualsToken, + ts.SyntaxKind.PercentEqualsToken, + ts.SyntaxKind.AsteriskAsteriskEqualsToken, + ts.SyntaxKind.AmpersandEqualsToken, + ts.SyntaxKind.BarEqualsToken, + ts.SyntaxKind.CaretEqualsToken, + ts.SyntaxKind.LessThanLessThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, +]); + +/** Whether the use of `t[i]!` tells undefined from NaN: a converting use + * that takes the value itself (see convertingUseOf), or a destination that + * holds a missing element (see assertedElementKeepsUndefined) other than a + * return, whose slot has the function's number type. */ +export function assertedReadObservesUndefined( + lowerer: Lowerer, + node: ts.NonNullExpression, +): boolean { + if (convertingUseOf(lowerer, node) === "value") return true; + let outer: ts.Expression = node; + while (ts.isParenthesizedExpression(outer.parent)) outer = outer.parent; + const parent = outer.parent; + if (ts.isReturnStatement(parent) || (ts.isArrowFunction(parent) && parent.body === outer)) + return false; + return assertedElementKeepsUndefined(lowerer, node); +} + export function runtimeOptionalAssertionErases( lowerer: Lowerer, expr: ts.Expression, @@ -1087,6 +1316,10 @@ export function lowerCondition(lowerer: Lowerer, expr: ts.Expression): IrExpr { lowerer.isStdlibGlobal(e.expression, "Object") ) return { kind: "boolLit", value: true, type: BOOL, loc: locOf(e) }; + // `if (runtimeBuffer)` over an alias of the Buffer global (a + // capability probe): Node always defines Buffer. + if (lowerer.isStdlibGlobal(e, "Buffer")) + return { kind: "boolLit", value: true, type: BOOL, loc: locOf(e) }; if (ts.isPropertyAccessExpression(e)) { const presence = lowerPromiseThenPresence(lowerer, e, { kind: "boolLit", diff --git a/packages/compiler/src/frontend/lowering/lower-generators.ts b/packages/compiler/src/frontend/lowering/lower-generators.ts index 80463fa6c..f837cc0af 100644 --- a/packages/compiler/src/frontend/lowering/lower-generators.ts +++ b/packages/compiler/src/frontend/lowering/lower-generators.ts @@ -333,7 +333,7 @@ export function stepDrivable(genT: GenType): boolean { /** Whether a yield channel's values have an OUT-slot form `value` reads. */ function hasOutSlot(yt: IrType): boolean { - return yt.kind === "f64" || yt.kind === "date" || yt.kind === "bool" || isRefCounted(yt); + return yt.kind === "f64" || yt.kind === "bool" || isRefCounted(yt); } /** `step` and `value` resumes of a generator-typed borrowed read. */ diff --git a/packages/compiler/src/frontend/lowering/lower-inspect.ts b/packages/compiler/src/frontend/lowering/lower-inspect.ts index 5266d5c4e..48bb0f9e6 100644 --- a/packages/compiler/src/frontend/lowering/lower-inspect.ts +++ b/packages/compiler/src/frontend/lowering/lower-inspect.ts @@ -14,10 +14,11 @@ import { InternalCompilerError } from "../../errors.js"; import * as ts from "../ts7/adapter.js"; import { unsignedHex } from "../../format-integer.js"; -import type { Lowerer } from "./lowerer.js"; +import { jsFuncNameOf, type Lowerer } from "./lowerer.js"; import { isJsSourceFile } from "../program.js"; import { BOOL, + CAUGHT, DYN, F64, type IrExpr, @@ -44,9 +45,9 @@ import { shapeKeyOrders, typeKey, } from "../../ir/ir.js"; -import type { ClassInfo } from "./lower-classes.js"; +import { accessorCall, findMethodOn, type ClassInfo } from "./lower-classes.js"; import { isSafeToDiscard, isSafeToRepeat } from "./expressions/evaluation-safety.js"; -import { symbolFieldDisplayName } from "./symbol-fields.js"; +import { symbolFieldDisplayName, TO_STRING_TAG_FIELD } from "./symbol-fields.js"; import { eventTargetRootOf } from "./lower-event-target.js"; import { dateRootOf } from "./builtins/date-classes.js"; import { lowerSystemErrorMap } from "./lower-system-errors.js"; @@ -736,11 +737,7 @@ function inspectSupport( case "array": return inspectSupport(lowerer, t.elem, visiting, out); case "bytes": - // A Uint8Array and a Buffer share the u8 representation and print - // differently; the other element kinds are never Buffers. - return t.elem === "u8" - ? "Uint8Array and Buffer values inside composites have no inspect lowering yet (Buffer's form vs Uint8Array's is not recorded in the static type here)" - : null; + return null; case "record": { if (visiting.has(t.shapeId)) { out.recursive = true; @@ -861,7 +858,10 @@ function inspectSupport( } // Generator objects print through their checked capsule: Node's // `Object [Generator] {}`, or the tag of the iterator they implement. + // URLs and URLSearchParams print through their handles. case "generator": + case "url": + case "searchParams": return null; default: return `'${lowerer.fmt(t)}' values have no inspect lowering`; @@ -902,6 +902,117 @@ export function inspectExpr( recurse: IrExpr, depth: IrExpr, loc: SrcLoc, +): IrExpr { + const render = inspectExprBody(lowerer, t, value, recurse, depth, loc); + // A top-level render through emitted helpers that can reach a refusal + // (the 'not supported yet' Error of insp.dyn) unwinds the inspector to + // its state before the render on every exit: the Error propagates out of + // the helpers before they close their frames. + if ( + render.kind !== "call" || + recurse.kind !== "numLit" || + recurse.value !== 0 || + !inspectMayRefuse(lowerer, t) + ) + return render; + return withInspectUnwind(lowerer, render, t, loc); +} + +/** A top-level helper render (`call helper(v, r, d)`) through a lifted + * wrapper that takes insp.mark first and runs insp.unwind however the + * render ends: `try { return helper(v, r, d); } finally { insp.unwind(); }`. + * A seqExpr cannot hold the try. */ +function withInspectUnwind( + lowerer: Lowerer, + render: IrExpr & { kind: "call" }, + t: IrType, + loc: SrcLoc, +): IrExpr { + const key = `unwind:${render.callee}`; + let name = lowerer.inspectHelpers.get(key); + if (name === undefined) { + name = keyedHelperName(lowerer, "%util.inspUnwound", key); + lowerer.inspectHelpers.set(key, name); + const params = [ + { localId: "v.0", name: "v", type: t }, + { localId: "r.0", name: "r", type: F64 }, + { localId: "d.0", name: "d", type: F64 }, + ]; + const call = (fn: "insp.mark" | "insp.unwind"): IrStmt => ({ + kind: "exprStmt", + expr: { kind: "libCall", fn, args: [], type: VOID, loc }, + loc, + }); + lowerer.liftedFns.push({ + name, + params, + returnType: STRING, + locals: params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })), + body: [ + call("insp.mark"), + { + kind: "tryCatch", + tryBody: [ + { + kind: "return", + value: { + kind: "call", + callee: render.callee, + args: params.map((p) => varRef(p.localId, p.type, loc)), + type: STRING, + loc, + }, + loc, + }, + ], + catchBody: null, + catchLocalId: null, + finallyBody: [call("insp.unwind")], + loc, + }, + ], + loc, + }); + } + return { ...render, callee: name }; +} + +/** Whether inspecting a `t` value can end in an inspection refusal: `t` + * or a type reachable from it is a checked-dynamic value, a generator, a + * Dirent row or a class instance, or a record or array that can keep + * undeclared properties, which can render through a checked-dynamic view. */ +function inspectMayRefuse(lowerer: Lowerer, t: IrType): boolean { + const seen = new Set(); + const walk = (u: IrType): boolean => { + if (u.kind === "dyn" || u.kind === "generator") return true; + if (u.kind === "record") { + const shape = lowerer.shapes.get(u.shapeId); + if (shape && isDirentShape(shape)) return true; + // The kept source properties render through the checked view (the + // helper's source gate). + if (recordMayKeepSource(lowerer, u)) return true; + } + // So do an array's properties besides its elements. + if (u.kind === "array" && lowerer.dynConvertible(u)) return true; + // A class instance can render through its checked-dynamic view, which + // a helper built earlier may only learn at the end of lowering + // (classInspectsAddedProperties). + if (u.kind === "object") return true; + const k = typeKey(u); + if (seen.has(k)) return false; + seen.add(k); + return typeConstituents(lowerer, u).some(walk); + }; + return walk(t); +} + +function inspectExprBody( + lowerer: Lowerer, + t: IrType, + value: IrExpr, + recurse: IrExpr, + depth: IrExpr, + loc: SrcLoc, ): IrExpr { // A Dirent row renders through its dyn view: Node's `Dirent { ... }` // with the Symbol(type) key. @@ -950,9 +1061,29 @@ export function inspectExpr( loc, }; } - return { kind: "libCall", fn: "insp.buffer", args: [value], type: STRING, loc }; + // A Uint8Array, a Buffer and a DataView share the u8 representation + // and print differently: the view's own flags pick. + return { + kind: "libCall", + fn: "insp.bytes", + args: [value, recurse, depth], + type: STRING, + loc, + }; case "dyn": return { kind: "libCall", fn: "insp.dyn", args: [value, recurse, depth], type: STRING, loc }; + // URLs and URLSearchParams render through their checked-dynamic + // handles: Node's `URL { href: ..., hash: ... }` and + // `URLSearchParams { 'a' => '1' }`. + case "url": + case "searchParams": + return { + kind: "libCall", + fn: "insp.dyn", + args: [lowerer.coerceToExpected(value, DYN), recurse, depth], + type: STRING, + loc, + }; case "generator": return { kind: "libCall", @@ -1870,8 +2001,86 @@ function inspectHelper(lowerer: Lowerer, t: IrType, loc: SrcLoc): string { // observable way — Node's inspect omits them entirely (verified), // so a class whose only fields are private prints as `C {}`. const visible = info.def.fields.filter((f) => isClassOwnEnumerableFieldName(f.name)); + // A Symbol.toStringTag getter (an own tag field is listed instead) + // brackets the tag after the name, `Name [tag] {`, when it is a + // nonempty string other than the name. A getter that throws shows + // no tag, as in Node. + const tagGetter = info.fields.has(TO_STRING_TAG_FIELD) + ? null + : findMethodOn(lowerer, info, "get:sym:toStringTag"); + let named: (plain: string, make: (head: IrExpr) => IrExpr) => IrExpr = (plain) => + strLit(plain, loc); + if (tagGetter?.sig.ret.kind === "string") { + const owner = tagGetter.declarer.def.name; + locals.push( + { id: "tag.0", name: "tag", type: STRING, mutable: true }, + { id: "tagError.0", name: "tagError", type: CAUGHT, mutable: false }, + ); + body.push( + { kind: "varDecl", localId: "tag.0", init: strLit("", loc), loc }, + { + kind: "tryCatch", + tryBody: [ + { + kind: "assign", + localId: "tag.0", + value: accessorCall( + lowerer, + owner, + "get:sym:toStringTag", + lowerer.upcastTo(v(), owner), + [], + STRING, + loc, + ), + loc, + }, + ], + catchBody: [], + catchLocalId: "tagError.0", + finallyBody: null, + loc, + }, + ); + const tag = (): IrExpr => varRef("tag.0", STRING, loc); + const shown: IrExpr = { + kind: "logical", + op: "&&", + left: { + kind: "strEq", + negated: true, + left: tag(), + right: strLit("", loc), + type: BOOL, + loc, + }, + right: { + kind: "strEq", + negated: true, + left: tag(), + right: strLit(display, loc), + type: BOOL, + loc, + }, + type: BOOL, + loc, + }; + const head = concatAll( + [strLit(`${display === "" ? "Object" : display} [`, loc), tag(), strLit("]", loc)], + loc, + ); + named = (plain, make) => ({ + kind: "ternary", + cond: shown, + then: make(head), + else_: strLit(plain, loc), + type: STRING, + loc, + }); + } + const after = (text: string) => (head: IrExpr) => concatAll([head, strLit(text, loc)], loc); if (visible.length === 0) { - body = [ret(strLit(display === "" ? "{}" : `${display} {}`, loc))]; + body.push(ret(named(display === "" ? "{}" : `${display} {}`, after(" {}")))); break; } const get = (field: string, type: IrType): IrExpr => ({ @@ -1882,7 +2091,22 @@ function inspectHelper(lowerer: Lowerer, t: IrType, loc: SrcLoc): string { type, loc, }); - body = [depthGate(display === "" ? "[Object]" : `[${display}]`), ...begin()]; + body.push( + { + kind: "if", + cond: { kind: "bin", op: ">", left: r(), right: d(), type: BOOL, loc }, + then: [ + ret( + named(display === "" ? "[Object]" : `[${display}]`, (head) => + concatAll([strLit("[", loc), head, strLit("]", loc)], loc), + ), + ), + ], + else_: null, + loc, + }, + ...begin(), + ); // def.fields carries layout order: the base chain first, then own — // exactly the own-property insertion order of a constructor that // assigns in declaration order (SEMANTICS.md 36's stance). SYMBOL- @@ -1911,7 +2135,7 @@ function inspectHelper(lowerer: Lowerer, t: IrType, loc: SrcLoc): string { ret( end( strLit("", loc), - strLit(display === "" ? "{" : `${display} {`, loc), + named(display === "" ? "{" : `${display} {`, after(" {")), strLit("}", loc), false, boolLit(false, loc), @@ -2181,15 +2405,17 @@ export function lowerConsoleInspectArg( ); } if (!isBufferTypeName(tname)) { + // A u8 value the checker types as a Uint8Array may still hold a + // Buffer, which Node prints as one: the view's flags pick. return { kind: "libCall", - fn: "insp.typedArray", + fn: t.elem === "u8" ? "insp.bytes" : "insp.typedArray", args: [value, numLit(0, loc), numLit(2, loc)], type: STRING, loc, }; } - return inspectExpr(lowerer, t, value, numLit(0, loc), numLit(2, loc), loc); + return { kind: "libCall", fn: "insp.buffer", args: [value], type: STRING, loc }; } if (t.kind === "void") { // Node prints "undefined" for a void call's result; a void expression @@ -2358,40 +2584,55 @@ function parseInspectOptions( /** A render under non-default breakLength/compact/sorted: the value is evaluated * first, the layout applies to this render only, and the defaults return - * afterwards. */ + * however the render ends, an inspection refusal included. The render runs + * in a lifted function, `(v) => { insp.layout(...); try { return render(v); } + * finally { insp.layout(80, 3, false); } }`, since a seqExpr cannot hold the + * try; `tag` names the render for the function's key. */ function withInspectLayout( lowerer: Lowerer, options: InspectOptions, value: IrExpr, + tag: string, render: (value: IrExpr) => IrExpr, loc: SrcLoc, ): IrExpr { if (options.breakLength === 80 && options.compact === 3 && !options.sorted) return render(value); - const input = lowerer.declareHiddenLocal("%inspectInput", value.type); - const text = lowerer.declareHiddenLocal("%inspectText", STRING); - const layout = (breakLength: number, compact: number, sorted: boolean): IrStmt => ({ - kind: "exprStmt", - expr: { - kind: "libCall", - fn: "insp.layout", - args: [numLit(breakLength, loc), numLit(compact, loc), boolLit(sorted, loc)], - type: VOID, + const key = `layout:${tag}:${typeKey(value.type)}:${options.breakLength}:${options.compact}:${options.sorted}:${options.depth}`; + let name = lowerer.inspectHelpers.get(key); + if (name === undefined) { + name = keyedHelperName(lowerer, "%util.inspLayout", key); + lowerer.inspectHelpers.set(key, name); + const layout = (breakLength: number, compact: number, sorted: boolean): IrStmt => ({ + kind: "exprStmt", + expr: { + kind: "libCall", + fn: "insp.layout", + args: [numLit(breakLength, loc), numLit(compact, loc), boolLit(sorted, loc)], + type: VOID, + loc, + }, loc, - }, - loc, - }); - return { - kind: "seqExpr", - stmts: [ - { kind: "varDecl", localId: input.id, init: value, loc }, - layout(options.breakLength, options.compact, options.sorted), - { kind: "varDecl", localId: text.id, init: render(varRef(input.id, value.type, loc)), loc }, - layout(80, 3, false), - ], - result: varRef(text.id, STRING, loc), - type: STRING, - loc, - }; + }); + lowerer.liftedFns.push({ + name, + params: [{ localId: "v.0", name: "v", type: value.type }], + returnType: STRING, + locals: [{ id: "v.0", name: "v", type: value.type, mutable: false }], + body: [ + layout(options.breakLength, options.compact, options.sorted), + { + kind: "tryCatch", + tryBody: [{ kind: "return", value: render(varRef("v.0", value.type, loc)), loc }], + catchBody: null, + catchLocalId: null, + finallyBody: [layout(80, 3, false)], + loc, + }, + ], + loc, + }); + } + return { kind: "call", callee: name, args: [value], type: STRING, loc }; } /** `util.inspect(fn)` / `util.inspect(Cls)` over a DIRECT identifier @@ -2416,6 +2657,12 @@ function directCallableInspect(lowerer: Lowerer, node: ts.Expression, loc: SrcLo } const decl = sym.declarations[0]; if (!decl) return null; + // A JavaScript function can hold own properties (`f.x = 1`, computed + // keys), which the runtime walk prints after its name. + if (isJsSourceFile(node.getSourceFile())) { + const type = lowerer.mapTypeOf(lowerer.typeOf(node)); + if (type?.kind === "func" && lowerer.dynConvertible(type)) return null; + } if (ts.isFunctionDeclaration(decl) && decl.name) { return strLit(`[Function: ${decl.name.text}]`, loc); } @@ -2476,7 +2723,17 @@ export function lowerInspectValue( loc, ); } - const value = lowerer.lowerExpr(valueNode); + let value = lowerer.lowerExpr(valueNode); + // A JavaScript function renders through the runtime walk, which prints + // its own properties after its name. + if ( + value.type.kind === "func" && + isJsSourceFile(valueNode.getSourceFile()) && + lowerer.dynConvertible(value.type) + ) { + const name = jsFuncNameOf(valueNode); + value = { kind: "dynFrom", value, type: DYN, ...(name !== null ? { fnName: name } : {}), loc }; + } if (value.type.kind === "bytes") { // One runtime representation serves Buffer AND Uint8Array; their // renderings differ ( vs Uint8Array(1) [ 170 ]), so the @@ -2508,9 +2765,13 @@ export function lowerInspectValue( lowerer, options, value, + "typedArray", (input) => ({ kind: "libCall", - fn: "insp.typedArray", + fn: + value.type.kind === "bytes" && value.type.elem === "u8" + ? "insp.bytes" + : "insp.typedArray", args: [input, numLit(0, loc), numLit(options.depth, loc)], type: STRING, loc, @@ -2519,7 +2780,7 @@ export function lowerInspectValue( ); } // Buffers render fully at any depth and on one line (custom inspect). - return inspectExpr(lowerer, value.type, value, numLit(0, loc), numLit(2, loc), loc); + return { kind: "libCall", fn: "insp.buffer", args: [value], type: STRING, loc }; } const walk = { recursive: false }; const reason = inspectSupport(lowerer, value.type, new Set(), walk); @@ -2533,6 +2794,7 @@ export function lowerInspectValue( lowerer, options, value, + "dirDyn", (input) => ({ kind: "libCall", fn: "console.dirDyn", @@ -2547,6 +2809,7 @@ export function lowerInspectValue( lowerer, options, value, + "inspect", (input) => inspectExpr(lowerer, value.type, input, numLit(0, loc), numLit(depth, loc), loc), loc, ); @@ -2623,10 +2886,21 @@ function formatSArg(lowerer: Lowerer, node: ts.Expression, depth: number, loc: S } /** %O (depth 2) / %o (showHidden, depth 4) — inspect with the spec's - * option deltas; %o fences over arrays (showHidden adds their hidden - * [length] entry, which has no lowering). */ + * option deltas. A checked-dynamic argument renders through the runtime's + * showHidden walk; static %o fences over arrays and nested dynamic values + * (showHidden adds an array's hidden [length] entry, which the static + * rendering has no lowering for). */ function formatOArg(lowerer: Lowerer, node: ts.Expression, depth: number, loc: SrcLoc): IrExpr { const value = lowerer.lowerExpr(node); + if (depth === 4 && value.type.kind === "dyn") { + return { + kind: "libCall", + fn: "insp.dynHidden", + args: [value, numLit(4, loc)], + type: STRING, + loc, + }; + } if (value.type.kind === "bytes") { lowerer.noLowering( `util.format %${depth === 4 ? "o" : "O"} of typed-array values`, @@ -2642,11 +2916,11 @@ function formatOArg(lowerer: Lowerer, node: ts.Expression, depth: number, loc: S node, reason, ); - if (depth === 4 && typeTreeHasArray(lowerer, value.type, new Set())) { + if (depth === 4 && typeTreeHasHidden(lowerer, value.type, new Set())) { lowerer.noLowering( "util.format %o over arrays", node, - "%o renders with showHidden (arrays gain a hidden [length] entry) — use %O for the default rendering", + "%o renders with showHidden (arrays gain a hidden [length] entry) — use %O for the default rendering, or pass the value as unknown", ); } return inspectExpr(lowerer, value.type, value, numLit(0, loc), numLit(depth, loc), loc); @@ -3047,35 +3321,36 @@ export function lowerFormatCall( } /** Arrays anywhere in the tree — the %o (showHidden) gate. */ -function typeTreeHasArray(lowerer: Lowerer, t: IrType, visiting: Set): boolean { +function typeTreeHasHidden(lowerer: Lowerer, t: IrType, visiting: Set): boolean { switch (t.kind) { case "array": + case "dyn": return true; case "record": { if (visiting.has(t.shapeId)) return false; visiting.add(t.shapeId); const shape = lowerer.shapes.get(t.shapeId); if (shape?.tuple) return true; - return (shape?.fields ?? []).some((f) => typeTreeHasArray(lowerer, f.type, visiting)); + return (shape?.fields ?? []).some((f) => typeTreeHasHidden(lowerer, f.type, visiting)); } case "map": return ( - typeTreeHasArray(lowerer, t.key, visiting) || typeTreeHasArray(lowerer, t.value, visiting) + typeTreeHasHidden(lowerer, t.key, visiting) || typeTreeHasHidden(lowerer, t.value, visiting) ); case "set": - return typeTreeHasArray(lowerer, t.elem, visiting); + return typeTreeHasHidden(lowerer, t.elem, visiting); case "union": { if (visiting.has(t.unionId)) return false; visiting.add(t.unionId); return (lowerer.unions.get(t.unionId)?.arms ?? []).some((a) => - typeTreeHasArray(lowerer, a, visiting), + typeTreeHasHidden(lowerer, a, visiting), ); } case "object": { if (visiting.has(t.className)) return false; visiting.add(t.className); return (lowerer.classes.get(t.className)?.def.fields ?? []).some((f) => - typeTreeHasArray(lowerer, f.type, visiting), + typeTreeHasHidden(lowerer, f.type, visiting), ); } case "bytes": diff --git a/packages/compiler/src/frontend/lowering/lower-modules.ts b/packages/compiler/src/frontend/lowering/lower-modules.ts index 5238333ad..75f18fe37 100644 --- a/packages/compiler/src/frontend/lowering/lower-modules.ts +++ b/packages/compiler/src/frontend/lowering/lower-modules.ts @@ -43,10 +43,14 @@ import { } from "./cycle-initialization.js"; import { markRequireEarlyBindings } from "./require-initialization.js"; import { + cjsHasLiveMemberExports, cjsTableMethodEntry, inlineProgramRequireOf, nestedCjsExportAssignmentsOf, requireNestedPatternsOf, + splitRequireBindingOf, + splitRequireBindingOfIdent, + wholeExportSymbolOfModule, } from "./cjs-inline-require.js"; import { isDeadTscHelperDecl, tscInteropBindingOfDecl } from "./cjs-tsc-interop.js"; import { collectJsonRequires, isJsonRequireDecl } from "./json-require.js"; @@ -1597,14 +1601,26 @@ export function collectGlobals( // require() of a program module is not: the exporter re-exports // that module's exports, which requirers resolve through the // checker's alias (the statement lowers to the dep's %init). + // A split binding re-exported whole with later member writes needs + // a mutable root: `module.exports = m; module.exports.extra = 1` + // extends the SAME object Node exposes. Without later members the + // binder is pure alias plumbing (no storage). + const splitWholeBinding = ts.isIdentifier(rhs) + ? splitRequireBindingOfIdent(lowerer, rhs) + : null; + const hasMemberExports = cjsHasLiveMemberExports(lowerer, sf); + const splitWhole = + splitWholeBinding !== null && + wholeExportSymbolOfModule(lowerer, splitWholeBinding.dep) !== null && + hasMemberExports; const factoryValued = - !ts.isIdentifier(rhs) && + (!ts.isIdentifier(rhs) || splitWhole) && !ts.isClassExpression(rhs) && !ts.isObjectLiteralExpression(rhs) && !( requireSpecOf(rhs) !== null && inlineProgramRequireOf(lowerer, rhs) !== null && - !sf.statements.nodes.some((s) => cjsExportAssignmentOf(s)?.kind === "member") + !hasMemberExports ) && cjsExportTargetLiteral(rhs, sf) === null; if (cjsScalarLiteral(rhs) || fnValued || factoryValued) { @@ -1617,6 +1633,9 @@ export function collectGlobals( // A returned object remains the live CommonJS root; later // exports.member writes must update that same open object. if (factoryValued && t?.kind === "record" && lowerer.dynConvertible(t)) t = DYN; + // A split whole-export root is extended by later member writes; + // keep it checked-dynamic so those writes reach the same object. + if (splitWhole && t && t.kind !== "dyn") t = DYN; if (t && t.kind !== "void") { const g: IrGlobal = { id: `%g.${tag}exports`, @@ -2361,7 +2380,6 @@ export function collectGlobals( // References from separately-declared functions cascade to // their own per-site runtime fences. const factoryType = - !isLet && isJsSourceFile(sf) && !decl.type && !hasJsTypeAnnotation(decl) && @@ -2708,6 +2726,7 @@ function noteVarGlobalEntryInit(lowerer: Lowerer, sf: ts.SourceFile, g: IrGlobal * plumbing (lowerVarDeclarator): no storage, the reads resolve through to * the module. JSON documents and native FFI requires keep their storage. */ function requireAliasDecl(lowerer: Lowerer, decl: ts.VariableDeclaration): boolean { + if (splitRequireBindingOf(lowerer, decl) !== null) return true; return ( decl.initializer !== undefined && requireSpecOf(decl.initializer) !== null && diff --git a/packages/compiler/src/frontend/lowering/lower-stdlib.ts b/packages/compiler/src/frontend/lowering/lower-stdlib.ts index 7e108fde3..a7b63eab1 100644 --- a/packages/compiler/src/frontend/lowering/lower-stdlib.ts +++ b/packages/compiler/src/frontend/lowering/lower-stdlib.ts @@ -1945,6 +1945,19 @@ export function lowerStaticReadableStreamNew( }; } +/** `new TransformStream()`: the identity transform, a native handle whose + * readable side is a native ReadableStream and whose writable side is the + * runtime's WritableStream. A transformer or queuing strategies have no + * native lowering yet and keep the SC2020 refusal. */ +export function lowerStaticTransformStreamNew( + lowerer: Lowerer, + expr: ts.NewExpression, +): IrExpr | null { + if (stdlibGlobalNameOf(lowerer, expr.expression) !== "TransformStream") return null; + if ((expr.arguments?.nodes.length ?? 0) > 0) return null; + return { kind: "libCall", fn: "fetch.transformNew", args: [], type: DYN, loc: locOf(expr) }; +} + /** Native Request and Headers values share the fetch runtime's handles. */ export function lowerFetchWebNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr | null { const name = stdlibGlobalNameOf(lowerer, expr.expression); diff --git a/packages/compiler/src/frontend/lowering/lower-stmts.ts b/packages/compiler/src/frontend/lowering/lower-stmts.ts index 66ecea602..c50676863 100644 --- a/packages/compiler/src/frontend/lowering/lower-stmts.ts +++ b/packages/compiler/src/frontend/lowering/lower-stmts.ts @@ -1,4 +1,11 @@ import { lowerTupleAliasDecl, stripTypeWrappers } from "./typed-arguments.js"; +import { + allBytesArms, + indexedUnionArms, + lowerIndexedUnionMember, + lowerIndexedUnionNumberRead, + lowerIndexedUnionRead, +} from "./indexed-unions.js"; import { declaredMemberOf, isPromisePrototypeRead } from "./expressions/property-access.js"; import { hostsAll } from "./expressions/hosted-narrowing.js"; import { requestGeneralStreamSurface } from "./general-streams.js"; @@ -29,6 +36,7 @@ import { lowerOptionalIndex } from "./undefined-index.js"; import { processDataMemberValue, programReadsExitCode } from "./builtins/process.js"; import { requireInitFlagStmt } from "./require-initialization.js"; import { isJsonRequireDecl } from "./json-require.js"; +import { isTimeValueDateDeclaration } from "./date-values.js"; import { bindingSource } from "../binding-source.js"; import { lowerConstructorReturn } from "./constructor-returns.js"; import type { Lowerer } from "./lowerer.js"; @@ -38,6 +46,11 @@ import { cjsTableMethodEntry, inlineProgramRequireOf, requireNestedPatternsOf, + splitRequireAssignmentOf, + splitRequireBindingOf, + splitRequireBindingOfIdent, + splitRequireEarlyReadOf, + wholeExportSymbolOfModule, } from "./cjs-inline-require.js"; import { cjsExportMemberWriteGlobalOf, @@ -64,6 +77,7 @@ import { BOOL, BYTES_U8, CAUGHT, + DATE_T, DYN, F64, type IrExpr, @@ -105,6 +119,7 @@ import { requireSpecOf, } from "../program.js"; import { isStrictCode } from "./strict-mode.js"; +import { markSloppyRecordWrites } from "./frozen-records.js"; import { COMPOUND_ASSIGN_OPS, type CompoundOp, @@ -1931,6 +1946,23 @@ function lowerVarDeclarator( isConst: boolean, isLet: boolean, ): IrStmt[] { + // `var m; m = require("./x")`: the declaration is alias plumbing; the + // assignment owns the module's run-once %init (cjs-inline-require.ts). + // A read that code before the assignment can reach keeps Node's hoisted + // `undefined` divergence visible at build time, like a var require + // declaration below runnable code. + const split = splitRequireBindingOf(lowerer, decl); + if (split !== null) { + const early = splitRequireEarlyReadOf(lowerer, split); + if (early !== null) { + lowerer.unsupported( + "SC1013", + early, + `var require() binding '${(decl.name as ts.Identifier).text}' below code that can reach it (Node reads undefined before the require runs, which is not modeled — assign the require above the code that can run first)`, + ); + } + return []; + } // The canonical ESM bridge to a compiled program module. Like a // top-level CommonJS require declaration, the binding is alias // plumbing while the dependency evaluates at this statement's @@ -5414,6 +5446,16 @@ export function lowerVarDecl( if (alias) return alias; } + // A Date local whose identity no use observes stores its time value. + if (isTimeValueDateDeclaration(lowerer, decl)) { + const loc = locOf(decl); + const date = lowerer.lowerExprExpecting(decl.initializer!, DATE_T); + const local = lowerer.declareLocal(decl.name, decl.name.text, F64, isLet); + lowerer.timeValueDateLocals.add(local); + const init: IrExpr = { kind: "libCall", fn: "date.getTime", args: [date], type: F64, loc }; + return { kind: "varDecl", localId: local.id, init, loc }; + } + // A NULLISH generic binding (`const i: I = null as any` — the // declared type has no mapping, the value is provably null/undefined // forever): no storage exists; reads know the value (member reads and @@ -6884,7 +6926,7 @@ function lowerDynSwitch( loc: locOf(expression), }; } - if (test.type.kind === "date" || !lowerer.dynConvertible(test.type)) { + if (!lowerer.dynConvertible(test.type)) { lowerer.unsupported( "SC1100", expression, @@ -7160,7 +7202,11 @@ function lowerDeleteStatement(lowerer: Lowerer, expr: ts.DeleteExpression): IrSt if (obj.type.kind === "record") { const shape = lowerer.shapes.get(obj.type.shapeId); if (shape?.indexValue && shape.fields.length === 0 && !shape.tuple) { - return { kind: "recordKeyDelete", obj, shapeId: obj.type.shapeId, key: lowerKey(), loc }; + return markSloppyRecordWrites( + lowerer, + { kind: "recordKeyDelete", obj, shapeId: obj.type.shapeId, key: lowerKey(), loc }, + expr, + ); } // `delete r.f` of a declared OPTIONAL field (undefined-armed slot) // writes the field's ABSENT state: a monomorphic shape cannot remove @@ -7181,14 +7227,19 @@ function lowerDeleteStatement(lowerer: Lowerer, expr: ts.DeleteExpression): IrSt if (field) { const absent = lowerer.absentFieldValue(field.type, loc); if (absent) { - return { - kind: "recordSet", - obj, - shapeId: obj.type.shapeId, - field: field.name, - value: absent, - loc, - }; + return markSloppyRecordWrites( + lowerer, + { + kind: "recordSet", + obj, + shapeId: obj.type.shapeId, + field: field.name, + value: absent, + loc, + }, + expr, + true, + ); } } if (shape?.indexValue) { @@ -7515,6 +7566,26 @@ function lowerCjsSingleValueExport( return lowerer.requireInitStmt(req.spec, req.call) ?? { kind: "block", body: [], loc }; } if (ts.isIdentifier(rhs)) { + const split = splitRequireBindingOfIdent(lowerer, rhs); + if (split !== null) { + if (wholeExportSymbolOfModule(lowerer, split.dep) === null) { + lowerer.unsupported( + "SC1090", + rhs, + `the exports object of '${split.spec}' as a value (its exports compile to a static table: read require(${JSON.stringify(split.spec)}).member directly)`, + ); + } + const root = lowerer.globalsByDeclNode.get(expr); + if (root !== undefined) { + return { + kind: "assign", + localId: root.id, + value: lowerer.lowerExprExpecting(rhs, root.type), + loc: locOf(rhs), + }; + } + return { kind: "block", body: [], loc }; + } const sym = lowerer.resolveValueSymbol(rhs); if (!sym) return null; if ( @@ -8425,6 +8496,16 @@ export function lowerExprStatement(lowerer: Lowerer, expr: ts.Expression): IrStm if (!ts.isIdentifier(expr.left)) { lowerer.unsupported("SC1090", expr.left, "assignment to non-variables"); } + const splitAssign = splitRequireAssignmentOf(lowerer, expr); + if (splitAssign !== null) { + return ( + lowerer.requireInitStmt(splitAssign.spec, splitAssign.call) ?? { + kind: "block", + body: [], + loc: locOf(expr), + } + ); + } const target = lowerer.resolveWritable(expr.left); if (!target) lowerer.rejectUnresolved( @@ -8739,13 +8820,20 @@ function destrSpellingOf(rhs: ts.Expression): string | null { return base === null ? null : `${base}.${e.name.text}`; } // `table[key]` and `list[0]`; V8 prints a numeric key in its canonical - // number form (`o[1.50]` reads "o[1.5]"). + // number form (`o[1.50]` reads "o[1.5]"), and a string key as a property + // name with its text decoded (`o["a.b"]` and `o[\`a.b\`]` read "o.a.b"). + // The message is a C string, so a key holding NUL keeps the refusal. if (ts.isElementAccessExpression(e) && !e.questionDotToken) { const base = destrSpellingOf(e.expression); const key = e.argumentExpression; if (base === null) return null; if (ts.isIdentifier(key)) return `${base}[${key.text}]`; if (ts.isNumericLiteral(key)) return `${base}[${String(Number(key.text))}]`; + if ( + (ts.isStringLiteral(key) || ts.isNoSubstitutionTemplateLiteral(key)) && + !key.text.includes("\0") + ) + return `${base}.${key.text}`; return null; } if (ts.isCallExpression(e) && !e.questionDotToken) { @@ -10132,13 +10220,8 @@ export function lowerForOf(lowerer: Lowerer, stmt: ts.ForOfStatement): IrStmt { loc: locOf(iterSrc), }; } - if (iterable.type.kind === "bytes") { - return lowerForOfBytes( - lowerer, - stmt, - iterable as IrExpr & { type: IrType & { kind: "bytes" } }, - labels, - ); + if (iterable.type.kind === "bytes" || indexedUnionArms(lowerer, iterable.type)) { + return lowerForOfBytes(lowerer, stmt, iterable, labels); } if (iterable.type.kind !== "array") { // The lib types strings as iterable too, so those for-ofs typecheck; @@ -10696,7 +10779,7 @@ export function lowerForOf(lowerer: Lowerer, stmt: ts.ForOfStatement): IrStmt { function lowerForOfBytes( lowerer: Lowerer, stmt: ts.ForOfStatement, - iterable: IrExpr & { type: IrType & { kind: "bytes" } }, + iterable: IrExpr, labels?: string[], ): IrStmt { const loc = locOf(stmt); @@ -10790,7 +10873,16 @@ function lowerForOfStoredNumericIterator( const loc = locOf(stmt); const sourceT = state.sourceType; - if (!((sourceT.kind === "array" && sourceT.elem.kind === "f64") || sourceT.kind === "bytes")) { + // A union of typed arrays and number arrays (indexed-unions.ts) reads + // through its arms' own length and element reads. + const unionArms = indexedUnionArms(lowerer, sourceT); + if ( + !( + (sourceT.kind === "array" && sourceT.elem.kind === "f64") || + sourceT.kind === "bytes" || + unionArms + ) + ) { throw new InternalCompilerError("internal: retained numeric iterator has a non-numeric source"); } @@ -10798,35 +10890,44 @@ function lowerForOfStoredNumericIterator( const doneRef = (): IrExpr => varRef(state.doneLocalId, BOOL, loc); const sourceRef = (): IrExpr => varRef(state.sourceLocalId, sourceT, loc); const sourceLength = (): IrExpr => - sourceT.kind === "array" - ? { - kind: "arrIntrinsic", - method: "length", - receiver: sourceRef(), - args: [], - type: F64, - loc, - } - : { - kind: "bytesIntrinsic", - method: "length", - receiver: sourceRef(), - args: [], - type: F64, - loc, - }; - const valueT = sourceT.kind === "array" ? arrayValueType(lowerer, F64) : F64; + unionArms + ? lowerIndexedUnionMember(lowerer, sourceRef(), unionArms, "length", loc)! + : sourceT.kind === "array" + ? { + kind: "arrIntrinsic", + method: "length", + receiver: sourceRef(), + args: [], + type: F64, + loc, + } + : { + kind: "bytesIntrinsic", + method: "length", + receiver: sourceRef(), + args: [], + type: F64, + loc, + }; + const valueT = + sourceT.kind === "array" || (unionArms && !allBytesArms(unionArms)) + ? arrayValueType(lowerer, F64) + : F64; const sourceValue = (): IrExpr => - sourceT.kind === "array" - ? arrayValueRead(lowerer, sourceRef(), indexRef(), F64, loc) - : { - kind: "bytesIntrinsic", - method: "get", - receiver: sourceRef(), - args: [indexRef()], - type: F64, - loc, - }; + unionArms + ? allBytesArms(unionArms) + ? lowerIndexedUnionNumberRead(lowerer, sourceRef(), unionArms, indexRef(), loc) + : lowerIndexedUnionRead(lowerer, sourceRef(), unionArms, indexRef(), loc) + : sourceT.kind === "array" + ? arrayValueRead(lowerer, sourceRef(), indexRef(), F64, loc) + : { + kind: "bytesIntrinsic", + method: "get", + receiver: sourceRef(), + args: [indexRef()], + type: F64, + loc, + }; const one: IrExpr = { kind: "numLit", value: 1, type: F64, loc }; lowerer.scopes.push(new Map()); diff --git a/packages/compiler/src/frontend/lowering/lowerer.ts b/packages/compiler/src/frontend/lowering/lowerer.ts index f26a8f14f..d4d9d59c1 100644 --- a/packages/compiler/src/frontend/lowering/lowerer.ts +++ b/packages/compiler/src/frontend/lowering/lowerer.ts @@ -31,6 +31,7 @@ import { refuseUncheckedCycleReads, type CycleInitFlag, } from "./cycle-initialization.js"; +import { bindCompletionCallbacks } from "./completion-callbacks.js"; import { isJsonRequireDecl, type JsonRequireModule } from "./json-require.js"; import { checkRequireBindingAccesses, @@ -146,11 +147,18 @@ import { } from "./function-context.js"; import type { FrontendServices } from "../services.js"; import { InternalCompilerError } from "../../errors.js"; +import type { FsPathPlan } from "./builtins/fs-paths.js"; import { defaultAfterUndefined, lowerStaticallyUndefinedArgument } from "./optional-arguments.js"; import { type ProtocolDispatch } from "./class-protocol-dispatch.js"; -import { ClassDynamicDispatch, trackClassFieldCreation } from "./class-dynamic-dispatch.js"; +import { + ClassDynamicDispatch, + boxClassInstancesExactly, + trackClassFieldCreation, +} from "./class-dynamic-dispatch.js"; import { guardClassAttributeWrites } from "./class-attributes.js"; +import { guardFrozenWrites, markSloppyRecordWrites } from "./frozen-records.js"; import { applyHandleProperties } from "./handle-properties.js"; +import { installBufferValue } from "./containers/bytes.js"; import { ClassProtocols } from "./class-protocols.js"; import { earlyReadVarSymbols } from "./var-hoisting.js"; import { recordTextCodecClass } from "../../ir/ir.js"; @@ -340,10 +348,13 @@ import { } from "./lower-modules.js"; import { prepareCjsModuleGraph, usesCjsTopLevelThis } from "./lower-node-module.js"; import { + type SplitRequireBinding, cjsNamespaceModule, cjsWholeExportValueOf, inlineNamespaceRequireOf, inlineProgramRequireOf, + splitRequireBindingOf, + splitWholeExportAliasTargetOf, } from "./cjs-inline-require.js"; import { tscInteropModuleOf } from "./cjs-tsc-interop.js"; import { usesCjsExportsIdentity } from "./cjs-exports-object.js"; @@ -423,6 +434,7 @@ import { collectSignatureInner, lambdaSignature, } from "./call-signatures.js"; +import { dynamicOverloadChoice, guessedOverloadIdentifier } from "./overload-choice.js"; import { implicitAnyParamSymbolsOf, implicitLocalFnNodeOf, @@ -558,6 +570,7 @@ import { lowerStaticReadableStreamCancelCall, lowerStaticReadableStreamControllerCall, lowerStaticReadableStreamNew, + lowerStaticTransformStreamNew, lowerStaticReadableStreamReaderCall, lowerStaticResponseCall, lowerStdlibMethodCall, @@ -2287,6 +2300,14 @@ export class Lowerer { /** Per CommonJS file using top-level `this`: the global holding the * object that stands for its original exports (lowerCjsTopLevelThis). */ readonly cjsExportsThis = new Map(); + /** `var m; m = require("./x")` alias bindings (cjs-inline-require.ts). + * Null entries cache declarations that keep ordinary storage. */ + readonly cjsSplitRequireBindings = new Map(); + /** Per-module whole-export symbol (cjs-inline-require.ts). Null means the + * module's exports are a table. */ + readonly cjsWholeExportSymbols = new Map(); + /** Per-module live member-export classification (cjs-inline-require.ts). */ + readonly cjsLiveMemberExports = new Map(); /** Inline program-module require calls whose %init the enclosing member * read or member call is already sequencing (cjs-inline-require.ts). */ readonly inlineRequireInitDone = new Set(); @@ -2337,6 +2358,10 @@ export class Lowerer { readonly runtimeOptionalFields = new Set(); /** Bindings initialized by a Date's `toJSON()` (analyzeDateJsonResults). */ readonly dateJsonBindings = new Set(); + /** isTimeValueDateDeclaration's answers, and the locals that hold a + * Date's time value instead of a Date object (date-values.ts). */ + readonly timeValueDateDecisions = new Map(); + readonly timeValueDateLocals = new Set(); /** Record bindings whose flow class reaches storage outside the class: * they keep the declared layout, so an initializer from unchecked reads * converts once instead of the binding adopting the widened variant. */ @@ -2821,6 +2846,8 @@ export class Lowerer { /** Parameters read as `any` in the error-path copy of an error-first * callback. */ readonly anySymbols = new Set(); + /** overload-choice's answers per call, binding and function. */ + readonly dynamicOverloadAnswers = new Map(); /** Error parameters of an error-first callback with a known value in * the copy being lowered: true while it holds an Error, false while it * holds null (staticCondition folds conditions on them). */ @@ -2896,6 +2923,9 @@ export class Lowerer { /** util.inspect's per-type traversal helpers (%util.insp.N), interned * by typeKey — the assertHelpers pattern with its own namespace. */ readonly inspectHelpers = new Map(); + /** Indexed-union safe-read helpers mapped to their numeric twins, for the + * fusion of an immediate ToNumber (tryLowerNumericIndexRead). */ + readonly indexedUnionNumericTwins = new Map(); private readonly unionLiteralOwners = new Map< string, { owners: Map | null; shapes: number; unions: number } @@ -2935,6 +2965,10 @@ export class Lowerer { * checks resolve through this map (stdlibGlobalNameOf), declarations * emit nothing. */ readonly stdlibGlobalAliases = new Map(); + /** The stdlibGlobalAliases bound through a cast of the global object + * (`(globalThis as { Buffer?: B }).Buffer`). Their static type is the + * cast's, so their value reads are refused (identifiers.ts). */ + readonly castGlobalAliases = new Set(); /** CJS export-table ACCESSORS (`module.exports = { get path() {...} }`), * lifted lazily as module-level functions and interned per accessor * declaration: member reads call the getter (lower-exprs). */ @@ -3021,6 +3055,23 @@ export class Lowerer { /** Named method slots observed before class collection need a callable * ABI and shared prototype descriptors instead of call-site specialization. */ readonly prototypeMethodAccesses = new Map(); + /** Every site the census found for a name in prototypeMethodAccesses (which + * keeps one): reads of the method and writes that may replace it. The + * key-order witness asks which replacements can return records. */ + readonly methodAccessSites = new Map(); + + /** Marks `name` as a method the program may read or replace at `site`. */ + noteMethodAccess(name: string, site: ts.Node): void { + this.prototypeMethodAccesses.set(name, site); + this.noteMethodSite(name, site); + } + + /** Records a site for `name` without changing its prototypeMethodAccesses entry. */ + noteMethodSite(name: string, site: ts.Node): void { + const sites = this.methodAccessSites.get(name); + if (sites === undefined) this.methodAccessSites.set(name, [site]); + else sites.push(site); + } /** Whether the program reads `Error.prototype`, the checked-dynamic * object an ES5 constructor function can inherit from, or has a class * whose heritage is evaluated at run time (`extends (Base ?? Error)`): @@ -3116,8 +3167,13 @@ export class Lowerer { * (builtins/fs-paths.ts): each lowers to the hidden local that holds its * value, which is null until the path's lowering has saved it. */ readonly stagedArguments = new Map(); - /** The staged arguments that follow each such URL path, in order. */ - readonly stagedAfterPath = new Map(); + /** What each fs path argument of a URL-path call lowers to (the plans + * of builtins/fs-paths.ts). */ + readonly fsPathPlans = new Map(); + /** The existsSync/fs.promises calls whose converted URL paths are being + * lowered into the call itself (lowerFallibleFsUrlCall). A call may be + * lowered more than once, and each lowering converts again. */ + readonly fallibleFsCalls = new Set(); readonly moduleNsParamOverrides = new Map< ts.ParameterDeclaration, IrType & { kind: "moduleNs" } @@ -3241,6 +3297,12 @@ export class Lowerer { // Stack of function contexts (bottom = the function being declared at // top level, top = the innermost nested function currently lowering). fnStack: FnCtx[] = []; + /** Source ranges of record writes in sloppy-mode code, and of the + * absent-state writes of `delete r.f` (markSloppyRecordWrites). */ + readonly sloppyRecordWrites = new Set(); + readonly recordFieldDeletes = new Set(); + /** typeKeys of the array types the program may freeze (IrModule.freezableArrays). */ + readonly freezableArrays = new Set(); readonly liftedFns: IrFunction[] = []; /** The first lifted function of each name (liftedFnNamed), for the * liftedFns indexed so far. liftedFns only grows. */ @@ -4095,6 +4157,11 @@ export class Lowerer { // registry (globals, fn signatures, classes) applies unchanged. const cjsValue = this.cjsExportValueSymbol(symbol); if (cjsValue) symbol = cjsValue; + // `var m; m = require("./x"); module.exports = m` over a whole-export + // target: TS types the wrapper's export= as `any`, so reconnect it to + // the target's value symbol explicitly. + const splitAlias = splitWholeExportAliasTargetOf(this, symbol); + if (splitAlias) symbol = splitAlias; this.flushDeferred(symbol); return symbol; } @@ -4211,7 +4278,7 @@ export class Lowerer { // exact mappings, while star exports resolve to the mapped declaration // owner through externalTypeSpecifiersByFile. const moduleSymbol = this.checker.getSymbolAtLocation(sf); - const exported = moduleSymbol?.getExports().get(exportName as ts.__String); + const exported = moduleSymbol?.getExports().get(ts.escapedSymbolName(exportName)); return this.externalTypeSpecifierOfSymbol(exported, seenSymbols, seenExports); } @@ -4354,7 +4421,7 @@ export class Lowerer { cjsModuleExportSymbol(sf: ts.SourceFile, name: string): ts.Symbol | undefined { const moduleSym = this.checker.getSymbolAtLocation(sf); const exports = moduleSym?.getExports(); - const direct = exports?.get(name as ts.__String); + const direct = exports?.get(ts.escapedSymbolName(name)); if (direct) return direct; // tsgo represents `module.exports = { ... }` as one `export=` symbol // and does not duplicate the object literal's properties into the @@ -4488,8 +4555,8 @@ export class Lowerer { const decl = decls.find(ts.isImportClause) ?? decls[0]; if (!decl) return null; if (ts.isImportClause(decl)) return this.cjsDefaultImportDepOf(decl); - if (!ts.isVariableDeclaration(decl) || !ts.isIdentifier(decl.name) || !decl.initializer) - return null; + if (!ts.isVariableDeclaration(decl) || !ts.isIdentifier(decl.name)) return null; + if (decl.initializer === undefined) return splitRequireBindingOf(this, decl)?.dep ?? null; { const interop = tscInteropModuleOf(this, expr); if (interop !== null) return cjsNamespaceModule(this, interop.dep) ? interop.dep : null; @@ -4594,6 +4661,9 @@ export class Lowerer { // a binding (cjs-inline-require.ts sequences the module's %init). if (!ts.isIdentifier(expr)) return this.cjsModuleRefDepOf(expr) !== null; const sym = this.checker.getSymbolAtLocation(expr); + const splitDecl = sym?.declarations.find(ts.isVariableDeclaration); + const split = splitDecl === undefined ? null : splitRequireBindingOf(this, splitDecl); + if (split !== null) return cjsNamespaceModule(this, split.dep); if (this.localModuleBindingDepOf(expr) === null) return false; if (this.createRequireWholeExportSymbolOf(expr) !== null) return false; // SINGLE-VALUE exporters (`module.exports = Countdown` / `= double` / @@ -4901,6 +4971,13 @@ export class Lowerer { * their external dependencies by name). Null for everything else * (builtins load nothing; the rest kept its preflight fence). */ requireInitStmt(spec: string, node: ts.Node): IrStmt | null { + if (this.inlineRequireCycleCalls.has(node)) { + this.unsupported( + "SC1090", + node, + `require() of '${spec}' while it is still evaluating (a require cycle: Node answers its partial exports, which the static export table does not model)`, + ); + } if (canonicalBuiltinModule(spec) === "punycode") return this.punycodeRequireWarning(node); // Project requires resolve within the program; another bare require can // be a program-module edge when it names an opted-in --npm-static package @@ -6411,6 +6488,22 @@ export class Lowerer { }); return keys; }; + // `t[i]!` on a typed array, stored into an array, keeps an invalid + // index's undefined (assertedReadObservesUndefined): `!` performs no + // runtime check. A read whose consumers treat undefined like NaN stores + // NaN instead. + const assertedReadMayBeUndefined = (node: ts.Expression): boolean => { + let e = node; + while (ts.isParenthesizedExpression(e)) e = e.expression; + if (!ts.isNonNullExpression(e)) return false; + let read = e.expression; + while (ts.isParenthesizedExpression(read)) read = read.expression; + return ( + ts.isElementAccessExpression(read) && + this.mapTypeOf(this.typeOf(read.expression))?.kind === "bytes" && + !nanCoded.isNumericRead(read) + ); + }; const elementStates = new ArrayElementStates({ elementAdmitsUndefined: (key) => { siteDependencies?.read(key); @@ -6420,7 +6513,7 @@ export class Lowerer { allocationOwner: (node) => ownership.allocationOwner(node), arrayKeys: arrayKeysOf, mayBeUndefined: (node) => { - if (explicitlyNonNull(node)) return false; + if (explicitlyNonNull(node)) return assertedReadMayBeUndefined(node); if (mayBeOptional(node)) return true; // `undefined as unknown as T` keeps the undefined value. for ( @@ -7697,8 +7790,11 @@ export class Lowerer { finalizeClassMethodValues(this, functions); finalizePrivateBrands(this, functions); trackClassFieldCreation(this, functions); + boxClassInstancesExactly(this, functions); guardClassAttributeWrites(this, functions); + guardFrozenWrites(this, functions); applyHandleProperties(this, functions); + installBufferValue(functions); // Globals typed by a class that never REGISTERED (a JS class whose // collection fenced — Symbol-keyed fields, an unsupported base): the @@ -7806,11 +7902,15 @@ export class Lowerer { ...(this.threadSlotKeys.size > 0 ? { threadSlotKeys: [...this.threadSlotKeys].sort() } : {}), + ...(this.freezableArrays.size > 0 + ? { freezableArrays: [...this.freezableArrays].sort() } + : {}), ...(isNodeEsmFile(this.entry, this.program) ? {} : { commonJsEntry: true }), ...(this.ffiImports.length > 0 ? { ffiImports: [...this.ffiImports] } : {}), }; if (module) sanitizeUnregisteredClassTypes(module, (name) => this.classes.has(name)); if (module) checkCycleBindingAccesses(this, module); + if (module) bindCompletionCallbacks(module); // Execution tiers from the final bodies (ir/tiers.ts). Assigned with or // without a module: coverage reports the dynamic tier of programs that // still have blockers. @@ -8967,6 +9067,20 @@ export class Lowerer { typeOf(node: ts.Node): ts.Type { if (this.anySymbols.size > 0 && this.readsAnySymbol(node)) return this.checker.getAnyType(); + // A checked-dynamic argument that another overload could take at run + // time leaves the resolved overload's return a guess, and JS bindings + // initialized with that result hold whatever it was. + if ( + (ts.isCallExpression(node) || ts.isNewExpression(node)) && + dynamicOverloadChoice(this, node) + ) + return this.checker.getAnyType(); + if ( + ts.isIdentifier(node) && + isJsSourceFile(ts.getSourceFileOfNode(node)) && + guessedOverloadIdentifier(this, node) + ) + return this.checker.getAnyType(); if (ts.isPropertyAccessExpression(node) && isJsSourceFile(ts.getSourceFileOfNode(node))) { const receiver = node.expression.kind === ts.SyntaxKind.ThisKeyword && this.fnStack.length > 0 @@ -12998,7 +13112,7 @@ export class Lowerer { } lowerElementWrite(expr: ts.BinaryExpression): IrStmt { - return lowerElementWrite(this, expr); + return markSloppyRecordWrites(this, lowerElementWrite(this, expr), expr); } lowerLiveWebValue(node: ts.Expression): IrExpr { @@ -13395,7 +13509,7 @@ export class Lowerer { } fieldSetStmt(target: FieldTarget, value: IrExpr, loc: SrcLoc, blame: ts.Node): IrStmt { - return fieldSetStmt(this, target, value, loc, blame); + return markSloppyRecordWrites(this, fieldSetStmt(this, target, value, loc, blame), blame); } lowerFieldCompound( @@ -13435,6 +13549,7 @@ export class Lowerer { lowerBlobNew(this, expr) ?? lowerNetAddressNew(this, expr) ?? lowerStaticReadableStreamNew(this, expr) ?? + lowerStaticTransformStreamNew(this, expr) ?? lowerConsoleNew(this, expr); return ( builtin ?? this.withRuntimeOptionalClassValue(expr.expression, () => lowerNew(this, expr)) @@ -13882,6 +13997,9 @@ export class Lowerer { loc, }; } + // The buffer module's Buffer is the global constructor's value. + if (bi.module === "buffer" && bi.member === "Buffer") + return { kind: "libCall", fn: "buffer.constructor", args: [], type: DYN, loc }; if (bi.module === "fs" && (bi.member === "realpathSync" || bi.member === "realpathSync.native")) return fsRealpathValue(this, bi.member === "realpathSync.native", loc); const fn = builtinModuleFnOf(this, bi.module, bi.member); diff --git a/packages/compiler/src/frontend/lowering/object-factory-new.ts b/packages/compiler/src/frontend/lowering/object-factory-new.ts index bcb675692..c672f82e2 100644 --- a/packages/compiler/src/frontend/lowering/object-factory-new.ts +++ b/packages/compiler/src/frontend/lowering/object-factory-new.ts @@ -6,10 +6,14 @@ import { bindingNeverReassigned } from "./binding-analysis.js"; import { implicitDefaultInstance } from "./generic-functions.js"; import { type FnSig } from "./call-signatures.js"; -/** An ordinary constructor's explicit object return replaces its receiver. - * Without observing this or new.target, it can use the function's call ABI. */ -function isObjectFactoryNew(lowerer: Lowerer, expr: ts.NewExpression): boolean { - if (!ts.isIdentifier(expr.expression) || lowerer.peekLocal(expr.expression)) return false; +/** The JavaScript function declaration (or never-reassigned function + * expression binding) `new` calls, when it is an ordinary constructor: not + * a generator or async function. */ +function jsConstructorFunctionOf( + lowerer: Lowerer, + expr: ts.NewExpression, +): ts.FunctionDeclaration | ts.FunctionExpression | null { + if (!ts.isIdentifier(expr.expression) || lowerer.peekLocal(expr.expression)) return null; // This probe also sees ordinary classes during global collection. Leave // their deferred diagnostics for the class declaration's lowering. const wasCollecting = lowerer.collecting; @@ -20,7 +24,7 @@ function isObjectFactoryNew(lowerer: Lowerer, expr: ts.NewExpression): boolean { } finally { lowerer.collecting = wasCollecting; } - if (!symbol) return false; + if (!symbol) return null; const owner = symbol.declarations.find( (node) => (ts.isFunctionDeclaration(node) && node.body !== undefined) || @@ -45,7 +49,15 @@ function isObjectFactoryNew(lowerer: Lowerer, expr: ts.NewExpression): boolean { declaration.asteriskToken || declaration.modifiers?.nodes.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ) - return false; + return null; + return declaration; +} + +/** An ordinary constructor's explicit object return replaces its receiver. + * Without observing this or new.target, it can use the function's call ABI. */ +function isObjectFactoryNew(lowerer: Lowerer, expr: ts.NewExpression): boolean { + const declaration = jsConstructorFunctionOf(lowerer, expr); + if (!declaration?.body) return false; const objectReturn = (expression: ts.Expression): boolean => ts.isObjectLiteralExpression(expression) || ts.isArrayLiteralExpression(expression) || @@ -81,8 +93,13 @@ function isObjectFactoryNew(lowerer: Lowerer, expr: ts.NewExpression): boolean { /** Collection must register the shared result before reaching factory * bodies: those bodies can reference later globals in the same module. */ +/** The storage of a JavaScript module binding initialized with `new` of an + * ordinary function: a factory's result and a constructor function's + * receiver (`const head = new Segment()`) are both checked-dynamic + * values, which functions declared elsewhere in the module read through + * the global. */ export function objectFactoryGlobalType(lowerer: Lowerer, expr: ts.NewExpression): IrType | null { - return isObjectFactoryNew(lowerer, expr) ? DYN : null; + return jsConstructorFunctionOf(lowerer, expr) !== null ? DYN : null; } export function objectFactorySignature(lowerer: Lowerer, expr: ts.NewExpression): FnSig | null { diff --git a/packages/compiler/src/frontend/lowering/overload-choice.ts b/packages/compiler/src/frontend/lowering/overload-choice.ts new file mode 100644 index 000000000..f973ccf2e --- /dev/null +++ b/packages/compiler/src/frontend/lowering/overload-choice.ts @@ -0,0 +1,197 @@ +import * as ts from "../ts7/adapter.js"; +import type { Lowerer } from "./lowerer.js"; +import { typeEquals, type IrType } from "../../ir/ir.js"; +import { isJsSourceFile } from "../program.js"; +import { isThisParameter } from "./call-signatures.js"; +import { hasExplicitJsDocReturn } from "./function-returns.js"; +import { hasJsTypeAnnotation } from "./lower-stmts.js"; + +/** Whether a checked-dynamic argument leaves a JS call's overload + * undecided. + * + * The checker resolves an overloaded call whose argument is `any` (an + * untyped parameter, a value read from one) to the FIRST overload that + * argument satisfies, but the value can match any overload at run time: + * `generateKeyPairSync("ec", options)` answers KeyObjects or encoded + * strings by the options' contents, `digest(encoding)` a Buffer when the + * encoding is undefined. When another overload the call could take at + * run time returns a different type, the resolved overload's return is a + * guess, and the result is checked-dynamic instead. + * + * An overload is reachable when the arguments fit its arity (trailing + * checked-dynamic or `undefined` arguments may be absent) and every + * statically typed argument is assignable to its parameter. Generic + * overloads whose return cannot be mapped without instantiation do not + * count, so the resolved instantiation stands for them. */ +export function dynamicOverloadChoice( + lowerer: Lowerer, + expr: ts.CallExpression | ts.NewExpression, +): boolean { + return cachedAnswer(lowerer, expr, () => computeDynamicOverloadChoice(lowerer, expr)); +} + +/** Whether a JavaScript value's checker type rests on an undecided + * overload (dynamicOverloadChoice): the call itself, an awaited one, a + * call to an unannotated JS function returning such a value, or a read of + * an unannotated JS binding initialized with one (destructured or not). + * Such a value is checked-dynamic wherever the checker's inference would + * otherwise carry the guess into a binding or a return. */ +export function guessedOverloadValue(lowerer: Lowerer, expr: ts.Expression): boolean { + while (ts.isParenthesizedExpression(expr) || ts.isAwaitExpression(expr)) expr = expr.expression; + if (ts.isCallExpression(expr) || ts.isNewExpression(expr)) { + if (!isJsSourceFile(ts.getSourceFileOfNode(expr))) return false; + if (dynamicOverloadChoice(lowerer, expr)) return true; + const decl = lowerer.checker.getResolvedSignature(expr)?.declaration; + return ( + decl !== undefined && + (ts.isFunctionDeclaration(decl) || + ts.isFunctionExpression(decl) || + ts.isArrowFunction(decl)) && + isJsSourceFile(ts.getSourceFileOfNode(decl)) && + returnsGuessedOverloadValue(lowerer, decl) + ); + } + return ts.isIdentifier(expr) && guessedOverloadIdentifier(lowerer, expr); +} + +/** Whether a JS identifier, as a binding's declared name or as a read, + * names a guessedOverloadBinding. */ +export function guessedOverloadIdentifier(lowerer: Lowerer, node: ts.Identifier): boolean { + return cachedAnswer(lowerer, node, () => { + const parent = node.parent; + if ( + parent !== undefined && + (ts.isVariableDeclaration(parent) || ts.isBindingElement(parent)) && + parent.name === node + ) + return guessedOverloadBinding(lowerer, parent); + const decl = lowerer.checker.getSymbolAtLocation(node)?.valueDeclaration; + return decl !== undefined && guessedOverloadBinding(lowerer, decl); + }); +} + +/** Whether `decl` is an unannotated JS variable or destructured binding + * whose declaration's initializer is a guessedOverloadValue. */ +function guessedOverloadBinding(lowerer: Lowerer, decl: ts.Node): boolean { + let root: ts.Node | undefined = decl; + while (root && ts.isBindingElement(root) && root.parent && ts.isBindingPattern(root.parent)) { + root = root.parent.parent; + } + if (!root) return false; + if (!ts.isVariableDeclaration(root) || !root.initializer || root.type) return false; + if (!isJsSourceFile(ts.getSourceFileOfNode(root)) || hasJsTypeAnnotation(root)) return false; + const init = root.initializer; + return cachedAnswer(lowerer, root, () => guessedOverloadValue(lowerer, init)); +} + +/** Whether a return of the unannotated JS function `decl` itself is a + * guessedOverloadValue. */ +export function returnsGuessedOverloadValue( + lowerer: Lowerer, + decl: ts.SignatureDeclaration, +): boolean { + if (decl.type || decl.asteriskToken || hasExplicitJsDocReturn(decl)) return false; + return cachedAnswer(lowerer, decl, () => { + if (ts.isArrowFunction(decl) && decl.body && !ts.isBlock(decl.body)) { + return guessedOverloadValue(lowerer, decl.body); + } + let found = false; + const scan = (node: ts.Node): void => { + if (found || (node !== decl && ts.isFunctionLike(node))) return; + if (ts.isReturnStatement(node) && node.expression) { + found = guessedOverloadValue(lowerer, node.expression); + } + ts.forEachChild(node, scan); + }; + scan(decl); + return found; + }); +} + +/** One answer per node; a node asked again while its answer is being + * computed (recursive functions, self-referencing bindings) answers + * false. */ +function cachedAnswer(lowerer: Lowerer, node: ts.Node, compute: () => boolean): boolean { + const cached = lowerer.dynamicOverloadAnswers.get(node); + if (cached !== undefined) return cached; + lowerer.dynamicOverloadAnswers.set(node, false); + const answer = compute(); + lowerer.dynamicOverloadAnswers.set(node, answer); + return answer; +} + +function computeDynamicOverloadChoice( + lowerer: Lowerer, + expr: ts.CallExpression | ts.NewExpression, +): boolean { + // TypeScript sources keep the resolved overload: their reads validate + // the value against it where it is used. + if (!isJsSourceFile(ts.getSourceFileOfNode(expr))) return false; + const args = expr.arguments?.nodes ?? []; + if (args.length === 0 || args.some(ts.isSpreadElement)) return false; + const dynamic = args.map((arg) => checkedDynamicArgument(lowerer, arg)); + if (!dynamic.includes(true)) return false; + const checker = lowerer.checker; + const calleeType = checker.getTypeAtLocation(expr.expression); + const signatures = ts.isNewExpression(expr) + ? checker.getConstructSignatures(calleeType) + : checker.getCallSignatures(calleeType); + if (signatures.length < 2) return false; + const resolved = checker.getResolvedSignature(expr); + if (!resolved) return false; + const resolvedReturn = lowerer.mapTypeOf(checker.getReturnTypeOfSignature(resolved)); + if (resolvedReturn === null) return false; + // Arguments that may be absent at run time: the trailing run of + // checked-dynamic and `undefined` arguments. + let present = args.length; + while (present > 0 && (dynamic[present - 1] || isUndefinedArgument(args[present - 1]!))) { + present--; + } + for (const signature of signatures) { + const decl = signature.declaration; + if (!decl || decl === resolved.declaration || !ts.isFunctionLike(decl)) continue; + const generic = signature.getTypeParameters().length > 0; + const params = decl.parameters.nodes.filter((param) => !isThisParameter(param)); + const required = params.filter( + (param) => !param.questionToken && !param.initializer && !param.dotDotDotToken, + ).length; + const maximum = params.some((param) => param.dotDotDotToken) ? Infinity : params.length; + if (required > args.length || present > maximum) continue; + const symbols = signature.getParameters(); + let fits = true; + for (let i = 0; i < args.length && i < params.length && fits; i++) { + if (dynamic[i] || generic || params[i]!.dotDotDotToken) continue; + const symbol = symbols[i]; + if (!symbol) continue; + fits = checker.isTypeAssignableTo( + checker.getTypeAtLocation(args[i]!), + checker.getTypeOfSymbol(symbol), + ); + } + if (!fits) continue; + const candidate = lowerer.mapTypeOf(checker.getReturnTypeOfSignature(signature)); + if (candidate !== null && !holdsResult(resolvedReturn, candidate)) return true; + } + return false; +} + +/** Whether a value of the resolved overload's mapped return also holds + * every value of `candidate`: the same type, or a checked-dynamic one (a + * promise of one fulfills with anything). */ +function holdsResult(resolved: IrType, candidate: IrType): boolean { + if (resolved.kind === "dyn" || typeEquals(resolved, candidate)) return true; + return ( + resolved.kind === "promise" && + candidate.kind === "promise" && + holdsResult(resolved.inner, candidate.inner) + ); +} + +function checkedDynamicArgument(lowerer: Lowerer, arg: ts.Expression): boolean { + if (lowerer.checker.getTypeAtLocation(arg).flags & ts.TypeFlags.Any) return true; + return (lowerer.typeOf(arg).flags & ts.TypeFlags.Any) !== 0; +} + +function isUndefinedArgument(arg: ts.Expression): boolean { + return (ts.isIdentifier(arg) && arg.text === "undefined") || ts.isVoidExpression(arg); +} diff --git a/packages/compiler/src/frontend/lowering/surfaces.ts b/packages/compiler/src/frontend/lowering/surfaces.ts index 44564f2f1..90acabce6 100644 --- a/packages/compiler/src/frontend/lowering/surfaces.ts +++ b/packages/compiler/src/frontend/lowering/surfaces.ts @@ -6,6 +6,20 @@ function unparenthesized(e: ts.Expression): ts.Expression { while (ts.isParenthesizedExpression(e)) e = e.expression; return e; } + +/** `e` without parentheses and type-only wrappers (as, satisfies, type + * assertions, non-null assertions), which change no runtime value. */ +function withoutTypeCasts(e: ts.Expression): ts.Expression { + while ( + ts.isParenthesizedExpression(e) || + ts.isAsExpression(e) || + ts.isSatisfiesExpression(e) || + ts.isTypeAssertion(e) || + ts.isNonNullExpression(e) + ) + e = e.expression; + return e; +} import { UNSUPPORTED } from "../../diagnostics/diagnostic.js"; import { BOOL, @@ -1563,6 +1577,7 @@ export const BUILTIN_MODULE_FNS: Record< generateKeyPair: { fn: "crypto.generateKeyPairCb", params: [], result: VOID }, sign: { fn: "crypto.signOneshot", params: [], result: DYN }, verify: { fn: "crypto.verifyOneshot", params: [], result: DYN }, + diffieHellman: { fn: "crypto.diffieHellman", params: [], result: DYN }, pbkdf2Sync: { fn: "crypto.pbkdf2", params: [BYTES_U8, BYTES_U8, F64, F64, STRING], @@ -1620,6 +1635,12 @@ export const BUILTIN_MODULE_FNS: Record< unzip: { fn: "zlib.unzipCb", params: [], result: VOID }, unzipSync: { fn: "zlib.unzipSync", params: [BYTES_U8], result: BYTES_U8 }, crc32: { fn: "zlib.crc32", params: [], result: F64 }, + // Brotli (special-cased in lowerZlibModuleCall): every form takes the + // options entries with Node's BROTLI_ENCODE or BROTLI_DECODE mode. + brotliCompress: { fn: "zlib.cbOpts", params: [], result: VOID }, + brotliCompressSync: { fn: "zlib.syncOpts", params: [], result: BYTES_U8 }, + brotliDecompress: { fn: "zlib.cbOpts", params: [], result: VOID }, + brotliDecompressSync: { fn: "zlib.syncOpts", params: [], result: BYTES_U8 }, // The stream factories (special-cased in lowerZlibModuleCall): each // answers a runtime Transform stream over the zlib engine. createDeflate: { fn: "zlib.streamNew", params: [], result: ZLIB_STREAM }, @@ -1629,6 +1650,8 @@ export const BUILTIN_MODULE_FNS: Record< createDeflateRaw: { fn: "zlib.streamNew", params: [], result: ZLIB_STREAM }, createInflateRaw: { fn: "zlib.streamNew", params: [], result: ZLIB_STREAM }, createUnzip: { fn: "zlib.streamNew", params: [], result: ZLIB_STREAM }, + createBrotliCompress: { fn: "zlib.streamNew", params: [], result: ZLIB_STREAM }, + createBrotliDecompress: { fn: "zlib.streamNew", params: [], result: ZLIB_STREAM }, }, url: { fileURLToPath: { @@ -1666,6 +1689,7 @@ export const BUILTIN_MODULE_FNS: Record< resolve: { fn: "url.legacyResolve", params: [DYN, DYN], result: STRING }, domainToASCII: { fn: "url.domainToASCII", params: [DYN], result: STRING }, domainToUnicode: { fn: "url.domainToUnicode", params: [DYN], result: STRING }, + urlToHttpOptions: { fn: "url.toHttpOptions", params: [DYN], result: DYN }, }, child_process: { // spawnSync's and spawn's call completions are entirely special-cased @@ -2070,7 +2094,16 @@ export const BUILTIN_MODULE_FN_ALIASES: Record< "fs.readFdEncoded", "fs.readFdSync", "fs.readFdSyncBytes", + // The runtime encoding's check before a URL path converts + // (fs-paths.ts). + "fs.checkReadEncoding", ], + // The string source's check before a URL destination converts + // (fs-paths.ts). + copyFileSync: ["fs.checkPath"], + renameSync: ["fs.checkPath"], + copyFile: ["fs.checkPath"], + rename: ["fs.checkPath"], readlinkSync: ["fs.readlinkSyncStr", "fs.readlinkSyncBuffer"], // The bytes-data form and the { mode } options form. writeFileSync: ["fs.writeFileSyncBytes", "fs.writeFileModeSync"], @@ -3033,17 +3066,6 @@ export function builtinModulesArrayLit(loc: { file: string; start: number; end: }; } -/** Member-specific hints for RECOGNIZED builtin modules whose member has - * no lowering. The default-options one-shot zlib/deflate-raw/gzip codecs - * lower for strings and Buffers; the remaining surface points at them. */ -const ZLIB_HINT = - "the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, " + - "and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface"; -/** Brotli has no codec in the static runtime (zlib is the only vendored - * compression library). */ -const BROTLI_HINT = - "Brotli needs a Brotli codec, which the static runtime does not vendor — " + ZLIB_HINT; - const ASSERT_MODULE_HINTS: Record = { ifError: "test explicitly instead: assert.strictEqual(err, null) / " + @@ -3101,23 +3123,10 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record< "createSign, createVerify, createECDH, and createDiffieHellman", ]), ), - diffieHellman: - "crypto.diffieHellman takes DH, EC or X25519 KeyObjects and has no lowering; " + - "the objects of createDiffieHellman, getDiffieHellman and createECDH compute shared secrets", setFips: "a compiled binary has no FIPS provider to enable, and Node itself throws on setFips(true) " + "in a non-FIPS build — getFips() answers 0 here", }, - zlib: { - brotliCompressSync: BROTLI_HINT, - brotliDecompressSync: BROTLI_HINT, - brotliCompress: BROTLI_HINT, - brotliDecompress: BROTLI_HINT, - createBrotliCompress: BROTLI_HINT, - createBrotliDecompress: BROTLI_HINT, - BrotliCompress: BROTLI_HINT, - BrotliDecompress: BROTLI_HINT, - }, http2: { connect: "HTTP/2 client sessions have no lowering — the lowered http2 surface is the SERVER side: " + @@ -3440,6 +3449,20 @@ export function stdlibGlobalNameOf(lowerer: Lowerer, expr: ts.Expression): strin return ts.symbolName(resolved) === "global" ? "globalThis" : ts.symbolName(resolved); } if (ts.isPropertyAccessExpression(expr) && !expr.questionDotToken) { + // The buffer module's Buffer (`require("buffer").Buffer`, `buffer_1.Buffer`) + // is the global constructor. + if (bufferNamespaceMember(lowerer, expr)) return "Buffer"; + // A cast of the global object (`(globalThis as { Buffer?: B }).Buffer`, + // `(globalThis as any).Buffer`) changes only its static type: the + // member is the global object's own. + const uncast = withoutTypeCasts(expr.expression); + if (uncast !== unparenthesized(expr.expression)) { + if (!ts.isIdentifier(uncast) || stdlibGlobalNameOf(lowerer, uncast) !== "globalThis") + return null; + const member = lowerer.checker.getPropertyOfType(lowerer.typeOf(uncast), expr.name.text); + if (!member || !lowerer.isStdlibSymbol(member)) return null; + return ts.symbolName(member) === "global" ? "globalThis" : ts.symbolName(member); + } if (stdlibGlobalNameOf(lowerer, expr.expression) !== "globalThis") return null; // Through a detection chain the checker types the receiver as the // union of every arm; the selected arm (the global object) owns the @@ -3572,9 +3595,28 @@ export function stdlibGlobalAliasDecl( const symbol = lowerer.checker.getSymbolAtLocation(nameNode); if (!symbol) return false; lowerer.stdlibGlobalAliases.set(symbol, name); + if (init !== undefined && castGlobalAliasSource(lowerer, init)) + lowerer.castGlobalAliases.add(symbol); return true; } +/** True when an alias initializer reads its global through a cast of the + * global object (`(globalThis as { Buffer?: B }).Buffer`), directly or as + * the left operand of `||` (stdlibGlobalAliasNameOf's guard), or copies an + * alias made that way. Such an alias carries the cast's static type. */ +function castGlobalAliasSource(lowerer: Lowerer, init: ts.Expression): boolean { + let source = unparenthesized(init); + if (ts.isBinaryExpression(source) && source.operatorToken.kind === ts.SyntaxKind.BarBarToken) + source = unparenthesized(source.left); + if (ts.isPropertyAccessExpression(source)) + return withoutTypeCasts(source.expression) !== unparenthesized(source.expression); + if (ts.isIdentifier(source)) { + const symbol = lowerer.checker.getSymbolAtLocation(source); + return symbol !== undefined && lowerer.castGlobalAliases.has(symbol); + } + return false; +} + /** True iff the symbol is declared ONLY by the adopted @types/node * surface (no es-lib or shipped-ambient declaration merges in) — chooses * the SC2020 fence's wording: "typed by @types/node", not "standard diff --git a/packages/compiler/src/frontend/lowering/symbol-fields.ts b/packages/compiler/src/frontend/lowering/symbol-fields.ts index 56718615e..cdfb9e1b8 100644 --- a/packages/compiler/src/frontend/lowering/symbol-fields.ts +++ b/packages/compiler/src/frontend/lowering/symbol-fields.ts @@ -77,6 +77,8 @@ export function fenceSymbolFieldCopy( } export function classSymbolKeyOf(lowerer: Lowerer, key: ts.Expression): ClassSymbolKey | null { + const wellKnown = wellKnownSymbolKeyOf(lowerer, key); + if (wellKnown !== undefined) return wellKnown; if ( !ts.isIdentifier(key) && (!ts.isPropertyAccessExpression(key) || key.questionDotToken || !ts.isIdentifier(key.name)) @@ -90,6 +92,53 @@ export function classSymbolKeyOf(lowerer: Lowerer, key: ts.Expression): ClassSym return sym ? classSymbolKeyOfSymbol(lowerer, sym) : null; } +/** Well-known symbols a class field may be keyed by: those no protocol + * consults on an instance, or whose protocol the lowering reads from the + * field (Symbol.toStringTag, by the class string conversions). A field + * keyed by any other well-known symbol (Symbol.iterator, toPrimitive, + * hasInstance, ...) would be skipped by its protocol, so it keeps the + * computed-key refusal. */ +const FIELD_WELL_KNOWN_SYMBOLS: ReadonlySet = new Set(["toStringTag"]); + +/** The key of `Symbol.` on the standard global: undefined when the + * expression is not one, null when it is a well-known symbol a field may + * not be keyed by. The identity is the lib's declaration of the property, + * one symbol for every program reference. */ +function wellKnownSymbolKeyOf( + lowerer: Lowerer, + key: ts.Expression, +): ClassSymbolKey | null | undefined { + while (ts.isParenthesizedExpression(key)) key = key.expression; + if ( + !ts.isPropertyAccessExpression(key) || + key.questionDotToken || + !ts.isIdentifier(key.expression) || + !ts.isIdentifier(key.name) || + !lowerer.isStdlibGlobal(key.expression, "Symbol") + ) + return undefined; + const name = key.name.text; + const sym = lowerer.checker.getSymbolAtLocation(key.name); + if (!sym || !(lowerer.typeOf(key).flags & ts.TypeFlags.UniqueESSymbol)) return undefined; + if (!FIELD_WELL_KNOWN_SYMBOLS.has(name)) return null; + return { sym, identity: sym, fieldName: wellKnownSymbolFieldName(name) }; +} + +/** The layout name of a field keyed by the well-known `Symbol.`: a + * reserved symbol-field name, printed as Node spells the key. */ +export function wellKnownSymbolFieldName(name: string): string { + return `${FIELD_PREFIX}Symbol(Symbol.${name})`; +} + +/** The well-known symbol a field name is keyed by (`toStringTag`), or null. */ +export function wellKnownSymbolOfField(field: string): string | null { + const match = /^%symbol:Symbol\(Symbol\.(\w+)\)$/.exec(field); + return match === null ? null : match[1]!; +} + +/** The layout name of a `Symbol.toStringTag` field. */ +export const TO_STRING_TAG_FIELD = wellKnownSymbolFieldName("toStringTag"); + /** A literal Symbol()/Symbol.for() initialized once at module scope. The * whole-file proof also covers var declarations emitted by bundlers. Keys * cannot be used before initialization: a computed class field evaluates diff --git a/packages/compiler/src/frontend/pipeline.ts b/packages/compiler/src/frontend/pipeline.ts index 4e6a1c0f5..b5771dedd 100644 --- a/packages/compiler/src/frontend/pipeline.ts +++ b/packages/compiler/src/frontend/pipeline.ts @@ -252,7 +252,8 @@ function detectAutoPackages( } else if (mode === "lib" && !viaTransitive) { // CJS packages spell their dep edges as top-level requires; the // import-statement scan alone would miss every one of them. - for (const req of requiresOf(stmt)) edges.push({ spec: req.spec, loc: locOf(req.node) }); + for (const req of requiresOf(stmt, load.program)) + edges.push({ spec: req.spec, loc: locOf(req.node) }); } } // Transitive selection counts every literal require() and import() diff --git a/packages/compiler/src/frontend/program-analysis.ts b/packages/compiler/src/frontend/program-analysis.ts index e49b9307e..d4ec9fa98 100644 --- a/packages/compiler/src/frontend/program-analysis.ts +++ b/packages/compiler/src/frontend/program-analysis.ts @@ -1,4 +1,5 @@ import type { + CallExpression, ExportDeclaration, Identifier, SourceFile, @@ -30,6 +31,13 @@ export class ProgramAnalysis { readonly requireEarlyReferences = new Map(); /** Per file: the first top-level statement that can run user code. */ readonly requireFirstRunnable = new Map(); + /** Per file: split require bindings (`var m; m = require("./x")`), from + * declaration to the require call of their one assignment + * (splitRequireBindings7). */ + readonly splitRequireBindings = new Map< + SourceFile, + ReadonlyMap + >(); clear(): void { this.createRequireReasons.clear(); @@ -41,5 +49,6 @@ export class ProgramAnalysis { this.moduleEarlyBindings = null; this.requireEarlyReferences.clear(); this.requireFirstRunnable.clear(); + this.splitRequireBindings.clear(); } } diff --git a/packages/compiler/src/frontend/program.test.ts b/packages/compiler/src/frontend/program.test.ts index bdd6260ed..6a068353a 100644 --- a/packages/compiler/src/frontend/program.test.ts +++ b/packages/compiler/src/frontend/program.test.ts @@ -371,15 +371,60 @@ test("repeated cycle edges reuse a file's binding index", () => { test.for([ [ - // A shared instance built by a user constructor: the reads of it in - // the partner's function can run early; the class cannot. + // A shared instance built by a user constructor that calls the + // partner's function: its read of the instance can run early. + { + "main.ts": 'import { shared } from "./a.ts"; console.log(shared.n);', + "a.ts": + 'import { f } from "./b.ts"; export class C { n = 1; constructor() { console.log(f()); } } export const shared = new C();', + "b.ts": 'import { shared } from "./a.ts"; export function f(): number { return shared.n; }', + }, + ["shared"], + ], + [ + // User code runs while the instance is built, but nothing it reaches + // calls the partner's function before the declaration completed. { "main.ts": 'import { shared } from "./a.ts"; console.log(shared.n);', "a.ts": 'import { f } from "./b.ts"; export class C { n = 1; } export const shared = new C(); console.log(f());', "b.ts": 'import { shared } from "./a.ts"; export function f(): number { return shared.n; }', }, - ["shared"], + [], + ], + [ + // The partner's top level runs first and calls a hoisted function of + // the importer that reads a const declared later; a second function + // reads another const but nothing calls it early. + { + "main.ts": 'import { late } from "./a.ts"; console.log(late());', + "a.ts": + 'import { probe } from "./b.ts"; export function read(): string { return name; } export function late(): string { return other + probe; } export const name = "n"; export const other = "o";', + "b.ts": + 'import { read } from "./a.ts"; function attempt(): string { try { return read(); } catch { return "tdz"; } } export const probe = attempt();', + }, + ["name"], + ], + [ + // The partner's class calls a method through `this` in a static + // initializer; the method reads a const of the importer declared later. + { + "main.ts": 'import { name } from "./a.ts"; console.log(name);', + "a.ts": 'import { Probe } from "./b.ts"; export const name = "n"; console.log(Probe.value);', + "b.ts": + 'import { name } from "./a.ts"; export class Probe { static read(): string { return name; } static value = this.read(); }', + }, + ["name"], + ], + [ + // The same through a static block of a class stored by a const. + { + "main.ts": 'import { name } from "./a.ts"; console.log(name);', + "a.ts": 'import { Box } from "./b.ts"; export const name = "n"; console.log(Box.value);', + "b.ts": + 'import { name } from "./a.ts"; export const Box = class { static value = ""; static read(): string { return name; } static { this.value = this.read(); } };', + }, + ["name"], ], [ // The partner's top level reads the importer's const before it ran. diff --git a/packages/compiler/src/frontend/program.ts b/packages/compiler/src/frontend/program.ts index 932ac7054..bf75127c4 100644 --- a/packages/compiler/src/frontend/program.ts +++ b/packages/compiler/src/frontend/program.ts @@ -1378,6 +1378,11 @@ function suppressedJsStrictness7(d: ts.Diagnostic): boolean { if (JS_ANY_OPERATOR_CODES.has(d.code)) { return ts.flattenDiagnosticMessageText(d, "\n").includes("'any'"); } + // The deprecated Buffer(...) call without `new`, which Node's Buffer + // function accepts (containers/bytes.ts lowers it). + if (d.code === 2348) { + return ts.flattenDiagnosticMessageText(d, "\n").includes("'BufferConstructor'"); + } return false; } @@ -1418,17 +1423,31 @@ function isRequireStatement7(stmt: ts.Statement): boolean { ); } +/** A require() occurrence of a top-level statement. `split` marks the + * assignment of a split require binding (splitRequireBindings7): `node` + * is its require call and `decl` the binding's declaration. */ +interface RequireOccurrence7 { + spec: string; + node: ts.Node; + decl: ts.VariableDeclaration | null; + split?: true; +} + /** The require() occurrences of one top-level statement: the declaration - * forms and the bare side-effect call. Empty for everything else. */ -function requiresOf7( - stmt: ts.Statement, -): { spec: string; node: ts.Node; decl: ts.VariableDeclaration | null }[] { + * forms, the bare side-effect call, and the assignment of a split require + * binding. Empty for everything else. */ +function requiresOf7(stmt: ts.Statement, program: ts.Program): RequireOccurrence7[] { if (ts.isExpressionStatement(stmt)) { + const split = splitRequireOfStatement7(program, stmt); + if (split !== null) + return [ + { spec: requireSpecOf7(split.call)!, node: split.call, decl: split.decl, split: true }, + ]; const spec = requireSpecOf7(stmt.expression); return spec !== null ? [{ spec, node: stmt.expression, decl: null }] : []; } if (!ts.isVariableStatement(stmt)) return []; - const out: { spec: string; node: ts.Node; decl: ts.VariableDeclaration | null }[] = []; + const out: RequireOccurrence7[] = []; for (const decl of stmt.declarationList.declarations.nodes) { const spec = decl.initializer !== undefined ? requireSpecOf7(decl.initializer) : null; if (spec !== null) out.push({ spec, node: decl, decl }); @@ -1436,6 +1455,172 @@ function requiresOf7( return out; } +/** CommonJS require bindings split into a declaration and one later + * assignment (`var m; m = require("./x")`): a top-level `var` or `let` + * declaration without initializer in a CommonJS file, assigned by exactly one + * top-level `m = require("")` statement of the require global over + * a compiled program module, and written nowhere else. The binding holds the + * module's exports from the assignment on, as `const m = require("./x")` + * does, so preflight treats the assignment as that require: its module edge, + * its cycle admission and its early-read fence. The lowering's recognizer + * (splitRequireBindingOf in cjs-inline-require.ts) accepts only these + * bindings. Keyed by declaration, with the assignment's require call. */ +function splitRequireBindings7( + program: ts.Program, + sf: ts.SourceFile, +): ReadonlyMap { + const memo = program.analysis.splitRequireBindings; + const cached = memo.get(sf); + if (cached !== undefined) return cached; + const out = new Map(); + memo.set(sf, out); + if (sf.isDeclarationFile || !isCjsJsFile7(sf, program) || !sf.text.includes("require")) + return out; + const checker = program.getTypeChecker(); + const stmts = sf.statements.nodes; + const candidates = new Map< + ts.Symbol, + { decl: ts.VariableDeclaration; call: ts.CallExpression | null; assigns: number } + >(); + for (const stmt of stmts) { + if (!ts.isVariableStatement(stmt)) continue; + if ((stmt.declarationList.flags & (ts.NodeFlags.Const | ts.NodeFlags.Using)) !== 0) continue; + for (const decl of stmt.declarationList.declarations.nodes) { + if (!ts.isIdentifier(decl.name) || decl.initializer !== undefined) continue; + const symbol = checker.getSymbolAtLocation(decl.name); + if (symbol === undefined || symbol.declarations.length !== 1) continue; + candidates.set(symbol, { decl, call: null, assigns: 0 }); + } + } + if (candidates.size === 0) return out; + // The one top-level `m = ...` statement, whose right side is the require. + for (const stmt of stmts) { + if (!ts.isExpressionStatement(stmt) || !ts.isBinaryExpression(stmt.expression)) continue; + const assign = stmt.expression; + if (assign.operatorToken.kind !== ts.SyntaxKind.EqualsToken || !ts.isIdentifier(assign.left)) + continue; + const symbol = checker.getSymbolAtLocation(assign.left); + const candidate = symbol !== undefined ? candidates.get(symbol) : undefined; + if (candidate === undefined) continue; + candidate.assigns++; + let right = assign.right; + while (ts.isParenthesizedExpression(right)) right = right.expression; + if (ts.isCallExpression(right) && splitRequireTarget7(program, sf, right)) + candidate.call = right; + } + const names = new Set(); + for (const candidate of candidates.values()) { + if (candidate.assigns === 1 && candidate.call !== null) + names.add((candidate.decl.name as ts.Identifier).text); + } + if (names.size === 0) return out; + // Exactly one write in the whole file: the assignment itself. + const writes = new Map(); + ts.walkPreorder(sf, (node) => { + if (!ts.isIdentifier(node) || !names.has(node.text)) return undefined; + const parent = node.parent; + const symbol = + parent !== undefined && ts.isShorthandPropertyAssignment(parent) && parent.name === node + ? (checker.getShorthandAssignmentValueSymbol(parent) ?? checker.getSymbolAtLocation(node)) + : checker.getSymbolAtLocation(node); + if (symbol === undefined || !candidates.has(symbol) || !isWriteReference7(node)) + return undefined; + writes.set(symbol, (writes.get(symbol) ?? 0) + 1); + return undefined; + }); + for (const [symbol, candidate] of candidates) { + if (candidate.assigns === 1 && candidate.call !== null && writes.get(symbol) === 1) + out.set(candidate.decl, candidate.call); + } + return out; +} + +/** True when `call` is a require of the CommonJS global over a compiled + * program module: not a builtin, a JSON document or a declaration file. */ +function splitRequireTarget7( + program: ts.Program, + sf: ts.SourceFile, + call: ts.CallExpression, +): boolean { + const spec = requireSpecOf7(call); + if (spec === null || canonicalBuiltinModule(spec) !== null) return false; + if (requireCallBindingKind7(program, call) !== "ambient") return false; + const dep = + resolveImport7(program, sf, spec, "require") ?? npmStaticDepSf7(program, sf, spec, "require"); + return dep !== null && !dep.isDeclarationFile && !dep.fileName.endsWith(".json"); +} + +/** The split require binding a top-level statement assigns, or null. */ +function splitRequireOfStatement7( + program: ts.Program, + stmt: ts.Statement, +): { decl: ts.VariableDeclaration; call: ts.CallExpression } | null { + if ( + !ts.isExpressionStatement(stmt) || + !ts.isBinaryExpression(stmt.expression) || + !ts.isSourceFile(stmt.parent) + ) + return null; + let right = stmt.expression.right; + while (ts.isParenthesizedExpression(right)) right = right.expression; + for (const [decl, call] of splitRequireBindings7(program, stmt.parent)) + if (call === right) return { decl, call }; + return null; +} + +/** The top-level statement of a split require binding's assignment, from + * its require call (`m = require("./x")` or `m = (require("./x"))`). */ +function splitRequireStatementOf7(call: ts.CallExpression): ts.Node | undefined { + let node: ts.Node = call; + while (node.parent !== undefined && ts.isParenthesizedExpression(node.parent)) node = node.parent; + return node.parent?.parent; +} + +/** The require call of a split require binding's declaration, or null. */ +function splitRequireCallOf7( + program: ts.Program, + decl: ts.VariableDeclaration, +): ts.CallExpression | null { + return splitRequireBindings7(program, ts.getSourceFileOfNode(decl)).get(decl) ?? null; +} + +/** True when `id` is written: an assignment target (compound and + * destructuring included), an update operand, or a for-in/of target. */ +function isWriteReference7(id: ts.Identifier): boolean { + let n: ts.Node = id; + for (;;) { + const p: ts.Node | undefined = n.parent; + if (p === undefined) return false; + if (ts.isBinaryExpression(p)) { + const k = p.operatorToken.kind; + return ( + p.left === n && k >= ts.SyntaxKind.FirstAssignment && k <= ts.SyntaxKind.LastAssignment + ); + } + if (ts.isPostfixUnaryExpression(p) || ts.isPrefixUnaryExpression(p)) { + return ( + (p.operator === ts.SyntaxKind.PlusPlusToken || + p.operator === ts.SyntaxKind.MinusMinusToken) && + p.operand === n + ); + } + if (ts.isForOfStatement(p) || ts.isForInStatement(p)) return p.initializer === n; + if ( + ts.isParenthesizedExpression(p) || + ts.isArrayLiteralExpression(p) || + ts.isSpreadElement(p) || + ts.isSpreadAssignment(p) || + ts.isShorthandPropertyAssignment(p) || + ts.isObjectLiteralExpression(p) || + (ts.isPropertyAssignment(p) && p.initializer === n) + ) { + n = p; + continue; + } + return false; + } +} + /** `true`, or `!0`, its minified spelling. */ function isTrueLiteral(expr: ts.Expression): boolean { return ( @@ -1575,7 +1760,7 @@ function purePrefixDecl7(decl: ts.VariableDeclaration): boolean { function purePrefixStmt7(program: ts.Program, s: ts.Statement): boolean { if (ts.isEmptyStatement(s) || ts.isFunctionDeclaration(s)) return true; if (ts.isExpressionStatement(s) && ts.isStringLiteral(s.expression)) return true; // directive - if (isRequireStatement7(s)) return true; + if (isRequireStatement7(s) || splitRequireOfStatement7(program, s) !== null) return true; if (isCjsJsFile7(ts.getSourceFileOfNode(s), program)) { if (ts.isExpressionStatement(s) && isEsModuleStamp(s.expression)) { const call = s.expression as ts.CallExpression; @@ -1702,12 +1887,20 @@ function requireEarlyReference7( const cached = memo.get(decl); if (cached !== undefined) return cached; let found: ts.Identifier | null = null; - const stmt = decl.parent?.parent; const sf = ts.getSourceFileOfNode(decl); - if (stmt !== undefined && ts.isVariableStatement(stmt) && ts.isSourceFile(stmt.parent)) { + // A split require binding is bound at its assignment statement. + const splitCall = splitRequireCallOf7(program, decl); + const stmt = splitCall !== null ? splitRequireStatementOf7(splitCall) : decl.parent?.parent; + if ( + stmt !== undefined && + (ts.isVariableStatement(stmt) || splitCall !== null) && + ts.isSourceFile(stmt.parent) + ) { const stmts = sf.statements.nodes; - const k = stmts.indexOf(stmt); - const declarations = stmt.declarationList.declarations.nodes; + const k = stmts.indexOf(stmt as ts.Statement); + const declarations = ts.isVariableStatement(stmt) + ? stmt.declarationList.declarations.nodes + : []; // Earlier declarators run too: `const x = read(), dep = require('./dep')`. const precedingDecls = declarations.slice(0, declarations.indexOf(decl)); let firstRunnable = program.analysis.requireFirstRunnable.get(sf); @@ -1760,6 +1953,12 @@ export function requireBindingEarlyUse7( if (sym) bindings.add(sym); } if (bindings.size === 0) return null; + // A split require binding's assignment writes the binding; it reads nothing. + let splitWrite: ts.Node | null = splitRequireCallOf7(program, decl); + while (splitWrite !== null && ts.isParenthesizedExpression(splitWrite.parent)) + splitWrite = splitWrite.parent; + splitWrite = + splitWrite !== null && ts.isBinaryExpression(splitWrite.parent) ? splitWrite.parent.left : null; const stmts = sf.statements.nodes; const cjs = isCjsJsFile7(sf, program); const symbolOf = (id: ts.Identifier): ts.Symbol | undefined => { @@ -1983,7 +2182,7 @@ export function requireBindingEarlyUse7( const sym = symbolOf(n); if (sym === undefined) return undefined; if (bindings.has(sym)) { - if (exactEarlyRead?.(n) === true) return undefined; + if (n === splitWrite || exactEarlyRead?.(n) === true) return undefined; hit = n; return "stop"; } @@ -3494,7 +3693,7 @@ function cjsCycleRefusal7( } // Bindings of other members must stay unreachable from top-level code. for (const st of stmts) { - for (const req of requiresOf7(st)) { + for (const req of requiresOf7(st, program)) { if (req.decl === null) continue; const dep = resolveImport7(program, m, req.spec, "require"); if (dep === null || !members.has(dep)) continue; @@ -3728,10 +3927,14 @@ function exactCycleRefusal7( if (ts.isExportAssignment(stmt) && !stmt.isExportEquals) { return `the default export expression at ${lineOf(stmt)} has no initialization state inside the cycle`; } + // `export * from` binds each name to the declaring member's own + // binding, like a named re-export, so only `export * as ns` (a + // namespace object of a member) has state of its own. if ( ts.isExportDeclaration(stmt) && !stmt.isTypeOnly && - (stmt.exportClause === undefined || ts.isNamespaceExport(stmt.exportClause)) + stmt.exportClause !== undefined && + ts.isNamespaceExport(stmt.exportClause) ) { const dep = targetOf(stmt); if (dep !== null && members.has(dep)) { @@ -3903,22 +4106,7 @@ function computeCycleEarlyBindings7( const evaluation = known ? comp.slice().sort((a, b) => position.get(a)! - position.get(b)!) : comp.slice(); - interface Candidate { - symbol: ts.Symbol; - name: ts.Identifier; - kind: CycleEarlyBinding["kind"]; - module: ts.SourceFile; - /** User code can run between the cycle's start and the end of the - * declaration (always true when the order is unknown). */ - exposed: boolean; - rank: number; - start: number; - end: number; - reference: ts.Identifier | null; - folded: ts.Identifier | null; - localNames: Map; - } - const candidates = new Map(); + const candidates = new Map(); const names = new Set(); let dirty = !known; evaluation.forEach((m, rank) => { @@ -3955,6 +4143,7 @@ function computeCycleEarlyBindings7( start: ts.tokenStartOfNode(stmt), end: stmt.end, reference: null, + deferredReference: null, folded: null, localNames: new Map(), }); @@ -4023,15 +4212,27 @@ function computeCycleEarlyBindings7( ref.end <= c.end ) continue; + if (inDeferredPosition7(ref)) { + if (c.exposed && c.deferredReference === null) c.deferredReference = ref; + continue; + } let early: boolean; - if (inDeferredPosition7(ref)) early = c.exposed; - else if (sf === c.module) early = ts.tokenStartOfNode(ref) < c.start; + if (sf === c.module) early = ts.tokenStartOfNode(ref) < c.start; else if (!memberSet.has(sf) || !rankOf.has(sf)) early = false; else early = !known || rankOf.get(sf)! < c.rank; if (early) c.reference = ref; } } + // With a static evaluation order, a deferred reference only runs early + // when the code that runs before the declaration completes can reach + // the function holding it. + const reached = + known && [...candidates.values()].some((c) => c.reference === null && c.deferredReference) + ? cycleReachedDeferredReferences7(program, evaluation, candidates) + : null; for (const c of candidates.values()) { + if (c.reference === null && c.deferredReference !== null) + c.reference = reached === null ? c.deferredReference : (reached.get(c.symbol) ?? null); if (c.reference === null) continue; const settled = new Set(order.filter((sf) => !rankOf.has(sf))); if (known) for (const m of evaluation.slice(c.rank + 1)) settled.add(m); @@ -4050,6 +4251,299 @@ function computeCycleEarlyBindings7( return out; } +/** Collects into `units` the callables an initializer expression stores + * (see moduleEarlyBindingsOf7), recording in `immediate` the parts of a + * stored node that run where it is evaluated. */ +function storedCallables7( + checker: ts.TypeChecker, + e: ts.Expression, + units: ts.Node[], + immediate: Map, +): void { + while ( + ts.isParenthesizedExpression(e) || + ts.isAsExpression(e) || + ts.isSatisfiesExpression(e) || + ts.isNonNullExpression(e) + ) + e = e.expression; + if (ts.isArrowFunction(e) || ts.isFunctionExpression(e)) { + units.push(e); + } else if (ts.isClassExpression(e)) { + units.push(e); + immediate.set(e, classSyncParts7(e)); + } else if (ts.isObjectLiteralExpression(e)) { + for (const p of e.properties.nodes) { + if ( + ts.isMethodDeclaration(p) || + ts.isGetAccessorDeclaration(p) || + ts.isSetAccessorDeclaration(p) + ) { + // A computed key evaluates where the literal does; a plain + // member-chain key (`[Equal.symbol]`) runs no user code. + if (!ts.isComputedPropertyName(p.name)) units.push(p); + else if (memberChainKey7(p.name.expression)) { + units.push(p); + immediate.set(p, [p.name]); + } + } else if (ts.isPropertyAssignment(p)) + storedCallables7(checker, p.initializer, units, immediate); + } + } else if (ts.isArrayLiteralExpression(e)) { + for (const el of e.elements.nodes) + if (!ts.isSpreadElement(el)) storedCallables7(checker, el, units, immediate); + } else if (ts.isCallExpression(e)) { + // `const f = wrap(callback)` where wrap only returns closures over + // the callback: it runs when the stored result is called. + e.arguments.nodes.forEach((arg, index) => { + let fn: ts.Expression = arg; + while (ts.isParenthesizedExpression(fn)) fn = fn.expression; + if ( + (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) && + returnsOnlyClosuresOver7(checker, e.expression, index) + ) + units.push(fn); + }); + } +} + +/** A binding of an import cycle and what the reference scan found. */ +interface CycleCandidate7 { + symbol: ts.Symbol; + name: ts.Identifier; + kind: CycleEarlyBinding["kind"]; + module: ts.SourceFile; + /** User code can run between the cycle's start and the end of the + * declaration (always true when the order is unknown). */ + exposed: boolean; + rank: number; + start: number; + end: number; + /** A reference read at top level before the declaration ran. */ + reference: ts.Identifier | null; + /** A deferred reference (function body, parameter default, instance + * field) when user code may run before the declaration completes. */ + deferredReference: ts.Identifier | null; + folded: ts.Identifier | null; + localNames: Map; +} + +/** For one import cycle with a static evaluation order: the first deferred + * reference to each candidate that code running before the candidate's + * declaration completes can reach, or null when the walk cannot be precise + * (top-level await, a default-exported function or class declaration). + * + * The cycle's top-level code runs in evaluation order. A stored callable + * (a function or class declaration, or a function, class or object-literal + * method stored by a variable initializer) becomes reachable once + * reachable code names its binding, through an import alias or a + * namespace member too; every other function-like in reachable code counts + * as reachable, since it may be handed to a callee. A namespace import of + * a cycle member used other than for a member read makes every stored + * callable reachable. moduleEarlyBindingsOf7 is the single-module form. */ +function cycleReachedDeferredReferences7( + program: ts.Program, + evaluation: readonly ts.SourceFile[], + candidates: ReadonlyMap, +): Map | null { + const checker = program.getTypeChecker(); + const members = new Set(evaluation); + for (const sf of evaluation) { + if (hasTopLevelAwait7(sf)) return null; + for (const stmt of sf.statements.nodes) { + if ( + (ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) && + (ts.getCombinedModifierFlags(stmt as unknown as ts.Declaration) & + ts.ModifierFlags.Default) !== + 0 + ) + return null; + } + } + const stored = new Set(); + const immediate = new Map(); + const storedNames: ts.Identifier[] = []; + const unitsOfName = new Map(); + const store = (name: ts.Identifier, units: ts.Node[]): void => { + if (units.length === 0) return; + for (const u of units) stored.add(u); + storedNames.push(name); + unitsOfName.set(name, units); + }; + for (const sf of evaluation) { + for (const stmt of sf.statements.nodes) { + if ( + (ts.getCombinedModifierFlags(stmt as unknown as ts.Declaration) & + ts.ModifierFlags.Ambient) !== + 0 + ) + continue; + if (ts.isFunctionDeclaration(stmt) && stmt.name !== undefined && stmt.body !== undefined) { + store(stmt.name, [stmt]); + } else if (ts.isClassDeclaration(stmt) && stmt.name !== undefined) { + store(stmt.name, [stmt]); + } else if (ts.isVariableStatement(stmt)) { + for (const d of stmt.declarationList.declarations.nodes) { + if (ts.isIdentifier(d.name) && d.initializer !== undefined) { + const units: ts.Node[] = []; + storedCallables7(checker, d.initializer, units, immediate); + store(d.name, units); + } + } + } + } + } + const unitsBySymbol = new Map(); + const allUnits: ts.Node[] = []; + for (const name of storedNames) { + const symbol = checker.getSymbolAtLocation(name); + if (symbol === undefined) continue; + const units = unitsOfName.get(name)!; + unitsBySymbol.set(symbol, [...(unitsBySymbol.get(symbol) ?? []), ...units]); + allUnits.push(...units); + } + // A named class expression's inner name (`class Impl { static x = + // Impl.m(); }`) names the class inside its own body, as the binding it + // is stored in names it outside. + const innerNames: ts.Identifier[] = []; + for (const unit of allUnits) { + if (!ts.isClassExpression(unit) || unit.name === undefined) continue; + const symbol = checker.getSymbolAtLocation(unit.name); + if (symbol === undefined) continue; + innerNames.push(unit.name); + unitsBySymbol.set(symbol, [...(unitsBySymbol.get(symbol) ?? []), unit]); + } + // The identifier texts worth resolving: declared names, and the local + // names of imports (named, default and namespace) in cycle members. + const names = new Set(); + for (const c of candidates.values()) names.add(c.name.text); + for (const name of storedNames) names.add(name.text); + for (const name of innerNames) names.add(name.text); + for (const sf of evaluation) { + for (const stmt of sf.statements.nodes) { + if (!ts.isImportDeclaration(stmt) || stmt.importClause === undefined) continue; + const clause = stmt.importClause; + if (clause.name !== undefined) names.add(clause.name.text); + const bindings = clause.namedBindings; + if (bindings === undefined) continue; + if (ts.isNamedImports(bindings)) + for (const element of bindings.elements.nodes) names.add(element.name.text); + else names.add(bindings.name.text); + } + } + const idents: ts.Identifier[] = []; + for (const sf of evaluation) { + ts.walkPreorder(sf, (node) => { + if (ts.isImportDeclaration(node)) return "skip"; + if (ts.isIdentifier(node) && names.has(node.text)) idents.push(node); + return undefined; + }); + } + checker.prefetchSymbolNodesExact(idents); + const symbolOf = (id: ts.Identifier): ts.Symbol | undefined => { + const parent = id.parent; + let symbol = checker.getSymbolAtLocation(id); + if (parent !== undefined && ts.isShorthandPropertyAssignment(parent) && parent.name === id) { + symbol = checker.getShorthandAssignmentValueSymbol(parent) ?? symbol; + } + if (symbol !== undefined && symbol.flags & ts.SymbolFlags.Alias) { + symbol = checker.getAliasedSymbol(symbol); + } + return symbol; + }; + const isMemberNamespace = (symbol: ts.Symbol): boolean => + (symbol.flags & ts.SymbolFlags.ValueModule) !== 0 && + symbol.declarations.some((d) => ts.isSourceFile(d) && members.has(d)); + const declared = new Set(); + const reachedUnits = new Set(); + const out = new Map(); + const work: ts.Node[] = []; + const reach = (unit: ts.Node): void => { + if (reachedUnits.has(unit)) return; + reachedUnits.add(unit); + work.push(unit); + }; + const visit = (root: ts.Node): void => { + ts.walkPreorder(root, (node) => { + if (node !== root && stored.has(node)) { + for (const part of immediate.get(node) ?? []) visit(part); + return "skip"; + } + if (ts.isImportDeclaration(node)) return "skip"; + if (node.kind === ts.SyntaxKind.ThisKeyword) { + // `this` in a static initializer or block names the class, so it + // reaches the class's methods as naming the class does. + const owner = staticThisClass7(node); + if (owner !== undefined && stored.has(owner)) reach(owner); + return undefined; + } + if (!ts.isIdentifier(node) || !names.has(node.text)) return undefined; + if (inTypePosition7(node)) return undefined; + const parent = node.parent; + if (parent !== undefined && ts.isExportSpecifier(parent)) return undefined; + if (parent !== undefined && declaredName7(parent) === node) return undefined; + const symbol = symbolOf(node); + if (symbol === undefined) return undefined; + if (isMemberNamespace(symbol)) { + // `ns.member` resolves through the member's own identifier; any + // other use can reach every export. + if ( + !( + parent !== undefined && + ts.isPropertyAccessExpression(parent) && + parent.expression === node + ) + ) + for (const unit of allUnits) reach(unit); + return undefined; + } + const c = candidates.get(symbol); + if ( + c !== undefined && + node !== c.name && + !declared.has(symbol) && + !out.has(symbol) && + inDeferredPosition7(node) && + !( + c.kind === "class" && + ts.getSourceFileOfNode(node) === c.module && + ts.tokenStartOfNode(node) >= c.start && + node.end <= c.end + ) + ) + out.set(symbol, node); + for (const unit of unitsBySymbol.get(symbol) ?? []) reach(unit); + return undefined; + }); + }; + const drain = (): void => { + while (work.length > 0) visit(work.pop()!); + }; + const declare = (name: ts.BindingName): void => { + for (const id of boundNames7(name)) { + const symbol = checker.getSymbolAtLocation(id); + if (symbol !== undefined) declared.add(symbol); + } + }; + for (const sf of evaluation) { + for (const stmt of sf.statements.nodes) { + if (ts.isClassDeclaration(stmt)) { + for (const part of classSyncParts7(stmt)) visit(part); + drain(); + if (stmt.name !== undefined) declare(stmt.name); + } else if (ts.isVariableStatement(stmt)) { + for (const d of stmt.declarationList.declarations.nodes) { + visit(d); + drain(); + declare(d.name); + } + } else if (!ts.isFunctionDeclaration(stmt)) visit(stmt); + drain(); + } + } + return out; +} + /** Top-level let/const and class bindings, in modules outside the * exactly-initialized cycles, that code running before their declaration * completes can read. @@ -4114,51 +4608,8 @@ function moduleEarlyBindingsOf7( storedBy.set(name.text, list); }; const checker = program.getTypeChecker(); - const storedIn = (e: ts.Expression, units: ts.Node[]): void => { - while ( - ts.isParenthesizedExpression(e) || - ts.isAsExpression(e) || - ts.isSatisfiesExpression(e) || - ts.isNonNullExpression(e) - ) - e = e.expression; - if (ts.isArrowFunction(e) || ts.isFunctionExpression(e)) { - units.push(e); - } else if (ts.isClassExpression(e)) { - units.push(e); - immediate.set(e, classSyncParts7(e)); - } else if (ts.isObjectLiteralExpression(e)) { - for (const p of e.properties.nodes) { - if ( - ts.isMethodDeclaration(p) || - ts.isGetAccessorDeclaration(p) || - ts.isSetAccessorDeclaration(p) - ) { - // A computed key evaluates where the literal does; a plain - // member-chain key (`[Equal.symbol]`) runs no user code. - if (!ts.isComputedPropertyName(p.name)) units.push(p); - else if (memberChainKey7(p.name.expression)) { - units.push(p); - immediate.set(p, [p.name]); - } - } else if (ts.isPropertyAssignment(p)) storedIn(p.initializer, units); - } - } else if (ts.isArrayLiteralExpression(e)) { - for (const el of e.elements.nodes) if (!ts.isSpreadElement(el)) storedIn(el, units); - } else if (ts.isCallExpression(e)) { - // `const f = wrap(callback)` where wrap only returns closures over - // the callback: it runs when the stored result is called. - e.arguments.nodes.forEach((arg, index) => { - let fn: ts.Expression = arg; - while (ts.isParenthesizedExpression(fn)) fn = fn.expression; - if ( - (ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) && - returnsOnlyClosuresOver7(checker, e.expression, index) - ) - units.push(fn); - }); - } - }; + const storedIn = (e: ts.Expression, units: ts.Node[]): void => + storedCallables7(checker, e, units, immediate); sf.statements.nodes.forEach((stmt, index) => { if (ambient(stmt)) return; if (ts.isFunctionDeclaration(stmt) && stmt.name !== undefined && stmt.body !== undefined) { @@ -4411,6 +4862,40 @@ function returnsOnlyClosuresOver7( * expressions, decorators, computed member keys, static field * initializers and static blocks. Methods and instance fields wait for a * call or a construction. */ +/** The class whose static field initializer or static block evaluates `this` + * (through arrow functions, which keep the enclosing `this`), or undefined + * when another function or a class's instance side binds it. */ +function staticThisClass7(node: ts.Node): ts.ClassLikeDeclaration | undefined { + let child = node; + for (let n = node.parent; n !== undefined; child = n, n = n.parent) { + if (ts.isArrowFunction(n)) continue; + if (ts.isClassStaticBlockDeclaration(n)) return n.parent as ts.ClassLikeDeclaration; + if (ts.isPropertyDeclaration(n)) { + if (n.initializer !== child) return undefined; + return (ts.getCombinedModifierFlags(n) & ts.ModifierFlags.Static) !== 0 + ? (n.parent as ts.ClassLikeDeclaration) + : undefined; + } + if (ts.isComputedPropertyName(n)) { + // A computed key evaluates in the scope around the class. + const member = n.parent; + const cls = member?.parent; + if (cls === undefined) return undefined; + child = cls; + n = cls; + continue; + } + if ( + ts.isFunctionLike(n) || + ts.isClassDeclaration(n) || + ts.isClassExpression(n) || + ts.isSourceFile(n) + ) + return undefined; + } + return undefined; +} + function classSyncParts7(c: ts.ClassLikeDeclaration): ts.Node[] { const decorators = (n: ts.Node): ts.Node[] => ((n as { modifiers?: ts.NodeArray }).modifiers?.nodes ?? []).filter( @@ -5488,7 +5973,7 @@ function preflight7(load: LoadResult): { // module aliases cannot represent; createRequire bindings have no // checked form either. const earlyRequireFence = ( - req: { decl: ts.VariableDeclaration | null }, + req: RequireOccurrence7, bindingKind: RequireCallBindingKind7, loc: { file: string; start: number; end: number }, ): boolean => { @@ -5496,6 +5981,7 @@ function preflight7(load: LoadResult): { if ( early === null || (bindingKind !== "createRequire" && + req.split === undefined && ((req.decl!.parent?.flags ?? 0) & (ts.NodeFlags.Const | ts.NodeFlags.Let)) !== 0) ) { return false; @@ -5506,14 +5992,16 @@ function preflight7(load: LoadResult): { loc, bindingKind === "createRequire" ? `createRequire() binding '${early.text}' below code that can reach it (Node throws ReferenceError on any earlier access, which is not modeled — move the require above the code that can run first)` - : `var require() binding '${early.text}' below code that can reach it (Node reads undefined before the require runs, which is not modeled — declare it with const or let, or move the require above the code that can run first)`, + : req.split !== undefined + ? `require() binding '${early.text}' assigned below code that can reach it (Node reads undefined before the assignment runs, which is not modeled — move the assignment above the code that can run first)` + : `var require() binding '${early.text}' below code that can reach it (Node reads undefined before the require runs, which is not modeled — declare it with const or let, or move the require above the code that can run first)`, ), ); return true; }; for (let k = 0; k < stmts.length; k++) { const stmt = stmts[k]!; - for (const req of requiresOf7(stmt)) { + for (const req of requiresOf7(stmt, program)) { const loc = { file: sf.fileName, start: ts.tokenStartOfNode(req.node), @@ -5540,8 +6028,8 @@ function preflight7(load: LoadResult): { const jsonDep = resolveImport7(program, sf, req.spec, "require"); if (jsonDep !== null && jsonDep.fileName.endsWith(".json")) { if (bindingKind === "createRequire") continue; - if (ts.isVariableStatement(stmt) && requiresOf7(stmt).length > 1) { - const others = requiresOf7(stmt).some((r) => { + if (ts.isVariableStatement(stmt) && requiresOf7(stmt, program).length > 1) { + const others = requiresOf7(stmt, program).some((r) => { const d = resolveImport7(program, sf, r.spec, "require"); return d === null || !d.fileName.endsWith(".json"); }); @@ -5713,8 +6201,12 @@ function preflight7(load: LoadResult): { // counts, and targets outside the compiled graph add nothing here: // their use sites keep their existing lowering fences. if (jsRequireFile && !isNodeEsmFile7(sf, program)) { + // A split require binding's assignment is a statement-level require + // edge (requiresOf7 above), parenthesized or not. + const splitCalls = new Set(splitRequireBindings7(program, sf).values()); for (const call of inlineRequiresOf7(sf)) { if (requireCallBindingKind7(program, call) !== "ambient") continue; + if (splitCalls.has(call)) continue; const spec = requireSpecOf7(call)!; if (load.externalTypes.has(spec) || canonicalBuiltinModule(spec) !== null) continue; const lazy = inDeferredPosition7(call); @@ -6155,7 +6647,7 @@ function analyzeEsmNamedImportLinks( const nextSeen = new Set(seenModules); nextSeen.add(dep); const module = checker.getSymbolAtLocation(dep); - const exported = module?.getExports().get(name as ts.__String); + const exported = module?.getExports().get(ts.escapedSymbolName(name)); if (exported !== undefined) { let resolved = exported; const seenSymbols = new Set(); @@ -6463,6 +6955,7 @@ export { requireSpecOf7 as requireSpecOf, isRequireStatement7 as isRequireStatement, requiresOf7 as requiresOf, + splitRequireCallOf7 as splitRequireCallOf, isNodeEsmFile7 as isNodeEsmFile, resolveImport7 as resolveImport, resolveNpmImport7 as resolveNpmImport, diff --git a/packages/compiler/src/frontend/ts7/lib-diagnostics.test.ts b/packages/compiler/src/frontend/ts7/lib-diagnostics.test.ts new file mode 100644 index 000000000..f041a6cb5 --- /dev/null +++ b/packages/compiler/src/frontend/ts7/lib-diagnostics.test.ts @@ -0,0 +1,221 @@ +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterAll, afterEach, expect, test } from "vitest"; +import type { SourceFile } from "./ast-types.js"; +import { + reusedLibDiagnostics, + setLibDiagnosticsStore, + type LibDiagnosticsStore, +} from "./lib-diagnostics.js"; +import type { Ts7DiagnosticData } from "./session-schema.generated.js"; +import type { Ts7CheckedFileDiagnosticData, Ts7SessionProgram } from "./session.js"; +import { closeSourceParser, parseSourceFile } from "./source-parser-node.js"; + +const dir = mkdtempSync(join(tmpdir(), "scriptc-lib-diagnostics-")); +const lib = join(dir, "lib.es5.d.ts"); +const fallback = join(dir, "scriptc-node-fallback.d.ts"); +const libText = "declare var NaN: number;\ninterface Array { length: number }\n"; +const fallbackText = "export {};\ndeclare global {\n var process: { argv: string[] };\n}\n"; +writeFileSync(lib, libText); +writeFileSync(fallback, fallbackText); +const main = join(dir, "main.ts"); +const util = join(dir, "\u{1F600}.ts"); +const other = join(dir, "�.ts"); + +afterEach(() => setLibDiagnosticsStore(null)); +afterAll(() => { + closeSourceParser(); + rmSync(dir, { recursive: true, force: true }); +}); + +function memoryStore(): LibDiagnosticsStore & { entries: Map } { + const entries = new Map(); + return { + entries, + read: (key) => entries.get(key) ?? null, + write: (key, text) => void entries.set(key, text), + }; +} + +function diagnostic(fileName: string | undefined, pos: number, code = 2322): Ts7DiagnosticData { + return { fileName, pos, end: pos + 1, code, category: 1, text: `at ${pos}` }; +} + +/** A session program whose full-check and checked-file answers are given; it + * records which requests it was asked. */ +function sessionProgram(answers: { + semantic: Map; + global?: Ts7DiagnosticData[]; + checked?: boolean; +}): Ts7SessionProgram & { asked: string[] } { + const asked: string[] = []; + const all = [...answers.semantic.values()].flat(); + return { + asked, + getCompilerOptions: () => ({ strict: true }), + diagnosticData: (method: string, file?: string) => { + asked.push(file === undefined ? method : `${method} ${file}`); + if (method === "getGlobalDiagnostics") return answers.global ?? []; + return file === undefined ? all : (answers.semantic.get(file) ?? []); + }, + checkedFileDiagnosticData: (files: readonly string[]): Ts7CheckedFileDiagnosticData | null => { + asked.push(`checked ${files.join(",")}`); + if (answers.checked === false) return null; + return { files: files.map((file) => answers.semantic.get(file) ?? []), global: [] }; + }, + } as unknown as Ts7SessionProgram & { asked: string[] }; +} + +/** The program's files, parsed: the supplied two and the given others. */ +function files(texts: Record): (name: string) => SourceFile | undefined { + const parsed = new Map([ + [lib, parseSourceFile(lib, libText, "ts")], + [fallback, parseSourceFile(fallback, fallbackText, "ts")], + ]); + for (const [name, text] of Object.entries(texts)) + parsed.set(name, parseSourceFile(name, text, "ts")); + return (name) => parsed.get(name); +} + +const names = [lib, fallback, main]; +const supplied = (name: string): boolean => name === lib || name === fallback; +const moduleFile = files({ [main]: "export const n: number = 'x';\n" }); + +test("without a store the full check answers", () => { + const program = sessionProgram({ semantic: new Map() }); + expect(reusedLibDiagnostics(program, names, supplied, moduleFile)).toBeNull(); + expect(program.asked).toEqual([]); +}); + +test("a stored answer stands in for checking the supplied files, merged by file name", () => { + const store = memoryStore(); + setLibDiagnosticsStore(store, "checker"); + const semantic = new Map([ + [lib, [diagnostic(lib, 1)]], + [fallback, []], + [main, [diagnostic(main, 5), diagnostic(main, 9)]], + ]); + const first = sessionProgram({ semantic }); + const full = reusedLibDiagnostics(first, names, supplied, moduleFile); + // The first build runs the full check and stores the supplied files' answers. + expect(first.asked).toEqual([ + "getGlobalDiagnostics", + "getSemanticDiagnostics", + "checked ", + `getSemanticDiagnostics ${lib}`, + `getSemanticDiagnostics ${fallback}`, + ]); + expect(store.entries.size).toBe(1); + const second = sessionProgram({ semantic }); + const reused = reusedLibDiagnostics(second, names, supplied, moduleFile); + expect(second.asked).toEqual([`checked ${main}`]); + // The files' diagnostics in the order of their names (lib.es5.d.ts, main.ts), as the full answer. + expect(reused).toEqual({ + semantic: [diagnostic(lib, 1), diagnostic(main, 5), diagnostic(main, 9)], + global: [], + }); + expect(full?.semantic).toEqual(reused?.semantic); +}); + +test("files merge in code point order, which differs from UTF-16 order", () => { + const store = memoryStore(); + setLibDiagnosticsStore(store, "checker"); + const all = [lib, fallback, util, other]; + const sources = files({ [util]: "export {};\n", [other]: "export {};\n" }); + const semantic = new Map([ + [util, [diagnostic(util, 1)]], + [other, [diagnostic(other, 2)]], + ]); + reusedLibDiagnostics(sessionProgram({ semantic }), all, supplied, sources); + const reused = reusedLibDiagnostics(sessionProgram({ semantic }), all, supplied, sources); + // U+FFFD sorts before U+1F600 by code point (its surrogates sort after it by UTF-16 unit). + expect(reused?.semantic).toEqual([diagnostic(other, 2), diagnostic(util, 1)]); +}); + +test("a full check with global diagnostics is not stored", () => { + const store = memoryStore(); + setLibDiagnosticsStore(store, "checker"); + const program = sessionProgram({ + semantic: new Map([[main, [diagnostic(main, 1)]]]), + global: [diagnostic(undefined, 0, 2318)], + }); + expect(reusedLibDiagnostics(program, names, supplied, moduleFile)?.global).toEqual([ + diagnostic(undefined, 0, 2318), + ]); + expect(store.entries.size).toBe(0); +}); + +test("scripts reuse the answer unless they declare a global the supplied files declare", () => { + const store = memoryStore(); + setLibDiagnosticsStore(store, "checker"); + const semantic = new Map([[main, []]]); + // A script declaring only new names: it stores the answer, and a later one reuses it. + const fresh = files({ + [main]: "const total = 1;\nfunction run() {}\nif (total) { var later = 2; }\n", + }); + reusedLibDiagnostics(sessionProgram({ semantic }), names, supplied, fresh); + expect(store.entries.size).toBe(1); + const again = sessionProgram({ semantic }); + expect(reusedLibDiagnostics(again, names, supplied, fresh)).not.toBeNull(); + expect(again.asked).toEqual([`checked ${main}`]); + // Scripts declaring a supplied global, at the top level, in a block, in a + // `declare global` of a supplied module, or an ambient module: the full check. + for (const text of [ + "interface Array { extra: T }\n", + "if (true) { var NaN = 1; }\n", + "declare var process: unknown;\n", + 'declare module "fs" {}\n', + ]) { + const program = sessionProgram({ semantic }); + const result = reusedLibDiagnostics(program, names, supplied, files({ [main]: text })); + expect(program.asked[0], text).not.toBe(`checked ${main}`); + if (result !== null) expect(program.asked[0]).toBe("getGlobalDiagnostics"); + } + expect(store.entries.size).toBe(1); +}); + +test("a program whose script declares a supplied global does not store its answer", () => { + const store = memoryStore(); + setLibDiagnosticsStore(store, "checker"); + const program = sessionProgram({ semantic: new Map([[main, []]]) }); + reusedLibDiagnostics(program, names, supplied, files({ [main]: "declare var NaN: number;\n" })); + expect(store.entries.size).toBe(0); +}); + +test("modules that can declare something global, and servers without the method, ask as before", () => { + const store = memoryStore(); + setLibDiagnosticsStore(store, "checker"); + const semantic = new Map([[main, []]]); + reusedLibDiagnostics(sessionProgram({ semantic }), names, supplied, moduleFile); + expect(store.entries.size).toBe(1); + for (const text of [ + "export {};\ndeclare global { var extra: number }\n", + 'export {};\ndeclare module "./other" { const extra: number }\n', + "export as namespace Lib;\nexport const n = 1;\n", + ]) { + const program = sessionProgram({ semantic }); + expect(reusedLibDiagnostics(program, names, supplied, files({ [main]: text }))).toBeNull(); + expect(program.asked).toEqual([]); + } + const old = sessionProgram({ semantic, checked: false }); + expect(reusedLibDiagnostics(old, names, supplied, moduleFile)).toBeNull(); + // Nor does a server without the method store an answer it could not use. + const fresh = memoryStore(); + setLibDiagnosticsStore(fresh, "checker"); + reusedLibDiagnostics(sessionProgram({ semantic, checked: false }), names, supplied, moduleFile); + expect(fresh.entries.size).toBe(0); +}); + +test("a supplied file that changes is a different entry", () => { + const store = memoryStore(); + setLibDiagnosticsStore(store, "checker"); + const semantic = new Map([[main, []]]); + reusedLibDiagnostics(sessionProgram({ semantic }), names, supplied, moduleFile); + writeFileSync(fallback, fallbackText + "// edited\n"); + const program = sessionProgram({ semantic }); + reusedLibDiagnostics(program, names, supplied, moduleFile); + expect(program.asked[0]).toBe("getGlobalDiagnostics"); + expect(store.entries.size).toBe(2); + writeFileSync(fallback, fallbackText); +}); diff --git a/packages/compiler/src/frontend/ts7/lib-diagnostics.ts b/packages/compiler/src/frontend/ts7/lib-diagnostics.ts new file mode 100644 index 000000000..827b337bd --- /dev/null +++ b/packages/compiler/src/frontend/ts7/lib-diagnostics.ts @@ -0,0 +1,324 @@ +/* The semantic diagnostics of the declaration files scriptc supplies (the + * TypeScript libs and scriptc's own declaration files), reused across + * builds. Checking them is most of the full check of a small program. When + * no other file of a program can change what checking those files reports + * (every one is a module without global or module augmentations, or a + * script declaring no global name the supplied files declare), the answer + * depends only on the checker, the compiler options and the files + * themselves, so a stored answer for the same three stands in for checking + * them again: the server checks only the program's other files + * (getDiagnosticsOfCheckedFiles), and the semantic diagnostics merge in the + * order the full check sorts them, by file name. An answer is stored only + * when the full check reports no global diagnostics: the global diagnostics + * of checking the other files are then the full check's. */ +import { createHash } from "node:crypto"; +import { statSync } from "node:fs"; +import type { Node, SourceFile } from "./ast-types.js"; +import type { Ts7DiagnosticData } from "./session-schema.generated.js"; +import type { Ts7SessionProgram } from "./session.js"; +import { NodeFlags, SyntaxKind } from "./enums.js"; +import { walkPreorder } from "./ast.js"; + +/** Key-value storage for reused diagnostics (best effort: failures miss). */ +export interface LibDiagnosticsStore { + read(key: string): string | null; + write(key: string, text: string): void; +} + +const SCHEMA = "scriptc.lib-diagnostics.v1"; + +let configured: { store: LibDiagnosticsStore; checker: string } | null = null; + +/** Has later builds of this process reuse the diagnostics of the supplied + * declaration files through `store`, for the checker identified by + * `checker` (its executable's identity), or stop reusing them (null). */ +export function setLibDiagnosticsStore(store: LibDiagnosticsStore | null, checker = ""): void { + configured = store === null ? null : { store, checker }; +} + +/** A checker executable's identity for the stored diagnostics' key: its + * path and file identity, or the path alone when it cannot be read. */ +export function checkerExecutableIdentity(path: string): string { + try { + const info = statSync(path); + return JSON.stringify([path, info.size, info.mtimeMs, info.ino]); + } catch { + return JSON.stringify([path]); + } +} + +/** The program's semantic and global diagnostics as the full check reports + * them, using stored diagnostics for the supplied declaration files; null + * when they cannot be reused for this program (then the caller asks for + * them as before). `supplied` tells whether a program file is one of the + * supplied declaration files. */ +export function reusedLibDiagnostics( + program: Ts7SessionProgram, + fileNames: readonly string[], + supplied: (fileName: string) => boolean, + sourceFile: (fileName: string) => SourceFile | undefined, +): { semantic: Ts7DiagnosticData[]; global: Ts7DiagnosticData[] } | null { + if (configured === null || process.env["SCRIPTC_LIB_DIAGNOSTICS"] === "0") return null; + const { store, checker } = configured; + const libs: string[] = []; + const own: string[] = []; + for (const name of fileNames) (supplied(name) ? libs : own).push(name); + if (libs.length === 0) return null; + // The global names each of the program's scripts declares; a module that + // declares nothing global contributes none. + const scripts: Set[] = []; + for (const name of own) { + const file = sourceFile(name); + if (file === undefined) return null; + if (file.externalModuleIndicator !== undefined) { + if (!declaresNothingGlobal(file)) return null; + continue; + } + const names = isJsFileName(name) ? null : scriptGlobalNames(file); + if (names === null) return null; + scripts.push(names); + } + // A script's globals cannot change what the supplied files report unless + // one of them is also a global the supplied files declare. + const collides = (globals: ReadonlySet): boolean => + scripts.some((names) => [...names].some((name) => globals.has(name))); + // The options without the configuration file's path: it names where the + // project is, which checking the supplied files does not read (the files a + // project's location brings in are part of the key). + const options = { ...program.getCompilerOptions(), configFilePath: undefined }; + const key = entryKey(checker, options, libs); + if (key === null) return null; + const stored = readEntry(store, key, libs); + if (stored !== null && !collides(stored.globals)) { + const checked = program.checkedFileDiagnosticData(own); + if (checked === null) return null; + const byFile = new Map(stored.files); + own.forEach((name, index) => byFile.set(name, checked.files[index] ?? [])); + return { semantic: inFileOrder(byFile), global: checked.global }; + } + // The full check, as before. Its answers for the supplied files are stored + // when it reports no global diagnostics and the program's scripts declare + // none of the supplied files' globals (so nothing of the program reached + // what those files report). + const global = program.diagnosticData("getGlobalDiagnostics"); + const semantic = program.diagnosticData("getSemanticDiagnostics"); + // A server that cannot check only some files could never use the answer. + if (global.length === 0 && stored === null && program.checkedFileDiagnosticData([]) !== null) { + const globals = suppliedGlobalNames(libs, sourceFile); + if (globals !== null && !collides(globals)) { + const files: Record = {}; + for (const name of libs) files[name] = program.diagnosticData("getSemanticDiagnostics", name); + try { + store.write(key, JSON.stringify({ schema: SCHEMA, globals: [...globals], files })); + } catch { + // Best effort: a failed write is a later miss. + } + } + } + return { semantic, global }; +} + +/** Whether a file cannot add to or change the global declarations the + * supplied files declare: a module with no `declare global`, no module + * augmentation and no UMD `export as namespace` declaration. */ +function declaresNothingGlobal(file: SourceFile): boolean { + if (file.externalModuleIndicator === undefined) return false; + if (file.moduleAugmentations.length > 0) return false; + for (const statement of file.statements.nodes) { + if (statement.kind === SyntaxKind.NamespaceExportDeclaration) return false; + // `declare global` (a module declaration named by the identifier `global`). + if ( + statement.kind === SyntaxKind.ModuleDeclaration && + statement.name?.kind === SyntaxKind.Identifier && + statement.name.text === "global" + ) + return false; + } + return true; +} + +function isJsFileName(name: string): boolean { + return /\.[cm]?jsx?$/.test(name); +} + +/** The names a script file declares in the global scope: its top-level + * declarations, and the `var` and function declarations of its top-level + * blocks; null when it declares an ambient module (`declare module "x"`), + * which can merge with one the supplied files declare. Over-approximates: + * a name more only makes the stored answer apply less often. */ +function scriptGlobalNames(file: SourceFile): Set | null { + const names = new Set(); + let ambientModule = false; + walkPreorder(file, (node) => { + if (node === file) return undefined; + if (node.kind === SyntaxKind.ModuleDeclaration && node.name?.kind === SyntaxKind.StringLiteral) + ambientModule = true; + const topLevel = node.parent === file; + if (topLevel || node.kind === SyntaxKind.FunctionDeclaration) addDeclaredNames(node, names); + else if ( + node.kind === SyntaxKind.VariableDeclarationList && + (node.flags & NodeFlags.BlockScoped) === 0 + ) + for (const declaration of node.declarations?.nodes ?? []) + addBindingNames(declaration.name, names); + // Functions, classes and namespaces have scopes of their own. + return GLOBAL_SCOPE_BOUNDARIES.has(node.kind) ? "skip" : undefined; + }); + return ambientModule ? null : names; +} + +/** The global names the supplied files declare: the declarations of their + * scripts (as scriptGlobalNames finds them, with ambient modules as their + * quoted names) and those of the `declare global` blocks of their modules; + * null when one of them is not available. */ +function suppliedGlobalNames( + libs: readonly string[], + sourceFile: (fileName: string) => SourceFile | undefined, +): Set | null { + const names = new Set(); + for (const name of libs) { + const file = sourceFile(name); + if (file === undefined) return null; + if (file.externalModuleIndicator === undefined) { + walkPreorder(file, (node) => { + if (node === file) return undefined; + if ( + node.kind === SyntaxKind.ModuleDeclaration && + node.name?.kind === SyntaxKind.StringLiteral + ) + names.add(JSON.stringify(node.name.text ?? "")); + if (node.parent === file || node.kind === SyntaxKind.FunctionDeclaration) + addDeclaredNames(node, names); + else if ( + node.kind === SyntaxKind.VariableDeclarationList && + (node.flags & NodeFlags.BlockScoped) === 0 + ) + for (const declaration of node.declarations?.nodes ?? []) + addBindingNames(declaration.name, names); + return GLOBAL_SCOPE_BOUNDARIES.has(node.kind) ? "skip" : undefined; + }); + continue; + } + for (const statement of file.statements.nodes) { + if ( + statement.kind !== SyntaxKind.ModuleDeclaration || + statement.name?.kind !== SyntaxKind.Identifier || + statement.name.text !== "global" + ) + continue; + for (const inner of statement.body?.statements?.nodes ?? []) addDeclaredNames(inner, names); + } + } + return names; +} + +const GLOBAL_SCOPE_BOUNDARIES = new Set([ + SyntaxKind.FunctionDeclaration, + SyntaxKind.FunctionExpression, + SyntaxKind.ArrowFunction, + SyntaxKind.MethodDeclaration, + SyntaxKind.Constructor, + SyntaxKind.GetAccessor, + SyntaxKind.SetAccessor, + SyntaxKind.ClassDeclaration, + SyntaxKind.ClassExpression, + SyntaxKind.ModuleDeclaration, +]); + +/** The names a statement declares in the scope it is in. */ +function addDeclaredNames(node: Node, names: Set): void { + switch (node.kind) { + case SyntaxKind.VariableStatement: + for (const declaration of node.declarationList?.declarations?.nodes ?? []) + addBindingNames(declaration.name, names); + return; + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ImportEqualsDeclaration: + if (node.name?.kind === SyntaxKind.Identifier) names.add(node.name.text ?? ""); + return; + } +} + +function addBindingNames(name: Node | undefined, names: Set): void { + if (name === undefined) return; + if (name.kind === SyntaxKind.Identifier) { + names.add(name.text ?? ""); + return; + } + // Object and array binding patterns. + for (const element of name.elements?.nodes ?? []) addBindingNames(element.name, names); +} + +/** The stored diagnostics' key: the checker, the compiler options and the + * supplied files in program order with each one's file identity; null when + * one of them cannot be read. */ +function entryKey(checker: string, options: unknown, libs: readonly string[]): string | null { + const files: unknown[] = []; + for (const name of libs) { + try { + const info = statSync(name); + files.push([name, info.size, info.mtimeMs, info.ino]); + } catch { + return null; + } + } + return createHash("sha256") + .update(JSON.stringify({ schema: SCHEMA, checker, options, files })) + .digest("hex"); +} + +function readEntry( + store: LibDiagnosticsStore, + key: string, + libs: readonly string[], +): { files: Map; globals: Set } | null { + let text: string | null; + try { + text = store.read(key); + } catch { + return null; + } + if (text === null) return null; + let entry: { schema?: unknown; globals?: unknown; files?: Record }; + try { + entry = JSON.parse(text) as typeof entry; + } catch { + return null; + } + if (entry.schema !== SCHEMA || entry.files === undefined || !Array.isArray(entry.globals)) + return null; + const files = new Map(); + for (const name of libs) { + const list = entry.files[name]; + if (!Array.isArray(list)) return null; + files.set(name, list); + } + return { files, globals: new Set(entry.globals.filter((name) => typeof name === "string")) }; +} + +/** Each file's diagnostics (already in the checker's order) in the order the + * checker sorts files: by file name, compared by code point. */ +function inFileOrder(byFile: ReadonlyMap): Ts7DiagnosticData[] { + const names = [...byFile.keys()].sort(compareCodePoints); + const out: Ts7DiagnosticData[] = []; + for (const name of names) for (const d of byFile.get(name)!) out.push(d); + return out; +} + +function compareCodePoints(a: string, b: string): number { + let i = 0; + let j = 0; + while (i < a.length && j < b.length) { + const x = a.codePointAt(i)!; + const y = b.codePointAt(j)!; + if (x !== y) return x < y ? -1 : 1; + i += x > 0xffff ? 2 : 1; + j += y > 0xffff ? 2 : 1; + } + return i < a.length ? 1 : j < b.length ? -1 : 0; +} diff --git a/packages/compiler/src/frontend/ts7/program-host.ts b/packages/compiler/src/frontend/ts7/program-host.ts index 639086e94..ec143e401 100644 --- a/packages/compiler/src/frontend/ts7/program-host.ts +++ b/packages/compiler/src/frontend/ts7/program-host.ts @@ -46,6 +46,10 @@ import { trackedRealpath, } from "../input-tracker.js"; import { packageShimDeclarationText } from "../package-shims.js"; +import { ambientDtsPath, fallbackDtsPath, overridesDtsPath } from "../dts-paths.js"; +import { reusedLibDiagnostics } from "./lib-diagnostics.js"; +import { ts7Diagnostic } from "./diagnostic.js"; +import type { Ts7DiagnosticData } from "./session-schema.generated.js"; /** The compiler options our createProgram accepts: TS7's CompilerOptions * shape (numeric enums for target/module/moduleResolution — the enums module @@ -393,6 +397,22 @@ export class Ts7Program { return this.project.program.getSourceFileMetadata(resolve(fileName)); } + /** The semantic and global diagnostics with the stored diagnostics of the + * declaration files scriptc supplies (lib-diagnostics.ts), or null. */ + reusedLibDiagnostics(): { semantic: Ts7DiagnosticData[]; global: Ts7DiagnosticData[] } | null { + this.snapshot.ensureActive(); + const program = this.project.program; + const declarations = new Set([ambientDtsPath(), overridesDtsPath(), fallbackDtsPath()]); + return reusedLibDiagnostics( + program, + this.getSourceFileNames(), + (name) => + declarations.has(tsgoPath(name)) || + program.getSourceFileMetadataByPath(name)?.isDefaultLibrary === true, + (name) => this.getSourceFile(name), + ); + } + isSourceFileDefaultLibrary(file: SourceFile): boolean { return this.project.program.isSourceFileDefaultLibrary(file); } @@ -423,9 +443,20 @@ export class Ts7Program { * package dropped the aggregate). Order mirrors 5.9.3: config, options/ * program, global, syntactic, semantic. */ export function getPreEmitDiagnostics(program: Ts7Program): readonly Diagnostic[] { + const config = program.getConfigFileParsingDiagnostics(); + const options = program.getProgramDiagnostics(); + const reused = program.reusedLibDiagnostics(); + if (reused !== null) + return [ + ...config, + ...options, + ...reused.global.map(ts7Diagnostic), + ...program.getSyntacticDiagnostics(), + ...reused.semantic.map(ts7Diagnostic), + ]; return [ - ...program.getConfigFileParsingDiagnostics(), - ...program.getProgramDiagnostics(), + ...config, + ...options, ...program.getGlobalDiagnostics(), ...program.getSyntacticDiagnostics(), ...program.getSemanticDiagnostics(), diff --git a/packages/compiler/src/frontend/ts7/semantic-model.test.ts b/packages/compiler/src/frontend/ts7/semantic-model.test.ts index 9d0f61a4e..bbcd53d8b 100644 --- a/packages/compiler/src/frontend/ts7/semantic-model.test.ts +++ b/packages/compiler/src/frontend/ts7/semantic-model.test.ts @@ -28,6 +28,7 @@ import { SemanticSnapshot, SemanticType, symbolNameOf, + escapedSymbolName, } from "./semantic-model.js"; import type { __String } from "./ast-types.js"; import { @@ -313,6 +314,11 @@ test("symbol tables keep escaped keys, display names, canonical context and cach const exports = parent.getExports(); expect(symbolNameOf(members.get("___x" as __String))).toBe("__x"); expect(symbolNameOf(exports.get("__x" as __String))).toBe("__x"); + // A name that starts with `__` is keyed with an extra `_`. + expect(symbolNameOf(members.get(escapedSymbolName("__x")))).toBe("__x"); + expect(escapedSymbolName("x")).toBe("x"); + expect(escapedSymbolName("_x")).toBe("_x"); + expect(escapedSymbolName("___y")).toBe("____y"); expect(parent.getMembers()).toBe(members); expect(parent.getExports()).toBe(exports); expect(parent.getParent()).toBe(first.symbol(symbol(2))); diff --git a/packages/compiler/src/frontend/ts7/semantic-model.ts b/packages/compiler/src/frontend/ts7/semantic-model.ts index d3d5fe61a..405f4775b 100644 --- a/packages/compiler/src/frontend/ts7/semantic-model.ts +++ b/packages/compiler/src/frontend/ts7/semantic-model.ts @@ -454,6 +454,12 @@ export function symbolName(symbol: SemanticSymbol): string { return name.startsWith("___") ? name.slice(1) : name; } +/** The key of the name `name` in a symbol table (`getExports()`, `getMembers()`): the name, with + * an extra `_` when it starts with `__`. The inverse of `symbolName`. */ +export function escapedSymbolName(name: string): __String { + return (name.startsWith("__") ? `_${name}` : name) as __String; +} + /** `symbolName(symbol)`, or undefined without a symbol. */ export function symbolNameOf(symbol: SemanticSymbol | null | undefined): string | undefined { return symbol === undefined || symbol === null ? undefined : symbolName(symbol); diff --git a/packages/compiler/src/frontend/ts7/semantic-types.ts b/packages/compiler/src/frontend/ts7/semantic-types.ts index ea43e3885..5a597ae4e 100644 --- a/packages/compiler/src/frontend/ts7/semantic-types.ts +++ b/packages/compiler/src/frontend/ts7/semantic-types.ts @@ -30,7 +30,7 @@ export type { SemanticSignature as Signature, SemanticSymbol as Symbol, } from "./semantic-model.js"; -export { symbolName, symbolNameOf } from "./semantic-model.js"; +export { escapedSymbolName, symbolName, symbolNameOf } from "./semantic-model.js"; export type { SemanticIndexInfo as IndexInfo, SemanticTypePredicate as TypePredicate, diff --git a/packages/compiler/src/frontend/ts7/session.ts b/packages/compiler/src/frontend/ts7/session.ts index d3f2ae9fe..082bb79a6 100644 --- a/packages/compiler/src/frontend/ts7/session.ts +++ b/packages/compiler/src/frontend/ts7/session.ts @@ -55,6 +55,13 @@ interface ProgramQuery { snapshot?: number; project?: string; file?: SemanticDocument; + files?: string[]; +} + +/** The answer of getDiagnosticsOfCheckedFiles, as the server sends it. */ +export interface Ts7CheckedFileDiagnosticData { + files: Ts7DiagnosticData[][]; + global: Ts7DiagnosticData[]; } /** Platform-independent session over an already connected transport. The @@ -344,6 +351,29 @@ export class Ts7SessionProgram { result.push(ts7Diagnostic(data)); return result; } + /** The diagnostics of `method` for `file`, or for the whole program, as + * the server sends them. */ + diagnosticData(method: string, file?: SemanticDocument): Ts7DiagnosticData[] { + return this.session.request(method, this.query(file)) ?? []; + } + /** The semantic diagnostics of each of `files` and the global diagnostics, + * after the server checks only those files (getDiagnosticsOfCheckedFiles), + * or null when the server does not answer that method. */ + checkedFileDiagnosticData(files: readonly string[]): Ts7CheckedFileDiagnosticData | null { + try { + return this.session.request("getDiagnosticsOfCheckedFiles", { + ...this.query(), + files: [...files], + }); + } catch (error) { + if ( + error instanceof Error && + error.message.includes('unknown API method "getDiagnosticsOfCheckedFiles"') + ) + return null; + throw error; + } + } getSyntacticDiagnostics(file?: SemanticDocument): Ts7Diagnostic[] { return this.diagnostics("getSyntacticDiagnostics", file); } diff --git a/packages/compiler/src/frontend/type-mapper.ts b/packages/compiler/src/frontend/type-mapper.ts index f1e86067e..161f73d27 100644 --- a/packages/compiler/src/frontend/type-mapper.ts +++ b/packages/compiler/src/frontend/type-mapper.ts @@ -183,6 +183,7 @@ export const CRYPTO_KEY_OPTION_DYN_TYPES: ReadonlySet = new Set([ "PrivateKeyInput", "PublicKeyInput", "RSAKeyPairKeyObjectOptions", + "X25519KeyPairOptions", "RSAKeyPairOptions", "SignKeyObjectInput", "SignPrivateKeyInput", @@ -213,6 +214,9 @@ export const FETCH_AMBIENT_TYPES = [ "ReadableStreamReadResult", "ReadableStreamReadValueResult", "ReadableStreamReadDoneResult", + "TransformStream", + "WritableStream", + "WritableStreamDefaultWriter", "Blob", "File", "BlobPropertyBag", @@ -1390,9 +1394,17 @@ function genChannels( if (!nextT || nextT.kind === "void" || nextT.kind === "nullT") return null; const ownChannel = (t: IrType): boolean => t.kind === "f64" || t.kind === "bool" || t.kind === "string"; + // A record yield channel keeps its type beside a dyn return (an + // `IterableIterator`). A record return channel beside a dyn yield + // does not: the value a delegating `yield*` answers (Effect.gen's + // generators) is often an object whose methods a record would drop. const dynMix = (yieldT.kind === "dyn" && retT.kind !== "dyn" && retT.kind !== "void" && !ownChannel(retT)) || - (retT.kind === "dyn" && yieldT.kind !== "dyn" && yieldT.kind !== "void" && !ownChannel(yieldT)); + (retT.kind === "dyn" && + yieldT.kind !== "dyn" && + yieldT.kind !== "void" && + !ownChannel(yieldT) && + yieldT.kind !== "record"); if (dynMix) return { yieldT: DYN, retT: DYN, nextT }; return { yieldT, retT, nextT }; } @@ -1419,16 +1431,18 @@ let mapTypeDepth = 0; let contextResolutions = 0; /** node:url's legacy record interfaces: url.parse()'s Url instance (and - * its two query-typed refinements), url.format()'s UrlObject input, and the - * URL form's URLFormatOptions. The values are checked-dynamic objects — - * Node's Url instance carries its class name and null fields, and format() - * reads any object's fields with JS truthiness. */ + * its two query-typed refinements), url.format()'s UrlObject input, the + * URL form's URLFormatOptions, and urlToHttpOptions's result. The values + * are checked-dynamic objects — Node's Url instance carries its class name + * and null fields, format() reads any object's fields with JS truthiness, + * and urlToHttpOptions answers a null-prototype object. */ const LEGACY_URL_TYPES = new Set([ "Url", "UrlObject", "UrlWithParsedQuery", "UrlWithStringQuery", "URLFormatOptions", + "UrlHttpOptions", ]); export function isLegacyUrlType( @@ -3109,7 +3123,7 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { psym && (FETCH_AMBIENT_TYPES as readonly string[]).includes(ts.symbolName(psym)) && !( - ts.symbolName(psym) === "ReadableStream" && + (ts.symbolName(psym) === "ReadableStream" || ts.symbolName(psym) === "WritableStream") && psym.declarations.some( (d) => ts.isInterfaceDeclaration(d) && isDeclaredInAmbientNamespace(d, "NodeJS"), ) @@ -3219,10 +3233,9 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { } const collectionView = mapCollectionView(widened, ctx); if (collectionView) return collectionView; - // Date: a TimeClip'd epoch-millisecond scalar in the static runtime. - // This supports stored/passed values and the read-only getter slice; - // identity and mutation stay fenced because the scalar deliberately - // carries no object identity. + // Date: a refcounted object holding its TimeClip'd time value (ScrDate), + // shared with the checked-dynamic Date handle, so it keeps its identity + // through every slot and through `unknown`. Setters stay fenced. if (isStdlibInterface("Date")) return DATE_T; // RegExp: a reference to the lib RegExp interface — provenance, not the // name (a user's own `interface RegExp` maps as a record). Regex values @@ -3955,8 +3968,10 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { // Mixed dyn/concrete value channels share checked storage, so either // branch of the result record has the same representation. A number, // boolean or string channel keeps its own type beside a dyn one (the - // `Generator` defaults): its values ride OUT typed and box only - // when the result record is built. + // `Generator` default), and so does a record yield channel beside + // a dyn return (`IterableIterator`): its values ride OUT typed and box + // only when the result record is built, so for-of and spread read them + // back unchecked. if ( isStdlibInterface("Generator") || isStdlibInterface("AsyncGenerator") || @@ -4755,6 +4770,9 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { if (hybrid) return hybrid; } const callSigs = checker.getCallSignatures(widened); + // AsyncLocalStorage.snapshot()'s generic runner is a dyn function value + // (als.snapshot), called through the dyn call path. + if (callSigs.length === 1 && isAsyncLocalStorageSnapshot(callSigs[0]!, ctx)) return DYN; if (callSigs.length === 1) return mapCallSignature(callSigs[0]!, ctx); // Records: object types whose members are all data properties (shorthand // methods in type position count — they're func-typed fields) with @@ -5051,7 +5069,6 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { (a) => a.kind === "void" || a.kind === "union" || - a.kind === "date" || a.kind === "dyn" || // A generator arm only comes from an Iterable (an optional // one keeps its unit arms beside the iterable union's). @@ -5087,6 +5104,26 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { /** One non-overloaded call signature's func type (mapTypeInner's * single-signature rule; erasedOverloadMemberType maps each overload of a * member through it). */ +/** Whether `sig` is the function type AsyncLocalStorage.snapshot() returns: + * declared as that static method's return type in the async_hooks module + * of the standard library declarations. */ +function isAsyncLocalStorageSnapshot(sig: ts.Signature, ctx: TypeMapperCtx): boolean { + const decl = sig.declaration; + if (decl === undefined || !ts.isFunctionTypeNode(decl)) return false; + const method = decl.parent; + return ( + method !== undefined && + ts.isMethodDeclaration(method) && + ts.isIdentifier(method.name) && + method.name.text === "snapshot" && + method.parent !== undefined && + ts.isClassDeclaration(method.parent) && + method.parent.name?.text === "AsyncLocalStorage" && + ctx.isStdlibFile(ts.getSourceFileOfNode(decl)) && + isDeclaredInAmbientModule(decl, "async_hooks") + ); +} + function mapCallSignature(sig: ts.Signature, ctx: TypeMapperCtx): IrType | null { const { checker } = ctx; if (sig.getTypeParameters().length && ctx.erasingSignature !== sig) return null; @@ -6584,7 +6621,7 @@ export function genResultRecord( for (const a of unions.get(t.unionId)?.arms ?? []) byKey.set(typeKey(a), a); return true; } - if (t.kind === "regex" || t.kind === "date" || t.kind === "generator") { + if (t.kind === "regex" || t.kind === "generator") { return false; // no legal union arm exists for these kinds } byKey.set(typeKey(t), t); @@ -6673,15 +6710,15 @@ export function withUnitArm( if (t.kind === "union") { const def = unions.get(t.unionId); if (!def) return null; - if (def.arms.some((a) => a.kind === "date")) return null; if (def.arms.some((a) => a.kind === kind)) return t; const arms = [...def.arms, unit]; + // Date and Promise arms beside other data arms have no union home. + if (!unionContainerArmsOk(arms)) return null; arms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1)); return { kind: "union", unionId: unions.transform(def, arms) }; } if ( t.kind === "void" || - t.kind === "date" || t.kind === "dyn" || // A bare unit field type cannot occur (units live only inside unions), // but guard against constructing a single-arm union from one. @@ -8271,7 +8308,6 @@ function armHasUnionHome(arm: IrType): boolean { case "void": case "union": case "regex": - case "date": case "generator": case "dyn": return false; diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts index 14bdebf3c..dbbf1d0b7 100644 --- a/packages/compiler/src/index.ts +++ b/packages/compiler/src/index.ts @@ -1,4 +1,5 @@ import { optimizationField, type NativeOptimization } from "./backend/optimization.js"; +import { targetCpuField, targetCpuTargetRefusal } from "./backend/target-cpu.js"; import { compilationTiming, type CompilationTiming, type TimingClock } from "./timing.js"; import { prepareExecutableModule } from "./executable/prepare.js"; import { @@ -9,6 +10,7 @@ import { } from "./library/prepare.js"; import { analyzeWithFrontend } from "./frontend/analysis.js"; import { checkerCacheIdentity } from "./frontend/ts7/checker-backend.js"; +import { setLibDiagnosticsStore } from "./frontend/ts7/lib-diagnostics.js"; import { analyzeWithVerdictCache, fileVerdictStore } from "./coverage/verdict-cache.js"; import { coverageCompilerIdentity } from "./coverage/verdict-startup.js"; import { commentOnlyReplay } from "./coverage/verdict-semantic.js"; @@ -458,6 +460,7 @@ export async function compile( ); return withCompileWarnings(result, warnings); } finally { + setLibDiagnosticsStore(null); frontendProcess?.release(); } } @@ -601,6 +604,7 @@ async function compileExecutableNative( ...(features.warningReplace ? { warningReplace: true } : {}), ...(features.stdioExtended ? { stdioExtended: true } : {}), zlib: features.zlib, + ...(features.brotli ? { brotli: true } : {}), assert: features.assert, inspect: features.inspect, dynInvoke: features.dynInvoke, @@ -631,6 +635,7 @@ async function compileExecutableNative( dgram: features.dgram, dnsPromises: features.dnsPromises, dgramDyn: features.dgramDyn, + textCodecDyn: features.textCodecDyn, watch: features.watch, foreignFfi: features.foreignFfi, ...(features.workers ? { workers: true } : {}), @@ -812,6 +817,7 @@ async function prepareExecutableInput( lowRegion: lowRegionTarget(buildPlatform, opts.sanitize === true), ...(opts.sanitize === true ? { wasmBoundsChecks: true } : {}), ...inlineRcOption(opts.optimization, opts.sanitize === true), + ...targetCpuField(opts.targetCpu), }); } catch (err) { if (!(err instanceof LlvmUnsupportedError)) throw err; @@ -882,6 +888,7 @@ async function prepareExecutableInput( ...(opts.sanitize === true ? { wasmBoundsChecks: true } : {}), ...inlineRcOption(opts.optimization, opts.sanitize === true), ...(opts.detachedExit === true ? { detachedExit: true } : {}), + ...targetCpuField(opts.targetCpu), }); } catch (err) { if (!(err instanceof LlvmUnsupportedError)) throw err; @@ -1169,10 +1176,36 @@ async function prepareExecutableInputInChild( * `requestPath` as `compileTracked` would in-process, and writes what it prepared, without the LLVM * text, to `resultPath`. */ +/** Has the build reuse the diagnostics of the declaration files scriptc + * supplies (lib-diagnostics.ts) through the build cache at `root`, when the + * build uses one (executables; source outputs create no cache) and the + * checker is tsc-ts. */ +function configureLibDiagnostics(root: string | null): void { + let checker: string | undefined; + try { + checker = root === null ? undefined : checkerCacheIdentity(); + } catch { + // A missing checker fails where the build starts it. + checker = undefined; + } + setLibDiagnosticsStore( + root === null || checker === undefined + ? null + : fileVerdictStore(join(root, "lib-diagnostics-v1")), + checker ?? "", + ); +} + export async function runFrontendProcess(requestPath: string, resultPath: string): Promise { const request = JSON.parse(await readFile(requestPath, "utf8")) as FrontendProcessRequest; clearCompileSessionCaches(); setProvenanceSources(request.provenance); + // The compile that started this process prepared the cache root. + configureLibDiagnostics( + (request.opts.outputKind ?? "exe") === "exe" && provenanceSources() === null + ? await prepareBuildCacheRoot(buildCacheRoot()) + : null, + ); const frontendInputs = new FrontendInputTracker(); const warnings: CompileWarningSink = { warnings: [], sourceTexts: null }; let result: FrontendProcessResult = { kind: "failed" }; @@ -1220,6 +1253,8 @@ export async function runFrontendProcess(requestPath: string, resultPath: string } } catch { result = { kind: "failed" }; + } finally { + setLibDiagnosticsStore(null); } await writeFile(resultPath, JSON.stringify(result), { mode: 0o600 }); } @@ -1422,6 +1457,18 @@ async function compileTracked( } } } + const targetCpuRefusal = targetCpuTargetRefusal( + opts.targetCpu, + buildPlatform, + nativeCodegenTarget()?.architecture ?? process.arch, + ); + if (targetCpuRefusal !== null) { + return { + ok: false, + diagnostics: [nativeCodegenDiag("SC3002", targetCpuRefusal, entryPath)], + sourceTexts: new Map(), + }; + } try { windowsSubsystemLinkerArgs(buildPlatform, opts.windowsSubsystem); } catch (err) { @@ -1457,6 +1504,7 @@ async function compileTracked( outputKind === "exe" && provenanceSources() === null ? await prepareBuildCacheRoot(buildCacheRoot()) : null; + configureLibDiagnostics(cacheRoot); let earlyCacheOptions: EarlyExecutableCacheOptions | null = null; if (outputKind === "exe") { const helperObjectRoute = @@ -1475,6 +1523,7 @@ async function compileTracked( ...(opts.strip ? { strip: true as const } : {}), ...(opts.windowsSubsystem === "gui" ? { windowsSubsystem: "gui" as const } : {}), ...(opts.detachedExit === true ? { detachedExit: true as const } : {}), + ...targetCpuField(opts.targetCpu), ...(profile === undefined ? {} : { profile: nativeProfileKey(profile) }), ...checkerField(), ...(youngNurseryRequested() ? { youngNursery: true as const } : {}), diff --git a/packages/compiler/src/ir/analysis.ts b/packages/compiler/src/ir/analysis.ts index 70ec381a1..ee1cee6be 100644 --- a/packages/compiler/src/ir/analysis.ts +++ b/packages/compiler/src/ir/analysis.ts @@ -4,6 +4,7 @@ import { BYTES_ELEMENT_NAME, DYN_HANDLE_KINDS, RUNTIME_STREAM_CLASSES, + recordTextCodecClass, typeEquals, type IrExpr, type IrLibFn, @@ -141,8 +142,10 @@ export function dynDesc( return "string"; case "bool": return "boolean"; - case "record": - return recordsById.get(t.shapeId)?.tuple ? "array" : "object"; + case "record": { + const shape = recordsById.get(t.shapeId); + return shape?.tuple ? "array" : ((shape && recordTextCodecClass(shape)) ?? "object"); + } case "array": return "array"; case "nullT": diff --git a/packages/compiler/src/ir/array-length-stability.test.ts b/packages/compiler/src/ir/array-length-stability.test.ts index f9b3cc9c2..97270e059 100644 --- a/packages/compiler/src/ir/array-length-stability.test.ts +++ b/packages/compiler/src/ir/array-length-stability.test.ts @@ -193,3 +193,34 @@ test("number and boolean writes to class fields run no user code", () => { // A reference stored into a field releases the old one. expect(preserves([fieldSet(arr("b"))], "i", new Set(), scope)).toBe(false); }); + +test("optional element reads, their assertion errors and present-undefined stores at an in-range index keep the length", () => { + const optional = (receiver: string, index: IrExpr): IrExpr => ({ + kind: "call", + callee: "%arr.idxOr.f64", + args: [arr(receiver), index], + type: F64, + loc, + }); + const undefinedAt = (receiver: string, index: IrExpr): IrStmt => ({ + kind: "arraySetUndefined", + arr: arr(receiver), + index, + loc, + }); + const thrown: IrExpr = { kind: "libCall", fn: "error.nodeThrow", args: [num(1)], type: F64, loc }; + expect(preserves([stmt(optional("a", ref("i"))), stmt(thrown)])).toBe(true); + expect(preserves([undefinedAt("a", ref("i"))])).toBe(true); + // Past the counter, a present undefined extends the array. + expect(preserves([undefinedAt("a", plus(ref("i"), num(1)))])).toBe(false); + // The high counter of a two-counter loop is in range as well. + const paired = (body: IrStmt[]): boolean => + preservesArrayLength( + [cond, ...body], + { receiver: "a", inRange: "i", inRangeHigh: "j" }, + locals, + new Set(), + ); + expect(paired([undefinedAt("a", ref("j")), set("a", ref("j"), num(0))])).toBe(true); + expect(preserves([set("a", ref("j"), num(0))])).toBe(false); +}); diff --git a/packages/compiler/src/ir/array-length-stability.ts b/packages/compiler/src/ir/array-length-stability.ts index ed57972b6..7715ea1e1 100644 --- a/packages/compiler/src/ir/array-length-stability.ts +++ b/packages/compiler/src/ir/array-length-stability.ts @@ -162,6 +162,10 @@ export interface ArrayLengthLoop { * long as the length itself has not changed). An element write at this * index replaces an existing element or sets a non-index property. */ inRange: string | null; + /** A second such binding: the high counter of a two-counter loop + * (`for (let i = lo, j = hi; i < j; i++, j--)`), whose values stay + * between the low counter's start and its own. */ + inRangeHigh?: string | null; } /** Whether running `nodes` (a loop's condition, body and update) can never @@ -191,7 +195,10 @@ export function preservesArrayLength( // escaping. const receiver = (e: IrExpr): boolean => (e.kind === "varRef" && e.localId === loop.receiver) || expr(e); - const written = (id: string): boolean => id === loop.receiver || id === loop.inRange; + const inRange = (id: string): boolean => + (loop.inRange !== null && id === loop.inRange) || + (loop.inRangeHigh != null && id === loop.inRangeHigh); + const written = (id: string): boolean => id === loop.receiver || inRange(id); const expr = (e: IrExpr): boolean => { switch (e.kind) { case "varRef": @@ -224,12 +231,21 @@ export function preservesArrayLength( case "call": // A checked union extraction (`x!`, coercions/unions.ts): the // asserted arm's payload or a TypeError, and no user code. - return e.callee.startsWith("%union.narrow.") && e.args.every(expr); + if (e.callee.startsWith("%union.narrow.")) return e.args.every(expr); + // An element read as `T | undefined` (int32-arrays.ts isElementReader). + return ( + e.callee.startsWith("%arr.idxOr.") && + e.args.length === 2 && + receiver(e.args[0]!) && + expr(e.args[1]!) + ); case "mapIntrinsic": return READING_MAP_METHODS.has(e.method) && expr(e.receiver) && e.args.every(expr); case "setIntrinsic": return READING_SET_METHODS.has(e.method) && expr(e.receiver) && e.args.every(expr); case "libCall": + // The TypeError of an assertion past a union arm leaves the loop. + if (e.fn === "error.nodeThrow") return e.args.every(expr); return ( isBorrowSafeMath(e.fn) && e.args.every((arg) => arg.type.kind === "f64") && @@ -254,14 +270,18 @@ export function preservesArrayLength( break; case "arraySet": return ( - (separate(s.arr) || - (s.index.kind === "varRef" && - loop.inRange !== null && - s.index.localId === loop.inRange)) && + (separate(s.arr) || (s.index.kind === "varRef" && inRange(s.index.localId))) && receiver(s.arr) && expr(s.index) && expr(s.value) ); + case "arraySetUndefined": + // A present undefined replacing an element below the length. + return ( + (separate(s.arr) || (s.index.kind === "varRef" && inRange(s.index.localId))) && + receiver(s.arr) && + expr(s.index) + ); case "return": return s.value === null || expr(s.value); case "fieldSet": diff --git a/packages/compiler/src/ir/builtin-effects.ts b/packages/compiler/src/ir/builtin-effects.ts index 53f4c3eda..390c7fbc3 100644 --- a/packages/compiler/src/ir/builtin-effects.ts +++ b/packages/compiler/src/ir/builtin-effects.ts @@ -184,6 +184,13 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "num.toStringRadix", "num.toStringRadixF64", "insp.jsonDyn", + // A checked-dynamic value the inspector does not render (a promise, a + // URL, a RegExp) throws the 'not supported yet' Error. + "insp.dyn", + "insp.dynS", + // %o's showHidden walk refuses the kinds whose hidden entries it cannot + // render. + "insp.dynHidden", // diagnostics_channel: publish runs subscribers synchronously (a throw // propagates — the documented divergence from triggerUncaughtException); // the subscribe/unsubscribe forms throw Node's ERR_INVALID_ARG_TYPE for @@ -207,6 +214,8 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "async.awaitDyn", "als.run", "als.exitRun", + "als.newOptions", + "als.bindCallbackDyn", "dc.chanBindStore", "dc.chanRunStores", "http.resWriteHeadDyn", @@ -694,6 +703,7 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "class.defineField", "class.throwReadOnly", "class.throwNotExtensible", + "record.throwFrozen", "dyn.toString", "dyn.valueOf", "string.fromCodePoint", @@ -736,6 +746,8 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "url.set", "url.setChecked", "url.fileURLToPathUrl", + "fs.checkPath", + "fs.checkReadEncoding", "url.fileURLToPathChecked", "url.fileURLToPathOptions", "url.fileURLToPathBuffer", @@ -748,6 +760,8 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "url.legacyResolve", "url.domainToASCII", "url.domainToUnicode", + // ERR_INVALID_ARG_TYPE and the credentials' URIError. + "url.toHttpOptions", // The win32-target flavor of pathToFileURL (same runtime entry point — // the bridge dispatches by the binary's platform): Node's win32 arm // raises ERR_INVALID_ARG_VALUE TypeErrors for malformed UNC inputs, so @@ -832,12 +846,15 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "buffer.poolFromDyn", "buffer.poolFromDynRef", "buffer.poolFromDynOffset", + "buffer.poolFromDynRange", // The checked-dynamic compare/equals validators: Node's argument // ladders throw ERR_INVALID_ARG_TYPE / ERR_OUT_OF_RANGE catchably. "buffer.compareChk", "bytes.equalsChk", "bytes.compareChk", "buffer.newStringFail", + // Inherited and symbol-keyed Buffer.prototype members throw SC2020. + "buffer.prototypeMember", "fs.toUnixTimestamp", "fs.existsChk", "fs.mkdtempChk", @@ -1028,6 +1045,7 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "crypto.generateKeyPairCb", "crypto.signOneshot", "crypto.verifyOneshot", + "crypto.diffieHellman", "crypto.createSign", "crypto.createVerify", ]); diff --git a/packages/compiler/src/ir/builtin-signatures.ts b/packages/compiler/src/ir/builtin-signatures.ts index 1be0f95cd..216e46a4d 100644 --- a/packages/compiler/src/ir/builtin-signatures.ts +++ b/packages/compiler/src/ir/builtin-signatures.ts @@ -163,6 +163,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "fetch.abortNow": { argTypes: [DYN], result: DYN }, "fetch.abortAny": { argTypes: [DYN], result: DYN }, "fetch.streamNew": { argTypes: [DYN], result: DYN }, + "fetch.transformNew": { argTypes: [], result: DYN }, // Program-dependent iterable: typed arrays/bytes/string stay intact so // the native stream can pull lazily; checked-dynamic values are the // fallback. The libCall validator in validate.ts checks the closed set. @@ -272,6 +273,28 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ /** Throws Node's TypeError for a class field initializer on a * non-extensible instance: the field name. */ "class.throwNotExtensible": { argTypes: [STRING], result: VOID }, + /** Object.freeze of a record whose shape is freezable (frozen-records.ts): + * sets the frozen bit in its rc word (scr_record_freeze). The receiver is + * any record type. */ + "record.freeze": { argTypes: [null], result: VOID }, + /** Whether a record is frozen: its rc word's frozen bit, set by + * record.freeze and dyn.freeze of a capsule. Emitted inline. The receiver + * is any record type. */ + "record.isFrozen": { argTypes: [null], result: BOOL }, + /** Throws Node's TypeError for a write to a frozen record: the key, the + * object's text in the message ("#") and the operation (0 assign, + * 1 add, 2 delete). */ + "record.throwFrozen": { argTypes: [STRING, STRING, F64], result: VOID }, + /** Object.freeze of an array (scr_arr_freeze): sets its frozen bit, which + * the write guards of IrModule.freezableArrays test, and isFrozen reads. + * The receiver is any array type. */ + "array.freeze": { argTypes: [null], result: VOID }, + "array.isFrozen": { argTypes: [null], result: BOOL }, + /** Object.freeze, seal or preventExtensions of a Map or Set + * (scr_map_prevent_extensions), which only makes it not extensible: its + * entries stay writable. The receiver is any map or set type. */ + "collection.preventExtensions": { argTypes: [null], result: VOID }, + "collection.isExtensible": { argTypes: [null], result: BOOL }, "dyn.nativeSetNew": { argTypes: [DYN], result: DYN }, "dyn.nativeSetIs": { argTypes: [DYN], result: BOOL }, "dyn.nativeMapIs": { argTypes: [DYN], result: BOOL }, @@ -983,6 +1006,14 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "url.set": { argTypes: [URL_T, STRING, STRING], result: VOID }, "url.setChecked": { argTypes: [URL_T, STRING, DYN], result: VOID }, "url.fileURLToPathUrl": { argTypes: [URL_T], result: STRING }, + /** fileURLToPathUrl as a value: the path string, or the error it would + * throw (fs.existsSync and fs.promises URL paths). Never throws. */ + "url.fileURLToPathValue": { argTypes: [URL_T], result: DYN }, + /** getValidatedPath's null-byte check on a string path, and + * readFileSync's encoding check: what Node validates before it converts + * a later URL path. (path, argument name) and (encoding). */ + "fs.checkPath": { argTypes: [STRING, STRING], result: VOID }, + "fs.checkReadEncoding": { argTypes: [STRING], result: VOID }, "url.fileURLToPathChecked": { argTypes: [DYN], result: STRING }, "url.fileURLToPathOptions": { argTypes: [DYN, DYN], result: STRING }, /** node:url's legacy API (scr_url_legacy.c): parse(url, parseQueryString, @@ -997,6 +1028,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "url.legacyResolve": { argTypes: [DYN, DYN, BOOL], result: STRING }, "url.domainToASCII": { argTypes: [DYN, BOOL], result: STRING }, "url.domainToUnicode": { argTypes: [DYN, BOOL], result: STRING }, + /** url.urlToHttpOptions over a checked-dynamic argument: a URL answers + * Node's null-prototype options object; a primitive or null throws + * ERR_INVALID_ARG_TYPE. */ + "url.toHttpOptions": { argTypes: [DYN], result: DYN }, /** node:punycode (scr_url.c): punycode.js 2.1.0's string conversions; * decode/toUnicode (and, past 32-bit deltas, encode/toASCII) throw the * module's RangeErrors, ucs2Encode String.fromCodePoint's. warn marks the @@ -2993,6 +3028,9 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ /** poolFromDyn that also copies an array crossing by reference. */ "buffer.poolFromDynRef": { argTypes: [DYN, STRING], result: BYTES_U8 }, "buffer.poolFromDynOffset": { argTypes: [DYN, DYN], result: BYTES_U8 }, + /** Buffer.from(value, byteOffset, length): an ArrayBuffer's range with + * Node's fromArrayBuffer checks; other values as poolFromDynOffset. */ + "buffer.poolFromDynRange": { argTypes: [DYN, DYN, DYN], result: BYTES_U8 }, "buffer.poolSize": { argTypes: [], result: F64 }, "buffer.setPoolSize": { argTypes: [F64], result: VOID }, /** Buffer.byteLength(string, enc) — enc a NORMALIZED literal like @@ -3027,6 +3065,12 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ * always throws Node's ERR_INVALID_ARG_TYPE ("The \"string\" argument * must be of type string. Received ..."). Borrowed dyn; may-throw. */ "buffer.newStringFail": { argTypes: [DYN], result: BYTES_U8 }, + "buffer.deprecation": { argTypes: [], result: VOID }, + "buffer.deprecationCount": { argTypes: [], result: VOID }, + "buffer.constructor": { argTypes: [], result: DYN }, + "buffer.installValue": { argTypes: [], result: VOID }, + "buffer.staticValue": { argTypes: [STRING], result: DYN }, + "buffer.prototypeMember": { argTypes: [DYN], result: DYN }, /** fs._toUnixTimestamp(time) over a dyn value: numeric strings and * finite numbers coerce (negatives answer now/1000, Node's shape); * everything else throws Node's ERR_INVALID_ARG_TYPE. May-throw. */ @@ -3149,6 +3193,8 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ * the +1 %Transform stream. flush takes the kind value, params the level * and strategy values; the Cb forms move their () => void callback. The * receivers are borrowed. */ + /** Connects scr_brotli.c to the zlib engine (%main, before any module). */ + "zlib.brotliInstall": { argTypes: [], result: VOID }, "zlib.streamNew": { argTypes: [F64, DYN], result: VOID }, "zlib.streamBytesWritten": { argTypes: [null], result: F64 }, /** `stream instanceof zlib.Gzip`: the borrowed stream and the class's @@ -3171,6 +3217,18 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ // or an optional Error | null slot (same success shape as fs.rename). "process.stdoutWriteBytesCb": { argTypes: [BYTES_U8, STRING, null], result: BOOL }, "process.stderrWriteBytesCb": { argTypes: [BYTES_U8, STRING, null], result: BOOL }, + /** The value of a FULFILLED promise, read without a microtask turn (+1 + * for references): the tuple form of Promise.all reads its entries this + * way once the combinator fulfills, each entry widened to Promise + * and read at the type it was built with (program-dependent). */ + "promise.fulfilledValue": { argTypes: [null], result: VOID }, + /** Promise.all's tuple form as a value: the entries (each widened to + * Promise) join the runtime combinator, and when it fulfills the + * function reads the fulfilled entries into the tuple inside that + * settlement, so the result settles on the combinator's turn; a + * rejection settles it the same way. The result is the tuple's promise + * (program-dependent). */ + "promise.allTuple": { argTypes: [null, null], result: VOID }, "fsp.readFile": { argTypes: [STRING, STRING], result: { kind: "promise", inner: STRING } }, "fsp.writeFile": { argTypes: [STRING, STRING], result: { kind: "promise", inner: VOID } }, /** fs.promises.writeFile(path, data, { mode }): the settled-promise @@ -4408,12 +4466,19 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "insp.buffer": { argTypes: [BYTES_U8], result: STRING }, /** A numeric typed array that is not a Buffer (any element kind). */ "insp.typedArray": { argTypes: [null, F64, F64], result: STRING }, + /** A u8 byte view in a union arm, element or field: a DataView, Buffer + * or Uint8Array by its own flags. */ + "insp.bytes": { argTypes: [null, F64, F64], result: STRING }, "insp.error": { argTypes: [null, F64, F64], result: STRING }, "insp.errorHasCause": { argTypes: [null], result: BOOL }, "insp.errorBagHasProperties": { argTypes: [DYN], result: BOOL }, "insp.errorCause": { argTypes: [null, F64, F64], result: STRING }, "insp.dyn": { argTypes: [DYN, F64, F64], result: STRING }, "insp.dynS": { argTypes: [DYN, F64], result: STRING }, + /** util.format's %o over a checked-dynamic value: inspect with + * showHidden. Kinds whose hidden entries have no rendering throw the + * 'not supported yet' Error. */ + "insp.dynHidden": { argTypes: [DYN, F64], result: STRING }, "insp.begin": { argTypes: [F64], result: VOID }, /** util.inspect's breakLength, compact (0 for false) and sorted for the * renders that follow; the call site restores 80, 3 and false afterwards. */ @@ -4435,6 +4500,11 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ * (inspect, Object.keys/values/entries) then answer through its live * checked view. The receiver is any such record type. Never throws. */ "record.sourceHas": { argTypes: [null], result: BOOL }, + /** `record.sourceSpread(built, value)`: the record a literal starting + * with `...value` just built keeps the order of value's kept source + * (key-order-witness.ts ordered spreads). Both are record types. Never + * throws. */ + "record.sourceSpread": { argTypes: [null, null], result: VOID }, "insp.key": { argTypes: [STRING], result: STRING }, "insp.moreItems": { argTypes: [F64], result: STRING }, "insp.end": { argTypes: [STRING, STRING, STRING, F64, BOOL, BOOL], result: STRING }, @@ -4444,6 +4514,11 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ * `prefix inner` re-indented at the caller's level (+1). */ "insp.nestedBegin": { argTypes: [], result: VOID }, "insp.nestedEnd": { argTypes: [STRING, STRING], result: STRING }, + /** Around a top-level render that an inspection refusal can end: mark + * records the inspector's frame and nesting depth, unwind (in the + * render's finally) closes what the render left open above it. */ + "insp.mark": { argTypes: [], result: VOID }, + "insp.unwind": { argTypes: [], result: VOID }, /** node:string_decoder's utf8 StringDecoder (scr_string.c): the decoder * value is a one-field record whose f64 PACKS the pending partial * sequence (count + up to 3 raw bytes — Node buffers at most 3 for @@ -4725,6 +4800,20 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "als.exitRun": { argTypes: [F64, DYN, DYN], result: DYN }, "als.enterWith": { argTypes: [F64, DYN], result: VOID }, "als.disable": { argTypes: [F64], result: VOID }, + /** new AsyncLocalStorage(options) (Node's object check throws; MAY + * THROW) and the store's name option (dyn; "" without one). */ + "als.newOptions": { argTypes: [DYN], result: F64 }, + "als.name": { argTypes: [F64], result: DYN }, + /** A function bound to the active context: the completion callbacks of + * callback-style APIs (always false: an empty context leaves the + * callback unbound) and AsyncLocalStorage.bind (always true). The typed + * form answers its closure argument's type; the dyn form throws Node's + * ERR_INVALID_ARG_TYPE for a non-function when always (MAY THROW). */ + "als.bindCallback": { argTypes: [null, BOOL], result: VOID }, + "als.bindCallbackDyn": { argTypes: [DYN, BOOL], result: DYN }, + /** AsyncLocalStorage.snapshot(): a dyn function (cb, ...args) that runs + * cb in the context active at the snapshot. */ + "als.snapshot": { argTypes: [], result: DYN }, /** Channel.bindStore/unbindStore/runStores (scr_dc.c): the * AsyncLocalStorage integration — set-semantics bindings per store, * runStores entering every bound store with transform(data) around the @@ -4780,6 +4869,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "date.getMilliseconds": { argTypes: [DATE_T], result: F64 }, "date.getUTCMilliseconds": { argTypes: [DATE_T], result: F64 }, "date.getTimezoneOffset": { argTypes: [DATE_T], result: F64 }, + /** A Date object for a time value a local holds (date-values.ts): a + * lib-call argument passes the time value itself, and any other use + * creates the Date. */ + "date.fromTime": { argTypes: [F64], result: DATE_T }, /** Date.prototype.toString / toDateString / toTimeString and String(date): * V8's local-time formats with the host zone's long name, "Invalid Date" * for an invalid date. Owned (+1) string; never throws. */ @@ -4832,6 +4925,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "crypto.generateKeyPairSync": { argTypes: [DYN, DYN], result: DYN }, "crypto.generateKeyPairCb": { argTypes: [DYN, DYN, DYN], result: VOID }, "crypto.signOneshot": { argTypes: [DYN, DYN, DYN], result: DYN }, + "crypto.diffieHellman": { argTypes: [DYN, DYN], result: DYN }, "crypto.verifyOneshot": { argTypes: [DYN, DYN, DYN, DYN], result: DYN }, "crypto.createSign": { argTypes: [DYN], result: DYN }, "crypto.createVerify": { argTypes: [DYN], result: DYN }, @@ -4852,6 +4946,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "text.decoderEncoding": { argTypes: [DYN], result: F64 }, "text.decoderName": { argTypes: [F64], result: STRING }, "text.encodeInto": { argTypes: [DYN, DYN], result: DYN }, + /** `value instanceof TextDecoder` (true) or `TextEncoder` (false) on a + * checked-dynamic value: a codec record's handle box (scr_text_codec_dyn.c). + * Borrowed; never throws. */ + "text.codecIs": { argTypes: [DYN, BOOL], result: BOOL }, "buffer.isAscii": { argTypes: [DYN], result: BOOL }, "buffer.isUtf8": { argTypes: [DYN], result: BOOL }, "buffer.transcode": { argTypes: [DYN, DYN, DYN], result: BYTES_U8 }, diff --git a/packages/compiler/src/ir/function-references.test.ts b/packages/compiler/src/ir/function-references.test.ts index 26539b17a..db872a525 100644 --- a/packages/compiler/src/ir/function-references.test.ts +++ b/packages/compiler/src/ir/function-references.test.ts @@ -157,10 +157,12 @@ const MODULE_FIELDS: Record>, Role> = { "classes[].errorNameAfter": "other", "classes[].prototypeDataHelper": "reference", "classes[].instancePrototypeHelper": "reference", + "classes[].stringTagHelper": "reference", "classes[].localCaptures[].name": "other", "classes[].localCaptures[].localId": "other", "classes[].symbolFields[].field": "other", "classes[].symbolFields[].globalId": "other", + "classes[].symbolFields[].wellKnown": "other", "classes[].fields[].name": "other", "classes[].methods[]": "method", "classes[].prototypeNames[]": "other", @@ -219,6 +221,7 @@ const MODULE_FIELDS: Record>, Role> = { "wasmUntranslated[].sha256": "other", "wasmUntranslated[].reason": "other", "threadSlotKeys[]": "other", + "freezableArrays[]": "other", }; const loc = { file: "references.ts", start: 0, end: 0 }; @@ -280,6 +283,10 @@ const REFERENCE_FIXTURES: Record IrModule> = { ...module(), classes: [{ name: "%m0.Cell", fields: [], instancePrototypeHelper: TARGET, loc }], }), + "classes[].stringTagHelper": () => ({ + ...module(), + classes: [{ name: "%m0.Cell", fields: [], stringTagHelper: TARGET, loc }], + }), "lib.exports[].fnName": () => ({ ...module(), lib: { diff --git a/packages/compiler/src/ir/function-references.ts b/packages/compiler/src/ir/function-references.ts index db474e9bb..6696a3c11 100644 --- a/packages/compiler/src/ir/function-references.ts +++ b/packages/compiler/src/ir/function-references.ts @@ -24,6 +24,7 @@ export function moduleFunctionReferences(mod: IrModule): string[] { for (const cls of mod.classes ?? []) { if (cls.prototypeDataHelper !== undefined) names.push(cls.prototypeDataHelper); if (cls.instancePrototypeHelper !== undefined) names.push(cls.instancePrototypeHelper); + if (cls.stringTagHelper !== undefined) names.push(cls.stringTagHelper); } return names; } diff --git a/packages/compiler/src/ir/int32-arrays.ts b/packages/compiler/src/ir/int32-arrays.ts index 02de7268f..353755c05 100644 --- a/packages/compiler/src/ir/int32-arrays.ts +++ b/packages/compiler/src/ir/int32-arrays.ts @@ -1,3 +1,4 @@ +import { exprFunctionReference, moduleFunctionReferences } from "./function-references.js"; import { F64, type IrExpr, type IrFunction, type IrModule, type IrStmt } from "./ir.js"; type SwitchCase = Extract["cases"][number]; @@ -6,22 +7,29 @@ import { everyExprChild, everyStmtChild, everyStmtList, + mapExprChildren, + mapStmtChildren, transformExpr, transformStmt, transformStmtList, type IrTransform, } from "./traverse.js"; -/** Local number arrays the backend may store as int32 slots. +/** Number arrays the backend may store as int32 slots. * - * A candidate is a local `number[]` of a synchronous function that no - * other binding can hold and only compiled element operations reach: - * every write of the local stores a fresh array (an array literal, or - * `new Array(n).fill(v)`), and every read of it is the receiver of an - * element read, write, state test, `length`, `push` or `pop` (a discarded - * `pop` included), or the array of an element-reader helper call. The - * array never reaches a runtime function that reads its slots, a library - * call, a closure, a return or untyped code. + * A candidate is a `number[]` binding that only compiled element + * operations reach: a local of a synchronous function, a module global, or + * a parameter. Every write of a local or a global stores a fresh array (an + * array literal, or `new Array(n).fill(v)`); a parameter is never written. + * Every read of a candidate is the receiver of an element read, write, + * state test, `length`, `push` or `pop` (a discarded `pop` included), the + * array of an element-reader helper call, or the argument of a direct call + * whose parameter at that position is a candidate itself. A parameter is a + * candidate only when its function is reached by direct calls alone and + * every call passes a candidate there, so every binding that can hold one + * of these arrays is a candidate: the array never reaches a runtime + * function that reads its slots, a library call, a closure, a return or + * untyped code. * * Candidates are only proposed here. The int32 slot analysis * (int32-slots.ts) keeps the ones whose every stored element is a proven @@ -33,6 +41,18 @@ import { * calls are inlined first (rewriteCandidates), so no shared helper ever * receives an int32 array. */ +/** The candidate key of the binding `localId` as `fnName` reads it: + * `function\0local` for a local or parameter, `\0global` for a module + * global (`%g.` ids), which every function reads alike. */ +export function int32ArrayKey(fnName: string, localId: string): string { + return localId.startsWith("%g.") ? `\0${localId}` : `${fnName}\0${localId}`; +} + +/** The int32 element slot (int32-slots.ts) of a candidate key. */ +export function int32ArraySlot(key: string): string { + return key.startsWith("\0") ? `g:${key.slice(1)}` : `l:${key}`; +} + /** Array methods a candidate may be the receiver of: the runtime paths * behind them read and write int32 slots through the double-valued slot * accessors. */ @@ -202,10 +222,70 @@ function writesLocal(e: IrExpr, id: string): boolean { return !expr(e); } -/** The candidate locals of `fn` (see the module comment). */ -export function int32ArrayCandidates(fn: IrFunction, readers: ReadonlySet): Set { +/** Expression kinds an index can be built from without running any code. */ +const PURE_INDEX_KINDS: ReadonlySet = new Set([ + "varRef", + "numLit", + "boolLit", + "bin", + "unary", + "ternary", + "logical", + "arrayGet", + "arrayState", +]); + +/** Whether evaluating an index runs no code (no call, accessor or + * conversion): plain reads, literals and their operators only. */ +function pureIndex(e: IrExpr): boolean { + return PURE_INDEX_KINDS.has(e.kind) && everyExprChild(e, pureIndex, () => false); +} + +/** The parameters and globals a module's functions share as candidates, + * still assumed to qualify. */ +export interface SharedCandidates { + /** Candidate parameters, as `function\0local` keys. */ + params: ReadonlySet; + /** Candidate globals, as `%g.` ids. */ + globals: ReadonlySet; + /** Whether the parameter at `index` of `callee` is a candidate. */ + paramAt: (callee: string, index: number) => boolean; +} + +const NO_SHARED: SharedCandidates = { + params: new Set(), + globals: new Set(), + paramAt: () => false, +}; + +/** The candidates of `fn` (see the module comment): its own locals, its + * parameters in `shared.params`, and the globals of `shared.globals` that + * it reads only in candidate positions. `written` receives the globals it + * assigns fresh arrays. */ +export function int32ArrayCandidates( + fn: IrFunction, + readers: ReadonlySet, + shared: SharedCandidates = NO_SHARED, + written: Set = new Set(), +): Set { const result = new Set(); - if (fn.async || fn.generator) return result; + if (fn.async || fn.generator) { + // Globals read here leave the candidates. + for (const id of shared.globals) result.add(id); + everyStmtList(fn.body, { + expr: (e) => { + if ((e.kind === "varRef" || e.kind === "assignExpr") && result.has(e.localId)) + result.delete(e.localId); + return true; + }, + stmt: (s) => { + if ((s.kind === "varDecl" || s.kind === "assign") && result.has(s.localId)) + result.delete(s.localId); + return true; + }, + }); + return result; + } const captured = new Set( [...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId), ); @@ -217,11 +297,12 @@ export function int32ArrayCandidates(fn: IrFunction, readers: ReadonlySet(); + const allocated = new Set(); // A receiver position: the candidate is read there without escaping. const receiver = (e: IrExpr): void => { if (e.kind !== "varRef") expr(e); @@ -255,13 +336,28 @@ export function int32ArrayCandidates(fn: IrFunction, readers: ReadonlySet { + if (shared.paramAt(e.callee, index)) receiver(arg); + else expr(arg); + }); return true; } } @@ -272,7 +368,8 @@ export function int32ArrayCandidates(fn: IrFunction, readers: ReadonlySet | undefined, live: ReadonlySet): Set { + const result = new Set(); + if (ids) for (const id of ids) if (live.has(id)) result.add(id); + return result; +} + +/** The candidates of every function of `mod` that has any, only those + * whose keys are in `keep` when given. Parameters and globals are shared: + * starting from every one that may qualify, each pass drops the ones a + * function reads outside candidate positions, the parameters some call + * passes anything but a candidate, and the globals never assigned a fresh + * array, until a pass drops nothing. Functions in `redirected` keep their + * parameters: some of their calls reach another function. */ +export function moduleInt32ArrayCandidates( + mod: IrModule, + readers: ReadonlySet, + keep: ReadonlySet | null = null, + redirected: ReadonlySet = new Set(), +): Map> { + const kept = (key: string): boolean => keep === null || keep.has(key); + const arrayParams = new Map(); + for (const fn of mod.functions) { + // Class methods, constructors and runtime helpers (`%` names) keep + // their parameters. + if (fn.name.startsWith("%") || fn.async || fn.generator || redirected.has(fn.name)) continue; + const indexes: number[] = []; + fn.params.forEach((p, i) => { + if (p.type.kind === "array" && p.type.elem.kind === "f64") indexes.push(i); + }); + if (indexes.length > 0) arrayParams.set(fn.name, indexes); + } + let globals = new Set(); + for (const g of mod.globals ?? []) + if ( + g.type.kind === "array" && + g.type.elem.kind === "f64" && + g.initFlag === undefined && + kept(`\0${g.id}`) + ) + globals.add(g.id); + const result = new Map>(); + if (arrayParams.size === 0 && globals.size === 0) { + for (const fn of mod.functions) { + const candidates = int32ArrayCandidates(fn, readers); + for (const id of candidates) if (!kept(`${fn.name}\0${id}`)) candidates.delete(id); + if (candidates.size > 0) result.set(fn.name, candidates); + } + return result; + } + // One walk: the functions reached other than by direct calls, the direct + // calls of functions with number[] parameters, the globals each function + // reads or writes, and whether a module namespace object exists (its + // member reads can reach any global by name). + const reached = new Set(moduleFunctionReferences(mod)); + const calls: { caller: string; call: IrExpr & { kind: "call" } }[] = []; + const globalsIn = new Map>(); + let namespaces = false; + for (const fn of mod.functions) { + const used = new Set(); + const note = (id: string): void => { + if (globals.has(id)) used.add(id); + }; + everyStmtList(fn.body, { + expr: (e) => { + const name = exprFunctionReference(e); + if (name !== undefined) reached.add(name); + if (e.kind === "moduleNsRef") namespaces = true; + if (e.kind === "call" && arrayParams.has(e.callee)) + calls.push({ caller: fn.name, call: e }); + if (e.kind === "varRef" || e.kind === "assignExpr") note(e.localId); + return true; + }, + stmt: (s) => { + if (s.kind === "varDecl" || s.kind === "assign") note(s.localId); + return true; + }, + }); + if (used.size > 0) globalsIn.set(fn.name, used); + } + if (namespaces) { + globals = new Set(); + globalsIn.clear(); + } + const paramIds = new Map(); + let params = new Set(); + for (const fn of mod.functions) { + const indexes = arrayParams.get(fn.name); + if (!indexes || reached.has(fn.name)) continue; + paramIds.set( + fn.name, + fn.params.map((p) => p.localId), + ); + for (const i of indexes) { + const key = `${fn.name}\0${fn.params[i]!.localId}`; + if (kept(key)) params.add(key); + } + } + for (;;) { + const current = params; + const written = new Set(); + const liveGlobals = new Set(globals); + result.clear(); + for (const fn of mod.functions) { + const shared: SharedCandidates = { + params: current, + globals: liveIn(globalsIn.get(fn.name), globals), + paramAt: (callee, index) => { + const id = paramIds.get(callee)?.[index]; + return id !== undefined && current.has(`${callee}\0${id}`); + }, + }; + const candidates = int32ArrayCandidates(fn, readers, shared, written); + for (const id of shared.globals) if (!candidates.has(id)) liveGlobals.delete(id); + for (const id of candidates) + if (!shared.globals.has(id) && !kept(`${fn.name}\0${id}`)) candidates.delete(id); + if (candidates.size > 0) result.set(fn.name, candidates); + } + for (const id of liveGlobals) if (!written.has(id)) liveGlobals.delete(id); + const live = new Set( + [...current].filter((key) => { + const cut = key.indexOf("\0"); + return result.get(key.slice(0, cut))?.has(key.slice(cut + 1)) === true; + }), + ); + // A parameter needs a candidate at that position of every call; a call + // with fewer arguments passes undefined. + for (const { caller, call } of calls) { + const ids = paramIds.get(call.callee); + if (!ids) continue; + ids.forEach((id, i) => { + const key = `${call.callee}\0${id}`; + if (!live.has(key)) return; + const arg = call.args[i]; + const ok = + arg?.kind === "varRef" && + (arg.localId.startsWith("%g.") + ? liveGlobals.has(arg.localId) + : result.get(caller)?.has(arg.localId) === true); + if (!ok) live.delete(key); + }); + } + if (live.size === current.size && liveGlobals.size === globals.size) break; + params = live; + globals = liveGlobals; + } + return result; +} + +/** The candidates one array can reach together: a direct call's argument + * and the parameter it binds hold the same array, so their element facts + * stand or fall together. `group` maps each of `keys` to its group's keys + * (itself alone for most), and `globalReaders` each candidate global's + * key to the functions that read or write it. */ +export function int32ArrayGroups( + mod: IrModule, + keys: ReadonlySet, +): { group: Map; globalReaders: Map> } { + const parent = new Map(); + const root = (key: string): string => { + let r = key; + while (parent.has(r)) r = parent.get(r)!; + if (r !== key) parent.set(key, r); + return r; + }; + const globalReaders = new Map>(); + const params = new Map(mod.functions.map((fn) => [fn.name, fn.params])); + const paramKeys = new Set( + mod.functions.flatMap((fn) => fn.params.map((p) => `${fn.name}\0${p.localId}`)), + ); + if ([...keys].some((key) => key.startsWith("\0") || paramKeys.has(key))) { + for (const fn of mod.functions) { + const touch = (id: string): void => { + const key = int32ArrayKey(fn.name, id); + if (!key.startsWith("\0") || !keys.has(key)) return; + const set = globalReaders.get(key) ?? new Set(); + set.add(fn.name); + globalReaders.set(key, set); + }; + everyStmtList(fn.body, { + expr: (e) => { + if (e.kind === "varRef" || e.kind === "assignExpr") touch(e.localId); + if (e.kind === "call") + e.args.forEach((arg, i) => { + const param = params.get(e.callee)?.[i]; + if (arg.kind !== "varRef" || !param) return; + const a = int32ArrayKey(fn.name, arg.localId), + p = `${e.callee}\0${param.localId}`; + if (!keys.has(a) || !keys.has(p)) return; + const ra = root(a), + rp = root(p); + if (ra !== rp) parent.set(ra, rp); + }); + return true; + }, + stmt: (s) => { + if (s.kind === "varDecl" || s.kind === "assign") touch(s.localId); + return true; + }, + }); + } + } + const members = new Map(); + for (const key of keys) { + const r = root(key); + const list = members.get(r); + if (list) list.push(key); + else members.set(r, [key]); + } + const group = new Map(); + for (const key of keys) group.set(key, members.get(root(key))!); + return { group, globalReaders }; +} + /** The element values stored into each candidate: element writes, pushed * values, literal elements and fill values. */ export function int32ArrayStores( @@ -341,16 +656,34 @@ export function int32ArrayStores( return stores; } +const REPEATED_INDEX_OPS = new Set(["+", "-", "*", "&", "|", "^", "<<", ">>", ">>>"]); + +/** An index the inlined read may evaluate at each of its uses: a plain read, + * a literal, or integer arithmetic over them. Repeating it keeps the forms + * the backend derives from its operands at every use, such as the exact i64 + * of an int32 product, where a bound local holds only the double. */ +function repeatableIndex(e: IrExpr): boolean { + if (e.kind === "varRef" || e.kind === "numLit") return true; + if (e.kind === "unary") return e.op === "-" && repeatableIndex(e.operand); + return ( + e.kind === "bin" && + REPEATED_INDEX_OPS.has(e.op) && + repeatableIndex(e.left) && + repeatableIndex(e.right) + ); +} + /** Rewrite a function's candidates for the backend. A discarded pop uses * the candidate in place of its hidden local. An element-reader helper call * whose array is a candidate is inlined: the helper's returned ternary, with * the candidate for its array and the index for its index, so the read means - * the same. An index that is not a plain read or literal is bound to a fresh - * local first: in its own declaration just before the statement when the - * call is the whole value of a declaration or assignment (the form lowering - * gives `a[i]!`, and the one the backend reads without a box), otherwise in a - * sequence around the ternary. Selection keeps out indexes that write the - * array, so evaluating the index before the array read is unobservable. */ + * the same. An index that is not repeatable (repeatableIndex) is bound to a + * fresh local first: in its own declaration just before the statement when + * the call is the whole value of a declaration or assignment (the form + * lowering gives `a[i]!`, and the one the backend reads without a box), + * otherwise in a sequence around the ternary. Selection keeps out indexes + * that write the array, so evaluating the index before the array read is + * unobservable. */ function rewriteCandidates( fn: IrFunction, candidates: ReadonlySet, @@ -374,14 +707,13 @@ function rewriteCandidates( candidates.has(e.args[0]!.localId) ? e : null; - const simple = (e: IrExpr): boolean => e.kind === "varRef" || e.kind === "numLit"; // The statements of a list, with complex read indexes declared first. const hoist = (list: IrStmt[]): IrStmt[] => list.flatMap((s): IrStmt[] => { if (s.kind !== "varDecl" && s.kind !== "assign") return [s]; const value = s.kind === "varDecl" ? s.init : s.value; const call = readerCall(value); - if (!call || simple(call.args[1]!)) return [s]; + if (!call || repeatableIndex(call.args[1]!)) return [s]; const { decl, ref } = bind(call.args[1]!); const read: IrExpr = { ...call, args: [call.args[0]!, ref] }; if (s.kind === "varDecl") return [decl, { ...s, init: read }]; @@ -444,21 +776,23 @@ function rewriteCandidates( if (ret.kind !== "return" || !ret.value) return e; let index = call.args[1]!; const stmts: IrStmt[] = []; - if (!simple(index)) { + if (!repeatableIndex(index)) { const { decl, ref } = bind(index); stmts.push(decl); index = ref; } const operand = index; - const body = transformExpr(reparseExpr(ret.value), { - expr: (x) => { - if (x.kind !== "varRef") return x; - if (x.localId === arrayParam!.localId) return reparseExpr(array); - if (x.localId === indexParam!.localId) return reparseExpr(operand); - return x; - }, - stmt: (x) => x, - }); + // The arguments' copies are not searched again: an index may read a + // local that has the id of one of the helper's parameters. + const substituteExpr = (x: IrExpr): IrExpr => { + if (x.kind !== "varRef") return mapExprChildren(x, substituteExpr, substituteStmt); + if (x.localId === arrayParam!.localId) return reparseExpr(array); + if (x.localId === indexParam!.localId) return reparseExpr(operand); + return x; + }; + const substituteStmt = (x: IrStmt): IrStmt => + mapStmtChildren(x, substituteExpr, substituteStmt); + const body = substituteExpr(reparseExpr(ret.value)); // The ternary is a fresh copy (reparseExpr): it takes the call's location. const typed = body; typed.loc = e.loc; @@ -471,40 +805,45 @@ function rewriteCandidates( return { ...fn, body, locals: locals.filter((local) => !popLocals.has(local.id)) }; } -/** Propose the module's int32 array candidates (`function\0local` keys in +/** Propose the module's int32 array candidates (int32ArrayKey keys in * `int32ArrayCandidates`), only those in `keep` when given, and rewrite * their functions. */ export function markInt32ArrayCandidates( mod: IrModule, keep: ReadonlySet | null = null, + redirected: ReadonlySet = new Set(), ): IrModule { if (mod.lib !== undefined) return mod; const readers = new Map(); for (const fn of mod.functions) if (isElementReader(fn)) readers.set(fn.name, fn); - const names = new Set(readers.keys()); - const keys: string[] = []; + const all = moduleInt32ArrayCandidates(mod, new Set(readers.keys()), keep, redirected); + if (all.size === 0) return mod; + const keys = new Set(); const functions = mod.functions.map((fn) => { - const candidates = int32ArrayCandidates(fn, names); - for (const id of candidates) - if (keep === null || keep.has(`${fn.name}\0${id}`)) keys.push(`${fn.name}\0${id}`); - else candidates.delete(id); - return candidates.size === 0 ? fn : rewriteCandidates(fn, candidates, readers); + const candidates = all.get(fn.name); + if (!candidates) return fn; + for (const id of candidates) keys.add(int32ArrayKey(fn.name, id)); + return rewriteCandidates(fn, candidates, readers); }); - return keys.length === 0 ? mod : { ...mod, functions, int32ArrayCandidates: keys }; + return { ...mod, functions, int32ArrayCandidates: [...keys] }; } /** The module the backend emits: rewritten for exactly the candidates the * int32 slot analysis (`prove`, returning its int32Arrays) keeps, so a * module without int32 arrays is `mod` itself. A dropped candidate's * rewrite is undone by marking again without it, until every candidate - * marked is kept. */ + * marked is kept. `redirected` names the functions some of whose calls the + * backend emits as calls of another function (a result-slot entry): their + * parameters keep doubles. */ export function selectInt32Arrays( mod: IrModule, prove: (marked: IrModule) => ReadonlySet, + redirected: (mod: IrModule) => ReadonlySet = () => new Set(), ): IrModule { let keep: ReadonlySet | null = null; + const others = redirected(mod); for (let round = 0; round < 4; round++) { - const marked = markInt32ArrayCandidates(mod, keep); + const marked = markInt32ArrayCandidates(mod, keep, others); if (marked === mod) return mod; const kept = prove(marked); const proposed = marked.int32ArrayCandidates ?? []; diff --git a/packages/compiler/src/ir/int32-slots.test.ts b/packages/compiler/src/ir/int32-slots.test.ts index be933ab38..0739bbb4f 100644 --- a/packages/compiler/src/ir/int32-slots.test.ts +++ b/packages/compiler/src/ir/int32-slots.test.ts @@ -281,6 +281,7 @@ test("dynamic stores disqualify the field names they can reach", () => { "dyn.typeof", "insp.dyn", "insp.dynS", + "insp.dynHidden", "dyn.objCreate", "dyn.getPrototype", "dyn.nameClassPrototype", @@ -357,6 +358,52 @@ test("an Error subclass constructor forwarding dynamic arguments stores no class expect(slots.isField("C", "a")).toBe(true); }); +test("Buffer.from over an ArrayBuffer range and Buffer as a value keep class fields int32", () => { + // `Buffer.from(view.buffer, offset, length)` passes its operands as + // checked values, and a program that reads the Buffer function's value + // installs it in %main; neither stores into a class instance. + const classes = [cls("C", ["a"])]; + const view: IrExpr = { loc, kind: "dynFrom", value: ref("o", obj("C")), type: DYN }; + const libCall = (fnName: string, args: IrExpr[], type: IrType = VOID): IrStmt => + stmt({ loc, kind: "libCall", fn: fnName, args, type } as IrExpr); + const slots = analyzeInt32Slots( + mod(classes, [ + fn( + "load", + [ + { id: "o", type: obj("C") }, + { id: "storage", type: DYN }, + ], + [ + set(ref("o", obj("C")), "C", "a", num(1)), + libCall("dyn.typedRefIs", [ + view, + { loc, kind: "strLit", value: "object:C", type: STRING }, + ]), + libCall("buffer.installValue", []), + libCall("buffer.constructor", [], DYN), + libCall( + "buffer.poolFromDynRange", + [ref("storage", DYN), num(2), num(4)].map((arg): IrExpr => + arg.type.kind === "dyn" ? arg : { loc, kind: "dynFrom", value: arg, type: DYN }, + ), + ), + libCall("buffer.poolFromDynOffset", [ + ref("storage", DYN), + { + loc, + kind: "dynFrom", + value: num(2), + type: DYN, + }, + ]), + ], + ), + ]), + ); + expect(slots.isField("C", "a")).toBe(true); +}); + test("captured bindings share one proof across the declaring function and its closures", () => { const closure: IrExpr = { loc, kind: "closure", fnName: "inner", captures: ["s"], type: DYN }; const outer = (): IrFunction => ({ diff --git a/packages/compiler/src/ir/int32-slots.ts b/packages/compiler/src/ir/int32-slots.ts index 8b992e6a8..06a173c1a 100644 --- a/packages/compiler/src/ir/int32-slots.ts +++ b/packages/compiler/src/ir/int32-slots.ts @@ -20,7 +20,13 @@ import { type IrNodeKind, } from "./node-index.js"; import { findInitializerBindings, withInitializerBindings } from "./initializer-bindings.js"; -import { checkableInt32Allocations, int32ArrayStores } from "./int32-arrays.js"; +import { + checkableInt32Allocations, + int32ArrayGroups, + int32ArrayKey, + int32ArraySlot, + int32ArrayStores, +} from "./int32-arrays.js"; import { everyExprChild, everyStmtChild, everyTypeChild } from "./traverse.js"; /** The storage slot an array-valued expression reads, as an @@ -257,9 +263,10 @@ export class Int32Slots { /** Whether the module has int32 array candidates (the narrowedReads * range fact). */ narrowedReads = false; - /** Local number arrays (`function\0local`) stored as int32 slots: the - * module's int32 array candidates whose every stored element the - * fixpoint proved an int32. */ + /** Number arrays stored as int32 slots (int32ArrayKey keys: locals and + * parameters as `function\0local`, globals as `\0global`): the module's + * int32 array candidates whose every stored element, through every + * binding that can hold the same array, the fixpoint proved an int32. */ readonly int32Arrays = new Set(); /** Local number arrays that may hold int32 slots with checked stores: the * candidates whose element stores the fixpoint proved exact integers but @@ -442,8 +449,16 @@ const DYN_READ_ONLY = new Set([ "error.new", "error.newOptions", "fs.utimesSync", + // Buffer.prototype members read as values: a lookup by key. + "buffer.prototypeMember", + // Buffer.from(value, offset[, length]) over checked values: an + // ArrayBuffer's view after Node's offset and length conversions, or a copy + // of a string, array-like or typed array's values. + "buffer.poolFromDynOffset", + "buffer.poolFromDynRange", "insp.dyn", "insp.dynS", + "insp.dynHidden", // The dynamic-tier operators run their dyn twins (scr_js_value.c); the // conversions move a value between dyn and jsval, and js.toF64 is // dynCheck's number check. @@ -847,17 +862,29 @@ export function analyzeInt32Slots(mod: IrModule, inputs: Int32SlotInputs = {}): for (const key of contracts?.int32Locals ?? []) slots.addInt32Local(key); for (const key of contracts?.int32Elements ?? []) slots.addInt32Elements(key); // Int32 array candidates (ir/int32-arrays.ts): assumed to hold int32 - // elements until a store the fixpoint cannot prove drops one. + // elements until a store the fixpoint cannot prove drops one, with every + // candidate the same array can reach (its group). const arrayCandidates = new Map>(); - slots.narrowedReads = (mod.int32ArrayCandidates?.length ?? 0) > 0; - for (const key of mod.int32ArrayCandidates ?? []) { + const globalArrays = new Set(); + const arrayKeys = new Set(mod.int32ArrayCandidates ?? []); + slots.narrowedReads = arrayKeys.size > 0; + for (const key of arrayKeys) { const cut = key.indexOf("\0"); - const fnName = key.slice(0, cut); - const set = arrayCandidates.get(fnName) ?? new Set(); - set.add(key.slice(cut + 1)); - arrayCandidates.set(fnName, set); - slots.addInt32Elements(`l:${key}`); + if (cut === 0) globalArrays.add(key.slice(1)); + else { + const fnName = key.slice(0, cut); + const set = arrayCandidates.get(fnName) ?? new Set(); + set.add(key.slice(cut + 1)); + arrayCandidates.set(fnName, set); + } + slots.addInt32Elements(int32ArraySlot(key)); } + const arrayGroups = arrayKeys.size > 0 ? int32ArrayGroups(mod, arrayKeys) : null; + // Functions that read or write a candidate global. + const globalArrayFunctions = new Set(); + if (arrayGroups) + for (const names of arrayGroups.globalReaders.values()) + for (const name of names) globalArrayFunctions.add(name); /* ── candidate fields ── */ const children = new Map(); @@ -1139,6 +1166,7 @@ export function analyzeInt32Slots(mod: IrModule, inputs: Int32SlotInputs = {}): const candidateWrites = nodes?.scan(CANDIDATE_WRITE_KINDS); const hasCandidateWrite = (fn: IrFunction): boolean => { if (arrayCandidates.has(fn.name)) return true; + if (globalArrayFunctions.has(fn.name)) return true; const box = (localId: string): boolean => slots.hasBox(slots.boxGroup(fn.name, localId)); const returns = slots.hasReturn(fn.name); let found = false; @@ -1209,25 +1237,49 @@ export function analyzeInt32Slots(mod: IrModule, inputs: Int32SlotInputs = {}): // (checkableInt32Allocations), it keeps int32 slots with checked stores // (checkedInt32Arrays), which widen it to doubles in place at the first // store that is not an int32. - const arrays = arrayCandidates.get(fn.name); - if (arrays) { - let dropped = false; + const locals = arrayCandidates.get(fn.name); + let arrays = locals; + if (globalArrayFunctions.has(fn.name)) { + const both = new Set(); + if (locals) for (const id of locals) both.add(id); + for (const id of globalArrays) both.add(id); + arrays = both; + } + if (arrays && arrayGroups) { + const requeue = (name: string): void => { + if (queued.has(name)) return; + queued.add(name); + queue.push(name); + }; + const params = new Set(fn.params.map((param) => param.localId)); for (const [id, values] of int32ArrayStores(fn, arrays)) { - const key = `l:${fn.name}\0${id}`; - if (!slots.hasInt32ElementSlot(key) || values.every(int32)) continue; - slots.dropInt32Elements(key); + const key = int32ArrayKey(fn.name, id); + if (!slots.hasInt32ElementSlot(int32ArraySlot(key)) || values.every(int32)) continue; + // The array's group drops together: every function reading a + // member is analyzed again without its facts. + const group = arrayGroups.group.get(key) ?? [key]; + for (const member of group) { + slots.dropInt32Elements(int32ArraySlot(member)); + if (member.startsWith("\0")) { + const names = arrayGroups.globalReaders.get(member); + if (names) for (const name of names) requeue(name); + } else requeue(member.slice(0, member.indexOf("\0"))); + } + // Checked stores serve a lone local: its allocations are the only + // arrays it holds. const checkable = (value: IrExpr): boolean => { const range = ranges.get(value); return int32(value) || range != null; }; - if (values.every(checkable) && checkableInt32Allocations(fn, id)) - slots.checkedInt32Arrays.add(`${fn.name}\0${id}`); - else reason(`array:${fn.name}\0${id}`, "an element store is not int32"); - dropped = true; - } - if (dropped && !queued.has(fn.name)) { - queued.add(fn.name); - queue.push(fn.name); + if ( + group.length === 1 && + !key.startsWith("\0") && + !params.has(id) && + values.every(checkable) && + checkableInt32Allocations(fn, id) + ) + slots.checkedInt32Arrays.add(key); + else for (const member of group) reason(`array:${member}`, "an element store is not int32"); } } // An int32 written into a slot still assumed non-negative: widen the @@ -1298,10 +1350,8 @@ export function analyzeInt32Slots(mod: IrModule, inputs: Int32SlotInputs = {}): } for (const proven of provenByFunction.values()) for (const value of proven) slots.provenWrites.add(value); - for (const [fnName, ids] of arrayCandidates) - for (const id of ids) - if (slots.hasInt32ElementSlot(`l:${fnName}\0${id}`)) - slots.int32Arrays.add(`${fnName}\0${id}`); + for (const key of arrayKeys) + if (slots.hasInt32ElementSlot(int32ArraySlot(key))) slots.int32Arrays.add(key); const ranged = rangedParameters( slots, functions, diff --git a/packages/compiler/src/ir/integer-loops.ts b/packages/compiler/src/ir/integer-loops.ts index d6942718a..2f7d2bea6 100644 --- a/packages/compiler/src/ir/integer-loops.ts +++ b/packages/compiler/src/ir/integer-loops.ts @@ -119,13 +119,26 @@ function stableLoopValue( stableLoopValue(value.receiver, locals, body, invariant, counter) ); case "bytesIntrinsic": - case "arrIntrinsic": + case "arrIntrinsic": { + // The receiver may be one arm of a union binding (indexed-unions.ts). + const receiver = + value.receiver.kind === "unionNarrow" ? value.receiver.value : value.receiver; return ( (value.method === "length" || (value.kind === "bytesIntrinsic" && value.method === "byteLength")) && - value.receiver.kind === "varRef" && - (value.kind === "bytesIntrinsic" || !invariant || stableLength(value.receiver.localId)) && - stableLoopValue(value.receiver, locals, body, invariant, counter) + receiver.kind === "varRef" && + (value.kind === "bytesIntrinsic" || !invariant || stableLength(receiver.localId)) && + stableLoopValue(receiver, locals, body, invariant, counter) + ); + } + // A union binding dispatched on its tag (indexed-unions.ts): stable + // when the binding is and every arm is. + case "ternary": + return ( + value.cond.kind === "unionIsTag" && + stableLoopValue(value.cond.value, locals, body, invariant, counter) && + stableLoopValue(value.then, locals, body, invariant, counter, stableLength) && + stableLoopValue(value.else_, locals, body, invariant, counter, stableLength) ); default: return false; diff --git a/packages/compiler/src/ir/integer-ranges.test.ts b/packages/compiler/src/ir/integer-ranges.test.ts index fcb040275..5ae04d28f 100644 --- a/packages/compiler/src/ir/integer-ranges.test.ts +++ b/packages/compiler/src/ir/integer-ranges.test.ts @@ -440,6 +440,37 @@ test("int32-closed locals keep the int32 range across loops and switches", () => expect(switched.get(afterSwitch)).toEqual(INT32); }); +test("loop-carried locals written from Int32[] element reads keep the int32 range", () => { + const arrType = { kind: "array", elem: F64 } as const; + const loop = (localId: string): { after: IrExpr; f: IrFunction } => { + const after = ref(); + // `x = a[x]!` in a loop: a cursor that follows indices stored in the array. + const read: IrExpr = { + loc, + kind: "arrayGet", + arr: { loc, kind: "varRef", localId, type: arrType }, + index: ref(), + type: F64, + }; + const f = fn([ + assign(num(0)), + { + loc, + kind: "while", + cond: condition("<", ref("unknown"), num(1)), + body: [assign(read)], + }, + observe(after), + ]); + return { after, f }; + }; + const elements = { elements: (arr: IrExpr) => arr.kind === "varRef" && arr.localId === "ints" }; + const ints = loop("ints"); + expect(analyzeIntegerRanges(ints.f, elements).get(ints.after)).toEqual(INT32); + const floats = loop("floats"); + expect(analyzeIntegerRanges(floats.f, elements).get(floats.after) ?? null).toBeNull(); +}); + test("uint32-closed locals keep the uint32 range across loop back edges", () => { // for (let i = 0; i < n; i++) { v = unknown >>> 0; acc = (acc + v) >>> 0; ... } const loop = (start: IrExpr | null, writes: IrExpr[]) => { diff --git a/packages/compiler/src/ir/integer-ranges.ts b/packages/compiler/src/ir/integer-ranges.ts index 0e5ca426d..683ceab75 100644 --- a/packages/compiler/src/ir/integer-ranges.ts +++ b/packages/compiler/src/ir/integer-ranges.ts @@ -480,6 +480,7 @@ export function analyzeIntegerRangeFacts( return boxedRange(e.localId); } case "fieldGet": + case "arrayGet": case "call": return slotRange(e); default: diff --git a/packages/compiler/src/ir/ir.test.ts b/packages/compiler/src/ir/ir.test.ts index d62c9c83c..30fe6f50e 100644 --- a/packages/compiler/src/ir/ir.test.ts +++ b/packages/compiler/src/ir/ir.test.ts @@ -57,7 +57,8 @@ describe("IR kind sets", () => { test("distinguishes pointer values from object-like scalars", () => { expect(POINTER_KINDS.has("record")).toBe(true); - expect(POINTER_KINDS.has("date")).toBe(false); + expect(POINTER_KINDS.has("date")).toBe(true); + expect(POINTER_KINDS.has("procStream")).toBe(false); }); test("distinguishes typed-rest closure ABIs", () => { diff --git a/packages/compiler/src/ir/ir.ts b/packages/compiler/src/ir/ir.ts index 878088d65..3818433f8 100644 --- a/packages/compiler/src/ir/ir.ts +++ b/packages/compiler/src/ir/ir.ts @@ -440,6 +440,7 @@ const POINTER_KIND_LIST = [ "url", "searchParams", "symbol", + "date", ...POINTER_HANDLE_KINDS, "func", "object", @@ -463,7 +464,7 @@ export const POINTER_KINDS = irKindSet(POINTER_KIND_LIST); export const RUNTIME_RC_STEMS: Record = { f64: "", bigint: "scr_bigint", - date: "", + date: "scr_date", string: "scr_str", bool: "", array: "scr_arr", @@ -645,6 +646,7 @@ export function isSupportedArrayElem(t: IrType): boolean { case "func": case "promise": case "regex": + case "date": case "child": case "netServer": // fs.Stats snapshots (`await Promise.all(paths.map(stat))`): immutable @@ -689,7 +691,8 @@ export function isIdentityCollectionKey(t: IrType): boolean { // functions intern one immortal closure, inner closures allocate once // per evaluation), so pointer identity is JS function identity. // Closures are cycle-headered and traced like records. - t.kind === "func" + t.kind === "func" || + t.kind === "date" ); } @@ -797,6 +800,9 @@ export function isSupportedMapValue(t: IrType): boolean { // the pre-settle closures. case "secureCtx": return true; + // A Date object: immutable, holds no references. + case "date": + return true; // A promise (Map> — the SNI callback's // in-flight dedupe map): refcounted, with trace/cycle support already // in place (scr_promise_trace_v — the race machinery's refcounting). @@ -849,8 +855,9 @@ export function funcOf(params: IrType[], ret: IrType): IrType { * promises nullable-only; declared maybe-promise unions are admitted by * maybePromiseUnionArmsOk. */ export function unionContainerArmsOk(arms: IrType[]): boolean { - return arms.every( - (a, i) => a.kind !== "promise" || arms.every((b, j) => j === i || isUnitType(b)), + return ( + arms.every((a, i) => a.kind !== "promise" || arms.every((b, j) => j === i || isUnitType(b))) && + dateUnionArmsOk(arms) ); } @@ -861,7 +868,13 @@ export function unionContainerArmsOk(arms: IrType[]): boolean { * non-thenable path). Data arms must not be thenable; the type mapper * checks that against the checker types before minting such a union. */ export function maybePromiseUnionArmsOk(arms: IrType[]): boolean { - return arms.filter((a) => a.kind === "promise").length <= 1; + return arms.filter((a) => a.kind === "promise").length <= 1 && dateUnionArmsOk(arms); +} + +/** A Date arm has a union home beside undefined and null arms only: no + * narrowing contract tells it from other data arms yet. */ +function dateUnionArmsOk(arms: IrType[]): boolean { + return arms.every((a, i) => a.kind !== "date" || arms.every((b, j) => j === i || isUnitType(b))); } /** Canonical, injective text form of an IrType — the building block of @@ -1056,6 +1069,10 @@ export interface IrModule { * each one's hierarchy carries a slot word (the slots.* intrinsics; * backend/llvm/thread-slots.ts). */ threadSlotKeys?: string[]; + /** typeKeys of the array types whose values the program may freeze + * (frontend frozen-records.ts): their writes test the array's frozen bit + * (SCR_ARR_FROZEN) and throw Node's TypeError when it is set. */ + freezableArrays?: string[]; /** The entry module is CommonJS under Node's classification. Node runs a * CommonJS main synchronously and drains process.nextTick callbacks * before promise jobs at the first checkpoint; an ES-module main's @@ -1468,11 +1485,18 @@ export interface IrClassDef { jsName?: string; /** JavaScript constructor arity, ending before the first default or rest parameter. */ jsLength?: number; + /** The class declares public static fields. util.inspect prints them + * after the class tag; a class object reached through `unknown` does + * not carry them unless the runtime holds its static table. */ + ownStaticFields?: true; /** Zero-argument native helper returning the shared prototype data object. * Used when materializing an instance's own-property view. */ prototypeDataHelper?: string; /** Native instance to its actual prototype, including factory captures and subclasses. */ instancePrototypeHelper?: string; + /** Native instance to the string its inherited Symbol.toStringTag getter + * answers, read by the runtime for an instance seen as `unknown`. */ + stringTagHelper?: string; /** Public JavaScript fields record their creation in the shared property * bag. Its descriptors preserve presence and insertion order independently * of native layout order; field values remain in their native slots. */ @@ -1482,8 +1506,14 @@ export interface IrClassDef { * class of the chain sets `name` during construction, and then an own * `name` follows every other own property. */ errorNameAfter?: string | null; - /** Stable module symbol identities for public instance layout fields. */ - symbolFields?: { field: string; globalId: string }[]; + /** JavaScript Error subclasses with a property bag: a view committed by a + * dynamic write keeps `name` and `message` where the bag recorded them + * (scr_error_dyn_follow_bag). */ + errorBagOrder?: boolean; + /** Stable symbol identities for public instance layout fields: the + * module global holding the key, or the name of the well-known symbol + * (`toStringTag` for `Symbol.toStringTag`). */ + symbolFields?: ({ field: string } & ({ globalId: string } | { wellKnown: string }))[]; /** RUNTIME-PROVIDED class (the builtin Error hierarchy): the struct, RC * helpers, and vtable live in the runtime (ScrError / scr_error_*), so * backends emit no definitions for it — only the preorder-interval @@ -1656,6 +1686,8 @@ export const ZLIB_STREAM_MODES: ReadonlyMap = new Map([ ["DeflateRaw", 5], ["InflateRaw", 6], ["Unzip", 7], + ["BrotliDecompress", 8], + ["BrotliCompress", 9], ]); export interface IrRecordShape { @@ -1685,6 +1717,10 @@ export interface IrRecordShape { * dynCheck), and JSON serialization appends overflow entries after the * declared fields in insertion order. */ indexValue?: IrType; + /** Values of this shape may be frozen (frozen-records.ts): their rc word + * can carry the frozen bit, which their release ignores, and every write + * to them checks it. */ + freezable?: true; /** Field names in FIRST-SEEN declaration order (the checker's property * order of the first ts.Type interned to this shape) — metadata, NOT * part of the interned identity: a later structurally-equal type with a @@ -1745,6 +1781,18 @@ export function recordTextCodecClass(shape: IrRecordShape): "TextEncoder" | "Tex return null; } +/** The codec class of a TextEncoder or TextDecoder record type, else null. + * Such a record crosses an `unknown` slot as a native handle that holds the + * record itself (scr_text_codec_dyn.c), never as a copy or a field view. */ +export function textCodecRecordClass( + t: IrType, + getRecord: (shapeId: string) => IrRecordShape | undefined, +): "TextEncoder" | "TextDecoder" | null { + if (t.kind !== "record") return null; + const shape = getRecord(t.shapeId); + return shape ? recordTextCodecClass(shape) : null; +} + /** Object-literal ACCESSOR properties (`{ get x() {...}, set x(v) {...} }`) * live on the shape as reserved '%'-fields holding closures: `%get:x` a * `() => T` invoked once per property READ (side effects and all — JS's @@ -2474,6 +2522,14 @@ export type IrBytesIntrinsicMethod = | "indexOfNum" | "lastIndexOfNum" | "includesNum" + /** TypedArray.prototype.indexOf / lastIndexOf / includes on any element + * kind: [f64 needle, f64 fromIndex]. indexOf and lastIndexOf compare + * with strict equality (NaN is never found, -0 matches 0), includes with + * SameValueZero (NaN is found). An omitted fromIndex is 0, or +Infinity + * for lastIndexOf (which clamps to the last index). */ + | "indexOfElem" + | "lastIndexOfElem" + | "includesElem" | "fill" | "fillNum" | "fillStr" @@ -4019,6 +4075,9 @@ function isJsonSafeAt( case "string": case "bool": return true; + // JSON.stringify writes a Date's toJSON(); JSON text never parses to one. + case "date": + return stringify; case "array": return isJsonSafeAt( t.elem, @@ -4117,7 +4176,6 @@ function isJsonSafeAt( // Maps are not JSON (JSON.stringify(new Map()) is "{}" in Node — an // empty-object husk nobody wants; stringify/dynCheck reject instead). case "map": - case "date": // URLSearchParams stringifies as the same "{}" husk — rejected; use // sp.toString() instead. case "searchParams": @@ -4273,6 +4331,7 @@ export function canConvertToDyn( visiting: Set = new Set(), ): boolean { if (isJsonSafeType(t, getRecord, getUnion)) return true; + if (textCodecRecordClass(t, getRecord) !== null) return true; // numeric typed arrays and boxable functions are dyn kinds the walker boxes // ANYWHERE (bytes and functions held by identity), including nested // in records/arrays/unions. isJsonSafeType rejects them, but dynFrom @@ -4319,6 +4378,7 @@ export function canConvertToDyn( // either its result or a promise of it. canBoxDynComposite(a, getRecord, getUnion, visiting) || isDynTypedRefType(a) || + textCodecRecordClass(a, getRecord) !== null || a.kind === "classval" || DYN_HANDLE_KINDS.has(a.kind) || (a.kind === "func" && canBoxFuncIntoDyn(a, getRecord, getUnion, visiting)) || @@ -4398,7 +4458,8 @@ function canBoxDynCompositeAt( case "record": { const shape = getRecord(t.shapeId); if (!shape) return false; - if (recordTextCodecClass(shape) !== null) return false; + // A codec boxes as its own handle wherever it sits (toDynHelper). + if (recordTextCodecClass(shape) !== null) return true; // Recursive shapes answer coinductively, like isJsonSafeType. if (visiting.has(t.shapeId)) return true; visiting.add(t.shapeId); @@ -4499,6 +4560,8 @@ function canDynCheckToAt( visiting.delete(key); return result; } + // A codec handle unboxes to the record it holds. + if (textCodecRecordClass(t, getRecord) !== null) return true; if (t.kind === "record") { const key = typeKey(t); if (visiting.has(key)) return false; diff --git a/packages/compiler/src/ir/runtime-features.ts b/packages/compiler/src/ir/runtime-features.ts index 4ad3dcc5c..118f0ef38 100644 --- a/packages/compiler/src/ir/runtime-features.ts +++ b/packages/compiler/src/ir/runtime-features.ts @@ -2,6 +2,7 @@ import type { IrClassDef, IrExpr, IrModule, IrRecordShape, IrType, IrUnionDef } import type { IrLibFn } from "./builtin-signatures.js"; import { isDirentShape, + recordTextCodecClass, RUNTIME_EMITTER_CLASS, RUNTIME_EVENT_CLASS, RUNTIME_EVENT_TARGET_CLASS, @@ -99,6 +100,8 @@ const DYN_ASYNC_LIB_FNS: ReadonlySet = new Set([ "process.setDeprecationFlag", "process.setEmitWarning", "punycode.warn", + // DEP0005 for the deprecated Buffer constructor. + "buffer.deprecation", "timers.immediatePromise", // The performance timeline (scr_perf.c, linked with this unit for its // MaxPerformanceEntryBufferExceededWarning). @@ -116,12 +119,16 @@ const DYN_ASYNC_LIB_FNS: ReadonlySet = new Set([ "perf.measure", // Blob and File (scr_blob.c, linked with this unit, which boxes their // reads as dyn promises). + "als.bindCallbackDyn", "als.disable", "als.enterWith", "als.exitRun", "als.get", + "als.name", "als.new", + "als.newOptions", "als.run", + "als.snapshot", "blob.arrayBuffer", "blob.bytes", "blob.fileNew", @@ -361,6 +368,10 @@ const SYMBOL_LIB_FNS: ReadonlySet = new Set([ "dyn.usingDispose", "dyn.usingMethod", "util.isDeepStrictEqual", + // The Buffer function value's Symbol(kIsEncodingSymbol) member + "buffer.constructor", + "buffer.staticValue", + "buffer.prototypeMember", ]); /** Entry points that link the querystring unit. */ @@ -415,7 +426,10 @@ interface TypeTables { unions: Map; classes: Map; /** mentionsDgramSocket's carriers, computed on first use. */ - dgramCarriers?: DgramCarriers; + dgramCarriers?: TypeCarriers; + /** mentionsTextCodec's and mentionsTextDecoder's carriers. */ + textCodecCarriers?: TypeCarriers; + textDecoderCarriers?: TypeCarriers; } /** Whether boxing a value of type `t` as dyn boxes a promise, which goes @@ -479,30 +493,42 @@ function boxesPromise(t: IrType, tables: TypeTables, seen: Set): boolean } } -/** The record shapes, unions and classes whose values carry a dgram socket - * (directly or through other members), computed once per module. */ -interface DgramCarriers { +/** The record shapes, unions and classes whose values carry a leaf type + * (directly or through other members), computed once per module and leaf. */ +interface TypeCarriers { records: Set; unions: Set; anyClass: boolean; } -/** Whether `t` carries a dgram socket, given the carrier sets so far: - * structural through functions (parameters and result), promises and - * collections; named records, unions and class instances by the sets. */ -function carriesDgram(t: IrType, carriers: DgramCarriers): boolean { +/** The leaf a carrier set tracks: a dgram socket, a codec record. */ +type CarrierLeaf = (t: IrType, tables: TypeTables) => boolean; + +/** Whether `t` carries a leaf, given the carrier sets so far: structural + * through functions (parameters and result), promises and collections; + * named records, unions and class instances by the sets. */ +function carriesLeaf( + t: IrType, + carriers: TypeCarriers, + leaf: CarrierLeaf, + tables: TypeTables, +): boolean { + if (leaf(t, tables)) return true; switch (t.kind) { - case "dgramSocket": - return true; case "func": - return t.params.some((p) => carriesDgram(p, carriers)) || carriesDgram(t.ret, carriers); + return ( + t.params.some((p) => carriesLeaf(p, carriers, leaf, tables)) || + carriesLeaf(t.ret, carriers, leaf, tables) + ); case "promise": - return carriesDgram(t.inner, carriers); + return carriesLeaf(t.inner, carriers, leaf, tables); case "array": case "set": - return carriesDgram(t.elem, carriers); + return carriesLeaf(t.elem, carriers, leaf, tables); case "map": - return carriesDgram(t.key, carriers) || carriesDgram(t.value, carriers); + return ( + carriesLeaf(t.key, carriers, leaf, tables) || carriesLeaf(t.value, carriers, leaf, tables) + ); case "record": return carriers.records.has(t.shapeId); case "union": @@ -515,25 +541,29 @@ function carriesDgram(t: IrType, carriers: DgramCarriers): boolean { } } -/** The module's dgram carriers: a fixpoint over its records, unions and +/** The module's carriers of `leaf`: a fixpoint over its records, unions and * class fields. */ -function dgramCarriers(tables: TypeTables): DgramCarriers { - const carriers: DgramCarriers = { records: new Set(), unions: new Set(), anyClass: false }; +function leafCarriers(tables: TypeTables, leaf: CarrierLeaf): TypeCarriers { + const carriers: TypeCarriers = { records: new Set(), unions: new Set(), anyClass: false }; + const carries = (t: IrType): boolean => carriesLeaf(t, carriers, leaf, tables); for (let changed = true; changed;) { changed = false; for (const [id, shape] of tables.records) - if (!carriers.records.has(id) && shape.fields.some((f) => carriesDgram(f.type, carriers))) { + if ( + !carriers.records.has(id) && + (leaf({ kind: "record", shapeId: id }, tables) || shape.fields.some((f) => carries(f.type))) + ) { carriers.records.add(id); changed = true; } for (const [id, def] of tables.unions) - if (!carriers.unions.has(id) && def.arms.some((a) => carriesDgram(a, carriers))) { + if (!carriers.unions.has(id) && def.arms.some(carries)) { carriers.unions.add(id); changed = true; } if ( !carriers.anyClass && - [...tables.classes.values()].some((c) => c.fields.some((f) => carriesDgram(f.type, carriers))) + [...tables.classes.values()].some((c) => c.fields.some((f) => carries(f.type))) ) { carriers.anyClass = true; changed = true; @@ -542,13 +572,42 @@ function dgramCarriers(tables: TypeTables): DgramCarriers { return carriers; } +const dgramLeaf: CarrierLeaf = (t) => t.kind === "dgramSocket"; + /** Whether converting a value of type `t` to or from dyn converts a dgram * socket, which goes through scr_dgram_dyn.c: the socket itself, or a * function, collection, record, union, promise or class instance that * carries one (boxed functions convert their parameters too). */ function mentionsDgramSocket(t: IrType, tables: TypeTables): boolean { - tables.dgramCarriers ??= dgramCarriers(tables); - return carriesDgram(t, tables.dgramCarriers); + tables.dgramCarriers ??= leafCarriers(tables, dgramLeaf); + return carriesLeaf(t, tables.dgramCarriers, dgramLeaf, tables); +} + +const textCodecLeaf: CarrierLeaf = (t, tables) => { + if (t.kind !== "record") return false; + const shape = tables.records.get(t.shapeId); + return shape !== undefined && recordTextCodecClass(shape) !== null; +}; + +const textDecoderLeaf: CarrierLeaf = (t, tables) => { + if (t.kind !== "record") return false; + const shape = tables.records.get(t.shapeId); + return shape !== undefined && recordTextCodecClass(shape) === "TextDecoder"; +}; + +/** Whether converting a value of type `t` to or from dyn converts a + * TextEncoder or TextDecoder record, which boxes it as a handle of + * scr_text_codec_dyn.c (the same carrier walk as dgram sockets). */ +function mentionsTextCodec(t: IrType, tables: TypeTables): boolean { + tables.textCodecCarriers ??= leafCarriers(tables, textCodecLeaf); + return carriesLeaf(t, tables.textCodecCarriers, textCodecLeaf, tables); +} + +/** The TextDecoder half: its boxed decode runs scr_text_decode_stream, + * which reaches the legacy decoders. */ +function mentionsTextDecoder(t: IrType, tables: TypeTables): boolean { + tables.textDecoderCarriers ??= leafCarriers(tables, textDecoderLeaf); + return carriesLeaf(t, tables.textDecoderCarriers, textDecoderLeaf, tables); } /** The libCall fns served by the OPTIONAL events unit (scr_events.c): @@ -679,6 +738,7 @@ export const PKI_LIB_FNS: ReadonlySet = new Set([ "crypto.generateKeyPairSync", "crypto.signOneshot", "crypto.verifyOneshot", + "crypto.diffieHellman", ]); /** The WebCrypto algorithms scr_webcrypto_pki.c implements over the pki @@ -794,6 +854,15 @@ const RUNTIME_FEATURE_RULES = { (node.kind === "dynFrom" && mentionsDgramSocket(node.value.type, tables())) || (node.kind === "dynCheck" && mentionsDgramSocket(node.type, tables())), }), + /** A TextEncoder or TextDecoder record converts to or from an untyped + * value (its handle box, or a boxed function, record or collection that + * carries one): scr_text_codec_dyn.c. */ + textCodecDyn: rule({ + fns: ["text.codecIs"], + expr: (node, tables) => + (node.kind === "dynFrom" && mentionsTextCodec(node.value.type, tables())) || + (node.kind === "dynCheck" && mentionsTextCodec(node.type, tables())), + }), /** node:dns/promises: scr_dns_promises.c over the dgram unit. */ dnsPromises: rule({ fns: ["dns.promisesLookup", "dns.promisesLookupService"] }), dynAsync: rule({ @@ -844,6 +913,7 @@ const RUNTIME_FEATURE_RULES = { ...UTIL_CALLABLE_LIB_FNS, ...BUILTIN_CLASS_LIB_FNS, "util.promisifyDyn", + "buffer.prototypeMember", ], // Constant-key reads of Object.prototype member names answer the // inherited member through scr_dyn_invoke.c. @@ -872,10 +942,22 @@ const RUNTIME_FEATURE_RULES = { /** Buffer.prototype methods called on checked-dynamic values * (scr_bytes_dyn.c). */ dynBuffer: rule({ + // Buffer.prototype members read as values dispatch to the same entries. + fns: ["buffer.prototypeMember"], expr: (node, _tables) => node.kind === "dynInvoke" && DYN_BUFFER_METHODS.has(node.method), }), /** Builtin functions read as values (scr_builtin_values.c). */ - builtinValues: rule({ fns: ["buffer.byteLenValue", "buffer.allocValue", "fs.realpathValue"] }), + builtinValues: rule({ + fns: [ + "buffer.byteLenValue", + "buffer.allocValue", + "fs.realpathValue", + "buffer.constructor", + "buffer.installValue", + "buffer.staticValue", + "buffer.prototypeMember", + ], + }), /** The stream unit emits through the emitter unit, also when the * program constructs no stream (a pipeline of functions). */ /** scr_ecdh.c over the mbedTLS archive; DEP0031 reports through the @@ -942,7 +1024,12 @@ const RUNTIME_FEATURE_RULES = { prefixes: ["insp.", "console.", "test."], libCall: (node) => node.fn === "rl.terminal" && !isDynUndefined(node.args[3]!), }), - legacyTextDecoder: rule({ fns: [...LEGACY_TEXT_DECODER_LIB_FNS] }), + legacyTextDecoder: rule({ + fns: [...LEGACY_TEXT_DECODER_LIB_FNS], + expr: (node, tables) => + (node.kind === "dynFrom" && mentionsTextDecoder(node.value.type, tables())) || + (node.kind === "dynCheck" && mentionsTextDecoder(node.type, tables())), + }), /** The program calls mock.timers members: its timer calls go through * scr_test.c's mock-aware entry points. */ mockTimers: rule({ prefixes: ["test.mockTimers", "test.ctxMockTimers"] }), @@ -1034,6 +1121,9 @@ const RUNTIME_FEATURE_RULES = { * scr_webcrypto.c. */ webcrypto: rule({ prefixes: ["webcrypto."] }), zlib: rule({ prefixes: ["zlib."] }), + /** node:zlib's Brotli forms: zlib.brotliInstall, which %main calls when + * the program uses Brotli (scr_brotli.c). */ + brotli: rule({ fns: ["zlib.brotliInstall"] }), }; type RuntimeFeatureName = keyof typeof RUNTIME_FEATURE_RULES; @@ -1056,6 +1146,7 @@ function noRuntimeFeatures(): RuntimeFeatures { dc: false, dgram: false, dgramDyn: false, + textCodecDyn: false, dnsPromises: false, dynAsync: false, dynBuiltinClass: false, @@ -1102,6 +1193,7 @@ function noRuntimeFeatures(): RuntimeFeatures { wasm: false, webcrypto: false, zlib: false, + brotli: false, }; } diff --git a/packages/compiler/src/ir/traverse.ts b/packages/compiler/src/ir/traverse.ts index 251904351..80425a056 100644 --- a/packages/compiler/src/ir/traverse.ts +++ b/packages/compiler/src/ir/traverse.ts @@ -473,9 +473,10 @@ export function everyStmtChild( throw new Error("unhandled IR node"); } -/** Called with each record literal mapExprChildren rebuilds and its - * replacement, so a side table keyed by node identity (the lowering's - * key-order witness notes) follows the literal into the rewritten tree. */ +/** Called with each record literal and checked conversion mapExprChildren + * rebuilds and its replacement, so a side table keyed by node identity (the + * lowering's key-order witness notes) follows the node into the rewritten + * tree. */ let recordLiteralRebuilt: ((from: IrExpr, to: IrExpr) => void) | null = null; /** Installs (or with null removes) the record-literal rebuild observer. */ @@ -504,8 +505,11 @@ export function mapExprChildren( return node; case "libCall": return { ...node, args: mapOf(node.args, expr) }; - case "dynCheck": - return { ...node, value: expr(node.value) }; + case "dynCheck": { + const rebuilt: IrExpr = { ...node, value: expr(node.value) }; + if (recordLiteralRebuilt !== null) recordLiteralRebuilt(node, rebuilt); + return rebuilt; + } case "strEq": return { ...node, left: expr(node.left), right: expr(node.right) }; case "fieldGet": diff --git a/packages/compiler/src/ir/validate.ts b/packages/compiler/src/ir/validate.ts index a404c918a..5bae241b1 100644 --- a/packages/compiler/src/ir/validate.ts +++ b/packages/compiler/src/ir/validate.ts @@ -55,6 +55,7 @@ import { typeEquals, typeKey, maybePromiseUnionArmsOk, + unionContainerArmsOk, VOID, } from "./ir.js"; import { DYN_CLASS_PROPERTIES, callSiteReturnType, classValueConstructor } from "./ir.js"; @@ -501,10 +502,29 @@ export function validateModule(mod: IrModule): IrValidationError[] { }); } } - for (const symbol of cls.symbolFields ?? []) { - const global = mod.globals?.find((global) => global.id === symbol.globalId); + if (cls.stringTagHelper !== undefined) { + const helper = functionsByName.get(cls.stringTagHelper); + const receiver = helper?.params[0]?.type; if ( - global?.type.kind !== "symbol" || + !helper || + helper.params.length !== 1 || + receiver?.kind !== "object" || + receiver.className !== cls.name || + helper.returnType.kind !== "string" || + helper.captures !== undefined || + helper.classCaptures !== undefined + ) { + errors.push({ + message: `class ${cls.name}: string tag helper must be a noncapturing (instance) => string function`, + loc: cls.loc, + }); + } + } + for (const symbol of cls.symbolFields ?? []) { + const global = + "globalId" in symbol ? mod.globals?.find((global) => global.id === symbol.globalId) : null; + if ( + ("globalId" in symbol ? global?.type.kind !== "symbol" : symbol.wellKnown === "") || !cls.fields.some((field) => field.name === symbol.field) ) { errors.push({ message: `class ${cls.name}: invalid symbol field metadata`, loc: cls.loc }); @@ -803,7 +823,6 @@ export function validateModule(mod: IrModule): IrValidationError[] { arm.kind === "void" || arm.kind === "union" || arm.kind === "dyn" || - arm.kind === "date" || (arm.kind === "generator" && iterableArmsElem(u.arms) === null) ) { errors.push({ message: `union ${u.id}: arm ${i} is ${arm.kind}`, loc: noLoc }); @@ -814,6 +833,9 @@ export function validateModule(mod: IrModule): IrValidationError[] { loc: noLoc, }); } + if (arm.kind === "date" && !unionContainerArmsOk(u.arms)) { + errors.push({ message: `union ${u.id}: date arm ${i} beside non-unit arms`, loc: noLoc }); + } if (arm.kind === "record" && !recordsById.has(arm.shapeId)) { errors.push({ message: `union ${u.id}: arm ${i} references undeclared shape "${arm.shapeId}"`, @@ -1507,6 +1529,7 @@ function validateFunction( e.left.type.kind === "map" || e.left.type.kind === "set" || e.left.type.kind === "regex" || + e.left.type.kind === "date" || e.left.type.kind === "url" || e.left.type.kind === "object" || e.left.type.kind === "record" || @@ -2192,6 +2215,9 @@ function validateFunction( indexOfNum: { argTypes: [F64, F64], minArgs: 1, result: F64 }, lastIndexOfNum: { argTypes: [F64, F64], minArgs: 1, result: F64 }, includesNum: { argTypes: [F64, F64], minArgs: 1, result: BOOL }, + indexOfElem: { argTypes: [F64, F64], minArgs: 2, result: F64 }, + lastIndexOfElem: { argTypes: [F64, F64], minArgs: 2, result: F64 }, + includesElem: { argTypes: [F64, F64], minArgs: 2, result: BOOL }, fill: { argTypes: [BYTES_U8, F64, F64], minArgs: 1, result: BYTES_U8 }, fillNum: { argTypes: [F64, F64, F64], minArgs: 1, result: BYTES_U8 }, // Per-element TypedArray fill (any elem — the non-u8 fill path). @@ -2372,9 +2398,13 @@ function validateFunction( (e.method === "sortPrimitive" || e.method === "toSortedPrimitive") && elem.kind !== "string" && elem.kind !== "f64" && - elem.kind !== "bool" + elem.kind !== "bool" && + elem.kind !== "date" ) { - err(`arrIntrinsic ${e.method} requires primitive elements, got ${elem.kind}`, e.loc); + err( + `arrIntrinsic ${e.method} requires primitive or Date elements, got ${elem.kind}`, + e.loc, + ); } if (e.method === "sortValues" || e.method === "sortDense") { const arms = elem.kind === "union" ? unions.get(elem.unionId)?.arms : undefined; @@ -2456,13 +2486,18 @@ function validateFunction( elem.kind !== "f64" && elem.kind !== "string" && elem.kind !== "bool" && + elem.kind !== "date" && !( elem.kind === "union" && (unions .get(elem.unionId) ?.arms.every( (a) => - a.kind === "f64" || a.kind === "string" || a.kind === "bool" || isUnitType(a), + a.kind === "f64" || + a.kind === "string" || + a.kind === "bool" || + a.kind === "date" || + isUnitType(a), ) ?? false) ) @@ -5339,6 +5374,39 @@ function validateFunction( } function checkLibSpecialCall(e: IrExpr & { kind: "libCall" }): boolean { + if (e.fn === "promise.fulfilledValue") { + const promise = e.args[0]?.type; + if ( + promise?.kind !== "promise" || + e.type.kind === "void" || + isUnitType(e.type) || + promise.inner.kind !== "void" + ) + err(`libCall ${e.fn} must read a Promise entry at a non-void type`, e.loc); + return true; + } + if (e.fn === "promise.allTuple") { + const [entries, build] = e.args; + if ( + entries?.type.kind !== "array" || + entries.type.elem.kind !== "promise" || + entries.type.elem.inner.kind !== "void" || + build?.kind !== "closure" || + build.captures.length > 0 || + build.type.kind !== "func" || + e.type.kind !== "promise" || + !typeEquals(build.type.ret, e.type.inner) + ) + err(`libCall ${e.fn} must join Promise entries into its builder's tuple`, e.loc); + return true; + } + if (e.fn === "als.bindCallback") { + // The bound function, at its closure argument's own type. + const fn = e.args[0]?.type; + if (fn?.kind !== "func" || !typeEquals(e.type, fn)) + err(`libCall ${e.fn} must return its function argument's type`, e.loc); + return true; + } if ( e.fn === "crypto.randomFillView" || e.fn === "crypto.randomFillViewRest" || diff --git a/packages/compiler/src/library/ir-policy.ts b/packages/compiler/src/library/ir-policy.ts index e8fad6dd1..f08b85232 100644 --- a/packages/compiler/src/library/ir-policy.ts +++ b/packages/compiler/src/library/ir-policy.ts @@ -31,6 +31,7 @@ const LIB_MODE_REFUSED_PREFIXES: readonly [string, string][] = [ ["zlib.cbOpts", "the async node:zlib callback surface"], ["zlib.promise", "the async node:zlib callback surface (util.promisify)"], ["zlib.stream", "the node:zlib stream classes"], + ["zlib.brotliInstall", "node:zlib's Brotli forms"], ["process.stdoutWriteBytesCb", "process.stdout.write completion callbacks"], ["process.stderrWriteBytesCb", "process.stderr.write completion callbacks"], ["timers.", "the timers surface (setTimeout family)"], @@ -63,6 +64,7 @@ const LIB_MODE_REFUSED_PREFIXES: readonly [string, string][] = [ ["crypto.generateKeyPair", "asymmetric KeyObjects, generateKeyPair, sign and verify"], ["crypto.signOneshot", "asymmetric KeyObjects, generateKeyPair, sign and verify"], ["crypto.verifyOneshot", "asymmetric KeyObjects, generateKeyPair, sign and verify"], + ["crypto.diffieHellman", "crypto.diffieHellman"], ["webcrypto.", "WebCrypto (crypto.subtle)"], ["fs.watch", "fs.watch"], ["watcher.", "fs.watch"], diff --git a/packages/compiler/src/native/cache.ts b/packages/compiler/src/native/cache.ts index 3c9bdf411..937b4b109 100644 --- a/packages/compiler/src/native/cache.ts +++ b/packages/compiler/src/native/cache.ts @@ -1,6 +1,7 @@ import { createHash } from "node:crypto"; import { closeSync, + existsSync, mkdirSync, mkdtempSync, openSync, @@ -88,14 +89,19 @@ export class NativeCache { } } - write(family: string, key: string, bytes: string | Uint8Array): void { + write( + family: string, + key: string, + bytes: string | Uint8Array, + digest = contentDigest(bytes), + ): void { let stage: string | null = null; try { const directory = join(this.root, family); mkdirSync(directory, { recursive: true, mode: 0o700 }); stage = mkdtempSync(join(directory, ".write-")); writeFileSync(join(stage, "payload"), bytes); - writeFileSync(join(stage, "digest"), contentDigest(bytes) + "\n"); + writeFileSync(join(stage, "digest"), digest + "\n"); const path = join(directory, key); // Concurrent writers publish identical content under the same key; // a reader between the two renames simply treats it as a miss. @@ -112,6 +118,29 @@ export class NativeCache { } } + /** Whether a marker or entry exists, without reading or checking it. */ + has(family: string, key: string): boolean { + return existsSync(join(this.root, family, key)); + } + + /** Records an empty marker that has() finds. */ + mark(family: string, key: string): void { + try { + const directory = join(this.root, family); + mkdirSync(directory, { recursive: true, mode: 0o700 }); + writeFileSync(join(directory, key), ""); + } catch { + /* Cache failures never change a build's result. */ + } + } + + /** Writes `bytes` under their content digest, and returns the digest. */ + writeContent(family: string, bytes: Uint8Array): string { + const digest = contentDigest(bytes); + this.write(family, digest, bytes, digest); + return digest; + } + /** Publish text parts without joining them into one string. */ writeParts(family: string, key: string, parts: Generator): void { let stage: string | null = null; @@ -149,6 +178,7 @@ export class NativeCache { "object", "sanitizer", "executable", + "linked", "binary", "dsym", "coverage", diff --git a/packages/compiler/src/native/cli.ts b/packages/compiler/src/native/cli.ts index 21047b929..aa46944f5 100644 --- a/packages/compiler/src/native/cli.ts +++ b/packages/compiler/src/native/cli.ts @@ -16,7 +16,11 @@ import { createNativeTs7Api } from "../frontend/ts7/native-api.js"; import { resolveProvenanceSourcesWithParser } from "../frontend/provenance-core.js"; import { setCompilerReleaseVersion } from "../library/sidecar.js"; import { NativeCompiler } from "./compiler.js"; -import { openNativeCache } from "./cache.js"; +import { NATIVE_STRING_BYTES, openNativeCache } from "./cache.js"; +import { + checkerExecutableIdentity, + setLibDiagnosticsStore, +} from "../frontend/ts7/lib-diagnostics.js"; import { loadNativeToolchain } from "./toolchain.js"; import { buildSanitizedRuntime } from "./sanitizer.js"; import { runCompilerTask } from "./task.js"; @@ -53,6 +57,17 @@ async function main(): Promise { if (toolchain.declarationsRoot !== undefined) setDeclarationRoot(toolchain.declarationsRoot); setCompilerReleaseVersion(toolchain.compilerVersion); cache = openNativeCache((path, harden) => compilerNativePrivateDirectory(path, harden)); + if (cache !== null) { + const store = cache; + setLibDiagnosticsStore( + { + read: (key) => + store.read("lib-diagnostics", key, NATIVE_STRING_BYTES)?.toString("utf8") ?? null, + write: (key, text) => store.write("lib-diagnostics", key, text), + }, + checkerExecutableIdentity(toolchain.ts7Executable), + ); + } compiler = new NativeCompiler(toolchain, cache); return compiler; }; @@ -122,6 +137,7 @@ async function main(): Promise { dgram: false, dnsPromises: false, dgramDyn: false, + textCodecDyn: false, watch: false, foreignFfi: false, nodeTest: false, diff --git a/packages/compiler/src/native/compiler.ts b/packages/compiler/src/native/compiler.ts index 8f88356e9..0d02b135f 100644 --- a/packages/compiler/src/native/compiler.ts +++ b/packages/compiler/src/native/compiler.ts @@ -56,7 +56,7 @@ import { clearFenceEvalCaches } from "../library/fence-eval.js"; import { archiveNativeLibrary, localizeNativeLibrary, linkNativeWasmLibrary } from "./library.js"; import type { NativeToolchain } from "./toolchain.js"; import { evaluateNativeComptime } from "./comptime.js"; -import { contentDigest, fileDigest, type NativeCache } from "./cache.js"; +import { contentDigest, fileDigest, partsDigest, type NativeCache } from "./cache.js"; import { nativeProfileKey, nativeProfileRefusal, @@ -169,6 +169,7 @@ export class NativeCompiler { outputKind: "obj" | "asm", partitions = 1, profile?: NativeProfileOptions, + llvmDigest: () => string = () => fileDigest(input), ): string[] { const toolchain = this.toolchain; const outputs = nativePartitionPaths(output, partitions); @@ -187,7 +188,7 @@ export class NativeCompiler { key = contentDigest( JSON.stringify({ schema: 1, - llvm: fileDigest(input), + llvm: llvmDigest(), outputKind, optimization, source, @@ -254,6 +255,7 @@ export class NativeCompiler { stage: string, library: boolean, profile?: NativeProfileOptions, + llvmDigest?: () => string, ): string[] { // Library archives merge dev shards relocatably; executables link the // helper's program partitions directly. @@ -264,7 +266,16 @@ export class NativeCompiler { const split = llvm === null ? null : splitLlvmLibraryProgram(llvm); if (split === null) { const partitions = library ? 1 : nativeProgramPartitions(statSync(input).size); - return this.emitObject(input, output, source, optimization, "obj", partitions, profile); + return this.emitObject( + input, + output, + source, + optimization, + "obj", + partitions, + profile, + llvmDigest, + ); } const objects: string[] = []; for (const [index, shard] of split.shards.entries()) { @@ -359,6 +370,9 @@ export class NativeCompiler { return { ok: true, artifact: { kind: "ir", path: output } }; } const { llvm, features } = prepared; + // The executable and object caches key the same LLVM text. + let llvmDigest: string | undefined; + const digestLlvm = (): string => (llvmDigest ??= partsDigest(llvm)); const inputDirectory = join(stage, "input"); mkdirSync(inputDirectory); const llvmInput = outputKind === "llvm" ? stagedOutput : join(inputDirectory, "program.ll"); @@ -386,7 +400,14 @@ export class NativeCompiler { const executableCache = executablePack === null ? null - : openNativeExecutableCache(this.cache, toolchain, llvm, options, ffi, executablePack); + : openNativeExecutableCache( + this.cache, + toolchain, + digestLlvm, + options, + ffi, + executablePack, + ); const restored = executableCache?.restore(stagedOutput) ?? false; timing(restored ? "executable-cache-hit" : "executable-cache-miss"); if (!restored) { @@ -409,6 +430,7 @@ export class NativeCompiler { stage, false, profile, + digestLlvm, ); else this.emitObject( diff --git a/packages/compiler/src/native/executable-cache-open.test.ts b/packages/compiler/src/native/executable-cache-open.test.ts new file mode 100644 index 000000000..05e57148d --- /dev/null +++ b/packages/compiler/src/native/executable-cache-open.test.ts @@ -0,0 +1,86 @@ +import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, expect, test } from "vitest"; +import type { NativeRuntimeSelection } from "../backend/runtime-pack-native.js"; +import { NativeCache } from "./cache.js"; +import { openNativeExecutableCache } from "./executable-cache.js"; +import type { NativeToolchain } from "./toolchain.js"; + +const directories: string[] = []; +const saved = new Map(); +beforeEach(() => { + for (const name of ["SCRIPTC_LINKER", "SCRIPTC_LLVM_HELPER", "CC", "LDFLAGS", "LIBRARY_PATH"]) { + saved.set(name, process.env[name]); + delete process.env[name]; + } +}); +afterEach(() => { + for (const [name, value] of saved) { + if (value === undefined) delete process.env[name]; + else process.env[name] = value; + } + saved.clear(); + for (const path of directories.splice(0)) rmSync(path, { recursive: true, force: true }); +}); + +/** A toolchain whose linker is a driver script named `driver`; it answers + * `-print-prog-name=clang` with its own path. */ +function fixture(platform: string, driver: string) { + const root = mkdtempSync(join(tmpdir(), "scriptc-executable-cache-open-")); + directories.push(root); + const bin = join(root, "bin"); + const helper = join(root, "helper"); + const runtime = join(root, "runtime"); + for (const directory of [bin, helper, runtime]) mkdirSync(directory); + const linker = join(bin, driver); + writeFileSync(linker, '#!/bin/sh\necho "$0"\n'); + chmodSync(linker, 0o755); + writeFileSync(join(helper, "codegen"), "helper"); + writeFileSync(join(helper, "package.json"), "{}"); + writeFileSync(join(runtime, "package.json"), "{}"); + writeFileSync(join(runtime, "runtime-pack.json"), "{}"); + const toolchain = { + compilerVersion: "test", + target: { platform }, + helperExecutable: join(helper, "codegen"), + helperPackageRoot: helper, + runtimePackRoot: runtime, + linker, + linkerArgs: [], + dsymutil: "dsymutil", + } as unknown as NativeToolchain; + const pack = { + root: runtime, + manifest: { licenses: [] }, + packageText: "{}", + manifestText: "{}", + selected: { runtime: [], archives: [] }, + flavor: "release", + } as unknown as NativeRuntimeSelection; + const open = () => + openNativeExecutableCache( + new NativeCache(join(root, "cache")), + toolchain, + () => "llvm digest", + { outPath: join(root, "program"), outDir: root, optimization: "release" }, + null, + pack, + ); + return { open }; +} + +test.skipIf(process.platform === "win32")( + "Linux builds that link with a clang driver use the executable cache", + () => { + expect(fixture("linux", "clang").open()).not.toBeNull(); + expect(fixture("linux", "clang-22").open()).not.toBeNull(); + expect(fixture("linux", "gcc").open()).toBeNull(); + expect(fixture("windows", "clang").open()).toBeNull(); + expect(fixture("wasi", "clang").open()).toBeNull(); + // On darwin only Apple's driver at /usr/bin/clang qualifies. + expect(fixture("darwin", "clang").open()).toBeNull(); + process.env["SCRIPTC_LLVM_HELPER"] = "/elsewhere/codegen"; + expect(fixture("linux", "clang").open()).toBeNull(); + }, +); diff --git a/packages/compiler/src/native/executable-cache.test.ts b/packages/compiler/src/native/executable-cache.test.ts index 3199ef97f..83a493721 100644 --- a/packages/compiler/src/native/executable-cache.test.ts +++ b/packages/compiler/src/native/executable-cache.test.ts @@ -40,6 +40,11 @@ function fixture(debug = false) { const library = join(sdk, "libSystem.tbd"); writeFileSync(runtime, "runtime"); writeFileSync(library, "system library"); + // A directory's identity is its stat. Where the file system stamps times + // at the kernel tick, a file added within the tick of the last write + // leaves the directory's times unchanged; an old time makes every later + // change to the SDK directory visible. + utimesSync(sdk, 0, 0); const cache = new NativeCache(join(root, "cache")); const key = contentDigest("llvm and options"); const open = () => new NativeExecutableCache(cache, key, "clang", debug, [runtime]); @@ -217,3 +222,61 @@ test("a failed or empty link trace cannot publish a completed executable", () => }); expect(entry.trace([], f.stage)).toBe(false); }); + +test.each(["ld", "crt", "libc"] as const)( + "a Linux key's second link records the linker, startup objects and libc; replacing the %s misses", + (input) => { + const f = fixture(); + const lib = join(f.root, "lib"); + mkdirSync(lib); + const paths = { + ld: join(f.root, "ld"), + crt: join(lib, "Scrt1.o"), + libc: join(lib, "libc.so.6"), + }; + writeFileSync(paths.ld, "linker"); + writeFileSync(paths.crt, "startup object"); + writeFileSync(paths.libc, "c library"); + const nonshared = join(lib, "libc_nonshared.a"); + writeFileSync(nonshared, "archive"); + const program = join(f.stage, "program.o"); + writeFileSync(program, "program"); + // With -v, clang prints the linker job it runs, as -### would; GNU ld's + // -t names each file it loads, and an archive member as archive(member). + const job = ` "${paths.ld}" "-pie" "-o" "${f.output}" "${paths.crt}" "-L${lib}" "${program}" "-lc"`; + const loaded = [paths.crt, program, paths.libc, `${nonshared}(atexit.oS)`].join("\n"); + vi.mocked(spawnSync).mockImplementation((_command, args) => { + const verbose = (args as string[]).includes("-v"); + return { + pid: 1, + status: 0, + signal: null, + output: [], + stdout: verbose ? loaded + "\n" : "", + stderr: verbose ? job + "\n" : "", + }; + }); + const open = () => new NativeExecutableCache(f.cache, f.key, "clang", false, [f.runtime], true); + // A key's first link is not traced; the caller links the output itself. + expect(open().trace(["-o", f.output], f.stage)).toBe(false); + expect(spawnSync).not.toHaveBeenCalled(); + expect(open().restore(f.destination)).toBe(false); + // Its next link traces both the driver and the linker in one run. + const entry = open(); + expect(entry.trace(["-o", f.output], f.stage)).toBe(true); + expect(spawnSync).toHaveBeenCalledTimes(1); + expect(vi.mocked(spawnSync).mock.calls[0]![1]).toEqual(["-o", f.output, "-v", "-Wl,-t"]); + entry.publish(f.output); + const recorded = ( + JSON.parse(f.cache.read("executable", f.key)!.toString()) as { inputs: { path: string }[] } + ).inputs.map((saved) => saved.path); + expect(recorded).toEqual(expect.arrayContaining([...Object.values(paths), nonshared, lib])); + expect(recorded).not.toContain(program); + expect(open().restore(f.destination)).toBe(true); + const before = statSync(paths[input]); + writeFileSync(paths[input] + ".new", "x".repeat(before.size)); + utimesSync(paths[input] + ".new", before.atime, before.mtime); + renameSync(paths[input] + ".new", paths[input]); + expect(open().restore(f.destination)).toBe(false); + }, +); diff --git a/packages/compiler/src/native/executable-cache.ts b/packages/compiler/src/native/executable-cache.ts index c0ac755b9..1dfbf0b00 100644 --- a/packages/compiler/src/native/executable-cache.ts +++ b/packages/compiler/src/native/executable-cache.ts @@ -11,7 +11,7 @@ import { import { runNativeTool } from "../backend/native-tools.js"; import type { NativeRuntimeSelection } from "../backend/runtime-pack-native.js"; import type { NativeToolchain } from "./toolchain.js"; -import { contentDigest, NativeCache, partsDigest } from "./cache.js"; +import { contentDigest, NativeCache } from "./cache.js"; interface InputIdentity { path: string; @@ -48,9 +48,22 @@ function identity(path: string): InputIdentity { }; } +function sameIdentity(a: InputIdentity, b: InputIdentity): boolean { + return ( + a.path === b.path && + a.canonical === b.canonical && + a.kind === b.kind && + a.dev === b.dev && + a.ino === b.ino && + a.size === b.size && + a.mtime === b.mtime && + a.ctime === b.ctime + ); +} + function stillMatches(inputs: InputIdentity[]): boolean { try { - return inputs.every((input) => JSON.stringify(identity(input.path)) === JSON.stringify(input)); + return inputs.every((input) => sameIdentity(identity(input.path), input)); } catch { return false; } @@ -165,6 +178,8 @@ function stageSymbols(bytes: Buffer, binary: string): void { export class NativeExecutableCache { private inputs: InputIdentity[]; private traced = false; + /** Linux: no entry exists and the key was never linked before. */ + private readonly firstLink: boolean; constructor( private readonly cache: NativeCache, @@ -172,23 +187,28 @@ export class NativeExecutableCache { private readonly linker: string, private readonly debug: boolean, paths: string[], + /** Linux: trace with one verbose link, from a key's second link on. */ + private readonly linux = false, ) { - this.inputs = [...new Set(paths)].sort().map(identity); + this.firstLink = linux && !cache.has("executable", key) && !cache.has("linked", key); + // A first link neither restores nor traces, so it needs no identities. + this.inputs = this.firstLink ? [] : [...new Set(paths)].sort().map(identity); } restore(output: string): boolean { + if (this.firstLink) return false; try { const metadata = this.cache.read("executable", this.key); if (metadata === null) return false; const entry: unknown = JSON.parse(metadata.toString("utf8")); if (!validEntry(entry) || !stillMatches(entry.inputs) || !stillMatches(this.inputs)) return false; + const saved = new Map(entry.inputs.map((input) => [input.path, input])); if ( - !this.inputs.every((input) => - entry.inputs.some( - (saved) => saved.path === input.path && JSON.stringify(saved) === JSON.stringify(input), - ), - ) + !this.inputs.every((input) => { + const recorded = saved.get(input.path); + return recorded !== undefined && sameIdentity(recorded, input); + }) ) return false; if (this.debug !== (entry.symbols !== null)) return false; @@ -209,14 +229,24 @@ export class NativeExecutableCache { * Directories from the driver's search line detect a newly added candidate. * Private build inputs already belong to the LLVM/runtime cache identity. * A true result means the requested output was linked successfully and its - * inputs remained stable. The caller can publish that output directly. */ + * inputs remained stable. The caller can publish that output directly. On + * false, the caller links the output itself. */ trace(args: string[], stage: string): boolean { this.traced = false; try { + // On Linux a key's first link only records that it was linked; a later + // link of the same key traces, so a build that runs once traces nothing. + if (this.firstLink) { + this.cache.mark("linked", this.key); + return false; + } const roots = [resolve(stage), realpathSync(stage)]; - const dry = toolOutput(this.linker, [...args, "-###"]); - const driverInputs = tracePaths(dry, true, roots).map(identity); - const linked = toolOutput(this.linker, [...args, "-Wl,-t"]); + let driverInputs = this.linux + ? [] + : tracePaths(toolOutput(this.linker, [...args, "-###"]), true, roots).map(identity); + // On Linux, -v prints the jobs that -### would, while the link runs. + const linked = toolOutput(this.linker, [...args, ...(this.linux ? ["-v"] : []), "-Wl,-t"]); + if (this.linux) driverInputs = tracePaths(linked, true, roots).map(identity); const paths = tracePaths(linked, false, roots); if (paths.length === 0 || !stillMatches(this.inputs) || !stillMatches(driverInputs)) return false; @@ -233,10 +263,8 @@ export class NativeExecutableCache { if (!this.traced || !stillMatches(this.inputs)) return; const binary = readFileSync(output); const symbols = this.debug ? readSymbols(output) : null; - const binaryKey = contentDigest(binary); - const symbolsKey = symbols === null ? null : contentDigest(symbols); - this.cache.write("binary", binaryKey, binary); - if (symbols !== null && symbolsKey !== null) this.cache.write("dsym", symbolsKey, symbols); + const binaryKey = this.cache.writeContent("binary", binary); + const symbolsKey = symbols === null ? null : this.cache.writeContent("dsym", symbols); if (!stillMatches(this.inputs)) return; const entry: ExecutableEntry = { schema: "scriptc.native-executable.v1", @@ -254,16 +282,18 @@ export class NativeExecutableCache { export function openNativeExecutableCache( cache: NativeCache | null, toolchain: NativeToolchain, - llvm: readonly string[], + llvmDigest: () => string, options: CompileRequestOptions, ffi: FfiProfile | null, pack: NativeRuntimeSelection, ): NativeExecutableCache | null { - // Match the established complete-cache contract: default Apple driver, - // known runtime inputs, and no wrapper or caller-supplied library searches. + // Match the established complete-cache contract: the default Apple driver + // or a Linux clang driver, known runtime inputs, and no wrapper or + // caller-supplied library searches. + const platform = toolchain.target.platform; if ( cache === null || - toolchain.target.platform !== "darwin" || + (platform !== "darwin" && platform !== "linux") || options.sanitize || // The key names a profile by path, not by its bytes. options.profileGenerate !== undefined || @@ -277,9 +307,23 @@ export function openNativeExecutableCache( return null; try { const linker = commandPath(toolchain.linker); - if (realpathSync(linker) !== "/usr/bin/clang") return null; - const effective = commandPath(runNativeTool(linker, ["-print-prog-name=clang"]).trim()); - const debug = options.optimization === "dev" && !options.strip; + // Apple's driver at /usr/bin/clang, or a clang driver on Linux. Both + // name every input of a link in their -### and -Wl,-t traces. + if ( + platform === "darwin" + ? realpathSync(linker) !== "/usr/bin/clang" + : !/^clang(-\d+)?$/.test(basename(realpathSync(linker))) + ) + return null; + // Apple's /usr/bin/clang is a shim for the active developer directory's + // driver; a Linux clang driver is itself the driver. + const effective = + platform === "darwin" + ? commandPath(runNativeTool(linker, ["-print-prog-name=clang"]).trim()) + : linker; + // Linux keeps debug information in the executable; darwin keeps it in a + // separate dSYM, which the cache stores beside the executable. + const debug = platform === "darwin" && options.optimization === "dev" && !options.strip; const paths = [ linker, effective, @@ -291,6 +335,7 @@ export function openNativeExecutableCache( (artifact) => join(pack.root, artifact.path), ), ]; + if (platform === "linux") paths.push(join(pack.root, "runtime-pack.json")); if (debug) { const dsymutil = commandPath(toolchain.dsymutil); if (realpathSync(dsymutil) !== "/usr/bin/dsymutil") return null; @@ -299,11 +344,16 @@ export function openNativeExecutableCache( const key = contentDigest( JSON.stringify({ schema: 1, - llvm: partsDigest(llvm), + llvm: llvmDigest(), options, toolchain, runtimeIdentity: pack.packageText, - runtimeManifest: pack.manifestText, + // On Linux the manifest's identity names it: restore checks that + // identity again, as it does every other input. + runtimeManifest: + platform === "darwin" + ? pack.manifestText + : identity(join(pack.root, "runtime-pack.json")), linker: identity(linker), effective: identity(effective), environment: toolchainEnvironmentFingerprint(), @@ -311,7 +361,7 @@ export function openNativeExecutableCache( path: process.env["PATH"] ?? null, }), ); - return new NativeExecutableCache(cache, key, linker, debug, paths); + return new NativeExecutableCache(cache, key, linker, debug, paths, platform === "linux"); } catch { return null; } diff --git a/packages/compiler/src/native/prepare.test.ts b/packages/compiler/src/native/prepare.test.ts index 112484f4a..7ae2d1d5f 100644 --- a/packages/compiler/src/native/prepare.test.ts +++ b/packages/compiler/src/native/prepare.test.ts @@ -2,7 +2,11 @@ import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSyn import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, expect, test } from "vitest"; -import { nativeCodegenTarget } from "../backend/targets.js"; +import { + LINUX_ARM64_GNU_TARGET, + nativeCodegenTarget, + WASM32_WASI_TARGET, +} from "../backend/targets.js"; import { runFrontend, type FrontendFactory } from "../frontend/pipeline.js"; import { loadProgram } from "../frontend/program-node.js"; import { ts7Executable } from "../frontend/ts7/rpc-api.js"; @@ -176,8 +180,31 @@ test("serialized IR travels as bounded parts through the frontend cache", () => const key = readdirSync(family).find((name) => /^[0-9a-f]{64}$/.test(name))!; const payload = JSON.parse(readFileSync(join(family, key), "utf8")); expect(payload.schema).toBe("scriptc.native-frontend.v3"); - expect(payload.input.llvm).toBe(""); + // The placeholders keep NativeExecutableInput's member types: the native + // compiler checks the parsed entry against them when it reads it back. + expect(payload.input.llvm).toEqual([]); expect(payload.input.ir).toBeNull(); expect(payload.llvm).toBe(first.llvm.join("")); expect(payload.ir).toEqual(first.ir); }); + +test("an x86-64 level is refused for targets that are not x86-64 before the frontend runs", () => { + const f = fixture(); + for (const target of [LINUX_ARM64_GNU_TARGET, WASM32_WASI_TARGET]) { + const result = prepareNativeExecutable( + f.entry, + { ...f.options, targetCpu: "x86-64-v3" }, + null, + { ...fixtureToolchain(), target }, + () => { + throw new Error("the frontend ran"); + }, + null, + ); + expect(result.ok).toBe(false); + if (result.ok) continue; + expect(result.diagnostics.map((d) => [d.code, d.message])).toEqual([ + ["SC3002", "--target-cpu=x86-64-v3 requires an x86-64 target"], + ]); + } +}); diff --git a/packages/compiler/src/native/prepare.ts b/packages/compiler/src/native/prepare.ts index f7f399f80..67a4b5a9e 100644 --- a/packages/compiler/src/native/prepare.ts +++ b/packages/compiler/src/native/prepare.ts @@ -1,4 +1,5 @@ -import type { ScrDiagnostic } from "../diagnostics/diagnostic.js"; +import { targetCpuField, targetCpuTargetRefusal } from "../backend/target-cpu.js"; +import { nativeCodegenDiag, type ScrDiagnostic } from "../diagnostics/diagnostic.js"; import type { CompilationTiming } from "../timing.js"; import { readFileSync } from "node:fs"; import { basename, dirname, join, resolve } from "node:path"; @@ -59,7 +60,7 @@ export interface NativeExecutableInput { /** The two large artifacts follow the input metadata as separate members so * an entry can be written as parts instead of one JSON string; the stored - * input carries empty placeholders (`""`, `null`) for them. */ + * input carries empty placeholders (`[]`, `null`) for them. */ interface FrontendCacheEntry { schema: "scriptc.native-frontend.v3"; probes: FrontendInputSnapshot; @@ -78,7 +79,7 @@ function* frontendCacheParts( const metadata = JSON.stringify({ schema: "scriptc.native-frontend.v3", probes, - input: { ...input, llvm: "", ir: null }, + input: { ...input, llvm: [], ir: null }, }); yield metadata.slice(0, metadata.length - 1) + ',"llvm":"'; for (const part of joinedParts(input.llvm)) @@ -239,6 +240,7 @@ function validFeatures(value: unknown): value is NativeLinkFeatures { "dgram", "dnsPromises", "dgramDyn", + "textCodecDyn", "watch", "foreignFfi", "nodeTest", @@ -315,6 +317,17 @@ export function prepareNativeExecutable( timing: CompilationTiming = () => {}, ): NativeExecutableInput | CompileFailure { const outputKind = options.outputKind ?? "exe"; + const targetCpuRefusal = targetCpuTargetRefusal( + options.targetCpu, + toolchain.target.platform, + toolchain.target.architecture, + ); + if (targetCpuRefusal !== null) + return { + ok: false, + diagnostics: [nativeCodegenDiag("SC3002", targetCpuRefusal, entry)], + sourceTexts: new Map(), + }; const stem = basename(entry).replace(/\.(ts|mts|cts|js|mjs|cjs)$/, ""); const outputPaths = [ resolve(options.outPath), @@ -395,6 +408,7 @@ export function prepareNativeExecutable( forkPrimitives: emitForkPrimitives(), ...inlineRcOption(options.optimization, options.sanitize === true), ...(options.detachedExit === true && outputKind === "exe" ? { detachedExit: true } : {}), + ...targetCpuField(options.targetCpu), ...(options.optimization === "dev" && !options.strip ? { debugSources: prepared.sourceTexts } : {}), diff --git a/packages/compiler/src/native/sanitizer.test.ts b/packages/compiler/src/native/sanitizer.test.ts index 4c031a8e7..1415f2971 100644 --- a/packages/compiler/src/native/sanitizer.test.ts +++ b/packages/compiler/src/native/sanitizer.test.ts @@ -86,6 +86,7 @@ test.skipIf(process.platform !== "linux" && process.platform !== "darwin")( builtinValues: false, warningReplace: false, stdioExtended: false, + brotli: false, regex: true, copying: false, textDecoderLegacy: false, @@ -114,6 +115,7 @@ test.skipIf(process.platform !== "linux" && process.platform !== "darwin")( dgram: false, dnsPromises: false, dgramDyn: false, + textCodecDyn: false, watch: false, foreignFfi: false, nodeTest: false, diff --git a/packages/compiler/src/native/sanitizer.ts b/packages/compiler/src/native/sanitizer.ts index 6c1ff1e35..cb3728ec0 100644 --- a/packages/compiler/src/native/sanitizer.ts +++ b/packages/compiler/src/native/sanitizer.ts @@ -94,6 +94,7 @@ export function buildSanitizedRuntime( const quickjs = join(vendor, "quickjs-ng"); const zlib = join(vendor, "zlib"); const mbedtls = join(vendor, "mbedtls"); + const brotli = join(vendor, "brotli", "c"); const cacheIdentity = contentDigest( JSON.stringify({ schema: 1, @@ -137,6 +138,8 @@ export function buildSanitizedRuntime( zlib, "-I", join(mbedtls, "include"), + "-I", + join(brotli, "include"), ...(toolchain.target.platform === "linux" ? ["-ffunction-sections", "-fdata-sections"] : []), ]; const runtimeObjects: string[] = []; @@ -173,9 +176,23 @@ export function buildSanitizedRuntime( .filter((file) => file.endsWith(".c") && !file.startsWith(".")) .sort(); args = ["-std=c11", "-I", join(mbedtls, "include"), "-I", directory]; + } else if (name === "brotli") { + directory = brotli; + sources = ["common", "dec", "enc"] + .flatMap((dir) => + readdirSync(join(brotli, dir)) + .filter((file) => file.endsWith(".c") && !file.startsWith(".")) + .map((file) => `${dir}/${file}`), + ) + .sort(); + args = ["-std=c11", "-I", join(brotli, "include")]; } else throw new NativeCodegenError("SC3003", `unsupported runtime vendor archive: ${name}`); const objects = sources.map((source) => - compile(join(directory, source), name + "-" + source.replace(/\.c$/, ".o"), args), + compile( + join(directory, source), + name + "-" + source.replaceAll("/", "-").replace(/\.c$/, ".o"), + args, + ), ); const output = join(stage, "libscriptc-" + name + ".a"); runNativeTool( diff --git a/packages/compiler/surface-manifest.json b/packages/compiler/surface-manifest.json index e44f5d31e..1afd4cd3a 100644 --- a/packages/compiler/surface-manifest.json +++ b/packages/compiler/surface-manifest.json @@ -1,6 +1,6 @@ { "schemaVersion": 2, - "compilerVersion": "0.4.2", + "compilerVersion": "0.4.4", "coverage": [ "Entries are projected mechanically from the compiler's own decision tables: the diagnostics registry, the unsupported-syntax dispatch tables, the stdlib and node-builtin lowering tables, and the supported-builtin-module list. Nothing is hand-maintained; the manifest regenerates byte-identically from the source tree at this version.", "Absence from this manifest means 'not projected', never 'unsupported'. Surfaces lowered through dedicated code paths rather than tables are not yet projected: console, JSON, Promise/async and the timer surface, the net/http/tls/https/dgram/dns/assert/test/stream/readline module member surfaces, template literals, the regex slice, global functions (parseInt, parseFloat, isNaN, isFinite), and the process surface outside its ambient slice.", @@ -568,9 +568,7 @@ "id": "node-builtin.crypto.diffieHellman", "kind": "node-builtin", "name": "crypto.diffieHellman", - "status": "unsupported", - "code": "SC2020", - "note": "crypto.diffieHellman takes DH, EC or X25519 KeyObjects and has no lowering; the objects of createDiffieHellman, getDiffieHellman and createECDH compute shared secrets" + "status": "static" }, { "id": "node-builtin.crypto.generateKey", @@ -2471,6 +2469,12 @@ "name": "url.resolve", "status": "static" }, + { + "id": "node-builtin.url.urlToHttpOptions", + "kind": "node-builtin", + "name": "url.urlToHttpOptions", + "status": "static" + }, { "id": "node-builtin.util", "kind": "node-builtin", @@ -2878,22 +2882,6 @@ "status": "static", "note": "recognized module (bare and node:-prefixed specifiers)" }, - { - "id": "node-builtin.zlib.BrotliCompress", - "kind": "node-builtin", - "name": "zlib.BrotliCompress", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" - }, - { - "id": "node-builtin.zlib.BrotliDecompress", - "kind": "node-builtin", - "name": "zlib.BrotliDecompress", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" - }, { "id": "node-builtin.zlib.DEFLATE", "kind": "node-builtin", @@ -3647,33 +3635,25 @@ "id": "node-builtin.zlib.brotliCompress", "kind": "node-builtin", "name": "zlib.brotliCompress", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" + "status": "static" }, { "id": "node-builtin.zlib.brotliCompressSync", "kind": "node-builtin", "name": "zlib.brotliCompressSync", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" + "status": "static" }, { "id": "node-builtin.zlib.brotliDecompress", "kind": "node-builtin", "name": "zlib.brotliDecompress", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" + "status": "static" }, { "id": "node-builtin.zlib.brotliDecompressSync", "kind": "node-builtin", "name": "zlib.brotliDecompressSync", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" + "status": "static" }, { "id": "node-builtin.zlib.crc32", @@ -3685,17 +3665,13 @@ "id": "node-builtin.zlib.createBrotliCompress", "kind": "node-builtin", "name": "zlib.createBrotliCompress", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" + "status": "static" }, { "id": "node-builtin.zlib.createBrotliDecompress", "kind": "node-builtin", "name": "zlib.createBrotliDecompress", - "status": "unsupported", - "code": "SC2020", - "note": "Brotli needs a Brotli codec, which the static runtime does not vendor — the deflate/inflate, raw, gzip/gunzip, and unzip sync/callback forms with Node's options, crc32, and the Deflate/Inflate/Gzip/Gunzip/DeflateRaw/InflateRaw/Unzip streams are the lowered zlib surface" + "status": "static" }, { "id": "node-builtin.zlib.createDeflate", diff --git a/packages/compiler/test/class-method-mutations.test.ts b/packages/compiler/test/class-method-mutations.test.ts index b508a3949..166b9d556 100644 --- a/packages/compiler/test/class-method-mutations.test.ts +++ b/packages/compiler/test/class-method-mutations.test.ts @@ -135,3 +135,79 @@ test("a reflection call with a well-known symbol key keeps checked dispatch", as const callers = await callersOf(`${range}${write}`, "%Range.sym:iterator"); expect(callers.filter((name) => !name.startsWith("%method.value:"))).toEqual([]); }); + +const dictionaries = ` +class Counter { + n = 0; + add(): number { return ++this.n; } +} +interface Settings { groups: Record> } +interface Tree { [key: string]: Tree } +function addGroup(settings: Settings, name: string): void { + settings.groups[name] = {}; +} +function grow(tree: Tree, name: string): void { + tree[name] = {}; +} +const settings: Settings = { groups: {} }; +addGroup(settings, "colours"); +grow({}, "a"); +const counter = new Counter(); +console.log(counter.add(), Object.keys(settings.groups)); +`; + +test("a store into a dictionary of dictionaries leaves method calls direct", async () => { + expect(await callersOf(dictionaries, "%dyn.class.properties")).toEqual([]); +}); + +test("a store into a dictionary whose values can be functions keeps checked dispatch", async () => { + const source = dictionaries.replace( + "Record>", + "Record | (() => number)>", + ); + expect(await callersOf(source, "%dyn.class.properties")).not.toEqual([]); +}); + +test("a store into a generic dictionary of dictionaries keeps checked dispatch", async () => { + const source = dictionaries + .replace( + "interface Settings { groups: Record> }", + "interface Settings { groups: Record> }", + ) + .replace("function addGroup(settings: Settings,", "function addGroup(settings: Settings,") + .replace("const settings: Settings =", "const settings: Settings ="); + expect(await callersOf(source, "%dyn.class.properties")).not.toEqual([]); +}); + +test("a runtime-key store into a dictionary view of a class instance keeps checked dispatch", async () => { + const source = ` +class Counter { + n = 0; + tick(): number { return ++this.n; } +} +const counter = new Counter(); +const view: {} = counter; +const slots: Record = view; +const key: string = String(process.argv.length); +slots[key] = "replaced"; +console.log(counter.tick()); +`; + // The method has a slot the write can replace, which calls consult. + expect(await callersOf(source, "%Counter.tick")).toContain("%method.value:%Counter.tick"); +}); + +test("a dot store into a dictionary view of a class instance keeps checked dispatch", async () => { + const source = ` +class Lamp { + on = false; + toggle(): boolean { this.on = !this.on; return this.on; } +} +const lamp = new Lamp(); +const view: {} = lamp; +const slots: Record = view; +slots.toggle = "replaced"; +console.log(lamp.toggle()); +`; + // The method has a slot the write can replace, which calls consult. + expect(await callersOf(source, "%Lamp.toggle")).toContain("%method.value:%Lamp.toggle"); +}); diff --git a/packages/compiler/test/collection-keys.test.ts b/packages/compiler/test/collection-keys.test.ts index d73655ef1..ec049f23c 100644 --- a/packages/compiler/test/collection-keys.test.ts +++ b/packages/compiler/test/collection-keys.test.ts @@ -90,7 +90,7 @@ test("mixed-value unions select value equality while unsupported arms remain fen expect(mapKeyKindNum(union, [reference, scalar])).toBe(7); expect(isSupportedSetElem(union, [reference, scalar])).toBe(true); } - for (const unsupported of [{ kind: "date" } as IrType, mapOf(STRING, F64), setOf(STRING)]) { + for (const unsupported of [{ kind: "regex" } as IrType, mapOf(STRING, F64), setOf(STRING)]) { expect(isSupportedMapKey(unsupported)).toBe(false); } }); @@ -171,7 +171,7 @@ test.each(["map", "set"] as const)("validator checks the arms behind a %s key un expect(validateModule(mod)).toEqual([]); mod.unions![0]!.arms = [reference, F64]; expect(validateModule(mod)).toEqual([]); - mod.unions![0]!.arms = [reference, { kind: "date" }]; + mod.unions![0]!.arms = [reference, { kind: "regex" }]; expect( validateModule(mod).some((error) => error.message.includes( diff --git a/packages/compiler/test/completion-callbacks.test.ts b/packages/compiler/test/completion-callbacks.test.ts new file mode 100644 index 000000000..817472c19 --- /dev/null +++ b/packages/compiler/test/completion-callbacks.test.ts @@ -0,0 +1,60 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, expect, test } from "vitest"; +import { compile } from "../src/index.js"; + +const directories: string[] = []; +afterEach(async () => { + await Promise.all(directories.splice(0).map((dir) => rm(dir, { recursive: true, force: true }))); +}); + +async function emit(source: string): Promise { + const outDir = await mkdtemp(join(tmpdir(), "scriptc-completion-callbacks-")); + directories.push(outDir); + const entry = join(outDir, "main.ts"); + await writeFile(entry, source); + const outPath = join(outDir, "program.ll"); + const result = await compile(entry, { entry, outDir, outPath, outputKind: "llvm" }); + expect(result.ok, JSON.stringify(result)).toBe(true); + return readFile(outPath, "utf8"); +} + +const randomBytes = ` +import { randomBytes } from "node:crypto"; +randomBytes(4, (error, buffer) => console.log(error === null, buffer.length)); +`; + +test("programs without AsyncLocalStorage pass completion callbacks unchanged", async () => { + const llvm = await emit(randomBytes); + expect(llvm).not.toContain("@scr_als_bind_closure"); + expect(llvm).not.toContain("sc_alsbound_"); +}); + +test("programs that enter stores bind each completion callback to the active context", async () => { + const llvm = await emit(` +import { AsyncLocalStorage } from "node:async_hooks"; +${randomBytes} +const store = new AsyncLocalStorage(); +store.run("a", () => randomBytes(1, (error, buffer) => console.log(store.getStore(), buffer.length))); +`); + const binds = llvm.match( + /call ptr @scr_als_bind_closure\(ptr [^,]+, ptr @sc_alsbound_\d+, i1 false\)/g, + ); + expect(binds?.length).toBe(2); + // One thunk per callback signature. + expect(llvm.match(/^define internal void @sc_alsbound_\d+\(/gm)?.length).toBe(1); +}); + +test("AsyncLocalStorage.bind binds a typed function with an empty context too", async () => { + const llvm = await emit(` +import { AsyncLocalStorage } from "node:async_hooks"; +const store = new AsyncLocalStorage(); +const read = AsyncLocalStorage.bind((n: number) => \`\${store.getStore()} \${n}\`); +console.log(store.run("x", () => read(1))); +`); + expect(llvm).toMatch( + /call ptr @scr_als_bind_closure\(ptr [^,]+, ptr @sc_alsbound_\d+, i1 true\)/, + ); + expect(llvm).toMatch(/^define internal ptr @sc_alsbound_\d+\(ptr %self, double %a0\)/m); +}); diff --git a/packages/compiler/test/date-time-values.test.ts b/packages/compiler/test/date-time-values.test.ts new file mode 100644 index 000000000..4e90ef3e6 --- /dev/null +++ b/packages/compiler/test/date-time-values.test.ts @@ -0,0 +1,100 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile, deserializeModule } from "../src/index.js"; +import { emitLlvmModule } from "../src/backend/llvm/emitter.js"; +import type { IrModule } from "../src/ir/ir.js"; + +async function lowerSource(source: string): Promise { + const dir = await mkdtemp(join(tmpdir(), "scriptc-date-time-values-")); + try { + const entry = join(dir, "main.ts"); + await writeFile(entry, source); + const outPath = join(dir, "main.ir.json"); + const result = await compile(entry, { outPath, outDir: dir, outputKind: "ir" }); + if (!result.ok) throw new Error(result.diagnostics.map((d) => d.message).join("\n")); + return deserializeModule(await readFile(outPath, "utf8")); + } finally { + await rm(dir, { recursive: true, force: true }); + } +} + +/** The IR type of each local named `name` in function `fn`. */ +function localKinds(mod: IrModule, fn: string, name: string): string[] { + const f = mod.functions.find((candidate) => candidate.name === fn); + if (!f) throw new Error(`no function ${fn}`); + return f.locals.filter((local) => local.name === name).map((local) => local.type.kind); +} + +test("a Date local read only for its time value stores the time value", async () => { + const mod = await lowerSource(` +function format(ms: number): string { + const d = new Date(ms); + const later = new Date(ms + 1); + return d.toISOString() + String(d) + \`\${later}\` + (d < later) + d.getUTCHours() + new Date(d).getTime(); +} +console.log(format(0)); +`); + expect(localKinds(mod, "format", "d")).toEqual(["f64"]); + expect(localKinds(mod, "format", "later")).toEqual(["f64"]); + // Reading the time value creates no Date object. + const llvm = emitLlvmModule(mod, { pointerBits: 64 }); + const body = /^define internal [^\n]*@sc_f_format\([^]*?^}/m.exec(llvm)?.[0] ?? ""; + expect(body).not.toContain("@scr_date_new("); +}); + +test("a Date local whose identity a use can observe keeps the Date object", async () => { + const mod = await lowerSource(` +const keep: Date[] = []; +function uses(ms: number): boolean { + const compared = new Date(ms); + const stored = new Date(ms); + const passed = new Date(ms); + const captured = new Date(ms); + const returned = new Date(ms); + let reassigned = new Date(ms); + reassigned = new Date(ms + 1); + keep.push(stored); + const read = (): number => captured.getTime(); + console.log(String(passed), read(), returned.getTime(), reassigned.getTime()); + return compared === compared && pass(passed) === passed && back(returned) === returned; +} +function pass(d: Date): Date { + return d; +} +function back(d: Date): Date { + return d; +} +const top = new Date(0); +console.log(uses(0), top.getTime()); +`); + for (const name of ["compared", "stored", "passed", "captured", "returned", "reassigned"]) { + expect(localKinds(mod, "uses", name), name).toEqual(["date"]); + } +}); + +test("a Date in a block or loop at module level stores the time value unless a use observes it", async () => { + const mod = await lowerSource(` +const seen: Date[] = []; +let total = 0; +for (let i = 0; i < 3; i++) { + const read = new Date(i * 1000); + const kept = new Date(i * 1000); + seen.push(kept); + total += read.getUTCSeconds() + read.toISOString().length; +} +{ + const block = new Date(5); + const captured = new Date(6); + const later = (): number => captured.getTime(); + total += block.getTime() + later(); +} +const top = new Date(7); +console.log(total, seen.length, top.getTime()); +`); + expect(localKinds(mod, "%init.0", "read")).toEqual(["f64"]); + expect(localKinds(mod, "%init.0", "block")).toEqual(["f64"]); + expect(localKinds(mod, "%init.0", "kept")).toEqual(["date"]); + expect(localKinds(mod, "%init.0", "captured")).toEqual(["date"]); +}); diff --git a/packages/compiler/test/dictionary-views.test.ts b/packages/compiler/test/dictionary-views.test.ts new file mode 100644 index 000000000..a9cfd1056 --- /dev/null +++ b/packages/compiler/test/dictionary-views.test.ts @@ -0,0 +1,89 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile, deserializeModule } from "../src/index.js"; + +/** The IR type kind of each named module-level binding. */ +async function globalKinds(source: string): Promise> { + const dir = await mkdtemp(join(tmpdir(), "scriptc-dictionary-views-")); + try { + const entry = join(dir, "main.ts"); + const outPath = join(dir, "main.ir.json"); + await writeFile(entry, source); + const result = await compile(entry, { outDir: dir, outPath, outputKind: "ir" }); + if (!result.ok) + throw new Error(result.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n")); + const mod = deserializeModule(await readFile(outPath, "utf8")); + return Object.fromEntries((mod.globals ?? []).map((g) => [g.name, g.type.kind])); + } finally { + await rm(dir, { recursive: true, force: true }); + } +} + +const program = (body: string): string => ` +const key = String(process.argv.length); +const counts: Record = { a: 1 }; +counts[key] = 2; +${body} +console.log(JSON.stringify(counts)); +`; + +test("a record written through a dictionary keeps that dictionary type checked", async () => { + const kinds = await globalKinds( + program(`const point: { a: string } = { a: "x" }; +const table: Record = point; +table[key] = "y"; +console.log(JSON.stringify(point));`), + ); + expect(kinds["table"]).toBe("dyn"); + // Dictionaries of other value types keep their native layout. + expect(kinds["counts"]).toBe("record"); +}); + +test("a record reaching a dictionary through a {} parameter keeps it checked", async () => { + const kinds = await globalKinds( + program(`function stamp(value: {}): void { + const inner: Record = value; + inner[key] = "y"; +} +const point: { a: string } = { a: "x" }; +stamp(point); +const other: Record = { b: "z" }; +console.log(JSON.stringify(point), other["b"]);`), + ); + expect(kinds["other"]).toBe("dyn"); +}); + +test("dictionaries keep their native layout without a record flowing in", async () => { + const kinds = await globalKinds( + program(`const table: Record = { a: "x" }; +table[key] = "y"; +const copy: Record = table; +copy[key] = "z"; +console.log(JSON.stringify(table));`), + ); + expect(kinds["table"]).toBe("record"); + expect(kinds["copy"]).toBe("record"); +}); + +test("a record converted to a dictionary that is only read keeps the native layout", async () => { + const kinds = await globalKinds( + program(`const point: { a: string } = { a: "x" }; +const table: Record = point; +console.log(table["a"]);`), + ); + expect(kinds["table"]).toBe("record"); +}); + +test("a dictionary literal whose methods read this keeps checked identity", async () => { + const kinds = await globalKinds( + program(`interface Table { [key: string]: () => number } +const methods: Table = { + base() { return 2; }, + twice() { return this.base() * 2; }, +}; +console.log(methods.twice());`), + ); + expect(kinds["methods"]).toBe("dyn"); +}); diff --git a/packages/compiler/test/int32-element-array-emission.test.ts b/packages/compiler/test/int32-element-array-emission.test.ts index 0b68a5d3e..605f64e04 100644 --- a/packages/compiler/test/int32-element-array-emission.test.ts +++ b/packages/compiler/test/int32-element-array-emission.test.ts @@ -77,6 +77,19 @@ function literalCounter(n: number): number { for (let i = 0; i < n; i++) a[0] = a[0]! + 1; return a[0]!; } +function walk(n: number, steps: number): number { + const next: number[] = []; + for (let i = 0; i < n; i++) next.push(i + 1 < n ? i + 1 : 0); + let at = next[0]!; + let total = 0; + for (let s = 0; s < steps; s++) { + const t = next[at]!; + next[at] = at; + at = t; + total = (total + at) | 0; + } + return total; +} function joined(n: number): string { const a = [n | 0, 2]; return a.join(","); @@ -91,6 +104,7 @@ console.log( shortCounter(), literalCounter(n), joined(n), + walk(n | 0, 5), ); `; @@ -119,6 +133,16 @@ test("arrays storing only int32 elements get 4-byte slots", async () => { expect(counts).not.toContain("popArray"); }); +test("an array that stores a cursor read back from its own elements gets 4-byte slots", async () => { + const llvm = await emitLlvm(source); + const walk = functions(llvm, "walk"); + expect(walk).not.toBe(""); + // `at` is assigned `next[at]!` in the loop and stored back into `next`: + // it holds an int32 whenever the array's elements are int32s. + expect(walk).toMatch(/@scr_arr_new_small\(i32 7\)/); + expect(walk).toMatch(/getelementptr inbounds i32, ptr %\w+, i64 %\w+\n\s+%\w+ = load i32/); +}); + test("arrays with a store that has no exact integer range, or that escape, keep doubles", async () => { const llvm = await emitLlvm(source); for (const name of ["fraction", "negativeZero", "joined"]) { @@ -196,3 +220,42 @@ test("only the candidates the proof keeps are rewritten", async () => { for (const fn of selected.functions) if (fn.name !== "compose") expect(fn).toBe(mod.functions.find((f) => f.name === fn.name)); }); + +const sharedSource = ` +const data: number[] = []; +for (let i = 0; i < 64; i++) data.push((i * 7) & 255); +function fnv(values: number[]): number { + let h = 2166136261; + for (let i = 0; i < values.length; i++) h = Math.imul(h ^ values[i]!, 16777619) >>> 0; + return h; +} +function mean(values: number[]): number { + let s = 0; + for (let i = 0; i < values.length; i++) s += values[i]!; + return s / values.length; +} +function first(values: number[]): number { + return values[0]!; +} +const ints = [1, 2, 3]; +const halves = [0.5, 1.5]; +const pick: (values: number[]) => number = first; +console.log(fnv(data), mean(ints), mean(halves), pick([4])); +`; + +test("arrays passed to helpers and held in module globals get 4-byte slots", async () => { + const mod = await lowerIr(sharedSource); + const proposed = markInt32ArrayCandidates(mod).int32ArrayCandidates ?? []; + // A global and the parameter every call passes it to; a function used as + // a value keeps its parameter. + expect(proposed).toEqual(expect.arrayContaining(["\0%g.e.data", "fnv\0values.0"])); + expect(proposed.some((key) => key.startsWith("first\0"))).toBe(false); + const llvm = await emitLlvm(sharedSource); + // The helper reads int32 slots; `mean`, also called with fractions, + // reads doubles. + expect(functions(llvm, "fnv")).toMatch( + /getelementptr inbounds i32, ptr %\w+, i64 %\w+\n\s+%\w+ = load i32/, + ); + expect(functions(llvm, "mean")).not.toMatch(/getelementptr inbounds i32, ptr/); + expect(llvm).toMatch(/@scr_arr_new(_small)?\(i32 7/); +}); diff --git a/packages/compiler/test/integer-view-compares.test.ts b/packages/compiler/test/integer-view-compares.test.ts new file mode 100644 index 000000000..e988d48bd --- /dev/null +++ b/packages/compiler/test/integer-view-compares.test.ts @@ -0,0 +1,52 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile, deserializeModule } from "../src/index.js"; +import { emitLlvmModule } from "../src/backend/llvm/emitter.js"; + +const source = `function siftDown(a: Int32Array, start: number, end: number): void { + let root = start; + while (2 * root + 1 < end) { + let child = 2 * root + 1; + if (child + 1 < end && a[child]! < a[child + 1]!) child++; + if (a[root]! >= a[child]!) return; + const t = a[root]!; + a[root] = a[child]!; + a[child] = t; + root = child; + } +} +const a = new Int32Array([3, 9, 1, 7, 5]); +siftDown(a, 0, Number(process.argv[2] ?? "5")); +console.log(Array.from(a).join()); +`; + +test("integer sums of speculated locals compare and index as integers", async () => { + const dir = await mkdtemp(join(tmpdir(), "scriptc-integer-views-")); + try { + const entry = join(dir, "sift.ts"); + await writeFile(entry, source); + const outPath = join(dir, "sift.ir.json"); + const result = await compile(entry, { outPath, outDir: dir, outputKind: "ir" }); + if (!result.ok) throw new Error(result.diagnostics.map((d) => d.message).join("\n")); + const ll = emitLlvmModule(deserializeModule(await readFile(outPath, "utf8"))); + const body = ll.match( + /define internal [^\n]+ @sc_bf_siftDown(?:\.i32)?\([^\n]*\) #0 \{([\s\S]*?)\n\}/, + )![1]!; + // `child + 1`: the integer the speculated copy already adds. + const sums = [...body.matchAll(/(%t\d+) = add nsw i64 %t\d+, 1\n/g)].map((m) => m[1]!); + expect(sums.length).toBeGreaterThan(0); + const uses = (op: string) => + sums.filter((sum) => new RegExp(`= ${op} i64 \\${sum}, %t\\d+\\n`).test(body)); + // `child + 1 < end` compares the integers, + expect(uses("icmp slt").length).toBeGreaterThan(0); + // and `a[child + 1]` bounds the integer index without a double round trip. + expect(uses("icmp ult").length).toBeGreaterThan(0); + // `2 * root + 1 < end` may pass 2^53, so it keeps the double compare. + expect(body).toMatch(/fmul double 0x4000000000000000, %t\d+\n\s+%t\d+ = mul nsw i64 2/); + expect(body).toContain("fcmp olt double"); + } finally { + await rm(dir, { recursive: true, force: true }); + } +}); diff --git a/packages/compiler/test/ir.test.ts b/packages/compiler/test/ir.test.ts index 7f1a1ef78..7f5e9f3c9 100644 --- a/packages/compiler/test/ir.test.ts +++ b/packages/compiler/test/ir.test.ts @@ -8,10 +8,15 @@ test("hand-built fib module validates", () => { expect(validateModule(fibModule)).toEqual([]); }); -test("validator rejects Date union arms unsupported by the backends", () => { +test("validator rejects Date union arms beside other data arms", () => { const bad = structuredClone(fibModule); - bad.unions = [{ id: "u-date", arms: [{ kind: "date" }, { kind: "undefinedT" }] }]; - expect(validateModule(bad).map((e) => e.message)).toContain("union u-date: arm 0 is date"); + bad.unions = [{ id: "u-date", arms: [{ kind: "date" }, { kind: "string" }] }]; + expect(validateModule(bad).map((e) => e.message)).toContain( + "union u-date: date arm 0 beside non-unit arms", + ); + const nullable = structuredClone(fibModule); + nullable.unions = [{ id: "u-date", arms: [{ kind: "date" }, { kind: "undefinedT" }] }]; + expect(validateModule(nullable)).toEqual([]); }); test("fib module JSON round-trips", () => { diff --git a/packages/compiler/test/js-class-layouts.test.ts b/packages/compiler/test/js-class-layouts.test.ts index c8491f3e8..987703110 100644 --- a/packages/compiler/test/js-class-layouts.test.ts +++ b/packages/compiler/test/js-class-layouts.test.ts @@ -101,6 +101,51 @@ delete loose.label; expect(layout(deleted, "Shape").tracks).toBe(true); }); +test("freezing values other than class instances keeps JavaScript class layouts fixed", async () => { + const module = await lower(`${SHAPES} +/** @param {{ value: any }} data */ +function settle(data) { + data.value = Object.freeze(data.value); + Object.seal(data); + return Object.preventExtensions(data.value); +} +settle({ value: { sides: 1 } }); +const square = new Square(); +Object.freeze(square); +`); + expect(layout(module, "Shape").tracks).toBe(false); + expect(layout(module, "Square").tracks).toBe(false); +}); + +test("accessors that share no field's name keep JavaScript class layouts fixed", async () => { + const module = await lower(` +class Path { + constructor(key) { + this.cache = []; + this.key = key; + } + get path() { + if (!this.cache.length) this.cache.push(this.key); + return this.cache; + } +} +class Named extends Path { + constructor(key) { + super(key); + this.label = "named"; + } + /** @param {string} value */ + set label2(value) { + this.label = value; + } +} +console.log(new Named("k").path.length); +`); + expect(layout(module, "Path")).toEqual({ fields: "cache:dyn key:dyn", tracks: false }); + expect(layout(module, "Named").tracks).toBe(false); + expect(layout(module, "Named").fields).toBe("cache:dyn key:dyn label:string"); +}); + test("JavaScript subclass fields keep their types when the base constructor only assigns fields", async () => { const module = await lower(` class Base { @@ -176,3 +221,21 @@ test("a JavaScript file TypeScript checks uses its JSDoc types for class layouts expect(layout(unchecked, "Account").fields).toBe("owner:dyn balance:dyn"); expect(params(unchecked, "%Account.constructor")).toBe("(object, dyn, dyn): void"); }); + +test("instances of a class with subclasses box at their exact class", async () => { + const module = await lower(`${SHAPES} +/** @param {any} value */ +function keep(value) { + return value; +} +/** @param {Shape} shape */ +function cross(shape) { + return keep(shape); +} +console.log(cross(new Square()).size, cross(new Shape()).label); +`); + const helper = module.functions.find((fn) => fn.name === "%dyn.class.boxExact:Shape"); + expect(helper).toBeDefined(); + expect(calls(module, "cross")).toContain("%dyn.class.boxExact:Shape"); + expect(JSON.stringify(helper!.body)).toContain('"className":"Square"'); +}); diff --git a/packages/compiler/test/native-codegen-lowering.test.ts b/packages/compiler/test/native-codegen-lowering.test.ts index e9891e945..857393f21 100644 --- a/packages/compiler/test/native-codegen-lowering.test.ts +++ b/packages/compiler/test/native-codegen-lowering.test.ts @@ -51,14 +51,12 @@ test("decompression options lower for run-time validation", () => { expect(diags).toEqual([]); }); -test("Brotli keeps its SC2020 refusal", () => { +test("Brotli options lower for run-time validation", () => { const diags = diagnostics(` - import { brotliCompressSync } from "node:zlib"; - console.log(brotliCompressSync("payload").length); + import { brotliCompressSync, constants } from "node:zlib"; + console.log(brotliCompressSync("payload", { params: { [constants.BROTLI_PARAM_QUALITY]: 4 } }).length); `); - expect(diags).toHaveLength(1); - expect(diags[0]!.code).toBe("SC2020"); - expect(diags[0]!.message).toContain("brotliCompressSync"); + expect(diags).toEqual([]); }); test("union writes keep the refusal for incompatible field storage", () => { diff --git a/packages/compiler/test/power-of-two-quotient-emission.test.ts b/packages/compiler/test/power-of-two-quotient-emission.test.ts new file mode 100644 index 000000000..4b748a2a1 --- /dev/null +++ b/packages/compiler/test/power-of-two-quotient-emission.test.ts @@ -0,0 +1,46 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile, deserializeModule } from "../src/index.js"; +import { emitLlvmModule } from "../src/backend/llvm/emitter.js"; + +test("a quotient by Math.pow(2, n) multiplies by the divisor's reciprocal power of two", async () => { + const dir = await mkdtemp(join(tmpdir(), "scriptc-pow2-quotient-")); + try { + const entry = join(dir, "main.ts"); + const outPath = join(dir, "main.ir.json"); + await writeFile( + entry, + ` +function scaled(x: number, n: number): number { return x / Math.pow(2, n); } +function plain(x: number, d: number): number { return x / d; } +function other(x: number, n: number): number { return x / Math.pow(3, n); } +console.log(scaled(3, 4), plain(3, 16), other(3, 4)); +`, + ); + const result = await compile(entry, { outDir: dir, outPath, outputKind: "ir" }); + if (!result.ok) + throw new Error(result.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n")); + const mod = deserializeModule(await readFile(outPath, "utf8")); + const ll = emitLlvmModule(mod); + const fn = (name: string): string => + new RegExp(`^define internal [^\\n]*@sc_[bf]+_${name}(?:\\.i32)?\\([^]*?^}`, "m").exec( + ll, + )![0]; + // The reciprocal's bits are 0x7FE0000000000000 minus the divisor's; a + // divisor that is not a power of two from 2^-1022 to 2^1022 divides. + const quotient = fn("scaled"); + expect(quotient).toContain(`sub i64 ${0x7fe0000000000000n}, `); + expect(quotient).toMatch(/fmul double %\w+, %\w+/); + expect(quotient).toMatch(/icmp ult i64 %\w+, 2045/); + expect(quotient).toMatch(/select i1 %\w+, double %\w+, double %\w+/); + expect(quotient.match(/ = fdiv double /g)).toHaveLength(1); + for (const name of ["plain", "other"]) { + expect(fn(name)).not.toContain(`${0x7fe0000000000000n}`); + expect(fn(name)).toMatch(/fdiv double/); + } + } finally { + await rm(dir, { recursive: true, force: true }); + } +}); diff --git a/packages/compiler/test/source-output.test.ts b/packages/compiler/test/source-output.test.ts index 8b8e3883c..ce411f102 100644 --- a/packages/compiler/test/source-output.test.ts +++ b/packages/compiler/test/source-output.test.ts @@ -3,6 +3,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, expect, test } from "vitest"; import { compile, deserializeModule, validateModule } from "../src/index.js"; +import { setLibDiagnosticsStore } from "../src/frontend/ts7/lib-diagnostics.js"; const dirs: string[] = []; afterEach(async () => { @@ -188,6 +189,38 @@ test("source outputs ignore invalid external compiler selection and create no ex } }); +test("source outputs neither read nor create declaration diagnostics a previous build stored", async () => { + const { dir, entry, outDir } = await fixture(); + const oldCache = process.env["SCRIPTC_CACHE_DIR"]; + const cache = join(dir, "must-not-exist"); + process.env["SCRIPTC_CACHE_DIR"] = cache; + // A store an earlier build of this process left configured. + const reads: string[] = []; + setLibDiagnosticsStore( + { read: (key) => (reads.push(key), null), write: () => {} }, + "earlier-checker", + ); + try { + for (const [kind, name] of [ + ["ir", "main.ir.json"], + ["llvm", "main.ll"], + ] as const) { + const result = await compile(entry, { + outDir, + outPath: join(outDir, name), + outputKind: kind, + }); + expect(result.ok).toBe(true); + } + expect(reads).toEqual([]); + await expect(readdir(cache)).rejects.toMatchObject({ code: "ENOENT" }); + } finally { + setLibDiagnosticsStore(null); + if (oldCache === undefined) delete process.env["SCRIPTC_CACHE_DIR"]; + else process.env["SCRIPTC_CACHE_DIR"] = oldCache; + } +}); + test.each(["wasm64-wasi", "totally-invalid"])( "source output rejects unsupported target %s without writing an artifact", async (target) => { diff --git a/packages/compiler/test/target-cpu-emit.test.ts b/packages/compiler/test/target-cpu-emit.test.ts new file mode 100644 index 000000000..5dee8243c --- /dev/null +++ b/packages/compiler/test/target-cpu-emit.test.ts @@ -0,0 +1,46 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile } from "../src/index.js"; + +const program = `const a = new Int32Array(4096); +for (let i = 0; i < a.length; i++) a[i] = (i * 2654435761) | 0; +let s = 0; +for (let r = 0; r < 64; r++) for (let i = 0; i < a.length; i++) s = (s + a[i]!) | 0; +console.log(s); +`; + +test.runIf(process.platform === "linux" && process.arch === "x64")( + "assembly, object and LLVM builds honor the target CPU", + async () => { + const dir = await mkdtemp(join(tmpdir(), "scriptc-target-cpu-emit-")); + const entry = join(dir, "main.ts"); + const emit = async ( + outputKind: "llvm" | "asm" | "obj", + targetCpu?: "x86-64-v3", + ): Promise => { + const outPath = join(dir, `main-${outputKind}-${targetCpu ?? "baseline"}`); + const result = await compile(entry, { + outPath, + outDir: dir, + outputKind, + ...(targetCpu === undefined ? {} : { targetCpu }), + }); + if (!result.ok) throw new Error(result.diagnostics.map((d) => d.message).join("\n")); + return readFile(outPath); + }; + try { + await writeFile(entry, program); + expect((await emit("llvm")).toString()).not.toContain('"target-cpu"'); + expect((await emit("llvm", "x86-64-v3")).toString()).toContain('"target-cpu"="x86-64-v3"'); + // The v3 build vectorizes the sums with AVX2's 256-bit registers. + expect((await emit("asm")).toString()).not.toContain("%ymm"); + expect((await emit("asm", "x86-64-v3")).toString()).toContain("%ymm"); + expect((await emit("obj", "x86-64-v3")).equals(await emit("obj"))).toBe(false); + } finally { + await rm(dir, { recursive: true, force: true }); + } + }, + 240_000, +); diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5965-date-objects.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5965-date-objects.ts.json new file mode 100644 index 000000000..c2497d502 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5965-date-objects.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5965-date-objects.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5966-date-time-values.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5966-date-time-values.ts.json new file mode 100644 index 000000000..e6155bcc4 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5966-date-time-values.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5966-date-time-values.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5967-date-alias-mutation/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5967-date-alias-mutation/main.ts.json new file mode 100644 index 000000000..bdd29bf77 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5967-date-alias-mutation/main.ts.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/5967-date-alias-mutation/clock.js", + "/tests/corpus/5967-date-alias-mutation/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6343-inspect-class-values.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6343-inspect-class-values.ts.json new file mode 100644 index 000000000..4fa8625ac --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6343-inspect-class-values.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6343-inspect-class-values.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6344-inspect-function-properties.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6344-inspect-function-properties.cjs.json new file mode 100644 index 000000000..003075140 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6344-inspect-function-properties.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6344-inspect-function-properties.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6357-format-o-hidden.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6357-format-o-hidden.ts.json new file mode 100644 index 000000000..cf0cd31d1 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6357-format-o-hidden.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6357-format-o-hidden.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6358-format-o-hidden-js.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6358-format-o-hidden-js.cjs.json new file mode 100644 index 000000000..73d4eefa6 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6358-format-o-hidden-js.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6358-format-o-hidden-js.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6935-fs-url-paths-ordered.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6935-fs-url-paths-ordered.ts.json new file mode 100644 index 000000000..ec3d3f004 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6935-fs-url-paths-ordered.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6935-fs-url-paths-ordered.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7012-dictionary-views-keep-objects.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7012-dictionary-views-keep-objects.ts.json new file mode 100644 index 000000000..6d7944cf7 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7012-dictionary-views-keep-objects.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7012-dictionary-views-keep-objects.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7013-dictionary-view-parameter.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7013-dictionary-view-parameter.ts.json new file mode 100644 index 000000000..41f80c41e --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7013-dictionary-view-parameter.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7013-dictionary-view-parameter.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7035-promise-all-tuple-turns.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7035-promise-all-tuple-turns.ts.json new file mode 100644 index 000000000..29b935e93 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7035-promise-all-tuple-turns.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7035-promise-all-tuple-turns.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7110-als-completion-callbacks.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7110-als-completion-callbacks.ts.json new file mode 100644 index 000000000..5645ac5df --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7110-als-completion-callbacks.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7110-als-completion-callbacks.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7111-als-bind-snapshot-options.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7111-als-bind-snapshot-options.ts.json new file mode 100644 index 000000000..27aa14a8d --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7111-als-bind-snapshot-options.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7111-als-bind-snapshot-options.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7112-als-bind-snapshot-dynamic.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7112-als-bind-snapshot-dynamic.mjs.json new file mode 100644 index 000000000..1915cae63 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7112-als-bind-snapshot-dynamic.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7112-als-bind-snapshot-dynamic.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7119-als-net-event-context.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7119-als-net-event-context.mjs.json new file mode 100644 index 000000000..7ff81ea34 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7119-als-net-event-context.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7119-als-net-event-context.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7189-non-null-assertion-nullish-destination.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7189-non-null-assertion-nullish-destination.ts.json new file mode 100644 index 000000000..dc0d277ee --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7189-non-null-assertion-nullish-destination.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7189-non-null-assertion-nullish-destination.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7260-teardown-segments.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7260-teardown-segments.ts.json new file mode 100644 index 000000000..a7eac099f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7260-teardown-segments.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7260-teardown-segments.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7264-array-dense-paired-loops.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7264-array-dense-paired-loops.ts.json new file mode 100644 index 000000000..8396d237c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7264-array-dense-paired-loops.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7264-array-dense-paired-loops.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7306-dunder-exports/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7306-dunder-exports/main.ts.json new file mode 100644 index 000000000..fd14c0dfe --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7306-dunder-exports/main.ts.json @@ -0,0 +1,10 @@ +{ + "order": [ + "/tests/corpus/7306-dunder-exports/dep.ts", + "/tests/corpus/7306-dunder-exports/star.ts", + "/tests/corpus/7306-dunder-exports/facade.ts", + "/tests/corpus/7306-dunder-exports/table.cjs", + "/tests/corpus/7306-dunder-exports/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7309-globalthis-buffer-aliases.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7309-globalthis-buffer-aliases.ts.json new file mode 100644 index 000000000..94d458e11 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7309-globalthis-buffer-aliases.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7309-globalthis-buffer-aliases.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7310-any-destructure-string-keys.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7310-any-destructure-string-keys.ts.json new file mode 100644 index 000000000..45a9f62ff --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7310-any-destructure-string-keys.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7310-any-destructure-string-keys.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7380-als-http-event-context.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7380-als-http-event-context.mjs.json new file mode 100644 index 000000000..1087d25fe --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7380-als-http-event-context.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7380-als-http-event-context.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7380-cycle-deferred-folded-key/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7380-cycle-deferred-folded-key/main.ts.json new file mode 100644 index 000000000..18ef785c5 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7380-cycle-deferred-folded-key/main.ts.json @@ -0,0 +1,8 @@ +{ + "order": [ + "/tests/corpus/7380-cycle-deferred-folded-key/util.ts", + "/tests/corpus/7380-cycle-deferred-folded-key/core.ts", + "/tests/corpus/7380-cycle-deferred-folded-key/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7381-als-async-generator-steps.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7381-als-async-generator-steps.ts.json new file mode 100644 index 000000000..47f93314d --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7381-als-async-generator-steps.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7381-als-async-generator-steps.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7381-cycle-deferred-reached-tdz/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7381-cycle-deferred-reached-tdz/main.ts.json new file mode 100644 index 000000000..65d9b19e9 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7381-cycle-deferred-reached-tdz/main.ts.json @@ -0,0 +1,8 @@ +{ + "order": [ + "/tests/corpus/7381-cycle-deferred-reached-tdz/beta.ts", + "/tests/corpus/7381-cycle-deferred-reached-tdz/alpha.ts", + "/tests/corpus/7381-cycle-deferred-reached-tdz/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7382-als-async-generator-steps.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7382-als-async-generator-steps.mjs.json new file mode 100644 index 000000000..5f55ba7bd --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7382-als-async-generator-steps.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7382-als-async-generator-steps.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7382-cycle-static-this-tdz/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7382-cycle-static-this-tdz/main.ts.json new file mode 100644 index 000000000..986683684 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7382-cycle-static-this-tdz/main.ts.json @@ -0,0 +1,8 @@ +{ + "order": [ + "/tests/corpus/7382-cycle-static-this-tdz/beta.ts", + "/tests/corpus/7382-cycle-static-this-tdz/alpha.ts", + "/tests/corpus/7382-cycle-static-this-tdz/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7383-als-stream-events.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7383-als-stream-events.ts.json new file mode 100644 index 000000000..d59808c2f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7383-als-stream-events.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7383-als-stream-events.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7383-cycle-class-expression-name-tdz/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7383-cycle-class-expression-name-tdz/main.ts.json new file mode 100644 index 000000000..69b6d63bb --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7383-cycle-class-expression-name-tdz/main.ts.json @@ -0,0 +1,8 @@ +{ + "order": [ + "/tests/corpus/7383-cycle-class-expression-name-tdz/beta.ts", + "/tests/corpus/7383-cycle-class-expression-name-tdz/alpha.ts", + "/tests/corpus/7383-cycle-class-expression-name-tdz/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7384-als-stream-events.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7384-als-stream-events.mjs.json new file mode 100644 index 000000000..51171c8d5 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7384-als-stream-events.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7384-als-stream-events.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7385-als-child-process-events.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7385-als-child-process-events.ts.json new file mode 100644 index 000000000..aac67defb --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7385-als-child-process-events.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7385-als-child-process-events.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7386-als-child-fork-events/main.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7386-als-child-fork-events/main.mjs.json new file mode 100644 index 000000000..a334cf0aa --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7386-als-child-fork-events/main.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7386-als-child-fork-events/main.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7387-als-http2-events.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7387-als-http2-events.mjs.json new file mode 100644 index 000000000..272024d1b --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7387-als-http2-events.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7387-als-http2-events.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7388-als-http2-compat-events.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7388-als-http2-compat-events.mjs.json new file mode 100644 index 000000000..8a64f395e --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7388-als-http2-compat-events.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7388-als-http2-compat-events.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7389-als-http2-secure-events.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7389-als-http2-secure-events.mjs.json new file mode 100644 index 000000000..aec8e2d2a --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7389-als-http2-secure-events.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7389-als-http2-secure-events.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7390-record-key-read-presence.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7390-record-key-read-presence.ts.json new file mode 100644 index 000000000..74bd7e30d --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7390-record-key-read-presence.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7390-record-key-read-presence.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7391-als-response-end-and-finished.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7391-als-response-end-and-finished.mjs.json new file mode 100644 index 000000000..49a506703 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7391-als-response-end-and-finished.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7391-als-response-end-and-finished.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7392-non-null-operands.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7392-non-null-operands.ts.json new file mode 100644 index 000000000..be7ffaa53 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7392-non-null-operands.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7392-non-null-operands.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7394-non-null-typed-array-reads.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7394-non-null-typed-array-reads.ts.json new file mode 100644 index 000000000..5fbae0c10 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7394-non-null-typed-array-reads.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7394-non-null-typed-array-reads.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7404-frozen-records.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7404-frozen-records.ts.json new file mode 100644 index 000000000..d85f2aab5 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7404-frozen-records.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7404-frozen-records.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7405-frozen-records-sloppy.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7405-frozen-records-sloppy.cjs.json new file mode 100644 index 000000000..3d87d019b --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7405-frozen-records-sloppy.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7405-frozen-records-sloppy.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7406-frozen-containers.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7406-frozen-containers.ts.json new file mode 100644 index 000000000..34fb3ba0e --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7406-frozen-containers.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7406-frozen-containers.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7449-crypto-raw-keys.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7449-crypto-raw-keys.mjs.json new file mode 100644 index 000000000..48bf2dfcc --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7449-crypto-raw-keys.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7449-crypto-raw-keys.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7450-crypto-x25519.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7450-crypto-x25519.mjs.json new file mode 100644 index 000000000..f156f7530 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7450-crypto-x25519.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7450-crypto-x25519.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7451-crypto-diffie-hellman.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7451-crypto-diffie-hellman.ts.json new file mode 100644 index 000000000..593054dd5 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7451-crypto-diffie-hellman.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7451-crypto-diffie-hellman.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7452-crypto-diffie-hellman-errors.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7452-crypto-diffie-hellman-errors.mjs.json new file mode 100644 index 000000000..7cb78758b --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7452-crypto-diffie-hellman-errors.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7452-crypto-diffie-hellman-errors.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7453-untyped-overload-results.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7453-untyped-overload-results.cjs.json new file mode 100644 index 000000000..9ba935c1e --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7453-untyped-overload-results.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7453-untyped-overload-results.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7527-buffer-from-array-buffer.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7527-buffer-from-array-buffer.ts.json new file mode 100644 index 000000000..577923b2e --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7527-buffer-from-array-buffer.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7527-buffer-from-array-buffer.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7528-buffer-from-array-buffer.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7528-buffer-from-array-buffer.cjs.json new file mode 100644 index 000000000..6ef4e3917 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7528-buffer-from-array-buffer.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7528-buffer-from-array-buffer.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7529-buffer-constructor.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7529-buffer-constructor.cjs.json new file mode 100644 index 000000000..5b199cd4c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7529-buffer-constructor.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7529-buffer-constructor.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7530-buffer-constructor.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7530-buffer-constructor.ts.json new file mode 100644 index 000000000..1927980cc --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7530-buffer-constructor.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7530-buffer-constructor.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7531-buffer-constructor-dependency/main.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7531-buffer-constructor-dependency/main.cjs.json new file mode 100644 index 000000000..b760b018a --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7531-buffer-constructor-dependency/main.cjs.json @@ -0,0 +1,18 @@ +{ + "order": [ + "/tests/corpus/7531-buffer-constructor-dependency/main.cjs" + ], + "diags": [ + { + "code": "SC1010", + "message": "the 'legacy-buf' module is not supported yet", + "loc": { + "file": "/tests/corpus/7531-buffer-constructor-dependency/main.cjs", + "start": 362, + "end": 392 + }, + "milestone": "M4", + "hint": "relative imports (./file, ../dir/file), package.json-mediated project imports (#alias via the imports field, self-name references via exports), statically compiled npm packages, and the built-in fs, fs/promises, path, os, url, crypto, zlib, child_process, net, http, tls, https, http2, dgram, dns, dns/promises, util, util/types, string_decoder, querystring, readline, events, stream, stream/promises, stream/consumers, stream/web, buffer, assert, assert/strict, worker_threads, cluster, tty, async_hooks, timers, timers/promises, diagnostics_channel, perf_hooks, module, v8, and console modules (bare or node:-prefixed) and node:test (node:-prefixed only, like in Node) are supported" + } + ] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7532-buffer-constructor-check-limit/main.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7532-buffer-constructor-check-limit/main.cjs.json new file mode 100644 index 000000000..e2f78ad2e --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7532-buffer-constructor-check-limit/main.cjs.json @@ -0,0 +1,18 @@ +{ + "order": [ + "/tests/corpus/7532-buffer-constructor-check-limit/main.cjs" + ], + "diags": [ + { + "code": "SC1010", + "message": "the 'legacy-buf' module is not supported yet", + "loc": { + "file": "/tests/corpus/7532-buffer-constructor-check-limit/main.cjs", + "start": 279, + "end": 309 + }, + "milestone": "M4", + "hint": "relative imports (./file, ../dir/file), package.json-mediated project imports (#alias via the imports field, self-name references via exports), statically compiled npm packages, and the built-in fs, fs/promises, path, os, url, crypto, zlib, child_process, net, http, tls, https, http2, dgram, dns, dns/promises, util, util/types, string_decoder, querystring, readline, events, stream, stream/promises, stream/consumers, stream/web, buffer, assert, assert/strict, worker_threads, cluster, tty, async_hooks, timers, timers/promises, diagnostics_channel, perf_hooks, module, v8, and console modules (bare or node:-prefixed) and node:test (node:-prefixed only, like in Node) are supported" + } + ] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7533-buffer-value.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7533-buffer-value.cjs.json new file mode 100644 index 000000000..de7123599 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7533-buffer-value.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7533-buffer-value.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7534-buffer-prototype-members.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7534-buffer-prototype-members.cjs.json new file mode 100644 index 000000000..03858cfce --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7534-buffer-prototype-members.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7534-buffer-prototype-members.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7535-inspect-function-properties.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7535-inspect-function-properties.cjs.json new file mode 100644 index 000000000..9831d7def --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7535-inspect-function-properties.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7535-inspect-function-properties.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7580-int32-arrays-across-calls.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7580-int32-arrays-across-calls.ts.json new file mode 100644 index 000000000..33282756c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7580-int32-arrays-across-calls.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7580-int32-arrays-across-calls.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7623-key-order-javascript-built-records/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7623-key-order-javascript-built-records/main.ts.json new file mode 100644 index 000000000..c3c2aa5b0 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7623-key-order-javascript-built-records/main.ts.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/7623-key-order-javascript-built-records/lib.js", + "/tests/corpus/7623-key-order-javascript-built-records/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7624-key-order-boxed-callback-records/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7624-key-order-boxed-callback-records/main.ts.json new file mode 100644 index 000000000..1a617ef56 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7624-key-order-boxed-callback-records/main.ts.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/7624-key-order-boxed-callback-records/lib.js", + "/tests/corpus/7624-key-order-boxed-callback-records/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7642-element-assignment-order.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7642-element-assignment-order.ts.json new file mode 100644 index 000000000..17369f496 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7642-element-assignment-order.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7642-element-assignment-order.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7660-pointer-union-forwarded-arguments.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7660-pointer-union-forwarded-arguments.ts.json new file mode 100644 index 000000000..8f317668f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7660-pointer-union-forwarded-arguments.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7660-pointer-union-forwarded-arguments.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7663-pointer-union-stored-payload.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7663-pointer-union-stored-payload.ts.json new file mode 100644 index 000000000..51998f0eb --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7663-pointer-union-stored-payload.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7663-pointer-union-stored-payload.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7680-typed-array-search.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7680-typed-array-search.ts.json new file mode 100644 index 000000000..d93d9afdc --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7680-typed-array-search.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7680-typed-array-search.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7681-typed-array-search-dynamic.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7681-typed-array-search-dynamic.mjs.json new file mode 100644 index 000000000..ab91940c0 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7681-typed-array-search-dynamic.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7681-typed-array-search-dynamic.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7682-constructor-function-globals.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7682-constructor-function-globals.js.json new file mode 100644 index 000000000..26d05338c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7682-constructor-function-globals.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7682-constructor-function-globals.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7713-buffer-numeric-untyped-arguments.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7713-buffer-numeric-untyped-arguments.cjs.json new file mode 100644 index 000000000..d0a4957c5 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7713-buffer-numeric-untyped-arguments.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7713-buffer-numeric-untyped-arguments.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7714-zlib-brotli-compress.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7714-zlib-brotli-compress.cjs.json new file mode 100644 index 000000000..fb8e2d1d9 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7714-zlib-brotli-compress.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7714-zlib-brotli-compress.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7715-zlib-brotli-decompress.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7715-zlib-brotli-decompress.cjs.json new file mode 100644 index 000000000..4a40657b1 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7715-zlib-brotli-decompress.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7715-zlib-brotli-decompress.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7716-zlib-brotli.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7716-zlib-brotli.ts.json new file mode 100644 index 000000000..b410a8230 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7716-zlib-brotli.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7716-zlib-brotli.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7736-key-order-replaceable-method-result/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7736-key-order-replaceable-method-result/main.ts.json new file mode 100644 index 000000000..006d4a8c7 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7736-key-order-replaceable-method-result/main.ts.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/7736-key-order-replaceable-method-result/registry.js", + "/tests/corpus/7736-key-order-replaceable-method-result/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7737-key-order-self-spread.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7737-key-order-self-spread.ts.json new file mode 100644 index 000000000..97f2ca7bf --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7737-key-order-self-spread.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7737-key-order-self-spread.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7742-response-clone-tee.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7742-response-clone-tee.ts.json new file mode 100644 index 000000000..a62bef2d0 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7742-response-clone-tee.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7742-response-clone-tee.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7743-transform-stream-identity.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7743-transform-stream-identity.ts.json new file mode 100644 index 000000000..0cfff542b --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7743-transform-stream-identity.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7743-transform-stream-identity.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7745-typed-array-unions.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7745-typed-array-unions.ts.json new file mode 100644 index 000000000..9c770f8dd --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7745-typed-array-unions.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7745-typed-array-unions.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7746-key-order-iterable-iterator.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7746-key-order-iterable-iterator.ts.json new file mode 100644 index 000000000..821fa4f21 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7746-key-order-iterable-iterator.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7746-key-order-iterable-iterator.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7747-text-codec-untyped-slot.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7747-text-codec-untyped-slot.js.json new file mode 100644 index 000000000..23efb7db6 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7747-text-codec-untyped-slot.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7747-text-codec-untyped-slot.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7747-typed-array-class-values.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7747-typed-array-class-values.ts.json new file mode 100644 index 000000000..a18e9e7df --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7747-typed-array-class-values.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7747-typed-array-class-values.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7748-npm-any-bytes-receiver/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7748-npm-any-bytes-receiver/main.ts.json new file mode 100644 index 000000000..89db88211 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7748-npm-any-bytes-receiver/main.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7748-npm-any-bytes-receiver/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7748-typed-array-number-array-unions.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7748-typed-array-number-array-unions.ts.json new file mode 100644 index 000000000..783fb1435 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7748-typed-array-number-array-unions.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7748-typed-array-number-array-unions.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7749-text-codec-unknown-roundtrip.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7749-text-codec-unknown-roundtrip.ts.json new file mode 100644 index 000000000..dd9f068f3 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7749-text-codec-unknown-roundtrip.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7749-text-codec-unknown-roundtrip.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7749-typed-array-element-searches.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7749-typed-array-element-searches.ts.json new file mode 100644 index 000000000..8a1372cea --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7749-typed-array-element-searches.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7749-typed-array-element-searches.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7750-key-order-javascript-spread-sources/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7750-key-order-javascript-spread-sources/main.ts.json new file mode 100644 index 000000000..19f1ba620 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7750-key-order-javascript-spread-sources/main.ts.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/7750-key-order-javascript-spread-sources/lib.js", + "/tests/corpus/7750-key-order-javascript-spread-sources/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7750-typed-array-union-inspect.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7750-typed-array-union-inspect.ts.json new file mode 100644 index 000000000..26cc31982 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7750-typed-array-union-inspect.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7750-typed-array-union-inspect.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7751-class-well-known-symbol-fields.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7751-class-well-known-symbol-fields.ts.json new file mode 100644 index 000000000..2985bdf05 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7751-class-well-known-symbol-fields.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7751-class-well-known-symbol-fields.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7752-typed-array-dynamic-methods.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7752-typed-array-dynamic-methods.js.json new file mode 100644 index 000000000..78c9633d6 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7752-typed-array-dynamic-methods.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7752-typed-array-dynamic-methods.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7790-js-class-integrity-layouts.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7790-js-class-integrity-layouts.js.json new file mode 100644 index 000000000..526b86044 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7790-js-class-integrity-layouts.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7790-js-class-integrity-layouts.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7791-js-class-accessor-layouts.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7791-js-class-accessor-layouts.js.json new file mode 100644 index 000000000..5350539f0 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7791-js-class-accessor-layouts.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7791-js-class-accessor-layouts.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7792-untyped-property-reads-mixed-receivers.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7792-untyped-property-reads-mixed-receivers.js.json new file mode 100644 index 000000000..1b28b3fbe --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7792-untyped-property-reads-mixed-receivers.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7792-untyped-property-reads-mixed-receivers.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7793-untyped-key-writes.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7793-untyped-key-writes.js.json new file mode 100644 index 000000000..b887e0fdb --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7793-untyped-key-writes.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7793-untyped-key-writes.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7794-untyped-array-iteration.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7794-untyped-array-iteration.js.json new file mode 100644 index 000000000..5ffe60f53 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7794-untyped-array-iteration.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7794-untyped-array-iteration.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7795-untyped-global-constructors.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7795-untyped-global-constructors.js.json new file mode 100644 index 000000000..48eb29e33 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7795-untyped-global-constructors.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7795-untyped-global-constructors.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7796-boxed-subclass-identity.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7796-boxed-subclass-identity.js.json new file mode 100644 index 000000000..37f785cc2 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7796-boxed-subclass-identity.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7796-boxed-subclass-identity.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7940-typed-array-prototype-set.mjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7940-typed-array-prototype-set.mjs.json new file mode 100644 index 000000000..3c59407b3 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7940-typed-array-prototype-set.mjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7940-typed-array-prototype-set.mjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7941-typed-array-prototype-set.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7941-typed-array-prototype-set.ts.json new file mode 100644 index 000000000..8fbec6a90 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7941-typed-array-prototype-set.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7941-typed-array-prototype-set.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7942-dictionary-store-method-calls.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7942-dictionary-store-method-calls.ts.json new file mode 100644 index 000000000..f04478490 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7942-dictionary-store-method-calls.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7942-dictionary-store-method-calls.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7981-typed-array-paired-loops.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7981-typed-array-paired-loops.ts.json new file mode 100644 index 000000000..71fa483a8 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7981-typed-array-paired-loops.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7981-typed-array-paired-loops.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7982-integer-view-compares.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7982-integer-view-compares.ts.json new file mode 100644 index 000000000..94919aab0 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7982-integer-view-compares.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7982-integer-view-compares.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7991-unknown-class-union-casts.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7991-unknown-class-union-casts.ts.json new file mode 100644 index 000000000..68185e837 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/7991-unknown-class-union-casts.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/7991-unknown-class-union-casts.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8100-typed-array-prototype-set-data-view.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8100-typed-array-prototype-set-data-view.ts.json new file mode 100644 index 000000000..916caae33 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8100-typed-array-prototype-set-data-view.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8100-typed-array-prototype-set-data-view.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8101-url-host-and-path-parsing.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8101-url-host-and-path-parsing.js.json new file mode 100644 index 000000000..d38f437bc --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8101-url-host-and-path-parsing.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8101-url-host-and-path-parsing.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8102-url-path-to-file-url-unc-hosts.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8102-url-path-to-file-url-unc-hosts.js.json new file mode 100644 index 000000000..987c72578 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8102-url-path-to-file-url-unc-hosts.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8102-url-path-to-file-url-unc-hosts.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8103-url-inspect.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8103-url-inspect.ts.json new file mode 100644 index 000000000..06632e9e7 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8103-url-inspect.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8103-url-inspect.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8104-url-to-http-options.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8104-url-to-http-options.ts.json new file mode 100644 index 000000000..aedf0a20c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8104-url-to-http-options.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8104-url-to-http-options.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8110-builtin-prototype-own-members.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8110-builtin-prototype-own-members.js.json new file mode 100644 index 000000000..033847082 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8110-builtin-prototype-own-members.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8110-builtin-prototype-own-members.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8111-plain-object-check-unknown.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8111-plain-object-check-unknown.ts.json new file mode 100644 index 000000000..2986186a2 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8111-plain-object-check-unknown.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8111-plain-object-check-unknown.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8112-error-factory-own-key-order.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8112-error-factory-own-key-order.js.json new file mode 100644 index 000000000..4b64d1d75 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8112-error-factory-own-key-order.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8112-error-factory-own-key-order.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8113-error-class-field-before-name.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8113-error-class-field-before-name.js.json new file mode 100644 index 000000000..ddf5981f0 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8113-error-class-field-before-name.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8113-error-class-field-before-name.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8115-error-prototype-identity.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8115-error-prototype-identity.js.json new file mode 100644 index 000000000..f4a6f13f0 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8115-error-prototype-identity.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8115-error-prototype-identity.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8129-brotli-long-param-name.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8129-brotli-long-param-name.cjs.json new file mode 100644 index 000000000..33ef96201 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8129-brotli-long-param-name.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8129-brotli-long-param-name.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8160-assert-expected-from-fields.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8160-assert-expected-from-fields.cjs.json new file mode 100644 index 000000000..fcaacbbee --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8160-assert-expected-from-fields.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8160-assert-expected-from-fields.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8180-pow2-quotients.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8180-pow2-quotients.ts.json new file mode 100644 index 000000000..58c36cf5d --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8180-pow2-quotients.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/8180-pow2-quotients.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8213-cycle-star-reexport/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8213-cycle-star-reexport/main.ts.json new file mode 100644 index 000000000..c22b59997 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/8213-cycle-star-reexport/main.ts.json @@ -0,0 +1,9 @@ +{ + "order": [ + "/tests/corpus/8213-cycle-star-reexport/services.ts", + "/tests/corpus/8213-cycle-star-reexport/runtime.ts", + "/tests/corpus/8213-cycle-star-reexport/sdk.ts", + "/tests/corpus/8213-cycle-star-reexport/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/class-brand-probe-keys.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/class-brand-probe-keys.ts.json new file mode 100644 index 000000000..fd60e3ff9 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/class-brand-probe-keys.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/class-brand-probe-keys.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/fetch-headers-copy-cache.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/fetch-headers-copy-cache.ts.json new file mode 100644 index 000000000..bd69651b1 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/fetch-headers-copy-cache.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/fetch-headers-copy-cache.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/split-cjs-require-binding/main.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/split-cjs-require-binding/main.cjs.json new file mode 100644 index 000000000..60b9b0736 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/split-cjs-require-binding/main.cjs.json @@ -0,0 +1,12 @@ +{ + "order": [ + "/tests/corpus/split-cjs-require-binding/lib.cjs", + "/tests/corpus/split-cjs-require-binding/whole.cjs", + "/tests/corpus/split-cjs-require-binding/reexport.cjs", + "/tests/corpus/split-cjs-require-binding/reexport-extra.cjs", + "/tests/corpus/split-cjs-require-binding/chain.cjs", + "/tests/corpus/split-cjs-require-binding/chain-wrap.cjs", + "/tests/corpus/split-cjs-require-binding/main.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/split-cjs-require-cycle/main.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/split-cjs-require-cycle/main.cjs.json new file mode 100644 index 000000000..0ea3366d3 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/split-cjs-require-cycle/main.cjs.json @@ -0,0 +1,8 @@ +{ + "order": [ + "/tests/corpus/split-cjs-require-cycle/odd.cjs", + "/tests/corpus/split-cjs-require-cycle/even.cjs", + "/tests/corpus/split-cjs-require-cycle/main.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/string-last-index-of-ascii.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/string-last-index-of-ascii.ts.json new file mode 100644 index 000000000..1d3107cd5 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/string-last-index-of-ascii.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/string-last-index-of-ascii.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/typed-array-masked-index-stores.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/typed-array-masked-index-stores.ts.json new file mode 100644 index 000000000..dde5a96fa --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/typed-array-masked-index-stores.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/typed-array-masked-index-stores.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/any-destructure-unspelled-keys.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/any-destructure-unspelled-keys.ts.json new file mode 100644 index 000000000..f38d71f47 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/any-destructure-unspelled-keys.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/diagnostics/any-destructure-unspelled-keys.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/date-union-fences.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/date-union-fences.ts.json deleted file mode 100644 index cf6ad7398..000000000 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/date-union-fences.ts.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "order": [ - "/tests/diagnostics/date-union-fences.ts" - ], - "diags": [] -} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/globalthis-buffer-alias-values.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/globalthis-buffer-alias-values.ts.json new file mode 100644 index 000000000..4f6fe0c8c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/globalthis-buffer-alias-values.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/diagnostics/globalthis-buffer-alias-values.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/split-cjs-require-cycle/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/split-cjs-require-cycle/main.js.json new file mode 100644 index 000000000..90d4a2353 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/split-cjs-require-cycle/main.js.json @@ -0,0 +1,19 @@ +{ + "order": [ + "/tests/diagnostics/split-cjs-require-cycle/b.js", + "/tests/diagnostics/split-cjs-require-cycle/main.js" + ], + "diags": [ + { + "code": "SC1016", + "message": "circular imports (/tests/diagnostics/split-cjs-require-cycle/main.js → /tests/diagnostics/split-cjs-require-cycle/b.js → /tests/diagnostics/split-cjs-require-cycle/main.js; the cycle closes through a require() edge: /tests/diagnostics/split-cjs-require-cycle/main.js uses its exports at /tests/diagnostics/split-cjs-require-cycle/main.js:10 outside the export statements that precede its first require, so a module of the cycle can observe them before they settle) are not supported yet", + "loc": { + "file": "/tests/diagnostics/split-cjs-require-cycle/main.js", + "start": 0, + "end": 0 + }, + "milestone": "later", + "hint": "ES-module cycles of let/const/function bindings compile as-is; rewrite the named construct (a named import instead of a namespace, a const instead of a default-export expression or var, a class declared before the code that can reach it) or break the named edge" + } + ] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/split-cjs-require-early/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/split-cjs-require-early/main.js.json new file mode 100644 index 000000000..acb338494 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/split-cjs-require-early/main.js.json @@ -0,0 +1,27 @@ +{ + "order": [ + "/tests/diagnostics/split-cjs-require-early/main.js" + ], + "diags": [ + { + "code": "SC1013", + "message": "require() binding 'lib' assigned below code that can reach it (Node reads undefined before the assignment runs, which is not modeled — move the assignment above the code that can run first) is not supported yet", + "loc": { + "file": "/tests/diagnostics/split-cjs-require-early/main.js", + "start": 396, + "end": 415 + }, + "milestone": "later" + }, + { + "code": "SC1013", + "message": "require() binding 'wrapped' assigned below code that can reach it (Node reads undefined before the assignment runs, which is not modeled — move the assignment above the code that can run first) is not supported yet", + "loc": { + "file": "/tests/diagnostics/split-cjs-require-early/main.js", + "start": 530, + "end": 549 + }, + "milestone": "later" + } + ] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/diffie-hellman.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/diffie-hellman.ts.json new file mode 100644 index 000000000..fb8a7a740 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/diffie-hellman.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/node-types/diffie-hellman.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/fs-url-fallible.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/fs-url-fallible.ts.json new file mode 100644 index 000000000..e4967ea93 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/fs-url-fallible.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/node-types/fs-url-fallible.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/fs-url-path-fences.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/fs-url-path-fences.ts.json deleted file mode 100644 index 7b63a5564..000000000 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/fs-url-path-fences.ts.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "order": [ - "/tests/fixtures/node-types/fs-url-path-fences.ts" - ], - "diags": [] -} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/hono-zod-default-hook/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/hono-zod-default-hook/main.ts.json new file mode 100644 index 000000000..2310d6e0f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/hono-zod-default-hook/main.ts.json @@ -0,0 +1,61 @@ +{ + "order": [ + "/tests/fixtures/npm/cases/hono-zod-default-hook/main.ts" + ], + "diags": [ + { + "code": "SC0001", + "message": "Cannot find name 'WebSocket'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 1398, + "end": 1407 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'WebSocket'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 1413, + "end": 1422 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'WebSocket'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 3716, + "end": 3725 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'FormData'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 4798, + "end": 4806 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'FormData'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/request.d.ts", + "start": 549, + "end": 557 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'FormData'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/request.d.ts", + "start": 5572, + "end": 5580 + } + } + ] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/hono-zod-validator/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/hono-zod-validator/main.ts.json new file mode 100644 index 000000000..72c2578e6 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/hono-zod-validator/main.ts.json @@ -0,0 +1,61 @@ +{ + "order": [ + "/tests/fixtures/npm/cases/hono-zod-validator/main.ts" + ], + "diags": [ + { + "code": "SC0001", + "message": "Cannot find name 'WebSocket'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 1398, + "end": 1407 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'WebSocket'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 1413, + "end": 1422 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'WebSocket'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 3716, + "end": 3725 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'FormData'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts", + "start": 4798, + "end": 4806 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'FormData'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/request.d.ts", + "start": 549, + "end": 557 + } + }, + { + "code": "SC0001", + "message": "Cannot find name 'FormData'.", + "loc": { + "file": "/tests/fixtures/npm/node_modules/hono/dist/types/request.d.ts", + "start": 5572, + "end": 5580 + } + } + ] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/zod4-boundary/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/zod4-boundary/main.ts.json new file mode 100644 index 000000000..0d656e939 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/zod4-boundary/main.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/npm/cases/zod4-boundary/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/zod4-record/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/zod4-record/main.ts.json new file mode 100644 index 000000000..8c7700ac8 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/npm/cases/zod4-record/main.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/npm/cases/zod4-record/main.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/typed-array-union-emission.test.ts b/packages/compiler/test/typed-array-union-emission.test.ts new file mode 100644 index 000000000..e9bb22103 --- /dev/null +++ b/packages/compiler/test/typed-array-union-emission.test.ts @@ -0,0 +1,116 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile } from "../src/index.js"; + +async function emitLlvm(source: string): Promise { + const dir = await mkdtemp(join(tmpdir(), "scriptc-typed-array-unions-")); + try { + const entry = join(dir, "main.ts"); + const outPath = join(dir, "main.ll"); + await writeFile(entry, source); + const result = await compile(entry, { outDir: dir, outPath, outputKind: "llvm" }); + expect(result.ok, JSON.stringify(result.diagnostics)).toBe(true); + return await readFile(outPath, "utf8"); + } finally { + await rm(dir, { recursive: true, force: true }); + } +} + +function body(llvm: string, name: string): string { + return new RegExp(`^define [^@]*@sc_b?f_${name}(\\.i32)?\\([^]*?^}`, "m").exec(llvm)?.[0] ?? ""; +} + +const source = ` +type UintArray = Uint8Array | Uint16Array | Uint32Array; +const pick = (max: number, n: number): UintArray => + max <= 256 ? new Uint8Array(n) : max <= 65536 ? new Uint16Array(n) : new Uint32Array(n); +function total(xs: UintArray): number { + let s = 0; + for (let i = 0; i < xs.length; i++) s += xs[i]!; + return s; +} +function first(max: number): number { + return pick(max, 4)[0]!; +} +const xs = pick(Number(process.argv.length) * 100000, 8); +for (let i = 0; i < xs.length; i++) xs[i] = i; +console.log(total(xs), first(10)); +`; + +test("element reads of a union of typed arrays load each arm's own element kind in place", async () => { + const llvm = await emitLlvm(source); + const total = body(llvm, "total"); + // One load per arm, each of its element width, behind the union's tag. + for (const width of ["i8", "i16", "i32"]) expect(total).toMatch(new RegExp(`load ${width}, ptr`)); + expect(total).not.toContain("@sc_bf__x25_union_getNumber"); + // The loop over the union's length keeps an integer counter. + expect(total).toMatch(/alloca i64/); +}); + +test("a typed-array union read through a computed receiver goes through one helper", async () => { + const llvm = await emitLlvm(source); + // The receiver is a call: evaluated once, then dispatched by the helper. + expect(body(llvm, "first")).toContain("@sc_bf__x25_union_getNumber"); +}); + +const stores = ` +type Wide = Uint8Array | Uint16Array; +const pick = (wide: boolean): Wide => (wide ? new Uint16Array(4) : new Uint8Array(4)); +function fill(xs: Wide): void { + for (let i = 0; i < xs.length; i++) xs[i] = i + 1; +} +function rebind(): void { + let u = pick(false); + let i = 0; + const swap = (): number => { + u = pick(true); + i = 2; + return 3; + }; + u[i] = swap(); + console.log(u, i); +} +fill(pick(process.argv.length > 9)); +rebind(); +`; + +test("a typed-array union store runs in place unless its value can rebind the receiver or index", async () => { + const llvm = await emitLlvm(stores); + expect(body(llvm, "fill")).not.toContain("@sc_bf__x25_union_idxSet"); + // The call may assign the captured receiver and index, which the store + // read before it: the helper evaluates them first. + expect(body(llvm, "rebind")).toContain("@sc_bf__x25_union_idxSet"); +}); + +test("typed-array methods on a union with a Buffer arm need a narrowing first", async () => { + const dir = await mkdtemp(join(tmpdir(), "scriptc-typed-array-unions-")); + try { + const entry = join(dir, "main.ts"); + // The Buffer arm is found by its type, also through an alias name. + for (const [declaration, method] of [ + ["const u: Buffer | Uint16Array", "slice(1)"], + ["type Bytes = Uint16Array | Buffer;\nconst u: Bytes", "slice(1)"], + ["type Bytes = Uint16Array | Buffer;\nconst u: Bytes", "fill(1)"], + ["type Bytes = Uint16Array | Buffer;\nconst u: Bytes", "indexOf(1)"], + ]) { + await writeFile( + entry, + `import { Buffer } from "node:buffer"; +${declaration} = process.argv.length > 9 ? new Uint16Array(2) : Buffer.alloc(2); +console.log(u.${method}); +`, + ); + const result = await compile(entry, { + outDir: dir, + outPath: join(dir, "main.ll"), + outputKind: "llvm", + }); + expect(result.ok, `${declaration} ${method}`).toBe(false); + expect(JSON.stringify(result.diagnostics)).toContain(method.slice(0, method.indexOf("("))); + } + } finally { + await rm(dir, { recursive: true, force: true }); + } +}); diff --git a/packages/experimental/package.json b/packages/experimental/package.json index cfb4151a4..583a2bc58 100644 --- a/packages/experimental/package.json +++ b/packages/experimental/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/experimental", - "version": "0.4.2", + "version": "0.4.4", "description": "Experimental scriptc APIs, with Node.js implementations: lifetime regions for ProgramLifetime objects, intern tables shared between threads, and per-thread slots on shared objects", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/llvm-darwin-arm64/package.json b/packages/llvm-darwin-arm64/package.json index 615fe9d81..76d5fdb72 100644 --- a/packages/llvm-darwin-arm64/package.json +++ b/packages/llvm-darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/llvm-darwin-arm64", - "version": "0.4.2", + "version": "0.4.4", "description": "Pinned LLVM code-generation helper for scriptc on macOS arm64", "license": "Apache-2.0 WITH LLVM-exception", "homepage": "https://scriptc.dev", diff --git a/packages/llvm-darwin-x64/package.json b/packages/llvm-darwin-x64/package.json index fd6caf976..fbb755f4a 100644 --- a/packages/llvm-darwin-x64/package.json +++ b/packages/llvm-darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/llvm-darwin-x64", - "version": "0.4.2", + "version": "0.4.4", "description": "Pinned LLVM code-generation helper for scriptc on macOS x64", "license": "Apache-2.0 WITH LLVM-exception", "homepage": "https://scriptc.dev", diff --git a/packages/llvm-linux-arm64-gnu/package.json b/packages/llvm-linux-arm64-gnu/package.json index 7d66257f4..81fdc7d5b 100644 --- a/packages/llvm-linux-arm64-gnu/package.json +++ b/packages/llvm-linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/llvm-linux-arm64-gnu", - "version": "0.4.2", + "version": "0.4.4", "description": "Pinned LLVM code-generation helper for scriptc on Linux arm64", "license": "Apache-2.0 WITH LLVM-exception", "homepage": "https://scriptc.dev", diff --git a/packages/llvm-linux-arm64-musl/package.json b/packages/llvm-linux-arm64-musl/package.json index b3a01089d..8b3b2f185 100644 --- a/packages/llvm-linux-arm64-musl/package.json +++ b/packages/llvm-linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/llvm-linux-arm64-musl", - "version": "0.4.2", + "version": "0.4.4", "description": "Statically linked LLVM code-generation helper for scriptc on Linux arm64 musl", "license": "Apache-2.0 WITH LLVM-exception", "homepage": "https://scriptc.dev", diff --git a/packages/llvm-linux-x64-gnu/package.json b/packages/llvm-linux-x64-gnu/package.json index 0b3264ec7..79b7eba3f 100644 --- a/packages/llvm-linux-x64-gnu/package.json +++ b/packages/llvm-linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/llvm-linux-x64-gnu", - "version": "0.4.2", + "version": "0.4.4", "description": "Pinned LLVM code-generation helper for scriptc on Linux x64", "license": "Apache-2.0 WITH LLVM-exception", "homepage": "https://scriptc.dev", diff --git a/packages/llvm-linux-x64-musl/package.json b/packages/llvm-linux-x64-musl/package.json index 6c83d1cb9..5bffa5bef 100644 --- a/packages/llvm-linux-x64-musl/package.json +++ b/packages/llvm-linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/llvm-linux-x64-musl", - "version": "0.4.2", + "version": "0.4.4", "description": "Statically linked LLVM code-generation helper for scriptc on Linux x64 musl", "license": "Apache-2.0 WITH LLVM-exception", "homepage": "https://scriptc.dev", diff --git a/packages/llvm-win32-x64-msvc/package.json b/packages/llvm-win32-x64-msvc/package.json index 9fb682f0f..14c747a4a 100644 --- a/packages/llvm-win32-x64-msvc/package.json +++ b/packages/llvm-win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/llvm-win32-x64-msvc", - "version": "0.4.2", + "version": "0.4.4", "description": "Pinned LLVM code-generation helper for scriptc on Windows x64", "license": "Apache-2.0 WITH LLVM-exception", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-android-arm64/package.json b/packages/runtime-android-arm64/package.json index 362fbc572..1dfa1b177 100644 --- a/packages/runtime-android-arm64/package.json +++ b/packages/runtime-android-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-android-arm64", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc library runtime pack for Android arm64", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-darwin-arm64/package.json b/packages/runtime-darwin-arm64/package.json index a4176ed60..32463357f 100644 --- a/packages/runtime-darwin-arm64/package.json +++ b/packages/runtime-darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-darwin-arm64", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for macOS arm64", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs b/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs index 7b2d3e115..197adc516 100644 --- a/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs +++ b/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs @@ -14,6 +14,7 @@ import { */ const any = (...features) => ({ any: features }); +const all = (...features) => ({ all: features }); // ld64 dead-strips Mach-O symbol subsections without an ELF-style compile // flag. Keep this checked-in metadata alongside the matrix so pack build and @@ -43,6 +44,7 @@ export const RUNTIME_PACK_MATRIX = withLibraryRuntimeFlavors( { id: "quickjs", predicate: false }, { id: "libregexp", predicate: "regex" }, { id: "zlib", predicate: "zlibEffective" }, + { id: "brotli", predicate: all("zlib", "brotli") }, { id: "mbedtls", predicate: any("tlsEffective", "cipher", "ecdh", "dh", "pki") }, ], system_libraries: [{ name: "System", predicate: true }], diff --git a/packages/runtime-darwin-arm64/scripts/build.mjs b/packages/runtime-darwin-arm64/scripts/build.mjs index e492c36b4..b6b855c05 100644 --- a/packages/runtime-darwin-arm64/scripts/build.mjs +++ b/packages/runtime-darwin-arm64/scripts/build.mjs @@ -72,6 +72,7 @@ async function build() { const quickjs = join(vendorRoot, "quickjs-ng"); const zlib = join(vendorRoot, "zlib"); const mbedtls = join(vendorRoot, "mbedtls"); + const brotli = join(vendorRoot, "brotli", "c"); const QJS_SOURCES = ["dtoa.c", "libregexp.c", "libunicode.c", "quickjs.c"]; const LRE_SOURCES = ["libregexp.c", "libunicode.c"]; const ZLIB_SOURCES = [ @@ -112,20 +113,23 @@ async function build() { return parallelMap(items, width, task); } + // Archive members are named by basename, so a source in a subdirectory + // (brotli's dec/ and enc/ share file names) is named after its path. + const objectName = (source) => source.replaceAll("/", "-").replace(/\.c$/, ".o"); async function archive(id, sources, sourceRoot, flags) { const root = join(stagedOutputRoot, "vendor", id); const objectRoot = join(root, "objects"); await parallel(sources, async (source) => { await compile( join(sourceRoot, source), - join(objectRoot, source.replace(/\.c$/, ".o")), + join(objectRoot, objectName(source)), unalignedVendorSources.has(source) ? flags.filter((flag) => !codeAlignment.includes(flag)) : flags, ); }); const output = join(root, `libscriptc-${id}.a`); - const objects = sources.map((source) => join(objectRoot, source.replace(/\.c$/, ".o"))); + const objects = sources.map((source) => join(objectRoot, objectName(source))); await createDeterministicArchive(archiver, output, objects); await rm(objectRoot, { recursive: true, force: true }); return { @@ -181,6 +185,7 @@ async function build() { ...(unit.source === "scr_zlib.c" || unit.source === "scr_fetch.c" ? ["-I", zlib] : []), + ...(unit.source === "scr_brotli.c" ? ["-I", join(brotli, "include")] : []), ]; const unitFlags = [ ...commonFlags, @@ -268,6 +273,17 @@ async function build() { const mbedtlsSources = (await readdir(join(mbedtls, "library"))) .filter((name) => !name.startsWith(".") && name.endsWith(".c")) .sort(); + const brotliSources = ( + await Promise.all( + ["common", "dec", "enc"].map(async (dir) => + (await readdir(join(brotli, dir))) + .filter((name) => !name.startsWith(".") && name.endsWith(".c")) + .map((name) => `${dir}/${name}`), + ), + ) + ) + .flat() + .sort(); const archives = [ await archive("quickjs", QJS_SOURCES, quickjs, [ "-target", @@ -301,6 +317,15 @@ async function build() { "-I", zlib, ]), + await archive("brotli", brotliSources, brotli, [ + "-target", + RUNTIME_PACK_MATRIX.target.llvm_triple, + "-std=c11", + "-Os", + ...codeAlignment, + "-I", + join(brotli, "include"), + ]), await archive("mbedtls", mbedtlsSources, join(mbedtls, "library"), [ "-target", RUNTIME_PACK_MATRIX.target.llvm_triple, @@ -325,6 +350,7 @@ async function build() { ], [join(vendorRoot, "unicode", "LICENSE"), "artifacts/licenses/unicode.txt", "Unicode-3.0"], [join(zlib, "LICENSE"), "artifacts/licenses/zlib.txt", "Zlib"], + [join(vendorRoot, "brotli", "LICENSE"), "artifacts/licenses/brotli.txt", "MIT"], [join(mbedtls, "LICENSE"), "artifacts/licenses/mbedtls.txt", "Apache-2.0"], ]; await Promise.all( diff --git a/packages/runtime-darwin-x64/package.json b/packages/runtime-darwin-x64/package.json index 91bc4182b..c0577b04c 100644 --- a/packages/runtime-darwin-x64/package.json +++ b/packages/runtime-darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-darwin-x64", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for macOS x64", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-ios-arm64/package.json b/packages/runtime-ios-arm64/package.json index 2fdd68897..9c2134280 100644 --- a/packages/runtime-ios-arm64/package.json +++ b/packages/runtime-ios-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-ios-arm64", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc library runtime pack for iOS arm64", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-ios-simulator-arm64/package.json b/packages/runtime-ios-simulator-arm64/package.json index cbdf96d1d..6b7563a6a 100644 --- a/packages/runtime-ios-simulator-arm64/package.json +++ b/packages/runtime-ios-simulator-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-ios-simulator-arm64", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc library runtime pack for iOS simulator arm64", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-linux-arm64-gnu/package.json b/packages/runtime-linux-arm64-gnu/package.json index e0d228925..f0ae56929 100644 --- a/packages/runtime-linux-arm64-gnu/package.json +++ b/packages/runtime-linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-linux-arm64-gnu", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for Linux arm64 glibc", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-linux-arm64-musl/package.json b/packages/runtime-linux-arm64-musl/package.json index 70eee223f..79fa8e7dd 100644 --- a/packages/runtime-linux-arm64-musl/package.json +++ b/packages/runtime-linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-linux-arm64-musl", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for Linux arm64 musl", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-linux-x64-gnu/package.json b/packages/runtime-linux-x64-gnu/package.json index 5822fd21d..45af8d2b7 100644 --- a/packages/runtime-linux-x64-gnu/package.json +++ b/packages/runtime-linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-linux-x64-gnu", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for Linux x64 glibc", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-linux-x64-musl/package.json b/packages/runtime-linux-x64-musl/package.json index 1db34d4a7..fe9d11bcd 100644 --- a/packages/runtime-linux-x64-musl/package.json +++ b/packages/runtime-linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-linux-x64-musl", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for Linux x64 musl", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-pack-common/runtime-pack-matrix.mjs b/packages/runtime-pack-common/runtime-pack-matrix.mjs index 89b6eaf19..600637ba6 100644 --- a/packages/runtime-pack-common/runtime-pack-matrix.mjs +++ b/packages/runtime-pack-common/runtime-pack-matrix.mjs @@ -83,6 +83,7 @@ export const OPTIONAL_RUNTIME_SOURCES = [ // Blob and File box their reads as dyn promises through scr_async_dyn.c. ["scr_blob.c", "dynAsync"], ["scr_zlib.c", "zlib"], + ["scr_brotli.c", all("zlib", "brotli")], ["scr_events.c", "events"], ["scr_readline.c", "events"], ["scr_readline_tty.c", all("events", "regex")], @@ -148,6 +149,8 @@ export const OPTIONAL_RUNTIME_SOURCES = [ ["scr_dns_promises.c", all("dgram", "dnsPromises")], // Sockets as untyped values run over the dgram unit. ["scr_dgram_dyn.c", all("dgram", "dgramDyn")], + // TextEncoder and TextDecoder records boxed as untyped values. + ["scr_text_codec_dyn.c", "textCodecDyn"], ["scr_watch.c", "watch"], ["scr_ffi_queue.c", "foreignFfi"], ["scr_test.c", "nodeTest"], @@ -225,6 +228,7 @@ export function createRuntimePackMatrix({ { id: "quickjs", predicate: false }, { id: "libregexp", predicate: "regex" }, { id: "zlib", predicate: "zlibEffective" }, + { id: "brotli", predicate: all("zlib", "brotli") }, { id: "mbedtls", predicate: any("tlsEffective", "cipher", "ecdh", "dh", "pki") }, ]; const matrix = withLibraryRuntimeFlavors( diff --git a/packages/runtime-pack-common/scripts/build.mjs b/packages/runtime-pack-common/scripts/build.mjs index e22f2a37c..d4794b7d1 100644 --- a/packages/runtime-pack-common/scripts/build.mjs +++ b/packages/runtime-pack-common/scripts/build.mjs @@ -85,6 +85,7 @@ async function build() { const quickjs = join(vendorRoot, "quickjs-ng"); const zlib = join(vendorRoot, "zlib"); const mbedtls = join(vendorRoot, "mbedtls"); + const brotli = join(vendorRoot, "brotli", "c"); const qjsSources = ["dtoa.c", "libregexp.c", "libunicode.c", "quickjs.c"]; const lreSources = ["libregexp.c", "libunicode.c"]; const zlibSources = [ @@ -131,6 +132,9 @@ async function build() { const width = Math.max(1, Math.min(8, availableParallelism())); return parallelMap(items, width, task); }; + // Archive members are named by basename, so a source in a subdirectory + // (brotli's dec/ and enc/ share file names) is named after its path. + const objectName = (source) => source.replaceAll("/", "-").replace(/\.c$/, ".o"); const archive = async (id, sources, sourceRoot, flags) => { process.stdout.write(`building ${packageManifest.name} ${id} archive\n`); const root = join(stagedOutputRoot, "vendor", id); @@ -138,7 +142,7 @@ async function build() { await parallel(sources, async (source) => compile( join(sourceRoot, source), - join(objectRoot, source.replace(/\.c$/, ".o")), + join(objectRoot, objectName(source)), unalignedVendorSources.has(source) ? flags.filter((flag) => !codeAlignment.includes(flag)) : flags, @@ -148,7 +152,7 @@ async function build() { await createDeterministicArchive( archiver, output, - sources.map((source) => join(objectRoot, source.replace(/\.c$/, ".o"))), + sources.map((source) => join(objectRoot, objectName(source))), archiverArgs, ); await rm(objectRoot, { recursive: true, force: true }); @@ -232,6 +236,7 @@ async function build() { ...(unit.source === "scr_zlib.c" || unit.source === "scr_fetch.c" ? ["-I", zlib] : []), + ...(unit.source === "scr_brotli.c" ? ["-I", join(brotli, "include")] : []), ]; // Worker variants link only into executables, whose own TLS block // holds every runtime thread-local: on ELF the initial-exec model @@ -334,6 +339,17 @@ async function build() { const mbedtlsSources = (await readdir(join(mbedtls, "library"))) .filter((name) => !name.startsWith(".") && name.endsWith(".c")) .sort(); + const brotliSources = ( + await Promise.all( + ["common", "dec", "enc"].map(async (dir) => + (await readdir(join(brotli, dir))) + .filter((name) => !name.startsWith(".") && name.endsWith(".c")) + .map((name) => `${dir}/${name}`), + ), + ) + ) + .flat() + .sort(); // Vendored QuickJS reads `clock_gettime` for its monotonic clock. WASI // exposes that declaration only when its process-clock emulation ABI is // selected, so vendor sources inherit the target's runtime defines too. @@ -388,6 +404,17 @@ async function build() { ]), ] : []), + ...(requestedArchives.has("brotli") + ? [ + await archive("brotli", brotliSources, brotli, [ + ...vendorTarget, + "-std=c11", + "-Os", + "-I", + join(brotli, "include"), + ]), + ] + : []), ...(requestedArchives.has("mbedtls") ? [ await archive("mbedtls", mbedtlsSources, join(mbedtls, "library"), [ @@ -417,6 +444,7 @@ async function build() { "BSD-3-Clause", ], [join(zlib, "LICENSE"), "artifacts/licenses/zlib.txt", "Zlib"], + [join(vendorRoot, "brotli", "LICENSE"), "artifacts/licenses/brotli.txt", "MIT"], [join(mbedtls, "LICENSE"), "artifacts/licenses/mbedtls.txt", "Apache-2.0"], ]; licensed.push([ diff --git a/packages/runtime-wasm32-wasi/package.json b/packages/runtime-wasm32-wasi/package.json index 5bbd23755..ac2f4a5e9 100644 --- a/packages/runtime-wasm32-wasi/package.json +++ b/packages/runtime-wasm32-wasi/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-wasm32-wasi", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for WASI Preview 1", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime-win32-x64-msvc/package.json b/packages/runtime-win32-x64-msvc/package.json index df4a9a109..9a5731da2 100644 --- a/packages/runtime-win32-x64-msvc/package.json +++ b/packages/runtime-win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime-win32-x64-msvc", - "version": "0.4.2", + "version": "0.4.4", "description": "Precompiled scriptc runtime pack for Windows x64 MSVC", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 9232266af..1d58b00e5 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/runtime", - "version": "0.4.2", + "version": "0.4.4", "description": "scriptc native runtime sources and vendored dependencies", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/runtime/src/scr_alloc.c b/packages/runtime/src/scr_alloc.c index 3a36f672e..a27df378a 100644 --- a/packages/runtime/src/scr_alloc.c +++ b/packages/runtime/src/scr_alloc.c @@ -567,7 +567,128 @@ static _Atomic(uintptr_t) scr_sa_heap_page; /* the page size, read with the mode * These loads run only on blocks too large for the small allocator, next to * a glibc malloc call: plain loads on x86-64, LDAR (or LDAPR) on arm64. */ +#if defined(SCR_WORKERS) && !defined(SCR_LIB) +#if defined(__aarch64__) +#include +#endif +/* glibc's per-thread heaps (worker executables; library archives run on + * their embedder's threads and leave its heaps alone). glibc serves a thread's + * blocks from a heap of its own: a 64 MiB mapping, aligned to its size, + * that it makes accessible with mprotect as it grows, a page-rounded step + * at a time, so every 2 MiB range is first touched while part of it is + * still inaccessible and gets small pages. Once a thread has allocated + * SCR_SA_THREAD_HUGE_AFTER bytes of large blocks (threads with few keep + * small pages), the heap of each large block it gets is made accessible + * whole and advised huge pages, once per heap; glibc's calloc still clears + * only what lies below its own high-water mark, so this touches nothing. + * glibc's mmap and trim thresholds are fixed at SCR_SA_THREAD_MMAP_THRESHOLD + * and SCR_SA_THREAD_TRIM_THRESHOLD: lower than the maxima glibc's adaptive + * thresholds reach, so that blocks of 4 MiB and more get mappings of their + * own and a heap's free top goes back once it passes 8 MiB, while huge + * pages keep resident memory that a heap would otherwise leave untouched. + * The brk heap policy below sets its own (higher) values when the main heap + * grows past SCR_SA_HEAP_HUGE_AFTER, and those take precedence. The + * policy is off unless THP is in madvise mode, when the glibc.malloc.hugetlb + * tunable is set (glibc then advises its heaps itself, or sizes them by + * hugetlbfs pages), when the system forbids overcommit (making a heap + * accessible would commit all of it), when malloc is not glibc's (a + * sanitizer or a preloaded allocator: blocks have no glibc chunk header), + * and when memory tagging is on (arm64 MTE, the glibc.mem.tagging tunable): + * a chunk header then carries another tag than the block's pointer, and + * reading it through that pointer faults. + * A heap is only advised when /proc/self/maps shows the anonymous mappings + * of exactly its range. */ +#define SCR_SA_GLIBC_HEAP ((uintptr_t)64 << 20) /* glibc's heap size and alignment */ +#define SCR_SA_THREAD_HUGE_AFTER ((size_t)16 << 20) +#define SCR_SA_THREAD_MMAP_THRESHOLD (4 << 20) +#define SCR_SA_THREAD_TRIM_THRESHOLD (8 << 20) +static _Thread_local size_t scr_sa_thread_large_bytes; +static _Thread_local uintptr_t scr_sa_thread_heap; /* the heap this thread advised last */ +static _Atomic(int) scr_sa_thread_heaps; /* 0 undecided, 1 advised, 2 off */ +extern void *__libc_malloc(size_t); + +/* Whether glibc tags heap memory: the glibc.mem.tagging tunable is set to a + * nonzero value, or (arm64) the kernel checks tags for this process. */ +static bool scr_sa_mem_tagging(const char *tunables) { + const char *t = tunables ? strstr(tunables, "glibc.mem.tagging=") : NULL; + if (t && t[sizeof "glibc.mem.tagging=" - 1] != '0') return true; +#if defined(__aarch64__) + /* PR_GET_TAGGED_ADDR_CTRL (56); PR_MTE_TCF_MASK is bits 1-2. A kernel + * without the call returns -1: no tag checks. */ + long ctrl = prctl(56, 0, 0, 0, 0); + if (ctrl > 0 && (ctrl & (3L << 1))) return true; +#endif + return false; +} + +/* Whether [heap, heap + SCR_SA_GLIBC_HEAP) is covered exactly by anonymous + * mappings, as a glibc heap is (its accessible part and the rest). */ +static bool scr_sa_glibc_heap_mapped(uintptr_t heap) { + FILE *f = fopen("/proc/self/maps", "r"); + if (!f) return false; + char line[512]; + uintptr_t next = heap, end = heap + SCR_SA_GLIBC_HEAP; + while (next < end && fgets(line, sizeof line, f)) { + unsigned long lo, hi, inode; + char path[2] = {0}; + if (sscanf(line, "%lx-%lx %*s %*s %*s %lu %1s", &lo, &hi, &inode, path) < 3) continue; + if (hi <= next) continue; + if (lo != next || inode != 0 || path[0]) break; + next = hi; + } + fclose(f); + return next == end; +} + +static void scr_sa_thread_heap_note(void *p, size_t n) { + if (scr_sa_thread_large_bytes < SCR_SA_THREAD_HUGE_AFTER) { + scr_sa_thread_large_bytes += n; + return; + } + int mode = atomic_load_explicit(&scr_sa_thread_heaps, memory_order_relaxed); + if (mode == 2) return; + if (mode == 0) { + int undecided = 0; + const char *tunables = getenv("GLIBC_TUNABLES"); + FILE *f = fopen("/proc/sys/vm/overcommit_memory", "r"); + int overcommit = f ? fgetc(f) : '0'; + if (f) fclose(f); + /* Loaded through volatile: the compiler takes two declared functions + * to have different addresses. */ + void *(*volatile used)(size_t) = malloc, *(*volatile own)(size_t) = __libc_malloc; + bool glibc_malloc = used == own; + mode = glibc_malloc && scr_sa_thp_mode() == 'm' && !(tunables && strstr(tunables, "glibc.malloc.hugetlb")) && + overcommit != '2' && !scr_sa_mem_tagging(tunables) + ? 1 + : 2; + if (atomic_compare_exchange_strong(&scr_sa_thread_heaps, &undecided, mode) && mode == 1 && + atomic_load_explicit(&scr_sa_heap_state, memory_order_relaxed) != 1) { + (void)mallopt(M_MMAP_THRESHOLD, SCR_SA_THREAD_MMAP_THRESHOLD); + (void)mallopt(M_TRIM_THRESHOLD, SCR_SA_THREAD_TRIM_THRESHOLD); + /* The brk heap policy marks itself on before it sets its values: if it + * did so meanwhile, its values are set again. */ + if (atomic_load(&scr_sa_heap_state) == 1) { + (void)mallopt(M_MMAP_THRESHOLD, SCR_SA_HEAP_MMAP_THRESHOLD); + (void)mallopt(M_TRIM_THRESHOLD, SCR_SA_HEAP_TRIM_THRESHOLD); + } + } + mode = atomic_load_explicit(&scr_sa_thread_heaps, memory_order_relaxed); + if (mode != 1) return; + } + /* glibc's chunk header: NON_MAIN_ARENA (4) set, IS_MMAPPED (2) clear. */ + if ((((const size_t *)p)[-1] & 6) != 4) return; + uintptr_t heap = (uintptr_t)p & ~(SCR_SA_GLIBC_HEAP - 1); + if (heap == scr_sa_thread_heap) return; + scr_sa_thread_heap = heap; + if (scr_sa_glibc_heap_mapped(heap) && mprotect((void *)heap, SCR_SA_GLIBC_HEAP, PROT_READ | PROT_WRITE) == 0) + (void)madvise((void *)heap, SCR_SA_GLIBC_HEAP, MADV_HUGEPAGE); +} +#endif + static void scr_sa_large_note(void *p, size_t n) { +#if defined(SCR_WORKERS) && !defined(SCR_LIB) + if (p) scr_sa_thread_heap_note(p, n); +#endif int state = atomic_load_explicit(&scr_sa_heap_state, memory_order_relaxed); if (state == 2 || !p) return; uintptr_t top = (uintptr_t)sbrk(0); diff --git a/packages/runtime/src/scr_alloc.test.c b/packages/runtime/src/scr_alloc.test.c index e20c0733b..dfdd9bf5c 100644 --- a/packages/runtime/src/scr_alloc.test.c +++ b/packages/runtime/src/scr_alloc.test.c @@ -343,6 +343,60 @@ static void *reclaim_arena(void *arg) { return NULL; } +#if defined(__linux__) && defined(__GLIBC__) +/* A thread whose large blocks pass 16 MiB gets them from a glibc heap of + * its own, which is then advised huge pages where THP is in madvise mode. */ +enum { HEAP_BLOCKS = 320, HEAP_BLOCK = 64 << 10 }; +static void *heap_blocks[HEAP_BLOCKS]; +static char heap_flags[256]; +static void *thread_heap_allocate(void *arg) { + (void)arg; + for (unsigned i = 0; i < HEAP_BLOCKS; i++) { + heap_blocks[i] = scr_mem_alloc(HEAP_BLOCK); + assert(heap_blocks[i] && !owned(heap_blocks[i])); + ((unsigned char *)heap_blocks[i])[0] = (unsigned char)i; + } + vm_flags(heap_blocks[HEAP_BLOCKS - 1], heap_flags, sizeof heap_flags); + for (unsigned i = 0; i < HEAP_BLOCKS; i++) { + assert(((unsigned char *)heap_blocks[i])[0] == (unsigned char)i); + scr_mem_free(heap_blocks[i]); + } + scr_sa_thread_park(); + return NULL; +} + +static void check_thread_heap_huge_pages(void) { + pthread_t t; + assert(pthread_create(&t, NULL, thread_heap_allocate, NULL) == 0); + assert(pthread_join(t, NULL) == 0); + char mode[96] = {0}; + FILE *thp = fopen("/sys/kernel/mm/transparent_hugepage/enabled", "r"); + if (thp) { + size_t n = fread(mode, 1, sizeof mode - 1, thp); + mode[n] = '\0'; + fclose(thp); + } + FILE *f = fopen("/proc/sys/vm/overcommit_memory", "r"); + int overcommit = f ? fgetc(f) : '0'; + if (f) fclose(f); + /* ThreadSanitizer replaces malloc, which turns the policy off. */ + bool glibc_malloc = true; +#if defined(__SANITIZE_THREAD__) + glibc_malloc = false; +#elif defined(__has_feature) +#if __has_feature(thread_sanitizer) + glibc_malloc = false; +#endif +#endif + /* With glibc's memory tagging tunable set the heap is left alone: under + * arm64 MTE, reading its chunk headers through block pointers faults. */ + const char *tunables = getenv("GLIBC_TUNABLES"); + if (tunables && strstr(tunables, "glibc.mem.tagging=3")) assert(!strstr(heap_flags, " hg")); + else if (glibc_malloc && strstr(mode, "[madvise]") && !tunables && overcommit != '2') + assert(strstr(heap_flags, " hg")); +} +#endif + static void check_threads(void) { void *mine = scr_mem_alloc(48); assert(owned(mine)); @@ -1047,6 +1101,9 @@ int main(void) { setenv("SCR_SA_DISCARD_DELAY_MS", "100", 1); setenv("SCR_SA_DISCARD_DELAY_MAX_MS", "0", 1); check_threads(); +#if defined(__linux__) && defined(__GLIBC__) + check_thread_heap_huge_pages(); +#endif #ifdef __linux__ check_trim(); check_release_frontiers(); diff --git a/packages/runtime/src/scr_alloc.test.ts b/packages/runtime/src/scr_alloc.test.ts index 187ffce51..0ceda33cd 100644 --- a/packages/runtime/src/scr_alloc.test.ts +++ b/packages/runtime/src/scr_alloc.test.ts @@ -11,13 +11,20 @@ const exec = promisify(execFile); // UBSan-only build is the one that exercises it; the ASan build pins the // system-allocator bypass the sanitizer lane relies on. // Worker executables (-DSCR_WORKERS) give each script thread its own arena; -// the ThreadSanitizer build checks the cross-thread handoff for races. +// the ThreadSanitizer build checks the cross-thread handoff for races. The +// last build runs with glibc's memory tagging tunable set, which turns the +// thread-heap huge-page policy off. test.each([ - ["size-class", ["-fsanitize=undefined"]], - ["system", ["-fsanitize=address,undefined"]], - ["size-class workers", ["-fsanitize=undefined", "-DSCR_WORKERS", "-pthread"]], - ["size-class workers", ["-fsanitize=thread", "-DSCR_WORKERS", "-pthread"]], -])("small-object allocator contracts hold (%s, %j)", async (mode, sanitize) => { + ["size-class", ["-fsanitize=undefined"], {}], + ["system", ["-fsanitize=address,undefined"], {}], + ["size-class workers", ["-fsanitize=undefined", "-DSCR_WORKERS", "-pthread"], {}], + ["size-class workers", ["-fsanitize=thread", "-DSCR_WORKERS", "-pthread"], {}], + [ + "size-class workers", + ["-fsanitize=undefined", "-DSCR_WORKERS", "-pthread"], + { GLIBC_TUNABLES: "glibc.mem.tagging=3" }, + ], +])("small-object allocator contracts hold (%s, %j, %j)", async (mode, sanitize, extraEnv) => { const scratch = await mkdtemp(join(tmpdir(), "scriptc-alloc-")); try { const binary = join(scratch, "alloc"); @@ -41,6 +48,7 @@ test.each([ ASAN_OPTIONS: "halt_on_error=1", TSAN_OPTIONS: "halt_on_error=1", UBSAN_OPTIONS: "halt_on_error=1", + ...extraEnv, }, }); expect(result.stdout).toBe(`allocator checks passed: ${mode}\n`); diff --git a/packages/runtime/src/scr_array.c b/packages/runtime/src/scr_array.c index 91328a1e8..4c7b84d98 100644 --- a/packages/runtime/src/scr_array.c +++ b/packages/runtime/src/scr_array.c @@ -1689,6 +1689,126 @@ void scr_arr_release(ScrArr *a) { double scr_arr_len(ScrArr *a) { return SCR_ARR_LEN_F64(a->len); } +/* ── frozen arrays ────────────────────────────────────────────────────── + * Node's TypeErrors for writes to a frozen array, shared by the emitted + * guards of published and frozen arrays and the checked-dynamic tier. */ +static void scr_frozen_throw(const char *before, double index, const char *after) { + char text[64]; + size_t n = scr_f64_to_str(index, text); + ScrJsonBuf buffer; + scr_jb_init(&buffer); + scr_jb_puts(&buffer, before); + scr_jb_write(&buffer, text, n); + scr_jb_puts(&buffer, after); + scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&buffer)); +} + +/* A relative array index argument (fill, copyWithin, splice): ToIntegerOrInfinity + * of `value`, counted from the end when negative, clamped to [0, len]. */ +static size_t scr_frozen_relative_index(double value, size_t len) { + if (value != value) return 0; + double n = trunc(value); + if (n < 0) return n + (double)len <= 0 ? 0 : (size_t)(n + (double)len); + return n >= (double)len ? len : (size_t)n; +} + +static void scr_frozen_read_only(double index) { + scr_frozen_throw("Cannot assign to read only property '", index, "' of object '[object Array]'"); +} + +static void scr_frozen_not_extensible(double index) { + scr_frozen_throw("Cannot add property ", index, ", object is not extensible"); +} + +static void scr_frozen_undeletable(double index) { + scr_frozen_throw("Cannot delete property '", index, "' of [object Array]"); +} + +/* Each case finds the operation's first write in the order the spec makes + * its writes. */ +bool scr_array_frozen_error(int op, size_t len, double x, double y, double z) { + switch (op) { + case 0: + if (x >= 0 && x < (double)len && x == (double)(size_t)x) scr_frozen_read_only(x); + else scr_frozen_not_extensible(x); + return true; + case 1: + if (y == 0) break; + scr_frozen_not_extensible((double)len); + return true; + case 2: + case 6: + if (len == 0) break; + if (op == 6 && len > 1) scr_frozen_read_only(0); + else scr_frozen_undeletable((double)(len - 1)); + return true; + case 3: break; + case 4: + if (y == 0) break; + scr_frozen_not_extensible(len ? (double)len + y - 1 : 0); + return true; + case 5: + if (!(x >= 0 && x < (double)len && x == (double)(size_t)x)) return false; + scr_frozen_undeletable(x); + return true; + case 7: + if (len < 2) return false; + scr_frozen_read_only(0); + return true; + case 8: { + size_t k = scr_frozen_relative_index(x, len), final = scr_frozen_relative_index(y, len); + if (k >= final) return false; + scr_frozen_read_only((double)k); + return true; + } + case 9: { + size_t to = scr_frozen_relative_index(x, len), from = scr_frozen_relative_index(y, len); + size_t final = scr_frozen_relative_index(z, len); + if (final <= from || len <= to) return false; + size_t count = final - from < len - to ? final - from : len - to; + /* An overlapping copy toward the end writes its last index first. */ + if (from < to && to < from + count) to += count - 1; + scr_frozen_read_only((double)to); + return true; + } + case 10: { + size_t start = scr_frozen_relative_index(x, len); + double count = y != y ? 0 : trunc(y); + size_t removed = count <= 0 ? 0 : count >= (double)(len - start) ? len - start : (size_t)count; + size_t items = (size_t)z; + if (items < removed) { + /* Elements after the removed ones move down, then the tail goes. */ + if (start < len - removed) scr_frozen_read_only((double)(start + items)); + else scr_frozen_undeletable((double)(len - 1)); + return true; + } + if (items > removed) { + /* Elements after the removed ones move up, past the end first. */ + if (len - removed > start) scr_frozen_not_extensible((double)(len - removed + items - 1)); + else if (start < len) scr_frozen_read_only((double)start); + else scr_frozen_not_extensible((double)start); + return true; + } + if (items > 0) { + scr_frozen_read_only((double)start); + return true; + } + break; + } + default: + return false; + } + static const char length[] = "Cannot assign to read only property 'length' of object '[object Array]'"; + scr_throw_error_msg(SCR_ERR_TYPE, length, sizeof length - 1); + return true; +} + +void scr_arr_freeze(ScrArr *a) { + if (a->rc != SIZE_MAX) a->spare |= SCR_ARR_FROZEN; +} + +bool scr_arr_is_frozen(const ScrArr *a) { return scr_arr_frozen(a); } + bool scr_arr_has(const ScrArr *a, double i) { size_t idx; if (!scr_arr_valid_index(i, &idx)) return scr_arr_prop_get(a, i, NULL); @@ -3108,10 +3228,12 @@ typedef struct { typedef struct { ScrElemKind kind; const ScrNumberSortKey *numbers; + ScrStr *const *texts; /* reference elements: each element's String() text */ } ScrPrimitiveOrder; static int scr_arr_primitive_compare(uint64_t left, uint64_t right, const ScrPrimitiveOrder *order) { + if (order->texts) return scr_str_cmp_u16(order->texts[left], order->texts[right]); if (order->kind == SCR_ELEM_STR) return scr_str_cmp_u16(scr_slot_to_ptr(left), scr_slot_to_ptr(right)); if (order->kind == SCR_ELEM_BOOL) return (left > right) - (left < right); @@ -3199,10 +3321,28 @@ static void scr_arr_primitive_order(uint64_t *values, size_t count, if (runs != local_runs) free(runs); } +static ScrArr *scr_arr_sort_default(ScrArr *a, bool copy, ScrStr *(*text)(void *)); + +static ScrArr *scr_arr_new_like(const ScrArr *a, size_t cap) { + return a->elem == SCR_ELEM_REF ? scr_arr_new_ops(scr_arr_ops(a), cap) : scr_arr_new(a->elem, cap); +} + ScrArr *scr_arr_sort_primitive(ScrArr *a, bool copy) { if (a->elem != SCR_ELEM_STR && a->elem != SCR_ELEM_F64 && a->elem != SCR_ELEM_BOOL) scr_trap("scriptc: invalid primitive array ordering\n"); - if (a->len == 0) return copy ? scr_arr_new(a->elem, 0) : scr_arr_retain(a); + return scr_arr_sort_default(a, copy, NULL); +} + +/* The default ordering of an array of reference elements that cannot call + * user code while converting to strings (Dates): `text` answers each + * element's String() text (+1), computed once per element. */ +ScrArr *scr_arr_sort_by_text(ScrArr *a, bool copy, ScrStr *(*text)(void *)) { + if (a->elem != SCR_ELEM_REF) scr_trap("scriptc: invalid text array ordering\n"); + return scr_arr_sort_default(a, copy, text); +} + +static ScrArr *scr_arr_sort_default(ScrArr *a, bool copy, ScrStr *(*text)(void *)) { + if (a->len == 0) return copy ? scr_arr_new_like(a, 0) : scr_arr_retain(a); size_t dense = a->len < a->cap ? a->len : a->cap; size_t count = 0, undefined_count = 0; const uint8_t *states = scr_arr_scan_states(a); @@ -3234,14 +3374,35 @@ ScrArr *scr_arr_sort_primitive(ScrArr *a, bool copy) { values[i] = i; } } - ScrPrimitiveOrder order = {a->elem, numbers}; + ScrStr **texts = NULL; + uint64_t *originals = NULL; + if (text && count > 1) { + if (count > SIZE_MAX / sizeof(*texts)) scr_arr_oom(); + texts = malloc(count * sizeof(*texts)); + originals = malloc(count * sizeof(*originals)); + if (!texts || !originals) scr_arr_oom(); + for (size_t i = 0; i < count; i++) { + originals[i] = values[i]; + texts[i] = text(scr_slot_to_ptr(values[i])); + values[i] = i; + } + } + ScrPrimitiveOrder order = {a->elem, numbers, texts}; scr_arr_primitive_order(values, count, &order); if (numbers) { for (size_t i = 0; i < count; i++) values[i] = numbers[values[i]].value; free(numbers); } + if (texts) { + for (size_t i = 0; i < count; i++) { + values[i] = originals[values[i]]; + scr_str_release(texts[i]); + } + free(texts); + free(originals); + } size_t present = copy ? a->len : count + undefined_count; - ScrArr *out = copy ? scr_arr_new(a->elem, present) : a; + ScrArr *out = copy ? scr_arr_new_like(a, present) : a; if (!copy && !states) { /* Values only, in place. */ if (count) scr_arr_put_slots(a, 0, count, values); @@ -4990,6 +5151,8 @@ bool scr_arr_sort_dense(ScrArr *a, ScrClosure *f, uint32_t arity, bool owned, in if (a->rc == SIZE_MAX || elem == SCR_ELEM_CREF || !scr_arr_dense_values(a)) return false; size_t n = a->len; if (n < 2) return true; + /* A frozen array takes the snapshot path, whose write-back throws. */ + if (scr_arr_frozen(a)) return false; if (n > SIZE_MAX / 2 / sizeof(uint64_t)) scr_arr_oom(); ScrSortCompare c = scr_sort_compare_for(a, f, arity, order, key); /* Nothing else can reach a private copy: its own storage is sorted, and @@ -5012,15 +5175,12 @@ bool scr_arr_sort_dense(ScrArr *a, ScrClosure *f, uint32_t arity, bool owned, in free(src); return true; } - if (a->rc == SIZE_MAX) { - /* The comparator published the array: the first write fails as in Node - * (scr_throw_published_array's index write). */ + if (a->rc == SIZE_MAX || scr_arr_frozen(a)) { + /* The comparator published or froze the array: the first write fails + * as in Node. */ if (retained) for (size_t i = 0; i < n; i++) scr_elem_release_with(elem, ops, src[i]); - static const char assign[] = "Cannot assign to read only property '0' of object '[object Array]'"; - static const char add[] = "Cannot add property 0, object is not extensible"; - if (a->len > 0) scr_throw_error_msg(SCR_ERR_TYPE, assign, sizeof assign - 1); - else scr_throw_error_msg(SCR_ERR_TYPE, add, sizeof add - 1); + scr_array_frozen_error(0, a->len, 0, 0, 0); } else if (a->len >= n && a->cap >= n) { if (!retained) { memcpy(a->data, src, n * sizeof(*src)); diff --git a/packages/runtime/src/scr_array.h b/packages/runtime/src/scr_array.h index 4666750ea..23390a183 100644 --- a/packages/runtime/src/scr_array.h +++ b/packages/runtime/src/scr_array.h @@ -128,7 +128,8 @@ typedef struct ScrArr { * header. Kept beside `elem` so the RC fast paths test it with one load. */ uint8_t cyc; /* The low SCR_ARR_OPS_SHIFT bits: storage flags (SCR_ARR_INLINE_SLOTS, - * SCR_ARR_HEAD_ROOM, SCR_ARR_UNSHIFTED, SCR_ARR_HAS_EXT, SCR_ARR_STATES). The rest: the + * SCR_ARR_HEAD_ROOM, SCR_ARR_UNSHIFTED, SCR_ARR_HAS_EXT, SCR_ARR_STATES) + * and SCR_ARR_FROZEN. The rest: the * registry id of the element type's ops (scr_arr_ops), 0 for every kind * but SCR_ELEM_REF and SCR_ELEM_CREF. */ uint16_t spare; @@ -204,6 +205,10 @@ _Static_assert(sizeof(ScrArr) == sizeof(size_t) + 16 + sizeof(void *), #define SCR_ARR_UNSHIFTED 4u #define SCR_ARR_HAS_EXT 8u #define SCR_ARR_STATES 16u +/* Object.freeze of the array (scr_arr_freeze). Emitted guards test it + * before the writes of array types a program may freeze; the runtime's own + * writes assume it is clear. */ +#define SCR_ARR_FROZEN 32u /* An array starts without state bytes. Without them, every index below * `len` is a present value in dense storage: len <= cap, there are no @@ -214,6 +219,19 @@ _Static_assert(sizeof(ScrArr) == sizeof(size_t) + 16 + sizeof(void *), * states (scr_arr_add_states), which it keeps. */ static inline bool scr_arr_has_states(const ScrArr *a) { return (a->spare & SCR_ARR_STATES) != 0; } +static inline bool scr_arr_frozen(const ScrArr *a) { return (a->spare & SCR_ARR_FROZEN) != 0; } +/* Sets SCR_ARR_FROZEN; immortal arrays are left as they are. */ +void scr_arr_freeze(ScrArr *a); +bool scr_arr_is_frozen(const ScrArr *a); +/* Throws Node's TypeError for the first write that the array operation + * `op` makes to a frozen array of length `len`, or answers false when the + * operation writes nothing. op: 0 index write (x = index), 1 push (y = + * argument count), 2 pop, 3 length write, 4 unshift (y = argument count), + * 5 delete (x = index), 6 shift, 7 reverse or sort, 8 fill (x = start, y = + * end), 9 copyWithin (x = target, y = start, z = end), 10 splice (x = + * start, y = delete count, z = item count). */ +bool scr_array_frozen_error(int op, size_t len, double x, double y, double z); + /* The start of the dense slots' allocation: `data` less any head room. */ static inline uint64_t *scr_arr_block(const ScrArr *a) { return (a->spare & SCR_ARR_HEAD_ROOM) ? a->data - (size_t)a->data[-1] : a->data; @@ -236,7 +254,7 @@ static inline struct ScrArrExt *scr_arr_ext_of(const ScrArr *a) { * Id 0 is no ops; ids are assigned on each descriptor's first array, in * any thread. A program with more element types than the field holds keeps * the ops of the rest in the array's ext (SCR_ARR_OPS_OVERFLOW). */ -#define SCR_ARR_OPS_SHIFT 5 +#define SCR_ARR_OPS_SHIFT 6 #define SCR_ARR_OPS_OVERFLOW (UINT16_MAX >> SCR_ARR_OPS_SHIFT) extern const ScrArrOps *scr_arr_ops_table[SCR_ARR_OPS_OVERFLOW]; /* By registry id: 1 when the descriptor has SCR_ARR_OPS_RELEASE_NOTHING. @@ -625,6 +643,10 @@ double scr_arr_unshift_spread(ScrArr *a, const ScrArr *src); ScrArr *scr_arr_reverse(ScrArr *a); /* Default primitive String ordering; stable, borrowed receiver, owned result. */ ScrArr *scr_arr_sort_primitive(ScrArr *a, bool copy); +/* Default ordering of reference elements by their String() text, which + * `text` answers (+1) for each element once (Date arrays). Like + * scr_arr_sort_primitive otherwise. */ +ScrArr *scr_arr_sort_by_text(ScrArr *a, bool copy, ScrStr *(*text)(void *)); /* Stable comparator sort of the first `count` slots of a private, compacted * snapshot (every slot a value; f64 or reference elements). `f` is a borrowed * closure of `arity` (0..2) element parameters returning a number. Elements diff --git a/packages/runtime/src/scr_async.c b/packages/runtime/src/scr_async.c index 6afd174b7..0ae774df7 100644 --- a/packages/runtime/src/scr_async.c +++ b/packages/runtime/src/scr_async.c @@ -750,11 +750,13 @@ static void scr_als_prefix_end(ScrFiber *f) { } } -/* A synchronous generator has no context of its own: each step runs in its - * resumer's execution, as Node runs it. A fiber-backed one switches to its - * own slot, so its step starts from the resumer's store and a store it - * enters is the resumer's after the step (scr_als_prefix_end on the way - * back); a stackless one runs in the resumer's slot (scr_gen_coro_hop). */ +/* A generator step runs in its resumer's execution, as Node runs it. A + * fiber-backed generator switches to its own slot, so its step starts from + * the resumer's store and a store it enters is the resumer's after the + * step (scr_als_prefix_end on the way back); a stackless synchronous one + * runs in the resumer's slot (scr_gen_coro_hop). An async generator's step + * starts the same way, and after an await it continues in its own copy of + * the context, as an async function's body does after its prefix. */ static void scr_als_step_begin(ScrFiber *f) { ScrAlsCtx **slot = SCR_ALS_SLOT(); if (f->als != *slot) { @@ -809,6 +811,17 @@ void scr_als_restore(ScrAlsCtx *prev) { *SCR_ALS_SLOT() = prev; /* ownership moves back in */ } +ScrAlsCtx *scr_als_dispatch_enter(ScrAlsCtx *ctx) { + ScrAlsCtx **slot = SCR_ALS_SLOT(); + ScrAlsCtx *prev = *slot; + *slot = scr_als_ctx_retain(ctx); + return prev; +} + +void scr_als_dispatch_leave(ScrAlsCtx *prev) { scr_als_restore(prev); } + +ScrAlsCtx *scr_als_capture(void) { return scr_als_ctx_retain(*SCR_ALS_SLOT()); } + /* The value of `id` in the active snapshot (borrowed), or NULL. */ ScrDyn *scr_als_peek(double id) { const ScrAlsCtx *c = *SCR_ALS_SLOT(); @@ -1257,10 +1270,14 @@ static SCR_TL ScrNtick *scr_nt_tail = NULL; * it, because 'exit' listeners run AFTER the loop's teardown and may * enqueue ticks that must never run (Node) yet must not leak. */ +static ScrClosure *scr_als_bind(ScrClosure *cb /*moves*/); + +/* A tick runs with the AsyncLocalStorage context active when it was + * queued, as Node's process.nextTick does. */ void scr_next_tick(ScrClosure *cb /*moves*/) { ScrNtick *t = calloc(1, sizeof *t); if (!t) scr_oom(); - t->cb = cb; + t->cb = scr_als_bind(cb); if (scr_nt_tail) scr_nt_tail->next = t; else scr_nt_head = t; scr_nt_tail = t; @@ -1874,6 +1891,12 @@ void scr_promise_all_store_ref(ScrArr *a, double i, ScrPromise *src) { scr_arr_set_ref(a, i, scr_promise_payload_ref(src)); } +/* The entry promise itself: Promise.all's tuple form reads each fulfilled + * entry at its own type once the combinator fulfills. */ +void scr_promise_all_store_promise(ScrArr *a, double i, ScrPromise *src) { + scr_arr_set_ref(a, i, scr_promise_retain(src)); +} + /* Fulfillment payload accessors for the emitted race adapters — all * retained/by-value (the source keeps its settlement). */ double scr_promise_payload_f64(ScrPromise *p) { return p->f64; } @@ -4044,9 +4067,50 @@ double scr_set_timeout_handle_als(ScrClosure *cb, double ms) { return scr_set_timeout_handle(scr_als_bind(cb), ms); } double scr_set_immediate_als(ScrClosure *cb) { return scr_set_immediate(scr_als_bind(cb)); } -void scr_next_tick_als(ScrClosure *cb) { scr_next_tick(scr_als_bind(cb)); } +void scr_next_tick_als(ScrClosure *cb) { scr_next_tick(cb); } /* scr_next_tick binds */ void scr_queue_microtask_als(ScrClosure *cb) { scr_queue_microtask(scr_als_bind(cb)); } +/* A typed callback bound to the context active when it was bound: the + * completion callbacks of callback-style APIs in programs that use + * AsyncLocalStorage, and AsyncLocalStorage.bind. The bound closure's entry + * is a thunk the compiler emits per callback signature; it reads the + * target (scr_als_bound_target), enters the captured context + * (scr_als_bound_enter), calls the target with its own arguments and + * restores the caller's context (scr_als_bound_leave). With `always` + * false an empty context leaves the callback unbound, since the loop runs + * callbacks with an empty context; AsyncLocalStorage.bind passes true, so + * a bound function called later inside run() still sees no store. */ +ScrClosure *scr_als_bind_closure(ScrClosure *cb /*moves*/, void *thunk, bool always) { + ScrAlsCtx *active = *SCR_ALS_SLOT(); + if (cb == NULL || (active == NULL && !always)) return cb; + ScrClosure *bound = scr_closure_new(thunk, 2); + bound->caps[0] = scr_box_new(SCR_BOX_FUNC); + scr_box_set_ref(bound->caps[0], cb); + bound->caps[1] = scr_box_new_obj(scr_als_ctx_retain_v, scr_als_ctx_release_v, NULL); + scr_box_set_ref(bound->caps[1], scr_als_ctx_retain(active)); + return bound; +} + +/* The bound target (+1, released by scr_als_bound_leave). */ +ScrClosure *scr_als_bound_target(ScrClosure *self) { + return scr_box_get_ref(self->caps[0]); +} + +/* Enters the captured context; returns the caller's (owned). */ +ScrAlsCtx *scr_als_bound_enter(ScrClosure *self) { + ScrAlsCtx **slot = SCR_ALS_SLOT(); + ScrAlsCtx *prev = *slot; + *slot = scr_box_get_ref(self->caps[1]); /* the slot owns this reference */ + return prev; +} + +/* Restores the caller's context, replacing whatever the target entered + * (an enterWith inside it), and releases the target. */ +void scr_als_bound_leave(ScrAlsCtx *prev, ScrClosure *target) { + scr_als_restore(prev); + scr_closure_release(target); +} + /* The checked-dynamic argument form (JS files — common.mustCall wrappers * and the suite's invalid-input probes): a non-function throws Node's * ERR_INVALID_ARG_TYPE synchronously; a function value is called with @@ -5286,6 +5350,10 @@ struct ScrGen { * since that return takes its own turn to await x. */ bool return_queued; bool return_awaited; + /* The AsyncLocalStorage context of a return() request deferred at a + * yield (+1): the body's unwind runs in it, as Node resumes it from the + * return value's await. */ + ScrAlsCtx *return_ctx; #ifdef SCR_WORKERS bool context_abandoned; #endif @@ -5389,6 +5457,7 @@ void scr_gen_release(ScrGen *g) { scr_gen_slot_reset(&g->in); scr_gen_slot_reset(&g->ret); scr_gen_exc_reset(&g->async_error); + scr_als_ctx_release(g->return_ctx); if (g->fiber != NULL) { if (g->state == SCR_GEN_UNSTARTED) { /* Never ran: nothing on the stack owns anything — clean teardown. @@ -5743,6 +5812,12 @@ __attribute__((always_inline)) static inline void scr_gen_switched_back(ScrGen * if (release) scr_gen_release(g); } +/* Out of line, so the hop that synchronous generators share stays as it + * was: an async generator's step begins from its resumer's context and + * hands the prefix's context back at its first suspension. */ +static __attribute__((noinline)) void scr_async_gen_step_begin(ScrFiber *f) { scr_als_step_begin(f); } +static __attribute__((noinline)) void scr_async_gen_step_end(ScrFiber *f) { scr_als_prefix_end(f); } + #ifndef __wasi__ /* The stackless form of the hop: the coroutine frame runs on this stack * until its next yield or its final suspension returns here. `retain` @@ -5751,14 +5826,17 @@ __attribute__((always_inline)) static inline void scr_gen_switched_back(ScrGen * __attribute__((always_inline)) static inline void scr_gen_coro_hop(ScrGen *g, bool retain) { ScrFiber *f = g->fiber; if (retain) scr_gen_retain(g); + /* A synchronous generator has no AsyncLocalStorage context of its own: + * its step runs in the resumer's slot, as Node runs it in the execution + * that called next(). An async generator's step runs in its own slot, + * which a new step (not the continuation after an await) starts from the + * resumer's context (scr_als_step_begin). */ + if (g->is_async && g->state != SCR_GEN_AWAITING) scr_async_gen_step_begin(f); g->state = SCR_GEN_RUNNING; f->resumer = scr_current; ScrAlsCtx **resumer_slot = scr_als_active; scr_current = f; scr_exc_swap_cell(&f->exc); - /* A synchronous generator has no AsyncLocalStorage context of its own: - * its step runs in the resumer's slot, as Node runs it in the execution - * that called next(). An async generator's step runs in its own. */ if (g->is_async) scr_als_active = &f->als; if (f->coro == NULL) f->entry(f, f->argpack); else scr_coro_resume(f->coro); @@ -5769,6 +5847,7 @@ __attribute__((always_inline)) static inline void scr_gen_coro_hop(ScrGen *g, bo scr_current = me; scr_exc_swap_cell(me != NULL ? &me->exc : NULL); scr_als_active = resumer_slot; + if (g->is_async) scr_async_gen_step_end(f); scr_gen_switched_back(g, f, retain, true); } @@ -5789,8 +5868,10 @@ static void scr_gen_switch_in(ScrGen *g) { } #endif scr_gen_retain(g); + /* Each step starts from the resumer's context; an async generator's + * continuation after an await keeps its own. */ + if (!g->is_async || g->state != SCR_GEN_AWAITING) scr_als_step_begin(f); g->state = SCR_GEN_RUNNING; - if (!g->is_async) scr_als_step_begin(f); #ifdef _WIN32 ScrCtx here = scr_win_self(); #elif defined(__wasi__) @@ -5962,6 +6043,25 @@ void scr_gen_resume_throw(ScrGen *g) { static void scr_async_gen_start_queued(ScrGen *g); +/* Runs fn(g) with `ctx` (borrowed) as the active AsyncLocalStorage + * context. A settlement job runs from the loop: a request queued behind + * another starts in the context of the step before it (Node drains the + * queue in the generator's own execution), and a deferred return() in its + * call's. */ +static __attribute__((noinline)) void scr_async_gen_in_context(ScrGen *g, ScrAlsCtx *ctx, + void (*fn)(ScrGen *)) { + ScrAlsCtx **slot = SCR_ALS_SLOT(); + ScrAlsCtx *prev = *slot; + *slot = scr_als_ctx_retain(ctx); + fn(g); + scr_als_restore(prev); +} + +/* Starts the queued requests in the context of the step before them. */ +static __attribute__((noinline)) void scr_async_gen_start_queued_in_own(ScrGen *g) { + scr_async_gen_in_context(g, g->fiber != NULL ? g->fiber->als : NULL, scr_async_gen_start_queued); +} + static void scr_async_gen_schedule_settle(ScrGen *g) { if (g->settle_queued) return; ScrFiber *job = scr_mem_calloc(sizeof *job); @@ -6009,6 +6109,11 @@ static bool scr_async_gen_settles_now(ScrGen *g) { return false; } +static __attribute__((noinline)) void scr_async_gen_capture_return_ctx(ScrGen *g) { + scr_als_ctx_release(g->return_ctx); + g->return_ctx = scr_als_ctx_retain(*SCR_ALS_SLOT()); +} + static void scr_async_gen_run_active(ScrGen *g, ScrAsyncGenRequest *req) { const bool was_done = g->state == SCR_GEN_DONE; if (req->mode == SCR_AG_NEXT) { @@ -6024,6 +6129,7 @@ static void scr_async_gen_run_active(ScrGen *g, ScrAsyncGenRequest *req) { /* At a yield, AsyncGeneratorYield awaits the return value before * the body resumes, so finally blocks run one turn later. */ g->return_queued = true; + scr_async_gen_capture_return_ctx(g); scr_async_gen_schedule_settle(g); return; } @@ -6054,6 +6160,16 @@ static void scr_async_gen_run_active(ScrGen *g, ScrAsyncGenRequest *req) { } } +static void scr_async_gen_resume_returning(ScrGen *g); + +/* The deferred return() resumes in its call's context. */ +static __attribute__((noinline)) void scr_async_gen_resume_returning_in_context(ScrGen *g) { + ScrAlsCtx *ctx = g->return_ctx; + g->return_ctx = NULL; + scr_async_gen_in_context(g, ctx, scr_async_gen_resume_returning); + scr_als_ctx_release(ctx); +} + /* The return() request's await has taken its turn: resume the body with * the return completion. */ static void scr_async_gen_resume_returning(ScrGen *g) { @@ -6186,12 +6302,12 @@ static void scr_async_gen_job(ScrGen *g) { } if (g->return_queued) { scr_gen_retain(g); - scr_async_gen_resume_returning(g); + scr_async_gen_resume_returning_in_context(g); scr_gen_release(g); return; } scr_async_gen_finish_active(g, false); - scr_async_gen_start_queued(g); + scr_async_gen_start_queued_in_own(g); } static void scr_async_gen_resume_ready(ScrGen *g) { @@ -6205,11 +6321,11 @@ static void scr_async_gen_resume_ready(ScrGen *g) { scr_gen_switch_in(g); if (g->async_error.kind != SCR_EXC_NONE) { scr_async_gen_finish_active(g, true); - scr_async_gen_start_queued(g); + scr_async_gen_start_queued_in_own(g); } else if (g->state != SCR_GEN_AWAITING) { if (scr_async_gen_settles_now(g)) { scr_async_gen_finish_active(g, false); - scr_async_gen_start_queued(g); + scr_async_gen_start_queued_in_own(g); } else { scr_async_gen_schedule_settle(g); } diff --git a/packages/runtime/src/scr_async.h b/packages/runtime/src/scr_async.h index fc0128294..e31ba5b1d 100644 --- a/packages/runtime/src/scr_async.h +++ b/packages/runtime/src/scr_async.h @@ -192,12 +192,29 @@ ScrDyn *scr_als_get(double id); /* +1; the undefined value when unset */ ScrAlsCtx *scr_als_enter(double id, ScrDyn *value); /* borrows value; prev out (owned) */ ScrAlsCtx *scr_als_enter_absent(double id); /* exit()'s cleared arm */ void scr_als_restore(ScrAlsCtx *prev); /* moves prev back in */ +/* A runtime dispatch (an I/O event the loop delivers, a deferred emit) + * runs its listeners in the AsyncLocalStorage context of the object or + * request Node would run them in: enter installs `ctx` (borrowed, NULL + * for no store) and answers the previous context, which leave puts back. + * A store a listener enters (enterWith) ends with the dispatch. */ +ScrAlsCtx *scr_als_dispatch_enter(ScrAlsCtx *ctx); +void scr_als_dispatch_leave(ScrAlsCtx *prev); +/* The active context, +1 (NULL outside a store): what a handle keeps. */ +ScrAlsCtx *scr_als_capture(void); void scr_als_enter_with(double id, ScrDyn *value); /* no restore point (enterWith) */ void scr_als_disable(double id); /* run/exit: enter (or clear), call the dyn function with the forwarded * argument vector, restore (the finally). Result +1 or NULL pending. */ ScrDyn *scr_als_run(double id, ScrDyn *value, ScrDyn *fn, ScrDyn *args); ScrDyn *scr_als_exit_run(double id, ScrDyn *fn, ScrDyn *args); +/* new AsyncLocalStorage(options) (options borrowed; NaN with the exception + * pending for a non-object) and the name it carries (+1). */ +double scr_als_new_options(const ScrDyn *options); +ScrDyn *scr_als_name(double id); +/* AsyncLocalStorage.bind over a dyn function and the dyn completion + * callbacks (fn borrowed; +1), and AsyncLocalStorage.snapshot() (+1). */ +ScrDyn *scr_als_bind_dyn(ScrDyn *fn, bool always); +ScrDyn *scr_als_snapshot(void); /* process.on/once('unhandledRejection', fn): dyn listeners dispatched * per never-observed rejection at the end of its nextTick/microtask @@ -252,6 +269,10 @@ void scr_process_set_emit_warning(ScrDyn *fn); * made from node_modules) — at once for a static ES import, which Node * links before any module evaluates, otherwise on the next tick. */ void scr_punycode_warn(bool immediate, bool report); +/* The deprecated Buffer() and new Buffer() called outside node_modules + * (scr_async_dyn.c): DEP0005 on the next tick, once, within Node's + * 10000-call check (scr_buffer_deprecation_count). */ +void scr_buffer_deprecation(void); /* scr_emit_warning once a 'warning' listener exists (scr_lib.c; set by the * listener registration): units that cannot link the dyn-async unit route * their warnings through it, and print the default report otherwise. */ @@ -369,6 +390,8 @@ void scr_promise_all_store_f64(ScrArr *a, double i, ScrPromise *src); void scr_promise_all_store_bool(ScrArr *a, double i, ScrPromise *src); void scr_promise_all_store_str(ScrArr *a, double i, ScrPromise *src); void scr_promise_all_store_ref(ScrArr *a, double i, ScrPromise *src); +/* Stores the fulfilled entry promise itself (+1), for Promise.all's tuple form. */ +void scr_promise_all_store_promise(ScrArr *a, double i, ScrPromise *src); void scr_promise_adapt_copy(ScrPromise *dst, ScrPromise *src); double scr_promise_payload_f64(ScrPromise *p); bool scr_promise_payload_bool(ScrPromise *p); @@ -473,6 +496,13 @@ double scr_set_timeout_handle_als(ScrClosure *cb, double ms); double scr_set_immediate_als(ScrClosure *cb); void scr_next_tick_als(ScrClosure *cb); void scr_queue_microtask_als(ScrClosure *cb); +/* A typed callback bound to the active context (scr_async.c): `thunk` is + * the compiler's per-signature entry, which brackets the call with + * scr_als_bound_target/enter/leave. cb moves in; the result is +1. */ +ScrClosure *scr_als_bind_closure(ScrClosure *cb, void *thunk, bool always); +ScrClosure *scr_als_bound_target(ScrClosure *self); +ScrAlsCtx *scr_als_bound_enter(ScrClosure *self); +void scr_als_bound_leave(ScrAlsCtx *prev, ScrClosure *target); /* A successful process.stdout/stderr.write completion on that same queue. * The callback and its program-shaped Error | null adapter both MOVE into * the entry; the adapter is invoked with NULL (Node's success argument). */ diff --git a/packages/runtime/src/scr_async_dyn.c b/packages/runtime/src/scr_async_dyn.c index a6cd813de..046aebb38 100644 --- a/packages/runtime/src/scr_async_dyn.c +++ b/packages/runtime/src/scr_async_dyn.c @@ -11,6 +11,7 @@ * 'warning' listeners + Node's stderr report). */ #include "scr_runtime.h" +#include #include #include #include @@ -30,6 +31,21 @@ static void scr_ad_oom(void) { static SCR_TL double scr_als_counter = 0; +/* The constructor options of each store, indexed by id - 1: the default + * value getStore() answers where the store has no entry, and the name. + * NULL until a store with options exists. */ +typedef struct { + ScrDyn *default_value; /* NULL: no default */ + ScrDyn *name; /* NULL: "" */ +} ScrAlsOptions; +static SCR_TL ScrAlsOptions *scr_als_options = NULL; +static SCR_TL size_t scr_als_options_len = 0; + +static const ScrAlsOptions *scr_als_options_of(double id) { + size_t i = (size_t)id - 1; + return id >= 1 && i < scr_als_options_len ? &scr_als_options[i] : NULL; +} + /* Main-slot teardown (atexit, LIFO after scr_init's audit registration — * the dc-registry precedent): a top-level enterWith leaves a context in * the main slot at exit; release it before the RC audit counts. */ @@ -37,6 +53,13 @@ static void scr_als_teardown(void) { /* atexit runs on the main context — the active slot IS main's. */ scr_als_ctx_release(*SCR_ALS_SLOT()); *SCR_ALS_SLOT() = NULL; + for (size_t i = 0; i < scr_als_options_len; i++) { + scr_dyn_release(scr_als_options[i].default_value); + scr_dyn_release(scr_als_options[i].name); + } + free(scr_als_options); + scr_als_options = NULL; + scr_als_options_len = 0; } double scr_als_new(void) { @@ -48,6 +71,89 @@ double scr_als_new(void) { return ++scr_als_counter; } +/* options[key] as JavaScript reads it (+1, or NULL with the exception + * pending): inherited properties and getters included. A class instance + * reads its own fields through its snapshot; a member its class declares + * (a getter or a method) has no runtime view here and is refused. */ +static ScrDyn *scr_als_option(ScrDyn *options, const char *key, size_t key_len) { + ScrStr *name = scr_str_new(key, key_len); + if (options->kind == SCR_DYN_TYPED_REF && scr_dyn_typed_ref_class_has(options, name)) { + char message[160]; + snprintf(message, sizeof message, + "AsyncLocalStorage options whose class declares '%s' are not supported yet", key); + scr_str_release(name); + scr_throw_error_msg(SCR_ERR_ERROR, message, strlen(message)); + return NULL; + } + ScrDyn *key_value = scr_dyn_new_str(name); + scr_str_release(name); + ScrDyn *value = scr_dyn_reflect_get(options, key_value, options); + scr_dyn_release(key_value); + if (value == NULL && !scr_exc_pending()) value = scr_dyn_retain(scr_dyn_undefined()); + return value; +} + +/* new AsyncLocalStorage(options): Node validates that options is an + * object, then reads options.defaultValue once and options.name twice + * (whether it is defined, then its value), keeping String(name). NaN with + * the exception pending when options is not an object or a read throws. */ +double scr_als_new_options(const ScrDyn *options) { + if (options->kind == SCR_DYN_UNDEF) return scr_als_new(); + if (options->kind != SCR_DYN_OBJ && options->kind != SCR_DYN_TYPED_REF && options->kind != SCR_DYN_PROXY) { + scr_dyn_arg_type_fail("options", "of type object", options); + return NAN; + } + ScrDyn *value = scr_als_option((ScrDyn *)options, "defaultValue", 12); + if (!value) return NAN; + ScrDyn *name = scr_als_option((ScrDyn *)options, "name", 4); + if (name && name->kind != SCR_DYN_UNDEF) { + scr_dyn_release(name); + name = scr_als_option((ScrDyn *)options, "name", 4); + if (name && name->kind == SCR_DYN_SYMBOL) { + static const char message[] = "Cannot convert a Symbol value to a string"; + scr_dyn_release(name); + name = NULL; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + } else if (name) { + ScrStr *text = scr_dyn_string_coerce_js(name); + scr_dyn_release(name); + name = text ? scr_dyn_new_str(text) : NULL; + scr_str_release(text); + } + } else { + scr_dyn_release(name); + name = scr_exc_pending() ? NULL : scr_dyn_retain(scr_dyn_undefined()); + } + if (!name) { + scr_dyn_release(value); + return NAN; + } + double id = scr_als_new(); + size_t index = (size_t)id - 1; + if (index >= scr_als_options_len) { + size_t len = index + 1; + ScrAlsOptions *grown = realloc(scr_als_options, len * sizeof *grown); + if (!grown) scr_ad_oom(); + for (size_t i = scr_als_options_len; i < len; i++) grown[i] = (ScrAlsOptions){NULL, NULL}; + scr_als_options = grown; + scr_als_options_len = len; + } + scr_als_options[index].default_value = value; /* the read's +1 moves */ + scr_als_options[index].name = name->kind != SCR_DYN_UNDEF ? name : NULL; + if (name->kind == SCR_DYN_UNDEF) scr_dyn_release(name); + return id; +} + +/* asyncLocalStorage.name (+1): the constructor's name option, or "". */ +ScrDyn *scr_als_name(double id) { + const ScrAlsOptions *options = scr_als_options_of(id); + if (options && options->name) return scr_dyn_retain(options->name); + ScrStr *empty = scr_str_new("", 0); + ScrDyn *result = scr_dyn_new_str(empty); + scr_str_release(empty); + return result; +} + ScrDyn *scr_als_get(double id) { const ScrAlsCtx *c = *SCR_ALS_SLOT(); if (c) { @@ -55,6 +161,8 @@ ScrDyn *scr_als_get(double id) { if (c->entries[i].id == id) return scr_dyn_retain(c->entries[i].value); } } + const ScrAlsOptions *options = scr_als_options_of(id); + if (options && options->default_value) return scr_dyn_retain(options->default_value); return scr_dyn_retain(scr_dyn_undefined()); } @@ -104,8 +212,80 @@ ScrDyn *scr_als_run(double id, ScrDyn *value, ScrDyn *fn, ScrDyn *args) { return scr_als_call_in(scr_als_enter(id, value), fn, args); } +/* exit() runs fn with the store undefined: a store with a default value + * holds an undefined entry for the call (getStore() answers undefined, + * not the default, as in Node); other stores drop their entry. */ ScrDyn *scr_als_exit_run(double id, ScrDyn *fn, ScrDyn *args) { - return scr_als_call_in(scr_als_enter_absent(id), fn, args); + const ScrAlsOptions *options = scr_als_options_of(id); + ScrAlsCtx *prev = options && options->default_value + ? scr_als_enter(id, scr_dyn_undefined()) + : scr_als_enter_absent(id); + return scr_als_call_in(prev, fn, args); +} + +/* AsyncLocalStorage.bind(fn) and the dyn completion callbacks of + * callback-style APIs: a function that calls fn in the context active + * when it was bound, with its own arguments and receiver. caps[0] holds + * fn, caps[1] the context, as scr_als_bind_closure lays them out. */ +static void *scr_als_ctx_retain_v(void *c) { return scr_als_ctx_retain(c); } +static void scr_als_ctx_release_v(void *c) { scr_als_ctx_release(c); } + +static ScrDyn *scr_als_bound_dyn_call(ScrClosure *self, ScrDyn *const *args, size_t argc) { + ScrDyn *fn = scr_box_get_ref(self->caps[0]); + ScrAlsCtx *prev = scr_als_bound_enter(self); + ScrDyn *result = scr_dyn_call(fn, args, argc, "callback"); + scr_als_restore(prev); + scr_dyn_release(fn); + return result; +} + +static ScrClosure *scr_als_context_closure(void *fn, size_t ncaps) { + ScrClosure *closure = scr_closure_new(fn, ncaps); + closure->caps[ncaps - 1] = scr_box_new_obj(scr_als_ctx_retain_v, scr_als_ctx_release_v, NULL); + scr_box_set_ref(closure->caps[ncaps - 1], scr_als_ctx_retain(*SCR_ALS_SLOT())); + return closure; +} + +/* fn borrowed; +1. With `always` false an empty context answers fn + * itself (the completion-callback form). AsyncLocalStorage.bind throws + * Node's ERR_INVALID_ARG_TYPE for a non-function. */ +ScrDyn *scr_als_bind_dyn(ScrDyn *fn, bool always) { + if (fn->kind != SCR_DYN_FUNC) { + if (always) { + scr_dyn_arg_type_fail("fn", "of type function", fn); + return NULL; + } + return scr_dyn_retain(fn); + } + if (!always && *SCR_ALS_SLOT() == NULL) return scr_dyn_retain(fn); + ScrClosure *closure = scr_als_context_closure(NULL, 2); + closure->caps[0] = scr_box_new_obj(scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v); + scr_box_set_ref(closure->caps[0], scr_dyn_retain(fn)); + return scr_dyn_new_func(closure, scr_als_bound_dyn_call, fn->v.fn.arity, "(...args)", "bound"); +} + +/* AsyncLocalStorage.snapshot(): a function (cb, ...args) that calls cb + * with args in the context active at the snapshot. */ +static ScrDyn *scr_als_snapshot_call(ScrClosure *self, ScrDyn *const *args, size_t argc) { + const ScrDyn *cb = argc > 0 ? args[0] : scr_dyn_undefined(); + if (cb->kind != SCR_DYN_FUNC) { + static const char message[] = "cb is not a function"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + return NULL; + } + ScrAlsCtx **slot = SCR_ALS_SLOT(); + ScrAlsCtx *prev = *slot; + *slot = scr_box_get_ref(self->caps[0]); /* the slot owns this reference */ + /* Node applies cb to an undefined receiver, whatever the runner's own. */ + scr_dyn_this_push_dyn(scr_dyn_undefined()); + ScrDyn *result = scr_dyn_call(cb, args + 1, argc - 1, "callback"); + scr_dyn_this_pop(); + scr_als_restore(prev); + return result; +} + +ScrDyn *scr_als_snapshot(void) { + return scr_dyn_new_func(scr_als_context_closure(NULL, 1), scr_als_snapshot_call, 1, "(cb, ...args)", "bound"); } /* The two rejection-event registries share one shape: listeners with a @@ -948,6 +1128,22 @@ void scr_punycode_warn(bool immediate, bool report) { scr_str_release(message); } +void scr_buffer_deprecation(void) { + /* Node's showFlaggedDeprecation: after 10000 calls the check stops. */ + if (scr_buffer_ctor_warned || ++scr_buffer_ctor_checks > 10000) return; + static const char text[] = + "Buffer() is deprecated due to security and usability issues. Please use the " + "Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead."; + ScrStr *message = scr_str_new(text, sizeof text - 1); + ScrStr *code = scr_str_new("DEP0005", 7); + ScrDyn *w = scr_warning_new(message, "DeprecationWarning", code); + scr_warning_emit_deferred(w); + scr_dyn_release(w); + scr_str_release(code); + scr_str_release(message); + scr_buffer_ctor_warned = true; +} + /* The console members' warnings. Node's emitWarning emits on * process.nextTick, so these queue in order and each rides one raw tick * on the shared tick queue; any left at exit are dropped with it. */ diff --git a/packages/runtime/src/scr_brotli.c b/packages/runtime/src/scr_brotli.c new file mode 100644 index 000000000..f66ecae75 --- /dev/null +++ b/packages/runtime/src/scr_brotli.c @@ -0,0 +1,323 @@ +/* node:zlib's Brotli codec over the vendored brotli 1.2.0 (the version Node + * v24.22.0 bundles, so compressed bytes match Node's). Node's + * BrotliEncoderContext and BrotliDecoderContext (src/node_zlib.cc) plug into + * scr_zlib.c's engine as a codec, so the one-shot, callback, promise and + * stream forms run exactly as their zlib counterparts do. The option ladder + * is lib/zlib.js's Brotli() followed by ZlibBase(). Linked only when the + * program uses Brotli; scr_brotli_install, which %main calls first, + * connects it to scr_zlib.c. */ +#include "scr_runtime.h" + +#include +#include +#include +#include +#include +#include + +enum { SCR_BROTLI_DECODE = 8, SCR_BROTLI_ENCODE = 9 }; +/* kMaxBrotliParam + 1: the largest BROTLI_PARAM_* value is + * BROTLI_PARAM_NDIRECT (8). An unset slot holds 0xFFFFFFFF, as Node's + * Uint32Array filled with -1 does. */ +#define SCR_BROTLI_PARAMS 9 +#define SCR_BROTLI_UNSET 0xFFFFFFFFu + +typedef struct { + bool encode; + BrotliEncoderState *enc; + BrotliEncoderPreparedDictionary *prepared; + BrotliDecoderState *dec; + /* The dictionary stays alive while the encoder's prepared dictionary or + * the decoder holds it. */ + unsigned char *dict; + size_t dict_len; + int flush; + bool enc_ok; + BrotliDecoderResult dec_result; + BrotliDecoderErrorCode dec_error; + char dec_code[64]; +} ScrBrotli; + +static void scr_brotli_oom(void) { + scr_trap("scriptc: out of memory\n"); +} + +static bool scr_brotli_fail(ScrZlibError *out, const char *message, const char *code, int err) { + out->message = message; + out->code = code; + out->err = err; + return true; +} + +static void scr_brotli_close(ScrBrotli *b) { + if (b->enc) BrotliEncoderDestroyInstance(b->enc); + if (b->prepared) BrotliEncoderDestroyPreparedDictionary(b->prepared); + if (b->dec) BrotliDecoderDestroyInstance(b->dec); + b->enc = NULL; + b->prepared = NULL; + b->dec = NULL; +} + +/* Init(dictionary): a fresh instance with the dictionary attached. As in + * Node, the decoder's last error survives a re-initialization. True with + * *out filled on failure. */ +static bool scr_brotli_init(ScrBrotli *b, ScrZlibError *out) { + scr_brotli_close(b); + if (b->encode) { + b->enc = BrotliEncoderCreateInstance(NULL, NULL, NULL); + b->enc_ok = false; + if (!b->enc) return scr_brotli_fail(out, "Could not initialize Brotli instance", "ERR_ZLIB_INITIALIZATION_FAILED", -1); + if (b->dict_len == 0) return false; + b->prepared = BrotliEncoderPrepareDictionary(BROTLI_SHARED_DICTIONARY_RAW, b->dict_len, b->dict, BROTLI_MAX_QUALITY, + NULL, NULL, NULL); + if (!b->prepared) return scr_brotli_fail(out, "Failed to prepare brotli dictionary", "ERR_ZLIB_DICTIONARY_LOAD_FAILED", -1); + if (!BrotliEncoderAttachPreparedDictionary(b->enc, b->prepared)) + return scr_brotli_fail(out, "Failed to attach brotli dictionary", "ERR_ZLIB_DICTIONARY_LOAD_FAILED", -1); + return false; + } + b->dec = BrotliDecoderCreateInstance(NULL, NULL, NULL); + if (!b->dec) return scr_brotli_fail(out, "Could not initialize Brotli instance", "ERR_ZLIB_INITIALIZATION_FAILED", -1); + if (b->dict_len != 0 && !BrotliDecoderAttachDictionary(b->dec, BROTLI_SHARED_DICTIONARY_RAW, b->dict_len, b->dict)) + return scr_brotli_fail(out, "Failed to attach brotli dictionary", "ERR_ZLIB_DICTIONARY_LOAD_FAILED", -1); + return false; +} + +static void scr_brotli_free(void *state) { + ScrBrotli *b = state; + if (!b) return; + scr_brotli_close(b); + free(b->dict); + free(b); +} + +/* DoThreadPoolWork: runs on a worker thread and touches only the state. */ +static void scr_brotli_work(void *state, int flush, const unsigned char *in, uint32_t in_len, unsigned char *out, + uint32_t out_len, uint32_t *avail_in, uint32_t *avail_out) { + ScrBrotli *b = state; + size_t in_left = in_len, out_left = out_len; + const uint8_t *next_in = in; + uint8_t *next_out = out; + b->flush = flush; + if (b->encode) { + b->enc_ok = BrotliEncoderCompressStream(b->enc, (BrotliEncoderOperation)flush, &in_left, &next_in, &out_left, + &next_out, NULL) != BROTLI_FALSE; + } else { + b->dec_result = BrotliDecoderDecompressStream(b->dec, &in_left, &next_in, &out_left, &next_out, NULL); + if (b->dec_result == BROTLI_DECODER_RESULT_ERROR) { + b->dec_error = BrotliDecoderGetErrorCode(b->dec); + snprintf(b->dec_code, sizeof b->dec_code, "ERR_%s", BrotliDecoderErrorString(b->dec_error)); + } + } + *avail_in = (uint32_t)in_left; + *avail_out = (uint32_t)out_left; +} + +/* GetErrorInfo. */ +static bool scr_brotli_error(const void *state, ScrZlibError *out) { + const ScrBrotli *b = state; + if (b->encode) { + if (!b->enc_ok) return scr_brotli_fail(out, "Compression failed", "ERR_BROTLI_COMPRESSION_FAILED", -1); + return false; + } + if (b->dec_error != BROTLI_DECODER_NO_ERROR) + return scr_brotli_fail(out, "Decompression failed", b->dec_code, (int)b->dec_error); + /* zlib's answer for a truncated stream; Brotli has no code of its own. */ + if (b->flush == BROTLI_OPERATION_FINISH && b->dec_result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) + return scr_brotli_fail(out, "unexpected end of file", "Z_BUF_ERROR", -5); + return false; +} + +/* ResetStream is Init() without a dictionary: as in Node, the reset + * instance drops the dictionary and the parameters. */ +static bool scr_brotli_reset(void *state, ScrZlibError *out) { + ScrBrotli *b = state; + free(b->dict); + b->dict = NULL; + b->dict_len = 0; + return scr_brotli_init(b, out); +} + +static const ScrZlibCodec scr_brotli_codec = { + scr_brotli_work, + scr_brotli_error, + scr_brotli_reset, + scr_brotli_free, +}; + +/* ── lib/zlib.js Brotli() ──────────────────────────────────────────── */ + +static const ScrDyn *scr_brotli_opt(const ScrDyn *opts, const char *key) { + if (opts == NULL || opts->kind != SCR_DYN_OBJ) return NULL; + return scr_dyn_obj_get((ScrDyn *)opts, key, strlen(key)); +} + +/* Uint32Array element assignment: ToUint32 of a number or boolean. */ +static uint32_t scr_brotli_uint32(const ScrDyn *v) { + if (v->kind == SCR_DYN_BOOL) return v->v.b ? 1 : 0; + double d = v->v.num; + if (!isfinite(d)) return 0; + double m = fmod(trunc(d), 4294967296.0); + if (m < 0) m += 4294967296.0; + return (uint32_t)m; +} + +/* ERR_BROTLI_INVALID_PARAM: the whole key, then Node's text. */ +static void scr_brotli_invalid_param(const ScrStr *key) { + static const char suffix[] = " is not a valid Brotli parameter"; + size_t len = key->len + sizeof suffix - 1; + char *message = malloc(len); + if (!message) scr_brotli_oom(); + memcpy(message, key->data, key->len); + memcpy(message + key->len, suffix, sizeof suffix - 1); + scr_throw_error_msg_code(SCR_ERR_RANGE, message, len, "ERR_BROTLI_INVALID_PARAM"); + free(message); +} + +/* opts.params: each own key must name a parameter (0..8) once, and each + * value must be a number or boolean. */ +static bool scr_brotli_params(const ScrDyn *params, uint32_t values[SCR_BROTLI_PARAMS]) { + for (size_t i = 0; i < SCR_BROTLI_PARAMS; i++) values[i] = SCR_BROTLI_UNSET; + if (params == NULL || !scr_dyn_truthy(params)) return true; + ScrDyn *view = params->kind == SCR_DYN_TYPED_REF ? scr_dyn_typed_ref_materialize(params) : NULL; + if (scr_exc_pending()) return false; + const ScrDyn *p = view ? view : params; + if (p->kind == SCR_DYN_STR) { + /* Object.keys of a string names its indices; the first value is a + * one-character string, which the type check rejects. */ + ScrStr *first = scr_str_new(p->v.str->data, 1); + ScrDyn *value = scr_dyn_new_str(first); + scr_str_release(first); + scr_dyn_prop_type_fail("options.params[key]", "of type number", value); + scr_dyn_release(value); + scr_dyn_release(view); + return false; + } + if (p->kind != SCR_DYN_OBJ && p->kind != SCR_DYN_ARR) { + scr_dyn_release(view); + return true; + } + ScrDyn *keys = scr_dyn_own_keys(p); + bool ok = keys != NULL; + size_t count = ok && keys->kind == SCR_DYN_ARR ? keys->v.arr.len : 0; + for (size_t i = 0; ok && i < count; i++) { + const ScrDyn *key = keys->v.arr.items[i]; + if (key->kind != SCR_DYN_STR) continue; + double index = NAN; + if (!scr_dyn_number_coerce_js(key, &index)) { + ok = false; + break; + } + if (isnan(index) || index < 0 || index > SCR_BROTLI_PARAMS - 1 || trunc(index) != index || + values[(int)index] != SCR_BROTLI_UNSET) { + scr_brotli_invalid_param(key->v.str); + ok = false; + break; + } + ScrDyn *value = scr_dyn_obj_read(p, key->v.str->data, key->v.str->len); + if (!value) { + ok = false; + break; + } + if (value->kind != SCR_DYN_NUM && value->kind != SCR_DYN_BOOL) { + scr_dyn_prop_type_fail("options.params[key]", "of type number", value); + scr_dyn_release(value); + ok = false; + break; + } + values[(int)index] = scr_brotli_uint32(value); + scr_dyn_release(value); + } + scr_dyn_release(keys); + scr_dyn_release(view); + return ok; +} + +/* opts.dictionary: an ArrayBufferView, or an ArrayBuffer read as a Buffer. + * A copy (or NULL for none) in *dict. */ +static bool scr_brotli_dictionary(const ScrDyn *d, unsigned char **dict, size_t *dict_len) { + *dict = NULL; + *dict_len = 0; + if (d == NULL || d->kind == SCR_DYN_UNDEF) return true; + ScrBytes *bytes = NULL; + if (d->kind == SCR_DYN_BYTES) { + bytes = scr_bytes_raw_view(d->v.bytes); + } else if (scr_buffer_storage_is(d)) { + bytes = scr_array_buffer_view(SCR_BYTES_U8, (ScrDyn *)d, scr_dyn_undefined(), scr_dyn_undefined()); + if (!bytes) return false; + } else { + scr_dyn_prop_type_fail("options.dictionary", "an instance of Buffer, TypedArray, DataView, or ArrayBuffer", d); + return false; + } + if (bytes->len) { + *dict = malloc(bytes->len); + if (!*dict) scr_brotli_oom(); + memcpy(*dict, bytes->data, bytes->len); + *dict_len = bytes->len; + } + scr_bytes_release(bytes); + return true; +} + +static void scr_brotli_init_failed(void) { + static const char message[] = "Initialization failed"; + scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "ERR_ZLIB_INITIALIZATION_FAILED"); +} + +static bool scr_brotli_options_object(int mode, const ScrDyn *opts, ScrZlibOptions *out) { + memset(out, 0, sizeof *out); + out->chunk_size = 16384; + out->flush = BROTLI_OPERATION_PROCESS; + out->finish_flush = BROTLI_OPERATION_FINISH; + out->max_output = 9007199254740991.0; + uint32_t params[SCR_BROTLI_PARAMS]; + if (!scr_brotli_params(scr_brotli_opt(opts, "params"), params)) return false; + unsigned char *dict; + size_t dict_len; + if (!scr_brotli_dictionary(scr_brotli_opt(opts, "dictionary"), &dict, &dict_len)) return false; + ScrBrotli *b = calloc(1, sizeof *b); + if (!b) scr_brotli_oom(); + b->encode = mode == SCR_BROTLI_ENCODE; + b->dict = dict; + b->dict_len = dict_len; + ScrZlibError err; + bool failed = scr_brotli_init(b, &err); + for (int i = 0; !failed && i < SCR_BROTLI_PARAMS; i++) { + if (params[i] == SCR_BROTLI_UNSET) continue; + failed = b->encode ? !BrotliEncoderSetParameter(b->enc, (BrotliEncoderParameter)i, params[i]) + : !BrotliDecoderSetParameter(b->dec, (BrotliDecoderParameter)i, params[i]); + } + if (failed) { + scr_brotli_free(b); + scr_brotli_init_failed(); + return false; + } + out->codec_state = b; + out->codec_state_free = scr_brotli_free; + /* ZlibBase's `if (opts)`: a falsy value reads no more options. */ + if (opts == NULL || !scr_dyn_truthy(opts)) return true; + return scr_zlib_base_options(opts, BROTLI_OPERATION_EMIT_METADATA, out); +} + +/* A typed options object is read through its materialized view. */ +static bool scr_brotli_options(int mode, const ScrDyn *opts, ScrZlibOptions *out) { + if (opts == NULL || opts->kind != SCR_DYN_TYPED_REF) return scr_brotli_options_object(mode, opts, out); + ScrDyn *view = scr_dyn_typed_ref_materialize(opts); + bool ok = false; + if (scr_exc_pending()) memset(out, 0, sizeof *out); + else ok = scr_brotli_options_object(mode, view, out); + scr_dyn_release(view); + return ok; +} + +/* Moves the prepared instance out of the options. */ +static ScrZlibEngine *scr_brotli_engine(int mode, ScrZlibOptions *o) { + ScrBrotli *b = o->codec_state; + o->codec_state = NULL; + return scr_zlib_engine_codec(mode, &scr_brotli_codec, b, o->reject_garbage); +} + +static const ScrZlibBrotliHooks scr_brotli_hooks = {scr_brotli_options, scr_brotli_engine}; + +void scr_brotli_install(void) { + scr_zlib_brotli_hooks = &scr_brotli_hooks; +} diff --git a/packages/runtime/src/scr_builtin_values.c b/packages/runtime/src/scr_builtin_values.c index ea240359a..f8dd2b79b 100644 --- a/packages/runtime/src/scr_builtin_values.c +++ b/packages/runtime/src/scr_builtin_values.c @@ -259,3 +259,217 @@ ScrDyn *scr_fs_realpath_value(const ScrDyn *path, const ScrDyn *options, bool na scr_str_release(resolved); return out; } + +/* ── Buffer as a value ─────────────────────────────────────────────────── + * The Buffer function read as a value in JavaScript (bn.js assigns + * `require('buffer').Buffer` to a variable and passes it to + * `allocate(ArrayType, size)`): one function per thread, named Buffer + * with length 3, whose own enumerable properties are Node's, in Node's + * order: poolSize, the statics and Symbol(kIsEncodingSymbol). The statics + * apply Node's argument handling. Calling or constructing Buffer through + * the value, reading its prototype, and the copyBytesFrom and of statics + * throw SC2020. poolSize is a data property the pool reads before each + * decision (scr_buffer_pool_refresh_hook). */ +static SCR_TL ScrDyn *scr_bv_buffer; + +#define SCR_BV_ARG(i) ((i) < argc ? args[i] : scr_dyn_undefined()) + +static ScrDyn *scr_bv_unsupported(const char *what) { + char message[160]; + int n = snprintf(message, sizeof message, "%s through a stored Buffer value is not supported yet", what); + scr_throw_error_msg_code(SCR_ERR_ERROR, message, (size_t)n, "SC2020"); + return NULL; +} + +/* A Buffer result as a value: +1, or NULL with the exception pending. */ +static ScrDyn *scr_bv_bytes(ScrBytes *bytes) { + if (!bytes) return NULL; + ScrBytes *buffer = scr_bytes_as_buffer(bytes); + scr_bytes_release(bytes); + ScrDyn *value = scr_dyn_new_bytes(buffer); + scr_bytes_release(buffer); + return value; +} + +static ScrDyn *scr_bv_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; (void)args; (void)argc; + return scr_bv_unsupported("Calling or constructing Buffer"); +} + +static ScrDyn *scr_bv_from(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + const ScrDyn *value = SCR_BV_ARG(0), *second = SCR_BV_ARG(1), *third = SCR_BV_ARG(2); + if (value->kind != SCR_DYN_STR) return scr_bv_bytes(scr_buffer_from_dyn_range_pooled(value, second, third)); + /* fromString: an encoding that is not a non-empty string means utf8. */ + if (second->kind == SCR_DYN_STR && second->v.str->len > 0) + return scr_bv_bytes(scr_buffer_from_str_checked(value->v.str, second->v.str)); + ScrStr *utf8 = scr_str_new("utf8", 4); + ScrBytes *bytes = scr_buffer_from_str_checked(value->v.str, utf8); + scr_str_release(utf8); + return scr_bv_bytes(bytes); +} + +static ScrDyn *scr_bv_copy_bytes_from(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; (void)args; (void)argc; + return scr_bv_unsupported("Buffer.copyBytesFrom"); +} + +static ScrDyn *scr_bv_of(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; (void)args; (void)argc; + return scr_bv_unsupported("Buffer.of"); +} + +static ScrDyn *scr_bv_alloc(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + return scr_bv_bytes(scr_buffer_alloc_value(SCR_BV_ARG(0), SCR_BV_ARG(1), SCR_BV_ARG(2))); +} + +static ScrDyn *scr_bv_alloc_unsafe(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + const ScrDyn *size = SCR_BV_ARG(0); + if (size->kind != SCR_DYN_NUM) { + scr_dyn_arg_type_fail("size", "of type number", size); + return NULL; + } + return scr_bv_bytes(scr_buffer_alloc_unsafe(size->v.num)); +} + +static ScrDyn *scr_bv_alloc_unsafe_slow(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + const ScrDyn *size = SCR_BV_ARG(0); + if (size->kind != SCR_DYN_NUM) { + scr_dyn_arg_type_fail("size", "of type number", size); + return NULL; + } + return scr_bv_bytes(scr_buffer_alloc_unsafe_slow(size->v.num)); +} + +static ScrDyn *scr_bv_is_buffer(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + const ScrDyn *value = SCR_BV_ARG(0); + return scr_dyn_new_bool(value->kind == SCR_DYN_BYTES && value->buffer); +} + +static ScrDyn *scr_bv_compare(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + double order = scr_buffer_compare_chk(SCR_BV_ARG(0), SCR_BV_ARG(1)); + return scr_exc_pending() ? NULL : scr_dyn_new_num(order); +} + +static ScrDyn *scr_bv_is_encoding(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + const ScrDyn *encoding = SCR_BV_ARG(0); + return scr_dyn_new_bool(encoding->kind == SCR_DYN_STR && encoding->v.str->len > 0 && + scr_bytes_is_encoding(encoding->v.str)); +} + +/* Buffer.concat(list, length): Node validates the list, answers an empty + * Buffer for an empty list before it reads the length, and otherwise + * validates the length as an offset. */ +static ScrDyn *scr_bv_concat(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + const ScrDyn *list = SCR_BV_ARG(0), *length = SCR_BV_ARG(1); + bool empty = list->kind == SCR_DYN_ARR && list->v.arr.len == 0; + if (length->kind == SCR_DYN_UNDEF || empty || list->kind != SCR_DYN_ARR) + return scr_bv_bytes(scr_buffer_concat_dyn(list)); + if (length->kind != SCR_DYN_NUM) { + scr_dyn_arg_type_fail("length", "of type number", length); + return NULL; + } + return scr_bv_bytes(scr_buffer_concat_len_dyn(list, length->v.num)); +} + +static ScrDyn *scr_bv_byte_length(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + double length = scr_buffer_byte_length_value((ScrDyn *)SCR_BV_ARG(0), (ScrDyn *)SCR_BV_ARG(1)); + return scr_exc_pending() ? NULL : scr_dyn_new_num(length); +} + +static ScrDyn *scr_bv_prototype(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; (void)args; (void)argc; + return scr_bv_unsupported("Buffer.prototype"); +} + +/* The pool's setting is the value's poolSize: a number as it is, another + * primitive through ToNumber, anything else NaN (no pooling). */ +static void scr_bv_pool_refresh(void) { + ScrDyn *table = scr_dyn_function_properties(scr_bv_buffer); + const ScrDyn *size = scr_dyn_obj_get(table, "poolSize", 8); + double setting = NAN; + if (size && size->kind == SCR_DYN_NUM) { + setting = size->v.num; + } else if (size && (size->kind == SCR_DYN_STR || size->kind == SCR_DYN_BOOL || size->kind == SCR_DYN_NULL || + size->kind == SCR_DYN_UNDEF)) { + if (!scr_dyn_number_coerce_js(size, &setting)) setting = NAN; + } + scr_dyn_release(table); + scr_buffer_pool_apply(setting); +} + +/* A static `Buffer.poolSize = n` write updates the value's property. */ +static void scr_bv_pool_publish(double size) { + ScrDyn *table = scr_dyn_function_properties(scr_bv_buffer); + scr_dyn_obj_set(table, "poolSize", 8, scr_dyn_new_num(size)); + scr_dyn_release(table); +} + +static void scr_bv_cleanup(void) { + scr_buffer_pool_refresh_hook = NULL; + scr_buffer_pool_publish_hook = NULL; + scr_dyn_release(scr_bv_buffer); + scr_bv_buffer = NULL; +} + +ScrDyn *scr_buffer_constructor_value(void) { + if (scr_bv_buffer) return scr_dyn_retain(scr_bv_buffer); + static const struct { const char *name; uint32_t length; ScrDynThunk thunk; } statics[] = { + {"from", 3, scr_bv_from}, + {"copyBytesFrom", 3, scr_bv_copy_bytes_from}, + {"of", 0, scr_bv_of}, + {"alloc", 3, scr_bv_alloc}, + {"allocUnsafe", 1, scr_bv_alloc_unsafe}, + {"allocUnsafeSlow", 1, scr_bv_alloc_unsafe_slow}, + {"isBuffer", 1, scr_bv_is_buffer}, + {"compare", 2, scr_bv_compare}, + {"isEncoding", 1, scr_bv_is_encoding}, + {"concat", 2, scr_bv_concat}, + {"byteLength", 2, scr_bv_byte_length}, + }; + double pool_size = scr_buffer_pool_size(); + scr_bv_buffer = scr_dyn_new_func(scr_closure_new(NULL, 0), scr_bv_call, 3, "native:Buffer", "Buffer"); + scr_atexit(scr_bv_cleanup); + ScrDyn *table = scr_dyn_function_properties(scr_bv_buffer); + ScrDyn *getter = scr_dyn_new_func(scr_closure_new(NULL, 0), scr_bv_prototype, 0, "native:function", "get prototype"); + scr_dyn_obj_set_accessor(table, "prototype", 9, getter, NULL); + ScrDynEntry *entry = &table->v.obj.entries[table->v.obj.len - 1]; + entry->enumerable = entry->configurable = false; + scr_dyn_obj_set(table, "poolSize", 8, scr_dyn_new_num(pool_size)); + ScrDyn *is_encoding = NULL; + for (size_t i = 0; i < sizeof statics / sizeof statics[0]; i++) { + ScrDyn *fn = scr_dyn_new_func(scr_closure_new(NULL, 0), statics[i].thunk, statics[i].length, "native:function", + statics[i].name); + if (statics[i].thunk == scr_bv_is_encoding) is_encoding = scr_dyn_retain(fn); + scr_dyn_obj_set(table, statics[i].name, strlen(statics[i].name), fn); + } + scr_dyn_release(table); + ScrStr *description = scr_str_new("kIsEncodingSymbol", 17); + ScrSym *symbol = scr_sym_new(description); + scr_str_release(description); + ScrDyn *key = scr_dyn_new_symbol(symbol); + scr_sym_release(symbol); + scr_dyn_symbol_key_set(scr_bv_buffer, key, is_encoding); + scr_dyn_release(key); + scr_dyn_release(is_encoding); + scr_buffer_pool_refresh_hook = scr_bv_pool_refresh; + scr_buffer_pool_publish_hook = scr_bv_pool_publish; + return scr_dyn_retain(scr_bv_buffer); +} + +void scr_buffer_install_value(void) { scr_bytes_buffer_constructor_fn = scr_buffer_constructor_value; } + +ScrDyn *scr_buffer_static_value(const ScrStr *name) { + ScrDyn *buffer = scr_buffer_constructor_value(); + ScrDyn *member = scr_dyn_fn_get(buffer, name->data, name->len); + scr_dyn_release(buffer); + return member; +} diff --git a/packages/runtime/src/scr_bytes.c b/packages/runtime/src/scr_bytes.c index 7bcfd0cfb..3926bf83e 100644 --- a/packages/runtime/src/scr_bytes.c +++ b/packages/runtime/src/scr_bytes.c @@ -874,6 +874,21 @@ ScrBytes *scr_bytes_from_dyn(ScrBytesElem elem, const ScrDyn *value, bool from) // Constructors drain array iterators before element coercion. Node's .from // fast path reads ordinary array elements during conversion instead. Strings // supplied to .from iterate by code point (set reads UTF-16 code units). + // An object with a Symbol.iterator method iterates; others are array-like. + ScrDyn *method = scr_dyn_iterator_method(value); + if (method) { + ScrDyn *items = scr_dyn_iterate_with(value, method); + scr_dyn_release(method); + if (!items) return NULL; + ScrBytes *out = scr_bytes_new(elem, (double)items->v.arr.len); + if (out && !scr_bytes_copy_array_like(out, items, 0, out->len)) { + scr_bytes_release(out); + out = NULL; + } + scr_dyn_release(items); + return out; + } + if (scr_exc_pending()) return NULL; if ((!from && value->kind == SCR_DYN_ARR) || (from && value->kind == SCR_DYN_STR)) { ScrDyn *items = scr_dyn_iter_pack(value, NULL); if (!items) return NULL; @@ -2787,16 +2802,30 @@ static SCR_TL bool scr_pool_registered; /* scr_buffer_pool_reset runs at context /* Buffer.poolSize >>> 1, kept with the setting. */ static SCR_TL size_t scr_pool_half = 32768; -double scr_buffer_pool_size(void) { return scr_pool_setting; } +SCR_TL void (*scr_buffer_pool_refresh_hook)(void) = NULL; +SCR_TL void (*scr_buffer_pool_publish_hook)(double size) = NULL; -void scr_buffer_set_pool_size(double size) { +double scr_buffer_pool_size(void) { + if (scr_buffer_pool_refresh_hook) scr_buffer_pool_refresh_hook(); + return scr_pool_setting; +} + +void scr_buffer_pool_apply(double size) { scr_pool_setting = size; double t = isfinite(size) ? fmod(trunc(size), 4294967296.0) : 0; if (t < 0) t += 4294967296.0; scr_pool_half = (size_t)((uint32_t)t >> 1); } -static size_t scr_pool_threshold(void) { return scr_pool_half; } +void scr_buffer_set_pool_size(double size) { + scr_buffer_pool_apply(size); + if (scr_buffer_pool_publish_hook) scr_buffer_pool_publish_hook(size); +} + +static size_t scr_pool_threshold(void) { + if (scr_buffer_pool_refresh_hook) scr_buffer_pool_refresh_hook(); + return scr_pool_half; +} /* Bytes left in the slab. Aligning can move the offset past the end of a * slab whose size is not a multiple of 8; nothing is left then. */ @@ -3300,6 +3329,55 @@ double scr_bytes_index_of_num(const ScrBytes *b, double v, double off, bool fwd) return scr_bytes_index_of(b, &needle, off, 1, fwd); } +/* The first index a TypedArray search from `from` starts at + * (ToIntegerOrInfinity, then relative to the end when negative), or false + * when it starts past the last element. */ +static bool scr_bytes_search_start(size_t len, double from, size_t *start) { + double n = from != from ? 0 : trunc(from); + if (n >= (double)len) return false; + if (n < 0) n = n + (double)len < 0 ? 0 : n + (double)len; + *start = (size_t)n; + return true; +} + +double scr_bytes_index_of_elem(const ScrBytes *b, double x, double from, bool last) { + /* A Buffer viewed as a Uint8Array keeps Buffer's own search: the needle + * is taken as a byte, and a NaN byteOffset searches the whole buffer. */ + if (scr_bytes_is_buffer(b)) return scr_bytes_index_of_num(b, x, from, !last); + SCR_BYTES_SNAPSHOT(b); + size_t len = b->len; + size_t size = scr_bytes_elem_size(b->elem); + if (len == 0 || x != x) return -1; + if (!last) { + size_t k; + if (!scr_bytes_search_start(len, from, &k)) return -1; + for (; k < len; k++) + if (scr_bytes_decode(b->elem, b->data + k * size) == x) return (double)k; + return -1; + } + double n = from != from ? 0 : trunc(from); + double k = n >= 0 ? (n < (double)len - 1 ? n : (double)len - 1) : (double)len + n; + if (k < 0) return -1; + for (size_t i = (size_t)k + 1; i-- > 0;) + if (scr_bytes_decode(b->elem, b->data + i * size) == x) return (double)i; + return -1; +} + +bool scr_bytes_includes_elem(const ScrBytes *b, double x, double from) { + if (scr_bytes_is_buffer(b)) return scr_bytes_index_of_num(b, x, from, true) >= 0; + SCR_BYTES_SNAPSHOT(b); + size_t len = b->len; + size_t size = scr_bytes_elem_size(b->elem); + size_t k; + if (len == 0 || !scr_bytes_search_start(len, from, &k)) return false; + bool nan = x != x; + for (; k < len; k++) { + double v = scr_bytes_decode(b->elem, b->data + k * size); + if (v == x || (nan && v != v)) return true; + } + return false; +} + /* fill's shared core over a normalized byte pattern. nargs counts the * present offset/end args. An EMPTY pattern zero-fills when zero_ok * (Node's '' string) and is the constant TypeError otherwise (an empty diff --git a/packages/runtime/src/scr_bytes.h b/packages/runtime/src/scr_bytes.h index 9b382dda5..7c0a935cc 100644 --- a/packages/runtime/src/scr_bytes.h +++ b/packages/runtime/src/scr_bytes.h @@ -357,6 +357,18 @@ ScrStr *scr_text_decode(const ScrBytes *b); double scr_text_decoder_encoding(const ScrDyn *label); ScrStr *scr_text_decoder_name(double encoding); ScrDyn *scr_text_encode_into(const ScrDyn *source, const ScrDyn *destination); +/* TextEncoder and TextDecoder records as checked-dynamic handles + * (scr_text_codec_dyn.c, linked by the textCodecDyn runtime feature). The + * boxes borrow the record and retain it through `retain`; a decoder also + * names the byte offsets of its encoding (f64), fatal and ignoreBOM (i8) + * and state (dyn) fields. from_dyn answers the retained record or throws + * the path-annotated TypeError and answers NULL. */ +ScrDyn *scr_text_encoder_to_dyn(void *rec, void *(*retain)(void *), void (*release)(void *)); +ScrDyn *scr_text_decoder_to_dyn(void *rec, void *(*retain)(void *), void (*release)(void *), + size_t encoding, size_t fatal, size_t ignore_bom, size_t state); +void *scr_text_codec_from_dyn(const ScrDyn *d, bool decoder, const struct ScrDynPath *path, + const char *want); +bool scr_text_codec_is(const ScrDyn *d, bool decoder); bool scr_buffer_is_ascii(const ScrDyn *input); bool scr_buffer_is_utf8(const ScrDyn *input); ScrBytes *scr_buffer_transcode(const ScrDyn *source, const ScrDyn *from, const ScrDyn *to); @@ -388,6 +400,13 @@ ScrBytes *scr_bytes_from_str(const ScrStr *s, const ScrStr *enc); * slab. A slab lives while any of its views does. All return +1 Buffers. */ double scr_buffer_pool_size(void); void scr_buffer_set_pool_size(double size); +/* The setting without publishing it (scr_buffer_pool_refresh_hook). */ +void scr_buffer_pool_apply(double size); +/* Set while a Buffer function value exists (scr_builtin_values.c): its + * poolSize property is the setting, which the refresh hook reads before + * each pool decision and the publish hook receives from static writes. */ +extern SCR_TL void (*scr_buffer_pool_refresh_hook)(void); +extern SCR_TL void (*scr_buffer_pool_publish_hook)(double size); ScrBytes *scr_buffer_alloc(double size); /* zeroed, never pooled */ ScrBytes *scr_buffer_alloc_unsafe(double size); /* pooled */ ScrBytes *scr_buffer_alloc_unsafe_slow(double size); /* never pooled */ @@ -398,6 +417,15 @@ ScrBytes *scr_buffer_from_arr(const ScrArr *arr); /* number[] */ ScrBytes *scr_buffer_from_dyn_pooled(const ScrDyn *value, const ScrStr *encoding); ScrBytes *scr_buffer_from_dyn_ref_pooled(const ScrDyn *value, const ScrStr *encoding); ScrBytes *scr_buffer_from_dyn_offset_pooled(const ScrDyn *value, const ScrDyn *second); +/* Buffer.from(value, byteOffset, length): an ArrayBuffer's view with + * Node's fromArrayBuffer checks; other values as the two-argument form. */ +ScrBytes *scr_buffer_from_dyn_range_pooled(const ScrDyn *value, const ScrDyn *second, + const ScrDyn *third); +/* The deprecated Buffer constructor's DEP0005 state (scr_bytes_io.c): a + * call from node_modules counts toward Node's 10000-call check. */ +extern SCR_TL bool scr_buffer_ctor_warned; +extern SCR_TL uint64_t scr_buffer_ctor_checks; +void scr_buffer_deprecation_count(void); ScrBytes *scr_buffer_concat(const ScrArr *list); ScrBytes *scr_buffer_concat_len(const ScrArr *list, double total); /* The untyped-list forms: Node's ERR_INVALID_ARG_TYPE checks on the list @@ -406,6 +434,17 @@ ScrBytes *scr_buffer_concat_dyn(const struct ScrDyn *list); ScrBytes *scr_buffer_alloc_value(const struct ScrDyn *size, const struct ScrDyn *fill, const struct ScrDyn *encoding); struct ScrDyn *scr_fs_realpath_value(const struct ScrDyn *path, const struct ScrDyn *options, bool native); +/* The Buffer function as a value (scr_builtin_values.c), one per thread. +1. */ +struct ScrDyn *scr_buffer_constructor_value(void); +/* Installs that function as a Buffer's `.constructor` (the program's + * %main calls it when it reads the value): scr_bytes_buffer_constructor_fn. */ +void scr_buffer_install_value(void); +extern struct ScrDyn *(*scr_bytes_buffer_constructor_fn)(void); +/* A static of that value by name (`Buffer.alloc` read as a value). +1. */ +struct ScrDyn *scr_buffer_static_value(const ScrStr *name); +/* `Buffer.prototype[key]` (scr_bytes_dyn.c). +1, or NULL with the + * exception pending. */ +struct ScrDyn *scr_buffer_prototype_member(const struct ScrDyn *key); ScrBytes *scr_buffer_concat_len_dyn(const struct ScrDyn *list, double total); /* Drops the pool's slab reference and restores Buffer.poolSize (process * exit audits and library session resets). */ @@ -499,6 +538,16 @@ ScrEmitter *scr_emitter_set_max_chk(ScrEmitter *em, const ScrDyn *n); void scr_emitter_set_default_max_chk(const ScrDyn *n, const ScrStr *name); double scr_bytes_index_of(const ScrBytes *b, const ScrBytes *needle, double off, double align, bool fwd); double scr_bytes_index_of_num(const ScrBytes *b, double v, double off, bool fwd); +/* TypedArray indexOf (last false) and lastIndexOf (last true) over any + * element kind, with strict equality: NaN is never found and -0 matches 0. + * `from` is the fromIndex argument (ToIntegerOrInfinity applies); the + * caller passes 0, or +Infinity for lastIndexOf, when it is omitted. A + * Buffer (a Uint8Array receiver can be one) searches as Buffer's + * indexOf and lastIndexOf do. */ +double scr_bytes_index_of_elem(const ScrBytes *b, double x, double from, bool last); +/* TypedArray includes: indexOf's search from `from` with SameValueZero, + * so NaN is found. A Buffer searches as Buffer's includes does. */ +bool scr_bytes_includes_elem(const ScrBytes *b, double x, double from); ScrBytes *scr_bytes_fill(ScrBytes *b, const ScrBytes *pattern, double nargs, double offset, double end); ScrBytes *scr_bytes_fill_num(ScrBytes *b, double v, double nargs, double offset, double end); ScrBytes *scr_bytes_fill_str(ScrBytes *b, const ScrStr *s, const ScrStr *enc, diff --git a/packages/runtime/src/scr_bytes_dyn.c b/packages/runtime/src/scr_bytes_dyn.c index c492d43b1..e390ddfce 100644 --- a/packages/runtime/src/scr_bytes_dyn.c +++ b/packages/runtime/src/scr_bytes_dyn.c @@ -592,3 +592,215 @@ ScrDyn *scr_dyn_invoke_buffer(ScrDyn *recv, ScrDyn *callee, const char *method, } return scr_dyn_invoke_prepared_id(recv, callee, method, mid_slot, args, argc, what); } + +/* A fixed-width read or write name (readUInt8, writeDoubleLE, ...). */ +static bool scr_bytes_dyn_num_method_name(const char *method) { + bool read = !strncmp(method, "read", 4); + if (!read && strncmp(method, "write", 5)) return false; + const char *rest = method + (read ? 4 : 5); + for (size_t at = 0; at < sizeof scr_bytes_dyn_names / sizeof scr_bytes_dyn_names[0]; at++) + if (!strcmp(rest, scr_bytes_dyn_names[at].name)) return true; + return false; +} + +/* ── Buffer.prototype members read as values ──────────────────────────── + * `Buffer.prototype[name]` (smart-buffer's typeof probe for the BigInt + * methods): an own member answers one function per name, with the name and + * length Node's function has; the parent and offset getters, read on the + * prototype itself, answer undefined; constructor is the Buffer value. + * Members inherited from Uint8Array.prototype and Object.prototype, symbol + * keys and object keys throw SC2020, and other keys answer undefined. The + * functions run the method on a Buffer receiver (`.call(buf, ...)`); other + * receivers throw SC2020. */ +static const struct { const char *key; const char *name; uint32_t length; } scr_buffer_proto_own[] = { + {"readBigUInt64LE", "readBigUInt64LE", 0}, + {"readBigUInt64BE", "readBigUInt64BE", 0}, + {"readBigUint64LE", "readBigUInt64LE", 0}, + {"readBigUint64BE", "readBigUInt64BE", 0}, + {"readBigInt64LE", "readBigInt64LE", 0}, + {"readBigInt64BE", "readBigInt64BE", 0}, + {"writeBigUInt64LE", "writeBigUInt64LE", 1}, + {"writeBigUInt64BE", "writeBigUInt64BE", 1}, + {"writeBigUint64LE", "writeBigUInt64LE", 1}, + {"writeBigUint64BE", "writeBigUInt64BE", 1}, + {"writeBigInt64LE", "writeBigInt64LE", 1}, + {"writeBigInt64BE", "writeBigInt64BE", 1}, + {"readUIntLE", "readUIntLE", 2}, + {"readUInt32LE", "", 1}, + {"readUInt16LE", "", 1}, + {"readUInt8", "", 1}, + {"readUIntBE", "readUIntBE", 2}, + {"readUInt32BE", "", 1}, + {"readUInt16BE", "", 1}, + {"readUintLE", "readUIntLE", 2}, + {"readUint32LE", "", 1}, + {"readUint16LE", "", 1}, + {"readUint8", "", 1}, + {"readUintBE", "readUIntBE", 2}, + {"readUint32BE", "", 1}, + {"readUint16BE", "", 1}, + {"readIntLE", "readIntLE", 2}, + {"readInt32LE", "", 1}, + {"readInt16LE", "", 1}, + {"readInt8", "", 1}, + {"readIntBE", "readIntBE", 2}, + {"readInt32BE", "", 1}, + {"readInt16BE", "", 1}, + {"writeUIntLE", "writeUIntLE", 3}, + {"writeUInt32LE", "writeUInt32LE", 1}, + {"writeUInt16LE", "writeUInt16LE", 1}, + {"writeUInt8", "writeUInt8", 1}, + {"writeUIntBE", "writeUIntBE", 3}, + {"writeUInt32BE", "writeUInt32BE", 1}, + {"writeUInt16BE", "writeUInt16BE", 1}, + {"writeUintLE", "writeUIntLE", 3}, + {"writeUint32LE", "writeUInt32LE", 1}, + {"writeUint16LE", "writeUInt16LE", 1}, + {"writeUint8", "writeUInt8", 1}, + {"writeUintBE", "writeUIntBE", 3}, + {"writeUint32BE", "writeUInt32BE", 1}, + {"writeUint16BE", "writeUInt16BE", 1}, + {"writeIntLE", "writeIntLE", 3}, + {"writeInt32LE", "writeInt32LE", 1}, + {"writeInt16LE", "writeInt16LE", 1}, + {"writeInt8", "writeInt8", 1}, + {"writeIntBE", "writeIntBE", 3}, + {"writeInt32BE", "writeInt32BE", 1}, + {"writeInt16BE", "writeInt16BE", 1}, + {"readFloatLE", "readFloatForwards", 0}, + {"readFloatBE", "readFloatBackwards", 0}, + {"readDoubleLE", "readDoubleForwards", 0}, + {"readDoubleBE", "readDoubleBackwards", 0}, + {"writeFloatLE", "writeFloatForwards", 1}, + {"writeFloatBE", "writeFloatBackwards", 1}, + {"writeDoubleLE", "writeDoubleForwards", 1}, + {"writeDoubleBE", "writeDoubleBackwards", 1}, + {"asciiSlice", "", 2}, + {"base64Slice", "", 2}, + {"base64urlSlice", "", 2}, + {"latin1Slice", "", 2}, + {"hexSlice", "", 2}, + {"ucs2Slice", "", 2}, + {"utf8Slice", "", 2}, + {"asciiWrite", "", 3}, + {"base64Write", "", 3}, + {"base64urlWrite", "", 3}, + {"latin1Write", "", 3}, + {"hexWrite", "", 3}, + {"ucs2Write", "", 3}, + {"utf8Write", "", 3}, + {"parent", NULL, 0}, + {"offset", NULL, 0}, + {"copy", "copy", 4}, + {"toString", "toString", 3}, + {"equals", "equals", 1}, + {"inspect", "inspect", 2}, + {"compare", "compare", 5}, + {"indexOf", "indexOf", 4}, + {"lastIndexOf", "lastIndexOf", 4}, + {"includes", "includes", 4}, + {"fill", "fill", 4}, + {"write", "write", 4}, + {"toJSON", "toJSON", 0}, + {"subarray", "subarray", 2}, + {"slice", "slice", 2}, + {"swap16", "swap16", 0}, + {"swap32", "swap32", 0}, + {"swap64", "swap64", 0}, + {"toLocaleString", "toString", 3}, +}; + +static const char *const scr_buffer_proto_inherited[] = { + "BYTES_PER_ELEMENT", "buffer", "byteLength", "byteOffset", "length", "entries", "keys", "values", + "at", "copyWithin", "every", "filter", "find", "findIndex", "findLast", "findLastIndex", + "forEach", "join", "map", "reverse", "reduce", "reduceRight", "set", "some", "sort", "toReversed", + "toSorted", "with", "__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", + "__lookupSetter__", "isPrototypeOf", "propertyIsEnumerable", "valueOf", "__proto__", +}; + +static SCR_TL ScrDyn *scr_buffer_proto_fns; + +static void scr_buffer_proto_cleanup(void) { + scr_dyn_release(scr_buffer_proto_fns); + scr_buffer_proto_fns = NULL; +} + +static ScrDyn *scr_buffer_proto_unsupported(const char *format, const char *name) { + char message[200]; + int n = snprintf(message, sizeof message, format, name); + scr_throw_error_msg_code(SCR_ERR_ERROR, message, (size_t)n, "SC2020"); + return NULL; +} + +static ScrDyn *scr_buffer_proto_run(ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc) { + if (!scr_bytes_dyn_buffer(recv)) + return scr_buffer_proto_unsupported("Buffer.prototype.%s on a receiver that is not a Buffer is not supported yet", + method); + const char *what = "Buffer.prototype method"; + /* The method-id cache slot of a call site, here a fresh one per call. */ + int32_t slot = -1; + int32_t *mid = &slot; + if (scr_bytes_dyn_num_method_name(method)) return scr_dyn_invoke_buffer_num(recv, NULL, method, mid, args, argc, what); + if (!strcmp(method, "write")) return scr_dyn_invoke_buffer_write(recv, NULL, method, mid, args, argc, what); + if (!strcmp(method, "fill")) return scr_dyn_invoke_buffer_fill(recv, NULL, method, mid, args, argc, what); + if (!strcmp(method, "toString") || !strcmp(method, "toLocaleString")) + return scr_dyn_invoke_buffer_to_string(recv, NULL, "toString", mid, args, argc, what); + if (!strcmp(method, "indexOf") || !strcmp(method, "lastIndexOf") || !strcmp(method, "includes")) + return scr_dyn_invoke_buffer_search(recv, NULL, method, mid, args, argc, what); + if (!strcmp(method, "subarray") || !strcmp(method, "slice")) + return scr_dyn_invoke(recv, method, args, argc, what); + if (!strcmp(method, "inspect")) + return scr_buffer_proto_unsupported("Buffer.prototype.%s through a stored value is not supported yet", method); + return scr_dyn_invoke_buffer(recv, NULL, method, mid, args, argc, what); +} + +static ScrDyn *scr_buffer_proto_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + ScrDyn *recv = scr_dyn_this_get(); + ScrDyn *result = scr_buffer_proto_run(recv, closure->fn, args, argc); + scr_dyn_release(recv); + return result; +} + +ScrDyn *scr_buffer_prototype_member(const ScrDyn *key) { + if (key->kind == SCR_DYN_SYMBOL) + return scr_buffer_proto_unsupported("Buffer.prototype members keyed by %s are not supported yet", "symbols"); + if (key->kind != SCR_DYN_STR) { + if (key->kind == SCR_DYN_NUM || key->kind == SCR_DYN_BOOL || key->kind == SCR_DYN_UNDEF || + key->kind == SCR_DYN_NULL || key->kind == SCR_DYN_BIGINT) + return scr_dyn_retain(scr_dyn_undefined()); + return scr_buffer_proto_unsupported("Buffer.prototype members keyed by %s are not supported yet", "objects"); + } + const ScrStr *name = key->v.str; + if (name->len == 11 && memcmp(name->data, "constructor", 11) == 0) { + /* A program that reads the Buffer function's value answers it (the + * builtin-values unit installs the hook); otherwise Buffer keeps its + * builtin identity token, as typed array reads do (scr_json.c). */ + if (scr_bytes_buffer_constructor_fn) return scr_bytes_buffer_constructor_fn(); + ScrStr *token = scr_str_new("[builtin Buffer]", 16); + ScrDyn *result = scr_dyn_new_str(token); + scr_str_release(token); + return result; + } + for (size_t i = 0; i < sizeof scr_buffer_proto_inherited / sizeof scr_buffer_proto_inherited[0]; i++) { + const char *inherited = scr_buffer_proto_inherited[i]; + if (strlen(inherited) == name->len && memcmp(inherited, name->data, name->len) == 0) + return scr_buffer_proto_unsupported("Buffer.prototype.%s is not supported yet", inherited); + } + for (size_t i = 0; i < sizeof scr_buffer_proto_own / sizeof scr_buffer_proto_own[0]; i++) { + const char *own = scr_buffer_proto_own[i].key; + if (strlen(own) != name->len || memcmp(own, name->data, name->len) != 0) continue; + if (!scr_buffer_proto_own[i].name) return scr_dyn_retain(scr_dyn_undefined()); + if (!scr_buffer_proto_fns) { + scr_buffer_proto_fns = scr_dyn_new_obj_null_proto(); + scr_atexit(scr_buffer_proto_cleanup); + } + ScrDyn *fn = scr_dyn_obj_get(scr_buffer_proto_fns, own, name->len); + if (!fn) { + fn = scr_dyn_new_func(scr_closure_new((void *)own, 0), scr_buffer_proto_call, scr_buffer_proto_own[i].length, + "native:function", scr_buffer_proto_own[i].name); + scr_dyn_obj_set(scr_buffer_proto_fns, own, name->len, fn); + } + return scr_dyn_retain(fn); + } + return scr_dyn_retain(scr_dyn_undefined()); +} diff --git a/packages/runtime/src/scr_bytes_io.c b/packages/runtime/src/scr_bytes_io.c index 137208938..253cad1da 100644 --- a/packages/runtime/src/scr_bytes_io.c +++ b/packages/runtime/src/scr_bytes_io.c @@ -59,6 +59,10 @@ static bool scr_fs_read_utf8(ScrStr *encoding) { return encoding->len == 0 || scr_bytes_encoding_is_utf8(encoding); } +void scr_fs_check_read_encoding(ScrStr *encoding) { + (void)scr_fs_read_encoding_valid(encoding); +} + ScrStr *scr_fs_read_file_encoded(ScrStr *path, ScrStr *encoding) { if (!scr_fs_read_encoding_valid(encoding)) return NULL; if (scr_fs_read_utf8(encoding)) return scr_fs_read_file(path); @@ -1299,13 +1303,16 @@ ScrBytes *scr_buffer_concat_len_dyn(const ScrDyn *list, double total) { return scr_buffer_concat_dyn_impl(list, total, true); } -/* Buffer.from(arrayBuffer, byteOffset) over a checked ArrayBuffer or - * SharedArrayBuffer, as Node's fromArrayBuffer: a Buffer from the offset to - * the end of the buffer that shares its memory. The offset converts with - * ToNumber, where a missing offset (NULL) or undefined and NaN are 0; an - * offset past the end throws ERR_BUFFER_OUT_OF_BOUNDS, and one at or below - * -1 V8's start-offset RangeError. NULL with the exception pending. */ -static ScrBytes *scr_buffer_from_storage(const ScrDyn *storage, const ScrDyn *offset_value) { +/* Buffer.from(arrayBuffer, byteOffset, length) over a checked ArrayBuffer + * or SharedArrayBuffer, as Node's fromArrayBuffer: a Buffer that shares its + * memory. The offset converts with ToNumber, where a missing offset (NULL) + * or undefined and NaN are 0; an offset past the end throws + * ERR_BUFFER_OUT_OF_BOUNDS, and one at or below -1 V8's start-offset + * RangeError. A missing length runs to the end; a given one converts with + * ToNumber, past the end throws ERR_BUFFER_OUT_OF_BOUNDS for "length", and + * zero, negative and NaN lengths are 0. NULL with the exception pending. */ +static ScrBytes *scr_buffer_from_storage(const ScrDyn *storage, const ScrDyn *offset_value, + const ScrDyn *length_value) { double offset = 0; if (offset_value && offset_value->kind != SCR_DYN_UNDEF) { if (!scr_dyn_number_coerce_js(offset_value, &offset)) return NULL; @@ -1313,11 +1320,26 @@ static ScrBytes *scr_buffer_from_storage(const ScrDyn *storage, const ScrDyn *of } ScrBytes *bytes = storage->v.handle.ptr; ScrBytes *owner = bytes->backing ? bytes->backing : bytes; - if ((double)scr_bytes_byte_len(owner) - offset < 0) { + double max_length = (double)scr_bytes_byte_len(owner) - offset; + if (max_length < 0) { static const char msg[] = "\"offset\" is outside of buffer bounds"; scr_throw_error_msg_code(SCR_ERR_RANGE, msg, sizeof msg - 1, "ERR_BUFFER_OUT_OF_BOUNDS"); return NULL; } + bool has_length = length_value && length_value->kind != SCR_DYN_UNDEF; + double length = max_length; + if (has_length) { + if (!scr_dyn_number_coerce_js(length_value, &length)) return NULL; + if (length > 0) { + if (length > max_length) { + static const char msg[] = "\"length\" is outside of buffer bounds"; + scr_throw_error_msg_code(SCR_ERR_RANGE, msg, sizeof msg - 1, "ERR_BUFFER_OUT_OF_BOUNDS"); + return NULL; + } + } else { + length = 0; + } + } double start = trunc(offset); if (start < 0) { /* The offset as JavaScript prints it (-1.5, -1e+21, -Infinity). */ @@ -1331,7 +1353,7 @@ static ScrBytes *scr_buffer_from_storage(const ScrDyn *storage, const ScrDyn *of scr_throw_error(SCR_ERR_RANGE, message); return NULL; } - ScrBytes *out = scr_bytes_buffer_view(bytes, SCR_BYTES_U8, start, false, 0); + ScrBytes *out = scr_bytes_buffer_view(bytes, SCR_BYTES_U8, start, has_length, trunc(length)); if (out) out->is_buffer = true; return out; } @@ -1340,7 +1362,7 @@ ScrBytes *scr_buffer_from_dyn(const ScrDyn *value, const ScrStr *encoding) { if (value->kind == SCR_DYN_STR) return scr_bytes_from_str(value->v.str, encoding); /* The second argument here is an encoding name, which as a byte offset * converts to NaN: offset 0. */ - if (scr_buffer_storage_is(value)) return scr_buffer_from_storage(value, NULL); + if (scr_buffer_storage_is(value)) return scr_buffer_from_storage(value, NULL, NULL); if (value->kind == SCR_DYN_BYTES) return scr_bytes_convert(SCR_BYTES_U8, value->v.bytes); if (value->kind == SCR_DYN_ARR) { return scr_buffer_from_array_like(value, (double)value->v.arr.len); @@ -1386,7 +1408,7 @@ ScrBytes *scr_buffer_from_dyn_pooled(const ScrDyn *value, const ScrStr *encoding return scr_buffer_from_bytes(value->v.bytes); /* A view of an ArrayBuffer shares its memory, so it never comes from the * pool. The second argument here is an encoding name: offset 0. */ - if (scr_buffer_storage_is(value)) return scr_buffer_from_storage(value, NULL); + if (scr_buffer_storage_is(value)) return scr_buffer_from_storage(value, NULL, NULL); ScrBytes *out = scr_buffer_from_dyn(value, encoding); if (!out) return NULL; out->is_buffer = true; @@ -1413,21 +1435,51 @@ ScrBytes *scr_buffer_from_dyn_ref_pooled(const ScrDyn *value, const ScrStr *enco /* Buffer.from(value, second) over checked values whose second argument is * not a literal encoding name: an ArrayBuffer or SharedArrayBuffer takes it - * as the byte offset (scr_buffer_from_storage); other values convert as - * scr_buffer_from_dyn_ref_pooled with utf8. */ + * as the byte offset (scr_buffer_from_storage); a string takes it as the + * encoding (Node's fromString: utf8 unless it is a non-empty string, which + * must name a Buffer encoding or throw ERR_UNKNOWN_ENCODING); other values + * convert as scr_buffer_from_dyn_ref_pooled with utf8. */ ScrBytes *scr_buffer_from_dyn_offset_pooled(const ScrDyn *value, const ScrDyn *second) { + if (value->kind == SCR_DYN_STR) { + if (second->kind == SCR_DYN_STR && second->v.str->len > 0) + return scr_buffer_from_str_checked(value->v.str, second->v.str); + ScrStr *utf8 = scr_str_new("utf8", 4); + ScrBytes *out = scr_buffer_from_str(value->v.str, utf8); + scr_str_release(utf8); + return out; + } if (!scr_buffer_storage_is(value)) { ScrStr *utf8 = scr_str_new("utf8", 4); ScrBytes *out = scr_buffer_from_dyn_ref_pooled(value, utf8); scr_str_release(utf8); return out; } - return scr_buffer_from_storage(value, second); + return scr_buffer_from_storage(value, second, NULL); +} + +/* Buffer.from(value, second, third): an ArrayBuffer or SharedArrayBuffer + * takes them as its byte offset and length; other values convert as + * scr_buffer_from_dyn_offset_pooled does, without the third argument. */ +ScrBytes *scr_buffer_from_dyn_range_pooled(const ScrDyn *value, const ScrDyn *second, + const ScrDyn *third) { + if (!scr_buffer_storage_is(value)) return scr_buffer_from_dyn_offset_pooled(value, second); + return scr_buffer_from_storage(value, second, third); +} + +/* The deprecated Buffer constructor's DEP0005 state, Node's + * bufferWarningAlreadyEmitted and nodeModulesCheckCounter: a call from + * node_modules only counts toward the 10000-call check, and + * scr_buffer_deprecation (scr_async_dyn.c) reports for the others. */ +SCR_TL bool scr_buffer_ctor_warned = false; +SCR_TL uint64_t scr_buffer_ctor_checks = 0; + +void scr_buffer_deprecation_count(void) { + if (!scr_buffer_ctor_warned) scr_buffer_ctor_checks++; } /* new Buffer(number, encoding) — the deprecated ctor's string arm with a - * non-string first argument: Node's exact ERR_INVALID_ARG_TYPE (the - * throwing path never fires DEP0005, so compiled silence matches). */ + * non-string first argument: Node's exact ERR_INVALID_ARG_TYPE, thrown + * after scr_buffer_deprecation has reported DEP0005. */ ScrBytes *scr_buffer_new_string_fail(const ScrDyn *got) { scr_dyn_arg_type_fail("string", "of type string", got); return NULL; diff --git a/packages/runtime/src/scr_child.c b/packages/runtime/src/scr_child.c index a70b10894..b30e8f1aa 100644 --- a/packages/runtime/src/scr_child.c +++ b/packages/runtime/src/scr_child.c @@ -1411,6 +1411,10 @@ struct ScrChildStream { size_t n_data, cap_data; ScrChildStreamEndL *end_ls; size_t n_end, cap_end; + /* The AsyncLocalStorage context of the spawn() call that created the + * stream (+1, NULL outside a store): its events run in it, as Node's + * pipe handles run theirs. */ + ScrAlsCtx *als; struct ScrChildStream *next; /* the service registry (+1) */ }; @@ -1439,6 +1443,11 @@ struct ScrChildWriter { size_t n_finish, cap_finish; ScrChildErrEntry *err_ls; size_t n_write_err, cap_write_err; + /* AsyncLocalStorage contexts (+1 each, NULL outside a store): the + * latest write() call's, for 'drain' and 'error' (the spawn() call's + * until the first write), and the end() call's, for 'finish'. */ + ScrAlsCtx *als; + ScrAlsCtx *end_als; struct ScrChildWriter *next; /* the service registry (+1) */ }; @@ -1485,6 +1494,9 @@ struct ScrChild { ScrChildWriter *in_stream; ScrDyn *out_dyn, *err_dyn, *in_dyn; ScrIpc *ipc; + /* The AsyncLocalStorage context of the spawn() call (+1, NULL outside + * a store): 'spawn', 'error', 'exit' and 'close' run in it. */ + ScrAlsCtx *als; struct ScrChild *next; /* the pending registry */ }; @@ -1543,6 +1555,7 @@ void scr_child_release(ScrChild *c) { scr_child_stream_release(c->err_stream); scr_child_writer_release(c->in_stream); scr_ipc_release(c->ipc); + scr_als_ctx_release(c->als); if (c->proc != NULL) CloseHandle(c->proc); free(c); } @@ -1575,6 +1588,7 @@ void scr_child_stream_release(ScrChildStream *s) { if (s->h != NULL) CloseHandle(s->h); scr_str_release(s->encoding); free(s->capture_data); + scr_als_ctx_release(s->als); free(s); } } @@ -1594,6 +1608,7 @@ static ScrChildStream *scr_child_stream_new(HANDLE h) { ScrChildStream *s = calloc(1, sizeof *s); if (!s) scr_child_oom(); s->rc = 1; + s->als = scr_als_capture(); s->h = h; if (h == NULL) { s->eof = true; @@ -1640,8 +1655,16 @@ static ScrStr *scr_child_stream_capture_value(ScrChildStream *s) { : scr_str_new((const char *)s->capture_data, s->capture_len); } -static void scr_child_stream_emit(ScrChildStream *s, ScrBytes *bytes, - ScrStr *text) { +static void scr_child_stream_emit_in(ScrChildStream *s, ScrBytes *bytes, ScrStr *text); + +/* 'data' runs in the spawn() call's context. */ +static void scr_child_stream_emit(ScrChildStream *s, ScrBytes *bytes, ScrStr *text) { + ScrAlsCtx *prev = scr_als_dispatch_enter(s->als); + scr_child_stream_emit_in(s, bytes, text); + scr_als_dispatch_leave(prev); +} + +static void scr_child_stream_emit_in(ScrChildStream *s, ScrBytes *bytes, ScrStr *text) { size_t nd = s->n_data; if (nd == 0) return; ScrChildStreamDataL *snap = malloc(nd * sizeof *snap); @@ -1683,7 +1706,16 @@ static void scr_child_stream_emit(ScrChildStream *s, ScrBytes *bytes, free(snap); } +static void scr_child_stream_finish_in(ScrChildStream *s, bool fire_end); + +/* A stream's 'end' runs in the spawn() call's context. */ static void scr_child_stream_finish(ScrChildStream *s, bool fire_end) { + ScrAlsCtx *prev = scr_als_dispatch_enter(s->als); + scr_child_stream_finish_in(s, fire_end); + scr_als_dispatch_leave(prev); +} + +static void scr_child_stream_finish_in(ScrChildStream *s, bool fire_end) { if (fire_end && s->encoding != NULL && s->n_data > 0) { ScrStr *tail = scr_strdec_end(s->encoding, s->dec_pending); s->dec_pending = 0; @@ -1914,6 +1946,8 @@ void scr_child_writer_release(ScrChildWriter *w) { free(w->buf); scr_str_release(w->err_msg); scr_child_writer_drop_listeners(w); + scr_als_ctx_release(w->als); + scr_als_ctx_release(w->end_als); free(w); } } @@ -1929,6 +1963,7 @@ static ScrChildWriter *scr_child_writer_new(HANDLE h) { ScrChildWriter *w = calloc(1, sizeof *w); if (!w) scr_child_oom(); w->rc = 1; + w->als = scr_als_capture(); w->h = h; w->writable = h != NULL; w->destroyed = h == NULL; @@ -2075,6 +2110,8 @@ static void scr_child_writer_pump(ScrChildWriter *w) { static bool scr_child_writer_write(ScrChildWriter *w, const void *data, size_t len) { + scr_als_ctx_release(w->als); + w->als = scr_als_capture(); if (!w->writable) { scr_child_writer_fail(w, "write after end", "ERR_STREAM_WRITE_AFTER_END"); return false; @@ -2105,6 +2142,8 @@ bool scr_child_writer_write_bytes(ScrChildWriter *w, const ScrBytes *data) { void scr_child_writer_end(ScrChildWriter *w) { if (w->ending || w->finish_pending || w->finished || w->destroyed) return; + scr_als_ctx_release(w->end_als); + w->end_als = scr_als_capture(); w->writable = false; w->ending = true; scr_child_writer_pump(w); @@ -2172,7 +2211,16 @@ void scr_child_writer_on_error(ScrChildWriter *w, ScrClosure *cb /*moves*/, w->err_ls[w->n_write_err++] = (ScrChildErrEntry){cb, fn}; } +static void scr_child_writer_fire_drain_in(ScrChildWriter *w); + +/* 'drain' runs in the latest write() call's context. */ static void scr_child_writer_fire_drain(ScrChildWriter *w) { + ScrAlsCtx *prev = scr_als_dispatch_enter(w->als); + scr_child_writer_fire_drain_in(w); + scr_als_dispatch_leave(prev); +} + +static void scr_child_writer_fire_drain_in(ScrChildWriter *w) { w->drain_pending = false; size_t n = w->n_drain; if (n == 0) return; @@ -2199,7 +2247,16 @@ static void scr_child_writer_fire_drain(ScrChildWriter *w) { free(snap); } +static void scr_child_writer_fire_finish_in(ScrChildWriter *w); + +/* 'finish' runs in the end() call's context. */ static void scr_child_writer_fire_finish(ScrChildWriter *w) { + ScrAlsCtx *prev = scr_als_dispatch_enter(w->end_als); + scr_child_writer_fire_finish_in(w); + scr_als_dispatch_leave(prev); +} + +static void scr_child_writer_fire_finish_in(ScrChildWriter *w) { w->finish_pending = false; w->finished = true; w->destroyed = true; @@ -2218,7 +2275,16 @@ static void scr_child_writer_fire_finish(ScrChildWriter *w) { scr_child_writer_drop_zero_listeners(w); } +static void scr_child_writer_fire_error_in(ScrChildWriter *w); + +/* 'error' runs in the latest write() call's context. */ static void scr_child_writer_fire_error(ScrChildWriter *w) { + ScrAlsCtx *prev = scr_als_dispatch_enter(w->als); + scr_child_writer_fire_error_in(w); + scr_als_dispatch_leave(prev); +} + +static void scr_child_writer_fire_error_in(ScrChildWriter *w) { w->error_pending = false; if (w->n_write_err == 0) { fflush(stdout); @@ -2312,6 +2378,7 @@ ScrChild *scr_spawn_opts(ScrStr *cmd, ScrArr *args, double in_mode, ScrChild *c = calloc(1, sizeof(ScrChild)); if (!c) scr_child_oom(); c->rc = 1; + c->als = scr_als_capture(); /* Piped slots: the pipe NOW; a pipe failure degrades the slot to NUL * and the stream husk answers eof (fd exhaustion — nothing real). */ @@ -2710,7 +2777,16 @@ static bool scr_child_close_ready(const ScrChild *c) { scr_ipc_close_ready(c->ipc); } +static void scr_child_fire_close_in(ScrChild *c); + +/* 'close' runs in the spawn() call's context. */ static void scr_child_fire_close(ScrChild *c) { + ScrAlsCtx *prev = scr_als_dispatch_enter(c->als); + scr_child_fire_close_in(c); + scr_als_dispatch_leave(prev); +} + +static void scr_child_fire_close_in(ScrChild *c) { c->close_done = true; bool has_code = c->state == SCR_CHILD_SPAWN_FAILED ? true : c->has_code; double code = c->state == SCR_CHILD_SPAWN_FAILED @@ -2774,7 +2850,16 @@ bool scr_children_wait(double max_wait_ms) { /* Fires one settled child's terminal event — the POSIX arm's settle, * verbatim (streams drain first so 'end' precedes 'exit'; spawn * failures fire 'error' or die unhandled). */ +static void scr_child_settle_in(ScrChild *c); + +/* A child's terminal events run in its spawn() call's context. */ static void scr_child_settle(ScrChild *c) { + ScrAlsCtx *prev = scr_als_dispatch_enter(c->als); + scr_child_settle_in(c); + scr_als_dispatch_leave(prev); +} + +static void scr_child_settle_in(ScrChild *c) { if (c->reffed) { c->reffed = false; scr_children_reffed_n--; @@ -3884,6 +3969,10 @@ struct ScrChildStream { size_t n_data, cap_data; ScrChildStreamEndL *end_ls; size_t n_end, cap_end; + /* The AsyncLocalStorage context of the spawn() call that created the + * stream (+1, NULL outside a store): its events run in it, as Node's + * pipe handles run theirs. */ + ScrAlsCtx *als; struct ScrChildStream *next; /* the service registry (+1) */ }; @@ -3908,6 +3997,11 @@ struct ScrChildWriter { size_t n_finish, cap_finish; ScrChildErrEntry *err_ls; size_t n_write_err, cap_write_err; + /* AsyncLocalStorage contexts (+1 each, NULL outside a store): the + * latest write() call's, for 'drain' and 'error' (the spawn() call's + * until the first write), and the end() call's, for 'finish'. */ + ScrAlsCtx *als; + ScrAlsCtx *end_als; struct ScrChildWriter *next; /* the service registry (+1) */ }; @@ -3958,6 +4052,9 @@ struct ScrChild { ScrChildWriter *in_stream; ScrDyn *out_dyn, *err_dyn, *in_dyn; ScrIpc *ipc; + /* The AsyncLocalStorage context of the spawn() call (+1, NULL outside + * a store): 'spawn', 'error', 'exit' and 'close' run in it. */ + ScrAlsCtx *als; struct ScrChild *next; /* the pending registry */ }; @@ -4154,6 +4251,7 @@ void scr_child_release(ScrChild *c) { scr_child_stream_release(c->err_stream); scr_child_writer_release(c->in_stream); scr_ipc_release(c->ipc); + scr_als_ctx_release(c->als); free(c); } } @@ -4206,6 +4304,7 @@ void scr_child_stream_release(ScrChildStream *s) { if (s->fd >= 0) close(s->fd); scr_str_release(s->encoding); free(s->capture_data); + scr_als_ctx_release(s->als); free(s); } } @@ -4228,6 +4327,7 @@ static ScrChildStream *scr_child_stream_new(int fd) { ScrChildStream *s = calloc(1, sizeof *s); if (!s) scr_child_oom(); s->rc = 1; + s->als = scr_als_capture(); s->fd = fd; if (fd < 0) { s->eof = true; @@ -4282,8 +4382,16 @@ static ScrStr *scr_child_stream_capture_value(ScrChildStream *s) { : scr_str_new((const char *)s->capture_data, s->capture_len); } -static void scr_child_stream_emit_posix(ScrChildStream *s, ScrBytes *bytes, - ScrStr *text) { +static void scr_child_stream_emit_posix_in(ScrChildStream *s, ScrBytes *bytes, ScrStr *text); + +/* 'data' runs in the spawn() call's context. */ +static void scr_child_stream_emit_posix(ScrChildStream *s, ScrBytes *bytes, ScrStr *text) { + ScrAlsCtx *prev = scr_als_dispatch_enter(s->als); + scr_child_stream_emit_posix_in(s, bytes, text); + scr_als_dispatch_leave(prev); +} + +static void scr_child_stream_emit_posix_in(ScrChildStream *s, ScrBytes *bytes, ScrStr *text) { size_t nd = s->n_data; if (nd == 0) return; ScrChildStreamDataL *snap = malloc(nd * sizeof *snap); @@ -4333,7 +4441,16 @@ static void scr_child_stream_emit_posix(ScrChildStream *s, ScrBytes *bytes, /* EOF/error tail: 'end' fires over a snapshot, everything drops, the fd * closes (deleting its kqueue filter), and the liveness/fallback counters * settle. The caller unlinks from the registry and releases its ref. */ +static void scr_child_stream_finish_in(ScrChildStream *s, bool fire_end); + +/* A stream's 'end' runs in the spawn() call's context. */ static void scr_child_stream_finish(ScrChildStream *s, bool fire_end) { + ScrAlsCtx *prev = scr_als_dispatch_enter(s->als); + scr_child_stream_finish_in(s, fire_end); + scr_als_dispatch_leave(prev); +} + +static void scr_child_stream_finish_in(ScrChildStream *s, bool fire_end) { if (fire_end && s->encoding != NULL && s->n_data > 0) { ScrStr *tail = scr_strdec_end(s->encoding, s->dec_pending); s->dec_pending = 0; @@ -4641,6 +4758,8 @@ void scr_child_writer_release(ScrChildWriter *w) { free(w->buf); scr_str_release(w->err_msg); scr_child_writer_drop_listeners(w); + scr_als_ctx_release(w->als); + scr_als_ctx_release(w->end_als); free(w); } } @@ -4656,6 +4775,7 @@ static ScrChildWriter *scr_child_writer_new(int fd) { ScrChildWriter *w = calloc(1, sizeof *w); if (!w) scr_child_oom(); w->rc = 1; + w->als = scr_als_capture(); w->fd = fd; w->writable = fd >= 0; w->destroyed = fd < 0; @@ -4789,6 +4909,8 @@ static void scr_child_writer_pump(ScrChildWriter *w) { static bool scr_child_writer_write(ScrChildWriter *w, const void *data, size_t len) { + scr_als_ctx_release(w->als); + w->als = scr_als_capture(); if (!w->writable) { scr_child_writer_fail(w, "write after end", "ERR_STREAM_WRITE_AFTER_END"); return false; @@ -4825,6 +4947,8 @@ bool scr_child_writer_write_bytes(ScrChildWriter *w, const ScrBytes *data) { void scr_child_writer_end(ScrChildWriter *w) { if (w->ending || w->finish_pending || w->finished || w->destroyed) return; + scr_als_ctx_release(w->end_als); + w->end_als = scr_als_capture(); w->writable = false; w->ending = true; scr_child_writer_pump(w); @@ -4892,7 +5016,16 @@ void scr_child_writer_on_error(ScrChildWriter *w, ScrClosure *cb /*moves*/, w->err_ls[w->n_write_err++] = (ScrChildErrEntry){cb, fn}; } +static void scr_child_writer_fire_drain_in(ScrChildWriter *w); + +/* 'drain' runs in the latest write() call's context. */ static void scr_child_writer_fire_drain(ScrChildWriter *w) { + ScrAlsCtx *prev = scr_als_dispatch_enter(w->als); + scr_child_writer_fire_drain_in(w); + scr_als_dispatch_leave(prev); +} + +static void scr_child_writer_fire_drain_in(ScrChildWriter *w) { w->drain_pending = false; size_t n = w->n_drain; if (n == 0) return; @@ -4919,7 +5052,16 @@ static void scr_child_writer_fire_drain(ScrChildWriter *w) { free(snap); } +static void scr_child_writer_fire_finish_in(ScrChildWriter *w); + +/* 'finish' runs in the end() call's context. */ static void scr_child_writer_fire_finish(ScrChildWriter *w) { + ScrAlsCtx *prev = scr_als_dispatch_enter(w->end_als); + scr_child_writer_fire_finish_in(w); + scr_als_dispatch_leave(prev); +} + +static void scr_child_writer_fire_finish_in(ScrChildWriter *w) { w->finish_pending = false; w->finished = true; w->destroyed = true; @@ -4938,7 +5080,16 @@ static void scr_child_writer_fire_finish(ScrChildWriter *w) { scr_child_writer_drop_zero_listeners(w); } +static void scr_child_writer_fire_error_in(ScrChildWriter *w); + +/* 'error' runs in the latest write() call's context. */ static void scr_child_writer_fire_error(ScrChildWriter *w) { + ScrAlsCtx *prev = scr_als_dispatch_enter(w->als); + scr_child_writer_fire_error_in(w); + scr_als_dispatch_leave(prev); +} + +static void scr_child_writer_fire_error_in(ScrChildWriter *w) { w->error_pending = false; if (w->n_write_err == 0) { fflush(stdout); @@ -5042,6 +5193,7 @@ ScrChild *scr_spawn_opts(ScrStr *cmd, ScrArr *args, double in_mode, ScrChild *c = calloc(1, sizeof(ScrChild)); if (!c) scr_child_oom(); c->rc = 1; + c->als = scr_als_capture(); /* Piped slots: pipe(2) NOW, both ends cloexec (the file action's dup2 * clears the flag on the child's copy — every other descriptor closes @@ -5558,7 +5710,16 @@ static bool scr_child_close_ready(const ScrChild *c) { scr_ipc_close_ready(c->ipc); } +static void scr_child_fire_close_in(ScrChild *c); + +/* 'close' runs in the spawn() call's context. */ static void scr_child_fire_close(ScrChild *c) { + ScrAlsCtx *prev = scr_als_dispatch_enter(c->als); + scr_child_fire_close_in(c); + scr_als_dispatch_leave(prev); +} + +static void scr_child_fire_close_in(ScrChild *c) { c->close_done = true; bool has_code = c->state == SCR_CHILD_SPAWN_FAILED ? true : c->has_code; double code = c->state == SCR_CHILD_SPAWN_FAILED @@ -5596,7 +5757,16 @@ static void scr_children_closing_poll(void) { * ordering runs first: consumer-owning piped streams drain to EOF/EAGAIN * so their 'end' events fire BEFORE 'exit' (Node-exact — verified: data*, * end, then exit for every output shape). */ +static void scr_child_settle_in(ScrChild *c); + +/* A child's terminal events run in its spawn() call's context. */ static void scr_child_settle(ScrChild *c) { + ScrAlsCtx *prev = scr_als_dispatch_enter(c->als); + scr_child_settle_in(c); + scr_als_dispatch_leave(prev); +} + +static void scr_child_settle_in(ScrChild *c) { if (c->reffed) { c->reffed = false; scr_children_reffed_n--; @@ -5767,7 +5937,16 @@ void scr_child_on_spawn(ScrChild *c, ScrClosure *cb) { /* Success is observable on the next loop pass, before any data or exit * event. Detaching the snapshot keeps listeners registered by a callback * from participating in an event that has already started. */ +static void scr_child_fire_spawn_in(ScrChild *c); + +/* 'spawn' runs in the spawn() call's context. */ static void scr_child_fire_spawn(ScrChild *c) { + ScrAlsCtx *prev = scr_als_dispatch_enter(c->als); + scr_child_fire_spawn_in(c); + scr_als_dispatch_leave(prev); +} + +static void scr_child_fire_spawn_in(ScrChild *c) { c->spawn_done = true; ScrClosure **callbacks = c->spawn_cbs; size_t count = c->n_spawn; @@ -5973,6 +6152,7 @@ typedef struct { typedef struct { ScrClosure *cb; ScrIpcSendFn fn; + ScrAlsCtx *ctx; /* the send() call's context (+1): the callback runs in it */ } ScrIpcSendEntry; struct ScrIpc { @@ -6000,6 +6180,9 @@ struct ScrIpc { ScrIpcSendEntry *send_cbs; size_t n_send, cap_send; ScrStr *send_error; + /* The context the channel was created in (+1): 'message' and + * 'disconnect' run in it. */ + ScrAlsCtx *als; struct ScrIpc *next; }; @@ -6035,8 +6218,12 @@ static void scr_ipc_release(ScrIpc *ipc) { for (size_t i = 0; i < ipc->n_pending; i++) scr_dyn_release(ipc->pending[i]); for (size_t i = 0; i < ipc->n_message; i++) scr_closure_release(ipc->message_ls[i].cb); for (size_t i = 0; i < ipc->n_disconnect; i++) scr_closure_release(ipc->disconnect_ls[i].cb); - for (size_t i = 0; i < ipc->n_send; i++) scr_closure_release(ipc->send_cbs[i].cb); + for (size_t i = 0; i < ipc->n_send; i++) { + scr_closure_release(ipc->send_cbs[i].cb); + scr_als_ctx_release(ipc->send_cbs[i].ctx); + } scr_str_release(ipc->send_error); + scr_als_ctx_release(ipc->als); free(ipc->buffer); free(ipc->pending); free(ipc->message_ls); @@ -6156,6 +6343,7 @@ static ScrIpc *scr_ipc_new(ScrChildStream *reader, ScrChildWriter *writer) { ScrIpc *ipc = calloc(1, sizeof(*ipc)); if (!ipc) scr_trap("out of memory"); ipc->rc = 1; + ipc->als = scr_als_capture(); ipc->connected = true; ipc->reader = reader; ipc->writer = writer; @@ -6186,7 +6374,7 @@ static void scr_ipc_add_send(ScrIpc *ipc, ScrClosure *cb, ScrIpcSendFn fn) { ipc->send_cbs = realloc(ipc->send_cbs, ipc->cap_send * sizeof(*ipc->send_cbs)); if (!ipc->send_cbs) scr_trap("out of memory"); } - ipc->send_cbs[ipc->n_send++] = (ScrIpcSendEntry){cb, fn}; + ipc->send_cbs[ipc->n_send++] = (ScrIpcSendEntry){cb, fn, scr_als_capture()}; } static bool scr_ipc_send(ScrIpc *ipc, ScrStr *json, ScrClosure *cb, @@ -6270,7 +6458,16 @@ static void scr_ipc_remove_message_once(ScrIpc *ipc, ScrClosure *cb) { } } +static void scr_ipc_fire_messages_in(ScrIpc *ipc); + +/* 'message' runs in the channel's creation context. */ static void scr_ipc_fire_messages(ScrIpc *ipc) { + ScrAlsCtx *prev = scr_als_dispatch_enter(ipc->als); + scr_ipc_fire_messages_in(ipc); + scr_als_dispatch_leave(prev); +} + +static void scr_ipc_fire_messages_in(ScrIpc *ipc) { while (ipc->n_pending > 0 && ipc->n_message > 0 && !scr_exc_pending()) { ScrDyn *message = ipc->pending[0]; memmove(ipc->pending, ipc->pending + 1, @@ -6307,16 +6504,28 @@ static void scr_ipc_fire_send_callbacks(ScrIpc *ipc) { error = scr_error_new(0, ipc->send_error); scr_error_set_code(error, "ERR_IPC_CHANNEL_CLOSED"); } + ScrAlsCtx *prev = scr_als_dispatch_enter(snapshot[i].ctx); if (!scr_exc_pending()) snapshot[i].fn(snapshot[i].cb, error); else scr_error_release(error); + scr_als_dispatch_leave(prev); scr_closure_release(snapshot[i].cb); + scr_als_ctx_release(snapshot[i].ctx); } free(snapshot); scr_str_release(ipc->send_error); ipc->send_error = NULL; } +static void scr_ipc_fire_disconnect_in(ScrIpc *ipc); + +/* 'disconnect' runs in the channel's creation context. */ static void scr_ipc_fire_disconnect(ScrIpc *ipc) { + ScrAlsCtx *prev = scr_als_dispatch_enter(ipc->als); + scr_ipc_fire_disconnect_in(ipc); + scr_als_dispatch_leave(prev); +} + +static void scr_ipc_fire_disconnect_in(ScrIpc *ipc) { if (!ipc->disconnect_pending || ipc->disconnect_emitted) return; ipc->disconnect_pending = false; ipc->disconnect_emitted = true; diff --git a/packages/runtime/src/scr_crypto.h b/packages/runtime/src/scr_crypto.h index f61d27406..03a9dd109 100644 --- a/packages/runtime/src/scr_crypto.h +++ b/packages/runtime/src/scr_crypto.h @@ -298,6 +298,9 @@ ScrDyn *scr_crypto_generate_key_pair_sync(ScrDyn *type, ScrDyn *options); void scr_crypto_generate_key_pair_async(ScrDyn *type, ScrDyn *options, ScrDyn *callback); ScrDyn *scr_crypto_sign_oneshot(ScrDyn *algorithm, ScrDyn *data, ScrDyn *key); ScrDyn *scr_crypto_verify_oneshot(ScrDyn *algorithm, ScrDyn *data, ScrDyn *key, ScrDyn *signature); +/* crypto.diffieHellman(options[, callback]): the shared secret, or undefined + * with the callback, which receives it in a later macrotask. */ +ScrDyn *scr_crypto_diffie_hellman(ScrDyn *options, ScrDyn *callback); ScrDyn *scr_crypto_create_sign(ScrDyn *algorithm); ScrDyn *scr_crypto_create_verify(ScrDyn *algorithm); bool scr_crypto_is_asymmetric_key(const ScrDyn *value); diff --git a/packages/runtime/src/scr_cycle.c b/packages/runtime/src/scr_cycle.c index 6dcd546b0..dbd636306 100644 --- a/packages/runtime/src/scr_cycle.c +++ b/packages/runtime/src/scr_cycle.c @@ -170,7 +170,8 @@ void scr_weak_dispose(void *object) { #include /* Every heap object's first member is `size_t rc`; the collector works on - * its count (SCR_RC_COUNT). */ + * its count (SCR_RC_COUNT), and tests it without a frozen record's bit + * (SCR_RC_REFS). */ #define SCR_RC(obj) SCR_RC_COUNT(obj) static void scr_cyc_oom(void) { @@ -293,24 +294,22 @@ static SCR_TL ScrDestroyEntry *scr_destroy_pending; static SCR_TL size_t scr_destroy_count, scr_destroy_capacity; #endif -void scr_rc_destroy(void *obj, void (*destroy)(void *)) { - if (scr_destroy_depth == SCR_DESTROY_DEPTH) { - if (scr_destroy_count == scr_destroy_capacity) { - size_t capacity = scr_destroy_capacity ? scr_destroy_capacity * 2 : 64; - if (capacity < scr_destroy_capacity || capacity > SIZE_MAX / sizeof(ScrDestroyEntry)) - scr_cyc_oom(); - ScrDestroyEntry *entries = realloc(scr_destroy_pending, capacity * sizeof(*entries)); - if (!entries) scr_cyc_oom(); - scr_destroy_pending = entries; - scr_destroy_capacity = capacity; - } - scr_destroy_pending[scr_destroy_count++] = (ScrDestroyEntry){ obj, destroy }; - return; +/* Queues a disposal past the depth budget; the outermost disposal runs it. */ +static void scr_rc_destroy_defer(void *obj, void (*destroy)(void *)) { + if (scr_destroy_count == scr_destroy_capacity) { + size_t capacity = scr_destroy_capacity ? scr_destroy_capacity * 2 : 64; + if (capacity < scr_destroy_capacity || capacity > SIZE_MAX / sizeof(ScrDestroyEntry)) + scr_cyc_oom(); + ScrDestroyEntry *entries = realloc(scr_destroy_pending, capacity * sizeof(*entries)); + if (!entries) scr_cyc_oom(); + scr_destroy_pending = entries; + scr_destroy_capacity = capacity; } - scr_destroy_depth++; - destroy(obj); - scr_destroy_depth--; - if (scr_destroy_depth) return; + scr_destroy_pending[scr_destroy_count++] = (ScrDestroyEntry){ obj, destroy }; +} + +/* Runs the queued disposals once the outermost one has returned. */ +static void scr_rc_destroy_drain(void) { while (scr_destroy_count) { ScrDestroyEntry entry = scr_destroy_pending[--scr_destroy_count]; scr_destroy_depth = 1; @@ -326,6 +325,30 @@ void scr_rc_destroy(void *obj, void (*destroy)(void *)) { } } +void scr_rc_destroy(void *obj, void (*destroy)(void *)) { + if (scr_destroy_depth == SCR_DESTROY_DEPTH) { + scr_rc_destroy_defer(obj, destroy); + return; + } + scr_destroy_depth++; + destroy(obj); + scr_destroy_depth--; + if (scr_destroy_depth == 0) scr_rc_destroy_drain(); +} + +void *scr_rc_destroy_seg(void *obj, void *(*destroy_seg)(void *, void *), + void (*destroy)(void *), void *tail) { + if (scr_destroy_depth == SCR_DESTROY_DEPTH) { + scr_rc_destroy_defer(obj, destroy); + return tail; + } + scr_destroy_depth++; + tail = destroy_seg(obj, tail); + scr_destroy_depth--; + if (scr_destroy_depth == 0) scr_rc_destroy_drain(); + return tail; +} + /* Live cycle-headered objects and the running count of OLD objects freed. * The inline allocation/free paths (scr_runtime.h, llvm/alloc.ts) maintain * both counters so full passes can measure old-generation reclamation. */ @@ -1308,7 +1331,8 @@ static void scr_mg_visit(void *child, void *ctx) { scr_mg_skip_edge(child); return; } - size_t rc = --SCR_RC(child); + --SCR_RC(child); + size_t rc = SCR_RC_REFS(child); if (scr_cyc_color(h) == SCR_CYC_GRAY) { if (rc == 0) { scr_gray_referenced--; @@ -1336,7 +1360,7 @@ static inline __attribute__((always_inline)) void scr_mark_gray(void *obj, uintp )) h->buf = scr_gray_comp; if (scr_white.n < SCR_CYC_GRAY_LIST_MAX) scr_cyc_push_gray(&scr_white, obj); - if (SCR_RC(obj) > 0) { + if (SCR_RC_REFS(obj) > 0) { scr_gray_referenced++; scr_comp_referenced++; } @@ -1408,7 +1432,7 @@ static void scr_scan_visit(void *child, void *ctx) { static void scr_scan(void *obj, uintptr_t depth) { ScrCycHdr *h = scr_cyc_hdr(obj); if (scr_cyc_color(h) != SCR_CYC_GRAY) return; - if (SCR_RC(obj) > 0) { + if (SCR_RC_REFS(obj) > 0) { scr_restore_from(obj); return; } @@ -1476,7 +1500,7 @@ static void scr_xg_destroy(void *obj) { } static SCR_CYC_COMPACT void scr_xg_release(void *obj) { - if (SCR_RC(obj) > 1) { + if (SCR_RC_REFS(obj) > 1) { SCR_RC(obj) -= 1; scr_cyc_on_release(obj); /* lost a reference: a possible cycle root */ return; @@ -1768,14 +1792,14 @@ static __attribute__((noinline)) size_t scr_yin_collect(void) { if (scr_cyc_gen(h) != SCR_CYC_YOUNG || !scr_cyc_is_buffered(h) || scr_cyc_buf_index(h) != k || scr_cyc_color(h) != SCR_CYC_BLACK) scr_trap("scriptc: corrupt young nursery entry\n"); - if (SCR_RC(v[k]) == 0 || SCR_RC(v[k]) < scr_yin_counts[k]) + if (SCR_RC_REFS(v[k]) == 0 || SCR_RC_REFS(v[k]) < scr_yin_counts[k]) scr_trap("scriptc: young object with uncounted references\n"); } #endif for (size_t k = 0; k < n; k++) { void *obj = v[k]; ScrCycHdr *h = scr_cyc_hdr(obj); - if (scr_cyc_color(h) == SCR_CYC_GRAY || SCR_RC(obj) <= scr_yin_counts[k]) continue; + if (scr_cyc_color(h) == SCR_CYC_GRAY || SCR_RC_REFS(obj) <= scr_yin_counts[k]) continue; scr_cyc_set_color(h, SCR_CYC_GRAY); scr_yin_push(&scr_yin_stack, obj); while (scr_yin_stack.n) { @@ -1799,7 +1823,7 @@ static __attribute__((noinline)) size_t scr_yin_collect(void) { scr_cyc_set_gen(h, SCR_CYC_YIN_PROMOTED); scr_cyc_clear_buffered(h); /* An outside root becomes a candidate (see "candidates" above). */ - if (SCR_RC(obj) > scr_yin_counts[k]) scr_cyc_on_release(obj); + if (SCR_RC_REFS(obj) > scr_yin_counts[k]) scr_cyc_on_release(obj); scr_yin_met(h->id, false); } else { scr_yin_met(h->id, true); @@ -2147,7 +2171,7 @@ static size_t scr_cyc_pass(unsigned gen_limit) { for (size_t j = start; j < end; j++) { void *obj = scr_white.v[j]; if (scr_cyc_color(scr_cyc_hdr(obj)) != SCR_CYC_GRAY) continue; - if (SCR_RC(obj) > 0) scr_restore_from(obj); + if (SCR_RC_REFS(obj) > 0) scr_restore_from(obj); else scr_white.v[kept++] = obj; } from = end; @@ -2176,7 +2200,7 @@ static size_t scr_cyc_pass(unsigned gen_limit) { * whitened only to be restored again. */ for (size_t i = 0; i < scr_cands.n; i++) { void *obj = scr_cands.v[i]; - if (scr_cyc_color(scr_cyc_hdr(obj)) == SCR_CYC_GRAY && SCR_RC(obj) > 0) scr_restore_from(obj); + if (scr_cyc_color(scr_cyc_hdr(obj)) == SCR_CYC_GRAY && SCR_RC_REFS(obj) > 0) scr_restore_from(obj); } for (size_t i = 0; i < scr_cands.n; i++) { void *obj = scr_cands.v[i]; diff --git a/packages/runtime/src/scr_dyn.h b/packages/runtime/src/scr_dyn.h index 669f4aedb..452eb0363 100644 --- a/packages/runtime/src/scr_dyn.h +++ b/packages/runtime/src/scr_dyn.h @@ -200,6 +200,8 @@ typedef enum { X(SHARED_ARRAY_BUFFER, 37) \ X(SIGN, 67) /* node:crypto Sign (scr_pki.c) */ \ X(STDIO, 27) /* stable process stdin/stdout/stderr values */ \ + X(TEXT_DECODER, 76) /* compiled TextDecoder record (scr_text_codec_dyn.c) */ \ + X(TEXT_ENCODER, 77) /* compiled TextEncoder record (scr_text_codec_dyn.c) */ \ X(URL, 28) /* native WHATWG URL */ \ X(VERIFY, 68) /* node:crypto Verify (scr_pki.c) */ \ X(WASM_GLOBAL, 61) /* WebAssembly.Global */ \ @@ -212,6 +214,9 @@ typedef enum { X(WEB_CONTROLLER, 11) /* native ReadableStreamDefaultController */ \ X(WEB_READER, 10) /* native ReadableStreamDefaultReader */ \ X(WEB_STREAM, 9) /* native WHATWG ReadableStream */ \ + X(WEB_TRANSFORM, 73) /* native identity TransformStream (scr_fetch.c) */ \ + X(WEB_WRITABLE, 74) /* a TransformStream's WritableStream (scr_fetch.c) */ \ + X(WEB_WRITER, 75) /* WritableStreamDefaultWriter (scr_fetch.c) */ \ X(WORKER, 35) typedef enum { @@ -393,6 +398,10 @@ typedef struct ScrDynTypedClassOps { size_t count; ScrStr *const *names; /* immortal member-name strings */ ScrStr *class_name; /* immortal constructor name; NULL for an anonymous class */ + /* The string an inherited Symbol.toStringTag getter answers for the + * instance (+1), or NULL with a pending exception from the getter; + * NULL when the class has no such getter. */ + ScrStr *(*string_tag)(void *instance); } ScrDynTypedClassOps; /* A compiled JavaScript class whose instances record their own properties @@ -540,6 +549,10 @@ _Static_assert(offsetof(ScrDyn, null_proto) == sizeof(size_t) + sizeof(ScrDynKin "dyn-to-record conversions read null_proto at byte 13 (9 on wasm32)"); _Static_assert(offsetof(ScrDyn, v.typed_ref.type_key) == 16 + 3 * sizeof(void *), "typed_ref.type_key offset"); +/* Emitted brand probes read type_key_len as the low 27 bits of the 32-bit + * word after `casts` (native 88, wasm32 52). */ +_Static_assert(offsetof(ScrDyn, v.typed_ref.casts) == 16 + 8 * sizeof(void *), + "typed_ref.casts offset"); /* The region links ride in payload space the capsule variant already * reserves, so dyn nodes keep their size (and allocation size class). */ _Static_assert(sizeof(((ScrDyn *)0)->v.arr) <= sizeof(((ScrDyn *)0)->v.typed_ref) && @@ -806,6 +819,8 @@ ScrDyn *scr_dyn_native_date_new_fields(const ScrDyn *arguments); ScrDyn *scr_dyn_native_date_new_value(const ScrDyn *arguments); ScrStr *scr_dyn_object_tag_of(const ScrDyn *d); ScrDyn *scr_dyn_native_date_from_ms(double ms); +typedef struct ScrDate ScrDate; +ScrDyn *scr_dyn_native_date(ScrDate *date); double scr_dyn_native_date_value(const ScrDyn *value); ScrDyn *scr_dyn_native_regex(ScrRegex *value); bool scr_dyn_native_regex_is(const ScrDyn *value); @@ -861,6 +876,10 @@ ScrDyn *scr_dyn_symbol_ref(ScrSym *value, void (*release)(ScrSym *), ScrStr *(*r ScrDyn *scr_dyn_new_str(ScrStr *s); ScrDyn *scr_dyn_new_arr(void); ScrDyn *scr_dyn_array_constructor(void); +/* Gives a builtin constructor value (Object, Array, Error) its builtin + * `prototype`: non-writable, non-enumerable, non-configurable. Takes the + * prototype. */ +void scr_dyn_fn_seed_prototype(ScrDyn *function, ScrDyn *prototype); /* The interned builtin constructor `name` (Object, Date, RegExp, Map, Set, * Promise, WeakMap, WeakSet) as a dynamic function value: +1. */ ScrDyn *scr_dyn_global_constructor(const ScrStr *name); @@ -906,6 +925,7 @@ void scr_dyn_install_iterator_symbol(ScrDyn *key); void scr_dyn_install_has_instance_symbol(ScrDyn *key); void scr_dyn_install_async_iterator_symbol(ScrDyn *key); void scr_dyn_install_async_dispose_symbol(ScrDyn *key); +void scr_dyn_install_to_string_tag_symbol(ScrDyn *key); /* True when key is Symbol.asyncDispose (registered by the symbol unit when * the program names it); handle symbol_get ops outside that unit use it. */ bool scr_dyn_is_async_dispose_symbol(const ScrDyn *key); @@ -1046,6 +1066,19 @@ void scr_dyn_typed_ref_bind_record(ScrDyn *d, const ScrDynTypedRecordOps *ops); void scr_record_source_note(void *rec, const ScrDyn *src, const char *const *names, const size_t *lens, size_t count); bool scr_record_source_has(const void *rec); +/* A literal that starts with `...src` creates src's keys first, in src's + * order: when src kept a source, the fresh record `dst` (whose shape's + * declared names are `names`) keeps one listing the keys it declares in + * that order. Undeclared keys are not copied, as the literal copies none. */ +void scr_record_source_spread(void *dst, const void *src, const char *const *names, + const size_t *lens, size_t count); +/* Object.freeze of a record a program may freeze: sets SCR_RC_FROZEN in its + * rc word (immortal records are left as they are). */ +void scr_record_freeze(void *record); +/* Node's TypeError for a write to a frozen record: operation 0 assigns the + * existing property `key`, 1 adds it, 2 deletes it. `owner` is the object's + * text in the message ("#"). */ +void scr_record_throw_frozen(ScrStr *key, ScrStr *owner, double operation); /* The commit of a live record view converts the view into a new record * `from`, whose conversion keeps the view's undeclared properties, then * moves its fields into `to`: this moves the kept properties to `to` too, @@ -1056,6 +1089,9 @@ ScrDyn *scr_record_source_apply(ScrDyn *snapshot, const void *rec); /* Attach the prototype member names of a class instance capsule * (idempotent), so `in` sees its methods and accessors. */ void scr_dyn_typed_ref_bind_class(ScrDyn *d, const ScrDynTypedClassOps *ops); +/* A class instance capsule's inherited Symbol.toStringTag string from its + * class's getter (+1); NULL when it has none or the getter threw. */ +ScrStr *scr_dyn_typed_ref_class_tag(const ScrDyn *d); /* Attach the bag and prototype hooks of a capsule of a bag-tracked class * instance (idempotent). */ void scr_dyn_typed_ref_bind_bag_class(ScrDyn *d, const ScrDynTypedBagClassOps *ops); @@ -1306,6 +1342,11 @@ ScrDyn *scr_dyn_from_error(const ScrError *e); void scr_error_register_dyn(const ScrVt *vt, ScrDyn *(*box)(void *)); ScrDyn *scr_error_dyn_fields(const ScrError *e); void scr_error_dyn_place_name(ScrDyn *view, const char *after, size_t after_len); +/* An Error subclass view of an instance whose class keeps a property bag: + * `name` and `message` take their committed positions, and data values + * from the slots (scr_json.c). `name_placed`: the class's construction sets + * `name`, and the view already has it in place. */ +void scr_error_dyn_follow_bag(ScrDyn *view, const ScrDyn *bag, const ScrError *e, bool name_placed); /* An accessor property of a fresh object: enumerable and configurable, * with the given getter and setter (either may be NULL). Moves both. */ void scr_dyn_obj_set_accessor(ScrDyn *obj, const char *key, size_t key_len, ScrDyn *getter, ScrDyn *setter); @@ -1360,11 +1401,25 @@ ScrDyn *scr_dyn_invoke_buffer_to_string(ScrDyn *recv, ScrDyn *callee, const char ScrDyn *scr_dyn_invoke_buffer_search(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what); ScrDyn *scr_dyn_invoke_buffer(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what); ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc, const char *what); +/* A %TypedArray%.prototype method called on a checked typed array (not a + * DataView): its answer (+1, or NULL with the exception pending), with + * *handled false for a name it does not implement. */ +ScrDyn *scr_dyn_bytes_method(ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc, bool *handled); +/* A plain object's Symbol.iterator method (+1), read once; NULL when it + * has none, or with the exception pending from its getter. */ +ScrDyn *scr_dyn_iterator_method(const ScrDyn *value); +/* The values the iterator `method` gives for `source` (+1), or NULL with + * the exception pending. */ +ScrDyn *scr_dyn_iterate_with(const ScrDyn *source, ScrDyn *method); /* Keyed read on a FUNC node: the own-property table first (defineProperties * writes land there), then "name" (the box's best-effort static name; "" * for anonymous) and "length" (the boxed arity). Returns +1, or NULL when * the key answers nothing (the caller's undefined). Never throws. */ ScrDyn *scr_dyn_fn_get(const ScrDyn *d, const char *key, size_t key_len); +/* A function value's own property table, created on first use. +1. */ +ScrDyn *scr_dyn_function_properties(const ScrDyn *function); +/* The table a function value already has. +1, or NULL. */ +ScrDyn *scr_dyn_function_properties_existing(const ScrDyn *function); /* Object.defineProperties over dyn values (targets: OBJ and FUNC): each * descriptor's `value` becomes an own data property. OBJ targets preserve * writable/enumerable/configurable; FUNC targets still use plain properties. @@ -1468,6 +1523,9 @@ ScrClassObj *scr_dyn_class_check(const ScrDyn *value, const char *type_key, cons * prints as "Uncaught TypeError: ..." and exits 1). This is * scriptc-specific behavior — JS `as` never checks (SEMANTICS.md). */ void scr_dyn_check_fail(const ScrDynPath *path, const char *want, const ScrDyn *got); +/* The Date object a checked-dynamic Date handle holds, +1, or NULL with + * the check failure pending. */ +ScrDate *scr_dyn_native_date_check(const ScrDyn *value, const ScrDynPath *path); /* Matchers and builders of recursive shapes (records and arrays whose type * reaches itself) track the dyn values they visit, so cyclic values match @@ -1569,6 +1627,12 @@ typedef struct ScrDynHandleOps { /* Past the depth limit util.inspect prints `[cls]` (the class's * inspector returns `this`) instead of the bare class name. */ bool inspect_bracketed_past_depth; + /* The class's inspector formats its view with a new util.inspect call + * (URL's does): the view renders from the top level with the caller's + * depth, and its lines take the caller's indentation. Past the depth + * limit the inspector returns the instance, which has no own + * properties, so util.inspect prints `cls {}`. */ + bool inspect_view_nested; /* The @@toStringTag when it differs from cls: String() answers * "[object ]" and util.inspect prints ` [] `, or * ` ` when the tag ends with "." and cls, as Node does. */ diff --git a/packages/runtime/src/scr_dyn_invoke.c b/packages/runtime/src/scr_dyn_invoke.c index 364017e05..ebbfa8411 100644 --- a/packages/runtime/src/scr_dyn_invoke.c +++ b/packages/runtime/src/scr_dyn_invoke.c @@ -144,6 +144,10 @@ static void scr_dyn_display_buf(ScrJsonBuf *b, const ScrDyn *d, bool to_string) return; case SCR_DYN_TYPED_REF: { + /* ToString of a class instance that converts through + * Object.prototype.toString shows its Symbol.toStringTag getter's + * string. */ + if (to_string && scr_dyn_typed_ref_put_tag(b, d)) return; ScrDyn *materialized = scr_dyn_typed_ref_materialize(d); scr_dyn_display_buf(b, materialized, to_string); scr_dyn_release(materialized); @@ -191,13 +195,6 @@ static double dyn_index_arg(ScrDyn *const *args, size_t argc, size_t i, double d return isnan(number) ? 0 : trunc(number); } -static double dyn_bytes_index_arg(ScrDyn *const *args, size_t argc, size_t index, double fallback) { - if (index >= argc || args[index]->kind == SCR_DYN_UNDEF) return fallback; - double value; - if (!scr_dyn_number_coerce_js(args[index], &value)) return 0; - return isnan(value) ? 0 : trunc(value); -} - /* JS relative-index normalization (slice's rule). */ static size_t dyn_rel_index(double rel, size_t len) { if (rel < 0) { @@ -716,7 +713,8 @@ static bool dyn_bytes_proto_real(int m) { case DYN_M_every: case DYN_M_find: case DYN_M_findIndex: case DYN_M_reverse: case DYN_M_fill: case DYN_M_set: case DYN_M_subarray: case DYN_M_sort: case DYN_M_keys: case DYN_M_values: case DYN_M_entries: case DYN_M_reduce: case DYN_M_reduceRight: case DYN_M_copyWithin: - case DYN_M_toString: case DYN_M_toLocaleString: + case DYN_M_toString: case DYN_M_toLocaleString: case DYN_M_findLast: case DYN_M_findLastIndex: + case DYN_M_toReversed: case DYN_M_toSorted: case DYN_M_with: return true; default: return false; @@ -740,6 +738,8 @@ static ScrDyn *dyn_live_array_method(ScrDyn *recv, int mid, ScrDyn *const *args, if (!ops) return NULL; ScrArr *arr = recv->v.typed_ref.ptr; size_t len = arr->len; + /* A frozen array's view refuses the write. */ + if (scr_arr_frozen(arr) && ((mid == DYN_M_push) || (mid == DYN_M_pop))) return NULL; if ((mid == DYN_M_push) && argc == 1 && ops->set) { if (!ops->set(arr, (double)len, args[0])) return NULL; *handled = true; @@ -1112,16 +1112,20 @@ static ScrDyn *scr_dyn_invoke_impl( return NULL; } } - if (recv->v.arr.frozen && ((mid == DYN_M_push) || - (mid == DYN_M_pop) || (mid == DYN_M_shift) || - (mid == DYN_M_unshift) || (mid == DYN_M_splice))) { - static const char message[] = "Cannot assign to read only property 'length'"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + /* A frozen array throws at the operation's first write, as in Node. */ + int frozen_op = !recv->v.arr.frozen ? -1 + : (mid == DYN_M_push) ? 1 + : (mid == DYN_M_pop) ? 2 + : (mid == DYN_M_shift) ? 6 + : (mid == DYN_M_unshift) ? 4 + : (mid == DYN_M_splice) ? 10 + : ((mid == DYN_M_sort) || (mid == DYN_M_reverse)) ? 7 + : -1; + double items = (mid == DYN_M_splice) ? (argc > 2 ? (double)(argc - 2) : 0) : (double)argc; + if (frozen_op >= 0 && + scr_array_frozen_error(frozen_op, len, (double)splice_start, frozen_op == 10 ? (double)splice_removed : items, items)) return NULL; - } - if ((grows && recv->non_extensible) || (shrinks && recv->v.arr.sealed && !(mid == DYN_M_shift)) || - (recv->v.arr.frozen && (((mid == DYN_M_sort) && len > 1) || - ((mid == DYN_M_reverse) && len > 1)))) { + if ((grows && recv->non_extensible) || (shrinks && recv->v.arr.sealed && !(mid == DYN_M_shift))) { static const char message[] = "Cannot modify a sealed or frozen array"; scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); return NULL; @@ -1390,11 +1394,7 @@ static ScrDyn *scr_dyn_invoke_impl( if (!dyn_arr_length_unchanged(recv, len)) return NULL; size_t start = dyn_rel_index(startD, len); size_t end = dyn_rel_index(endD, len); - if (recv->v.arr.frozen && start < end) { - static const char message[] = "Cannot assign to read only array index"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); - return NULL; - } + if (recv->v.arr.frozen && scr_array_frozen_error(8, len, (double)start, (double)end, 0)) return NULL; ScrDyn *fill = argc ? args[0] : scr_dyn_undefined(); if (start < end) scr_dyn_region_store(recv, fill); for (size_t i = start; i < end; i++) { @@ -1423,11 +1423,8 @@ static ScrDyn *scr_dyn_invoke_impl( size_t end = dyn_rel_index(endD, len); size_t count = end > start ? end - start : 0; if (count > len - target) count = len - target; - if (recv->v.arr.frozen && count > 0) { - static const char message[] = "Cannot assign to read only array index"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + if (recv->v.arr.frozen && scr_array_frozen_error(9, len, (double)target, (double)start, (double)end)) return NULL; - } if (start < target && target < start + count) { for (size_t i = count; i > 0; i--) { ScrDyn *old = recv->v.arr.items[target + i - 1]; @@ -1661,63 +1658,9 @@ static ScrDyn *scr_dyn_invoke_impl( dyn_throw_not_fn(what); return NULL; } - size_t blen = bytes->len; - if ((mid == DYN_M_at)) { - double iD = dyn_bytes_index_arg(args, argc, 0, 0); - if (scr_exc_pending()) return NULL; - double idx = iD < 0 ? (double)blen + iD : iD; - if (idx < 0 || idx >= (double)blen) return scr_dyn_retain(scr_dyn_undefined()); - return scr_dyn_new_num(scr_bytes_get(bytes, idx)); - } - if ((mid == DYN_M_slice) || (mid == DYN_M_subarray)) { - /* subarray and Buffer.slice alias their source. TypedArray.slice - * owns an independent copy; all keep the receiver's Buffer flavor. */ - double startD = dyn_bytes_index_arg(args, argc, 0, 0); - if (scr_exc_pending()) return NULL; - double endD = dyn_bytes_index_arg(args, argc, 1, (double)blen); - if (scr_exc_pending()) return NULL; - ScrBytes *out = recv->buffer || (mid == DYN_M_subarray) - ? scr_bytes_subarray(bytes, startD, endD) : scr_bytes_slice(bytes, startD, endD); - ScrDyn *d = recv->buffer ? scr_dyn_new_buffer(out) : scr_dyn_new_bytes(out); - scr_bytes_release(out); - return d; - } - if ((mid == DYN_M_set)) { - double offset = dyn_bytes_index_arg(args, argc, 1, 0); - if (scr_exc_pending()) return NULL; - scr_bytes_set_from_dyn(bytes, argc ? args[0] : scr_dyn_undefined(), offset); - return scr_exc_pending() ? NULL : scr_dyn_retain(scr_dyn_undefined()); - } - if ((mid == DYN_M_copyWithin)) { - double target = dyn_bytes_index_arg(args, argc, 0, 0); - if (scr_exc_pending()) return NULL; - double start = dyn_bytes_index_arg(args, argc, 1, 0); - if (scr_exc_pending()) return NULL; - double end = dyn_bytes_index_arg(args, argc, 2, (double)blen); - if (scr_exc_pending()) return NULL; - scr_bytes_release(scr_bytes_copy_within(bytes, target, start, end)); - return scr_dyn_retain(recv); - } - if ((mid == DYN_M_fill) && !recv->buffer) { - double value; - if (!scr_dyn_number_coerce_js(argc ? args[0] : scr_dyn_undefined(), &value)) return NULL; - double start = dyn_bytes_index_arg(args, argc, 1, 0); - if (scr_exc_pending()) return NULL; - double end = dyn_bytes_index_arg(args, argc, 2, (double)blen); - if (scr_exc_pending()) return NULL; - scr_bytes_release(scr_bytes_fill_elem(bytes, value, start, end)); - return scr_dyn_retain(recv); - } - if ((mid == DYN_M_join) || ((mid == DYN_M_toString) && !recv->buffer)) { - ScrStr *separator = (mid == DYN_M_join) && argc && args[0]->kind != SCR_DYN_UNDEF - ? scr_dyn_string_coerce_js(args[0]) : scr_str_new(",", 1); - if (scr_exc_pending()) { scr_str_release(separator); return NULL; } - ScrStr *joined = scr_bytes_join(bytes, separator); - scr_str_release(separator); - ScrDyn *out = scr_dyn_new_str(joined); - scr_str_release(joined); - return out; - } + bool handled; + ScrDyn *answer = scr_dyn_bytes_method(recv, method, args, argc, &handled); + if (handled) return answer; if (dyn_bytes_proto_real(mid)) { dyn_throw_unsupported(scr_bytes_elem_name(bytes->elem), method); return NULL; @@ -1972,7 +1915,7 @@ static bool dyn_global_construct(const ScrDyn *callee, const ScrDyn *args, ScrDy static ScrDyn *dyn_global_ctor(const char *name) { for (size_t i = 0; i < DYN_GLOBAL_CTORS; i++) { - if (strcmp(dyn_global_ctor_names[i], name) != 0) continue; + if (dyn_global_ctor_names[i][0] != name[0] || strcmp(dyn_global_ctor_names[i], name) != 0) continue; if (!dyn_global_ctors_registered) { dyn_global_ctors_registered = true; scr_atexit(dyn_global_ctors_cleanup); @@ -1985,6 +1928,9 @@ static ScrDyn *dyn_global_ctor(const char *name) { dyn_global_ctors[i] = scr_dyn_new_func(scr_closure_new((void *)dyn_global_ctor_names[i], 0), dyn_global_ctor_call, length, dyn_s6, dyn_global_ctor_names[i]); + /* `o.constructor.prototype` of a plain object is Object.prototype + * (zod's isPlainObject). The other prototypes are not modeled. */ + if (i == 0) scr_dyn_fn_seed_prototype(dyn_global_ctors[i], scr_dyn_object_prototype()); } return scr_dyn_retain(dyn_global_ctors[i]); } diff --git a/packages/runtime/src/scr_error.h b/packages/runtime/src/scr_error.h index fbb64aad9..4962db217 100644 --- a/packages/runtime/src/scr_error.h +++ b/packages/runtime/src/scr_error.h @@ -48,6 +48,12 @@ typedef struct ScrClassObj { size_t length; struct ScrBox *prototype_data; struct ScrBox *static_data; + /* The base class's name, which util.inspect prints as + * `[class C extends B]`; NULL without a named base. */ + const ScrStr *base_name; + /* Nonzero when the class declares public static fields, which the + * class object does not carry without static_data. */ + size_t own_static_fields; struct ScrBox *caps[]; } ScrClassObj; diff --git a/packages/runtime/src/scr_events.c b/packages/runtime/src/scr_events.c index 3391f54b1..6e0bf5146 100644 --- a/packages/runtime/src/scr_events.c +++ b/packages/runtime/src/scr_events.c @@ -388,8 +388,80 @@ static SCR_TL bool scr_stdin_destroyed = false; static SCR_TL bool scr_stdin_paused = false; static SCR_TL bool scr_stdin_resumed = false; static SCR_TL bool scr_stdin_read_eof = false; -static SCR_TL ScrBytes *scr_stdin_buffer; +/* Input held for read() and later consumers, as Node's BufferList: the + * chunks in arrival order and their total length, so an arrival appends + * without copying what is already held. */ +static SCR_TL ScrBytes **scr_stdin_held = NULL; +static SCR_TL size_t scr_stdin_nheld = 0, scr_stdin_held_cap = 0; +static SCR_TL size_t scr_stdin_buffered = 0; static SCR_TL size_t scr_stdin_read_demand; +/* 'readable' listeners (paused mode, as Node's once one is added): input + * stays buffered for read(), and the event fires on the next tick when + * input arrives while a read wanted more (`need`), and once at EOF. */ +static SCR_TL ScrStdinEndL *scr_stdin_readable = NULL; +static SCR_TL size_t scr_stdin_nreadable = 0, scr_stdin_readable_cap = 0; +static SCR_TL bool scr_stdin_readable_need = false; +static SCR_TL bool scr_stdin_readable_queued = false; + +/* Appends an arrived chunk (moves) to the held input. */ +static void scr_stdin_hold(ScrBytes *chunk) { + if (chunk->len == 0) { + scr_bytes_release(chunk); + return; + } + if (scr_stdin_nheld == scr_stdin_held_cap) { + scr_stdin_held_cap = scr_stdin_held_cap ? scr_stdin_held_cap * 2 : 4; + scr_stdin_held = realloc(scr_stdin_held, scr_stdin_held_cap * sizeof *scr_stdin_held); + if (!scr_stdin_held) scr_events_oom(); + } + scr_stdin_held[scr_stdin_nheld++] = chunk; + scr_stdin_buffered += chunk->len; +} + +/* The first n held bytes as one chunk (+1), 0 < n <= scr_stdin_buffered. + * A whole first chunk moves out without a copy; otherwise the bytes are + * joined once, and a partly taken chunk keeps its rest. */ +static ScrBytes *scr_stdin_take(size_t n) { + ScrBytes *first = scr_stdin_held[0]; + size_t used = 0; + ScrBytes *out; + if (n == first->len) { + out = first; + used = 1; + } else if (n < first->len) { + out = scr_bytes_slice(first, 0, (double)n); + scr_stdin_held[0] = scr_bytes_slice(first, (double)n, (double)first->len); + scr_bytes_release(first); + } else { + out = scr_bytes_new(SCR_BYTES_U8, (double)n); + size_t at = 0; + while (at < n) { + ScrBytes *chunk = scr_stdin_held[used]; + size_t part = chunk->len <= n - at ? chunk->len : n - at; + memcpy(out->data + at, chunk->data, part); + at += part; + if (part == chunk->len) { + scr_bytes_release(chunk); + used++; + } else { + scr_stdin_held[used] = scr_bytes_slice(chunk, (double)part, (double)chunk->len); + scr_bytes_release(chunk); + } + } + } + memmove(scr_stdin_held, scr_stdin_held + used, (scr_stdin_nheld - used) * sizeof *scr_stdin_held); + scr_stdin_nheld -= used; + scr_stdin_buffered -= n; + return out; +} + +static void scr_stdin_drop_held(void) { + for (size_t i = 0; i < scr_stdin_nheld; i++) scr_bytes_release(scr_stdin_held[i]); + free(scr_stdin_held); + scr_stdin_held = NULL; + scr_stdin_nheld = scr_stdin_held_cap = 0; + scr_stdin_buffered = 0; +} #ifdef SCR_STDIO_EXTENDED /* process.stdin.setEncoding(enc): the canonical encoding name (NULL until @@ -475,6 +547,78 @@ void scr_stdin_on_end(ScrClosure *cb /*moves*/, bool once) { scr_stdin_nend++; } +static void scr_stdin_finish(bool fire_end); + +static void scr_stdin_emit_readable(void) { + scr_stdin_readable_queued = false; + size_t length = scr_stdin_buffered; + if (scr_stdin_eof || scr_stdin_destroyed || (!length && !scr_stdin_read_eof)) return; + size_t n = scr_stdin_nreadable; + ScrStdinEndL *snap = malloc((n ? n : 1) * sizeof *snap); + if (!snap) scr_events_oom(); + for (size_t i = 0; i < n; i++) { + snap[i] = scr_stdin_readable[i]; + scr_closure_retain(snap[i].cb); + } + for (size_t i = 0; i < n; i++) { + if (snap[i].once) { + for (size_t j = 0; j < scr_stdin_nreadable; j++) { + if (scr_stdin_readable[j].cb == snap[i].cb) { + scr_closure_release(scr_stdin_readable[j].cb); + memmove(scr_stdin_readable + j, scr_stdin_readable + j + 1, + (scr_stdin_nreadable - j - 1) * sizeof *scr_stdin_readable); + scr_stdin_nreadable--; + break; + } + } + } + if (!scr_exc_pending() && !scr_stdin_eof && !scr_stdin_destroyed) + ((void (*)(ScrClosure *))snap[i].cb->fn)(snap[i].cb); + scr_closure_release(snap[i].cb); + } + free(snap); + length = scr_stdin_buffered; + /* Another event is due once the buffer reaches the 64 KiB mark, or the + * larger size a listener's read(size) asked for. */ + scr_stdin_readable_need = + !scr_stdin_read_eof && (length <= 65536 || length < scr_stdin_read_demand); +} + +static void scr_stdin_queue_readable(void) { + if (scr_stdin_readable_queued || !scr_stdin_nreadable) return; + scr_stdin_readable_queued = true; + scr_next_tick_raw(scr_stdin_emit_readable); +} + +/* Adding a 'readable' listener starts reading ahead to the 64 KiB mark; a + * buffer that already holds input fires the event at once (next tick). */ +static void scr_stdin_on_readable(ScrClosure *cb /*moves*/, bool once) { + if (scr_stdin_eof || scr_stdin_destroyed) { + scr_closure_release(cb); + return; + } + if (scr_stdin_nreadable == scr_stdin_readable_cap) { + scr_stdin_readable_cap = scr_stdin_readable_cap ? scr_stdin_readable_cap * 2 : 2; + scr_stdin_readable = realloc(scr_stdin_readable, scr_stdin_readable_cap * sizeof *scr_stdin_readable); + if (!scr_stdin_readable) scr_events_oom(); + } + scr_stdin_readable[scr_stdin_nreadable].cb = cb; + scr_stdin_readable[scr_stdin_nreadable].once = once; + scr_stdin_nreadable++; + scr_stdin_readable_need = true; + if (scr_stdin_read_demand < 65536) scr_stdin_read_demand = 65536; + if (scr_stdin_buffered) scr_stdin_queue_readable(); +} + +/* The input ended: with 'readable' listeners the event fires once more and + * 'end' waits for the read that finds nothing left; otherwise 'end' fires + * once the buffer is drained. */ +static void scr_stdin_reached_eof(void) { + scr_stdin_read_eof = true; + if (scr_stdin_nreadable) scr_stdin_queue_readable(); + else if (!scr_stdin_buffered) scr_stdin_finish(true); +} + void scr_stdin_on_error(ScrClosure *cb /*moves*/, ScrChildErrFn fn, bool once) { if (scr_stdin_eof || scr_stdin_destroyed) { scr_closure_release(cb); @@ -516,10 +660,15 @@ bool scr_stdin_ended(void) { return scr_stdin_eof || scr_stdin_destroyed; } * alive (a paused Node stdin with only those never flows — the process * exits with them registered). */ static bool scr_stdin_watching(void) { - return !scr_stdin_eof && !scr_stdin_destroyed && - ((!scr_stdin_paused && (scr_stdin_ndata > 0 || scr_stdin_resumed)) || - scr_stdin_waiter != NULL || - (!scr_stdin_read_eof && scr_stdin_read_demand > (scr_stdin_buffer ? scr_stdin_buffer->len : 0))); + if (scr_stdin_eof || scr_stdin_destroyed) return false; + /* 'readable' listeners keep the stream paused, as in Node, where + * resume() and 'data' listeners do not make it flow: it reads ahead only + * while a read wants more than is held (the 64 KiB mark, or a larger + * read(size)), and stops until something drains it. */ + if (scr_stdin_nreadable) return !scr_stdin_read_eof && scr_stdin_read_demand > scr_stdin_buffered; + return (!scr_stdin_paused && (scr_stdin_ndata > 0 || scr_stdin_resumed)) || + scr_stdin_waiter != NULL || + (!scr_stdin_read_eof && scr_stdin_read_demand > scr_stdin_buffered); } bool scr_stdin_pending(void) { return scr_stdin_watching(); } @@ -528,6 +677,10 @@ static void scr_stdin_drop_listeners(void) { for (size_t i = 0; i < scr_stdin_ndata; i++) scr_closure_release(scr_stdin_data[i].cb); for (size_t i = 0; i < scr_stdin_nend; i++) scr_closure_release(scr_stdin_end[i].cb); for (size_t i = 0; i < scr_stdin_nerr; i++) scr_closure_release(scr_stdin_err[i].cb); + for (size_t i = 0; i < scr_stdin_nreadable; i++) scr_closure_release(scr_stdin_readable[i].cb); + free(scr_stdin_readable); + scr_stdin_readable = NULL; + scr_stdin_nreadable = scr_stdin_readable_cap = 0; free(scr_stdin_data); free(scr_stdin_end); free(scr_stdin_err); @@ -593,8 +746,7 @@ static void scr_stdin_finish(bool fire_end) { void scr_stdin_destroy(void) { if (scr_stdin_destroyed) return; scr_stdin_destroyed = true; - scr_bytes_release(scr_stdin_buffer); - scr_stdin_buffer = NULL; + scr_stdin_drop_held(); scr_stdin_read_demand = 0; scr_stdin_settle_done(); scr_stdin_drop_listeners(); @@ -627,16 +779,9 @@ static void scr_stdin_deliver(ScrBytes *chunk); /* A chunk that arrived (moves): delivered, or buffered while paused or * without a consumer. */ static void scr_stdin_arrived(ScrBytes *chunk) { - if (scr_stdin_paused || (!scr_stdin_ndata && !scr_stdin_resumed && !scr_stdin_waiter)) { - if (scr_stdin_buffer) { - size_t before = scr_stdin_buffer->len; - ScrBytes *joined = scr_bytes_new(SCR_BYTES_U8, (double)(before + chunk->len)); - memcpy(joined->data, scr_stdin_buffer->data, before); - memcpy(joined->data + before, chunk->data, chunk->len); - scr_bytes_release(scr_stdin_buffer); - scr_bytes_release(chunk); - scr_stdin_buffer = joined; - } else scr_stdin_buffer = chunk; + if (scr_stdin_paused || scr_stdin_nreadable || (!scr_stdin_ndata && !scr_stdin_resumed && !scr_stdin_waiter)) { + if (scr_stdin_nreadable && scr_stdin_readable_need) scr_stdin_queue_readable(); + scr_stdin_hold(chunk); return; } scr_stdin_deliver(chunk); @@ -645,15 +790,18 @@ static void scr_stdin_arrived(ScrBytes *chunk) { static void scr_stdin_service(void) { if (!scr_stdin_watching()) return; - if (!scr_stdin_paused && scr_stdin_buffer) { - ScrBytes *chunk = scr_stdin_buffer; - scr_stdin_buffer = NULL; + /* Buffered input goes to a consumer: 'data' listeners, a resumed + * stream, or a parked for-await read. After the last 'readable' listener + * it stays buffered until one arrives, as in Node. */ + if (!scr_stdin_paused && !scr_stdin_nreadable && scr_stdin_buffered && + (scr_stdin_ndata > 0 || scr_stdin_resumed || scr_stdin_waiter != NULL)) { + ScrBytes *chunk = scr_stdin_take(scr_stdin_buffered); scr_stdin_deliver(chunk); scr_bytes_release(chunk); return; } if (scr_stdin_read_eof) { - if (!scr_stdin_buffer) scr_stdin_finish(true); + if (!scr_stdin_buffered && !scr_stdin_nreadable) scr_stdin_finish(true); return; } if (scr_stdin_fed) { @@ -662,10 +810,7 @@ static void scr_stdin_service(void) { bool ended = false; ScrBytes *chunk = scr_stdin_fed_take(&ended); if (chunk) scr_stdin_arrived(chunk); - else if (ended) { - scr_stdin_read_eof = true; - if (!scr_stdin_buffer) scr_stdin_finish(true); - } + else if (ended) scr_stdin_reached_eof(); return; } #ifdef _WIN32 @@ -727,8 +872,7 @@ static void scr_stdin_service(void) { return; } if (n == 0) { - scr_stdin_read_eof = true; - if (!scr_stdin_buffer) scr_stdin_finish(true); + scr_stdin_reached_eof(); return; } ScrBytes *chunk = scr_bytes_new(SCR_BYTES_U8, (double)n); @@ -802,6 +946,8 @@ static void scr_stdin_deliver(ScrBytes *chunk) { #endif ScrPromise *p = scr_stdin_waiter; scr_stdin_waiter = NULL; + /* Node's stdin yields Buffers. */ + chunk->is_buffer = true; scr_promise_fulfill_ref(p, scr_bytes_retain(chunk), scr_bytes_retain_v, scr_bytes_release_v, NULL); scr_promise_release(p); @@ -878,6 +1024,7 @@ static void scr_stdio_data_text(ScrClosure *cb, ScrStr *text) { } #endif static void scr_stdio_end(ScrClosure *cb) { scr_stdio_fire(cb, NULL, 0); } +static void scr_stdio_readable(ScrClosure *cb) { scr_stdio_fire(cb, NULL, 0); } static void scr_stdio_error(ScrClosure *cb, ScrStr *message) { ScrError *error = scr_error_new(SCR_ERR_ERROR, message); ScrDyn *arg = scr_dyn_from_error(error); @@ -971,7 +1118,7 @@ static void scr_stdio_queue_write_error(ScrStdio *stream, ScrDyn *callback, int } static void scr_stdio_finish_read(void) { - if (scr_stdin_read_eof && !scr_stdin_buffer && !scr_stdin_eof && !scr_stdin_destroyed) + if (scr_stdin_read_eof && !scr_stdin_buffered && !scr_stdin_eof && !scr_stdin_destroyed) scr_stdin_finish(true); } @@ -980,21 +1127,21 @@ static ScrDyn *scr_stdio_read(ScrDyn *size) { if (!all && (size->kind != SCR_DYN_NUM || !isfinite(size->v.num) || size->v.num < 0 || size->v.num > 1073741824 || floor(size->v.num) != size->v.num)) return scr_stdio_refusal("read size"); - size_t available = scr_stdin_buffer ? scr_stdin_buffer->len : 0; + size_t available = scr_stdin_buffered; size_t wanted = all ? available : (size_t)size->v.num; if (scr_stdin_read_eof && wanted > available) wanted = available; if (wanted == 0 || wanted > available) { if (!scr_stdin_eof && !scr_stdin_destroyed) { + scr_stdin_readable_need = true; scr_stdin_read_demand = wanted > 65536 ? wanted : 65536; if (scr_stdin_read_eof) scr_next_tick_raw(scr_stdio_finish_read); } return scr_dyn_new_null(); } - ScrBytes *chunk = scr_bytes_slice(scr_stdin_buffer, 0, (double)wanted); - ScrBytes *rest = wanted < available ? scr_bytes_slice(scr_stdin_buffer, (double)wanted, (double)available) : NULL; - scr_bytes_release(scr_stdin_buffer); - scr_stdin_buffer = rest; + ScrBytes *chunk = scr_stdin_take(wanted); + bool rest = scr_stdin_buffered > 0; scr_stdin_read_demand = 65536; + if (!rest && !scr_stdin_read_eof) scr_stdin_readable_need = true; scr_stdin_deliver(chunk); ScrDyn *result = scr_dyn_new_chunk(chunk); scr_bytes_release(chunk); @@ -1150,12 +1297,14 @@ static ScrDyn *scr_stdio_method(ScrClosure *cb, ScrDyn *const *args, size_t argc bool data = arg->kind == SCR_DYN_STR && arg->v.str->len == 4 && memcmp(event, "data", 4) == 0; bool end = arg->kind == SCR_DYN_STR && arg->v.str->len == 3 && memcmp(event, "end", 3) == 0; bool error = arg->kind == SCR_DYN_STR && arg->v.str->len == 5 && memcmp(event, "error", 5) == 0; + bool readable = stream->fd == 0 && arg->kind == SCR_DYN_STR && arg->v.str->len == 8 && + memcmp(event, "readable", 8) == 0; #ifdef SCR_STDIO_EXTENDED bool resize = stream->fd != 0 && arg->kind == SCR_DYN_STR && arg->v.str->len == 6 && memcmp(event, "resize", 6) == 0; - if ((!data && !end && !error && !resize) || (stream->fd != 0 && !error && !resize)) { + if ((!data && !end && !error && !readable && !resize) || (stream->fd != 0 && !error && !resize)) { #else - if ((!data && !end && !error) || (stream->fd != 0 && !error)) { + if ((!data && !end && !error && !readable) || (stream->fd != 0 && !error)) { #endif result = scr_stdio_refusal("event"); goto done; @@ -1175,6 +1324,7 @@ static ScrDyn *scr_stdio_method(ScrClosure *cb, ScrDyn *const *args, size_t argc if (data) scr_stdin_on_data(scr_stdio_callback(listener, (void *)scr_stdio_data), scr_stdio_data, once); #endif if (end) scr_stdin_on_end(scr_stdio_callback(listener, (void *)scr_stdio_end), once); + if (readable) scr_stdin_on_readable(scr_stdio_callback(listener, (void *)scr_stdio_readable), once); if (error) scr_stdin_on_error(scr_stdio_callback(listener, (void *)scr_stdio_error), scr_stdio_error, once); } else { #define REMOVE_STDIO_LISTENER(list, count, fire) \ @@ -1187,6 +1337,7 @@ static ScrDyn *scr_stdio_method(ScrClosure *cb, ScrDyn *const *args, size_t argc } if (data) { REMOVE_STDIO_LISTENER(scr_stdin_data, scr_stdin_ndata, scr_stdio_data); } if (end) { REMOVE_STDIO_LISTENER(scr_stdin_end, scr_stdin_nend, scr_stdio_end); } + if (readable) { REMOVE_STDIO_LISTENER(scr_stdin_readable, scr_stdin_nreadable, scr_stdio_readable); } if (error) { REMOVE_STDIO_LISTENER(scr_stdin_err, scr_stdin_nerr, scr_stdio_error); } #undef REMOVE_STDIO_LISTENER } @@ -1246,7 +1397,7 @@ static ScrDyn *scr_stdio_get(void *ptr, const char *key, size_t length) { return dimension < 0 ? scr_dyn_retain(scr_dyn_undefined()) : scr_dyn_new_num(dimension); } if (stream->fd == 0 && length == 14 && memcmp(key, "readableLength", 14) == 0) - return scr_dyn_new_num(scr_stdin_buffer ? (double)scr_stdin_buffer->len : 0); + return scr_dyn_new_num((double)scr_stdin_buffered); if (stream->fd == 0 && length == 5 && memcmp(key, "isRaw", 5) == 0) return scr_process_is_tty(0) ? scr_dyn_new_bool(scr_stdio_raw) : scr_dyn_retain(scr_dyn_undefined()); if (stream->fd == 0 && length == 9 && memcmp(key, "destroyed", 9) == 0) return scr_dyn_new_bool(scr_stdin_destroyed); @@ -1531,8 +1682,7 @@ static void scr_events_cleanup_atexit(void) { if (scr_context_is_main()) scr_sig_cleanup(); scr_stdin_settle_done(); scr_stdin_drop_listeners(); - scr_bytes_release(scr_stdin_buffer); - scr_stdin_buffer = NULL; + scr_stdin_drop_held(); if (scr_stdio_initialized) scr_stdio_cleanup(); } diff --git a/packages/runtime/src/scr_fetch.c b/packages/runtime/src/scr_fetch.c index aebe62659..d28707d5d 100644 --- a/packages/runtime/src/scr_fetch.c +++ b/packages/runtime/src/scr_fetch.c @@ -147,6 +147,9 @@ struct SfStream { bool pull_again; bool discarded; bool request_discarding; + /* A zero high-water mark (a TransformStream's readable side): pull only + * while a read is pending. */ + bool hwm_zero; ScrDyn *error; SfReader *reader; /* owned while locked */ SfCollector *collector; /* owned while a body reader is active */ @@ -167,12 +170,23 @@ struct SfStream { SfTransfer *response_owner; /* weak; transfer owns the stream */ bool callbacks_tracked; SfStream *callbacks_next; + /* Told when the stream errors (a tee watching its source's closed + * promise); ctx is borrowed and the watcher clears it before it goes. */ + void (*error_watch)(void *ctx, ScrDyn *reason); + void *error_watch_ctx; }; struct SfHeaders { size_t rc; ScrArr *pairs; + /* Fetch's "sort and combine" list of `pairs` as flat name/value pairs, + * built on the first iteration step and dropped by every mutation. */ + ScrArr *sorted; bool immutable; + /* `pairs` is in the form sf_add_converted_headers builds: lowercase valid + * names, trimmed valid UTF-8 values, one combined pair per name except + * set-cookie. Headers built from wire lines are not (duplicates stay). */ + bool canonical; }; struct SfResponse { @@ -898,6 +912,7 @@ static void sf_stream_release(SfStream *s) { sf_response_discard_if_unobserved(s); return; } + scr_weak_dispose_inline(s); sf_stream_drop_chunks(s); if (s->reader) { s->reader->stream = NULL; @@ -1291,6 +1306,7 @@ static void sf_stream_error(SfStream *s, ScrDyn *reason) { if (s->reader) { sf_reject_promise_reason(s->reader->closed, reason); } + if (s->error_watch) s->error_watch(s->error_watch_ctx, reason); if (s->request_owner) { sf_transfer_stream_error(s->request_owner, reason); } @@ -1618,6 +1634,14 @@ static void sf_stream_schedule_pull_complete(SfStream *s) { scr_queue_microtask(cb); } +/* A consumer waiting for the stream's next chunk: a pending read, a body + * collector, or a fetch upload (fetch keeps reading the request body, + * including after an aborted transfer stops writing it). */ +static bool sf_stream_has_consumer(const SfStream *s) { + return s->collector || (s->reader && s->reader->pending_head) || s->request_owner || + s->request_discarding; +} + static void sf_stream_pull(SfStream *s) { if (!s->started || (!s->pull_cb && !sf_stream_has_from_source(s)) || @@ -1625,6 +1649,10 @@ static void sf_stream_pull(SfStream *s) { s->error) { return; } + /* ReadableStreamDefaultControllerShouldCallPull: with no room in the + * queue, only a consumer asks for a chunk: a pending read, a body + * collector, or a fetch upload, whose reader is always waiting. */ + if (s->hwm_zero && !sf_stream_has_consumer(s)) return; if (s->pulling) { s->pull_again = true; return; @@ -1852,9 +1880,241 @@ static ScrPromise *sf_stream_collect(SfStream *s, int mode) { c->mode = mode; s->collector = c; sf_stream_drain(s); + if (s->hwm_zero && !s->head) sf_stream_pull(s); return p; } +/* ── ReadableStreamDefaultTee ────────────────────────────────────── */ + +/* + * The streams spec's ReadableStreamDefaultTee over a native stream: the + * tee locks the source with a reader and feeds two native branches whose + * pull and cancel algorithms are native functions. A pull on either branch + * reads one chunk from the source and enqueues it into every branch that + * was not canceled; canceling both branches cancels the source with both + * reasons. Response.clone and Request.clone use it (undici's cloneBody). + * + * Ownership: the tee owns the source, its reader and both branches; each + * branch's callbacks and every pending read own the tee. Closing, erroring + * or canceling a branch drops its callbacks (sf_stream_drop_source_callbacks), + * and fetch teardown drops the callbacks of branches nobody finished. + */ +typedef struct SfTee { + size_t rc; + SfStream *source; + SfReader *reader; + SfStream *branch[2]; + bool reading; + bool read_again; + bool canceled[2]; + ScrDyn *reason[2]; + ScrPromise *cancel_promise; +} SfTee; + +static SfTee *sf_tee_retain(SfTee *t) { + t->rc++; + return t; +} + +static void sf_tee_release(SfTee *t) { + if (!t || --t->rc > 0) return; + if (t->source->error_watch_ctx == t) { + t->source->error_watch = NULL; + t->source->error_watch_ctx = NULL; + } + sf_stream_release(t->branch[0]); + sf_stream_release(t->branch[1]); + sf_reader_release(t->reader); + sf_stream_release(t->source); + scr_dyn_release(t->reason[0]); + scr_dyn_release(t->reason[1]); + scr_promise_release(t->cancel_promise); + free(t); +} + +static void *sf_tee_retain_v(void *p) { return sf_tee_retain((SfTee *)p); } +static void sf_tee_release_v(void *p) { sf_tee_release((SfTee *)p); } + +static ScrBox *sf_tee_box(SfTee *t) { + ScrBox *box = scr_box_new_obj(&sf_tee_retain_v, &sf_tee_release_v, NULL); + scr_box_set_ref(box, sf_tee_retain(t)); + return box; +} + +static void sf_tee_pull(SfTee *t); + +/* The read request's chunk steps run in a microtask (spec: "queue a + * microtask"), after the read that produced the chunk settles. */ +static void sf_tee_chunk_task(ScrClosure *cb) { + SfTee *t = (SfTee *)scr_box_get_ref(cb->caps[0]); + ScrDyn *chunk = (ScrDyn *)scr_box_get_ref(cb->caps[1]); + t->read_again = false; + for (int i = 0; i < 2; i++) { + if (t->canceled[i]) continue; + SfStream *b = t->branch[i]; + if (b->close_requested || b->closed || b->error) continue; + sf_stream_enqueue_value(b, chunk); + if (scr_exc_pending()) scr_exc_clear(); + } + t->reading = false; + if (t->read_again) sf_tee_pull(t); + scr_dyn_release(chunk); + sf_tee_release(t); +} + +static void sf_tee_sink_chunk(void *ctx, ScrDyn *value) { + SfTee *t = ctx; + ScrClosure *cb = scr_closure_new((void *)&sf_tee_chunk_task, 2); + cb->caps[0] = sf_tee_box(t); + cb->caps[1] = scr_box_new_obj(&scr_dyn_retain_v, &scr_dyn_release_v, NULL); + scr_box_set_ref(cb->caps[1], scr_dyn_retain(value)); + scr_queue_microtask(cb); + sf_tee_release(t); /* the read request's reference */ +} + +static void sf_tee_sink_close(void *ctx) { + SfTee *t = ctx; + t->reading = false; + for (int i = 0; i < 2; i++) { + if (!t->canceled[i]) sf_stream_close(t->branch[i]); + } + if (!t->canceled[0] || !t->canceled[1]) { + scr_promise_fulfill_void(t->cancel_promise); + } + sf_tee_release(t); +} + +/* The source reader's closed promise rejected (spec: upon its rejection + * with r, error both branches, and resolve cancelPromise unless both + * branches were canceled), as a promise reaction: in a microtask. */ +static void sf_tee_source_errored_task(ScrClosure *cb) { + SfTee *t = (SfTee *)scr_box_get_ref(cb->caps[0]); + ScrDyn *reason = (ScrDyn *)scr_box_get_ref(cb->caps[1]); + sf_stream_error(t->branch[0], reason); + sf_stream_error(t->branch[1], reason); + if (!t->canceled[0] || !t->canceled[1]) scr_promise_fulfill_void(t->cancel_promise); + scr_dyn_release(reason); + sf_tee_release(t); +} + +/* The source errored, with or without a read of it pending. */ +static void sf_tee_source_errored(void *ctx, ScrDyn *reason) { + SfTee *t = ctx; + t->source->error_watch = NULL; + t->source->error_watch_ctx = NULL; + ScrClosure *cb = scr_closure_new((void *)&sf_tee_source_errored_task, 2); + cb->caps[0] = sf_tee_box(t); + cb->caps[1] = scr_box_new_obj(&scr_dyn_retain_v, &scr_dyn_release_v, NULL); + scr_box_set_ref(cb->caps[1], scr_dyn_retain(reason)); + scr_queue_microtask(cb); +} + +/* A read request's error steps only end the read: the source's error + * reaches the branches through sf_tee_source_errored. */ +static void sf_tee_sink_error(void *ctx, ScrDyn *reason) { + (void)reason; + SfTee *t = ctx; + t->reading = false; + sf_tee_release(t); +} + +static const ScrFetchReadSink sf_tee_sink = { + &sf_tee_sink_chunk, &sf_tee_sink_close, &sf_tee_sink_error}; + +static void sf_tee_pull(SfTee *t) { + if (t->reading) { + t->read_again = true; + return; + } + t->reading = true; + sf_tee_retain(t); /* owned by the read request */ + if (!sf_reader_read_request(t->reader, NULL, &sf_tee_sink, t)) { + scr_exc_clear(); + t->reading = false; + sf_tee_release(t); + } +} + +static ScrDyn *sf_tee_pull_thunk(ScrClosure *cb, ScrDyn *const *args, + size_t argc) { + (void)args; + (void)argc; + SfTee *t = (SfTee *)scr_box_get_ref(cb->caps[0]); + sf_tee_pull(t); + sf_tee_release(t); + return scr_dyn_retain(scr_dyn_undefined()); +} + +static ScrDyn *sf_tee_cancel(SfTee *t, int i, ScrDyn *reason) { + t->canceled[i] = true; + scr_dyn_release(t->reason[i]); + t->reason[i] = scr_dyn_retain(reason); + if (t->canceled[0] && t->canceled[1]) { + ScrDyn *composite = scr_dyn_new_arr(); + scr_dyn_arr_push(composite, scr_dyn_retain(t->reason[0])); + scr_dyn_arr_push(composite, scr_dyn_retain(t->reason[1])); + ScrPromise *result = sf_stream_cancel(t->source, composite, true); + scr_dyn_release(composite); + ScrDyn *boxed = scr_dyn_new_promise(result); + scr_promise_release(result); + sf_then_adopt(t->cancel_promise, boxed); + scr_dyn_release(boxed); + } + return scr_dyn_new_promise(t->cancel_promise); +} + +static ScrDyn *sf_tee_cancel_thunk_0(ScrClosure *cb, ScrDyn *const *args, + size_t argc) { + SfTee *t = (SfTee *)scr_box_get_ref(cb->caps[0]); + ScrDyn *out = sf_tee_cancel(t, 0, argc ? args[0] : scr_dyn_undefined()); + sf_tee_release(t); + return out; +} + +static ScrDyn *sf_tee_cancel_thunk_1(ScrClosure *cb, ScrDyn *const *args, + size_t argc) { + SfTee *t = (SfTee *)scr_box_get_ref(cb->caps[0]); + ScrDyn *out = sf_tee_cancel(t, 1, argc ? args[0] : scr_dyn_undefined()); + sf_tee_release(t); + return out; +} + +static ScrDyn *sf_tee_func(SfTee *t, ScrDynThunk thunk, const char *name) { + ScrClosure *cb = scr_closure_new((void *)thunk, 1); + cb->caps[0] = sf_tee_box(t); + return scr_dyn_new_func(cb, thunk, 1, "(controller)", name); +} + +/* Tees an unlocked, undisturbed stream: +1 branches in out[0] and out[1]. + * The source stays locked by the tee. */ +static void sf_stream_tee(SfStream *s, SfStream *out[2]) { + SfTee *t = calloc(1, sizeof *t); + if (!t) sf_oom(); + t->rc = 1; + t->source = sf_stream_retain(s); + t->reader = sf_stream_get_reader(s); + s->error_watch = &sf_tee_source_errored; + s->error_watch_ctx = t; + t->cancel_promise = scr_promise_new(); + scr_promise_mark_handled(t->cancel_promise); + for (int i = 0; i < 2; i++) { + SfStream *b = sf_stream_new_native(); + b->pull_cb = sf_tee_func(t, &sf_tee_pull_thunk, "pull"); + b->cancel_cb = sf_tee_func( + t, i == 0 ? &sf_tee_cancel_thunk_0 : &sf_tee_cancel_thunk_1, "cancel"); + b->source_this = scr_dyn_retain(scr_dyn_undefined()); + sf_stream_track_callbacks(b); + t->branch[i] = sf_stream_retain(b); + out[i] = b; + } + /* An errored source's closed promise has already rejected. */ + if (s->error) sf_tee_source_errored(t, s->error); + /* The branches start at once; a branch with room then pulls. */ + sf_stream_schedule_initial_pull(out[0]); + sf_stream_schedule_initial_pull(out[1]); + sf_tee_release(t); +} + ScrDyn *scr_fetch_stream_new(ScrDyn *source) { ScrDyn *source_view = source; ScrDyn *materialized = NULL; @@ -2164,6 +2424,24 @@ static ScrDyn *sf_controller_get(void *ptr, const char *key, size_t len) { static bool sf_header_name_ok(const char *s, size_t len); +/* A borrowed element of a runtime-built header pair array. These arrays are + * only ever appended to or rewritten whole, so they are dense. */ +static inline ScrStr *sf_pair_at(ScrArr *pairs, size_t i) { + if (i < pairs->filled) return (ScrStr *)(uintptr_t)scr_arr_dense_get(pairs, i); + return (ScrStr *)scr_arr_peek_ref(pairs, (double)i); +} + +static ScrStr *sf_header_name_lower(const char *data, size_t len); + +/* `name` in lowercase, +1: the same string when it has no uppercase byte. */ +static ScrStr *sf_header_name_lower_str(ScrStr *name) { + for (size_t i = 0; i < name->len; i++) { + char c = name->data[i]; + if (c >= 'A' && c <= 'Z') return sf_header_name_lower(name->data, name->len); + } + return scr_str_retain(name); +} + static ScrStr *sf_header_name_lower(const char *data, size_t len) { char *lower = malloc(len ? len : 1); if (!lower) sf_oom(); @@ -2232,13 +2510,8 @@ static SfHeaders *sf_headers_new_response(ScrArr *raw_pairs) { size_t n = (size_t)scr_arr_len(raw_pairs); ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, n); for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *raw_name = scr_arr_get_ref(raw_pairs, (double)i); - scr_arr_push_ref( - pairs, sf_header_name_lower(raw_name->data, raw_name->len)); - scr_str_release(raw_name); - ScrStr *raw = scr_arr_get_ref(raw_pairs, (double)(i + 1)); - scr_arr_push_ref(pairs, sf_latin1_to_utf8(raw)); - scr_str_release(raw); + scr_arr_push_ref(pairs, sf_header_name_lower_str(sf_pair_at(raw_pairs, i))); + scr_arr_push_ref(pairs, sf_latin1_to_utf8(sf_pair_at(raw_pairs, i + 1))); } scr_arr_release(raw_pairs); return sf_headers_new(pairs, true); @@ -2252,9 +2525,17 @@ static SfHeaders *sf_headers_retain(SfHeaders *h) { static void sf_headers_release(SfHeaders *h) { if (!h || --h->rc > 0) return; scr_arr_release(h->pairs); + if (h->sorted) scr_arr_release(h->sorted); free(h); } +/* Every change to h->pairs goes through here first. */ +static void sf_headers_changed(SfHeaders *h) { + if (!h->sorted) return; + scr_arr_release(h->sorted); + h->sorted = NULL; +} + static void *sf_headers_retain_v(void *p) { return sf_headers_retain((SfHeaders *)p); } @@ -2294,7 +2575,7 @@ static ScrStr *sf_headers_name(const ScrDyn *value, const char *method) { scr_str_release(raw); return NULL; } - ScrStr *out = sf_header_name_lower(raw->data, raw->len); + ScrStr *out = sf_header_name_lower_str(raw); scr_str_release(raw); return out; } @@ -2305,40 +2586,36 @@ static bool sf_headers_key_eq(const ScrStr *left, const ScrStr *right) { } static ScrStr *sf_headers_get_value(SfHeaders *h, const ScrStr *name) { - size_t n = (size_t)scr_arr_len(h->pairs); + ScrArr *pairs = h->pairs; + size_t n = (size_t)scr_arr_len(pairs); size_t count = 0; size_t total = 0; + ScrStr *first = NULL; for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = scr_arr_get_ref(h->pairs, (double)i); - ScrStr *value = scr_arr_get_ref(h->pairs, (double)(i + 1)); - if (sf_headers_key_eq(key, name)) { - if (count > 0) total += 2; - total += value->len; - count++; - } - scr_str_release(key); - scr_str_release(value); + if (!sf_headers_key_eq(sf_pair_at(pairs, i), name)) continue; + ScrStr *value = sf_pair_at(pairs, i + 1); + if (count > 0) total += 2; + else first = value; + total += value->len; + count++; } if (count == 0) return NULL; + if (count == 1) return scr_str_retain(first); char *joined = malloc(total > 0 ? total : 1); if (!joined) sf_oom(); size_t at = 0; size_t emitted = 0; for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = scr_arr_get_ref(h->pairs, (double)i); - ScrStr *value = scr_arr_get_ref(h->pairs, (double)(i + 1)); - if (sf_headers_key_eq(key, name)) { - if (emitted > 0) { - joined[at++] = ','; - joined[at++] = ' '; - } - memcpy(joined + at, value->data, value->len); - at += value->len; - emitted++; + if (!sf_headers_key_eq(sf_pair_at(pairs, i), name)) continue; + ScrStr *value = sf_pair_at(pairs, i + 1); + if (emitted > 0) { + joined[at++] = ','; + joined[at++] = ' '; } - scr_str_release(key); - scr_str_release(value); + memcpy(joined + at, value->data, value->len); + at += value->len; + emitted++; } ScrStr *out = scr_str_new(joined, total); free(joined); @@ -2348,17 +2625,17 @@ static ScrStr *sf_headers_get_value(SfHeaders *h, const ScrStr *name) { static void sf_headers_delete_name(SfHeaders *h, const ScrStr *name) { ScrArr *old = h->pairs; size_t n = (size_t)scr_arr_len(old); + bool present = false; + for (size_t i = 0; i + 1 < n && !present; i += 2) + present = sf_headers_key_eq(sf_pair_at(old, i), name); + if (!present) return; + sf_headers_changed(h); ScrArr *next = scr_arr_new(SCR_ELEM_STR, n); for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = scr_arr_get_ref(old, (double)i); - ScrStr *value = scr_arr_get_ref(old, (double)(i + 1)); - if (sf_headers_key_eq(key, name)) { - scr_str_release(key); - scr_str_release(value); - continue; - } - scr_arr_push_ref(next, key); - scr_arr_push_ref(next, value); + ScrStr *key = sf_pair_at(old, i); + if (sf_headers_key_eq(key, name)) continue; + scr_arr_push_ref(next, scr_str_retain(key)); + scr_arr_push_ref(next, scr_str_retain(sf_pair_at(old, i + 1))); } h->pairs = next; scr_arr_release(old); @@ -2399,15 +2676,13 @@ static bool sf_headers_convert_pair( * Fetch-specific semicolon separator for Cookie. */ static void sf_headers_append_pair( SfHeaders *h, ScrStr *name, ScrStr *value) { + sf_headers_changed(h); if (!(name->len == 10 && memcmp(name->data, "set-cookie", 10) == 0)) { size_t n = (size_t)scr_arr_len(h->pairs); for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = scr_arr_get_ref(h->pairs, (double)i); - bool match = sf_headers_key_eq(key, name); - scr_str_release(key); - if (!match) continue; + if (!sf_headers_key_eq(sf_pair_at(h->pairs, i), name)) continue; - ScrStr *previous = scr_arr_get_ref(h->pairs, (double)(i + 1)); + ScrStr *previous = sf_pair_at(h->pairs, i + 1); const char *separator = name->len == 6 && memcmp(name->data, "cookie", 6) == 0 ? "; " @@ -2420,7 +2695,10 @@ static void sf_headers_append_pair( memcpy(joined_data + previous->len + 2, value->data, value->len); ScrStr *joined = scr_str_new(joined_data, joined_len); free(joined_data); - scr_str_release(previous); + /* Appending an empty value leaves the separator's space at the end of + * the combined value, which a copy trims again: the pairs are no + * longer in their canonical, trimmed form. */ + if (value->len == 0) h->canonical = false; scr_str_release(name); scr_str_release(value); scr_arr_set_ref(h->pairs, (double)(i + 1), joined); @@ -2447,7 +2725,7 @@ static ScrStr **sf_headers_sorted_names(SfHeaders *h, size_t *count_out) { if (capacity && !names) sf_oom(); size_t count = 0; for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = scr_arr_get_ref(h->pairs, (double)i); + ScrStr *key = sf_pair_at(h->pairs, i); bool seen = false; for (size_t j = 0; j < count; j++) { if (sf_headers_key_eq(names[j], key)) { @@ -2455,8 +2733,7 @@ static ScrStr **sf_headers_sorted_names(SfHeaders *h, size_t *count_out) { break; } } - if (seen) scr_str_release(key); - else names[count++] = key; + if (!seen) names[count++] = key; } if (count > 1) { qsort(names, count, sizeof *names, &sf_headers_name_cmp); @@ -2469,44 +2746,44 @@ static bool sf_headers_is_set_cookie(const ScrStr *name) { return name->len == 10 && memcmp(name->data, "set-cookie", 10) == 0; } -/* The `index`th entry of Fetch's "sort and combine" list: names in - * byte order, each with its combined value, except that every set-cookie - * value is its own entry (in insertion order), as Node's Headers - * iterator yields them. False past the end. Outputs are +1. */ +/* Fetch's "sort and combine" list as flat name/value pairs: names in byte + * order, each with its combined value, except that every set-cookie value + * is its own entry (in insertion order), as Node's Headers iterator yields + * them. Cached until the next mutation; borrowed. */ +static ScrArr *sf_headers_sorted(SfHeaders *h) { + if (h->sorted) return h->sorted; + size_t count = 0; + ScrStr **names = sf_headers_sorted_names(h, &count); /* borrowed names */ + size_t n = (size_t)scr_arr_len(h->pairs); + ScrArr *sorted = scr_arr_new(SCR_ELEM_STR, n); + for (size_t i = 0; i < count; i++) { + if (!sf_headers_is_set_cookie(names[i])) { + scr_arr_push_ref(sorted, scr_str_retain(names[i])); + scr_arr_push_ref(sorted, sf_headers_get_value(h, names[i])); + continue; + } + for (size_t j = 0; j + 1 < n; j += 2) { + if (!sf_headers_is_set_cookie(sf_pair_at(h->pairs, j))) continue; + scr_arr_push_ref(sorted, scr_str_retain(names[i])); + scr_arr_push_ref(sorted, scr_str_retain(sf_pair_at(h->pairs, j + 1))); + } + } + free(names); + h->sorted = sorted; + return sorted; +} + +/* The `index`th entry of the sort-and-combine list (sf_headers_sorted). + * False past the end. Outputs are +1. */ static bool sf_headers_sorted_entry(SfHeaders *h, size_t index, ScrStr **name_out, ScrStr **value_out) { - size_t count = 0; - ScrStr **names = sf_headers_sorted_names(h, &count); - bool found = false; - size_t at = 0; - for (size_t i = 0; i < count && !found; i++) { - if (!sf_headers_is_set_cookie(names[i])) { - if (at == index) { - *name_out = scr_str_retain(names[i]); - *value_out = sf_headers_get_value(h, names[i]); - found = true; - } - at++; - continue; - } - size_t n = (size_t)scr_arr_len(h->pairs); - for (size_t j = 0; j + 1 < n && !found; j += 2) { - ScrStr *key = scr_arr_get_ref(h->pairs, (double)j); - bool match = sf_headers_is_set_cookie(key); - scr_str_release(key); - if (!match) continue; - if (at == index) { - *name_out = scr_str_retain(names[i]); - *value_out = scr_arr_get_ref(h->pairs, (double)(j + 1)); - found = true; - } - at++; - } - } - for (size_t j = 0; j < count; j++) scr_str_release(names[j]); - free(names); - return found; + ScrArr *sorted = sf_headers_sorted(h); + size_t entries = (size_t)scr_arr_len(sorted) / 2; + if (index >= entries) return false; + *name_out = scr_str_retain(sf_pair_at(sorted, 2 * index)); + *value_out = scr_str_retain(sf_pair_at(sorted, 2 * index + 1)); + return true; } /* One step of a live Headers iterator (entries, keys or values): the @@ -2570,14 +2847,8 @@ static ScrDyn *sf_headers_invoke(void *ptr, ScrDyn *self, ScrDyn *out = scr_dyn_new_arr(); size_t n = (size_t)scr_arr_len(h->pairs); for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = scr_arr_get_ref(h->pairs, (double)i); - ScrStr *value = scr_arr_get_ref(h->pairs, (double)(i + 1)); - if (key->len == 10 && - memcmp(key->data, "set-cookie", 10) == 0) { - scr_dyn_arr_push(out, scr_dyn_new_str(value)); - } - scr_str_release(key); - scr_str_release(value); + if (sf_headers_is_set_cookie(sf_pair_at(h->pairs, i))) + scr_dyn_arr_push(out, scr_dyn_new_str(sf_pair_at(h->pairs, i + 1))); } return out; } @@ -2664,6 +2935,940 @@ static ScrDyn *sf_headers_get(void *ptr, const char *key, size_t len) { return NULL; } +/* ── TransformStream / WritableStream ────────────────────────────── */ + +/* + * The identity TransformStream (`new TransformStream()`): a native + * readable side (an SfStream with a zero high-water mark, fed by the + * transform) and a writable side implemented here with the streams spec's + * WritableStream state machine (default high-water mark 1, chunk size 1). + * The writable side has no other sink, so the TransformStream object, its + * WritableStream and the sink share one SfTransform; the handles differ + * only by tag. Promise reactions the spec chains on already-settled + * promises run after the same number of microtask turns as in Node's + * implementation (lib/internal/webstreams), so interleavings with user + * code match. + * + * Ownership: the transform owns its readable side and its writer; the + * readable side's pull/cancel callbacks own the transform until that side + * closes or errors (fetch teardown drops the callbacks of transforms + * nobody finished). A writer handle retains the transform while bound, + * like a reader handle retains its stream. + */ +enum { SFW_WRITABLE, SFW_ERRORING, SFW_ERRORED, SFW_CLOSED }; + +typedef struct SfTransform SfTransform; + +typedef struct SfWriteReq { + ScrDyn *chunk; + ScrPromise *promise; + struct SfWriteReq *next; +} SfWriteReq; + +typedef struct SfWriter { + size_t rc; + size_t handle_refs; + SfTransform *stream; /* weak; handle refs retain it while bound */ + ScrPromise *ready; + ScrPromise *closed; +} SfWriter; + +struct SfTransform { + size_t rc; + SfStream *readable; + /* WritableStream */ + bool started; /* the start algorithm's promise settled (one turn) */ + int state; + ScrDyn *stored_error; + SfWriter *writer; /* owned while locked */ + SfWriteReq *head; /* queued writes (chunk + request promise) */ + SfWriteReq *tail; + SfWriteReq *in_flight; + size_t queue_size; /* queueTotalSize: queued chunks + the in-flight one */ + bool close_queued; /* the close sentinel is in the queue */ + ScrPromise *close_request; + ScrPromise *in_flight_close; + bool has_pending_abort; + ScrPromise *pending_abort; + /* The sink abort found a finish promise from a cancel or close, which + * settles the abort instead (Node returns that promise). */ + bool abort_joins_finish; + /* The abort request's outcome between SFT_ABORT_DONE and + * SFT_ABORT_SETTLE: a rejection reason (+1), or NULL to fulfill. */ + ScrDyn *abort_settle_error; + ScrDyn *pending_abort_reason; + bool pending_abort_was_erroring; + bool backpressure; + /* TransformStream */ + bool t_backpressure; + ScrPromise *bp_change; + bool write_waiting; /* the in-flight write waits for bp_change */ + ScrDyn *write_error; /* the in-flight write's failure, if any */ + ScrDyn *cancel_reason; /* the readable side's cancel reason, until it reacts */ + ScrPromise *finish; /* finishPromise of close/abort/cancel */ +}; + +static SfTransform *sf_transform_retain(SfTransform *t) { + t->rc++; + return t; +} + +static void sf_write_req_free(SfWriteReq *w) { + if (!w) return; + scr_dyn_release(w->chunk); + scr_promise_release(w->promise); + free(w); +} + +static void sf_writer_release(SfWriter *w) { + if (!w || --w->rc > 0) return; + scr_promise_release(w->ready); + scr_promise_release(w->closed); + free(w); +} + +static void sf_transform_release(SfTransform *t) { + if (!t || --t->rc > 0) return; + sf_stream_release(t->readable); + scr_dyn_release(t->stored_error); + if (t->writer) { + t->writer->stream = NULL; + sf_writer_release(t->writer); + } + while (t->head) { + SfWriteReq *w = t->head; + t->head = w->next; + sf_write_req_free(w); + } + sf_write_req_free(t->in_flight); + scr_promise_release(t->close_request); + scr_promise_release(t->in_flight_close); + scr_promise_release(t->pending_abort); + scr_dyn_release(t->pending_abort_reason); + scr_dyn_release(t->abort_settle_error); + scr_promise_release(t->bp_change); + scr_promise_release(t->finish); + scr_dyn_release(t->write_error); + scr_dyn_release(t->cancel_reason); + free(t); +} + +static void *sf_transform_retain_v(void *p) { + return sf_transform_retain((SfTransform *)p); +} +static void sf_transform_release_v(void *p) { + sf_transform_release((SfTransform *)p); +} + +static void *sf_writer_handle_retain_v(void *p) { + SfWriter *w = p; + w->rc++; + w->handle_refs++; + if (w->stream) sf_transform_retain(w->stream); + return w; +} + +static void sf_writer_handle_release_v(void *p) { + SfWriter *w = p; + SfTransform *t = w->stream; + if (w->handle_refs > 0) w->handle_refs--; + sf_writer_release(w); + sf_transform_release(t); +} + +static ScrBox *sf_transform_box(SfTransform *t) { + ScrBox *box = scr_box_new_obj(&sf_transform_retain_v, + &sf_transform_release_v, NULL); + scr_box_set_ref(box, sf_transform_retain(t)); + return box; +} + +static void sf_invalid_state(const char *message) { + char buf[96]; + int n = snprintf(buf, sizeof buf, "Invalid state: %s", message); + scr_throw_error_msg_code(SCR_ERR_TYPE, buf, (size_t)n, "ERR_INVALID_STATE"); +} + +static ScrPromise *sf_promise_invalid_state(const char *message) { + ScrPromise *p = scr_promise_new(); + sf_invalid_state(message); + scr_promise_reject_pending(p); + return p; +} + +static ScrPromise *sf_promise_rejected_with(ScrDyn *reason) { + ScrPromise *p = scr_promise_new(); + sf_reject_promise_reason(p, reason); + return p; +} + +static ScrPromise *sf_promise_resolved(void) { + ScrPromise *p = scr_promise_new(); + scr_promise_fulfill_void(p); + return p; +} + +/* Deferred steps: the reaction `op` after `ticks` microtask turns. */ +enum { + SFT_WRITE_DONE, + SFT_WRITE_CONTINUE, + SFT_CLOSE_DONE, + SFT_ABORT_DONE, + SFT_START, + SFT_CANCEL_DONE, + SFT_ABORT_SETTLE, +}; + +static void sf_transform_step(SfTransform *t, int op); + +typedef struct SfTransformTask { + size_t rc; + SfTransform *t; + int op; + int ticks; +} SfTransformTask; + +static void *sf_transform_task_retain_v(void *p) { + ((SfTransformTask *)p)->rc++; + return p; +} + +static void sf_transform_task_release_v(void *p) { + SfTransformTask *task = p; + if (--task->rc > 0) return; + sf_transform_release(task->t); + free(task); +} + +static void sf_transform_tick_task(ScrClosure *cb) { + SfTransformTask *task = (SfTransformTask *)scr_box_get_ref(cb->caps[0]); + if (--task->ticks > 0) { + ScrClosure *next = scr_closure_new((void *)&sf_transform_tick_task, 1); + next->caps[0] = scr_box_new_obj(&sf_transform_task_retain_v, + &sf_transform_task_release_v, NULL); + scr_box_set_ref(next->caps[0], sf_transform_task_retain_v(task)); + scr_queue_microtask(next); + } else { + sf_transform_step(task->t, task->op); + } + sf_transform_task_release_v(task); +} + +static void sf_transform_defer(SfTransform *t, int op, int ticks) { + SfTransformTask *task = calloc(1, sizeof *task); + if (!task) sf_oom(); + task->rc = 1; + task->t = sf_transform_retain(t); + task->op = op; + task->ticks = ticks; + ScrClosure *cb = scr_closure_new((void *)&sf_transform_tick_task, 1); + cb->caps[0] = scr_box_new_obj(&sf_transform_task_retain_v, + &sf_transform_task_release_v, NULL); + scr_box_set_ref(cb->caps[0], task); /* the box takes the reference */ + scr_queue_microtask(cb); +} + +/* ── WritableStream abstract operations ── */ + +static bool sfw_close_queued_or_in_flight(const SfTransform *t) { + return t->close_request || t->in_flight_close; +} + +static bool sfw_has_operation_in_flight(const SfTransform *t) { + return t->in_flight || t->in_flight_close; +} + +static void sfw_writer_ensure_ready_rejected(SfWriter *w, ScrDyn *reason) { + if (!scr_promise_is_pending(w->ready)) { + scr_promise_release(w->ready); + w->ready = scr_promise_new(); + } + sf_reject_promise_reason(w->ready, reason); + scr_promise_mark_handled(w->ready); +} + +static void sfw_writer_ensure_closed_rejected(SfWriter *w, ScrDyn *reason) { + if (!scr_promise_is_pending(w->closed)) { + scr_promise_release(w->closed); + w->closed = scr_promise_new(); + } + sf_reject_promise_reason(w->closed, reason); + scr_promise_mark_handled(w->closed); +} + +static void sfw_update_backpressure(SfTransform *t, bool backpressure) { + SfWriter *w = t->writer; + if (w && backpressure != t->backpressure) { + if (backpressure) { + scr_promise_release(w->ready); + w->ready = scr_promise_new(); + scr_promise_mark_handled(w->ready); + } else { + scr_promise_fulfill_void(w->ready); + } + } + t->backpressure = backpressure; +} + +static void sfw_reject_close_and_closed(SfTransform *t) { + if (t->close_request) { + sf_reject_promise_reason(t->close_request, t->stored_error); + scr_promise_release(t->close_request); + t->close_request = NULL; + } + if (t->writer) { + sf_reject_promise_reason(t->writer->closed, t->stored_error); + scr_promise_mark_handled(t->writer->closed); + } +} + +static void sft_abort_algorithm(SfTransform *t, ScrDyn *reason); + +static void sfw_finish_erroring(SfTransform *t) { + t->state = SFW_ERRORED; + /* The controller's queue resets; every queued write rejects. */ + while (t->head) { + SfWriteReq *w = t->head; + t->head = w->next; + sf_reject_promise_reason(w->promise, t->stored_error); + sf_write_req_free(w); + } + t->tail = NULL; + t->queue_size = 0; + t->close_queued = false; + if (!t->has_pending_abort) { + sfw_reject_close_and_closed(t); + return; + } + if (t->pending_abort_was_erroring) { + t->has_pending_abort = false; + sf_reject_promise_reason(t->pending_abort, t->stored_error); + scr_promise_release(t->pending_abort); + t->pending_abort = NULL; + scr_dyn_release(t->pending_abort_reason); + t->pending_abort_reason = NULL; + sfw_reject_close_and_closed(t); + return; + } + /* The sink's abort runs; its promise settles the abort request. */ + sft_abort_algorithm(t, t->pending_abort_reason); +} + +static void sfw_start_erroring(SfTransform *t, ScrDyn *reason) { + t->state = SFW_ERRORING; + scr_dyn_release(t->stored_error); + t->stored_error = scr_dyn_retain(reason); + if (t->writer) sfw_writer_ensure_ready_rejected(t->writer, reason); + if (!sfw_has_operation_in_flight(t) && t->started) sfw_finish_erroring(t); +} + +static void sfw_deal_with_rejection(SfTransform *t, ScrDyn *error) { + if (t->state == SFW_WRITABLE) { + sfw_start_erroring(t, error); + return; + } + sfw_finish_erroring(t); +} + +static void sfw_error_if_needed(SfTransform *t, ScrDyn *error) { + if (t->state == SFW_WRITABLE) sfw_start_erroring(t, error); +} + +static void sft_set_backpressure(SfTransform *t, bool backpressure); +static void sft_sink_write(SfTransform *t); +static void sft_sink_close(SfTransform *t); + +static void sfw_advance_queue(SfTransform *t) { + if (!t->started || t->in_flight || t->in_flight_close) return; + if (t->state == SFW_ERRORING) { + sfw_finish_erroring(t); + return; + } + if (t->head) { + SfWriteReq *w = t->head; + t->head = w->next; + if (!t->head) t->tail = NULL; + w->next = NULL; + t->in_flight = w; + sft_sink_write(t); + return; + } + if (t->close_queued) { + t->close_queued = false; + t->in_flight_close = t->close_request; + t->close_request = NULL; + sft_sink_close(t); + } +} + +static void sfw_finish_write(SfTransform *t, ScrDyn *error) { + SfWriteReq *w = t->in_flight; + t->in_flight = NULL; + if (error) { + sf_reject_promise_reason(w->promise, error); + sf_write_req_free(w); + sfw_deal_with_rejection(t, error); + return; + } + scr_promise_fulfill_void(w->promise); + sf_write_req_free(w); + if (t->queue_size) t->queue_size--; + if (!sfw_close_queued_or_in_flight(t) && t->state == SFW_WRITABLE) { + sfw_update_backpressure(t, t->queue_size >= 1); + } + sfw_advance_queue(t); +} + +static void sfw_finish_close(SfTransform *t, ScrDyn *error) { + ScrPromise *p = t->in_flight_close; + t->in_flight_close = NULL; + if (error) { + sf_reject_promise_reason(p, error); + scr_promise_release(p); + if (t->has_pending_abort) { + t->has_pending_abort = false; + sf_reject_promise_reason(t->pending_abort, error); + scr_promise_release(t->pending_abort); + t->pending_abort = NULL; + scr_dyn_release(t->pending_abort_reason); + t->pending_abort_reason = NULL; + } + sfw_deal_with_rejection(t, error); + return; + } + scr_promise_fulfill_void(p); + scr_promise_release(p); + if (t->state == SFW_ERRORING) { + scr_dyn_release(t->stored_error); + t->stored_error = NULL; + if (t->has_pending_abort) { + t->has_pending_abort = false; + scr_promise_fulfill_void(t->pending_abort); + scr_promise_release(t->pending_abort); + t->pending_abort = NULL; + scr_dyn_release(t->pending_abort_reason); + t->pending_abort_reason = NULL; + } + } + t->state = SFW_CLOSED; + if (t->writer) scr_promise_fulfill_void(t->writer->closed); +} + +/* WritableStreamAbort: +1 promise. */ +static ScrPromise *sfw_abort(SfTransform *t, ScrDyn *reason) { + if (t->state == SFW_CLOSED || t->state == SFW_ERRORED) { + return sf_promise_resolved(); + } + if (t->has_pending_abort) return scr_promise_retain(t->pending_abort); + bool was_erroring = t->state == SFW_ERRORING; + ScrPromise *p = scr_promise_new(); + t->has_pending_abort = true; + t->pending_abort = scr_promise_retain(p); + t->pending_abort_reason = + scr_dyn_retain(was_erroring ? scr_dyn_undefined() : reason); + t->pending_abort_was_erroring = was_erroring; + if (!was_erroring) sfw_start_erroring(t, reason); + return p; +} + +/* WritableStreamClose: +1 promise. */ +static ScrPromise *sfw_close(SfTransform *t) { + if (t->state == SFW_CLOSED || t->state == SFW_ERRORED) { + char message[64]; + snprintf(message, sizeof message, "WritableStream is %s", + t->state == SFW_CLOSED ? "closed" : "errored"); + return sf_promise_invalid_state(message); + } + ScrPromise *p = scr_promise_new(); + t->close_request = scr_promise_retain(p); + if (t->writer && t->backpressure && t->state == SFW_WRITABLE) { + scr_promise_fulfill_void(t->writer->ready); + } + t->close_queued = true; + sfw_advance_queue(t); + return p; +} + +/* WritableStreamDefaultWriterWrite: +1 promise. */ +static ScrPromise *sfw_writer_write(SfWriter *w, ScrDyn *chunk) { + SfTransform *t = w->stream; + if (!t) return sf_promise_invalid_state("Writer is not bound to a WritableStream"); + if (t->state == SFW_ERRORED || t->state == SFW_ERRORING) { + return sf_promise_rejected_with(t->stored_error); + } + if (sfw_close_queued_or_in_flight(t) || t->state == SFW_CLOSED) { + return sf_promise_invalid_state("WritableStream is closed"); + } + SfWriteReq *req = calloc(1, sizeof *req); + if (!req) sf_oom(); + req->chunk = scr_dyn_retain(chunk); + req->promise = scr_promise_new(); + ScrPromise *p = scr_promise_retain(req->promise); + if (t->tail) t->tail->next = req; + else t->head = req; + t->tail = req; + t->queue_size++; + sfw_update_backpressure(t, t->queue_size >= 1); + sfw_advance_queue(t); + return p; +} + +static SfWriter *sfw_get_writer(SfTransform *t) { + if (t->writer) { + sf_invalid_state("WritableStream is locked"); + return NULL; + } + SfWriter *w = calloc(1, sizeof *w); + if (!w) sf_oom(); + w->rc = 1; + w->stream = t; + w->ready = scr_promise_new(); + w->closed = scr_promise_new(); + scr_promise_mark_handled(w->ready); + scr_promise_mark_handled(w->closed); + switch (t->state) { + case SFW_WRITABLE: + if (sfw_close_queued_or_in_flight(t) || !t->backpressure) { + scr_promise_fulfill_void(w->ready); + } + break; + case SFW_ERRORING: + sf_reject_promise_reason(w->ready, t->stored_error); + break; + case SFW_CLOSED: + scr_promise_fulfill_void(w->ready); + scr_promise_fulfill_void(w->closed); + break; + default: + sf_reject_promise_reason(w->ready, t->stored_error); + sf_reject_promise_reason(w->closed, t->stored_error); + break; + } + t->writer = w; + w->rc++; /* the stream's ownership */ + return w; +} + +static void sfw_writer_release_lock(SfWriter *w) { + SfTransform *t = w->stream; + if (!t) return; + sf_invalid_state("Writer has been released"); + ScrCaught *caught = scr_exc_take(); + ScrDyn *released = scr_caught_to_dyn(caught); + scr_caught_release(caught); + sfw_writer_ensure_ready_rejected(w, released); + sfw_writer_ensure_closed_rejected(w, released); + scr_dyn_release(released); + size_t handle_refs = w->handle_refs; + t->writer = NULL; + w->stream = NULL; + sf_writer_release(w); /* the stream's ownership */ + while (handle_refs-- > 0) sf_transform_release(t); +} + +/* ── TransformStream algorithms (identity transformer) ── */ + +static void sft_set_backpressure(SfTransform *t, bool backpressure) { + if (t->bp_change) { + scr_promise_fulfill_void(t->bp_change); + scr_promise_release(t->bp_change); + } + t->bp_change = scr_promise_new(); + t->t_backpressure = backpressure; + if (t->write_waiting) { + t->write_waiting = false; + sf_transform_defer(t, SFT_WRITE_CONTINUE, 1); + } +} + +/* After an enqueue: whether a read is still waiting. A fetch upload's read + * was just satisfied by the chunk, and it reads again through the next + * pull, as a reader's next read() does. */ +static bool sft_readable_wants_chunk(const SfStream *r) { + return r->collector || (r->reader && r->reader->pending_head); +} + +/* The readable side is closed or errored: a pull still waiting on + * bp_change can no longer receive a chunk, so complete it. */ +static void sft_finish_pull(SfTransform *t) { + if (t->bp_change && scr_promise_is_pending(t->bp_change)) scr_promise_fulfill_void(t->bp_change); +} + +static bool sft_readable_can_enqueue(const SfStream *r) { + return !r->close_requested && !r->closed && !r->error; +} + +/* TransformStreamErrorWritableAndUnblockWrite */ +static void sft_error_writable(SfTransform *t, ScrDyn *error) { + sfw_error_if_needed(t, error); + if (t->t_backpressure) sft_set_backpressure(t, false); +} + +/* The identity transform's enqueue: NULL on success, else +1 error. */ +static ScrDyn *sft_perform_transform(SfTransform *t, ScrDyn *chunk) { + SfStream *r = t->readable; + if (!sft_readable_can_enqueue(r)) { + sf_invalid_state("Unable to enqueue"); + ScrCaught *caught = scr_exc_take(); + ScrDyn *error = scr_caught_to_dyn(caught); + scr_caught_release(caught); + sft_error_writable(t, error); + return error; + } + sf_stream_enqueue_value(r, chunk); + if (scr_exc_pending()) { + ScrCaught *caught = scr_exc_take(); + ScrDyn *error = scr_caught_to_dyn(caught); + scr_caught_release(caught); + sf_stream_error(r, error); + sft_error_writable(t, error); + return error; + } + bool backpressure = !sft_readable_wants_chunk(r); + if (backpressure != t->t_backpressure) sft_set_backpressure(t, true); + return NULL; +} + +static void sft_sink_write(SfTransform *t) { + if (t->t_backpressure) { + t->write_waiting = true; + return; + } + ScrDyn *error = sft_perform_transform(t, t->in_flight->chunk); + if (error) { + scr_dyn_release(t->write_error); + t->write_error = error; + } + sf_transform_defer(t, SFT_WRITE_DONE, 2); +} + +static void sft_sink_close(SfTransform *t) { + if (!t->finish) { + t->finish = scr_promise_new(); + scr_promise_mark_handled(t->finish); + sf_transform_defer(t, SFT_CLOSE_DONE, 1); + return; + } + /* A cancel or abort already finished the transform. */ + sf_transform_defer(t, SFT_CLOSE_DONE, 1); +} + +static void sft_abort_algorithm(SfTransform *t, ScrDyn *reason) { + (void)reason; + t->abort_joins_finish = t->finish != NULL; + if (!t->finish) { + t->finish = scr_promise_new(); + scr_promise_mark_handled(t->finish); + } + sf_transform_defer(t, SFT_ABORT_DONE, 1); +} + +static void sf_transform_step(SfTransform *t, int op) { + switch (op) { + case SFT_WRITE_CONTINUE: { + if (!t->in_flight) return; + if (t->state == SFW_ERRORING) { + ScrDyn *error = scr_dyn_retain(t->stored_error); + scr_dyn_release(t->write_error); + t->write_error = error; + } else { + ScrDyn *error = sft_perform_transform(t, t->in_flight->chunk); + if (error) { + scr_dyn_release(t->write_error); + t->write_error = error; + } + } + sf_transform_defer(t, SFT_WRITE_DONE, 3); + return; + } + case SFT_WRITE_DONE: { + if (!t->in_flight) return; + ScrDyn *error = t->write_error; + t->write_error = NULL; + sfw_finish_write(t, error); + scr_dyn_release(error); + return; + } + case SFT_CLOSE_DONE: { + SfStream *r = t->readable; + ScrDyn *error = NULL; + if (r->error) { + error = scr_dyn_retain(r->error); + } else { + sf_stream_close(r); + } + sft_finish_pull(t); + if (error) sf_reject_promise_reason(t->finish, error); + else scr_promise_fulfill_void(t->finish); + if (t->in_flight_close) sfw_finish_close(t, error); + scr_dyn_release(error); + return; + } + case SFT_ABORT_DONE: { + SfStream *r = t->readable; + scr_dyn_release(t->abort_settle_error); + t->abort_settle_error = NULL; + if (t->abort_joins_finish && !scr_promise_is_pending(t->finish)) { + /* The cancel or close that created the finish promise settled + * it; the abort request takes its result. */ + if (scr_promise_is_rejected(t->finish)) + t->abort_settle_error = scr_promise_reason_dyn(t->finish); + } else { + ScrDyn *reason = t->pending_abort_reason + ? scr_dyn_retain(t->pending_abort_reason) + : scr_dyn_retain(scr_dyn_undefined()); + bool readable_errored = r->error != NULL; + if (!readable_errored) sf_stream_error(r, reason); + sft_finish_pull(t); + /* The sink abort algorithm's finish promise, which a + * readable.cancel() after the abort answers too: rejected with the + * readable side's stored error when it had already errored, else + * resolved. */ + if (t->finish && scr_promise_is_pending(t->finish)) { + if (readable_errored) sf_reject_promise_reason(t->finish, r->error); + else scr_promise_fulfill_void(t->finish); + } + if (readable_errored) t->abort_settle_error = scr_dyn_retain(r->error); + scr_dyn_release(reason); + } + /* WritableStreamFinishErroring settles the abort request in a + * reaction to the sink abort's promise, two turns after it. */ + sf_transform_defer(t, SFT_ABORT_SETTLE, 2); + return; + } + case SFT_ABORT_SETTLE: { + ScrDyn *error = t->abort_settle_error; + t->abort_settle_error = NULL; + if (t->has_pending_abort) { + t->has_pending_abort = false; + if (error) sf_reject_promise_reason(t->pending_abort, error); + else scr_promise_fulfill_void(t->pending_abort); + scr_promise_release(t->pending_abort); + t->pending_abort = NULL; + scr_dyn_release(t->pending_abort_reason); + t->pending_abort_reason = NULL; + } + sfw_reject_close_and_closed(t); + scr_dyn_release(error); + return; + } + case SFT_CANCEL_DONE: { + ScrDyn *reason = t->cancel_reason; + t->cancel_reason = NULL; + if (t->state == SFW_ERRORED) { + sf_reject_promise_reason(t->finish, t->stored_error); + } else { + sfw_error_if_needed(t, reason ? reason : scr_dyn_undefined()); + if (t->t_backpressure) sft_set_backpressure(t, false); + sft_finish_pull(t); + scr_promise_fulfill_void(t->finish); + } + scr_dyn_release(reason); + return; + } + case SFT_START: { + /* Both sides start one turn after construction, the writable side + * first (Node registers its start reaction first). */ + t->started = true; + sfw_advance_queue(t); + SfStream *r = t->readable; + r->started = true; + if (!r->close_requested && !r->closed && !r->error) { + sf_stream_drain(r); + sf_stream_pull(r); + } + return; + } + } +} + +/* The readable side's pull: release backpressure; the pull completes when + * backpressure changes again. */ +static ScrDyn *sft_pull_thunk(ScrClosure *cb, ScrDyn *const *args, + size_t argc) { + (void)args; + (void)argc; + SfTransform *t = (SfTransform *)scr_box_get_ref(cb->caps[0]); + sft_set_backpressure(t, false); + ScrDyn *out = scr_dyn_new_promise(t->bp_change); + sf_transform_release(t); + return out; +} + +/* The readable side's cancel (TransformStreamDefaultSourceCancelAlgorithm): + * errors the writable side with the reason and unblocks a waiting write. */ +static ScrDyn *sft_cancel_thunk(ScrClosure *cb, ScrDyn *const *args, + size_t argc) { + SfTransform *t = (SfTransform *)scr_box_get_ref(cb->caps[0]); + ScrDyn *reason = argc ? args[0] : scr_dyn_undefined(); + if (!t->finish) { + t->finish = scr_promise_new(); + scr_promise_mark_handled(t->finish); + scr_dyn_release(t->cancel_reason); + t->cancel_reason = scr_dyn_retain(reason); + /* The (identity) cancel algorithm's resolved promise reacts a turn + * later. */ + sf_transform_defer(t, SFT_CANCEL_DONE, 1); + } + ScrDyn *out = scr_dyn_new_promise(t->finish); + sf_transform_release(t); + return out; +} + +static ScrDyn *sft_func(SfTransform *t, ScrDynThunk thunk, const char *name) { + ScrClosure *cb = scr_closure_new((void *)thunk, 1); + cb->caps[0] = sf_transform_box(t); + return scr_dyn_new_func(cb, thunk, 1, "(controller)", name); +} + +ScrDyn *scr_fetch_transform_new(void) { + SfTransform *t = calloc(1, sizeof *t); + if (!t) sf_oom(); + t->rc = 1; + t->state = SFW_WRITABLE; + SfStream *r = sf_stream_new_native(); + r->started = false; + r->hwm_zero = true; + r->pull_cb = sft_func(t, &sft_pull_thunk, "pull"); + r->cancel_cb = sft_func(t, &sft_cancel_thunk, "cancel"); + r->source_this = scr_dyn_retain(scr_dyn_undefined()); + sf_stream_track_callbacks(r); + t->readable = r; + /* The writable side's high-water mark of 1 leaves room: no backpressure. + * The readable side's 0 does: writes wait for a read. */ + t->backpressure = false; + sft_set_backpressure(t, true); + sf_transform_defer(t, SFT_START, 1); + ScrDyn *out = scr_dyn_new_handle(t, SCR_DYNH_WEB_TRANSFORM); + sf_transform_release(t); + return out; +} + +static ScrDyn *sf_transform_get(void *ptr, const char *key, size_t len) { + SfTransform *t = ptr; + if (sf_name(key, len, "readable")) { + return scr_dyn_new_handle(t->readable, SCR_DYNH_WEB_STREAM); + } + if (sf_name(key, len, "writable")) { + return scr_dyn_new_handle(t, SCR_DYNH_WEB_WRITABLE); + } + return NULL; +} + +static ScrDyn *sf_transform_invoke(void *ptr, ScrDyn *self, + const char *method, ScrDyn *const *args, + size_t argc, const char *what) { + (void)ptr; + (void)self; + (void)args; + (void)argc; + (void)method; + sf_not_function(what); + return NULL; +} + +static ScrDyn *sf_promise_dyn(ScrPromise *p) { + ScrDyn *out = scr_dyn_new_promise(p); + scr_promise_release(p); + return out; +} + +static ScrDyn *sf_writable_invoke(void *ptr, ScrDyn *self, + const char *method, ScrDyn *const *args, + size_t argc, const char *what) { + SfTransform *t = ptr; + (void)self; + if (strcmp(method, "getWriter") == 0) { + SfWriter *w = sfw_get_writer(t); + if (!w) return NULL; + ScrDyn *out = scr_dyn_new_handle(w, SCR_DYNH_WEB_WRITER); + sf_writer_release(w); + return out; + } + if (strcmp(method, "abort") == 0) { + if (t->writer) { + return sf_promise_dyn( + sf_promise_invalid_state("WritableStream is locked")); + } + return sf_promise_dyn(sfw_abort(t, argc ? args[0] : scr_dyn_undefined())); + } + if (strcmp(method, "close") == 0) { + if (t->writer) { + return sf_promise_dyn( + sf_promise_invalid_state("WritableStream is locked")); + } + if (sfw_close_queued_or_in_flight(t)) { + return sf_promise_dyn( + sf_promise_invalid_state("WritableStream is closed")); + } + return sf_promise_dyn(sfw_close(t)); + } + sf_not_function(what); + return NULL; +} + +static ScrDyn *sf_writable_get(void *ptr, const char *key, size_t len) { + SfTransform *t = ptr; + if (sf_name(key, len, "locked")) return scr_dyn_new_bool(t->writer != NULL); + return NULL; +} + +static ScrDyn *sf_writer_invoke(void *ptr, ScrDyn *self, const char *method, + ScrDyn *const *args, size_t argc, + const char *what) { + SfWriter *w = ptr; + (void)self; + if (strcmp(method, "write") == 0) { + return sf_promise_dyn( + sfw_writer_write(w, argc ? args[0] : scr_dyn_undefined())); + } + if (strcmp(method, "close") == 0) { + SfTransform *t = w->stream; + if (!t) { + return sf_promise_dyn(sf_promise_invalid_state( + "Writer is not bound to a WritableStream")); + } + if (sfw_close_queued_or_in_flight(t)) { + return sf_promise_dyn( + sf_promise_invalid_state("WritableStream is closed")); + } + return sf_promise_dyn(sfw_close(t)); + } + if (strcmp(method, "abort") == 0) { + SfTransform *t = w->stream; + if (!t) { + return sf_promise_dyn(sf_promise_invalid_state( + "Writer is not bound to a WritableStream")); + } + return sf_promise_dyn(sfw_abort(t, argc ? args[0] : scr_dyn_undefined())); + } + if (strcmp(method, "releaseLock") == 0) { + sfw_writer_release_lock(w); + return scr_dyn_retain(scr_dyn_undefined()); + } + sf_not_function(what); + return NULL; +} + +static ScrDyn *sf_writer_get(void *ptr, const char *key, size_t len) { + SfWriter *w = ptr; + if (sf_name(key, len, "ready")) return scr_dyn_new_promise(w->ready); + if (sf_name(key, len, "closed")) return scr_dyn_new_promise(w->closed); + if (sf_name(key, len, "desiredSize")) { + SfTransform *t = w->stream; + if (!t) { + sf_invalid_state("Writer is not bound to a WritableStream"); + return NULL; + } + if (t->state == SFW_ERRORED || t->state == SFW_ERRORING) { + return scr_dyn_new_null(); + } + if (t->state == SFW_CLOSED) return scr_dyn_new_num(0); + return scr_dyn_new_num(1.0 - (double)t->queue_size); + } + return NULL; +} + /* ── Response ────────────────────────────────────────────────────── */ static SfResponse *sf_response_retain(SfResponse *r) { @@ -2729,12 +3934,54 @@ static ScrPromise *sf_response_collect(SfResponse *r, int mode) { return p; } +/* The body and metadata copy of Response.clone and Request.clone (undici + * cloneResponse/cloneBody): the body tees, so the original and the clone + * each keep one branch, and the clone's headers are a copy with the same + * guard. NULL with a TypeError pending when the body is unusable. */ +static SfResponse *sf_response_clone(SfResponse *r, const char *unusable) { + if (!r->null_body && + (r->body->disturbed || r->body->reader || r->body->internal_lock)) { + sf_type_error(unusable); + return NULL; + } + SfResponse *c = calloc(1, sizeof *c); + if (!c) sf_oom(); + c->rc = 1; + if (r->null_body) { + c->body = sf_stream_new_native(); + sf_stream_close(c->body); + } else { + SfStream *branches[2]; + sf_stream_tee(r->body, branches); + sf_stream_release(r->body); + r->body = branches[0]; + c->body = branches[1]; + } + c->headers = sf_headers_new(scr_arr_slice(r->headers->pairs, 0, INFINITY), + r->headers->immutable); + c->url = scr_str_retain(r->url); + c->status_text = scr_str_retain(r->status_text); + c->status = r->status; + c->redirected = r->redirected; + c->null_body = r->null_body; + c->light_length = r->light_length; + return c; +} + static ScrDyn *sf_response_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc, const char *what) { SfResponse *r = ptr; sf_response_materialize(r); + if (strcmp(method, "clone") == 0) { + SfResponse *c = sf_response_clone( + r, "Response.clone: Body has already been consumed."); + if (!c) return NULL; + ScrDyn *out = scr_dyn_new_handle(c, SCR_DYNH_FETCH_RESPONSE); + sf_response_release(c); + return out; + } int mode = -1; if (strcmp(method, "json") == 0) mode = SF_COLLECT_JSON; else if (strcmp(method, "text") == 0) mode = SF_COLLECT_TEXT; @@ -3323,14 +4570,13 @@ static bool sf_pairs_have(ScrArr *pairs, const char *name) { size_t n = (size_t)scr_arr_len(pairs); size_t want = strlen(name); for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = (ScrStr *)scr_arr_get_ref(pairs, (double)i); + ScrStr *key = sf_pair_at(pairs, i); bool same = key->len == want; for (size_t j = 0; j < want && same; j++) { char c = key->data[j]; if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a'); same = c == name[j]; } - scr_str_release(key); if (same) return true; } return false; @@ -3430,6 +4676,21 @@ static bool sf_request_header_ok( !sf_header_name_ci(name, name_len, "expect"); } +/* Whether every value in `pairs` is already trimmed (sf_trim_header_value + * would keep it): the form a copy can take unchanged (SfHeaders.canonical). + * Combining duplicate names with an empty value leaves the separator's + * space at the end of the combined value, which a copy trims again. */ +static bool sf_header_values_trimmed(ScrArr *pairs) { + size_t n = (size_t)scr_arr_len(pairs); + for (size_t i = 1; i < n; i += 2) { + const ScrStr *value = sf_pair_at(pairs, i); + if (value->len == 0) continue; + char first = value->data[0], last = value->data[value->len - 1]; + if (first == ' ' || first == '\t' || last == ' ' || last == '\t') return false; + } + return true; +} + static ScrStr *sf_trim_header_value(const ScrStr *value) { size_t start = 0; size_t end = value->len; @@ -3441,11 +4702,14 @@ static ScrStr *sf_trim_header_value(const ScrStr *value) { (value->data[end - 1] == ' ' || value->data[end - 1] == '\t')) { end--; } + if (start == 0 && end == value->len) return scr_str_retain((ScrStr *)value); return scr_str_new(value->data + start, end - start); } -static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len, +static bool sf_push_header(ScrArr *pairs, ScrStr *name_str, ScrStr *value, bool request_guard) { + const char *name = name_str->data; + size_t name_len = name_str->len; if (!sf_header_name_ok(name, name_len)) { ScrStr *display = scr_str_new(name, name_len); sf_headers_invalid("append", display, "name"); @@ -3471,7 +4735,7 @@ static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len, if (!sf_header_name_ci(name, name_len, "set-cookie")) { size_t n = (size_t)scr_arr_len(pairs); for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *key = scr_arr_get_ref(pairs, (double)i); + ScrStr *key = sf_pair_at(pairs, i); bool match = key->len == name_len; for (size_t j = 0; j < name_len && match; j++) { unsigned char a = (unsigned char)key->data[j]; @@ -3480,11 +4744,9 @@ static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len, if (b >= 'A' && b <= 'Z') b = (unsigned char)(b - 'A' + 'a'); match = a == b; } - scr_str_release(key); if (!match) continue; - ScrStr *previous = - scr_arr_get_ref(pairs, (double)(i + 1)); + ScrStr *previous = sf_pair_at(pairs, i + 1); const char *separator = sf_header_name_ci(name, name_len, "cookie") ? "; " : ", "; size_t separator_len = 2; @@ -3498,7 +4760,6 @@ static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len, normalized->data, normalized->len); ScrStr *joined = scr_str_new(joined_data, joined_len); free(joined_data); - scr_str_release(previous); scr_str_release(normalized); if (request_guard && !sf_request_header_ok(name, name_len, joined)) { scr_str_release(joined); @@ -3517,8 +4778,8 @@ static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len, scr_arr_push_ref( pairs, request_guard - ? scr_str_new(name, name_len) - : sf_header_name_lower(name, name_len)); + ? scr_str_retain(name_str) + : sf_header_name_lower_str(name_str)); scr_arr_push_ref(pairs, normalized); return true; } @@ -3545,10 +4806,13 @@ static void sf_headers_pair_length_error(size_t len) { * leaves header-name/value syntax validation for initialization. Response * construction converts this snapshot before status/statusText and only * validates it after body extraction, matching Undici's two phases. */ -static ScrArr *sf_headers_convert_init(const ScrDyn *headers) { +/* *canonical_out reports that the result copies a canonical Headers + * object's pairs (SfHeaders.canonical). */ +static ScrArr *sf_headers_convert_init(const ScrDyn *headers, bool *canonical_out) { + *canonical_out = false; if (headers && headers->kind == SCR_DYN_TYPED_REF) { ScrDyn *materialized = scr_dyn_typed_ref_materialize(headers); - ScrArr *converted = sf_headers_convert_init(materialized); + ScrArr *converted = sf_headers_convert_init(materialized, canonical_out); scr_dyn_release(materialized); return converted; } @@ -3639,13 +4903,9 @@ static ScrArr *sf_headers_convert_init(const ScrDyn *headers) { headers->v.handle.tag == SCR_DYNH_FETCH_HEADERS) { SfHeaders *source = (SfHeaders *)headers->v.handle.ptr; size_t n = (size_t)scr_arr_len(source->pairs); - for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *name = scr_arr_get_ref(source->pairs, (double)i); - ScrStr *value = - scr_arr_get_ref(source->pairs, (double)(i + 1)); - scr_arr_push_ref(converted, name); - scr_arr_push_ref(converted, value); - } + for (size_t i = 0; i < n - n % 2; i++) + scr_arr_push_ref(converted, scr_str_retain(sf_pair_at(source->pairs, i))); + *canonical_out = source->canonical; return converted; } /* Functions are WebIDL objects and an ordinary function has no @@ -3660,25 +4920,29 @@ fail: } static bool sf_add_converted_headers( - ScrArr *pairs, ScrArr *converted, bool request_guard) { + ScrArr *pairs, ScrArr *converted, bool request_guard, bool canonical) { size_t n = (size_t)scr_arr_len(converted); + /* Pushing canonical pairs into an empty list one by one through + * sf_push_header would reproduce them exactly. */ + if (canonical && !request_guard && scr_arr_len(pairs) == 0) { + for (size_t i = 0; i < n; i++) + scr_arr_push_ref(pairs, scr_str_retain(sf_pair_at(converted, i))); + return true; + } for (size_t i = 0; i + 1 < n; i += 2) { - ScrStr *name = scr_arr_get_ref(converted, (double)i); - ScrStr *value = scr_arr_get_ref(converted, (double)(i + 1)); - bool ok = sf_push_header( - pairs, name->data, name->len, value, request_guard); - scr_str_release(name); - scr_str_release(value); - if (!ok) return false; + if (!sf_push_header(pairs, sf_pair_at(converted, i), + sf_pair_at(converted, i + 1), request_guard)) + return false; } return true; } static bool sf_add_headers( ScrArr *pairs, const ScrDyn *headers, bool request_guard) { - ScrArr *converted = sf_headers_convert_init(headers); + bool canonical = false; + ScrArr *converted = sf_headers_convert_init(headers, &canonical); if (!converted) return false; - bool ok = sf_add_converted_headers(pairs, converted, request_guard); + bool ok = sf_add_converted_headers(pairs, converted, request_guard, canonical); scr_arr_release(converted); return ok; } @@ -4858,6 +6122,7 @@ static ScrDyn *sf_response_construct(ScrDyn *body, ScrDyn *init, SfJsonSerialize serialize) { ScrArr *header_pairs = NULL; ScrArr *converted_headers = NULL; + bool converted_canonical = false; ScrBytes *body_bytes = NULL; ScrStr *status_text = NULL; SfStream *stream = NULL; @@ -4922,7 +6187,7 @@ static ScrDyn *sf_response_construct(ScrDyn *body, ScrDyn *init, * later response-initialization step, after body extraction. */ ScrDyn *init_headers = sf_response_init_get(init, "headers", 7); if (light_ok) light_form = sf_light_header_form(init_headers, json_body, &light_record); - converted_headers = sf_headers_convert_init(init_headers); + converted_headers = sf_headers_convert_init(init_headers, &converted_canonical); scr_dyn_release(init_headers); if (!converted_headers) goto fail; @@ -5017,7 +6282,7 @@ static ScrDyn *sf_response_construct(ScrDyn *body, ScrDyn *init, header_pairs = scr_arr_new(SCR_ELEM_STR, 8); if (!sf_add_converted_headers( - header_pairs, converted_headers, false)) goto fail; + header_pairs, converted_headers, false, converted_canonical)) goto fail; scr_arr_release(converted_headers); converted_headers = NULL; @@ -5048,6 +6313,7 @@ static ScrDyn *sf_response_construct(ScrDyn *body, ScrDyn *init, response->rc = 1; response->body = stream; response->headers = sf_headers_new(header_pairs, false); + response->headers->canonical = sf_header_values_trimmed(header_pairs); response->url = scr_str_new("", 0); response->status_text = status_text; response->status = status; @@ -5157,19 +6423,50 @@ static ScrDyn *sf_request_get(void *ptr, const char *key, size_t len) { if (sf_name(key, len, "body") || sf_name(key, len, "bodyUsed") || sf_name(key, len, "headers")) return sf_response_get(r->payload, key, len); return NULL; } +/* Request.clone: the payload clones as a Response does, and the clone's + * signal follows the original's (undici: an AbortController aborted with + * the original signal's reason, at once when it already aborted). */ +static ScrDyn *sf_request_clone(SfRequest *r) { + SfResponse *payload = sf_response_clone(r->payload, "unusable"); + if (!payload) return NULL; + ScrDyn *sources = scr_dyn_new_arr(); + scr_dyn_arr_push(sources, + scr_dyn_new_handle(r->signal, SCR_DYNH_ABORT_SIGNAL)); + ScrDyn *follower = scr_fetch_abort_any(sources); + scr_dyn_release(sources); + if (!follower) { + sf_response_release(payload); + return NULL; + } + SfRequest *c = calloc(1, sizeof *c); + if (!c) sf_oom(); + c->rc = 1; + c->payload = payload; + c->url = scr_str_retain(r->url); + c->method = scr_str_retain(r->method); + c->redirect = scr_str_retain(r->redirect); + c->signal = sf_signal_retain((SfSignal *)follower->v.handle.ptr); + scr_dyn_release(follower); + ScrDyn *out = scr_dyn_new_handle(c, SCR_DYNH_FETCH_REQUEST); + sf_request_release_v(c); + return out; +} static ScrDyn *sf_request_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc, const char *what) { SfRequest *r = ptr; + if (strcmp(method, "clone") == 0) return sf_request_clone(r); return sf_response_invoke(r->payload, self, method, args, argc, what); } ScrDyn *scr_fetch_headers_new(ScrDyn *init) { - ScrArr *converted = sf_headers_convert_init(init); + bool canonical = false; + ScrArr *converted = sf_headers_convert_init(init, &canonical); if (!converted) return NULL; ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, 8); - bool ok = sf_add_converted_headers(pairs, converted, false); + bool ok = sf_add_converted_headers(pairs, converted, false, canonical); scr_arr_release(converted); if (!ok) { scr_arr_release(pairs); return NULL; } SfHeaders *headers = sf_headers_new(pairs, false); + headers->canonical = sf_header_values_trimmed(pairs); ScrDyn *result = scr_dyn_new_handle(headers, SCR_DYNH_FETCH_HEADERS); sf_headers_release(headers); return result; @@ -5672,13 +6969,20 @@ static const ScrDynHandleMethod sf_reader_methods[] = { {NULL, 0, false}}; static const ScrDynHandleMethod sf_body_methods[] = { {"json", 0, true}, {"text", 0, true}, {"bytes", 0, true}, - {"arrayBuffer", 0, true}, {NULL, 0, false}}; + {"arrayBuffer", 0, true}, {"clone", 0, false}, {NULL, 0, false}}; static const ScrDynHandleMethod sf_headers_methods[] = { {"append", 2, false}, {"delete", 1, false}, {"entries", 0, false}, {"forEach", 1, false}, {"get", 1, false}, {"getSetCookie", 0, false}, {"has", 1, false}, {"keys", 0, false}, {"set", 2, false}, {"values", 0, false}, {NULL, 0, false}}; +static const ScrDynHandleMethod sf_writable_methods[] = { + {"abort", 0, true}, {"close", 0, true}, {"getWriter", 0, false}, + {NULL, 0, false}}; +static const ScrDynHandleMethod sf_writer_methods[] = { + {"abort", 0, true}, {"close", 0, true}, {"releaseLock", 0, false}, + {"write", 0, true}, {NULL, 0, false}}; + static ScrDyn *sf_rejected(void) { ScrPromise *p = scr_promise_settled_ref(NULL, &scr_dyn_retain_v, &scr_dyn_release_v, NULL); @@ -5725,6 +7029,23 @@ static const ScrDynHandleOps sf_headers_ops = { &sf_headers_invoke, &sf_headers_get, &sf_no_set, NULL, NULL, &sf_headers_step, .methods = sf_headers_methods}; +static const ScrDynHandleOps sf_transform_ops = { + "TransformStream", &sf_transform_retain_v, &sf_transform_release_v, + &sf_transform_invoke, &sf_transform_get, &sf_no_set, NULL}; +static const ScrDynHandleOps sf_writable_ops = { + "WritableStream", &sf_transform_retain_v, &sf_transform_release_v, + &sf_writable_invoke, &sf_writable_get, &sf_no_set, NULL, + .methods = sf_writable_methods, + .invalid_this = "Value of \"this\" must be of type WritableStream", + .rejected = &sf_rejected}; +static const ScrDynHandleOps sf_writer_ops = { + "WritableStreamDefaultWriter", &sf_writer_handle_retain_v, + &sf_writer_handle_release_v, &sf_writer_invoke, &sf_writer_get, + &sf_no_set, NULL, .methods = sf_writer_methods, + .invalid_this = + "Value of \"this\" must be of type WritableStreamDefaultWriter", + .rejected = &sf_rejected}; + static void sf_teardown(void) { while (sf_live) { SfTransfer *t = sf_live; @@ -5764,6 +7085,9 @@ void scr_fetch_install(void) { scr_dyn_handle_install(SCR_DYNH_FETCH_REQUEST, &sf_request_ops); scr_dyn_handle_install(SCR_DYNH_FETCH_RESPONSE, &sf_response_ops); scr_dyn_handle_install(SCR_DYNH_FETCH_HEADERS, &sf_headers_ops); + scr_dyn_handle_install(SCR_DYNH_WEB_TRANSFORM, &sf_transform_ops); + scr_dyn_handle_install(SCR_DYNH_WEB_WRITABLE, &sf_writable_ops); + scr_dyn_handle_install(SCR_DYNH_WEB_WRITER, &sf_writer_ops); scr_et_signal_core_fn = sf_signal_core_of; scr_atexit(sf_teardown); } diff --git a/packages/runtime/src/scr_fs_stream.c b/packages/runtime/src/scr_fs_stream.c index 70a5ab075..940068a7b 100644 --- a/packages/runtime/src/scr_fs_stream.c +++ b/packages/runtime/src/scr_fs_stream.c @@ -79,6 +79,9 @@ typedef struct ScrFssOp { int64_t result; /* answer or -errno */ int retries; /* WRITE: zero-byte retries */ ScrError *carry; /* FSYNC/CLOSE: the error to pass on (+1) */ + /* The AsyncLocalStorage context the step was started in (+1): its + * continuation runs in it, as Node runs an fs request's callback. */ + ScrAlsCtx *ctx; } ScrFssOp; static void scr_fss_work(void *p) { @@ -101,6 +104,7 @@ static void scr_fss_op_destroy(void *p) { free(op->buf); if (op->chunk) scr_bytes_release(op->chunk); if (op->carry) scr_error_release(op->carry); + scr_als_ctx_release(op->ctx); free(op); } @@ -111,13 +115,20 @@ static ScrFssOp *scr_fss_op_new(ScrStream *s, ScrFssOpKind kind, int fd) { op->kind = kind; op->fd = fd; op->pos = -1; + op->ctx = scr_als_capture(); return op; } static void scr_fss_after(void *p); +static void scr_fss_after_in_context(void *p) { + ScrAlsCtx *prev = scr_als_dispatch_enter(((ScrFssOp *)p)->ctx); + scr_fss_after(p); + scr_als_dispatch_leave(prev); +} + static void scr_fss_submit(ScrFssOp *op) { - scr_work_submit(op, &scr_fss_work, &scr_fss_after, &scr_fss_op_destroy); + scr_work_submit(op, &scr_fss_work, &scr_fss_after_in_context, &scr_fss_op_destroy); } /* ── close (Node's close/_close) ──────────────────────────────────────── */ diff --git a/packages/runtime/src/scr_generator.h b/packages/runtime/src/scr_generator.h index 828f339d5..c73ceb900 100644 --- a/packages/runtime/src/scr_generator.h +++ b/packages/runtime/src/scr_generator.h @@ -192,6 +192,7 @@ ScrDyn *scr_fetch_abort_timeout(ScrDyn *delay); ScrDyn *scr_fetch_abort_now(ScrDyn *reason); /* borrowed reason; +1 handle */ ScrDyn *scr_fetch_abort_any(ScrDyn *signals); /* borrowed array; +1 handle or NULL pending */ ScrDyn *scr_fetch_stream_new(ScrDyn *source); /* borrowed source; +1 handle or NULL pending */ +ScrDyn *scr_fetch_transform_new(void); /* +1 identity TransformStream handle */ ScrDyn *scr_fetch_stream_from(ScrDyn *iterable); /* borrowed iterable; +1 handle or NULL pending */ ScrDyn *scr_fetch_stream_from_array( ScrArr *iterable, diff --git a/packages/runtime/src/scr_http.c b/packages/runtime/src/scr_http.c index 29eb52ef7..3fb0fdcba 100644 --- a/packages/runtime/src/scr_http.c +++ b/packages/runtime/src/scr_http.c @@ -209,6 +209,11 @@ void scr_http_set_h2_upgrade_hook(void (*hook)(void *h2ctx, ScrClosure *cb, void struct ScrHttpReq { size_t rc; + /* The AsyncLocalStorage context of its creation (+1, NULL outside a + * store): the deferred emits run in it (scr_http_proto_sweep). A server + * request is created in its connection's dispatch, which runs in the + * server's listen() context; a client response in its request's. */ + ScrAlsCtx *als; int status; /* -1 on server requests (Node's undefined statusCode) */ ScrStr *method; ScrStr *url; @@ -303,6 +308,7 @@ void scr_http_req_set_encoding(ScrHttpReq *r, ScrStr *enc /*borrowed*/) { void scr_http_req_release(ScrHttpReq *r) { if (!r || r->rc == SIZE_MAX) return; if (--r->rc == 0) { + scr_als_ctx_release(r->als); scr_str_release(r->method); scr_str_release(r->url); for (size_t i = 0; i < r->nheaders; i++) { @@ -950,6 +956,7 @@ static bool scr_http_trailers_replace(ScrHttpTrailers *t, ScrArr *pairs /*borrow struct ScrHttpRes { size_t rc; + ScrAlsCtx *als; /* +1: as ScrHttpReq's */ ScrNetSocket *sock; /* +1; writes after the connection died are no-ops */ void *h2_stream; /* +1 through scr_http_h2_ops — set on h2 compat * responses; every write path routes to frames */ @@ -990,6 +997,13 @@ struct ScrHttpRes { bool req_cleared; ScrNetLs close_ls, timeout_ls; ScrNetLs finish_ls; /* res.on('finish') — fires deferred once the body went out */ + /* The AsyncLocalStorage context of the end() call (+1, NULL outside a + * store) once `end_ctx_set`: 'finish', the end() callbacks and 'close' + * after an end run in it, as Node's do. An http2 compatibility response + * never sets it: its events follow the server stream, which has no + * store. */ + ScrAlsCtx *end_als; + bool end_ctx_set; ScrNetLs endcb_ls; /* res.end(cb) — fires just before the 'finish' listeners */ ScrNetLs wcb_ls; /* res.write(chunk, cb) — fires from the queue */ bool writable_finished; /* set where Node emits 'finish' (writableFinished) */ @@ -1007,6 +1021,8 @@ ScrHttpRes *scr_http_res_retain(ScrHttpRes *r) { void scr_http_res_release(ScrHttpRes *r) { if (!r || r->rc == SIZE_MAX) return; if (--r->rc == 0) { + scr_als_ctx_release(r->als); + scr_als_ctx_release(r->end_als); for (size_t i = 0; i < r->nheaders; i++) { scr_str_release(r->hnames[i]); scr_str_release(r->hvalues[i]); @@ -1937,6 +1953,10 @@ static void scr_http_res_cork_flush(ScrHttpRes *r) { static void scr_http_res_end_raw(ScrHttpRes *r, const char *data, size_t len) { if (r->finished) return; + if (!r->end_ctx_set && r->h2_stream == NULL) { + r->end_ctx_set = true; + r->end_als = scr_als_capture(); + } double expected; if (len > 0 && r->head_sent && scr_http_res_expected_length(r, &expected) && (double)(r->strict_bytes_written + len) != expected) { @@ -2279,9 +2299,28 @@ void scr_http_res_on_write_flush(ScrHttpRes *r, ScrClosure *cb /*moves*/) { static bool scr_http_proto_pending(void) { return scr_http_emits_head < scr_http_emits_len; } +static ScrAlsCtx *scr_http_client_ctx(struct ScrHttpClientReq *c); + +/* The context an emit's handle was created in (borrowed). */ +static ScrAlsCtx *scr_http_emit_ctx(const ScrHttpEmit *e) { + switch (e->kind) { + case SCR_HTTP_EMIT_RES_CLOSE: + case SCR_HTTP_EMIT_RES_FINISH: { + const ScrHttpRes *res = (const ScrHttpRes *)e->h; + return res->end_ctx_set ? res->end_als : res->als; + } + case SCR_HTTP_EMIT_RES_WCB: return ((ScrHttpRes *)e->h)->als; + case SCR_HTTP_EMIT_REQ_CLOSE: + case SCR_HTTP_EMIT_REQ_DRAIN: + case SCR_HTTP_EMIT_REQ_ABORTED: return ((ScrHttpReq *)e->h)->als; + default: return scr_http_client_ctx((struct ScrHttpClientReq *)e->h); + } +} + static void scr_http_proto_sweep(void) { while (scr_http_emits_head < scr_http_emits_len) { ScrHttpEmit e = scr_http_emits[scr_http_emits_head++]; + ScrAlsCtx *prev = scr_als_dispatch_enter(scr_http_emit_ctx(&e)); switch (e.kind) { case SCR_HTTP_EMIT_RES_CLOSE: { ScrHttpRes *res = (ScrHttpRes *)e.h; @@ -2372,6 +2411,7 @@ static void scr_http_proto_sweep(void) { break; } } + scr_als_dispatch_leave(prev); scr_http_emit_release(&e); if (scr_exc_pending()) return; } @@ -2894,6 +2934,7 @@ static bool scr_http_conn_parse_head(ScrHttpConn *conn, size_t head_len, bool *o ScrHttpReq *req = calloc(1, sizeof *req); if (!req) scr_http_oom(); req->rc = 1; + req->als = scr_als_capture(); req->status = -1; /* Node: statusCode is undefined on server requests */ req->gated = true; req->join_dup = conn->srv != NULL && conn->srv->join_dup; @@ -3062,6 +3103,7 @@ static bool scr_http_conn_parse_head(ScrHttpConn *conn, size_t head_len, bool *o ScrHttpRes *res = calloc(1, sizeof *res); if (!res) scr_http_oom(); res->rc = 1; + res->als = scr_als_capture(); #ifdef SCR_RC_AUDIT scr_http_live++; #endif @@ -3614,6 +3656,7 @@ ScrHttpReq *scr_http_h2_req_new(ScrNetSocket *sock /*borrowed, nullable*/, ScrHttpReq *req = calloc(1, sizeof *req); if (!req) scr_http_oom(); req->rc = 1; + req->als = scr_als_capture(); req->status = -1; /* server requests: statusCode is Node's undefined */ req->http2 = true; req->sock = sock ? scr_net_sock_retain(sock) : NULL; @@ -3685,6 +3728,7 @@ ScrHttpRes *scr_http_h2_res_new(ScrNetSocket *sock /*borrowed, nullable*/, ScrHttpRes *res = calloc(1, sizeof *res); if (!res) scr_http_oom(); res->rc = 1; + res->als = scr_als_capture(); res->sock = sock ? scr_net_sock_retain(sock) : NULL; res->h2_stream = scr_http_h2_ops->retain(stream); res->keep_alive = true; /* inert on h2 — no Connection header exists */ @@ -3794,6 +3838,9 @@ void scr_http_handler_thunk0(ScrClosure *cb, ScrHttpReq *req, ScrHttpRes *res) { struct ScrHttpClientReq { size_t rc; + /* The http.request() call's AsyncLocalStorage context (+1): the + * request's deferred emits ('socket', 'finish', 'close') run in it. */ + ScrAlsCtx *als; ScrNetSocket *sock; /* +1 */ ScrHttpConn *conn; /* borrowed backref (the socket's ctx); NULL after free */ ScrStr *host, *path, *method; @@ -3858,6 +3905,7 @@ ScrHttpClientReq *scr_http_client_retain(ScrHttpClientReq *c) { void scr_http_client_release(ScrHttpClientReq *c) { if (!c || c->rc == SIZE_MAX) return; if (--c->rc == 0) { + scr_als_ctx_release(c->als); scr_net_ls_drop(&c->resp_ls); scr_net_ls_drop(&c->err_ls); scr_net_ls_drop(&c->timeout_ls); @@ -3896,6 +3944,8 @@ static void scr_http_client_release_internal(struct ScrHttpClientReq *c) { scr_http_client_release(c); } +static ScrAlsCtx *scr_http_client_ctx(struct ScrHttpClientReq *c) { return c->als; } + void *scr_http_client_retain_v(void *p) { return scr_http_client_retain((ScrHttpClientReq *)p); } void scr_http_client_release_v(void *p) { scr_http_client_release((ScrHttpClientReq *)p); } @@ -4690,6 +4740,7 @@ static bool scr_http_client_parse_head(ScrHttpConn *conn, size_t head_len) { ScrHttpReq *res = calloc(1, sizeof *res); if (!res) scr_http_oom(); res->rc = 1; + res->als = scr_als_capture(); res->status = status; res->sock = scr_net_sock_retain(conn->sock); #ifdef SCR_RC_AUDIT @@ -5083,6 +5134,7 @@ static ScrHttpClientReq *scr_http_request_impl(ScrStr *host /*borrowed*/, double ScrHttpClientReq *c = calloc(1, sizeof *c); if (!c) scr_http_oom(); c->rc = 1; + c->als = scr_als_capture(); #ifdef SCR_RC_AUDIT scr_http_live++; #endif @@ -5128,6 +5180,7 @@ static ScrHttpClientReq *scr_http_request_impl(ScrStr *host /*borrowed*/, double if (presock != NULL) { c->sock = presock; + scr_net_sock_set_context(c->sock, c->als); } else { ScrStr *dial = scr_net_blocking_lookup(host); c->sock = scr_net_connect(port, dial, NULL); @@ -5386,6 +5439,8 @@ static void scr_http_client_rebind(ScrHttpClientReq *c, ScrHttpAgentFree *f, boo scr_net_sock_release(old); ScrHttpConn *conn = f->conn; c->sock = f->sock; /* the free entry's +1 moves */ + /* Node runs a pooled socket's events for this request in its context. */ + scr_net_sock_set_context(c->sock, c->als); c->conn = conn; conn->client = scr_http_client_retain(c); conn->pool_agent = NULL; diff --git a/packages/runtime/src/scr_http2.c b/packages/runtime/src/scr_http2.c index 4c5267e6a..55d880c01 100644 --- a/packages/runtime/src/scr_http2.c +++ b/packages/runtime/src/scr_http2.c @@ -678,6 +678,10 @@ struct ScrH2Stream { int64_t send_window; ScrNetLs data_ls, end_ls, close_ls, err_ls, resp_ls, aborted_ls, ready_ls, trailers_ls, wanttrailers_ls, push_ls, frameerr_ls; + /* The AsyncLocalStorage context of the request() call that created a + * client stream (+1, NULL outside a store); NULL on server streams, + * whose own events Node runs with no store. */ + ScrAlsCtx *als; }; /* The server-side ctx (the ScrHttpSrvCtx shape; `proto` FIRST — the @@ -757,6 +761,14 @@ struct ScrH2Session { bool error_emitted; ScrNetLs stream_ls, close_ls, err_ls, connect_ls, goaway_ls, remote_settings_ls, local_settings_ls; + /* The stream the last lookup by id found (borrowed from `streams`): + * the frames of one stream arrive together, and a frame's context + * lookup and its handling ask for the same stream. */ + ScrH2Stream *last_found; + /* The AsyncLocalStorage context the session was created in (+1, NULL + * outside a store): the connect() call's for a client session, the + * server's listen() context for a server session. */ + ScrAlsCtx *als; }; /* ── refcounts + the RC audit ────────────────────────────────────────── */ @@ -838,6 +850,7 @@ static void scr_h2_stream_free(ScrH2Stream *st) { } free(st->wbuf.data); scr_http2_session_release(st->sess); + scr_als_ctx_release(st->als); free(st); #ifdef SCR_RC_AUDIT scr_h2_streams_live--; @@ -864,6 +877,7 @@ static void scr_h2_session_free(ScrH2Session *s) { scr_h2_dyntab_free(&s->dec_table); if (s->srv) scr_h2_srv_ctx_release(s->srv); if (s->sock) scr_net_sock_release(s->sock); + scr_als_ctx_release(s->als); free(s); #ifdef SCR_RC_AUDIT scr_h2_sessions_live--; @@ -1011,8 +1025,9 @@ static void scr_h2_send_headers(ScrH2Session *s, int32_t stream_id, const ScrH2B /* ── stream bookkeeping ──────────────────────────────────────────────── */ static ScrH2Stream *scr_h2_find_stream(ScrH2Session *s, int32_t id) { + if (s->last_found != NULL && s->last_found->id == id) return s->last_found; for (size_t i = 0; i < s->nstreams; i++) { - if (s->streams[i]->id == id) return s->streams[i]; + if (s->streams[i]->id == id) return s->last_found = s->streams[i]; } return NULL; } @@ -1023,6 +1038,7 @@ static ScrH2Stream *scr_h2_stream_new(ScrH2Session *sess, int32_t id) { st->rc = 1; st->id = id; st->sess = scr_http2_session_retain(sess); + if (sess->client) st->als = scr_als_capture(); st->send_window = sess->peer_init_window ? (int64_t)sess->peer_init_window : SCR_H2_DEFAULT_WINDOW; #ifdef SCR_RC_AUDIT scr_h2_streams_live++; @@ -1039,6 +1055,7 @@ static ScrH2Stream *scr_h2_stream_new(ScrH2Session *sess, int32_t id) { /* Drop the session's ref on a settled stream (its 'close' is queued or * emitted — the vector entry is what kept it discoverable by id). */ static void scr_h2_session_forget_stream(ScrH2Session *s, ScrH2Stream *st) { + if (s->last_found == st) s->last_found = NULL; for (size_t i = 0; i < s->nstreams; i++) { if (s->streams[i] == st) { memmove(s->streams + i, s->streams + i + 1, (s->nstreams - i - 1) * sizeof *s->streams); @@ -1150,6 +1167,10 @@ static bool scr_h2_proto_pending(void) { return scr_h2_emits_head < scr_h2_emits static void scr_h2_proto_sweep(void) { while (scr_h2_emits_head < scr_h2_emits_len) { ScrH2Emit e = scr_h2_emits[scr_h2_emits_head++]; + ScrAlsCtx *prev = scr_als_dispatch_enter( + e.kind == SCR_H2_EMIT_STREAM_CLOSE || e.kind == SCR_H2_EMIT_STREAM_DRAIN + ? ((ScrH2Stream *)e.h)->als + : ((ScrH2Session *)e.h)->als); switch (e.kind) { case SCR_H2_EMIT_STREAM_CLOSE: { ScrH2Stream *st = (ScrH2Stream *)e.h; @@ -1227,6 +1248,7 @@ static void scr_h2_proto_sweep(void) { break; } } + scr_als_dispatch_leave(prev); scr_h2_emit_release(&e); if (scr_exc_pending()) return; } @@ -1380,8 +1402,29 @@ static void scr_h2_fire_settings(ScrNetLs *ls, ScrH2Session *s, const ScrH2Setti } /* One complete frame. Returns false when the session tore down. */ +static bool scr_h2_on_frame_in(ScrH2Session *s, uint8_t type, uint8_t flags, int32_t stream_id, + const uint8_t *p, size_t n); + +/* A frame that drives an open stream's events (DATA, WINDOW_UPDATE, + * RST_STREAM, and a client's HEADERS, CONTINUATION and PUSH_PROMISE) runs + * in that stream's context; other frames, a server stream's opening + * HEADERS included, in the session's, which the socket callbacks below + * enter. */ static bool scr_h2_on_frame(ScrH2Session *s, uint8_t type, uint8_t flags, int32_t stream_id, const uint8_t *p, size_t n) { + bool stream_frame = type == SCR_H2_F_DATA || type == SCR_H2_F_WINDOW_UPDATE || + type == SCR_H2_F_RST_STREAM || + (s->client && (type == SCR_H2_F_HEADERS || type == SCR_H2_F_CONTINUATION || + type == SCR_H2_F_PUSH_PROMISE)); + ScrH2Stream *st = stream_frame && stream_id != 0 ? scr_h2_find_stream(s, stream_id) : NULL; + ScrAlsCtx *prev = st != NULL ? scr_als_dispatch_enter(st->als) : NULL; + bool ok = scr_h2_on_frame_in(s, type, flags, stream_id, p, n); + if (st != NULL) scr_als_dispatch_leave(prev); + return ok; +} + +static bool scr_h2_on_frame_in(ScrH2Session *s, uint8_t type, uint8_t flags, int32_t stream_id, + const uint8_t *p, size_t n) { /* An open header block admits only its CONTINUATIONs. */ if (s->hblock_active && type != SCR_H2_F_CONTINUATION) { goto proto_err; @@ -1629,7 +1672,16 @@ proto_err: return false; } +static void scr_h2_on_data_in(void *ctx, const char *buf, size_t n); + +/* The socket's callbacks run in the session's context. */ static void scr_h2_on_data(void *ctx, const char *buf, size_t n) { + ScrAlsCtx *prev = scr_als_dispatch_enter(((ScrH2Session *)ctx)->als); + scr_h2_on_data_in(ctx, buf, n); + scr_als_dispatch_leave(prev); +} + +static void scr_h2_on_data_in(void *ctx, const char *buf, size_t n) { ScrH2Session *s = ctx; if (s->destroyed) return; /* append to the reassembly buffer */ @@ -1680,7 +1732,15 @@ done: scr_http2_session_release(s); } +static void scr_h2_on_eof_in(void *ctx); + static void scr_h2_on_eof(void *ctx) { + ScrAlsCtx *prev = scr_als_dispatch_enter(((ScrH2Session *)ctx)->als); + scr_h2_on_eof_in(ctx); + scr_als_dispatch_leave(prev); +} + +static void scr_h2_on_eof_in(void *ctx) { ScrH2Session *s = ctx; if (s->destroyed) return; /* The peer is done sending. After a graceful GOAWAY exchange this is @@ -1688,7 +1748,15 @@ static void scr_h2_on_eof(void *ctx) { scr_h2_session_abort_streams(s); } +static void scr_h2_on_closed_in(void *ctx); + static void scr_h2_on_closed(void *ctx) { + ScrAlsCtx *prev = scr_als_dispatch_enter(((ScrH2Session *)ctx)->als); + scr_h2_on_closed_in(ctx); + scr_als_dispatch_leave(prev); +} + +static void scr_h2_on_closed_in(void *ctx) { ScrH2Session *s = ctx; s->destroyed = true; scr_h2_session_abort_streams(s); @@ -1700,7 +1768,16 @@ static void scr_h2_on_closed(void *ctx) { } } +static bool scr_h2_on_err_in(void *ctx, ScrStr *msg); + static bool scr_h2_on_err(void *ctx, ScrStr *msg) { + ScrAlsCtx *prev = scr_als_dispatch_enter(((ScrH2Session *)ctx)->als); + bool consumed = scr_h2_on_err_in(ctx, msg); + scr_als_dispatch_leave(prev); + return consumed; +} + +static bool scr_h2_on_err_in(void *ctx, ScrStr *msg) { ScrH2Session *s = ctx; if (s->err_ls.n == 0 && (s->srv == NULL || s->srv->session_error_ls.n == 0)) { return false; /* the socket's error story applies */ @@ -1716,6 +1793,7 @@ static ScrH2Session *scr_h2_session_new(bool client) { ScrH2Session *s = calloc(1, sizeof *s); if (!s) scr_h2_oom(); s->rc = 1; + s->als = scr_als_capture(); s->client = client; s->preface_done = client; /* clients never RECEIVE the magic */ s->next_stream_id = 1; @@ -2287,6 +2365,8 @@ static void scr_h2_upgrade_hook(void *ctx, ScrClosure *cb /*moves*/, void *fn, b * 'request' listeners (this = the net server, the http/1 shape). */ static void scr_h2_compat_dispatch(ScrH2Session *s, ScrH2Stream *st, const ScrH2Headers *h, bool end_stream, ScrNetLs *listeners) { + /* The compat pair's own events follow the stream's context. */ + ScrAlsCtx *prev = scr_als_dispatch_enter(st->als); ScrHttpReq *req = scr_http_h2_req_new(s->sock, st); for (size_t i = 0; i < h->n; i++) { ScrStr *name = h->names[i]; @@ -2301,6 +2381,7 @@ static void scr_h2_compat_dispatch(ScrH2Session *s, ScrH2Stream *st, const ScrH2 scr_http_h2_req_header(req, name, value); } ScrHttpRes *res = scr_http_h2_res_new(s->sock, st); + scr_als_dispatch_leave(prev); scr_http_res_set_req(res, req); /* res.req — the compat pair's backref */ st->compat_req = scr_http_req_retain(req); st->compat_res = scr_http_res_retain(res); @@ -2342,7 +2423,7 @@ static void scr_h2_srv_ctx_settle(void *p) { static void scr_h2_on_connection(void *ctx, ScrNetSocket *sock) { ScrH2SrvCtx *srv = (ScrH2SrvCtx *)ctx; - ScrH2Session *sess = scr_h2_session_new(false); + ScrH2Session *sess = scr_h2_session_new(false); /* in the listen() context */ sess->srv = scr_h2_srv_ctx_retain(srv); sess->sock = scr_net_sock_retain(sock); sess->next_stream_id = 2; /* server-initiated ids are even (push, unused) */ @@ -2354,6 +2435,14 @@ static void scr_h2_on_connection(void *ctx, ScrNetSocket *sock) { scr_h2_emit_push(SCR_H2_EMIT_SERVER_SESSION, scr_http2_session_retain(sess)); } +/* An accepted h2c connection: the socket's own events run with no store, + * as an accepted net socket's do (a TLS server's sockets keep the listen() + * context); the session keeps the listen() context the accept runs in. */ +static void scr_h2_on_plain_connection(void *ctx, ScrNetSocket *sock) { + scr_net_sock_set_context(sock, NULL); + scr_h2_on_connection(ctx, sock); +} + ScrNetServer *scr_http2_create_server(void) { scr_http2_install(); ScrNetServer *s = scr_net_create_server(NULL, NULL); @@ -2361,7 +2450,7 @@ ScrNetServer *scr_http2_create_server(void) { if (!ctx) scr_h2_oom(); ctx->proto = SCR_NET_PROTO_H2; ctx->rc = 1; - scr_net_server_set_native_conn(s, &scr_h2_on_connection, ctx, &scr_h2_srv_ctx_free_v); + scr_net_server_set_native_conn(s, &scr_h2_on_plain_connection, ctx, &scr_h2_srv_ctx_free_v); scr_net_server_set_http_ctx(s, ctx); scr_net_server_set_proto_settle(s, &scr_h2_srv_ctx_settle); return s; diff --git a/packages/runtime/src/scr_inspect.c b/packages/runtime/src/scr_inspect.c index 1dce8d3c2..75317f420 100644 --- a/packages/runtime/src/scr_inspect.c +++ b/packages/runtime/src/scr_inspect.c @@ -324,6 +324,9 @@ typedef struct { static SCR_TL InspSeenEnt *g_seen; static SCR_TL size_t g_nseen; +/* util.format's %o (scr_insp_dyn_hidden): this render shows hidden own + * properties, like util.inspect's showHidden. */ +static SCR_TL bool g_show_hidden; static SCR_TL size_t g_seen_cap; /* Detection-numbered circular targets (persist across the call). */ static SCR_TL const void *g_circ[64]; @@ -643,6 +646,54 @@ ScrStr *scr_insp_nested_end(ScrStr *prefix, ScrStr *inner) { return ib_take(&out); } +/* The inspector's state at the start of a top-level render. A render that + * an inspection refusal ends leaves its compiled frames open: the Error + * propagates out of the emitted helpers before their scr_insp_end and + * scr_insp_nested_end calls. The emitted render takes a mark first and + * unwinds to it on every exit (lower-inspect.ts), so later renders start + * from the state the refused one found. */ +typedef struct { + size_t nframes; + size_t nsaved; + size_t nseen; + size_t indent; + double cur_depth; +} InspMark; + +static SCR_TL InspMark *g_marks; +static SCR_TL size_t g_nmarks; +static SCR_TL size_t g_marks_cap; + +void scr_insp_mark(void) { + if (g_nmarks == g_marks_cap) { + g_marks_cap = g_marks_cap ? g_marks_cap * 2 : 4; + g_marks = realloc(g_marks, g_marks_cap * sizeof(InspMark)); + if (!g_marks) insp_oom(); + } + g_marks[g_nmarks++] = (InspMark){g_nframes, g_nsaved, g_nseen, g_indent, g_cur_depth}; +} + +void scr_insp_unwind(void) { + InspMark m = g_marks[--g_nmarks]; + while (g_nframes > m.nframes) { + InspFrame *f = &g_frames[--g_nframes]; + for (size_t i = 0; i < f->len; i++) scr_str_release(f->items[i]); + free(f->items); + } + while (g_nsaved > m.nsaved) { + InspSaved *sv = &g_saved[--g_nsaved]; + free(g_seen); + g_seen = sv->seen; + g_seen_cap = sv->seen_cap; + memcpy(g_circ, sv->circ, sizeof g_circ); + g_ncirc = sv->ncirc; + } + if (g_nseen > m.nseen) g_nseen = m.nseen; + g_indent = m.indent; + g_cur_depth = m.cur_depth; + g_pending_seen = NULL; +} + /* ── string values (formatPrimitive's string half) ───────────────────── */ /* Byte offset of UTF-16 unit `units` — when the boundary splits an astral @@ -1207,6 +1258,20 @@ static bool insp_tag_names_class(const char *tag, const char *cls) { memcmp(tag + tag_len - cls_len, cls, cls_len) == 0; } +/* The Symbol.toStringTag a class instance inherits from its class's + * getter (+1), which Node brackets after the class name when it is a + * nonempty string other than the name. An own tag field is listed with + * the fields instead. A getter that throws shows no tag, as in Node. */ +static ScrStr *insp_inherited_tag(const ScrDyn *instance, const ScrStr *name) { + ScrStr *tag = scr_dyn_typed_ref_class_tag(instance); + if (!tag && scr_exc_pending()) scr_exc_clear(); + if (tag && (tag->len == 0 || (tag->len == name->len && memcmp(tag->data, name->data, name->len) == 0))) { + scr_str_release(tag); + return NULL; + } + return tag; +} + /* A native Set or Map held as a checked-dynamic value: `Set(n) { a, b }`, * `Map(n) { k => v }`, at most 100 entries like Node's maxArrayLength. */ /* Node's formatWeakCollection: the entries of a WeakMap or WeakSet are not @@ -1384,18 +1449,25 @@ static ScrStr *scr_insp_dyn_collection(ScrDyn *d, double recurse, double depth, return out; } -/* An own enumerable symbol-keyed property of `d` (borrowed), or NULL. */ +/* An own symbol-keyed property of `d` (borrowed) this render shows, or + * NULL: enumerable ones, and under showHidden the non-enumerable ones too. */ static ScrDynEntry *scr_insp_symbol_entry(ScrDyn *d, size_t i) { ScrDynEntry *ent = scr_dyn_own_symbol_entry(d, d->symbol_keys->v.arr.items[i]); - return ent && ent->enumerable ? ent : NULL; + return ent && (ent->enumerable || g_show_hidden) ? ent : NULL; } /* An ordinary object: `Name { key: value }` with its constructor name * `cls` (NULL for a plain object). Node's default view lists the own * enumerable string keys, then the own enumerable symbol keys. */ +/* An own entry this render shows: enumerable ones, and under showHidden + * the non-enumerable ones too (never the encoding's `%` markers). */ +static bool insp_obj_shows(const ScrDynEntry *ent) { + return ent->enumerable || (g_show_hidden && !(ent->key_len && ent->key[0] == '%')); +} + static ScrStr *scr_insp_obj(ScrDyn *d, double recurse, double depth, const char *cls) { size_t shown = 0; - for (size_t i = 0; i < d->v.obj.len; i++) shown += d->v.obj.entries[i].enumerable; + for (size_t i = 0; i < d->v.obj.len; i++) shown += insp_obj_shows(&d->v.obj.entries[i]); size_t nsymbols = d->symbol_keys ? d->symbol_keys->v.arr.len : 0; for (size_t i = 0; i < nsymbols; i++) shown += scr_insp_symbol_entry(d, i) != NULL; if (shown == 0) { @@ -1411,10 +1483,17 @@ static ScrStr *scr_insp_obj(ScrDyn *d, double recurse, double depth, const char scr_insp_begin(recurse + 1); for (size_t i = 0; i < d->v.obj.len; i++) { ScrDynEntry *ent = &d->v.obj.entries[i]; - if (!ent->enumerable) continue; - ScrStr *val = scr_insp_dyn(ent->value, recurse + 1, depth); + if (!insp_obj_shows(ent)) continue; + ScrStr *val = ent->accessor ? insp_accessor_tag(ent) : scr_insp_dyn(ent->value, recurse + 1, depth); InspBuf eb = {0}; - insp_key_into(&eb, ent->key, ent->key_len); + if (ent->enumerable) { + insp_key_into(&eb, ent->key, ent->key_len); + } else { + /* formatProperty's hidden key: the key's rendering in brackets. */ + ib_char(&eb, '['); + insp_key_into(&eb, ent->key, ent->key_len); + ib_char(&eb, ']'); + } ib_cstr(&eb, ": "); ib_bytes(&eb, val->data, val->len); scr_str_release(val); @@ -1426,9 +1505,12 @@ static ScrStr *scr_insp_obj(ScrDyn *d, double recurse, double depth, const char ScrDynEntry *ent = scr_insp_symbol_entry(d, i); if (!ent) continue; ScrStr *key = scr_insp_dyn(d->symbol_keys->v.arr.items[i], recurse + 1, depth); - ScrStr *val = scr_insp_dyn(ent->value, recurse + 1, depth); + ScrStr *val = ent->accessor ? insp_accessor_tag(ent) : scr_insp_dyn(ent->value, recurse + 1, depth); InspBuf eb = {0}; + /* A hidden symbol key is bracketed like a hidden string key. */ + if (!ent->enumerable) ib_char(&eb, '['); ib_bytes(&eb, key->data, key->len); + if (!ent->enumerable) ib_char(&eb, ']'); ib_cstr(&eb, ": "); ib_bytes(&eb, val->data, val->len); scr_str_release(key); @@ -1489,8 +1571,171 @@ ScrStr *scr_insp_typed_array(ScrBytes *b, double recurse, double depth) { return out; } +/* Node's function rendering: [Function: name] / [Function (anonymous)], + * and [class C extends B] / [class (anonymous)] for a class. The boxed + * name is the compiler's best-effort static spelling (SEMANTICS.md — + * reference-site naming). Enumerable own properties (a class's static + * fields, properties assigned to a function) follow in braces; past the + * depth limit such a function is [Function]. Borrows d; +1. */ +static ScrStr *scr_insp_dyn_func(ScrDyn *d, double recurse, double depth) { + const char *name = d->v.fn.name; + const ScrClassObj *cls = d->v.fn.class_obj; + ScrClosure *owner = scr_closure_identity(d->v.fn.clo); + ScrBox *props = owner->props ? owner->props : cls ? cls->static_data : NULL; + ScrDyn *table = props ? (ScrDyn *)scr_box_get_ref(props) : NULL; /* +1 */ + InspBuf out = {0}; + if (cls) { + ib_cstr(&out, "[class "); + ib_cstr(&out, name && name[0] ? name : "(anonymous)"); + const ScrDyn *base = table ? table->prototype : NULL; + if (cls->base_name && cls->base_name->len) { + ib_cstr(&out, " extends "); + ib_bytes(&out, cls->base_name->data, cls->base_name->len); + } else if (base && base->kind == SCR_DYN_FUNC && base->v.fn.name && base->v.fn.name[0]) { + ib_cstr(&out, " extends "); + ib_cstr(&out, base->v.fn.name); + } + ib_char(&out, ']'); + } else if (name && name[0]) { + ib_cstr(&out, "[Function: "); + ib_cstr(&out, name); + ib_char(&out, ']'); + } else { + ib_cstr(&out, "[Function (anonymous)]"); + } + ScrStr *tag = ib_take(&out); + if (cls && !props && cls->own_static_fields) { + /* Node prints the static fields after the tag; the class object + * does not hold them (the reflection fence's stance). */ + scr_str_release(tag); + static const char message[] = "util.inspect of a class with static fields through unknown is not supported yet"; + scr_throw_error_msg(SCR_ERR_ERROR, message, sizeof message - 1); + return scr_str_new("", 0); + } + size_t shown = 0; + if (table && table->kind == SCR_DYN_OBJ) { + for (size_t i = 0; i < table->v.obj.len; i++) shown += table->v.obj.entries[i].enumerable; + size_t nsymbols = table->symbol_keys ? table->symbol_keys->v.arr.len : 0; + for (size_t i = 0; i < nsymbols; i++) shown += scr_insp_symbol_entry(table, i) != NULL; + } + if (shown == 0) { + scr_dyn_release(table); + return tag; + } + /* A function already on the traversal stack renders as [Circular *N], + * even past the depth limit. Its identity is the class object for a class + * (each dynamic box of a class may carry its own closure) and the closure + * every box of a function shares otherwise. */ + const void *identity = cls ? (const void *)cls : (const void *)owner; + double circular = scr_insp_circ_check(identity); + if (circular > 0) { + scr_dyn_release(table); + scr_str_release(tag); + return scr_insp_circular(circular); + } + if (recurse > depth) { + scr_dyn_release(table); + scr_str_release(tag); + return scr_str_new("[Function]", 10); + } + char *prefix = malloc(tag->len + 1); + if (!prefix) scr_trap("scriptc: out of memory\n"); + memcpy(prefix, tag->data, tag->len); + prefix[tag->len] = '\0'; + scr_str_release(tag); + size_t nseen = g_nseen; + g_pending_seen = identity; + ScrStr *rendered = scr_insp_obj(table, recurse, depth, prefix); + g_pending_seen = NULL; + if (g_nseen > nseen) g_nseen = nseen; + free(prefix); + scr_dyn_release(table); + return rendered; +} + +/* Under showHidden, the kinds whose hidden entries this inspector does not + * render (a function's length/name/prototype, an error's stack, a typed + * array's byte fields, a native handle's internals): the kind's name, NULL + * for the kinds it renders. */ +static const char *insp_hidden_refusal(const ScrDyn *d) { + switch (d->kind) { + case SCR_DYN_FUNC: return "function"; + case SCR_DYN_BYTES: return d->buffer ? "Buffer" : d->v.bytes->is_data_view ? "DataView" : "typed array"; + case SCR_DYN_OBJ: return scr_dyn_obj_get(d, "%error", 6) != NULL ? "Error" : NULL; + case SCR_DYN_HANDLE: + return d->v.handle.tag == SCR_DYNH_MAP || d->v.handle.tag == SCR_DYNH_SET || + d->v.handle.tag == SCR_DYNH_DATE + ? NULL + : scr_dyn_handle_cls(d); + default: return NULL; + } +} + +ScrStr *scr_insp_bytes(ScrBytes *b, double recurse, double depth) { + if (b->is_data_view) return scr_insp_data_view(b, recurse, depth); + if (b->is_buffer) return scr_insp_buffer(b); + return scr_insp_typed_array(b, recurse, depth); +} + +/* Node's URLSearchParams inspector: [Object] past the depth limit, and + * otherwise each pair as `name => value` with both strings inspected, on + * one line, or one pair per line when the pairs' text is longer than + * breakLength (the closing brace stays on the last pair's line). The lines + * take the caller's indentation, as a custom inspector's text does. */ +static ScrStr *scr_insp_search_params(ScrSearchParams *sp, double recurse, double depth) { + if (recurse > depth) return scr_str_new("[Object]", 8); + size_t n = (size_t)scr_sp_size(sp); + if (n == 0) return scr_str_new("URLSearchParams {}", 18); + ScrStr **items = malloc(n * sizeof *items); + if (!items) insp_oom(); + double length = -2; + for (size_t i = 0; i < n; i++) { + ScrStr *name = scr_sp_key_at(sp, (double)i); + ScrStr *value = scr_sp_val_at(sp, (double)i); + ScrStr *name_text = scr_insp_str(name); + ScrStr *value_text = scr_insp_str(value); + InspBuf item = {0}; + ib_str(&item, name_text); + ib_cstr(&item, " => "); + ib_str(&item, value_text); + items[i] = ib_take(&item); + length += (double)insp_utf16_len(items[i]->data, items[i]->len) + 2; + scr_str_release(name); + scr_str_release(value); + scr_str_release(name_text); + scr_str_release(value_text); + } + bool multiline = length > g_break_length; + InspBuf out = {0}; + ib_cstr(&out, multiline ? "URLSearchParams {\n " : "URLSearchParams { "); + for (size_t i = 0; i < n; i++) { + if (i) ib_cstr(&out, multiline ? ",\n " : ", "); + ib_str(&out, items[i]); + scr_str_release(items[i]); + } + free(items); + ib_cstr(&out, " }"); + ScrStr *text = ib_take(&out); + InspBuf indented = {0}; + for (size_t k = 0; k < text->len; k++) { + ib_char(&indented, text->data[k]); + if (text->data[k] == '\n') ib_spaces(&indented, g_indent); + } + scr_str_release(text); + return ib_take(&indented); +} + ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { if (d->kind == SCR_DYN_PROXY) return scr_insp_dyn(d->v.proxy.target, recurse, depth); + const char *refused = g_show_hidden ? insp_hidden_refusal(d) : NULL; + if (refused) { + InspBuf out = {0}; + ib_cstr(&out, "util.format %o of "); + ib_cstr(&out, refused); + ib_cstr(&out, " values is not supported yet"); + scr_throw_error(SCR_ERR_ERROR, ib_take(&out)); + return scr_str_new("", 0); + } switch (d->kind) { case SCR_DYN_NULL: return scr_str_new("null", 4); @@ -1509,11 +1754,12 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { case SCR_DYN_ARR: { size_t len = d->v.arr.len; /* Named and noncanonical numeric properties follow the elements as - * `key: value` entries (Node's array extras). */ + * `key: value` entries (Node's array extras); under showHidden the + * own `length` comes first, and non-enumerable properties show too. */ const ScrDyn *props = d->v.arr.properties; size_t nprops = 0; - for (size_t i = 0; props && i < props->v.obj.len; i++) nprops += props->v.obj.entries[i].enumerable; - if (len == 0 && nprops == 0) return scr_str_new("[]", 2); + for (size_t i = 0; props && i < props->v.obj.len; i++) nprops += insp_obj_shows(&props->v.obj.entries[i]); + if (len == 0 && nprops == 0 && !g_show_hidden) return scr_str_new("[]", 2); if (recurse > depth) return scr_str_new("[Array]", 7); scr_insp_begin(recurse + 1); /* Node's maxArrayLength (100) counts output entries; a run of holes @@ -1545,12 +1791,27 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { scr_insp_entry(m, d->v.arr.items[i]->kind == SCR_DYN_NUM); scr_str_release(m); } + if (g_show_hidden) { + /* The own non-enumerable `length`, after the items. Node's + * grouping reads value[k] for every output position k, this one + * included. */ + char line[48]; + int n = snprintf(line, sizeof line, "[length]: %zu", len); + ScrStr *s = scr_str_new(line, (size_t)n); + scr_insp_entry(s, entries < len && (!presence || presence[entries]) && + d->v.arr.items[entries]->kind == SCR_DYN_NUM); + scr_str_release(s); + entries++; + } for (size_t k = 0; props && k < props->v.obj.len; k++) { const ScrDynEntry *ent = &props->v.obj.entries[k]; - if (!ent->enumerable) continue; + if (!insp_obj_shows(ent)) continue; ScrStr *val = ent->accessor ? insp_accessor_tag(ent) : scr_insp_dyn(ent->value, recurse + 1, depth); InspBuf eb = {0}; + /* formatProperty's hidden key: the key's rendering in brackets. */ + if (!ent->enumerable) ib_char(&eb, '['); insp_key_into(&eb, ent->key, ent->key_len); + if (!ent->enumerable) ib_char(&eb, ']'); ib_cstr(&eb, ": "); ib_bytes(&eb, val->data, val->len); scr_str_release(val); @@ -1564,8 +1825,8 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { entries++; } /* Node's grouping leaves out the last entry when there are more - * entries than maxArrayLength (the `... more items` entry when the - * array has no properties). */ + * entries than maxArrayLength (100): the `... more items` entry, + * `[length]` under showHidden, or the last property. */ bool last_out = entries > 100; ScrStr *base = scr_str_new("", 0); ScrStr *b0 = scr_str_new("[", 1); @@ -1587,21 +1848,8 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { * the bare marker (where a plain object says [Object]). */ return scr_insp_obj(d, recurse, depth, scr_dyn_class_name(d)); } - case SCR_DYN_FUNC: { - /* Node's function rendering: [Function: name] / [Function - * (anonymous)]. The boxed name is the compiler's best-effort static - * spelling (SEMANTICS.md — reference-site naming). */ - const char *name = d->v.fn.name; - InspBuf out = {0}; - if (name && name[0]) { - ib_cstr(&out, "[Function: "); - ib_cstr(&out, name); - ib_char(&out, ']'); - } else { - ib_cstr(&out, "[Function (anonymous)]"); - } - return ib_take(&out); - } + case SCR_DYN_FUNC: + return scr_insp_dyn_func(d, recurse, depth); case SCR_DYN_HANDLE: { if (d->v.handle.tag == SCR_DYNH_ARRAY_BUFFER || d->v.handle.tag == SCR_DYNH_SHARED_ARRAY_BUFFER) { @@ -1613,6 +1861,8 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { return scr_insp_weak_collection(d->v.handle.tag == SCR_DYNH_WEAK_MAP ? "WeakMap" : "WeakSet", recurse, depth); if (d->v.handle.tag == SCR_DYNH_DATE) return scr_insp_date(scr_dyn_native_date_value(d)); + if (d->v.handle.tag == SCR_DYNH_SEARCH_PARAMS) + return scr_insp_search_params(d->v.handle.ptr, recurse, depth); if (d->v.handle.tag == SCR_DYNH_ITERATOR) { ScrStr *iterator = scr_insp_native_iterator(d, recurse, depth); if (iterator) return iterator; @@ -1623,6 +1873,14 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { * depth limit (`depth < 0`); the ones that return `this` there * print util.inspect's `[cls]`. */ if (recurse > depth && !ops->inspect_view_empty) { + /* A nested-view inspector returns the instance itself there, + * which has no own properties: `URL {}`. */ + if (ops->inspect_view_nested) { + InspBuf out = {0}; + ib_cstr(&out, ops->cls); + ib_cstr(&out, " {}"); + return ib_take(&out); + } if (!ops->inspect_bracketed_past_depth) return scr_str_new(ops->cls, strlen(ops->cls)); InspBuf out = {0}; ib_char(&out, '['); @@ -1631,6 +1889,16 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { return ib_take(&out); } ScrDyn *view = ops->inspect_view(d->v.handle.ptr); + if (ops->inspect_view_nested) { + scr_insp_nested_begin(); + ScrStr *inner = scr_insp_dyn(view, 0, depth); + scr_dyn_release(view); + ScrStr *prefix = scr_str_new(ops->cls, strlen(ops->cls)); + ScrStr *rendered = scr_insp_nested_end(prefix, inner); + scr_str_release(prefix); + scr_str_release(inner); + return rendered; + } ScrStr *body = scr_insp_dyn(view, recurse, depth); scr_dyn_release(view); InspBuf out = {0}; @@ -1725,10 +1993,23 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { free(cls); } else if (name && materialized->kind == SCR_DYN_OBJ && scr_dyn_obj_get(materialized, "%error", 6) == NULL) { - char *cls = malloc(name->len + 1); + ScrStr *tag = insp_inherited_tag(d, name); + if (scr_exc_pending()) { + scr_dyn_release(materialized); + return scr_str_new("", 0); + } + size_t tag_len = tag ? tag->len + 3 : 0; + char *cls = malloc(name->len + tag_len + 1); if (!cls) scr_trap("scriptc: out of memory\n"); memcpy(cls, name->data, name->len); - cls[name->len] = '\0'; + if (tag) { + /* Node's `Name [tag]` prefix. */ + memcpy(cls + name->len, " [", 2); + memcpy(cls + name->len + 2, tag->data, tag->len); + cls[name->len + tag_len - 1] = ']'; + scr_str_release(tag); + } + cls[name->len + tag_len] = '\0'; size_t nseen = g_nseen; g_pending_seen = instance; out = scr_insp_obj(materialized, recurse, depth, cls); @@ -1772,6 +2053,16 @@ ScrStr *scr_insp_dyn_s(ScrDyn *d, double depth) { return scr_insp_dyn(d, 0, depth); } +/* util.format's %o over a dyn value: util.inspect with showHidden at the + * given depth. */ +ScrStr *scr_insp_dyn_hidden(ScrDyn *d, double depth) { + bool outer = g_show_hidden; + g_show_hidden = true; + ScrStr *out = scr_insp_dyn(d, 0, depth); + g_show_hidden = outer; + return out; +} + /* ── console.table cells ─────────────────────────────────────────────── */ size_t scr_insp_display_width(const char *data, size_t len) { return insp_width(data, len); } diff --git a/packages/runtime/src/scr_inspect.h b/packages/runtime/src/scr_inspect.h index 5ac3aa9dd..8af60f0c0 100644 --- a/packages/runtime/src/scr_inspect.h +++ b/packages/runtime/src/scr_inspect.h @@ -45,8 +45,13 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth); /* A numeric typed array that is not a Buffer: `Uint8Array(n) [ ... ]`, * `[Uint8Array]` past the depth limit. Borrows b; +1. */ ScrStr *scr_insp_typed_array(ScrBytes *b, double recurse, double depth); +/* A u8 byte view whose static type does not say which one it is (a union + * arm, an element or field): a DataView, a Buffer () or a + * Uint8Array, by the view's own flags. */ +ScrStr *scr_insp_bytes(ScrBytes *b, double recurse, double depth); /* format's %s / rest-args twin: dyn strings pass verbatim. */ ScrStr *scr_insp_dyn_s(ScrDyn *d, double depth); +ScrStr *scr_insp_dyn_hidden(ScrDyn *d, double depth); /* %o: showHidden */ void scr_insp_begin(double recurse); /* util.inspect's breakLength, compact (0 for false) and sorted for the * renders that follow; the call site restores the defaults (80, 3, false) @@ -75,6 +80,10 @@ ScrStr *scr_insp_end(ScrStr *base, ScrStr *b0, ScrStr *b1, double recurse, * caller's level (+1; borrows both). */ void scr_insp_nested_begin(void); ScrStr *scr_insp_nested_end(ScrStr *prefix, ScrStr *inner); +/* Mark the inspector's state before a top-level render, and return to the + * mark when the render ends, normally or by an inspection refusal. */ +void scr_insp_mark(void); +void scr_insp_unwind(void); /* The buf.read* / buf.write* numeric families on u8 bytes, Node-exact: * boundsError's ladder (non-integer offset → 'It must be an integer', a @@ -115,6 +124,9 @@ ScrBytes *scr_fs_read_file_bytes(ScrStr *path); /* +1 */ * with the exception pending. */ ScrDyn *scr_fs_read_file_sync_dyn(ScrStr *path, const ScrDyn *enc); ScrStr *scr_fs_read_file_encoded(ScrStr *path, ScrStr *encoding); +/* readFileSync's encoding check (ERR_INVALID_ARG_VALUE), on its own: the + * check Node runs before it converts a URL path. */ +void scr_fs_check_read_encoding(ScrStr *encoding); ScrStr *scr_fs_read_fd_encoded(double fd, ScrStr *encoding); ScrDyn *scr_fs_callback_value(ScrStr *member); ScrDyn *scr_fs_callback_call(ScrStr *member, const ScrDyn *args); diff --git a/packages/runtime/src/scr_json.c b/packages/runtime/src/scr_json.c index da18a1c0b..fd6db7ce8 100644 --- a/packages/runtime/src/scr_json.c +++ b/packages/runtime/src/scr_json.c @@ -202,6 +202,19 @@ void scr_dyn_install_has_instance_symbol(ScrDyn *key) { scr_atexit(scr_has_instance_symbol_cleanup); } +/* Symbol.toStringTag, installed when the program first evaluates it: no + * object holds the key before then. */ +static SCR_TL ScrDyn *scr_to_string_tag_symbol; +static void scr_to_string_tag_symbol_cleanup(void) { + scr_dyn_release(scr_to_string_tag_symbol); + scr_to_string_tag_symbol = NULL; +} +void scr_dyn_install_to_string_tag_symbol(ScrDyn *key) { + if (scr_to_string_tag_symbol) return; + scr_to_string_tag_symbol = scr_dyn_retain(key); + scr_atexit(scr_to_string_tag_symbol_cleanup); +} + void scr_dyn_install_iterator_symbol(ScrDyn *key) { if (scr_iterator_symbol) return; scr_iterator_symbol = scr_dyn_retain(key); @@ -1911,7 +1924,10 @@ static ScrDyn *scr_error_ctor_call(ScrClosure *closure, ScrDyn *const *args, siz ScrDyn *scr_dyn_error_constructor(const char *name) { for (size_t i = 0; i < SCR_ERROR_CTORS; i++) { - if (!scr_error_ctor_names[i] || strcmp(scr_error_ctor_names[i], name) != 0) continue; + /* The first byte rules out most names without a call. */ + if (!scr_error_ctor_names[i] || scr_error_ctor_names[i][0] != name[0] || + strcmp(scr_error_ctor_names[i], name) != 0) + continue; if (!scr_error_ctors_registered) { scr_error_ctors_registered = true; scr_atexit(scr_error_ctors_cleanup); @@ -1921,6 +1937,8 @@ ScrDyn *scr_dyn_error_constructor(const char *name) { scr_error_ctor_values[i] = scr_dyn_new_func(scr_closure_new((void *)scr_error_ctor_names[i], 0), scr_error_ctor_call, length, "native:constructor", scr_error_ctor_names[i]); + /* Error.prototype is modeled; the other error prototypes are not. */ + if (i == 0) scr_dyn_fn_seed_prototype(scr_error_ctor_values[i], scr_dyn_error_prototype()); } return scr_dyn_retain(scr_error_ctor_values[i]); } @@ -2186,8 +2204,11 @@ ScrDyn *scr_dyn_obj_create(ScrDyn *prototype) { return object; } +static bool scr_dyn_error_view_on_error_prototype(const ScrDyn *object); + ScrDyn *scr_dyn_get_prototype(ScrDyn *object) { if (object == scr_builtin_object_prototype) return scr_dyn_new_null(); + if (object->kind == SCR_DYN_OBJ && scr_dyn_error_view_on_error_prototype(object)) return scr_dyn_error_prototype(); if (scr_dyn_class_reflection_fence(object)) return NULL; if (object->kind == SCR_DYN_TYPED_REF && strncmp(object->v.typed_ref.type_key, "object:", 7)) { ScrDyn *view = scr_dyn_typed_ref_materialize(object); @@ -2313,6 +2334,79 @@ ScrDyn *scr_dyn_set_prototype(ScrDyn *object, ScrDyn *prototype) { return scr_dyn_retain(object); } +/* The native record a record capsule refers to, or NULL for any other + * value. */ +static void *scr_dyn_typed_record(const ScrDyn *d) { + if (d->kind != SCR_DYN_TYPED_REF || d->v.typed_ref.type_key_len < 7 || + memcmp(d->v.typed_ref.type_key, "record:", 7) != 0) + return NULL; + return d->v.typed_ref.ptr; +} + +/* The native array an array capsule refers to, or NULL for any other + * value. */ +static ScrArr *scr_dyn_typed_array(const ScrDyn *d) { + if (d->kind != SCR_DYN_TYPED_REF || d->v.typed_ref.type_key_len < 6 || + memcmp(d->v.typed_ref.type_key, "array<", 6) != 0) + return NULL; + return d->v.typed_ref.ptr; +} + +/* The native Map or Set a handle holds, or NULL for any other value. */ +static ScrMap *scr_dyn_native_collection(const ScrDyn *d) { + if (d->kind != SCR_DYN_HANDLE || (d->v.handle.tag != SCR_DYNH_MAP && d->v.handle.tag != SCR_DYNH_SET)) + return NULL; + return d->v.handle.ptr; +} + +/* Whether a capsule's native record or array is frozen. */ +static bool scr_dyn_typed_ref_frozen(const ScrDyn *d) { + void *record = scr_dyn_typed_record(d); + if (record) return scr_rc_frozen(record); + ScrArr *array = scr_dyn_typed_array(d); + return array && scr_arr_frozen(array); +} + +/* Freezes the checked view of a frozen record or array: its fields or + * elements, and every property added through the view. */ +static void scr_dyn_record_view_freeze(ScrDyn *view) { + view->non_extensible = true; + if (view->kind == SCR_DYN_OBJ) { + for (size_t i = 0; i < view->v.obj.len; i++) { + ScrDynEntry *entry = &view->v.obj.entries[i]; + entry->configurable = false; + if (!entry->accessor) entry->writable = false; + } + } else if (view->kind == SCR_DYN_ARR) { + view->v.arr.sealed = true; + view->v.arr.frozen = true; + if (view->v.arr.properties) scr_dyn_release(scr_dyn_freeze_data(view->v.arr.properties)); + } + if (view->symbol_properties) scr_dyn_release(scr_dyn_freeze_data(view->symbol_properties)); +} + +void scr_record_freeze(void *record) { + size_t *rc = record; + if (*rc != SIZE_MAX) *rc |= SCR_RC_FROZEN; +} + +void scr_record_throw_frozen(ScrStr *key, ScrStr *owner, double operation) { + ScrJsonBuf buffer; + scr_jb_init(&buffer); + if (operation == 1) { + scr_jb_puts(&buffer, "Cannot add property "); + scr_jb_write(&buffer, key->data, key->len); + scr_jb_puts(&buffer, ", object is not extensible"); + } else { + scr_jb_puts(&buffer, operation == 2 ? "Cannot delete property '" : "Cannot assign to read only property '"); + scr_jb_write(&buffer, key->data, key->len); + scr_jb_puts(&buffer, operation == 2 ? "' of " : "' of object '"); + scr_jb_write(&buffer, owner->data, owner->len); + if (operation != 2) scr_jb_puts(&buffer, "'"); + } + scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&buffer)); +} + /* A class instance's integrity state lives in its native layout, which only * statically typed calls can reach (the compiler adds the state to the * hierarchies they name). Through an untyped value the operation throws. */ @@ -2321,7 +2415,9 @@ static bool scr_dyn_class_integrity_fence(const ScrDyn *object, const char *oper ScrJsonBuf buffer; scr_jb_init(&buffer); scr_jb_puts(&buffer, operation); - scr_jb_puts(&buffer, " of a class instance held in an untyped value is not supported yet"); + scr_jb_puts(&buffer, scr_dyn_typed_record(object) ? " of an object held in an untyped value is not supported yet" + : scr_dyn_typed_array(object) ? " of an array held in an untyped value is not supported yet" + : " of a class instance held in an untyped value is not supported yet"); ScrStr *message = scr_jb_finish(&buffer); scr_throw_error_msg_code(SCR_ERR_ERROR, message->data, message->len, "SC2020"); scr_str_release(message); @@ -2366,9 +2462,18 @@ static ScrDyn *scr_dyn_fn_existing_table(const ScrDyn *function) { return owner->props ? (ScrDyn *)scr_box_get_ref(owner->props) : NULL; } +ScrDyn *scr_dyn_function_properties_existing(const ScrDyn *function) { + return scr_dyn_fn_existing_table(function); +} + ScrDyn *scr_dyn_prevent_extensions(ScrDyn *object) { if (scr_dyn_class_reflection_fence(object) || scr_dyn_error_prototype_fence(object)) return NULL; if (object->kind == SCR_DYN_PROXY) { scr_dyn_proxy_unsupported("Object.preventExtensions"); return NULL; } + ScrMap *collection = scr_dyn_native_collection(object); + if (collection) { + scr_map_prevent_extensions(collection); + return scr_dyn_retain(object); + } if (scr_dyn_class_integrity_fence(object, "Object.preventExtensions")) return NULL; ScrDyn *table = scr_dyn_fn_integrity_table(object); if (table) { @@ -2388,6 +2493,8 @@ bool scr_dyn_is_extensible(ScrDyn *object) { if (object->kind == SCR_DYN_PROXY) { scr_dyn_proxy_unsupported("Object.isExtensible"); return false; } bool answer; if (scr_dyn_typed_ref_view_answer(object, scr_dyn_is_extensible_view, &answer)) return answer; + ScrMap *collection = scr_dyn_native_collection(object); + if (collection) return scr_map_is_extensible(collection); if (scr_dyn_plain_fn(object)) { ScrDyn *table = scr_dyn_fn_existing_table(object); answer = !table || !table->non_extensible; @@ -2400,6 +2507,11 @@ bool scr_dyn_is_extensible(ScrDyn *object) { ScrDyn *scr_dyn_seal(ScrDyn *object) { if (scr_dyn_class_reflection_fence(object) || scr_dyn_error_prototype_fence(object)) return NULL; if (object->kind == SCR_DYN_PROXY) { scr_dyn_proxy_unsupported("Object.seal"); return NULL; } + ScrMap *collection = scr_dyn_native_collection(object); + if (collection) { + scr_map_prevent_extensions(collection); + return scr_dyn_retain(object); + } if (scr_dyn_class_integrity_fence(object, "Object.seal")) return NULL; if (object->symbol_properties) scr_dyn_release(scr_dyn_seal(object->symbol_properties)); ScrDyn *table = scr_dyn_fn_integrity_table(object); @@ -2424,6 +2536,8 @@ bool scr_dyn_is_sealed(const ScrDyn *object) { if (object->kind == SCR_DYN_PROXY) { scr_dyn_proxy_unsupported("Object.isSealed"); return false; } bool answer; if (scr_dyn_typed_ref_view_answer((ScrDyn *)object, scr_dyn_is_sealed, &answer)) return answer; + ScrMap *collection = scr_dyn_native_collection(object); + if (collection) return !scr_map_is_extensible(collection); if (object->symbol_properties && !scr_dyn_is_sealed(object->symbol_properties)) return false; if (scr_dyn_plain_fn(object)) { ScrDyn *table = scr_dyn_fn_existing_table(object); @@ -3191,6 +3305,45 @@ void scr_record_source_note(void *rec, const ScrDyn *src, const char *const *nam scr_record_source_put(&probe); } +void scr_record_source_spread(void *dst, const void *src, const char *const *names, + const size_t *lens, size_t count) { + if (!scr_record_source_count || !dst || !src) return; + ScrRecordSource *found = scr_record_source_find(src); + if (!found) return; + ScrRecordSource probe = { dst, NULL, (const char *)names, (const char *)lens, + sizeof *names, sizeof *lens, count }; + /* Nothing to keep when the declared keys come in the table's order and + * the keys src lacks would follow them in it anyway. */ + const ScrDyn *from = found->view; + bool keep = false; + size_t next = 0; + for (size_t i = 0; i < from->v.obj.len && !keep; i++) { + const ScrDynEntry *entry = &from->v.obj.entries[i]; + size_t index = scr_record_source_index(&probe, entry->key, entry->key_len); + if (index == count) continue; + keep = index != next; + next++; + } + if (!keep) return; + ScrDyn *view = scr_dyn_new_obj(); + for (size_t i = 0; i < from->v.obj.len; i++) { + const ScrDynEntry *entry = &from->v.obj.entries[i]; + if (scr_record_source_declared(&probe, entry->key, entry->key_len)) + scr_dyn_obj_set(view, entry->key, entry->key_len, scr_dyn_retain(scr_dyn_undefined())); + } + /* The keys src lacks follow in the table's order. The view lists them + * too, so an integer key among them comes first in its own-key order, as + * the literal creates it. */ + for (size_t i = 0; i < count; i++) { + if (scr_dyn_obj_get(view, names[i], lens[i])) continue; + scr_dyn_obj_set(view, names[i], lens[i], scr_dyn_retain(scr_dyn_undefined())); + } + ScrRecordSource *old = scr_record_source_find(dst); + if (old) scr_record_source_drop(old); + probe.view = view; + scr_record_source_put(&probe); +} + void scr_record_source_move(void *from, void *to) { if (!scr_record_source_count) return; ScrRecordSource *old = scr_record_source_find(to); @@ -3649,12 +3802,14 @@ ScrDyn *scr_dyn_typed_ref_materialize(const ScrDyn *d) { fresh->rc = fresh_rc; scr_dyn_release(fresh); } + if (scr_dyn_typed_ref_frozen(capsule)) scr_dyn_record_view_freeze(capsule->v.typed_ref.materialized); return scr_dyn_retain(capsule->v.typed_ref.materialized); } void scr_dyn_typed_ref_commit(ScrDyn *d) { + /* A frozen record's or array's view cannot have changed its contents. */ if (d && d->kind == SCR_DYN_TYPED_REF && d->v.typed_ref.commit && - d->v.typed_ref.materialized) { + d->v.typed_ref.materialized && !scr_dyn_typed_ref_frozen(d)) { d->v.typed_ref.commit(d->v.typed_ref.ptr, d->v.typed_ref.materialized); } @@ -3790,6 +3945,12 @@ static bool scr_dyn_name_in_strs(const ScrStr *key, ScrStr *const *names, size_t return false; } +ScrStr *scr_dyn_typed_ref_class_tag(const ScrDyn *d) { + if (!d || d->kind != SCR_DYN_TYPED_REF || d->prototype) return NULL; + const ScrDynTypedClassOps *ops = scr_typed_ref_class_ops(d); + return ops && ops->string_tag ? ops->string_tag(d->v.typed_ref.ptr) : NULL; +} + bool scr_dyn_typed_ref_class_has(const ScrDyn *d, const ScrStr *key) { if (!d || d->kind != SCR_DYN_TYPED_REF || d->prototype) return false; const ScrDynTypedClassOps *ops = scr_typed_ref_class_ops(d); @@ -3878,6 +4039,9 @@ ScrDyn *scr_dyn_index_get(const ScrDyn *d, double index) { return scr_dyn_typed_ref_element(d, i); } +static ScrStr *scr_dyn_typed_ref_conversion_tag(const ScrDyn *d); +static ScrStr *scr_dyn_tag_string(const ScrStr *tag); + ScrDyn *scr_dyn_typed_ref_string_view(const ScrDyn *d) { /* An instance of a class declared `extends Date` converts through its * toString, declared or inherited. With an exception pending the view is @@ -3891,6 +4055,16 @@ ScrDyn *scr_dyn_typed_ref_string_view(const ScrDyn *d) { scr_str_release(empty); return out; } + /* A class instance converting through Object.prototype.toString shows + * its Symbol.toStringTag getter's string. */ + ScrStr *class_tag = scr_dyn_typed_ref_conversion_tag(d); + if (class_tag || scr_exc_pending()) { + ScrStr *text = class_tag ? scr_dyn_tag_string(class_tag) : scr_str_new("", 0); + scr_str_release(class_tag); + ScrDyn *out = scr_dyn_new_str(text); + scr_str_release(text); + return out; + } ScrDyn *view = scr_dyn_typed_ref_materialize(d); const char *tag = scr_event_ref_hooks ? scr_event_ref_hooks->string_tag(d) : NULL; if (!tag || view->kind != SCR_DYN_OBJ || scr_dyn_property_owner(view, "toString", 8)) return view; @@ -3961,6 +4135,8 @@ static bool scr_typed_ref_array_has_own(const ScrDyn *d, const ScrStr *key) { static bool scr_typed_ref_array_set(ScrDyn *d, const ScrStr *key, const ScrDyn *value) { const ScrDynTypedArrayOps *ops = scr_dyn_typed_ref_array_ops(d); if (!ops || !ops->set) return false; + /* A frozen array's view refuses the write. */ + if (scr_arr_frozen(d->v.typed_ref.ptr)) return false; /* Numeric properties live in the native array's property table, which * holds values of the element type. */ double number; @@ -4114,6 +4290,212 @@ static ScrDyn *scr_bytes_ctor_call(ScrClosure *closure, ScrDyn *const *args, siz scr_trap("scriptc: invalid typed array constructor\n"); } static ScrDyn *scr_dyn_fn_properties(const ScrDyn *function); + +/* The element kind a typed array constructor value makes, or -1. */ +static int scr_bytes_ctor_elem(const ScrDyn *callee) { + for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) + if (callee->kind == SCR_DYN_FUNC && scr_bytes_ctors[i] && callee->v.fn.clo == scr_bytes_ctors[i]->v.fn.clo) return (int)i; + return -1; +} + +ScrDyn *scr_dyn_iterator_method(const ScrDyn *value) { + if (!scr_iterator_symbol || value->kind != SCR_DYN_OBJ) return NULL; + ScrDyn *method = scr_dyn_symbol_key_get(value, scr_iterator_symbol, true); + if (method && (method->kind == SCR_DYN_UNDEF || method->kind == SCR_DYN_NULL)) { + scr_dyn_release(method); + return NULL; + } + return method; +} + +ScrDyn *scr_dyn_iterate_with(const ScrDyn *source, ScrDyn *method) { + if (!scr_dyn_is_callable(method)) { + static const char message[] = "Symbol.iterator is not a function"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + return NULL; + } + scr_dyn_this_push_dyn(source); + ScrDyn *iterator = scr_dyn_call(method, NULL, 0, "[Symbol.iterator]"); + scr_dyn_this_pop(); + if (!iterator) return NULL; + if (!scr_iterator_object(iterator)) { + scr_dyn_release(iterator); + static const char message[] = "Result of the Symbol.iterator method is not an object"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + return NULL; + } + /* A generator or native iterator drains through its own iteration. */ + if (scr_builtin_iterable(iterator)) { + ScrDyn *drained = scr_dyn_destructure_rest(iterator, NULL); + scr_dyn_release(iterator); + return drained; + } + ScrDyn *next = scr_dyn_iterator_result_get(iterator, "next", 4); + ScrDyn *values = next ? scr_dyn_new_arr() : NULL; + while (values) { + scr_dyn_this_push_dyn(iterator); + ScrDyn *result = scr_dyn_call(next, NULL, 0, "next"); + scr_dyn_this_pop(); + ScrDyn *checked = result ? scr_dyn_iterator_result(result) : NULL; + scr_dyn_release(result); + if (!checked) { scr_dyn_release(values); values = NULL; break; } + ScrDyn *done = scr_dyn_iterator_result_get(checked, "done", 4); + bool finished = done && scr_dyn_truthy(done); + scr_dyn_release(done); + ScrDyn *value = !scr_exc_pending() && !finished ? scr_dyn_iterator_result_get(checked, "value", 5) : NULL; + scr_dyn_release(checked); + if (scr_exc_pending()) { scr_dyn_release(value); scr_dyn_release(values); values = NULL; break; } + if (finished) break; + scr_dyn_arr_push(values, value); + } + scr_dyn_release(next); + scr_dyn_release(iterator); + return values; +} + +/* The values of `source` when it is iterable (+1): a builtin iterable's + * own iteration, or an object's Symbol.iterator method, read once. NULL + * for an array-like source, or with the exception pending. */ +static ScrDyn *scr_dyn_from_iterated(const ScrDyn *source, bool *iterable) { + *iterable = true; + if (scr_builtin_iterable(source)) return scr_dyn_destructure_rest(source, NULL); + ScrDyn *method = scr_dyn_iterator_method(source); + if (!method) { *iterable = false; return NULL; } + ScrDyn *values = scr_dyn_iterate_with(source, method); + scr_dyn_release(method); + return values; +} + +/* TypedArrayCreate(C, [n]) for TypedArray.from and TypedArray.of: a typed + * array constructor makes one directly; any other constructor is called + * and must answer a typed array of at least n elements. */ +static ScrDyn *scr_bytes_static_create(ScrDyn *constructor, size_t n) { + int elem = scr_bytes_ctor_elem(constructor); + if (elem >= 0) { + ScrBytes *out = scr_bytes_new((ScrBytesElem)elem, (double)n); + ScrDyn *result = scr_dyn_new_bytes(out); + scr_bytes_release(out); + return result; + } + if (!scr_dyn_is_callable(constructor)) { + static const char message[] = "this is not a constructor"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + return NULL; + } + ScrDyn *args = scr_dyn_new_arr(); + scr_dyn_arr_push(args, scr_dyn_new_num((double)n)); + ScrStr *what = scr_str_new("TypedArray species", 18); + ScrDyn *result = scr_dyn_construct(constructor, args, what); + scr_str_release(what); + scr_dyn_release(args); + if (!result) return NULL; + const char *problem = result->kind != SCR_DYN_BYTES || result->v.bytes->is_data_view + ? "this is not a typed array." + : result->v.bytes->len < n ? "Derived TypedArray constructor created an array which was too small" : NULL; + if (problem) { + scr_dyn_release(result); + scr_throw_error_msg(SCR_ERR_TYPE, problem, strlen(problem)); + return NULL; + } + return result; +} + +/* target[k] = value, converting with ToNumber as a typed array [[Set]] does. */ +static bool scr_bytes_static_set(ScrDyn *target, size_t k, const ScrDyn *value) { + double v; + if (!scr_dyn_number_coerce_js(value, &v)) return false; + if (k < target->v.bytes->len) scr_bytes_set(target->v.bytes, (double)k, v); + return true; +} + +/* mapfn(value, k) with thisArg, or the value itself (+1). */ +static ScrDyn *scr_bytes_static_map(ScrDyn *mapfn, ScrDyn *this_arg, ScrDyn *value, size_t k) { + if (!mapfn) return scr_dyn_retain(value); + ScrDyn *index = scr_dyn_new_num((double)k); + ScrDyn *cbargs[2] = { value, index }; + scr_dyn_this_push_dyn(this_arg); + ScrDyn *mapped = scr_dyn_call(mapfn, cbargs, 2, "mapfn"); + scr_dyn_this_pop(); + scr_dyn_release(index); + return mapped; +} + +/* TypedArray.from(source, mapfn, thisArg): an iterable source is drained + * first, then the target is created; an array-like source is read element + * by element after the target exists. */ +static ScrDyn *scr_bytes_from_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + ScrDyn *constructor = scr_dyn_this_get(); + ScrDyn *result = NULL, *values = NULL; + ScrDyn *source = argc ? args[0] : scr_dyn_undefined(); + ScrDyn *mapfn = argc > 1 && args[1]->kind != SCR_DYN_UNDEF ? args[1] : NULL; + ScrDyn *this_arg = argc > 2 ? args[2] : scr_dyn_undefined(); + if (scr_bytes_ctor_elem(constructor) < 0 && !scr_dyn_is_callable(constructor)) { + static const char message[] = "this is not a constructor"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + goto done; + } + if (mapfn && !scr_dyn_is_callable(mapfn)) { + static const char message[] = "TypedArray.from: the mapping function is not callable"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + goto done; + } + if (source->kind == SCR_DYN_UNDEF || source->kind == SCR_DYN_NULL) { + static const char message[] = "Cannot convert undefined or null to object"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + goto done; + } + bool iterable; + values = scr_dyn_from_iterated(source, &iterable); + if (iterable) { + if (!values) goto done; + size_t n = values->v.arr.len; + result = scr_bytes_static_create(constructor, n); + for (size_t k = 0; result && k < n; k++) { + ScrDyn *mapped = scr_bytes_static_map(mapfn, this_arg, values->v.arr.items[k], k); + bool ok = mapped && scr_bytes_static_set(result, k, mapped); + scr_dyn_release(mapped); + if (!ok) { scr_dyn_release(result); result = NULL; } + } + goto done; + } + if (scr_exc_pending()) goto done; + double n = 0; + if (source->kind == SCR_DYN_OBJ) { + ScrDyn *length = scr_dyn_obj_read(source, "length", 6); + bool ok = !scr_exc_pending() && (!length || scr_dyn_number_coerce_js(length, &n)); + scr_dyn_release(length); + if (!ok) goto done; + n = isnan(n) || n < 0 ? 0 : n > 9007199254740991.0 ? 9007199254740991.0 : trunc(n); + } + result = scr_bytes_static_create(constructor, (size_t)n); + for (size_t k = 0; result && k < (size_t)n; k++) { + char key[32]; + int size = snprintf(key, sizeof key, "%zu", k); + ScrDyn *item = scr_dyn_obj_read(source, key, (size_t)size); + ScrDyn *mapped = scr_exc_pending() ? NULL : scr_bytes_static_map(mapfn, this_arg, item ? item : scr_dyn_undefined(), k); + bool ok = mapped && scr_bytes_static_set(result, k, mapped); + scr_dyn_release(item); + scr_dyn_release(mapped); + if (!ok) { scr_dyn_release(result); result = NULL; } + } +done: + scr_dyn_release(values); + scr_dyn_release(constructor); + return result; +} + +/* TypedArray.of(...items). */ +static ScrDyn *scr_bytes_of_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + (void)closure; + ScrDyn *constructor = scr_dyn_this_get(); + ScrDyn *result = scr_bytes_static_create(constructor, argc); + for (size_t k = 0; result && k < argc; k++) + if (!scr_bytes_static_set(result, k, args[k])) { scr_dyn_release(result); result = NULL; } + scr_dyn_release(constructor); + return result; +} + static ScrDyn *scr_bytes_constructor_for(ScrBytesElem elem) { if (!scr_bytes_ctors[elem]) { bool initialized = false; @@ -4125,6 +4507,14 @@ static ScrDyn *scr_bytes_constructor_for(ScrBytesElem elem) { scr_dyn_obj_set(properties, "BYTES_PER_ELEMENT", 17, scr_dyn_new_num((double)scr_bytes_elem_size(elem))); ScrDynEntry *entry = &properties->v.obj.entries[properties->v.obj.len - 1]; entry->writable = entry->enumerable = entry->configurable = false; + /* from and of live on %TypedArray%, which is not modeled: each + * constructor carries them as non-enumerable properties. */ + scr_dyn_obj_set(properties, "from", 4, + scr_dyn_new_func(scr_closure_new(NULL, 0), scr_bytes_from_call, 1, "native:TypedArray", "from")); + properties->v.obj.entries[properties->v.obj.len - 1].enumerable = false; + scr_dyn_obj_set(properties, "of", 2, + scr_dyn_new_func(scr_closure_new(NULL, 0), scr_bytes_of_call, 0, "native:TypedArray", "of")); + properties->v.obj.entries[properties->v.obj.len - 1].enumerable = false; scr_dyn_release(properties); } return scr_dyn_retain(scr_bytes_ctors[elem]); @@ -4167,6 +4557,7 @@ static ScrDyn *scr_array_ctor_call(ScrClosure *closure, ScrDyn *const *args, siz ScrDyn *scr_dyn_array_constructor(void) { if (!scr_array_ctor) { scr_array_ctor = scr_dyn_new_func(scr_closure_new(NULL, 0), scr_array_ctor_call, 1, "native:Array", "Array"); + scr_dyn_fn_seed_prototype(scr_array_ctor, scr_dyn_array_prototype_base()); scr_atexit(scr_array_ctor_cleanup); } return scr_dyn_retain(scr_array_ctor); @@ -4269,6 +4660,9 @@ bool scr_bytes_instanceof_at(const ScrDyn *value, const ScrDyn *callee, const Sc } if (scr_array_buffer_ctor && callee->v.fn.clo == scr_array_buffer_ctor->v.fn.clo) return scr_array_buffer_is(value); if (scr_array_ctor && callee->v.fn.clo == scr_array_ctor->v.fn.clo) return value->kind == SCR_DYN_ARR || scr_dyn_ref_is_array(value); + /* The Buffer function value: Buffer instances, not other Uint8Arrays. */ + if (callee->v.fn.sig && strcmp(callee->v.fn.sig, "native:Buffer") == 0) + return value->kind == SCR_DYN_BYTES && value->buffer; for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) { if (scr_bytes_ctors[i] && callee->v.fn.clo == scr_bytes_ctors[i]->v.fn.clo) return scr_dyn_typed_array_is(value, (int)i); @@ -4331,6 +4725,7 @@ bool scr_bytes_instanceof(const ScrDyn *value, const ScrDyn *callee) { return scr_bytes_instanceof_at(value, callee, NULL); } SCR_TL bool (*scr_dyn_global_construct_fn)(const ScrDyn *callee, const ScrDyn *args, ScrDyn **result) = NULL; +ScrDyn *(*scr_bytes_buffer_constructor_fn)(void) = NULL; ScrDyn *scr_bytes_construct(const ScrDyn *callee, const ScrDyn *args, const ScrStr *what) { if (callee->kind == SCR_DYN_FUNC && scr_array_ctor && callee->v.fn.clo == scr_array_ctor->v.fn.clo) @@ -4351,6 +4746,14 @@ ScrDyn *scr_bytes_construct(const ScrDyn *callee, const ScrDyn *args, const ScrS ScrDyn *constructed = NULL; if (scr_dyn_global_construct_fn && scr_dyn_global_construct_fn(callee, args, &constructed)) return constructed; + /* The Buffer function value (scr_builtin_values.c): Node's construction + * reports DEP0005 by the caller's location, which a stored value does + * not know. */ + if (callee->kind == SCR_DYN_FUNC && callee->v.fn.sig && strcmp(callee->v.fn.sig, "native:Buffer") == 0) { + static const char message[] = "Calling or constructing Buffer through a stored Buffer value is not supported yet"; + scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); + return NULL; + } ScrJsonBuf message; scr_jb_init(&message); scr_jb_write(&message, what->data, what->len); @@ -4359,6 +4762,399 @@ ScrDyn *scr_bytes_construct(const ScrDyn *callee, const ScrDyn *args, const ScrS return NULL; } +static bool scr_dyn_bytes_named(const char *method, const char *name) { + return strcmp(method, name) == 0; +} + +/* An optional index argument: ToIntegerOrInfinity, or `fallback` when + * absent or undefined. */ +static double scr_dyn_bytes_index_arg(ScrDyn *const *args, size_t argc, size_t index, double fallback) { + if (index >= argc || args[index]->kind == SCR_DYN_UNDEF) return fallback; + double value; + if (!scr_dyn_number_coerce_js(args[index], &value)) return 0; + return isnan(value) ? 0 : trunc(value); +} + +/* The callable-callback gate: " is not a function". */ +static bool scr_dyn_bytes_cb_check(ScrDyn *const *args, size_t argc) { + ScrDyn *cb = argc > 0 ? args[0] : scr_dyn_undefined(); + if (scr_dyn_is_callable(cb)) return true; + bool primitive = cb->kind == SCR_DYN_NUM || cb->kind == SCR_DYN_STR || cb->kind == SCR_DYN_BOOL || + cb->kind == SCR_DYN_UNDEF || cb->kind == SCR_DYN_NULL || cb->kind == SCR_DYN_BIGINT; + ScrStr *text = primitive ? scr_dyn_string_coerce_js(cb) : scr_str_new("#", 9); + if (!text) return false; + ScrJsonBuf b; + scr_jb_init(&b); + scr_jb_write(&b, text->data, text->len); + scr_jb_puts(&b, " is not a function"); + scr_str_release(text); + scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b)); + return false; +} + +/* ── %TypedArray%.prototype methods over a checked typed array ────────── + * One dispatch for scr_dyn_invoke's typed array receivers and for the + * method values their reads give, in this always-linked unit. + * Elements are read live at each step: a callback can write the array it + * walks. Results of map, filter and the copying methods take the + * receiver's element kind (and a Buffer's flavor). */ + +static ScrDyn *dyn_bytes_wrap(const ScrDyn *recv, ScrBytes *out) { + ScrDyn *d = recv->buffer ? scr_dyn_new_buffer(out) : scr_dyn_new_bytes(out); + scr_bytes_release(out); + return d; +} + +/* The element at k, or undefined past the view's current end. */ +static ScrDyn *dyn_bytes_item(const ScrBytes *b, size_t k) { + return k < b->len ? scr_dyn_new_num(scr_bytes_get(b, (double)k)) : scr_dyn_retain(scr_dyn_undefined()); +} + +/* callback(value, index, array) with thisArg as `this`. */ +static ScrDyn *dyn_bytes_call(ScrDyn *cb, ScrDyn *this_arg, ScrDyn *const *cbargs, size_t n) { + scr_dyn_this_push_dyn(this_arg); + ScrDyn *r = scr_dyn_call(cb, (ScrDyn **)cbargs, n, "callback"); + scr_dyn_this_pop(); + return r; +} + +/* ToIntegerOrInfinity of a present argument; `fallback` when absent. */ +static bool dyn_bytes_integer_arg(ScrDyn *const *args, size_t argc, size_t i, double fallback, double *out) { + if (i >= argc) { *out = fallback; return true; } + double v; + if (!scr_dyn_number_coerce_js(args[i], &v)) return false; + *out = isnan(v) ? 0 : trunc(v); + return true; +} + +/* SortCompare: the comparator's ToNumber answer (NaN as 0), or the + * numeric order with -0 before +0 and NaN last. */ +static bool dyn_bytes_less_eq(ScrDyn *cmp, double x, double y, bool *ok) { + if (cmp->kind != SCR_DYN_UNDEF) { + ScrDyn *a = scr_dyn_new_num(x), *b = scr_dyn_new_num(y); + ScrDyn *cbargs[2] = { a, b }; + /* The comparator is called with an undefined `this`. */ + scr_dyn_this_push_dyn(scr_dyn_undefined()); + ScrDyn *r = scr_dyn_call(cmp, cbargs, 2, "comparefn"); + scr_dyn_this_pop(); + scr_dyn_release(a); + scr_dyn_release(b); + double v = 0; + if (!r || !scr_dyn_number_coerce_js(r, &v)) { scr_dyn_release(r); *ok = false; return true; } + scr_dyn_release(r); + return isnan(v) || v <= 0; + } + if (isnan(x)) return isnan(y); + if (isnan(y)) return true; + if (x < y) return true; + if (x > y) return false; + return signbit(x) || !signbit(y); +} + +/* A stable merge sort of values[0..n). False with the exception pending. */ +static bool dyn_bytes_sort_values(double *values, size_t n, ScrDyn *cmp) { + if (n < 2) return true; + double *tmp = malloc(n * sizeof *tmp); + if (!tmp) scr_trap("scriptc: out of memory\n"); + bool ok = true; + for (size_t width = 1; width < n && ok; width *= 2) { + for (size_t lo = 0; lo < n && ok; lo += 2 * width) { + size_t mid = lo + width < n ? lo + width : n, hi = lo + 2 * width < n ? lo + 2 * width : n; + size_t i = lo, j = mid, k = lo; + while (i < mid && j < hi && ok) tmp[k++] = dyn_bytes_less_eq(cmp, values[i], values[j], &ok) ? values[i++] : values[j++]; + while (i < mid) tmp[k++] = values[i++]; + while (j < hi) tmp[k++] = values[j++]; + } + if (ok) memcpy(values, tmp, n * sizeof *values); + } + free(tmp); + return ok; +} + +static bool dyn_bytes_comparefn_check(ScrDyn *const *args, size_t argc) { + if (argc == 0 || args[0]->kind == SCR_DYN_UNDEF || scr_dyn_is_callable(args[0])) return true; + static const char message[] = "The comparison function must be either a function or undefined"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + return false; +} + +static double *dyn_bytes_values(const ScrBytes *b, size_t n) { + double *values = malloc((n ? n : 1) * sizeof *values); + if (!values) scr_trap("scriptc: out of memory\n"); + for (size_t i = 0; i < n; i++) values[i] = scr_bytes_get(b, (double)i); + return values; +} + +ScrDyn *scr_dyn_bytes_method(ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc, bool *handled) { + ScrBytes *bytes = recv->v.bytes; + size_t len = bytes->len; + *handled = true; + size_t blen = bytes->len; + if (scr_dyn_bytes_named(method, "at")) { + double iD = scr_dyn_bytes_index_arg(args, argc, 0, 0); + if (scr_exc_pending()) return NULL; + double idx = iD < 0 ? (double)blen + iD : iD; + if (idx < 0 || idx >= (double)blen) return scr_dyn_retain(scr_dyn_undefined()); + return scr_dyn_new_num(scr_bytes_get(bytes, idx)); + } + if (scr_dyn_bytes_named(method, "slice") || scr_dyn_bytes_named(method, "subarray")) { + /* subarray and Buffer.slice alias their source. TypedArray.slice + * owns an independent copy; all keep the receiver's Buffer flavor. */ + double startD = scr_dyn_bytes_index_arg(args, argc, 0, 0); + if (scr_exc_pending()) return NULL; + double endD = scr_dyn_bytes_index_arg(args, argc, 1, (double)blen); + if (scr_exc_pending()) return NULL; + ScrBytes *out = recv->buffer || scr_dyn_bytes_named(method, "subarray") + ? scr_bytes_subarray(bytes, startD, endD) : scr_bytes_slice(bytes, startD, endD); + ScrDyn *d = recv->buffer ? scr_dyn_new_buffer(out) : scr_dyn_new_bytes(out); + scr_bytes_release(out); + return d; + } + if (scr_dyn_bytes_named(method, "set")) { + double offset = scr_dyn_bytes_index_arg(args, argc, 1, 0); + if (scr_exc_pending()) return NULL; + scr_bytes_set_from_dyn(bytes, argc ? args[0] : scr_dyn_undefined(), offset); + return scr_exc_pending() ? NULL : scr_dyn_retain(scr_dyn_undefined()); + } + if (scr_dyn_bytes_named(method, "copyWithin")) { + double target = scr_dyn_bytes_index_arg(args, argc, 0, 0); + if (scr_exc_pending()) return NULL; + double start = scr_dyn_bytes_index_arg(args, argc, 1, 0); + if (scr_exc_pending()) return NULL; + double end = scr_dyn_bytes_index_arg(args, argc, 2, (double)blen); + if (scr_exc_pending()) return NULL; + scr_bytes_release(scr_bytes_copy_within(bytes, target, start, end)); + return scr_dyn_retain(recv); + } + if (scr_dyn_bytes_named(method, "fill") && !recv->buffer) { + double value; + if (!scr_dyn_number_coerce_js(argc ? args[0] : scr_dyn_undefined(), &value)) return NULL; + double start = scr_dyn_bytes_index_arg(args, argc, 1, 0); + if (scr_exc_pending()) return NULL; + double end = scr_dyn_bytes_index_arg(args, argc, 2, (double)blen); + if (scr_exc_pending()) return NULL; + scr_bytes_release(scr_bytes_fill_elem(bytes, value, start, end)); + return scr_dyn_retain(recv); + } + if (scr_dyn_bytes_named(method, "join") || (scr_dyn_bytes_named(method, "toString") && !recv->buffer)) { + ScrStr *separator = scr_dyn_bytes_named(method, "join") && argc && args[0]->kind != SCR_DYN_UNDEF + ? scr_dyn_string_coerce_js(args[0]) : scr_str_new(",", 1); + if (scr_exc_pending()) { scr_str_release(separator); return NULL; } + ScrStr *joined = scr_bytes_join(bytes, separator); + scr_str_release(separator); + ScrDyn *out = scr_dyn_new_str(joined); + scr_str_release(joined); + return out; + } + if (scr_dyn_bytes_named(method, "keys") || scr_dyn_bytes_named(method, "values") || scr_dyn_bytes_named(method, "entries")) + return scr_dyn_native_handle_iterator(recv, scr_dyn_bytes_named(method, "keys") ? 1 : scr_dyn_bytes_named(method, "entries") ? 2 : 0); + if (scr_dyn_bytes_named(method, "indexOf") || scr_dyn_bytes_named(method, "lastIndexOf") || scr_dyn_bytes_named(method, "includes")) { + const bool includes = scr_dyn_bytes_named(method, "includes"), last = scr_dyn_bytes_named(method, "lastIndexOf"); + if (len == 0) return includes ? scr_dyn_new_bool(false) : scr_dyn_new_num(-1); + double n; + if (!dyn_bytes_integer_arg(args, argc, 1, last ? (double)len - 1 : 0, &n)) return NULL; + ScrDyn *needle = argc ? args[0] : scr_dyn_undefined(); + const bool number = needle->kind == SCR_DYN_NUM; + const double target = number ? needle->v.num : 0; + if (last) { + if (n == -INFINITY) return scr_dyn_new_num(-1); + double k = n >= 0 ? (n < (double)len - 1 ? n + 0.0 : (double)len - 1) : (double)len + n; + for (; number && k >= 0; k--) + if ((size_t)k < bytes->len && scr_bytes_get(bytes, k) == target) return scr_dyn_new_num(k); + return scr_dyn_new_num(-1); + } + if (n == INFINITY) return includes ? scr_dyn_new_bool(false) : scr_dyn_new_num(-1); + double k = n >= 0 ? n + 0.0 : ((double)len + n < 0 ? 0 : (double)len + n); + for (; k < (double)len; k++) { + /* An element past a shrunk view reads undefined, which includes finds. */ + if ((size_t)k >= bytes->len) { + if (includes && needle->kind == SCR_DYN_UNDEF) return scr_dyn_new_bool(true); + continue; + } + if (!number) continue; + double v = scr_bytes_get(bytes, k); + if (v == target || (includes && isnan(v) && isnan(target))) + return includes ? scr_dyn_new_bool(true) : scr_dyn_new_num(k); + } + return includes ? scr_dyn_new_bool(false) : scr_dyn_new_num(-1); + } + if (scr_dyn_bytes_named(method, "forEach") || scr_dyn_bytes_named(method, "map") || scr_dyn_bytes_named(method, "filter") || scr_dyn_bytes_named(method, "some") || + scr_dyn_bytes_named(method, "every") || scr_dyn_bytes_named(method, "find") || scr_dyn_bytes_named(method, "findIndex") || + scr_dyn_bytes_named(method, "findLast") || scr_dyn_bytes_named(method, "findLastIndex")) { + if (!scr_dyn_bytes_cb_check(args, argc)) return NULL; + ScrDyn *cb = args[0], *this_arg = argc > 1 ? args[1] : scr_dyn_undefined(); + const bool backward = scr_dyn_bytes_named(method, "findLast") || scr_dyn_bytes_named(method, "findLastIndex"); + ScrBytes *mapped = scr_dyn_bytes_named(method, "map") ? scr_bytes_new(bytes->elem, (double)len) : NULL; + double *kept = scr_dyn_bytes_named(method, "filter") ? malloc((len ? len : 1) * sizeof *kept) : NULL; + size_t nkept = 0; + for (size_t step = 0; step < len; step++) { + size_t k = backward ? len - 1 - step : step; + ScrDyn *item = dyn_bytes_item(bytes, k), *index = scr_dyn_new_num((double)k); + ScrDyn *cbargs[3] = { item, index, recv }; + ScrDyn *r = dyn_bytes_call(cb, this_arg, cbargs, 3); + scr_dyn_release(index); + if (!r) { + scr_dyn_release(item); + if (mapped) scr_bytes_release(mapped); + free(kept); + return NULL; + } + if (mapped) { + double v; + bool converted = scr_dyn_number_coerce_js(r, &v); + scr_dyn_release(r); + scr_dyn_release(item); + if (!converted) { scr_bytes_release(mapped); return NULL; } + if (k < mapped->len) scr_bytes_set(mapped, (double)k, v); + continue; + } + bool truthy = scr_dyn_truthy(r); + scr_dyn_release(r); + if (kept) { + if (truthy) kept[nkept++] = item->kind == SCR_DYN_NUM ? item->v.num : NAN; + } else if (truthy && (scr_dyn_bytes_named(method, "find") || scr_dyn_bytes_named(method, "findLast"))) { + return item; + } else if (truthy && (scr_dyn_bytes_named(method, "findIndex") || scr_dyn_bytes_named(method, "findLastIndex"))) { + scr_dyn_release(item); + return scr_dyn_new_num((double)k); + } else if (truthy && scr_dyn_bytes_named(method, "some")) { + scr_dyn_release(item); + return scr_dyn_new_bool(true); + } else if (!truthy && scr_dyn_bytes_named(method, "every")) { + scr_dyn_release(item); + return scr_dyn_new_bool(false); + } + scr_dyn_release(item); + } + if (mapped) return dyn_bytes_wrap(recv, mapped); + if (kept) { + ScrBytes *out = scr_bytes_new(bytes->elem, (double)nkept); + for (size_t i = 0; i < nkept; i++) scr_bytes_set(out, (double)i, kept[i]); + free(kept); + return dyn_bytes_wrap(recv, out); + } + if (scr_dyn_bytes_named(method, "some")) return scr_dyn_new_bool(false); + if (scr_dyn_bytes_named(method, "every")) return scr_dyn_new_bool(true); + if (scr_dyn_bytes_named(method, "findIndex") || scr_dyn_bytes_named(method, "findLastIndex")) return scr_dyn_new_num(-1); + return scr_dyn_retain(scr_dyn_undefined()); /* forEach, find, findLast */ + } + if (scr_dyn_bytes_named(method, "reduce") || scr_dyn_bytes_named(method, "reduceRight")) { + if (!scr_dyn_bytes_cb_check(args, argc)) return NULL; + const bool backward = scr_dyn_bytes_named(method, "reduceRight"); + size_t step = 0; + ScrDyn *acc; + if (argc > 1) acc = scr_dyn_retain(args[1]); + else if (len == 0) { + static const char message[] = "Reduce of empty array with no initial value"; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + return NULL; + } else acc = dyn_bytes_item(bytes, backward ? len - 1 : 0), step = 1; + for (; step < len; step++) { + size_t k = backward ? len - 1 - step : step; + ScrDyn *item = dyn_bytes_item(bytes, k), *index = scr_dyn_new_num((double)k); + ScrDyn *cbargs[4] = { acc, item, index, recv }; + /* The reducer is called with an undefined `this`. */ + scr_dyn_this_push_dyn(scr_dyn_undefined()); + ScrDyn *next = scr_dyn_call(args[0], cbargs, 4, "callback"); + scr_dyn_this_pop(); + scr_dyn_release(index); + scr_dyn_release(item); + scr_dyn_release(acc); + if (!next) return NULL; + acc = next; + } + return acc; + } + if (scr_dyn_bytes_named(method, "reverse")) { + for (size_t lo = 0, hi = len; lo + 1 < hi; lo++, hi--) { + double a = scr_bytes_get(bytes, (double)lo), b = scr_bytes_get(bytes, (double)(hi - 1)); + scr_bytes_set(bytes, (double)lo, b); + scr_bytes_set(bytes, (double)(hi - 1), a); + } + return scr_dyn_retain(recv); + } + if (scr_dyn_bytes_named(method, "sort") || scr_dyn_bytes_named(method, "toSorted")) { + if (!dyn_bytes_comparefn_check(args, argc)) return NULL; + double *values = dyn_bytes_values(bytes, len); + if (!dyn_bytes_sort_values(values, len, argc ? args[0] : scr_dyn_undefined())) { free(values); return NULL; } + ScrBytes *target = scr_dyn_bytes_named(method, "sort") ? bytes : scr_bytes_new(bytes->elem, (double)len); + for (size_t i = 0; i < len && i < target->len; i++) scr_bytes_set(target, (double)i, values[i]); + free(values); + return scr_dyn_bytes_named(method, "sort") ? scr_dyn_retain(recv) : dyn_bytes_wrap(recv, target); + } + if (scr_dyn_bytes_named(method, "toReversed")) { + ScrBytes *out = scr_bytes_new(bytes->elem, (double)len); + for (size_t i = 0; i < len; i++) scr_bytes_set(out, (double)i, scr_bytes_get(bytes, (double)(len - 1 - i))); + return dyn_bytes_wrap(recv, out); + } + if (scr_dyn_bytes_named(method, "with")) { + double rel, value; + if (!dyn_bytes_integer_arg(args, argc, 0, 0, &rel)) return NULL; + double actual = rel >= 0 ? rel : (double)len + rel; + if (!scr_dyn_number_coerce_js(argc > 1 ? args[1] : scr_dyn_undefined(), &value)) return NULL; + if (!(actual >= 0 && actual < (double)bytes->len)) { + static const char message[] = "Invalid typed array index"; + scr_throw_error_msg(SCR_ERR_RANGE, message, sizeof message - 1); + return NULL; + } + ScrBytes *out = scr_bytes_new(bytes->elem, (double)len); + for (size_t i = 0; i < len; i++) scr_bytes_set(out, (double)i, scr_bytes_get(bytes, (double)i)); + scr_bytes_set(out, actual, value); + return dyn_bytes_wrap(recv, out); + } + *handled = false; + return NULL; +} + +/* A %TypedArray%.prototype method read as a value: it calls the method on + * its `this`, which must be a typed array. */ +static ScrDyn *dyn_bytes_method_value_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) { + ScrStr *method = scr_box_get_ref(closure->caps[0]); + ScrDyn *receiver = scr_dyn_this_get(); + ScrDyn *result = NULL; + if (receiver->kind == SCR_DYN_BYTES && !receiver->v.bytes->is_data_view) { + bool handled; + result = scr_dyn_bytes_method(receiver, method->data, args, argc, &handled); + if (!handled) { + ScrJsonBuf b; + scr_jb_init(&b); + scr_jb_puts(&b, "'TypedArray.prototype."); + scr_jb_puts(&b, method->data); + scr_jb_puts(&b, "' through a method value is not supported yet"); + scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b)); + } + } else { + static const char message[] = "this is not a typed array."; + scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + } + scr_dyn_release(receiver); + scr_str_release(method); + return result; +} + +static SCR_TL ScrDyn *dyn_bytes_method_values; + +static void dyn_bytes_method_values_cleanup(void) { + scr_dyn_release(dyn_bytes_method_values); + dyn_bytes_method_values = NULL; +} + +static ScrDyn *scr_dyn_bytes_method_value(const ScrStr *name, size_t arity) { + if (!dyn_bytes_method_values) { + dyn_bytes_method_values = scr_dyn_new_obj_null_proto(); + scr_atexit(dyn_bytes_method_values_cleanup); + } + ScrDyn *value = scr_dyn_obj_get(dyn_bytes_method_values, name->data, name->len); + if (!value) { + ScrClosure *closure = scr_closure_new(NULL, 1); + closure->caps[0] = scr_box_new(SCR_BOX_STR); + scr_box_set_ref(closure->caps[0], scr_str_retain((ScrStr *)name)); + value = scr_dyn_new_func(closure, dyn_bytes_method_value_call, arity, "native:TypedArray.prototype", name->data); + scr_dyn_obj_set(dyn_bytes_method_values, name->data, name->len, value); /* the table owns it */ + } + return scr_dyn_retain(value); +} + /* Shared view metadata and indexed reads for both compiler backends. */ ScrDyn *scr_dyn_bytes_key_get(const ScrDyn *value, const ScrStr *key) { ScrBytes *bytes = value->v.bytes; @@ -4380,7 +5176,9 @@ ScrDyn *scr_dyn_bytes_key_get(const ScrDyn *value, const ScrStr *key) { return scr_dyn_new_num((double)scr_bytes_elem_size(bytes->elem)); if (key->len == 11 && memcmp(key->data, "constructor", 11) == 0) { if (!value->buffer) return scr_bytes_constructor_for(bytes->elem); - /* Buffer retains its separate builtin identity. */ + /* A program that reads the Buffer function's value answers it; + * otherwise Buffer keeps its separate builtin identity token. */ + if (scr_bytes_buffer_constructor_fn) return scr_bytes_buffer_constructor_fn(); char token[64]; int length = snprintf(token, sizeof token, "[builtin %s]", value->buffer ? "Buffer" : scr_bytes_elem_name(bytes->elem)); ScrStr *name = scr_str_new(token, (size_t)length); @@ -4397,6 +5195,18 @@ ScrDyn *scr_dyn_bytes_key_get(const ScrDyn *value, const ScrStr *key) { } if (digits && index < bytes->len) return scr_dyn_new_num(scr_bytes_get(bytes, (double)index)); } + /* %TypedArray%.prototype methods, with their lengths. */ + static const struct { const char *name; size_t arity; } methods[] = { + {"at", 1}, {"copyWithin", 2}, {"entries", 0}, {"every", 1}, {"fill", 1}, {"filter", 1}, + {"find", 1}, {"findIndex", 1}, {"findLast", 1}, {"findLastIndex", 1}, {"forEach", 1}, + {"includes", 1}, {"indexOf", 1}, {"join", 1}, {"keys", 0}, {"lastIndexOf", 1}, {"map", 1}, + {"reduce", 1}, {"reduceRight", 1}, {"reverse", 0}, {"set", 1}, {"slice", 2}, {"some", 1}, + {"sort", 1}, {"subarray", 2}, {"toLocaleString", 0}, {"toReversed", 0}, {"toSorted", 1}, + {"toString", 0}, {"values", 0}, {"with", 2}, + }; + for (size_t i = 0; i < sizeof methods / sizeof methods[0]; i++) + if (key->len == strlen(methods[i].name) && memcmp(key->data, methods[i].name, key->len) == 0) + return scr_dyn_bytes_method_value(key, methods[i].arity); return scr_dyn_retain(scr_dyn_undefined()); } @@ -5374,6 +6184,18 @@ static ScrDyn *scr_dyn_fn_properties(const ScrDyn *function) { return (ScrDyn *)scr_box_get_ref(owner->props); } +void scr_dyn_fn_seed_prototype(ScrDyn *function, ScrDyn *prototype) { + ScrDyn *table = scr_dyn_fn_properties(function); + scr_dyn_obj_set(table, "prototype", 9, prototype); + ScrDynEntry *entry = scr_dyn_find_entry(table, "prototype", 9); + entry->writable = false; + entry->enumerable = false; + entry->configurable = false; + scr_dyn_release(table); +} + +ScrDyn *scr_dyn_function_properties(const ScrDyn *function) { return scr_dyn_fn_properties(function); } + ScrDyn *scr_dyn_class_inherit(ScrDyn *constructor, ScrDyn *base) { if (constructor->kind != SCR_DYN_FUNC || !constructor->v.fn.class_obj || (base->kind != SCR_DYN_FUNC && base->kind != SCR_DYN_UNDEF)) { static const char message[] = "Class inheritance requires native constructors"; @@ -5719,6 +6541,23 @@ static void scr_native_iterator_release(void *ptr) { * next() calls still receive a fresh, independently mutable result object. */ static ScrDyn *scr_native_iterator_value(ScrNativeIterator *iterator, bool *done_out) { ScrDyn *source = iterator->source; + /* An own element of an untyped array, or its end: no getter can run, so + * the general path below is not needed. */ + if (source && source->kind == SCR_DYN_ARR && iterator->kind == 0 && !iterator->array_like && + iterator->selection == 0) { + if (iterator->index >= (double)source->v.arr.len) { + iterator->source = NULL; + scr_dyn_release(source); + *done_out = true; + return scr_dyn_retain(scr_dyn_undefined()); + } + if (scr_dyn_arr_has_index(source, (size_t)iterator->index)) { + size_t index = (size_t)iterator->index; + iterator->index++; + *done_out = false; + return scr_dyn_retain(source->v.arr.items[index]); + } + } /* An inherited indexed getter can advance this same iterator recursively. * Keep the source alive even if that nested call exhausts the iterator. */ if (source) scr_dyn_retain(source); @@ -5794,7 +6633,18 @@ static ScrDyn *scr_native_iterator_value(ScrNativeIterator *iterator, bool *done } } } else if (items->kind == SCR_DYN_BYTES) { - value = scr_dyn_new_num(scr_bytes_get(items->v.bytes, iterator->index++)); + /* keys(), values() and entries() of a typed array. */ + double index = iterator->index++; + if (iterator->selection == 1) value = scr_dyn_new_num(index); + else { + value = scr_dyn_new_num(scr_bytes_get(items->v.bytes, index)); + if (iterator->selection == 2) { + ScrDyn *entry = scr_dyn_new_arr(); + scr_dyn_arr_push(entry, scr_dyn_new_num(index)); + scr_dyn_arr_push(entry, value); + value = entry; + } + } } else { ScrStr *point = scr_str_cp_at(items->v.str, iterator->index); iterator->index += scr_str_utf16_len(point); @@ -6863,6 +7713,12 @@ ScrDyn *scr_dyn_iterator(const ScrDyn *source, const ScrStr *spell) { scr_dyn_release(method); return scr_dyn_not_iterable(source, spell); } + /* An array whose iterator method is the builtin values(): what calling + * it with the array as `this` returns, without the call. */ + if (source->kind == SCR_DYN_ARR && method == scr_iterator_functions[1]) { + scr_dyn_release(method); + return scr_native_iterator_new((ScrDyn *)source); + } ScrDyn *iterator = scr_iterator_call(method, source, "value[Symbol.iterator]"); scr_dyn_release(method); if (!iterator) return NULL; @@ -7497,11 +8353,66 @@ void scr_dyn_throw(ScrDyn *d) { scr_dyn_trace_v, scr_dyn_is_object(d)); } +static ScrStr *scr_dyn_tag_string(const ScrStr *tag) { + ScrJsonBuf b; + scr_jb_init(&b); + scr_jb_puts(&b, "[object "); + scr_jb_write(&b, tag->data, tag->len); + scr_jb_putc(&b, ']'); + return scr_jb_finish(&b); +} + +/* The Symbol.toStringTag getter's string of a class instance whose + * ToString is Object.prototype.toString (its class has no toString; the + * string hint never reaches valueOf, since that answers a string): +1, or + * NULL otherwise or with the getter's exception pending. */ +static ScrStr *scr_dyn_typed_ref_conversion_tag(const ScrDyn *d) { + ScrStr *to_string_key = scr_str_new("toString", 8); + bool own_conversion = scr_dyn_typed_ref_class_has(d, to_string_key); + scr_str_release(to_string_key); + return own_conversion ? NULL : scr_dyn_typed_ref_class_tag(d); +} + +bool scr_dyn_typed_ref_put_tag(ScrJsonBuf *b, const ScrDyn *d) { + ScrStr *tag = scr_dyn_typed_ref_conversion_tag(d); + if (!tag) return scr_exc_pending(); + scr_jb_puts(b, "[object "); + scr_jb_write(b, tag->data, tag->len); + scr_jb_putc(b, ']'); + scr_str_release(tag); + return true; +} + +/* Object.prototype.toString of an object whose own or inherited + * @@toStringTag is a string (+1), NULL otherwise or with a pending + * exception from its getter. */ +static ScrStr *scr_dyn_string_tag(const ScrDyn *d) { + ScrStr *getter_tag = scr_dyn_typed_ref_class_tag(d); + if (getter_tag) { + ScrStr *out = scr_dyn_tag_string(getter_tag); + scr_str_release(getter_tag); + return out; + } + if (scr_exc_pending() || !scr_to_string_tag_symbol) return NULL; + ScrDyn *tag = scr_dyn_symbol_key_get(d, scr_to_string_tag_symbol, true); + if (!tag) return NULL; + ScrStr *out = tag->kind == SCR_DYN_STR ? scr_dyn_tag_string(tag->v.str) : NULL; + scr_dyn_release(tag); + return out; +} + ScrStr *scr_dyn_object_tag(const ScrDyn *d) { if (d->kind == SCR_DYN_PROXY) { scr_dyn_proxy_unsupported("Symbol.toStringTag lookup"); return NULL; } + /* Object.prototype.toString answers a string @@toStringTag over the + * builtin tag of a plain object or a class instance. */ + if ((d->kind == SCR_DYN_OBJ || (d->kind == SCR_DYN_TYPED_REF && !scr_dyn_ref_is_error(d) && + !scr_dyn_generator(d)))) { + ScrStr *tag = scr_dyn_string_tag(d); + if (tag || scr_exc_pending()) return tag; + } if (d->kind == SCR_DYN_TYPED_REF) { if (scr_dyn_ref_is_error(d)) return scr_str_new("[object Error]", 14); if (scr_dyn_generator(d)) { @@ -7749,6 +8660,12 @@ typedef struct { * or NULL. The code slot holds only strings, and a subclass's typed view * rebuilds its properties from the slots on every read. */ ScrDyn *code; + /* The prototype a view built from the error's fields starts with + * (scr_error_dyn_fields: the holder of its default name and message, or + * none), retained so that no later object takes its address. Only such a + * view sets `fields_view`. */ + ScrDyn *view_prototype; + bool fields_view; } ScrErrDynEnt; static SCR_TL ScrErrDynEnt *scr_errdyn_cache = NULL; @@ -7853,6 +8770,7 @@ static void scr_errdyn_sweep(void) { scr_error_release(dead[i].err); scr_errdyn_release_node(dead[i].dyn); scr_dyn_release(dead[i].code); + scr_dyn_release(dead[i].view_prototype); } free(dead); } @@ -7874,6 +8792,8 @@ static void scr_errdyn_append(ScrError *e, ScrDyn *d) { scr_errdyn_cache[scr_errdyn_n].err = scr_error_retain(e); scr_errdyn_cache[scr_errdyn_n].dyn = scr_dyn_retain(d); scr_errdyn_cache[scr_errdyn_n].code = NULL; + scr_errdyn_cache[scr_errdyn_n].view_prototype = NULL; + scr_errdyn_cache[scr_errdyn_n].fields_view = false; scr_errdyn_n++; if (scr_errdyn_n * 2 > scr_errdyn_slots) { free(scr_errdyn_by_err); @@ -7930,6 +8850,7 @@ static void scr_errdyn_teardown(void) { scr_error_release(cache[i].err); scr_errdyn_release_node(cache[i].dyn); scr_dyn_release(cache[i].code); + scr_dyn_release(cache[i].view_prototype); } free(cache); } @@ -8033,6 +8954,19 @@ static void scr_error_dyn_add_stack(ScrDyn *d) { entry->enumerable = false; } +/* Whether `object` is the view scr_error_dyn_fields built for an error + * created as an Error (not a subclass or another error kind) and its + * prototype is still the one the view started with. Such an error's + * prototype is Error.prototype. */ +static bool scr_dyn_error_view_on_error_prototype(const ScrDyn *object) { + if (object->null_proto) return false; + size_t hit = scr_errdyn_find(object, false); + if (hit == SIZE_MAX) return false; + const ScrErrDynEnt *ent = &scr_errdyn_cache[hit]; + return ent->fields_view && object->prototype == ent->view_prototype && + ent->err->vt == &scr_error_vts[SCR_ERR_ERROR]; +} + ScrDyn *scr_error_dyn_fields(const ScrError *e) { ScrDyn *d = scr_dyn_new_obj(); scr_dyn_obj_set(d, "%error", 6, scr_dyn_new_bool(true)); /* the checked-dynamic tree's error marker */ @@ -8152,6 +9086,102 @@ void scr_error_dyn_place_name(ScrDyn *view, const char *after, size_t after_len) view->v.obj.index = NULL; } +static size_t scr_dyn_entry_index(const ScrDyn *object, const char *key, size_t key_len) { + for (size_t i = 0; i < object->v.obj.len; i++) { + const ScrDynEntry *entry = &object->v.obj.entries[i]; + if (entry->key_len == key_len && memcmp(entry->key, key, key_len) == 0) return i; + } + return SIZE_MAX; +} + +/* Whether bag entry b is one of the entries Error's constructor creates + * before the instance's class fields: `stack`, a hidden entry (a key + * starting with '%'), and a `message` that directly follows them. */ +static bool scr_error_bag_constructed(const ScrDyn *bag, size_t b) { + for (size_t i = 0; i <= b; i++) { + const ScrDynEntry *entry = &bag->v.obj.entries[i]; + bool constructed = (entry->key_len == 5 && memcmp(entry->key, "stack", 5) == 0) || + (entry->key_len > 0 && entry->key[0] == '%') || + (entry->key_len == 7 && memcmp(entry->key, "message", 7) == 0); + if (!constructed) return false; + } + return true; +} + +/* The view index at which bag entry b, now at `from`, belongs: after the + * nearest earlier bag property the view has. An entry created after + * construction (any `name` placed here, since no constructor of the class + * sets it, and a `message` that is not one of Error's constructor entries) + * also follows the class fields, which live in slots and not in the bag: it + * passes over the constructor's entries and over a `name` the construction + * set, and without another earlier property goes before the nearest later + * one the view has, or stays where the view put it. A `message` Error's + * constructor created follows the nearest earlier entry, or goes first. */ +static size_t scr_error_bag_place(const ScrDyn *view, const ScrDyn *bag, size_t b, bool name, + bool name_placed, size_t from) { + bool late = name || !scr_error_bag_constructed(bag, b); + for (size_t p = b; p-- > 0;) { + const ScrDynEntry *anchor = &bag->v.obj.entries[p]; + if (late && scr_error_bag_constructed(bag, p)) break; + if (late && name_placed && anchor->key_len == 4 && memcmp(anchor->key, "name", 4) == 0) continue; + size_t after = scr_dyn_entry_index(view, anchor->key, anchor->key_len); + if (after != SIZE_MAX) return after < from ? after + 1 : after; + } + if (!late) return 0; + for (size_t p = b + 1; p < bag->v.obj.len; p++) { + size_t before = scr_dyn_entry_index(view, bag->v.obj.entries[p].key, bag->v.obj.entries[p].key_len); + if (before != SIZE_MAX) return before > from ? before - 1 : before; + } + return from; +} + +/* The bag holds the view as it was last committed, in the order its + * properties were created: zod's error factory assigns `name` and then + * defines `message`, and Node lists them in that order. The slots rebuild + * both at fixed positions (scr_error_dyn_fields, scr_error_dyn_place_name), + * so each one present in the bag moves to follow the property it follows + * there. A typed write to `name` or `message` reaches only the slot, so a + * data property takes the slot's value. */ +void scr_error_dyn_follow_bag(ScrDyn *view, const ScrDyn *bag, const ScrError *e, bool name_placed) { + if (!bag || bag->kind != SCR_DYN_OBJ || view->kind != SCR_DYN_OBJ) return; + static const struct { const char *key; size_t len; } slots[] = {{"name", 4}, {"message", 7}}; + for (size_t s = 0; s < 2; s++) { + bool name = s == 0; + if (!(name ? e->name_present : e->message_present)) continue; + size_t at = scr_dyn_entry_index(view, slots[s].key, slots[s].len); + if (at == SIZE_MAX || view->v.obj.entries[at].accessor) continue; + ScrDyn *value = scr_dyn_new_str(name ? e->name : e->message); + scr_dyn_region_store(view, value); + ScrDyn *old = view->v.obj.entries[at].value; + view->v.obj.entries[at].value = value; + scr_dyn_release(old); + } + bool moved = false; + for (size_t b = 0; b < bag->v.obj.len; b++) { + const ScrDynEntry *bag_entry = &bag->v.obj.entries[b]; + bool slot = (bag_entry->key_len == 4 && memcmp(bag_entry->key, "name", 4) == 0) || + (bag_entry->key_len == 7 && memcmp(bag_entry->key, "message", 7) == 0); + bool name = bag_entry->key_len == 4; + /* A `name` the class's construction sets is in place already. */ + if (!slot || (name && name_placed)) continue; + size_t from = scr_dyn_entry_index(view, bag_entry->key, bag_entry->key_len); + if (from == SIZE_MAX) continue; + size_t to = scr_error_bag_place(view, bag, b, name, name_placed, from); + if (to == from) continue; + ScrDynEntry *entries = view->v.obj.entries; + ScrDynEntry entry = entries[from]; + if (to < from) memmove(&entries[to + 1], &entries[to], (from - to) * sizeof *entries); + else memmove(&entries[from], &entries[from + 1], (to - from) * sizeof *entries); + entries[to] = entry; + moved = true; + } + if (moved) { + SCR_JS_SHAPE_RESET(view); + free(view->v.obj.index); + view->v.obj.index = NULL; + } +} + ScrDyn *scr_dyn_from_error(const ScrError *e) { size_t hit = scr_errdyn_find(e, true); if (hit != SIZE_MAX) return scr_dyn_retain(scr_errdyn_cache[hit].dyn); @@ -8162,8 +9192,14 @@ ScrDyn *scr_dyn_from_error(const ScrError *e) { break; } } + bool fields_view = d == NULL; if (!d) d = scr_error_dyn_fields(e); scr_errdyn_append((ScrError *)e, d); + if (fields_view) { + ScrErrDynEnt *ent = &scr_errdyn_cache[scr_errdyn_n - 1]; + ent->fields_view = true; + ent->view_prototype = d->prototype ? scr_dyn_retain(d->prototype) : NULL; + } return d; } @@ -8719,6 +9755,13 @@ ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d) { return NULL; } if (d->kind == SCR_DYN_TYPED_REF) { + ScrStr *tag = scr_dyn_typed_ref_conversion_tag(d); + if (tag) { + ScrStr *out = scr_dyn_tag_string(tag); + scr_str_release(tag); + return out; + } + if (scr_exc_pending()) return NULL; ScrDyn *materialized = scr_dyn_typed_ref_string_view(d); ScrStr *out = scr_dyn_string_coerce_js(materialized); scr_dyn_release(materialized); @@ -8739,7 +9782,7 @@ ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d) { scr_error_release(error); return text; } - return scr_str_new("[object Object]", 15); + return scr_dyn_object_tag(d); } if (!m || m->kind != SCR_DYN_FUNC) { scr_dyn_release(m); continue; } /* OrdinaryToPrimitive performs a method call, not a bare function @@ -9369,10 +10412,32 @@ bool scr_dyn_in(const ScrDyn *v, const ScrStr *key) { return false; } +/* Node's TypeErrors for assigning a read-only property and deleting a + * non-configurable one of a checked object: the object reads as + * #, or [object Object] without a prototype. */ +static void scr_dyn_throw_property(const ScrDyn *object, const char *key, size_t key_len, bool deleting) { + const char *name = scr_dyn_class_name(object); + ScrJsonBuf buffer; + scr_jb_init(&buffer); + scr_jb_puts(&buffer, deleting ? "Cannot delete property '" : "Cannot assign to read only property '"); + scr_jb_write(&buffer, key, key_len); + scr_jb_puts(&buffer, deleting ? "' of " : "' of object '"); + if (object->null_proto) scr_jb_puts(&buffer, "[object Object]"); + else { + scr_jb_puts(&buffer, "#<"); + scr_jb_puts(&buffer, name ? name : "Object"); + scr_jb_puts(&buffer, ">"); + } + if (!deleting) scr_jb_puts(&buffer, "'"); + scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&buffer)); +} + +/* One lookup of the own property decides the assignment: an own property + * is written (or runs its setter, or refuses the write); an absent one + * consults the prototype chain and is then added. */ static void scr_dyn_object_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value, const ScrDyn *receiver) { - for (size_t i = 0; i < recv->v.obj.len; i++) { - ScrDynEntry *entry = &recv->v.obj.entries[i]; - if (entry->key_len != key->len || memcmp(entry->key, key->data, key->len) != 0) continue; + ScrDynEntry *entry = scr_dyn_entry(recv, key); + if (entry) { if (entry->accessor && entry->setter) { ScrDyn *args[] = {value}; ScrDyn *setter = scr_dyn_retain(entry->setter); @@ -9384,12 +10449,17 @@ static void scr_dyn_object_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value, con return; } if (entry->accessor || !entry->writable) { - static const char msg[] = "Cannot assign to read only property"; - scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1); + scr_dyn_throw_property(receiver, key->data, key->len, false); return; } + scr_dyn_region_store(recv, value); + ScrDyn *old = entry->value; + entry->value = scr_dyn_retain(value); + scr_dyn_release(old); + scr_error_sync_cause(recv, key); + return; } - if (!scr_dyn_entry(recv, key)) { + { for (const ScrDyn *current = scr_dyn_proto_next(recv); current;) { ScrDyn *table = current->kind == SCR_DYN_FUNC ? scr_dyn_fn_properties(current) : NULL; const ScrDyn *object = table ? table : current; @@ -9412,8 +10482,7 @@ static void scr_dyn_object_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value, con return; } if (inherited->accessor || !inherited->writable) { - static const char message[] = "Cannot assign to read only property"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + scr_dyn_throw_property(receiver, key->data, key->len, false); scr_dyn_release(table); return; } @@ -9421,7 +10490,7 @@ static void scr_dyn_object_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value, con break; } } - if (recv->non_extensible && !scr_dyn_entry(recv, key)) { + if (recv->non_extensible) { ScrJsonBuf buffer; scr_jb_init(&buffer); scr_jb_puts(&buffer, "Cannot add property "); @@ -9430,9 +10499,8 @@ static void scr_dyn_object_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value, con scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&buffer)); return; } - bool created = scr_dyn_entry(recv, key) == NULL; scr_dyn_obj_set(recv, key->data, key->len, scr_dyn_retain(value)); - if (created && key->len == 5 && memcmp(key->data, "stack", 5) == 0 && + if (key->len == 5 && memcmp(key->data, "stack", 5) == 0 && scr_errdyn_find(recv, false) != SIZE_MAX) { /* An error's own `stack` exists from construction as a non-enumerable * data property, so assigning it adds no enumerable key. */ @@ -9493,8 +10561,7 @@ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) { if (recv->kind == SCR_DYN_ARR) { if (key->len == 6 && memcmp(key->data, "length", 6) == 0) { if (recv->v.arr.frozen) { - static const char message[] = "Cannot assign to read only property"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + scr_array_frozen_error(3, recv->v.arr.len, 0, 0, 0); return; } double number; @@ -9513,8 +10580,7 @@ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) { while (recv->v.arr.len < length) scr_dyn_arr_push_hole(recv); while (recv->v.arr.len > length) { if (recv->v.arr.sealed && scr_dyn_arr_has_index(recv, recv->v.arr.len - 1)) { - static const char message[] = "Cannot delete non-configurable property"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + scr_array_frozen_error(5, recv->v.arr.len, (double)(recv->v.arr.len - 1), 0, 0); return; } scr_dyn_release(recv->v.arr.items[--recv->v.arr.len]); @@ -9532,14 +10598,13 @@ void scr_dyn_key_set(ScrDyn *recv, ScrStr *key, ScrDyn *value) { } if (is_index && idx < 4294967295ULL) { if (scr_dyn_array_prototype_element_fence(recv)) return; - if (recv->v.arr.frozen && idx < recv->v.arr.len) { - static const char message[] = "Cannot assign to read only property"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + if (recv->v.arr.frozen && idx < recv->v.arr.len && scr_dyn_arr_has_index(recv, idx)) { + scr_array_frozen_error(0, recv->v.arr.len, (double)idx, 0, 0); return; } if (recv->non_extensible && !scr_dyn_arr_has_index(recv, idx)) { - static const char message[] = "Cannot add property, object is not extensible"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); + /* An absent index, a hole included, is a property to add. */ + scr_array_frozen_error(0, 0, (double)idx, 0, 0); return; } if (idx > recv->v.arr.len + 1000000) { @@ -11368,10 +12433,7 @@ void scr_dyn_key_delete(ScrDyn *recv, const ScrStr *key, bool strict) { if (recv->kind == SCR_DYN_OBJ) { ScrDynEntry *entry = scr_dyn_entry(recv, key); if (entry && !entry->configurable) { - if (strict) { - static const char message[] = "Cannot delete non-configurable property"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); - } + if (strict) scr_dyn_throw_property(recv, key->data, key->len, true); return; } scr_json_delete_member(recv, key); @@ -11383,10 +12445,7 @@ void scr_dyn_key_delete(ScrDyn *recv, const ScrStr *key, bool strict) { for (size_t i = 0; i < key->len; i++) index = index * 10 + (size_t)(key->data[i] - '0'); if (!scr_dyn_arr_has_index(recv, index)) return; if (recv->v.arr.sealed) { - if (strict) { - static const char message[] = "Cannot delete non-configurable property"; - scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1); - } + if (strict) scr_array_frozen_error(5, recv->v.arr.len, (double)index, 0, 0); return; } if (!recv->v.arr.presence) { @@ -12271,6 +13330,13 @@ ScrDyn *scr_dyn_fn_get(const ScrDyn *d, const char *key, size_t key_len) { ScrClosure *owner = scr_closure_identity(d->v.fn.clo); if (d->v.fn.class_obj && d->v.fn.class_obj->static_data && !owner->props) owner->props = scr_box_retain(d->v.fn.class_obj->static_data); + if (d->v.fn.thunk == scr_function_constructor_call && key_len == 9 && !memcmp(key, "prototype", 9)) { + /* A function's `constructor.prototype`: Function.prototype has no value + * here, and undefined would be a wrong answer. */ + static const char function_prototype[] = "Function.prototype through a constructor value is not supported yet"; + scr_throw_error_msg_code(SCR_ERR_ERROR, function_prototype, sizeof function_prototype - 1, "SC2020"); + return NULL; + } if (!d->v.fn.class_obj && key_len == 9 && !memcmp(key, "prototype", 9) && !owner->props) { if (owner->function_kind & 1) { static const char message[] = "Native generator function prototypes have no lowering"; @@ -13047,6 +14113,20 @@ ScrDyn *scr_dyn_get_own_property_names(const ScrDyn *value) { } if (value->kind == SCR_DYN_OBJ) { ScrDyn *keys = scr_dyn_obj_own_keys(value); + if (value == scr_builtin_object_prototype) { + /* Object.prototype's builtin members, in Node's order. */ + static const char *const names[] = { + "constructor", "__defineGetter__", "__defineSetter__", "hasOwnProperty", + "__lookupGetter__", "__lookupSetter__", "isPrototypeOf", "propertyIsEnumerable", + "toString", "valueOf", "__proto__", "toLocaleString" + }; + ScrDyn *all = scr_dyn_new_arr(); + for (size_t i = 0; i < sizeof names / sizeof names[0]; i++) + scr_dyn_arr_push(all, scr_dyn_objwalk_key(names[i], strlen(names[i]))); + for (size_t i = 0; i < keys->v.arr.len; i++) scr_dyn_arr_push(all, scr_dyn_retain(keys->v.arr.items[i])); + scr_dyn_release(keys); + return all; + } if (!scr_dyn_obj_get(value, "%error", 6)) return keys; /* The checked-dynamic tree's error marker is not a property. */ ScrDyn *names = scr_dyn_new_arr(); @@ -13072,10 +14152,18 @@ ScrDyn *scr_dyn_get_own_property_names(const ScrDyn *value) { } if (value->kind != SCR_DYN_BYTES) scr_dyn_arr_push(keys, scr_dyn_objwalk_key("length", 6)); + bool array_prototype = value == scr_builtin_array_prototype; + if (array_prototype) scr_dyn_arr_push(keys, scr_dyn_objwalk_key("constructor", 11)); if (value->kind == SCR_DYN_ARR && value->v.arr.properties) { ScrDyn *properties = scr_dyn_obj_own_keys(value->v.arr.properties); - for (size_t i = 0; i < properties->v.arr.len; i++) - scr_dyn_arr_push(keys, scr_dyn_retain(properties->v.arr.items[i])); + for (size_t i = 0; i < properties->v.arr.len; i++) { + ScrDyn *name = properties->v.arr.items[i]; + /* A constructor stored on Array.prototype keeps the position above. */ + if (array_prototype && name->kind == SCR_DYN_STR && name->v.str->len == 11 && + memcmp(name->v.str->data, "constructor", 11) == 0) + continue; + scr_dyn_arr_push(keys, scr_dyn_retain(name)); + } scr_dyn_release(properties); } return keys; @@ -14158,10 +15246,16 @@ bool scr_dyn_has_own(const ScrDyn *v, const ScrStr *key) { if (v->kind == SCR_DYN_OBJ) { + /* Object.prototype's builtin members are its own properties (zod's + * isPlainObject asks whether the prototype has its own isPrototypeOf). */ + if (v == scr_builtin_object_prototype && scr_dyn_object_proto_name(key)) return true; return scr_dyn_obj_get(v, key->data, key->len) != NULL; } if (v->kind == SCR_DYN_ARR) { if (key->len == 6 && memcmp(key->data, "length", 6) == 0) return true; + /* Array.prototype's own constructor (its methods are table entries). */ + if (v == scr_builtin_array_prototype && key->len == 11 && memcmp(key->data, "constructor", 11) == 0) + return true; if (v->v.arr.properties && scr_dyn_obj_get(v->v.arr.properties, key->data, key->len)) return true; if (!scr_dyn_canonical_own_index(key, v->v.arr.len)) return false; size_t index = 0; @@ -15109,6 +16203,7 @@ static int scr_weak_key(const ScrDyn *key, void **ptr, unsigned *kind) { key->v.handle.tag != SCR_DYNH_WEAK_SET && key->v.handle.tag != SCR_DYNH_FINALIZATION_REGISTRY && key->v.handle.tag != SCR_DYNH_STDIO && key->v.handle.tag != SCR_DYNH_FETCH_REQUEST && key->v.handle.tag != SCR_DYNH_FETCH_RESPONSE && + key->v.handle.tag != SCR_DYNH_WEB_STREAM && key->v.handle.tag != SCR_DYNH_MAP && key->v.handle.tag != SCR_DYNH_SET && key->v.handle.tag != SCR_DYNH_NET_SOCKET && key->v.handle.tag != SCR_DYNH_NET_SERVER) return -1; *ptr = key->v.handle.ptr; *kind = 4 + key->v.handle.tag; return 1; @@ -15876,8 +16971,29 @@ ScrStr *scr_dyn_to_string_method(const ScrDyn *d, const ScrStr *enc, const ScrSt return scr_dyn_to_string_method_args(d, enc, what, NULL, 0); } +/* Freezing a record or array capsule freezes the native value, whose + * emitted writes then refuse to change it, and its checked view. A Map or + * Set handle only stops being extensible: its entries stay writable. Answers + * NULL for every other value. */ +static ScrDyn *scr_dyn_freeze_native(ScrDyn *value) { + void *record = scr_dyn_typed_record(value); + ScrArr *array = record ? NULL : scr_dyn_typed_array(value); + ScrMap *collection = scr_dyn_native_collection(value); + if (collection) { + scr_map_prevent_extensions(collection); + return scr_dyn_retain(value); + } + if (!record && !array) return NULL; + if (record) scr_record_freeze(record); + else scr_arr_freeze(array); + if (value->v.typed_ref.materialized) scr_dyn_record_view_freeze(value->v.typed_ref.materialized); + return scr_dyn_retain(value); +} + ScrDyn *scr_dyn_freeze(ScrDyn *value) { if (scr_dyn_class_reflection_fence(value) || scr_dyn_error_prototype_fence(value)) return NULL; + ScrDyn *native = scr_dyn_freeze_native(value); + if (native) return native; if (scr_dyn_class_integrity_fence(value, "Object.freeze")) return NULL; ScrDyn *table = scr_dyn_fn_integrity_table(value); if (!table) return scr_dyn_freeze_data(value); @@ -15889,6 +17005,8 @@ ScrDyn *scr_dyn_freeze(ScrDyn *value) { ScrDyn *scr_dyn_freeze_data(ScrDyn *value) { if (scr_dyn_class_reflection_fence(value) || scr_dyn_error_prototype_fence(value)) return NULL; + ScrDyn *native = scr_dyn_freeze_native(value); + if (native) return native; if (scr_dyn_class_integrity_fence(value, "Object.freeze")) return NULL; if (value->symbol_properties) scr_dyn_release(scr_dyn_freeze_data(value->symbol_properties)); if (value->kind == SCR_DYN_ARR) { @@ -15919,6 +17037,8 @@ bool scr_dyn_is_frozen(const ScrDyn *value) { if (scr_dyn_class_reflection_fence(value)) return false; bool answer; if (scr_dyn_typed_ref_view_answer((ScrDyn *)value, scr_dyn_is_frozen, &answer)) return answer; + ScrMap *collection = scr_dyn_native_collection(value); + if (collection) return !scr_map_is_extensible(collection); if (value->symbol_properties && !scr_dyn_is_frozen(value->symbol_properties)) return false; if (scr_dyn_plain_fn(value)) { ScrDyn *table = scr_dyn_fn_existing_table(value); diff --git a/packages/runtime/src/scr_json.h b/packages/runtime/src/scr_json.h index e3077703e..8a4ecc62d 100644 --- a/packages/runtime/src/scr_json.h +++ b/packages/runtime/src/scr_json.h @@ -119,6 +119,11 @@ _Static_assert(offsetof(ScrJsonBuf, data) == 0 && offsetof(ScrJsonBuf, len) == s "LLVM backend expects ScrJsonBuf to start with { data, len, cap }"); void scr_jb_init(ScrJsonBuf *b); +/* String conversion of a class instance whose ToString is + * Object.prototype.toString and whose class has a Symbol.toStringTag + * getter: appends "[object Tag]" and answers true, also with the getter's + * exception pending; false when the ordinary conversion applies. */ +bool scr_dyn_typed_ref_put_tag(ScrJsonBuf *b, const ScrDyn *d); /* Release a buffer's text without finishing it (error paths). */ void scr_jb_abandon(ScrJsonBuf *b); /* Append a borrowed byte span verbatim, including embedded NUL bytes. */ @@ -162,6 +167,10 @@ void scr_jb_put_number(ScrJsonBuf *b, double v); * as \u00XX, lone surrogates as \udXXX, everything else (UTF-8 included) * verbatim — exactly the JS JSON.stringify escape set. */ void scr_jb_put_json_str(ScrJsonBuf *b, const ScrStr *s); +/* JSON.stringify of a Date object: toJSON()'s ISO text, null if invalid + * (scr_lib.c). */ +typedef struct ScrDate ScrDate; +void scr_jb_put_date(ScrJsonBuf *b, const ScrDate *date); /* JSON-serialize a dyn value into the buffer — the sc_jw_* walker for the * dyn leaves the compiler cannot type: undefined prints null (an array diff --git a/packages/runtime/src/scr_json.test.c b/packages/runtime/src/scr_json.test.c index 736eca540..a28759876 100644 --- a/packages/runtime/src/scr_json.test.c +++ b/packages/runtime/src/scr_json.test.c @@ -802,8 +802,21 @@ static void json_schema_union_members(void) { assert(test_opts_live == 0); } +/* Freezing a record sets the frozen bit beside its count; an immortal + * record keeps its word, which does not read as frozen. */ +static void record_frozen_bit(void) { + size_t record[2] = {3, 0}; + assert(!scr_rc_frozen(record)); + scr_record_freeze(record); + assert(scr_rc_frozen(record) && SCR_RC_REFS(record) == 3); + size_t immortal[2] = {SIZE_MAX, 0}; + scr_record_freeze(immortal); + assert(immortal[0] == SIZE_MAX && !scr_rc_frozen(immortal)); +} + int main(void) { json_string_boundaries(); + record_frozen_bit(); json_indent_ownership(); scr_init(); checked_storage(); diff --git a/packages/runtime/src/scr_lib.c b/packages/runtime/src/scr_lib.c index c17f44f9d..fb3524c81 100644 --- a/packages/runtime/src/scr_lib.c +++ b/packages/runtime/src/scr_lib.c @@ -1078,6 +1078,10 @@ bool scr_fs_path_ok(const ScrStr *path, const char *name) { return false; } +void scr_fs_check_path(ScrStr *path, ScrStr *name) { + (void)scr_fs_path_ok(path, name->data); +} + void scr_process_load_env_file(const ScrDyn *value) { ScrStr *path; if (value->kind == SCR_DYN_UNDEF || value->kind == SCR_DYN_NULL) path = scr_str_new("./.env", 6); @@ -12549,6 +12553,39 @@ static ScrStr *scr_date_text(double ms, int mode) { } ScrStr *scr_date_to_string(double ms) { return scr_date_text(ms, SCR_DATE_TEXT_FULL); } + +/* JSON.stringify of a Date object: its toJSON(), the quoted + * toISOString() text, or null for an invalid date. */ +void scr_jb_put_date(ScrJsonBuf *b, const ScrDate *date) { + if (isnan(date->ms)) { + scr_jb_puts(b, "null"); + return; + } + ScrStr *iso = scr_date_to_iso(date->ms); + scr_jb_put_json_str(b, iso); + scr_str_release(iso); +} + +/* String(date) of a Date object: Date arrays' default ordering key. +1. */ +ScrStr *scr_date_object_to_string(void *date) { + return scr_date_text(((ScrDate *)date)->ms, SCR_DATE_TEXT_FULL); +} + +/* Array.prototype.join over Date objects: each element's String() text, + * holes empty. Both borrowed; +1. */ +ScrStr *scr_date_arr_join(ScrArr *dates, const ScrStr *sep) { + ScrJsonBuf b; + scr_jb_init(&b); + double len = scr_arr_len(dates); + for (double i = 0; i < len; i++) { + if (i > 0) scr_jb_put_str(&b, sep); + if (scr_arr_state(dates, i) != 1) continue; + ScrStr *text = scr_date_object_to_string(scr_arr_peek_ref(dates, i)); + scr_jb_put_str(&b, text); + scr_str_release(text); + } + return scr_jb_finish(&b); +} ScrStr *scr_date_to_date_string(double ms) { return scr_date_text(ms, SCR_DATE_TEXT_DATE); } ScrStr *scr_date_to_time_string(double ms) { return scr_date_text(ms, SCR_DATE_TEXT_TIME); } @@ -13009,11 +13046,29 @@ ScrArr *scr_set_to_arr_ref(const ScrMap *s, const ScrArrOps *ops) { return out; } -/* JS Date storage owns an identity; the typed read-only Date ABI continues - * to use milliseconds and explicitly extracts them for native getters. */ -typedef struct { size_t rc; double milliseconds; } ScrNativeDate; -static void *scr_native_date_retain(void *ptr) { ((ScrNativeDate *)ptr)->rc++; return ptr; } -static void scr_native_date_release(void *ptr) { if (--((ScrNativeDate *)ptr)->rc == 0) free(ptr); } +/* A Date object: one time value, and its identity. Compiled code and + * checked-dynamic Date handles share these objects, so a Date keeps its + * identity across `unknown` conversions. */ +ScrDate *scr_date_new(double ms) { + ScrDate *date = malloc(sizeof *date); + if (!date) scr_trap("scriptc: out of memory\n"); + date->rc = 1; + date->ms = ms; + return date; +} + +ScrDate *scr_date_retain(ScrDate *date) { + if (date->rc != SIZE_MAX) date->rc++; + return date; +} + +void scr_date_release(ScrDate *date) { + if (!date || date->rc == SIZE_MAX) return; + if (--date->rc == 0) free(date); +} + +void *scr_date_retain_v(void *p) { return scr_date_retain(p); } +void scr_date_release_v(void *p) { scr_date_release(p); } bool scr_dyn_native_date_is(const ScrDyn *value) { return value && value->kind == SCR_DYN_HANDLE && value->v.handle.tag == SCR_DYNH_DATE; @@ -13124,13 +13179,25 @@ static ScrStr *scr_date_class_name(const ScrDyn *value) { } double scr_dyn_native_date_value(const ScrDyn *value) { - if (scr_dyn_native_date_is(value)) return ((ScrNativeDate *)value->v.handle.ptr)->milliseconds; + if (scr_dyn_native_date_is(value)) return ((ScrDate *)value->v.handle.ptr)->ms; double time; if (scr_dyn_date_class_time(value, &time)) return time; scr_dyn_check_fail(NULL, "Date", value); return NAN; } +/* The Date object a checked-dynamic Date value holds, +1: the Date itself, + * or a new Date with the time value of an instance of a class declared + * `extends Date`. NULL with the check failure pending for any other + * value. */ +ScrDate *scr_dyn_native_date_check(const ScrDyn *value, const ScrDynPath *path) { + if (scr_dyn_native_date_is(value)) return scr_date_retain(value->v.handle.ptr); + double time; + if (scr_dyn_date_class_time(value, &time)) return scr_date_new(time); + scr_dyn_check_fail(path, "Date", value); + return NULL; +} + /* Method names without another use in this file, in named arrays (see * scr_tz_posix_ids). */ static const char scr_date_to_date_string_name[] = "toDateString"; @@ -13139,7 +13206,7 @@ static const char scr_date_to_time_string_name[] = "toTimeString"; static ScrDyn *scr_native_date_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc, const char *what) { (void)self; (void)what; - double ms = ((ScrNativeDate *)ptr)->milliseconds; + double ms = ((ScrDate *)ptr)->ms; if (!strcmp(method, "getTime") || !strcmp(method, "valueOf")) return scr_dyn_new_num(ms); if (!strcmp(method, "toString") || !strcmp(method, scr_date_to_date_string_name) || !strcmp(method, scr_date_to_time_string_name) || !strcmp(method, "toUTCString")) { @@ -13162,8 +13229,8 @@ static ScrDyn *scr_native_date_invoke(void *ptr, ScrDyn *self, const char *metho if (!strcmp(method, "setTime")) { double time = NAN; if (argc > 0 && !scr_dyn_number_coerce_js(args[0], &time)) return NULL; - ((ScrNativeDate *)ptr)->milliseconds = scr_date_new_ms(time); - return scr_dyn_new_num(((ScrNativeDate *)ptr)->milliseconds); + ((ScrDate *)ptr)->ms = scr_date_new_ms(time); + return scr_dyn_new_num(((ScrDate *)ptr)->ms); } #define DATE_GET(name, fn) if (!strcmp(method, name)) return scr_dyn_new_num(fn(ms)) DATE_GET("getFullYear", scr_date_get_full_year_local); @@ -13209,7 +13276,7 @@ double scr_dyn_date_arguments_time(const ScrDyn *arguments) { ScrDyn *date = arguments->v.arr.len > 1 ? scr_dyn_native_date_new_fields(arguments) : scr_dyn_native_date_new_value(arguments); if (!date) return NAN; - double time = ((ScrNativeDate *)date->v.handle.ptr)->milliseconds; + double time = ((ScrDate *)date->v.handle.ptr)->ms; scr_dyn_release(date); return time; } @@ -13274,10 +13341,10 @@ static ScrDyn *scr_native_date_method_call(ScrClosure *closure, ScrDyn *const *a result = isfinite(time) ? scr_date_class_call(SCR_DATE_CLASS_TO_ISO_STRING, self, NULL) : scr_dyn_new_null(); } else { - ScrNativeDate date = { 1, time }; + ScrDate date = { 1, time }; result = scr_native_date_invoke(&date, self, method, args, argc, method); - if (result && date.milliseconds != time && !(isnan(date.milliseconds) && isnan(time))) { - ScrDyn *stored = scr_dyn_new_num(date.milliseconds); + if (result && date.ms != time && !(isnan(date.ms) && isnan(time))) { + ScrDyn *stored = scr_dyn_new_num(date.ms); ScrDyn *set = scr_date_class_call(SCR_DATE_CLASS_SET_TIME, self, stored); scr_dyn_release(stored); if (!set) { @@ -13330,19 +13397,22 @@ static ScrDyn *scr_native_date_get(void *ptr, const char *key, size_t key_len) { return NULL; } -/* A Date handle holding the time value `ms` (already TimeClip'd). */ -ScrDyn *scr_dyn_native_date_from_ms(double ms) { +/* A checked-dynamic handle for the Date object `date` (borrowed; the + * handle retains it), so the value stays the same object. */ +ScrDyn *scr_dyn_native_date(ScrDate *date) { static const ScrDynHandleOps ops = { - "Date", &scr_native_date_retain, &scr_native_date_release, &scr_native_date_invoke, + "Date", &scr_date_retain_v, &scr_date_release_v, &scr_native_date_invoke, &scr_native_date_get, NULL, NULL, NULL, }; scr_dyn_handle_install(SCR_DYNH_DATE, &ops); - ScrNativeDate *date = malloc(sizeof *date); - if (!date) scr_trap("scriptc: out of memory\n"); - date->rc = 1; - date->milliseconds = ms; - ScrDyn *result = scr_dyn_new_handle(date, SCR_DYNH_DATE); - scr_native_date_release(date); + return scr_dyn_new_handle(date, SCR_DYNH_DATE); +} + +/* A Date handle holding the time value `ms` (already TimeClip'd). */ +ScrDyn *scr_dyn_native_date_from_ms(double ms) { + ScrDate *date = scr_date_new(ms); + ScrDyn *result = scr_dyn_native_date(date); + scr_date_release(date); return result; } diff --git a/packages/runtime/src/scr_lib.h b/packages/runtime/src/scr_lib.h index 95f67044a..fa447ff41 100644 --- a/packages/runtime/src/scr_lib.h +++ b/packages/runtime/src/scr_lib.h @@ -409,6 +409,9 @@ void scr_fs_invalid_named_path(const ScrDyn *value, const ScrStr *path, const ch /* A string path argument: false after throwing Node's ERR_INVALID_ARG_VALUE * when it holds a null byte (`name` is the argument's name). */ bool scr_fs_path_ok(const ScrStr *path, const char *name); +/* scr_fs_path_ok as a statement: getValidatedPath's null-byte check on a + * path the call validates before converting a later URL path. */ +void scr_fs_check_path(ScrStr *path, ScrStr *name); void scr_fs_futimes(double fd, double atime, double mtime); void scr_fs_link(ScrStr *source, ScrStr *dest); void scr_fs_symlink(ScrStr *target, ScrStr *destination, int kind); diff --git a/packages/runtime/src/scr_loop_epoll.test.c b/packages/runtime/src/scr_loop_epoll.test.c index 6ab1621c5..7129a48d2 100644 --- a/packages/runtime/src/scr_loop_epoll.test.c +++ b/packages/runtime/src/scr_loop_epoll.test.c @@ -23,9 +23,21 @@ static double now_ms(void) { } static int keys[KEYS]; -static double deadline[KEYS]; +/* The poller reads the clock inside scrp_timer_arm, so a key's deadline is + * only known to lie between the delay added to the clock read before the + * call and the delay added to the read after it. A loaded machine can pause + * the test between its read and the poller's for milliseconds, so the + * checks below use these bounds instead of one guessed deadline. */ +static double earliest[KEYS], latest[KEYS]; static int state[KEYS]; /* 0 unarmed/cancelled, 1 armed, 2 fired */ +static void arm(ScrPoller *p, int i, double ms) { + double before = now_ms(); + assert(scrp_timer_arm(p, &keys[i], ms, &keys[i])); + earliest[i] = before + ms; + latest[i] = now_ms() + ms; +} + /* Wait for the poller fd, then drain; returns the events drained. */ static int pump(ScrPoller *p, ScrPollerEvent *evs, int max, int wait_ms) { struct pollfd pfd = {.fd = scrp_poller_fd(p), .events = POLLIN}; @@ -45,20 +57,14 @@ int main(void) { if (i == KEYS / 2) for (double until = now_ms() + 3; now_ms() < until;) { } - double ms = 5 + rand() % 120; - double t = now_ms(); - assert(scrp_timer_arm(p, &keys[i], ms, &keys[i])); - deadline[i] = t + ms; + arm(p, i, 5 + rand() % 120); state[i] = 1; } /* re-arm a third later and earlier, cancel a third (the heap and the key * table move entries on every one of these) */ for (int i = 0; i < KEYS; i++) { if (i % 3 == 0) { - double ms = 1 + rand() % 150; - double t = now_ms(); - assert(scrp_timer_arm(p, &keys[i], ms, &keys[i])); - deadline[i] = t + ms; + arm(p, i, 1 + rand() % 150); } else if (i % 3 == 1) { scrp_timer_cancel(p, &keys[i]); state[i] = 0; @@ -74,7 +80,7 @@ int main(void) { int armed = 0; for (int i = 0; i < KEYS; i++) armed += state[i] == 1; int fired = 0, piped = 0; - double last = 0; + double fired_earliest = 0; /* the largest earliest bound of a fired key */ ScrPollerEvent evs[16]; /* small batches leave due timers for the next drain */ while (fired < armed) { int n = pump(p, evs, 16, 1000); @@ -91,9 +97,11 @@ int main(void) { assert(i >= 0 && i < KEYS); assert(state[i] == 1); double t = now_ms(); - assert(t + 1.0 >= deadline[i]); /* never early (timerfd rounding aside) */ - assert(deadline[i] + 1.0 >= last); /* deadline order */ - if (deadline[i] > last) last = deadline[i]; + assert(t + 1.0 >= earliest[i]); /* never early (timerfd rounding aside) */ + /* deadline order: no key fired earlier has a deadline after this + * one's (equal deadlines fire in arm order, which the bounds allow) */ + assert(latest[i] >= fired_earliest); + if (earliest[i] > fired_earliest) fired_earliest = earliest[i]; state[i] = 2; fired++; } diff --git a/packages/runtime/src/scr_map.c b/packages/runtime/src/scr_map.c index 1a6a3fc00..1cb77b0f6 100644 --- a/packages/runtime/src/scr_map.c +++ b/packages/runtime/src/scr_map.c @@ -988,6 +988,12 @@ double scr_map_size(const ScrMap *m) { return (double)m->nlive; } * table. Regrowth is amortized against the insertions that need it. * A map without entries has empty tables already: clearing it does no * work and keeps its storage for the next fill. */ +void scr_map_prevent_extensions(ScrMap *m) { + if (m->rc != SIZE_MAX) m->flags |= SCR_MAP_NOT_EXTENSIBLE; +} + +bool scr_map_is_extensible(const ScrMap *m) { return (m->flags & SCR_MAP_NOT_EXTENSIBLE) == 0; } + void scr_map_clear(ScrMap *m) { size_t walked = m->nentries; if (walked == 0) return; diff --git a/packages/runtime/src/scr_map.h b/packages/runtime/src/scr_map.h index d1ca387bf..a581cc1c7 100644 --- a/packages/runtime/src/scr_map.h +++ b/packages/runtime/src/scr_map.h @@ -116,6 +116,10 @@ typedef struct ScrMapOps { * whose object has a null prototype (Object.groupBy results). Compiled * code sets and tests the byte; the runtime never copies it. */ #define SCR_MAP_NULL_PROTO 1u +/* Object.freeze, Object.seal or Object.preventExtensions of a Map or Set: + * it has no own properties, so it is then frozen, sealed and not + * extensible at once. Its entries stay writable, as in Node. */ +#define SCR_MAP_NOT_EXTENSIBLE 2u typedef struct ScrMap { size_t rc; /* SIZE_MAX = immortal (unused for maps; kept per convention) */ @@ -272,6 +276,10 @@ void scr_map_release_v(void *m); void scr_map_trace_v(void *m, ScrTraceVisit visit, void *ctx); double scr_map_size(const ScrMap *m); /* live entries (Map.size) */ +/* Object.freeze, seal and preventExtensions of a Map or Set + * (SCR_MAP_NOT_EXTENSIBLE); immortal maps are left as they are. */ +void scr_map_prevent_extensions(ScrMap *m); +bool scr_map_is_extensible(const ScrMap *m); void scr_map_clear(ScrMap *m); bool scr_map_has_f64(const ScrMap *m, double key); diff --git a/packages/runtime/src/scr_net.c b/packages/runtime/src/scr_net.c index 4d5a37f77..9ff9ba8b0 100644 --- a/packages/runtime/src/scr_net.c +++ b/packages/runtime/src/scr_net.c @@ -420,6 +420,11 @@ enum { SCR_NET_K_SERVER = 1, SCR_NET_K_SOCKET = 2 }; struct ScrNetServer { int kind; /* FIRST: poller udata routes on it */ size_t rc; + /* The AsyncLocalStorage contexts of the listen() and close() calls + * (+1, NULL outside a store): 'listening', 'connection' and 'error' run + * in the first, 'close' and the close() callbacks in the second, as + * Node runs them in its handle's and its close request's contexts. */ + ScrAlsCtx *listen_ctx, *close_ctx; int fd; /* -1 while not listening */ int port; bool listening; @@ -486,6 +491,14 @@ struct ScrNetServer { struct ScrNetSocket { int kind; /* FIRST: poller udata routes on it */ size_t rc; + /* The AsyncLocalStorage context of the socket's creation (+1, NULL + * outside a store): its I/O events ('connect', 'data', 'end', 'close', + * 'error', 'timeout') run in it, as Node runs them in its handle's + * context. An accepted socket has none, except under an HTTP or TLS + * server, whose sockets take the server's listen() context. end_ctx is + * the end() call's context, for 'finish' and the end() callback (Node's + * shutdown request). */ + ScrAlsCtx *als, *end_ctx; int fd; /* -1 once closed */ bool unrefd; /* socket.unref(): does not keep the loop alive */ bool connecting; @@ -672,6 +685,13 @@ static SCR_TL ScrPoller *scr_net_poller = NULL; static void scr_net_close_fd_raw(int fd); /* forget-then-close, defined below */ +/* The listen() call's context: the server's 'listening', 'connection' + * and 'error' events run in it. */ +static void scr_net_server_capture_listen(ScrNetServer *s) { + scr_als_ctx_release(s->listen_ctx); + s->listen_ctx = scr_als_capture(); +} + ScrNetServer *scr_net_server_retain(ScrNetServer *s) { if (s->rc != SIZE_MAX) s->rc++; return s; @@ -687,6 +707,8 @@ void scr_net_server_release(ScrNetServer *s) { scr_net_ls_drop(&s->listening_cbs); scr_net_ls_drop(&s->timeout_ls); scr_net_ls_drop(&s->drop_ls); + scr_als_ctx_release(s->listen_ctx); + scr_als_ctx_release(s->close_ctx); scr_closure_release(s->close_override); for (size_t i = 0; i < 5; i++) scr_dyn_release(s->http_timeout_dyn[i]); scr_dyn_release(s->drop_max_connection); @@ -725,6 +747,8 @@ void scr_net_sock_release(ScrNetSocket *s) { scr_net_ls_drop(&s->finish_ls); scr_net_ls_drop(&s->wcb_ls); scr_net_ls_drop(&s->ready_ls); + scr_als_ctx_release(s->als); + scr_als_ctx_release(s->end_ctx); scr_str_release(s->pending_err); scr_str_release(s->remote_cache); for (size_t i = 0; i < s->dial_n; i++) scr_str_release(s->dial_ips[i]); @@ -969,6 +993,7 @@ static ScrNetSocket *scr_net_sock_new(void) { s->kind = SCR_NET_K_SOCKET; s->rc = 1; s->fd = -1; + s->als = scr_als_capture(); #ifdef SCR_RC_AUDIT scr_net_live++; #endif @@ -1679,6 +1704,7 @@ ScrNetServer *scr_net_create_server(ScrClosure *handler /*moves, nullable*/, Scr * listen: IPv6 any with dual-stack when the kernel allows, IPv4 fallback. */ static void scr_net_listen_any(ScrNetServer *s, double port, bool reuse_port, ScrClosure *cb /*moves, nullable*/) { + scr_net_server_capture_listen(s); if (cb) scr_net_ls_add(&s->listening_cbs, cb, NULL, true); if (s->listening || s->close_emitted) return; if (!scr_net_poller_init()) { @@ -1757,6 +1783,7 @@ void scr_net_listen(ScrNetServer *s, double port, ScrClosure *cb /*moves, nullab static void scr_net_listen_opts_impl(ScrNetServer *s, double port, ScrStr *host /*borrowed*/, bool ipv6_only, bool reuse_port, ScrClosure *cb /*moves, nullable*/) { + scr_net_server_capture_listen(s); if (host == NULL || host->len == 0) { scr_net_listen_any(s, port, reuse_port, cb); return; @@ -1972,6 +1999,8 @@ void scr_net_server_close_direct(ScrNetServer *s, ScrClosure *cb /*moves, nullab scr_closure_release(cb); return; } + scr_als_ctx_release(s->close_ctx); + s->close_ctx = scr_als_capture(); if (cb) scr_net_ls_add(&s->close_ls, cb, NULL, true); if (s->listening) { scr_net_close_fd_raw(s->fd); /* stops accepting NOW (registrations dropped first) */ @@ -2229,6 +2258,13 @@ static void scr_net_server_accept(ScrNetServer *srv) { continue; } ScrNetSocket *sock = scr_net_sock_new(); + /* Node runs an accepted socket's own events with no store, except + * under the protocol layer (an HTTP or TLS server), whose connections + * run in the listen() context this accept runs in. */ + if (!srv->native_conn && !srv->defer_conn) { + scr_als_ctx_release(sock->als); + sock->als = NULL; + } sock->fd = fd; sock->server = scr_net_server_retain(srv); srv->nconns++; @@ -2780,6 +2816,7 @@ void scr_net_server_address_check(ScrNetServer *s) { * deferred 'error', success the deferred 'listening', as for a port. */ void scr_net_listen_path(ScrNetServer *s, ScrStr *path /*borrowed*/, ScrClosure *cb /*moves, nullable*/) { + scr_net_server_capture_listen(s); /* Node's isPipeName: a string that reads as a non-negative number is a port */ double port = scr_string_to_number(path); if (port >= 0) { @@ -3082,6 +3119,8 @@ void scr_net_sock_end(ScrNetSocket *s) { } if (s->fd < 0 || s->wr_ending) return; s->wr_ending = true; + scr_als_ctx_release(s->end_ctx); + s->end_ctx = scr_als_capture(); scr_net_sock_maybe_finish_write(s); if (s->rd_eof && s->wr_done) scr_net_sock_close_fd(s); } @@ -3188,6 +3227,62 @@ void scr_net_sock_on_finish(ScrNetSocket *s, ScrClosure *cb /*moves*/) { if (s->wr_done) s->finish_pending = true; /* already finished: next sweep */ } +/* The context the socket's events run in from now on (borrowed): an HTTP + * agent hands a pooled socket to another request. */ +void scr_net_sock_set_context(ScrNetSocket *s, ScrAlsCtx *ctx) { + ScrAlsCtx *next = scr_als_ctx_retain(ctx); + scr_als_ctx_release(s->als); + s->als = next; +} + +/* A write callback runs in the write() call's AsyncLocalStorage context + * (Node's write request): with a store active, the list holds a closure + * that enters it around the callback. caps[0] is the callback, caps[1] + * the context. */ +static void *scr_net_ctx_retain_v(void *c) { return scr_als_ctx_retain(c); } +static void scr_net_ctx_release_v(void *c) { scr_als_ctx_release(c); } + +static void scr_net_ctx_fire0(ScrClosure *self) { + ScrClosure *cb = scr_box_get_ref(self->caps[0]); + ScrAlsCtx *ctx = scr_box_get_ref(self->caps[1]); + ScrAlsCtx *prev = scr_als_dispatch_enter(ctx); + ((void (*)(ScrClosure *))cb->fn)(cb); + scr_als_dispatch_leave(prev); + scr_als_ctx_release(ctx); + scr_closure_release(cb); +} + +static void scr_net_dyn_fire_arg(ScrClosure *cb, const ScrDyn *arg); + +static void scr_net_ctx_fire_arg(ScrClosure *self, const ScrDyn *arg) { + ScrClosure *cb = scr_box_get_ref(self->caps[0]); + ScrAlsCtx *ctx = scr_box_get_ref(self->caps[1]); + ScrAlsCtx *prev = scr_als_dispatch_enter(ctx); + scr_net_dyn_fire_arg(cb, arg); + scr_als_dispatch_leave(prev); + scr_als_ctx_release(ctx); + scr_closure_release(cb); +} + +/* Adds a write callback (moves) with its firing adapter (NULL: a + * zero-argument closure; scr_net_dyn_fire_arg: a dyn listener). */ +static __attribute__((noinline)) ScrClosure *scr_net_bind_write_cb(ScrClosure *cb /*moves*/, void **fn, + ScrAlsCtx *active) { + ScrClosure *bound = scr_closure_new(*fn == NULL ? (void *)scr_net_ctx_fire0 : NULL, 2); + bound->caps[0] = scr_box_new(SCR_BOX_FUNC); + scr_box_set_ref(bound->caps[0], cb); + bound->caps[1] = scr_box_new_obj(scr_net_ctx_retain_v, scr_net_ctx_release_v, NULL); + scr_box_set_ref(bound->caps[1], scr_als_ctx_retain(active)); + if (*fn != NULL) *fn = (void *)&scr_net_ctx_fire_arg; + return bound; +} + +static void scr_net_sock_add_write_cb(ScrNetSocket *s, ScrClosure *cb, void *fn) { + ScrAlsCtx *active = *SCR_ALS_SLOT(); + if (active != NULL) cb = scr_net_bind_write_cb(cb, &fn, active); + scr_net_ls_add(&s->wcb_ls, cb, fn, true); +} + /* write(chunk, callback): fires when the write buffer drains (this * surface's flush moment), from the sweep. */ void scr_net_sock_on_write_flush(ScrNetSocket *s, ScrClosure *cb /*moves*/) { @@ -3195,7 +3290,7 @@ void scr_net_sock_on_write_flush(ScrNetSocket *s, ScrClosure *cb /*moves*/) { scr_closure_release(cb); return; } - scr_net_ls_add(&s->wcb_ls, cb, NULL, true); + scr_net_sock_add_write_cb(s, cb, NULL); } double scr_net_sock_bytes_written(ScrNetSocket *s) { return (double)s->bytes_written; } @@ -4597,8 +4692,8 @@ void scr_net_server_close_dyn(ScrNetServer *s, const ScrDyn *listener /*borrowed void scr_net_sock_on_write_flush_dyn(ScrNetSocket *s, const ScrDyn *listener /*borrowed*/) { if (s->close_emitted) return; - scr_net_ls_add(&s->wcb_ls, scr_dyn_listener_closure_fn(listener, (void *)&scr_net_dyn_fire_arg), - (void *)&scr_net_dyn_fire_arg, true); + scr_net_sock_add_write_cb(s, scr_dyn_listener_closure_fn(listener, (void *)&scr_net_dyn_fire_arg), + (void *)&scr_net_dyn_fire_arg); } /* socket.setTimeout(ms[, callback]) as the program calls it: Node's @@ -5395,7 +5490,9 @@ static void scr_net_sweep(void) { ((sock->tops && sock->t_est && sock->tops->pending && sock->tops->pending(sock->tctx)) || (!sock->tops && sock->rlen > sock->rhead))) { /* deliver bytes the kernel can't re-signal (see flags_pending) */ + ScrAlsCtx *prev = scr_als_dispatch_enter(sock->als); scr_net_sock_read(sock); + scr_als_dispatch_leave(prev); if (scr_exc_pending()) { scr_net_sock_release(sock); return; @@ -5417,14 +5514,18 @@ static void scr_net_sweep(void) { } if (sock->finish_pending) { sock->finish_pending = false; + ScrAlsCtx *prev = scr_als_dispatch_enter(sock->end_ctx); scr_net_fire0_this(&sock->finish_ls, sock, SCR_DYNH_NET_SOCKET); + scr_als_dispatch_leave(prev); if (scr_exc_pending()) { scr_net_sock_release(sock); return; } } if (sock->lookup_evs != NULL && sock->pending_err) { + ScrAlsCtx *prev = scr_als_dispatch_enter(sock->als); scr_net_lookup_ops->flush(sock); + scr_als_dispatch_leave(prev); if (scr_exc_pending()) { scr_net_sock_release(sock); return; @@ -5438,6 +5539,7 @@ static void scr_net_sweep(void) { * teardown without an event); unconsumed errors fall through to the socket's own * listeners — and the unhandled-'error' exit */ bool consumed = false; + ScrAlsCtx *prev = scr_als_dispatch_enter(sock->als); if (sock->native_err) consumed = sock->native_err(sock->native_ctx, msg); if (!consumed && !scr_exc_pending()) { ScrError *coded = NULL; @@ -5451,6 +5553,7 @@ static void scr_net_sweep(void) { scr_event_error_payload = saved; if (coded != NULL) scr_error_release(coded); } + scr_als_dispatch_leave(prev); sock->pending_code = NULL; scr_str_release(msg); if (scr_exc_pending()) { @@ -5459,7 +5562,10 @@ static void scr_net_sweep(void) { } } if (sock->emit_close && !sock->close_emitted && sock->fd < 0) { - if (!scr_net_sock_settle(sock)) { + ScrAlsCtx *prev = scr_als_dispatch_enter(sock->als); + bool settled = scr_net_sock_settle(sock); + scr_als_dispatch_leave(prev); + if (!settled) { scr_net_sock_release(sock); return; } @@ -5473,7 +5579,9 @@ static void scr_net_sweep(void) { scr_net_server_retain(srv); if (srv->emit_listening) { srv->emit_listening = false; + ScrAlsCtx *prev = scr_als_dispatch_enter(srv->listen_ctx); scr_net_fire0_this(&srv->listening_cbs, srv, SCR_DYNH_NET_SERVER); + scr_als_dispatch_leave(prev); if (scr_exc_pending()) { scr_net_server_release(srv); return; @@ -5482,7 +5590,9 @@ static void scr_net_sweep(void) { if (srv->pending_err) { ScrStr *msg = srv->pending_err; srv->pending_err = NULL; + ScrAlsCtx *prev = scr_als_dispatch_enter(srv->listen_ctx); scr_net_fire_err_this(&srv->err_ls, msg, srv, SCR_DYNH_NET_SERVER); + scr_als_dispatch_leave(prev); scr_str_release(msg); /* A failed listen SETTLES the handle: listeners drop (an error * listener capturing its own server would otherwise cycle forever — @@ -5518,7 +5628,9 @@ static void scr_net_sweep(void) { } if (due == NULL) return; scr_net_server_retain(due); + ScrAlsCtx *prev = scr_als_dispatch_enter(due->close_ctx); scr_net_server_settle(due); + scr_als_dispatch_leave(prev); scr_net_server_release(due); if (scr_exc_pending()) return; } @@ -5552,6 +5664,69 @@ bool scr_loop_io_take(void); bool scr_loop_io_more(void); void scr_loop_io_reopen(void); +/* One poller event of a socket; false with an exception pending. */ +static bool scr_net_sock_event(ScrNetSocket *s, unsigned events) { + if (events & SCRP_TIMER) { + /* the idle period elapsed: 'timeout' fires once (one-shot — the + * next activity re-arms); the socket stays open, like Node */ + s->timeout_armed = false; + if (s->native_timeout) { + s->native_timeout(s->native_ctx); + if (scr_exc_pending()) return false; + } + if (s->server && s->server->http_timeout_surface && + s->native_http_timeout) { + ScrNetServer *srv = scr_net_server_retain(s->server); + bool handled = s->native_http_timeout(s->native_ctx); + if (scr_exc_pending()) { + scr_net_server_release(srv); + return false; + } + bool server_handled = srv->timeout_ls.n > 0; + if (server_handled) { + ScrNetL *snap; + size_t count = scr_net_ls_snapshot(&srv->timeout_ls, &snap); + scr_dyn_this_push(srv, SCR_DYNH_NET_SERVER); + for (size_t j = 0; j < count; j++) { + if (!scr_exc_pending()) ((ScrNetConnFn)snap[j].fn)(snap[j].cb, scr_net_sock_retain(s)); + scr_closure_release(snap[j].cb); + } + scr_dyn_this_pop(); + free(snap); + } + if (!handled && !server_handled && !scr_exc_pending()) + scr_net_sock_destroy(s); /* Node's unhandled server timeout */ + scr_net_server_release(srv); + if (scr_exc_pending()) return false; + } + scr_net_fire0_this(&s->timeout_ls, s, SCR_DYNH_NET_SOCKET); + if (scr_exc_pending()) return false; + if (s->fd < 0) return true; + } + /* kqueue delivers one direction per event (today's exact order); + * epoll may coalesce both into one — writes first (connect + * completion, flushes), then reads, re-checking the fd a write + * failure may have closed. */ + if (events & SCRP_WRITABLE) { + if (s->connecting) scr_net_sock_connect_done(s); + else if (s->tunnel != NULL) scr_net_sock_tunnel_pump(s); + else if (s->tops && !s->t_est) { + s->t_want_write = false; + scr_net_sock_transport_pump(s); + if (!scr_exc_pending() && s->fd >= 0) scr_net_sock_update_write(s); + } else { + s->t_want_write = false; + scr_net_sock_flush(s); + } + } + if ((events & SCRP_READABLE) && !scr_exc_pending() && s->fd >= 0) { + if (s->tunnel != NULL) scr_net_sock_tunnel_pump(s); + else if (s->tops && !s->t_est) scr_net_sock_transport_pump(s); + else scr_net_sock_read(s); + } + return true; +} + static void scr_net_dispatch(void) { if (!scr_net_servers && !scr_net_socks && !scr_net_proto_pending()) { @@ -5577,71 +5752,22 @@ static void scr_net_dispatch(void) { if (!udata) continue; int kind = *(int *)udata; if (kind == SCR_NET_K_SERVER) { - scr_net_server_accept((ScrNetServer *)udata); + /* 'connection' runs in the context of the listen() call. */ + ScrNetServer *srv = (ScrNetServer *)udata; + ScrAlsCtx *prev = scr_als_dispatch_enter(srv->listen_ctx); + scr_net_server_accept(srv); + scr_als_dispatch_leave(prev); } else if (kind == SCR_NET_K_SOCKET) { ScrNetSocket *s = (ScrNetSocket *)udata; /* A callback earlier in this batch may have closed this fd * (destroy inside a data listener) — its remaining events are * stale; the handle itself stays alive until the sweep. */ if (s->fd < 0) continue; - if (evs[i].events & SCRP_TIMER) { - /* the idle period elapsed: 'timeout' fires once (one-shot — the - * next activity re-arms); the socket stays open, like Node */ - s->timeout_armed = false; - if (s->native_timeout) { - s->native_timeout(s->native_ctx); - if (scr_exc_pending()) return; - } - if (s->server && s->server->http_timeout_surface && - s->native_http_timeout) { - ScrNetServer *srv = scr_net_server_retain(s->server); - bool handled = s->native_http_timeout(s->native_ctx); - if (scr_exc_pending()) { - scr_net_server_release(srv); - return; - } - bool server_handled = srv->timeout_ls.n > 0; - if (server_handled) { - ScrNetL *snap; - size_t count = scr_net_ls_snapshot(&srv->timeout_ls, &snap); - scr_dyn_this_push(srv, SCR_DYNH_NET_SERVER); - for (size_t j = 0; j < count; j++) { - if (!scr_exc_pending()) ((ScrNetConnFn)snap[j].fn)(snap[j].cb, scr_net_sock_retain(s)); - scr_closure_release(snap[j].cb); - } - scr_dyn_this_pop(); - free(snap); - } - if (!handled && !server_handled && !scr_exc_pending()) - scr_net_sock_destroy(s); /* Node's unhandled server timeout */ - scr_net_server_release(srv); - if (scr_exc_pending()) return; - } - scr_net_fire0_this(&s->timeout_ls, s, SCR_DYNH_NET_SOCKET); - if (scr_exc_pending()) return; - if (s->fd < 0) continue; - } - /* kqueue delivers one direction per event (today's exact order); - * epoll may coalesce both into one — writes first (connect - * completion, flushes), then reads, re-checking the fd a write - * failure may have closed. */ - if (evs[i].events & SCRP_WRITABLE) { - if (s->connecting) scr_net_sock_connect_done(s); - else if (s->tunnel != NULL) scr_net_sock_tunnel_pump(s); - else if (s->tops && !s->t_est) { - s->t_want_write = false; - scr_net_sock_transport_pump(s); - if (!scr_exc_pending() && s->fd >= 0) scr_net_sock_update_write(s); - } else { - s->t_want_write = false; - scr_net_sock_flush(s); - } - } - if ((evs[i].events & SCRP_READABLE) && !scr_exc_pending() && s->fd >= 0) { - if (s->tunnel != NULL) scr_net_sock_tunnel_pump(s); - else if (s->tops && !s->t_est) scr_net_sock_transport_pump(s); - else scr_net_sock_read(s); - } + /* The socket's events run in its AsyncLocalStorage context. */ + ScrAlsCtx *prev = scr_als_dispatch_enter(s->als); + bool ok = scr_net_sock_event(s, evs[i].events); + scr_als_dispatch_leave(prev); + if (!ok) return; } if (scr_exc_pending()) return; } diff --git a/packages/runtime/src/scr_net.h b/packages/runtime/src/scr_net.h index 6df6945ea..9e6dd1e0b 100644 --- a/packages/runtime/src/scr_net.h +++ b/packages/runtime/src/scr_net.h @@ -36,6 +36,10 @@ typedef struct { } ScrNetLs; void scr_net_ls_add(ScrNetLs *l, ScrClosure *cb, void *fn, bool once); +/* The AsyncLocalStorage context a socket's events run in from now on + * (borrowed; scr_net.c keeps the creation context otherwise). */ +struct ScrAlsCtx; +void scr_net_sock_set_context(ScrNetSocket *s, struct ScrAlsCtx *ctx); ScrNetOnce *scr_net_once_new(void); /* +1 */ ScrNetOnce *scr_net_once_retain(ScrNetOnce *once); /* +1 */ void scr_net_once_release(ScrNetOnce *once); diff --git a/packages/runtime/src/scr_number.h b/packages/runtime/src/scr_number.h index 78d19614b..a71b0ccfd 100644 --- a/packages/runtime/src/scr_number.h +++ b/packages/runtime/src/scr_number.h @@ -111,6 +111,18 @@ double scr_date_now(void); /* integer ms since epoch, like Node */ extern SCR_TL const double *scr_date_mock_now; double scr_date_new_ms(double ms); /* TimeClip (NaN when invalid) */ double scr_date_get_time(double ms); +/* A Date object: the TimeClip'd time value in milliseconds (NaN for an + * invalid date). Dates are immutable; their identity is the pointer. + * Retain/release follow the runtime's RC conventions (SIZE_MAX immortal). */ +typedef struct ScrDate { + size_t rc; + double ms; +} ScrDate; +ScrDate *scr_date_new(double ms); /* +1 */ +ScrDate *scr_date_retain(ScrDate *date); +void scr_date_release(ScrDate *date); +void *scr_date_retain_v(void *p); +void scr_date_release_v(void *p); /* Node's exact ISO 8601 UTC format (expanded ±YYYYYY years outside * 0–9999). THROWS Node's "Invalid time value" RangeError on NaN or * |ms| > 8.64e15 and returns NULL; +1 otherwise. */ @@ -150,6 +162,11 @@ double scr_date_get_timezone_offset(double ms); ScrStr *scr_date_to_string(double ms); ScrStr *scr_date_to_date_string(double ms); ScrStr *scr_date_to_time_string(double ms); +/* String() of a Date object (ScrDate *), and Array.prototype.join over an + * array of Date objects (holes empty). +1. */ +ScrStr *scr_date_object_to_string(void *date); +struct ScrArr; +ScrStr *scr_date_arr_join(struct ScrArr *dates, const ScrStr *sep); /* The long time zone name Node prints after the GMT offset for the host * zone at time value `ms`; `isdst` is tm_isdst at `ms`, or -1 when not * known. Borrowed; valid until the next call on the same thread. */ diff --git a/packages/runtime/src/scr_object.c b/packages/runtime/src/scr_object.c index 7cafbeca3..2f47a1c7b 100644 --- a/packages/runtime/src/scr_object.c +++ b/packages/runtime/src/scr_object.c @@ -33,6 +33,8 @@ ScrClassObj *scr_classobj_new(const ScrClassObj *template, size_t ncaps) { c->length = template->length; c->prototype_data = NULL; c->static_data = NULL; + c->base_name = template->base_name; + c->own_static_fields = template->own_static_fields; scr_obj_alloc_note(); return c; } diff --git a/packages/runtime/src/scr_pki.c b/packages/runtime/src/scr_pki.c index a1a5e97c2..a1d5b98a6 100644 --- a/packages/runtime/src/scr_pki.c +++ b/packages/runtime/src/scr_pki.c @@ -1,12 +1,14 @@ /* node:crypto asymmetric keys and signatures over the vendored mbedTLS * public-key layer: createPrivateKey, createPublicKey, generateKeyPairSync - * and generateKeyPair for "rsa", "ec" and "ed25519" keys, the - * PrivateKeyObject and PublicKeyObject handles (type, asymmetricKeyType, + * and generateKeyPair for "rsa", "ec", "ed25519" and "x25519" keys, + * crypto.diffieHellman (ECDH and X25519), the PrivateKeyObject and + * PublicKeyObject handles (type, asymmetricKeyType, * asymmetricKeyDetails, export(), equals()), crypto.sign and crypto.verify, * and the Sign and Verify objects of createSign and createVerify. Keys read * and write PEM and DER as PKCS#1, SEC1, PKCS#8 and SubjectPublicKeyInfo, - * and JWK; the EC curves are prime256v1, secp384r1, secp521r1 and - * secp256k1, and the Ed25519 arithmetic is scr_ed25519.c. Argument checks + * JWK, and the raw-public and raw-private encodings; the EC curves are + * prime256v1, secp384r1, secp521r1 and secp256k1, and the Ed25519 + * arithmetic is scr_ed25519.c. Argument checks * and errors follow Node 24's lib/internal/crypto/keys.js, keygen.js and * sig.js. Encrypted keys and other key types throw a coded SC2020 error. * All arguments are borrowed. */ @@ -22,6 +24,7 @@ #include "mbedtls/asn1write.h" #include "mbedtls/base64.h" #include "mbedtls/bignum.h" +#include "mbedtls/ecdh.h" #include "mbedtls/ecdsa.h" #include "mbedtls/ecp.h" #include "mbedtls/md.h" @@ -39,6 +42,30 @@ static void sp_throw(int kind, const char *code, const char *message) { scr_throw_error_msg_code(kind, message, strlen(message), code); } +/* An OpenSSL error as Node's ThrowCryptoError reports it, + * "error::::", with own library, reason and code + * properties in Node's key order. A crypto job's callback gets the error + * without them (`props` false), as from Node's async jobs. */ +static void sp_throw_openssl(const char *packed, const char *library, const char *reason, const char *code, + bool props) { + char message[160]; + snprintf(message, sizeof message, "error:%s:%s::%s", packed, library, reason); + ScrStr *text = scr_str_new(message, strlen(message)); + ScrError *e = scr_error_new(SCR_ERR_ERROR, text); + scr_str_release(text); + if (props) { + const char *keys[] = {"library", "reason", "code"}; + const char *values[] = {library, reason, code}; + for (size_t i = 0; i < 3; i++) { + if (i == 2) scr_error_set_code(e, code); + ScrStr *value = scr_str_new(values[i], strlen(values[i])); + scr_error_set_prop(e, keys[i], strlen(keys[i]), scr_dyn_new_str(value)); + scr_str_release(value); + } + } + scr_throw_obj(e, &scr_error_retain_v, &scr_error_release_v, scr_error_trace_arg()); +} + void scr_pki_throw_fmt(int kind, const char *code, const char *fmt, ...) { va_list ap, sized; va_start(ap, fmt); @@ -156,6 +183,8 @@ typedef struct SpKey { mbedtls_pk_context pk; /* RSA and EC keys */ bool private_key; bool ed25519; /* an Ed25519 key: ed_seed (private keys) and ed_pub */ + bool x25519; /* an X25519 key, in the same two fields: the private key's + * bytes as given, before clamping, and the public key */ uint8_t ed_seed[32]; uint8_t ed_pub[32]; } SpKey; @@ -214,6 +243,43 @@ static bool sp_is_ec(const SpKey *k) { return mbedtls_pk_get_type(&k->pk) == MBE static bool sp_is_ed(const SpKey *k) { return k->ed25519; } +static bool sp_is_x(const SpKey *k) { return k->x25519; } + +/* An Ed25519 or X25519 key: 32-byte private and public keys, no details. */ +static bool sp_is_okp(const SpKey *k) { return k->ed25519 || k->x25519; } + +/* X25519(scalar, point) per RFC 7748 over mbedTLS's Curve25519 group, or + * of the base point when point is NULL. False when the result is the + * all-zero value (a small-order point), which OpenSSL refuses. */ +static bool sp_x25519(const uint8_t scalar[32], const uint8_t *point, uint8_t out[32]) { + mbedtls_ecp_group grp; + mbedtls_ecp_point P, R; + mbedtls_mpi d; + mbedtls_ecp_group_init(&grp); + mbedtls_ecp_point_init(&P); + mbedtls_ecp_point_init(&R); + mbedtls_mpi_init(&d); + uint8_t k[32]; + memcpy(k, scalar, 32); + k[0] &= 248; + k[31] &= 127; + k[31] |= 64; + size_t n = 0; + bool ok = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_CURVE25519) == 0 && + mbedtls_mpi_read_binary_le(&d, k, 32) == 0 && + (point ? mbedtls_ecp_point_read_binary(&grp, &P, point, 32) : mbedtls_ecp_copy(&P, &grp.G)) == 0 && + mbedtls_ecp_mul(&grp, &R, &d, &P, sp_random, NULL) == 0 && + mbedtls_ecp_point_write_binary(&grp, &R, MBEDTLS_ECP_PF_UNCOMPRESSED, &n, out, 32) == 0 && n == 32; + uint8_t any = 0; + for (size_t i = 0; ok && i < 32; i++) any |= out[i]; + mbedtls_platform_zeroize(k, sizeof k); + mbedtls_mpi_free(&d); + mbedtls_ecp_point_free(&R); + mbedtls_ecp_point_free(&P); + mbedtls_ecp_group_free(&grp); + return ok && any != 0; +} + typedef struct { const char *name; /* OpenSSL short name (namedCurve) */ const char *nist; /* EC_curve_nist2nid alias, or NULL */ @@ -240,7 +306,7 @@ static const SpCurve *sp_curve_of(const SpKey *k) { /* A parsed key is usable when it is RSA or EC on a curve above. */ static bool sp_key_supported(const SpKey *k) { - return sp_is_ed(k) || sp_is_rsa(k) || (sp_is_ec(k) && sp_curve_of(k) != NULL); + return sp_is_okp(k) || sp_is_rsa(k) || (sp_is_ec(k) && sp_curve_of(k) != NULL); } /* ── DER writing ──────────────────────────────────────────────────────── */ @@ -259,6 +325,11 @@ static const uint8_t sp_ed_pkcs8_prefix[16] = {0x30, 0x2e, 0x02, 0x01, 0x00, 0x3 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20}; static const uint8_t sp_ed_spki_prefix[12] = {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00}; +/* X25519's, which differ in the OID's last byte (1.3.101.110). */ +static const uint8_t sp_x_pkcs8_prefix[16] = {0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, + 0x03, 0x2b, 0x65, 0x6e, 0x04, 0x22, 0x04, 0x20}; +static const uint8_t sp_x_spki_prefix[12] = {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, + 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00}; /* Writes prefix || 32 bytes at the end of buf, as the mbedtls writers do. */ static int sp_write_ed_der(const uint8_t *prefix, size_t prefix_len, const uint8_t key[32], @@ -271,7 +342,8 @@ static int sp_write_ed_der(const uint8_t *prefix, size_t prefix_len, const uint8 } static int sp_write_spki_der(SpKey *k, unsigned char *buf, size_t size) { - if (sp_is_ed(k)) return sp_write_ed_der(sp_ed_spki_prefix, sizeof sp_ed_spki_prefix, k->ed_pub, buf, size); + if (sp_is_okp(k)) + return sp_write_ed_der(sp_is_x(k) ? sp_x_spki_prefix : sp_ed_spki_prefix, 12, k->ed_pub, buf, size); return mbedtls_pk_write_pubkey_der(&k->pk, buf, size); } @@ -286,8 +358,8 @@ static int sp_write_pkcs1_public_der(SpKey *k, unsigned char *buf, size_t size) * parameters, or id-ecPublicKey with the curve OID), and the inner key, * which for EC omits the parameters field. */ static int sp_write_pkcs8_der(SpKey *k, unsigned char *buf, size_t size) { - if (sp_is_ed(k)) - return sp_write_ed_der(sp_ed_pkcs8_prefix, sizeof sp_ed_pkcs8_prefix, k->ed_seed, buf, size); + if (sp_is_okp(k)) + return sp_write_ed_der(sp_is_x(k) ? sp_x_pkcs8_prefix : sp_ed_pkcs8_prefix, 16, k->ed_seed, buf, size); unsigned char inner[SP_DER_MAX]; size_t inner_len; if (sp_is_rsa(k)) { @@ -472,9 +544,9 @@ static ScrDyn *sp_b64url(const uint8_t *raw, size_t len) { static ScrDyn *sp_export_jwk(SpKey *k) { ScrDyn *jwk = scr_dyn_new_obj(); - if (sp_is_ed(k)) { + if (sp_is_okp(k)) { sp_set(jwk, "kty", sp_dyn_str(scr_str_new("OKP", 3))); - sp_set(jwk, "crv", sp_dyn_str(scr_str_new("Ed25519", 7))); + sp_set(jwk, "crv", sp_dyn_str(sp_is_x(k) ? scr_str_new("X25519", 6) : scr_str_new("Ed25519", 7))); sp_set(jwk, "x", sp_b64url(k->ed_pub, 32)); if (k->private_key) sp_set(jwk, "d", sp_b64url(k->ed_seed, 32)); return jwk; @@ -586,7 +658,8 @@ static SpKey *sp_key_from_jwk_plain(const ScrDyn *jwk, bool want_private) { bool ok = false; if (sp_is_str(kty, "OKP")) { const ScrDyn *crv = sp_opt(jwk, "crv"); - if (!sp_is_str(crv, "Ed25519")) { + bool x = sp_is_str(crv, "X25519"); + if (!x && !sp_is_str(crv, "Ed25519")) { sp_key_release(k); if (crv->kind != SCR_DYN_STR) { scr_dyn_prop_type_fail("key.crv", "of type string", crv); @@ -595,10 +668,13 @@ static SpKey *sp_key_from_jwk_plain(const ScrDyn *jwk, bool want_private) { sp_not_supported("JWK keys of this crv"); return NULL; } - k->ed25519 = true; + k->ed25519 = !x; + k->x25519 = x; if (private_key) { ok = sp_jwk_bytes(jwk, "d", k->ed_seed); - if (ok) { + if (ok && x) { + ok = sp_x25519(k->ed_seed, NULL, k->ed_pub); + } else if (ok) { const ScrPkiEd25519 *ed = sp_ed(); if (!ed) { sp_key_release(k); @@ -677,9 +753,10 @@ static SpKey *sp_key_from_jwk_plain(const ScrDyn *jwk, bool want_private) { /* The private key of a public SpKey derived from `k`. */ static SpKey *sp_public_of(SpKey *k) { - if (sp_is_ed(k)) { + if (sp_is_okp(k)) { SpKey *pub = sp_key_alloc(false); - pub->ed25519 = true; + pub->ed25519 = k->ed25519; + pub->x25519 = k->x25519; memcpy(pub->ed_pub, k->ed_pub, 32); return pub; } @@ -769,6 +846,24 @@ static bool sp_cert_spki(const unsigned char *der, size_t len, const unsigned ch * as public answers its public key. */ static SpKey *sp_ed_from_der(const unsigned char *der, size_t len, bool private_key, bool *failed) { *failed = false; + /* X25519 keys, which need no other unit. */ + if (len == 48 && memcmp(der, sp_x_pkcs8_prefix, 16) == 0) { + SpKey *k = sp_key_alloc(private_key); + k->x25519 = true; + memcpy(k->ed_seed, der + 16, 32); + if (!sp_x25519(k->ed_seed, NULL, k->ed_pub)) { + sp_key_release(k); + return NULL; + } + if (!private_key) mbedtls_platform_zeroize(k->ed_seed, 32); + return k; + } + if (!private_key && len == 44 && memcmp(der, sp_x_spki_prefix, 12) == 0) { + SpKey *k = sp_key_alloc(false); + k->x25519 = true; + memcpy(k->ed_pub, der + 12, 32); + return k; + } if (len == 48 && memcmp(der, sp_ed_pkcs8_prefix, 16) == 0) { const ScrPkiEd25519 *ed = sp_ed(); if (!ed) { @@ -878,6 +973,8 @@ static ScrBytes *sp_source_bytes(const ScrDyn *v, const ScrDyn *encoding) { return NULL; } +static SpKey *sp_key_from_raw(const ScrDyn *input, const ScrDyn *data, const ScrDyn *format, bool private_input); + static const char sp_key_input_types[] = "of type string or an instance of ArrayBuffer, Buffer, TypedArray, DataView, or URL"; @@ -929,14 +1026,22 @@ static ScrDyn *sp_create_key(ScrDyn *input, bool private_key) { SpKey *k = sp_key_from_jwk(data, private_key); return k ? sp_key_box(k) : NULL; } + if (sp_is_str(format, "raw-public") || sp_is_str(format, "raw-private") || sp_is_str(format, "raw-seed")) { + SpKey *k = sp_key_from_raw(input, data, format, private_key); + if (!k || private_key || !k->private_key) return k ? sp_key_box(k) : NULL; + SpKey *pub = sp_public_of(k); + sp_key_release(k); + if (!pub) sp_unsupported_input(); + return pub ? sp_key_box(pub) : NULL; + } if (!sp_absent(format) && !sp_is_str(format, "pem") && !sp_is_str(format, "der")) { sp_property_invalid("key.format", format); return NULL; } if (sp_is_str(format, "der")) { - bool valid = private_key ? sp_is_str(type, "pkcs1") || sp_is_str(type, "pkcs8") || - sp_is_str(type, "sec1") - : sp_is_str(type, "pkcs1") || sp_is_str(type, "spki"); + /* createPublicKey also reads a private key's encodings. */ + bool valid = sp_is_str(type, "pkcs1") || sp_is_str(type, "pkcs8") || sp_is_str(type, "sec1") || + (!private_key && sp_is_str(type, "spki")); if (!valid) { sp_property_invalid("key.type", type); return NULL; @@ -959,6 +1064,191 @@ ScrDyn *scr_crypto_create_private_key(ScrDyn *key) { return sp_create_key(key, t ScrDyn *scr_crypto_create_public_key(ScrDyn *key) { return sp_create_key(key, false); } +/* ── raw keys ─────────────────────────────────────────────────────────── */ + +/* A namedCurve as Node reads it: the OpenSSL short name or the NIST name, + * matched exactly. NULL for other names. */ +static const SpCurve *sp_curve_named(const ScrDyn *name) { + for (size_t i = 0; i < sizeof sp_curves / sizeof sp_curves[0]; i++) + if (sp_is_str(name, sp_curves[i].name) || (sp_curves[i].nist && sp_is_str(name, sp_curves[i].nist))) + return &sp_curves[i]; + return NULL; +} + +static void sp_incompatible_key_type(void) { + sp_throw(SCR_ERR_ERROR, "ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS", + "The selected key encoding is incompatible with the key type"); +} + +static void sp_invalid_key_data(void) { sp_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_VALUE", "Invalid key data"); } + +/* A key's raw encoding: an EC public point, compressed when asked, or the + * private scalar at the curve's size, or an Ed25519 public key or seed. + * raw-seed, and every encoding of an RSA key, throw + * ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS. NULL with the error pending. */ +static ScrDyn *sp_export_raw(SpKey *k, bool private_part, bool seed, bool compressed) { + if (seed || sp_is_rsa(k)) { + sp_incompatible_key_type(); + return NULL; + } + if (sp_is_okp(k)) return sp_dyn_buffer(private_part ? k->ed_seed : k->ed_pub, 32); + mbedtls_ecp_keypair *ec = mbedtls_pk_ec(k->pk); + const mbedtls_ecp_group *grp = &ec->MBEDTLS_PRIVATE(grp); + unsigned char buf[1 + 2 * 66]; + size_t n = (grp->nbits + 7) / 8; + int form = compressed ? MBEDTLS_ECP_PF_COMPRESSED : MBEDTLS_ECP_PF_UNCOMPRESSED; + int rc = private_part ? mbedtls_mpi_write_binary(&ec->MBEDTLS_PRIVATE(d), buf, n) + : mbedtls_ecp_point_write_binary(grp, &ec->MBEDTLS_PRIVATE(Q), form, &n, buf, sizeof buf); + ScrDyn *out = rc == 0 ? sp_dyn_buffer(buf, n) : NULL; + mbedtls_platform_zeroize(buf, sizeof buf); + if (!out) sp_unsupported_input(); + return out; +} + +/* Node's named key types that this runtime does not generate or read: + * X448, Ed448 and the post-quantum types. */ +static const char *const sp_other_named_types[] = { + "ed448", + "x448", + "ml-dsa-44", + "ml-dsa-65", + "ml-dsa-87", + "ml-kem-512", + "ml-kem-768", + "ml-kem-1024", + "slh-dsa-sha2-128f", + "slh-dsa-sha2-128s", + "slh-dsa-sha2-192f", + "slh-dsa-sha2-192s", + "slh-dsa-sha2-256f", + "slh-dsa-sha2-256s", + "slh-dsa-shake-128f", + "slh-dsa-shake-128s", + "slh-dsa-shake-192f", + "slh-dsa-shake-192s", + "slh-dsa-shake-256f", + "slh-dsa-shake-256s", +}; + +/* createPrivateKey / createPublicKey of { key, format: "raw-public" | + * "raw-private" | "raw-seed", asymmetricKeyType, namedCurve }: an EC point + * (uncompressed or compressed) or scalar, or an Ed25519 public key or + * seed. The argument checks are Node's prepareAsymmetricKey order, then + * the key type, the seed format, the curve and the data. NULL with the + * error pending. */ +static SpKey *sp_key_from_raw(const ScrDyn *input, const ScrDyn *data, const ScrDyn *format, bool private_input) { + bool raw_public = sp_is_str(format, "raw-public"); + bool seed = sp_is_str(format, "raw-seed"); + if (private_input && raw_public) { + sp_property_invalid("key.format", format); + return NULL; + } + if (data->kind != SCR_DYN_BYTES && !scr_buffer_storage_is(data)) { + scr_dyn_arg_type_fail("key.key", "an instance of ArrayBuffer, Buffer, TypedArray, or DataView", data); + return NULL; + } + const ScrDyn *type = sp_opt(input, "asymmetricKeyType"); + if (type->kind != SCR_DYN_STR) { + scr_dyn_prop_type_fail("key.asymmetricKeyType", "of type string", type); + return NULL; + } + bool ec = sp_is_str(type, "ec"); + bool ed = sp_is_str(type, "ed25519"); + const ScrDyn *curve_name = sp_opt(input, "namedCurve"); + if (ec && curve_name->kind != SCR_DYN_STR) { + scr_dyn_prop_type_fail("key.namedCurve", "of type string", curve_name); + return NULL; + } + if (sp_is_str(type, "rsa") || sp_is_str(type, "rsa-pss") || sp_is_str(type, "dsa") || sp_is_str(type, "dh")) { + sp_incompatible_key_type(); + return NULL; + } + bool x = sp_is_str(type, "x25519"); + bool other = false; + for (size_t i = 0; i < sizeof sp_other_named_types / sizeof sp_other_named_types[0]; i++) + other = other || sp_is_str(type, sp_other_named_types[i]); + if (!ec && !ed && !x && !other) { + ScrStr *s = type->v.str; + scr_pki_throw_fmt(SCR_ERR_TYPE, "ERR_INVALID_ARG_VALUE", "Invalid asymmetricKeyType: %.*s", (int)s->len, s->data); + return NULL; + } + bool ml = other && type->v.str->len > 3 && memcmp(type->v.str->data, "ml-", 3) == 0; + if (seed && !ml) { + sp_incompatible_key_type(); + return NULL; + } + if (other) { + sp_not_supported("raw keys of this asymmetricKeyType"); + return NULL; + } + const SpCurve *curve = NULL; + if (ec && !(curve = sp_curve_named(curve_name))) { + sp_throw(SCR_ERR_TYPE, "ERR_CRYPTO_INVALID_CURVE", "Invalid EC curve name"); + return NULL; + } + ScrBytes *bytes = scr_bytes_buffer_source(data); + if (!bytes) return NULL; + const unsigned char *p = bytes->data; + size_t len = (size_t)scr_bytes_byte_len(bytes); + SpKey *k = sp_key_alloc(!raw_public); + bool ok = false; + if (x) { + k->x25519 = true; + if (len == 32 && raw_public) { + memcpy(k->ed_pub, p, 32); + ok = true; + } else if (len == 32) { + memcpy(k->ed_seed, p, 32); + ok = sp_x25519(k->ed_seed, NULL, k->ed_pub); + } + } else if (ed) { + k->ed25519 = true; + if (len == 32 && raw_public) { + memcpy(k->ed_pub, p, 32); + ok = true; + } else if (len == 32) { + const ScrPkiEd25519 *eds = sp_ed(); + if (!eds) { + sp_key_release(k); + scr_bytes_release(bytes); + return NULL; + } + memcpy(k->ed_seed, p, 32); + eds->public_key(k->ed_pub, k->ed_seed); + ok = true; + } + } else if (raw_public && len == 1 && p[0] == 0) { + sp_key_release(k); + scr_bytes_release(bytes); + sp_not_supported("EC public keys at the point at infinity"); + return NULL; + } else if (mbedtls_pk_setup(&k->pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) == 0) { + mbedtls_ecp_keypair *kp = mbedtls_pk_ec(k->pk); + mbedtls_ecp_group *grp = &kp->MBEDTLS_PRIVATE(grp); + ok = mbedtls_ecp_group_load(grp, curve->id) == 0; + if (ok && raw_public) { + ok = mbedtls_ecp_point_read_binary(grp, &kp->MBEDTLS_PRIVATE(Q), p, len) == 0 && + mbedtls_ecp_check_pubkey(grp, &kp->MBEDTLS_PRIVATE(Q)) == 0; + } else if (ok) { + ok = len == (grp->nbits + 7) / 8 && mbedtls_mpi_read_binary(&kp->MBEDTLS_PRIVATE(d), p, len) == 0; + if (ok && mbedtls_ecp_check_privkey(grp, &kp->MBEDTLS_PRIVATE(d)) != 0) { + sp_key_release(k); + scr_bytes_release(bytes); + sp_not_supported("EC private keys outside 1 to n - 1"); + return NULL; + } + ok = ok && mbedtls_ecp_mul(grp, &kp->MBEDTLS_PRIVATE(Q), &kp->MBEDTLS_PRIVATE(d), &grp->G, sp_random, NULL) == 0; + } + } + scr_bytes_release(bytes); + if (!ok) { + sp_key_release(k); + sp_invalid_key_data(); + return NULL; + } + return k; +} + /* ── export ───────────────────────────────────────────────────────────── */ /* export({ format, type }) per Node's parseKeyEncoding: format "pem", @@ -975,11 +1265,37 @@ static ScrDyn *sp_export(SpKey *k, const ScrDyn *options) { } static ScrDyn *sp_export_plain(SpKey *k, const ScrDyn *options) { + const ScrDyn *format = sp_opt(options, "format"); + /* A private key's passphrase applies only to PEM and DER. */ + if (k->private_key && !sp_absent(sp_opt(options, "passphrase")) && !sp_is_str(format, "pem") && + !sp_is_str(format, "der")) { + ScrStr *text = scr_dyn_string_coerce_js(format); + if (!text) return NULL; + scr_pki_throw_fmt(SCR_ERR_ERROR, "ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS", + "The selected key encoding %.*s does not support encryption.", (int)text->len, text->data); + scr_str_release(text); + return NULL; + } + if (!k->private_key && sp_is_str(format, "raw-public")) { + const ScrDyn *type = sp_opt(options, "type"); + bool compressed = sp_is_str(type, "compressed"); + /* Only an EC point has a compressed form. */ + if (sp_is_ec(k) && !sp_absent(type) && !compressed && !sp_is_str(type, "uncompressed")) { + ScrStr *received = sp_received(type, false); + scr_pki_throw_fmt(SCR_ERR_TYPE, "ERR_INVALID_ARG_VALUE", + "The property 'options.type' must be one of: 'compressed', 'uncompressed'. Received %.*s", + (int)received->len, received->data); + scr_str_release(received); + return NULL; + } + return sp_export_raw(k, false, false, sp_is_ec(k) && compressed); + } + if (k->private_key && (sp_is_str(format, "raw-private") || sp_is_str(format, "raw-seed"))) + return sp_export_raw(k, true, sp_is_str(format, "raw-seed"), false); if (options == NULL || !sp_is_object(options)) { scr_dyn_arg_type_fail("options", "of type object", options ? options : scr_dyn_undefined()); return NULL; } - const ScrDyn *format = sp_opt(options, "format"); if (sp_is_str(format, "jwk")) return sp_export_jwk(k); bool pem = sp_is_str(format, "pem"); if (!pem && !sp_is_str(format, "der")) { @@ -1040,7 +1356,7 @@ static ScrDyn *sp_export_plain(SpKey *k, const ScrDyn *options) { static ScrDyn *sp_key_details(SpKey *k) { ScrDyn *details = scr_dyn_new_obj(); - if (sp_is_ed(k)) return details; + if (sp_is_okp(k)) return details; if (sp_is_rsa(k)) { mbedtls_rsa_context *rsa = mbedtls_pk_rsa(k->pk); mbedtls_mpi e; @@ -1066,7 +1382,7 @@ static ScrDyn *sp_key_details(SpKey *k) { /* The DER that identifies a key for equals(): its private encoding for a * private key, its SubjectPublicKeyInfo otherwise. */ static int sp_identity_der(SpKey *k, unsigned char *buf, size_t size) { - if (sp_is_ed(k)) return k->private_key ? sp_write_pkcs8_der(k, buf, size) : sp_write_spki_der(k, buf, size); + if (sp_is_okp(k)) return k->private_key ? sp_write_pkcs8_der(k, buf, size) : sp_write_spki_der(k, buf, size); return k->private_key ? sp_write_private_der(k, buf, size) : sp_write_spki_der(k, buf, size); } @@ -1112,6 +1428,7 @@ static ScrDyn *sp_key_get(void *h, const char *name, size_t len) { return k->private_key ? sp_dyn_str(scr_str_new("private", 7)) : sp_dyn_str(scr_str_new("public", 6)); if (len == 17 && memcmp(name, "asymmetricKeyType", 17) == 0) return sp_is_ed(k) ? sp_dyn_str(scr_str_new("ed25519", 7)) + : sp_is_x(k) ? sp_dyn_str(scr_str_new("x25519", 6)) : sp_is_rsa(k) ? sp_dyn_str(scr_str_new("rsa", 3)) : sp_dyn_str(scr_str_new("ec", 2)); if (len == 20 && memcmp(name, "asymmetricKeyDetails", 20) == 0) return sp_key_details(k); @@ -1227,7 +1544,7 @@ static bool sp_check_encoding_plain(const ScrDyn *enc, const char *name, const S /* Node's parseKeyEncoding for options.publicKeyEncoding or * options.privateKeyEncoding of a `type` key: true when the encoding is - * undefined, null or valid, false with the error pending. A valid raw or + * undefined, null or valid, false with the error pending. A valid * encrypted encoding, which this runtime does not write, sets *refusal * when it is unset. */ static bool sp_check_encoding(const ScrDyn *options, const char *key, const ScrDyn *type, bool private_key, @@ -1323,43 +1640,17 @@ static bool sp_check_encoding_plain(const ScrDyn *enc, const char *name, const S return false; } } - if (!*refusal && (raw_public || raw_private)) *refusal = "generateKeyPair with a raw key encoding"; if (!*refusal && encrypted) *refusal = "encrypted private key export"; return true; } /* The key generateKeyPair makes, as sp_keygen_check reads it. */ typedef struct { - enum { SP_GEN_RSA, SP_GEN_EC, SP_GEN_ED25519 } kind; + enum { SP_GEN_RSA, SP_GEN_EC, SP_GEN_ED25519, SP_GEN_X25519 } kind; double bits, exponent; /* RSA */ const SpCurve *curve; /* EC */ } SpKeyGen; -/* The key types Node generates that this runtime does not, apart from - * rsa-pss, dsa and dh: Node's named key pairs. */ -static const char *const sp_other_named_types[] = { - "ed448", - "x25519", - "x448", - "ml-dsa-44", - "ml-dsa-65", - "ml-dsa-87", - "ml-kem-512", - "ml-kem-768", - "ml-kem-1024", - "slh-dsa-sha2-128f", - "slh-dsa-sha2-128s", - "slh-dsa-sha2-192f", - "slh-dsa-sha2-192s", - "slh-dsa-sha2-256f", - "slh-dsa-sha2-256s", - "slh-dsa-shake-128f", - "slh-dsa-shake-128s", - "slh-dsa-shake-192f", - "slh-dsa-shake-192s", - "slh-dsa-shake-256f", - "slh-dsa-shake-256s", -}; /* createJob's checks for generateKeyPair(Sync)(type, options) in Node's * order: the type, the options object, the public and private key @@ -1416,6 +1707,8 @@ static bool sp_keygen_check(const ScrDyn *type, const ScrDyn *options, SpKeyGen } } else if (sp_is_str(type, "ed25519")) { gen->kind = SP_GEN_ED25519; + } else if (sp_is_str(type, "x25519")) { + gen->kind = SP_GEN_X25519; } else { bool named = false; for (size_t i = 0; i < sizeof sp_other_named_types / sizeof sp_other_named_types[0]; i++) @@ -1458,6 +1751,20 @@ static SpKey *sp_keygen_run(const SpKeyGen *gen, bool *job_error) { scr_pki_ed25519->public_key(k->ed_pub, k->ed_seed); return k; } + if (gen->kind == SP_GEN_X25519) { + /* OpenSSL stores a generated X25519 key clamped. */ + k->x25519 = true; + sp_random(NULL, k->ed_seed, 32); + k->ed_seed[0] &= 248; + k->ed_seed[31] &= 127; + k->ed_seed[31] |= 64; + if (!sp_x25519(k->ed_seed, NULL, k->ed_pub)) { + sp_key_release(k); + sp_unsupported_input(); + return NULL; + } + return k; + } if (gen->kind == SP_GEN_RSA) { const char *failure = sp_rsa_keygen_failure(gen->bits, gen->exponent); if (failure) { @@ -1502,14 +1809,18 @@ static ScrDyn *sp_key_pair(const ScrDyn *type, const ScrDyn *options, bool *job_ sp_unsupported_input(); return NULL; } + /* An encoding the key type has no form for (a raw encoding of an RSA + * key, raw-seed) fails the job, as in Node. */ ScrDyn *public_value = sp_encoded(pub, sp_opt(options, "publicKeyEncoding")); if (!public_value) { sp_key_release(k); + *job_error = true; return NULL; } ScrDyn *private_value = sp_encoded(k, sp_opt(options, "privateKeyEncoding")); if (!private_value) { scr_dyn_release(public_value); + *job_error = true; return NULL; } ScrDyn *pair = scr_dyn_new_obj(); @@ -1785,6 +2096,16 @@ static bool sp_verify_hash(SpSigKey *key, mbedtls_md_type_t md, const unsigned c return mbedtls_pk_verify(&key->key->pk, md, hash, hash_len, sig, sig_len) == 0; } +/* X25519 keys neither sign nor verify: OpenSSL's error, after the key + * checks. Releases the key and answers true when it rejects. */ +static bool sp_reject_x25519(SpSigKey *k) { + if (!sp_is_x(k->key)) return false; + sp_key_release(k->key); + sp_throw_openssl("03000096", "digital envelope routines", "operation not supported for this keytype", + "ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", true); + return true; +} + static const char sp_data_types[] = "an instance of Buffer, TypedArray, or DataView"; /* Ed25519 signs the message itself: an algorithm other than null or @@ -1805,6 +2126,7 @@ ScrDyn *scr_crypto_sign_oneshot(ScrDyn *algorithm, ScrDyn *data, ScrDyn *key) { } SpSigKey k; if (!sp_sig_key(key, true, &k)) return NULL; + if (sp_reject_x25519(&k)) return NULL; ScrBytes *bytes = scr_bytes_buffer_source(data); if (sp_is_ed(k.key)) { ScrDyn *result = NULL; @@ -1842,6 +2164,7 @@ ScrDyn *scr_crypto_verify_oneshot(ScrDyn *algorithm, ScrDyn *data, ScrDyn *key, scr_dyn_arg_type_fail("signature", sp_data_types, signature); return NULL; } + if (sp_reject_x25519(&k)) return NULL; ScrBytes *bytes = scr_bytes_buffer_source(data); ScrBytes *sig = scr_bytes_buffer_source(signature); if (sp_is_ed(k.key)) { @@ -1928,6 +2251,7 @@ static ScrDyn *sp_signer_invoke(void *h, ScrDyn *self, const char *method, ScrDy } SpSigKey k; if (!sp_sig_key(argc ? args[0] : scr_dyn_undefined(), true, &k)) return NULL; + if (sp_reject_x25519(&k)) return NULL; if (sp_is_ed(k.key)) { sp_key_release(k.key); sp_throw(SCR_ERR_ERROR, "ERR_CRYPTO_UNSUPPORTED_OPERATION", "Unsupported crypto operation"); @@ -1965,6 +2289,7 @@ static ScrDyn *sp_signer_invoke(void *h, ScrDyn *self, const char *method, ScrDy signature); return NULL; } + if (sp_reject_x25519(&k)) return NULL; const ScrStr *encoding = sp_encoding_arg(argc > 2 ? args[2] : NULL); ScrDyn *encoding_value = encoding ? scr_dyn_new_str((ScrStr *)encoding) : NULL; ScrBytes *sig = sp_source_bytes(signature, encoding_value); @@ -2131,6 +2456,156 @@ void scr_crypto_generate_key_pair_async(ScrDyn *type, ScrDyn *options, ScrDyn *c scr_loop_defer(c); } +/* ── diffieHellman ────────────────────────────────────────────────────── */ + +/* The keys an options.privateKey or options.publicKey value can be. */ +static const char sp_dh_private_types[] = + "of type string or an instance of ArrayBuffer, Buffer, TypedArray, DataView, KeyObject, CryptoKey, or URL"; +static const char sp_dh_public_types[] = + "of type string or an instance of ArrayBuffer, Buffer, TypedArray, DataView, KeyObject, or CryptoKey"; + +/* Node's message names: a KeyObject's asymmetricKeyType. */ +static const char *sp_type_name(const SpKey *k) { + return sp_is_ed(k) ? "ed25519" : sp_is_x(k) ? "x25519" : sp_is_rsa(k) ? "rsa" : "ec"; +} + +/* A key input of diffieHellman's options: a primitive other than a string + * throws Node's ERR_INVALID_ARG_TYPE for `name`; other values read as + * sign and verify read their keys. */ +static bool sp_dh_key(const ScrDyn *value, const char *name, bool private_key, SpSigKey *out) { + out->key = NULL; + /* Node's key preparation destructures the value before it checks it. */ + if (value->kind == SCR_DYN_NULL) { + scr_pki_throw_fmt(SCR_ERR_TYPE, NULL, "Cannot destructure property 'key' of 'key' as it is null."); + return false; + } + bool keyish = value->kind == SCR_DYN_STR || value->kind == SCR_DYN_BYTES || scr_buffer_storage_is(value) || + (value->kind != SCR_DYN_NULL && value->kind != SCR_DYN_FUNC && scr_dyn_is_object(value)); + if (!keyish) { + scr_dyn_prop_type_fail(name, private_key ? sp_dh_private_types : sp_dh_public_types, value); + return false; + } + return sp_sig_key(value, private_key, out); +} + +/* The shared secret of two keys of one type: X25519, or ECDH's x + * coordinate at the field's size. NULL with OpenSSL's error pending. */ +static void sp_dh_failed(bool props) { + sp_throw_openssl("1C8000A4", "Provider routines", "failed during derivation", "ERR_OSSL_FAILED_DURING_DERIVATION", + props); +} + +static ScrDyn *sp_dh_compute(SpKey *priv, SpKey *pub, bool props) { + if (sp_is_x(priv) && sp_is_x(pub)) { + uint8_t out[32]; + if (!sp_x25519(priv->ed_seed, pub->ed_pub, out)) { + sp_dh_failed(props); + return NULL; + } + ScrDyn *secret = sp_dyn_buffer(out, 32); + mbedtls_platform_zeroize(out, sizeof out); + return secret; + } + if (!sp_is_ec(priv) || !sp_is_ec(pub)) { + sp_throw_openssl("03000096", "digital envelope routines", "operation not supported for this keytype", + "ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", props); + return NULL; + } + mbedtls_ecp_keypair *a = mbedtls_pk_ec(priv->pk); + const mbedtls_ecp_keypair *b = mbedtls_pk_ec(pub->pk); + if (a->MBEDTLS_PRIVATE(grp).id != b->MBEDTLS_PRIVATE(grp).id) { + sp_throw_openssl("1C8000CB", "Provider routines", "mismatching domain parameters", + "ERR_OSSL_MISMATCHING_DOMAIN_PARAMETERS", props); + return NULL; + } + mbedtls_mpi z; + mbedtls_mpi_init(&z); + unsigned char out[66]; + size_t n = (a->MBEDTLS_PRIVATE(grp).pbits + 7) / 8; + bool ok = mbedtls_ecdh_compute_shared(&a->MBEDTLS_PRIVATE(grp), &z, &b->MBEDTLS_PRIVATE(Q), + &a->MBEDTLS_PRIVATE(d), sp_random, NULL) == 0 && + mbedtls_mpi_write_binary(&z, out, n) == 0; + mbedtls_mpi_free(&z); + ScrDyn *secret = ok ? sp_dyn_buffer(out, n) : NULL; + mbedtls_platform_zeroize(out, sizeof out); + if (!secret) sp_dh_failed(props); + return secret; +} + +static void sp_dh_deliver(ScrClosure *self) { + ScrDyn *callback = sp_cap(self, 0); + ScrDyn *secret = sp_cap(self, 1); + ScrDyn *args[2] = {scr_dyn_new_null(), secret}; + ScrDyn *result = scr_dyn_call(callback, args, 2, "callback"); + if (result) scr_dyn_release(result); + scr_dyn_release(args[0]); + scr_dyn_release(secret); + scr_dyn_release(callback); +} + +/* crypto.diffieHellman(options[, callback]) in Node's order: the options + * object, the callback, both keys present, the key types when both are + * KeyObjects, then the public and private key inputs. With a callback, + * the secret, or the error of computing it, arrives in a later macrotask. */ +ScrDyn *scr_crypto_diffie_hellman(ScrDyn *options, ScrDyn *callback) { + ScrDyn *plain = sp_plain(options); + if (!plain) return NULL; + ScrDyn *result = NULL; + SpSigKey pub = {0}, priv = {0}; + if (!sp_check_object(plain, "options")) goto done; + bool async = !sp_absent(callback); + if (async && !scr_dyn_is_callable(callback)) { + scr_dyn_arg_type_fail("callback", "of type function", callback); + goto done; + } + const ScrDyn *private_value = sp_opt(plain, "privateKey"); + const ScrDyn *public_value = sp_opt(plain, "publicKey"); + if (sp_absent(private_value)) { + sp_property_invalid("options.privateKey", private_value); + goto done; + } + if (sp_absent(public_value)) { + sp_property_invalid("options.publicKey", public_value); + goto done; + } + SpKey *a = sp_key_of(private_value), *b = sp_key_of(public_value); + if (a && b && a->private_key) { + const char *ta = sp_type_name(a), *tb = sp_type_name(b); + if (strcmp(ta, tb) != 0 || (!sp_is_ec(a) && !sp_is_x(a))) { + scr_pki_throw_fmt(SCR_ERR_ERROR, "ERR_CRYPTO_INCOMPATIBLE_KEY", + "Incompatible key types for Diffie-Hellman: %s and %s", ta, tb); + goto done; + } + } + if (!sp_dh_key(public_value, "options.publicKey", false, &pub) || + !sp_dh_key(private_value, "options.privateKey", true, &priv)) + goto done; + ScrDyn *secret = sp_dh_compute(priv.key, pub.key, !async); + if (!async) { + result = secret; + goto done; + } + void *deliver = (void *)sp_dh_deliver; + if (!secret) { + ScrCaught *caught = scr_exc_take(); + secret = scr_caught_to_dyn(caught); + scr_caught_release(caught); + deliver = (void *)sp_key_pair_fail; + } + ScrClosure *c = scr_closure_new(deliver, 2); + c->caps[0] = scr_box_new_obj(scr_dyn_retain_v, scr_dyn_release_v, NULL); + scr_box_set_ref(c->caps[0], scr_dyn_retain(callback)); + c->caps[1] = scr_box_new_obj(scr_dyn_retain_v, scr_dyn_release_v, NULL); + scr_box_set_ref(c->caps[1], secret); + scr_loop_defer(c); + result = scr_dyn_retain(scr_dyn_undefined()); +done: + if (pub.key) sp_key_release(pub.key); + if (priv.key) sp_key_release(priv.key); + scr_dyn_release(plain); + return result; +} + /* ── the core for the WebCrypto layer (scr_pki.h) ─────────────────────── */ ScrPkiKey *scr_pki_key_retain(ScrPkiKey *key) { return sp_key_retain(key); } diff --git a/packages/runtime/src/scr_publish.c b/packages/runtime/src/scr_publish.c index 4767122dd..afbe4d6af 100644 --- a/packages/runtime/src/scr_publish.c +++ b/packages/runtime/src/scr_publish.c @@ -527,62 +527,12 @@ void scr_throw_published_field(const char *prop, const char *owner) { scr_pub_throw("Cannot assign to read only property '%s' of object '#<%s>'", prop, owner); } -static void scr_pub_index_text(double index, char *out, size_t size) { - char buf[64]; - size_t n = scr_f64_to_str(index, buf); - if (n > size - 1) n = size - 1; - memcpy(out, buf, n); - out[n] = 0; +bool scr_throw_published_array(int op, const ScrArr *a, double index, double n) { + return scr_array_frozen_error(op, a->len, index, n, 0); } -/* op: 0 index write, 1 push (n = argument count), 2 pop, 3 length write, - * 4 unshift (n = argument count), 5 delete, 6 shift, 7 other in-place - * rewrites (splice, reverse, fill, copyWithin, sort). Returns false when - * Node's operation would not write the frozen array at all (it then is not - * an error: e.g. reversing an empty array). */ -bool scr_throw_published_array(int op, const ScrArr *a, double index, double n) { - static const char length[] = "Cannot assign to read only property 'length' of object '[object Array]'"; - char text[64]; - size_t len = a->len; - switch (op) { - case 0: - scr_pub_index_text(index, text, sizeof text); - if (index >= 0 && index < (double)len && index == (double)(size_t)index) - scr_pub_throw("Cannot assign to read only property '%s' of object '[object Array]'%s", text, ""); - else scr_pub_throw("Cannot add property %s, object is not extensible%s", text, ""); - return true; - case 1: - if (n == 0) break; - scr_pub_index_text((double)len, text, sizeof text); - scr_pub_throw("Cannot add property %s, object is not extensible%s", text, ""); - return true; - case 2: - case 6: - if (len == 0) break; - if (op == 6 && len > 1) { - scr_pub_throw("Cannot assign to read only property '0' of object '[object Array]'%s%s", "", ""); - return true; - } - scr_pub_index_text((double)(len - 1), text, sizeof text); - scr_pub_throw("Cannot delete property '%s' of [object Array]%s", text, ""); - return true; - case 3: break; - case 4: - if (n == 0) break; - scr_pub_index_text(len ? (double)len + n - 1 : 0, text, sizeof text); - scr_pub_throw("Cannot add property %s, object is not extensible%s", text, ""); - return true; - case 5: - scr_pub_index_text(index, text, sizeof text); - scr_pub_throw("Cannot delete property '%s' of [object Array]%s", text, ""); - return true; - default: - if (len == 0) return false; - scr_pub_throw("Cannot assign to read only property '0' of object '[object Array]'%s%s", "", ""); - return true; - } - scr_throw_error_msg(SCR_ERR_TYPE, length, sizeof length - 1); - return true; +bool scr_throw_published_array_range(int op, const ScrArr *a, double x, double y, double z) { + return scr_array_frozen_error(op, a->len, x, y, z); } void scr_throw_published_collection(bool set) { diff --git a/packages/runtime/src/scr_rc.h b/packages/runtime/src/scr_rc.h index 18df3a237..4d8e059a9 100644 --- a/packages/runtime/src/scr_rc.h +++ b/packages/runtime/src/scr_rc.h @@ -45,14 +45,32 @@ typedef size_t ScrRcCount; /* Whether `obj`, an object of any kind, is published (immortal). */ #define SCR_RC_PUBLISHED(obj) (SCR_RC_COUNT(obj) >= SCR_RC_PUBLISHED_MIN) +/* Frozen records (Object.freeze). The rc word of a record a program may + * freeze (llvm/shapes.ts) carries SCR_RC_FROZEN beside its count, and that + * record's emitted release ignores the bit. On 64-bit targets the bit is in + * the high half, outside the count; on 32-bit targets it is the count's bit + * 30, which SCR_RC_REFS masks. An immortal word has the bit set too; it + * does not read as frozen. */ +#if SIZE_MAX > UINT32_MAX +#define SCR_RC_FROZEN ((size_t)1 << 62) +#define SCR_RC_REFS(obj) SCR_RC_COUNT(obj) +#else +#define SCR_RC_FROZEN ((size_t)1 << 30) +#define SCR_RC_REFS(obj) (SCR_RC_COUNT(obj) == SCR_RC_IMMORTAL ? SCR_RC_IMMORTAL : SCR_RC_COUNT(obj) & ~SCR_RC_FROZEN) +#endif +static inline bool scr_rc_frozen(const void *obj) { + size_t word = *(const size_t *)obj; + return word != SIZE_MAX && (word & SCR_RC_FROZEN) != 0; +} + /* The class id in the high half of `obj`'s rc word, or 0 for an object - * without one: the high half of any other object's word is 0, or 0xffffffff - * (immortal) or 0x80000000 (program-lifetime), and a class id is never one of - * these. */ + * without one: the high half of any other object's word is 0, 0xffffffff + * (immortal), 0x80000000 (program-lifetime) or 0x40000000 (a frozen record), + * and a class id is never one of these. */ static inline uint32_t scr_rc_class_id(const void *obj) { #if SIZE_MAX > UINT32_MAX uint32_t high = (uint32_t)(*(const size_t *)obj >> 32); - return high == UINT32_MAX || high == UINT32_C(0x80000000) ? 0 : high; + return high == UINT32_MAX || high == UINT32_C(0x80000000) || high == UINT32_C(0x40000000) ? 0 : high; #else (void)obj; return 0; @@ -552,6 +570,14 @@ _Static_assert(sizeof(((ScrCycHdr *)0)->gen) == 1 && SCR_CYC_OLD == 2, * depth; deeper objects wait until the outer disposal drains the worklist. * The callback owns the object and must release its children and free it. */ void scr_rc_destroy(void *obj, void (*destroy)(void *)); +/* scr_rc_destroy for an emitted teardown that keeps the blocks it frees in + * a segment, linked through their first words, instead of freeing them onto + * the free list (llvm/shapes.ts): destroy_seg tears obj down, appending its + * blocks after the segment's last link `tail`, and returns the new last + * link, which this returns. A disposal past the depth budget is queued with + * `destroy`, which frees its own blocks, and leaves `tail` as it was. */ +void *scr_rc_destroy_seg(void *obj, void *(*destroy_seg)(void *, void *), + void (*destroy)(void *), void *tail); /* RC hooks for cycle-headered types. on_release: rc was decremented and * stayed above zero — buffer the object as a possible cycle root (may run @@ -725,12 +751,14 @@ void scr_pub_dyn_fn(void *v, ScrPub *ctx); void scr_pub_arr(void *v, ScrPub *ctx, ScrPubFn elem); void scr_pub_map(void *v, ScrPub *ctx, ScrPubFn key, ScrPubFn val); void scr_publish_refuse(const char *message); -/* Writes into published objects (emitted guards): Node's frozen-object - * TypeErrors. scr_throw_published_array answers false (and throws nothing) - * when Node's operation would not write the array. */ +/* Writes into published or frozen objects (emitted guards): Node's + * frozen-object TypeErrors. The array guards answer false (and throw + * nothing) when Node's operation would not write the array; their ops are + * scr_array_frozen_error's (scr_array.h). */ struct ScrArr; SCR_COLD void scr_throw_published_field(const char *prop, const char *owner); bool scr_throw_published_array(int op, const struct ScrArr *a, double index, double n); +bool scr_throw_published_array_range(int op, const struct ScrArr *a, double x, double y, double z); SCR_COLD void scr_throw_published_collection(bool set); /* Young-in nursery (scr_cycle.c built with SCR_YIN, linked by programs diff --git a/packages/runtime/src/scr_stream.c b/packages/runtime/src/scr_stream.c index cd95e12cd..ec061e10c 100644 --- a/packages/runtime/src/scr_stream.c +++ b/packages/runtime/src/scr_stream.c @@ -543,6 +543,10 @@ typedef struct ScrStreamTick { ScrError *err; /* owned or NULL (SCR_ST_ERROR) */ ScrClosure *cb; /* owned or NULL (SCR_ST_AFTER_WRITE / SCR_ST_WCB_ERR / SCR_ST_WRITE_ERR) */ bool hold; /* SCR_ST_AFTER_WRITE: counted in w.after_holds */ + /* The AsyncLocalStorage context active when the tick was scheduled (+1, + * NULL outside a store): Node schedules these steps with + * process.nextTick, so their events and callbacks run in it. */ + ScrAlsCtx *ctx; struct ScrStreamTick *next; } ScrStreamTick; @@ -559,6 +563,7 @@ static void scr_st_tick(ScrStream *s, ScrStreamTickOp op, ScrError *err /*moves* t->op = op; t->err = err; t->cb = cb; + t->ctx = scr_als_capture(); if (scr_st_tail) scr_st_tail->next = t; else scr_st_head = t; scr_st_tail = t; @@ -7835,6 +7840,21 @@ static void scr_stream_run_tick(ScrStreamTick *t) { static void scr_stream_dispatch(void); +/* A tick runs in the context it was scheduled in. */ +static void scr_stream_run_tick_in_context(ScrStreamTick *t) { + ScrAlsCtx *prev = scr_als_dispatch_enter(t->ctx); + scr_stream_run_tick(t); + scr_als_dispatch_leave(prev); +} + +static void scr_stream_tick_free(ScrStreamTick *t) { + scr_stream_release(t->s); + if (t->err) scr_error_release(t->err); + if (t->cb) scr_closure_release(t->cb); + scr_als_ctx_release(t->ctx); + free(t); +} + /* One tick-marker's dispatch: the queue's FIFO head (markers and entries * are enqueued 1:1 in the same order). After an uncaught throw the * remaining entries drop through the station's exc branch below, keeping @@ -7844,11 +7864,8 @@ static void scr_stream_dispatch_one(void) { if (t == NULL || scr_exc_pending()) return; scr_st_head = t->next; if (scr_st_head == NULL) scr_st_tail = NULL; - scr_stream_run_tick(t); - scr_stream_release(t->s); - if (t->err) scr_error_release(t->err); - if (t->cb) scr_closure_release(t->cb); - free(t); + scr_stream_run_tick_in_context(t); + scr_stream_tick_free(t); if (scr_exc_pending()) scr_stream_dispatch(); /* its exc branch drops the rest */ } @@ -7857,11 +7874,8 @@ static void scr_stream_dispatch(void) { ScrStreamTick *t = scr_st_head; scr_st_head = t->next; if (scr_st_head == NULL) scr_st_tail = NULL; - scr_stream_run_tick(t); - scr_stream_release(t->s); - if (t->err) scr_error_release(t->err); - if (t->cb) scr_closure_release(t->cb); - free(t); + scr_stream_run_tick_in_context(t); + scr_stream_tick_free(t); } /* An uncaught throw ends the process (the loop returns to main): the * undelivered ticks are DROPPED — release their references here so the @@ -7870,10 +7884,7 @@ static void scr_stream_dispatch(void) { while (scr_st_head != NULL) { ScrStreamTick *t = scr_st_head; scr_st_head = t->next; - scr_stream_release(t->s); - if (t->err) scr_error_release(t->err); - if (t->cb) scr_closure_release(t->cb); - free(t); + scr_stream_tick_free(t); } scr_st_tail = NULL; } diff --git a/packages/runtime/src/scr_string.c b/packages/runtime/src/scr_string.c index f94554642..37b92e48c 100644 --- a/packages/runtime/src/scr_string.c +++ b/packages/runtime/src/scr_string.c @@ -1928,6 +1928,22 @@ static __attribute__((noinline)) double scr_str_unit_search(const ScrStr *s, con /* Search backwards from a clamped UTF-16 position. A low-surrogate position * maps to its scalar's first byte, so that scalar remains searchable. */ double scr_str_last_index_of_from(ScrStr *s, ScrStr *needle, double position) { + /* A proven-ASCII receiver: byte offsets are UTF-16 indexes, and a needle + * with other bytes (including lone surrogate halves) is never found. */ + if (s->ascii) { + size_t start = isnan(position) || position >= (double)s->len ? s->len + : position <= 0 ? 0 : (size_t)trunc(position); + if (needle->len == 0) return (double)start; + if (needle->len > s->len) return -1.0; + size_t max_start = s->len - needle->len; + if (start < max_start) max_start = start; + char first = needle->data[0]; + for (size_t i = max_start + 1; i-- > 0;) { + if (s->data[i] == first && memcmp(s->data + i, needle->data, needle->len) == 0) + return (double)i; + } + return -1.0; + } ScrSidx *e = scr_sidx_for(s); size_t len16 = scr_sidx_len(s, e); size_t start16 = isnan(position) || position >= (double)len16 ? len16 diff --git a/packages/runtime/src/scr_symbol.c b/packages/runtime/src/scr_symbol.c index 647c31dc3..727e008b5 100644 --- a/packages/runtime/src/scr_symbol.c +++ b/packages/runtime/src/scr_symbol.c @@ -90,6 +90,11 @@ ScrSym *scr_sym_well_known(ScrStr *name) { scr_dyn_install_has_instance_symbol(key); scr_dyn_release(key); } + if (name->len == 11 && !memcmp(name->data, "toStringTag", 11)) { + ScrDyn *key = scr_dyn_new_symbol(symbol); + scr_dyn_install_to_string_tag_symbol(key); + scr_dyn_release(key); + } if (name->len == 13 && !memcmp(name->data, "asyncIterator", 13)) { ScrDyn *key = scr_dyn_new_symbol(symbol); scr_dyn_install_async_iterator_symbol(key); diff --git a/packages/runtime/src/scr_text_codec_dyn.c b/packages/runtime/src/scr_text_codec_dyn.c new file mode 100644 index 000000000..5102bc558 --- /dev/null +++ b/packages/runtime/src/scr_text_codec_dyn.c @@ -0,0 +1,215 @@ +/* WHATWG TextEncoder and TextDecoder instances as checked-dynamic values + * (SCR_DYNH_TEXT_ENCODER, SCR_DYNH_TEXT_DECODER). A codec is a compiled + * record (type-mapper.ts): the handle holds that record by reference, so + * a codec stored in an untyped slot and read back keeps its identity and + * a decoder keeps its streaming state. The compiler passes the record's + * reference-count entry points and, for decoders, the byte offsets of the + * encoding, fatal, ignoreBOM and state fields when it boxes one. A unit + * of its own so programs that never box a codec link none of it + * (native-toolchain gating: the textCodecDyn runtime feature). */ +#include "scr_platform.h" +#include "scr_runtime.h" + +#include + +typedef struct { + void *(*retain)(void *); + void (*release)(void *); + size_t encoding, fatal, ignore_bom, state; +} ScrTextCodecLayout; + +/* A program has one record shape per codec class, so one layout each. */ +static SCR_TL ScrTextCodecLayout scr_tcd_encoder_layout, scr_tcd_decoder_layout; + +static void *scr_tcd_encoder_retain(void *rec) { return scr_tcd_encoder_layout.retain(rec); } +static void scr_tcd_encoder_release(void *rec) { scr_tcd_encoder_layout.release(rec); } +static void *scr_tcd_decoder_retain(void *rec) { return scr_tcd_decoder_layout.retain(rec); } +static void scr_tcd_decoder_release(void *rec) { scr_tcd_decoder_layout.release(rec); } + +static void scr_tcd_codec_not_a_function(const char *what) { + ScrJsonBuf b; + scr_jb_init(&b); + scr_jb_puts(&b, what); + scr_jb_puts(&b, " is not a function"); + scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b)); +} + +/* Boxes an owned string (+1 box; the string's reference moves in). */ +static ScrDyn *scr_tcd_str(ScrStr *text) { + ScrDyn *boxed = scr_dyn_new_str(text); + scr_str_release(text); + return boxed; +} + +static ScrDyn *scr_tcd_codec_arg(ScrDyn *const *args, size_t argc, size_t index) { + return index < argc ? args[index] : scr_dyn_undefined(); +} + +static double scr_tcd_decoder_encoding(const void *rec) { + double encoding; + memcpy(&encoding, (const char *)rec + scr_tcd_decoder_layout.encoding, sizeof encoding); + return encoding; +} + +static bool scr_tcd_decoder_flag(const void *rec, size_t offset) { + return ((const unsigned char *)rec)[offset] != 0; +} + +static ScrDyn *scr_tcd_encoder_invoke(void *rec, ScrDyn *self, const char *method, + ScrDyn *const *args, size_t argc, const char *what) { + (void)rec; + (void)self; + if (strcmp(method, "encode") == 0) { + /* encode(input = ""): any other input converts with ToString. */ + ScrDyn *input = scr_tcd_codec_arg(args, argc, 0); + ScrStr *text = + input->kind == SCR_DYN_UNDEF ? scr_str_new("", 0) : scr_dyn_string_coerce_js(input); + if (!text) return NULL; + ScrStr *utf8 = scr_str_new("utf8", 4); + ScrBytes *bytes = scr_bytes_from_str(text, utf8); + scr_str_release(utf8); + scr_str_release(text); + /* A fresh Uint8Array, not a Buffer. */ + bytes->is_buffer = false; + ScrDyn *result = scr_dyn_new_bytes(bytes); + scr_bytes_release(bytes); + return result; + } + if (strcmp(method, "encodeInto") == 0) + return scr_text_encode_into(scr_tcd_codec_arg(args, argc, 0), + scr_tcd_codec_arg(args, argc, 1)); + scr_tcd_codec_not_a_function(what); + return NULL; +} + +static ScrDyn *scr_tcd_encoder_get(void *rec, const char *key, size_t len) { + (void)rec; + if (len == 8 && memcmp(key, "encoding", 8) == 0) return scr_tcd_str(scr_str_new("utf-8", 5)); + return NULL; +} + +/* decode(input, options): the options object's `stream` member, read + * with JS truthiness; undefined and null mean no options. */ +static bool scr_tcd_decoder_stream_option(const ScrDyn *options, bool *stream) { + *stream = false; + if (options->kind == SCR_DYN_UNDEF || options->kind == SCR_DYN_NULL) return true; + if (options->kind != SCR_DYN_OBJ && options->kind != SCR_DYN_ARR && + options->kind != SCR_DYN_FUNC && options->kind != SCR_DYN_TYPED_REF && + options->kind != SCR_DYN_HANDLE) { + scr_dyn_arg_type_fail("options", "of type object", options); + return false; + } + ScrDyn *value = scr_dyn_obj_read(options, "stream", 6); + if (!value) return false; + *stream = scr_dyn_truthy(value); + scr_dyn_release(value); + return !scr_exc_pending(); +} + +static ScrDyn *scr_tcd_decoder_invoke(void *rec, ScrDyn *self, const char *method, + ScrDyn *const *args, size_t argc, const char *what) { + (void)self; + if (strcmp(method, "decode") != 0) { + scr_tcd_codec_not_a_function(what); + return NULL; + } + ScrBytes *input = scr_bytes_buffer_source(scr_tcd_codec_arg(args, argc, 0)); + if (!input) return NULL; + bool stream; + if (!scr_tcd_decoder_stream_option(scr_tcd_codec_arg(args, argc, 1), &stream)) { + scr_bytes_release(input); + return NULL; + } + const ScrTextCodecLayout *layout = &scr_tcd_decoder_layout; + ScrDyn *state; + memcpy(&state, (const char *)rec + layout->state, sizeof state); + ScrStr *text = scr_text_decode_stream(state, input, scr_tcd_decoder_encoding(rec), + scr_tcd_decoder_flag(rec, layout->fatal), + scr_tcd_decoder_flag(rec, layout->ignore_bom), stream); + scr_bytes_release(input); + return text ? scr_tcd_str(text) : NULL; +} + +static ScrDyn *scr_tcd_decoder_get(void *rec, const char *key, size_t len) { + if (len == 8 && memcmp(key, "encoding", 8) == 0) + return scr_tcd_str(scr_text_decoder_name(scr_tcd_decoder_encoding(rec))); + if (len == 5 && memcmp(key, "fatal", 5) == 0) + return scr_dyn_new_bool(scr_tcd_decoder_flag(rec, scr_tcd_decoder_layout.fatal)); + if (len == 9 && memcmp(key, "ignoreBOM", 9) == 0) + return scr_dyn_new_bool(scr_tcd_decoder_flag(rec, scr_tcd_decoder_layout.ignore_bom)); + return NULL; +} + +static bool scr_tcd_codec_set(void *rec, const char *key, size_t len, const ScrDyn *value) { + (void)rec; + (void)key; + (void)len; + (void)value; + return false; +} + +static const ScrDynHandleMethod scr_tcd_encoder_methods[] = { + {.name = "encode", .length = 0}, + {.name = "encodeInto", .length = 2}, + {.name = NULL}, +}; + +static const ScrDynHandleMethod scr_tcd_decoder_methods[] = { + {.name = "decode", .length = 0}, + {.name = NULL}, +}; + +static const ScrDynHandleOps scr_tcd_encoder_ops = { + .cls = "TextEncoder", + .to_string_tag = "TextEncoder", + .retain = scr_tcd_encoder_retain, + .release = scr_tcd_encoder_release, + .invoke = scr_tcd_encoder_invoke, + .get = scr_tcd_encoder_get, + .set = scr_tcd_codec_set, + .json_empty_object = true, + .methods = scr_tcd_encoder_methods, + .invalid_this = "Value of \"this\" must be of type TextEncoder", +}; + +static const ScrDynHandleOps scr_tcd_decoder_ops = { + .cls = "TextDecoder", + .to_string_tag = "TextDecoder", + .retain = scr_tcd_decoder_retain, + .release = scr_tcd_decoder_release, + .invoke = scr_tcd_decoder_invoke, + .get = scr_tcd_decoder_get, + .set = scr_tcd_codec_set, + .json_empty_object = true, + .methods = scr_tcd_decoder_methods, + .invalid_this = "Value of \"this\" must be of type TextDecoder", +}; + +ScrDyn *scr_text_encoder_to_dyn(void *rec, void *(*retain)(void *), void (*release)(void *)) { + if (!scr_tcd_encoder_layout.retain) { + scr_tcd_encoder_layout.retain = retain; + scr_tcd_encoder_layout.release = release; + scr_dyn_handle_install(SCR_DYNH_TEXT_ENCODER, &scr_tcd_encoder_ops); + } + return scr_dyn_new_handle(rec, SCR_DYNH_TEXT_ENCODER); +} + +ScrDyn *scr_text_decoder_to_dyn(void *rec, void *(*retain)(void *), void (*release)(void *), + size_t encoding, size_t fatal, size_t ignore_bom, size_t state) { + if (!scr_tcd_decoder_layout.retain) { + scr_tcd_decoder_layout = (ScrTextCodecLayout){retain, release, encoding, fatal, ignore_bom, state}; + scr_dyn_handle_install(SCR_DYNH_TEXT_DECODER, &scr_tcd_decoder_ops); + } + return scr_dyn_new_handle(rec, SCR_DYNH_TEXT_DECODER); +} + +void *scr_text_codec_from_dyn(const ScrDyn *d, bool decoder, const ScrDynPath *path, + const char *want) { + return scr_dyn_handle_unbox(d, decoder ? SCR_DYNH_TEXT_DECODER : SCR_DYNH_TEXT_ENCODER, path, + want); +} + +bool scr_text_codec_is(const ScrDyn *d, bool decoder) { + return d->kind == SCR_DYN_HANDLE && + d->v.handle.tag == (decoder ? SCR_DYNH_TEXT_DECODER : SCR_DYNH_TEXT_ENCODER); +} diff --git a/packages/runtime/src/scr_url.c b/packages/runtime/src/scr_url.c index 67de82471..09ccb87fe 100644 --- a/packages/runtime/src/scr_url.c +++ b/packages/runtime/src/scr_url.c @@ -6,7 +6,7 @@ * against Node by the differential corpus: * - scheme lowercasing; "Invalid URL" TypeError on schemeless input * - special schemes (http/https/ws/wss/ftp/file): authority parsing with - * host lowercasing, default-port removal (leading zeros stripped), + * percent-decoded, lowercased hosts, default-port removal (leading zeros stripped), * canonical bracketed IPv6 literals, * backslash-as-slash, and any run of leading slashes tolerated * (http:foo.com works); file: takes an authority only after exactly @@ -17,13 +17,14 @@ * (space, quotes, angle brackets, braces, non-ASCII UTF-8 bytes, ...); * existing %-sequences pass through verbatim (no decode, no case * normalization — like Node) - * - non-special schemes: "//" parses an authority (host case preserved), - * a single "/" roots a normalized path, anything else is an OPAQUE - * path kept verbatim (data:text/plain,hi there) + * - non-special schemes: "//" parses an authority (an opaque host, + * case preserved, C0-encoded), a single "/" roots a normalized path, + * anything else is an OPAQUE path with the C0 control set encoded + * (data:text/plain,hi there) * - * DOCUMENTED DIVERGENCES (SEMANTICS.md): non-ASCII and %-escaped hosts are - * rejected with "Invalid URL" (no IDNA/punycode), and opaque paths skip - * the spec's C0-encode pass (kept verbatim). + * DOCUMENTED DIVERGENCE: a special host that holds non-ASCII text after + * percent-decoding is rejected with "Invalid URL" (no UTS #46 processing), + * and "xn--" labels are kept without Punycode validation. * * Failures THROW catchable TypeErrors through the exception cell with * Node's messages; callers are compiler-emitted pending checks. */ @@ -176,7 +177,7 @@ static bool enc_query(unsigned char c, bool special) { } static bool enc_path(unsigned char c) { - return enc_query(c, false) || c == '?' || c == '`' || c == '{' || c == '}'; + return enc_query(c, false) || c == '?' || c == '^' || c == '`' || c == '{' || c == '}'; } static bool enc_userinfo(unsigned char c) { @@ -392,17 +393,58 @@ static bool url_ipv4_parse(const char *s, size_t n, UrlBuf *out) { return true; } -/* Shared constructor/setter host parsing keeps the existing ASCII-host - * boundary and canonical IPv6 serialization in both execution tiers. A - * special host that ends in a number is an IPv4 address: it serializes - * as dotted decimal, or the URL is invalid. */ +/* Byte classes for the host parser: URL_FORBIDDEN_HOST marks the URL + * Standard's forbidden host code points (opaque hosts), URL_FORBIDDEN_DOMAIN + * its forbidden domain code points (those plus the C0 controls, '%' and + * U+007F) and every non-ASCII byte, which a special host cannot hold + * without UTS #46 processing. */ +enum { URL_FORBIDDEN_HOST = 1, URL_FORBIDDEN_DOMAIN = 2 }; + +static const unsigned char url_host_byte_class[256] = { + 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 0, 0, 3, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 3, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static bool enc_c0_control(unsigned char c) { + return c < 0x20 || c > 0x7E; +} + +static int hex_val(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} + +/* The URL Standard's host parser for the constructor and the setters, + * with canonical IPv6 serialization: + * - a non-special (opaque) host refuses forbidden host code points and + * percent-encodes the C0 control set; + * - a special host is percent-decoded, lowercased, refused when it holds a + * forbidden domain code point, and serialized as dotted decimal when it + * ends in a number (or refused when that number is invalid). + * Divergence: a special host that decodes to non-ASCII needs UTS #46 + * processing, which this unit does not link, so it is refused; "xn--" + * labels are kept without Punycode validation. */ static bool parse_host(const char *hp, size_t host_len, bool special, bool is_file, ScrStr **host) { bool ipv6 = host_len > 0 && hp[0] == '['; size_t ipv6_end = host_len ? host_len - 1 : 0; if (ipv6 && (host_len < 3 || hp[ipv6_end] != ']')) return false; - /* Validate + lowercase (special) the host. Divergence: non-ASCII and - * %-escapes (IDNA territory) are rejected outright. */ UrlBuf hb; ub_init(&hb); if (ipv6) { @@ -410,22 +452,37 @@ static bool parse_host(const char *hp, size_t host_len, bool special, bool is_fi free(hb.data); return false; } - } else { + *host = ub_take(&hb); + return true; + } + if (!special) { for (size_t i = 0; i < host_len; i++) { unsigned char c = (unsigned char)hp[i]; - if (c >= 0x80 || c == '%' || c == ':' || c == '[' || c == ']' || - c <= 0x20 || c == '#' || c == '/' || c == '<' || c == '>' || - c == '?' || c == '@' || c == '\\' || c == '^' || c == '|') { + if (url_host_byte_class[c] & URL_FORBIDDEN_HOST) { free(hb.data); return false; } - if (special && c >= 'A' && c <= 'Z') { - c = (unsigned char)(c - 'A' + 'a'); - } - ub_push(&hb, (char)c); + ub_push_encoded(&hb, c, enc_c0_control); } + *host = ub_take(&hb); + return true; } - if (!ipv6 && special && url_ends_in_number(hb.data, hb.len)) { + for (size_t i = 0; i < host_len; i++) { + unsigned char c = (unsigned char)hp[i]; + int hi, lo; + if (c == '%' && i + 2 < host_len && (hi = hex_val(hp[i + 1])) >= 0 && + (lo = hex_val(hp[i + 2])) >= 0) { + c = (unsigned char)(hi * 16 + lo); + i += 2; + } + if (url_host_byte_class[c] & URL_FORBIDDEN_DOMAIN) { + free(hb.data); + return false; + } + if (c >= 'A' && c <= 'Z') c = (unsigned char)(c - 'A' + 'a'); + ub_push(&hb, (char)c); + } + if (url_ends_in_number(hb.data, hb.len)) { UrlBuf ip; ub_init(&ip); bool ok = url_ipv4_parse(hb.data, hb.len, &ip); @@ -438,7 +495,7 @@ static bool parse_host(const char *hp, size_t host_len, bool special, bool is_fi } /* file: "localhost" normalizes to "" at parse time (Node). */ if (is_file && hb.len == 9 && memcmp(hb.data, "localhost", 9) == 0) hb.len = 0; - if (hb.len == 0 && special && !is_file) { + if (hb.len == 0 && !is_file) { free(hb.data); return false; /* http:// — special non-file needs a host */ } @@ -481,6 +538,13 @@ static bool parse_authority(const char *raw, size_t len, bool special, bool is_f } const char *hp = at >= 0 ? raw + at + 1 : raw; size_t hp_len = at >= 0 ? len - (size_t)at - 1 : len; + /* Credentials need a host. A file: authority is a host alone, so its + * '@' and ':' reach the host parser, which refuses them. */ + if (at >= 0 && hp_len == 0) return false; + if (is_file) { + *port = scr_str_new("", 0); + return at < 0 && parse_host(hp, hp_len, special, is_file, host); + } /* host[:port], with IPv6 literals bracketed per WHATWG. */ long colon = -1; size_t host_len = hp_len; @@ -500,6 +564,7 @@ static bool parse_authority(const char *raw, size_t len, bool special, bool is_f } host_len = colon >= 0 ? (size_t)colon : hp_len; } + if (colon >= 0 && host_len == 0) return false; if (!parse_host(hp, host_len, special, is_file, host)) return false; /* Port: digits only, leading zeros stripped, defaults dropped. */ UrlBuf pb; @@ -700,11 +765,18 @@ ScrUrl *scr_url_new(ScrStr *input) { port = scr_str_new("", 0); path = parse_rooted_path(body, body_len, false); } else { - /* Opaque path — kept verbatim (divergence: the spec C0-encodes). */ + /* Opaque path: the C0 control set is percent-encoded, and a space + * right before a query or fragment becomes %20. */ userinfo = scr_str_new("", 0); host = scr_str_new("", 0); port = scr_str_new("", 0); - path = scr_str_new(body, body_len); + UrlBuf ob; + ub_init(&ob); + bool trailing_space = qmark < rest_len && body_len > 0 && body[body_len - 1] == ' '; + size_t kept = trailing_space ? body_len - 1 : body_len; + for (size_t i = 0; i < kept; i++) ub_push_encoded(&ob, (unsigned char)body[i], enc_c0_control); + if (trailing_space) ub_append(&ob, "%20", 3); + path = ub_take(&ob); } /* Query and fragment, encoded and stored WITH their sigils. */ @@ -823,18 +895,22 @@ ScrUrl *scr_url_new_base(ScrStr *input, ScrStr *base_input) { bool drive = file && path_len >= 2 && url_drive_letter(raw, 2) && (path_len == 2 || raw[2] == '/' || raw[2] == '\\'); if (base->has_authority) { + /* A drive letter relative to a file: base keeps the base's host. */ ub_append(&absolute, "//", 2); - if (!drive) { - if (base->userinfo->len) { - ub_append(&absolute, base->userinfo->data, base->userinfo->len); - ub_push(&absolute, '@'); - } - ub_append(&absolute, base->host->data, base->host->len); - if (base->port->len) { - ub_push(&absolute, ':'); - ub_append(&absolute, base->port->data, base->port->len); - } + if (base->userinfo->len) { + ub_append(&absolute, base->userinfo->data, base->userinfo->len); + ub_push(&absolute, '@'); } + ub_append(&absolute, base->host->data, base->host->len); + if (base->port->len) { + ub_push(&absolute, ':'); + ub_append(&absolute, base->port->data, base->port->len); + } + } else if (!opaque) { + /* A host-less base path can start with "//"; the "/." prefix keeps + * the resolved path from reparsing as an authority (dot-segment + * removal drops it). */ + ub_append(&absolute, "/.", 2); } if (!path_len) { ub_append(&absolute, base->path->data, base->path->len); @@ -1279,13 +1355,6 @@ void scr_url_set_checked(ScrUrl *u, ScrStr *field, const ScrDyn *value) { /* ── the file-URL bridge ─────────────────────────────────────────────── */ -static int hex_val(char c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - return -1; -} - static bool url_file_scheme(ScrUrl *u) { if (u->scheme->len == 4 && memcmp(u->scheme->data, "file", 4) == 0) return true; scr_throw_error_msg_code(SCR_ERR_TYPE, "The URL must be of scheme file", 30, "ERR_INVALID_URL_SCHEME"); @@ -1394,6 +1463,22 @@ ScrStr *scr_url_to_path(ScrUrl *u) { #endif } +/* fs.existsSync and the fs.promises functions convert a URL path inside a + * try and turn its error into false or a rejection: the conversion's result + * as a value, the path string or the error. */ +ScrDyn *scr_url_to_path_value(ScrUrl *u) { + ScrStr *path = scr_url_to_path(u); + if (path) { + ScrDyn *value = scr_dyn_new_str(path); + scr_str_release(path); + return value; + } + ScrCaught *caught = scr_exc_take(); + ScrDyn *error = scr_caught_to_dyn(caught); + scr_caught_release(caught); + return error; +} + /* The string-receiver form parses first (Node accepts URL strings). */ ScrStr *scr_url_str_to_path(ScrStr *input) { ScrUrl *u = scr_url_new(input); @@ -1451,7 +1536,7 @@ static void scr_url_throw_arg_value(const char *reason, ScrStr *value) { * bindingUrl.pathToFileURL does, reusing the parser's rooted-path * machinery (WHATWG dot-segment removal with empty segments preserved, * backslash-as-slash when `backslash_sep`, the enc_path set): the bytes - * Node encodes BEYOND that set ('%', '[', ']', '^', '|' — and '\\' when + * Node encodes BEYOND that set ('%', '[', ']', '^', '|', '~' — and '\\' when * it is a path byte rather than a separator) pre-encode here so the * parser passes them through. */ static ScrStr *scr_url_encode_file_path(const char *raw, size_t len, bool backslash_sep) { @@ -1459,7 +1544,7 @@ static ScrStr *scr_url_encode_file_path(const char *raw, size_t len, bool backsl ub_init(&pre); for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)raw[i]; - if (c == '%' || c == '[' || c == ']' || c == '^' || c == '|' || + if (c == '%' || c == '[' || c == ']' || c == '^' || c == '|' || c == '~' || (c == '\\' && !backslash_sep)) { char hex[4]; snprintf(hex, sizeof hex, "%%%02X", c); @@ -1521,24 +1606,30 @@ static ScrUrl *scr_url_from_path_impl(ScrStr *path, bool win32) { scr_str_release(resolved); return NULL; } - /* The URL host parser lowercases ASCII and TERMINATES at the URL - * delimiters '/', '?', '#' (Node feeds the servername through - * set_hostname: "\\\\?\\C:\\x" gets an EMPTY host and stays a - * drive path). No IDNA (documented divergence — Node punycodes - * non-ASCII names), and names with ':' or '%' stay verbatim where - * Node aborts on a native set_hostname assertion. */ - UrlBuf host; - ub_init(&host); + /* The servername goes through the host parser, as Node's hostname + * setter does: it drops tabs and newlines, stops at the URL + * delimiters '/', '?' and '#' + * ("\\\\?\\C:\\x" gets an empty host and stays a drive path), + * and a host it refuses throws ERR_INVALID_URL. */ + UrlBuf name; + ub_init(&name); for (size_t i = prefix; i < (size_t)host_end; i++) { char c = resolved->data[i]; if (c == '/' || c == '?' || c == '#') break; - if (c >= 'A' && c <= 'Z') c = (char)(c + 32); - ub_push(&host, c); + if (c != '\t' && c != '\n' && c != '\r') ub_push(&name, c); + } + ScrStr *host = NULL; + bool valid = parse_host(name.data, name.len, true, true, &host); + free(name.data); + if (!valid) { + scr_str_release(resolved); + scr_url_throw_invalid(); + return NULL; } ScrStr *encoded = scr_url_encode_file_path(resolved->data + host_end, resolved->len - (size_t)host_end, true); scr_str_release(resolved); - return scr_url_new_file(ub_take(&host), encoded); + return scr_url_new_file(host, encoded); } } else { ScrArr *pack = scr_arr_new(SCR_ELEM_STR, 1); @@ -1649,10 +1740,117 @@ static bool scr_native_url_set(void *ptr, const char *key, size_t len, const Scr return true; } +/* util.inspect's view of a URL: the properties Node's URL inspector lists, + * in its order, with the live searchParams object. Past the depth limit + * the inspector returns the URL itself, which util.inspect prints as + * `URL {}` (it has no own properties). */ +static ScrDyn *scr_native_url_inspect_view(void *ptr) { + static const char *const keys[] = {"href", "origin", "protocol", "username", "password", + "host", "hostname", "port", "pathname", "search", + "searchParams", "hash"}; + ScrDyn *view = scr_dyn_new_obj(); + for (size_t i = 0; i < sizeof keys / sizeof keys[0]; i++) { + size_t len = strlen(keys[i]); + scr_dyn_obj_set(view, keys[i], len, scr_native_url_get(ptr, keys[i], len)); + } + return view; +} + +/* Node's urlToHttpOptions over a URL: protocol, hostname (an IPv6 literal + * without its brackets), hash, search, pathname, path (pathname and + * search), href, then port as a number when the URL has one and auth + * (the decoded username and password) when it has credentials. */ +ScrDyn *scr_url_to_http_options(ScrUrl *u) { + ScrDyn *options = scr_dyn_new_obj_null_proto(); + ScrStr *pathname = scr_url_pathname(u); + ScrStr *search = scr_url_search(u); + ScrStr *hostname = scr_url_hostname(u); + ScrStr *port = scr_url_port(u); + ScrStr *username = scr_url_username(u); + ScrStr *password = scr_url_password(u); + ScrStr *auth = NULL; + if (username->len || password->len) { + ScrStr *user = scr_str_decode_uri_component(username); + ScrStr *pass = user ? scr_str_decode_uri_component(password) : NULL; + if (pass) { + UrlBuf b; + ub_init(&b); + ub_append(&b, user->data, user->len); + ub_push(&b, ':'); + ub_append(&b, pass->data, pass->len); + auth = ub_take(&b); + } + scr_str_release(user); + scr_str_release(pass); + if (!auth) { + scr_str_release(pathname); + scr_str_release(search); + scr_str_release(hostname); + scr_str_release(port); + scr_str_release(username); + scr_str_release(password); + scr_dyn_release(options); + return NULL; + } + } +#define PUT_STR(key, value) \ + do { \ + ScrStr *text_ = (value); \ + scr_dyn_obj_set(options, key, sizeof key - 1, scr_dyn_new_str(text_)); \ + scr_str_release(text_); \ + } while (0) + PUT_STR("protocol", scr_url_protocol(u)); + if (hostname->len >= 2 && hostname->data[0] == '[') + PUT_STR("hostname", scr_str_new(hostname->data + 1, hostname->len - 2)); + else + PUT_STR("hostname", scr_str_retain(hostname)); + PUT_STR("hash", scr_url_hash(u)); + PUT_STR("search", scr_str_retain(search)); + PUT_STR("pathname", scr_str_retain(pathname)); + UrlBuf path; + ub_init(&path); + ub_append(&path, pathname->data, pathname->len); + ub_append(&path, search->data, search->len); + PUT_STR("path", ub_take(&path)); + PUT_STR("href", scr_url_href(u)); + if (port->len) { + double number = 0; + for (size_t i = 0; i < port->len; i++) number = number * 10 + (port->data[i] - '0'); + scr_dyn_obj_set(options, "port", 4, scr_dyn_new_num(number)); + } + if (auth) PUT_STR("auth", auth); +#undef PUT_STR + scr_str_release(pathname); + scr_str_release(search); + scr_str_release(hostname); + scr_str_release(port); + scr_str_release(username); + scr_str_release(password); + return options; +} + +ScrDyn *scr_url_to_http_options_dyn(const ScrDyn *value) { + if (scr_dyn_native_url_is(value)) return scr_url_to_http_options(value->v.handle.ptr); + if (!value || value->kind <= SCR_DYN_STR || value->kind == SCR_DYN_UNDEF || + value->kind == SCR_DYN_BIGINT || value->kind == SCR_DYN_SYMBOL) { + scr_dyn_arg_type_fail("url", "of type object", value); + return NULL; + } + static const char message[] = "url.urlToHttpOptions of an object that is not a URL is not supported yet"; + scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); + return NULL; +} + ScrDyn *scr_dyn_native_url(ScrUrl *value) { static const ScrDynHandleOps ops = { - "URL", &scr_url_retain_v, &scr_url_release_v, &scr_native_url_invoke, - &scr_native_url_get, &scr_native_url_set, NULL, NULL, + .cls = "URL", + .retain = &scr_url_retain_v, + .release = &scr_url_release_v, + .invoke = &scr_native_url_invoke, + .get = &scr_native_url_get, + .set = &scr_native_url_set, + .inspect_view = &scr_native_url_inspect_view, + .inspect_view_nested = true, }; scr_dyn_handle_install(SCR_DYNH_URL, &ops); return scr_dyn_new_handle(value, SCR_DYNH_URL); diff --git a/packages/runtime/src/scr_url.h b/packages/runtime/src/scr_url.h index 05e5702bb..c7c9c63c8 100644 --- a/packages/runtime/src/scr_url.h +++ b/packages/runtime/src/scr_url.h @@ -41,6 +41,12 @@ ScrStr *scr_url_href(ScrUrl *u); /* +1; also toString() */ void scr_url_set(ScrUrl *u, ScrStr *field, ScrStr *value); /* borrowed; href may throw */ bool scr_url_set_component(ScrUrl *u, ScrStr *field, ScrStr *value); /* borrowed; false for ignored writes */ void scr_url_set_checked(ScrUrl *u, ScrStr *field, const ScrDyn *value); /* borrowed; conversion may throw */ +/* url.urlToHttpOptions: Node's null-prototype options object for a URL. + * The checked-dynamic form throws ERR_INVALID_ARG_TYPE for a primitive or + * null and has no lowering for an object that is not a URL. Borrowed; +1, + * or NULL with the error pending. */ +ScrDyn *scr_url_to_http_options(ScrUrl *u); +ScrDyn *scr_url_to_http_options_dyn(const ScrDyn *value); /* node:url's legacy surface (scr_url_legacy.c — linked with the regex, * querystring and dyn-async units). url.parse() answers a checked-dynamic * Url instance; format() accepts a Url-shaped object, a string, or a @@ -64,6 +70,9 @@ ScrStr *scr_punycode_to_unicode(const ScrStr *input); ScrArr *scr_punycode_ucs2_decode(const ScrStr *input); ScrStr *scr_url_domain_to_unicode(const ScrDyn *domain, bool missing); ScrStr *scr_url_to_path(ScrUrl *u); /* +1, or throws */ +/* scr_url_to_path as a value: the path string, or the error it would throw + * (+1 dyn, never throws). */ +ScrDyn *scr_url_to_path_value(ScrUrl *u); ScrStr *scr_url_str_to_path(ScrStr *s); /* +1, or throws */ ScrUrl *scr_url_from_path(ScrStr *path); /* +1; throws on win32 UNC malformations only */ ScrUrl *scr_url_from_path_platform(ScrStr *path, bool windows); /* +1; explicit options.windows */ diff --git a/packages/runtime/src/scr_zlib.c b/packages/runtime/src/scr_zlib.c index ff09e7ea1..8756c3d17 100644 --- a/packages/runtime/src/scr_zlib.c +++ b/packages/runtime/src/scr_zlib.c @@ -133,6 +133,11 @@ bool scr_zlib_inflate_exact(const unsigned char *src, size_t src_len, * Modes are Node's node_zlib_mode numbers (DEFLATE 1 ... UNZIP 7). */ struct ScrZlibEngine { z_stream strm; + /* Another codec's engine (scr_zlib_engine_codec): its state answers the + * entries below, which skip the z_stream. */ + const ScrZlibCodec *codec; + void *codec_state; + uint32_t codec_avail_in, codec_avail_out; int mode; int level, window_bits, mem_level, strategy; int flush; @@ -172,6 +177,17 @@ ScrZlibEngine *scr_zlib_engine_new(int mode, int level, int window_bits, int mem return e; } +ScrZlibEngine *scr_zlib_engine_codec(int mode, const ScrZlibCodec *codec, void *state, + bool reject_garbage) { + ScrZlibEngine *e = calloc(1, sizeof *e); + if (!e) scr_zlib_oom(); + e->mode = mode; + e->codec = codec; + e->codec_state = state; + e->reject_garbage = reject_garbage; + return e; +} + static bool scr_zlib_engine_deflating(const ScrZlibEngine *e) { return e->mode == SCR_Z_DEFLATE || e->mode == SCR_Z_GZIP || e->mode == SCR_Z_DEFLATERAW; } @@ -229,6 +245,11 @@ static int scr_zlib_engine_reset_stream(ScrZlibEngine *e) { void scr_zlib_engine_work(ScrZlibEngine *e, int flush, const unsigned char *in, uint32_t in_len, unsigned char *out, uint32_t out_len) { + if (e->codec) { + e->codec->work(e->codec_state, flush, in, in_len, out, out_len, &e->codec_avail_in, + &e->codec_avail_out); + return; + } e->strm.avail_in = in_len; e->strm.next_in = (Bytef *)(uintptr_t)in; e->strm.avail_out = out_len; @@ -286,6 +307,11 @@ void scr_zlib_engine_work(ScrZlibEngine *e, int flush, const unsigned char *in, } void scr_zlib_engine_offsets(const ScrZlibEngine *e, uint32_t *avail_in, uint32_t *avail_out) { + if (e->codec) { + *avail_in = e->codec_avail_in; + *avail_out = e->codec_avail_out; + return; + } *avail_in = e->strm.avail_in; *avail_out = e->strm.avail_out; } @@ -313,6 +339,7 @@ static bool scr_zlib_engine_fail(const ScrZlibEngine *e, const char *message, Sc } bool scr_zlib_engine_error(const ScrZlibEngine *e, ScrZlibError *out) { + if (e->codec) return e->codec->error(e->codec_state, out); switch (e->err) { case Z_OK: case Z_BUF_ERROR: @@ -327,6 +354,7 @@ bool scr_zlib_engine_error(const ScrZlibEngine *e, ScrZlibError *out) { } bool scr_zlib_engine_reset(ScrZlibEngine *e, ScrZlibError *out) { + if (e->codec) return e->codec->reset(e->codec_state, out); switch (scr_zlib_engine_reset_stream(e)) { case 0: return false; case 1: return scr_zlib_engine_fail(e, "Failed to init stream before reset", out); @@ -338,6 +366,7 @@ bool scr_zlib_engine_reset(ScrZlibEngine *e, ScrZlibError *out) { /* SetParams: deflateParams for DEFLATE and DEFLATERAW only (Node's * switch leaves GZIP's level and strategy unchanged). */ bool scr_zlib_engine_params(ScrZlibEngine *e, int level, int strategy, ScrZlibError *out) { + if (e->codec) return false; /* Node's Brotli streams have no params() */ if (scr_zlib_engine_init(e) && e->err != Z_OK) return scr_zlib_engine_fail(e, "Failed to init stream before set parameters", out); e->err = Z_OK; @@ -350,6 +379,11 @@ bool scr_zlib_engine_params(ScrZlibEngine *e, int level, int strategy, ScrZlibEr void scr_zlib_engine_free(ScrZlibEngine *e) { if (!e) return; + if (e->codec) { + e->codec->free(e->codec_state); + free(e); + return; + } if (e->init_done) { if (scr_zlib_engine_deflating(e)) deflateEnd(&e->strm); else inflateEnd(&e->strm); @@ -418,6 +452,15 @@ static bool scr_zlib_is_null(const ScrDyn *v) { void scr_zlib_options_drop(ScrZlibOptions *o) { if (o->dict) scr_bytes_release(o->dict); o->dict = NULL; + if (o->codec_state) o->codec_state_free(o->codec_state); + o->codec_state = NULL; +} + +const ScrZlibBrotliHooks *scr_zlib_brotli_hooks = NULL; + +/* Node's BROTLI_DECODE and BROTLI_ENCODE modes. */ +static bool scr_zlib_brotli_mode(int mode) { + return mode == 8 || mode == 9; } static bool scr_zlib_options_parse_object(int mode, const ScrDyn *opts, ScrZlibOptions *out); @@ -426,6 +469,10 @@ static bool scr_zlib_options_parse_object(int mode, const ScrDyn *opts, ScrZlibO * slot by reference) is read through its materialized view, as a plain * object is. */ bool scr_zlib_options_parse(int mode, const ScrDyn *opts, ScrZlibOptions *out) { + if (scr_zlib_brotli_mode(mode)) { + if (!scr_zlib_brotli_hooks) scr_trap("scriptc: the Brotli codec is not linked\n"); + return scr_zlib_brotli_hooks->options(mode, opts, out); + } if (opts == NULL || opts->kind != SCR_DYN_TYPED_REF) return scr_zlib_options_parse_object(mode, opts, out); ScrDyn *view = scr_dyn_typed_ref_materialize(opts); bool ok = false; @@ -475,6 +522,10 @@ static bool scr_zlib_options_parse_object(int mode, const ScrDyn *opts, ScrZlibO } out->dict = scr_bytes_raw_view(d->v.bytes); } + return scr_zlib_base_options(o, 5, out); +} + +bool scr_zlib_base_options(const ScrDyn *o, double flush_max, ScrZlibOptions *out) { const ScrDyn *rg = scr_zlib_opt(o, "rejectGarbageAfterEnd"); out->reject_garbage = rg != NULL && rg->kind == SCR_DYN_BOOL && rg->v.b; const ScrDyn *cs = scr_zlib_opt(o, "chunkSize"); @@ -488,10 +539,10 @@ static bool scr_zlib_options_parse_object(int mode, const ScrDyn *opts, ScrZlibO } } if (!ok || - !scr_zlib_check_range(scr_zlib_opt(o, "flush"), "options.flush", true, 0, 5, Z_NO_FLUSH, - &out->flush) || - !scr_zlib_check_range(scr_zlib_opt(o, "finishFlush"), "options.finishFlush", true, 0, 5, - Z_FINISH, &out->finish_flush) || + !scr_zlib_check_range(scr_zlib_opt(o, "flush"), "options.flush", true, 0, flush_max, + out->flush, &out->flush) || + !scr_zlib_check_range(scr_zlib_opt(o, "finishFlush"), "options.finishFlush", true, 0, + flush_max, out->finish_flush, &out->finish_flush) || !scr_zlib_check_range(scr_zlib_opt(o, "maxOutputLength"), "options.maxOutputLength", true, 1, 9007199254740991.0, 9007199254740991.0, &out->max_output)) { scr_zlib_options_drop(out); @@ -513,7 +564,8 @@ static bool scr_zlib_options_parse_object(int mode, const ScrDyn *opts, ScrZlibO return true; } -ScrZlibEngine *scr_zlib_engine_from_options(int mode, const ScrZlibOptions *o) { +ScrZlibEngine *scr_zlib_engine_from_options(int mode, ScrZlibOptions *o) { + if (scr_zlib_brotli_mode(mode)) return scr_zlib_brotli_hooks->engine(mode, o); return scr_zlib_engine_new(mode, (int)o->level, (int)o->window_bits, (int)o->mem_level, (int)o->strategy, o->dict ? o->dict->data : NULL, o->dict ? o->dict->len : 0, o->reject_garbage); diff --git a/packages/runtime/src/scr_zlib.h b/packages/runtime/src/scr_zlib.h index 124e157ab..d34b81424 100644 --- a/packages/runtime/src/scr_zlib.h +++ b/packages/runtime/src/scr_zlib.h @@ -42,6 +42,19 @@ bool scr_zlib_engine_error(const ScrZlibEngine *e, ScrZlibError *out); bool scr_zlib_engine_reset(ScrZlibEngine *e, ScrZlibError *out); bool scr_zlib_engine_params(ScrZlibEngine *e, int level, int strategy, ScrZlibError *out); void scr_zlib_engine_free(ScrZlibEngine *e); +/* Another codec behind the same engine (Brotli, scr_brotli.c): the engine + * entries above call these on its state. work runs on a worker thread and + * reports the unused input and output; error and reset fill *out as the + * zlib entries do. free releases the state. */ +typedef struct { + void (*work)(void *state, int flush, const unsigned char *in, uint32_t in_len, unsigned char *out, + uint32_t out_len, uint32_t *avail_in, uint32_t *avail_out); + bool (*error)(const void *state, ScrZlibError *out); + bool (*reset)(void *state, ScrZlibError *out); + void (*free)(void *state); +} ScrZlibCodec; +ScrZlibEngine *scr_zlib_engine_codec(int mode, const ScrZlibCodec *codec, void *state /* moves */, + bool reject_garbage); /* Node's Zlib() and ZlibBase() option ladder, shared by the stream classes * and the one-shot forms with options: false with Node's error pending. * `dict` is a +1 view (or NULL) that scr_zlib_options_drop releases. @@ -52,12 +65,32 @@ typedef struct { ScrBytes *dict; bool reject_garbage, info; double chunk_size, flush, finish_flush, max_output; + /* Another codec's prepared state (a Brotli instance with its parameters + * set), which scr_zlib_engine_from_options moves into the engine and + * scr_zlib_options_drop frees otherwise. */ + void *codec_state; + void (*codec_state_free)(void *state); } ScrZlibOptions; bool scr_zlib_options_parse(int mode, const ScrDyn *opts, ScrZlibOptions *out); void scr_zlib_options_drop(ScrZlibOptions *o); +/* ZlibBase's reads over a truthy options object, after the codec's own: + * chunkSize, flush and finishFlush (within 0..flush_max), maxOutputLength, + * rejectGarbageAfterEnd and info. False with Node's error pending, *out + * dropped. */ +bool scr_zlib_base_options(const ScrDyn *opts, double flush_max, ScrZlibOptions *out); +/* Brotli's option ladder and engine (scr_brotli.c), installed when the + * program uses Brotli: scr_zlib_options_parse and + * scr_zlib_engine_from_options call them for Node's BROTLI_DECODE (8) and + * BROTLI_ENCODE (9) modes. */ +typedef struct { + bool (*options)(int mode, const ScrDyn *opts, ScrZlibOptions *out); + ScrZlibEngine *(*engine)(int mode, ScrZlibOptions *o); +} ScrZlibBrotliHooks; +extern const ScrZlibBrotliHooks *scr_zlib_brotli_hooks; +void scr_brotli_install(void); bool scr_zlib_check_range(const ScrDyn *v, const char *name, bool prop, double lower, double upper, double def, double *out); -ScrZlibEngine *scr_zlib_engine_from_options(int mode, const ScrZlibOptions *o); +ScrZlibEngine *scr_zlib_engine_from_options(int mode, ScrZlibOptions *o); ScrError *scr_zlib_error_new(const ScrZlibError *ze); /* +1 */ /* deflateSync(data, options) and the other one-shot forms with an options * value (undefined for none): Node's processChunkSync over the engine, diff --git a/packages/runtime/src/scr_zlib_stream.c b/packages/runtime/src/scr_zlib_stream.c index 40daa6c76..8b932a63a 100644 --- a/packages/runtime/src/scr_zlib_stream.c +++ b/packages/runtime/src/scr_zlib_stream.c @@ -51,6 +51,9 @@ typedef struct ScrZlibStream { bool done_pending; /* Transform's kCallback: completion waits for _read */ double rlen_before; /* readableLength when the chunk's transform began */ ScrBytes *flush_bufs[6]; /* zero-length sentinels, one per flush kind */ + /* The AsyncLocalStorage context of the stream's creation (+1, NULL + * outside a store): Node runs the handle's step callbacks in it. */ + ScrAlsCtx *als; } ScrZlibStream; static void scr_zlib_stream_oom(void) { @@ -81,6 +84,7 @@ static void scr_zs_release_v(void *p) { for (size_t i = 0; i < 6; i++) { if (zs->flush_bufs[i]) scr_bytes_release(zs->flush_bufs[i]); } + scr_als_ctx_release(zs->als); free(zs); } @@ -242,6 +246,7 @@ static void scr_zs_step_after(void *payload) { ScrZlibStep *op = payload; ScrZlibStream *zs = op->zs; ScrStream *s = op->s; + ScrAlsCtx *prev = scr_als_dispatch_enter(zs->als); zs->write_in_progress = false; ScrZlibError ze; if (scr_zlib_engine_error(zs->eng, &ze)) { @@ -252,6 +257,7 @@ static void scr_zs_step_after(void *payload) { scr_zs_process(zs, s); } if (zs->pending_close) scr_zs_close_engine(zs); + scr_als_dispatch_leave(prev); } static void scr_zs_step_destroy(void *payload) { @@ -427,6 +433,8 @@ static const char *scr_zs_class_name(int mode) { case SCR_ZM_GUNZIP: return "Gunzip"; case SCR_ZM_DEFLATERAW: return "DeflateRaw"; case SCR_ZM_INFLATERAW: return "InflateRaw"; + case 8: return "BrotliDecompress"; + case 9: return "BrotliCompress"; default: return "Unzip"; } } @@ -463,6 +471,7 @@ ScrStream *scr_zlib_stream_new(double mode_d, ScrDyn *opts) { ScrZlibStream *zs = calloc(1, sizeof *zs); if (!zs) scr_zlib_stream_oom(); zs->rc = 1; + zs->als = scr_als_capture(); zs->mode = mode; zs->eng = scr_zlib_engine_from_options(mode, &zo); scr_zlib_options_drop(&zo); @@ -471,7 +480,8 @@ ScrStream *scr_zlib_stream_new(double mode_d, ScrDyn *opts) { if (!zs->out_buf) scr_zlib_stream_oom(); zs->default_flush = (int)zo.flush; zs->finish_flush = (int)zo.finish_flush; - zs->full_flush = SCR_ZF_FULL; + /* Brotli's full flush is BROTLI_OPERATION_FLUSH (brotliDefaultOpts). */ + zs->full_flush = mode == 8 || mode == 9 ? 1 : SCR_ZF_FULL; zs->level = zo.level; zs->strategy = zo.strategy; zs->reject_garbage = zo.reject_garbage; diff --git a/packages/runtime/test/common-symbols.test.ts b/packages/runtime/test/common-symbols.test.ts index 43d4461e6..344bd01d6 100644 --- a/packages/runtime/test/common-symbols.test.ts +++ b/packages/runtime/test/common-symbols.test.ts @@ -38,6 +38,7 @@ async function commonSymbols(label: string, compiler: string[]): Promise---/` directory — one flavor per driver/target and compiler environment/identity (the lre-objects pattern). Zlib-free binaries never compile any of this; compressed OUTPUT bytes are zlib-version-dependent, which is why the corpus only compares round-trips and fixed-blob inflation. +## brotli/ + +- Project: https://github.com/google/brotli +- Version: 1.2.0 (release tarball brotli-1.2.0.tar.gz, sha256 816c96e8e8f193b40151dad7e8ff37b1221d019dbcb9c35cd3fadbfe6477dfec), the version Node v24.22.0 bundles (`process.versions.brotli`) +- License: MIT (see brotli/LICENSE) + +The codec behind node:zlib's Brotli functions and streams (see packages/runtime/src/scr_brotli.c). Only `c/common`, `c/dec`, `c/enc` and `c/include` are vendored, in upstream's layout (LICENSE beside `c/`); the C++ lazy static initializer (`c/enc/static_init_lazy.cc`), the tools, tests, bindings and build machinery are not. No vendored file is modified, and the files match the copy Node bundles in `deps/brotli`. Because the version is Node's, compressed bytes match Node's for the same parameters, so the corpus compares compressed output as well as round trips. To update, re-fetch the release tarball, copy the same directories, and bump `BROTLI_VERSION` in vendor-inputs.ts. + +The `dec/` and `enc/` directories share file names (`static_init.c`, `state.h`, `prefix.h`), so archive members are named after their directory (`dec-static_init.o`). Brotli-free binaries compile and link none of it. + ## mbedtls/ - Project: https://github.com/Mbed-TLS/mbedtls diff --git a/packages/runtime/vendor/brotli/LICENSE b/packages/runtime/vendor/brotli/LICENSE new file mode 100644 index 000000000..33b7cdd2d --- /dev/null +++ b/packages/runtime/vendor/brotli/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/runtime/vendor/brotli/c/common/constants.c b/packages/runtime/vendor/brotli/c/common/constants.c new file mode 100644 index 000000000..ef5a2e2e0 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/constants.c @@ -0,0 +1,15 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "constants.h" + +const BROTLI_MODEL("small") +BrotliPrefixCodeRange _kBrotliPrefixCodeRanges[BROTLI_NUM_BLOCK_LEN_SYMBOLS] = { + {1, 2}, {5, 2}, {9, 2}, {13, 2}, {17, 3}, {25, 3}, + {33, 3}, {41, 3}, {49, 4}, {65, 4}, {81, 4}, {97, 4}, + {113, 5}, {145, 5}, {177, 5}, {209, 5}, {241, 6}, {305, 6}, + {369, 7}, {497, 8}, {753, 9}, {1265, 10}, {2289, 11}, {4337, 12}, + {8433, 13}, {16625, 24}}; diff --git a/packages/runtime/vendor/brotli/c/common/constants.h b/packages/runtime/vendor/brotli/c/common/constants.h new file mode 100644 index 000000000..85e9802c0 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/constants.h @@ -0,0 +1,198 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/** + * @file + * Common constants used in decoder and encoder API. + */ + +#ifndef BROTLI_COMMON_CONSTANTS_H_ +#define BROTLI_COMMON_CONSTANTS_H_ + +#include "platform.h" + +/* Specification: 7.3. Encoding of the context map */ +#define BROTLI_CONTEXT_MAP_MAX_RLE 16 + +/* Specification: 2. Compressed representation overview */ +#define BROTLI_MAX_NUMBER_OF_BLOCK_TYPES 256 + +/* Specification: 3.3. Alphabet sizes: insert-and-copy length */ +#define BROTLI_NUM_LITERAL_SYMBOLS 256 +#define BROTLI_NUM_COMMAND_SYMBOLS 704 +#define BROTLI_NUM_BLOCK_LEN_SYMBOLS 26 +#define BROTLI_MAX_CONTEXT_MAP_SYMBOLS (BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + \ + BROTLI_CONTEXT_MAP_MAX_RLE) +#define BROTLI_MAX_BLOCK_TYPE_SYMBOLS (BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + 2) + +/* Specification: 3.5. Complex prefix codes */ +#define BROTLI_REPEAT_PREVIOUS_CODE_LENGTH 16 +#define BROTLI_REPEAT_ZERO_CODE_LENGTH 17 +#define BROTLI_CODE_LENGTH_CODES (BROTLI_REPEAT_ZERO_CODE_LENGTH + 1) +/* "code length of 8 is repeated" */ +#define BROTLI_INITIAL_REPEATED_CODE_LENGTH 8 + +/* "Large Window Brotli" */ + +/** + * The theoretical maximum number of distance bits specified for large window + * brotli, for 64-bit encoders and decoders. Even when in practice 32-bit + * encoders and decoders only support up to 30 max distance bits, the value is + * set to 62 because it affects the large window brotli file format. + * Specifically, it affects the encoding of simple huffman tree for distances, + * see Specification RFC 7932 chapter 3.4. + */ +#define BROTLI_LARGE_MAX_DISTANCE_BITS 62U +#define BROTLI_LARGE_MIN_WBITS 10 +/** + * The maximum supported large brotli window bits by the encoder and decoder. + * Large window brotli allows up to 62 bits, however the current encoder and + * decoder, designed for 32-bit integers, only support up to 30 bits maximum. + */ +#define BROTLI_LARGE_MAX_WBITS 30 + +/* Specification: 4. Encoding of distances */ +#define BROTLI_NUM_DISTANCE_SHORT_CODES 16 +/** + * Maximal number of "postfix" bits. + * + * Number of "postfix" bits is stored as 2 bits in meta-block header. + */ +#define BROTLI_MAX_NPOSTFIX 3 +#define BROTLI_MAX_NDIRECT 120 +#define BROTLI_MAX_DISTANCE_BITS 24U +#define BROTLI_DISTANCE_ALPHABET_SIZE(NPOSTFIX, NDIRECT, MAXNBITS) ( \ + BROTLI_NUM_DISTANCE_SHORT_CODES + (NDIRECT) + \ + ((MAXNBITS) << ((NPOSTFIX) + 1))) +/* BROTLI_NUM_DISTANCE_SYMBOLS == 1128 */ +#define BROTLI_NUM_DISTANCE_SYMBOLS \ + BROTLI_DISTANCE_ALPHABET_SIZE( \ + BROTLI_MAX_NDIRECT, BROTLI_MAX_NPOSTFIX, BROTLI_LARGE_MAX_DISTANCE_BITS) + +/* ((1 << 26) - 4) is the maximal distance that can be expressed in RFC 7932 + brotli stream using NPOSTFIX = 0 and NDIRECT = 0. With other NPOSTFIX and + NDIRECT values distances up to ((1 << 29) + 88) could be expressed. */ +#define BROTLI_MAX_DISTANCE 0x3FFFFFC + +/* ((1 << 31) - 4) is the safe distance limit. Using this number as a limit + allows safe distance calculation without overflows, given the distance + alphabet size is limited to corresponding size + (see kLargeWindowDistanceCodeLimits). */ +#define BROTLI_MAX_ALLOWED_DISTANCE 0x7FFFFFFC + + +/* Specification: 4. Encoding of Literal Insertion Lengths and Copy Lengths */ +#define BROTLI_NUM_INS_COPY_CODES 24 + +/* 7.1. Context modes and context ID lookup for literals */ +/* "context IDs for literals are in the range of 0..63" */ +#define BROTLI_LITERAL_CONTEXT_BITS 6 + +/* 7.2. Context ID for distances */ +#define BROTLI_DISTANCE_CONTEXT_BITS 2 + +/* 9.1. Format of the Stream Header */ +/* Number of slack bytes for window size. Don't confuse + with BROTLI_NUM_DISTANCE_SHORT_CODES. */ +#define BROTLI_WINDOW_GAP 16 +#define BROTLI_MAX_BACKWARD_LIMIT(W) (((size_t)1 << (W)) - BROTLI_WINDOW_GAP) + +typedef struct BrotliDistanceCodeLimit { + uint32_t max_alphabet_size; + uint32_t max_distance; +} BrotliDistanceCodeLimit; + +/* This function calculates maximal size of distance alphabet, such that the + distances greater than the given values can not be represented. + + This limits are designed to support fast and safe 32-bit decoders. + "32-bit" means that signed integer values up to ((1 << 31) - 1) could be + safely expressed. + + Brotli distance alphabet symbols do not represent consecutive distance + ranges. Each distance alphabet symbol (excluding direct distances and short + codes), represent interleaved (for NPOSTFIX > 0) range of distances. + A "group" of consecutive (1 << NPOSTFIX) symbols represent non-interleaved + range. Two consecutive groups require the same amount of "extra bits". + + It is important that distance alphabet represents complete "groups". + To avoid complex logic on encoder side about interleaved ranges + it was decided to restrict both sides to complete distance code "groups". + */ +BROTLI_UNUSED_FUNCTION BrotliDistanceCodeLimit BrotliCalculateDistanceCodeLimit( + uint32_t max_distance, uint32_t npostfix, uint32_t ndirect) { + BrotliDistanceCodeLimit result; + /* Marking this function as unused, because not all files + including "constants.h" use it -> compiler warns about that. */ + BROTLI_UNUSED(&BrotliCalculateDistanceCodeLimit); + if (max_distance <= ndirect) { + /* This case never happens / exists only for the sake of completeness. */ + result.max_alphabet_size = max_distance + BROTLI_NUM_DISTANCE_SHORT_CODES; + result.max_distance = max_distance; + return result; + } else { + /* The first prohibited value. */ + uint32_t forbidden_distance = max_distance + 1; + /* Subtract "directly" encoded region. */ + uint32_t offset = forbidden_distance - ndirect - 1; + uint32_t ndistbits = 0; + uint32_t tmp; + uint32_t half; + uint32_t group; + /* Postfix for the last dcode in the group. */ + uint32_t postfix = (1u << npostfix) - 1; + uint32_t extra; + uint32_t start; + /* Remove postfix and "head-start". */ + offset = (offset >> npostfix) + 4; + /* Calculate the number of distance bits. */ + tmp = offset / 2; + /* Poor-man's log2floor, to avoid extra dependencies. */ + while (tmp != 0) {ndistbits++; tmp = tmp >> 1;} + /* One bit is covered with subrange addressing ("half"). */ + ndistbits--; + /* Find subrange. */ + half = (offset >> ndistbits) & 1; + /* Calculate the "group" part of dcode. */ + group = ((ndistbits - 1) << 1) | half; + /* Calculated "group" covers the prohibited distance value. */ + if (group == 0) { + /* This case is added for correctness; does not occur for limit > 128. */ + result.max_alphabet_size = ndirect + BROTLI_NUM_DISTANCE_SHORT_CODES; + result.max_distance = ndirect; + return result; + } + /* Decrement "group", so it is the last permitted "group". */ + group--; + /* After group was decremented, ndistbits and half must be recalculated. */ + ndistbits = (group >> 1) + 1; + /* The last available distance in the subrange has all extra bits set. */ + extra = (1u << ndistbits) - 1; + /* Calculate region start. NB: ndistbits >= 1. */ + start = (1u << (ndistbits + 1)) - 4; + /* Move to subregion. */ + start += (group & 1) << ndistbits; + /* Calculate the alphabet size. */ + result.max_alphabet_size = ((group << npostfix) | postfix) + ndirect + + BROTLI_NUM_DISTANCE_SHORT_CODES + 1; + /* Calculate the maximal distance representable by alphabet. */ + result.max_distance = ((start + extra) << npostfix) + postfix + ndirect + 1; + return result; + } +} + +/* Represents the range of values belonging to a prefix code: + [offset, offset + 2^nbits) */ +typedef struct { + uint16_t offset; + uint8_t nbits; +} BrotliPrefixCodeRange; + +/* "Soft-private", it is exported, but not "advertised" as API. */ +BROTLI_COMMON_API extern const BROTLI_MODEL("small") +BrotliPrefixCodeRange _kBrotliPrefixCodeRanges[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; + +#endif /* BROTLI_COMMON_CONSTANTS_H_ */ diff --git a/packages/runtime/vendor/brotli/c/common/context.c b/packages/runtime/vendor/brotli/c/common/context.c new file mode 100644 index 000000000..d3c910cb1 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/context.c @@ -0,0 +1,156 @@ +#include "context.h" + +#include "platform.h" + +/* Common context lookup table for all context modes. */ +const BROTLI_MODEL("small") uint8_t _kBrotliContextLookupTable[2048] = { + /* CONTEXT_LSB6, last byte. */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + + /* CONTEXT_LSB6, second last byte, */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + /* CONTEXT_MSB6, last byte. */ + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, + 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, + 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, + 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, + 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, + 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, + 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, + 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, + 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, + 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, + 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, + 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, + + /* CONTEXT_MSB6, second last byte, */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + /* CONTEXT_UTF8, last byte. */ + /* ASCII range. */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 32, 32, 24, 40, 28, 12, + 12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48, + 52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12, + 12, 56, 60, 60, 60, 56, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56, + 60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0, + /* UTF8 continuation byte range. */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + /* UTF8 lead byte range. */ + 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + + /* CONTEXT_UTF8 second last byte. */ + /* ASCII range. */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 0, + /* UTF8 continuation byte range. */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* UTF8 lead byte range. */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + + /* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */ + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56, + + /* CONTEXT_SIGNED, second last byte. */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, +}; diff --git a/packages/runtime/vendor/brotli/c/common/context.h b/packages/runtime/vendor/brotli/c/common/context.h new file mode 100644 index 000000000..45b57d8c3 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/context.h @@ -0,0 +1,112 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Lookup table to map the previous two bytes to a context id. + + There are four different context modeling modes defined here: + CONTEXT_LSB6: context id is the least significant 6 bits of the last byte, + CONTEXT_MSB6: context id is the most significant 6 bits of the last byte, + CONTEXT_UTF8: second-order context model tuned for UTF8-encoded text, + CONTEXT_SIGNED: second-order context model tuned for signed integers. + + If |p1| and |p2| are the previous two bytes, and |mode| is current context + mode, we calculate the context as: + + context = ContextLut(mode)[p1] | ContextLut(mode)[p2 + 256]. + + For CONTEXT_UTF8 mode, if the previous two bytes are ASCII characters + (i.e. < 128), this will be equivalent to + + context = 4 * context1(p1) + context2(p2), + + where context1 is based on the previous byte in the following way: + + 0 : non-ASCII control + 1 : \t, \n, \r + 2 : space + 3 : other punctuation + 4 : " ' + 5 : % + 6 : ( < [ { + 7 : ) > ] } + 8 : , ; : + 9 : . + 10 : = + 11 : number + 12 : upper-case vowel + 13 : upper-case consonant + 14 : lower-case vowel + 15 : lower-case consonant + + and context2 is based on the second last byte: + + 0 : control, space + 1 : punctuation + 2 : upper-case letter, number + 3 : lower-case letter + + If the last byte is ASCII, and the second last byte is not (in a valid UTF8 + stream it will be a continuation byte, value between 128 and 191), the + context is the same as if the second last byte was an ASCII control or space. + + If the last byte is a UTF8 lead byte (value >= 192), then the next byte will + be a continuation byte and the context id is 2 or 3 depending on the LSB of + the last byte and to a lesser extent on the second last byte if it is ASCII. + + If the last byte is a UTF8 continuation byte, the second last byte can be: + - continuation byte: the next byte is probably ASCII or lead byte (assuming + 4-byte UTF8 characters are rare) and the context id is 0 or 1. + - lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1 + - lead byte (208 - 255): next byte is continuation byte, context is 2 or 3 + + The possible value combinations of the previous two bytes, the range of + context ids and the type of the next byte is summarized in the table below: + + |--------\-----------------------------------------------------------------| + | \ Last byte | + | Second \---------------------------------------------------------------| + | last byte \ ASCII | cont. byte | lead byte | + | \ (0-127) | (128-191) | (192-) | + |=============|===================|=====================|==================| + | ASCII | next: ASCII/lead | not valid | next: cont. | + | (0-127) | context: 4 - 63 | | context: 2 - 3 | + |-------------|-------------------|---------------------|------------------| + | cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. | + | (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 | + |-------------|-------------------|---------------------|------------------| + | lead byte | not valid | next: ASCII/lead | not valid | + | (192-207) | | context: 0 - 1 | | + |-------------|-------------------|---------------------|------------------| + | lead byte | not valid | next: cont. | not valid | + | (208-) | | context: 2 - 3 | | + |-------------|-------------------|---------------------|------------------| +*/ + +#ifndef BROTLI_COMMON_CONTEXT_H_ +#define BROTLI_COMMON_CONTEXT_H_ + +#include "platform.h" + +typedef enum ContextType { + CONTEXT_LSB6 = 0, + CONTEXT_MSB6 = 1, + CONTEXT_UTF8 = 2, + CONTEXT_SIGNED = 3 +} ContextType; + +/* "Soft-private", it is exported, but not "advertised" as API. */ +/* Common context lookup table for all context modes. */ +BROTLI_COMMON_API extern const uint8_t _kBrotliContextLookupTable[2048]; + +typedef const uint8_t* ContextLut; + +/* typeof(MODE) == ContextType; returns ContextLut */ +#define BROTLI_CONTEXT_LUT(MODE) (&_kBrotliContextLookupTable[(MODE) << 9]) + +/* typeof(LUT) == ContextLut */ +#define BROTLI_CONTEXT(P1, P2, LUT) ((LUT)[P1] | ((LUT) + 256)[P2]) + +#endif /* BROTLI_COMMON_CONTEXT_H_ */ diff --git a/packages/runtime/vendor/brotli/c/common/dictionary.c b/packages/runtime/vendor/brotli/c/common/dictionary.c new file mode 100644 index 000000000..ef43af9bf --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/dictionary.c @@ -0,0 +1,64 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "dictionary.h" +#include "platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if !defined(BROTLI_EXTERNAL_DICTIONARY_DATA) +/* Embed kBrotliDictionaryData */ +#include "dictionary_inc.h" +static const BROTLI_MODEL("small") BrotliDictionary kBrotliDictionary = { +#else +static BROTLI_MODEL("small") BrotliDictionary kBrotliDictionary = { +#endif + /* size_bits_by_length */ + { + 0, 0, 0, 0, 10, 10, 11, 11, + 10, 10, 10, 10, 10, 9, 9, 8, + 7, 7, 8, 7, 7, 6, 6, 5, + 5, 0, 0, 0, 0, 0, 0, 0 + }, + + /* offsets_by_length */ + { + 0, 0, 0, 0, 0, 4096, 9216, 21504, + 35840, 44032, 53248, 63488, 74752, 87040, 93696, 100864, + 104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280, + 122016, 122784, 122784, 122784, 122784, 122784, 122784, 122784 + }, + + /* data_size == sizeof(kBrotliDictionaryData) */ + 122784, + + /* data */ +#if defined(BROTLI_EXTERNAL_DICTIONARY_DATA) + NULL +#else + kBrotliDictionaryData +#endif +}; + +const BrotliDictionary* BrotliGetDictionary(void) { + return &kBrotliDictionary; +} + +void BrotliSetDictionaryData(const uint8_t* data) { +#if defined(BROTLI_EXTERNAL_DICTIONARY_DATA) + if (!!data && !kBrotliDictionary.data) { + kBrotliDictionary.data = data; + } +#else + BROTLI_UNUSED(data); // Appease -Werror=unused-parameter +#endif +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/common/dictionary.h b/packages/runtime/vendor/brotli/c/common/dictionary.h new file mode 100644 index 000000000..ed683b0ae --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/dictionary.h @@ -0,0 +1,63 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Collection of static dictionary words. */ + +#ifndef BROTLI_COMMON_DICTIONARY_H_ +#define BROTLI_COMMON_DICTIONARY_H_ + +#include "platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct BrotliDictionary { + /** + * Number of bits to encode index of dictionary word in a bucket. + * + * Specification: Appendix A. Static Dictionary Data + * + * Words in a dictionary are bucketed by length. + * @c 0 means that there are no words of a given length. + * Dictionary consists of words with length of [4..24] bytes. + * Values at [0..3] and [25..31] indices should not be addressed. + */ + uint8_t size_bits_by_length[32]; + + /* assert(offset[i + 1] == offset[i] + (bits[i] ? (i << bits[i]) : 0)) */ + uint32_t offsets_by_length[32]; + + /* assert(data_size == offsets_by_length[31]) */ + size_t data_size; + + /* Data array is not bound, and should obey to size_bits_by_length values. + Specified size matches default (RFC 7932) dictionary. Its size is + defined by data_size */ + const uint8_t* data; +} BrotliDictionary; + +BROTLI_COMMON_API const BrotliDictionary* BrotliGetDictionary(void); + +/** + * Sets dictionary data. + * + * When dictionary data is already set / present, this method is no-op. + * + * Dictionary data MUST be provided before BrotliGetDictionary is invoked. + * This method is used ONLY in multi-client environment (e.g. C + Java), + * to reduce storage by sharing single dictionary between implementations. + */ +BROTLI_COMMON_API void BrotliSetDictionaryData(const uint8_t* data); + +#define BROTLI_MIN_DICTIONARY_WORD_LENGTH 4 +#define BROTLI_MAX_DICTIONARY_WORD_LENGTH 24 + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_COMMON_DICTIONARY_H_ */ diff --git a/packages/runtime/vendor/brotli/c/common/dictionary_inc.h b/packages/runtime/vendor/brotli/c/common/dictionary_inc.h new file mode 100644 index 000000000..f40a93ecc --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/dictionary_inc.h @@ -0,0 +1,5847 @@ +static const BROTLI_MODEL("small") uint8_t kBrotliDictionaryData[] = { +116,105,109,101,100,111,119,110,108,105,102,101,108,101,102,116,98,97,99,107,99, +111,100,101,100,97,116,97,115,104,111,119,111,110,108,121,115,105,116,101,99,105 +,116,121,111,112,101,110,106,117,115,116,108,105,107,101,102,114,101,101,119,111 +,114,107,116,101,120,116,121,101,97,114,111,118,101,114,98,111,100,121,108,111, +118,101,102,111,114,109,98,111,111,107,112,108,97,121,108,105,118,101,108,105, +110,101,104,101,108,112,104,111,109,101,115,105,100,101,109,111,114,101,119,111, +114,100,108,111,110,103,116,104,101,109,118,105,101,119,102,105,110,100,112,97, +103,101,100,97,121,115,102,117,108,108,104,101,97,100,116,101,114,109,101,97,99, +104,97,114,101,97,102,114,111,109,116,114,117,101,109,97,114,107,97,98,108,101, +117,112,111,110,104,105,103,104,100,97,116,101,108,97,110,100,110,101,119,115, +101,118,101,110,110,101,120,116,99,97,115,101,98,111,116,104,112,111,115,116,117 +,115,101,100,109,97,100,101,104,97,110,100,104,101,114,101,119,104,97,116,110,97 +,109,101,76,105,110,107,98,108,111,103,115,105,122,101,98,97,115,101,104,101,108 +,100,109,97,107,101,109,97,105,110,117,115,101,114,39,41,32,43,104,111,108,100, +101,110,100,115,119,105,116,104,78,101,119,115,114,101,97,100,119,101,114,101, +115,105,103,110,116,97,107,101,104,97,118,101,103,97,109,101,115,101,101,110,99, +97,108,108,112,97,116,104,119,101,108,108,112,108,117,115,109,101,110,117,102, +105,108,109,112,97,114,116,106,111,105,110,116,104,105,115,108,105,115,116,103, +111,111,100,110,101,101,100,119,97,121,115,119,101,115,116,106,111,98,115,109, +105,110,100,97,108,115,111,108,111,103,111,114,105,99,104,117,115,101,115,108,97 +,115,116,116,101,97,109,97,114,109,121,102,111,111,100,107,105,110,103,119,105, +108,108,101,97,115,116,119,97,114,100,98,101,115,116,102,105,114,101,80,97,103, +101,107,110,111,119,97,119,97,121,46,112,110,103,109,111,118,101,116,104,97,110, +108,111,97,100,103,105,118,101,115,101,108,102,110,111,116,101,109,117,99,104, +102,101,101,100,109,97,110,121,114,111,99,107,105,99,111,110,111,110,99,101,108, +111,111,107,104,105,100,101,100,105,101,100,72,111,109,101,114,117,108,101,104, +111,115,116,97,106,97,120,105,110,102,111,99,108,117,98,108,97,119,115,108,101, +115,115,104,97,108,102,115,111,109,101,115,117,99,104,122,111,110,101,49,48,48, +37,111,110,101,115,99,97,114,101,84,105,109,101,114,97,99,101,98,108,117,101,102 +,111,117,114,119,101,101,107,102,97,99,101,104,111,112,101,103,97,118,101,104,97 +,114,100,108,111,115,116,119,104,101,110,112,97,114,107,107,101,112,116,112,97, +115,115,115,104,105,112,114,111,111,109,72,84,77,76,112,108,97,110,84,121,112, +101,100,111,110,101,115,97,118,101,107,101,101,112,102,108,97,103,108,105,110, +107,115,111,108,100,102,105,118,101,116,111,111,107,114,97,116,101,116,111,119, +110,106,117,109,112,116,104,117,115,100,97,114,107,99,97,114,100,102,105,108,101 +,102,101,97,114,115,116,97,121,107,105,108,108,116,104,97,116,102,97,108,108,97, +117,116,111,101,118,101,114,46,99,111,109,116,97,108,107,115,104,111,112,118,111 +,116,101,100,101,101,112,109,111,100,101,114,101,115,116,116,117,114,110,98,111, +114,110,98,97,110,100,102,101,108,108,114,111,115,101,117,114,108,40,115,107,105 +,110,114,111,108,101,99,111,109,101,97,99,116,115,97,103,101,115,109,101,101,116 +,103,111,108,100,46,106,112,103,105,116,101,109,118,97,114,121,102,101,108,116, +116,104,101,110,115,101,110,100,100,114,111,112,86,105,101,119,99,111,112,121,49 +,46,48,34,60,47,97,62,115,116,111,112,101,108,115,101,108,105,101,115,116,111, +117,114,112,97,99,107,46,103,105,102,112,97,115,116,99,115,115,63,103,114,97,121 +,109,101,97,110,38,103,116,59,114,105,100,101,115,104,111,116,108,97,116,101,115 +,97,105,100,114,111,97,100,118,97,114,32,102,101,101,108,106,111,104,110,114,105 +,99,107,112,111,114,116,102,97,115,116,39,85,65,45,100,101,97,100,60,47,98,62, +112,111,111,114,98,105,108,108,116,121,112,101,85,46,83,46,119,111,111,100,109, +117,115,116,50,112,120,59,73,110,102,111,114,97,110,107,119,105,100,101,119,97, +110,116,119,97,108,108,108,101,97,100,91,48,93,59,112,97,117,108,119,97,118,101, +115,117,114,101,36,40,39,35,119,97,105,116,109,97,115,115,97,114,109,115,103,111 +,101,115,103,97,105,110,108,97,110,103,112,97,105,100,33,45,45,32,108,111,99,107 +,117,110,105,116,114,111,111,116,119,97,108,107,102,105,114,109,119,105,102,101, +120,109,108,34,115,111,110,103,116,101,115,116,50,48,112,120,107,105,110,100,114 +,111,119,115,116,111,111,108,102,111,110,116,109,97,105,108,115,97,102,101,115, +116,97,114,109,97,112,115,99,111,114,101,114,97,105,110,102,108,111,119,98,97,98 +,121,115,112,97,110,115,97,121,115,52,112,120,59,54,112,120,59,97,114,116,115, +102,111,111,116,114,101,97,108,119,105,107,105,104,101,97,116,115,116,101,112, +116,114,105,112,111,114,103,47,108,97,107,101,119,101,97,107,116,111,108,100,70, +111,114,109,99,97,115,116,102,97,110,115,98,97,110,107,118,101,114,121,114,117, +110,115,106,117,108,121,116,97,115,107,49,112,120,59,103,111,97,108,103,114,101, +119,115,108,111,119,101,100,103,101,105,100,61,34,115,101,116,115,53,112,120,59, +46,106,115,63,52,48,112,120,105,102,32,40,115,111,111,110,115,101,97,116,110,111 +,110,101,116,117,98,101,122,101,114,111,115,101,110,116,114,101,101,100,102,97, +99,116,105,110,116,111,103,105,102,116,104,97,114,109,49,56,112,120,99,97,109, +101,104,105,108,108,98,111,108,100,122,111,111,109,118,111,105,100,101,97,115, +121,114,105,110,103,102,105,108,108,112,101,97,107,105,110,105,116,99,111,115, +116,51,112,120,59,106,97,99,107,116,97,103,115,98,105,116,115,114,111,108,108, +101,100,105,116,107,110,101,119,110,101,97,114,60,33,45,45,103,114,111,119,74,83 +,79,78,100,117,116,121,78,97,109,101,115,97,108,101,121,111,117,32,108,111,116, +115,112,97,105,110,106,97,122,122,99,111,108,100,101,121,101,115,102,105,115,104 +,119,119,119,46,114,105,115,107,116,97,98,115,112,114,101,118,49,48,112,120,114, +105,115,101,50,53,112,120,66,108,117,101,100,105,110,103,51,48,48,44,98,97,108, +108,102,111,114,100,101,97,114,110,119,105,108,100,98,111,120,46,102,97,105,114, +108,97,99,107,118,101,114,115,112,97,105,114,106,117,110,101,116,101,99,104,105, +102,40,33,112,105,99,107,101,118,105,108,36,40,34,35,119,97,114,109,108,111,114, +100,100,111,101,115,112,117,108,108,44,48,48,48,105,100,101,97,100,114,97,119, +104,117,103,101,115,112,111,116,102,117,110,100,98,117,114,110,104,114,101,102, +99,101,108,108,107,101,121,115,116,105,99,107,104,111,117,114,108,111,115,115, +102,117,101,108,49,50,112,120,115,117,105,116,100,101,97,108,82,83,83,34,97,103, +101,100,103,114,101,121,71,69,84,34,101,97,115,101,97,105,109,115,103,105,114, +108,97,105,100,115,56,112,120,59,110,97,118,121,103,114,105,100,116,105,112,115, +35,57,57,57,119,97,114,115,108,97,100,121,99,97,114,115,41,59,32,125,112,104,112 +,63,104,101,108,108,116,97,108,108,119,104,111,109,122,104,58,229,42,47,13,10,32 +,49,48,48,104,97,108,108,46,10,10,65,55,112,120,59,112,117,115,104,99,104,97,116 +,48,112,120,59,99,114,101,119,42,47,60,47,104,97,115,104,55,53,112,120,102,108, +97,116,114,97,114,101,32,38,38,32,116,101,108,108,99,97,109,112,111,110,116,111, +108,97,105,100,109,105,115,115,115,107,105,112,116,101,110,116,102,105,110,101, +109,97,108,101,103,101,116,115,112,108,111,116,52,48,48,44,13,10,13,10,99,111, +111,108,102,101,101,116,46,112,104,112,60,98,114,62,101,114,105,99,109,111,115, +116,103,117,105,100,98,101,108,108,100,101,115,99,104,97,105,114,109,97,116,104, +97,116,111,109,47,105,109,103,38,35,56,50,108,117,99,107,99,101,110,116,48,48,48 +,59,116,105,110,121,103,111,110,101,104,116,109,108,115,101,108,108,100,114,117, +103,70,82,69,69,110,111,100,101,110,105,99,107,63,105,100,61,108,111,115,101,110 +,117,108,108,118,97,115,116,119,105,110,100,82,83,83,32,119,101,97,114,114,101, +108,121,98,101,101,110,115,97,109,101,100,117,107,101,110,97,115,97,99,97,112, +101,119,105,115,104,103,117,108,102,84,50,51,58,104,105,116,115,115,108,111,116, +103,97,116,101,107,105,99,107,98,108,117,114,116,104,101,121,49,53,112,120,39,39 +,41,59,41,59,34,62,109,115,105,101,119,105,110,115,98,105,114,100,115,111,114, +116,98,101,116,97,115,101,101,107,84,49,56,58,111,114,100,115,116,114,101,101, +109,97,108,108,54,48,112,120,102,97,114,109,226,128,153,115,98,111,121,115,91,48 +,93,46,39,41,59,34,80,79,83,84,98,101,97,114,107,105,100,115,41,59,125,125,109, +97,114,121,116,101,110,100,40,85,75,41,113,117,97,100,122,104,58,230,45,115,105, +122,45,45,45,45,112,114,111,112,39,41,59,13,108,105,102,116,84,49,57,58,118,105, +99,101,97,110,100,121,100,101,98,116,62,82,83,83,112,111,111,108,110,101,99,107, +98,108,111,119,84,49,54,58,100,111,111,114,101,118,97,108,84,49,55,58,108,101, +116,115,102,97,105,108,111,114,97,108,112,111,108,108,110,111,118,97,99,111,108, +115,103,101,110,101,32,226,128,148,115,111,102,116,114,111,109,101,116,105,108, +108,114,111,115,115,60,104,51,62,112,111,117,114,102,97,100,101,112,105,110,107, +60,116,114,62,109,105,110,105,41,124,33,40,109,105,110,101,122,104,58,232,98,97, +114,115,104,101,97,114,48,48,41,59,109,105,108,107,32,45,45,62,105,114,111,110, +102,114,101,100,100,105,115,107,119,101,110,116,115,111,105,108,112,117,116,115, +47,106,115,47,104,111,108,121,84,50,50,58,73,83,66,78,84,50,48,58,97,100,97,109, +115,101,101,115,60,104,50,62,106,115,111,110,39,44,32,39,99,111,110,116,84,50,49 +,58,32,82,83,83,108,111,111,112,97,115,105,97,109,111,111,110,60,47,112,62,115, +111,117,108,76,73,78,69,102,111,114,116,99,97,114,116,84,49,52,58,60,104,49,62, +56,48,112,120,33,45,45,60,57,112,120,59,84,48,52,58,109,105,107,101,58,52,54,90, +110,105,99,101,105,110,99,104,89,111,114,107,114,105,99,101,122,104,58,228,39,41 +,41,59,112,117,114,101,109,97,103,101,112,97,114,97,116,111,110,101,98,111,110, +100,58,51,55,90,95,111,102,95,39,93,41,59,48,48,48,44,122,104,58,231,116,97,110, +107,121,97,114,100,98,111,119,108,98,117,115,104,58,53,54,90,74,97,118,97,51,48, +112,120,10,124,125,10,37,67,51,37,58,51,52,90,106,101,102,102,69,88,80,73,99,97, +115,104,118,105,115,97,103,111,108,102,115,110,111,119,122,104,58,233,113,117, +101,114,46,99,115,115,115,105,99,107,109,101,97,116,109,105,110,46,98,105,110, +100,100,101,108,108,104,105,114,101,112,105,99,115,114,101,110,116,58,51,54,90, +72,84,84,80,45,50,48,49,102,111,116,111,119,111,108,102,69,78,68,32,120,98,111, +120,58,53,52,90,66,79,68,89,100,105,99,107,59,10,125,10,101,120,105,116,58,51,53 +,90,118,97,114,115,98,101,97,116,39,125,41,59,100,105,101,116,57,57,57,59,97,110 +,110,101,125,125,60,47,91,105,93,46,76,97,110,103,107,109,194,178,119,105,114, +101,116,111,121,115,97,100,100,115,115,101,97,108,97,108,101,120,59,10,9,125,101 +,99,104,111,110,105,110,101,46,111,114,103,48,48,53,41,116,111,110,121,106,101, +119,115,115,97,110,100,108,101,103,115,114,111,111,102,48,48,48,41,32,50,48,48, +119,105,110,101,103,101,97,114,100,111,103,115,98,111,111,116,103,97,114,121,99, +117,116,115,116,121,108,101,116,101,109,112,116,105,111,110,46,120,109,108,99, +111,99,107,103,97,110,103,36,40,39,46,53,48,112,120,80,104,46,68,109,105,115,99, +97,108,97,110,108,111,97,110,100,101,115,107,109,105,108,101,114,121,97,110,117, +110,105,120,100,105,115,99,41,59,125,10,100,117,115,116,99,108,105,112,41,46,10, +10,55,48,112,120,45,50,48,48,68,86,68,115,55,93,62,60,116,97,112,101,100,101,109 +,111,105,43,43,41,119,97,103,101,101,117,114,111,112,104,105,108,111,112,116,115 +,104,111,108,101,70,65,81,115,97,115,105,110,45,50,54,84,108,97,98,115,112,101, +116,115,85,82,76,32,98,117,108,107,99,111,111,107,59,125,13,10,72,69,65,68,91,48 +,93,41,97,98,98,114,106,117,97,110,40,49,57,56,108,101,115,104,116,119,105,110, +60,47,105,62,115,111,110,121,103,117,121,115,102,117,99,107,112,105,112,101,124, +45,10,33,48,48,50,41,110,100,111,119,91,49,93,59,91,93,59,10,76,111,103,32,115, +97,108,116,13,10,9,9,98,97,110,103,116,114,105,109,98,97,116,104,41,123,13,10,48 +,48,112,120,10,125,41,59,107,111,58,236,102,101,101,115,97,100,62,13,115,58,47, +47,32,91,93,59,116,111,108,108,112,108,117,103,40,41,123,10,123,13,10,32,46,106, +115,39,50,48,48,112,100,117,97,108,98,111,97,116,46,74,80,71,41,59,10,125,113, +117,111,116,41,59,10,10,39,41,59,10,13,10,125,13,50,48,49,52,50,48,49,53,50,48, +49,54,50,48,49,55,50,48,49,56,50,48,49,57,50,48,50,48,50,48,50,49,50,48,50,50,50 +,48,50,51,50,48,50,52,50,48,50,53,50,48,50,54,50,48,50,55,50,48,50,56,50,48,50, +57,50,48,51,48,50,48,51,49,50,48,51,50,50,48,51,51,50,48,51,52,50,48,51,53,50,48 +,51,54,50,48,51,55,50,48,49,51,50,48,49,50,50,48,49,49,50,48,49,48,50,48,48,57, +50,48,48,56,50,48,48,55,50,48,48,54,50,48,48,53,50,48,48,52,50,48,48,51,50,48,48 +,50,50,48,48,49,50,48,48,48,49,57,57,57,49,57,57,56,49,57,57,55,49,57,57,54,49, +57,57,53,49,57,57,52,49,57,57,51,49,57,57,50,49,57,57,49,49,57,57,48,49,57,56,57 +,49,57,56,56,49,57,56,55,49,57,56,54,49,57,56,53,49,57,56,52,49,57,56,51,49,57, +56,50,49,57,56,49,49,57,56,48,49,57,55,57,49,57,55,56,49,57,55,55,49,57,55,54,49 +,57,55,53,49,57,55,52,49,57,55,51,49,57,55,50,49,57,55,49,49,57,55,48,49,57,54, +57,49,57,54,56,49,57,54,55,49,57,54,54,49,57,54,53,49,57,54,52,49,57,54,51,49,57 +,54,50,49,57,54,49,49,57,54,48,49,57,53,57,49,57,53,56,49,57,53,55,49,57,53,54, +49,57,53,53,49,57,53,52,49,57,53,51,49,57,53,50,49,57,53,49,49,57,53,48,49,48,48 +,48,49,48,50,52,49,51,57,52,48,48,48,48,57,57,57,57,99,111,109,111,109,195,161, +115,101,115,116,101,101,115,116,97,112,101,114,111,116,111,100,111,104,97,99,101 +,99,97,100,97,97,195,177,111,98,105,101,110,100,195,173,97,97,115,195,173,118, +105,100,97,99,97,115,111,111,116,114,111,102,111,114,111,115,111,108,111,111,116 +,114,97,99,117,97,108,100,105,106,111,115,105,100,111,103,114,97,110,116,105,112 +,111,116,101,109,97,100,101,98,101,97,108,103,111,113,117,195,169,101,115,116, +111,110,97,100,97,116,114,101,115,112,111,99,111,99,97,115,97,98,97,106,111,116, +111,100,97,115,105,110,111,97,103,117,97,112,117,101,115,117,110,111,115,97,110, +116,101,100,105,99,101,108,117,105,115,101,108,108,97,109,97,121,111,122,111,110 +,97,97,109,111,114,112,105,115,111,111,98,114,97,99,108,105,99,101,108,108,111, +100,105,111,115,104,111,114,97,99,97,115,105,208,183,208,176,208,189,208,176,208 +,190,208,188,209,128,208,176,209,128,209,131,209,130,208,176,208,189,208,181,208 +,191,208,190,208,190,209,130,208,184,208,183,208,189,208,190,208,180,208,190,209 +,130,208,190,208,182,208,181,208,190,208,189,208,184,209,133,208,157,208,176,208 +,181,208,181,208,177,209,139,208,188,209,139,208,146,209,139,209,129,208,190,208 +,178,209,139,208,178,208,190,208,157,208,190,208,190,208,177,208,159,208,190,208 +,187,208,184,208,189,208,184,208,160,208,164,208,157,208,181,208,156,209,139,209 +,130,209,139,208,158,208,189,208,184,208,188,208,180,208,176,208,151,208,176,208 +,148,208,176,208,157,209,131,208,158,208,177,209,130,208,181,208,152,208,183,208 +,181,208,185,208,189,209,131,208,188,208,188,208,162,209,139,209,131,208,182,217 +,129,217,138,216,163,217,134,217,133,216,167,217,133,216,185,217,131,217,132,216 +,163,217,136,216,177,216,175,217,138,216,167,217,129,217,137,217,135,217,136,217 +,132,217,133,217,132,217,131,216,167,217,136,217,132,217,135,216,168,216,179,216 +,167,217,132,216,165,217,134,217,135,217,138,216,163,217,138,217,130,216,175,217 +,135,217,132,216,171,217,133,216,168,217,135,217,132,217,136,217,132,217,138,216 +,168,217,132,216,167,217,138,216,168,217,131,216,180,217,138,216,167,217,133,216 +,163,217,133,217,134,216,170,216,168,217,138,217,132,217,134,216,173,216,168,217 +,135,217,133,217,133,216,180,217,136,216,180,102,105,114,115,116,118,105,100,101 +,111,108,105,103,104,116,119,111,114,108,100,109,101,100,105,97,119,104,105,116, +101,99,108,111,115,101,98,108,97,99,107,114,105,103,104,116,115,109,97,108,108, +98,111,111,107,115,112,108,97,99,101,109,117,115,105,99,102,105,101,108,100,111, +114,100,101,114,112,111,105,110,116,118,97,108,117,101,108,101,118,101,108,116, +97,98,108,101,98,111,97,114,100,104,111,117,115,101,103,114,111,117,112,119,111, +114,107,115,121,101,97,114,115,115,116,97,116,101,116,111,100,97,121,119,97,116, +101,114,115,116,97,114,116,115,116,121,108,101,100,101,97,116,104,112,111,119, +101,114,112,104,111,110,101,110,105,103,104,116,101,114,114,111,114,105,110,112, +117,116,97,98,111,117,116,116,101,114,109,115,116,105,116,108,101,116,111,111, +108,115,101,118,101,110,116,108,111,99,97,108,116,105,109,101,115,108,97,114,103 +,101,119,111,114,100,115,103,97,109,101,115,115,104,111,114,116,115,112,97,99, +101,102,111,99,117,115,99,108,101,97,114,109,111,100,101,108,98,108,111,99,107, +103,117,105,100,101,114,97,100,105,111,115,104,97,114,101,119,111,109,101,110,97 +,103,97,105,110,109,111,110,101,121,105,109,97,103,101,110,97,109,101,115,121, +111,117,110,103,108,105,110,101,115,108,97,116,101,114,99,111,108,111,114,103, +114,101,101,110,102,114,111,110,116,38,97,109,112,59,119,97,116,99,104,102,111, +114,99,101,112,114,105,99,101,114,117,108,101,115,98,101,103,105,110,97,102,116, +101,114,118,105,115,105,116,105,115,115,117,101,97,114,101,97,115,98,101,108,111 +,119,105,110,100,101,120,116,111,116,97,108,104,111,117,114,115,108,97,98,101, +108,112,114,105,110,116,112,114,101,115,115,98,117,105,108,116,108,105,110,107, +115,115,112,101,101,100,115,116,117,100,121,116,114,97,100,101,102,111,117,110, +100,115,101,110,115,101,117,110,100,101,114,115,104,111,119,110,102,111,114,109, +115,114,97,110,103,101,97,100,100,101,100,115,116,105,108,108,109,111,118,101, +100,116,97,107,101,110,97,98,111,118,101,102,108,97,115,104,102,105,120,101,100, +111,102,116,101,110,111,116,104,101,114,118,105,101,119,115,99,104,101,99,107, +108,101,103,97,108,114,105,118,101,114,105,116,101,109,115,113,117,105,99,107, +115,104,97,112,101,104,117,109,97,110,101,120,105,115,116,103,111,105,110,103, +109,111,118,105,101,116,104,105,114,100,98,97,115,105,99,112,101,97,99,101,115, +116,97,103,101,119,105,100,116,104,108,111,103,105,110,105,100,101,97,115,119, +114,111,116,101,112,97,103,101,115,117,115,101,114,115,100,114,105,118,101,115, +116,111,114,101,98,114,101,97,107,115,111,117,116,104,118,111,105,99,101,115,105 +,116,101,115,109,111,110,116,104,119,104,101,114,101,98,117,105,108,100,119,104, +105,99,104,101,97,114,116,104,102,111,114,117,109,116,104,114,101,101,115,112, +111,114,116,112,97,114,116,121,67,108,105,99,107,108,111,119,101,114,108,105,118 +,101,115,99,108,97,115,115,108,97,121,101,114,101,110,116,114,121,115,116,111, +114,121,117,115,97,103,101,115,111,117,110,100,99,111,117,114,116,121,111,117, +114,32,98,105,114,116,104,112,111,112,117,112,116,121,112,101,115,97,112,112,108 +,121,73,109,97,103,101,98,101,105,110,103,117,112,112,101,114,110,111,116,101, +115,101,118,101,114,121,115,104,111,119,115,109,101,97,110,115,101,120,116,114, +97,109,97,116,99,104,116,114,97,99,107,107,110,111,119,110,101,97,114,108,121,98 +,101,103,97,110,115,117,112,101,114,112,97,112,101,114,110,111,114,116,104,108, +101,97,114,110,103,105,118,101,110,110,97,109,101,100,101,110,100,101,100,84,101 +,114,109,115,112,97,114,116,115,71,114,111,117,112,98,114,97,110,100,117,115,105 +,110,103,119,111,109,97,110,102,97,108,115,101,114,101,97,100,121,97,117,100,105 +,111,116,97,107,101,115,119,104,105,108,101,46,99,111,109,47,108,105,118,101,100 +,99,97,115,101,115,100,97,105,108,121,99,104,105,108,100,103,114,101,97,116,106, +117,100,103,101,116,104,111,115,101,117,110,105,116,115,110,101,118,101,114,98, +114,111,97,100,99,111,97,115,116,99,111,118,101,114,97,112,112,108,101,102,105, +108,101,115,99,121,99,108,101,115,99,101,110,101,112,108,97,110,115,99,108,105, +99,107,119,114,105,116,101,113,117,101,101,110,112,105,101,99,101,101,109,97,105 +,108,102,114,97,109,101,111,108,100,101,114,112,104,111,116,111,108,105,109,105, +116,99,97,99,104,101,99,105,118,105,108,115,99,97,108,101,101,110,116,101,114, +116,104,101,109,101,116,104,101,114,101,116,111,117,99,104,98,111,117,110,100, +114,111,121,97,108,97,115,107,101,100,119,104,111,108,101,115,105,110,99,101,115 +,116,111,99,107,32,110,97,109,101,102,97,105,116,104,104,101,97,114,116,101,109, +112,116,121,111,102,102,101,114,115,99,111,112,101,111,119,110,101,100,109,105, +103,104,116,97,108,98,117,109,116,104,105,110,107,98,108,111,111,100,97,114,114, +97,121,109,97,106,111,114,116,114,117,115,116,99,97,110,111,110,117,110,105,111, +110,99,111,117,110,116,118,97,108,105,100,115,116,111,110,101,83,116,121,108,101 +,76,111,103,105,110,104,97,112,112,121,111,99,99,117,114,108,101,102,116,58,102, +114,101,115,104,113,117,105,116,101,102,105,108,109,115,103,114,97,100,101,110, +101,101,100,115,117,114,98,97,110,102,105,103,104,116,98,97,115,105,115,104,111, +118,101,114,97,117,116,111,59,114,111,117,116,101,46,104,116,109,108,109,105,120 +,101,100,102,105,110,97,108,89,111,117,114,32,115,108,105,100,101,116,111,112, +105,99,98,114,111,119,110,97,108,111,110,101,100,114,97,119,110,115,112,108,105, +116,114,101,97,99,104,82,105,103,104,116,100,97,116,101,115,109,97,114,99,104, +113,117,111,116,101,103,111,111,100,115,76,105,110,107,115,100,111,117,98,116,97 +,115,121,110,99,116,104,117,109,98,97,108,108,111,119,99,104,105,101,102,121,111 +,117,116,104,110,111,118,101,108,49,48,112,120,59,115,101,114,118,101,117,110, +116,105,108,104,97,110,100,115,67,104,101,99,107,83,112,97,99,101,113,117,101, +114,121,106,97,109,101,115,101,113,117,97,108,116,119,105,99,101,48,44,48,48,48, +83,116,97,114,116,112,97,110,101,108,115,111,110,103,115,114,111,117,110,100,101 +,105,103,104,116,115,104,105,102,116,119,111,114,116,104,112,111,115,116,115,108 +,101,97,100,115,119,101,101,107,115,97,118,111,105,100,116,104,101,115,101,109, +105,108,101,115,112,108,97,110,101,115,109,97,114,116,97,108,112,104,97,112,108, +97,110,116,109,97,114,107,115,114,97,116,101,115,112,108,97,121,115,99,108,97, +105,109,115,97,108,101,115,116,101,120,116,115,115,116,97,114,115,119,114,111, +110,103,60,47,104,51,62,116,104,105,110,103,46,111,114,103,47,109,117,108,116, +105,104,101,97,114,100,80,111,119,101,114,115,116,97,110,100,116,111,107,101,110 +,115,111,108,105,100,40,116,104,105,115,98,114,105,110,103,115,104,105,112,115, +115,116,97,102,102,116,114,105,101,100,99,97,108,108,115,102,117,108,108,121,102 +,97,99,116,115,97,103,101,110,116,84,104,105,115,32,47,47,45,45,62,97,100,109, +105,110,101,103,121,112,116,69,118,101,110,116,49,53,112,120,59,69,109,97,105, +108,116,114,117,101,34,99,114,111,115,115,115,112,101,110,116,98,108,111,103,115 +,98,111,120,34,62,110,111,116,101,100,108,101,97,118,101,99,104,105,110,97,115, +105,122,101,115,103,117,101,115,116,60,47,104,52,62,114,111,98,111,116,104,101, +97,118,121,116,114,117,101,44,115,101,118,101,110,103,114,97,110,100,99,114,105, +109,101,115,105,103,110,115,97,119,97,114,101,100,97,110,99,101,112,104,97,115, +101,62,60,33,45,45,101,110,95,85,83,38,35,51,57,59,50,48,48,112,120,95,110,97, +109,101,108,97,116,105,110,101,110,106,111,121,97,106,97,120,46,97,116,105,111, +110,115,109,105,116,104,85,46,83,46,32,104,111,108,100,115,112,101,116,101,114, +105,110,100,105,97,110,97,118,34,62,99,104,97,105,110,115,99,111,114,101,99,111, +109,101,115,100,111,105,110,103,112,114,105,111,114,83,104,97,114,101,49,57,57, +48,115,114,111,109,97,110,108,105,115,116,115,106,97,112,97,110,102,97,108,108, +115,116,114,105,97,108,111,119,110,101,114,97,103,114,101,101,60,47,104,50,62,97 +,98,117,115,101,97,108,101,114,116,111,112,101,114,97,34,45,47,47,87,99,97,114, +100,115,104,105,108,108,115,116,101,97,109,115,80,104,111,116,111,116,114,117, +116,104,99,108,101,97,110,46,112,104,112,63,115,97,105,110,116,109,101,116,97, +108,108,111,117,105,115,109,101,97,110,116,112,114,111,111,102,98,114,105,101, +102,114,111,119,34,62,103,101,110,114,101,116,114,117,99,107,108,111,111,107,115 +,86,97,108,117,101,70,114,97,109,101,46,110,101,116,47,45,45,62,10,60,116,114, +121,32,123,10,118,97,114,32,109,97,107,101,115,99,111,115,116,115,112,108,97,105 +,110,97,100,117,108,116,113,117,101,115,116,116,114,97,105,110,108,97,98,111,114 +,104,101,108,112,115,99,97,117,115,101,109,97,103,105,99,109,111,116,111,114,116 +,104,101,105,114,50,53,48,112,120,108,101,97,115,116,115,116,101,112,115,67,111, +117,110,116,99,111,117,108,100,103,108,97,115,115,115,105,100,101,115,102,117, +110,100,115,104,111,116,101,108,97,119,97,114,100,109,111,117,116,104,109,111, +118,101,115,112,97,114,105,115,103,105,118,101,115,100,117,116,99,104,116,101, +120,97,115,102,114,117,105,116,110,117,108,108,44,124,124,91,93,59,116,111,112, +34,62,10,60,33,45,45,80,79,83,84,34,111,99,101,97,110,60,98,114,47,62,102,108, +111,111,114,115,112,101,97,107,100,101,112,116,104,32,115,105,122,101,98,97,110, +107,115,99,97,116,99,104,99,104,97,114,116,50,48,112,120,59,97,108,105,103,110, +100,101,97,108,115,119,111,117,108,100,53,48,112,120,59,117,114,108,61,34,112,97 +,114,107,115,109,111,117,115,101,77,111,115,116,32,46,46,46,60,47,97,109,111,110 +,103,98,114,97,105,110,98,111,100,121,32,110,111,110,101,59,98,97,115,101,100,99 +,97,114,114,121,100,114,97,102,116,114,101,102,101,114,112,97,103,101,95,104,111 +,109,101,46,109,101,116,101,114,100,101,108,97,121,100,114,101,97,109,112,114, +111,118,101,106,111,105,110,116,60,47,116,114,62,100,114,117,103,115,60,33,45,45 +,32,97,112,114,105,108,105,100,101,97,108,97,108,108,101,110,101,120,97,99,116, +102,111,114,116,104,99,111,100,101,115,108,111,103,105,99,86,105,101,119,32,115, +101,101,109,115,98,108,97,110,107,112,111,114,116,115,32,40,50,48,48,115,97,118, +101,100,95,108,105,110,107,103,111,97,108,115,103,114,97,110,116,103,114,101,101 +,107,104,111,109,101,115,114,105,110,103,115,114,97,116,101,100,51,48,112,120,59 +,119,104,111,115,101,112,97,114,115,101,40,41,59,34,32,66,108,111,99,107,108,105 +,110,117,120,106,111,110,101,115,112,105,120,101,108,39,41,59,34,62,41,59,105, +102,40,45,108,101,102,116,100,97,118,105,100,104,111,114,115,101,70,111,99,117, +115,114,97,105,115,101,98,111,120,101,115,84,114,97,99,107,101,109,101,110,116, +60,47,101,109,62,98,97,114,34,62,46,115,114,99,61,116,111,119,101,114,97,108,116 +,61,34,99,97,98,108,101,104,101,110,114,121,50,52,112,120,59,115,101,116,117,112 +,105,116,97,108,121,115,104,97,114,112,109,105,110,111,114,116,97,115,116,101, +119,97,110,116,115,116,104,105,115,46,114,101,115,101,116,119,104,101,101,108, +103,105,114,108,115,47,99,115,115,47,49,48,48,37,59,99,108,117,98,115,115,116, +117,102,102,98,105,98,108,101,118,111,116,101,115,32,49,48,48,48,107,111,114,101 +,97,125,41,59,13,10,98,97,110,100,115,113,117,101,117,101,61,32,123,125,59,56,48 +,112,120,59,99,107,105,110,103,123,13,10,9,9,97,104,101,97,100,99,108,111,99,107 +,105,114,105,115,104,108,105,107,101,32,114,97,116,105,111,115,116,97,116,115,70 +,111,114,109,34,121,97,104,111,111,41,91,48,93,59,65,98,111,117,116,102,105,110, +100,115,60,47,104,49,62,100,101,98,117,103,116,97,115,107,115,85,82,76,32,61,99, +101,108,108,115,125,41,40,41,59,49,50,112,120,59,112,114,105,109,101,116,101,108 +,108,115,116,117,114,110,115,48,120,54,48,48,46,106,112,103,34,115,112,97,105, +110,98,101,97,99,104,116,97,120,101,115,109,105,99,114,111,97,110,103,101,108,45 +,45,62,60,47,103,105,102,116,115,115,116,101,118,101,45,108,105,110,107,98,111, +100,121,46,125,41,59,10,9,109,111,117,110,116,32,40,49,57,57,70,65,81,60,47,114, +111,103,101,114,102,114,97,110,107,67,108,97,115,115,50,56,112,120,59,102,101, +101,100,115,60,104,49,62,60,115,99,111,116,116,116,101,115,116,115,50,50,112,120 +,59,100,114,105,110,107,41,32,124,124,32,108,101,119,105,115,115,104,97,108,108, +35,48,51,57,59,32,102,111,114,32,108,111,118,101,100,119,97,115,116,101,48,48, +112,120,59,106,97,58,227,130,115,105,109,111,110,60,102,111,110,116,114,101,112, +108,121,109,101,101,116,115,117,110,116,101,114,99,104,101,97,112,116,105,103, +104,116,66,114,97,110,100,41,32,33,61,32,100,114,101,115,115,99,108,105,112,115, +114,111,111,109,115,111,110,107,101,121,109,111,98,105,108,109,97,105,110,46,78, +97,109,101,32,112,108,97,116,101,102,117,110,110,121,116,114,101,101,115,99,111, +109,47,34,49,46,106,112,103,119,109,111,100,101,112,97,114,97,109,83,84,65,82,84 +,108,101,102,116,32,105,100,100,101,110,44,32,50,48,49,41,59,10,125,10,102,111, +114,109,46,118,105,114,117,115,99,104,97,105,114,116,114,97,110,115,119,111,114, +115,116,80,97,103,101,115,105,116,105,111,110,112,97,116,99,104,60,33,45,45,10, +111,45,99,97,99,102,105,114,109,115,116,111,117,114,115,44,48,48,48,32,97,115, +105,97,110,105,43,43,41,123,97,100,111,98,101,39,41,91,48,93,105,100,61,49,48,98 +,111,116,104,59,109,101,110,117,32,46,50,46,109,105,46,112,110,103,34,107,101, +118,105,110,99,111,97,99,104,67,104,105,108,100,98,114,117,99,101,50,46,106,112, +103,85,82,76,41,43,46,106,112,103,124,115,117,105,116,101,115,108,105,99,101,104 +,97,114,114,121,49,50,48,34,32,115,119,101,101,116,116,114,62,13,10,110,97,109, +101,61,100,105,101,103,111,112,97,103,101,32,115,119,105,115,115,45,45,62,10,10, +35,102,102,102,59,34,62,76,111,103,46,99,111,109,34,116,114,101,97,116,115,104, +101,101,116,41,32,38,38,32,49,52,112,120,59,115,108,101,101,112,110,116,101,110, +116,102,105,108,101,100,106,97,58,227,131,105,100,61,34,99,78,97,109,101,34,119, +111,114,115,101,115,104,111,116,115,45,98,111,120,45,100,101,108,116,97,10,38, +108,116,59,98,101,97,114,115,58,52,56,90,60,100,97,116,97,45,114,117,114,97,108, +60,47,97,62,32,115,112,101,110,100,98,97,107,101,114,115,104,111,112,115,61,32, +34,34,59,112,104,112,34,62,99,116,105,111,110,49,51,112,120,59,98,114,105,97,110 +,104,101,108,108,111,115,105,122,101,61,111,61,37,50,70,32,106,111,105,110,109, +97,121,98,101,60,105,109,103,32,105,109,103,34,62,44,32,102,106,115,105,109,103, +34,32,34,41,91,48,93,77,84,111,112,66,84,121,112,101,34,110,101,119,108,121,68, +97,110,115,107,99,122,101,99,104,116,114,97,105,108,107,110,111,119,115,60,47, +104,53,62,102,97,113,34,62,122,104,45,99,110,49,48,41,59,10,45,49,34,41,59,116, +121,112,101,61,98,108,117,101,115,116,114,117,108,121,100,97,118,105,115,46,106, +115,39,59,62,13,10,60,33,115,116,101,101,108,32,121,111,117,32,104,50,62,13,10, +102,111,114,109,32,106,101,115,117,115,49,48,48,37,32,109,101,110,117,46,13,10,9 +,13,10,119,97,108,101,115,114,105,115,107,115,117,109,101,110,116,100,100,105, +110,103,98,45,108,105,107,116,101,97,99,104,103,105,102,34,32,118,101,103,97,115 +,100,97,110,115,107,101,101,115,116,105,115,104,113,105,112,115,117,111,109,105, +115,111,98,114,101,100,101,115,100,101,101,110,116,114,101,116,111,100,111,115, +112,117,101,100,101,97,195,177,111,115,101,115,116,195,161,116,105,101,110,101, +104,97,115,116,97,111,116,114,111,115,112,97,114,116,101,100,111,110,100,101,110 +,117,101,118,111,104,97,99,101,114,102,111,114,109,97,109,105,115,109,111,109, +101,106,111,114,109,117,110,100,111,97,113,117,195,173,100,195,173,97,115,115, +195,179,108,111,97,121,117,100,97,102,101,99,104,97,116,111,100,97,115,116,97, +110,116,111,109,101,110,111,115,100,97,116,111,115,111,116,114,97,115,115,105, +116,105,111,109,117,99,104,111,97,104,111,114,97,108,117,103,97,114,109,97,121, +111,114,101,115,116,111,115,104,111,114,97,115,116,101,110,101,114,97,110,116, +101,115,102,111,116,111,115,101,115,116,97,115,112,97,195,173,115,110,117,101, +118,97,115,97,108,117,100,102,111,114,111,115,109,101,100,105,111,113,117,105, +101,110,109,101,115,101,115,112,111,100,101,114,99,104,105,108,101,115,101,114, +195,161,118,101,99,101,115,100,101,99,105,114,106,111,115,195,169,101,115,116,97 +,114,118,101,110,116,97,103,114,117,112,111,104,101,99,104,111,101,108,108,111, +115,116,101,110,103,111,97,109,105,103,111,99,111,115,97,115,110,105,118,101,108 +,103,101,110,116,101,109,105,115,109,97,97,105,114,101,115,106,117,108,105,111, +116,101,109,97,115,104,97,99,105,97,102,97,118,111,114,106,117,110,105,111,108, +105,98,114,101,112,117,110,116,111,98,117,101,110,111,97,117,116,111,114,97,98, +114,105,108,98,117,101,110,97,116,101,120,116,111,109,97,114,122,111,115,97,98, +101,114,108,105,115,116,97,108,117,101,103,111,99,195,179,109,111,101,110,101, +114,111,106,117,101,103,111,112,101,114,195,186,104,97,98,101,114,101,115,116, +111,121,110,117,110,99,97,109,117,106,101,114,118,97,108,111,114,102,117,101,114 +,97,108,105,98,114,111,103,117,115,116,97,105,103,117,97,108,118,111,116,111,115 +,99,97,115,111,115,103,117,195,173,97,112,117,101,100,111,115,111,109,111,115,97 +,118,105,115,111,117,115,116,101,100,100,101,98,101,110,110,111,99,104,101,98, +117,115,99,97,102,97,108,116,97,101,117,114,111,115,115,101,114,105,101,100,105, +99,104,111,99,117,114,115,111,99,108,97,118,101,99,97,115,97,115,108,101,195,179 +,110,112,108,97,122,111,108,97,114,103,111,111,98,114,97,115,118,105,115,116,97, +97,112,111,121,111,106,117,110,116,111,116,114,97,116,97,118,105,115,116,111,99, +114,101,97,114,99,97,109,112,111,104,101,109,111,115,99,105,110,99,111,99,97,114 +,103,111,112,105,115,111,115,111,114,100,101,110,104,97,99,101,110,195,161,114, +101,97,100,105,115,99,111,112,101,100,114,111,99,101,114,99,97,112,117,101,100, +97,112,97,112,101,108,109,101,110,111,114,195,186,116,105,108,99,108,97,114,111, +106,111,114,103,101,99,97,108,108,101,112,111,110,101,114,116,97,114,100,101,110 +,97,100,105,101,109,97,114,99,97,115,105,103,117,101,101,108,108,97,115,115,105, +103,108,111,99,111,99,104,101,109,111,116,111,115,109,97,100,114,101,99,108,97, +115,101,114,101,115,116,111,110,105,195,177,111,113,117,101,100,97,112,97,115,97 +,114,98,97,110,99,111,104,105,106,111,115,118,105,97,106,101,112,97,98,108,111, +195,169,115,116,101,118,105,101,110,101,114,101,105,110,111,100,101,106,97,114, +102,111,110,100,111,99,97,110,97,108,110,111,114,116,101,108,101,116,114,97,99, +97,117,115,97,116,111,109,97,114,109,97,110,111,115,108,117,110,101,115,97,117, +116,111,115,118,105,108,108,97,118,101,110,100,111,112,101,115,97,114,116,105, +112,111,115,116,101,110,103,97,109,97,114,99,111,108,108,101,118,97,112,97,100, +114,101,117,110,105,100,111,118,97,109,111,115,122,111,110,97,115,97,109,98,111, +115,98,97,110,100,97,109,97,114,105,97,97,98,117,115,111,109,117,99,104,97,115, +117,98,105,114,114,105,111,106,97,118,105,118,105,114,103,114,97,100,111,99,104, +105,99,97,97,108,108,195,173,106,111,118,101,110,100,105,99,104,97,101,115,116, +97,110,116,97,108,101,115,115,97,108,105,114,115,117,101,108,111,112,101,115,111 +,115,102,105,110,101,115,108,108,97,109,97,98,117,115,99,111,195,169,115,116,97, +108,108,101,103,97,110,101,103,114,111,112,108,97,122,97,104,117,109,111,114,112 +,97,103,97,114,106,117,110,116,97,100,111,98,108,101,105,115,108,97,115,98,111, +108,115,97,98,97,195,177,111,104,97,98,108,97,108,117,99,104,97,195,129,114,101, +97,100,105,99,101,110,106,117,103,97,114,110,111,116,97,115,118,97,108,108,101, +97,108,108,195,161,99,97,114,103,97,100,111,108,111,114,97,98,97,106,111,101,115 +,116,195,169,103,117,115,116,111,109,101,110,116,101,109,97,114,105,111,102,105, +114,109,97,99,111,115,116,111,102,105,99,104,97,112,108,97,116,97,104,111,103,97 +,114,97,114,116,101,115,108,101,121,101,115,97,113,117,101,108,109,117,115,101, +111,98,97,115,101,115,112,111,99,111,115,109,105,116,97,100,99,105,101,108,111, +99,104,105,99,111,109,105,101,100,111,103,97,110,97,114,115,97,110,116,111,101, +116,97,112,97,100,101,98,101,115,112,108,97,121,97,114,101,100,101,115,115,105, +101,116,101,99,111,114,116,101,99,111,114,101,97,100,117,100,97,115,100,101,115, +101,111,118,105,101,106,111,100,101,115,101,97,97,103,117,97,115,38,113,117,111, +116,59,100,111,109,97,105,110,99,111,109,109,111,110,115,116,97,116,117,115,101, +118,101,110,116,115,109,97,115,116,101,114,115,121,115,116,101,109,97,99,116,105 +,111,110,98,97,110,110,101,114,114,101,109,111,118,101,115,99,114,111,108,108, +117,112,100,97,116,101,103,108,111,98,97,108,109,101,100,105,117,109,102,105,108 +,116,101,114,110,117,109,98,101,114,99,104,97,110,103,101,114,101,115,117,108, +116,112,117,98,108,105,99,115,99,114,101,101,110,99,104,111,111,115,101,110,111, +114,109,97,108,116,114,97,118,101,108,105,115,115,117,101,115,115,111,117,114,99 +,101,116,97,114,103,101,116,115,112,114,105,110,103,109,111,100,117,108,101,109, +111,98,105,108,101,115,119,105,116,99,104,112,104,111,116,111,115,98,111,114,100 +,101,114,114,101,103,105,111,110,105,116,115,101,108,102,115,111,99,105,97,108, +97,99,116,105,118,101,99,111,108,117,109,110,114,101,99,111,114,100,102,111,108, +108,111,119,116,105,116,108,101,62,101,105,116,104,101,114,108,101,110,103,116, +104,102,97,109,105,108,121,102,114,105,101,110,100,108,97,121,111,117,116,97,117 +,116,104,111,114,99,114,101,97,116,101,114,101,118,105,101,119,115,117,109,109, +101,114,115,101,114,118,101,114,112,108,97,121,101,100,112,108,97,121,101,114, +101,120,112,97,110,100,112,111,108,105,99,121,102,111,114,109,97,116,100,111,117 +,98,108,101,112,111,105,110,116,115,115,101,114,105,101,115,112,101,114,115,111, +110,108,105,118,105,110,103,100,101,115,105,103,110,109,111,110,116,104,115,102, +111,114,99,101,115,117,110,105,113,117,101,119,101,105,103,104,116,112,101,111, +112,108,101,101,110,101,114,103,121,110,97,116,117,114,101,115,101,97,114,99,104 +,102,105,103,117,114,101,104,97,118,105,110,103,99,117,115,116,111,109,111,102, +102,115,101,116,108,101,116,116,101,114,119,105,110,100,111,119,115,117,98,109, +105,116,114,101,110,100,101,114,103,114,111,117,112,115,117,112,108,111,97,100, +104,101,97,108,116,104,109,101,116,104,111,100,118,105,100,101,111,115,115,99, +104,111,111,108,102,117,116,117,114,101,115,104,97,100,111,119,100,101,98,97,116 +,101,118,97,108,117,101,115,79,98,106,101,99,116,111,116,104,101,114,115,114,105 +,103,104,116,115,108,101,97,103,117,101,99,104,114,111,109,101,115,105,109,112, +108,101,110,111,116,105,99,101,115,104,97,114,101,100,101,110,100,105,110,103, +115,101,97,115,111,110,114,101,112,111,114,116,111,110,108,105,110,101,115,113, +117,97,114,101,98,117,116,116,111,110,105,109,97,103,101,115,101,110,97,98,108, +101,109,111,118,105,110,103,108,97,116,101,115,116,119,105,110,116,101,114,70, +114,97,110,99,101,112,101,114,105,111,100,115,116,114,111,110,103,114,101,112, +101,97,116,76,111,110,100,111,110,100,101,116,97,105,108,102,111,114,109,101,100 +,100,101,109,97,110,100,115,101,99,117,114,101,112,97,115,115,101,100,116,111, +103,103,108,101,112,108,97,99,101,115,100,101,118,105,99,101,115,116,97,116,105, +99,99,105,116,105,101,115,115,116,114,101,97,109,121,101,108,108,111,119,97,116, +116,97,99,107,115,116,114,101,101,116,102,108,105,103,104,116,104,105,100,100, +101,110,105,110,102,111,34,62,111,112,101,110,101,100,117,115,101,102,117,108, +118,97,108,108,101,121,99,97,117,115,101,115,108,101,97,100,101,114,115,101,99, +114,101,116,115,101,99,111,110,100,100,97,109,97,103,101,115,112,111,114,116,115 +,101,120,99,101,112,116,114,97,116,105,110,103,115,105,103,110,101,100,116,104, +105,110,103,115,101,102,102,101,99,116,102,105,101,108,100,115,115,116,97,116, +101,115,111,102,102,105,99,101,118,105,115,117,97,108,101,100,105,116,111,114, +118,111,108,117,109,101,82,101,112,111,114,116,109,117,115,101,117,109,109,111, +118,105,101,115,112,97,114,101,110,116,97,99,99,101,115,115,109,111,115,116,108, +121,109,111,116,104,101,114,34,32,105,100,61,34,109,97,114,107,101,116,103,114, +111,117,110,100,99,104,97,110,99,101,115,117,114,118,101,121,98,101,102,111,114, +101,115,121,109,98,111,108,109,111,109,101,110,116,115,112,101,101,99,104,109, +111,116,105,111,110,105,110,115,105,100,101,109,97,116,116,101,114,67,101,110, +116,101,114,111,98,106,101,99,116,101,120,105,115,116,115,109,105,100,100,108, +101,69,117,114,111,112,101,103,114,111,119,116,104,108,101,103,97,99,121,109,97, +110,110,101,114,101,110,111,117,103,104,99,97,114,101,101,114,97,110,115,119,101 +,114,111,114,105,103,105,110,112,111,114,116,97,108,99,108,105,101,110,116,115, +101,108,101,99,116,114,97,110,100,111,109,99,108,111,115,101,100,116,111,112,105 +,99,115,99,111,109,105,110,103,102,97,116,104,101,114,111,112,116,105,111,110, +115,105,109,112,108,121,114,97,105,115,101,100,101,115,99,97,112,101,99,104,111, +115,101,110,99,104,117,114,99,104,100,101,102,105,110,101,114,101,97,115,111,110 +,99,111,114,110,101,114,111,117,116,112,117,116,109,101,109,111,114,121,105,102, +114,97,109,101,112,111,108,105,99,101,109,111,100,101,108,115,78,117,109,98,101, +114,100,117,114,105,110,103,111,102,102,101,114,115,115,116,121,108,101,115,107, +105,108,108,101,100,108,105,115,116,101,100,99,97,108,108,101,100,115,105,108, +118,101,114,109,97,114,103,105,110,100,101,108,101,116,101,98,101,116,116,101, +114,98,114,111,119,115,101,108,105,109,105,116,115,71,108,111,98,97,108,115,105, +110,103,108,101,119,105,100,103,101,116,99,101,110,116,101,114,98,117,100,103, +101,116,110,111,119,114,97,112,99,114,101,100,105,116,99,108,97,105,109,115,101, +110,103,105,110,101,115,97,102,101,116,121,99,104,111,105,99,101,115,112,105,114 +,105,116,45,115,116,121,108,101,115,112,114,101,97,100,109,97,107,105,110,103, +110,101,101,100,101,100,114,117,115,115,105,97,112,108,101,97,115,101,101,120, +116,101,110,116,83,99,114,105,112,116,98,114,111,107,101,110,97,108,108,111,119, +115,99,104,97,114,103,101,100,105,118,105,100,101,102,97,99,116,111,114,109,101, +109,98,101,114,45,98,97,115,101,100,116,104,101,111,114,121,99,111,110,102,105, +103,97,114,111,117,110,100,119,111,114,107,101,100,104,101,108,112,101,100,67, +104,117,114,99,104,105,109,112,97,99,116,115,104,111,117,108,100,97,108,119,97, +121,115,108,111,103,111,34,32,98,111,116,116,111,109,108,105,115,116,34,62,41, +123,118,97,114,32,112,114,101,102,105,120,111,114,97,110,103,101,72,101,97,100, +101,114,46,112,117,115,104,40,99,111,117,112,108,101,103,97,114,100,101,110,98, +114,105,100,103,101,108,97,117,110,99,104,82,101,118,105,101,119,116,97,107,105, +110,103,118,105,115,105,111,110,108,105,116,116,108,101,100,97,116,105,110,103, +66,117,116,116,111,110,98,101,97,117,116,121,116,104,101,109,101,115,102,111,114 +,103,111,116,83,101,97,114,99,104,97,110,99,104,111,114,97,108,109,111,115,116, +108,111,97,100,101,100,67,104,97,110,103,101,114,101,116,117,114,110,115,116,114 +,105,110,103,114,101,108,111,97,100,77,111,98,105,108,101,105,110,99,111,109,101 +,115,117,112,112,108,121,83,111,117,114,99,101,111,114,100,101,114,115,118,105, +101,119,101,100,38,110,98,115,112,59,99,111,117,114,115,101,65,98,111,117,116,32 +,105,115,108,97,110,100,60,104,116,109,108,32,99,111,111,107,105,101,110,97,109, +101,61,34,97,109,97,122,111,110,109,111,100,101,114,110,97,100,118,105,99,101, +105,110,60,47,97,62,58,32,84,104,101,32,100,105,97,108,111,103,104,111,117,115, +101,115,66,69,71,73,78,32,77,101,120,105,99,111,115,116,97,114,116,115,99,101, +110,116,114,101,104,101,105,103,104,116,97,100,100,105,110,103,73,115,108,97,110 +,100,97,115,115,101,116,115,69,109,112,105,114,101,83,99,104,111,111,108,101,102 +,102,111,114,116,100,105,114,101,99,116,110,101,97,114,108,121,109,97,110,117,97 +,108,83,101,108,101,99,116,46,10,10,79,110,101,106,111,105,110,101,100,109,101, +110,117,34,62,80,104,105,108,105,112,97,119,97,114,100,115,104,97,110,100,108, +101,105,109,112,111,114,116,79,102,102,105,99,101,114,101,103,97,114,100,115,107 +,105,108,108,115,110,97,116,105,111,110,83,112,111,114,116,115,100,101,103,114, +101,101,119,101,101,107,108,121,32,40,101,46,103,46,98,101,104,105,110,100,100, +111,99,116,111,114,108,111,103,103,101,100,117,110,105,116,101,100,60,47,98,62, +60,47,98,101,103,105,110,115,112,108,97,110,116,115,97,115,115,105,115,116,97, +114,116,105,115,116,105,115,115,117,101,100,51,48,48,112,120,124,99,97,110,97, +100,97,97,103,101,110,99,121,115,99,104,101,109,101,114,101,109,97,105,110,66, +114,97,122,105,108,115,97,109,112,108,101,108,111,103,111,34,62,98,101,121,111, +110,100,45,115,99,97,108,101,97,99,99,101,112,116,115,101,114,118,101,100,109,97 +,114,105,110,101,70,111,111,116,101,114,99,97,109,101,114,97,60,47,104,49,62,10, +95,102,111,114,109,34,108,101,97,118,101,115,115,116,114,101,115,115,34,32,47,62 +,13,10,46,103,105,102,34,32,111,110,108,111,97,100,108,111,97,100,101,114,79,120 +,102,111,114,100,115,105,115,116,101,114,115,117,114,118,105,118,108,105,115,116 +,101,110,102,101,109,97,108,101,68,101,115,105,103,110,115,105,122,101,61,34,97, +112,112,101,97,108,116,101,120,116,34,62,108,101,118,101,108,115,116,104,97,110, +107,115,104,105,103,104,101,114,102,111,114,99,101,100,97,110,105,109,97,108,97, +110,121,111,110,101,65,102,114,105,99,97,97,103,114,101,101,100,114,101,99,101, +110,116,80,101,111,112,108,101,60,98,114,32,47,62,119,111,110,100,101,114,112, +114,105,99,101,115,116,117,114,110,101,100,124,124,32,123,125,59,109,97,105,110, +34,62,105,110,108,105,110,101,115,117,110,100,97,121,119,114,97,112,34,62,102,97 +,105,108,101,100,99,101,110,115,117,115,109,105,110,117,116,101,98,101,97,99,111 +,110,113,117,111,116,101,115,49,53,48,112,120,124,101,115,116,97,116,101,114,101 +,109,111,116,101,101,109,97,105,108,34,108,105,110,107,101,100,114,105,103,104, +116,59,115,105,103,110,97,108,102,111,114,109,97,108,49,46,104,116,109,108,115, +105,103,110,117,112,112,114,105,110,99,101,102,108,111,97,116,58,46,112,110,103, +34,32,102,111,114,117,109,46,65,99,99,101,115,115,112,97,112,101,114,115,115,111 +,117,110,100,115,101,120,116,101,110,100,72,101,105,103,104,116,115,108,105,100, +101,114,85,84,70,45,56,34,38,97,109,112,59,32,66,101,102,111,114,101,46,32,87, +105,116,104,115,116,117,100,105,111,111,119,110,101,114,115,109,97,110,97,103, +101,112,114,111,102,105,116,106,81,117,101,114,121,97,110,110,117,97,108,112,97, +114,97,109,115,98,111,117,103,104,116,102,97,109,111,117,115,103,111,111,103,108 +,101,108,111,110,103,101,114,105,43,43,41,32,123,105,115,114,97,101,108,115,97, +121,105,110,103,100,101,99,105,100,101,104,111,109,101,34,62,104,101,97,100,101, +114,101,110,115,117,114,101,98,114,97,110,99,104,112,105,101,99,101,115,98,108, +111,99,107,59,115,116,97,116,101,100,116,111,112,34,62,60,114,97,99,105,110,103, +114,101,115,105,122,101,45,45,38,103,116,59,112,97,99,105,116,121,115,101,120, +117,97,108,98,117,114,101,97,117,46,106,112,103,34,32,49,48,44,48,48,48,111,98, +116,97,105,110,116,105,116,108,101,115,97,109,111,117,110,116,44,32,73,110,99,46 +,99,111,109,101,100,121,109,101,110,117,34,32,108,121,114,105,99,115,116,111,100 +,97,121,46,105,110,100,101,101,100,99,111,117,110,116,121,95,108,111,103,111,46, +70,97,109,105,108,121,108,111,111,107,101,100,77,97,114,107,101,116,108,115,101, +32,105,102,80,108,97,121,101,114,116,117,114,107,101,121,41,59,118,97,114,32,102 +,111,114,101,115,116,103,105,118,105,110,103,101,114,114,111,114,115,68,111,109, +97,105,110,125,101,108,115,101,123,105,110,115,101,114,116,66,108,111,103,60,47, +102,111,111,116,101,114,108,111,103,105,110,46,102,97,115,116,101,114,97,103,101 +,110,116,115,60,98,111,100,121,32,49,48,112,120,32,48,112,114,97,103,109,97,102, +114,105,100,97,121,106,117,110,105,111,114,100,111,108,108,97,114,112,108,97,99, +101,100,99,111,118,101,114,115,112,108,117,103,105,110,53,44,48,48,48,32,112,97, +103,101,34,62,98,111,115,116,111,110,46,116,101,115,116,40,97,118,97,116,97,114, +116,101,115,116,101,100,95,99,111,117,110,116,102,111,114,117,109,115,115,99,104 +,101,109,97,105,110,100,101,120,44,102,105,108,108,101,100,115,104,97,114,101, +115,114,101,97,100,101,114,97,108,101,114,116,40,97,112,112,101,97,114,83,117,98 +,109,105,116,108,105,110,101,34,62,98,111,100,121,34,62,10,42,32,84,104,101,84, +104,111,117,103,104,115,101,101,105,110,103,106,101,114,115,101,121,78,101,119, +115,60,47,118,101,114,105,102,121,101,120,112,101,114,116,105,110,106,117,114, +121,119,105,100,116,104,61,67,111,111,107,105,101,83,84,65,82,84,32,97,99,114, +111,115,115,95,105,109,97,103,101,116,104,114,101,97,100,110,97,116,105,118,101, +112,111,99,107,101,116,98,111,120,34,62,10,83,121,115,116,101,109,32,68,97,118, +105,100,99,97,110,99,101,114,116,97,98,108,101,115,112,114,111,118,101,100,65, +112,114,105,108,32,114,101,97,108,108,121,100,114,105,118,101,114,105,116,101, +109,34,62,109,111,114,101,34,62,98,111,97,114,100,115,99,111,108,111,114,115,99, +97,109,112,117,115,102,105,114,115,116,32,124,124,32,91,93,59,109,101,100,105,97 +,46,103,117,105,116,97,114,102,105,110,105,115,104,119,105,100,116,104,58,115, +104,111,119,101,100,79,116,104,101,114,32,46,112,104,112,34,32,97,115,115,117, +109,101,108,97,121,101,114,115,119,105,108,115,111,110,115,116,111,114,101,115, +114,101,108,105,101,102,115,119,101,100,101,110,67,117,115,116,111,109,101,97, +115,105,108,121,32,121,111,117,114,32,83,116,114,105,110,103,10,10,87,104,105, +108,116,97,121,108,111,114,99,108,101,97,114,58,114,101,115,111,114,116,102,114, +101,110,99,104,116,104,111,117,103,104,34,41,32,43,32,34,60,98,111,100,121,62,98 +,117,121,105,110,103,98,114,97,110,100,115,77,101,109,98,101,114,110,97,109,101, +34,62,111,112,112,105,110,103,115,101,99,116,111,114,53,112,120,59,34,62,118,115 +,112,97,99,101,112,111,115,116,101,114,109,97,106,111,114,32,99,111,102,102,101, +101,109,97,114,116,105,110,109,97,116,117,114,101,104,97,112,112,101,110,60,47, +110,97,118,62,107,97,110,115,97,115,108,105,110,107,34,62,73,109,97,103,101,115, +61,102,97,108,115,101,119,104,105,108,101,32,104,115,112,97,99,101,48,38,97,109, +112,59,32,10,10,73,110,32,32,112,111,119,101,114,80,111,108,115,107,105,45,99, +111,108,111,114,106,111,114,100,97,110,66,111,116,116,111,109,83,116,97,114,116, +32,45,99,111,117,110,116,50,46,104,116,109,108,110,101,119,115,34,62,48,49,46, +106,112,103,79,110,108,105,110,101,45,114,105,103,104,116,109,105,108,108,101, +114,115,101,110,105,111,114,73,83,66,78,32,48,48,44,48,48,48,32,103,117,105,100, +101,115,118,97,108,117,101,41,101,99,116,105,111,110,114,101,112,97,105,114,46, +120,109,108,34,32,32,114,105,103,104,116,115,46,104,116,109,108,45,98,108,111,99 +,107,114,101,103,69,120,112,58,104,111,118,101,114,119,105,116,104,105,110,118, +105,114,103,105,110,112,104,111,110,101,115,60,47,116,114,62,13,117,115,105,110, +103,32,10,9,118,97,114,32,62,39,41,59,10,9,60,47,116,100,62,10,60,47,116,114,62, +10,98,97,104,97,115,97,98,114,97,115,105,108,103,97,108,101,103,111,109,97,103, +121,97,114,112,111,108,115,107,105,115,114,112,115,107,105,216,177,216,175,217, +136,228,184,173,230,150,135,231,174,128,228,189,147,231,185,129,233,171,148,228, +191,161,230,129,175,228,184,173,229,155,189,230,136,145,228,187,172,228,184,128, +228,184,170,229,133,172,229,143,184,231,174,161,231,144,134,232,174,186,229,157, +155,229,143,175,228,187,165,230,156,141,229,138,161,230,151,182,233,151,180,228, +184,170,228,186,186,228,186,167,229,147,129,232,135,170,229,183,177,228,188,129, +228,184,154,230,159,165,231,156,139,229,183,165,228,189,156,232,129,148,231,179, +187,230,178,161,230,156,137,231,189,145,231,171,153,230,137,128,230,156,137,232, +175,132,232,174,186,228,184,173,229,191,131,230,150,135,231,171,160,231,148,168, +230,136,183,233,166,150,233,161,181,228,189,156,232,128,133,230,138,128,230,156, +175,233,151,174,233,162,152,231,155,184,229,133,179,228,184,139,232,189,189,230, +144,156,231,180,162,228,189,191,231,148,168,232,189,175,228,187,182,229,156,168, +231,186,191,228,184,187,233,162,152,232,181,132,230,150,153,232,167,134,233,162, +145,229,155,158,229,164,141,230,179,168,229,134,140,231,189,145,231,187,156,230, +148,182,232,151,143,229,134,133,229,174,185,230,142,168,232,141,144,229,184,130, +229,156,186,230,182,136,230,129,175,231,169,186,233,151,180,229,143,145,229,184, +131,228,187,128,228,185,136,229,165,189,229,143,139,231,148,159,230,180,187,229, +155,190,231,137,135,229,143,145,229,177,149,229,166,130,230,158,156,230,137,139, +230,156,186,230,150,176,233,151,187,230,156,128,230,150,176,230,150,185,229,188, +143,229,140,151,228,186,172,230,143,144,228,190,155,229,133,179,228,186,142,230, +155,180,229,164,154,232,191,153,228,184,170,231,179,187,231,187,159,231,159,165, +233,129,147,230,184,184,230,136,143,229,185,191,229,145,138,229,133,182,228,187, +150,229,143,145,232,161,168,229,174,137,229,133,168,231,172,172,228,184,128,228, +188,154,229,145,152,232,191,155,232,161,140,231,130,185,229,135,187,231,137,136, +230,157,131,231,148,181,229,173,144,228,184,150,231,149,140,232,174,190,232,174, +161,229,133,141,232,180,185,230,149,153,232,130,178,229,138,160,229,133,165,230, +180,187,229,138,168,228,187,150,228,187,172,229,149,134,229,147,129,229,141,154, +229,174,162,231,142,176,229,156,168,228,184,138,230,181,183,229,166,130,228,189, +149,229,183,178,231,187,143,231,149,153,232,168,128,232,175,166,231,187,134,231, +164,190,229,140,186,231,153,187,229,189,149,230,156,172,231,171,153,233,156,128, +232,166,129,228,187,183,230,160,188,230,148,175,230,140,129,229,155,189,233,153, +133,233,147,190,230,142,165,229,155,189,229,174,182,229,187,186,232,174,190,230, +156,139,229,143,139,233,152,133,232,175,187,230,179,149,229,190,139,228,189,141, +231,189,174,231,187,143,230,181,142,233,128,137,230,139,169,232,191,153,230,160, +183,229,189,147,229,137,141,229,136,134,231,177,187,230,142,146,232,161,140,229, +155,160,228,184,186,228,186,164,230,152,147,230,156,128,229,144,142,233,159,179, +228,185,144,228,184,141,232,131,189,233,128,154,232,191,135,232,161,140,228,184, +154,231,167,145,230,138,128,229,143,175,232,131,189,232,174,190,229,164,135,229, +144,136,228,189,156,229,164,167,229,174,182,231,164,190,228,188,154,231,160,148, +231,169,182,228,184,147,228,184,154,229,133,168,233,131,168,233,161,185,231,155, +174,232,191,153,233,135,140,232,191,152,230,152,175,229,188,128,229,167,139,230, +131,133,229,134,181,231,148,181,232,132,145,230,150,135,228,187,182,229,147,129, +231,137,140,229,184,174,229,138,169,230,150,135,229,140,150,232,181,132,230,186, +144,229,164,167,229,173,166,229,173,166,228,185,160,229,156,176,229,157,128,230, +181,143,232,167,136,230,138,149,232,181,132,229,183,165,231,168,139,232,166,129, +230,177,130,230,128,142,228,185,136,230,151,182,229,128,153,229,138,159,232,131, +189,228,184,187,232,166,129,231,155,174,229,137,141,232,181,132,232,174,175,229, +159,142,229,184,130,230,150,185,230,179,149,231,148,181,229,189,177,230,139,155, +232,129,152,229,163,176,230,152,142,228,187,187,228,189,149,229,129,165,229,186, +183,230,149,176,230,141,174,231,190,142,229,155,189,230,177,189,232,189,166,228, +187,139,231,187,141,228,189,134,230,152,175,228,186,164,230,181,129,231,148,159, +228,186,167,230,137,128,228,187,165,231,148,181,232,175,157,230,152,190,231,164, +186,228,184,128,228,186,155,229,141,149,228,189,141,228,186,186,229,145,152,229, +136,134,230,158,144,229,156,176,229,155,190,230,151,133,230,184,184,229,183,165, +229,133,183,229,173,166,231,148,159,231,179,187,229,136,151,231,189,145,229,143, +139,229,184,150,229,173,144,229,175,134,231,160,129,233,162,145,233,129,147,230, +142,167,229,136,182,229,156,176,229,140,186,229,159,186,230,156,172,229,133,168, +229,155,189,231,189,145,228,184,138,233,135,141,232,166,129,231,172,172,228,186, +140,229,150,156,230,172,162,232,191,155,229,133,165,229,143,139,230,131,133,232, +191,153,228,186,155,232,128,131,232,175,149,229,143,145,231,142,176,229,159,185, +232,174,173,228,187,165,228,184,138,230,148,191,229,186,156,230,136,144,228,184, +186,231,142,175,229,162,131,233,166,153,230,184,175,229,144,140,230,151,182,229, +168,177,228,185,144,229,143,145,233,128,129,228,184,128,229,174,154,229,188,128, +229,143,145,228,189,156,229,147,129,230,160,135,229,135,134,230,172,162,232,191, +142,232,167,163,229,134,179,229,156,176,230,150,185,228,184,128,228,184,139,228, +187,165,229,143,138,232,180,163,228,187,187,230,136,150,232,128,133,229,174,162, +230,136,183,228,187,163,232,161,168,231,167,175,229,136,134,229,165,179,228,186, +186,230,149,176,231,160,129,233,148,128,229,148,174,229,135,186,231,142,176,231, +166,187,231,186,191,229,186,148,231,148,168,229,136,151,232,161,168,228,184,141, +229,144,140,231,188,150,232,190,145,231,187,159,232,174,161,230,159,165,232,175, +162,228,184,141,232,166,129,230,156,137,229,133,179,230,156,186,230,158,132,229, +190,136,229,164,154,230,146,173,230,148,190,231,187,132,231,187,135,230,148,191, +231,173,150,231,155,180,230,142,165,232,131,189,229,138,155,230,157,165,230,186, +144,230,153,130,233,150,147,231,156,139,229,136,176,231,131,173,233,151,168,229, +133,179,233,148,174,228,184,147,229,140,186,233,157,158,229,184,184,232,139,177, +232,175,173,231,153,190,229,186,166,229,184,140,230,156,155,231,190,142,229,165, +179,230,175,148,232,190,131,231,159,165,232,175,134,232,167,132,229,174,154,229, +187,186,232,174,174,233,131,168,233,151,168,230,132,143,232,167,129,231,178,190, +229,189,169,230,151,165,230,156,172,230,143,144,233,171,152,229,143,145,232,168, +128,230,150,185,233,157,162,229,159,186,233,135,145,229,164,132,231,144,134,230, +157,131,233,153,144,229,189,177,231,137,135,233,147,182,232,161,140,232,191,152, +230,156,137,229,136,134,228,186,171,231,137,169,229,147,129,231,187,143,232,144, +165,230,183,187,229,138,160,228,184,147,229,174,182,232,191,153,231,167,141,232, +175,157,233,162,152,232,181,183,230,157,165,228,184,154,229,138,161,229,133,172, +229,145,138,232,174,176,229,189,149,231,174,128,228,187,139,232,180,168,233,135, +143,231,148,183,228,186,186,229,189,177,229,147,141,229,188,149,231,148,168,230, +138,165,229,145,138,233,131,168,229,136,134,229,191,171,233,128,159,229,146,168, +232,175,162,230,151,182,229,176,154,230,179,168,230,132,143,231,148,179,232,175, +183,229,173,166,230,160,161,229,186,148,232,175,165,229,142,134,229,143,178,229, +143,170,230,152,175,232,191,148,229,155,158,232,180,173,228,185,176,229,144,141, +231,167,176,228,184,186,228,186,134,230,136,144,229,138,159,232,175,180,230,152, +142,228,190,155,229,186,148,229,173,169,229,173,144,228,184,147,233,162,152,231, +168,139,229,186,143,228,184,128,232,136,172,230,156,131,229,147,161,229,143,170, +230,156,137,229,133,182,229,174,131,228,191,157,230,138,164,232,128,140,228,184, +148,228,187,138,229,164,169,231,170,151,229,143,163,229,138,168,230,128,129,231, +138,182,230,128,129,231,137,185,229,136,171,232,174,164,228,184,186,229,191,133, +233,161,187,230,155,180,230,150,176,229,176,143,232,175,180,230,136,145,229,128, +145,228,189,156,228,184,186,229,170,146,228,189,147,229,140,133,230,139,172,233, +130,163,228,185,136,228,184,128,230,160,183,229,155,189,229,134,133,230,152,175, +229,144,166,230,160,185,230,141,174,231,148,181,232,167,134,229,173,166,233,153, +162,229,133,183,230,156,137,232,191,135,231,168,139,231,148,177,228,186,142,228, +186,186,230,137,141,229,135,186,230,157,165,228,184,141,232,191,135,230,173,163, +229,156,168,230,152,142,230,152,159,230,149,133,228,186,139,229,133,179,231,179, +187,230,160,135,233,162,152,229,149,134,229,138,161,232,190,147,229,133,165,228, +184,128,231,155,180,229,159,186,231,161,128,230,149,153,229,173,166,228,186,134, +232,167,163,229,187,186,231,173,145,231,187,147,230,158,156,229,133,168,231,144, +131,233,128,154,231,159,165,232,174,161,229,136,146,229,175,185,228,186,142,232, +137,186,230,156,175,231,155,184,229,134,140,229,143,145,231,148,159,231,156,159, +231,154,132,229,187,186,231,171,139,231,173,137,231,186,167,231,177,187,229,158, +139,231,187,143,233,170,140,229,174,158,231,142,176,229,136,182,228,189,156,230, +157,165,232,135,170,230,160,135,231,173,190,228,187,165,228,184,139,229,142,159, +229,136,155,230,151,160,230,179,149,229,133,182,228,184,173,229,128,139,228,186, +186,228,184,128,229,136,135,230,140,135,229,141,151,229,133,179,233,151,173,233, +155,134,229,155,162,231,172,172,228,184,137,229,133,179,230,179,168,229,155,160, +230,173,164,231,133,167,231,137,135,230,183,177,229,156,179,229,149,134,228,184, +154,229,185,191,229,183,158,230,151,165,230,156,159,233,171,152,231,186,167,230, +156,128,232,191,145,231,187,188,229,144,136,232,161,168,231,164,186,228,184,147, +232,190,145,232,161,140,228,184,186,228,186,164,233,128,154,232,175,132,228,187, +183,232,167,137,229,190,151,231,178,190,229,141,142,229,174,182,229,186,173,229, +174,140,230,136,144,230,132,159,232,167,137,229,174,137,232,163,133,229,190,151, +229,136,176,233,130,174,228,187,182,229,136,182,229,186,166,233,163,159,229,147, +129,232,153,189,231,132,182,232,189,172,232,189,189,230,138,165,228,187,183,232, +174,176,232,128,133,230,150,185,230,161,136,232,161,140,230,148,191,228,186,186, +230,176,145,231,148,168,229,147,129,228,184,156,232,165,191,230,143,144,229,135, +186,233,133,146,229,186,151,231,132,182,229,144,142,228,187,152,230,172,190,231, +131,173,231,130,185,228,187,165,229,137,141,229,174,140,229,133,168,229,143,145, +229,184,150,232,174,190,231,189,174,233,162,134,229,175,188,229,183,165,228,184, +154,229,140,187,233,153,162,231,156,139,231,156,139,231,187,143,229,133,184,229, +142,159,229,155,160,229,185,179,229,143,176,229,144,132,231,167,141,229,162,158, +229,138,160,230,157,144,230,150,153,230,150,176,229,162,158,228,185,139,229,144, +142,232,129,140,228,184,154,230,149,136,230,158,156,228,187,138,229,185,180,232, +174,186,230,150,135,230,136,145,229,155,189,229,145,138,232,175,137,231,137,136, +228,184,187,228,191,174,230,148,185,229,143,130,228,184,142,230,137,147,229,141, +176,229,191,171,228,185,144,230,156,186,230,162,176,232,167,130,231,130,185,229, +173,152,229,156,168,231,178,190,231,165,158,232,142,183,229,190,151,229,136,169, +231,148,168,231,187,167,231,187,173,228,189,160,228,187,172,232,191,153,228,185, +136,230,168,161,229,188,143,232,175,173,232,168,128,232,131,189,229,164,159,233, +155,133,232,153,142,230,147,141,228,189,156,233,163,142,230,160,188,228,184,128, +232,181,183,231,167,145,229,173,166,228,189,147,232,130,178,231,159,173,228,191, +161,230,157,161,228,187,182,230,178,187,231,150,151,232,191,144,229,138,168,228, +186,167,228,184,154,228,188,154,232,174,174,229,175,188,232,136,170,229,133,136, +231,148,159,232,129,148,231,155,159,229,143,175,230,152,175,229,149,143,233,161, +140,231,187,147,230,158,132,228,189,156,231,148,168,232,176,131,230,159,165,232, +179,135,230,150,153,232,135,170,229,138,168,232,180,159,232,180,163,229,134,156, +228,184,154,232,174,191,233,151,174,229,174,158,230,150,189,230,142,165,229,143, +151,232,174,168,232,174,186,233,130,163,228,184,170,229,143,141,233,166,136,229, +138,160,229,188,186,229,165,179,230,128,167,232,140,131,229,155,180,230,156,141, +229,139,153,228,188,145,233,151,178,228,187,138,230,151,165,229,174,162,230,156, +141,232,167,128,231,156,139,229,143,130,229,138,160,231,154,132,232,175,157,228, +184,128,231,130,185,228,191,157,232,175,129,229,155,190,228,185,166,230,156,137, +230,149,136,230,181,139,232,175,149,231,167,187,229,138,168,230,137,141,232,131, +189,229,134,179,229,174,154,232,130,161,231,165,168,228,184,141,230,150,173,233, +156,128,230,177,130,228,184,141,229,190,151,229,138,158,230,179,149,228,185,139, +233,151,180,233,135,135,231,148,168,232,144,165,233,148,128,230,138,149,232,175, +137,231,155,174,230,160,135,231,136,177,230,131,133,230,145,132,229,189,177,230, +156,137,228,186,155,232,164,135,232,163,189,230,150,135,229,173,166,230,156,186, +228,188,154,230,149,176,229,173,151,232,163,133,228,191,174,232,180,173,231,137, +169,229,134,156,230,157,145,229,133,168,233,157,162,231,178,190,229,147,129,229, +133,182,229,174,158,228,186,139,230,131,133,230,176,180,229,185,179,230,143,144, +231,164,186,228,184,138,229,184,130,232,176,162,232,176,162,230,153,174,233,128, +154,230,149,153,229,184,136,228,184,138,228,188,160,231,177,187,229,136,171,230, +173,140,230,155,178,230,139,165,230,156,137,229,136,155,230,150,176,233,133,141, +228,187,182,229,143,170,232,166,129,230,151,182,228,187,163,232,179,135,232,168, +138,232,190,190,229,136,176,228,186,186,231,148,159,232,174,162,233,152,133,232, +128,129,229,184,136,229,177,149,231,164,186,229,191,131,231,144,134,232,180,180, +229,173,144,231,182,178,231,171,153,228,184,187,233,161,140,232,135,170,231,132, +182,231,186,167,229,136,171,231,174,128,229,141,149,230,148,185,233,157,169,233, +130,163,228,186,155,230,157,165,232,175,180,230,137,147,229,188,128,228,187,163, +231,160,129,229,136,160,233,153,164,232,175,129,229,136,184,232,138,130,231,155, +174,233,135,141,231,130,185,230,172,161,230,149,184,229,164,154,229,176,145,232, +167,132,229,136,146,232,181,132,233,135,145,230,137,190,229,136,176,228,187,165, +229,144,142,229,164,167,229,133,168,228,184,187,233,161,181,230,156,128,228,189, +179,229,155,158,231,173,148,229,164,169,228,184,139,228,191,157,233,154,156,231, +142,176,228,187,163,230,163,128,230,159,165,230,138,149,231,165,168,229,176,143, +230,151,182,230,178,146,230,156,137,230,173,163,229,184,184,231,148,154,232,135, +179,228,187,163,231,144,134,231,155,174,229,189,149,229,133,172,229,188,128,229, +164,141,229,136,182,233,135,145,232,158,141,229,185,184,231,166,143,231,137,136, +230,156,172,229,189,162,230,136,144,229,135,134,229,164,135,232,161,140,230,131, +133,229,155,158,229,136,176,230,128,157,230,131,179,230,128,142,230,160,183,229, +141,143,232,174,174,232,174,164,232,175,129,230,156,128,229,165,189,228,186,167, +231,148,159,230,140,137,231,133,167,230,156,141,232,163,133,229,185,191,228,184, +156,229,138,168,230,188,171,233,135,135,232,180,173,230,150,176,230,137,139,231, +187,132,229,155,190,233,157,162,230,157,191,229,143,130,232,128,131,230,148,191, +230,178,187,229,174,185,230,152,147,229,164,169,229,156,176,229,138,170,229,138, +155,228,186,186,228,187,172,229,141,135,231,186,167,233,128,159,229,186,166,228, +186,186,231,137,169,232,176,131,230,149,180,230,181,129,232,161,140,233,128,160, +230,136,144,230,150,135,229,173,151,233,159,169,229,155,189,232,180,184,230,152, +147,229,188,128,229,177,149,231,155,184,233,151,156,232,161,168,231,142,176,229, +189,177,232,167,134,229,166,130,230,173,164,231,190,142,229,174,185,229,164,167, +229,176,143,230,138,165,233,129,147,230,157,161,230,172,190,229,191,131,230,131, +133,232,174,184,229,164,154,230,179,149,232,167,132,229,174,182,229,177,133,228, +185,166,229,186,151,232,191,158,230,142,165,231,171,139,229,141,179,228,184,190, +230,138,165,230,138,128,229,183,167,229,165,165,232,191,144,231,153,187,229,133, +165,228,187,165,230,157,165,231,144,134,232,174,186,228,186,139,228,187,182,232, +135,170,231,148,177,228,184,173,229,141,142,229,138,158,229,133,172,229,166,136, +229,166,136,231,156,159,230,173,163,228,184,141,233,148,153,229,133,168,230,150, +135,229,144,136,229,144,140,228,187,183,229,128,188,229,136,171,228,186,186,231, +155,145,231,157,163,229,133,183,228,189,147,228,184,150,231,186,170,229,155,162, +233,152,159,229,136,155,228,184,154,230,137,191,230,139,133,229,162,158,233,149, +191,230,156,137,228,186,186,228,191,157,230,140,129,229,149,134,229,174,182,231, +187,180,228,191,174,229,143,176,230,185,190,229,183,166,229,143,179,232,130,161, +228,187,189,231,173,148,230,161,136,229,174,158,233,153,133,231,148,181,228,191, +161,231,187,143,231,144,134,231,148,159,229,145,189,229,174,163,228,188,160,228, +187,187,229,138,161,230,173,163,229,188,143,231,137,185,232,137,178,228,184,139, +230,157,165,229,141,143,228,188,154,229,143,170,232,131,189,229,189,147,231,132, +182,233,135,141,230,150,176,229,133,167,229,174,185,230,140,135,229,175,188,232, +191,144,232,161,140,230,151,165,229,191,151,232,179,163,229,174,182,232,182,133, +232,191,135,229,156,159,229,156,176,230,181,153,230,177,159,230,148,175,228,187, +152,230,142,168,229,135,186,231,171,153,233,149,191,230,157,173,229,183,158,230, +137,167,232,161,140,229,136,182,233,128,160,228,185,139,228,184,128,230,142,168, +229,185,191,231,142,176,229,156,186,230,143,143,232,191,176,229,143,152,229,140, +150,228,188,160,231,187,159,230,173,140,230,137,139,228,191,157,233,153,169,232, +175,190,231,168,139,229,140,187,231,150,151,231,187,143,232,191,135,232,191,135, +229,142,187,228,185,139,229,137,141,230,148,182,229,133,165,229,185,180,229,186, +166,230,157,130,229,191,151,231,190,142,228,184,189,230,156,128,233,171,152,231, +153,187,233,153,134,230,156,170,230,157,165,229,138,160,229,183,165,229,133,141, +232,180,163,230,149,153,231,168,139,231,137,136,229,157,151,232,186,171,228,189, +147,233,135,141,229,186,134,229,135,186,229,148,174,230,136,144,230,156,172,229, +189,162,229,188,143,229,156,159,232,177,134,229,135,186,229,131,185,228,184,156, +230,150,185,233,130,174,231,174,177,229,141,151,228,186,172,230,177,130,232,129, +140,229,143,150,229,190,151,232,129,140,228,189,141,231,155,184,228,191,161,233, +161,181,233,157,162,229,136,134,233,146,159,231,189,145,233,161,181,231,161,174, +229,174,154,229,155,190,228,190,139,231,189,145,229,157,128,231,167,175,230,158, +129,233,148,153,232,175,175,231,155,174,231,154,132,229,174,157,232,180,157,230, +156,186,229,133,179,233,163,142,233,153,169,230,142,136,230,157,131,231,151,133, +230,175,146,229,174,160,231,137,169,233,153,164,228,186,134,232,169,149,232,171, +150,231,150,190,231,151,133,229,143,138,230,151,182,230,177,130,232,180,173,231, +171,153,231,130,185,229,132,191,231,171,165,230,175,143,229,164,169,228,184,173, +229,164,174,232,174,164,232,175,134,230,175,143,228,184,170,229,164,169,230,180, +165,229,173,151,228,189,147,229,143,176,231,129,163,231,187,180,230,138,164,230, +156,172,233,161,181,228,184,170,230,128,167,229,174,152,230,150,185,229,184,184, +232,167,129,231,155,184,230,156,186,230,136,152,231,149,165,229,186,148,229,189, +147,229,190,139,229,184,136,230,150,185,228,190,191,230,160,161,229,155,173,232, +130,161,229,184,130,230,136,191,229,177,139,230,160,143,231,155,174,229,145,152, +229,183,165,229,175,188,232,135,180,231,170,129,231,132,182,233,129,147,229,133, +183,230,156,172,231,189,145,231,187,147,229,144,136,230,161,163,230,161,136,229, +138,179,229,138,168,229,143,166,229,164,150,231,190,142,229,133,131,229,188,149, +232,181,183,230,148,185,229,143,152,231,172,172,229,155,155,228,188,154,232,174, +161,232,170,170,230,152,142,233,154,144,231,167,129,229,174,157,229,174,157,232, +167,132,232,140,131,230,182,136,232,180,185,229,133,177,229,144,140,229,191,152, +232,174,176,228,189,147,231,179,187,229,184,166,230,157,165,229,144,141,229,173, +151,231,153,188,232,161,168,229,188,128,230,148,190,229,138,160,231,155,159,229, +143,151,229,136,176,228,186,140,230,137,139,229,164,167,233,135,143,230,136,144, +228,186,186,230,149,176,233,135,143,229,133,177,228,186,171,229,140,186,229,159, +159,229,165,179,229,173,169,229,142,159,229,136,153,230,137,128,229,156,168,231, +187,147,230,157,159,233,128,154,228,191,161,232,182,133,231,186,167,233,133,141, +231,189,174,229,189,147,230,151,182,228,188,152,231,167,128,230,128,167,230,132, +159,230,136,191,228,186,167,233,129,138,230,136,178,229,135,186,229,143,163,230, +143,144,228,186,164,229,176,177,228,184,154,228,191,157,229,129,165,231,168,139, +229,186,166,229,143,130,230,149,176,228,186,139,228,184,154,230,149,180,228,184, +170,229,177,177,228,184,156,230,131,133,230,132,159,231,137,185,230,174,138,229, +136,134,233,161,158,230,144,156,229,176,139,229,177,158,228,186,142,233,151,168, +230,136,183,232,180,162,229,138,161,229,163,176,233,159,179,229,143,138,229,133, +182,232,180,162,231,187,143,229,157,154,230,140,129,229,185,178,233,131,168,230, +136,144,231,171,139,229,136,169,231,155,138,232,128,131,232,153,145,230,136,144, +233,131,189,229,140,133,232,163,133,231,148,168,230,136,182,230,175,148,232,181, +155,230,150,135,230,152,142,230,139,155,229,149,134,229,174,140,230,149,180,231, +156,159,230,152,175,231,156,188,231,157,155,228,188,153,228,188,180,229,168,129, +230,156,155,233,162,134,229,159,159,229,141,171,231,148,159,228,188,152,230,131, +160,232,171,150,229,163,135,229,133,172,229,133,177,232,137,175,229,165,189,229, +133,133,229,136,134,231,172,166,229,144,136,233,153,132,228,187,182,231,137,185, +231,130,185,228,184,141,229,143,175,232,139,177,230,150,135,232,181,132,228,186, +167,230,160,185,230,156,172,230,152,142,230,152,190,229,175,134,231,162,188,229, +133,172,228,188,151,230,176,145,230,151,143,230,155,180,229,138,160,228,186,171, +229,143,151,229,144,140,229,173,166,229,144,175,229,138,168,233,128,130,229,144, +136,229,142,159,230,157,165,233,151,174,231,173,148,230,156,172,230,150,135,231, +190,142,233,163,159,231,187,191,232,137,178,231,168,179,229,174,154,231,187,136, +228,186,142,231,148,159,231,137,169,228,190,155,230,177,130,230,144,156,231,139, +144,229,138,155,233,135,143,228,184,165,233,135,141,230,176,184,232,191,156,229, +134,153,231,156,159,230,156,137,233,153,144,231,171,158,228,186,137,229,175,185, +232,177,161,232,180,185,231,148,168,228,184,141,229,165,189,231,187,157,229,175, +185,229,141,129,229,136,134,228,191,131,232,191,155,231,130,185,232,175,132,229, +189,177,233,159,179,228,188,152,229,138,191,228,184,141,229,176,145,230,172,163, +232,181,143,229,185,182,228,184,148,230,156,137,231,130,185,230,150,185,229,144, +145,229,133,168,230,150,176,228,191,161,231,148,168,232,174,190,230,150,189,229, +189,162,232,177,161,232,181,132,230,160,188,231,170,129,231,160,180,233,154,143, +231,157,128,233,135,141,229,164,167,228,186,142,230,152,175,230,175,149,228,184, +154,230,153,186,232,131,189,229,140,150,229,183,165,229,174,140,231,190,142,229, +149,134,229,159,142,231,187,159,228,184,128,229,135,186,231,137,136,230,137,147, +233,128,160,231,148,162,229,147,129,230,166,130,229,134,181,231,148,168,228,186, +142,228,191,157,231,149,153,229,155,160,231,180,160,228,184,173,229,156,139,229, +173,152,229,130,168,232,180,180,229,155,190,230,156,128,230,132,155,233,149,191, +230,156,159,229,143,163,228,187,183,231,144,134,232,180,162,229,159,186,229,156, +176,229,174,137,230,142,146,230,173,166,230,177,137,233,135,140,233,157,162,229, +136,155,229,187,186,229,164,169,231,169,186,233,166,150,229,133,136,229,174,140, +229,150,132,233,169,177,229,138,168,228,184,139,233,157,162,228,184,141,229,134, +141,232,175,154,228,191,161,230,132,143,228,185,137,233,152,179,229,133,137,232, +139,177,229,155,189,230,188,130,228,186,174,229,134,155,228,186,139,231,142,169, +229,174,182,231,190,164,228,188,151,229,134,156,230,176,145,229,141,179,229,143, +175,229,144,141,231,168,177,229,174,182,229,133,183,229,138,168,231,148,187,230, +131,179,229,136,176,230,179,168,230,152,142,229,176,143,229,173,166,230,128,167, +232,131,189,232,128,131,231,160,148,231,161,172,228,187,182,232,167,130,231,156, +139,230,184,133,230,165,154,230,144,158,231,172,145,233,166,150,233,160,129,233, +187,132,233,135,145,233,128,130,231,148,168,230,177,159,232,139,143,231,156,159, +229,174,158,228,184,187,231,174,161,233,152,182,230,174,181,232,168,187,229,134, +138,231,191,187,232,175,145,230,157,131,229,136,169,229,129,154,229,165,189,228, +188,188,228,185,142,233,128,154,232,174,175,230,150,189,229,183,165,231,139,128, +230,133,139,228,185,159,232,174,184,231,142,175,228,191,157,229,159,185,229,133, +187,230,166,130,229,191,181,229,164,167,229,158,139,230,156,186,231,165,168,231, +144,134,232,167,163,229,140,191,229,144,141,99,117,97,110,100,111,101,110,118, +105,97,114,109,97,100,114,105,100,98,117,115,99,97,114,105,110,105,99,105,111, +116,105,101,109,112,111,112,111,114,113,117,101,99,117,101,110,116,97,101,115, +116,97,100,111,112,117,101,100,101,110,106,117,101,103,111,115,99,111,110,116, +114,97,101,115,116,195,161,110,110,111,109,98,114,101,116,105,101,110,101,110, +112,101,114,102,105,108,109,97,110,101,114,97,97,109,105,103,111,115,99,105,117, +100,97,100,99,101,110,116,114,111,97,117,110,113,117,101,112,117,101,100,101,115 +,100,101,110,116,114,111,112,114,105,109,101,114,112,114,101,99,105,111,115,101, +103,195,186,110,98,117,101,110,111,115,118,111,108,118,101,114,112,117,110,116, +111,115,115,101,109,97,110,97,104,97,98,195,173,97,97,103,111,115,116,111,110, +117,101,118,111,115,117,110,105,100,111,115,99,97,114,108,111,115,101,113,117, +105,112,111,110,105,195,177,111,115,109,117,99,104,111,115,97,108,103,117,110,97 +,99,111,114,114,101,111,105,109,97,103,101,110,112,97,114,116,105,114,97,114,114 +,105,98,97,109,97,114,195,173,97,104,111,109,98,114,101,101,109,112,108,101,111, +118,101,114,100,97,100,99,97,109,98,105,111,109,117,99,104,97,115,102,117,101, +114,111,110,112,97,115,97,100,111,108,195,173,110,101,97,112,97,114,101,99,101, +110,117,101,118,97,115,99,117,114,115,111,115,101,115,116,97,98,97,113,117,105, +101,114,111,108,105,98,114,111,115,99,117,97,110,116,111,97,99,99,101,115,111, +109,105,103,117,101,108,118,97,114,105,111,115,99,117,97,116,114,111,116,105,101 +,110,101,115,103,114,117,112,111,115,115,101,114,195,161,110,101,117,114,111,112 +,97,109,101,100,105,111,115,102,114,101,110,116,101,97,99,101,114,99,97,100,101, +109,195,161,115,111,102,101,114,116,97,99,111,99,104,101,115,109,111,100,101,108 +,111,105,116,97,108,105,97,108,101,116,114,97,115,97,108,103,195,186,110,99,111, +109,112,114,97,99,117,97,108,101,115,101,120,105,115,116,101,99,117,101,114,112, +111,115,105,101,110,100,111,112,114,101,110,115,97,108,108,101,103,97,114,118, +105,97,106,101,115,100,105,110,101,114,111,109,117,114,99,105,97,112,111,100,114 +,195,161,112,117,101,115,116,111,100,105,97,114,105,111,112,117,101,98,108,111, +113,117,105,101,114,101,109,97,110,117,101,108,112,114,111,112,105,111,99,114, +105,115,105,115,99,105,101,114,116,111,115,101,103,117,114,111,109,117,101,114, +116,101,102,117,101,110,116,101,99,101,114,114,97,114,103,114,97,110,100,101,101 +,102,101,99,116,111,112,97,114,116,101,115,109,101,100,105,100,97,112,114,111, +112,105,97,111,102,114,101,99,101,116,105,101,114,114,97,101,45,109,97,105,108, +118,97,114,105,97,115,102,111,114,109,97,115,102,117,116,117,114,111,111,98,106, +101,116,111,115,101,103,117,105,114,114,105,101,115,103,111,110,111,114,109,97, +115,109,105,115,109,111,115,195,186,110,105,99,111,99,97,109,105,110,111,115,105 +,116,105,111,115,114,97,122,195,179,110,100,101,98,105,100,111,112,114,117,101, +98,97,116,111,108,101,100,111,116,101,110,195,173,97,106,101,115,195,186,115,101 +,115,112,101,114,111,99,111,99,105,110,97,111,114,105,103,101,110,116,105,101, +110,100,97,99,105,101,110,116,111,99,195,161,100,105,122,104,97,98,108,97,114, +115,101,114,195,173,97,108,97,116,105,110,97,102,117,101,114,122,97,101,115,116, +105,108,111,103,117,101,114,114,97,101,110,116,114,97,114,195,169,120,105,116, +111,108,195,179,112,101,122,97,103,101,110,100,97,118,195,173,100,101,111,101, +118,105,116,97,114,112,97,103,105,110,97,109,101,116,114,111,115,106,97,118,105, +101,114,112,97,100,114,101,115,102,195,161,99,105,108,99,97,98,101,122,97,195, +161,114,101,97,115,115,97,108,105,100,97,101,110,118,195,173,111,106,97,112,195, +179,110,97,98,117,115,111,115,98,105,101,110,101,115,116,101,120,116,111,115,108 +,108,101,118,97,114,112,117,101,100,97,110,102,117,101,114,116,101,99,111,109, +195,186,110,99,108,97,115,101,115,104,117,109,97,110,111,116,101,110,105,100,111 +,98,105,108,98,97,111,117,110,105,100,97,100,101,115,116,195,161,115,101,100,105 +,116,97,114,99,114,101,97,100,111,208,180,208,187,209,143,209,135,209,130,208, +190,208,186,208,176,208,186,208,184,208,187,208,184,209,141,209,130,208,190,208, +178,209,129,208,181,208,181,208,179,208,190,208,191,209,128,208,184,209,130,208, +176,208,186,208,181,209,137,208,181,209,131,208,182,208,181,208,154,208,176,208, +186,208,177,208,181,208,183,208,177,209,139,208,187,208,190,208,189,208,184,208, +146,209,129,208,181,208,191,208,190,208,180,208,173,209,130,208,190,209,130,208, +190,208,188,209,135,208,181,208,188,208,189,208,181,209,130,208,187,208,181,209, +130,209,128,208,176,208,183,208,190,208,189,208,176,208,179,208,180,208,181,208, +188,208,189,208,181,208,148,208,187,209,143,208,159,209,128,208,184,208,189,208, +176,209,129,208,189,208,184,209,133,209,130,208,181,208,188,208,186,209,130,208, +190,208,179,208,190,208,180,208,178,208,190,209,130,209,130,208,176,208,188,208, +161,208,168,208,144,208,188,208,176,209,143,208,167,209,130,208,190,208,178,208, +176,209,129,208,178,208,176,208,188,208,181,208,188,209,131,208,162,208,176,208, +186,208,180,208,178,208,176,208,189,208,176,208,188,209,141,209,130,208,184,209, +141,209,130,209,131,208,146,208,176,208,188,209,130,208,181,209,133,208,191,209, +128,208,190,209,130,209,131,209,130,208,189,208,176,208,180,208,180,208,189,209, +143,208,146,208,190,209,130,209,130,209,128,208,184,208,189,208,181,208,185,208, +146,208,176,209,129,208,189,208,184,208,188,209,129,208,176,208,188,209,130,208, +190,209,130,209,128,209,131,208,177,208,158,208,189,208,184,208,188,208,184,209, +128,208,189,208,181,208,181,208,158,208,158,208,158,208,187,208,184,209,134,209, +141,209,130,208,176,208,158,208,189,208,176,208,189,208,181,208,188,208,180,208, +190,208,188,208,188,208,190,208,185,208,180,208,178,208,181,208,190,208,189,208, +190,209,129,209,131,208,180,224,164,149,224,165,135,224,164,185,224,165,136,224, +164,149,224,165,128,224,164,184,224,165,135,224,164,149,224,164,190,224,164,149, +224,165,139,224,164,148,224,164,176,224,164,170,224,164,176,224,164,168,224,165, +135,224,164,143,224,164,149,224,164,149,224,164,191,224,164,173,224,165,128,224, +164,135,224,164,184,224,164,149,224,164,176,224,164,164,224,165,139,224,164,185, +224,165,139,224,164,134,224,164,170,224,164,185,224,165,128,224,164,175,224,164, +185,224,164,175,224,164,190,224,164,164,224,164,149,224,164,165,224,164,190,106, +97,103,114,97,110,224,164,134,224,164,156,224,164,156,224,165,139,224,164,133, +224,164,172,224,164,166,224,165,139,224,164,151,224,164,136,224,164,156,224,164, +190,224,164,151,224,164,143,224,164,185,224,164,174,224,164,135,224,164,168,224, +164,181,224,164,185,224,164,175,224,165,135,224,164,165,224,165,135,224,164,165, +224,165,128,224,164,152,224,164,176,224,164,156,224,164,172,224,164,166,224,165, +128,224,164,149,224,164,136,224,164,156,224,165,128,224,164,181,224,165,135,224, +164,168,224,164,136,224,164,168,224,164,143,224,164,185,224,164,176,224,164,137, +224,164,184,224,164,174,224,165,135,224,164,149,224,164,174,224,164,181,224,165, +139,224,164,178,224,165,135,224,164,184,224,164,172,224,164,174,224,164,136,224, +164,166,224,165,135,224,164,147,224,164,176,224,164,134,224,164,174,224,164,172, +224,164,184,224,164,173,224,164,176,224,164,172,224,164,168,224,164,154,224,164, +178,224,164,174,224,164,168,224,164,134,224,164,151,224,164,184,224,165,128,224, +164,178,224,165,128,216,185,217,132,217,137,216,165,217,132,217,137,217,135,216, +176,216,167,216,162,216,174,216,177,216,185,216,175,216,175,216,167,217,132,217, +137,217,135,216,176,217,135,216,181,217,136,216,177,216,186,217,138,216,177,217, +131,216,167,217,134,217,136,217,132,216,167,216,168,217,138,217,134,216,185,216, +177,216,182,216,176,217,132,217,131,217,135,217,134,216,167,217,138,217,136,217, +133,217,130,216,167,217,132,216,185,217,132,217,138,216,167,217,134,216,167,217, +132,217,131,217,134,216,173,216,170,217,137,217,130,216,168,217,132,217,136,216, +173,216,169,216,167,216,174,216,177,217,129,217,130,216,183,216,185,216,168,216, +175,216,177,217,131,217,134,216,165,216,176,216,167,217,131,217,133,216,167,216, +167,216,173,216,175,216,165,217,132,216,167,217,129,217,138,217,135,216,168,216, +185,216,182,217,131,217,138,217,129,216,168,216,173,216,171,217,136,217,133,217, +134,217,136,217,135,217,136,216,163,217,134,216,167,216,172,216,175,216,167,217, +132,217,135,216,167,216,179,217,132,217,133,216,185,217,134,216,175,217,132,217, +138,216,179,216,185,216,168,216,177,216,181,217,132,217,137,217,133,217,134,216, +176,216,168,217,135,216,167,216,163,217,134,217,135,217,133,216,171,217,132,217, +131,217,134,216,170,216,167,217,132,216,167,216,173,217,138,216,171,217,133,216, +181,216,177,216,180,216,177,216,173,216,173,217,136,217,132,217,136,217,129,217, +138,216,167,216,176,216,167,217,132,217,131,217,132,217,133,216,177,216,169,216, +167,217,134,216,170,216,167,217,132,217,129,216,163,216,168,217,136,216,174,216, +167,216,181,216,163,217,134,216,170,216,167,217,134,217,135,216,167,217,132,217, +138,216,185,216,182,217,136,217,136,217,130,216,175,216,167,216,168,217,134,216, +174,217,138,216,177,216,168,217,134,216,170,217,132,217,131,217,133,216,180,216, +167,216,161,217,136,217,135,217,138,216,167,216,168,217,136,217,130,216,181,216, +181,217,136,217,133,216,167,216,177,217,130,217,133,216,163,216,173,216,175,217, +134,216,173,217,134,216,185,216,175,217,133,216,177,216,163,217,138,216,167,216, +173,216,169,217,131,216,170,216,168,216,175,217,136,217,134,217,138,216,172,216, +168,217,133,217,134,217,135,216,170,216,173,216,170,216,172,217,135,216,169,216, +179,217,134,216,169,217,138,216,170,217,133,217,131,216,177,216,169,216,186,216, +178,216,169,217,134,217,129,216,179,216,168,217,138,216,170,217,132,217,132,217, +135,217,132,217,134,216,167,216,170,217,132,217,131,217,130,217,132,216,168,217, +132,217,133,216,167,216,185,217,134,217,135,216,163,217,136,217,132,216,180,217, +138,216,161,217,134,217,136,216,177,216,163,217,133,216,167,217,129,217,138,217, +131,216,168,217,131,217,132,216,176,216,167,216,170,216,177,216,170,216,168,216, +168,216,163,217,134,217,135,217,133,216,179,216,167,217,134,217,131,216,168,217, +138,216,185,217,129,217,130,216,175,216,173,216,179,217,134,217,132,217,135,217, +133,216,180,216,185,216,177,216,163,217,135,217,132,216,180,217,135,216,177,217, +130,216,183,216,177,216,183,217,132,216,168,112,114,111,102,105,108,101,115,101, +114,118,105,99,101,100,101,102,97,117,108,116,104,105,109,115,101,108,102,100, +101,116,97,105,108,115,99,111,110,116,101,110,116,115,117,112,112,111,114,116, +115,116,97,114,116,101,100,109,101,115,115,97,103,101,115,117,99,99,101,115,115, +102,97,115,104,105,111,110,60,116,105,116,108,101,62,99,111,117,110,116,114,121, +97,99,99,111,117,110,116,99,114,101,97,116,101,100,115,116,111,114,105,101,115, +114,101,115,117,108,116,115,114,117,110,110,105,110,103,112,114,111,99,101,115, +115,119,114,105,116,105,110,103,111,98,106,101,99,116,115,118,105,115,105,98,108 +,101,119,101,108,99,111,109,101,97,114,116,105,99,108,101,117,110,107,110,111, +119,110,110,101,116,119,111,114,107,99,111,109,112,97,110,121,100,121,110,97,109 +,105,99,98,114,111,119,115,101,114,112,114,105,118,97,99,121,112,114,111,98,108, +101,109,83,101,114,118,105,99,101,114,101,115,112,101,99,116,100,105,115,112,108 +,97,121,114,101,113,117,101,115,116,114,101,115,101,114,118,101,119,101,98,115, +105,116,101,104,105,115,116,111,114,121,102,114,105,101,110,100,115,111,112,116, +105,111,110,115,119,111,114,107,105,110,103,118,101,114,115,105,111,110,109,105, +108,108,105,111,110,99,104,97,110,110,101,108,119,105,110,100,111,119,46,97,100, +100,114,101,115,115,118,105,115,105,116,101,100,119,101,97,116,104,101,114,99, +111,114,114,101,99,116,112,114,111,100,117,99,116,101,100,105,114,101,99,116,102 +,111,114,119,97,114,100,121,111,117,32,99,97,110,114,101,109,111,118,101,100,115 +,117,98,106,101,99,116,99,111,110,116,114,111,108,97,114,99,104,105,118,101,99, +117,114,114,101,110,116,114,101,97,100,105,110,103,108,105,98,114,97,114,121,108 +,105,109,105,116,101,100,109,97,110,97,103,101,114,102,117,114,116,104,101,114, +115,117,109,109,97,114,121,109,97,99,104,105,110,101,109,105,110,117,116,101,115 +,112,114,105,118,97,116,101,99,111,110,116,101,120,116,112,114,111,103,114,97, +109,115,111,99,105,101,116,121,110,117,109,98,101,114,115,119,114,105,116,116, +101,110,101,110,97,98,108,101,100,116,114,105,103,103,101,114,115,111,117,114,99 +,101,115,108,111,97,100,105,110,103,101,108,101,109,101,110,116,112,97,114,116, +110,101,114,102,105,110,97,108,108,121,112,101,114,102,101,99,116,109,101,97,110 +,105,110,103,115,121,115,116,101,109,115,107,101,101,112,105,110,103,99,117,108, +116,117,114,101,38,113,117,111,116,59,44,106,111,117,114,110,97,108,112,114,111, +106,101,99,116,115,117,114,102,97,99,101,115,38,113,117,111,116,59,101,120,112, +105,114,101,115,114,101,118,105,101,119,115,98,97,108,97,110,99,101,69,110,103, +108,105,115,104,67,111,110,116,101,110,116,116,104,114,111,117,103,104,80,108, +101,97,115,101,32,111,112,105,110,105,111,110,99,111,110,116,97,99,116,97,118, +101,114,97,103,101,112,114,105,109,97,114,121,118,105,108,108,97,103,101,83,112, +97,110,105,115,104,103,97,108,108,101,114,121,100,101,99,108,105,110,101,109,101 +,101,116,105,110,103,109,105,115,115,105,111,110,112,111,112,117,108,97,114,113, +117,97,108,105,116,121,109,101,97,115,117,114,101,103,101,110,101,114,97,108,115 +,112,101,99,105,101,115,115,101,115,115,105,111,110,115,101,99,116,105,111,110, +119,114,105,116,101,114,115,99,111,117,110,116,101,114,105,110,105,116,105,97, +108,114,101,112,111,114,116,115,102,105,103,117,114,101,115,109,101,109,98,101, +114,115,104,111,108,100,105,110,103,100,105,115,112,117,116,101,101,97,114,108, +105,101,114,101,120,112,114,101,115,115,100,105,103,105,116,97,108,112,105,99, +116,117,114,101,65,110,111,116,104,101,114,109,97,114,114,105,101,100,116,114,97 +,102,102,105,99,108,101,97,100,105,110,103,99,104,97,110,103,101,100,99,101,110, +116,114,97,108,118,105,99,116,111,114,121,105,109,97,103,101,115,47,114,101,97, +115,111,110,115,115,116,117,100,105,101,115,102,101,97,116,117,114,101,108,105, +115,116,105,110,103,109,117,115,116,32,98,101,115,99,104,111,111,108,115,86,101, +114,115,105,111,110,117,115,117,97,108,108,121,101,112,105,115,111,100,101,112, +108,97,121,105,110,103,103,114,111,119,105,110,103,111,98,118,105,111,117,115, +111,118,101,114,108,97,121,112,114,101,115,101,110,116,97,99,116,105,111,110,115 +,60,47,117,108,62,13,10,119,114,97,112,112,101,114,97,108,114,101,97,100,121,99, +101,114,116,97,105,110,114,101,97,108,105,116,121,115,116,111,114,97,103,101,97, +110,111,116,104,101,114,100,101,115,107,116,111,112,111,102,102,101,114,101,100, +112,97,116,116,101,114,110,117,110,117,115,117,97,108,68,105,103,105,116,97,108, +99,97,112,105,116,97,108,87,101,98,115,105,116,101,102,97,105,108,117,114,101,99 +,111,110,110,101,99,116,114,101,100,117,99,101,100,65,110,100,114,111,105,100, +100,101,99,97,100,101,115,114,101,103,117,108,97,114,32,38,97,109,112,59,32,97, +110,105,109,97,108,115,114,101,108,101,97,115,101,65,117,116,111,109,97,116,103, +101,116,116,105,110,103,109,101,116,104,111,100,115,110,111,116,104,105,110,103, +80,111,112,117,108,97,114,99,97,112,116,105,111,110,108,101,116,116,101,114,115, +99,97,112,116,117,114,101,115,99,105,101,110,99,101,108,105,99,101,110,115,101, +99,104,97,110,103,101,115,69,110,103,108,97,110,100,61,49,38,97,109,112,59,72, +105,115,116,111,114,121,32,61,32,110,101,119,32,67,101,110,116,114,97,108,117, +112,100,97,116,101,100,83,112,101,99,105,97,108,78,101,116,119,111,114,107,114, +101,113,117,105,114,101,99,111,109,109,101,110,116,119,97,114,110,105,110,103,67 +,111,108,108,101,103,101,116,111,111,108,98,97,114,114,101,109,97,105,110,115,98 +,101,99,97,117,115,101,101,108,101,99,116,101,100,68,101,117,116,115,99,104,102, +105,110,97,110,99,101,119,111,114,107,101,114,115,113,117,105,99,107,108,121,98, +101,116,119,101,101,110,101,120,97,99,116,108,121,115,101,116,116,105,110,103, +100,105,115,101,97,115,101,83,111,99,105,101,116,121,119,101,97,112,111,110,115, +101,120,104,105,98,105,116,38,108,116,59,33,45,45,67,111,110,116,114,111,108,99, +108,97,115,115,101,115,99,111,118,101,114,101,100,111,117,116,108,105,110,101,97 +,116,116,97,99,107,115,100,101,118,105,99,101,115,40,119,105,110,100,111,119,112 +,117,114,112,111,115,101,116,105,116,108,101,61,34,77,111,98,105,108,101,32,107, +105,108,108,105,110,103,115,104,111,119,105,110,103,73,116,97,108,105,97,110,100 +,114,111,112,112,101,100,104,101,97,118,105,108,121,101,102,102,101,99,116,115, +45,49,39,93,41,59,10,99,111,110,102,105,114,109,67,117,114,114,101,110,116,97, +100,118,97,110,99,101,115,104,97,114,105,110,103,111,112,101,110,105,110,103,100 +,114,97,119,105,110,103,98,105,108,108,105,111,110,111,114,100,101,114,101,100, +71,101,114,109,97,110,121,114,101,108,97,116,101,100,60,47,102,111,114,109,62, +105,110,99,108,117,100,101,119,104,101,116,104,101,114,100,101,102,105,110,101, +100,83,99,105,101,110,99,101,99,97,116,97,108,111,103,65,114,116,105,99,108,101, +98,117,116,116,111,110,115,108,97,114,103,101,115,116,117,110,105,102,111,114, +109,106,111,117,114,110,101,121,115,105,100,101,98,97,114,67,104,105,99,97,103, +111,104,111,108,105,100,97,121,71,101,110,101,114,97,108,112,97,115,115,97,103, +101,44,38,113,117,111,116,59,97,110,105,109,97,116,101,102,101,101,108,105,110, +103,97,114,114,105,118,101,100,112,97,115,115,105,110,103,110,97,116,117,114,97, +108,114,111,117,103,104,108,121,46,10,10,84,104,101,32,98,117,116,32,110,111,116 +,100,101,110,115,105,116,121,66,114,105,116,97,105,110,67,104,105,110,101,115, +101,108,97,99,107,32,111,102,116,114,105,98,117,116,101,73,114,101,108,97,110, +100,34,32,100,97,116,97,45,102,97,99,116,111,114,115,114,101,99,101,105,118,101, +116,104,97,116,32,105,115,76,105,98,114,97,114,121,104,117,115,98,97,110,100,105 +,110,32,102,97,99,116,97,102,102,97,105,114,115,67,104,97,114,108,101,115,114,97 +,100,105,99,97,108,98,114,111,117,103,104,116,102,105,110,100,105,110,103,108,97 +,110,100,105,110,103,58,108,97,110,103,61,34,114,101,116,117,114,110,32,108,101, +97,100,101,114,115,112,108,97,110,110,101,100,112,114,101,109,105,117,109,112,97 +,99,107,97,103,101,65,109,101,114,105,99,97,69,100,105,116,105,111,110,93,38,113 +,117,111,116,59,77,101,115,115,97,103,101,110,101,101,100,32,116,111,118,97,108, +117,101,61,34,99,111,109,112,108,101,120,108,111,111,107,105,110,103,115,116,97, +116,105,111,110,98,101,108,105,101,118,101,115,109,97,108,108,101,114,45,109,111 +,98,105,108,101,114,101,99,111,114,100,115,119,97,110,116,32,116,111,107,105,110 +,100,32,111,102,70,105,114,101,102,111,120,121,111,117,32,97,114,101,115,105,109 +,105,108,97,114,115,116,117,100,105,101,100,109,97,120,105,109,117,109,104,101, +97,100,105,110,103,114,97,112,105,100,108,121,99,108,105,109,97,116,101,107,105, +110,103,100,111,109,101,109,101,114,103,101,100,97,109,111,117,110,116,115,102, +111,117,110,100,101,100,112,105,111,110,101,101,114,102,111,114,109,117,108,97, +100,121,110,97,115,116,121,104,111,119,32,116,111,32,83,117,112,112,111,114,116, +114,101,118,101,110,117,101,101,99,111,110,111,109,121,82,101,115,117,108,116, +115,98,114,111,116,104,101,114,115,111,108,100,105,101,114,108,97,114,103,101, +108,121,99,97,108,108,105,110,103,46,38,113,117,111,116,59,65,99,99,111,117,110, +116,69,100,119,97,114,100,32,115,101,103,109,101,110,116,82,111,98,101,114,116, +32,101,102,102,111,114,116,115,80,97,99,105,102,105,99,108,101,97,114,110,101, +100,117,112,32,119,105,116,104,104,101,105,103,104,116,58,119,101,32,104,97,118, +101,65,110,103,101,108,101,115,110,97,116,105,111,110,115,95,115,101,97,114,99, +104,97,112,112,108,105,101,100,97,99,113,117,105,114,101,109,97,115,115,105,118, +101,103,114,97,110,116,101,100,58,32,102,97,108,115,101,116,114,101,97,116,101, +100,98,105,103,103,101,115,116,98,101,110,101,102,105,116,100,114,105,118,105, +110,103,83,116,117,100,105,101,115,109,105,110,105,109,117,109,112,101,114,104, +97,112,115,109,111,114,110,105,110,103,115,101,108,108,105,110,103,105,115,32, +117,115,101,100,114,101,118,101,114,115,101,118,97,114,105,97,110,116,32,114,111 +,108,101,61,34,109,105,115,115,105,110,103,97,99,104,105,101,118,101,112,114,111 +,109,111,116,101,115,116,117,100,101,110,116,115,111,109,101,111,110,101,101,120 +,116,114,101,109,101,114,101,115,116,111,114,101,98,111,116,116,111,109,58,101, +118,111,108,118,101,100,97,108,108,32,116,104,101,115,105,116,101,109,97,112,101 +,110,103,108,105,115,104,119,97,121,32,116,111,32,32,65,117,103,117,115,116,115, +121,109,98,111,108,115,67,111,109,112,97,110,121,109,97,116,116,101,114,115,109, +117,115,105,99,97,108,97,103,97,105,110,115,116,115,101,114,118,105,110,103,125, +41,40,41,59,13,10,112,97,121,109,101,110,116,116,114,111,117,98,108,101,99,111, +110,99,101,112,116,99,111,109,112,97,114,101,112,97,114,101,110,116,115,112,108, +97,121,101,114,115,114,101,103,105,111,110,115,109,111,110,105,116,111,114,32,39 +,39,84,104,101,32,119,105,110,110,105,110,103,101,120,112,108,111,114,101,97,100 +,97,112,116,101,100,71,97,108,108,101,114,121,112,114,111,100,117,99,101,97,98, +105,108,105,116,121,101,110,104,97,110,99,101,99,97,114,101,101,114,115,41,46,32 +,84,104,101,32,99,111,108,108,101,99,116,83,101,97,114,99,104,32,97,110,99,105, +101,110,116,101,120,105,115,116,101,100,102,111,111,116,101,114,32,104,97,110, +100,108,101,114,112,114,105,110,116,101,100,99,111,110,115,111,108,101,69,97,115 +,116,101,114,110,101,120,112,111,114,116,115,119,105,110,100,111,119,115,67,104, +97,110,110,101,108,105,108,108,101,103,97,108,110,101,117,116,114,97,108,115,117 +,103,103,101,115,116,95,104,101,97,100,101,114,115,105,103,110,105,110,103,46, +104,116,109,108,34,62,115,101,116,116,108,101,100,119,101,115,116,101,114,110,99 +,97,117,115,105,110,103,45,119,101,98,107,105,116,99,108,97,105,109,101,100,74, +117,115,116,105,99,101,99,104,97,112,116,101,114,118,105,99,116,105,109,115,84, +104,111,109,97,115,32,109,111,122,105,108,108,97,112,114,111,109,105,115,101,112 +,97,114,116,105,101,115,101,100,105,116,105,111,110,111,117,116,115,105,100,101, +58,102,97,108,115,101,44,104,117,110,100,114,101,100,79,108,121,109,112,105,99, +95,98,117,116,116,111,110,97,117,116,104,111,114,115,114,101,97,99,104,101,100, +99,104,114,111,110,105,99,100,101,109,97,110,100,115,115,101,99,111,110,100,115, +112,114,111,116,101,99,116,97,100,111,112,116,101,100,112,114,101,112,97,114,101 +,110,101,105,116,104,101,114,103,114,101,97,116,108,121,103,114,101,97,116,101, +114,111,118,101,114,97,108,108,105,109,112,114,111,118,101,99,111,109,109,97,110 +,100,115,112,101,99,105,97,108,115,101,97,114,99,104,46,119,111,114,115,104,105, +112,102,117,110,100,105,110,103,116,104,111,117,103,104,116,104,105,103,104,101, +115,116,105,110,115,116,101,97,100,117,116,105,108,105,116,121,113,117,97,114, +116,101,114,67,117,108,116,117,114,101,116,101,115,116,105,110,103,99,108,101,97 +,114,108,121,101,120,112,111,115,101,100,66,114,111,119,115,101,114,108,105,98, +101,114,97,108,125,32,99,97,116,99,104,80,114,111,106,101,99,116,101,120,97,109, +112,108,101,104,105,100,101,40,41,59,70,108,111,114,105,100,97,97,110,115,119, +101,114,115,97,108,108,111,119,101,100,69,109,112,101,114,111,114,100,101,102, +101,110,115,101,115,101,114,105,111,117,115,102,114,101,101,100,111,109,83,101, +118,101,114,97,108,45,98,117,116,116,111,110,70,117,114,116,104,101,114,111,117, +116,32,111,102,32,33,61,32,110,117,108,108,116,114,97,105,110,101,100,68,101,110 +,109,97,114,107,118,111,105,100,40,48,41,47,97,108,108,46,106,115,112,114,101, +118,101,110,116,82,101,113,117,101,115,116,83,116,101,112,104,101,110,10,10,87, +104,101,110,32,111,98,115,101,114,118,101,60,47,104,50,62,13,10,77,111,100,101, +114,110,32,112,114,111,118,105,100,101,34,32,97,108,116,61,34,98,111,114,100,101 +,114,115,46,10,10,70,111,114,32,10,10,77,97,110,121,32,97,114,116,105,115,116, +115,112,111,119,101,114,101,100,112,101,114,102,111,114,109,102,105,99,116,105, +111,110,116,121,112,101,32,111,102,109,101,100,105,99,97,108,116,105,99,107,101, +116,115,111,112,112,111,115,101,100,67,111,117,110,99,105,108,119,105,116,110, +101,115,115,106,117,115,116,105,99,101,71,101,111,114,103,101,32,66,101,108,103, +105,117,109,46,46,46,60,47,97,62,116,119,105,116,116,101,114,110,111,116,97,98, +108,121,119,97,105,116,105,110,103,119,97,114,102,97,114,101,32,79,116,104,101, +114,32,114,97,110,107,105,110,103,112,104,114,97,115,101,115,109,101,110,116,105 +,111,110,115,117,114,118,105,118,101,115,99,104,111,108,97,114,60,47,112,62,13, +10,32,67,111,117,110,116,114,121,105,103,110,111,114,101,100,108,111,115,115,32, +111,102,106,117,115,116,32,97,115,71,101,111,114,103,105,97,115,116,114,97,110, +103,101,60,104,101,97,100,62,60,115,116,111,112,112,101,100,49,39,93,41,59,13,10 +,105,115,108,97,110,100,115,110,111,116,97,98,108,101,98,111,114,100,101,114,58, +108,105,115,116,32,111,102,99,97,114,114,105,101,100,49,48,48,44,48,48,48,60,47, +104,51,62,10,32,115,101,118,101,114,97,108,98,101,99,111,109,101,115,115,101,108 +,101,99,116,32,119,101,100,100,105,110,103,48,48,46,104,116,109,108,109,111,110, +97,114,99,104,111,102,102,32,116,104,101,116,101,97,99,104,101,114,104,105,103, +104,108,121,32,98,105,111,108,111,103,121,108,105,102,101,32,111,102,111,114,32, +101,118,101,110,114,105,115,101,32,111,102,38,114,97,113,117,111,59,112,108,117, +115,111,110,101,104,117,110,116,105,110,103,40,116,104,111,117,103,104,68,111, +117,103,108,97,115,106,111,105,110,105,110,103,99,105,114,99,108,101,115,70,111, +114,32,116,104,101,65,110,99,105,101,110,116,86,105,101,116,110,97,109,118,101, +104,105,99,108,101,115,117,99,104,32,97,115,99,114,121,115,116,97,108,118,97,108 +,117,101,32,61,87,105,110,100,111,119,115,101,110,106,111,121,101,100,97,32,115, +109,97,108,108,97,115,115,117,109,101,100,60,97,32,105,100,61,34,102,111,114,101 +,105,103,110,32,65,108,108,32,114,105,104,111,119,32,116,104,101,68,105,115,112, +108,97,121,114,101,116,105,114,101,100,104,111,119,101,118,101,114,104,105,100, +100,101,110,59,98,97,116,116,108,101,115,115,101,101,107,105,110,103,99,97,98, +105,110,101,116,119,97,115,32,110,111,116,108,111,111,107,32,97,116,99,111,110, +100,117,99,116,103,101,116,32,116,104,101,74,97,110,117,97,114,121,104,97,112, +112,101,110,115,116,117,114,110,105,110,103,97,58,104,111,118,101,114,79,110,108 +,105,110,101,32,70,114,101,110,99,104,32,108,97,99,107,105,110,103,116,121,112, +105,99,97,108,101,120,116,114,97,99,116,101,110,101,109,105,101,115,101,118,101, +110,32,105,102,103,101,110,101,114,97,116,100,101,99,105,100,101,100,97,114,101, +32,110,111,116,47,115,101,97,114,99,104,98,101,108,105,101,102,115,45,105,109,97 +,103,101,58,108,111,99,97,116,101,100,115,116,97,116,105,99,46,108,111,103,105, +110,34,62,99,111,110,118,101,114,116,118,105,111,108,101,110,116,101,110,116,101 +,114,101,100,102,105,114,115,116,34,62,99,105,114,99,117,105,116,70,105,110,108, +97,110,100,99,104,101,109,105,115,116,115,104,101,32,119,97,115,49,48,112,120,59 +,34,62,97,115,32,115,117,99,104,100,105,118,105,100,101,100,60,47,115,112,97,110 +,62,119,105,108,108,32,98,101,108,105,110,101,32,111,102,97,32,103,114,101,97, +116,109,121,115,116,101,114,121,47,105,110,100,101,120,46,102,97,108,108,105,110 +,103,100,117,101,32,116,111,32,114,97,105,108,119,97,121,99,111,108,108,101,103, +101,109,111,110,115,116,101,114,100,101,115,99,101,110,116,105,116,32,119,105, +116,104,110,117,99,108,101,97,114,74,101,119,105,115,104,32,112,114,111,116,101, +115,116,66,114,105,116,105,115,104,102,108,111,119,101,114,115,112,114,101,100, +105,99,116,114,101,102,111,114,109,115,98,117,116,116,111,110,32,119,104,111,32, +119,97,115,108,101,99,116,117,114,101,105,110,115,116,97,110,116,115,117,105,99, +105,100,101,103,101,110,101,114,105,99,112,101,114,105,111,100,115,109,97,114, +107,101,116,115,83,111,99,105,97,108,32,102,105,115,104,105,110,103,99,111,109, +98,105,110,101,103,114,97,112,104,105,99,119,105,110,110,101,114,115,60,98,114, +32,47,62,60,98,121,32,116,104,101,32,78,97,116,117,114,97,108,80,114,105,118,97, +99,121,99,111,111,107,105,101,115,111,117,116,99,111,109,101,114,101,115,111,108 +,118,101,83,119,101,100,105,115,104,98,114,105,101,102,108,121,80,101,114,115, +105,97,110,115,111,32,109,117,99,104,67,101,110,116,117,114,121,100,101,112,105, +99,116,115,99,111,108,117,109,110,115,104,111,117,115,105,110,103,115,99,114,105 +,112,116,115,110,101,120,116,32,116,111,98,101,97,114,105,110,103,109,97,112,112 +,105,110,103,114,101,118,105,115,101,100,106,81,117,101,114,121,40,45,119,105, +100,116,104,58,116,105,116,108,101,34,62,116,111,111,108,116,105,112,83,101,99, +116,105,111,110,100,101,115,105,103,110,115,84,117,114,107,105,115,104,121,111, +117,110,103,101,114,46,109,97,116,99,104,40,125,41,40,41,59,10,10,98,117,114,110 +,105,110,103,111,112,101,114,97,116,101,100,101,103,114,101,101,115,115,111,117, +114,99,101,61,82,105,99,104,97,114,100,99,108,111,115,101,108,121,112,108,97,115 +,116,105,99,101,110,116,114,105,101,115,60,47,116,114,62,13,10,99,111,108,111, +114,58,35,117,108,32,105,100,61,34,112,111,115,115,101,115,115,114,111,108,108, +105,110,103,112,104,121,115,105,99,115,102,97,105,108,105,110,103,101,120,101,99 +,117,116,101,99,111,110,116,101,115,116,108,105,110,107,32,116,111,68,101,102,97 +,117,108,116,60,98,114,32,47,62,10,58,32,116,114,117,101,44,99,104,97,114,116, +101,114,116,111,117,114,105,115,109,99,108,97,115,115,105,99,112,114,111,99,101, +101,100,101,120,112,108,97,105,110,60,47,104,49,62,13,10,111,110,108,105,110,101 +,46,63,120,109,108,32,118,101,104,101,108,112,105,110,103,100,105,97,109,111,110 +,100,117,115,101,32,116,104,101,97,105,114,108,105,110,101,101,110,100,32,45,45, +62,41,46,97,116,116,114,40,114,101,97,100,101,114,115,104,111,115,116,105,110, +103,35,102,102,102,102,102,102,114,101,97,108,105,122,101,86,105,110,99,101,110, +116,115,105,103,110,97,108,115,32,115,114,99,61,34,47,80,114,111,100,117,99,116, +100,101,115,112,105,116,101,100,105,118,101,114,115,101,116,101,108,108,105,110, +103,80,117,98,108,105,99,32,104,101,108,100,32,105,110,74,111,115,101,112,104,32 +,116,104,101,97,116,114,101,97,102,102,101,99,116,115,60,115,116,121,108,101,62, +97,32,108,97,114,103,101,100,111,101,115,110,39,116,108,97,116,101,114,44,32,69, +108,101,109,101,110,116,102,97,118,105,99,111,110,99,114,101,97,116,111,114,72, +117,110,103,97,114,121,65,105,114,112,111,114,116,115,101,101,32,116,104,101,115 +,111,32,116,104,97,116,77,105,99,104,97,101,108,83,121,115,116,101,109,115,80, +114,111,103,114,97,109,115,44,32,97,110,100,32,32,119,105,100,116,104,61,101,38, +113,117,111,116,59,116,114,97,100,105,110,103,108,101,102,116,34,62,10,112,101, +114,115,111,110,115,71,111,108,100,101,110,32,65,102,102,97,105,114,115,103,114, +97,109,109,97,114,102,111,114,109,105,110,103,100,101,115,116,114,111,121,105, +100,101,97,32,111,102,99,97,115,101,32,111,102,111,108,100,101,115,116,32,116, +104,105,115,32,105,115,46,115,114,99,32,61,32,99,97,114,116,111,111,110,114,101, +103,105,115,116,114,67,111,109,109,111,110,115,77,117,115,108,105,109,115,87,104 +,97,116,32,105,115,105,110,32,109,97,110,121,109,97,114,107,105,110,103,114,101, +118,101,97,108,115,73,110,100,101,101,100,44,101,113,117,97,108,108,121,47,115, +104,111,119,95,97,111,117,116,100,111,111,114,101,115,99,97,112,101,40,65,117, +115,116,114,105,97,103,101,110,101,116,105,99,115,121,115,116,101,109,44,73,110, +32,116,104,101,32,115,105,116,116,105,110,103,72,101,32,97,108,115,111,73,115, +108,97,110,100,115,65,99,97,100,101,109,121,10,9,9,60,33,45,45,68,97,110,105,101 +,108,32,98,105,110,100,105,110,103,98,108,111,99,107,34,62,105,109,112,111,115, +101,100,117,116,105,108,105,122,101,65,98,114,97,104,97,109,40,101,120,99,101, +112,116,123,119,105,100,116,104,58,112,117,116,116,105,110,103,41,46,104,116,109 +,108,40,124,124,32,91,93,59,10,68,65,84,65,91,32,42,107,105,116,99,104,101,110, +109,111,117,110,116,101,100,97,99,116,117,97,108,32,100,105,97,108,101,99,116, +109,97,105,110,108,121,32,95,98,108,97,110,107,39,105,110,115,116,97,108,108,101 +,120,112,101,114,116,115,105,102,40,116,121,112,101,73,116,32,97,108,115,111,38, +99,111,112,121,59,32,34,62,84,101,114,109,115,98,111,114,110,32,105,110,79,112, +116,105,111,110,115,101,97,115,116,101,114,110,116,97,108,107,105,110,103,99,111 +,110,99,101,114,110,103,97,105,110,101,100,32,111,110,103,111,105,110,103,106, +117,115,116,105,102,121,99,114,105,116,105,99,115,102,97,99,116,111,114,121,105, +116,115,32,111,119,110,97,115,115,97,117,108,116,105,110,118,105,116,101,100,108 +,97,115,116,105,110,103,104,105,115,32,111,119,110,104,114,101,102,61,34,47,34, +32,114,101,108,61,34,100,101,118,101,108,111,112,99,111,110,99,101,114,116,100, +105,97,103,114,97,109,100,111,108,108,97,114,115,99,108,117,115,116,101,114,112, +104,112,63,105,100,61,97,108,99,111,104,111,108,41,59,125,41,40,41,59,117,115, +105,110,103,32,97,62,60,115,112,97,110,62,118,101,115,115,101,108,115,114,101, +118,105,118,97,108,65,100,100,114,101,115,115,97,109,97,116,101,117,114,97,110, +100,114,111,105,100,97,108,108,101,103,101,100,105,108,108,110,101,115,115,119, +97,108,107,105,110,103,99,101,110,116,101,114,115,113,117,97,108,105,102,121,109 +,97,116,99,104,101,115,117,110,105,102,105,101,100,101,120,116,105,110,99,116,68 +,101,102,101,110,115,101,100,105,101,100,32,105,110,10,9,60,33,45,45,32,99,117, +115,116,111,109,115,108,105,110,107,105,110,103,76,105,116,116,108,101,32,66,111 +,111,107,32,111,102,101,118,101,110,105,110,103,109,105,110,46,106,115,63,97,114 +,101,32,116,104,101,107,111,110,116,97,107,116,116,111,100,97,121,39,115,46,104, +116,109,108,34,32,116,97,114,103,101,116,61,119,101,97,114,105,110,103,65,108, +108,32,82,105,103,59,10,125,41,40,41,59,114,97,105,115,105,110,103,32,65,108,115 +,111,44,32,99,114,117,99,105,97,108,97,98,111,117,116,34,62,100,101,99,108,97, +114,101,45,45,62,10,60,115,99,102,105,114,101,102,111,120,97,115,32,109,117,99, +104,97,112,112,108,105,101,115,105,110,100,101,120,44,32,115,44,32,98,117,116,32 +,116,121,112,101,32,61,32,10,13,10,60,33,45,45,116,111,119,97,114,100,115,82,101 +,99,111,114,100,115,80,114,105,118,97,116,101,70,111,114,101,105,103,110,80,114, +101,109,105,101,114,99,104,111,105,99,101,115,86,105,114,116,117,97,108,114,101, +116,117,114,110,115,67,111,109,109,101,110,116,80,111,119,101,114,101,100,105, +110,108,105,110,101,59,112,111,118,101,114,116,121,99,104,97,109,98,101,114,76, +105,118,105,110,103,32,118,111,108,117,109,101,115,65,110,116,104,111,110,121, +108,111,103,105,110,34,32,82,101,108,97,116,101,100,69,99,111,110,111,109,121, +114,101,97,99,104,101,115,99,117,116,116,105,110,103,103,114,97,118,105,116,121, +108,105,102,101,32,105,110,67,104,97,112,116,101,114,45,115,104,97,100,111,119, +78,111,116,97,98,108,101,60,47,116,100,62,13,10,32,114,101,116,117,114,110,115, +116,97,100,105,117,109,119,105,100,103,101,116,115,118,97,114,121,105,110,103, +116,114,97,118,101,108,115,104,101,108,100,32,98,121,119,104,111,32,97,114,101, +119,111,114,107,32,105,110,102,97,99,117,108,116,121,97,110,103,117,108,97,114, +119,104,111,32,104,97,100,97,105,114,112,111,114,116,116,111,119,110,32,111,102, +10,10,83,111,109,101,32,39,99,108,105,99,107,39,99,104,97,114,103,101,115,107, +101,121,119,111,114,100,105,116,32,119,105,108,108,99,105,116,121,32,111,102,40, +116,104,105,115,41,59,65,110,100,114,101,119,32,117,110,105,113,117,101,32,99, +104,101,99,107,101,100,111,114,32,109,111,114,101,51,48,48,112,120,59,32,114,101 +,116,117,114,110,59,114,115,105,111,110,61,34,112,108,117,103,105,110,115,119, +105,116,104,105,110,32,104,101,114,115,101,108,102,83,116,97,116,105,111,110,70, +101,100,101,114,97,108,118,101,110,116,117,114,101,112,117,98,108,105,115,104, +115,101,110,116,32,116,111,116,101,110,115,105,111,110,97,99,116,114,101,115,115 +,99,111,109,101,32,116,111,102,105,110,103,101,114,115,68,117,107,101,32,111,102 +,112,101,111,112,108,101,44,101,120,112,108,111,105,116,119,104,97,116,32,105, +115,104,97,114,109,111,110,121,97,32,109,97,106,111,114,34,58,34,104,116,116,112 +,105,110,32,104,105,115,32,109,101,110,117,34,62,10,109,111,110,116,104,108,121, +111,102,102,105,99,101,114,99,111,117,110,99,105,108,103,97,105,110,105,110,103, +101,118,101,110,32,105,110,83,117,109,109,97,114,121,100,97,116,101,32,111,102, +108,111,121,97,108,116,121,102,105,116,110,101,115,115,97,110,100,32,119,97,115, +101,109,112,101,114,111,114,115,117,112,114,101,109,101,83,101,99,111,110,100,32 +,104,101,97,114,105,110,103,82,117,115,115,105,97,110,108,111,110,103,101,115, +116,65,108,98,101,114,116,97,108,97,116,101,114,97,108,115,101,116,32,111,102,32 +,115,109,97,108,108,34,62,46,97,112,112,101,110,100,100,111,32,119,105,116,104, +102,101,100,101,114,97,108,98,97,110,107,32,111,102,98,101,110,101,97,116,104,68 +,101,115,112,105,116,101,67,97,112,105,116,97,108,103,114,111,117,110,100,115,41 +,44,32,97,110,100,32,112,101,114,99,101,110,116,105,116,32,102,114,111,109,99, +108,111,115,105,110,103,99,111,110,116,97,105,110,73,110,115,116,101,97,100,102, +105,102,116,101,101,110,97,115,32,119,101,108,108,46,121,97,104,111,111,46,114, +101,115,112,111,110,100,102,105,103,104,116,101,114,111,98,115,99,117,114,101, +114,101,102,108,101,99,116,111,114,103,97,110,105,99,61,32,77,97,116,104,46,101, +100,105,116,105,110,103,111,110,108,105,110,101,32,112,97,100,100,105,110,103,97 +,32,119,104,111,108,101,111,110,101,114,114,111,114,121,101,97,114,32,111,102, +101,110,100,32,111,102,32,98,97,114,114,105,101,114,119,104,101,110,32,105,116, +104,101,97,100,101,114,32,104,111,109,101,32,111,102,114,101,115,117,109,101,100 +,114,101,110,97,109,101,100,115,116,114,111,110,103,62,104,101,97,116,105,110, +103,114,101,116,97,105,110,115,99,108,111,117,100,102,114,119,97,121,32,111,102, +32,77,97,114,99,104,32,49,107,110,111,119,105,110,103,105,110,32,112,97,114,116, +66,101,116,119,101,101,110,108,101,115,115,111,110,115,99,108,111,115,101,115, +116,118,105,114,116,117,97,108,108,105,110,107,115,34,62,99,114,111,115,115,101, +100,69,78,68,32,45,45,62,102,97,109,111,117,115,32,97,119,97,114,100,101,100,76, +105,99,101,110,115,101,72,101,97,108,116,104,32,102,97,105,114,108,121,32,119, +101,97,108,116,104,121,109,105,110,105,109,97,108,65,102,114,105,99,97,110,99, +111,109,112,101,116,101,108,97,98,101,108,34,62,115,105,110,103,105,110,103,102, +97,114,109,101,114,115,66,114,97,115,105,108,41,100,105,115,99,117,115,115,114, +101,112,108,97,99,101,71,114,101,103,111,114,121,102,111,110,116,32,99,111,112, +117,114,115,117,101,100,97,112,112,101,97,114,115,109,97,107,101,32,117,112,114, +111,117,110,100,101,100,98,111,116,104,32,111,102,98,108,111,99,107,101,100,115, +97,119,32,116,104,101,111,102,102,105,99,101,115,99,111,108,111,117,114,115,105, +102,40,100,111,99,117,119,104,101,110,32,104,101,101,110,102,111,114,99,101,112, +117,115,104,40,102,117,65,117,103,117,115,116,32,85,84,70,45,56,34,62,70,97,110, +116,97,115,121,105,110,32,109,111,115,116,105,110,106,117,114,101,100,85,115,117 +,97,108,108,121,102,97,114,109,105,110,103,99,108,111,115,117,114,101,111,98,106 +,101,99,116,32,100,101,102,101,110,99,101,117,115,101,32,111,102,32,77,101,100, +105,99,97,108,60,98,111,100,121,62,10,101,118,105,100,101,110,116,98,101,32,117, +115,101,100,107,101,121,67,111,100,101,115,105,120,116,101,101,110,73,115,108,97 +,109,105,99,35,48,48,48,48,48,48,101,110,116,105,114,101,32,119,105,100,101,108, +121,32,97,99,116,105,118,101,32,40,116,121,112,101,111,102,111,110,101,32,99,97, +110,99,111,108,111,114,32,61,115,112,101,97,107,101,114,101,120,116,101,110,100, +115,80,104,121,115,105,99,115,116,101,114,114,97,105,110,60,116,98,111,100,121, +62,102,117,110,101,114,97,108,118,105,101,119,105,110,103,109,105,100,100,108, +101,32,99,114,105,99,107,101,116,112,114,111,112,104,101,116,115,104,105,102,116 +,101,100,100,111,99,116,111,114,115,82,117,115,115,101,108,108,32,116,97,114,103 +,101,116,99,111,109,112,97,99,116,97,108,103,101,98,114,97,115,111,99,105,97,108 +,45,98,117,108,107,32,111,102,109,97,110,32,97,110,100,60,47,116,100,62,10,32, +104,101,32,108,101,102,116,41,46,118,97,108,40,41,102,97,108,115,101,41,59,108, +111,103,105,99,97,108,98,97,110,107,105,110,103,104,111,109,101,32,116,111,110, +97,109,105,110,103,32,65,114,105,122,111,110,97,99,114,101,100,105,116,115,41,59 +,10,125,41,59,10,102,111,117,110,100,101,114,105,110,32,116,117,114,110,67,111, +108,108,105,110,115,98,101,102,111,114,101,32,66,117,116,32,116,104,101,99,104, +97,114,103,101,100,84,105,116,108,101,34,62,67,97,112,116,97,105,110,115,112,101 +,108,108,101,100,103,111,100,100,101,115,115,84,97,103,32,45,45,62,65,100,100, +105,110,103,58,98,117,116,32,119,97,115,82,101,99,101,110,116,32,112,97,116,105, +101,110,116,98,97,99,107,32,105,110,61,102,97,108,115,101,38,76,105,110,99,111, +108,110,119,101,32,107,110,111,119,67,111,117,110,116,101,114,74,117,100,97,105, +115,109,115,99,114,105,112,116,32,97,108,116,101,114,101,100,39,93,41,59,10,32, +32,104,97,115,32,116,104,101,117,110,99,108,101,97,114,69,118,101,110,116,39,44, +98,111,116,104,32,105,110,110,111,116,32,97,108,108,10,10,60,33,45,45,32,112,108 +,97,99,105,110,103,104,97,114,100,32,116,111,32,99,101,110,116,101,114,115,111, +114,116,32,111,102,99,108,105,101,110,116,115,115,116,114,101,101,116,115,66,101 +,114,110,97,114,100,97,115,115,101,114,116,115,116,101,110,100,32,116,111,102,97 +,110,116,97,115,121,100,111,119,110,32,105,110,104,97,114,98,111,117,114,70,114, +101,101,100,111,109,106,101,119,101,108,114,121,47,97,98,111,117,116,46,46,115, +101,97,114,99,104,108,101,103,101,110,100,115,105,115,32,109,97,100,101,109,111, +100,101,114,110,32,111,110,108,121,32,111,110,111,110,108,121,32,116,111,105,109 +,97,103,101,34,32,108,105,110,101,97,114,32,112,97,105,110,116,101,114,97,110, +100,32,110,111,116,114,97,114,101,108,121,32,97,99,114,111,110,121,109,100,101, +108,105,118,101,114,115,104,111,114,116,101,114,48,48,38,97,109,112,59,97,115,32 +,109,97,110,121,119,105,100,116,104,61,34,47,42,32,60,33,91,67,116,105,116,108, +101,32,61,111,102,32,116,104,101,32,108,111,119,101,115,116,32,112,105,99,107, +101,100,32,101,115,99,97,112,101,100,117,115,101,115,32,111,102,112,101,111,112, +108,101,115,32,80,117,98,108,105,99,77,97,116,116,104,101,119,116,97,99,116,105, +99,115,100,97,109,97,103,101,100,119,97,121,32,102,111,114,108,97,119,115,32,111 +,102,101,97,115,121,32,116,111,32,119,105,110,100,111,119,115,116,114,111,110, +103,32,32,115,105,109,112,108,101,125,99,97,116,99,104,40,115,101,118,101,110, +116,104,105,110,102,111,98,111,120,119,101,110,116,32,116,111,112,97,105,110,116 +,101,100,99,105,116,105,122,101,110,73,32,100,111,110,39,116,114,101,116,114,101 +,97,116,46,32,83,111,109,101,32,119,119,46,34,41,59,10,98,111,109,98,105,110,103 +,109,97,105,108,116,111,58,109,97,100,101,32,105,110,46,32,77,97,110,121,32,99, +97,114,114,105,101,115,124,124,123,125,59,119,105,119,111,114,107,32,111,102,115 +,121,110,111,110,121,109,100,101,102,101,97,116,115,102,97,118,111,114,101,100, +111,112,116,105,99,97,108,112,97,103,101,84,114,97,117,110,108,101,115,115,32, +115,101,110,100,105,110,103,108,101,102,116,34,62,60,99,111,109,83,99,111,114,65 +,108,108,32,116,104,101,106,81,117,101,114,121,46,116,111,117,114,105,115,116,67 +,108,97,115,115,105,99,102,97,108,115,101,34,32,87,105,108,104,101,108,109,115, +117,98,117,114,98,115,103,101,110,117,105,110,101,98,105,115,104,111,112,115,46, +115,112,108,105,116,40,103,108,111,98,97,108,32,102,111,108,108,111,119,115,98, +111,100,121,32,111,102,110,111,109,105,110,97,108,67,111,110,116,97,99,116,115, +101,99,117,108,97,114,108,101,102,116,32,116,111,99,104,105,101,102,108,121,45, +104,105,100,100,101,110,45,98,97,110,110,101,114,60,47,108,105,62,10,10,46,32,87 +,104,101,110,32,105,110,32,98,111,116,104,100,105,115,109,105,115,115,69,120,112 +,108,111,114,101,97,108,119,97,121,115,32,118,105,97,32,116,104,101,115,112,97, +195,177,111,108,119,101,108,102,97,114,101,114,117,108,105,110,103,32,97,114,114 +,97,110,103,101,99,97,112,116,97,105,110,104,105,115,32,115,111,110,114,117,108, +101,32,111,102,104,101,32,116,111,111,107,105,116,115,101,108,102,44,61,48,38,97 +,109,112,59,40,99,97,108,108,101,100,115,97,109,112,108,101,115,116,111,32,109, +97,107,101,99,111,109,47,112,97,103,77,97,114,116,105,110,32,75,101,110,110,101, +100,121,97,99,99,101,112,116,115,102,117,108,108,32,111,102,104,97,110,100,108, +101,100,66,101,115,105,100,101,115,47,47,45,45,62,60,47,97,98,108,101,32,116,111 +,116,97,114,103,101,116,115,101,115,115,101,110,99,101,104,105,109,32,116,111,32 +,105,116,115,32,98,121,32,99,111,109,109,111,110,46,109,105,110,101,114,97,108, +116,111,32,116,97,107,101,119,97,121,115,32,116,111,115,46,111,114,103,47,108,97 +,100,118,105,115,101,100,112,101,110,97,108,116,121,115,105,109,112,108,101,58, +105,102,32,116,104,101,121,76,101,116,116,101,114,115,97,32,115,104,111,114,116, +72,101,114,98,101,114,116,115,116,114,105,107,101,115,32,103,114,111,117,112,115 +,46,108,101,110,103,116,104,102,108,105,103,104,116,115,111,118,101,114,108,97, +112,115,108,111,119,108,121,32,108,101,115,115,101,114,32,115,111,99,105,97,108, +32,60,47,112,62,10,9,9,105,116,32,105,110,116,111,114,97,110,107,101,100,32,114, +97,116,101,32,111,102,117,108,62,13,10,32,32,97,116,116,101,109,112,116,112,97, +105,114,32,111,102,109,97,107,101,32,105,116,75,111,110,116,97,107,116,65,110, +116,111,110,105,111,104,97,118,105,110,103,32,114,97,116,105,110,103,115,32,97, +99,116,105,118,101,115,116,114,101,97,109,115,116,114,97,112,112,101,100,34,41, +46,99,115,115,40,104,111,115,116,105,108,101,108,101,97,100,32,116,111,108,105, +116,116,108,101,32,103,114,111,117,112,115,44,80,105,99,116,117,114,101,45,45,62 +,13,10,13,10,32,114,111,119,115,61,34,32,111,98,106,101,99,116,105,110,118,101, +114,115,101,60,102,111,111,116,101,114,67,117,115,116,111,109,86,62,60,92,47,115 +,99,114,115,111,108,118,105,110,103,67,104,97,109,98,101,114,115,108,97,118,101, +114,121,119,111,117,110,100,101,100,119,104,101,114,101,97,115,33,61,32,39,117, +110,100,102,111,114,32,97,108,108,112,97,114,116,108,121,32,45,114,105,103,104, +116,58,65,114,97,98,105,97,110,98,97,99,107,101,100,32,99,101,110,116,117,114, +121,117,110,105,116,32,111,102,109,111,98,105,108,101,45,69,117,114,111,112,101, +44,105,115,32,104,111,109,101,114,105,115,107,32,111,102,100,101,115,105,114,101 +,100,67,108,105,110,116,111,110,99,111,115,116,32,111,102,97,103,101,32,111,102, +32,98,101,99,111,109,101,32,110,111,110,101,32,111,102,112,38,113,117,111,116,59 +,77,105,100,100,108,101,32,101,97,100,39,41,91,48,67,114,105,116,105,99,115,115, +116,117,100,105,111,115,62,38,99,111,112,121,59,103,114,111,117,112,34,62,97,115 +,115,101,109,98,108,109,97,107,105,110,103,32,112,114,101,115,115,101,100,119, +105,100,103,101,116,46,112,115,58,34,32,63,32,114,101,98,117,105,108,116,98,121, +32,115,111,109,101,70,111,114,109,101,114,32,101,100,105,116,111,114,115,100,101 +,108,97,121,101,100,67,97,110,111,110,105,99,104,97,100,32,116,104,101,112,117, +115,104,105,110,103,99,108,97,115,115,61,34,98,117,116,32,97,114,101,112,97,114, +116,105,97,108,66,97,98,121,108,111,110,98,111,116,116,111,109,32,99,97,114,114, +105,101,114,67,111,109,109,97,110,100,105,116,115,32,117,115,101,65,115,32,119, +105,116,104,99,111,117,114,115,101,115,97,32,116,104,105,114,100,100,101,110,111 +,116,101,115,97,108,115,111,32,105,110,72,111,117,115,116,111,110,50,48,112,120, +59,34,62,97,99,99,117,115,101,100,100,111,117,98,108,101,32,103,111,97,108,32, +111,102,70,97,109,111,117,115,32,41,46,98,105,110,100,40,112,114,105,101,115,116 +,115,32,79,110,108,105,110,101,105,110,32,74,117,108,121,115,116,32,43,32,34,103 +,99,111,110,115,117,108,116,100,101,99,105,109,97,108,104,101,108,112,102,117, +108,114,101,118,105,118,101,100,105,115,32,118,101,114,121,114,39,43,39,105,112, +116,108,111,115,105,110,103,32,102,101,109,97,108,101,115,105,115,32,97,108,115, +111,115,116,114,105,110,103,115,100,97,121,115,32,111,102,97,114,114,105,118,97, +108,102,117,116,117,114,101,32,60,111,98,106,101,99,116,102,111,114,99,105,110, +103,83,116,114,105,110,103,40,34,32,47,62,10,9,9,104,101,114,101,32,105,115,101, +110,99,111,100,101,100,46,32,32,84,104,101,32,98,97,108,108,111,111,110,100,111, +110,101,32,98,121,47,99,111,109,109,111,110,98,103,99,111,108,111,114,108,97,119 +,32,111,102,32,73,110,100,105,97,110,97,97,118,111,105,100,101,100,98,117,116,32 +,116,104,101,50,112,120,32,51,112,120,106,113,117,101,114,121,46,97,102,116,101, +114,32,97,112,111,108,105,99,121,46,109,101,110,32,97,110,100,102,111,111,116, +101,114,45,61,32,116,114,117,101,59,102,111,114,32,117,115,101,115,99,114,101, +101,110,46,73,110,100,105,97,110,32,105,109,97,103,101,32,61,102,97,109,105,108, +121,44,104,116,116,112,58,47,47,32,38,110,98,115,112,59,100,114,105,118,101,114, +115,101,116,101,114,110,97,108,115,97,109,101,32,97,115,110,111,116,105,99,101, +100,118,105,101,119,101,114,115,125,41,40,41,59,10,32,105,115,32,109,111,114,101 +,115,101,97,115,111,110,115,102,111,114,109,101,114,32,116,104,101,32,110,101, +119,105,115,32,106,117,115,116,99,111,110,115,101,110,116,32,83,101,97,114,99, +104,119,97,115,32,116,104,101,119,104,121,32,116,104,101,115,104,105,112,112,101 +,100,98,114,62,60,98,114,62,119,105,100,116,104,58,32,104,101,105,103,104,116,61 +,109,97,100,101,32,111,102,99,117,105,115,105,110,101,105,115,32,116,104,97,116, +97,32,118,101,114,121,32,65,100,109,105,114,97,108,32,102,105,120,101,100,59,110 +,111,114,109,97,108,32,77,105,115,115,105,111,110,80,114,101,115,115,44,32,111, +110,116,97,114,105,111,99,104,97,114,115,101,116,116,114,121,32,116,111,32,105, +110,118,97,100,101,100,61,34,116,114,117,101,34,115,112,97,99,105,110,103,105, +115,32,109,111,115,116,97,32,109,111,114,101,32,116,111,116,97,108,108,121,102, +97,108,108,32,111,102,125,41,59,13,10,32,32,105,109,109,101,110,115,101,116,105, +109,101,32,105,110,115,101,116,32,111,117,116,115,97,116,105,115,102,121,116,111 +,32,102,105,110,100,100,111,119,110,32,116,111,108,111,116,32,111,102,32,80,108, +97,121,101,114,115,105,110,32,74,117,110,101,113,117,97,110,116,117,109,110,111, +116,32,116,104,101,116,105,109,101,32,116,111,100,105,115,116,97,110,116,70,105, +110,110,105,115,104,115,114,99,32,61,32,40,115,105,110,103,108,101,32,104,101, +108,112,32,111,102,71,101,114,109,97,110,32,108,97,119,32,97,110,100,108,97,98, +101,108,101,100,102,111,114,101,115,116,115,99,111,111,107,105,110,103,115,112, +97,99,101,34,62,104,101,97,100,101,114,45,119,101,108,108,32,97,115,83,116,97, +110,108,101,121,98,114,105,100,103,101,115,47,103,108,111,98,97,108,67,114,111, +97,116,105,97,32,65,98,111,117,116,32,91,48,93,59,10,32,32,105,116,44,32,97,110, +100,103,114,111,117,112,101,100,98,101,105,110,103,32,97,41,123,116,104,114,111, +119,104,101,32,109,97,100,101,108,105,103,104,116,101,114,101,116,104,105,99,97, +108,70,70,70,70,70,70,34,98,111,116,116,111,109,34,108,105,107,101,32,97,32,101, +109,112,108,111,121,115,108,105,118,101,32,105,110,97,115,32,115,101,101,110,112 +,114,105,110,116,101,114,109,111,115,116,32,111,102,117,98,45,108,105,110,107, +114,101,106,101,99,116,115,97,110,100,32,117,115,101,105,109,97,103,101,34,62, +115,117,99,99,101,101,100,102,101,101,100,105,110,103,78,117,99,108,101,97,114, +105,110,102,111,114,109,97,116,111,32,104,101,108,112,87,111,109,101,110,39,115, +78,101,105,116,104,101,114,77,101,120,105,99,97,110,112,114,111,116,101,105,110, +60,116,97,98,108,101,32,98,121,32,109,97,110,121,104,101,97,108,116,104,121,108, +97,119,115,117,105,116,100,101,118,105,115,101,100,46,112,117,115,104,40,123,115 +,101,108,108,101,114,115,115,105,109,112,108,121,32,84,104,114,111,117,103,104, +46,99,111,111,107,105,101,32,73,109,97,103,101,40,111,108,100,101,114,34,62,117, +115,46,106,115,34,62,32,83,105,110,99,101,32,117,110,105,118,101,114,115,108,97, +114,103,101,114,32,111,112,101,110,32,116,111,33,45,45,32,101,110,100,108,105, +101,115,32,105,110,39,93,41,59,13,10,32,32,109,97,114,107,101,116,119,104,111,32 +,105,115,32,40,34,68,79,77,67,111,109,97,110,97,103,101,100,111,110,101,32,102, +111,114,116,121,112,101,111,102,32,75,105,110,103,100,111,109,112,114,111,102, +105,116,115,112,114,111,112,111,115,101,116,111,32,115,104,111,119,99,101,110, +116,101,114,59,109,97,100,101,32,105,116,100,114,101,115,115,101,100,119,101,114 +,101,32,105,110,109,105,120,116,117,114,101,112,114,101,99,105,115,101,97,114, +105,115,105,110,103,115,114,99,32,61,32,39,109,97,107,101,32,97,32,115,101,99, +117,114,101,100,66,97,112,116,105,115,116,118,111,116,105,110,103,32,10,9,9,118, +97,114,32,77,97,114,99,104,32,50,103,114,101,119,32,117,112,67,108,105,109,97, +116,101,46,114,101,109,111,118,101,115,107,105,108,108,101,100,119,97,121,32,116 +,104,101,60,47,104,101,97,100,62,102,97,99,101,32,111,102,97,99,116,105,110,103, +32,114,105,103,104,116,34,62,116,111,32,119,111,114,107,114,101,100,117,99,101, +115,104,97,115,32,104,97,100,101,114,101,99,116,101,100,115,104,111,119,40,41,59 +,97,99,116,105,111,110,61,98,111,111,107,32,111,102,97,110,32,97,114,101,97,61, +61,32,34,104,116,116,60,104,101,97,100,101,114,10,60,104,116,109,108,62,99,111, +110,102,111,114,109,102,97,99,105,110,103,32,99,111,111,107,105,101,46,114,101, +108,121,32,111,110,104,111,115,116,101,100,32,46,99,117,115,116,111,109,104,101, +32,119,101,110,116,98,117,116,32,102,111,114,115,112,114,101,97,100,32,70,97,109 +,105,108,121,32,97,32,109,101,97,110,115,111,117,116,32,116,104,101,102,111,114, +117,109,115,46,102,111,111,116,97,103,101,34,62,77,111,98,105,108,67,108,101,109 +,101,110,116,115,34,32,105,100,61,34,97,115,32,104,105,103,104,105,110,116,101, +110,115,101,45,45,62,60,33,45,45,102,101,109,97,108,101,32,105,115,32,115,101, +101,110,105,109,112,108,105,101,100,115,101,116,32,116,104,101,97,32,115,116,97, +116,101,97,110,100,32,104,105,115,102,97,115,116,101,115,116,98,101,115,105,100, +101,115,98,117,116,116,111,110,95,98,111,117,110,100,101,100,34,62,60,105,109, +103,32,73,110,102,111,98,111,120,101,118,101,110,116,115,44,97,32,121,111,117, +110,103,97,110,100,32,97,114,101,78,97,116,105,118,101,32,99,104,101,97,112,101, +114,84,105,109,101,111,117,116,97,110,100,32,104,97,115,101,110,103,105,110,101, +115,119,111,110,32,116,104,101,40,109,111,115,116,108,121,114,105,103,104,116,58 +,32,102,105,110,100,32,97,32,45,98,111,116,116,111,109,80,114,105,110,99,101,32, +97,114,101,97,32,111,102,109,111,114,101,32,111,102,115,101,97,114,99,104,95,110 +,97,116,117,114,101,44,108,101,103,97,108,108,121,112,101,114,105,111,100,44,108 +,97,110,100,32,111,102,111,114,32,119,105,116,104,105,110,100,117,99,101,100,112 +,114,111,118,105,110,103,109,105,115,115,105,108,101,108,111,99,97,108,108,121, +65,103,97,105,110,115,116,116,104,101,32,119,97,121,107,38,113,117,111,116,59, +112,120,59,34,62,13,10,112,117,115,104,101,100,32,97,98,97,110,100,111,110,110, +117,109,101,114,97,108,67,101,114,116,97,105,110,73,110,32,116,104,105,115,109, +111,114,101,32,105,110,111,114,32,115,111,109,101,110,97,109,101,32,105,115,97, +110,100,44,32,105,110,99,114,111,119,110,101,100,73,83,66,78,32,48,45,99,114,101 +,97,116,101,115,79,99,116,111,98,101,114,109,97,121,32,110,111,116,99,101,110, +116,101,114,32,108,97,116,101,32,105,110,68,101,102,101,110,99,101,101,110,97,99 +,116,101,100,119,105,115,104,32,116,111,98,114,111,97,100,108,121,99,111,111,108 +,105,110,103,111,110,108,111,97,100,61,105,116,46,32,84,104,101,114,101,99,111, +118,101,114,77,101,109,98,101,114,115,104,101,105,103,104,116,32,97,115,115,117, +109,101,115,60,104,116,109,108,62,10,112,101,111,112,108,101,46,105,110,32,111, +110,101,32,61,119,105,110,100,111,119,102,111,111,116,101,114,95,97,32,103,111, +111,100,32,114,101,107,108,97,109,97,111,116,104,101,114,115,44,116,111,32,116, +104,105,115,95,99,111,111,107,105,101,112,97,110,101,108,34,62,76,111,110,100, +111,110,44,100,101,102,105,110,101,115,99,114,117,115,104,101,100,98,97,112,116, +105,115,109,99,111,97,115,116,97,108,115,116,97,116,117,115,32,116,105,116,108, +101,34,32,109,111,118,101,32,116,111,108,111,115,116,32,105,110,98,101,116,116, +101,114,32,105,109,112,108,105,101,115,114,105,118,97,108,114,121,115,101,114, +118,101,114,115,32,83,121,115,116,101,109,80,101,114,104,97,112,115,101,115,32, +97,110,100,32,99,111,110,116,101,110,100,102,108,111,119,105,110,103,108,97,115, +116,101,100,32,114,105,115,101,32,105,110,71,101,110,101,115,105,115,118,105,101 +,119,32,111,102,114,105,115,105,110,103,32,115,101,101,109,32,116,111,98,117,116 +,32,105,110,32,98,97,99,107,105,110,103,104,101,32,119,105,108,108,103,105,118, +101,110,32,97,103,105,118,105,110,103,32,99,105,116,105,101,115,46,102,108,111, +119,32,111,102,32,76,97,116,101,114,32,97,108,108,32,98,117,116,72,105,103,104, +119,97,121,111,110,108,121,32,98,121,115,105,103,110,32,111,102,104,101,32,100, +111,101,115,100,105,102,102,101,114,115,98,97,116,116,101,114,121,38,97,109,112, +59,108,97,115,105,110,103,108,101,115,116,104,114,101,97,116,115,105,110,116,101 +,103,101,114,116,97,107,101,32,111,110,114,101,102,117,115,101,100,99,97,108,108 +,101,100,32,61,85,83,38,97,109,112,83,101,101,32,116,104,101,110,97,116,105,118, +101,115,98,121,32,116,104,105,115,115,121,115,116,101,109,46,104,101,97,100,32, +111,102,58,104,111,118,101,114,44,108,101,115,98,105,97,110,115,117,114,110,97, +109,101,97,110,100,32,97,108,108,99,111,109,109,111,110,47,104,101,97,100,101, +114,95,95,112,97,114,97,109,115,72,97,114,118,97,114,100,47,112,105,120,101,108, +46,114,101,109,111,118,97,108,115,111,32,108,111,110,103,114,111,108,101,32,111, +102,106,111,105,110,116,108,121,115,107,121,115,99,114,97,85,110,105,99,111,100, +101,98,114,32,47,62,13,10,65,116,108,97,110,116,97,110,117,99,108,101,117,115,67 +,111,117,110,116,121,44,112,117,114,101,108,121,32,99,111,117,110,116,34,62,101, +97,115,105,108,121,32,98,117,105,108,100,32,97,111,110,99,108,105,99,107,97,32, +103,105,118,101,110,112,111,105,110,116,101,114,104,38,113,117,111,116,59,101, +118,101,110,116,115,32,101,108,115,101,32,123,10,100,105,116,105,111,110,115,110 +,111,119,32,116,104,101,44,32,119,105,116,104,32,109,97,110,32,119,104,111,111, +114,103,47,87,101,98,111,110,101,32,97,110,100,99,97,118,97,108,114,121,72,101, +32,100,105,101,100,115,101,97,116,116,108,101,48,48,44,48,48,48,32,123,119,105, +110,100,111,119,104,97,118,101,32,116,111,105,102,40,119,105,110,100,97,110,100, +32,105,116,115,115,111,108,101,108,121,32,109,38,113,117,111,116,59,114,101,110, +101,119,101,100,68,101,116,114,111,105,116,97,109,111,110,103,115,116,101,105, +116,104,101,114,32,116,104,101,109,32,105,110,83,101,110,97,116,111,114,85,115, +60,47,97,62,60,75,105,110,103,32,111,102,70,114,97,110,99,105,115,45,112,114,111 +,100,117,99,104,101,32,117,115,101,100,97,114,116,32,97,110,100,104,105,109,32, +97,110,100,117,115,101,100,32,98,121,115,99,111,114,105,110,103,97,116,32,104, +111,109,101,116,111,32,104,97,118,101,114,101,108,97,116,101,115,105,98,105,108, +105,116,121,102,97,99,116,105,111,110,66,117,102,102,97,108,111,108,105,110,107, +34,62,60,119,104,97,116,32,104,101,102,114,101,101,32,116,111,67,105,116,121,32, +111,102,99,111,109,101,32,105,110,115,101,99,116,111,114,115,99,111,117,110,116, +101,100,111,110,101,32,100,97,121,110,101,114,118,111,117,115,115,113,117,97,114 +,101,32,125,59,105,102,40,103,111,105,110,32,119,104,97,116,105,109,103,34,32,97 +,108,105,115,32,111,110,108,121,115,101,97,114,99,104,47,116,117,101,115,100,97, +121,108,111,111,115,101,108,121,83,111,108,111,109,111,110,115,101,120,117,97, +108,32,45,32,60,97,32,104,114,109,101,100,105,117,109,34,68,79,32,78,79,84,32,70 +,114,97,110,99,101,44,119,105,116,104,32,97,32,119,97,114,32,97,110,100,115,101, +99,111,110,100,32,116,97,107,101,32,97,32,62,13,10,13,10,13,10,109,97,114,107, +101,116,46,104,105,103,104,119,97,121,100,111,110,101,32,105,110,99,116,105,118, +105,116,121,34,108,97,115,116,34,62,111,98,108,105,103,101,100,114,105,115,101, +32,116,111,34,117,110,100,101,102,105,109,97,100,101,32,116,111,32,69,97,114,108 +,121,32,112,114,97,105,115,101,100,105,110,32,105,116,115,32,102,111,114,32,104, +105,115,97,116,104,108,101,116,101,74,117,112,105,116,101,114,89,97,104,111,111, +33,32,116,101,114,109,101,100,32,115,111,32,109,97,110,121,114,101,97,108,108, +121,32,115,46,32,84,104,101,32,97,32,119,111,109,97,110,63,118,97,108,117,101,61 +,100,105,114,101,99,116,32,114,105,103,104,116,34,32,98,105,99,121,99,108,101,97 +,99,105,110,103,61,34,100,97,121,32,97,110,100,115,116,97,116,105,110,103,82,97, +116,104,101,114,44,104,105,103,104,101,114,32,79,102,102,105,99,101,32,97,114, +101,32,110,111,119,116,105,109,101,115,44,32,119,104,101,110,32,97,32,112,97,121 +,32,102,111,114,111,110,32,116,104,105,115,45,108,105,110,107,34,62,59,98,111, +114,100,101,114,97,114,111,117,110,100,32,97,110,110,117,97,108,32,116,104,101, +32,78,101,119,112,117,116,32,116,104,101,46,99,111,109,34,32,116,97,107,105,110, +32,116,111,97,32,98,114,105,101,102,40,105,110,32,116,104,101,103,114,111,117, +112,115,46,59,32,119,105,100,116,104,101,110,122,121,109,101,115,115,105,109,112 +,108,101,32,105,110,32,108,97,116,101,123,114,101,116,117,114,110,116,104,101, +114,97,112,121,97,32,112,111,105,110,116,98,97,110,110,105,110,103,105,110,107, +115,34,62,10,40,41,59,34,32,114,101,97,32,112,108,97,99,101,92,117,48,48,51,67, +97,97,98,111,117,116,32,97,116,114,62,13,10,9,9,99,99,111,117,110,116,32,103,105 +,118,101,115,32,97,60,83,67,82,73,80,84,82,97,105,108,119,97,121,116,104,101,109 +,101,115,47,116,111,111,108,98,111,120,66,121,73,100,40,34,120,104,117,109,97, +110,115,44,119,97,116,99,104,101,115,105,110,32,115,111,109,101,32,105,102,32,40 +,119,105,99,111,109,105,110,103,32,102,111,114,109,97,116,115,32,85,110,100,101, +114,32,98,117,116,32,104,97,115,104,97,110,100,101,100,32,109,97,100,101,32,98, +121,116,104,97,110,32,105,110,102,101,97,114,32,111,102,100,101,110,111,116,101, +100,47,105,102,114,97,109,101,108,101,102,116,32,105,110,118,111,108,116,97,103, +101,105,110,32,101,97,99,104,97,38,113,117,111,116,59,98,97,115,101,32,111,102, +73,110,32,109,97,110,121,117,110,100,101,114,103,111,114,101,103,105,109,101,115 +,97,99,116,105,111,110,32,60,47,112,62,13,10,60,117,115,116,111,109,86,97,59,38, +103,116,59,60,47,105,109,112,111,114,116,115,111,114,32,116,104,97,116,109,111, +115,116,108,121,32,38,97,109,112,59,114,101,32,115,105,122,101,61,34,60,47,97,62 +,60,47,104,97,32,99,108,97,115,115,112,97,115,115,105,118,101,72,111,115,116,32, +61,32,87,104,101,116,104,101,114,102,101,114,116,105,108,101,86,97,114,105,111, +117,115,61,91,93,59,40,102,117,99,97,109,101,114,97,115,47,62,60,47,116,100,62, +97,99,116,115,32,97,115,73,110,32,115,111,109,101,62,13,10,13,10,60,33,111,114, +103,97,110,105,115,32,60,98,114,32,47,62,66,101,105,106,105,110,103,99,97,116,97 +,108,195,160,100,101,117,116,115,99,104,101,117,114,111,112,101,117,101,117,115, +107,97,114,97,103,97,101,105,108,103,101,115,118,101,110,115,107,97,101,115,112, +97,195,177,97,109,101,110,115,97,106,101,117,115,117,97,114,105,111,116,114,97, +98,97,106,111,109,195,169,120,105,99,111,112,195,161,103,105,110,97,115,105,101, +109,112,114,101,115,105,115,116,101,109,97,111,99,116,117,98,114,101,100,117,114 +,97,110,116,101,97,195,177,97,100,105,114,101,109,112,114,101,115,97,109,111,109 +,101,110,116,111,110,117,101,115,116,114,111,112,114,105,109,101,114,97,116,114, +97,118,195,169,115,103,114,97,99,105,97,115,110,117,101,115,116,114,97,112,114, +111,99,101,115,111,101,115,116,97,100,111,115,99,97,108,105,100,97,100,112,101, +114,115,111,110,97,110,195,186,109,101,114,111,97,99,117,101,114,100,111,109,195 +,186,115,105,99,97,109,105,101,109,98,114,111,111,102,101,114,116,97,115,97,108, +103,117,110,111,115,112,97,195,173,115,101,115,101,106,101,109,112,108,111,100, +101,114,101,99,104,111,97,100,101,109,195,161,115,112,114,105,118,97,100,111,97, +103,114,101,103,97,114,101,110,108,97,99,101,115,112,111,115,105,98,108,101,104, +111,116,101,108,101,115,115,101,118,105,108,108,97,112,114,105,109,101,114,111, +195,186,108,116,105,109,111,101,118,101,110,116,111,115,97,114,99,104,105,118, +111,99,117,108,116,117,114,97,109,117,106,101,114,101,115,101,110,116,114,97,100 +,97,97,110,117,110,99,105,111,101,109,98,97,114,103,111,109,101,114,99,97,100, +111,103,114,97,110,100,101,115,101,115,116,117,100,105,111,109,101,106,111,114, +101,115,102,101,98,114,101,114,111,100,105,115,101,195,177,111,116,117,114,105, +115,109,111,99,195,179,100,105,103,111,112,111,114,116,97,100,97,101,115,112,97, +99,105,111,102,97,109,105,108,105,97,97,110,116,111,110,105,111,112,101,114,109, +105,116,101,103,117,97,114,100,97,114,97,108,103,117,110,97,115,112,114,101,99, +105,111,115,97,108,103,117,105,101,110,115,101,110,116,105,100,111,118,105,115, +105,116,97,115,116,195,173,116,117,108,111,99,111,110,111,99,101,114,115,101,103 +,117,110,100,111,99,111,110,115,101,106,111,102,114,97,110,99,105,97,109,105,110 +,117,116,111,115,115,101,103,117,110,100,97,116,101,110,101,109,111,115,101,102, +101,99,116,111,115,109,195,161,108,97,103,97,115,101,115,105,195,179,110,114,101 +,118,105,115,116,97,103,114,97,110,97,100,97,99,111,109,112,114,97,114,105,110, +103,114,101,115,111,103,97,114,99,195,173,97,97,99,99,105,195,179,110,101,99,117 +,97,100,111,114,113,117,105,101,110,101,115,105,110,99,108,117,115,111,100,101, +98,101,114,195,161,109,97,116,101,114,105,97,104,111,109,98,114,101,115,109,117, +101,115,116,114,97,112,111,100,114,195,173,97,109,97,195,177,97,110,97,195,186, +108,116,105,109,97,101,115,116,97,109,111,115,111,102,105,99,105,97,108,116,97, +109,98,105,101,110,110,105,110,103,195,186,110,115,97,108,117,100,111,115,112, +111,100,101,109,111,115,109,101,106,111,114,97,114,112,111,115,105,116,105,111, +110,98,117,115,105,110,101,115,115,104,111,109,101,112,97,103,101,115,101,99,117 +,114,105,116,121,108,97,110,103,117,97,103,101,115,116,97,110,100,97,114,100,99, +97,109,112,97,105,103,110,102,101,97,116,117,114,101,115,99,97,116,101,103,111, +114,121,101,120,116,101,114,110,97,108,99,104,105,108,100,114,101,110,114,101, +115,101,114,118,101,100,114,101,115,101,97,114,99,104,101,120,99,104,97,110,103, +101,102,97,118,111,114,105,116,101,116,101,109,112,108,97,116,101,109,105,108, +105,116,97,114,121,105,110,100,117,115,116,114,121,115,101,114,118,105,99,101, +115,109,97,116,101,114,105,97,108,112,114,111,100,117,99,116,115,122,45,105,110, +100,101,120,58,99,111,109,109,101,110,116,115,115,111,102,116,119,97,114,101,99, +111,109,112,108,101,116,101,99,97,108,101,110,100,97,114,112,108,97,116,102,111, +114,109,97,114,116,105,99,108,101,115,114,101,113,117,105,114,101,100,109,111, +118,101,109,101,110,116,113,117,101,115,116,105,111,110,98,117,105,108,100,105, +110,103,112,111,108,105,116,105,99,115,112,111,115,115,105,98,108,101,114,101, +108,105,103,105,111,110,112,104,121,115,105,99,97,108,102,101,101,100,98,97,99, +107,114,101,103,105,115,116,101,114,112,105,99,116,117,114,101,115,100,105,115, +97,98,108,101,100,112,114,111,116,111,99,111,108,97,117,100,105,101,110,99,101, +115,101,116,116,105,110,103,115,97,99,116,105,118,105,116,121,101,108,101,109, +101,110,116,115,108,101,97,114,110,105,110,103,97,110,121,116,104,105,110,103,97 +,98,115,116,114,97,99,116,112,114,111,103,114,101,115,115,111,118,101,114,118, +105,101,119,109,97,103,97,122,105,110,101,101,99,111,110,111,109,105,99,116,114, +97,105,110,105,110,103,112,114,101,115,115,117,114,101,118,97,114,105,111,117, +115,32,60,115,116,114,111,110,103,62,112,114,111,112,101,114,116,121,115,104,111 +,112,112,105,110,103,116,111,103,101,116,104,101,114,97,100,118,97,110,99,101, +100,98,101,104,97,118,105,111,114,100,111,119,110,108,111,97,100,102,101,97,116, +117,114,101,100,102,111,111,116,98,97,108,108,115,101,108,101,99,116,101,100,76, +97,110,103,117,97,103,101,100,105,115,116,97,110,99,101,114,101,109,101,109,98, +101,114,116,114,97,99,107,105,110,103,112,97,115,115,119,111,114,100,109,111,100 +,105,102,105,101,100,115,116,117,100,101,110,116,115,100,105,114,101,99,116,108, +121,102,105,103,104,116,105,110,103,110,111,114,116,104,101,114,110,100,97,116, +97,98,97,115,101,102,101,115,116,105,118,97,108,98,114,101,97,107,105,110,103, +108,111,99,97,116,105,111,110,105,110,116,101,114,110,101,116,100,114,111,112, +100,111,119,110,112,114,97,99,116,105,99,101,101,118,105,100,101,110,99,101,102, +117,110,99,116,105,111,110,109,97,114,114,105,97,103,101,114,101,115,112,111,110 +,115,101,112,114,111,98,108,101,109,115,110,101,103,97,116,105,118,101,112,114, +111,103,114,97,109,115,97,110,97,108,121,115,105,115,114,101,108,101,97,115,101, +100,98,97,110,110,101,114,34,62,112,117,114,99,104,97,115,101,112,111,108,105,99 +,105,101,115,114,101,103,105,111,110,97,108,99,114,101,97,116,105,118,101,97,114 +,103,117,109,101,110,116,98,111,111,107,109,97,114,107,114,101,102,101,114,114, +101,114,99,104,101,109,105,99,97,108,100,105,118,105,115,105,111,110,99,97,108, +108,98,97,99,107,115,101,112,97,114,97,116,101,112,114,111,106,101,99,116,115,99 +,111,110,102,108,105,99,116,104,97,114,100,119,97,114,101,105,110,116,101,114, +101,115,116,100,101,108,105,118,101,114,121,109,111,117,110,116,97,105,110,111, +98,116,97,105,110,101,100,61,32,102,97,108,115,101,59,102,111,114,40,118,97,114, +32,97,99,99,101,112,116,101,100,99,97,112,97,99,105,116,121,99,111,109,112,117, +116,101,114,105,100,101,110,116,105,116,121,97,105,114,99,114,97,102,116,101,109 +,112,108,111,121,101,100,112,114,111,112,111,115,101,100,100,111,109,101,115,116 +,105,99,105,110,99,108,117,100,101,115,112,114,111,118,105,100,101,100,104,111, +115,112,105,116,97,108,118,101,114,116,105,99,97,108,99,111,108,108,97,112,115, +101,97,112,112,114,111,97,99,104,112,97,114,116,110,101,114,115,108,111,103,111, +34,62,60,97,100,97,117,103,104,116,101,114,97,117,116,104,111,114,34,32,99,117, +108,116,117,114,97,108,102,97,109,105,108,105,101,115,47,105,109,97,103,101,115, +47,97,115,115,101,109,98,108,121,112,111,119,101,114,102,117,108,116,101,97,99, +104,105,110,103,102,105,110,105,115,104,101,100,100,105,115,116,114,105,99,116, +99,114,105,116,105,99,97,108,99,103,105,45,98,105,110,47,112,117,114,112,111,115 +,101,115,114,101,113,117,105,114,101,115,101,108,101,99,116,105,111,110,98,101, +99,111,109,105,110,103,112,114,111,118,105,100,101,115,97,99,97,100,101,109,105, +99,101,120,101,114,99,105,115,101,97,99,116,117,97,108,108,121,109,101,100,105, +99,105,110,101,99,111,110,115,116,97,110,116,97,99,99,105,100,101,110,116,77,97, +103,97,122,105,110,101,100,111,99,117,109,101,110,116,115,116,97,114,116,105,110 +,103,98,111,116,116,111,109,34,62,111,98,115,101,114,118,101,100,58,32,38,113, +117,111,116,59,101,120,116,101,110,100,101,100,112,114,101,118,105,111,117,115, +83,111,102,116,119,97,114,101,99,117,115,116,111,109,101,114,100,101,99,105,115, +105,111,110,115,116,114,101,110,103,116,104,100,101,116,97,105,108,101,100,115, +108,105,103,104,116,108,121,112,108,97,110,110,105,110,103,116,101,120,116,97, +114,101,97,99,117,114,114,101,110,99,121,101,118,101,114,121,111,110,101,115,116 +,114,97,105,103,104,116,116,114,97,110,115,102,101,114,112,111,115,105,116,105, +118,101,112,114,111,100,117,99,101,100,104,101,114,105,116,97,103,101,115,104, +105,112,112,105,110,103,97,98,115,111,108,117,116,101,114,101,99,101,105,118,101 +,100,114,101,108,101,118,97,110,116,98,117,116,116,111,110,34,32,118,105,111,108 +,101,110,99,101,97,110,121,119,104,101,114,101,98,101,110,101,102,105,116,115, +108,97,117,110,99,104,101,100,114,101,99,101,110,116,108,121,97,108,108,105,97, +110,99,101,102,111,108,108,111,119,101,100,109,117,108,116,105,112,108,101,98, +117,108,108,101,116,105,110,105,110,99,108,117,100,101,100,111,99,99,117,114,114 +,101,100,105,110,116,101,114,110,97,108,36,40,116,104,105,115,41,46,114,101,112, +117,98,108,105,99,62,60,116,114,62,60,116,100,99,111,110,103,114,101,115,115,114 +,101,99,111,114,100,101,100,117,108,116,105,109,97,116,101,115,111,108,117,116, +105,111,110,60,117,108,32,105,100,61,34,100,105,115,99,111,118,101,114,72,111, +109,101,60,47,97,62,119,101,98,115,105,116,101,115,110,101,116,119,111,114,107, +115,97,108,116,104,111,117,103,104,101,110,116,105,114,101,108,121,109,101,109, +111,114,105,97,108,109,101,115,115,97,103,101,115,99,111,110,116,105,110,117,101 +,97,99,116,105,118,101,34,62,115,111,109,101,119,104,97,116,118,105,99,116,111, +114,105,97,87,101,115,116,101,114,110,32,32,116,105,116,108,101,61,34,76,111,99, +97,116,105,111,110,99,111,110,116,114,97,99,116,118,105,115,105,116,111,114,115, +68,111,119,110,108,111,97,100,119,105,116,104,111,117,116,32,114,105,103,104,116 +,34,62,10,109,101,97,115,117,114,101,115,119,105,100,116,104,32,61,32,118,97,114 +,105,97,98,108,101,105,110,118,111,108,118,101,100,118,105,114,103,105,110,105, +97,110,111,114,109,97,108,108,121,104,97,112,112,101,110,101,100,97,99,99,111, +117,110,116,115,115,116,97,110,100,105,110,103,110,97,116,105,111,110,97,108,82, +101,103,105,115,116,101,114,112,114,101,112,97,114,101,100,99,111,110,116,114, +111,108,115,97,99,99,117,114,97,116,101,98,105,114,116,104,100,97,121,115,116, +114,97,116,101,103,121,111,102,102,105,99,105,97,108,103,114,97,112,104,105,99, +115,99,114,105,109,105,110,97,108,112,111,115,115,105,98,108,121,99,111,110,115, +117,109,101,114,80,101,114,115,111,110,97,108,115,112,101,97,107,105,110,103,118 +,97,108,105,100,97,116,101,97,99,104,105,101,118,101,100,46,106,112,103,34,32,47 +,62,109,97,99,104,105,110,101,115,60,47,104,50,62,10,32,32,107,101,121,119,111, +114,100,115,102,114,105,101,110,100,108,121,98,114,111,116,104,101,114,115,99, +111,109,98,105,110,101,100,111,114,105,103,105,110,97,108,99,111,109,112,111,115 +,101,100,101,120,112,101,99,116,101,100,97,100,101,113,117,97,116,101,112,97,107 +,105,115,116,97,110,102,111,108,108,111,119,34,32,118,97,108,117,97,98,108,101, +60,47,108,97,98,101,108,62,114,101,108,97,116,105,118,101,98,114,105,110,103,105 +,110,103,105,110,99,114,101,97,115,101,103,111,118,101,114,110,111,114,112,108, +117,103,105,110,115,47,76,105,115,116,32,111,102,32,72,101,97,100,101,114,34,62, +34,32,110,97,109,101,61,34,32,40,38,113,117,111,116,59,103,114,97,100,117,97,116 +,101,60,47,104,101,97,100,62,10,99,111,109,109,101,114,99,101,109,97,108,97,121, +115,105,97,100,105,114,101,99,116,111,114,109,97,105,110,116,97,105,110,59,104, +101,105,103,104,116,58,115,99,104,101,100,117,108,101,99,104,97,110,103,105,110, +103,98,97,99,107,32,116,111,32,99,97,116,104,111,108,105,99,112,97,116,116,101, +114,110,115,99,111,108,111,114,58,32,35,103,114,101,97,116,101,115,116,115,117, +112,112,108,105,101,115,114,101,108,105,97,98,108,101,60,47,117,108,62,10,9,9,60 +,115,101,108,101,99,116,32,99,105,116,105,122,101,110,115,99,108,111,116,104,105 +,110,103,119,97,116,99,104,105,110,103,60,108,105,32,105,100,61,34,115,112,101, +99,105,102,105,99,99,97,114,114,121,105,110,103,115,101,110,116,101,110,99,101, +60,99,101,110,116,101,114,62,99,111,110,116,114,97,115,116,116,104,105,110,107, +105,110,103,99,97,116,99,104,40,101,41,115,111,117,116,104,101,114,110,77,105,99 +,104,97,101,108,32,109,101,114,99,104,97,110,116,99,97,114,111,117,115,101,108, +112,97,100,100,105,110,103,58,105,110,116,101,114,105,111,114,46,115,112,108,105 +,116,40,34,108,105,122,97,116,105,111,110,79,99,116,111,98,101,114,32,41,123,114 +,101,116,117,114,110,105,109,112,114,111,118,101,100,45,45,38,103,116,59,10,10, +99,111,118,101,114,97,103,101,99,104,97,105,114,109,97,110,46,112,110,103,34,32, +47,62,115,117,98,106,101,99,116,115,82,105,99,104,97,114,100,32,119,104,97,116, +101,118,101,114,112,114,111,98,97,98,108,121,114,101,99,111,118,101,114,121,98, +97,115,101,98,97,108,108,106,117,100,103,109,101,110,116,99,111,110,110,101,99, +116,46,46,99,115,115,34,32,47,62,32,119,101,98,115,105,116,101,114,101,112,111, +114,116,101,100,100,101,102,97,117,108,116,34,47,62,60,47,97,62,13,10,101,108, +101,99,116,114,105,99,115,99,111,116,108,97,110,100,99,114,101,97,116,105,111, +110,113,117,97,110,116,105,116,121,46,32,73,83,66,78,32,48,100,105,100,32,110, +111,116,32,105,110,115,116,97,110,99,101,45,115,101,97,114,99,104,45,34,32,108, +97,110,103,61,34,115,112,101,97,107,101,114,115,67,111,109,112,117,116,101,114, +99,111,110,116,97,105,110,115,97,114,99,104,105,118,101,115,109,105,110,105,115, +116,101,114,114,101,97,99,116,105,111,110,100,105,115,99,111,117,110,116,73,116, +97,108,105,97,110,111,99,114,105,116,101,114,105,97,115,116,114,111,110,103,108, +121,58,32,39,104,116,116,112,58,39,115,99,114,105,112,116,39,99,111,118,101,114, +105,110,103,111,102,102,101,114,105,110,103,97,112,112,101,97,114,101,100,66,114 +,105,116,105,115,104,32,105,100,101,110,116,105,102,121,70,97,99,101,98,111,111, +107,110,117,109,101,114,111,117,115,118,101,104,105,99,108,101,115,99,111,110,99 +,101,114,110,115,65,109,101,114,105,99,97,110,104,97,110,100,108,105,110,103,100 +,105,118,32,105,100,61,34,87,105,108,108,105,97,109,32,112,114,111,118,105,100, +101,114,95,99,111,110,116,101,110,116,97,99,99,117,114,97,99,121,115,101,99,116, +105,111,110,32,97,110,100,101,114,115,111,110,102,108,101,120,105,98,108,101,67, +97,116,101,103,111,114,121,108,97,119,114,101,110,99,101,60,115,99,114,105,112, +116,62,108,97,121,111,117,116,61,34,97,112,112,114,111,118,101,100,32,109,97,120 +,105,109,117,109,104,101,97,100,101,114,34,62,60,47,116,97,98,108,101,62,83,101, +114,118,105,99,101,115,104,97,109,105,108,116,111,110,99,117,114,114,101,110,116 +,32,99,97,110,97,100,105,97,110,99,104,97,110,110,101,108,115,47,116,104,101,109 +,101,115,47,47,97,114,116,105,99,108,101,111,112,116,105,111,110,97,108,112,111, +114,116,117,103,97,108,118,97,108,117,101,61,34,34,105,110,116,101,114,118,97, +108,119,105,114,101,108,101,115,115,101,110,116,105,116,108,101,100,97,103,101, +110,99,105,101,115,83,101,97,114,99,104,34,32,109,101,97,115,117,114,101,100,116 +,104,111,117,115,97,110,100,115,112,101,110,100,105,110,103,38,104,101,108,108, +105,112,59,110,101,119,32,68,97,116,101,34,32,115,105,122,101,61,34,112,97,103, +101,78,97,109,101,109,105,100,100,108,101,34,32,34,32,47,62,60,47,97,62,104,105, +100,100,101,110,34,62,115,101,113,117,101,110,99,101,112,101,114,115,111,110,97, +108,111,118,101,114,102,108,111,119,111,112,105,110,105,111,110,115,105,108,108, +105,110,111,105,115,108,105,110,107,115,34,62,10,9,60,116,105,116,108,101,62,118 +,101,114,115,105,111,110,115,115,97,116,117,114,100,97,121,116,101,114,109,105, +110,97,108,105,116,101,109,112,114,111,112,101,110,103,105,110,101,101,114,115, +101,99,116,105,111,110,115,100,101,115,105,103,110,101,114,112,114,111,112,111, +115,97,108,61,34,102,97,108,115,101,34,69,115,112,97,195,177,111,108,114,101,108 +,101,97,115,101,115,115,117,98,109,105,116,34,32,101,114,38,113,117,111,116,59, +97,100,100,105,116,105,111,110,115,121,109,112,116,111,109,115,111,114,105,101, +110,116,101,100,114,101,115,111,117,114,99,101,114,105,103,104,116,34,62,60,112, +108,101,97,115,117,114,101,115,116,97,116,105,111,110,115,104,105,115,116,111, +114,121,46,108,101,97,118,105,110,103,32,32,98,111,114,100,101,114,61,99,111,110 +,116,101,110,116,115,99,101,110,116,101,114,34,62,46,10,10,83,111,109,101,32,100 +,105,114,101,99,116,101,100,115,117,105,116,97,98,108,101,98,117,108,103,97,114, +105,97,46,115,104,111,119,40,41,59,100,101,115,105,103,110,101,100,71,101,110, +101,114,97,108,32,99,111,110,99,101,112,116,115,69,120,97,109,112,108,101,115, +119,105,108,108,105,97,109,115,79,114,105,103,105,110,97,108,34,62,60,115,112,97 +,110,62,115,101,97,114,99,104,34,62,111,112,101,114,97,116,111,114,114,101,113, +117,101,115,116,115,97,32,38,113,117,111,116,59,97,108,108,111,119,105,110,103, +68,111,99,117,109,101,110,116,114,101,118,105,115,105,111,110,46,32,10,10,84,104 +,101,32,121,111,117,114,115,101,108,102,67,111,110,116,97,99,116,32,109,105,99, +104,105,103,97,110,69,110,103,108,105,115,104,32,99,111,108,117,109,98,105,97, +112,114,105,111,114,105,116,121,112,114,105,110,116,105,110,103,100,114,105,110, +107,105,110,103,102,97,99,105,108,105,116,121,114,101,116,117,114,110,101,100,67 +,111,110,116,101,110,116,32,111,102,102,105,99,101,114,115,82,117,115,115,105,97 +,110,32,103,101,110,101,114,97,116,101,45,56,56,53,57,45,49,34,105,110,100,105, +99,97,116,101,102,97,109,105,108,105,97,114,32,113,117,97,108,105,116,121,109,97 +,114,103,105,110,58,48,32,99,111,110,116,101,110,116,118,105,101,119,112,111,114 +,116,99,111,110,116,97,99,116,115,45,116,105,116,108,101,34,62,112,111,114,116, +97,98,108,101,46,108,101,110,103,116,104,32,101,108,105,103,105,98,108,101,105, +110,118,111,108,118,101,115,97,116,108,97,110,116,105,99,111,110,108,111,97,100, +61,34,100,101,102,97,117,108,116,46,115,117,112,112,108,105,101,100,112,97,121, +109,101,110,116,115,103,108,111,115,115,97,114,121,10,10,65,102,116,101,114,32, +103,117,105,100,97,110,99,101,60,47,116,100,62,60,116,100,101,110,99,111,100,105 +,110,103,109,105,100,100,108,101,34,62,99,97,109,101,32,116,111,32,100,105,115, +112,108,97,121,115,115,99,111,116,116,105,115,104,106,111,110,97,116,104,97,110, +109,97,106,111,114,105,116,121,119,105,100,103,101,116,115,46,99,108,105,110,105 +,99,97,108,116,104,97,105,108,97,110,100,116,101,97,99,104,101,114,115,60,104, +101,97,100,62,10,9,97,102,102,101,99,116,101,100,115,117,112,112,111,114,116,115 +,112,111,105,110,116,101,114,59,116,111,83,116,114,105,110,103,60,47,115,109,97, +108,108,62,111,107,108,97,104,111,109,97,119,105,108,108,32,98,101,32,105,110, +118,101,115,116,111,114,48,34,32,97,108,116,61,34,104,111,108,105,100,97,121,115 +,82,101,115,111,117,114,99,101,108,105,99,101,110,115,101,100,32,40,119,104,105, +99,104,32,46,32,65,102,116,101,114,32,99,111,110,115,105,100,101,114,118,105,115 +,105,116,105,110,103,101,120,112,108,111,114,101,114,112,114,105,109,97,114,121, +32,115,101,97,114,99,104,34,32,97,110,100,114,111,105,100,34,113,117,105,99,107, +108,121,32,109,101,101,116,105,110,103,115,101,115,116,105,109,97,116,101,59,114 +,101,116,117,114,110,32,59,99,111,108,111,114,58,35,32,104,101,105,103,104,116, +61,97,112,112,114,111,118,97,108,44,32,38,113,117,111,116,59,32,99,104,101,99, +107,101,100,46,109,105,110,46,106,115,34,109,97,103,110,101,116,105,99,62,60,47, +97,62,60,47,104,102,111,114,101,99,97,115,116,46,32,87,104,105,108,101,32,116, +104,117,114,115,100,97,121,100,118,101,114,116,105,115,101,38,101,97,99,117,116, +101,59,104,97,115,67,108,97,115,115,101,118,97,108,117,97,116,101,111,114,100, +101,114,105,110,103,101,120,105,115,116,105,110,103,112,97,116,105,101,110,116, +115,32,79,110,108,105,110,101,32,99,111,108,111,114,97,100,111,79,112,116,105, +111,110,115,34,99,97,109,112,98,101,108,108,60,33,45,45,32,101,110,100,60,47,115 +,112,97,110,62,60,60,98,114,32,47,62,13,10,95,112,111,112,117,112,115,124,115,99 +,105,101,110,99,101,115,44,38,113,117,111,116,59,32,113,117,97,108,105,116,121, +32,87,105,110,100,111,119,115,32,97,115,115,105,103,110,101,100,104,101,105,103, +104,116,58,32,60,98,32,99,108,97,115,115,108,101,38,113,117,111,116,59,32,118,97 +,108,117,101,61,34,32,67,111,109,112,97,110,121,101,120,97,109,112,108,101,115, +60,105,102,114,97,109,101,32,98,101,108,105,101,118,101,115,112,114,101,115,101, +110,116,115,109,97,114,115,104,97,108,108,112,97,114,116,32,111,102,32,112,114, +111,112,101,114,108,121,41,46,10,10,84,104,101,32,116,97,120,111,110,111,109,121 +,109,117,99,104,32,111,102,32,60,47,115,112,97,110,62,10,34,32,100,97,116,97,45, +115,114,116,117,103,117,195,170,115,115,99,114,111,108,108,84,111,32,112,114,111 +,106,101,99,116,60,104,101,97,100,62,13,10,97,116,116,111,114,110,101,121,101, +109,112,104,97,115,105,115,115,112,111,110,115,111,114,115,102,97,110,99,121,98, +111,120,119,111,114,108,100,39,115,32,119,105,108,100,108,105,102,101,99,104,101 +,99,107,101,100,61,115,101,115,115,105,111,110,115,112,114,111,103,114,97,109, +109,112,120,59,102,111,110,116,45,32,80,114,111,106,101,99,116,106,111,117,114, +110,97,108,115,98,101,108,105,101,118,101,100,118,97,99,97,116,105,111,110,116, +104,111,109,112,115,111,110,108,105,103,104,116,105,110,103,97,110,100,32,116, +104,101,32,115,112,101,99,105,97,108,32,98,111,114,100,101,114,61,48,99,104,101, +99,107,105,110,103,60,47,116,98,111,100,121,62,60,98,117,116,116,111,110,32,67, +111,109,112,108,101,116,101,99,108,101,97,114,102,105,120,10,60,104,101,97,100, +62,10,97,114,116,105,99,108,101,32,60,115,101,99,116,105,111,110,102,105,110,100 +,105,110,103,115,114,111,108,101,32,105,110,32,112,111,112,117,108,97,114,32,32, +79,99,116,111,98,101,114,119,101,98,115,105,116,101,32,101,120,112,111,115,117, +114,101,117,115,101,100,32,116,111,32,32,99,104,97,110,103,101,115,111,112,101, +114,97,116,101,100,99,108,105,99,107,105,110,103,101,110,116,101,114,105,110,103 +,99,111,109,109,97,110,100,115,105,110,102,111,114,109,101,100,32,110,117,109,98 +,101,114,115,32,32,60,47,100,105,118,62,99,114,101,97,116,105,110,103,111,110,83 +,117,98,109,105,116,109,97,114,121,108,97,110,100,99,111,108,108,101,103,101,115 +,97,110,97,108,121,116,105,99,108,105,115,116,105,110,103,115,99,111,110,116,97, +99,116,46,108,111,103,103,101,100,73,110,97,100,118,105,115,111,114,121,115,105, +98,108,105,110,103,115,99,111,110,116,101,110,116,34,115,38,113,117,111,116,59, +41,115,46,32,84,104,105,115,32,112,97,99,107,97,103,101,115,99,104,101,99,107,98 +,111,120,115,117,103,103,101,115,116,115,112,114,101,103,110,97,110,116,116,111, +109,111,114,114,111,119,115,112,97,99,105,110,103,61,105,99,111,110,46,112,110, +103,106,97,112,97,110,101,115,101,99,111,100,101,98,97,115,101,98,117,116,116, +111,110,34,62,103,97,109,98,108,105,110,103,115,117,99,104,32,97,115,32,44,32, +119,104,105,108,101,32,60,47,115,112,97,110,62,32,109,105,115,115,111,117,114, +105,115,112,111,114,116,105,110,103,116,111,112,58,49,112,120,32,46,60,47,115, +112,97,110,62,116,101,110,115,105,111,110,115,119,105,100,116,104,61,34,50,108, +97,122,121,108,111,97,100,110,111,118,101,109,98,101,114,117,115,101,100,32,105, +110,32,104,101,105,103,104,116,61,34,99,114,105,112,116,34,62,10,38,110,98,115, +112,59,60,47,60,116,114,62,60,116,100,32,104,101,105,103,104,116,58,50,47,112, +114,111,100,117,99,116,99,111,117,110,116,114,121,32,105,110,99,108,117,100,101, +32,102,111,111,116,101,114,34,32,38,108,116,59,33,45,45,32,116,105,116,108,101, +34,62,60,47,106,113,117,101,114,121,46,60,47,102,111,114,109,62,10,40,231,174, +128,228,189,147,41,40,231,185,129,233,171,148,41,104,114,118,97,116,115,107,105, +105,116,97,108,105,97,110,111,114,111,109,195,162,110,196,131,116,195,188,114, +107,195,167,101,216,167,216,177,216,175,217,136,116,97,109,98,105,195,169,110, +110,111,116,105,99,105,97,115,109,101,110,115,97,106,101,115,112,101,114,115,111 +,110,97,115,100,101,114,101,99,104,111,115,110,97,99,105,111,110,97,108,115,101, +114,118,105,99,105,111,99,111,110,116,97,99,116,111,117,115,117,97,114,105,111, +115,112,114,111,103,114,97,109,97,103,111,98,105,101,114,110,111,101,109,112,114 +,101,115,97,115,97,110,117,110,99,105,111,115,118,97,108,101,110,99,105,97,99, +111,108,111,109,98,105,97,100,101,115,112,117,195,169,115,100,101,112,111,114, +116,101,115,112,114,111,121,101,99,116,111,112,114,111,100,117,99,116,111,112, +195,186,98,108,105,99,111,110,111,115,111,116,114,111,115,104,105,115,116,111, +114,105,97,112,114,101,115,101,110,116,101,109,105,108,108,111,110,101,115,109, +101,100,105,97,110,116,101,112,114,101,103,117,110,116,97,97,110,116,101,114,105 +,111,114,114,101,99,117,114,115,111,115,112,114,111,98,108,101,109,97,115,97,110 +,116,105,97,103,111,110,117,101,115,116,114,111,115,111,112,105,110,105,195,179, +110,105,109,112,114,105,109,105,114,109,105,101,110,116,114,97,115,97,109,195, +169,114,105,99,97,118,101,110,100,101,100,111,114,115,111,99,105,101,100,97,100, +114,101,115,112,101,99,116,111,114,101,97,108,105,122,97,114,114,101,103,105,115 +,116,114,111,112,97,108,97,98,114,97,115,105,110,116,101,114,195,169,115,101,110 +,116,111,110,99,101,115,101,115,112,101,99,105,97,108,109,105,101,109,98,114,111 +,115,114,101,97,108,105,100,97,100,99,195,179,114,100,111,98,97,122,97,114,97, +103,111,122,97,112,195,161,103,105,110,97,115,115,111,99,105,97,108,101,115,98, +108,111,113,117,101,97,114,103,101,115,116,105,195,179,110,97,108,113,117,105, +108,101,114,115,105,115,116,101,109,97,115,99,105,101,110,99,105,97,115,99,111, +109,112,108,101,116,111,118,101,114,115,105,195,179,110,99,111,109,112,108,101, +116,97,101,115,116,117,100,105,111,115,112,195,186,98,108,105,99,97,111,98,106, +101,116,105,118,111,97,108,105,99,97,110,116,101,98,117,115,99,97,100,111,114,99 +,97,110,116,105,100,97,100,101,110,116,114,97,100,97,115,97,99,99,105,111,110, +101,115,97,114,99,104,105,118,111,115,115,117,112,101,114,105,111,114,109,97,121 +,111,114,195,173,97,97,108,101,109,97,110,105,97,102,117,110,99,105,195,179,110, +195,186,108,116,105,109,111,115,104,97,99,105,101,110,100,111,97,113,117,101,108 +,108,111,115,101,100,105,99,105,195,179,110,102,101,114,110,97,110,100,111,97, +109,98,105,101,110,116,101,102,97,99,101,98,111,111,107,110,117,101,115,116,114, +97,115,99,108,105,101,110,116,101,115,112,114,111,99,101,115,111,115,98,97,115, +116,97,110,116,101,112,114,101,115,101,110,116,97,114,101,112,111,114,116,97,114 +,99,111,110,103,114,101,115,111,112,117,98,108,105,99,97,114,99,111,109,101,114, +99,105,111,99,111,110,116,114,97,116,111,106,195,179,118,101,110,101,115,100,105 +,115,116,114,105,116,111,116,195,169,99,110,105,99,97,99,111,110,106,117,110,116 +,111,101,110,101,114,103,195,173,97,116,114,97,98,97,106,97,114,97,115,116,117, +114,105,97,115,114,101,99,105,101,110,116,101,117,116,105,108,105,122,97,114,98, +111,108,101,116,195,173,110,115,97,108,118,97,100,111,114,99,111,114,114,101,99, +116,97,116,114,97,98,97,106,111,115,112,114,105,109,101,114,111,115,110,101,103, +111,99,105,111,115,108,105,98,101,114,116,97,100,100,101,116,97,108,108,101,115, +112,97,110,116,97,108,108,97,112,114,195,179,120,105,109,111,97,108,109,101,114, +195,173,97,97,110,105,109,97,108,101,115,113,117,105,195,169,110,101,115,99,111, +114,97,122,195,179,110,115,101,99,99,105,195,179,110,98,117,115,99,97,110,100, +111,111,112,99,105,111,110,101,115,101,120,116,101,114,105,111,114,99,111,110,99 +,101,112,116,111,116,111,100,97,118,195,173,97,103,97,108,101,114,195,173,97,101 +,115,99,114,105,98,105,114,109,101,100,105,99,105,110,97,108,105,99,101,110,99, +105,97,99,111,110,115,117,108,116,97,97,115,112,101,99,116,111,115,99,114,195, +173,116,105,99,97,100,195,179,108,97,114,101,115,106,117,115,116,105,99,105,97, +100,101,98,101,114,195,161,110,112,101,114,195,173,111,100,111,110,101,99,101, +115,105,116,97,109,97,110,116,101,110,101,114,112,101,113,117,101,195,177,111, +114,101,99,105,98,105,100,97,116,114,105,98,117,110,97,108,116,101,110,101,114, +105,102,101,99,97,110,99,105,195,179,110,99,97,110,97,114,105,97,115,100,101,115 +,99,97,114,103,97,100,105,118,101,114,115,111,115,109,97,108,108,111,114,99,97, +114,101,113,117,105,101,114,101,116,195,169,99,110,105,99,111,100,101,98,101,114 +,195,173,97,118,105,118,105,101,110,100,97,102,105,110,97,110,122,97,115,97,100, +101,108,97,110,116,101,102,117,110,99,105,111,110,97,99,111,110,115,101,106,111, +115,100,105,102,195,173,99,105,108,99,105,117,100,97,100,101,115,97,110,116,105, +103,117,97,115,97,118,97,110,122,97,100,97,116,195,169,114,109,105,110,111,117, +110,105,100,97,100,101,115,115,195,161,110,99,104,101,122,99,97,109,112,97,195, +177,97,115,111,102,116,111,110,105,99,114,101,118,105,115,116,97,115,99,111,110, +116,105,101,110,101,115,101,99,116,111,114,101,115,109,111,109,101,110,116,111, +115,102,97,99,117,108,116,97,100,99,114,195,169,100,105,116,111,100,105,118,101, +114,115,97,115,115,117,112,117,101,115,116,111,102,97,99,116,111,114,101,115,115 +,101,103,117,110,100,111,115,112,101,113,117,101,195,177,97,208,179,208,190,208, +180,208,176,208,181,209,129,208,187,208,184,208,181,209,129,209,130,209,140,208, +177,209,139,208,187,208,190,208,177,209,139,209,130,209,140,209,141,209,130,208, +190,208,188,208,149,209,129,208,187,208,184,209,130,208,190,208,179,208,190,208, +188,208,181,208,189,209,143,208,178,209,129,208,181,209,133,209,141,209,130,208, +190,208,185,208,180,208,176,208,182,208,181,208,177,209,139,208,187,208,184,208, +179,208,190,208,180,209,131,208,180,208,181,208,189,209,140,209,141,209,130,208, +190,209,130,208,177,209,139,208,187,208,176,209,129,208,181,208,177,209,143,208, +190,208,180,208,184,208,189,209,129,208,181,208,177,208,181,208,189,208,176,208, +180,208,190,209,129,208,176,208,185,209,130,209,132,208,190,209,130,208,190,208, +189,208,181,208,179,208,190,209,129,208,178,208,190,208,184,209,129,208,178,208, +190,208,185,208,184,208,179,209,128,209,139,209,130,208,190,208,182,208,181,208, +178,209,129,208,181,208,188,209,129,208,178,208,190,209,142,208,187,208,184,209, +136,209,140,209,141,209,130,208,184,209,133,208,191,208,190,208,186,208,176,208, +180,208,189,208,181,208,185,208,180,208,190,208,188,208,176,208,188,208,184,209, +128,208,176,208,187,208,184,208,177,208,190,209,130,208,181,208,188,209,131,209, +133,208,190,209,130,209,143,208,180,208,178,209,131,209,133,209,129,208,181,209, +130,208,184,208,187,209,142,208,180,208,184,208,180,208,181,208,187,208,190,208, +188,208,184,209,128,208,181,209,130,208,181,208,177,209,143,209,129,208,178,208, +190,208,181,208,178,208,184,208,180,208,181,209,135,208,181,208,179,208,190,209, +141,209,130,208,184,208,188,209,129,209,135,208,181,209,130,209,130,208,181,208, +188,209,139,209,134,208,181,208,189,209,139,209,129,209,130,208,176,208,187,208, +178,208,181,208,180,209,140,209,130,208,181,208,188,208,181,208,178,208,190,208, +180,209,139,209,130,208,181,208,177,208,181,208,178,209,139,209,136,208,181,208, +189,208,176,208,188,208,184,209,130,208,184,208,191,208,176,209,130,208,190,208, +188,209,131,208,191,209,128,208,176,208,178,208,187,208,184,209,134,208,176,208, +190,208,180,208,189,208,176,208,179,208,190,208,180,209,139,208,183,208,189,208, +176,209,142,208,188,208,190,208,179,209,131,208,180,209,128,209,131,208,179,208, +178,209,129,208,181,208,185,208,184,208,180,208,181,209,130,208,186,208,184,208, +189,208,190,208,190,208,180,208,189,208,190,208,180,208,181,208,187,208,176,208, +180,208,181,208,187,208,181,209,129,209,128,208,190,208,186,208,184,209,142,208, +189,209,143,208,178,208,181,209,129,209,140,208,149,209,129,209,130,209,140,209, +128,208,176,208,183,208,176,208,189,208,176,209,136,208,184,216,167,217,132,217, +132,217,135,216,167,217,132,216,170,217,138,216,172,217,133,217,138,216,185,216, +174,216,167,216,181,216,169,216,167,217,132,216,176,217,138,216,185,217,132,217, +138,217,135,216,172,216,175,217,138,216,175,216,167,217,132,216,162,217,134,216, +167,217,132,216,177,216,175,216,170,216,173,217,131,217,133,216,181,217,129,216, +173,216,169,217,131,216,167,217,134,216,170,216,167,217,132,217,132,217,138,217, +138,217,131,217,136,217,134,216,180,216,168,217,131,216,169,217,129,217,138,217, +135,216,167,216,168,217,134,216,167,216,170,216,173,217,136,216,167,216,161,216, +163,217,131,216,171,216,177,216,174,217,132,216,167,217,132,216,167,217,132,216, +173,216,168,216,175,217,132,217,138,217,132,216,175,216,177,217,136,216,179,216, +167,216,182,216,186,216,183,216,170,217,131,217,136,217,134,217,135,217,134,216, +167,217,131,216,179,216,167,216,173,216,169,217,134,216,167,216,175,217,138,216, +167,217,132,216,183,216,168,216,185,217,132,217,138,217,131,216,180,217,131,216, +177,216,167,217,138,217,133,217,131,217,134,217,133,217,134,217,135,216,167,216, +180,216,177,217,131,216,169,216,177,216,166,217,138,216,179,217,134,216,180,217, +138,216,183,217,133,216,167,216,176,216,167,216,167,217,132,217,129,217,134,216, +180,216,168,216,167,216,168,216,170,216,185,216,168,216,177,216,177,216,173,217, +133,216,169,217,131,216,167,217,129,216,169,217,138,217,130,217,136,217,132,217, +133,216,177,217,131,216,178,217,131,217,132,217,133,216,169,216,163,216,173,217, +133,216,175,217,130,217,132,216,168,217,138,217,138,216,185,217,134,217,138,216, +181,217,136,216,177,216,169,216,183,216,177,217,138,217,130,216,180,216,167,216, +177,217,131,216,172,217,136,216,167,217,132,216,163,216,174,216,177,217,137,217, +133,216,185,217,134,216,167,216,167,216,168,216,173,216,171,216,185,216,177,217, +136,216,182,216,168,216,180,217,131,217,132,217,133,216,179,216,172,217,132,216, +168,217,134,216,167,217,134,216,174,216,167,217,132,216,175,217,131,216,170,216, +167,216,168,217,131,217,132,217,138,216,169,216,168,216,175,217,136,217,134,216, +163,217,138,216,182,216,167,217,138,217,136,216,172,216,175,217,129,216,177,217, +138,217,130,217,131,216,170,216,168,216,170,216,163,217,129,216,182,217,132,217, +133,216,183,216,168,216,174,216,167,217,131,216,171,216,177,216,168,216,167,216, +177,217,131,216,167,217,129,216,182,217,132,216,167,216,173,217,132,217,137,217, +134,217,129,216,179,217,135,216,163,217,138,216,167,217,133,216,177,216,175,217, +136,216,175,216,163,217,134,217,135,216,167,216,175,217,138,217,134,216,167,216, +167,217,132,216,167,217,134,217,133,216,185,216,177,216,182,216,170,216,185,217, +132,217,133,216,175,216,167,216,174,217,132,217,133,217,133,217,131,217,134,0,0, +0,0,0,0,0,0,1,0,1,0,1,0,1,0,2,0,2,0,2,0,2,0,4,0,4,0,4,0,4,0,0,1,2,3,4,5,6,7,7,6, +5,4,3,2,1,0,8,9,10,11,12,13,14,15,15,14,13,12,11,10,9,8,16,17,18,19,20,21,22,23, +23,22,21,20,19,18,17,16,24,25,26,27,28,29,30,31,31,30,29,28,27,26,25,24,255,255, +255,255,0,0,0,0,0,0,0,0,255,255,255,255,1,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0, +3,0,0,0,255,255,0,1,0,0,0,1,0,0,255,255,0,1,0,0,0,8,0,8,0,8,0,8,0,0,0,1,0,2,0,3, +0,4,0,5,0,6,0,7,114,101,115,111,117,114,99,101,115,99,111,117,110,116,114,105, +101,115,113,117,101,115,116,105,111,110,115,101,113,117,105,112,109,101,110,116, +99,111,109,109,117,110,105,116,121,97,118,97,105,108,97,98,108,101,104,105,103, +104,108,105,103,104,116,68,84,68,47,120,104,116,109,108,109,97,114,107,101,116, +105,110,103,107,110,111,119,108,101,100,103,101,115,111,109,101,116,104,105,110, +103,99,111,110,116,97,105,110,101,114,100,105,114,101,99,116,105,111,110,115,117 +,98,115,99,114,105,98,101,97,100,118,101,114,116,105,115,101,99,104,97,114,97,99 +,116,101,114,34,32,118,97,108,117,101,61,34,60,47,115,101,108,101,99,116,62,65, +117,115,116,114,97,108,105,97,34,32,99,108,97,115,115,61,34,115,105,116,117,97, +116,105,111,110,97,117,116,104,111,114,105,116,121,102,111,108,108,111,119,105, +110,103,112,114,105,109,97,114,105,108,121,111,112,101,114,97,116,105,111,110,99 +,104,97,108,108,101,110,103,101,100,101,118,101,108,111,112,101,100,97,110,111, +110,121,109,111,117,115,102,117,110,99,116,105,111,110,32,102,117,110,99,116,105 +,111,110,115,99,111,109,112,97,110,105,101,115,115,116,114,117,99,116,117,114, +101,97,103,114,101,101,109,101,110,116,34,32,116,105,116,108,101,61,34,112,111, +116,101,110,116,105,97,108,101,100,117,99,97,116,105,111,110,97,114,103,117,109, +101,110,116,115,115,101,99,111,110,100,97,114,121,99,111,112,121,114,105,103,104 +,116,108,97,110,103,117,97,103,101,115,101,120,99,108,117,115,105,118,101,99,111 +,110,100,105,116,105,111,110,60,47,102,111,114,109,62,13,10,115,116,97,116,101, +109,101,110,116,97,116,116,101,110,116,105,111,110,66,105,111,103,114,97,112,104 +,121,125,32,101,108,115,101,32,123,10,115,111,108,117,116,105,111,110,115,119, +104,101,110,32,116,104,101,32,65,110,97,108,121,116,105,99,115,116,101,109,112, +108,97,116,101,115,100,97,110,103,101,114,111,117,115,115,97,116,101,108,108,105 +,116,101,100,111,99,117,109,101,110,116,115,112,117,98,108,105,115,104,101,114, +105,109,112,111,114,116,97,110,116,112,114,111,116,111,116,121,112,101,105,110, +102,108,117,101,110,99,101,38,114,97,113,117,111,59,60,47,101,102,102,101,99,116 +,105,118,101,103,101,110,101,114,97,108,108,121,116,114,97,110,115,102,111,114, +109,98,101,97,117,116,105,102,117,108,116,114,97,110,115,112,111,114,116,111,114 +,103,97,110,105,122,101,100,112,117,98,108,105,115,104,101,100,112,114,111,109, +105,110,101,110,116,117,110,116,105,108,32,116,104,101,116,104,117,109,98,110,97 +,105,108,78,97,116,105,111,110,97,108,32,46,102,111,99,117,115,40,41,59,111,118, +101,114,32,116,104,101,32,109,105,103,114,97,116,105,111,110,97,110,110,111,117, +110,99,101,100,102,111,111,116,101,114,34,62,10,101,120,99,101,112,116,105,111, +110,108,101,115,115,32,116,104,97,110,101,120,112,101,110,115,105,118,101,102, +111,114,109,97,116,105,111,110,102,114,97,109,101,119,111,114,107,116,101,114, +114,105,116,111,114,121,110,100,105,99,97,116,105,111,110,99,117,114,114,101,110 +,116,108,121,99,108,97,115,115,78,97,109,101,99,114,105,116,105,99,105,115,109, +116,114,97,100,105,116,105,111,110,101,108,115,101,119,104,101,114,101,65,108, +101,120,97,110,100,101,114,97,112,112,111,105,110,116,101,100,109,97,116,101,114 +,105,97,108,115,98,114,111,97,100,99,97,115,116,109,101,110,116,105,111,110,101, +100,97,102,102,105,108,105,97,116,101,60,47,111,112,116,105,111,110,62,116,114, +101,97,116,109,101,110,116,100,105,102,102,101,114,101,110,116,47,100,101,102,97 +,117,108,116,46,80,114,101,115,105,100,101,110,116,111,110,99,108,105,99,107,61, +34,98,105,111,103,114,97,112,104,121,111,116,104,101,114,119,105,115,101,112,101 +,114,109,97,110,101,110,116,70,114,97,110,195,167,97,105,115,72,111,108,108,121, +119,111,111,100,101,120,112,97,110,115,105,111,110,115,116,97,110,100,97,114,100 +,115,60,47,115,116,121,108,101,62,10,114,101,100,117,99,116,105,111,110,68,101, +99,101,109,98,101,114,32,112,114,101,102,101,114,114,101,100,67,97,109,98,114, +105,100,103,101,111,112,112,111,110,101,110,116,115,66,117,115,105,110,101,115, +115,32,99,111,110,102,117,115,105,111,110,62,10,60,116,105,116,108,101,62,112, +114,101,115,101,110,116,101,100,101,120,112,108,97,105,110,101,100,100,111,101, +115,32,110,111,116,32,119,111,114,108,100,119,105,100,101,105,110,116,101,114, +102,97,99,101,112,111,115,105,116,105,111,110,115,110,101,119,115,112,97,112,101 +,114,60,47,116,97,98,108,101,62,10,109,111,117,110,116,97,105,110,115,108,105, +107,101,32,116,104,101,32,101,115,115,101,110,116,105,97,108,102,105,110,97,110, +99,105,97,108,115,101,108,101,99,116,105,111,110,97,99,116,105,111,110,61,34,47, +97,98,97,110,100,111,110,101,100,69,100,117,99,97,116,105,111,110,112,97,114,115 +,101,73,110,116,40,115,116,97,98,105,108,105,116,121,117,110,97,98,108,101,32, +116,111,60,47,116,105,116,108,101,62,10,114,101,108,97,116,105,111,110,115,78, +111,116,101,32,116,104,97,116,101,102,102,105,99,105,101,110,116,112,101,114,102 +,111,114,109,101,100,116,119,111,32,121,101,97,114,115,83,105,110,99,101,32,116, +104,101,116,104,101,114,101,102,111,114,101,119,114,97,112,112,101,114,34,62,97, +108,116,101,114,110,97,116,101,105,110,99,114,101,97,115,101,100,66,97,116,116, +108,101,32,111,102,112,101,114,99,101,105,118,101,100,116,114,121,105,110,103,32 +,116,111,110,101,99,101,115,115,97,114,121,112,111,114,116,114,97,121,101,100, +101,108,101,99,116,105,111,110,115,69,108,105,122,97,98,101,116,104,60,47,105, +102,114,97,109,101,62,100,105,115,99,111,118,101,114,121,105,110,115,117,114,97, +110,99,101,115,46,108,101,110,103,116,104,59,108,101,103,101,110,100,97,114,121, +71,101,111,103,114,97,112,104,121,99,97,110,100,105,100,97,116,101,99,111,114, +112,111,114,97,116,101,115,111,109,101,116,105,109,101,115,115,101,114,118,105, +99,101,115,46,105,110,104,101,114,105,116,101,100,60,47,115,116,114,111,110,103, +62,67,111,109,109,117,110,105,116,121,114,101,108,105,103,105,111,117,115,108, +111,99,97,116,105,111,110,115,67,111,109,109,105,116,116,101,101,98,117,105,108, +100,105,110,103,115,116,104,101,32,119,111,114,108,100,110,111,32,108,111,110, +103,101,114,98,101,103,105,110,110,105,110,103,114,101,102,101,114,101,110,99, +101,99,97,110,110,111,116,32,98,101,102,114,101,113,117,101,110,99,121,116,121, +112,105,99,97,108,108,121,105,110,116,111,32,116,104,101,32,114,101,108,97,116, +105,118,101,59,114,101,99,111,114,100,105,110,103,112,114,101,115,105,100,101, +110,116,105,110,105,116,105,97,108,108,121,116,101,99,104,110,105,113,117,101, +116,104,101,32,111,116,104,101,114,105,116,32,99,97,110,32,98,101,101,120,105, +115,116,101,110,99,101,117,110,100,101,114,108,105,110,101,116,104,105,115,32, +116,105,109,101,116,101,108,101,112,104,111,110,101,105,116,101,109,115,99,111, +112,101,112,114,97,99,116,105,99,101,115,97,100,118,97,110,116,97,103,101,41,59, +114,101,116,117,114,110,32,70,111,114,32,111,116,104,101,114,112,114,111,118,105 +,100,105,110,103,100,101,109,111,99,114,97,99,121,98,111,116,104,32,116,104,101, +32,101,120,116,101,110,115,105,118,101,115,117,102,102,101,114,105,110,103,115, +117,112,112,111,114,116,101,100,99,111,109,112,117,116,101,114,115,32,102,117, +110,99,116,105,111,110,112,114,97,99,116,105,99,97,108,115,97,105,100,32,116,104 +,97,116,105,116,32,109,97,121,32,98,101,69,110,103,108,105,115,104,60,47,102,114 +,111,109,32,116,104,101,32,115,99,104,101,100,117,108,101,100,100,111,119,110, +108,111,97,100,115,60,47,108,97,98,101,108,62,10,115,117,115,112,101,99,116,101, +100,109,97,114,103,105,110,58,32,48,115,112,105,114,105,116,117,97,108,60,47,104 +,101,97,100,62,10,10,109,105,99,114,111,115,111,102,116,103,114,97,100,117,97, +108,108,121,100,105,115,99,117,115,115,101,100,104,101,32,98,101,99,97,109,101, +101,120,101,99,117,116,105,118,101,106,113,117,101,114,121,46,106,115,104,111, +117,115,101,104,111,108,100,99,111,110,102,105,114,109,101,100,112,117,114,99, +104,97,115,101,100,108,105,116,101,114,97,108,108,121,100,101,115,116,114,111, +121,101,100,117,112,32,116,111,32,116,104,101,118,97,114,105,97,116,105,111,110, +114,101,109,97,105,110,105,110,103,105,116,32,105,115,32,110,111,116,99,101,110, +116,117,114,105,101,115,74,97,112,97,110,101,115,101,32,97,109,111,110,103,32, +116,104,101,99,111,109,112,108,101,116,101,100,97,108,103,111,114,105,116,104, +109,105,110,116,101,114,101,115,116,115,114,101,98,101,108,108,105,111,110,117, +110,100,101,102,105,110,101,100,101,110,99,111,117,114,97,103,101,114,101,115, +105,122,97,98,108,101,105,110,118,111,108,118,105,110,103,115,101,110,115,105, +116,105,118,101,117,110,105,118,101,114,115,97,108,112,114,111,118,105,115,105, +111,110,40,97,108,116,104,111,117,103,104,102,101,97,116,117,114,105,110,103,99, +111,110,100,117,99,116,101,100,41,44,32,119,104,105,99,104,32,99,111,110,116,105 +,110,117,101,100,45,104,101,97,100,101,114,34,62,70,101,98,114,117,97,114,121,32 +,110,117,109,101,114,111,117,115,32,111,118,101,114,102,108,111,119,58,99,111, +109,112,111,110,101,110,116,102,114,97,103,109,101,110,116,115,101,120,99,101, +108,108,101,110,116,99,111,108,115,112,97,110,61,34,116,101,99,104,110,105,99,97 +,108,110,101,97,114,32,116,104,101,32,65,100,118,97,110,99,101,100,32,115,111, +117,114,99,101,32,111,102,101,120,112,114,101,115,115,101,100,72,111,110,103,32, +75,111,110,103,32,70,97,99,101,98,111,111,107,109,117,108,116,105,112,108,101,32 +,109,101,99,104,97,110,105,115,109,101,108,101,118,97,116,105,111,110,111,102, +102,101,110,115,105,118,101,60,47,102,111,114,109,62,10,9,115,112,111,110,115, +111,114,101,100,100,111,99,117,109,101,110,116,46,111,114,32,38,113,117,111,116, +59,116,104,101,114,101,32,97,114,101,116,104,111,115,101,32,119,104,111,109,111, +118,101,109,101,110,116,115,112,114,111,99,101,115,115,101,115,100,105,102,102, +105,99,117,108,116,115,117,98,109,105,116,116,101,100,114,101,99,111,109,109,101 +,110,100,99,111,110,118,105,110,99,101,100,112,114,111,109,111,116,105,110,103, +34,32,119,105,100,116,104,61,34,46,114,101,112,108,97,99,101,40,99,108,97,115, +115,105,99,97,108,99,111,97,108,105,116,105,111,110,104,105,115,32,102,105,114, +115,116,100,101,99,105,115,105,111,110,115,97,115,115,105,115,116,97,110,116,105 +,110,100,105,99,97,116,101,100,101,118,111,108,117,116,105,111,110,45,119,114,97 +,112,112,101,114,34,101,110,111,117,103,104,32,116,111,97,108,111,110,103,32,116 +,104,101,100,101,108,105,118,101,114,101,100,45,45,62,13,10,60,33,45,45,65,109, +101,114,105,99,97,110,32,112,114,111,116,101,99,116,101,100,78,111,118,101,109, +98,101,114,32,60,47,115,116,121,108,101,62,60,102,117,114,110,105,116,117,114, +101,73,110,116,101,114,110,101,116,32,32,111,110,98,108,117,114,61,34,115,117, +115,112,101,110,100,101,100,114,101,99,105,112,105,101,110,116,98,97,115,101,100 +,32,111,110,32,77,111,114,101,111,118,101,114,44,97,98,111,108,105,115,104,101, +100,99,111,108,108,101,99,116,101,100,119,101,114,101,32,109,97,100,101,101,109, +111,116,105,111,110,97,108,101,109,101,114,103,101,110,99,121,110,97,114,114,97, +116,105,118,101,97,100,118,111,99,97,116,101,115,112,120,59,98,111,114,100,101, +114,99,111,109,109,105,116,116,101,100,100,105,114,61,34,108,116,114,34,101,109, +112,108,111,121,101,101,115,114,101,115,101,97,114,99,104,46,32,115,101,108,101, +99,116,101,100,115,117,99,99,101,115,115,111,114,99,117,115,116,111,109,101,114, +115,100,105,115,112,108,97,121,101,100,83,101,112,116,101,109,98,101,114,97,100, +100,67,108,97,115,115,40,70,97,99,101,98,111,111,107,32,115,117,103,103,101,115, +116,101,100,97,110,100,32,108,97,116,101,114,111,112,101,114,97,116,105,110,103, +101,108,97,98,111,114,97,116,101,83,111,109,101,116,105,109,101,115,73,110,115, +116,105,116,117,116,101,99,101,114,116,97,105,110,108,121,105,110,115,116,97,108 +,108,101,100,102,111,108,108,111,119,101,114,115,74,101,114,117,115,97,108,101, +109,116,104,101,121,32,104,97,118,101,99,111,109,112,117,116,105,110,103,103,101 +,110,101,114,97,116,101,100,112,114,111,118,105,110,99,101,115,103,117,97,114,97 +,110,116,101,101,97,114,98,105,116,114,97,114,121,114,101,99,111,103,110,105,122 +,101,119,97,110,116,101,100,32,116,111,112,120,59,119,105,100,116,104,58,116,104 +,101,111,114,121,32,111,102,98,101,104,97,118,105,111,117,114,87,104,105,108,101 +,32,116,104,101,101,115,116,105,109,97,116,101,100,98,101,103,97,110,32,116,111, +32,105,116,32,98,101,99,97,109,101,109,97,103,110,105,116,117,100,101,109,117, +115,116,32,104,97,118,101,109,111,114,101,32,116,104,97,110,68,105,114,101,99, +116,111,114,121,101,120,116,101,110,115,105,111,110,115,101,99,114,101,116,97, +114,121,110,97,116,117,114,97,108,108,121,111,99,99,117,114,114,105,110,103,118, +97,114,105,97,98,108,101,115,103,105,118,101,110,32,116,104,101,112,108,97,116, +102,111,114,109,46,60,47,108,97,98,101,108,62,60,102,97,105,108,101,100,32,116, +111,99,111,109,112,111,117,110,100,115,107,105,110,100,115,32,111,102,32,115,111 +,99,105,101,116,105,101,115,97,108,111,110,103,115,105,100,101,32,45,45,38,103, +116,59,10,10,115,111,117,116,104,119,101,115,116,116,104,101,32,114,105,103,104, +116,114,97,100,105,97,116,105,111,110,109,97,121,32,104,97,118,101,32,117,110, +101,115,99,97,112,101,40,115,112,111,107,101,110,32,105,110,34,32,104,114,101, +102,61,34,47,112,114,111,103,114,97,109,109,101,111,110,108,121,32,116,104,101, +32,99,111,109,101,32,102,114,111,109,100,105,114,101,99,116,111,114,121,98,117, +114,105,101,100,32,105,110,97,32,115,105,109,105,108,97,114,116,104,101,121,32, +119,101,114,101,60,47,102,111,110,116,62,60,47,78,111,114,119,101,103,105,97,110 +,115,112,101,99,105,102,105,101,100,112,114,111,100,117,99,105,110,103,112,97, +115,115,101,110,103,101,114,40,110,101,119,32,68,97,116,101,116,101,109,112,111, +114,97,114,121,102,105,99,116,105,111,110,97,108,65,102,116,101,114,32,116,104, +101,101,113,117,97,116,105,111,110,115,100,111,119,110,108,111,97,100,46,114,101 +,103,117,108,97,114,108,121,100,101,118,101,108,111,112,101,114,97,98,111,118, +101,32,116,104,101,108,105,110,107,101,100,32,116,111,112,104,101,110,111,109, +101,110,97,112,101,114,105,111,100,32,111,102,116,111,111,108,116,105,112,34,62, +115,117,98,115,116,97,110,99,101,97,117,116,111,109,97,116,105,99,97,115,112,101 +,99,116,32,111,102,65,109,111,110,103,32,116,104,101,99,111,110,110,101,99,116, +101,100,101,115,116,105,109,97,116,101,115,65,105,114,32,70,111,114,99,101,115, +121,115,116,101,109,32,111,102,111,98,106,101,99,116,105,118,101,105,109,109,101 +,100,105,97,116,101,109,97,107,105,110,103,32,105,116,112,97,105,110,116,105,110 +,103,115,99,111,110,113,117,101,114,101,100,97,114,101,32,115,116,105,108,108, +112,114,111,99,101,100,117,114,101,103,114,111,119,116,104,32,111,102,104,101,97 +,100,101,100,32,98,121,69,117,114,111,112,101,97,110,32,100,105,118,105,115,105, +111,110,115,109,111,108,101,99,117,108,101,115,102,114,97,110,99,104,105,115,101 +,105,110,116,101,110,116,105,111,110,97,116,116,114,97,99,116,101,100,99,104,105 +,108,100,104,111,111,100,97,108,115,111,32,117,115,101,100,100,101,100,105,99,97 +,116,101,100,115,105,110,103,97,112,111,114,101,100,101,103,114,101,101,32,111, +102,102,97,116,104,101,114,32,111,102,99,111,110,102,108,105,99,116,115,60,47,97 +,62,60,47,112,62,10,99,97,109,101,32,102,114,111,109,119,101,114,101,32,117,115, +101,100,110,111,116,101,32,116,104,97,116,114,101,99,101,105,118,105,110,103,69, +120,101,99,117,116,105,118,101,101,118,101,110,32,109,111,114,101,97,99,99,101, +115,115,32,116,111,99,111,109,109,97,110,100,101,114,80,111,108,105,116,105,99, +97,108,109,117,115,105,99,105,97,110,115,100,101,108,105,99,105,111,117,115,112, +114,105,115,111,110,101,114,115,97,100,118,101,110,116,32,111,102,85,84,70,45,56 +,34,32,47,62,60,33,91,67,68,65,84,65,91,34,62,67,111,110,116,97,99,116,83,111, +117,116,104,101,114,110,32,98,103,99,111,108,111,114,61,34,115,101,114,105,101, +115,32,111,102,46,32,73,116,32,119,97,115,32,105,110,32,69,117,114,111,112,101, +112,101,114,109,105,116,116,101,100,118,97,108,105,100,97,116,101,46,97,112,112, +101,97,114,105,110,103,111,102,102,105,99,105,97,108,115,115,101,114,105,111,117 +,115,108,121,45,108,97,110,103,117,97,103,101,105,110,105,116,105,97,116,101,100 +,101,120,116,101,110,100,105,110,103,108,111,110,103,45,116,101,114,109,105,110, +102,108,97,116,105,111,110,115,117,99,104,32,116,104,97,116,103,101,116,67,111, +111,107,105,101,109,97,114,107,101,100,32,98,121,60,47,98,117,116,116,111,110,62 +,105,109,112,108,101,109,101,110,116,98,117,116,32,105,116,32,105,115,105,110,99 +,114,101,97,115,101,115,100,111,119,110,32,116,104,101,32,114,101,113,117,105, +114,105,110,103,100,101,112,101,110,100,101,110,116,45,45,62,10,60,33,45,45,32, +105,110,116,101,114,118,105,101,119,87,105,116,104,32,116,104,101,32,99,111,112, +105,101,115,32,111,102,99,111,110,115,101,110,115,117,115,119,97,115,32,98,117, +105,108,116,86,101,110,101,122,117,101,108,97,40,102,111,114,109,101,114,108,121 +,116,104,101,32,115,116,97,116,101,112,101,114,115,111,110,110,101,108,115,116, +114,97,116,101,103,105,99,102,97,118,111,117,114,32,111,102,105,110,118,101,110, +116,105,111,110,87,105,107,105,112,101,100,105,97,99,111,110,116,105,110,101,110 +,116,118,105,114,116,117,97,108,108,121,119,104,105,99,104,32,119,97,115,112,114 +,105,110,99,105,112,108,101,67,111,109,112,108,101,116,101,32,105,100,101,110, +116,105,99,97,108,115,104,111,119,32,116,104,97,116,112,114,105,109,105,116,105, +118,101,97,119,97,121,32,102,114,111,109,109,111,108,101,99,117,108,97,114,112, +114,101,99,105,115,101,108,121,100,105,115,115,111,108,118,101,100,85,110,100, +101,114,32,116,104,101,118,101,114,115,105,111,110,61,34,62,38,110,98,115,112,59 +,60,47,73,116,32,105,115,32,116,104,101,32,84,104,105,115,32,105,115,32,119,105, +108,108,32,104,97,118,101,111,114,103,97,110,105,115,109,115,115,111,109,101,32, +116,105,109,101,70,114,105,101,100,114,105,99,104,119,97,115,32,102,105,114,115, +116,116,104,101,32,111,110,108,121,32,102,97,99,116,32,116,104,97,116,102,111, +114,109,32,105,100,61,34,112,114,101,99,101,100,105,110,103,84,101,99,104,110, +105,99,97,108,112,104,121,115,105,99,105,115,116,111,99,99,117,114,115,32,105, +110,110,97,118,105,103,97,116,111,114,115,101,99,116,105,111,110,34,62,115,112, +97,110,32,105,100,61,34,115,111,117,103,104,116,32,116,111,98,101,108,111,119,32 +,116,104,101,115,117,114,118,105,118,105,110,103,125,60,47,115,116,121,108,101, +62,104,105,115,32,100,101,97,116,104,97,115,32,105,110,32,116,104,101,99,97,117, +115,101,100,32,98,121,112,97,114,116,105,97,108,108,121,101,120,105,115,116,105, +110,103,32,117,115,105,110,103,32,116,104,101,119,97,115,32,103,105,118,101,110, +97,32,108,105,115,116,32,111,102,108,101,118,101,108,115,32,111,102,110,111,116, +105,111,110,32,111,102,79,102,102,105,99,105,97,108,32,100,105,115,109,105,115, +115,101,100,115,99,105,101,110,116,105,115,116,114,101,115,101,109,98,108,101, +115,100,117,112,108,105,99,97,116,101,101,120,112,108,111,115,105,118,101,114, +101,99,111,118,101,114,101,100,97,108,108,32,111,116,104,101,114,103,97,108,108, +101,114,105,101,115,123,112,97,100,100,105,110,103,58,112,101,111,112,108,101,32 +,111,102,114,101,103,105,111,110,32,111,102,97,100,100,114,101,115,115,101,115, +97,115,115,111,99,105,97,116,101,105,109,103,32,97,108,116,61,34,105,110,32,109, +111,100,101,114,110,115,104,111,117,108,100,32,98,101,109,101,116,104,111,100,32 +,111,102,114,101,112,111,114,116,105,110,103,116,105,109,101,115,116,97,109,112, +110,101,101,100,101,100,32,116,111,116,104,101,32,71,114,101,97,116,114,101,103, +97,114,100,105,110,103,115,101,101,109,101,100,32,116,111,118,105,101,119,101, +100,32,97,115,105,109,112,97,99,116,32,111,110,105,100,101,97,32,116,104,97,116, +116,104,101,32,87,111,114,108,100,104,101,105,103,104,116,32,111,102,101,120,112 +,97,110,100,105,110,103,84,104,101,115,101,32,97,114,101,99,117,114,114,101,110, +116,34,62,99,97,114,101,102,117,108,108,121,109,97,105,110,116,97,105,110,115,99 +,104,97,114,103,101,32,111,102,67,108,97,115,115,105,99,97,108,97,100,100,114, +101,115,115,101,100,112,114,101,100,105,99,116,101,100,111,119,110,101,114,115, +104,105,112,60,100,105,118,32,105,100,61,34,114,105,103,104,116,34,62,13,10,114, +101,115,105,100,101,110,99,101,108,101,97,118,101,32,116,104,101,99,111,110,116, +101,110,116,34,62,97,114,101,32,111,102,116,101,110,32,32,125,41,40,41,59,13,10, +112,114,111,98,97,98,108,121,32,80,114,111,102,101,115,115,111,114,45,98,117,116 +,116,111,110,34,32,114,101,115,112,111,110,100,101,100,115,97,121,115,32,116,104 +,97,116,104,97,100,32,116,111,32,98,101,112,108,97,99,101,100,32,105,110,72,117, +110,103,97,114,105,97,110,115,116,97,116,117,115,32,111,102,115,101,114,118,101, +115,32,97,115,85,110,105,118,101,114,115,97,108,101,120,101,99,117,116,105,111, +110,97,103,103,114,101,103,97,116,101,102,111,114,32,119,104,105,99,104,105,110, +102,101,99,116,105,111,110,97,103,114,101,101,100,32,116,111,104,111,119,101,118 +,101,114,44,32,112,111,112,117,108,97,114,34,62,112,108,97,99,101,100,32,111,110 +,99,111,110,115,116,114,117,99,116,101,108,101,99,116,111,114,97,108,115,121,109 +,98,111,108,32,111,102,105,110,99,108,117,100,105,110,103,114,101,116,117,114, +110,32,116,111,97,114,99,104,105,116,101,99,116,67,104,114,105,115,116,105,97, +110,112,114,101,118,105,111,117,115,32,108,105,118,105,110,103,32,105,110,101,97 +,115,105,101,114,32,116,111,112,114,111,102,101,115,115,111,114,10,38,108,116,59 +,33,45,45,32,101,102,102,101,99,116,32,111,102,97,110,97,108,121,116,105,99,115, +119,97,115,32,116,97,107,101,110,119,104,101,114,101,32,116,104,101,116,111,111, +107,32,111,118,101,114,98,101,108,105,101,102,32,105,110,65,102,114,105,107,97, +97,110,115,97,115,32,102,97,114,32,97,115,112,114,101,118,101,110,116,101,100, +119,111,114,107,32,119,105,116,104,97,32,115,112,101,99,105,97,108,60,102,105, +101,108,100,115,101,116,67,104,114,105,115,116,109,97,115,82,101,116,114,105,101 +,118,101,100,10,10,73,110,32,116,104,101,32,98,97,99,107,32,105,110,116,111,110, +111,114,116,104,101,97,115,116,109,97,103,97,122,105,110,101,115,62,60,115,116, +114,111,110,103,62,99,111,109,109,105,116,116,101,101,103,111,118,101,114,110, +105,110,103,103,114,111,117,112,115,32,111,102,115,116,111,114,101,100,32,105, +110,101,115,116,97,98,108,105,115,104,97,32,103,101,110,101,114,97,108,105,116, +115,32,102,105,114,115,116,116,104,101,105,114,32,111,119,110,112,111,112,117, +108,97,116,101,100,97,110,32,111,98,106,101,99,116,67,97,114,105,98,98,101,97, +110,97,108,108,111,119,32,116,104,101,100,105,115,116,114,105,99,116,115,119,105 +,115,99,111,110,115,105,110,108,111,99,97,116,105,111,110,46,59,32,119,105,100, +116,104,58,32,105,110,104,97,98,105,116,101,100,83,111,99,105,97,108,105,115,116 +,74,97,110,117,97,114,121,32,49,60,47,102,111,111,116,101,114,62,115,105,109,105 +,108,97,114,108,121,99,104,111,105,99,101,32,111,102,116,104,101,32,115,97,109, +101,32,115,112,101,99,105,102,105,99,32,98,117,115,105,110,101,115,115,32,84,104 +,101,32,102,105,114,115,116,46,108,101,110,103,116,104,59,32,100,101,115,105,114 +,101,32,116,111,100,101,97,108,32,119,105,116,104,115,105,110,99,101,32,116,104, +101,117,115,101,114,65,103,101,110,116,99,111,110,99,101,105,118,101,100,105,110 +,100,101,120,46,112,104,112,97,115,32,38,113,117,111,116,59,101,110,103,97,103, +101,32,105,110,114,101,99,101,110,116,108,121,44,102,101,119,32,121,101,97,114, +115,119,101,114,101,32,97,108,115,111,10,60,104,101,97,100,62,10,60,101,100,105, +116,101,100,32,98,121,97,114,101,32,107,110,111,119,110,99,105,116,105,101,115, +32,105,110,97,99,99,101,115,115,107,101,121,99,111,110,100,101,109,110,101,100, +97,108,115,111,32,104,97,118,101,115,101,114,118,105,99,101,115,44,102,97,109, +105,108,121,32,111,102,83,99,104,111,111,108,32,111,102,99,111,110,118,101,114, +116,101,100,110,97,116,117,114,101,32,111,102,32,108,97,110,103,117,97,103,101, +109,105,110,105,115,116,101,114,115,60,47,111,98,106,101,99,116,62,116,104,101, +114,101,32,105,115,32,97,32,112,111,112,117,108,97,114,115,101,113,117,101,110, +99,101,115,97,100,118,111,99,97,116,101,100,84,104,101,121,32,119,101,114,101,97 +,110,121,32,111,116,104,101,114,108,111,99,97,116,105,111,110,61,101,110,116,101 +,114,32,116,104,101,109,117,99,104,32,109,111,114,101,114,101,102,108,101,99,116 +,101,100,119,97,115,32,110,97,109,101,100,111,114,105,103,105,110,97,108,32,97, +32,116,121,112,105,99,97,108,119,104,101,110,32,116,104,101,121,101,110,103,105, +110,101,101,114,115,99,111,117,108,100,32,110,111,116,114,101,115,105,100,101, +110,116,115,119,101,100,110,101,115,100,97,121,116,104,101,32,116,104,105,114, +100,32,112,114,111,100,117,99,116,115,74,97,110,117,97,114,121,32,50,119,104,97, +116,32,116,104,101,121,97,32,99,101,114,116,97,105,110,114,101,97,99,116,105,111 +,110,115,112,114,111,99,101,115,115,111,114,97,102,116,101,114,32,104,105,115, +116,104,101,32,108,97,115,116,32,99,111,110,116,97,105,110,101,100,34,62,60,47, +100,105,118,62,10,60,47,97,62,60,47,116,100,62,100,101,112,101,110,100,32,111, +110,115,101,97,114,99,104,34,62,10,112,105,101,99,101,115,32,111,102,99,111,109, +112,101,116,105,110,103,82,101,102,101,114,101,110,99,101,116,101,110,110,101, +115,115,101,101,119,104,105,99,104,32,104,97,115,32,118,101,114,115,105,111,110, +61,60,47,115,112,97,110,62,32,60,60,47,104,101,97,100,101,114,62,103,105,118,101 +,115,32,116,104,101,104,105,115,116,111,114,105,97,110,118,97,108,117,101,61,34, +34,62,112,97,100,100,105,110,103,58,48,118,105,101,119,32,116,104,97,116,116,111 +,103,101,116,104,101,114,44,116,104,101,32,109,111,115,116,32,119,97,115,32,102, +111,117,110,100,115,117,98,115,101,116,32,111,102,97,116,116,97,99,107,32,111, +110,99,104,105,108,100,114,101,110,44,112,111,105,110,116,115,32,111,102,112,101 +,114,115,111,110,97,108,32,112,111,115,105,116,105,111,110,58,97,108,108,101,103 +,101,100,108,121,67,108,101,118,101,108,97,110,100,119,97,115,32,108,97,116,101, +114,97,110,100,32,97,102,116,101,114,97,114,101,32,103,105,118,101,110,119,97, +115,32,115,116,105,108,108,115,99,114,111,108,108,105,110,103,100,101,115,105, +103,110,32,111,102,109,97,107,101,115,32,116,104,101,109,117,99,104,32,108,101, +115,115,65,109,101,114,105,99,97,110,115,46,10,10,65,102,116,101,114,32,44,32,98 +,117,116,32,116,104,101,77,117,115,101,117,109,32,111,102,108,111,117,105,115, +105,97,110,97,40,102,114,111,109,32,116,104,101,109,105,110,110,101,115,111,116, +97,112,97,114,116,105,99,108,101,115,97,32,112,114,111,99,101,115,115,68,111,109 +,105,110,105,99,97,110,118,111,108,117,109,101,32,111,102,114,101,116,117,114, +110,105,110,103,100,101,102,101,110,115,105,118,101,48,48,112,120,124,114,105, +103,104,109,97,100,101,32,102,114,111,109,109,111,117,115,101,111,118,101,114,34 +,32,115,116,121,108,101,61,34,115,116,97,116,101,115,32,111,102,40,119,104,105, +99,104,32,105,115,99,111,110,116,105,110,117,101,115,70,114,97,110,99,105,115,99 +,111,98,117,105,108,100,105,110,103,32,119,105,116,104,111,117,116,32,97,119,105 +,116,104,32,115,111,109,101,119,104,111,32,119,111,117,108,100,97,32,102,111,114 +,109,32,111,102,97,32,112,97,114,116,32,111,102,98,101,102,111,114,101,32,105, +116,107,110,111,119,110,32,97,115,32,32,83,101,114,118,105,99,101,115,108,111,99 +,97,116,105,111,110,32,97,110,100,32,111,102,116,101,110,109,101,97,115,117,114, +105,110,103,97,110,100,32,105,116,32,105,115,112,97,112,101,114,98,97,99,107,118 +,97,108,117,101,115,32,111,102,13,10,60,116,105,116,108,101,62,61,32,119,105,110 +,100,111,119,46,100,101,116,101,114,109,105,110,101,101,114,38,113,117,111,116, +59,32,112,108,97,121,101,100,32,98,121,97,110,100,32,101,97,114,108,121,60,47,99 +,101,110,116,101,114,62,102,114,111,109,32,116,104,105,115,116,104,101,32,116, +104,114,101,101,112,111,119,101,114,32,97,110,100,111,102,32,38,113,117,111,116, +59,105,110,110,101,114,72,84,77,76,60,97,32,104,114,101,102,61,34,121,58,105,110 +,108,105,110,101,59,67,104,117,114,99,104,32,111,102,116,104,101,32,101,118,101, +110,116,118,101,114,121,32,104,105,103,104,111,102,102,105,99,105,97,108,32,45, +104,101,105,103,104,116,58,32,99,111,110,116,101,110,116,61,34,47,99,103,105,45, +98,105,110,47,116,111,32,99,114,101,97,116,101,97,102,114,105,107,97,97,110,115, +101,115,112,101,114,97,110,116,111,102,114,97,110,195,167,97,105,115,108,97,116, +118,105,101,197,161,117,108,105,101,116,117,118,105,197,179,196,140,101,197,161, +116,105,110,97,196,141,101,197,161,116,105,110,97,224,185,132,224,184,151,224, +184,162,230,151,165,230,156,172,232,170,158,231,174,128,228,189,147,229,173,151, +231,185,129,233,171,148,229,173,151,237,149,156,234,181,173,236,150,180,228,184, +186,228,187,128,228,185,136,232,174,161,231,174,151,230,156,186,231,172,148,232, +174,176,230,156,172,232,168,142,232,171,150,229,141,128,230,156,141,229,138,161, +229,153,168,228,186,146,232,129,148,231,189,145,230,136,191,229,156,176,228,186, +167,228,191,177,228,185,144,233,131,168,229,135,186,231,137,136,231,164,190,230, +142,146,232,161,140,230,166,156,233,131,168,232,144,189,230,160,188,232,191,155, +228,184,128,230,173,165,230,148,175,228,187,152,229,174,157,233,170,140,232,175, +129,231,160,129,229,167,148,229,145,152,228,188,154,230,149,176,230,141,174,229, +186,147,230,182,136,232,180,185,232,128,133,229,138,158,229,133,172,229,174,164, +232,174,168,232,174,186,229,140,186,230,183,177,229,156,179,229,184,130,230,146, +173,230,148,190,229,153,168,229,140,151,228,186,172,229,184,130,229,164,167,229, +173,166,231,148,159,232,182,138,230,157,165,232,182,138,231,174,161,231,144,134, +229,145,152,228,191,161,230,129,175,231,189,145,115,101,114,118,105,99,105,111, +115,97,114,116,195,173,99,117,108,111,97,114,103,101,110,116,105,110,97,98,97, +114,99,101,108,111,110,97,99,117,97,108,113,117,105,101,114,112,117,98,108,105, +99,97,100,111,112,114,111,100,117,99,116,111,115,112,111,108,195,173,116,105,99, +97,114,101,115,112,117,101,115,116,97,119,105,107,105,112,101,100,105,97,115,105 +,103,117,105,101,110,116,101,98,195,186,115,113,117,101,100,97,99,111,109,117, +110,105,100,97,100,115,101,103,117,114,105,100,97,100,112,114,105,110,99,105,112 +,97,108,112,114,101,103,117,110,116,97,115,99,111,110,116,101,110,105,100,111, +114,101,115,112,111,110,100,101,114,118,101,110,101,122,117,101,108,97,112,114, +111,98,108,101,109,97,115,100,105,99,105,101,109,98,114,101,114,101,108,97,99, +105,195,179,110,110,111,118,105,101,109,98,114,101,115,105,109,105,108,97,114, +101,115,112,114,111,121,101,99,116,111,115,112,114,111,103,114,97,109,97,115,105 +,110,115,116,105,116,117,116,111,97,99,116,105,118,105,100,97,100,101,110,99,117 +,101,110,116,114,97,101,99,111,110,111,109,195,173,97,105,109,195,161,103,101, +110,101,115,99,111,110,116,97,99,116,97,114,100,101,115,99,97,114,103,97,114,110 +,101,99,101,115,97,114,105,111,97,116,101,110,99,105,195,179,110,116,101,108,195 +,169,102,111,110,111,99,111,109,105,115,105,195,179,110,99,97,110,99,105,111,110 +,101,115,99,97,112,97,99,105,100,97,100,101,110,99,111,110,116,114,97,114,97,110 +,195,161,108,105,115,105,115,102,97,118,111,114,105,116,111,115,116,195,169,114, +109,105,110,111,115,112,114,111,118,105,110,99,105,97,101,116,105,113,117,101, +116,97,115,101,108,101,109,101,110,116,111,115,102,117,110,99,105,111,110,101, +115,114,101,115,117,108,116,97,100,111,99,97,114,195,161,99,116,101,114,112,114, +111,112,105,101,100,97,100,112,114,105,110,99,105,112,105,111,110,101,99,101,115 +,105,100,97,100,109,117,110,105,99,105,112,97,108,99,114,101,97,99,105,195,179, +110,100,101,115,99,97,114,103,97,115,112,114,101,115,101,110,99,105,97,99,111, +109,101,114,99,105,97,108,111,112,105,110,105,111,110,101,115,101,106,101,114,99 +,105,99,105,111,101,100,105,116,111,114,105,97,108,115,97,108,97,109,97,110,99, +97,103,111,110,122,195,161,108,101,122,100,111,99,117,109,101,110,116,111,112, +101,108,195,173,99,117,108,97,114,101,99,105,101,110,116,101,115,103,101,110,101 +,114,97,108,101,115,116,97,114,114,97,103,111,110,97,112,114,195,161,99,116,105, +99,97,110,111,118,101,100,97,100,101,115,112,114,111,112,117,101,115,116,97,112, +97,99,105,101,110,116,101,115,116,195,169,99,110,105,99,97,115,111,98,106,101, +116,105,118,111,115,99,111,110,116,97,99,116,111,115,224,164,174,224,165,135,224 +,164,130,224,164,178,224,164,191,224,164,143,224,164,185,224,165,136,224,164,130 +,224,164,151,224,164,175,224,164,190,224,164,184,224,164,190,224,164,165,224,164 +,143,224,164,181,224,164,130,224,164,176,224,164,185,224,165,135,224,164,149,224 +,165,139,224,164,136,224,164,149,224,165,129,224,164,155,224,164,176,224,164,185 +,224,164,190,224,164,172,224,164,190,224,164,166,224,164,149,224,164,185,224,164 +,190,224,164,184,224,164,173,224,165,128,224,164,185,224,165,129,224,164,143,224 +,164,176,224,164,185,224,165,128,224,164,174,224,165,136,224,164,130,224,164,166 +,224,164,191,224,164,168,224,164,172,224,164,190,224,164,164,100,105,112,108,111 +,100,111,99,115,224,164,184,224,164,174,224,164,175,224,164,176,224,165,130,224, +164,170,224,164,168,224,164,190,224,164,174,224,164,170,224,164,164,224,164,190, +224,164,171,224,164,191,224,164,176,224,164,148,224,164,184,224,164,164,224,164, +164,224,164,176,224,164,185,224,164,178,224,165,139,224,164,151,224,164,185,224, +165,129,224,164,134,224,164,172,224,164,190,224,164,176,224,164,166,224,165,135, +224,164,182,224,164,185,224,165,129,224,164,136,224,164,150,224,165,135,224,164, +178,224,164,175,224,164,166,224,164,191,224,164,149,224,164,190,224,164,174,224, +164,181,224,165,135,224,164,172,224,164,164,224,165,128,224,164,168,224,164,172, +224,165,128,224,164,154,224,164,174,224,165,140,224,164,164,224,164,184,224,164, +190,224,164,178,224,164,178,224,165,135,224,164,150,224,164,156,224,165,137,224, +164,172,224,164,174,224,164,166,224,164,166,224,164,164,224,164,165,224,164,190, +224,164,168,224,164,185,224,165,128,224,164,182,224,164,185,224,164,176,224,164, +133,224,164,178,224,164,151,224,164,149,224,164,173,224,165,128,224,164,168,224, +164,151,224,164,176,224,164,170,224,164,190,224,164,184,224,164,176,224,164,190, +224,164,164,224,164,149,224,164,191,224,164,143,224,164,137,224,164,184,224,165, +135,224,164,151,224,164,175,224,165,128,224,164,185,224,165,130,224,164,129,224, +164,134,224,164,151,224,165,135,224,164,159,224,165,128,224,164,174,224,164,150, +224,165,139,224,164,156,224,164,149,224,164,190,224,164,176,224,164,133,224,164, +173,224,165,128,224,164,151,224,164,175,224,165,135,224,164,164,224,165,129,224, +164,174,224,164,181,224,165,139,224,164,159,224,164,166,224,165,135,224,164,130, +224,164,133,224,164,151,224,164,176,224,164,144,224,164,184,224,165,135,224,164, +174,224,165,135,224,164,178,224,164,178,224,164,151,224,164,190,224,164,185,224, +164,190,224,164,178,224,164,138,224,164,170,224,164,176,224,164,154,224,164,190, +224,164,176,224,164,144,224,164,184,224,164,190,224,164,166,224,165,135,224,164, +176,224,164,156,224,164,191,224,164,184,224,164,166,224,164,191,224,164,178,224, +164,172,224,164,130,224,164,166,224,164,172,224,164,168,224,164,190,224,164,185, +224,165,130,224,164,130,224,164,178,224,164,190,224,164,150,224,164,156,224,165, +128,224,164,164,224,164,172,224,164,159,224,164,168,224,164,174,224,164,191,224, +164,178,224,164,135,224,164,184,224,165,135,224,164,134,224,164,168,224,165,135, +224,164,168,224,164,175,224,164,190,224,164,149,224,165,129,224,164,178,224,164, +178,224,165,137,224,164,151,224,164,173,224,164,190,224,164,151,224,164,176,224, +165,135,224,164,178,224,164,156,224,164,151,224,164,185,224,164,176,224,164,190, +224,164,174,224,164,178,224,164,151,224,165,135,224,164,170,224,165,135,224,164, +156,224,164,185,224,164,190,224,164,165,224,164,135,224,164,184,224,165,128,224, +164,184,224,164,185,224,165,128,224,164,149,224,164,178,224,164,190,224,164,160, +224,165,128,224,164,149,224,164,185,224,164,190,224,164,129,224,164,166,224,165, +130,224,164,176,224,164,164,224,164,185,224,164,164,224,164,184,224,164,190,224, +164,164,224,164,175,224,164,190,224,164,166,224,164,134,224,164,175,224,164,190, +224,164,170,224,164,190,224,164,149,224,164,149,224,165,140,224,164,168,224,164, +182,224,164,190,224,164,174,224,164,166,224,165,135,224,164,150,224,164,175,224, +164,185,224,165,128,224,164,176,224,164,190,224,164,175,224,164,150,224,165,129, +224,164,166,224,164,178,224,164,151,224,165,128,99,97,116,101,103,111,114,105, +101,115,101,120,112,101,114,105,101,110,99,101,60,47,116,105,116,108,101,62,13, +10,67,111,112,121,114,105,103,104,116,32,106,97,118,97,115,99,114,105,112,116,99 +,111,110,100,105,116,105,111,110,115,101,118,101,114,121,116,104,105,110,103,60, +112,32,99,108,97,115,115,61,34,116,101,99,104,110,111,108,111,103,121,98,97,99, +107,103,114,111,117,110,100,60,97,32,99,108,97,115,115,61,34,109,97,110,97,103, +101,109,101,110,116,38,99,111,112,121,59,32,50,48,49,106,97,118,97,83,99,114,105 +,112,116,99,104,97,114,97,99,116,101,114,115,98,114,101,97,100,99,114,117,109,98 +,116,104,101,109,115,101,108,118,101,115,104,111,114,105,122,111,110,116,97,108, +103,111,118,101,114,110,109,101,110,116,67,97,108,105,102,111,114,110,105,97,97, +99,116,105,118,105,116,105,101,115,100,105,115,99,111,118,101,114,101,100,78,97, +118,105,103,97,116,105,111,110,116,114,97,110,115,105,116,105,111,110,99,111,110 +,110,101,99,116,105,111,110,110,97,118,105,103,97,116,105,111,110,97,112,112,101 +,97,114,97,110,99,101,60,47,116,105,116,108,101,62,60,109,99,104,101,99,107,98, +111,120,34,32,116,101,99,104,110,105,113,117,101,115,112,114,111,116,101,99,116, +105,111,110,97,112,112,97,114,101,110,116,108,121,97,115,32,119,101,108,108,32, +97,115,117,110,116,39,44,32,39,85,65,45,114,101,115,111,108,117,116,105,111,110, +111,112,101,114,97,116,105,111,110,115,116,101,108,101,118,105,115,105,111,110, +116,114,97,110,115,108,97,116,101,100,87,97,115,104,105,110,103,116,111,110,110, +97,118,105,103,97,116,111,114,46,32,61,32,119,105,110,100,111,119,46,105,109,112 +,114,101,115,115,105,111,110,38,108,116,59,98,114,38,103,116,59,108,105,116,101, +114,97,116,117,114,101,112,111,112,117,108,97,116,105,111,110,98,103,99,111,108, +111,114,61,34,35,101,115,112,101,99,105,97,108,108,121,32,99,111,110,116,101,110 +,116,61,34,112,114,111,100,117,99,116,105,111,110,110,101,119,115,108,101,116, +116,101,114,112,114,111,112,101,114,116,105,101,115,100,101,102,105,110,105,116, +105,111,110,108,101,97,100,101,114,115,104,105,112,84,101,99,104,110,111,108,111 +,103,121,80,97,114,108,105,97,109,101,110,116,99,111,109,112,97,114,105,115,111, +110,117,108,32,99,108,97,115,115,61,34,46,105,110,100,101,120,79,102,40,34,99, +111,110,99,108,117,115,105,111,110,100,105,115,99,117,115,115,105,111,110,99,111 +,109,112,111,110,101,110,116,115,98,105,111,108,111,103,105,99,97,108,82,101,118 +,111,108,117,116,105,111,110,95,99,111,110,116,97,105,110,101,114,117,110,100, +101,114,115,116,111,111,100,110,111,115,99,114,105,112,116,62,60,112,101,114,109 +,105,115,115,105,111,110,101,97,99,104,32,111,116,104,101,114,97,116,109,111,115 +,112,104,101,114,101,32,111,110,102,111,99,117,115,61,34,60,102,111,114,109,32, +105,100,61,34,112,114,111,99,101,115,115,105,110,103,116,104,105,115,46,118,97, +108,117,101,103,101,110,101,114,97,116,105,111,110,67,111,110,102,101,114,101, +110,99,101,115,117,98,115,101,113,117,101,110,116,119,101,108,108,45,107,110,111 +,119,110,118,97,114,105,97,116,105,111,110,115,114,101,112,117,116,97,116,105, +111,110,112,104,101,110,111,109,101,110,111,110,100,105,115,99,105,112,108,105, +110,101,108,111,103,111,46,112,110,103,34,32,40,100,111,99,117,109,101,110,116, +44,98,111,117,110,100,97,114,105,101,115,101,120,112,114,101,115,115,105,111,110 +,115,101,116,116,108,101,109,101,110,116,66,97,99,107,103,114,111,117,110,100, +111,117,116,32,111,102,32,116,104,101,101,110,116,101,114,112,114,105,115,101,40 +,34,104,116,116,112,115,58,34,32,117,110,101,115,99,97,112,101,40,34,112,97,115, +115,119,111,114,100,34,32,100,101,109,111,99,114,97,116,105,99,60,97,32,104,114, +101,102,61,34,47,119,114,97,112,112,101,114,34,62,10,109,101,109,98,101,114,115, +104,105,112,108,105,110,103,117,105,115,116,105,99,112,120,59,112,97,100,100,105 +,110,103,112,104,105,108,111,115,111,112,104,121,97,115,115,105,115,116,97,110, +99,101,117,110,105,118,101,114,115,105,116,121,102,97,99,105,108,105,116,105,101 +,115,114,101,99,111,103,110,105,122,101,100,112,114,101,102,101,114,101,110,99, +101,105,102,32,40,116,121,112,101,111,102,109,97,105,110,116,97,105,110,101,100, +118,111,99,97,98,117,108,97,114,121,104,121,112,111,116,104,101,115,105,115,46, +115,117,98,109,105,116,40,41,59,38,97,109,112,59,110,98,115,112,59,97,110,110, +111,116,97,116,105,111,110,98,101,104,105,110,100,32,116,104,101,70,111,117,110, +100,97,116,105,111,110,112,117,98,108,105,115,104,101,114,34,97,115,115,117,109, +112,116,105,111,110,105,110,116,114,111,100,117,99,101,100,99,111,114,114,117, +112,116,105,111,110,115,99,105,101,110,116,105,115,116,115,101,120,112,108,105, +99,105,116,108,121,105,110,115,116,101,97,100,32,111,102,100,105,109,101,110,115 +,105,111,110,115,32,111,110,67,108,105,99,107,61,34,99,111,110,115,105,100,101, +114,101,100,100,101,112,97,114,116,109,101,110,116,111,99,99,117,112,97,116,105, +111,110,115,111,111,110,32,97,102,116,101,114,105,110,118,101,115,116,109,101, +110,116,112,114,111,110,111,117,110,99,101,100,105,100,101,110,116,105,102,105, +101,100,101,120,112,101,114,105,109,101,110,116,77,97,110,97,103,101,109,101,110 +,116,103,101,111,103,114,97,112,104,105,99,34,32,104,101,105,103,104,116,61,34, +108,105,110,107,32,114,101,108,61,34,46,114,101,112,108,97,99,101,40,47,100,101, +112,114,101,115,115,105,111,110,99,111,110,102,101,114,101,110,99,101,112,117, +110,105,115,104,109,101,110,116,101,108,105,109,105,110,97,116,101,100,114,101, +115,105,115,116,97,110,99,101,97,100,97,112,116,97,116,105,111,110,111,112,112, +111,115,105,116,105,111,110,119,101,108,108,32,107,110,111,119,110,115,117,112, +112,108,101,109,101,110,116,100,101,116,101,114,109,105,110,101,100,104,49,32,99 +,108,97,115,115,61,34,48,112,120,59,109,97,114,103,105,110,109,101,99,104,97,110 +,105,99,97,108,115,116,97,116,105,115,116,105,99,115,99,101,108,101,98,114,97, +116,101,100,71,111,118,101,114,110,109,101,110,116,10,10,68,117,114,105,110,103, +32,116,100,101,118,101,108,111,112,101,114,115,97,114,116,105,102,105,99,105,97, +108,101,113,117,105,118,97,108,101,110,116,111,114,105,103,105,110,97,116,101, +100,67,111,109,109,105,115,115,105,111,110,97,116,116,97,99,104,109,101,110,116, +60,115,112,97,110,32,105,100,61,34,116,104,101,114,101,32,119,101,114,101,78,101 +,100,101,114,108,97,110,100,115,98,101,121,111,110,100,32,116,104,101,114,101, +103,105,115,116,101,114,101,100,106,111,117,114,110,97,108,105,115,116,102,114, +101,113,117,101,110,116,108,121,97,108,108,32,111,102,32,116,104,101,108,97,110, +103,61,34,101,110,34,32,60,47,115,116,121,108,101,62,13,10,97,98,115,111,108,117 +,116,101,59,32,115,117,112,112,111,114,116,105,110,103,101,120,116,114,101,109, +101,108,121,32,109,97,105,110,115,116,114,101,97,109,60,47,115,116,114,111,110, +103,62,32,112,111,112,117,108,97,114,105,116,121,101,109,112,108,111,121,109,101 +,110,116,60,47,116,97,98,108,101,62,13,10,32,99,111,108,115,112,97,110,61,34,60, +47,102,111,114,109,62,10,32,32,99,111,110,118,101,114,115,105,111,110,97,98,111, +117,116,32,116,104,101,32,60,47,112,62,60,47,100,105,118,62,105,110,116,101,103, +114,97,116,101,100,34,32,108,97,110,103,61,34,101,110,80,111,114,116,117,103,117 +,101,115,101,115,117,98,115,116,105,116,117,116,101,105,110,100,105,118,105,100, +117,97,108,105,109,112,111,115,115,105,98,108,101,109,117,108,116,105,109,101, +100,105,97,97,108,109,111,115,116,32,97,108,108,112,120,32,115,111,108,105,100, +32,35,97,112,97,114,116,32,102,114,111,109,115,117,98,106,101,99,116,32,116,111, +105,110,32,69,110,103,108,105,115,104,99,114,105,116,105,99,105,122,101,100,101, +120,99,101,112,116,32,102,111,114,103,117,105,100,101,108,105,110,101,115,111, +114,105,103,105,110,97,108,108,121,114,101,109,97,114,107,97,98,108,101,116,104, +101,32,115,101,99,111,110,100,104,50,32,99,108,97,115,115,61,34,60,97,32,116,105 +,116,108,101,61,34,40,105,110,99,108,117,100,105,110,103,112,97,114,97,109,101, +116,101,114,115,112,114,111,104,105,98,105,116,101,100,61,32,34,104,116,116,112, +58,47,47,100,105,99,116,105,111,110,97,114,121,112,101,114,99,101,112,116,105, +111,110,114,101,118,111,108,117,116,105,111,110,102,111,117,110,100,97,116,105, +111,110,112,120,59,104,101,105,103,104,116,58,115,117,99,99,101,115,115,102,117, +108,115,117,112,112,111,114,116,101,114,115,109,105,108,108,101,110,110,105,117, +109,104,105,115,32,102,97,116,104,101,114,116,104,101,32,38,113,117,111,116,59, +110,111,45,114,101,112,101,97,116,59,99,111,109,109,101,114,99,105,97,108,105, +110,100,117,115,116,114,105,97,108,101,110,99,111,117,114,97,103,101,100,97,109, +111,117,110,116,32,111,102,32,117,110,111,102,102,105,99,105,97,108,101,102,102, +105,99,105,101,110,99,121,82,101,102,101,114,101,110,99,101,115,99,111,111,114, +100,105,110,97,116,101,100,105,115,99,108,97,105,109,101,114,101,120,112,101,100 +,105,116,105,111,110,100,101,118,101,108,111,112,105,110,103,99,97,108,99,117, +108,97,116,101,100,115,105,109,112,108,105,102,105,101,100,108,101,103,105,116, +105,109,97,116,101,115,117,98,115,116,114,105,110,103,40,48,34,32,99,108,97,115, +115,61,34,99,111,109,112,108,101,116,101,108,121,105,108,108,117,115,116,114,97, +116,101,102,105,118,101,32,121,101,97,114,115,105,110,115,116,114,117,109,101, +110,116,80,117,98,108,105,115,104,105,110,103,49,34,32,99,108,97,115,115,61,34, +112,115,121,99,104,111,108,111,103,121,99,111,110,102,105,100,101,110,99,101,110 +,117,109,98,101,114,32,111,102,32,97,98,115,101,110,99,101,32,111,102,102,111,99 +,117,115,101,100,32,111,110,106,111,105,110,101,100,32,116,104,101,115,116,114, +117,99,116,117,114,101,115,112,114,101,118,105,111,117,115,108,121,62,60,47,105, +102,114,97,109,101,62,111,110,99,101,32,97,103,97,105,110,98,117,116,32,114,97, +116,104,101,114,105,109,109,105,103,114,97,110,116,115,111,102,32,99,111,117,114 +,115,101,44,97,32,103,114,111,117,112,32,111,102,76,105,116,101,114,97,116,117, +114,101,85,110,108,105,107,101,32,116,104,101,60,47,97,62,38,110,98,115,112,59, +10,102,117,110,99,116,105,111,110,32,105,116,32,119,97,115,32,116,104,101,67,111 +,110,118,101,110,116,105,111,110,97,117,116,111,109,111,98,105,108,101,80,114, +111,116,101,115,116,97,110,116,97,103,103,114,101,115,115,105,118,101,97,102,116 +,101,114,32,116,104,101,32,83,105,109,105,108,97,114,108,121,44,34,32,47,62,60, +47,100,105,118,62,99,111,108,108,101,99,116,105,111,110,13,10,102,117,110,99,116 +,105,111,110,118,105,115,105,98,105,108,105,116,121,116,104,101,32,117,115,101, +32,111,102,118,111,108,117,110,116,101,101,114,115,97,116,116,114,97,99,116,105, +111,110,117,110,100,101,114,32,116,104,101,32,116,104,114,101,97,116,101,110,101 +,100,42,60,33,91,67,68,65,84,65,91,105,109,112,111,114,116,97,110,99,101,105,110 +,32,103,101,110,101,114,97,108,116,104,101,32,108,97,116,116,101,114,60,47,102, +111,114,109,62,10,60,47,46,105,110,100,101,120,79,102,40,39,105,32,61,32,48,59, +32,105,32,60,100,105,102,102,101,114,101,110,99,101,100,101,118,111,116,101,100, +32,116,111,116,114,97,100,105,116,105,111,110,115,115,101,97,114,99,104,32,102, +111,114,117,108,116,105,109,97,116,101,108,121,116,111,117,114,110,97,109,101, +110,116,97,116,116,114,105,98,117,116,101,115,115,111,45,99,97,108,108,101,100, +32,125,10,60,47,115,116,121,108,101,62,101,118,97,108,117,97,116,105,111,110,101 +,109,112,104,97,115,105,122,101,100,97,99,99,101,115,115,105,98,108,101,60,47, +115,101,99,116,105,111,110,62,115,117,99,99,101,115,115,105,111,110,97,108,111, +110,103,32,119,105,116,104,77,101,97,110,119,104,105,108,101,44,105,110,100,117, +115,116,114,105,101,115,60,47,97,62,60,98,114,32,47,62,104,97,115,32,98,101,99, +111,109,101,97,115,112,101,99,116,115,32,111,102,84,101,108,101,118,105,115,105, +111,110,115,117,102,102,105,99,105,101,110,116,98,97,115,107,101,116,98,97,108, +108,98,111,116,104,32,115,105,100,101,115,99,111,110,116,105,110,117,105,110,103 +,97,110,32,97,114,116,105,99,108,101,60,105,109,103,32,97,108,116,61,34,97,100, +118,101,110,116,117,114,101,115,104,105,115,32,109,111,116,104,101,114,109,97, +110,99,104,101,115,116,101,114,112,114,105,110,99,105,112,108,101,115,112,97,114 +,116,105,99,117,108,97,114,99,111,109,109,101,110,116,97,114,121,101,102,102,101 +,99,116,115,32,111,102,100,101,99,105,100,101,100,32,116,111,34,62,60,115,116, +114,111,110,103,62,112,117,98,108,105,115,104,101,114,115,74,111,117,114,110,97, +108,32,111,102,100,105,102,102,105,99,117,108,116,121,102,97,99,105,108,105,116, +97,116,101,97,99,99,101,112,116,97,98,108,101,115,116,121,108,101,46,99,115,115, +34,9,102,117,110,99,116,105,111,110,32,105,110,110,111,118,97,116,105,111,110,62 +,67,111,112,121,114,105,103,104,116,115,105,116,117,97,116,105,111,110,115,119, +111,117,108,100,32,104,97,118,101,98,117,115,105,110,101,115,115,101,115,68,105, +99,116,105,111,110,97,114,121,115,116,97,116,101,109,101,110,116,115,111,102,116 +,101,110,32,117,115,101,100,112,101,114,115,105,115,116,101,110,116,105,110,32, +74,97,110,117,97,114,121,99,111,109,112,114,105,115,105,110,103,60,47,116,105, +116,108,101,62,10,9,100,105,112,108,111,109,97,116,105,99,99,111,110,116,97,105, +110,105,110,103,112,101,114,102,111,114,109,105,110,103,101,120,116,101,110,115, +105,111,110,115,109,97,121,32,110,111,116,32,98,101,99,111,110,99,101,112,116,32 +,111,102,32,111,110,99,108,105,99,107,61,34,73,116,32,105,115,32,97,108,115,111, +102,105,110,97,110,99,105,97,108,32,109,97,107,105,110,103,32,116,104,101,76,117 +,120,101,109,98,111,117,114,103,97,100,100,105,116,105,111,110,97,108,97,114,101 +,32,99,97,108,108,101,100,101,110,103,97,103,101,100,32,105,110,34,115,99,114, +105,112,116,34,41,59,98,117,116,32,105,116,32,119,97,115,101,108,101,99,116,114, +111,110,105,99,111,110,115,117,98,109,105,116,61,34,10,60,33,45,45,32,69,110,100 +,32,101,108,101,99,116,114,105,99,97,108,111,102,102,105,99,105,97,108,108,121, +115,117,103,103,101,115,116,105,111,110,116,111,112,32,111,102,32,116,104,101, +117,110,108,105,107,101,32,116,104,101,65,117,115,116,114,97,108,105,97,110,79, +114,105,103,105,110,97,108,108,121,114,101,102,101,114,101,110,99,101,115,10,60, +47,104,101,97,100,62,13,10,114,101,99,111,103,110,105,115,101,100,105,110,105, +116,105,97,108,105,122,101,108,105,109,105,116,101,100,32,116,111,65,108,101,120 +,97,110,100,114,105,97,114,101,116,105,114,101,109,101,110,116,65,100,118,101, +110,116,117,114,101,115,102,111,117,114,32,121,101,97,114,115,10,10,38,108,116, +59,33,45,45,32,105,110,99,114,101,97,115,105,110,103,100,101,99,111,114,97,116, +105,111,110,104,51,32,99,108,97,115,115,61,34,111,114,105,103,105,110,115,32,111 +,102,111,98,108,105,103,97,116,105,111,110,114,101,103,117,108,97,116,105,111, +110,99,108,97,115,115,105,102,105,101,100,40,102,117,110,99,116,105,111,110,40, +97,100,118,97,110,116,97,103,101,115,98,101,105,110,103,32,116,104,101,32,104, +105,115,116,111,114,105,97,110,115,60,98,97,115,101,32,104,114,101,102,114,101, +112,101,97,116,101,100,108,121,119,105,108,108,105,110,103,32,116,111,99,111,109 +,112,97,114,97,98,108,101,100,101,115,105,103,110,97,116,101,100,110,111,109,105 +,110,97,116,105,111,110,102,117,110,99,116,105,111,110,97,108,105,110,115,105, +100,101,32,116,104,101,114,101,118,101,108,97,116,105,111,110,101,110,100,32,111 +,102,32,116,104,101,115,32,102,111,114,32,116,104,101,32,97,117,116,104,111,114, +105,122,101,100,114,101,102,117,115,101,100,32,116,111,116,97,107,101,32,112,108 +,97,99,101,97,117,116,111,110,111,109,111,117,115,99,111,109,112,114,111,109,105 +,115,101,112,111,108,105,116,105,99,97,108,32,114,101,115,116,97,117,114,97,110, +116,116,119,111,32,111,102,32,116,104,101,70,101,98,114,117,97,114,121,32,50,113 +,117,97,108,105,116,121,32,111,102,115,119,102,111,98,106,101,99,116,46,117,110, +100,101,114,115,116,97,110,100,110,101,97,114,108,121,32,97,108,108,119,114,105, +116,116,101,110,32,98,121,105,110,116,101,114,118,105,101,119,115,34,32,119,105, +100,116,104,61,34,49,119,105,116,104,100,114,97,119,97,108,102,108,111,97,116,58 +,108,101,102,116,105,115,32,117,115,117,97,108,108,121,99,97,110,100,105,100,97, +116,101,115,110,101,119,115,112,97,112,101,114,115,109,121,115,116,101,114,105, +111,117,115,68,101,112,97,114,116,109,101,110,116,98,101,115,116,32,107,110,111, +119,110,112,97,114,108,105,97,109,101,110,116,115,117,112,112,114,101,115,115, +101,100,99,111,110,118,101,110,105,101,110,116,114,101,109,101,109,98,101,114, +101,100,100,105,102,102,101,114,101,110,116,32,115,121,115,116,101,109,97,116, +105,99,104,97,115,32,108,101,100,32,116,111,112,114,111,112,97,103,97,110,100,97 +,99,111,110,116,114,111,108,108,101,100,105,110,102,108,117,101,110,99,101,115, +99,101,114,101,109,111,110,105,97,108,112,114,111,99,108,97,105,109,101,100,80, +114,111,116,101,99,116,105,111,110,108,105,32,99,108,97,115,115,61,34,83,99,105, +101,110,116,105,102,105,99,99,108,97,115,115,61,34,110,111,45,116,114,97,100,101 +,109,97,114,107,115,109,111,114,101,32,116,104,97,110,32,119,105,100,101,115,112 +,114,101,97,100,76,105,98,101,114,97,116,105,111,110,116,111,111,107,32,112,108, +97,99,101,100,97,121,32,111,102,32,116,104,101,97,115,32,108,111,110,103,32,97, +115,105,109,112,114,105,115,111,110,101,100,65,100,100,105,116,105,111,110,97, +108,10,60,104,101,97,100,62,10,60,109,76,97,98,111,114,97,116,111,114,121,78,111 +,118,101,109,98,101,114,32,50,101,120,99,101,112,116,105,111,110,115,73,110,100, +117,115,116,114,105,97,108,118,97,114,105,101,116,121,32,111,102,102,108,111,97, +116,58,32,108,101,102,68,117,114,105,110,103,32,116,104,101,97,115,115,101,115, +115,109,101,110,116,104,97,118,101,32,98,101,101,110,32,100,101,97,108,115,32, +119,105,116,104,83,116,97,116,105,115,116,105,99,115,111,99,99,117,114,114,101, +110,99,101,47,117,108,62,60,47,100,105,118,62,99,108,101,97,114,102,105,120,34, +62,116,104,101,32,112,117,98,108,105,99,109,97,110,121,32,121,101,97,114,115,119 +,104,105,99,104,32,119,101,114,101,111,118,101,114,32,116,105,109,101,44,115,121 +,110,111,110,121,109,111,117,115,99,111,110,116,101,110,116,34,62,10,112,114,101 +,115,117,109,97,98,108,121,104,105,115,32,102,97,109,105,108,121,117,115,101,114 +,65,103,101,110,116,46,117,110,101,120,112,101,99,116,101,100,105,110,99,108,117 +,100,105,110,103,32,99,104,97,108,108,101,110,103,101,100,97,32,109,105,110,111, +114,105,116,121,117,110,100,101,102,105,110,101,100,34,98,101,108,111,110,103, +115,32,116,111,116,97,107,101,110,32,102,114,111,109,105,110,32,79,99,116,111,98 +,101,114,112,111,115,105,116,105,111,110,58,32,115,97,105,100,32,116,111,32,98, +101,114,101,108,105,103,105,111,117,115,32,70,101,100,101,114,97,116,105,111,110 +,32,114,111,119,115,112,97,110,61,34,111,110,108,121,32,97,32,102,101,119,109, +101,97,110,116,32,116,104,97,116,108,101,100,32,116,111,32,116,104,101,45,45,62, +13,10,60,100,105,118,32,60,102,105,101,108,100,115,101,116,62,65,114,99,104,98, +105,115,104,111,112,32,99,108,97,115,115,61,34,110,111,98,101,105,110,103,32,117 +,115,101,100,97,112,112,114,111,97,99,104,101,115,112,114,105,118,105,108,101, +103,101,115,110,111,115,99,114,105,112,116,62,10,114,101,115,117,108,116,115,32, +105,110,109,97,121,32,98,101,32,116,104,101,69,97,115,116,101,114,32,101,103,103 +,109,101,99,104,97,110,105,115,109,115,114,101,97,115,111,110,97,98,108,101,80, +111,112,117,108,97,116,105,111,110,67,111,108,108,101,99,116,105,111,110,115,101 +,108,101,99,116,101,100,34,62,110,111,115,99,114,105,112,116,62,13,47,105,110, +100,101,120,46,112,104,112,97,114,114,105,118,97,108,32,111,102,45,106,115,115, +100,107,39,41,41,59,109,97,110,97,103,101,100,32,116,111,105,110,99,111,109,112, +108,101,116,101,99,97,115,117,97,108,116,105,101,115,99,111,109,112,108,101,116, +105,111,110,67,104,114,105,115,116,105,97,110,115,83,101,112,116,101,109,98,101, +114,32,97,114,105,116,104,109,101,116,105,99,112,114,111,99,101,100,117,114,101, +115,109,105,103,104,116,32,104,97,118,101,80,114,111,100,117,99,116,105,111,110, +105,116,32,97,112,112,101,97,114,115,80,104,105,108,111,115,111,112,104,121,102, +114,105,101,110,100,115,104,105,112,108,101,97,100,105,110,103,32,116,111,103, +105,118,105,110,103,32,116,104,101,116,111,119,97,114,100,32,116,104,101,103,117 +,97,114,97,110,116,101,101,100,100,111,99,117,109,101,110,116,101,100,99,111,108 +,111,114,58,35,48,48,48,118,105,100,101,111,32,103,97,109,101,99,111,109,109,105 +,115,115,105,111,110,114,101,102,108,101,99,116,105,110,103,99,104,97,110,103, +101,32,116,104,101,97,115,115,111,99,105,97,116,101,100,115,97,110,115,45,115, +101,114,105,102,111,110,107,101,121,112,114,101,115,115,59,32,112,97,100,100,105 +,110,103,58,72,101,32,119,97,115,32,116,104,101,117,110,100,101,114,108,121,105, +110,103,116,121,112,105,99,97,108,108,121,32,44,32,97,110,100,32,116,104,101,32, +115,114,99,69,108,101,109,101,110,116,115,117,99,99,101,115,115,105,118,101,115, +105,110,99,101,32,116,104,101,32,115,104,111,117,108,100,32,98,101,32,110,101, +116,119,111,114,107,105,110,103,97,99,99,111,117,110,116,105,110,103,117,115,101 +,32,111,102,32,116,104,101,108,111,119,101,114,32,116,104,97,110,115,104,111,119 +,115,32,116,104,97,116,60,47,115,112,97,110,62,10,9,9,99,111,109,112,108,97,105, +110,116,115,99,111,110,116,105,110,117,111,117,115,113,117,97,110,116,105,116, +105,101,115,97,115,116,114,111,110,111,109,101,114,104,101,32,100,105,100,32,110 +,111,116,100,117,101,32,116,111,32,105,116,115,97,112,112,108,105,101,100,32,116 +,111,97,110,32,97,118,101,114,97,103,101,101,102,102,111,114,116,115,32,116,111, +116,104,101,32,102,117,116,117,114,101,97,116,116,101,109,112,116,32,116,111,84, +104,101,114,101,102,111,114,101,44,99,97,112,97,98,105,108,105,116,121,82,101, +112,117,98,108,105,99,97,110,119,97,115,32,102,111,114,109,101,100,69,108,101,99 +,116,114,111,110,105,99,107,105,108,111,109,101,116,101,114,115,99,104,97,108, +108,101,110,103,101,115,112,117,98,108,105,115,104,105,110,103,116,104,101,32, +102,111,114,109,101,114,105,110,100,105,103,101,110,111,117,115,100,105,114,101, +99,116,105,111,110,115,115,117,98,115,105,100,105,97,114,121,99,111,110,115,112, +105,114,97,99,121,100,101,116,97,105,108,115,32,111,102,97,110,100,32,105,110,32 +,116,104,101,97,102,102,111,114,100,97,98,108,101,115,117,98,115,116,97,110,99, +101,115,114,101,97,115,111,110,32,102,111,114,99,111,110,118,101,110,116,105,111 +,110,105,116,101,109,116,121,112,101,61,34,97,98,115,111,108,117,116,101,108,121 +,115,117,112,112,111,115,101,100,108,121,114,101,109,97,105,110,101,100,32,97,97 +,116,116,114,97,99,116,105,118,101,116,114,97,118,101,108,108,105,110,103,115, +101,112,97,114,97,116,101,108,121,102,111,99,117,115,101,115,32,111,110,101,108, +101,109,101,110,116,97,114,121,97,112,112,108,105,99,97,98,108,101,102,111,117, +110,100,32,116,104,97,116,115,116,121,108,101,115,104,101,101,116,109,97,110,117 +,115,99,114,105,112,116,115,116,97,110,100,115,32,102,111,114,32,110,111,45,114, +101,112,101,97,116,40,115,111,109,101,116,105,109,101,115,67,111,109,109,101,114 +,99,105,97,108,105,110,32,65,109,101,114,105,99,97,117,110,100,101,114,116,97, +107,101,110,113,117,97,114,116,101,114,32,111,102,97,110,32,101,120,97,109,112, +108,101,112,101,114,115,111,110,97,108,108,121,105,110,100,101,120,46,112,104, +112,63,60,47,98,117,116,116,111,110,62,10,112,101,114,99,101,110,116,97,103,101, +98,101,115,116,45,107,110,111,119,110,99,114,101,97,116,105,110,103,32,97,34,32, +100,105,114,61,34,108,116,114,76,105,101,117,116,101,110,97,110,116,10,60,100, +105,118,32,105,100,61,34,116,104,101,121,32,119,111,117,108,100,97,98,105,108, +105,116,121,32,111,102,109,97,100,101,32,117,112,32,111,102,110,111,116,101,100, +32,116,104,97,116,99,108,101,97,114,32,116,104,97,116,97,114,103,117,101,32,116, +104,97,116,116,111,32,97,110,111,116,104,101,114,99,104,105,108,100,114,101,110, +39,115,112,117,114,112,111,115,101,32,111,102,102,111,114,109,117,108,97,116,101 +,100,98,97,115,101,100,32,117,112,111,110,116,104,101,32,114,101,103,105,111,110 +,115,117,98,106,101,99,116,32,111,102,112,97,115,115,101,110,103,101,114,115,112 +,111,115,115,101,115,115,105,111,110,46,10,10,73,110,32,116,104,101,32,66,101, +102,111,114,101,32,116,104,101,97,102,116,101,114,119,97,114,100,115,99,117,114, +114,101,110,116,108,121,32,97,99,114,111,115,115,32,116,104,101,115,99,105,101, +110,116,105,102,105,99,99,111,109,109,117,110,105,116,121,46,99,97,112,105,116, +97,108,105,115,109,105,110,32,71,101,114,109,97,110,121,114,105,103,104,116,45, +119,105,110,103,116,104,101,32,115,121,115,116,101,109,83,111,99,105,101,116,121 +,32,111,102,112,111,108,105,116,105,99,105,97,110,100,105,114,101,99,116,105,111 +,110,58,119,101,110,116,32,111,110,32,116,111,114,101,109,111,118,97,108,32,111, +102,32,78,101,119,32,89,111,114,107,32,97,112,97,114,116,109,101,110,116,115,105 +,110,100,105,99,97,116,105,111,110,100,117,114,105,110,103,32,116,104,101,117, +110,108,101,115,115,32,116,104,101,104,105,115,116,111,114,105,99,97,108,104,97, +100,32,98,101,101,110,32,97,100,101,102,105,110,105,116,105,118,101,105,110,103, +114,101,100,105,101,110,116,97,116,116,101,110,100,97,110,99,101,67,101,110,116, +101,114,32,102,111,114,112,114,111,109,105,110,101,110,99,101,114,101,97,100,121 +,83,116,97,116,101,115,116,114,97,116,101,103,105,101,115,98,117,116,32,105,110, +32,116,104,101,97,115,32,112,97,114,116,32,111,102,99,111,110,115,116,105,116, +117,116,101,99,108,97,105,109,32,116,104,97,116,108,97,98,111,114,97,116,111,114 +,121,99,111,109,112,97,116,105,98,108,101,102,97,105,108,117,114,101,32,111,102, +44,32,115,117,99,104,32,97,115,32,98,101,103,97,110,32,119,105,116,104,117,115, +105,110,103,32,116,104,101,32,116,111,32,112,114,111,118,105,100,101,102,101,97, +116,117,114,101,32,111,102,102,114,111,109,32,119,104,105,99,104,47,34,32,99,108 +,97,115,115,61,34,103,101,111,108,111,103,105,99,97,108,115,101,118,101,114,97, +108,32,111,102,100,101,108,105,98,101,114,97,116,101,105,109,112,111,114,116,97, +110,116,32,104,111,108,100,115,32,116,104,97,116,105,110,103,38,113,117,111,116, +59,32,118,97,108,105,103,110,61,116,111,112,116,104,101,32,71,101,114,109,97,110 +,111,117,116,115,105,100,101,32,111,102,110,101,103,111,116,105,97,116,101,100, +104,105,115,32,99,97,114,101,101,114,115,101,112,97,114,97,116,105,111,110,105, +100,61,34,115,101,97,114,99,104,119,97,115,32,99,97,108,108,101,100,116,104,101, +32,102,111,117,114,116,104,114,101,99,114,101,97,116,105,111,110,111,116,104,101 +,114,32,116,104,97,110,112,114,101,118,101,110,116,105,111,110,119,104,105,108, +101,32,116,104,101,32,101,100,117,99,97,116,105,111,110,44,99,111,110,110,101,99 +,116,105,110,103,97,99,99,117,114,97,116,101,108,121,119,101,114,101,32,98,117, +105,108,116,119,97,115,32,107,105,108,108,101,100,97,103,114,101,101,109,101,110 +,116,115,109,117,99,104,32,109,111,114,101,32,68,117,101,32,116,111,32,116,104, +101,119,105,100,116,104,58,32,49,48,48,115,111,109,101,32,111,116,104,101,114,75 +,105,110,103,100,111,109,32,111,102,116,104,101,32,101,110,116,105,114,101,102, +97,109,111,117,115,32,102,111,114,116,111,32,99,111,110,110,101,99,116,111,98, +106,101,99,116,105,118,101,115,116,104,101,32,70,114,101,110,99,104,112,101,111, +112,108,101,32,97,110,100,102,101,97,116,117,114,101,100,34,62,105,115,32,115,97 +,105,100,32,116,111,115,116,114,117,99,116,117,114,97,108,114,101,102,101,114, +101,110,100,117,109,109,111,115,116,32,111,102,116,101,110,97,32,115,101,112,97, +114,97,116,101,45,62,10,60,100,105,118,32,105,100,32,79,102,102,105,99,105,97, +108,32,119,111,114,108,100,119,105,100,101,46,97,114,105,97,45,108,97,98,101,108 +,116,104,101,32,112,108,97,110,101,116,97,110,100,32,105,116,32,119,97,115,100, +34,32,118,97,108,117,101,61,34,108,111,111,107,105,110,103,32,97,116,98,101,110, +101,102,105,99,105,97,108,97,114,101,32,105,110,32,116,104,101,109,111,110,105, +116,111,114,105,110,103,114,101,112,111,114,116,101,100,108,121,116,104,101,32, +109,111,100,101,114,110,119,111,114,107,105,110,103,32,111,110,97,108,108,111, +119,101,100,32,116,111,119,104,101,114,101,32,116,104,101,32,105,110,110,111,118 +,97,116,105,118,101,60,47,97,62,60,47,100,105,118,62,115,111,117,110,100,116,114 +,97,99,107,115,101,97,114,99,104,70,111,114,109,116,101,110,100,32,116,111,32,98 +,101,105,110,112,117,116,32,105,100,61,34,111,112,101,110,105,110,103,32,111,102 +,114,101,115,116,114,105,99,116,101,100,97,100,111,112,116,101,100,32,98,121,97, +100,100,114,101,115,115,105,110,103,116,104,101,111,108,111,103,105,97,110,109, +101,116,104,111,100,115,32,111,102,118,97,114,105,97,110,116,32,111,102,67,104, +114,105,115,116,105,97,110,32,118,101,114,121,32,108,97,114,103,101,97,117,116, +111,109,111,116,105,118,101,98,121,32,102,97,114,32,116,104,101,114,97,110,103, +101,32,102,114,111,109,112,117,114,115,117,105,116,32,111,102,102,111,108,108, +111,119,32,116,104,101,98,114,111,117,103,104,116,32,116,111,105,110,32,69,110, +103,108,97,110,100,97,103,114,101,101,32,116,104,97,116,97,99,99,117,115,101,100 +,32,111,102,99,111,109,101,115,32,102,114,111,109,112,114,101,118,101,110,116, +105,110,103,100,105,118,32,115,116,121,108,101,61,104,105,115,32,111,114,32,104, +101,114,116,114,101,109,101,110,100,111,117,115,102,114,101,101,100,111,109,32, +111,102,99,111,110,99,101,114,110,105,110,103,48,32,49,101,109,32,49,101,109,59, +66,97,115,107,101,116,98,97,108,108,47,115,116,121,108,101,46,99,115,115,97,110, +32,101,97,114,108,105,101,114,101,118,101,110,32,97,102,116,101,114,47,34,32,116 +,105,116,108,101,61,34,46,99,111,109,47,105,110,100,101,120,116,97,107,105,110, +103,32,116,104,101,112,105,116,116,115,98,117,114,103,104,99,111,110,116,101,110 +,116,34,62,13,60,115,99,114,105,112,116,62,40,102,116,117,114,110,101,100,32,111 +,117,116,104,97,118,105,110,103,32,116,104,101,60,47,115,112,97,110,62,13,10,32, +111,99,99,97,115,105,111,110,97,108,98,101,99,97,117,115,101,32,105,116,115,116, +97,114,116,101,100,32,116,111,112,104,121,115,105,99,97,108,108,121,62,60,47,100 +,105,118,62,10,32,32,99,114,101,97,116,101,100,32,98,121,67,117,114,114,101,110, +116,108,121,44,32,98,103,99,111,108,111,114,61,34,116,97,98,105,110,100,101,120, +61,34,100,105,115,97,115,116,114,111,117,115,65,110,97,108,121,116,105,99,115,32 +,97,108,115,111,32,104,97,115,32,97,62,60,100,105,118,32,105,100,61,34,60,47,115 +,116,121,108,101,62,10,60,99,97,108,108,101,100,32,102,111,114,115,105,110,103, +101,114,32,97,110,100,46,115,114,99,32,61,32,34,47,47,118,105,111,108,97,116,105 +,111,110,115,116,104,105,115,32,112,111,105,110,116,99,111,110,115,116,97,110, +116,108,121,105,115,32,108,111,99,97,116,101,100,114,101,99,111,114,100,105,110, +103,115,100,32,102,114,111,109,32,116,104,101,110,101,100,101,114,108,97,110,100 +,115,112,111,114,116,117,103,117,195,170,115,215,162,215,145,215,168,215,153,215 +,170,217,129,216,167,216,177,216,179,219,140,100,101,115,97,114,114,111,108,108, +111,99,111,109,101,110,116,97,114,105,111,101,100,117,99,97,99,105,195,179,110, +115,101,112,116,105,101,109,98,114,101,114,101,103,105,115,116,114,97,100,111, +100,105,114,101,99,99,105,195,179,110,117,98,105,99,97,99,105,195,179,110,112, +117,98,108,105,99,105,100,97,100,114,101,115,112,117,101,115,116,97,115,114,101, +115,117,108,116,97,100,111,115,105,109,112,111,114,116,97,110,116,101,114,101, +115,101,114,118,97,100,111,115,97,114,116,195,173,99,117,108,111,115,100,105,102 +,101,114,101,110,116,101,115,115,105,103,117,105,101,110,116,101,115,114,101,112 +,195,186,98,108,105,99,97,115,105,116,117,97,99,105,195,179,110,109,105,110,105, +115,116,101,114,105,111,112,114,105,118,97,99,105,100,97,100,100,105,114,101,99, +116,111,114,105,111,102,111,114,109,97,99,105,195,179,110,112,111,98,108,97,99, +105,195,179,110,112,114,101,115,105,100,101,110,116,101,99,111,110,116,101,110, +105,100,111,115,97,99,99,101,115,111,114,105,111,115,116,101,99,104,110,111,114, +97,116,105,112,101,114,115,111,110,97,108,101,115,99,97,116,101,103,111,114,195, +173,97,101,115,112,101,99,105,97,108,101,115,100,105,115,112,111,110,105,98,108, +101,97,99,116,117,97,108,105,100,97,100,114,101,102,101,114,101,110,99,105,97, +118,97,108,108,97,100,111,108,105,100,98,105,98,108,105,111,116,101,99,97,114, +101,108,97,99,105,111,110,101,115,99,97,108,101,110,100,97,114,105,111,112,111, +108,195,173,116,105,99,97,115,97,110,116,101,114,105,111,114,101,115,100,111,99, +117,109,101,110,116,111,115,110,97,116,117,114,97,108,101,122,97,109,97,116,101, +114,105,97,108,101,115,100,105,102,101,114,101,110,99,105,97,101,99,111,110,195, +179,109,105,99,97,116,114,97,110,115,112,111,114,116,101,114,111,100,114,195,173 +,103,117,101,122,112,97,114,116,105,99,105,112,97,114,101,110,99,117,101,110,116 +,114,97,110,100,105,115,99,117,115,105,195,179,110,101,115,116,114,117,99,116, +117,114,97,102,117,110,100,97,99,105,195,179,110,102,114,101,99,117,101,110,116, +101,115,112,101,114,109,97,110,101,110,116,101,116,111,116,97,108,109,101,110, +116,101,208,188,208,190,208,182,208,189,208,190,208,177,209,131,208,180,208,181, +209,130,208,188,208,190,208,182,208,181,209,130,208,178,209,128,208,181,208,188, +209,143,209,130,208,176,208,186,208,182,208,181,209,135,209,130,208,190,208,177, +209,139,208,177,208,190,208,187,208,181,208,181,208,190,209,135,208,181,208,189, +209,140,209,141,209,130,208,190,208,179,208,190,208,186,208,190,208,179,208,180, +208,176,208,191,208,190,209,129,208,187,208,181,208,178,209,129,208,181,208,179, +208,190,209,129,208,176,208,185,209,130,208,181,209,135,208,181,209,128,208,181, +208,183,208,188,208,190,208,179,209,131,209,130,209,129,208,176,208,185,209,130, +208,176,208,182,208,184,208,183,208,189,208,184,208,188,208,181,208,182,208,180, +209,131,208,177,209,131,208,180,209,131,209,130,208,159,208,190,208,184,209,129, +208,186,208,183,208,180,208,181,209,129,209,140,208,178,208,184,208,180,208,181, +208,190,209,129,208,178,209,143,208,183,208,184,208,189,209,131,208,182,208,189, +208,190,209,129,208,178,208,190,208,181,208,185,208,187,209,142,208,180,208,181, +208,185,208,191,208,190,209,128,208,189,208,190,208,188,208,189,208,190,208,179, +208,190,208,180,208,181,209,130,208,181,208,185,209,129,208,178,208,190,208,184, +209,133,208,191,209,128,208,176,208,178,208,176,209,130,208,176,208,186,208,190, +208,185,208,188,208,181,209,129,209,130,208,190,208,184,208,188,208,181,208,181, +209,130,208,182,208,184,208,183,208,189,209,140,208,190,208,180,208,189,208,190, +208,185,208,187,209,131,209,135,209,136,208,181,208,191,208,181,209,128,208,181, +208,180,209,135,208,176,209,129,209,130,208,184,209,135,208,176,209,129,209,130, +209,140,209,128,208,176,208,177,208,190,209,130,208,189,208,190,208,178,209,139, +209,133,208,191,209,128,208,176,208,178,208,190,209,129,208,190,208,177,208,190, +208,185,208,191,208,190,209,130,208,190,208,188,208,188,208,181,208,189,208,181, +208,181,209,135,208,184,209,129,208,187,208,181,208,189,208,190,208,178,209,139, +208,181,209,131,209,129,208,187,209,131,208,179,208,190,208,186,208,190,208,187, +208,190,208,189,208,176,208,183,208,176,208,180,209,130,208,176,208,186,208,190, +208,181,209,130,208,190,208,179,208,180,208,176,208,191,208,190,209,135,209,130, +208,184,208,159,208,190,209,129,208,187,208,181,209,130,208,176,208,186,208,184, +208,181,208,189,208,190,208,178,209,139,208,185,209,129,209,130,208,190,208,184, +209,130,209,130,208,176,208,186,208,184,209,133,209,129,209,128,208,176,208,183, +209,131,208,161,208,176,208,189,208,186,209,130,209,132,208,190,209,128,209,131, +208,188,208,154,208,190,208,179,208,180,208,176,208,186,208,189,208,184,208,179, +208,184,209,129,208,187,208,190,208,178,208,176,208,189,208,176,209,136,208,181, +208,185,208,189,208,176,208,185,209,130,208,184,209,129,208,178,208,190,208,184, +208,188,209,129,208,178,209,143,208,183,209,140,208,187,209,142,208,177,208,190, +208,185,209,135,208,176,209,129,209,130,208,190,209,129,209,128,208,181,208,180, +208,184,208,154,209,128,208,190,208,188,208,181,208,164,208,190,209,128,209,131, +208,188,209,128,209,139,208,189,208,186,208,181,209,129,209,130,208,176,208,187, +208,184,208,191,208,190,208,184,209,129,208,186,209,130,209,139,209,129,209,143, +209,135,208,188,208,181,209,129,209,143,209,134,209,134,208,181,208,189,209,130, +209,128,209,130,209,128,209,131,208,180,208,176,209,129,208,176,208,188,209,139, +209,133,209,128,209,139,208,189,208,186,208,176,208,157,208,190,208,178,209,139, +208,185,209,135,208,176,209,129,208,190,208,178,208,188,208,181,209,129,209,130, +208,176,209,132,208,184,208,187,209,140,208,188,208,188,208,176,209,128,209,130, +208,176,209,129,209,130,209,128,208,176,208,189,208,188,208,181,209,129,209,130, +208,181,209,130,208,181,208,186,209,129,209,130,208,189,208,176,209,136,208,184, +209,133,208,188,208,184,208,189,209,131,209,130,208,184,208,188,208,181,208,189, +208,184,208,184,208,188,208,181,209,142,209,130,208,189,208,190,208,188,208,181, +209,128,208,179,208,190,209,128,208,190,208,180,209,129,208,176,208,188,208,190, +208,188,209,141,209,130,208,190,208,188,209,131,208,186,208,190,208,189,209,134, +208,181,209,129,208,178,208,190,208,181,208,188,208,186,208,176,208,186,208,190, +208,185,208,144,209,128,209,133,208,184,208,178,217,133,217,134,216,170,216,175, +217,137,216,165,216,177,216,179,216,167,217,132,216,177,216,179,216,167,217,132, +216,169,216,167,217,132,216,185,216,167,217,133,217,131,216,170,216,168,217,135, +216,167,216,168,216,177,216,167,217,133,216,172,216,167,217,132,217,138,217,136, +217,133,216,167,217,132,216,181,217,136,216,177,216,172,216,175,217,138,216,175, +216,169,216,167,217,132,216,185,216,182,217,136,216,165,216,182,216,167,217,129, +216,169,216,167,217,132,217,130,216,179,217,133,216,167,217,132,216,185,216,167, +216,168,216,170,216,173,217,133,217,138,217,132,217,133,217,132,217,129,216,167, +216,170,217,133,217,132,216,170,217,130,217,137,216,170,216,185,216,175,217,138, +217,132,216,167,217,132,216,180,216,185,216,177,216,163,216,174,216,168,216,167, +216,177,216,170,216,183,217,136,217,138,216,177,216,185,217,132,217,138,217,131, +217,133,216,165,216,177,217,129,216,167,217,130,216,183,217,132,216,168,216,167, +216,170,216,167,217,132,217,132,216,186,216,169,216,170,216,177,216,170,217,138, +216,168,216,167,217,132,217,134,216,167,216,179,216,167,217,132,216,180,217,138, +216,174,217,133,217,134,216,170,216,175,217,138,216,167,217,132,216,185,216,177, +216,168,216,167,217,132,217,130,216,181,216,181,216,167,217,129,217,132,216,167, +217,133,216,185,217,132,217,138,217,135,216,167,216,170,216,173,216,175,217,138, +216,171,216,167,217,132,217,132,217,135,217,133,216,167,217,132,216,185,217,133, +217,132,217,133,217,131,216,170,216,168,216,169,217,138,217,133,217,131,217,134, +217,131,216,167,217,132,216,183,217,129,217,132,217,129,217,138,216,175,217,138, +217,136,216,165,216,175,216,167,216,177,216,169,216,170,216,167,216,177,217,138, +216,174,216,167,217,132,216,181,216,173,216,169,216,170,216,179,216,172,217,138, +217,132,216,167,217,132,217,136,217,130,216,170,216,185,217,134,216,175,217,133, +216,167,217,133,216,175,217,138,217,134,216,169,216,170,216,181,217,133,217,138, +217,133,216,163,216,177,216,180,217,138,217,129,216,167,217,132,216,176,217,138, +217,134,216,185,216,177,216,168,217,138,216,169,216,168,217,136,216,167,216,168, +216,169,216,163,217,132,216,185,216,167,216,168,216,167,217,132,216,179,217,129, +216,177,217,133,216,180,216,167,217,131,217,132,216,170,216,185,216,167,217,132, +217,137,216,167,217,132,216,163,217,136,217,132,216,167,217,132,216,179,217,134, +216,169,216,172,216,167,217,133,216,185,216,169,216,167,217,132,216,181,216,173, +217,129,216,167,217,132,216,175,217,138,217,134,217,131,217,132,217,133,216,167, +216,170,216,167,217,132,216,174,216,167,216,181,216,167,217,132,217,133,217,132, +217,129,216,163,216,185,216,182,216,167,216,161,217,131,216,170,216,167,216,168, +216,169,216,167,217,132,216,174,217,138,216,177,216,177,216,179,216,167,216,166, +217,132,216,167,217,132,217,130,217,132,216,168,216,167,217,132,216,163,216,175, +216,168,217,133,217,130,216,167,216,183,216,185,217,133,216,177,216,167,216,179, +217,132,217,133,217,134,216,183,217,130,216,169,216,167,217,132,217,131,216,170, +216,168,216,167,217,132,216,177,216,172,217,132,216,167,216,180,216,170,216,177, +217,131,216,167,217,132,217,130,216,175,217,133,217,138,216,185,216,183,217,138, +217,131,115,66,121,84,97,103,78,97,109,101,40,46,106,112,103,34,32,97,108,116,61 +,34,49,112,120,32,115,111,108,105,100,32,35,46,103,105,102,34,32,97,108,116,61, +34,116,114,97,110,115,112,97,114,101,110,116,105,110,102,111,114,109,97,116,105, +111,110,97,112,112,108,105,99,97,116,105,111,110,34,32,111,110,99,108,105,99,107 +,61,34,101,115,116,97,98,108,105,115,104,101,100,97,100,118,101,114,116,105,115, +105,110,103,46,112,110,103,34,32,97,108,116,61,34,101,110,118,105,114,111,110, +109,101,110,116,112,101,114,102,111,114,109,97,110,99,101,97,112,112,114,111,112 +,114,105,97,116,101,38,97,109,112,59,109,100,97,115,104,59,105,109,109,101,100, +105,97,116,101,108,121,60,47,115,116,114,111,110,103,62,60,47,114,97,116,104,101 +,114,32,116,104,97,110,116,101,109,112,101,114,97,116,117,114,101,100,101,118, +101,108,111,112,109,101,110,116,99,111,109,112,101,116,105,116,105,111,110,112, +108,97,99,101,104,111,108,100,101,114,118,105,115,105,98,105,108,105,116,121,58, +99,111,112,121,114,105,103,104,116,34,62,48,34,32,104,101,105,103,104,116,61,34, +101,118,101,110,32,116,104,111,117,103,104,114,101,112,108,97,99,101,109,101,110 +,116,100,101,115,116,105,110,97,116,105,111,110,67,111,114,112,111,114,97,116, +105,111,110,60,117,108,32,99,108,97,115,115,61,34,65,115,115,111,99,105,97,116, +105,111,110,105,110,100,105,118,105,100,117,97,108,115,112,101,114,115,112,101, +99,116,105,118,101,115,101,116,84,105,109,101,111,117,116,40,117,114,108,40,104, +116,116,112,58,47,47,109,97,116,104,101,109,97,116,105,99,115,109,97,114,103,105 +,110,45,116,111,112,58,101,118,101,110,116,117,97,108,108,121,32,100,101,115,99, +114,105,112,116,105,111,110,41,32,110,111,45,114,101,112,101,97,116,99,111,108, +108,101,99,116,105,111,110,115,46,74,80,71,124,116,104,117,109,98,124,112,97,114 +,116,105,99,105,112,97,116,101,47,104,101,97,100,62,60,98,111,100,121,102,108, +111,97,116,58,108,101,102,116,59,60,108,105,32,99,108,97,115,115,61,34,104,117, +110,100,114,101,100,115,32,111,102,10,10,72,111,119,101,118,101,114,44,32,99,111 +,109,112,111,115,105,116,105,111,110,99,108,101,97,114,58,98,111,116,104,59,99, +111,111,112,101,114,97,116,105,111,110,119,105,116,104,105,110,32,116,104,101,32 +,108,97,98,101,108,32,102,111,114,61,34,98,111,114,100,101,114,45,116,111,112,58 +,78,101,119,32,90,101,97,108,97,110,100,114,101,99,111,109,109,101,110,100,101, +100,112,104,111,116,111,103,114,97,112,104,121,105,110,116,101,114,101,115,116, +105,110,103,38,108,116,59,115,117,112,38,103,116,59,99,111,110,116,114,111,118, +101,114,115,121,78,101,116,104,101,114,108,97,110,100,115,97,108,116,101,114,110 +,97,116,105,118,101,109,97,120,108,101,110,103,116,104,61,34,115,119,105,116,122 +,101,114,108,97,110,100,68,101,118,101,108,111,112,109,101,110,116,101,115,115, +101,110,116,105,97,108,108,121,10,10,65,108,116,104,111,117,103,104,32,60,47,116 +,101,120,116,97,114,101,97,62,116,104,117,110,100,101,114,98,105,114,100,114,101 +,112,114,101,115,101,110,116,101,100,38,97,109,112,59,110,100,97,115,104,59,115, +112,101,99,117,108,97,116,105,111,110,99,111,109,109,117,110,105,116,105,101,115 +,108,101,103,105,115,108,97,116,105,111,110,101,108,101,99,116,114,111,110,105, +99,115,10,9,60,100,105,118,32,105,100,61,34,105,108,108,117,115,116,114,97,116, +101,100,101,110,103,105,110,101,101,114,105,110,103,116,101,114,114,105,116,111, +114,105,101,115,97,117,116,104,111,114,105,116,105,101,115,100,105,115,116,114, +105,98,117,116,101,100,54,34,32,104,101,105,103,104,116,61,34,115,97,110,115,45, +115,101,114,105,102,59,99,97,112,97,98,108,101,32,111,102,32,100,105,115,97,112, +112,101,97,114,101,100,105,110,116,101,114,97,99,116,105,118,101,108,111,111,107 +,105,110,103,32,102,111,114,105,116,32,119,111,117,108,100,32,98,101,65,102,103, +104,97,110,105,115,116,97,110,119,97,115,32,99,114,101,97,116,101,100,77,97,116, +104,46,102,108,111,111,114,40,115,117,114,114,111,117,110,100,105,110,103,99,97, +110,32,97,108,115,111,32,98,101,111,98,115,101,114,118,97,116,105,111,110,109,97 +,105,110,116,101,110,97,110,99,101,101,110,99,111,117,110,116,101,114,101,100,60 +,104,50,32,99,108,97,115,115,61,34,109,111,114,101,32,114,101,99,101,110,116,105 +,116,32,104,97,115,32,98,101,101,110,105,110,118,97,115,105,111,110,32,111,102, +41,46,103,101,116,84,105,109,101,40,41,102,117,110,100,97,109,101,110,116,97,108 +,68,101,115,112,105,116,101,32,116,104,101,34,62,60,100,105,118,32,105,100,61,34 +,105,110,115,112,105,114,97,116,105,111,110,101,120,97,109,105,110,97,116,105, +111,110,112,114,101,112,97,114,97,116,105,111,110,101,120,112,108,97,110,97,116, +105,111,110,60,105,110,112,117,116,32,105,100,61,34,60,47,97,62,60,47,115,112,97 +,110,62,118,101,114,115,105,111,110,115,32,111,102,105,110,115,116,114,117,109, +101,110,116,115,98,101,102,111,114,101,32,116,104,101,32,32,61,32,39,104,116,116 +,112,58,47,47,68,101,115,99,114,105,112,116,105,111,110,114,101,108,97,116,105, +118,101,108,121,32,46,115,117,98,115,116,114,105,110,103,40,101,97,99,104,32,111 +,102,32,116,104,101,101,120,112,101,114,105,109,101,110,116,115,105,110,102,108, +117,101,110,116,105,97,108,105,110,116,101,103,114,97,116,105,111,110,109,97,110 +,121,32,112,101,111,112,108,101,100,117,101,32,116,111,32,116,104,101,32,99,111, +109,98,105,110,97,116,105,111,110,100,111,32,110,111,116,32,104,97,118,101,77, +105,100,100,108,101,32,69,97,115,116,60,110,111,115,99,114,105,112,116,62,60,99, +111,112,121,114,105,103,104,116,34,32,112,101,114,104,97,112,115,32,116,104,101, +105,110,115,116,105,116,117,116,105,111,110,105,110,32,68,101,99,101,109,98,101, +114,97,114,114,97,110,103,101,109,101,110,116,109,111,115,116,32,102,97,109,111, +117,115,112,101,114,115,111,110,97,108,105,116,121,99,114,101,97,116,105,111,110 +,32,111,102,108,105,109,105,116,97,116,105,111,110,115,101,120,99,108,117,115, +105,118,101,108,121,115,111,118,101,114,101,105,103,110,116,121,45,99,111,110, +116,101,110,116,34,62,10,60,116,100,32,99,108,97,115,115,61,34,117,110,100,101, +114,103,114,111,117,110,100,112,97,114,97,108,108,101,108,32,116,111,100,111,99, +116,114,105,110,101,32,111,102,111,99,99,117,112,105,101,100,32,98,121,116,101, +114,109,105,110,111,108,111,103,121,82,101,110,97,105,115,115,97,110,99,101,97, +32,110,117,109,98,101,114,32,111,102,115,117,112,112,111,114,116,32,102,111,114, +101,120,112,108,111,114,97,116,105,111,110,114,101,99,111,103,110,105,116,105, +111,110,112,114,101,100,101,99,101,115,115,111,114,60,105,109,103,32,115,114,99, +61,34,47,60,104,49,32,99,108,97,115,115,61,34,112,117,98,108,105,99,97,116,105, +111,110,109,97,121,32,97,108,115,111,32,98,101,115,112,101,99,105,97,108,105,122 +,101,100,60,47,102,105,101,108,100,115,101,116,62,112,114,111,103,114,101,115, +115,105,118,101,109,105,108,108,105,111,110,115,32,111,102,115,116,97,116,101, +115,32,116,104,97,116,101,110,102,111,114,99,101,109,101,110,116,97,114,111,117, +110,100,32,116,104,101,32,111,110,101,32,97,110,111,116,104,101,114,46,112,97, +114,101,110,116,78,111,100,101,97,103,114,105,99,117,108,116,117,114,101,65,108, +116,101,114,110,97,116,105,118,101,114,101,115,101,97,114,99,104,101,114,115,116 +,111,119,97,114,100,115,32,116,104,101,77,111,115,116,32,111,102,32,116,104,101, +109,97,110,121,32,111,116,104,101,114,32,40,101,115,112,101,99,105,97,108,108, +121,60,116,100,32,119,105,100,116,104,61,34,59,119,105,100,116,104,58,49,48,48, +37,105,110,100,101,112,101,110,100,101,110,116,60,104,51,32,99,108,97,115,115,61 +,34,32,111,110,99,104,97,110,103,101,61,34,41,46,97,100,100,67,108,97,115,115,40 +,105,110,116,101,114,97,99,116,105,111,110,79,110,101,32,111,102,32,116,104,101, +32,100,97,117,103,104,116,101,114,32,111,102,97,99,99,101,115,115,111,114,105, +101,115,98,114,97,110,99,104,101,115,32,111,102,13,10,60,100,105,118,32,105,100, +61,34,116,104,101,32,108,97,114,103,101,115,116,100,101,99,108,97,114,97,116,105 +,111,110,114,101,103,117,108,97,116,105,111,110,115,73,110,102,111,114,109,97, +116,105,111,110,116,114,97,110,115,108,97,116,105,111,110,100,111,99,117,109,101 +,110,116,97,114,121,105,110,32,111,114,100,101,114,32,116,111,34,62,10,60,104, +101,97,100,62,10,60,34,32,104,101,105,103,104,116,61,34,49,97,99,114,111,115,115 +,32,116,104,101,32,111,114,105,101,110,116,97,116,105,111,110,41,59,60,47,115,99 +,114,105,112,116,62,105,109,112,108,101,109,101,110,116,101,100,99,97,110,32,98, +101,32,115,101,101,110,116,104,101,114,101,32,119,97,115,32,97,100,101,109,111, +110,115,116,114,97,116,101,99,111,110,116,97,105,110,101,114,34,62,99,111,110, +110,101,99,116,105,111,110,115,116,104,101,32,66,114,105,116,105,115,104,119,97, +115,32,119,114,105,116,116,101,110,33,105,109,112,111,114,116,97,110,116,59,112, +120,59,32,109,97,114,103,105,110,45,102,111,108,108,111,119,101,100,32,98,121,97 +,98,105,108,105,116,121,32,116,111,32,99,111,109,112,108,105,99,97,116,101,100, +100,117,114,105,110,103,32,116,104,101,32,105,109,109,105,103,114,97,116,105,111 +,110,97,108,115,111,32,99,97,108,108,101,100,60,104,52,32,99,108,97,115,115,61, +34,100,105,115,116,105,110,99,116,105,111,110,114,101,112,108,97,99,101,100,32, +98,121,103,111,118,101,114,110,109,101,110,116,115,108,111,99,97,116,105,111,110 +,32,111,102,105,110,32,78,111,118,101,109,98,101,114,119,104,101,116,104,101,114 +,32,116,104,101,60,47,112,62,10,60,47,100,105,118,62,97,99,113,117,105,115,105, +116,105,111,110,99,97,108,108,101,100,32,116,104,101,32,112,101,114,115,101,99, +117,116,105,111,110,100,101,115,105,103,110,97,116,105,111,110,123,102,111,110, +116,45,115,105,122,101,58,97,112,112,101,97,114,101,100,32,105,110,105,110,118, +101,115,116,105,103,97,116,101,101,120,112,101,114,105,101,110,99,101,100,109, +111,115,116,32,108,105,107,101,108,121,119,105,100,101,108,121,32,117,115,101, +100,100,105,115,99,117,115,115,105,111,110,115,112,114,101,115,101,110,99,101,32 +,111,102,32,40,100,111,99,117,109,101,110,116,46,101,120,116,101,110,115,105,118 +,101,108,121,73,116,32,104,97,115,32,98,101,101,110,105,116,32,100,111,101,115, +32,110,111,116,99,111,110,116,114,97,114,121,32,116,111,105,110,104,97,98,105, +116,97,110,116,115,105,109,112,114,111,118,101,109,101,110,116,115,99,104,111, +108,97,114,115,104,105,112,99,111,110,115,117,109,112,116,105,111,110,105,110, +115,116,114,117,99,116,105,111,110,102,111,114,32,101,120,97,109,112,108,101,111 +,110,101,32,111,114,32,109,111,114,101,112,120,59,32,112,97,100,100,105,110,103, +116,104,101,32,99,117,114,114,101,110,116,97,32,115,101,114,105,101,115,32,111, +102,97,114,101,32,117,115,117,97,108,108,121,114,111,108,101,32,105,110,32,116, +104,101,112,114,101,118,105,111,117,115,108,121,32,100,101,114,105,118,97,116, +105,118,101,115,101,118,105,100,101,110,99,101,32,111,102,101,120,112,101,114, +105,101,110,99,101,115,99,111,108,111,114,115,99,104,101,109,101,115,116,97,116, +101,100,32,116,104,97,116,99,101,114,116,105,102,105,99,97,116,101,60,47,97,62, +60,47,100,105,118,62,10,32,115,101,108,101,99,116,101,100,61,34,104,105,103,104, +32,115,99,104,111,111,108,114,101,115,112,111,110,115,101,32,116,111,99,111,109, +102,111,114,116,97,98,108,101,97,100,111,112,116,105,111,110,32,111,102,116,104, +114,101,101,32,121,101,97,114,115,116,104,101,32,99,111,117,110,116,114,121,105, +110,32,70,101,98,114,117,97,114,121,115,111,32,116,104,97,116,32,116,104,101,112 +,101,111,112,108,101,32,119,104,111,32,112,114,111,118,105,100,101,100,32,98,121 +,60,112,97,114,97,109,32,110,97,109,101,97,102,102,101,99,116,101,100,32,98,121, +105,110,32,116,101,114,109,115,32,111,102,97,112,112,111,105,110,116,109,101,110 +,116,73,83,79,45,56,56,53,57,45,49,34,119,97,115,32,98,111,114,110,32,105,110, +104,105,115,116,111,114,105,99,97,108,32,114,101,103,97,114,100,101,100,32,97, +115,109,101,97,115,117,114,101,109,101,110,116,105,115,32,98,97,115,101,100,32, +111,110,32,97,110,100,32,111,116,104,101,114,32,58,32,102,117,110,99,116,105,111 +,110,40,115,105,103,110,105,102,105,99,97,110,116,99,101,108,101,98,114,97,116, +105,111,110,116,114,97,110,115,109,105,116,116,101,100,47,106,115,47,106,113,117 +,101,114,121,46,105,115,32,107,110,111,119,110,32,97,115,116,104,101,111,114,101 +,116,105,99,97,108,32,116,97,98,105,110,100,101,120,61,34,105,116,32,99,111,117, +108,100,32,98,101,60,110,111,115,99,114,105,112,116,62,10,104,97,118,105,110,103 +,32,98,101,101,110,13,10,60,104,101,97,100,62,13,10,60,32,38,113,117,111,116,59, +84,104,101,32,99,111,109,112,105,108,97,116,105,111,110,104,101,32,104,97,100,32 +,98,101,101,110,112,114,111,100,117,99,101,100,32,98,121,112,104,105,108,111,115 +,111,112,104,101,114,99,111,110,115,116,114,117,99,116,101,100,105,110,116,101, +110,100,101,100,32,116,111,97,109,111,110,103,32,111,116,104,101,114,99,111,109, +112,97,114,101,100,32,116,111,116,111,32,115,97,121,32,116,104,97,116,69,110,103 +,105,110,101,101,114,105,110,103,97,32,100,105,102,102,101,114,101,110,116,114, +101,102,101,114,114,101,100,32,116,111,100,105,102,102,101,114,101,110,99,101, +115,98,101,108,105,101,102,32,116,104,97,116,112,104,111,116,111,103,114,97,112, +104,115,105,100,101,110,116,105,102,121,105,110,103,72,105,115,116,111,114,121, +32,111,102,32,82,101,112,117,98,108,105,99,32,111,102,110,101,99,101,115,115,97, +114,105,108,121,112,114,111,98,97,98,105,108,105,116,121,116,101,99,104,110,105, +99,97,108,108,121,108,101,97,118,105,110,103,32,116,104,101,115,112,101,99,116, +97,99,117,108,97,114,102,114,97,99,116,105,111,110,32,111,102,101,108,101,99,116 +,114,105,99,105,116,121,104,101,97,100,32,111,102,32,116,104,101,114,101,115,116 +,97,117,114,97,110,116,115,112,97,114,116,110,101,114,115,104,105,112,101,109, +112,104,97,115,105,115,32,111,110,109,111,115,116,32,114,101,99,101,110,116,115, +104,97,114,101,32,119,105,116,104,32,115,97,121,105,110,103,32,116,104,97,116, +102,105,108,108,101,100,32,119,105,116,104,100,101,115,105,103,110,101,100,32, +116,111,105,116,32,105,115,32,111,102,116,101,110,34,62,60,47,105,102,114,97,109 +,101,62,97,115,32,102,111,108,108,111,119,115,58,109,101,114,103,101,100,32,119, +105,116,104,116,104,114,111,117,103,104,32,116,104,101,99,111,109,109,101,114,99 +,105,97,108,32,112,111,105,110,116,101,100,32,111,117,116,111,112,112,111,114, +116,117,110,105,116,121,118,105,101,119,32,111,102,32,116,104,101,114,101,113, +117,105,114,101,109,101,110,116,100,105,118,105,115,105,111,110,32,111,102,112, +114,111,103,114,97,109,109,105,110,103,104,101,32,114,101,99,101,105,118,101,100 +,115,101,116,73,110,116,101,114,118,97,108,34,62,60,47,115,112,97,110,62,60,47, +105,110,32,78,101,119,32,89,111,114,107,97,100,100,105,116,105,111,110,97,108,32 +,99,111,109,112,114,101,115,115,105,111,110,10,10,60,100,105,118,32,105,100,61, +34,105,110,99,111,114,112,111,114,97,116,101,59,60,47,115,99,114,105,112,116,62, +60,97,116,116,97,99,104,69,118,101,110,116,98,101,99,97,109,101,32,116,104,101, +32,34,32,116,97,114,103,101,116,61,34,95,99,97,114,114,105,101,100,32,111,117, +116,83,111,109,101,32,111,102,32,116,104,101,115,99,105,101,110,99,101,32,97,110 +,100,116,104,101,32,116,105,109,101,32,111,102,67,111,110,116,97,105,110,101,114 +,34,62,109,97,105,110,116,97,105,110,105,110,103,67,104,114,105,115,116,111,112, +104,101,114,77,117,99,104,32,111,102,32,116,104,101,119,114,105,116,105,110,103, +115,32,111,102,34,32,104,101,105,103,104,116,61,34,50,115,105,122,101,32,111,102 +,32,116,104,101,118,101,114,115,105,111,110,32,111,102,32,109,105,120,116,117, +114,101,32,111,102,32,98,101,116,119,101,101,110,32,116,104,101,69,120,97,109, +112,108,101,115,32,111,102,101,100,117,99,97,116,105,111,110,97,108,99,111,109, +112,101,116,105,116,105,118,101,32,111,110,115,117,98,109,105,116,61,34,100,105, +114,101,99,116,111,114,32,111,102,100,105,115,116,105,110,99,116,105,118,101,47, +68,84,68,32,88,72,84,77,76,32,114,101,108,97,116,105,110,103,32,116,111,116,101, +110,100,101,110,99,121,32,116,111,112,114,111,118,105,110,99,101,32,111,102,119, +104,105,99,104,32,119,111,117,108,100,100,101,115,112,105,116,101,32,116,104,101 +,115,99,105,101,110,116,105,102,105,99,32,108,101,103,105,115,108,97,116,117,114 +,101,46,105,110,110,101,114,72,84,77,76,32,97,108,108,101,103,97,116,105,111,110 +,115,65,103,114,105,99,117,108,116,117,114,101,119,97,115,32,117,115,101,100,32, +105,110,97,112,112,114,111,97,99,104,32,116,111,105,110,116,101,108,108,105,103, +101,110,116,121,101,97,114,115,32,108,97,116,101,114,44,115,97,110,115,45,115, +101,114,105,102,100,101,116,101,114,109,105,110,105,110,103,80,101,114,102,111, +114,109,97,110,99,101,97,112,112,101,97,114,97,110,99,101,115,44,32,119,104,105, +99,104,32,105,115,32,102,111,117,110,100,97,116,105,111,110,115,97,98,98,114,101 +,118,105,97,116,101,100,104,105,103,104,101,114,32,116,104,97,110,115,32,102,114 +,111,109,32,116,104,101,32,105,110,100,105,118,105,100,117,97,108,32,99,111,109, +112,111,115,101,100,32,111,102,115,117,112,112,111,115,101,100,32,116,111,99,108 +,97,105,109,115,32,116,104,97,116,97,116,116,114,105,98,117,116,105,111,110,102, +111,110,116,45,115,105,122,101,58,49,101,108,101,109,101,110,116,115,32,111,102, +72,105,115,116,111,114,105,99,97,108,32,104,105,115,32,98,114,111,116,104,101, +114,97,116,32,116,104,101,32,116,105,109,101,97,110,110,105,118,101,114,115,97, +114,121,103,111,118,101,114,110,101,100,32,98,121,114,101,108,97,116,101,100,32, +116,111,32,117,108,116,105,109,97,116,101,108,121,32,105,110,110,111,118,97,116, +105,111,110,115,105,116,32,105,115,32,115,116,105,108,108,99,97,110,32,111,110, +108,121,32,98,101,100,101,102,105,110,105,116,105,111,110,115,116,111,71,77,84, +83,116,114,105,110,103,65,32,110,117,109,98,101,114,32,111,102,105,109,103,32,99 +,108,97,115,115,61,34,69,118,101,110,116,117,97,108,108,121,44,119,97,115,32,99, +104,97,110,103,101,100,111,99,99,117,114,114,101,100,32,105,110,110,101,105,103, +104,98,111,114,105,110,103,100,105,115,116,105,110,103,117,105,115,104,119,104, +101,110,32,104,101,32,119,97,115,105,110,116,114,111,100,117,99,105,110,103,116, +101,114,114,101,115,116,114,105,97,108,77,97,110,121,32,111,102,32,116,104,101, +97,114,103,117,101,115,32,116,104,97,116,97,110,32,65,109,101,114,105,99,97,110, +99,111,110,113,117,101,115,116,32,111,102,119,105,100,101,115,112,114,101,97,100 +,32,119,101,114,101,32,107,105,108,108,101,100,115,99,114,101,101,110,32,97,110, +100,32,73,110,32,111,114,100,101,114,32,116,111,101,120,112,101,99,116,101,100, +32,116,111,100,101,115,99,101,110,100,97,110,116,115,97,114,101,32,108,111,99,97 +,116,101,100,108,101,103,105,115,108,97,116,105,118,101,103,101,110,101,114,97, +116,105,111,110,115,32,98,97,99,107,103,114,111,117,110,100,109,111,115,116,32, +112,101,111,112,108,101,121,101,97,114,115,32,97,102,116,101,114,116,104,101,114 +,101,32,105,115,32,110,111,116,104,101,32,104,105,103,104,101,115,116,102,114, +101,113,117,101,110,116,108,121,32,116,104,101,121,32,100,111,32,110,111,116,97, +114,103,117,101,100,32,116,104,97,116,115,104,111,119,101,100,32,116,104,97,116, +112,114,101,100,111,109,105,110,97,110,116,116,104,101,111,108,111,103,105,99,97 +,108,98,121,32,116,104,101,32,116,105,109,101,99,111,110,115,105,100,101,114,105 +,110,103,115,104,111,114,116,45,108,105,118,101,100,60,47,115,112,97,110,62,60, +47,97,62,99,97,110,32,98,101,32,117,115,101,100,118,101,114,121,32,108,105,116, +116,108,101,111,110,101,32,111,102,32,116,104,101,32,104,97,100,32,97,108,114, +101,97,100,121,105,110,116,101,114,112,114,101,116,101,100,99,111,109,109,117, +110,105,99,97,116,101,102,101,97,116,117,114,101,115,32,111,102,103,111,118,101, +114,110,109,101,110,116,44,60,47,110,111,115,99,114,105,112,116,62,101,110,116, +101,114,101,100,32,116,104,101,34,32,104,101,105,103,104,116,61,34,51,73,110,100 +,101,112,101,110,100,101,110,116,112,111,112,117,108,97,116,105,111,110,115,108, +97,114,103,101,45,115,99,97,108,101,46,32,65,108,116,104,111,117,103,104,32,117, +115,101,100,32,105,110,32,116,104,101,100,101,115,116,114,117,99,116,105,111,110 +,112,111,115,115,105,98,105,108,105,116,121,115,116,97,114,116,105,110,103,32, +105,110,116,119,111,32,111,114,32,109,111,114,101,101,120,112,114,101,115,115, +105,111,110,115,115,117,98,111,114,100,105,110,97,116,101,108,97,114,103,101,114 +,32,116,104,97,110,104,105,115,116,111,114,121,32,97,110,100,60,47,111,112,116, +105,111,110,62,13,10,67,111,110,116,105,110,101,110,116,97,108,101,108,105,109, +105,110,97,116,105,110,103,119,105,108,108,32,110,111,116,32,98,101,112,114,97, +99,116,105,99,101,32,111,102,105,110,32,102,114,111,110,116,32,111,102,115,105, +116,101,32,111,102,32,116,104,101,101,110,115,117,114,101,32,116,104,97,116,116, +111,32,99,114,101,97,116,101,32,97,109,105,115,115,105,115,115,105,112,112,105, +112,111,116,101,110,116,105,97,108,108,121,111,117,116,115,116,97,110,100,105, +110,103,98,101,116,116,101,114,32,116,104,97,110,119,104,97,116,32,105,115,32, +110,111,119,115,105,116,117,97,116,101,100,32,105,110,109,101,116,97,32,110,97, +109,101,61,34,84,114,97,100,105,116,105,111,110,97,108,115,117,103,103,101,115, +116,105,111,110,115,84,114,97,110,115,108,97,116,105,111,110,116,104,101,32,102, +111,114,109,32,111,102,97,116,109,111,115,112,104,101,114,105,99,105,100,101,111 +,108,111,103,105,99,97,108,101,110,116,101,114,112,114,105,115,101,115,99,97,108 +,99,117,108,97,116,105,110,103,101,97,115,116,32,111,102,32,116,104,101,114,101, +109,110,97,110,116,115,32,111,102,112,108,117,103,105,110,115,112,97,103,101,47, +105,110,100,101,120,46,112,104,112,63,114,101,109,97,105,110,101,100,32,105,110, +116,114,97,110,115,102,111,114,109,101,100,72,101,32,119,97,115,32,97,108,115, +111,119,97,115,32,97,108,114,101,97,100,121,115,116,97,116,105,115,116,105,99,97 +,108,105,110,32,102,97,118,111,114,32,111,102,77,105,110,105,115,116,114,121,32, +111,102,109,111,118,101,109,101,110,116,32,111,102,102,111,114,109,117,108,97, +116,105,111,110,105,115,32,114,101,113,117,105,114,101,100,60,108,105,110,107,32 +,114,101,108,61,34,84,104,105,115,32,105,115,32,116,104,101,32,60,97,32,104,114, +101,102,61,34,47,112,111,112,117,108,97,114,105,122,101,100,105,110,118,111,108, +118,101,100,32,105,110,97,114,101,32,117,115,101,100,32,116,111,97,110,100,32, +115,101,118,101,114,97,108,109,97,100,101,32,98,121,32,116,104,101,115,101,101, +109,115,32,116,111,32,98,101,108,105,107,101,108,121,32,116,104,97,116,80,97,108 +,101,115,116,105,110,105,97,110,110,97,109,101,100,32,97,102,116,101,114,105,116 +,32,104,97,100,32,98,101,101,110,109,111,115,116,32,99,111,109,109,111,110,116, +111,32,114,101,102,101,114,32,116,111,98,117,116,32,116,104,105,115,32,105,115, +99,111,110,115,101,99,117,116,105,118,101,116,101,109,112,111,114,97,114,105,108 +,121,73,110,32,103,101,110,101,114,97,108,44,99,111,110,118,101,110,116,105,111, +110,115,116,97,107,101,115,32,112,108,97,99,101,115,117,98,100,105,118,105,115, +105,111,110,116,101,114,114,105,116,111,114,105,97,108,111,112,101,114,97,116, +105,111,110,97,108,112,101,114,109,97,110,101,110,116,108,121,119,97,115,32,108, +97,114,103,101,108,121,111,117,116,98,114,101,97,107,32,111,102,105,110,32,116, +104,101,32,112,97,115,116,102,111,108,108,111,119,105,110,103,32,97,32,120,109, +108,110,115,58,111,103,61,34,62,60,97,32,99,108,97,115,115,61,34,99,108,97,115, +115,61,34,116,101,120,116,67,111,110,118,101,114,115,105,111,110,32,109,97,121, +32,98,101,32,117,115,101,100,109,97,110,117,102,97,99,116,117,114,101,97,102,116 +,101,114,32,98,101,105,110,103,99,108,101,97,114,102,105,120,34,62,10,113,117, +101,115,116,105,111,110,32,111,102,119,97,115,32,101,108,101,99,116,101,100,116, +111,32,98,101,99,111,109,101,32,97,98,101,99,97,117,115,101,32,111,102,32,115, +111,109,101,32,112,101,111,112,108,101,105,110,115,112,105,114,101,100,32,98,121 +,115,117,99,99,101,115,115,102,117,108,32,97,32,116,105,109,101,32,119,104,101, +110,109,111,114,101,32,99,111,109,109,111,110,97,109,111,110,103,115,116,32,116, +104,101,97,110,32,111,102,102,105,99,105,97,108,119,105,100,116,104,58,49,48,48, +37,59,116,101,99,104,110,111,108,111,103,121,44,119,97,115,32,97,100,111,112,116 +,101,100,116,111,32,107,101,101,112,32,116,104,101,115,101,116,116,108,101,109, +101,110,116,115,108,105,118,101,32,98,105,114,116,104,115,105,110,100,101,120,46 +,104,116,109,108,34,67,111,110,110,101,99,116,105,99,117,116,97,115,115,105,103, +110,101,100,32,116,111,38,97,109,112,59,116,105,109,101,115,59,97,99,99,111,117, +110,116,32,102,111,114,97,108,105,103,110,61,114,105,103,104,116,116,104,101,32, +99,111,109,112,97,110,121,97,108,119,97,121,115,32,98,101,101,110,114,101,116, +117,114,110,101,100,32,116,111,105,110,118,111,108,118,101,109,101,110,116,66, +101,99,97,117,115,101,32,116,104,101,116,104,105,115,32,112,101,114,105,111,100, +34,32,110,97,109,101,61,34,113,34,32,99,111,110,102,105,110,101,100,32,116,111, +97,32,114,101,115,117,108,116,32,111,102,118,97,108,117,101,61,34,34,32,47,62, +105,115,32,97,99,116,117,97,108,108,121,69,110,118,105,114,111,110,109,101,110, +116,13,10,60,47,104,101,97,100,62,13,10,67,111,110,118,101,114,115,101,108,121, +44,62,10,60,100,105,118,32,105,100,61,34,48,34,32,119,105,100,116,104,61,34,49, +105,115,32,112,114,111,98,97,98,108,121,104,97,118,101,32,98,101,99,111,109,101, +99,111,110,116,114,111,108,108,105,110,103,116,104,101,32,112,114,111,98,108,101 +,109,99,105,116,105,122,101,110,115,32,111,102,112,111,108,105,116,105,99,105,97 +,110,115,114,101,97,99,104,101,100,32,116,104,101,97,115,32,101,97,114,108,121, +32,97,115,58,110,111,110,101,59,32,111,118,101,114,60,116,97,98,108,101,32,99, +101,108,108,118,97,108,105,100,105,116,121,32,111,102,100,105,114,101,99,116,108 +,121,32,116,111,111,110,109,111,117,115,101,100,111,119,110,119,104,101,114,101, +32,105,116,32,105,115,119,104,101,110,32,105,116,32,119,97,115,109,101,109,98, +101,114,115,32,111,102,32,114,101,108,97,116,105,111,110,32,116,111,97,99,99,111 +,109,109,111,100,97,116,101,97,108,111,110,103,32,119,105,116,104,32,73,110,32, +116,104,101,32,108,97,116,101,116,104,101,32,69,110,103,108,105,115,104,100,101, +108,105,99,105,111,117,115,34,62,116,104,105,115,32,105,115,32,110,111,116,116, +104,101,32,112,114,101,115,101,110,116,105,102,32,116,104,101,121,32,97,114,101, +97,110,100,32,102,105,110,97,108,108,121,97,32,109,97,116,116,101,114,32,111,102 +,13,10,9,60,47,100,105,118,62,13,10,13,10,60,47,115,99,114,105,112,116,62,102,97 +,115,116,101,114,32,116,104,97,110,109,97,106,111,114,105,116,121,32,111,102,97, +102,116,101,114,32,119,104,105,99,104,99,111,109,112,97,114,97,116,105,118,101, +116,111,32,109,97,105,110,116,97,105,110,105,109,112,114,111,118,101,32,116,104, +101,97,119,97,114,100,101,100,32,116,104,101,101,114,34,32,99,108,97,115,115,61, +34,102,114,97,109,101,98,111,114,100,101,114,114,101,115,116,111,114,97,116,105, +111,110,105,110,32,116,104,101,32,115,97,109,101,97,110,97,108,121,115,105,115, +32,111,102,116,104,101,105,114,32,102,105,114,115,116,68,117,114,105,110,103,32, +116,104,101,32,99,111,110,116,105,110,101,110,116,97,108,115,101,113,117,101,110 +,99,101,32,111,102,102,117,110,99,116,105,111,110,40,41,123,102,111,110,116,45, +115,105,122,101,58,32,119,111,114,107,32,111,110,32,116,104,101,60,47,115,99,114 +,105,112,116,62,10,60,98,101,103,105,110,115,32,119,105,116,104,106,97,118,97, +115,99,114,105,112,116,58,99,111,110,115,116,105,116,117,101,110,116,119,97,115, +32,102,111,117,110,100,101,100,101,113,117,105,108,105,98,114,105,117,109,97,115 +,115,117,109,101,32,116,104,97,116,105,115,32,103,105,118,101,110,32,98,121,110, +101,101,100,115,32,116,111,32,98,101,99,111,111,114,100,105,110,97,116,101,115, +116,104,101,32,118,97,114,105,111,117,115,97,114,101,32,112,97,114,116,32,111, +102,111,110,108,121,32,105,110,32,116,104,101,115,101,99,116,105,111,110,115,32, +111,102,105,115,32,97,32,99,111,109,109,111,110,116,104,101,111,114,105,101,115, +32,111,102,100,105,115,99,111,118,101,114,105,101,115,97,115,115,111,99,105,97, +116,105,111,110,101,100,103,101,32,111,102,32,116,104,101,115,116,114,101,110, +103,116,104,32,111,102,112,111,115,105,116,105,111,110,32,105,110,112,114,101, +115,101,110,116,45,100,97,121,117,110,105,118,101,114,115,97,108,108,121,116,111 +,32,102,111,114,109,32,116,104,101,98,117,116,32,105,110,115,116,101,97,100,99, +111,114,112,111,114,97,116,105,111,110,97,116,116,97,99,104,101,100,32,116,111, +105,115,32,99,111,109,109,111,110,108,121,114,101,97,115,111,110,115,32,102,111, +114,32,38,113,117,111,116,59,116,104,101,32,99,97,110,32,98,101,32,109,97,100, +101,119,97,115,32,97,98,108,101,32,116,111,119,104,105,99,104,32,109,101,97,110, +115,98,117,116,32,100,105,100,32,110,111,116,111,110,77,111,117,115,101,79,118, +101,114,97,115,32,112,111,115,115,105,98,108,101,111,112,101,114,97,116,101,100, +32,98,121,99,111,109,105,110,103,32,102,114,111,109,116,104,101,32,112,114,105, +109,97,114,121,97,100,100,105,116,105,111,110,32,111,102,102,111,114,32,115,101, +118,101,114,97,108,116,114,97,110,115,102,101,114,114,101,100,97,32,112,101,114, +105,111,100,32,111,102,97,114,101,32,97,98,108,101,32,116,111,104,111,119,101, +118,101,114,44,32,105,116,115,104,111,117,108,100,32,104,97,118,101,109,117,99, +104,32,108,97,114,103,101,114,10,9,60,47,115,99,114,105,112,116,62,97,100,111, +112,116,101,100,32,116,104,101,112,114,111,112,101,114,116,121,32,111,102,100, +105,114,101,99,116,101,100,32,98,121,101,102,102,101,99,116,105,118,101,108,121, +119,97,115,32,98,114,111,117,103,104,116,99,104,105,108,100,114,101,110,32,111, +102,80,114,111,103,114,97,109,109,105,110,103,108,111,110,103,101,114,32,116,104 +,97,110,109,97,110,117,115,99,114,105,112,116,115,119,97,114,32,97,103,97,105, +110,115,116,98,121,32,109,101,97,110,115,32,111,102,97,110,100,32,109,111,115, +116,32,111,102,115,105,109,105,108,97,114,32,116,111,32,112,114,111,112,114,105, +101,116,97,114,121,111,114,105,103,105,110,97,116,105,110,103,112,114,101,115, +116,105,103,105,111,117,115,103,114,97,109,109,97,116,105,99,97,108,101,120,112, +101,114,105,101,110,99,101,46,116,111,32,109,97,107,101,32,116,104,101,73,116,32 +,119,97,115,32,97,108,115,111,105,115,32,102,111,117,110,100,32,105,110,99,111, +109,112,101,116,105,116,111,114,115,105,110,32,116,104,101,32,85,46,83,46,114, +101,112,108,97,99,101,32,116,104,101,98,114,111,117,103,104,116,32,116,104,101, +99,97,108,99,117,108,97,116,105,111,110,102,97,108,108,32,111,102,32,116,104,101 +,116,104,101,32,103,101,110,101,114,97,108,112,114,97,99,116,105,99,97,108,108, +121,105,110,32,104,111,110,111,114,32,111,102,114,101,108,101,97,115,101,100,32, +105,110,114,101,115,105,100,101,110,116,105,97,108,97,110,100,32,115,111,109,101 +,32,111,102,107,105,110,103,32,111,102,32,116,104,101,114,101,97,99,116,105,111, +110,32,116,111,49,115,116,32,69,97,114,108,32,111,102,99,117,108,116,117,114,101 +,32,97,110,100,112,114,105,110,99,105,112,97,108,108,121,60,47,116,105,116,108, +101,62,10,32,32,116,104,101,121,32,99,97,110,32,98,101,98,97,99,107,32,116,111, +32,116,104,101,115,111,109,101,32,111,102,32,104,105,115,101,120,112,111,115,117 +,114,101,32,116,111,97,114,101,32,115,105,109,105,108,97,114,102,111,114,109,32, +111,102,32,116,104,101,97,100,100,70,97,118,111,114,105,116,101,99,105,116,105, +122,101,110,115,104,105,112,112,97,114,116,32,105,110,32,116,104,101,112,101,111 +,112,108,101,32,119,105,116,104,105,110,32,112,114,97,99,116,105,99,101,116,111, +32,99,111,110,116,105,110,117,101,38,97,109,112,59,109,105,110,117,115,59,97,112 +,112,114,111,118,101,100,32,98,121,32,116,104,101,32,102,105,114,115,116,32,97, +108,108,111,119,101,100,32,116,104,101,97,110,100,32,102,111,114,32,116,104,101, +102,117,110,99,116,105,111,110,105,110,103,112,108,97,121,105,110,103,32,116,104 +,101,115,111,108,117,116,105,111,110,32,116,111,104,101,105,103,104,116,61,34,48 +,34,32,105,110,32,104,105,115,32,98,111,111,107,109,111,114,101,32,116,104,97, +110,32,97,102,111,108,108,111,119,115,32,116,104,101,99,114,101,97,116,101,100, +32,116,104,101,112,114,101,115,101,110,99,101,32,105,110,38,110,98,115,112,59,60 +,47,116,100,62,110,97,116,105,111,110,97,108,105,115,116,116,104,101,32,105,100, +101,97,32,111,102,97,32,99,104,97,114,97,99,116,101,114,119,101,114,101,32,102, +111,114,99,101,100,32,99,108,97,115,115,61,34,98,116,110,100,97,121,115,32,111, +102,32,116,104,101,102,101,97,116,117,114,101,100,32,105,110,115,104,111,119,105 +,110,103,32,116,104,101,105,110,116,101,114,101,115,116,32,105,110,105,110,32, +112,108,97,99,101,32,111,102,116,117,114,110,32,111,102,32,116,104,101,116,104, +101,32,104,101,97,100,32,111,102,76,111,114,100,32,111,102,32,116,104,101,112, +111,108,105,116,105,99,97,108,108,121,104,97,115,32,105,116,115,32,111,119,110, +69,100,117,99,97,116,105,111,110,97,108,97,112,112,114,111,118,97,108,32,111,102 +,115,111,109,101,32,111,102,32,116,104,101,101,97,99,104,32,111,116,104,101,114, +44,98,101,104,97,118,105,111,114,32,111,102,97,110,100,32,98,101,99,97,117,115, +101,97,110,100,32,97,110,111,116,104,101,114,97,112,112,101,97,114,101,100,32, +111,110,114,101,99,111,114,100,101,100,32,105,110,98,108,97,99,107,38,113,117, +111,116,59,109,97,121,32,105,110,99,108,117,100,101,116,104,101,32,119,111,114, +108,100,39,115,99,97,110,32,108,101,97,100,32,116,111,114,101,102,101,114,115,32 +,116,111,32,97,98,111,114,100,101,114,61,34,48,34,32,103,111,118,101,114,110,109 +,101,110,116,32,119,105,110,110,105,110,103,32,116,104,101,114,101,115,117,108, +116,101,100,32,105,110,32,119,104,105,108,101,32,116,104,101,32,87,97,115,104, +105,110,103,116,111,110,44,116,104,101,32,115,117,98,106,101,99,116,99,105,116, +121,32,105,110,32,116,104,101,62,60,47,100,105,118,62,13,10,9,9,114,101,102,108, +101,99,116,32,116,104,101,116,111,32,99,111,109,112,108,101,116,101,98,101,99,97 +,109,101,32,109,111,114,101,114,97,100,105,111,97,99,116,105,118,101,114,101,106 +,101,99,116,101,100,32,98,121,119,105,116,104,111,117,116,32,97,110,121,104,105, +115,32,102,97,116,104,101,114,44,119,104,105,99,104,32,99,111,117,108,100,99,111 +,112,121,32,111,102,32,116,104,101,116,111,32,105,110,100,105,99,97,116,101,97, +32,112,111,108,105,116,105,99,97,108,97,99,99,111,117,110,116,115,32,111,102,99, +111,110,115,116,105,116,117,116,101,115,119,111,114,107,101,100,32,119,105,116, +104,101,114,60,47,97,62,60,47,108,105,62,111,102,32,104,105,115,32,108,105,102, +101,97,99,99,111,109,112,97,110,105,101,100,99,108,105,101,110,116,87,105,100, +116,104,112,114,101,118,101,110,116,32,116,104,101,76,101,103,105,115,108,97,116 +,105,118,101,100,105,102,102,101,114,101,110,116,108,121,116,111,103,101,116,104 +,101,114,32,105,110,104,97,115,32,115,101,118,101,114,97,108,102,111,114,32,97, +110,111,116,104,101,114,116,101,120,116,32,111,102,32,116,104,101,102,111,117, +110,100,101,100,32,116,104,101,101,32,119,105,116,104,32,116,104,101,32,105,115, +32,117,115,101,100,32,102,111,114,99,104,97,110,103,101,100,32,116,104,101,117, +115,117,97,108,108,121,32,116,104,101,112,108,97,99,101,32,119,104,101,114,101, +119,104,101,114,101,97,115,32,116,104,101,62,32,60,97,32,104,114,101,102,61,34, +34,62,60,97,32,104,114,101,102,61,34,116,104,101,109,115,101,108,118,101,115,44, +97,108,116,104,111,117,103,104,32,104,101,116,104,97,116,32,99,97,110,32,98,101, +116,114,97,100,105,116,105,111,110,97,108,114,111,108,101,32,111,102,32,116,104, +101,97,115,32,97,32,114,101,115,117,108,116,114,101,109,111,118,101,67,104,105, +108,100,100,101,115,105,103,110,101,100,32,98,121,119,101,115,116,32,111,102,32, +116,104,101,83,111,109,101,32,112,101,111,112,108,101,112,114,111,100,117,99,116 +,105,111,110,44,115,105,100,101,32,111,102,32,116,104,101,110,101,119,115,108, +101,116,116,101,114,115,117,115,101,100,32,98,121,32,116,104,101,100,111,119,110 +,32,116,111,32,116,104,101,97,99,99,101,112,116,101,100,32,98,121,108,105,118, +101,32,105,110,32,116,104,101,97,116,116,101,109,112,116,115,32,116,111,111,117, +116,115,105,100,101,32,116,104,101,102,114,101,113,117,101,110,99,105,101,115,72 +,111,119,101,118,101,114,44,32,105,110,112,114,111,103,114,97,109,109,101,114, +115,97,116,32,108,101,97,115,116,32,105,110,97,112,112,114,111,120,105,109,97, +116,101,97,108,116,104,111,117,103,104,32,105,116,119,97,115,32,112,97,114,116, +32,111,102,97,110,100,32,118,97,114,105,111,117,115,71,111,118,101,114,110,111, +114,32,111,102,116,104,101,32,97,114,116,105,99,108,101,116,117,114,110,101,100, +32,105,110,116,111,62,60,97,32,104,114,101,102,61,34,47,116,104,101,32,101,99, +111,110,111,109,121,105,115,32,116,104,101,32,109,111,115,116,109,111,115,116,32 +,119,105,100,101,108,121,119,111,117,108,100,32,108,97,116,101,114,97,110,100,32 +,112,101,114,104,97,112,115,114,105,115,101,32,116,111,32,116,104,101,111,99,99, +117,114,115,32,119,104,101,110,117,110,100,101,114,32,119,104,105,99,104,99,111, +110,100,105,116,105,111,110,115,46,116,104,101,32,119,101,115,116,101,114,110, +116,104,101,111,114,121,32,116,104,97,116,105,115,32,112,114,111,100,117,99,101, +100,116,104,101,32,99,105,116,121,32,111,102,105,110,32,119,104,105,99,104,32, +104,101,115,101,101,110,32,105,110,32,116,104,101,116,104,101,32,99,101,110,116, +114,97,108,98,117,105,108,100,105,110,103,32,111,102,109,97,110,121,32,111,102, +32,104,105,115,97,114,101,97,32,111,102,32,116,104,101,105,115,32,116,104,101,32 +,111,110,108,121,109,111,115,116,32,111,102,32,116,104,101,109,97,110,121,32,111 +,102,32,116,104,101,116,104,101,32,87,101,115,116,101,114,110,84,104,101,114,101 +,32,105,115,32,110,111,101,120,116,101,110,100,101,100,32,116,111,83,116,97,116, +105,115,116,105,99,97,108,99,111,108,115,112,97,110,61,50,32,124,115,104,111,114 +,116,32,115,116,111,114,121,112,111,115,115,105,98,108,101,32,116,111,116,111, +112,111,108,111,103,105,99,97,108,99,114,105,116,105,99,97,108,32,111,102,114, +101,112,111,114,116,101,100,32,116,111,97,32,67,104,114,105,115,116,105,97,110, +100,101,99,105,115,105,111,110,32,116,111,105,115,32,101,113,117,97,108,32,116, +111,112,114,111,98,108,101,109,115,32,111,102,84,104,105,115,32,99,97,110,32,98, +101,109,101,114,99,104,97,110,100,105,115,101,102,111,114,32,109,111,115,116,32, +111,102,110,111,32,101,118,105,100,101,110,99,101,101,100,105,116,105,111,110, +115,32,111,102,101,108,101,109,101,110,116,115,32,105,110,38,113,117,111,116,59, +46,32,84,104,101,99,111,109,47,105,109,97,103,101,115,47,119,104,105,99,104,32, +109,97,107,101,115,116,104,101,32,112,114,111,99,101,115,115,114,101,109,97,105, +110,115,32,116,104,101,108,105,116,101,114,97,116,117,114,101,44,105,115,32,97, +32,109,101,109,98,101,114,116,104,101,32,112,111,112,117,108,97,114,116,104,101, +32,97,110,99,105,101,110,116,112,114,111,98,108,101,109,115,32,105,110,116,105, +109,101,32,111,102,32,116,104,101,100,101,102,101,97,116,101,100,32,98,121,98, +111,100,121,32,111,102,32,116,104,101,97,32,102,101,119,32,121,101,97,114,115, +109,117,99,104,32,111,102,32,116,104,101,116,104,101,32,119,111,114,107,32,111, +102,67,97,108,105,102,111,114,110,105,97,44,115,101,114,118,101,100,32,97,115,32 +,97,103,111,118,101,114,110,109,101,110,116,46,99,111,110,99,101,112,116,115,32, +111,102,109,111,118,101,109,101,110,116,32,105,110,9,9,60,100,105,118,32,105,100 +,61,34,105,116,34,32,118,97,108,117,101,61,34,108,97,110,103,117,97,103,101,32, +111,102,97,115,32,116,104,101,121,32,97,114,101,112,114,111,100,117,99,101,100, +32,105,110,105,115,32,116,104,97,116,32,116,104,101,101,120,112,108,97,105,110, +32,116,104,101,100,105,118,62,60,47,100,105,118,62,10,72,111,119,101,118,101,114 +,32,116,104,101,108,101,97,100,32,116,111,32,116,104,101,9,60,97,32,104,114,101, +102,61,34,47,119,97,115,32,103,114,97,110,116,101,100,112,101,111,112,108,101,32 +,104,97,118,101,99,111,110,116,105,110,117,97,108,108,121,119,97,115,32,115,101, +101,110,32,97,115,97,110,100,32,114,101,108,97,116,101,100,116,104,101,32,114, +111,108,101,32,111,102,112,114,111,112,111,115,101,100,32,98,121,111,102,32,116, +104,101,32,98,101,115,116,101,97,99,104,32,111,116,104,101,114,46,67,111,110,115 +,116,97,110,116,105,110,101,112,101,111,112,108,101,32,102,114,111,109,100,105, +97,108,101,99,116,115,32,111,102,116,111,32,114,101,118,105,115,105,111,110,119, +97,115,32,114,101,110,97,109,101,100,97,32,115,111,117,114,99,101,32,111,102,116 +,104,101,32,105,110,105,116,105,97,108,108,97,117,110,99,104,101,100,32,105,110, +112,114,111,118,105,100,101,32,116,104,101,116,111,32,116,104,101,32,119,101,115 +,116,119,104,101,114,101,32,116,104,101,114,101,97,110,100,32,115,105,109,105, +108,97,114,98,101,116,119,101,101,110,32,116,119,111,105,115,32,97,108,115,111, +32,116,104,101,69,110,103,108,105,115,104,32,97,110,100,99,111,110,100,105,116, +105,111,110,115,44,116,104,97,116,32,105,116,32,119,97,115,101,110,116,105,116, +108,101,100,32,116,111,116,104,101,109,115,101,108,118,101,115,46,113,117,97,110 +,116,105,116,121,32,111,102,114,97,110,115,112,97,114,101,110,99,121,116,104,101 +,32,115,97,109,101,32,97,115,116,111,32,106,111,105,110,32,116,104,101,99,111, +117,110,116,114,121,32,97,110,100,116,104,105,115,32,105,115,32,116,104,101,84, +104,105,115,32,108,101,100,32,116,111,97,32,115,116,97,116,101,109,101,110,116, +99,111,110,116,114,97,115,116,32,116,111,108,97,115,116,73,110,100,101,120,79, +102,116,104,114,111,117,103,104,32,104,105,115,105,115,32,100,101,115,105,103, +110,101,100,116,104,101,32,116,101,114,109,32,105,115,105,115,32,112,114,111,118 +,105,100,101,100,112,114,111,116,101,99,116,32,116,104,101,110,103,60,47,97,62, +60,47,108,105,62,84,104,101,32,99,117,114,114,101,110,116,116,104,101,32,115,105 +,116,101,32,111,102,115,117,98,115,116,97,110,116,105,97,108,101,120,112,101,114 +,105,101,110,99,101,44,105,110,32,116,104,101,32,87,101,115,116,116,104,101,121, +32,115,104,111,117,108,100,115,108,111,118,101,110,196,141,105,110,97,99,111,109 +,101,110,116,97,114,105,111,115,117,110,105,118,101,114,115,105,100,97,100,99, +111,110,100,105,99,105,111,110,101,115,97,99,116,105,118,105,100,97,100,101,115, +101,120,112,101,114,105,101,110,99,105,97,116,101,99,110,111,108,111,103,195,173 +,97,112,114,111,100,117,99,99,105,195,179,110,112,117,110,116,117,97,99,105,195, +179,110,97,112,108,105,99,97,99,105,195,179,110,99,111,110,116,114,97,115,101, +195,177,97,99,97,116,101,103,111,114,195,173,97,115,114,101,103,105,115,116,114, +97,114,115,101,112,114,111,102,101,115,105,111,110,97,108,116,114,97,116,97,109, +105,101,110,116,111,114,101,103,195,173,115,116,114,97,116,101,115,101,99,114, +101,116,97,114,195,173,97,112,114,105,110,99,105,112,97,108,101,115,112,114,111, +116,101,99,99,105,195,179,110,105,109,112,111,114,116,97,110,116,101,115,105,109 +,112,111,114,116,97,110,99,105,97,112,111,115,105,98,105,108,105,100,97,100,105, +110,116,101,114,101,115,97,110,116,101,99,114,101,99,105,109,105,101,110,116,111 +,110,101,99,101,115,105,100,97,100,101,115,115,117,115,99,114,105,98,105,114,115 +,101,97,115,111,99,105,97,99,105,195,179,110,100,105,115,112,111,110,105,98,108, +101,115,101,118,97,108,117,97,99,105,195,179,110,101,115,116,117,100,105,97,110, +116,101,115,114,101,115,112,111,110,115,97,98,108,101,114,101,115,111,108,117,99 +,105,195,179,110,103,117,97,100,97,108,97,106,97,114,97,114,101,103,105,115,116, +114,97,100,111,115,111,112,111,114,116,117,110,105,100,97,100,99,111,109,101,114 +,99,105,97,108,101,115,102,111,116,111,103,114,97,102,195,173,97,97,117,116,111, +114,105,100,97,100,101,115,105,110,103,101,110,105,101,114,195,173,97,116,101, +108,101,118,105,115,105,195,179,110,99,111,109,112,101,116,101,110,99,105,97,111 +,112,101,114,97,99,105,111,110,101,115,101,115,116,97,98,108,101,99,105,100,111, +115,105,109,112,108,101,109,101,110,116,101,97,99,116,117,97,108,109,101,110,116 +,101,110,97,118,101,103,97,99,105,195,179,110,99,111,110,102,111,114,109,105,100 +,97,100,108,105,110,101,45,104,101,105,103,104,116,58,102,111,110,116,45,102,97, +109,105,108,121,58,34,32,58,32,34,104,116,116,112,58,47,47,97,112,112,108,105,99 +,97,116,105,111,110,115,108,105,110,107,34,32,104,114,101,102,61,34,115,112,101, +99,105,102,105,99,97,108,108,121,47,47,60,33,91,67,68,65,84,65,91,10,79,114,103, +97,110,105,122,97,116,105,111,110,100,105,115,116,114,105,98,117,116,105,111,110 +,48,112,120,59,32,104,101,105,103,104,116,58,114,101,108,97,116,105,111,110,115, +104,105,112,100,101,118,105,99,101,45,119,105,100,116,104,60,100,105,118,32,99, +108,97,115,115,61,34,60,108,97,98,101,108,32,102,111,114,61,34,114,101,103,105, +115,116,114,97,116,105,111,110,60,47,110,111,115,99,114,105,112,116,62,10,47,105 +,110,100,101,120,46,104,116,109,108,34,119,105,110,100,111,119,46,111,112,101, +110,40,32,33,105,109,112,111,114,116,97,110,116,59,97,112,112,108,105,99,97,116, +105,111,110,47,105,110,100,101,112,101,110,100,101,110,99,101,47,47,119,119,119, +46,103,111,111,103,108,101,111,114,103,97,110,105,122,97,116,105,111,110,97,117, +116,111,99,111,109,112,108,101,116,101,114,101,113,117,105,114,101,109,101,110, +116,115,99,111,110,115,101,114,118,97,116,105,118,101,60,102,111,114,109,32,110, +97,109,101,61,34,105,110,116,101,108,108,101,99,116,117,97,108,109,97,114,103, +105,110,45,108,101,102,116,58,49,56,116,104,32,99,101,110,116,117,114,121,97,110 +,32,105,109,112,111,114,116,97,110,116,105,110,115,116,105,116,117,116,105,111, +110,115,97,98,98,114,101,118,105,97,116,105,111,110,60,105,109,103,32,99,108,97, +115,115,61,34,111,114,103,97,110,105,115,97,116,105,111,110,99,105,118,105,108, +105,122,97,116,105,111,110,49,57,116,104,32,99,101,110,116,117,114,121,97,114,99 +,104,105,116,101,99,116,117,114,101,105,110,99,111,114,112,111,114,97,116,101, +100,50,48,116,104,32,99,101,110,116,117,114,121,45,99,111,110,116,97,105,110,101 +,114,34,62,109,111,115,116,32,110,111,116,97,98,108,121,47,62,60,47,97,62,60,47, +100,105,118,62,110,111,116,105,102,105,99,97,116,105,111,110,39,117,110,100,101, +102,105,110,101,100,39,41,70,117,114,116,104,101,114,109,111,114,101,44,98,101, +108,105,101,118,101,32,116,104,97,116,105,110,110,101,114,72,84,77,76,32,61,32, +112,114,105,111,114,32,116,111,32,116,104,101,100,114,97,109,97,116,105,99,97, +108,108,121,114,101,102,101,114,114,105,110,103,32,116,111,110,101,103,111,116, +105,97,116,105,111,110,115,104,101,97,100,113,117,97,114,116,101,114,115,83,111, +117,116,104,32,65,102,114,105,99,97,117,110,115,117,99,99,101,115,115,102,117, +108,80,101,110,110,115,121,108,118,97,110,105,97,65,115,32,97,32,114,101,115,117 +,108,116,44,60,104,116,109,108,32,108,97,110,103,61,34,38,108,116,59,47,115,117, +112,38,103,116,59,100,101,97,108,105,110,103,32,119,105,116,104,112,104,105,108, +97,100,101,108,112,104,105,97,104,105,115,116,111,114,105,99,97,108,108,121,41, +59,60,47,115,99,114,105,112,116,62,10,112,97,100,100,105,110,103,45,116,111,112, +58,101,120,112,101,114,105,109,101,110,116,97,108,103,101,116,65,116,116,114,105 +,98,117,116,101,105,110,115,116,114,117,99,116,105,111,110,115,116,101,99,104, +110,111,108,111,103,105,101,115,112,97,114,116,32,111,102,32,116,104,101,32,61, +102,117,110,99,116,105,111,110,40,41,123,115,117,98,115,99,114,105,112,116,105, +111,110,108,46,100,116,100,34,62,13,10,60,104,116,103,101,111,103,114,97,112,104 +,105,99,97,108,67,111,110,115,116,105,116,117,116,105,111,110,39,44,32,102,117, +110,99,116,105,111,110,40,115,117,112,112,111,114,116,101,100,32,98,121,97,103, +114,105,99,117,108,116,117,114,97,108,99,111,110,115,116,114,117,99,116,105,111, +110,112,117,98,108,105,99,97,116,105,111,110,115,102,111,110,116,45,115,105,122, +101,58,32,49,97,32,118,97,114,105,101,116,121,32,111,102,60,100,105,118,32,115, +116,121,108,101,61,34,69,110,99,121,99,108,111,112,101,100,105,97,105,102,114,97 +,109,101,32,115,114,99,61,34,100,101,109,111,110,115,116,114,97,116,101,100,97, +99,99,111,109,112,108,105,115,104,101,100,117,110,105,118,101,114,115,105,116, +105,101,115,68,101,109,111,103,114,97,112,104,105,99,115,41,59,60,47,115,99,114, +105,112,116,62,60,100,101,100,105,99,97,116,101,100,32,116,111,107,110,111,119, +108,101,100,103,101,32,111,102,115,97,116,105,115,102,97,99,116,105,111,110,112, +97,114,116,105,99,117,108,97,114,108,121,60,47,100,105,118,62,60,47,100,105,118, +62,69,110,103,108,105,115,104,32,40,85,83,41,97,112,112,101,110,100,67,104,105, +108,100,40,116,114,97,110,115,109,105,115,115,105,111,110,115,46,32,72,111,119, +101,118,101,114,44,32,105,110,116,101,108,108,105,103,101,110,99,101,34,32,116, +97,98,105,110,100,101,120,61,34,102,108,111,97,116,58,114,105,103,104,116,59,67, +111,109,109,111,110,119,101,97,108,116,104,114,97,110,103,105,110,103,32,102,114 +,111,109,105,110,32,119,104,105,99,104,32,116,104,101,97,116,32,108,101,97,115, +116,32,111,110,101,114,101,112,114,111,100,117,99,116,105,111,110,101,110,99,121 +,99,108,111,112,101,100,105,97,59,102,111,110,116,45,115,105,122,101,58,49,106, +117,114,105,115,100,105,99,116,105,111,110,97,116,32,116,104,97,116,32,116,105, +109,101,34,62,60,97,32,99,108,97,115,115,61,34,73,110,32,97,100,100,105,116,105, +111,110,44,100,101,115,99,114,105,112,116,105,111,110,43,99,111,110,118,101,114, +115,97,116,105,111,110,99,111,110,116,97,99,116,32,119,105,116,104,105,115,32, +103,101,110,101,114,97,108,108,121,114,34,32,99,111,110,116,101,110,116,61,34, +114,101,112,114,101,115,101,110,116,105,110,103,38,108,116,59,109,97,116,104,38, +103,116,59,112,114,101,115,101,110,116,97,116,105,111,110,111,99,99,97,115,105, +111,110,97,108,108,121,60,105,109,103,32,119,105,100,116,104,61,34,110,97,118, +105,103,97,116,105,111,110,34,62,99,111,109,112,101,110,115,97,116,105,111,110, +99,104,97,109,112,105,111,110,115,104,105,112,109,101,100,105,97,61,34,97,108, +108,34,32,118,105,111,108,97,116,105,111,110,32,111,102,114,101,102,101,114,101, +110,99,101,32,116,111,114,101,116,117,114,110,32,116,114,117,101,59,83,116,114, +105,99,116,47,47,69,78,34,32,116,114,97,110,115,97,99,116,105,111,110,115,105, +110,116,101,114,118,101,110,116,105,111,110,118,101,114,105,102,105,99,97,116, +105,111,110,73,110,102,111,114,109,97,116,105,111,110,32,100,105,102,102,105,99, +117,108,116,105,101,115,67,104,97,109,112,105,111,110,115,104,105,112,99,97,112, +97,98,105,108,105,116,105,101,115,60,33,91,101,110,100,105,102,93,45,45,62,125, +10,60,47,115,99,114,105,112,116,62,10,67,104,114,105,115,116,105,97,110,105,116, +121,102,111,114,32,101,120,97,109,112,108,101,44,80,114,111,102,101,115,115,105, +111,110,97,108,114,101,115,116,114,105,99,116,105,111,110,115,115,117,103,103, +101,115,116,32,116,104,97,116,119,97,115,32,114,101,108,101,97,115,101,100,40, +115,117,99,104,32,97,115,32,116,104,101,114,101,109,111,118,101,67,108,97,115, +115,40,117,110,101,109,112,108,111,121,109,101,110,116,116,104,101,32,65,109,101 +,114,105,99,97,110,115,116,114,117,99,116,117,114,101,32,111,102,47,105,110,100, +101,120,46,104,116,109,108,32,112,117,98,108,105,115,104,101,100,32,105,110,115, +112,97,110,32,99,108,97,115,115,61,34,34,62,60,97,32,104,114,101,102,61,34,47, +105,110,116,114,111,100,117,99,116,105,111,110,98,101,108,111,110,103,105,110, +103,32,116,111,99,108,97,105,109,101,100,32,116,104,97,116,99,111,110,115,101, +113,117,101,110,99,101,115,60,109,101,116,97,32,110,97,109,101,61,34,71,117,105, +100,101,32,116,111,32,116,104,101,111,118,101,114,119,104,101,108,109,105,110, +103,97,103,97,105,110,115,116,32,116,104,101,32,99,111,110,99,101,110,116,114,97 +,116,101,100,44,10,46,110,111,110,116,111,117,99,104,32,111,98,115,101,114,118, +97,116,105,111,110,115,60,47,97,62,10,60,47,100,105,118,62,10,102,32,40,100,111, +99,117,109,101,110,116,46,98,111,114,100,101,114,58,32,49,112,120,32,123,102,111 +,110,116,45,115,105,122,101,58,49,116,114,101,97,116,109,101,110,116,32,111,102, +48,34,32,104,101,105,103,104,116,61,34,49,109,111,100,105,102,105,99,97,116,105, +111,110,73,110,100,101,112,101,110,100,101,110,99,101,100,105,118,105,100,101, +100,32,105,110,116,111,103,114,101,97,116,101,114,32,116,104,97,110,97,99,104, +105,101,118,101,109,101,110,116,115,101,115,116,97,98,108,105,115,104,105,110, +103,74,97,118,97,83,99,114,105,112,116,34,32,110,101,118,101,114,116,104,101,108 +,101,115,115,115,105,103,110,105,102,105,99,97,110,99,101,66,114,111,97,100,99, +97,115,116,105,110,103,62,38,110,98,115,112,59,60,47,116,100,62,99,111,110,116, +97,105,110,101,114,34,62,10,115,117,99,104,32,97,115,32,116,104,101,32,105,110, +102,108,117,101,110,99,101,32,111,102,97,32,112,97,114,116,105,99,117,108,97,114 +,115,114,99,61,39,104,116,116,112,58,47,47,110,97,118,105,103,97,116,105,111,110 +,34,32,104,97,108,102,32,111,102,32,116,104,101,32,115,117,98,115,116,97,110,116 +,105,97,108,32,38,110,98,115,112,59,60,47,100,105,118,62,97,100,118,97,110,116, +97,103,101,32,111,102,100,105,115,99,111,118,101,114,121,32,111,102,102,117,110, +100,97,109,101,110,116,97,108,32,109,101,116,114,111,112,111,108,105,116,97,110, +116,104,101,32,111,112,112,111,115,105,116,101,34,32,120,109,108,58,108,97,110, +103,61,34,100,101,108,105,98,101,114,97,116,101,108,121,97,108,105,103,110,61,99 +,101,110,116,101,114,101,118,111,108,117,116,105,111,110,32,111,102,112,114,101, +115,101,114,118,97,116,105,111,110,105,109,112,114,111,118,101,109,101,110,116, +115,98,101,103,105,110,110,105,110,103,32,105,110,74,101,115,117,115,32,67,104, +114,105,115,116,80,117,98,108,105,99,97,116,105,111,110,115,100,105,115,97,103, +114,101,101,109,101,110,116,116,101,120,116,45,97,108,105,103,110,58,114,44,32, +102,117,110,99,116,105,111,110,40,41,115,105,109,105,108,97,114,105,116,105,101, +115,98,111,100,121,62,60,47,104,116,109,108,62,105,115,32,99,117,114,114,101,110 +,116,108,121,97,108,112,104,97,98,101,116,105,99,97,108,105,115,32,115,111,109, +101,116,105,109,101,115,116,121,112,101,61,34,105,109,97,103,101,47,109,97,110, +121,32,111,102,32,116,104,101,32,102,108,111,119,58,104,105,100,100,101,110,59, +97,118,97,105,108,97,98,108,101,32,105,110,100,101,115,99,114,105,98,101,32,116, +104,101,101,120,105,115,116,101,110,99,101,32,111,102,97,108,108,32,111,118,101, +114,32,116,104,101,116,104,101,32,73,110,116,101,114,110,101,116,9,60,117,108,32 +,99,108,97,115,115,61,34,105,110,115,116,97,108,108,97,116,105,111,110,110,101, +105,103,104,98,111,114,104,111,111,100,97,114,109,101,100,32,102,111,114,99,101, +115,114,101,100,117,99,105,110,103,32,116,104,101,99,111,110,116,105,110,117,101 +,115,32,116,111,78,111,110,101,116,104,101,108,101,115,115,44,116,101,109,112, +101,114,97,116,117,114,101,115,10,9,9,60,97,32,104,114,101,102,61,34,99,108,111, +115,101,32,116,111,32,116,104,101,101,120,97,109,112,108,101,115,32,111,102,32, +105,115,32,97,98,111,117,116,32,116,104,101,40,115,101,101,32,98,101,108,111,119 +,41,46,34,32,105,100,61,34,115,101,97,114,99,104,112,114,111,102,101,115,115,105 +,111,110,97,108,105,115,32,97,118,97,105,108,97,98,108,101,116,104,101,32,111, +102,102,105,99,105,97,108,9,9,60,47,115,99,114,105,112,116,62,10,10,9,9,60,100, +105,118,32,105,100,61,34,97,99,99,101,108,101,114,97,116,105,111,110,116,104,114 +,111,117,103,104,32,116,104,101,32,72,97,108,108,32,111,102,32,70,97,109,101,100 +,101,115,99,114,105,112,116,105,111,110,115,116,114,97,110,115,108,97,116,105, +111,110,115,105,110,116,101,114,102,101,114,101,110,99,101,32,116,121,112,101,61 +,39,116,101,120,116,47,114,101,99,101,110,116,32,121,101,97,114,115,105,110,32, +116,104,101,32,119,111,114,108,100,118,101,114,121,32,112,111,112,117,108,97,114 +,123,98,97,99,107,103,114,111,117,110,100,58,116,114,97,100,105,116,105,111,110, +97,108,32,115,111,109,101,32,111,102,32,116,104,101,32,99,111,110,110,101,99,116 +,101,100,32,116,111,101,120,112,108,111,105,116,97,116,105,111,110,101,109,101, +114,103,101,110,99,101,32,111,102,99,111,110,115,116,105,116,117,116,105,111,110 +,65,32,72,105,115,116,111,114,121,32,111,102,115,105,103,110,105,102,105,99,97, +110,116,32,109,97,110,117,102,97,99,116,117,114,101,100,101,120,112,101,99,116, +97,116,105,111,110,115,62,60,110,111,115,99,114,105,112,116,62,60,99,97,110,32, +98,101,32,102,111,117,110,100,98,101,99,97,117,115,101,32,116,104,101,32,104,97, +115,32,110,111,116,32,98,101,101,110,110,101,105,103,104,98,111,117,114,105,110, +103,119,105,116,104,111,117,116,32,116,104,101,32,97,100,100,101,100,32,116,111, +32,116,104,101,9,60,108,105,32,99,108,97,115,115,61,34,105,110,115,116,114,117, +109,101,110,116,97,108,83,111,118,105,101,116,32,85,110,105,111,110,97,99,107, +110,111,119,108,101,100,103,101,100,119,104,105,99,104,32,99,97,110,32,98,101, +110,97,109,101,32,102,111,114,32,116,104,101,97,116,116,101,110,116,105,111,110, +32,116,111,97,116,116,101,109,112,116,115,32,116,111,32,100,101,118,101,108,111, +112,109,101,110,116,115,73,110,32,102,97,99,116,44,32,116,104,101,60,108,105,32, +99,108,97,115,115,61,34,97,105,109,112,108,105,99,97,116,105,111,110,115,115,117 +,105,116,97,98,108,101,32,102,111,114,109,117,99,104,32,111,102,32,116,104,101, +32,99,111,108,111,110,105,122,97,116,105,111,110,112,114,101,115,105,100,101,110 +,116,105,97,108,99,97,110,99,101,108,66,117,98,98,108,101,32,73,110,102,111,114, +109,97,116,105,111,110,109,111,115,116,32,111,102,32,116,104,101,32,105,115,32, +100,101,115,99,114,105,98,101,100,114,101,115,116,32,111,102,32,116,104,101,32, +109,111,114,101,32,111,114,32,108,101,115,115,105,110,32,83,101,112,116,101,109, +98,101,114,73,110,116,101,108,108,105,103,101,110,99,101,115,114,99,61,34,104, +116,116,112,58,47,47,112,120,59,32,104,101,105,103,104,116,58,32,97,118,97,105, +108,97,98,108,101,32,116,111,109,97,110,117,102,97,99,116,117,114,101,114,104, +117,109,97,110,32,114,105,103,104,116,115,108,105,110,107,32,104,114,101,102,61, +34,47,97,118,97,105,108,97,98,105,108,105,116,121,112,114,111,112,111,114,116, +105,111,110,97,108,111,117,116,115,105,100,101,32,116,104,101,32,97,115,116,114, +111,110,111,109,105,99,97,108,104,117,109,97,110,32,98,101,105,110,103,115,110, +97,109,101,32,111,102,32,116,104,101,32,97,114,101,32,102,111,117,110,100,32,105 +,110,97,114,101,32,98,97,115,101,100,32,111,110,115,109,97,108,108,101,114,32, +116,104,97,110,97,32,112,101,114,115,111,110,32,119,104,111,101,120,112,97,110, +115,105,111,110,32,111,102,97,114,103,117,105,110,103,32,116,104,97,116,110,111, +119,32,107,110,111,119,110,32,97,115,73,110,32,116,104,101,32,101,97,114,108,121 +,105,110,116,101,114,109,101,100,105,97,116,101,100,101,114,105,118,101,100,32, +102,114,111,109,83,99,97,110,100,105,110,97,118,105,97,110,60,47,97,62,60,47,100 +,105,118,62,13,10,99,111,110,115,105,100,101,114,32,116,104,101,97,110,32,101, +115,116,105,109,97,116,101,100,116,104,101,32,78,97,116,105,111,110,97,108,60, +100,105,118,32,105,100,61,34,112,97,103,114,101,115,117,108,116,105,110,103,32, +105,110,99,111,109,109,105,115,115,105,111,110,101,100,97,110,97,108,111,103,111 +,117,115,32,116,111,97,114,101,32,114,101,113,117,105,114,101,100,47,117,108,62, +10,60,47,100,105,118,62,10,119,97,115,32,98,97,115,101,100,32,111,110,97,110,100 +,32,98,101,99,97,109,101,32,97,38,110,98,115,112,59,38,110,98,115,112,59,116,34, +32,118,97,108,117,101,61,34,34,32,119,97,115,32,99,97,112,116,117,114,101,100, +110,111,32,109,111,114,101,32,116,104,97,110,114,101,115,112,101,99,116,105,118, +101,108,121,99,111,110,116,105,110,117,101,32,116,111,32,62,13,10,60,104,101,97, +100,62,13,10,60,119,101,114,101,32,99,114,101,97,116,101,100,109,111,114,101,32, +103,101,110,101,114,97,108,105,110,102,111,114,109,97,116,105,111,110,32,117,115 +,101,100,32,102,111,114,32,116,104,101,105,110,100,101,112,101,110,100,101,110, +116,32,116,104,101,32,73,109,112,101,114,105,97,108,99,111,109,112,111,110,101, +110,116,32,111,102,116,111,32,116,104,101,32,110,111,114,116,104,105,110,99,108, +117,100,101,32,116,104,101,32,67,111,110,115,116,114,117,99,116,105,111,110,115, +105,100,101,32,111,102,32,116,104,101,32,119,111,117,108,100,32,110,111,116,32, +98,101,102,111,114,32,105,110,115,116,97,110,99,101,105,110,118,101,110,116,105, +111,110,32,111,102,109,111,114,101,32,99,111,109,112,108,101,120,99,111,108,108, +101,99,116,105,118,101,108,121,98,97,99,107,103,114,111,117,110,100,58,32,116, +101,120,116,45,97,108,105,103,110,58,32,105,116,115,32,111,114,105,103,105,110, +97,108,105,110,116,111,32,97,99,99,111,117,110,116,116,104,105,115,32,112,114, +111,99,101,115,115,97,110,32,101,120,116,101,110,115,105,118,101,104,111,119,101 +,118,101,114,44,32,116,104,101,116,104,101,121,32,97,114,101,32,110,111,116,114, +101,106,101,99,116,101,100,32,116,104,101,99,114,105,116,105,99,105,115,109,32, +111,102,100,117,114,105,110,103,32,119,104,105,99,104,112,114,111,98,97,98,108, +121,32,116,104,101,116,104,105,115,32,97,114,116,105,99,108,101,40,102,117,110, +99,116,105,111,110,40,41,123,73,116,32,115,104,111,117,108,100,32,98,101,97,110, +32,97,103,114,101,101,109,101,110,116,97,99,99,105,100,101,110,116,97,108,108, +121,100,105,102,102,101,114,115,32,102,114,111,109,65,114,99,104,105,116,101,99, +116,117,114,101,98,101,116,116,101,114,32,107,110,111,119,110,97,114,114,97,110, +103,101,109,101,110,116,115,105,110,102,108,117,101,110,99,101,32,111,110,97,116 +,116,101,110,100,101,100,32,116,104,101,105,100,101,110,116,105,99,97,108,32,116 +,111,115,111,117,116,104,32,111,102,32,116,104,101,112,97,115,115,32,116,104,114 +,111,117,103,104,120,109,108,34,32,116,105,116,108,101,61,34,119,101,105,103,104 +,116,58,98,111,108,100,59,99,114,101,97,116,105,110,103,32,116,104,101,100,105, +115,112,108,97,121,58,110,111,110,101,114,101,112,108,97,99,101,100,32,116,104, +101,60,105,109,103,32,115,114,99,61,34,47,105,104,116,116,112,115,58,47,47,119, +119,119,46,87,111,114,108,100,32,87,97,114,32,73,73,116,101,115,116,105,109,111, +110,105,97,108,115,102,111,117,110,100,32,105,110,32,116,104,101,114,101,113,117 +,105,114,101,100,32,116,111,32,97,110,100,32,116,104,97,116,32,116,104,101,98, +101,116,119,101,101,110,32,116,104,101,32,119,97,115,32,100,101,115,105,103,110, +101,100,99,111,110,115,105,115,116,115,32,111,102,32,99,111,110,115,105,100,101, +114,97,98,108,121,112,117,98,108,105,115,104,101,100,32,98,121,116,104,101,32, +108,97,110,103,117,97,103,101,67,111,110,115,101,114,118,97,116,105,111,110,99, +111,110,115,105,115,116,101,100,32,111,102,114,101,102,101,114,32,116,111,32,116 +,104,101,98,97,99,107,32,116,111,32,116,104,101,32,99,115,115,34,32,109,101,100, +105,97,61,34,80,101,111,112,108,101,32,102,114,111,109,32,97,118,97,105,108,97, +98,108,101,32,111,110,112,114,111,118,101,100,32,116,111,32,98,101,115,117,103, +103,101,115,116,105,111,110,115,34,119,97,115,32,107,110,111,119,110,32,97,115, +118,97,114,105,101,116,105,101,115,32,111,102,108,105,107,101,108,121,32,116,111 +,32,98,101,99,111,109,112,114,105,115,101,100,32,111,102,115,117,112,112,111,114 +,116,32,116,104,101,32,104,97,110,100,115,32,111,102,32,116,104,101,99,111,117, +112,108,101,100,32,119,105,116,104,99,111,110,110,101,99,116,32,97,110,100,32,98 +,111,114,100,101,114,58,110,111,110,101,59,112,101,114,102,111,114,109,97,110,99 +,101,115,98,101,102,111,114,101,32,98,101,105,110,103,108,97,116,101,114,32,98, +101,99,97,109,101,99,97,108,99,117,108,97,116,105,111,110,115,111,102,116,101, +110,32,99,97,108,108,101,100,114,101,115,105,100,101,110,116,115,32,111,102,109, +101,97,110,105,110,103,32,116,104,97,116,62,60,108,105,32,99,108,97,115,115,61, +34,101,118,105,100,101,110,99,101,32,102,111,114,101,120,112,108,97,110,97,116, +105,111,110,115,101,110,118,105,114,111,110,109,101,110,116,115,34,62,60,47,97, +62,60,47,100,105,118,62,119,104,105,99,104,32,97,108,108,111,119,115,73,110,116, +114,111,100,117,99,116,105,111,110,100,101,118,101,108,111,112,101,100,32,98,121 +,97,32,119,105,100,101,32,114,97,110,103,101,111,110,32,98,101,104,97,108,102,32 +,111,102,118,97,108,105,103,110,61,34,116,111,112,34,112,114,105,110,99,105,112, +108,101,32,111,102,97,116,32,116,104,101,32,116,105,109,101,44,60,47,110,111,115 +,99,114,105,112,116,62,13,115,97,105,100,32,116,111,32,104,97,118,101,105,110,32 +,116,104,101,32,102,105,114,115,116,119,104,105,108,101,32,111,116,104,101,114, +115,104,121,112,111,116,104,101,116,105,99,97,108,112,104,105,108,111,115,111, +112,104,101,114,115,112,111,119,101,114,32,111,102,32,116,104,101,99,111,110,116 +,97,105,110,101,100,32,105,110,112,101,114,102,111,114,109,101,100,32,98,121,105 +,110,97,98,105,108,105,116,121,32,116,111,119,101,114,101,32,119,114,105,116,116 +,101,110,115,112,97,110,32,115,116,121,108,101,61,34,105,110,112,117,116,32,110, +97,109,101,61,34,116,104,101,32,113,117,101,115,116,105,111,110,105,110,116,101, +110,100,101,100,32,102,111,114,114,101,106,101,99,116,105,111,110,32,111,102,105 +,109,112,108,105,101,115,32,116,104,97,116,105,110,118,101,110,116,101,100,32, +116,104,101,116,104,101,32,115,116,97,110,100,97,114,100,119,97,115,32,112,114, +111,98,97,98,108,121,108,105,110,107,32,98,101,116,119,101,101,110,112,114,111, +102,101,115,115,111,114,32,111,102,105,110,116,101,114,97,99,116,105,111,110,115 +,99,104,97,110,103,105,110,103,32,116,104,101,73,110,100,105,97,110,32,79,99,101 +,97,110,32,99,108,97,115,115,61,34,108,97,115,116,119,111,114,107,105,110,103,32 +,119,105,116,104,39,104,116,116,112,58,47,47,119,119,119,46,121,101,97,114,115, +32,98,101,102,111,114,101,84,104,105,115,32,119,97,115,32,116,104,101,114,101,99 +,114,101,97,116,105,111,110,97,108,101,110,116,101,114,105,110,103,32,116,104, +101,109,101,97,115,117,114,101,109,101,110,116,115,97,110,32,101,120,116,114,101 +,109,101,108,121,118,97,108,117,101,32,111,102,32,116,104,101,115,116,97,114,116 +,32,111,102,32,116,104,101,10,60,47,115,99,114,105,112,116,62,10,10,97,110,32, +101,102,102,111,114,116,32,116,111,105,110,99,114,101,97,115,101,32,116,104,101, +116,111,32,116,104,101,32,115,111,117,116,104,115,112,97,99,105,110,103,61,34,48 +,34,62,115,117,102,102,105,99,105,101,110,116,108,121,116,104,101,32,69,117,114, +111,112,101,97,110,99,111,110,118,101,114,116,101,100,32,116,111,99,108,101,97, +114,84,105,109,101,111,117,116,100,105,100,32,110,111,116,32,104,97,118,101,99, +111,110,115,101,113,117,101,110,116,108,121,102,111,114,32,116,104,101,32,110, +101,120,116,101,120,116,101,110,115,105,111,110,32,111,102,101,99,111,110,111, +109,105,99,32,97,110,100,97,108,116,104,111,117,103,104,32,116,104,101,97,114, +101,32,112,114,111,100,117,99,101,100,97,110,100,32,119,105,116,104,32,116,104, +101,105,110,115,117,102,102,105,99,105,101,110,116,103,105,118,101,110,32,98,121 +,32,116,104,101,115,116,97,116,105,110,103,32,116,104,97,116,101,120,112,101,110 +,100,105,116,117,114,101,115,60,47,115,112,97,110,62,60,47,97,62,10,116,104,111, +117,103,104,116,32,116,104,97,116,111,110,32,116,104,101,32,98,97,115,105,115,99 +,101,108,108,112,97,100,100,105,110,103,61,105,109,97,103,101,32,111,102,32,116, +104,101,114,101,116,117,114,110,105,110,103,32,116,111,105,110,102,111,114,109, +97,116,105,111,110,44,115,101,112,97,114,97,116,101,100,32,98,121,97,115,115,97, +115,115,105,110,97,116,101,100,115,34,32,99,111,110,116,101,110,116,61,34,97,117 +,116,104,111,114,105,116,121,32,111,102,110,111,114,116,104,119,101,115,116,101, +114,110,60,47,100,105,118,62,10,60,100,105,118,32,34,62,60,47,100,105,118,62,13, +10,32,32,99,111,110,115,117,108,116,97,116,105,111,110,99,111,109,109,117,110, +105,116,121,32,111,102,116,104,101,32,110,97,116,105,111,110,97,108,105,116,32, +115,104,111,117,108,100,32,98,101,112,97,114,116,105,99,105,112,97,110,116,115, +32,97,108,105,103,110,61,34,108,101,102,116,116,104,101,32,103,114,101,97,116, +101,115,116,115,101,108,101,99,116,105,111,110,32,111,102,115,117,112,101,114, +110,97,116,117,114,97,108,100,101,112,101,110,100,101,110,116,32,111,110,105,115 +,32,109,101,110,116,105,111,110,101,100,97,108,108,111,119,105,110,103,32,116, +104,101,119,97,115,32,105,110,118,101,110,116,101,100,97,99,99,111,109,112,97, +110,121,105,110,103,104,105,115,32,112,101,114,115,111,110,97,108,97,118,97,105, +108,97,98,108,101,32,97,116,115,116,117,100,121,32,111,102,32,116,104,101,111, +110,32,116,104,101,32,111,116,104,101,114,101,120,101,99,117,116,105,111,110,32, +111,102,72,117,109,97,110,32,82,105,103,104,116,115,116,101,114,109,115,32,111, +102,32,116,104,101,97,115,115,111,99,105,97,116,105,111,110,115,114,101,115,101, +97,114,99,104,32,97,110,100,115,117,99,99,101,101,100,101,100,32,98,121,100,101, +102,101,97,116,101,100,32,116,104,101,97,110,100,32,102,114,111,109,32,116,104, +101,98,117,116,32,116,104,101,121,32,97,114,101,99,111,109,109,97,110,100,101, +114,32,111,102,115,116,97,116,101,32,111,102,32,116,104,101,121,101,97,114,115, +32,111,102,32,97,103,101,116,104,101,32,115,116,117,100,121,32,111,102,60,117, +108,32,99,108,97,115,115,61,34,115,112,108,97,99,101,32,105,110,32,116,104,101, +119,104,101,114,101,32,104,101,32,119,97,115,60,108,105,32,99,108,97,115,115,61, +34,102,116,104,101,114,101,32,97,114,101,32,110,111,119,104,105,99,104,32,98,101 +,99,97,109,101,104,101,32,112,117,98,108,105,115,104,101,100,101,120,112,114,101 +,115,115,101,100,32,105,110,116,111,32,119,104,105,99,104,32,116,104,101,99,111, +109,109,105,115,115,105,111,110,101,114,102,111,110,116,45,119,101,105,103,104, +116,58,116,101,114,114,105,116,111,114,121,32,111,102,101,120,116,101,110,115, +105,111,110,115,34,62,82,111,109,97,110,32,69,109,112,105,114,101,101,113,117,97 +,108,32,116,111,32,116,104,101,73,110,32,99,111,110,116,114,97,115,116,44,104, +111,119,101,118,101,114,44,32,97,110,100,105,115,32,116,121,112,105,99,97,108, +108,121,97,110,100,32,104,105,115,32,119,105,102,101,40,97,108,115,111,32,99,97, +108,108,101,100,62,60,117,108,32,99,108,97,115,115,61,34,101,102,102,101,99,116, +105,118,101,108,121,32,101,118,111,108,118,101,100,32,105,110,116,111,115,101, +101,109,32,116,111,32,104,97,118,101,119,104,105,99,104,32,105,115,32,116,104, +101,116,104,101,114,101,32,119,97,115,32,110,111,97,110,32,101,120,99,101,108, +108,101,110,116,97,108,108,32,111,102,32,116,104,101,115,101,100,101,115,99,114, +105,98,101,100,32,98,121,73,110,32,112,114,97,99,116,105,99,101,44,98,114,111,97 +,100,99,97,115,116,105,110,103,99,104,97,114,103,101,100,32,119,105,116,104,114, +101,102,108,101,99,116,101,100,32,105,110,115,117,98,106,101,99,116,101,100,32, +116,111,109,105,108,105,116,97,114,121,32,97,110,100,116,111,32,116,104,101,32, +112,111,105,110,116,101,99,111,110,111,109,105,99,97,108,108,121,115,101,116,84, +97,114,103,101,116,105,110,103,97,114,101,32,97,99,116,117,97,108,108,121,118, +105,99,116,111,114,121,32,111,118,101,114,40,41,59,60,47,115,99,114,105,112,116, +62,99,111,110,116,105,110,117,111,117,115,108,121,114,101,113,117,105,114,101, +100,32,102,111,114,101,118,111,108,117,116,105,111,110,97,114,121,97,110,32,101, +102,102,101,99,116,105,118,101,110,111,114,116,104,32,111,102,32,116,104,101,44, +32,119,104,105,99,104,32,119,97,115,32,102,114,111,110,116,32,111,102,32,116,104 +,101,111,114,32,111,116,104,101,114,119,105,115,101,115,111,109,101,32,102,111, +114,109,32,111,102,104,97,100,32,110,111,116,32,98,101,101,110,103,101,110,101, +114,97,116,101,100,32,98,121,105,110,102,111,114,109,97,116,105,111,110,46,112, +101,114,109,105,116,116,101,100,32,116,111,105,110,99,108,117,100,101,115,32,116 +,104,101,100,101,118,101,108,111,112,109,101,110,116,44,101,110,116,101,114,101, +100,32,105,110,116,111,116,104,101,32,112,114,101,118,105,111,117,115,99,111,110 +,115,105,115,116,101,110,116,108,121,97,114,101,32,107,110,111,119,110,32,97,115 +,116,104,101,32,102,105,101,108,100,32,111,102,116,104,105,115,32,116,121,112, +101,32,111,102,103,105,118,101,110,32,116,111,32,116,104,101,116,104,101,32,116, +105,116,108,101,32,111,102,99,111,110,116,97,105,110,115,32,116,104,101,105,110, +115,116,97,110,99,101,115,32,111,102,105,110,32,116,104,101,32,110,111,114,116, +104,100,117,101,32,116,111,32,116,104,101,105,114,97,114,101,32,100,101,115,105, +103,110,101,100,99,111,114,112,111,114,97,116,105,111,110,115,119,97,115,32,116, +104,97,116,32,116,104,101,111,110,101,32,111,102,32,116,104,101,115,101,109,111, +114,101,32,112,111,112,117,108,97,114,115,117,99,99,101,101,100,101,100,32,105, +110,115,117,112,112,111,114,116,32,102,114,111,109,105,110,32,100,105,102,102, +101,114,101,110,116,100,111,109,105,110,97,116,101,100,32,98,121,100,101,115,105 +,103,110,101,100,32,102,111,114,111,119,110,101,114,115,104,105,112,32,111,102, +97,110,100,32,112,111,115,115,105,98,108,121,115,116,97,110,100,97,114,100,105, +122,101,100,114,101,115,112,111,110,115,101,84,101,120,116,119,97,115,32,105,110 +,116,101,110,100,101,100,114,101,99,101,105,118,101,100,32,116,104,101,97,115, +115,117,109,101,100,32,116,104,97,116,97,114,101,97,115,32,111,102,32,116,104, +101,112,114,105,109,97,114,105,108,121,32,105,110,116,104,101,32,98,97,115,105, +115,32,111,102,105,110,32,116,104,101,32,115,101,110,115,101,97,99,99,111,117, +110,116,115,32,102,111,114,100,101,115,116,114,111,121,101,100,32,98,121,97,116, +32,108,101,97,115,116,32,116,119,111,119,97,115,32,100,101,99,108,97,114,101,100 +,99,111,117,108,100,32,110,111,116,32,98,101,83,101,99,114,101,116,97,114,121,32 +,111,102,97,112,112,101,97,114,32,116,111,32,98,101,109,97,114,103,105,110,45, +116,111,112,58,49,47,94,92,115,43,124,92,115,43,36,47,103,101,41,123,116,104,114 +,111,119,32,101,125,59,116,104,101,32,115,116,97,114,116,32,111,102,116,119,111, +32,115,101,112,97,114,97,116,101,108,97,110,103,117,97,103,101,32,97,110,100,119 +,104,111,32,104,97,100,32,98,101,101,110,111,112,101,114,97,116,105,111,110,32, +111,102,100,101,97,116,104,32,111,102,32,116,104,101,114,101,97,108,32,110,117, +109,98,101,114,115,9,60,108,105,110,107,32,114,101,108,61,34,112,114,111,118,105 +,100,101,100,32,116,104,101,116,104,101,32,115,116,111,114,121,32,111,102,99,111 +,109,112,101,116,105,116,105,111,110,115,101,110,103,108,105,115,104,32,40,85,75 +,41,101,110,103,108,105,115,104,32,40,85,83,41,208,156,208,190,208,189,208,179, +208,190,208,187,208,161,209,128,208,191,209,129,208,186,208,184,209,129,209,128, +208,191,209,129,208,186,208,184,209,129,209,128,208,191,209,129,208,186,208,190, +217,132,216,185,216,177,216,168,217,138,216,169,230,173,163,233,171,148,228,184, +173,230,150,135,231,174,128,228,189,147,228,184,173,230,150,135,231,185,129,228, +189,147,228,184,173,230,150,135,230,156,137,233,153,144,229,133,172,229,143,184, +228,186,186,230,176,145,230,148,191,229,186,156,233,152,191,233,135,140,229,183, +180,229,183,180,231,164,190,228,188,154,228,184,187,228,185,137,230,147,141,228, +189,156,231,179,187,231,187,159,230,148,191,231,173,150,230,179,149,232,167,132, +105,110,102,111,114,109,97,99,105,195,179,110,104,101,114,114,97,109,105,101,110 +,116,97,115,101,108,101,99,116,114,195,179,110,105,99,111,100,101,115,99,114,105 +,112,99,105,195,179,110,99,108,97,115,105,102,105,99,97,100,111,115,99,111,110, +111,99,105,109,105,101,110,116,111,112,117,98,108,105,99,97,99,105,195,179,110, +114,101,108,97,99,105,111,110,97,100,97,115,105,110,102,111,114,109,195,161,116, +105,99,97,114,101,108,97,99,105,111,110,97,100,111,115,100,101,112,97,114,116,97 +,109,101,110,116,111,116,114,97,98,97,106,97,100,111,114,101,115,100,105,114,101 +,99,116,97,109,101,110,116,101,97,121,117,110,116,97,109,105,101,110,116,111,109 +,101,114,99,97,100,111,76,105,98,114,101,99,111,110,116,195,161,99,116,101,110, +111,115,104,97,98,105,116,97,99,105,111,110,101,115,99,117,109,112,108,105,109, +105,101,110,116,111,114,101,115,116,97,117,114,97,110,116,101,115,100,105,115, +112,111,115,105,99,105,195,179,110,99,111,110,115,101,99,117,101,110,99,105,97, +101,108,101,99,116,114,195,179,110,105,99,97,97,112,108,105,99,97,99,105,111,110 +,101,115,100,101,115,99,111,110,101,99,116,97,100,111,105,110,115,116,97,108,97, +99,105,195,179,110,114,101,97,108,105,122,97,99,105,195,179,110,117,116,105,108, +105,122,97,99,105,195,179,110,101,110,99,105,99,108,111,112,101,100,105,97,101, +110,102,101,114,109,101,100,97,100,101,115,105,110,115,116,114,117,109,101,110, +116,111,115,101,120,112,101,114,105,101,110,99,105,97,115,105,110,115,116,105, +116,117,99,105,195,179,110,112,97,114,116,105,99,117,108,97,114,101,115,115,117, +98,99,97,116,101,103,111,114,105,97,209,130,208,190,208,187,209,140,208,186,208, +190,208,160,208,190,209,129,209,129,208,184,208,184,209,128,208,176,208,177,208, +190,209,130,209,139,208,177,208,190,208,187,209,140,209,136,208,181,208,191,209, +128,208,190,209,129,209,130,208,190,208,188,208,190,208,182,208,181,209,130,208, +181,208,180,209,128,209,131,208,179,208,184,209,133,209,129,208,187,209,131,209, +135,208,176,208,181,209,129,208,181,208,185,209,135,208,176,209,129,208,178,209, +129,208,181,208,179,208,180,208,176,208,160,208,190,209,129,209,129,208,184,209, +143,208,156,208,190,209,129,208,186,208,178,208,181,208,180,209,128,209,131,208, +179,208,184,208,181,208,179,208,190,209,128,208,190,208,180,208,176,208,178,208, +190,208,191,209,128,208,190,209,129,208,180,208,176,208,189,208,189,209,139,209, +133,208,180,208,190,208,187,208,182,208,189,209,139,208,184,208,188,208,181,208, +189,208,189,208,190,208,156,208,190,209,129,208,186,208,178,209,139,209,128,209, +131,208,177,208,187,208,181,208,185,208,156,208,190,209,129,208,186,208,178,208, +176,209,129,209,130,209,128,208,176,208,189,209,139,208,189,208,184,209,135,208, +181,208,179,208,190,209,128,208,176,208,177,208,190,209,130,208,181,208,180,208, +190,208,187,208,182,208,181,208,189,209,131,209,129,208,187,209,131,208,179,208, +184,209,130,208,181,208,191,208,181,209,128,209,140,208,158,208,180,208,189,208, +176,208,186,208,190,208,191,208,190,209,130,208,190,208,188,209,131,209,128,208, +176,208,177,208,190,209,130,209,131,208,176,208,191,209,128,208,181,208,187,209, +143,208,178,208,190,208,190,208,177,209,137,208,181,208,190,208,180,208,189,208, +190,208,179,208,190,209,129,208,178,208,190,208,181,208,179,208,190,209,129,209, +130,208,176,209,130,209,140,208,184,208,180,209,128,209,131,208,179,208,190,208, +185,209,132,208,190,209,128,209,131,208,188,208,181,209,133,208,190,209,128,208, +190,209,136,208,190,208,191,209,128,208,190,209,130,208,184,208,178,209,129,209, +129,209,139,208,187,208,186,208,176,208,186,208,176,208,182,208,180,209,139,208, +185,208,178,208,187,208,176,209,129,209,130,208,184,208,179,209,128,209,131,208, +191,208,191,209,139,208,178,208,188,208,181,209,129,209,130,208,181,209,128,208, +176,208,177,208,190,209,130,208,176,209,129,208,186,208,176,208,183,208,176,208, +187,208,191,208,181,209,128,208,178,209,139,208,185,208,180,208,181,208,187,208, +176,209,130,209,140,208,180,208,181,208,189,209,140,208,179,208,184,208,191,208, +181,209,128,208,184,208,190,208,180,208,177,208,184,208,183,208,189,208,181,209, +129,208,190,209,129,208,189,208,190,208,178,208,181,208,188,208,190,208,188,208, +181,208,189,209,130,208,186,209,131,208,191,208,184,209,130,209,140,208,180,208, +190,208,187,208,182,208,189,208,176,209,128,208,176,208,188,208,186,208,176,209, +133,208,189,208,176,209,135,208,176,208,187,208,190,208,160,208,176,208,177,208, +190,209,130,208,176,208,162,208,190,208,187,209,140,208,186,208,190,209,129,208, +190,208,178,209,129,208,181,208,188,208,178,209,130,208,190,209,128,208,190,208, +185,208,189,208,176,209,135,208,176,208,187,208,176,209,129,208,191,208,184,209, +129,208,190,208,186,209,129,208,187,209,131,208,182,208,177,209,139,209,129,208, +184,209,129,209,130,208,181,208,188,208,191,208,181,209,135,208,176,209,130,208, +184,208,189,208,190,208,178,208,190,208,179,208,190,208,191,208,190,208,188,208, +190,209,137,208,184,209,129,208,176,208,185,209,130,208,190,208,178,208,191,208, +190,209,135,208,181,208,188,209,131,208,191,208,190,208,188,208,190,209,137,209, +140,208,180,208,190,208,187,208,182,208,189,208,190,209,129,209,129,209,139,208, +187,208,186,208,184,208,177,209,139,209,129,209,130,209,128,208,190,208,180,208, +176,208,189,208,189,209,139,208,181,208,188,208,189,208,190,208,179,208,184,208, +181,208,191,209,128,208,190,208,181,208,186,209,130,208,161,208,181,208,185,209, +135,208,176,209,129,208,188,208,190,208,180,208,181,208,187,208,184,209,130,208, +176,208,186,208,190,208,179,208,190,208,190,208,189,208,187,208,176,208,185,208, +189,208,179,208,190,209,128,208,190,208,180,208,181,208,178,208,181,209,128,209, +129,208,184,209,143,209,129,209,130,209,128,208,176,208,189,208,181,209,132,208, +184,208,187,209,140,208,188,209,139,209,131,209,128,208,190,208,178,208,189,209, +143,209,128,208,176,208,183,208,189,209,139,209,133,208,184,209,129,208,186,208, +176,209,130,209,140,208,189,208,181,208,180,208,181,208,187,209,142,209,143,208, +189,208,178,208,176,209,128,209,143,208,188,208,181,208,189,209,140,209,136,208, +181,208,188,208,189,208,190,208,179,208,184,209,133,208,180,208,176,208,189,208, +189,208,190,208,185,208,183,208,189,208,176,209,135,208,184,209,130,208,189,208, +181,208,187,209,140,208,183,209,143,209,132,208,190,209,128,209,131,208,188,208, +176,208,162,208,181,208,191,208,181,209,128,209,140,208,188,208,181,209,129,209, +143,209,134,208,176,208,183,208,176,209,137,208,184,209,130,209,139,208,155,209, +131,209,135,209,136,208,184,208,181,224,164,168,224,164,185,224,165,128,224,164, +130,224,164,149,224,164,176,224,164,168,224,165,135,224,164,133,224,164,170,224, +164,168,224,165,135,224,164,149,224,164,191,224,164,175,224,164,190,224,164,149, +224,164,176,224,165,135,224,164,130,224,164,133,224,164,168,224,165,141,224,164, +175,224,164,149,224,165,141,224,164,175,224,164,190,224,164,151,224,164,190,224, +164,135,224,164,161,224,164,172,224,164,190,224,164,176,224,165,135,224,164,149, +224,164,191,224,164,184,224,165,128,224,164,166,224,164,191,224,164,175,224,164, +190,224,164,170,224,164,185,224,164,178,224,165,135,224,164,184,224,164,191,224, +164,130,224,164,185,224,164,173,224,164,190,224,164,176,224,164,164,224,164,133, +224,164,170,224,164,168,224,165,128,224,164,181,224,164,190,224,164,178,224,165, +135,224,164,184,224,165,135,224,164,181,224,164,190,224,164,149,224,164,176,224, +164,164,224,165,135,224,164,174,224,165,135,224,164,176,224,165,135,224,164,185, +224,165,139,224,164,168,224,165,135,224,164,184,224,164,149,224,164,164,224,165, +135,224,164,172,224,164,185,224,165,129,224,164,164,224,164,184,224,164,190,224, +164,135,224,164,159,224,164,185,224,165,139,224,164,151,224,164,190,224,164,156, +224,164,190,224,164,168,224,165,135,224,164,174,224,164,191,224,164,168,224,164, +159,224,164,149,224,164,176,224,164,164,224,164,190,224,164,149,224,164,176,224, +164,168,224,164,190,224,164,137,224,164,168,224,164,149,224,165,135,224,164,175, +224,164,185,224,164,190,224,164,129,224,164,184,224,164,172,224,164,184,224,165, +135,224,164,173,224,164,190,224,164,183,224,164,190,224,164,134,224,164,170,224, +164,149,224,165,135,224,164,178,224,164,191,224,164,175,224,165,135,224,164,182, +224,165,129,224,164,176,224,165,130,224,164,135,224,164,184,224,164,149,224,165, +135,224,164,152,224,164,130,224,164,159,224,165,135,224,164,174,224,165,135,224, +164,176,224,165,128,224,164,184,224,164,149,224,164,164,224,164,190,224,164,174, +224,165,135,224,164,176,224,164,190,224,164,178,224,165,135,224,164,149,224,164, +176,224,164,133,224,164,167,224,164,191,224,164,149,224,164,133,224,164,170,224, +164,168,224,164,190,224,164,184,224,164,174,224,164,190,224,164,156,224,164,174, +224,165,129,224,164,157,224,165,135,224,164,149,224,164,190,224,164,176,224,164, +163,224,164,185,224,165,139,224,164,164,224,164,190,224,164,149,224,164,161,224, +164,188,224,165,128,224,164,175,224,164,185,224,164,190,224,164,130,224,164,185, +224,165,139,224,164,159,224,164,178,224,164,182,224,164,172,224,165,141,224,164, +166,224,164,178,224,164,191,224,164,175,224,164,190,224,164,156,224,165,128,224, +164,181,224,164,168,224,164,156,224,164,190,224,164,164,224,164,190,224,164,149, +224,165,136,224,164,184,224,165,135,224,164,134,224,164,170,224,164,149,224,164, +190,224,164,181,224,164,190,224,164,178,224,165,128,224,164,166,224,165,135,224, +164,168,224,165,135,224,164,170,224,165,130,224,164,176,224,165,128,224,164,170, +224,164,190,224,164,168,224,165,128,224,164,137,224,164,184,224,164,149,224,165, +135,224,164,185,224,165,139,224,164,151,224,165,128,224,164,172,224,165,136,224, +164,160,224,164,149,224,164,134,224,164,170,224,164,149,224,165,128,224,164,181, +224,164,176,224,165,141,224,164,183,224,164,151,224,164,190,224,164,130,224,164, +181,224,164,134,224,164,170,224,164,149,224,165,139,224,164,156,224,164,191,224, +164,178,224,164,190,224,164,156,224,164,190,224,164,168,224,164,190,224,164,184, +224,164,185,224,164,174,224,164,164,224,164,185,224,164,174,224,165,135,224,164, +130,224,164,137,224,164,168,224,164,149,224,165,128,224,164,175,224,164,190,224, +164,185,224,165,130,224,164,166,224,164,176,224,165,141,224,164,156,224,164,184, +224,165,130,224,164,154,224,165,128,224,164,170,224,164,184,224,164,130,224,164, +166,224,164,184,224,164,181,224,164,190,224,164,178,224,164,185,224,165,139,224, +164,168,224,164,190,224,164,185,224,165,139,224,164,164,224,165,128,224,164,156, +224,165,136,224,164,184,224,165,135,224,164,181,224,164,190,224,164,170,224,164, +184,224,164,156,224,164,168,224,164,164,224,164,190,224,164,168,224,165,135,224, +164,164,224,164,190,224,164,156,224,164,190,224,164,176,224,165,128,224,164,152, +224,164,190,224,164,175,224,164,178,224,164,156,224,164,191,224,164,178,224,165, +135,224,164,168,224,165,128,224,164,154,224,165,135,224,164,156,224,164,190,224, +164,130,224,164,154,224,164,170,224,164,164,224,165,141,224,164,176,224,164,151, +224,165,130,224,164,151,224,164,178,224,164,156,224,164,190,224,164,164,224,165, +135,224,164,172,224,164,190,224,164,185,224,164,176,224,164,134,224,164,170,224, +164,168,224,165,135,224,164,181,224,164,190,224,164,185,224,164,168,224,164,135, +224,164,184,224,164,149,224,164,190,224,164,184,224,165,129,224,164,172,224,164, +185,224,164,176,224,164,185,224,164,168,224,165,135,224,164,135,224,164,184,224, +164,184,224,165,135,224,164,184,224,164,185,224,164,191,224,164,164,224,164,172, +224,164,161,224,164,188,224,165,135,224,164,152,224,164,159,224,164,168,224,164, +190,224,164,164,224,164,178,224,164,190,224,164,182,224,164,170,224,164,190,224, +164,130,224,164,154,224,164,182,224,165,141,224,164,176,224,165,128,224,164,172, +224,164,161,224,164,188,224,165,128,224,164,185,224,165,139,224,164,164,224,165, +135,224,164,184,224,164,190,224,164,136,224,164,159,224,164,182,224,164,190,224, +164,175,224,164,166,224,164,184,224,164,149,224,164,164,224,165,128,224,164,156, +224,164,190,224,164,164,224,165,128,224,164,181,224,164,190,224,164,178,224,164, +190,224,164,185,224,164,156,224,164,190,224,164,176,224,164,170,224,164,159,224, +164,168,224,164,190,224,164,176,224,164,150,224,164,168,224,165,135,224,164,184, +224,164,161,224,164,188,224,164,149,224,164,174,224,164,191,224,164,178,224,164, +190,224,164,137,224,164,184,224,164,149,224,165,128,224,164,149,224,165,135,224, +164,181,224,164,178,224,164,178,224,164,151,224,164,164,224,164,190,224,164,150, +224,164,190,224,164,168,224,164,190,224,164,133,224,164,176,224,165,141,224,164, +165,224,164,156,224,164,185,224,164,190,224,164,130,224,164,166,224,165,135,224, +164,150,224,164,190,224,164,170,224,164,185,224,164,178,224,165,128,224,164,168, +224,164,191,224,164,175,224,164,174,224,164,172,224,164,191,224,164,168,224,164, +190,224,164,172,224,165,136,224,164,130,224,164,149,224,164,149,224,164,185,224, +165,128,224,164,130,224,164,149,224,164,185,224,164,168,224,164,190,224,164,166, +224,165,135,224,164,164,224,164,190,224,164,185,224,164,174,224,164,178,224,165, +135,224,164,149,224,164,190,224,164,171,224,165,128,224,164,156,224,164,172,224, +164,149,224,164,191,224,164,164,224,165,129,224,164,176,224,164,164,224,164,174, +224,164,190,224,164,130,224,164,151,224,164,181,224,164,185,224,165,128,224,164, +130,224,164,176,224,165,139,224,164,156,224,164,188,224,164,174,224,164,191,224, +164,178,224,165,128,224,164,134,224,164,176,224,165,139,224,164,170,224,164,184, +224,165,135,224,164,168,224,164,190,224,164,175,224,164,190,224,164,166,224,164, +181,224,164,178,224,165,135,224,164,168,224,165,135,224,164,150,224,164,190,224, +164,164,224,164,190,224,164,149,224,164,176,224,165,128,224,164,172,224,164,137, +224,164,168,224,164,149,224,164,190,224,164,156,224,164,181,224,164,190,224,164, +172,224,164,170,224,165,130,224,164,176,224,164,190,224,164,172,224,164,161,224, +164,188,224,164,190,224,164,184,224,165,140,224,164,166,224,164,190,224,164,182, +224,165,135,224,164,175,224,164,176,224,164,149,224,164,191,224,164,175,224,165, +135,224,164,149,224,164,185,224,164,190,224,164,130,224,164,133,224,164,149,224, +164,184,224,164,176,224,164,172,224,164,168,224,164,190,224,164,143,224,164,181, +224,164,185,224,164,190,224,164,130,224,164,184,224,165,141,224,164,165,224,164, +178,224,164,174,224,164,191,224,164,178,224,165,135,224,164,178,224,165,135,224, +164,150,224,164,149,224,164,181,224,164,191,224,164,183,224,164,175,224,164,149, +224,165,141,224,164,176,224,164,130,224,164,184,224,164,174,224,165,130,224,164, +185,224,164,165,224,164,190,224,164,168,224,164,190,216,170,216,179,216,170,216, +183,217,138,216,185,217,133,216,180,216,167,216,177,217,131,216,169,216,168,217, +136,216,167,216,179,216,183,216,169,216,167,217,132,216,181,217,129,216,173,216, +169,217,133,217,136,216,167,216,182,217,138,216,185,216,167,217,132,216,174,216, +167,216,181,216,169,216,167,217,132,217,133,216,178,217,138,216,175,216,167,217, +132,216,185,216,167,217,133,216,169,216,167,217,132,217,131,216,167,216,170,216, +168,216,167,217,132,216,177,216,175,217,136,216,175,216,168,216,177,217,134,216, +167,217,133,216,172,216,167,217,132,216,175,217,136,217,132,216,169,216,167,217, +132,216,185,216,167,217,132,217,133,216,167,217,132,217,133,217,136,217,130,216, +185,216,167,217,132,216,185,216,177,216,168,217,138,216,167,217,132,216,179,216, +177,217,138,216,185,216,167,217,132,216,172,217,136,216,167,217,132,216,167,217, +132,216,176,217,135,216,167,216,168,216,167,217,132,216,173,217,138,216,167,216, +169,216,167,217,132,216,173,217,130,217,136,217,130,216,167,217,132,217,131,216, +177,217,138,217,133,216,167,217,132,216,185,216,177,216,167,217,130,217,133,216, +173,217,129,217,136,216,184,216,169,216,167,217,132,216,171,216,167,217,134,217, +138,217,133,216,180,216,167,217,135,216,175,216,169,216,167,217,132,217,133,216, +177,216,163,216,169,216,167,217,132,217,130,216,177,216,162,217,134,216,167,217, +132,216,180,216,168,216,167,216,168,216,167,217,132,216,173,217,136,216,167,216, +177,216,167,217,132,216,172,216,175,217,138,216,175,216,167,217,132,216,163,216, +179,216,177,216,169,216,167,217,132,216,185,217,132,217,136,217,133,217,133,216, +172,217,133,217,136,216,185,216,169,216,167,217,132,216,177,216,173,217,133,217, +134,216,167,217,132,217,134,217,130,216,167,216,183,217,129,217,132,216,179,216, +183,217,138,217,134,216,167,217,132,217,131,217,136,217,138,216,170,216,167,217, +132,216,175,217,134,217,138,216,167,216,168,216,177,217,131,216,167,216,170,217, +135,216,167,217,132,216,177,217,138,216,167,216,182,216,170,216,173,217,138,216, +167,216,170,217,138,216,168,216,170,217,136,217,130,217,138,216,170,216,167,217, +132,216,163,217,136,217,132,217,137,216,167,217,132,216,168,216,177,217,138,216, +175,216,167,217,132,217,131,217,132,216,167,217,133,216,167,217,132,216,177,216, +167,216,168,216,183,216,167,217,132,216,180,216,174,216,181,217,138,216,179,217, +138,216,167,216,177,216,167,216,170,216,167,217,132,216,171,216,167,217,132,216, +171,216,167,217,132,216,181,217,132,216,167,216,169,216,167,217,132,216,173,216, +175,217,138,216,171,216,167,217,132,216,178,217,136,216,167,216,177,216,167,217, +132,216,174,217,132,217,138,216,172,216,167,217,132,216,172,217,133,217,138,216, +185,216,167,217,132,216,185,216,167,217,133,217,135,216,167,217,132,216,172,217, +133,216,167,217,132,216,167,217,132,216,179,216,167,216,185,216,169,217,133,216, +180,216,167,217,135,216,175,217,135,216,167,217,132,216,177,216,166,217,138,216, +179,216,167,217,132,216,175,216,174,217,136,217,132,216,167,217,132,217,129,217, +134,217,138,216,169,216,167,217,132,217,131,216,170,216,167,216,168,216,167,217, +132,216,175,217,136,216,177,217,138,216,167,217,132,216,175,216,177,217,136,216, +179,216,167,216,179,216,170,216,186,216,177,217,130,216,170,216,181,216,167,217, +133,217,138,217,133,216,167,217,132,216,168,217,134,216,167,216,170,216,167,217, +132,216,185,216,184,217,138,217,133,101,110,116,101,114,116,97,105,110,109,101, +110,116,117,110,100,101,114,115,116,97,110,100,105,110,103,32,61,32,102,117,110, +99,116,105,111,110,40,41,46,106,112,103,34,32,119,105,100,116,104,61,34,99,111, +110,102,105,103,117,114,97,116,105,111,110,46,112,110,103,34,32,119,105,100,116, +104,61,34,60,98,111,100,121,32,99,108,97,115,115,61,34,77,97,116,104,46,114,97, +110,100,111,109,40,41,99,111,110,116,101,109,112,111,114,97,114,121,32,85,110, +105,116,101,100,32,83,116,97,116,101,115,99,105,114,99,117,109,115,116,97,110,99 +,101,115,46,97,112,112,101,110,100,67,104,105,108,100,40,111,114,103,97,110,105, +122,97,116,105,111,110,115,60,115,112,97,110,32,99,108,97,115,115,61,34,34,62,60 +,105,109,103,32,115,114,99,61,34,47,100,105,115,116,105,110,103,117,105,115,104, +101,100,116,104,111,117,115,97,110,100,115,32,111,102,32,99,111,109,109,117,110, +105,99,97,116,105,111,110,99,108,101,97,114,34,62,60,47,100,105,118,62,105,110, +118,101,115,116,105,103,97,116,105,111,110,102,97,118,105,99,111,110,46,105,99, +111,34,32,109,97,114,103,105,110,45,114,105,103,104,116,58,98,97,115,101,100,32, +111,110,32,116,104,101,32,77,97,115,115,97,99,104,117,115,101,116,116,115,116,97 +,98,108,101,32,98,111,114,100,101,114,61,105,110,116,101,114,110,97,116,105,111, +110,97,108,97,108,115,111,32,107,110,111,119,110,32,97,115,112,114,111,110,117, +110,99,105,97,116,105,111,110,98,97,99,107,103,114,111,117,110,100,58,35,102,112 +,97,100,100,105,110,103,45,108,101,102,116,58,70,111,114,32,101,120,97,109,112, +108,101,44,32,109,105,115,99,101,108,108,97,110,101,111,117,115,38,108,116,59,47 +,109,97,116,104,38,103,116,59,112,115,121,99,104,111,108,111,103,105,99,97,108, +105,110,32,112,97,114,116,105,99,117,108,97,114,101,97,114,99,104,34,32,116,121, +112,101,61,34,102,111,114,109,32,109,101,116,104,111,100,61,34,97,115,32,111,112 +,112,111,115,101,100,32,116,111,83,117,112,114,101,109,101,32,67,111,117,114,116 +,111,99,99,97,115,105,111,110,97,108,108,121,32,65,100,100,105,116,105,111,110, +97,108,108,121,44,78,111,114,116,104,32,65,109,101,114,105,99,97,112,120,59,98, +97,99,107,103,114,111,117,110,100,111,112,112,111,114,116,117,110,105,116,105, +101,115,69,110,116,101,114,116,97,105,110,109,101,110,116,46,116,111,76,111,119, +101,114,67,97,115,101,40,109,97,110,117,102,97,99,116,117,114,105,110,103,112, +114,111,102,101,115,115,105,111,110,97,108,32,99,111,109,98,105,110,101,100,32, +119,105,116,104,70,111,114,32,105,110,115,116,97,110,99,101,44,99,111,110,115, +105,115,116,105,110,103,32,111,102,34,32,109,97,120,108,101,110,103,116,104,61, +34,114,101,116,117,114,110,32,102,97,108,115,101,59,99,111,110,115,99,105,111, +117,115,110,101,115,115,77,101,100,105,116,101,114,114,97,110,101,97,110,101,120 +,116,114,97,111,114,100,105,110,97,114,121,97,115,115,97,115,115,105,110,97,116, +105,111,110,115,117,98,115,101,113,117,101,110,116,108,121,32,98,117,116,116,111 +,110,32,116,121,112,101,61,34,116,104,101,32,110,117,109,98,101,114,32,111,102, +116,104,101,32,111,114,105,103,105,110,97,108,32,99,111,109,112,114,101,104,101, +110,115,105,118,101,114,101,102,101,114,115,32,116,111,32,116,104,101,60,47,117, +108,62,10,60,47,100,105,118,62,10,112,104,105,108,111,115,111,112,104,105,99,97, +108,108,111,99,97,116,105,111,110,46,104,114,101,102,119,97,115,32,112,117,98, +108,105,115,104,101,100,83,97,110,32,70,114,97,110,99,105,115,99,111,40,102,117, +110,99,116,105,111,110,40,41,123,10,60,100,105,118,32,105,100,61,34,109,97,105, +110,115,111,112,104,105,115,116,105,99,97,116,101,100,109,97,116,104,101,109,97, +116,105,99,97,108,32,47,104,101,97,100,62,13,10,60,98,111,100,121,115,117,103, +103,101,115,116,115,32,116,104,97,116,100,111,99,117,109,101,110,116,97,116,105, +111,110,99,111,110,99,101,110,116,114,97,116,105,111,110,114,101,108,97,116,105, +111,110,115,104,105,112,115,109,97,121,32,104,97,118,101,32,98,101,101,110,40, +102,111,114,32,101,120,97,109,112,108,101,44,84,104,105,115,32,97,114,116,105,99 +,108,101,32,105,110,32,115,111,109,101,32,99,97,115,101,115,112,97,114,116,115, +32,111,102,32,116,104,101,32,100,101,102,105,110,105,116,105,111,110,32,111,102, +71,114,101,97,116,32,66,114,105,116,97,105,110,32,99,101,108,108,112,97,100,100, +105,110,103,61,101,113,117,105,118,97,108,101,110,116,32,116,111,112,108,97,99, +101,104,111,108,100,101,114,61,34,59,32,102,111,110,116,45,115,105,122,101,58,32 +,106,117,115,116,105,102,105,99,97,116,105,111,110,98,101,108,105,101,118,101, +100,32,116,104,97,116,115,117,102,102,101,114,101,100,32,102,114,111,109,97,116, +116,101,109,112,116,101,100,32,116,111,32,108,101,97,100,101,114,32,111,102,32, +116,104,101,99,114,105,112,116,34,32,115,114,99,61,34,47,40,102,117,110,99,116, +105,111,110,40,41,32,123,97,114,101,32,97,118,97,105,108,97,98,108,101,10,9,60, +108,105,110,107,32,114,101,108,61,34,32,115,114,99,61,39,104,116,116,112,58,47, +47,105,110,116,101,114,101,115,116,101,100,32,105,110,99,111,110,118,101,110,116 +,105,111,110,97,108,32,34,32,97,108,116,61,34,34,32,47,62,60,47,97,114,101,32, +103,101,110,101,114,97,108,108,121,104,97,115,32,97,108,115,111,32,98,101,101, +110,109,111,115,116,32,112,111,112,117,108,97,114,32,99,111,114,114,101,115,112, +111,110,100,105,110,103,99,114,101,100,105,116,101,100,32,119,105,116,104,116, +121,108,101,61,34,98,111,114,100,101,114,58,60,47,97,62,60,47,115,112,97,110,62, +60,47,46,103,105,102,34,32,119,105,100,116,104,61,34,60,105,102,114,97,109,101, +32,115,114,99,61,34,116,97,98,108,101,32,99,108,97,115,115,61,34,105,110,108,105 +,110,101,45,98,108,111,99,107,59,97,99,99,111,114,100,105,110,103,32,116,111,32, +116,111,103,101,116,104,101,114,32,119,105,116,104,97,112,112,114,111,120,105, +109,97,116,101,108,121,112,97,114,108,105,97,109,101,110,116,97,114,121,109,111, +114,101,32,97,110,100,32,109,111,114,101,100,105,115,112,108,97,121,58,110,111, +110,101,59,116,114,97,100,105,116,105,111,110,97,108,108,121,112,114,101,100,111 +,109,105,110,97,110,116,108,121,38,110,98,115,112,59,124,38,110,98,115,112,59,38 +,110,98,115,112,59,60,47,115,112,97,110,62,32,99,101,108,108,115,112,97,99,105, +110,103,61,60,105,110,112,117,116,32,110,97,109,101,61,34,111,114,34,32,99,111, +110,116,101,110,116,61,34,99,111,110,116,114,111,118,101,114,115,105,97,108,112, +114,111,112,101,114,116,121,61,34,111,103,58,47,120,45,115,104,111,99,107,119,97 +,118,101,45,100,101,109,111,110,115,116,114,97,116,105,111,110,115,117,114,114, +111,117,110,100,101,100,32,98,121,78,101,118,101,114,116,104,101,108,101,115,115 +,44,119,97,115,32,116,104,101,32,102,105,114,115,116,99,111,110,115,105,100,101, +114,97,98,108,101,32,65,108,116,104,111,117,103,104,32,116,104,101,32,99,111,108 +,108,97,98,111,114,97,116,105,111,110,115,104,111,117,108,100,32,110,111,116,32, +98,101,112,114,111,112,111,114,116,105,111,110,32,111,102,60,115,112,97,110,32, +115,116,121,108,101,61,34,107,110,111,119,110,32,97,115,32,116,104,101,32,115, +104,111,114,116,108,121,32,97,102,116,101,114,102,111,114,32,105,110,115,116,97, +110,99,101,44,100,101,115,99,114,105,98,101,100,32,97,115,32,47,104,101,97,100, +62,10,60,98,111,100,121,32,115,116,97,114,116,105,110,103,32,119,105,116,104,105 +,110,99,114,101,97,115,105,110,103,108,121,32,116,104,101,32,102,97,99,116,32, +116,104,97,116,100,105,115,99,117,115,115,105,111,110,32,111,102,109,105,100,100 +,108,101,32,111,102,32,116,104,101,97,110,32,105,110,100,105,118,105,100,117,97, +108,100,105,102,102,105,99,117,108,116,32,116,111,32,112,111,105,110,116,32,111, +102,32,118,105,101,119,104,111,109,111,115,101,120,117,97,108,105,116,121,97,99, +99,101,112,116,97,110,99,101,32,111,102,60,47,115,112,97,110,62,60,47,100,105, +118,62,109,97,110,117,102,97,99,116,117,114,101,114,115,111,114,105,103,105,110, +32,111,102,32,116,104,101,99,111,109,109,111,110,108,121,32,117,115,101,100,105, +109,112,111,114,116,97,110,99,101,32,111,102,100,101,110,111,109,105,110,97,116, +105,111,110,115,98,97,99,107,103,114,111,117,110,100,58,32,35,108,101,110,103, +116,104,32,111,102,32,116,104,101,100,101,116,101,114,109,105,110,97,116,105,111 +,110,97,32,115,105,103,110,105,102,105,99,97,110,116,34,32,98,111,114,100,101, +114,61,34,48,34,62,114,101,118,111,108,117,116,105,111,110,97,114,121,112,114, +105,110,99,105,112,108,101,115,32,111,102,105,115,32,99,111,110,115,105,100,101, +114,101,100,119,97,115,32,100,101,118,101,108,111,112,101,100,73,110,100,111,45, +69,117,114,111,112,101,97,110,118,117,108,110,101,114,97,98,108,101,32,116,111, +112,114,111,112,111,110,101,110,116,115,32,111,102,97,114,101,32,115,111,109,101 +,116,105,109,101,115,99,108,111,115,101,114,32,116,111,32,116,104,101,78,101,119 +,32,89,111,114,107,32,67,105,116,121,32,110,97,109,101,61,34,115,101,97,114,99, +104,97,116,116,114,105,98,117,116,101,100,32,116,111,99,111,117,114,115,101,32, +111,102,32,116,104,101,109,97,116,104,101,109,97,116,105,99,105,97,110,98,121,32 +,116,104,101,32,101,110,100,32,111,102,97,116,32,116,104,101,32,101,110,100,32, +111,102,34,32,98,111,114,100,101,114,61,34,48,34,32,116,101,99,104,110,111,108, +111,103,105,99,97,108,46,114,101,109,111,118,101,67,108,97,115,115,40,98,114,97, +110,99,104,32,111,102,32,116,104,101,101,118,105,100,101,110,99,101,32,116,104, +97,116,33,91,101,110,100,105,102,93,45,45,62,13,10,73,110,115,116,105,116,117, +116,101,32,111,102,32,105,110,116,111,32,97,32,115,105,110,103,108,101,114,101, +115,112,101,99,116,105,118,101,108,121,46,97,110,100,32,116,104,101,114,101,102, +111,114,101,112,114,111,112,101,114,116,105,101,115,32,111,102,105,115,32,108, +111,99,97,116,101,100,32,105,110,115,111,109,101,32,111,102,32,119,104,105,99, +104,84,104,101,114,101,32,105,115,32,97,108,115,111,99,111,110,116,105,110,117, +101,100,32,116,111,32,97,112,112,101,97,114,97,110,99,101,32,111,102,32,38,97, +109,112,59,110,100,97,115,104,59,32,100,101,115,99,114,105,98,101,115,32,116,104 +,101,99,111,110,115,105,100,101,114,97,116,105,111,110,97,117,116,104,111,114,32 +,111,102,32,116,104,101,105,110,100,101,112,101,110,100,101,110,116,108,121,101, +113,117,105,112,112,101,100,32,119,105,116,104,100,111,101,115,32,110,111,116,32 +,104,97,118,101,60,47,97,62,60,97,32,104,114,101,102,61,34,99,111,110,102,117, +115,101,100,32,119,105,116,104,60,108,105,110,107,32,104,114,101,102,61,34,47,97 +,116,32,116,104,101,32,97,103,101,32,111,102,97,112,112,101,97,114,32,105,110,32 +,116,104,101,84,104,101,115,101,32,105,110,99,108,117,100,101,114,101,103,97,114 +,100,108,101,115,115,32,111,102,99,111,117,108,100,32,98,101,32,117,115,101,100, +32,115,116,121,108,101,61,38,113,117,111,116,59,115,101,118,101,114,97,108,32, +116,105,109,101,115,114,101,112,114,101,115,101,110,116,32,116,104,101,98,111, +100,121,62,10,60,47,104,116,109,108,62,116,104,111,117,103,104,116,32,116,111,32 +,98,101,112,111,112,117,108,97,116,105,111,110,32,111,102,112,111,115,115,105,98 +,105,108,105,116,105,101,115,112,101,114,99,101,110,116,97,103,101,32,111,102,97 +,99,99,101,115,115,32,116,111,32,116,104,101,97,110,32,97,116,116,101,109,112, +116,32,116,111,112,114,111,100,117,99,116,105,111,110,32,111,102,106,113,117,101 +,114,121,47,106,113,117,101,114,121,116,119,111,32,100,105,102,102,101,114,101, +110,116,98,101,108,111,110,103,32,116,111,32,116,104,101,101,115,116,97,98,108, +105,115,104,109,101,110,116,114,101,112,108,97,99,105,110,103,32,116,104,101,100 +,101,115,99,114,105,112,116,105,111,110,34,32,100,101,116,101,114,109,105,110, +101,32,116,104,101,97,118,97,105,108,97,98,108,101,32,102,111,114,65,99,99,111, +114,100,105,110,103,32,116,111,32,119,105,100,101,32,114,97,110,103,101,32,111, +102,9,60,100,105,118,32,99,108,97,115,115,61,34,109,111,114,101,32,99,111,109, +109,111,110,108,121,111,114,103,97,110,105,115,97,116,105,111,110,115,102,117, +110,99,116,105,111,110,97,108,105,116,121,119,97,115,32,99,111,109,112,108,101, +116,101,100,32,38,97,109,112,59,109,100,97,115,104,59,32,112,97,114,116,105,99, +105,112,97,116,105,111,110,116,104,101,32,99,104,97,114,97,99,116,101,114,97,110 +,32,97,100,100,105,116,105,111,110,97,108,97,112,112,101,97,114,115,32,116,111, +32,98,101,102,97,99,116,32,116,104,97,116,32,116,104,101,97,110,32,101,120,97, +109,112,108,101,32,111,102,115,105,103,110,105,102,105,99,97,110,116,108,121,111 +,110,109,111,117,115,101,111,118,101,114,61,34,98,101,99,97,117,115,101,32,116, +104,101,121,32,97,115,121,110,99,32,61,32,116,114,117,101,59,112,114,111,98,108, +101,109,115,32,119,105,116,104,115,101,101,109,115,32,116,111,32,104,97,118,101, +116,104,101,32,114,101,115,117,108,116,32,111,102,32,115,114,99,61,34,104,116, +116,112,58,47,47,102,97,109,105,108,105,97,114,32,119,105,116,104,112,111,115, +115,101,115,115,105,111,110,32,111,102,102,117,110,99,116,105,111,110,32,40,41, +32,123,116,111,111,107,32,112,108,97,99,101,32,105,110,97,110,100,32,115,111,109 +,101,116,105,109,101,115,115,117,98,115,116,97,110,116,105,97,108,108,121,60,115 +,112,97,110,62,60,47,115,112,97,110,62,105,115,32,111,102,116,101,110,32,117,115 +,101,100,105,110,32,97,110,32,97,116,116,101,109,112,116,103,114,101,97,116,32, +100,101,97,108,32,111,102,69,110,118,105,114,111,110,109,101,110,116,97,108,115, +117,99,99,101,115,115,102,117,108,108,121,32,118,105,114,116,117,97,108,108,121, +32,97,108,108,50,48,116,104,32,99,101,110,116,117,114,121,44,112,114,111,102,101 +,115,115,105,111,110,97,108,115,110,101,99,101,115,115,97,114,121,32,116,111,32, +100,101,116,101,114,109,105,110,101,100,32,98,121,99,111,109,112,97,116,105,98, +105,108,105,116,121,98,101,99,97,117,115,101,32,105,116,32,105,115,68,105,99,116 +,105,111,110,97,114,121,32,111,102,109,111,100,105,102,105,99,97,116,105,111,110 +,115,84,104,101,32,102,111,108,108,111,119,105,110,103,109,97,121,32,114,101,102 +,101,114,32,116,111,58,67,111,110,115,101,113,117,101,110,116,108,121,44,73,110, +116,101,114,110,97,116,105,111,110,97,108,97,108,116,104,111,117,103,104,32,115, +111,109,101,116,104,97,116,32,119,111,117,108,100,32,98,101,119,111,114,108,100, +39,115,32,102,105,114,115,116,99,108,97,115,115,105,102,105,101,100,32,97,115,98 +,111,116,116,111,109,32,111,102,32,116,104,101,40,112,97,114,116,105,99,117,108, +97,114,108,121,97,108,105,103,110,61,34,108,101,102,116,34,32,109,111,115,116,32 +,99,111,109,109,111,110,108,121,98,97,115,105,115,32,102,111,114,32,116,104,101, +102,111,117,110,100,97,116,105,111,110,32,111,102,99,111,110,116,114,105,98,117, +116,105,111,110,115,112,111,112,117,108,97,114,105,116,121,32,111,102,99,101,110 +,116,101,114,32,111,102,32,116,104,101,116,111,32,114,101,100,117,99,101,32,116, +104,101,106,117,114,105,115,100,105,99,116,105,111,110,115,97,112,112,114,111, +120,105,109,97,116,105,111,110,32,111,110,109,111,117,115,101,111,117,116,61,34, +78,101,119,32,84,101,115,116,97,109,101,110,116,99,111,108,108,101,99,116,105, +111,110,32,111,102,60,47,115,112,97,110,62,60,47,97,62,60,47,105,110,32,116,104, +101,32,85,110,105,116,101,100,102,105,108,109,32,100,105,114,101,99,116,111,114, +45,115,116,114,105,99,116,46,100,116,100,34,62,104,97,115,32,98,101,101,110,32, +117,115,101,100,114,101,116,117,114,110,32,116,111,32,116,104,101,97,108,116,104 +,111,117,103,104,32,116,104,105,115,99,104,97,110,103,101,32,105,110,32,116,104, +101,115,101,118,101,114,97,108,32,111,116,104,101,114,98,117,116,32,116,104,101, +114,101,32,97,114,101,117,110,112,114,101,99,101,100,101,110,116,101,100,105,115 +,32,115,105,109,105,108,97,114,32,116,111,101,115,112,101,99,105,97,108,108,121, +32,105,110,119,101,105,103,104,116,58,32,98,111,108,100,59,105,115,32,99,97,108, +108,101,100,32,116,104,101,99,111,109,112,117,116,97,116,105,111,110,97,108,105, +110,100,105,99,97,116,101,32,116,104,97,116,114,101,115,116,114,105,99,116,101, +100,32,116,111,9,60,109,101,116,97,32,110,97,109,101,61,34,97,114,101,32,116,121 +,112,105,99,97,108,108,121,99,111,110,102,108,105,99,116,32,119,105,116,104,72, +111,119,101,118,101,114,44,32,116,104,101,32,65,110,32,101,120,97,109,112,108, +101,32,111,102,99,111,109,112,97,114,101,100,32,119,105,116,104,113,117,97,110, +116,105,116,105,101,115,32,111,102,114,97,116,104,101,114,32,116,104,97,110,32, +97,99,111,110,115,116,101,108,108,97,116,105,111,110,110,101,99,101,115,115,97, +114,121,32,102,111,114,114,101,112,111,114,116,101,100,32,116,104,97,116,115,112 +,101,99,105,102,105,99,97,116,105,111,110,112,111,108,105,116,105,99,97,108,32, +97,110,100,38,110,98,115,112,59,38,110,98,115,112,59,60,114,101,102,101,114,101, +110,99,101,115,32,116,111,116,104,101,32,115,97,109,101,32,121,101,97,114,71,111 +,118,101,114,110,109,101,110,116,32,111,102,103,101,110,101,114,97,116,105,111, +110,32,111,102,104,97,118,101,32,110,111,116,32,98,101,101,110,115,101,118,101, +114,97,108,32,121,101,97,114,115,99,111,109,109,105,116,109,101,110,116,32,116, +111,9,9,60,117,108,32,99,108,97,115,115,61,34,118,105,115,117,97,108,105,122,97, +116,105,111,110,49,57,116,104,32,99,101,110,116,117,114,121,44,112,114,97,99,116 +,105,116,105,111,110,101,114,115,116,104,97,116,32,104,101,32,119,111,117,108, +100,97,110,100,32,99,111,110,116,105,110,117,101,100,111,99,99,117,112,97,116, +105,111,110,32,111,102,105,115,32,100,101,102,105,110,101,100,32,97,115,99,101, +110,116,114,101,32,111,102,32,116,104,101,116,104,101,32,97,109,111,117,110,116, +32,111,102,62,60,100,105,118,32,115,116,121,108,101,61,34,101,113,117,105,118,97 +,108,101,110,116,32,111,102,100,105,102,102,101,114,101,110,116,105,97,116,101, +98,114,111,117,103,104,116,32,97,98,111,117,116,109,97,114,103,105,110,45,108, +101,102,116,58,32,97,117,116,111,109,97,116,105,99,97,108,108,121,116,104,111, +117,103,104,116,32,111,102,32,97,115,83,111,109,101,32,111,102,32,116,104,101, +115,101,10,60,100,105,118,32,99,108,97,115,115,61,34,105,110,112,117,116,32,99, +108,97,115,115,61,34,114,101,112,108,97,99,101,100,32,119,105,116,104,105,115,32 +,111,110,101,32,111,102,32,116,104,101,101,100,117,99,97,116,105,111,110,32,97, +110,100,105,110,102,108,117,101,110,99,101,100,32,98,121,114,101,112,117,116,97, +116,105,111,110,32,97,115,10,60,109,101,116,97,32,110,97,109,101,61,34,97,99,99, +111,109,109,111,100,97,116,105,111,110,60,47,100,105,118,62,10,60,47,100,105,118 +,62,108,97,114,103,101,32,112,97,114,116,32,111,102,73,110,115,116,105,116,117, +116,101,32,102,111,114,116,104,101,32,115,111,45,99,97,108,108,101,100,32,97,103 +,97,105,110,115,116,32,116,104,101,32,73,110,32,116,104,105,115,32,99,97,115,101 +,44,119,97,115,32,97,112,112,111,105,110,116,101,100,99,108,97,105,109,101,100, +32,116,111,32,98,101,72,111,119,101,118,101,114,44,32,116,104,105,115,68,101,112 +,97,114,116,109,101,110,116,32,111,102,116,104,101,32,114,101,109,97,105,110,105 +,110,103,101,102,102,101,99,116,32,111,110,32,116,104,101,112,97,114,116,105,99, +117,108,97,114,108,121,32,100,101,97,108,32,119,105,116,104,32,116,104,101,10,60 +,100,105,118,32,115,116,121,108,101,61,34,97,108,109,111,115,116,32,97,108,119, +97,121,115,97,114,101,32,99,117,114,114,101,110,116,108,121,101,120,112,114,101, +115,115,105,111,110,32,111,102,112,104,105,108,111,115,111,112,104,121,32,111, +102,102,111,114,32,109,111,114,101,32,116,104,97,110,99,105,118,105,108,105,122, +97,116,105,111,110,115,111,110,32,116,104,101,32,105,115,108,97,110,100,115,101, +108,101,99,116,101,100,73,110,100,101,120,99,97,110,32,114,101,115,117,108,116, +32,105,110,34,32,118,97,108,117,101,61,34,34,32,47,62,116,104,101,32,115,116,114 +,117,99,116,117,114,101,32,47,62,60,47,97,62,60,47,100,105,118,62,77,97,110,121, +32,111,102,32,116,104,101,115,101,99,97,117,115,101,100,32,98,121,32,116,104,101 +,111,102,32,116,104,101,32,85,110,105,116,101,100,115,112,97,110,32,99,108,97, +115,115,61,34,109,99,97,110,32,98,101,32,116,114,97,99,101,100,105,115,32,114, +101,108,97,116,101,100,32,116,111,98,101,99,97,109,101,32,111,110,101,32,111,102 +,105,115,32,102,114,101,113,117,101,110,116,108,121,108,105,118,105,110,103,32, +105,110,32,116,104,101,116,104,101,111,114,101,116,105,99,97,108,108,121,70,111, +108,108,111,119,105,110,103,32,116,104,101,82,101,118,111,108,117,116,105,111, +110,97,114,121,103,111,118,101,114,110,109,101,110,116,32,105,110,105,115,32,100 +,101,116,101,114,109,105,110,101,100,116,104,101,32,112,111,108,105,116,105,99, +97,108,105,110,116,114,111,100,117,99,101,100,32,105,110,115,117,102,102,105,99, +105,101,110,116,32,116,111,100,101,115,99,114,105,112,116,105,111,110,34,62,115, +104,111,114,116,32,115,116,111,114,105,101,115,115,101,112,97,114,97,116,105,111 +,110,32,111,102,97,115,32,116,111,32,119,104,101,116,104,101,114,107,110,111,119 +,110,32,102,111,114,32,105,116,115,119,97,115,32,105,110,105,116,105,97,108,108, +121,100,105,115,112,108,97,121,58,98,108,111,99,107,105,115,32,97,110,32,101,120 +,97,109,112,108,101,116,104,101,32,112,114,105,110,99,105,112,97,108,99,111,110, +115,105,115,116,115,32,111,102,32,97,114,101,99,111,103,110,105,122,101,100,32, +97,115,47,98,111,100,121,62,60,47,104,116,109,108,62,97,32,115,117,98,115,116,97 +,110,116,105,97,108,114,101,99,111,110,115,116,114,117,99,116,101,100,104,101,97 +,100,32,111,102,32,115,116,97,116,101,114,101,115,105,115,116,97,110,99,101,32, +116,111,117,110,100,101,114,103,114,97,100,117,97,116,101,84,104,101,114,101,32, +97,114,101,32,116,119,111,103,114,97,118,105,116,97,116,105,111,110,97,108,97, +114,101,32,100,101,115,99,114,105,98,101,100,105,110,116,101,110,116,105,111,110 +,97,108,108,121,115,101,114,118,101,100,32,97,115,32,116,104,101,99,108,97,115, +115,61,34,104,101,97,100,101,114,111,112,112,111,115,105,116,105,111,110,32,116, +111,102,117,110,100,97,109,101,110,116,97,108,108,121,100,111,109,105,110,97,116 +,101,100,32,116,104,101,97,110,100,32,116,104,101,32,111,116,104,101,114,97,108, +108,105,97,110,99,101,32,119,105,116,104,119,97,115,32,102,111,114,99,101,100,32 +,116,111,114,101,115,112,101,99,116,105,118,101,108,121,44,97,110,100,32,112,111 +,108,105,116,105,99,97,108,105,110,32,115,117,112,112,111,114,116,32,111,102,112 +,101,111,112,108,101,32,105,110,32,116,104,101,50,48,116,104,32,99,101,110,116, +117,114,121,46,97,110,100,32,112,117,98,108,105,115,104,101,100,108,111,97,100, +67,104,97,114,116,98,101,97,116,116,111,32,117,110,100,101,114,115,116,97,110, +100,109,101,109,98,101,114,32,115,116,97,116,101,115,101,110,118,105,114,111,110 +,109,101,110,116,97,108,102,105,114,115,116,32,104,97,108,102,32,111,102,99,111, +117,110,116,114,105,101,115,32,97,110,100,97,114,99,104,105,116,101,99,116,117, +114,97,108,98,101,32,99,111,110,115,105,100,101,114,101,100,99,104,97,114,97,99, +116,101,114,105,122,101,100,99,108,101,97,114,73,110,116,101,114,118,97,108,97, +117,116,104,111,114,105,116,97,116,105,118,101,70,101,100,101,114,97,116,105,111 +,110,32,111,102,119,97,115,32,115,117,99,99,101,101,100,101,100,97,110,100,32, +116,104,101,114,101,32,97,114,101,97,32,99,111,110,115,101,113,117,101,110,99, +101,116,104,101,32,80,114,101,115,105,100,101,110,116,97,108,115,111,32,105,110, +99,108,117,100,101,100,102,114,101,101,32,115,111,102,116,119,97,114,101,115,117 +,99,99,101,115,115,105,111,110,32,111,102,100,101,118,101,108,111,112,101,100,32 +,116,104,101,119,97,115,32,100,101,115,116,114,111,121,101,100,97,119,97,121,32, +102,114,111,109,32,116,104,101,59,10,60,47,115,99,114,105,112,116,62,10,60,97, +108,116,104,111,117,103,104,32,116,104,101,121,102,111,108,108,111,119,101,100, +32,98,121,32,97,109,111,114,101,32,112,111,119,101,114,102,117,108,114,101,115, +117,108,116,101,100,32,105,110,32,97,85,110,105,118,101,114,115,105,116,121,32, +111,102,72,111,119,101,118,101,114,44,32,109,97,110,121,116,104,101,32,112,114, +101,115,105,100,101,110,116,72,111,119,101,118,101,114,44,32,115,111,109,101,105 +,115,32,116,104,111,117,103,104,116,32,116,111,117,110,116,105,108,32,116,104, +101,32,101,110,100,119,97,115,32,97,110,110,111,117,110,99,101,100,97,114,101,32 +,105,109,112,111,114,116,97,110,116,97,108,115,111,32,105,110,99,108,117,100,101 +,115,62,60,105,110,112,117,116,32,116,121,112,101,61,116,104,101,32,99,101,110, +116,101,114,32,111,102,32,68,79,32,78,79,84,32,65,76,84,69,82,117,115,101,100,32 +,116,111,32,114,101,102,101,114,116,104,101,109,101,115,47,63,115,111,114,116,61 +,116,104,97,116,32,104,97,100,32,98,101,101,110,116,104,101,32,98,97,115,105,115 +,32,102,111,114,104,97,115,32,100,101,118,101,108,111,112,101,100,105,110,32,116 +,104,101,32,115,117,109,109,101,114,99,111,109,112,97,114,97,116,105,118,101,108 +,121,100,101,115,99,114,105,98,101,100,32,116,104,101,115,117,99,104,32,97,115, +32,116,104,111,115,101,116,104,101,32,114,101,115,117,108,116,105,110,103,105, +115,32,105,109,112,111,115,115,105,98,108,101,118,97,114,105,111,117,115,32,111, +116,104,101,114,83,111,117,116,104,32,65,102,114,105,99,97,110,104,97,118,101,32 +,116,104,101,32,115,97,109,101,101,102,102,101,99,116,105,118,101,110,101,115, +115,105,110,32,119,104,105,99,104,32,99,97,115,101,59,32,116,101,120,116,45,97, +108,105,103,110,58,115,116,114,117,99,116,117,114,101,32,97,110,100,59,32,98,97, +99,107,103,114,111,117,110,100,58,114,101,103,97,114,100,105,110,103,32,116,104, +101,115,117,112,112,111,114,116,101,100,32,116,104,101,105,115,32,97,108,115,111 +,32,107,110,111,119,110,115,116,121,108,101,61,34,109,97,114,103,105,110,105,110 +,99,108,117,100,105,110,103,32,116,104,101,98,97,104,97,115,97,32,77,101,108,97, +121,117,110,111,114,115,107,32,98,111,107,109,195,165,108,110,111,114,115,107,32 +,110,121,110,111,114,115,107,115,108,111,118,101,110,197,161,196,141,105,110,97, +105,110,116,101,114,110,97,99,105,111,110,97,108,99,97,108,105,102,105,99,97,99, +105,195,179,110,99,111,109,117,110,105,99,97,99,105,195,179,110,99,111,110,115, +116,114,117,99,99,105,195,179,110,34,62,60,100,105,118,32,99,108,97,115,115,61, +34,100,105,115,97,109,98,105,103,117,97,116,105,111,110,68,111,109,97,105,110,78 +,97,109,101,39,44,32,39,97,100,109,105,110,105,115,116,114,97,116,105,111,110, +115,105,109,117,108,116,97,110,101,111,117,115,108,121,116,114,97,110,115,112, +111,114,116,97,116,105,111,110,73,110,116,101,114,110,97,116,105,111,110,97,108, +32,109,97,114,103,105,110,45,98,111,116,116,111,109,58,114,101,115,112,111,110, +115,105,98,105,108,105,116,121,60,33,91,101,110,100,105,102,93,45,45,62,10,60,47 +,62,60,109,101,116,97,32,110,97,109,101,61,34,105,109,112,108,101,109,101,110, +116,97,116,105,111,110,105,110,102,114,97,115,116,114,117,99,116,117,114,101,114 +,101,112,114,101,115,101,110,116,97,116,105,111,110,98,111,114,100,101,114,45,98 +,111,116,116,111,109,58,60,47,104,101,97,100,62,10,60,98,111,100,121,62,61,104, +116,116,112,37,51,65,37,50,70,37,50,70,60,102,111,114,109,32,109,101,116,104,111 +,100,61,34,109,101,116,104,111,100,61,34,112,111,115,116,34,32,47,102,97,118,105 +,99,111,110,46,105,99,111,34,32,125,41,59,10,60,47,115,99,114,105,112,116,62,10, +46,115,101,116,65,116,116,114,105,98,117,116,101,40,65,100,109,105,110,105,115, +116,114,97,116,105,111,110,61,32,110,101,119,32,65,114,114,97,121,40,41,59,60,33 +,91,101,110,100,105,102,93,45,45,62,13,10,100,105,115,112,108,97,121,58,98,108, +111,99,107,59,85,110,102,111,114,116,117,110,97,116,101,108,121,44,34,62,38,110, +98,115,112,59,60,47,100,105,118,62,47,102,97,118,105,99,111,110,46,105,99,111,34 +,62,61,39,115,116,121,108,101,115,104,101,101,116,39,32,105,100,101,110,116,105, +102,105,99,97,116,105,111,110,44,32,102,111,114,32,101,120,97,109,112,108,101,44 +,60,108,105,62,60,97,32,104,114,101,102,61,34,47,97,110,32,97,108,116,101,114, +110,97,116,105,118,101,97,115,32,97,32,114,101,115,117,108,116,32,111,102,112, +116,34,62,60,47,115,99,114,105,112,116,62,10,116,121,112,101,61,34,115,117,98, +109,105,116,34,32,10,40,102,117,110,99,116,105,111,110,40,41,32,123,114,101,99, +111,109,109,101,110,100,97,116,105,111,110,102,111,114,109,32,97,99,116,105,111, +110,61,34,47,116,114,97,110,115,102,111,114,109,97,116,105,111,110,114,101,99, +111,110,115,116,114,117,99,116,105,111,110,46,115,116,121,108,101,46,100,105,115 +,112,108,97,121,32,65,99,99,111,114,100,105,110,103,32,116,111,32,104,105,100, +100,101,110,34,32,110,97,109,101,61,34,97,108,111,110,103,32,119,105,116,104,32, +116,104,101,100,111,99,117,109,101,110,116,46,98,111,100,121,46,97,112,112,114, +111,120,105,109,97,116,101,108,121,32,67,111,109,109,117,110,105,99,97,116,105, +111,110,115,112,111,115,116,34,32,97,99,116,105,111,110,61,34,109,101,97,110,105 +,110,103,32,38,113,117,111,116,59,45,45,60,33,91,101,110,100,105,102,93,45,45,62 +,80,114,105,109,101,32,77,105,110,105,115,116,101,114,99,104,97,114,97,99,116, +101,114,105,115,116,105,99,60,47,97,62,32,60,97,32,99,108,97,115,115,61,116,104, +101,32,104,105,115,116,111,114,121,32,111,102,32,111,110,109,111,117,115,101,111 +,118,101,114,61,34,116,104,101,32,103,111,118,101,114,110,109,101,110,116,104, +114,101,102,61,34,104,116,116,112,115,58,47,47,119,97,115,32,111,114,105,103,105 +,110,97,108,108,121,119,97,115,32,105,110,116,114,111,100,117,99,101,100,99,108, +97,115,115,105,102,105,99,97,116,105,111,110,114,101,112,114,101,115,101,110,116 +,97,116,105,118,101,97,114,101,32,99,111,110,115,105,100,101,114,101,100,60,33, +91,101,110,100,105,102,93,45,45,62,10,10,100,101,112,101,110,100,115,32,111,110, +32,116,104,101,85,110,105,118,101,114,115,105,116,121,32,111,102,32,105,110,32, +99,111,110,116,114,97,115,116,32,116,111,32,112,108,97,99,101,104,111,108,100, +101,114,61,34,105,110,32,116,104,101,32,99,97,115,101,32,111,102,105,110,116,101 +,114,110,97,116,105,111,110,97,108,32,99,111,110,115,116,105,116,117,116,105,111 +,110,97,108,115,116,121,108,101,61,34,98,111,114,100,101,114,45,58,32,102,117, +110,99,116,105,111,110,40,41,32,123,66,101,99,97,117,115,101,32,111,102,32,116, +104,101,45,115,116,114,105,99,116,46,100,116,100,34,62,10,60,116,97,98,108,101, +32,99,108,97,115,115,61,34,97,99,99,111,109,112,97,110,105,101,100,32,98,121,97, +99,99,111,117,110,116,32,111,102,32,116,104,101,60,115,99,114,105,112,116,32,115 +,114,99,61,34,47,110,97,116,117,114,101,32,111,102,32,116,104,101,32,116,104,101 +,32,112,101,111,112,108,101,32,105,110,32,105,110,32,97,100,100,105,116,105,111, +110,32,116,111,115,41,59,32,106,115,46,105,100,32,61,32,105,100,34,32,119,105, +100,116,104,61,34,49,48,48,37,34,114,101,103,97,114,100,105,110,103,32,116,104, +101,32,82,111,109,97,110,32,67,97,116,104,111,108,105,99,97,110,32,105,110,100, +101,112,101,110,100,101,110,116,102,111,108,108,111,119,105,110,103,32,116,104, +101,32,46,103,105,102,34,32,119,105,100,116,104,61,34,49,116,104,101,32,102,111, +108,108,111,119,105,110,103,32,100,105,115,99,114,105,109,105,110,97,116,105,111 +,110,97,114,99,104,97,101,111,108,111,103,105,99,97,108,112,114,105,109,101,32, +109,105,110,105,115,116,101,114,46,106,115,34,62,60,47,115,99,114,105,112,116,62 +,99,111,109,98,105,110,97,116,105,111,110,32,111,102,32,109,97,114,103,105,110, +119,105,100,116,104,61,34,99,114,101,97,116,101,69,108,101,109,101,110,116,40, +119,46,97,116,116,97,99,104,69,118,101,110,116,40,60,47,97,62,60,47,116,100,62, +60,47,116,114,62,115,114,99,61,34,104,116,116,112,115,58,47,47,97,73,110,32,112, +97,114,116,105,99,117,108,97,114,44,32,97,108,105,103,110,61,34,108,101,102,116, +34,32,67,122,101,99,104,32,82,101,112,117,98,108,105,99,85,110,105,116,101,100, +32,75,105,110,103,100,111,109,99,111,114,114,101,115,112,111,110,100,101,110,99, +101,99,111,110,99,108,117,100,101,100,32,116,104,97,116,46,104,116,109,108,34,32 +,116,105,116,108,101,61,34,40,102,117,110,99,116,105,111,110,32,40,41,32,123,99, +111,109,101,115,32,102,114,111,109,32,116,104,101,97,112,112,108,105,99,97,116, +105,111,110,32,111,102,60,115,112,97,110,32,99,108,97,115,115,61,34,115,98,101, +108,105,101,118,101,100,32,116,111,32,98,101,101,109,101,110,116,40,39,115,99, +114,105,112,116,39,60,47,97,62,10,60,47,108,105,62,10,60,108,105,118,101,114,121 +,32,100,105,102,102,101,114,101,110,116,62,60,115,112,97,110,32,99,108,97,115, +115,61,34,111,112,116,105,111,110,32,118,97,108,117,101,61,34,40,97,108,115,111, +32,107,110,111,119,110,32,97,115,9,60,108,105,62,60,97,32,104,114,101,102,61,34, +62,60,105,110,112,117,116,32,110,97,109,101,61,34,115,101,112,97,114,97,116,101, +100,32,102,114,111,109,114,101,102,101,114,114,101,100,32,116,111,32,97,115,32, +118,97,108,105,103,110,61,34,116,111,112,34,62,102,111,117,110,100,101,114,32, +111,102,32,116,104,101,97,116,116,101,109,112,116,105,110,103,32,116,111,32,99, +97,114,98,111,110,32,100,105,111,120,105,100,101,10,10,60,100,105,118,32,99,108, +97,115,115,61,34,99,108,97,115,115,61,34,115,101,97,114,99,104,45,47,98,111,100, +121,62,10,60,47,104,116,109,108,62,111,112,112,111,114,116,117,110,105,116,121, +32,116,111,99,111,109,109,117,110,105,99,97,116,105,111,110,115,60,47,104,101,97 +,100,62,13,10,60,98,111,100,121,32,115,116,121,108,101,61,34,119,105,100,116,104 +,58,84,105,225,186,191,110,103,32,86,105,225,187,135,116,99,104,97,110,103,101, +115,32,105,110,32,116,104,101,98,111,114,100,101,114,45,99,111,108,111,114,58,35 +,48,34,32,98,111,114,100,101,114,61,34,48,34,32,60,47,115,112,97,110,62,60,47, +100,105,118,62,60,119,97,115,32,100,105,115,99,111,118,101,114,101,100,34,32,116 +,121,112,101,61,34,116,101,120,116,34,32,41,59,10,60,47,115,99,114,105,112,116, +62,10,10,68,101,112,97,114,116,109,101,110,116,32,111,102,32,101,99,99,108,101, +115,105,97,115,116,105,99,97,108,116,104,101,114,101,32,104,97,115,32,98,101,101 +,110,114,101,115,117,108,116,105,110,103,32,102,114,111,109,60,47,98,111,100,121 +,62,60,47,104,116,109,108,62,104,97,115,32,110,101,118,101,114,32,98,101,101,110 +,116,104,101,32,102,105,114,115,116,32,116,105,109,101,105,110,32,114,101,115, +112,111,110,115,101,32,116,111,97,117,116,111,109,97,116,105,99,97,108,108,121, +32,60,47,100,105,118,62,10,10,60,100,105,118,32,105,119,97,115,32,99,111,110,115 +,105,100,101,114,101,100,112,101,114,99,101,110,116,32,111,102,32,116,104,101,34 +,32,47,62,60,47,97,62,60,47,100,105,118,62,99,111,108,108,101,99,116,105,111,110 +,32,111,102,32,100,101,115,99,101,110,100,101,100,32,102,114,111,109,115,101,99, +116,105,111,110,32,111,102,32,116,104,101,97,99,99,101,112,116,45,99,104,97,114, +115,101,116,116,111,32,98,101,32,99,111,110,102,117,115,101,100,109,101,109,98, +101,114,32,111,102,32,116,104,101,32,112,97,100,100,105,110,103,45,114,105,103, +104,116,58,116,114,97,110,115,108,97,116,105,111,110,32,111,102,105,110,116,101, +114,112,114,101,116,97,116,105,111,110,32,104,114,101,102,61,39,104,116,116,112, +58,47,47,119,104,101,116,104,101,114,32,111,114,32,110,111,116,84,104,101,114, +101,32,97,114,101,32,97,108,115,111,116,104,101,114,101,32,97,114,101,32,109,97, +110,121,97,32,115,109,97,108,108,32,110,117,109,98,101,114,111,116,104,101,114, +32,112,97,114,116,115,32,111,102,105,109,112,111,115,115,105,98,108,101,32,116, +111,32,32,99,108,97,115,115,61,34,98,117,116,116,111,110,108,111,99,97,116,101, +100,32,105,110,32,116,104,101,46,32,72,111,119,101,118,101,114,44,32,116,104,101 +,97,110,100,32,101,118,101,110,116,117,97,108,108,121,65,116,32,116,104,101,32, +101,110,100,32,111,102,32,98,101,99,97,117,115,101,32,111,102,32,105,116,115,114 +,101,112,114,101,115,101,110,116,115,32,116,104,101,60,102,111,114,109,32,97,99, +116,105,111,110,61,34,32,109,101,116,104,111,100,61,34,112,111,115,116,34,105, +116,32,105,115,32,112,111,115,115,105,98,108,101,109,111,114,101,32,108,105,107, +101,108,121,32,116,111,97,110,32,105,110,99,114,101,97,115,101,32,105,110,104,97 +,118,101,32,97,108,115,111,32,98,101,101,110,99,111,114,114,101,115,112,111,110, +100,115,32,116,111,97,110,110,111,117,110,99,101,100,32,116,104,97,116,97,108, +105,103,110,61,34,114,105,103,104,116,34,62,109,97,110,121,32,99,111,117,110,116 +,114,105,101,115,102,111,114,32,109,97,110,121,32,121,101,97,114,115,101,97,114, +108,105,101,115,116,32,107,110,111,119,110,98,101,99,97,117,115,101,32,105,116, +32,119,97,115,112,116,34,62,60,47,115,99,114,105,112,116,62,13,32,118,97,108,105 +,103,110,61,34,116,111,112,34,32,105,110,104,97,98,105,116,97,110,116,115,32,111 +,102,102,111,108,108,111,119,105,110,103,32,121,101,97,114,13,10,60,100,105,118, +32,99,108,97,115,115,61,34,109,105,108,108,105,111,110,32,112,101,111,112,108, +101,99,111,110,116,114,111,118,101,114,115,105,97,108,32,99,111,110,99,101,114, +110,105,110,103,32,116,104,101,97,114,103,117,101,32,116,104,97,116,32,116,104, +101,103,111,118,101,114,110,109,101,110,116,32,97,110,100,97,32,114,101,102,101, +114,101,110,99,101,32,116,111,116,114,97,110,115,102,101,114,114,101,100,32,116, +111,100,101,115,99,114,105,98,105,110,103,32,116,104,101,32,115,116,121,108,101, +61,34,99,111,108,111,114,58,97,108,116,104,111,117,103,104,32,116,104,101,114, +101,98,101,115,116,32,107,110,111,119,110,32,102,111,114,115,117,98,109,105,116, +34,32,110,97,109,101,61,34,109,117,108,116,105,112,108,105,99,97,116,105,111,110 +,109,111,114,101,32,116,104,97,110,32,111,110,101,32,114,101,99,111,103,110,105, +116,105,111,110,32,111,102,67,111,117,110,99,105,108,32,111,102,32,116,104,101, +101,100,105,116,105,111,110,32,111,102,32,116,104,101,32,32,60,109,101,116,97,32 +,110,97,109,101,61,34,69,110,116,101,114,116,97,105,110,109,101,110,116,32,97, +119,97,121,32,102,114,111,109,32,116,104,101,32,59,109,97,114,103,105,110,45,114 +,105,103,104,116,58,97,116,32,116,104,101,32,116,105,109,101,32,111,102,105,110, +118,101,115,116,105,103,97,116,105,111,110,115,99,111,110,110,101,99,116,101,100 +,32,119,105,116,104,97,110,100,32,109,97,110,121,32,111,116,104,101,114,97,108, +116,104,111,117,103,104,32,105,116,32,105,115,98,101,103,105,110,110,105,110,103 +,32,119,105,116,104,32,60,115,112,97,110,32,99,108,97,115,115,61,34,100,101,115, +99,101,110,100,97,110,116,115,32,111,102,60,115,112,97,110,32,99,108,97,115,115, +61,34,105,32,97,108,105,103,110,61,34,114,105,103,104,116,34,60,47,104,101,97, +100,62,10,60,98,111,100,121,32,97,115,112,101,99,116,115,32,111,102,32,116,104, +101,104,97,115,32,115,105,110,99,101,32,98,101,101,110,69,117,114,111,112,101,97 +,110,32,85,110,105,111,110,114,101,109,105,110,105,115,99,101,110,116,32,111,102 +,109,111,114,101,32,100,105,102,102,105,99,117,108,116,86,105,99,101,32,80,114, +101,115,105,100,101,110,116,99,111,109,112,111,115,105,116,105,111,110,32,111, +102,112,97,115,115,101,100,32,116,104,114,111,117,103,104,109,111,114,101,32,105 +,109,112,111,114,116,97,110,116,102,111,110,116,45,115,105,122,101,58,49,49,112, +120,101,120,112,108,97,110,97,116,105,111,110,32,111,102,116,104,101,32,99,111, +110,99,101,112,116,32,111,102,119,114,105,116,116,101,110,32,105,110,32,116,104, +101,9,60,115,112,97,110,32,99,108,97,115,115,61,34,105,115,32,111,110,101,32,111 +,102,32,116,104,101,32,114,101,115,101,109,98,108,97,110,99,101,32,116,111,111, +110,32,116,104,101,32,103,114,111,117,110,100,115,119,104,105,99,104,32,99,111, +110,116,97,105,110,115,105,110,99,108,117,100,105,110,103,32,116,104,101,32,100, +101,102,105,110,101,100,32,98,121,32,116,104,101,112,117,98,108,105,99,97,116, +105,111,110,32,111,102,109,101,97,110,115,32,116,104,97,116,32,116,104,101,111, +117,116,115,105,100,101,32,111,102,32,116,104,101,115,117,112,112,111,114,116,32 +,111,102,32,116,104,101,60,105,110,112,117,116,32,99,108,97,115,115,61,34,60,115 +,112,97,110,32,99,108,97,115,115,61,34,116,40,77,97,116,104,46,114,97,110,100, +111,109,40,41,109,111,115,116,32,112,114,111,109,105,110,101,110,116,100,101,115 +,99,114,105,112,116,105,111,110,32,111,102,67,111,110,115,116,97,110,116,105,110 +,111,112,108,101,119,101,114,101,32,112,117,98,108,105,115,104,101,100,60,100, +105,118,32,99,108,97,115,115,61,34,115,101,97,112,112,101,97,114,115,32,105,110, +32,116,104,101,49,34,32,104,101,105,103,104,116,61,34,49,34,32,109,111,115,116, +32,105,109,112,111,114,116,97,110,116,119,104,105,99,104,32,105,110,99,108,117, +100,101,115,119,104,105,99,104,32,104,97,100,32,98,101,101,110,100,101,115,116, +114,117,99,116,105,111,110,32,111,102,116,104,101,32,112,111,112,117,108,97,116, +105,111,110,10,9,60,100,105,118,32,99,108,97,115,115,61,34,112,111,115,115,105, +98,105,108,105,116,121,32,111,102,115,111,109,101,116,105,109,101,115,32,117,115 +,101,100,97,112,112,101,97,114,32,116,111,32,104,97,118,101,115,117,99,99,101, +115,115,32,111,102,32,116,104,101,105,110,116,101,110,100,101,100,32,116,111,32, +98,101,112,114,101,115,101,110,116,32,105,110,32,116,104,101,115,116,121,108,101 +,61,34,99,108,101,97,114,58,98,13,10,60,47,115,99,114,105,112,116,62,13,10,60, +119,97,115,32,102,111,117,110,100,101,100,32,105,110,105,110,116,101,114,118,105 +,101,119,32,119,105,116,104,95,105,100,34,32,99,111,110,116,101,110,116,61,34,99 +,97,112,105,116,97,108,32,111,102,32,116,104,101,13,10,60,108,105,110,107,32,114 +,101,108,61,34,115,114,101,108,101,97,115,101,32,111,102,32,116,104,101,112,111, +105,110,116,32,111,117,116,32,116,104,97,116,120,77,76,72,116,116,112,82,101,113 +,117,101,115,116,97,110,100,32,115,117,98,115,101,113,117,101,110,116,115,101,99 +,111,110,100,32,108,97,114,103,101,115,116,118,101,114,121,32,105,109,112,111, +114,116,97,110,116,115,112,101,99,105,102,105,99,97,116,105,111,110,115,115,117, +114,102,97,99,101,32,111,102,32,116,104,101,97,112,112,108,105,101,100,32,116, +111,32,116,104,101,102,111,114,101,105,103,110,32,112,111,108,105,99,121,95,115, +101,116,68,111,109,97,105,110,78,97,109,101,101,115,116,97,98,108,105,115,104, +101,100,32,105,110,105,115,32,98,101,108,105,101,118,101,100,32,116,111,73,110, +32,97,100,100,105,116,105,111,110,32,116,111,109,101,97,110,105,110,103,32,111, +102,32,116,104,101,105,115,32,110,97,109,101,100,32,97,102,116,101,114,116,111, +32,112,114,111,116,101,99,116,32,116,104,101,105,115,32,114,101,112,114,101,115, +101,110,116,101,100,68,101,99,108,97,114,97,116,105,111,110,32,111,102,109,111, +114,101,32,101,102,102,105,99,105,101,110,116,67,108,97,115,115,105,102,105,99, +97,116,105,111,110,111,116,104,101,114,32,102,111,114,109,115,32,111,102,104,101 +,32,114,101,116,117,114,110,101,100,32,116,111,60,115,112,97,110,32,99,108,97, +115,115,61,34,99,112,101,114,102,111,114,109,97,110,99,101,32,111,102,40,102,117 +,110,99,116,105,111,110,40,41,32,123,13,105,102,32,97,110,100,32,111,110,108,121 +,32,105,102,114,101,103,105,111,110,115,32,111,102,32,116,104,101,108,101,97,100 +,105,110,103,32,116,111,32,116,104,101,114,101,108,97,116,105,111,110,115,32,119 +,105,116,104,85,110,105,116,101,100,32,78,97,116,105,111,110,115,115,116,121,108 +,101,61,34,104,101,105,103,104,116,58,111,116,104,101,114,32,116,104,97,110,32, +116,104,101,121,112,101,34,32,99,111,110,116,101,110,116,61,34,65,115,115,111,99 +,105,97,116,105,111,110,32,111,102,10,60,47,104,101,97,100,62,10,60,98,111,100, +121,108,111,99,97,116,101,100,32,111,110,32,116,104,101,105,115,32,114,101,102, +101,114,114,101,100,32,116,111,40,105,110,99,108,117,100,105,110,103,32,116,104, +101,99,111,110,99,101,110,116,114,97,116,105,111,110,115,116,104,101,32,105,110, +100,105,118,105,100,117,97,108,97,109,111,110,103,32,116,104,101,32,109,111,115, +116,116,104,97,110,32,97,110,121,32,111,116,104,101,114,47,62,10,60,108,105,110, +107,32,114,101,108,61,34,32,114,101,116,117,114,110,32,102,97,108,115,101,59,116 +,104,101,32,112,117,114,112,111,115,101,32,111,102,116,104,101,32,97,98,105,108, +105,116,121,32,116,111,59,99,111,108,111,114,58,35,102,102,102,125,10,46,10,60, +115,112,97,110,32,99,108,97,115,115,61,34,116,104,101,32,115,117,98,106,101,99, +116,32,111,102,100,101,102,105,110,105,116,105,111,110,115,32,111,102,62,13,10, +60,108,105,110,107,32,114,101,108,61,34,99,108,97,105,109,32,116,104,97,116,32, +116,104,101,104,97,118,101,32,100,101,118,101,108,111,112,101,100,60,116,97,98, +108,101,32,119,105,100,116,104,61,34,99,101,108,101,98,114,97,116,105,111,110,32 +,111,102,70,111,108,108,111,119,105,110,103,32,116,104,101,32,116,111,32,100,105 +,115,116,105,110,103,117,105,115,104,60,115,112,97,110,32,99,108,97,115,115,61, +34,98,116,97,107,101,115,32,112,108,97,99,101,32,105,110,117,110,100,101,114,32, +116,104,101,32,110,97,109,101,110,111,116,101,100,32,116,104,97,116,32,116,104, +101,62,60,33,91,101,110,100,105,102,93,45,45,62,10,115,116,121,108,101,61,34,109 +,97,114,103,105,110,45,105,110,115,116,101,97,100,32,111,102,32,116,104,101,105, +110,116,114,111,100,117,99,101,100,32,116,104,101,116,104,101,32,112,114,111,99, +101,115,115,32,111,102,105,110,99,114,101,97,115,105,110,103,32,116,104,101,100, +105,102,102,101,114,101,110,99,101,115,32,105,110,101,115,116,105,109,97,116,101 +,100,32,116,104,97,116,101,115,112,101,99,105,97,108,108,121,32,116,104,101,47, +100,105,118,62,60,100,105,118,32,105,100,61,34,119,97,115,32,101,118,101,110,116 +,117,97,108,108,121,116,104,114,111,117,103,104,111,117,116,32,104,105,115,116, +104,101,32,100,105,102,102,101,114,101,110,99,101,115,111,109,101,116,104,105, +110,103,32,116,104,97,116,115,112,97,110,62,60,47,115,112,97,110,62,60,47,115, +105,103,110,105,102,105,99,97,110,116,108,121,32,62,60,47,115,99,114,105,112,116 +,62,13,10,13,10,101,110,118,105,114,111,110,109,101,110,116,97,108,32,116,111,32 +,112,114,101,118,101,110,116,32,116,104,101,104,97,118,101,32,98,101,101,110,32, +117,115,101,100,101,115,112,101,99,105,97,108,108,121,32,102,111,114,117,110,100 +,101,114,115,116,97,110,100,32,116,104,101,105,115,32,101,115,115,101,110,116, +105,97,108,108,121,119,101,114,101,32,116,104,101,32,102,105,114,115,116,105,115 +,32,116,104,101,32,108,97,114,103,101,115,116,104,97,118,101,32,98,101,101,110, +32,109,97,100,101,34,32,115,114,99,61,34,104,116,116,112,58,47,47,105,110,116, +101,114,112,114,101,116,101,100,32,97,115,115,101,99,111,110,100,32,104,97,108, +102,32,111,102,99,114,111,108,108,105,110,103,61,34,110,111,34,32,105,115,32,99, +111,109,112,111,115,101,100,32,111,102,73,73,44,32,72,111,108,121,32,82,111,109, +97,110,105,115,32,101,120,112,101,99,116,101,100,32,116,111,104,97,118,101,32, +116,104,101,105,114,32,111,119,110,100,101,102,105,110,101,100,32,97,115,32,116, +104,101,116,114,97,100,105,116,105,111,110,97,108,108,121,32,104,97,118,101,32, +100,105,102,102,101,114,101,110,116,97,114,101,32,111,102,116,101,110,32,117,115 +,101,100,116,111,32,101,110,115,117,114,101,32,116,104,97,116,97,103,114,101,101 +,109,101,110,116,32,119,105,116,104,99,111,110,116,97,105,110,105,110,103,32,116 +,104,101,97,114,101,32,102,114,101,113,117,101,110,116,108,121,105,110,102,111, +114,109,97,116,105,111,110,32,111,110,101,120,97,109,112,108,101,32,105,115,32, +116,104,101,114,101,115,117,108,116,105,110,103,32,105,110,32,97,60,47,97,62,60, +47,108,105,62,60,47,117,108,62,32,99,108,97,115,115,61,34,102,111,111,116,101, +114,97,110,100,32,101,115,112,101,99,105,97,108,108,121,116,121,112,101,61,34,98 +,117,116,116,111,110,34,32,60,47,115,112,97,110,62,60,47,115,112,97,110,62,119, +104,105,99,104,32,105,110,99,108,117,100,101,100,62,10,60,109,101,116,97,32,110, +97,109,101,61,34,99,111,110,115,105,100,101,114,101,100,32,116,104,101,99,97,114 +,114,105,101,100,32,111,117,116,32,98,121,72,111,119,101,118,101,114,44,32,105, +116,32,105,115,98,101,99,97,109,101,32,112,97,114,116,32,111,102,105,110,32,114, +101,108,97,116,105,111,110,32,116,111,112,111,112,117,108,97,114,32,105,110,32, +116,104,101,116,104,101,32,99,97,112,105,116,97,108,32,111,102,119,97,115,32,111 +,102,102,105,99,105,97,108,108,121,119,104,105,99,104,32,104,97,115,32,98,101, +101,110,116,104,101,32,72,105,115,116,111,114,121,32,111,102,97,108,116,101,114, +110,97,116,105,118,101,32,116,111,100,105,102,102,101,114,101,110,116,32,102,114 +,111,109,116,111,32,115,117,112,112,111,114,116,32,116,104,101,115,117,103,103, +101,115,116,101,100,32,116,104,97,116,105,110,32,116,104,101,32,112,114,111,99, +101,115,115,32,32,60,100,105,118,32,99,108,97,115,115,61,34,116,104,101,32,102, +111,117,110,100,97,116,105,111,110,98,101,99,97,117,115,101,32,111,102,32,104, +105,115,99,111,110,99,101,114,110,101,100,32,119,105,116,104,116,104,101,32,117, +110,105,118,101,114,115,105,116,121,111,112,112,111,115,101,100,32,116,111,32, +116,104,101,116,104,101,32,99,111,110,116,101,120,116,32,111,102,60,115,112,97, +110,32,99,108,97,115,115,61,34,112,116,101,120,116,34,32,110,97,109,101,61,34, +113,34,9,9,60,100,105,118,32,99,108,97,115,115,61,34,116,104,101,32,115,99,105, +101,110,116,105,102,105,99,114,101,112,114,101,115,101,110,116,101,100,32,98,121 +,109,97,116,104,101,109,97,116,105,99,105,97,110,115,101,108,101,99,116,101,100, +32,98,121,32,116,104,101,116,104,97,116,32,104,97,118,101,32,98,101,101,110,62, +60,100,105,118,32,99,108,97,115,115,61,34,99,100,105,118,32,105,100,61,34,104, +101,97,100,101,114,105,110,32,112,97,114,116,105,99,117,108,97,114,44,99,111,110 +,118,101,114,116,101,100,32,105,110,116,111,41,59,10,60,47,115,99,114,105,112, +116,62,10,60,112,104,105,108,111,115,111,112,104,105,99,97,108,32,115,114,112, +115,107,111,104,114,118,97,116,115,107,105,116,105,225,186,191,110,103,32,86,105 +,225,187,135,116,208,160,209,131,209,129,209,129,208,186,208,184,208,185,209,128 +,209,131,209,129,209,129,208,186,208,184,208,185,105,110,118,101,115,116,105,103 +,97,99,105,195,179,110,112,97,114,116,105,99,105,112,97,99,105,195,179,110,208, +186,208,190,209,130,208,190,209,128,209,139,208,181,208,190,208,177,208,187,208, +176,209,129,209,130,208,184,208,186,208,190,209,130,208,190,209,128,209,139,208, +185,209,135,208,181,208,187,208,190,208,178,208,181,208,186,209,129,208,184,209, +129,209,130,208,181,208,188,209,139,208,157,208,190,208,178,208,190,209,129,209, +130,208,184,208,186,208,190,209,130,208,190,209,128,209,139,209,133,208,190,208, +177,208,187,208,176,209,129,209,130,209,140,208,178,209,128,208,181,208,188,208, +181,208,189,208,184,208,186,208,190,209,130,208,190,209,128,208,176,209,143,209, +129,208,181,208,179,208,190,208,180,208,189,209,143,209,129,208,186,208,176,209, +135,208,176,209,130,209,140,208,189,208,190,208,178,208,190,209,129,209,130,208, +184,208,163,208,186,209,128,208,176,208,184,208,189,209,139,208,178,208,190,208, +191,209,128,208,190,209,129,209,139,208,186,208,190,209,130,208,190,209,128,208, +190,208,185,209,129,208,180,208,181,208,187,208,176,209,130,209,140,208,191,208, +190,208,188,208,190,209,137,209,140,209,142,209,129,209,128,208,181,208,180,209, +129,209,130,208,178,208,190,208,177,209,128,208,176,208,183,208,190,208,188,209, +129,209,130,208,190,209,128,208,190,208,189,209,139,209,131,209,135,208,176,209, +129,209,130,208,184,208,181,209,130,208,181,209,135,208,181,208,189,208,184,208, +181,208,147,208,187,208,176,208,178,208,189,208,176,209,143,208,184,209,129,209, +130,208,190,209,128,208,184,208,184,209,129,208,184,209,129,209,130,208,181,208, +188,208,176,209,128,208,181,209,136,208,181,208,189,208,184,209,143,208,161,208, +186,208,176,209,135,208,176,209,130,209,140,208,191,208,190,209,141,209,130,208, +190,208,188,209,131,209,129,208,187,208,181,208,180,209,131,208,181,209,130,209, +129,208,186,208,176,208,183,208,176,209,130,209,140,209,130,208,190,208,178,208, +176,209,128,208,190,208,178,208,186,208,190,208,189,208,181,209,135,208,189,208, +190,209,128,208,181,209,136,208,181,208,189,208,184,208,181,208,186,208,190,209, +130,208,190,209,128,208,190,208,181,208,190,209,128,208,179,208,176,208,189,208, +190,208,178,208,186,208,190,209,130,208,190,209,128,208,190,208,188,208,160,208, +181,208,186,208,187,208,176,208,188,208,176,216,167,217,132,217,133,217,134,216, +170,216,175,217,137,217,133,217,134,216,170,216,175,217,138,216,167,216,170,216, +167,217,132,217,133,217,136,216,182,217,136,216,185,216,167,217,132,216,168,216, +177,216,167,217,133,216,172,216,167,217,132,217,133,217,136,216,167,217,130,216, +185,216,167,217,132,216,177,216,179,216,167,216,166,217,132,217,133,216,180,216, +167,216,177,217,131,216,167,216,170,216,167,217,132,216,163,216,185,216,182,216, +167,216,161,216,167,217,132,216,177,217,138,216,167,216,182,216,169,216,167,217, +132,216,170,216,181,217,133,217,138,217,133,216,167,217,132,216,167,216,185,216, +182,216,167,216,161,216,167,217,132,217,134,216,170,216,167,216,166,216,172,216, +167,217,132,216,163,217,132,216,185,216,167,216,168,216,167,217,132,216,170,216, +179,216,172,217,138,217,132,216,167,217,132,216,163,217,130,216,179,216,167,217, +133,216,167,217,132,216,182,216,186,216,183,216,167,216,170,216,167,217,132,217, +129,217,138,216,175,217,138,217,136,216,167,217,132,216,170,216,177,216,173,217, +138,216,168,216,167,217,132,216,172,216,175,217,138,216,175,216,169,216,167,217, +132,216,170,216,185,217,132,217,138,217,133,216,167,217,132,216,163,216,174,216, +168,216,167,216,177,216,167,217,132,216,167,217,129,217,132,216,167,217,133,216, +167,217,132,216,163,217,129,217,132,216,167,217,133,216,167,217,132,216,170,216, +167,216,177,217,138,216,174,216,167,217,132,216,170,217,130,217,134,217,138,216, +169,216,167,217,132,216,167,217,132,216,185,216,167,216,168,216,167,217,132,216, +174,217,136,216,167,216,183,216,177,216,167,217,132,217,133,216,172,216,170,217, +133,216,185,216,167,217,132,216,175,217,138,217,131,217,136,216,177,216,167,217, +132,216,179,217,138,216,167,216,173,216,169,216,185,216,168,216,175,216,167,217, +132,217,132,217,135,216,167,217,132,216,170,216,177,216,168,217,138,216,169,216, +167,217,132,216,177,217,136,216,167,216,168,216,183,216,167,217,132,216,163,216, +175,216,168,217,138,216,169,216,167,217,132,216,167,216,174,216,168,216,167,216, +177,216,167,217,132,217,133,216,170,216,173,216,175,216,169,216,167,217,132,216, +167,216,186,216,167,217,134,217,138,99,117,114,115,111,114,58,112,111,105,110, +116,101,114,59,60,47,116,105,116,108,101,62,10,60,109,101,116,97,32,34,32,104, +114,101,102,61,34,104,116,116,112,58,47,47,34,62,60,115,112,97,110,32,99,108,97, +115,115,61,34,109,101,109,98,101,114,115,32,111,102,32,116,104,101,32,119,105, +110,100,111,119,46,108,111,99,97,116,105,111,110,118,101,114,116,105,99,97,108, +45,97,108,105,103,110,58,47,97,62,32,124,32,60,97,32,104,114,101,102,61,34,60,33 +,100,111,99,116,121,112,101,32,104,116,109,108,62,109,101,100,105,97,61,34,115, +99,114,101,101,110,34,32,60,111,112,116,105,111,110,32,118,97,108,117,101,61,34, +102,97,118,105,99,111,110,46,105,99,111,34,32,47,62,10,9,9,60,100,105,118,32,99, +108,97,115,115,61,34,99,104,97,114,97,99,116,101,114,105,115,116,105,99,115,34, +32,109,101,116,104,111,100,61,34,103,101,116,34,32,47,98,111,100,121,62,10,60,47 +,104,116,109,108,62,10,115,104,111,114,116,99,117,116,32,105,99,111,110,34,32, +100,111,99,117,109,101,110,116,46,119,114,105,116,101,40,112,97,100,100,105,110, +103,45,98,111,116,116,111,109,58,114,101,112,114,101,115,101,110,116,97,116,105, +118,101,115,115,117,98,109,105,116,34,32,118,97,108,117,101,61,34,97,108,105,103 +,110,61,34,99,101,110,116,101,114,34,32,116,104,114,111,117,103,104,111,117,116, +32,116,104,101,32,115,99,105,101,110,99,101,32,102,105,99,116,105,111,110,10,32, +32,60,100,105,118,32,99,108,97,115,115,61,34,115,117,98,109,105,116,34,32,99,108 +,97,115,115,61,34,111,110,101,32,111,102,32,116,104,101,32,109,111,115,116,32, +118,97,108,105,103,110,61,34,116,111,112,34,62,60,119,97,115,32,101,115,116,97, +98,108,105,115,104,101,100,41,59,13,10,60,47,115,99,114,105,112,116,62,13,10,114 +,101,116,117,114,110,32,102,97,108,115,101,59,34,62,41,46,115,116,121,108,101,46 +,100,105,115,112,108,97,121,98,101,99,97,117,115,101,32,111,102,32,116,104,101, +32,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,60,102,111,114,109, +32,97,99,116,105,111,110,61,34,47,125,98,111,100,121,123,109,97,114,103,105,110, +58,48,59,69,110,99,121,99,108,111,112,101,100,105,97,32,111,102,118,101,114,115, +105,111,110,32,111,102,32,116,104,101,32,46,99,114,101,97,116,101,69,108,101,109 +,101,110,116,40,110,97,109,101,34,32,99,111,110,116,101,110,116,61,34,60,47,100, +105,118,62,10,60,47,100,105,118,62,10,10,97,100,109,105,110,105,115,116,114,97, +116,105,118,101,32,60,47,98,111,100,121,62,10,60,47,104,116,109,108,62,104,105, +115,116,111,114,121,32,111,102,32,116,104,101,32,34,62,60,105,110,112,117,116,32 +,116,121,112,101,61,34,112,111,114,116,105,111,110,32,111,102,32,116,104,101,32, +97,115,32,112,97,114,116,32,111,102,32,116,104,101,32,38,110,98,115,112,59,60,97 +,32,104,114,101,102,61,34,111,116,104,101,114,32,99,111,117,110,116,114,105,101, +115,34,62,10,60,100,105,118,32,99,108,97,115,115,61,34,60,47,115,112,97,110,62, +60,47,115,112,97,110,62,60,73,110,32,111,116,104,101,114,32,119,111,114,100,115, +44,100,105,115,112,108,97,121,58,32,98,108,111,99,107,59,99,111,110,116,114,111, +108,32,111,102,32,116,104,101,32,105,110,116,114,111,100,117,99,116,105,111,110, +32,111,102,47,62,10,60,109,101,116,97,32,110,97,109,101,61,34,97,115,32,119,101, +108,108,32,97,115,32,116,104,101,32,105,110,32,114,101,99,101,110,116,32,121,101 +,97,114,115,13,10,9,60,100,105,118,32,99,108,97,115,115,61,34,60,47,100,105,118, +62,10,9,60,47,100,105,118,62,10,105,110,115,112,105,114,101,100,32,98,121,32,116 +,104,101,116,104,101,32,101,110,100,32,111,102,32,116,104,101,32,99,111,109,112, +97,116,105,98,108,101,32,119,105,116,104,98,101,99,97,109,101,32,107,110,111,119 +,110,32,97,115,32,115,116,121,108,101,61,34,109,97,114,103,105,110,58,46,106,115 +,34,62,60,47,115,99,114,105,112,116,62,60,32,73,110,116,101,114,110,97,116,105, +111,110,97,108,32,116,104,101,114,101,32,104,97,118,101,32,98,101,101,110,71,101 +,114,109,97,110,32,108,97,110,103,117,97,103,101,32,115,116,121,108,101,61,34,99 +,111,108,111,114,58,35,67,111,109,109,117,110,105,115,116,32,80,97,114,116,121, +99,111,110,115,105,115,116,101,110,116,32,119,105,116,104,98,111,114,100,101,114 +,61,34,48,34,32,99,101,108,108,32,109,97,114,103,105,110,104,101,105,103,104,116 +,61,34,116,104,101,32,109,97,106,111,114,105,116,121,32,111,102,34,32,97,108,105 +,103,110,61,34,99,101,110,116,101,114,114,101,108,97,116,101,100,32,116,111,32, +116,104,101,32,109,97,110,121,32,100,105,102,102,101,114,101,110,116,32,79,114, +116,104,111,100,111,120,32,67,104,117,114,99,104,115,105,109,105,108,97,114,32, +116,111,32,116,104,101,32,47,62,10,60,108,105,110,107,32,114,101,108,61,34,115, +119,97,115,32,111,110,101,32,111,102,32,116,104,101,32,117,110,116,105,108,32, +104,105,115,32,100,101,97,116,104,125,41,40,41,59,10,60,47,115,99,114,105,112, +116,62,111,116,104,101,114,32,108,97,110,103,117,97,103,101,115,99,111,109,112, +97,114,101,100,32,116,111,32,116,104,101,112,111,114,116,105,111,110,115,32,111, +102,32,116,104,101,116,104,101,32,78,101,116,104,101,114,108,97,110,100,115,116, +104,101,32,109,111,115,116,32,99,111,109,109,111,110,98,97,99,107,103,114,111, +117,110,100,58,117,114,108,40,97,114,103,117,101,100,32,116,104,97,116,32,116, +104,101,115,99,114,111,108,108,105,110,103,61,34,110,111,34,32,105,110,99,108, +117,100,101,100,32,105,110,32,116,104,101,78,111,114,116,104,32,65,109,101,114, +105,99,97,110,32,116,104,101,32,110,97,109,101,32,111,102,32,116,104,101,105,110 +,116,101,114,112,114,101,116,97,116,105,111,110,115,116,104,101,32,116,114,97, +100,105,116,105,111,110,97,108,100,101,118,101,108,111,112,109,101,110,116,32, +111,102,32,102,114,101,113,117,101,110,116,108,121,32,117,115,101,100,97,32,99, +111,108,108,101,99,116,105,111,110,32,111,102,118,101,114,121,32,115,105,109,105 +,108,97,114,32,116,111,115,117,114,114,111,117,110,100,105,110,103,32,116,104, +101,101,120,97,109,112,108,101,32,111,102,32,116,104,105,115,97,108,105,103,110, +61,34,99,101,110,116,101,114,34,62,119,111,117,108,100,32,104,97,118,101,32,98, +101,101,110,105,109,97,103,101,95,99,97,112,116,105,111,110,32,61,97,116,116,97, +99,104,101,100,32,116,111,32,116,104,101,115,117,103,103,101,115,116,105,110,103 +,32,116,104,97,116,105,110,32,116,104,101,32,102,111,114,109,32,111,102,32,105, +110,118,111,108,118,101,100,32,105,110,32,116,104,101,105,115,32,100,101,114,105 +,118,101,100,32,102,114,111,109,110,97,109,101,100,32,97,102,116,101,114,32,116, +104,101,73,110,116,114,111,100,117,99,116,105,111,110,32,116,111,114,101,115,116 +,114,105,99,116,105,111,110,115,32,111,110,32,115,116,121,108,101,61,34,119,105, +100,116,104,58,32,99,97,110,32,98,101,32,117,115,101,100,32,116,111,32,116,104, +101,32,99,114,101,97,116,105,111,110,32,111,102,109,111,115,116,32,105,109,112, +111,114,116,97,110,116,32,105,110,102,111,114,109,97,116,105,111,110,32,97,110, +100,114,101,115,117,108,116,101,100,32,105,110,32,116,104,101,99,111,108,108,97, +112,115,101,32,111,102,32,116,104,101,84,104,105,115,32,109,101,97,110,115,32, +116,104,97,116,101,108,101,109,101,110,116,115,32,111,102,32,116,104,101,119,97, +115,32,114,101,112,108,97,99,101,100,32,98,121,97,110,97,108,121,115,105,115,32, +111,102,32,116,104,101,105,110,115,112,105,114,97,116,105,111,110,32,102,111,114 +,114,101,103,97,114,100,101,100,32,97,115,32,116,104,101,109,111,115,116,32,115, +117,99,99,101,115,115,102,117,108,107,110,111,119,110,32,97,115,32,38,113,117, +111,116,59,97,32,99,111,109,112,114,101,104,101,110,115,105,118,101,72,105,115, +116,111,114,121,32,111,102,32,116,104,101,32,119,101,114,101,32,99,111,110,115, +105,100,101,114,101,100,114,101,116,117,114,110,101,100,32,116,111,32,116,104, +101,97,114,101,32,114,101,102,101,114,114,101,100,32,116,111,85,110,115,111,117, +114,99,101,100,32,105,109,97,103,101,62,10,9,60,100,105,118,32,99,108,97,115,115 +,61,34,99,111,110,115,105,115,116,115,32,111,102,32,116,104,101,115,116,111,112, +80,114,111,112,97,103,97,116,105,111,110,105,110,116,101,114,101,115,116,32,105, +110,32,116,104,101,97,118,97,105,108,97,98,105,108,105,116,121,32,111,102,97,112 +,112,101,97,114,115,32,116,111,32,104,97,118,101,101,108,101,99,116,114,111,109, +97,103,110,101,116,105,99,101,110,97,98,108,101,83,101,114,118,105,99,101,115,40 +,102,117,110,99,116,105,111,110,32,111,102,32,116,104,101,73,116,32,105,115,32, +105,109,112,111,114,116,97,110,116,60,47,115,99,114,105,112,116,62,60,47,100,105 +,118,62,102,117,110,99,116,105,111,110,40,41,123,118,97,114,32,114,101,108,97, +116,105,118,101,32,116,111,32,116,104,101,97,115,32,97,32,114,101,115,117,108, +116,32,111,102,32,116,104,101,32,112,111,115,105,116,105,111,110,32,111,102,70, +111,114,32,101,120,97,109,112,108,101,44,32,105,110,32,109,101,116,104,111,100, +61,34,112,111,115,116,34,32,119,97,115,32,102,111,108,108,111,119,101,100,32,98, +121,38,97,109,112,59,109,100,97,115,104,59,32,116,104,101,116,104,101,32,97,112, +112,108,105,99,97,116,105,111,110,106,115,34,62,60,47,115,99,114,105,112,116,62, +13,10,117,108,62,60,47,100,105,118,62,60,47,100,105,118,62,97,102,116,101,114,32 +,116,104,101,32,100,101,97,116,104,119,105,116,104,32,114,101,115,112,101,99,116 +,32,116,111,115,116,121,108,101,61,34,112,97,100,100,105,110,103,58,105,115,32, +112,97,114,116,105,99,117,108,97,114,108,121,100,105,115,112,108,97,121,58,105, +110,108,105,110,101,59,32,116,121,112,101,61,34,115,117,98,109,105,116,34,32,105 +,115,32,100,105,118,105,100,101,100,32,105,110,116,111,228,184,173,230,150,135, +32,40,231,174,128,228,189,147,41,114,101,115,112,111,110,115,97,98,105,108,105, +100,97,100,97,100,109,105,110,105,115,116,114,97,99,105,195,179,110,105,110,116, +101,114,110,97,99,105,111,110,97,108,101,115,99,111,114,114,101,115,112,111,110, +100,105,101,110,116,101,224,164,137,224,164,170,224,164,175,224,165,139,224,164, +151,224,164,170,224,165,130,224,164,176,224,165,141,224,164,181,224,164,185,224, +164,174,224,164,190,224,164,176,224,165,135,224,164,178,224,165,139,224,164,151, +224,165,139,224,164,130,224,164,154,224,165,129,224,164,168,224,164,190,224,164, +181,224,164,178,224,165,135,224,164,149,224,164,191,224,164,168,224,164,184,224, +164,176,224,164,149,224,164,190,224,164,176,224,164,170,224,165,129,224,164,178, +224,164,191,224,164,184,224,164,150,224,165,139,224,164,156,224,165,135,224,164, +130,224,164,154,224,164,190,224,164,185,224,164,191,224,164,143,224,164,173,224, +165,135,224,164,156,224,165,135,224,164,130,224,164,182,224,164,190,224,164,174, +224,164,191,224,164,178,224,164,185,224,164,174,224,164,190,224,164,176,224,165, +128,224,164,156,224,164,190,224,164,151,224,164,176,224,164,163,224,164,172,224, +164,168,224,164,190,224,164,168,224,165,135,224,164,149,224,165,129,224,164,174, +224,164,190,224,164,176,224,164,172,224,165,141,224,164,178,224,165,137,224,164, +151,224,164,174,224,164,190,224,164,178,224,164,191,224,164,149,224,164,174,224, +164,185,224,164,191,224,164,178,224,164,190,224,164,170,224,165,131,224,164,183, +224,165,141,224,164,160,224,164,172,224,164,162,224,164,188,224,164,164,224,165, +135,224,164,173,224,164,190,224,164,156,224,164,170,224,164,190,224,164,149,224, +165,141,224,164,178,224,164,191,224,164,149,224,164,159,224,165,141,224,164,176, +224,165,135,224,164,168,224,164,150,224,164,191,224,164,178,224,164,190,224,164, +171,224,164,166,224,165,140,224,164,176,224,164,190,224,164,168,224,164,174,224, +164,190,224,164,174,224,164,178,224,165,135,224,164,174,224,164,164,224,164,166, +224,164,190,224,164,168,224,164,172,224,164,190,224,164,156,224,164,190,224,164, +176,224,164,181,224,164,191,224,164,149,224,164,190,224,164,184,224,164,149,224, +165,141,224,164,175,224,165,139,224,164,130,224,164,154,224,164,190,224,164,185, +224,164,164,224,165,135,224,164,170,224,164,185,224,165,129,224,164,129,224,164, +154,224,164,172,224,164,164,224,164,190,224,164,175,224,164,190,224,164,184,224, +164,130,224,164,181,224,164,190,224,164,166,224,164,166,224,165,135,224,164,150, +224,164,168,224,165,135,224,164,170,224,164,191,224,164,155,224,164,178,224,165, +135,224,164,181,224,164,191,224,164,182,224,165,135,224,164,183,224,164,176,224, +164,190,224,164,156,224,165,141,224,164,175,224,164,137,224,164,164,224,165,141, +224,164,164,224,164,176,224,164,174,224,165,129,224,164,130,224,164,172,224,164, +136,224,164,166,224,165,139,224,164,168,224,165,139,224,164,130,224,164,137,224, +164,170,224,164,149,224,164,176,224,164,163,224,164,170,224,164,162,224,164,188, +224,165,135,224,164,130,224,164,184,224,165,141,224,164,165,224,164,191,224,164, +164,224,164,171,224,164,191,224,164,178,224,165,141,224,164,174,224,164,174,224, +165,129,224,164,150,224,165,141,224,164,175,224,164,133,224,164,154,224,165,141, +224,164,155,224,164,190,224,164,155,224,165,130,224,164,159,224,164,164,224,165, +128,224,164,184,224,164,130,224,164,151,224,165,128,224,164,164,224,164,156,224, +164,190,224,164,143,224,164,151,224,164,190,224,164,181,224,164,191,224,164,173, +224,164,190,224,164,151,224,164,152,224,164,163,224,165,141,224,164,159,224,165, +135,224,164,166,224,165,130,224,164,184,224,164,176,224,165,135,224,164,166,224, +164,191,224,164,168,224,165,139,224,164,130,224,164,185,224,164,164,224,165,141, +224,164,175,224,164,190,224,164,184,224,165,135,224,164,149,224,165,141,224,164, +184,224,164,151,224,164,190,224,164,130,224,164,167,224,165,128,224,164,181,224, +164,191,224,164,182,224,165,141,224,164,181,224,164,176,224,164,190,224,164,164, +224,165,135,224,164,130,224,164,166,224,165,136,224,164,159,224,165,141,224,164, +184,224,164,168,224,164,149,224,165,141,224,164,182,224,164,190,224,164,184,224, +164,190,224,164,174,224,164,168,224,165,135,224,164,133,224,164,166,224,164,190, +224,164,178,224,164,164,224,164,172,224,164,191,224,164,156,224,164,178,224,165, +128,224,164,170,224,165,129,224,164,176,224,165,130,224,164,183,224,164,185,224, +164,191,224,164,130,224,164,166,224,165,128,224,164,174,224,164,191,224,164,164, +224,165,141,224,164,176,224,164,149,224,164,181,224,164,191,224,164,164,224,164, +190,224,164,176,224,165,129,224,164,170,224,164,175,224,165,135,224,164,184,224, +165,141,224,164,165,224,164,190,224,164,168,224,164,149,224,164,176,224,165,139, +224,164,161,224,164,188,224,164,174,224,165,129,224,164,149,224,165,141,224,164, +164,224,164,175,224,165,139,224,164,156,224,164,168,224,164,190,224,164,149,224, +165,131,224,164,170,224,164,175,224,164,190,224,164,170,224,165,139,224,164,184, +224,165,141,224,164,159,224,164,152,224,164,176,224,165,135,224,164,178,224,165, +130,224,164,149,224,164,190,224,164,176,224,165,141,224,164,175,224,164,181,224, +164,191,224,164,154,224,164,190,224,164,176,224,164,184,224,165,130,224,164,154, +224,164,168,224,164,190,224,164,174,224,165,130,224,164,178,224,165,141,224,164, +175,224,164,166,224,165,135,224,164,150,224,165,135,224,164,130,224,164,185,224, +164,174,224,165,135,224,164,182,224,164,190,224,164,184,224,165,141,224,164,149, +224,165,130,224,164,178,224,164,174,224,165,136,224,164,130,224,164,168,224,165, +135,224,164,164,224,165,136,224,164,175,224,164,190,224,164,176,224,164,156,224, +164,191,224,164,184,224,164,149,224,165,135,114,115,115,43,120,109,108,34,32,116 +,105,116,108,101,61,34,45,116,121,112,101,34,32,99,111,110,116,101,110,116,61,34 +,116,105,116,108,101,34,32,99,111,110,116,101,110,116,61,34,97,116,32,116,104, +101,32,115,97,109,101,32,116,105,109,101,46,106,115,34,62,60,47,115,99,114,105, +112,116,62,10,60,34,32,109,101,116,104,111,100,61,34,112,111,115,116,34,32,60,47 +,115,112,97,110,62,60,47,97,62,60,47,108,105,62,118,101,114,116,105,99,97,108,45 +,97,108,105,103,110,58,116,47,106,113,117,101,114,121,46,109,105,110,46,106,115, +34,62,46,99,108,105,99,107,40,102,117,110,99,116,105,111,110,40,32,115,116,121, +108,101,61,34,112,97,100,100,105,110,103,45,125,41,40,41,59,10,60,47,115,99,114, +105,112,116,62,10,60,47,115,112,97,110,62,60,97,32,104,114,101,102,61,34,60,97, +32,104,114,101,102,61,34,104,116,116,112,58,47,47,41,59,32,114,101,116,117,114, +110,32,102,97,108,115,101,59,116,101,120,116,45,100,101,99,111,114,97,116,105, +111,110,58,32,115,99,114,111,108,108,105,110,103,61,34,110,111,34,32,98,111,114, +100,101,114,45,99,111,108,108,97,112,115,101,58,97,115,115,111,99,105,97,116,101 +,100,32,119,105,116,104,32,66,97,104,97,115,97,32,73,110,100,111,110,101,115,105 +,97,69,110,103,108,105,115,104,32,108,97,110,103,117,97,103,101,60,116,101,120, +116,32,120,109,108,58,115,112,97,99,101,61,46,103,105,102,34,32,98,111,114,100, +101,114,61,34,48,34,60,47,98,111,100,121,62,10,60,47,104,116,109,108,62,10,111, +118,101,114,102,108,111,119,58,104,105,100,100,101,110,59,105,109,103,32,115,114 +,99,61,34,104,116,116,112,58,47,47,97,100,100,69,118,101,110,116,76,105,115,116, +101,110,101,114,114,101,115,112,111,110,115,105,98,108,101,32,102,111,114,32,115 +,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,47,102,97,118,105,99,111, +110,46,105,99,111,34,32,47,62,111,112,101,114,97,116,105,110,103,32,115,121,115, +116,101,109,34,32,115,116,121,108,101,61,34,119,105,100,116,104,58,49,116,97,114 +,103,101,116,61,34,95,98,108,97,110,107,34,62,83,116,97,116,101,32,85,110,105, +118,101,114,115,105,116,121,116,101,120,116,45,97,108,105,103,110,58,108,101,102 +,116,59,10,100,111,99,117,109,101,110,116,46,119,114,105,116,101,40,44,32,105, +110,99,108,117,100,105,110,103,32,116,104,101,32,97,114,111,117,110,100,32,116, +104,101,32,119,111,114,108,100,41,59,13,10,60,47,115,99,114,105,112,116,62,13,10 +,60,34,32,115,116,121,108,101,61,34,104,101,105,103,104,116,58,59,111,118,101, +114,102,108,111,119,58,104,105,100,100,101,110,109,111,114,101,32,105,110,102, +111,114,109,97,116,105,111,110,97,110,32,105,110,116,101,114,110,97,116,105,111, +110,97,108,97,32,109,101,109,98,101,114,32,111,102,32,116,104,101,32,111,110,101 +,32,111,102,32,116,104,101,32,102,105,114,115,116,99,97,110,32,98,101,32,102,111 +,117,110,100,32,105,110,32,60,47,100,105,118,62,10,9,9,60,47,100,105,118,62,10, +100,105,115,112,108,97,121,58,32,110,111,110,101,59,34,62,34,32,47,62,10,60,108, +105,110,107,32,114,101,108,61,34,10,32,32,40,102,117,110,99,116,105,111,110,40, +41,32,123,116,104,101,32,49,53,116,104,32,99,101,110,116,117,114,121,46,112,114, +101,118,101,110,116,68,101,102,97,117,108,116,40,108,97,114,103,101,32,110,117, +109,98,101,114,32,111,102,32,66,121,122,97,110,116,105,110,101,32,69,109,112,105 +,114,101,46,106,112,103,124,116,104,117,109,98,124,108,101,102,116,124,118,97, +115,116,32,109,97,106,111,114,105,116,121,32,111,102,109,97,106,111,114,105,116, +121,32,111,102,32,116,104,101,32,32,97,108,105,103,110,61,34,99,101,110,116,101, +114,34,62,85,110,105,118,101,114,115,105,116,121,32,80,114,101,115,115,100,111, +109,105,110,97,116,101,100,32,98,121,32,116,104,101,83,101,99,111,110,100,32,87, +111,114,108,100,32,87,97,114,100,105,115,116,114,105,98,117,116,105,111,110,32, +111,102,32,115,116,121,108,101,61,34,112,111,115,105,116,105,111,110,58,116,104, +101,32,114,101,115,116,32,111,102,32,116,104,101,32,99,104,97,114,97,99,116,101, +114,105,122,101,100,32,98,121,32,114,101,108,61,34,110,111,102,111,108,108,111, +119,34,62,100,101,114,105,118,101,115,32,102,114,111,109,32,116,104,101,114,97, +116,104,101,114,32,116,104,97,110,32,116,104,101,32,97,32,99,111,109,98,105,110, +97,116,105,111,110,32,111,102,115,116,121,108,101,61,34,119,105,100,116,104,58, +49,48,48,69,110,103,108,105,115,104,45,115,112,101,97,107,105,110,103,99,111,109 +,112,117,116,101,114,32,115,99,105,101,110,99,101,98,111,114,100,101,114,61,34, +48,34,32,97,108,116,61,34,116,104,101,32,101,120,105,115,116,101,110,99,101,32, +111,102,68,101,109,111,99,114,97,116,105,99,32,80,97,114,116,121,34,32,115,116, +121,108,101,61,34,109,97,114,103,105,110,45,70,111,114,32,116,104,105,115,32,114 +,101,97,115,111,110,44,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,9,115 +,66,121,84,97,103,78,97,109,101,40,115,41,91,48,93,106,115,34,62,60,47,115,99, +114,105,112,116,62,13,10,60,46,106,115,34,62,60,47,115,99,114,105,112,116,62,13, +10,108,105,110,107,32,114,101,108,61,34,105,99,111,110,34,32,39,32,97,108,116,61 +,39,39,32,99,108,97,115,115,61,39,102,111,114,109,97,116,105,111,110,32,111,102, +32,116,104,101,118,101,114,115,105,111,110,115,32,111,102,32,116,104,101,32,60, +47,97,62,60,47,100,105,118,62,60,47,100,105,118,62,47,112,97,103,101,62,10,32,32 +,60,112,97,103,101,62,10,60,100,105,118,32,99,108,97,115,115,61,34,99,111,110, +116,98,101,99,97,109,101,32,116,104,101,32,102,105,114,115,116,98,97,104,97,115, +97,32,73,110,100,111,110,101,115,105,97,101,110,103,108,105,115,104,32,40,115, +105,109,112,108,101,41,206,149,206,187,206,187,206,183,206,189,206,185,206,186, +206,172,209,133,209,128,208,178,208,176,209,130,209,129,208,186,208,184,208,186, +208,190,208,188,208,191,208,176,208,189,208,184,208,184,209,143,208,178,208,187, +209,143,208,181,209,130,209,129,209,143,208,148,208,190,208,177,208,176,208,178, +208,184,209,130,209,140,209,135,208,181,208,187,208,190,208,178,208,181,208,186, +208,176,209,128,208,176,208,183,208,178,208,184,209,130,208,184,209,143,208,152, +208,189,209,130,208,181,209,128,208,189,208,181,209,130,208,158,209,130,208,178, +208,181,209,130,208,184,209,130,209,140,208,189,208,176,208,191,209,128,208,184, +208,188,208,181,209,128,208,184,208,189,209,130,208,181,209,128,208,189,208,181, +209,130,208,186,208,190,209,130,208,190,209,128,208,190,208,179,208,190,209,129, +209,130,209,128,208,176,208,189,208,184,209,134,209,139,208,186,208,176,209,135, +208,181,209,129,209,130,208,178,208,181,209,131,209,129,208,187,208,190,208,178, +208,184,209,143,209,133,208,191,209,128,208,190,208,177,208,187,208,181,208,188, +209,139,208,191,208,190,208,187,209,131,209,135,208,184,209,130,209,140,209,143, +208,178,208,187,209,143,209,142,209,130,209,129,209,143,208,189,208,176,208,184, +208,177,208,190,208,187,208,181,208,181,208,186,208,190,208,188,208,191,208,176, +208,189,208,184,209,143,208,178,208,189,208,184,208,188,208,176,208,189,208,184, +208,181,209,129,209,128,208,181,208,180,209,129,209,130,208,178,208,176,216,167, +217,132,217,133,217,136,216,167,216,182,217,138,216,185,216,167,217,132,216,177, +216,166,217,138,216,179,217,138,216,169,216,167,217,132,216,167,217,134,216,170, +217,130,216,167,217,132,217,133,216,180,216,167,216,177,217,131,216,167,216,170, +217,131,216,167,217,132,216,179,217,138,216,167,216,177,216,167,216,170,216,167, +217,132,217,133,217,131,216,170,217,136,216,168,216,169,216,167,217,132,216,179, +216,185,217,136,216,175,217,138,216,169,216,167,216,173,216,181,216,167,216,166, +217,138,216,167,216,170,216,167,217,132,216,185,216,167,217,132,217,133,217,138, +216,169,216,167,217,132,216,181,217,136,216,170,217,138,216,167,216,170,216,167, +217,132,216,167,217,134,216,170,216,177,217,134,216,170,216,167,217,132,216,170, +216,181,216,167,217,133,217,138,217,133,216,167,217,132,216,165,216,179,217,132, +216,167,217,133,217,138,216,167,217,132,217,133,216,180,216,167,216,177,217,131, +216,169,216,167,217,132,217,133,216,177,216,166,217,138,216,167,216,170,114,111, +98,111,116,115,34,32,99,111,110,116,101,110,116,61,34,60,100,105,118,32,105,100, +61,34,102,111,111,116,101,114,34,62,116,104,101,32,85,110,105,116,101,100,32,83, +116,97,116,101,115,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47, +46,106,112,103,124,114,105,103,104,116,124,116,104,117,109,98,124,46,106,115,34, +62,60,47,115,99,114,105,112,116,62,13,10,60,108,111,99,97,116,105,111,110,46,112 +,114,111,116,111,99,111,108,102,114,97,109,101,98,111,114,100,101,114,61,34,48, +34,32,115,34,32,47,62,10,60,109,101,116,97,32,110,97,109,101,61,34,60,47,97,62, +60,47,100,105,118,62,60,47,100,105,118,62,60,102,111,110,116,45,119,101,105,103, +104,116,58,98,111,108,100,59,38,113,117,111,116,59,32,97,110,100,32,38,113,117, +111,116,59,100,101,112,101,110,100,105,110,103,32,111,110,32,116,104,101,32,109, +97,114,103,105,110,58,48,59,112,97,100,100,105,110,103,58,34,32,114,101,108,61, +34,110,111,102,111,108,108,111,119,34,32,80,114,101,115,105,100,101,110,116,32, +111,102,32,116,104,101,32,116,119,101,110,116,105,101,116,104,32,99,101,110,116, +117,114,121,101,118,105,115,105,111,110,62,10,32,32,60,47,112,97,103,101,73,110, +116,101,114,110,101,116,32,69,120,112,108,111,114,101,114,97,46,97,115,121,110, +99,32,61,32,116,114,117,101,59,13,10,105,110,102,111,114,109,97,116,105,111,110, +32,97,98,111,117,116,60,100,105,118,32,105,100,61,34,104,101,97,100,101,114,34, +62,34,32,97,99,116,105,111,110,61,34,104,116,116,112,58,47,47,60,97,32,104,114, +101,102,61,34,104,116,116,112,115,58,47,47,60,100,105,118,32,105,100,61,34,99, +111,110,116,101,110,116,34,60,47,100,105,118,62,13,10,60,47,100,105,118,62,13,10 +,60,100,101,114,105,118,101,100,32,102,114,111,109,32,116,104,101,32,60,105,109, +103,32,115,114,99,61,39,104,116,116,112,58,47,47,97,99,99,111,114,100,105,110, +103,32,116,111,32,116,104,101,32,10,60,47,98,111,100,121,62,10,60,47,104,116,109 +,108,62,10,115,116,121,108,101,61,34,102,111,110,116,45,115,105,122,101,58,115, +99,114,105,112,116,32,108,97,110,103,117,97,103,101,61,34,65,114,105,97,108,44, +32,72,101,108,118,101,116,105,99,97,44,60,47,97,62,60,115,112,97,110,32,99,108, +97,115,115,61,34,60,47,115,99,114,105,112,116,62,60,115,99,114,105,112,116,32, +112,111,108,105,116,105,99,97,108,32,112,97,114,116,105,101,115,116,100,62,60,47 +,116,114,62,60,47,116,97,98,108,101,62,60,104,114,101,102,61,34,104,116,116,112, +58,47,47,119,119,119,46,105,110,116,101,114,112,114,101,116,97,116,105,111,110, +32,111,102,114,101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,32,100, +111,99,117,109,101,110,116,46,119,114,105,116,101,40,39,60,99,104,97,114,115,101 +,116,61,34,117,116,102,45,56,34,62,10,98,101,103,105,110,110,105,110,103,32,111, +102,32,116,104,101,32,114,101,118,101,97,108,101,100,32,116,104,97,116,32,116, +104,101,116,101,108,101,118,105,115,105,111,110,32,115,101,114,105,101,115,34,32 +,114,101,108,61,34,110,111,102,111,108,108,111,119,34,62,32,116,97,114,103,101, +116,61,34,95,98,108,97,110,107,34,62,99,108,97,105,109,105,110,103,32,116,104,97 +,116,32,116,104,101,104,116,116,112,37,51,65,37,50,70,37,50,70,119,119,119,46, +109,97,110,105,102,101,115,116,97,116,105,111,110,115,32,111,102,80,114,105,109, +101,32,77,105,110,105,115,116,101,114,32,111,102,105,110,102,108,117,101,110,99, +101,100,32,98,121,32,116,104,101,99,108,97,115,115,61,34,99,108,101,97,114,102, +105,120,34,62,47,100,105,118,62,13,10,60,47,100,105,118,62,13,10,13,10,116,104, +114,101,101,45,100,105,109,101,110,115,105,111,110,97,108,67,104,117,114,99,104, +32,111,102,32,69,110,103,108,97,110,100,111,102,32,78,111,114,116,104,32,67,97, +114,111,108,105,110,97,115,113,117,97,114,101,32,107,105,108,111,109,101,116,114 +,101,115,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110,101,114,100,105 +,115,116,105,110,99,116,32,102,114,111,109,32,116,104,101,99,111,109,109,111,110 +,108,121,32,107,110,111,119,110,32,97,115,80,104,111,110,101,116,105,99,32,65, +108,112,104,97,98,101,116,100,101,99,108,97,114,101,100,32,116,104,97,116,32,116 +,104,101,99,111,110,116,114,111,108,108,101,100,32,98,121,32,116,104,101,66,101, +110,106,97,109,105,110,32,70,114,97,110,107,108,105,110,114,111,108,101,45,112, +108,97,121,105,110,103,32,103,97,109,101,116,104,101,32,85,110,105,118,101,114, +115,105,116,121,32,111,102,105,110,32,87,101,115,116,101,114,110,32,69,117,114, +111,112,101,112,101,114,115,111,110,97,108,32,99,111,109,112,117,116,101,114,80, +114,111,106,101,99,116,32,71,117,116,101,110,98,101,114,103,114,101,103,97,114, +100,108,101,115,115,32,111,102,32,116,104,101,104,97,115,32,98,101,101,110,32, +112,114,111,112,111,115,101,100,116,111,103,101,116,104,101,114,32,119,105,116, +104,32,116,104,101,62,60,47,108,105,62,60,108,105,32,99,108,97,115,115,61,34,105 +,110,32,115,111,109,101,32,99,111,117,110,116,114,105,101,115,109,105,110,46,106 +,115,34,62,60,47,115,99,114,105,112,116,62,111,102,32,116,104,101,32,112,111,112 +,117,108,97,116,105,111,110,111,102,102,105,99,105,97,108,32,108,97,110,103,117, +97,103,101,60,105,109,103,32,115,114,99,61,34,105,109,97,103,101,115,47,105,100, +101,110,116,105,102,105,101,100,32,98,121,32,116,104,101,110,97,116,117,114,97, +108,32,114,101,115,111,117,114,99,101,115,99,108,97,115,115,105,102,105,99,97, +116,105,111,110,32,111,102,99,97,110,32,98,101,32,99,111,110,115,105,100,101,114 +,101,100,113,117,97,110,116,117,109,32,109,101,99,104,97,110,105,99,115,78,101, +118,101,114,116,104,101,108,101,115,115,44,32,116,104,101,109,105,108,108,105, +111,110,32,121,101,97,114,115,32,97,103,111,60,47,98,111,100,121,62,13,10,60,47, +104,116,109,108,62,13,206,149,206,187,206,187,206,183,206,189,206,185,206,186, +206,172,10,116,97,107,101,32,97,100,118,97,110,116,97,103,101,32,111,102,97,110, +100,44,32,97,99,99,111,114,100,105,110,103,32,116,111,97,116,116,114,105,98,117, +116,101,100,32,116,111,32,116,104,101,77,105,99,114,111,115,111,102,116,32,87, +105,110,100,111,119,115,116,104,101,32,102,105,114,115,116,32,99,101,110,116,117 +,114,121,117,110,100,101,114,32,116,104,101,32,99,111,110,116,114,111,108,100, +105,118,32,99,108,97,115,115,61,34,104,101,97,100,101,114,115,104,111,114,116, +108,121,32,97,102,116,101,114,32,116,104,101,110,111,116,97,98,108,101,32,101, +120,99,101,112,116,105,111,110,116,101,110,115,32,111,102,32,116,104,111,117,115 +,97,110,100,115,115,101,118,101,114,97,108,32,100,105,102,102,101,114,101,110, +116,97,114,111,117,110,100,32,116,104,101,32,119,111,114,108,100,46,114,101,97, +99,104,105,110,103,32,109,105,108,105,116,97,114,121,105,115,111,108,97,116,101, +100,32,102,114,111,109,32,116,104,101,111,112,112,111,115,105,116,105,111,110,32 +,116,111,32,116,104,101,116,104,101,32,79,108,100,32,84,101,115,116,97,109,101, +110,116,65,102,114,105,99,97,110,32,65,109,101,114,105,99,97,110,115,105,110,115 +,101,114,116,101,100,32,105,110,116,111,32,116,104,101,115,101,112,97,114,97,116 +,101,32,102,114,111,109,32,116,104,101,109,101,116,114,111,112,111,108,105,116, +97,110,32,97,114,101,97,109,97,107,101,115,32,105,116,32,112,111,115,115,105,98, +108,101,97,99,107,110,111,119,108,101,100,103,101,100,32,116,104,97,116,97,114, +103,117,97,98,108,121,32,116,104,101,32,109,111,115,116,116,121,112,101,61,34, +116,101,120,116,47,99,115,115,34,62,10,116,104,101,32,73,110,116,101,114,110,97, +116,105,111,110,97,108,65,99,99,111,114,100,105,110,103,32,116,111,32,116,104, +101,32,112,101,61,34,116,101,120,116,47,99,115,115,34,32,47,62,10,99,111,105,110 +,99,105,100,101,32,119,105,116,104,32,116,104,101,116,119,111,45,116,104,105,114 +,100,115,32,111,102,32,116,104,101,68,117,114,105,110,103,32,116,104,105,115,32, +116,105,109,101,44,100,117,114,105,110,103,32,116,104,101,32,112,101,114,105,111 +,100,97,110,110,111,117,110,99,101,100,32,116,104,97,116,32,104,101,116,104,101, +32,105,110,116,101,114,110,97,116,105,111,110,97,108,97,110,100,32,109,111,114, +101,32,114,101,99,101,110,116,108,121,98,101,108,105,101,118,101,100,32,116,104, +97,116,32,116,104,101,99,111,110,115,99,105,111,117,115,110,101,115,115,32,97, +110,100,102,111,114,109,101,114,108,121,32,107,110,111,119,110,32,97,115,115,117 +,114,114,111,117,110,100,101,100,32,98,121,32,116,104,101,102,105,114,115,116,32 +,97,112,112,101,97,114,101,100,32,105,110,111,99,99,97,115,105,111,110,97,108, +108,121,32,117,115,101,100,112,111,115,105,116,105,111,110,58,97,98,115,111,108, +117,116,101,59,34,32,116,97,114,103,101,116,61,34,95,98,108,97,110,107,34,32,112 +,111,115,105,116,105,111,110,58,114,101,108,97,116,105,118,101,59,116,101,120, +116,45,97,108,105,103,110,58,99,101,110,116,101,114,59,106,97,120,47,108,105,98, +115,47,106,113,117,101,114,121,47,49,46,98,97,99,107,103,114,111,117,110,100,45, +99,111,108,111,114,58,35,116,121,112,101,61,34,97,112,112,108,105,99,97,116,105, +111,110,47,97,110,103,117,97,103,101,34,32,99,111,110,116,101,110,116,61,34,60, +109,101,116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,80,114,105,118,97 +,99,121,32,80,111,108,105,99,121,60,47,97,62,101,40,34,37,51,67,115,99,114,105, +112,116,32,115,114,99,61,39,34,32,116,97,114,103,101,116,61,34,95,98,108,97,110, +107,34,62,79,110,32,116,104,101,32,111,116,104,101,114,32,104,97,110,100,44,46, +106,112,103,124,116,104,117,109,98,124,114,105,103,104,116,124,50,60,47,100,105, +118,62,60,100,105,118,32,99,108,97,115,115,61,34,60,100,105,118,32,115,116,121, +108,101,61,34,102,108,111,97,116,58,110,105,110,101,116,101,101,110,116,104,32, +99,101,110,116,117,114,121,60,47,98,111,100,121,62,13,10,60,47,104,116,109,108, +62,13,10,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,115,59,116, +101,120,116,45,97,108,105,103,110,58,99,101,110,116,101,114,102,111,110,116,45, +119,101,105,103,104,116,58,32,98,111,108,100,59,32,65,99,99,111,114,100,105,110, +103,32,116,111,32,116,104,101,32,100,105,102,102,101,114,101,110,99,101,32,98, +101,116,119,101,101,110,34,32,102,114,97,109,101,98,111,114,100,101,114,61,34,48 +,34,32,34,32,115,116,121,108,101,61,34,112,111,115,105,116,105,111,110,58,108, +105,110,107,32,104,114,101,102,61,34,104,116,116,112,58,47,47,104,116,109,108,52 +,47,108,111,111,115,101,46,100,116,100,34,62,10,100,117,114,105,110,103,32,116, +104,105,115,32,112,101,114,105,111,100,60,47,116,100,62,60,47,116,114,62,60,47, +116,97,98,108,101,62,99,108,111,115,101,108,121,32,114,101,108,97,116,101,100,32 +,116,111,102,111,114,32,116,104,101,32,102,105,114,115,116,32,116,105,109,101,59 +,102,111,110,116,45,119,101,105,103,104,116,58,98,111,108,100,59,105,110,112,117 +,116,32,116,121,112,101,61,34,116,101,120,116,34,32,60,115,112,97,110,32,115,116 +,121,108,101,61,34,102,111,110,116,45,111,110,114,101,97,100,121,115,116,97,116, +101,99,104,97,110,103,101,9,60,100,105,118,32,99,108,97,115,115,61,34,99,108,101 +,97,114,100,111,99,117,109,101,110,116,46,108,111,99,97,116,105,111,110,46,32,70 +,111,114,32,101,120,97,109,112,108,101,44,32,116,104,101,32,97,32,119,105,100, +101,32,118,97,114,105,101,116,121,32,111,102,32,60,33,68,79,67,84,89,80,69,32, +104,116,109,108,62,13,10,60,38,110,98,115,112,59,38,110,98,115,112,59,38,110,98, +115,112,59,34,62,60,97,32,104,114,101,102,61,34,104,116,116,112,58,47,47,115,116 +,121,108,101,61,34,102,108,111,97,116,58,108,101,102,116,59,99,111,110,99,101, +114,110,101,100,32,119,105,116,104,32,116,104,101,61,104,116,116,112,37,51,65,37 +,50,70,37,50,70,119,119,119,46,105,110,32,112,111,112,117,108,97,114,32,99,117, +108,116,117,114,101,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34,32,47 +,62,105,116,32,105,115,32,112,111,115,115,105,98,108,101,32,116,111,32,72,97,114 +,118,97,114,100,32,85,110,105,118,101,114,115,105,116,121,116,121,108,101,115, +104,101,101,116,34,32,104,114,101,102,61,34,47,116,104,101,32,109,97,105,110,32, +99,104,97,114,97,99,116,101,114,79,120,102,111,114,100,32,85,110,105,118,101,114 +,115,105,116,121,32,32,110,97,109,101,61,34,107,101,121,119,111,114,100,115,34, +32,99,115,116,121,108,101,61,34,116,101,120,116,45,97,108,105,103,110,58,116,104 +,101,32,85,110,105,116,101,100,32,75,105,110,103,100,111,109,102,101,100,101,114 +,97,108,32,103,111,118,101,114,110,109,101,110,116,60,100,105,118,32,115,116,121 +,108,101,61,34,109,97,114,103,105,110,32,100,101,112,101,110,100,105,110,103,32, +111,110,32,116,104,101,32,100,101,115,99,114,105,112,116,105,111,110,32,111,102, +32,116,104,101,60,100,105,118,32,99,108,97,115,115,61,34,104,101,97,100,101,114, +46,109,105,110,46,106,115,34,62,60,47,115,99,114,105,112,116,62,100,101,115,116, +114,117,99,116,105,111,110,32,111,102,32,116,104,101,115,108,105,103,104,116,108 +,121,32,100,105,102,102,101,114,101,110,116,105,110,32,97,99,99,111,114,100,97, +110,99,101,32,119,105,116,104,116,101,108,101,99,111,109,109,117,110,105,99,97, +116,105,111,110,115,105,110,100,105,99,97,116,101,115,32,116,104,97,116,32,116, +104,101,115,104,111,114,116,108,121,32,116,104,101,114,101,97,102,116,101,114, +101,115,112,101,99,105,97,108,108,121,32,105,110,32,116,104,101,32,69,117,114, +111,112,101,97,110,32,99,111,117,110,116,114,105,101,115,72,111,119,101,118,101, +114,44,32,116,104,101,114,101,32,97,114,101,115,114,99,61,34,104,116,116,112,58, +47,47,115,116,97,116,105,99,115,117,103,103,101,115,116,101,100,32,116,104,97, +116,32,116,104,101,34,32,115,114,99,61,34,104,116,116,112,58,47,47,119,119,119, +46,97,32,108,97,114,103,101,32,110,117,109,98,101,114,32,111,102,32,84,101,108, +101,99,111,109,109,117,110,105,99,97,116,105,111,110,115,34,32,114,101,108,61,34 +,110,111,102,111,108,108,111,119,34,32,116,72,111,108,121,32,82,111,109,97,110, +32,69,109,112,101,114,111,114,97,108,109,111,115,116,32,101,120,99,108,117,115, +105,118,101,108,121,34,32,98,111,114,100,101,114,61,34,48,34,32,97,108,116,61,34 +,83,101,99,114,101,116,97,114,121,32,111,102,32,83,116,97,116,101,99,117,108,109 +,105,110,97,116,105,110,103,32,105,110,32,116,104,101,67,73,65,32,87,111,114,108 +,100,32,70,97,99,116,98,111,111,107,116,104,101,32,109,111,115,116,32,105,109, +112,111,114,116,97,110,116,97,110,110,105,118,101,114,115,97,114,121,32,111,102, +32,116,104,101,115,116,121,108,101,61,34,98,97,99,107,103,114,111,117,110,100,45 +,60,108,105,62,60,101,109,62,60,97,32,104,114,101,102,61,34,47,116,104,101,32,65 +,116,108,97,110,116,105,99,32,79,99,101,97,110,115,116,114,105,99,116,108,121,32 +,115,112,101,97,107,105,110,103,44,115,104,111,114,116,108,121,32,98,101,102,111 +,114,101,32,116,104,101,100,105,102,102,101,114,101,110,116,32,116,121,112,101, +115,32,111,102,116,104,101,32,79,116,116,111,109,97,110,32,69,109,112,105,114, +101,62,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,65,110,32,73, +110,116,114,111,100,117,99,116,105,111,110,32,116,111,99,111,110,115,101,113,117 +,101,110,99,101,32,111,102,32,116,104,101,100,101,112,97,114,116,117,114,101,32, +102,114,111,109,32,116,104,101,67,111,110,102,101,100,101,114,97,116,101,32,83, +116,97,116,101,115,105,110,100,105,103,101,110,111,117,115,32,112,101,111,112, +108,101,115,80,114,111,99,101,101,100,105,110,103,115,32,111,102,32,116,104,101, +105,110,102,111,114,109,97,116,105,111,110,32,111,110,32,116,104,101,116,104,101 +,111,114,105,101,115,32,104,97,118,101,32,98,101,101,110,105,110,118,111,108,118 +,101,109,101,110,116,32,105,110,32,116,104,101,100,105,118,105,100,101,100,32, +105,110,116,111,32,116,104,114,101,101,97,100,106,97,99,101,110,116,32,99,111, +117,110,116,114,105,101,115,105,115,32,114,101,115,112,111,110,115,105,98,108, +101,32,102,111,114,100,105,115,115,111,108,117,116,105,111,110,32,111,102,32,116 +,104,101,99,111,108,108,97,98,111,114,97,116,105,111,110,32,119,105,116,104,119, +105,100,101,108,121,32,114,101,103,97,114,100,101,100,32,97,115,104,105,115,32, +99,111,110,116,101,109,112,111,114,97,114,105,101,115,102,111,117,110,100,105, +110,103,32,109,101,109,98,101,114,32,111,102,68,111,109,105,110,105,99,97,110,32 +,82,101,112,117,98,108,105,99,103,101,110,101,114,97,108,108,121,32,97,99,99,101 +,112,116,101,100,116,104,101,32,112,111,115,115,105,98,105,108,105,116,121,32, +111,102,97,114,101,32,97,108,115,111,32,97,118,97,105,108,97,98,108,101,117,110, +100,101,114,32,99,111,110,115,116,114,117,99,116,105,111,110,114,101,115,116,111 +,114,97,116,105,111,110,32,111,102,32,116,104,101,116,104,101,32,103,101,110,101 +,114,97,108,32,112,117,98,108,105,99,105,115,32,97,108,109,111,115,116,32,101, +110,116,105,114,101,108,121,112,97,115,115,101,115,32,116,104,114,111,117,103, +104,32,116,104,101,104,97,115,32,98,101,101,110,32,115,117,103,103,101,115,116, +101,100,99,111,109,112,117,116,101,114,32,97,110,100,32,118,105,100,101,111,71, +101,114,109,97,110,105,99,32,108,97,110,103,117,97,103,101,115,32,97,99,99,111, +114,100,105,110,103,32,116,111,32,116,104,101,32,100,105,102,102,101,114,101,110 +,116,32,102,114,111,109,32,116,104,101,115,104,111,114,116,108,121,32,97,102,116 +,101,114,119,97,114,100,115,104,114,101,102,61,34,104,116,116,112,115,58,47,47, +119,119,119,46,114,101,99,101,110,116,32,100,101,118,101,108,111,112,109,101,110 +,116,66,111,97,114,100,32,111,102,32,68,105,114,101,99,116,111,114,115,60,100, +105,118,32,99,108,97,115,115,61,34,115,101,97,114,99,104,124,32,60,97,32,104,114 +,101,102,61,34,104,116,116,112,58,47,47,73,110,32,112,97,114,116,105,99,117,108, +97,114,44,32,116,104,101,77,117,108,116,105,112,108,101,32,102,111,111,116,110, +111,116,101,115,111,114,32,111,116,104,101,114,32,115,117,98,115,116,97,110,99, +101,116,104,111,117,115,97,110,100,115,32,111,102,32,121,101,97,114,115,116,114, +97,110,115,108,97,116,105,111,110,32,111,102,32,116,104,101,60,47,100,105,118,62 +,13,10,60,47,100,105,118,62,13,10,13,10,60,97,32,104,114,101,102,61,34,105,110, +100,101,120,46,112,104,112,119,97,115,32,101,115,116,97,98,108,105,115,104,101, +100,32,105,110,109,105,110,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10, +112,97,114,116,105,99,105,112,97,116,101,32,105,110,32,116,104,101,97,32,115,116 +,114,111,110,103,32,105,110,102,108,117,101,110,99,101,115,116,121,108,101,61,34 +,109,97,114,103,105,110,45,116,111,112,58,114,101,112,114,101,115,101,110,116, +101,100,32,98,121,32,116,104,101,103,114,97,100,117,97,116,101,100,32,102,114, +111,109,32,116,104,101,84,114,97,100,105,116,105,111,110,97,108,108,121,44,32, +116,104,101,69,108,101,109,101,110,116,40,34,115,99,114,105,112,116,34,41,59,72, +111,119,101,118,101,114,44,32,115,105,110,99,101,32,116,104,101,47,100,105,118, +62,10,60,47,100,105,118,62,10,60,100,105,118,32,108,101,102,116,59,32,109,97,114 +,103,105,110,45,108,101,102,116,58,112,114,111,116,101,99,116,105,111,110,32,97, +103,97,105,110,115,116,48,59,32,118,101,114,116,105,99,97,108,45,97,108,105,103, +110,58,85,110,102,111,114,116,117,110,97,116,101,108,121,44,32,116,104,101,116, +121,112,101,61,34,105,109,97,103,101,47,120,45,105,99,111,110,47,100,105,118,62, +10,60,100,105,118,32,99,108,97,115,115,61,34,32,99,108,97,115,115,61,34,99,108, +101,97,114,102,105,120,34,62,60,100,105,118,32,99,108,97,115,115,61,34,102,111, +111,116,101,114,9,9,60,47,100,105,118,62,10,9,9,60,47,100,105,118,62,10,116,104, +101,32,109,111,116,105,111,110,32,112,105,99,116,117,114,101,208,145,209,138,208 +,187,208,179,208,176,209,128,209,129,208,186,208,184,208,177,209,138,208,187,208 +,179,208,176,209,128,209,129,208,186,208,184,208,164,208,181,208,180,208,181,209 +,128,208,176,209,134,208,184,208,184,208,189,208,181,209,129,208,186,208,190,208 +,187,209,140,208,186,208,190,209,129,208,190,208,190,208,177,209,137,208,181,208 +,189,208,184,208,181,209,129,208,190,208,190,208,177,209,137,208,181,208,189,208 +,184,209,143,208,191,209,128,208,190,208,179,209,128,208,176,208,188,208,188,209 +,139,208,158,209,130,208,191,209,128,208,176,208,178,208,184,209,130,209,140,208 +,177,208,181,209,129,208,191,208,187,208,176,209,130,208,189,208,190,208,188,208 +,176,209,130,208,181,209,128,208,184,208,176,208,187,209,139,208,191,208,190,208 +,183,208,178,208,190,208,187,209,143,208,181,209,130,208,191,208,190,209,129,208 +,187,208,181,208,180,208,189,208,184,208,181,209,128,208,176,208,183,208,187,208 +,184,209,135,208,189,209,139,209,133,208,191,209,128,208,190,208,180,209,131,208 +,186,209,134,208,184,208,184,208,191,209,128,208,190,208,179,209,128,208,176,208 +,188,208,188,208,176,208,191,208,190,208,187,208,189,208,190,209,129,209,130,209 +,140,209,142,208,189,208,176,209,133,208,190,208,180,208,184,209,130,209,129,209 +,143,208,184,208,183,208,177,209,128,208,176,208,189,208,189,208,190,208,181,208 +,189,208,176,209,129,208,181,208,187,208,181,208,189,208,184,209,143,208,184,208 +,183,208,188,208,181,208,189,208,181,208,189,208,184,209,143,208,186,208,176,209 +,130,208,181,208,179,208,190,209,128,208,184,208,184,208,144,208,187,208,181,208 +,186,209,129,208,176,208,189,208,180,209,128,224,164,166,224,165,141,224,164,181 +,224,164,190,224,164,176,224,164,190,224,164,174,224,165,136,224,164,168,224,165 +,129,224,164,133,224,164,178,224,164,170,224,165,141,224,164,176,224,164,166,224 +,164,190,224,164,168,224,164,173,224,164,190,224,164,176,224,164,164,224,165,128 +,224,164,175,224,164,133,224,164,168,224,165,129,224,164,166,224,165,135,224,164 +,182,224,164,185,224,164,191,224,164,168,224,165,141,224,164,166,224,165,128,224 +,164,135,224,164,130,224,164,161,224,164,191,224,164,175,224,164,190,224,164,166 +,224,164,191,224,164,178,224,165,141,224,164,178,224,165,128,224,164,133,224,164 +,167,224,164,191,224,164,149,224,164,190,224,164,176,224,164,181,224,165,128,224 +,164,161,224,164,191,224,164,175,224,165,139,224,164,154,224,164,191,224,164,159 +,224,165,141,224,164,160,224,165,135,224,164,184,224,164,174,224,164,190,224,164 +,154,224,164,190,224,164,176,224,164,156,224,164,130,224,164,149,224,165,141,224 +,164,182,224,164,168,224,164,166,224,165,129,224,164,168,224,164,191,224,164,175 +,224,164,190,224,164,170,224,165,141,224,164,176,224,164,175,224,165,139,224,164 +,151,224,164,133,224,164,168,224,165,129,224,164,184,224,164,190,224,164,176,224 +,164,145,224,164,168,224,164,178,224,164,190,224,164,135,224,164,168,224,164,170 +,224,164,190,224,164,176,224,165,141,224,164,159,224,165,128,224,164,182,224,164 +,176,224,165,141,224,164,164,224,165,139,224,164,130,224,164,178,224,165,139,224 +,164,149,224,164,184,224,164,173,224,164,190,224,164,171,224,164,188,224,165,141 +,224,164,178,224,165,136,224,164,182,224,164,182,224,164,176,224,165,141,224,164 +,164,224,165,135,224,164,130,224,164,170,224,165,141,224,164,176,224,164,166,224 +,165,135,224,164,182,224,164,170,224,165,141,224,164,178,224,165,135,224,164,175 +,224,164,176,224,164,149,224,165,135,224,164,130,224,164,166,224,165,141,224,164 +,176,224,164,184,224,165,141,224,164,165,224,164,191,224,164,164,224,164,191,224 +,164,137,224,164,164,224,165,141,224,164,170,224,164,190,224,164,166,224,164,137 +,224,164,168,224,165,141,224,164,185,224,165,135,224,164,130,224,164,154,224,164 +,191,224,164,159,224,165,141,224,164,160,224,164,190,224,164,175,224,164,190,224 +,164,164,224,165,141,224,164,176,224,164,190,224,164,156,224,165,141,224,164,175 +,224,164,190,224,164,166,224,164,190,224,164,170,224,165,129,224,164,176,224,164 +,190,224,164,168,224,165,135,224,164,156,224,165,139,224,164,161,224,164,188,224 +,165,135,224,164,130,224,164,133,224,164,168,224,165,129,224,164,181,224,164,190 +,224,164,166,224,164,182,224,165,141,224,164,176,224,165,135,224,164,163,224,165 +,128,224,164,182,224,164,191,224,164,149,224,165,141,224,164,183,224,164,190,224 +,164,184,224,164,176,224,164,149,224,164,190,224,164,176,224,165,128,224,164,184 +,224,164,130,224,164,151,224,165,141,224,164,176,224,164,185,224,164,170,224,164 +,176,224,164,191,224,164,163,224,164,190,224,164,174,224,164,172,224,165,141,224 +,164,176,224,164,190,224,164,130,224,164,161,224,164,172,224,164,154,224,165,141 +,224,164,154,224,165,139,224,164,130,224,164,137,224,164,170,224,164,178,224,164 +,172,224,165,141,224,164,167,224,164,174,224,164,130,224,164,164,224,165,141,224 +,164,176,224,165,128,224,164,184,224,164,130,224,164,170,224,164,176,224,165,141 +,224,164,149,224,164,137,224,164,174,224,165,141,224,164,174,224,165,128,224,164 +,166,224,164,174,224,164,190,224,164,167,224,165,141,224,164,175,224,164,174,224 +,164,184,224,164,185,224,164,190,224,164,175,224,164,164,224,164,190,224,164,182 +,224,164,172,224,165,141,224,164,166,224,165,139,224,164,130,224,164,174,224,165 +,128,224,164,161,224,164,191,224,164,175,224,164,190,224,164,134,224,164,136,224 +,164,170,224,165,128,224,164,143,224,164,178,224,164,174,224,165,139,224,164,172 +,224,164,190,224,164,135,224,164,178,224,164,184,224,164,130,224,164,150,224,165 +,141,224,164,175,224,164,190,224,164,134,224,164,170,224,164,176,224,165,135,224 +,164,182,224,164,168,224,164,133,224,164,168,224,165,129,224,164,172,224,164,130 +,224,164,167,224,164,172,224,164,190,224,164,156,224,164,188,224,164,190,224,164 +,176,224,164,168,224,164,181,224,165,128,224,164,168,224,164,164,224,164,174,224 +,164,170,224,165,141,224,164,176,224,164,174,224,165,129,224,164,150,224,164,170 +,224,165,141,224,164,176,224,164,182,224,165,141,224,164,168,224,164,170,224,164 +,176,224,164,191,224,164,181,224,164,190,224,164,176,224,164,168,224,165,129,224 +,164,149,224,164,184,224,164,190,224,164,168,224,164,184,224,164,174,224,164,176 +,224,165,141,224,164,165,224,164,168,224,164,134,224,164,175,224,165,139,224,164 +,156,224,164,191,224,164,164,224,164,184,224,165,139,224,164,174,224,164,181,224 +,164,190,224,164,176,216,167,217,132,217,133,216,180,216,167,216,177,217,131,216 +,167,216,170,216,167,217,132,217,133,217,134,216,170,216,175,217,138,216,167,216 +,170,216,167,217,132,217,131,217,133,216,168,217,138,217,136,216,170,216,177,216 +,167,217,132,217,133,216,180,216,167,217,135,216,175,216,167,216,170,216,185,216 +,175,216,175,216,167,217,132,216,178,217,136,216,167,216,177,216,185,216,175,216 +,175,216,167,217,132,216,177,216,175,217,136,216,175,216,167,217,132,216,165,216 +,179,217,132,216,167,217,133,217,138,216,169,216,167,217,132,217,129,217,136,216 +,170,217,136,216,180,217,136,216,168,216,167,217,132,217,133,216,179,216,167,216 +,168,217,130,216,167,216,170,216,167,217,132,217,133,216,185,217,132,217,136,217 +,133,216,167,216,170,216,167,217,132,217,133,216,179,217,132,216,179,217,132,216 +,167,216,170,216,167,217,132,216,172,216,177,216,167,217,129,217,138,217,131,216 +,179,216,167,217,132,216,167,216,179,217,132,216,167,217,133,217,138,216,169,216 +,167,217,132,216,167,216,170,216,181,216,167,217,132,216,167,216,170,107,101,121 +,119,111,114,100,115,34,32,99,111,110,116,101,110,116,61,34,119,51,46,111,114, +103,47,49,57,57,57,47,120,104,116,109,108,34,62,60,97,32,116,97,114,103,101,116, +61,34,95,98,108,97,110,107,34,32,116,101,120,116,47,104,116,109,108,59,32,99,104 +,97,114,115,101,116,61,34,32,116,97,114,103,101,116,61,34,95,98,108,97,110,107, +34,62,60,116,97,98,108,101,32,99,101,108,108,112,97,100,100,105,110,103,61,34,97 +,117,116,111,99,111,109,112,108,101,116,101,61,34,111,102,102,34,32,116,101,120, +116,45,97,108,105,103,110,58,32,99,101,110,116,101,114,59,116,111,32,108,97,115, +116,32,118,101,114,115,105,111,110,32,98,121,32,98,97,99,107,103,114,111,117,110 +,100,45,99,111,108,111,114,58,32,35,34,32,104,114,101,102,61,34,104,116,116,112, +58,47,47,119,119,119,46,47,100,105,118,62,60,47,100,105,118,62,60,100,105,118,32 +,105,100,61,60,97,32,104,114,101,102,61,34,35,34,32,99,108,97,115,115,61,34,34, +62,60,105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,99,114,105,112, +116,34,32,115,114,99,61,34,104,116,116,112,58,47,47,10,60,115,99,114,105,112,116 +,32,108,97,110,103,117,97,103,101,61,34,47,47,69,78,34,32,34,104,116,116,112,58, +47,47,119,119,119,46,119,101,110,99,111,100,101,85,82,73,67,111,109,112,111,110, +101,110,116,40,34,32,104,114,101,102,61,34,106,97,118,97,115,99,114,105,112,116, +58,60,100,105,118,32,99,108,97,115,115,61,34,99,111,110,116,101,110,116,100,111, +99,117,109,101,110,116,46,119,114,105,116,101,40,39,60,115,99,112,111,115,105, +116,105,111,110,58,32,97,98,115,111,108,117,116,101,59,115,99,114,105,112,116,32 +,115,114,99,61,34,104,116,116,112,58,47,47,32,115,116,121,108,101,61,34,109,97, +114,103,105,110,45,116,111,112,58,46,109,105,110,46,106,115,34,62,60,47,115,99, +114,105,112,116,62,10,60,47,100,105,118,62,10,60,100,105,118,32,99,108,97,115, +115,61,34,119,51,46,111,114,103,47,49,57,57,57,47,120,104,116,109,108,34,32,10, +13,10,60,47,98,111,100,121,62,13,10,60,47,104,116,109,108,62,100,105,115,116,105 +,110,99,116,105,111,110,32,98,101,116,119,101,101,110,47,34,32,116,97,114,103, +101,116,61,34,95,98,108,97,110,107,34,62,60,108,105,110,107,32,104,114,101,102, +61,34,104,116,116,112,58,47,47,101,110,99,111,100,105,110,103,61,34,117,116,102, +45,56,34,63,62,10,119,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110, +101,114,63,97,99,116,105,111,110,61,34,104,116,116,112,58,47,47,119,119,119,46, +105,99,111,110,34,32,104,114,101,102,61,34,104,116,116,112,58,47,47,32,115,116, +121,108,101,61,34,98,97,99,107,103,114,111,117,110,100,58,116,121,112,101,61,34, +116,101,120,116,47,99,115,115,34,32,47,62,10,109,101,116,97,32,112,114,111,112, +101,114,116,121,61,34,111,103,58,116,60,105,110,112,117,116,32,116,121,112,101, +61,34,116,101,120,116,34,32,32,115,116,121,108,101,61,34,116,101,120,116,45,97, +108,105,103,110,58,116,104,101,32,100,101,118,101,108,111,112,109,101,110,116,32 +,111,102,32,116,121,108,101,115,104,101,101,116,34,32,116,121,112,101,61,34,116, +101,104,116,109,108,59,32,99,104,97,114,115,101,116,61,117,116,102,45,56,105,115 +,32,99,111,110,115,105,100,101,114,101,100,32,116,111,32,98,101,116,97,98,108, +101,32,119,105,100,116,104,61,34,49,48,48,37,34,32,73,110,32,97,100,100,105,116, +105,111,110,32,116,111,32,116,104,101,32,99,111,110,116,114,105,98,117,116,101, +100,32,116,111,32,116,104,101,32,100,105,102,102,101,114,101,110,99,101,115,32, +98,101,116,119,101,101,110,100,101,118,101,108,111,112,109,101,110,116,32,111, +102,32,116,104,101,32,73,116,32,105,115,32,105,109,112,111,114,116,97,110,116,32 +,116,111,32,60,47,115,99,114,105,112,116,62,10,10,60,115,99,114,105,112,116,32, +32,115,116,121,108,101,61,34,102,111,110,116,45,115,105,122,101,58,49,62,60,47, +115,112,97,110,62,60,115,112,97,110,32,105,100,61,103,98,76,105,98,114,97,114, +121,32,111,102,32,67,111,110,103,114,101,115,115,60,105,109,103,32,115,114,99,61 +,34,104,116,116,112,58,47,47,105,109,69,110,103,108,105,115,104,32,116,114,97, +110,115,108,97,116,105,111,110,65,99,97,100,101,109,121,32,111,102,32,83,99,105, +101,110,99,101,115,100,105,118,32,115,116,121,108,101,61,34,100,105,115,112,108, +97,121,58,99,111,110,115,116,114,117,99,116,105,111,110,32,111,102,32,116,104, +101,46,103,101,116,69,108,101,109,101,110,116,66,121,73,100,40,105,100,41,105, +110,32,99,111,110,106,117,110,99,116,105,111,110,32,119,105,116,104,69,108,101, +109,101,110,116,40,39,115,99,114,105,112,116,39,41,59,32,60,109,101,116,97,32, +112,114,111,112,101,114,116,121,61,34,111,103,58,208,145,209,138,208,187,208,179 +,208,176,209,128,209,129,208,186,208,184,10,32,116,121,112,101,61,34,116,101,120 +,116,34,32,110,97,109,101,61,34,62,80,114,105,118,97,99,121,32,80,111,108,105,99 +,121,60,47,97,62,97,100,109,105,110,105,115,116,101,114,101,100,32,98,121,32,116 +,104,101,101,110,97,98,108,101,83,105,110,103,108,101,82,101,113,117,101,115,116 +,115,116,121,108,101,61,38,113,117,111,116,59,109,97,114,103,105,110,58,60,47, +100,105,118,62,60,47,100,105,118,62,60,47,100,105,118,62,60,62,60,105,109,103,32 +,115,114,99,61,34,104,116,116,112,58,47,47,105,32,115,116,121,108,101,61,38,113, +117,111,116,59,102,108,111,97,116,58,114,101,102,101,114,114,101,100,32,116,111, +32,97,115,32,116,104,101,32,116,111,116,97,108,32,112,111,112,117,108,97,116,105 +,111,110,32,111,102,105,110,32,87,97,115,104,105,110,103,116,111,110,44,32,68,46 +,67,46,32,115,116,121,108,101,61,34,98,97,99,107,103,114,111,117,110,100,45,97, +109,111,110,103,32,111,116,104,101,114,32,116,104,105,110,103,115,44,111,114,103 +,97,110,105,122,97,116,105,111,110,32,111,102,32,116,104,101,112,97,114,116,105, +99,105,112,97,116,101,100,32,105,110,32,116,104,101,116,104,101,32,105,110,116, +114,111,100,117,99,116,105,111,110,32,111,102,105,100,101,110,116,105,102,105, +101,100,32,119,105,116,104,32,116,104,101,102,105,99,116,105,111,110,97,108,32, +99,104,97,114,97,99,116,101,114,32,79,120,102,111,114,100,32,85,110,105,118,101, +114,115,105,116,121,32,109,105,115,117,110,100,101,114,115,116,97,110,100,105, +110,103,32,111,102,84,104,101,114,101,32,97,114,101,44,32,104,111,119,101,118, +101,114,44,115,116,121,108,101,115,104,101,101,116,34,32,104,114,101,102,61,34, +47,67,111,108,117,109,98,105,97,32,85,110,105,118,101,114,115,105,116,121,101, +120,112,97,110,100,101,100,32,116,111,32,105,110,99,108,117,100,101,117,115,117, +97,108,108,121,32,114,101,102,101,114,114,101,100,32,116,111,105,110,100,105,99, +97,116,105,110,103,32,116,104,97,116,32,116,104,101,104,97,118,101,32,115,117, +103,103,101,115,116,101,100,32,116,104,97,116,97,102,102,105,108,105,97,116,101, +100,32,119,105,116,104,32,116,104,101,99,111,114,114,101,108,97,116,105,111,110, +32,98,101,116,119,101,101,110,110,117,109,98,101,114,32,111,102,32,100,105,102, +102,101,114,101,110,116,62,60,47,116,100,62,60,47,116,114,62,60,47,116,97,98,108 +,101,62,82,101,112,117,98,108,105,99,32,111,102,32,73,114,101,108,97,110,100,10, +60,47,115,99,114,105,112,116,62,10,60,115,99,114,105,112,116,32,117,110,100,101, +114,32,116,104,101,32,105,110,102,108,117,101,110,99,101,99,111,110,116,114,105, +98,117,116,105,111,110,32,116,111,32,116,104,101,79,102,102,105,99,105,97,108,32 +,119,101,98,115,105,116,101,32,111,102,104,101,97,100,113,117,97,114,116,101,114 +,115,32,111,102,32,116,104,101,99,101,110,116,101,114,101,100,32,97,114,111,117, +110,100,32,116,104,101,105,109,112,108,105,99,97,116,105,111,110,115,32,111,102, +32,116,104,101,104,97,118,101,32,98,101,101,110,32,100,101,118,101,108,111,112, +101,100,70,101,100,101,114,97,108,32,82,101,112,117,98,108,105,99,32,111,102,98, +101,99,97,109,101,32,105,110,99,114,101,97,115,105,110,103,108,121,99,111,110, +116,105,110,117,97,116,105,111,110,32,111,102,32,116,104,101,78,111,116,101,44, +32,104,111,119,101,118,101,114,44,32,116,104,97,116,115,105,109,105,108,97,114, +32,116,111,32,116,104,97,116,32,111,102,32,99,97,112,97,98,105,108,105,116,105, +101,115,32,111,102,32,116,104,101,97,99,99,111,114,100,97,110,99,101,32,119,105, +116,104,32,116,104,101,112,97,114,116,105,99,105,112,97,110,116,115,32,105,110, +32,116,104,101,102,117,114,116,104,101,114,32,100,101,118,101,108,111,112,109, +101,110,116,117,110,100,101,114,32,116,104,101,32,100,105,114,101,99,116,105,111 +,110,105,115,32,111,102,116,101,110,32,99,111,110,115,105,100,101,114,101,100, +104,105,115,32,121,111,117,110,103,101,114,32,98,114,111,116,104,101,114,60,47, +116,100,62,60,47,116,114,62,60,47,116,97,98,108,101,62,60,97,32,104,116,116,112, +45,101,113,117,105,118,61,34,88,45,85,65,45,112,104,121,115,105,99,97,108,32,112 +,114,111,112,101,114,116,105,101,115,111,102,32,66,114,105,116,105,115,104,32,67 +,111,108,117,109,98,105,97,104,97,115,32,98,101,101,110,32,99,114,105,116,105,99 +,105,122,101,100,40,119,105,116,104,32,116,104,101,32,101,120,99,101,112,116,105 +,111,110,113,117,101,115,116,105,111,110,115,32,97,98,111,117,116,32,116,104,101 +,112,97,115,115,105,110,103,32,116,104,114,111,117,103,104,32,116,104,101,48,34, +32,99,101,108,108,112,97,100,100,105,110,103,61,34,48,34,32,116,104,111,117,115, +97,110,100,115,32,111,102,32,112,101,111,112,108,101,114,101,100,105,114,101,99, +116,115,32,104,101,114,101,46,32,70,111,114,104,97,118,101,32,99,104,105,108,100 +,114,101,110,32,117,110,100,101,114,37,51,69,37,51,67,47,115,99,114,105,112,116, +37,51,69,34,41,41,59,60,97,32,104,114,101,102,61,34,104,116,116,112,58,47,47,119 +,119,119,46,60,108,105,62,60,97,32,104,114,101,102,61,34,104,116,116,112,58,47, +47,115,105,116,101,95,110,97,109,101,34,32,99,111,110,116,101,110,116,61,34,116, +101,120,116,45,100,101,99,111,114,97,116,105,111,110,58,110,111,110,101,115,116, +121,108,101,61,34,100,105,115,112,108,97,121,58,32,110,111,110,101,60,109,101, +116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,88,45,110,101,119,32,68, +97,116,101,40,41,46,103,101,116,84,105,109,101,40,41,32,116,121,112,101,61,34, +105,109,97,103,101,47,120,45,105,99,111,110,34,60,47,115,112,97,110,62,60,115, +112,97,110,32,99,108,97,115,115,61,34,108,97,110,103,117,97,103,101,61,34,106,97 +,118,97,115,99,114,105,112,116,119,105,110,100,111,119,46,108,111,99,97,116,105, +111,110,46,104,114,101,102,60,97,32,104,114,101,102,61,34,106,97,118,97,115,99, +114,105,112,116,58,45,45,62,13,10,60,115,99,114,105,112,116,32,116,121,112,101, +61,34,116,60,97,32,104,114,101,102,61,39,104,116,116,112,58,47,47,119,119,119,46 +,104,111,114,116,99,117,116,32,105,99,111,110,34,32,104,114,101,102,61,34,60,47, +100,105,118,62,13,10,60,100,105,118,32,99,108,97,115,115,61,34,60,115,99,114,105 +,112,116,32,115,114,99,61,34,104,116,116,112,58,47,47,34,32,114,101,108,61,34, +115,116,121,108,101,115,104,101,101,116,34,32,116,60,47,100,105,118,62,10,60,115 +,99,114,105,112,116,32,116,121,112,101,61,47,97,62,32,60,97,32,104,114,101,102, +61,34,104,116,116,112,58,47,47,32,97,108,108,111,119,84,114,97,110,115,112,97, +114,101,110,99,121,61,34,88,45,85,65,45,67,111,109,112,97,116,105,98,108,101,34, +32,99,111,110,114,101,108,97,116,105,111,110,115,104,105,112,32,98,101,116,119, +101,101,110,10,60,47,115,99,114,105,112,116,62,13,10,60,115,99,114,105,112,116, +32,60,47,97,62,60,47,108,105,62,60,47,117,108,62,60,47,100,105,118,62,97,115,115 +,111,99,105,97,116,101,100,32,119,105,116,104,32,116,104,101,32,112,114,111,103, +114,97,109,109,105,110,103,32,108,97,110,103,117,97,103,101,60,47,97,62,60,97,32 +,104,114,101,102,61,34,104,116,116,112,58,47,47,60,47,97,62,60,47,108,105,62,60, +108,105,32,99,108,97,115,115,61,34,102,111,114,109,32,97,99,116,105,111,110,61, +34,104,116,116,112,58,47,47,60,100,105,118,32,115,116,121,108,101,61,34,100,105, +115,112,108,97,121,58,116,121,112,101,61,34,116,101,120,116,34,32,110,97,109,101 +,61,34,113,34,60,116,97,98,108,101,32,119,105,100,116,104,61,34,49,48,48,37,34, +32,98,97,99,107,103,114,111,117,110,100,45,112,111,115,105,116,105,111,110,58,34 +,32,98,111,114,100,101,114,61,34,48,34,32,119,105,100,116,104,61,34,114,101,108, +61,34,115,104,111,114,116,99,117,116,32,105,99,111,110,34,32,104,54,62,60,117, +108,62,60,108,105,62,60,97,32,104,114,101,102,61,34,32,32,60,109,101,116,97,32, +104,116,116,112,45,101,113,117,105,118,61,34,99,115,115,34,32,109,101,100,105,97 +,61,34,115,99,114,101,101,110,34,32,114,101,115,112,111,110,115,105,98,108,101, +32,102,111,114,32,116,104,101,32,34,32,116,121,112,101,61,34,97,112,112,108,105, +99,97,116,105,111,110,47,34,32,115,116,121,108,101,61,34,98,97,99,107,103,114, +111,117,110,100,45,104,116,109,108,59,32,99,104,97,114,115,101,116,61,117,116, +102,45,56,34,32,97,108,108,111,119,116,114,97,110,115,112,97,114,101,110,99,121, +61,34,115,116,121,108,101,115,104,101,101,116,34,32,116,121,112,101,61,34,116, +101,13,10,60,109,101,116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,62, +60,47,115,112,97,110,62,60,115,112,97,110,32,99,108,97,115,115,61,34,48,34,32,99 +,101,108,108,115,112,97,99,105,110,103,61,34,48,34,62,59,10,60,47,115,99,114,105 +,112,116,62,10,60,115,99,114,105,112,116,32,115,111,109,101,116,105,109,101,115, +32,99,97,108,108,101,100,32,116,104,101,100,111,101,115,32,110,111,116,32,110, +101,99,101,115,115,97,114,105,108,121,70,111,114,32,109,111,114,101,32,105,110, +102,111,114,109,97,116,105,111,110,97,116,32,116,104,101,32,98,101,103,105,110, +110,105,110,103,32,111,102,32,60,33,68,79,67,84,89,80,69,32,104,116,109,108,62, +60,104,116,109,108,112,97,114,116,105,99,117,108,97,114,108,121,32,105,110,32, +116,104,101,32,116,121,112,101,61,34,104,105,100,100,101,110,34,32,110,97,109, +101,61,34,106,97,118,97,115,99,114,105,112,116,58,118,111,105,100,40,48,41,59,34 +,101,102,102,101,99,116,105,118,101,110,101,115,115,32,111,102,32,116,104,101,32 +,97,117,116,111,99,111,109,112,108,101,116,101,61,34,111,102,102,34,32,103,101, +110,101,114,97,108,108,121,32,99,111,110,115,105,100,101,114,101,100,62,60,105, +110,112,117,116,32,116,121,112,101,61,34,116,101,120,116,34,32,34,62,60,47,115, +99,114,105,112,116,62,13,10,60,115,99,114,105,112,116,116,104,114,111,117,103, +104,111,117,116,32,116,104,101,32,119,111,114,108,100,99,111,109,109,111,110,32, +109,105,115,99,111,110,99,101,112,116,105,111,110,97,115,115,111,99,105,97,116, +105,111,110,32,119,105,116,104,32,116,104,101,60,47,100,105,118,62,10,60,47,100, +105,118,62,10,60,100,105,118,32,99,100,117,114,105,110,103,32,104,105,115,32,108 +,105,102,101,116,105,109,101,44,99,111,114,114,101,115,112,111,110,100,105,110, +103,32,116,111,32,116,104,101,116,121,112,101,61,34,105,109,97,103,101,47,120,45 +,105,99,111,110,34,32,97,110,32,105,110,99,114,101,97,115,105,110,103,32,110,117 +,109,98,101,114,100,105,112,108,111,109,97,116,105,99,32,114,101,108,97,116,105, +111,110,115,97,114,101,32,111,102,116,101,110,32,99,111,110,115,105,100,101,114, +101,100,109,101,116,97,32,99,104,97,114,115,101,116,61,34,117,116,102,45,56,34, +32,60,105,110,112,117,116,32,116,121,112,101,61,34,116,101,120,116,34,32,101,120 +,97,109,112,108,101,115,32,105,110,99,108,117,100,101,32,116,104,101,34,62,60, +105,109,103,32,115,114,99,61,34,104,116,116,112,58,47,47,105,112,97,114,116,105, +99,105,112,97,116,105,111,110,32,105,110,32,116,104,101,116,104,101,32,101,115, +116,97,98,108,105,115,104,109,101,110,116,32,111,102,10,60,47,100,105,118,62,10, +60,100,105,118,32,99,108,97,115,115,61,34,38,97,109,112,59,110,98,115,112,59,38, +97,109,112,59,110,98,115,112,59,116,111,32,100,101,116,101,114,109,105,110,101, +32,119,104,101,116,104,101,114,113,117,105,116,101,32,100,105,102,102,101,114, +101,110,116,32,102,114,111,109,109,97,114,107,101,100,32,116,104,101,32,98,101, +103,105,110,110,105,110,103,100,105,115,116,97,110,99,101,32,98,101,116,119,101, +101,110,32,116,104,101,99,111,110,116,114,105,98,117,116,105,111,110,115,32,116, +111,32,116,104,101,99,111,110,102,108,105,99,116,32,98,101,116,119,101,101,110, +32,116,104,101,119,105,100,101,108,121,32,99,111,110,115,105,100,101,114,101,100 +,32,116,111,119,97,115,32,111,110,101,32,111,102,32,116,104,101,32,102,105,114, +115,116,119,105,116,104,32,118,97,114,121,105,110,103,32,100,101,103,114,101,101 +,115,104,97,118,101,32,115,112,101,99,117,108,97,116,101,100,32,116,104,97,116, +40,100,111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,112, +97,114,116,105,99,105,112,97,116,105,110,103,32,105,110,32,116,104,101,111,114, +105,103,105,110,97,108,108,121,32,100,101,118,101,108,111,112,101,100,101,116,97 +,32,99,104,97,114,115,101,116,61,34,117,116,102,45,56,34,62,32,116,121,112,101, +61,34,116,101,120,116,47,99,115,115,34,32,47,62,10,105,110,116,101,114,99,104,97 +,110,103,101,97,98,108,121,32,119,105,116,104,109,111,114,101,32,99,108,111,115, +101,108,121,32,114,101,108,97,116,101,100,115,111,99,105,97,108,32,97,110,100,32 +,112,111,108,105,116,105,99,97,108,116,104,97,116,32,119,111,117,108,100,32,111, +116,104,101,114,119,105,115,101,112,101,114,112,101,110,100,105,99,117,108,97, +114,32,116,111,32,116,104,101,115,116,121,108,101,32,116,121,112,101,61,34,116, +101,120,116,47,99,115,115,116,121,112,101,61,34,115,117,98,109,105,116,34,32,110 +,97,109,101,61,34,102,97,109,105,108,105,101,115,32,114,101,115,105,100,105,110, +103,32,105,110,100,101,118,101,108,111,112,105,110,103,32,99,111,117,110,116,114 +,105,101,115,99,111,109,112,117,116,101,114,32,112,114,111,103,114,97,109,109, +105,110,103,101,99,111,110,111,109,105,99,32,100,101,118,101,108,111,112,109,101 +,110,116,100,101,116,101,114,109,105,110,97,116,105,111,110,32,111,102,32,116, +104,101,102,111,114,32,109,111,114,101,32,105,110,102,111,114,109,97,116,105,111 +,110,111,110,32,115,101,118,101,114,97,108,32,111,99,99,97,115,105,111,110,115, +112,111,114,116,117,103,117,195,170,115,32,40,69,117,114,111,112,101,117,41,208, +163,208,186,209,128,208,176,209,151,208,189,209,129,209,140,208,186,208,176,209, +131,208,186,209,128,208,176,209,151,208,189,209,129,209,140,208,186,208,176,208, +160,208,190,209,129,209,129,208,184,208,185,209,129,208,186,208,190,208,185,208, +188,208,176,209,130,208,181,209,128,208,184,208,176,208,187,208,190,208,178,208, +184,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,208,184,209, +131,208,191,209,128,208,176,208,178,208,187,208,181,208,189,208,184,209,143,208, +189,208,181,208,190,208,177,209,133,208,190,208,180,208,184,208,188,208,190,208, +184,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,209,143,208, +152,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,209,143,208, +160,208,181,209,129,208,191,209,131,208,177,208,187,208,184,208,186,208,184,208, +186,208,190,208,187,208,184,209,135,208,181,209,129,209,130,208,178,208,190,208, +184,208,189,209,132,208,190,209,128,208,188,208,176,209,134,208,184,209,142,209, +130,208,181,209,128,209,128,208,184,209,130,208,190,209,128,208,184,208,184,208, +180,208,190,209,129,209,130,208,176,209,130,208,190,209,135,208,189,208,190,216, +167,217,132,217,133,216,170,217,136,216,167,216,172,216,175,217,136,217,134,216, +167,217,132,216,167,216,180,216,170,216,177,216,167,217,131,216,167,216,170,216, +167,217,132,216,167,217,130,216,170,216,177,216,167,216,173,216,167,216,170,104, +116,109,108,59,32,99,104,97,114,115,101,116,61,85,84,70,45,56,34,32,115,101,116, +84,105,109,101,111,117,116,40,102,117,110,99,116,105,111,110,40,41,100,105,115, +112,108,97,121,58,105,110,108,105,110,101,45,98,108,111,99,107,59,60,105,110,112 +,117,116,32,116,121,112,101,61,34,115,117,98,109,105,116,34,32,116,121,112,101, +32,61,32,39,116,101,120,116,47,106,97,118,97,115,99,114,105,60,105,109,103,32, +115,114,99,61,34,104,116,116,112,58,47,47,119,119,119,46,34,32,34,104,116,116, +112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,115,104,111,114,116,99,117, +116,32,105,99,111,110,34,32,104,114,101,102,61,34,34,32,97,117,116,111,99,111, +109,112,108,101,116,101,61,34,111,102,102,34,32,60,47,97,62,60,47,100,105,118,62 +,60,100,105,118,32,99,108,97,115,115,61,60,47,97,62,60,47,108,105,62,10,60,108, +105,32,99,108,97,115,115,61,34,99,115,115,34,32,116,121,112,101,61,34,116,101, +120,116,47,99,115,115,34,32,60,102,111,114,109,32,97,99,116,105,111,110,61,34, +104,116,116,112,58,47,47,120,116,47,99,115,115,34,32,104,114,101,102,61,34,104, +116,116,112,58,47,47,108,105,110,107,32,114,101,108,61,34,97,108,116,101,114,110 +,97,116,101,34,32,13,10,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116, +101,120,116,47,32,111,110,99,108,105,99,107,61,34,106,97,118,97,115,99,114,105, +112,116,58,40,110,101,119,32,68,97,116,101,41,46,103,101,116,84,105,109,101,40, +41,125,104,101,105,103,104,116,61,34,49,34,32,119,105,100,116,104,61,34,49,34,32 +,80,101,111,112,108,101,39,115,32,82,101,112,117,98,108,105,99,32,111,102,32,32, +60,97,32,104,114,101,102,61,34,104,116,116,112,58,47,47,119,119,119,46,116,101, +120,116,45,100,101,99,111,114,97,116,105,111,110,58,117,110,100,101,114,116,104, +101,32,98,101,103,105,110,110,105,110,103,32,111,102,32,116,104,101,32,60,47,100 +,105,118,62,10,60,47,100,105,118,62,10,60,47,100,105,118,62,10,101,115,116,97,98 +,108,105,115,104,109,101,110,116,32,111,102,32,116,104,101,32,60,47,100,105,118, +62,60,47,100,105,118,62,60,47,100,105,118,62,60,47,100,35,118,105,101,119,112, +111,114,116,123,109,105,110,45,104,101,105,103,104,116,58,10,60,115,99,114,105, +112,116,32,115,114,99,61,34,104,116,116,112,58,47,47,111,112,116,105,111,110,62, +60,111,112,116,105,111,110,32,118,97,108,117,101,61,111,102,116,101,110,32,114, +101,102,101,114,114,101,100,32,116,111,32,97,115,32,47,111,112,116,105,111,110, +62,10,60,111,112,116,105,111,110,32,118,97,108,117,60,33,68,79,67,84,89,80,69,32 +,104,116,109,108,62,10,60,33,45,45,91,73,110,116,101,114,110,97,116,105,111,110, +97,108,32,65,105,114,112,111,114,116,62,10,60,97,32,104,114,101,102,61,34,104, +116,116,112,58,47,47,119,119,119,60,47,97,62,60,97,32,104,114,101,102,61,34,104, +116,116,112,58,47,47,119,224,184,160,224,184,178,224,184,169,224,184,178,224,185 +,132,224,184,151,224,184,162,225,131,165,225,131,144,225,131,160,225,131,151,225 +,131,163,225,131,154,225,131,152,230,173,163,233,171,148,228,184,173,230,150,135 +,32,40,231,185,129,233,171,148,41,224,164,168,224,164,191,224,164,176,224,165, +141,224,164,166,224,165,135,224,164,182,224,164,161,224,164,190,224,164,137,224, +164,168,224,164,178,224,165,139,224,164,161,224,164,149,224,165,141,224,164,183, +224,165,135,224,164,164,224,165,141,224,164,176,224,164,156,224,164,190,224,164, +168,224,164,149,224,164,190,224,164,176,224,165,128,224,164,184,224,164,130,224, +164,172,224,164,130,224,164,167,224,164,191,224,164,164,224,164,184,224,165,141, +224,164,165,224,164,190,224,164,170,224,164,168,224,164,190,224,164,184,224,165, +141,224,164,181,224,165,128,224,164,149,224,164,190,224,164,176,224,164,184,224, +164,130,224,164,184,224,165,141,224,164,149,224,164,176,224,164,163,224,164,184, +224,164,190,224,164,174,224,164,151,224,165,141,224,164,176,224,165,128,224,164, +154,224,164,191,224,164,159,224,165,141,224,164,160,224,165,139,224,164,130,224, +164,181,224,164,191,224,164,156,224,165,141,224,164,158,224,164,190,224,164,168, +224,164,133,224,164,174,224,165,135,224,164,176,224,164,191,224,164,149,224,164, +190,224,164,181,224,164,191,224,164,173,224,164,191,224,164,168,224,165,141,224, +164,168,224,164,151,224,164,190,224,164,161,224,164,191,224,164,175,224,164,190, +224,164,129,224,164,149,224,165,141,224,164,175,224,165,139,224,164,130,224,164, +149,224,164,191,224,164,184,224,165,129,224,164,176,224,164,149,224,165,141,224, +164,183,224,164,190,224,164,170,224,164,185,224,165,129,224,164,129,224,164,154, +224,164,164,224,165,128,224,164,170,224,165,141,224,164,176,224,164,172,224,164, +130,224,164,167,224,164,168,224,164,159,224,164,191,224,164,170,224,165,141,224, +164,170,224,164,163,224,165,128,224,164,149,224,165,141,224,164,176,224,164,191, +224,164,149,224,165,135,224,164,159,224,164,170,224,165,141,224,164,176,224,164, +190,224,164,176,224,164,130,224,164,173,224,164,170,224,165,141,224,164,176,224, +164,190,224,164,170,224,165,141,224,164,164,224,164,174,224,164,190,224,164,178, +224,164,191,224,164,149,224,165,139,224,164,130,224,164,176,224,164,171,224,164, +188,224,165,141,224,164,164,224,164,190,224,164,176,224,164,168,224,164,191,224, +164,176,224,165,141,224,164,174,224,164,190,224,164,163,224,164,178,224,164,191, +224,164,174,224,164,191,224,164,159,224,165,135,224,164,161,100,101,115,99,114, +105,112,116,105,111,110,34,32,99,111,110,116,101,110,116,61,34,100,111,99,117, +109,101,110,116,46,108,111,99,97,116,105,111,110,46,112,114,111,116,46,103,101, +116,69,108,101,109,101,110,116,115,66,121,84,97,103,78,97,109,101,40,60,33,68,79 +,67,84,89,80,69,32,104,116,109,108,62,10,60,104,116,109,108,32,60,109,101,116,97 +,32,99,104,97,114,115,101,116,61,34,117,116,102,45,56,34,62,58,117,114,108,34,32 +,99,111,110,116,101,110,116,61,34,104,116,116,112,58,47,47,46,99,115,115,34,32, +114,101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,115,116,121,108,101 +,32,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34,62,116,121,112,101,61 +,34,116,101,120,116,47,99,115,115,34,32,104,114,101,102,61,34,119,51,46,111,114, +103,47,49,57,57,57,47,120,104,116,109,108,34,32,120,109,108,116,121,112,101,61, +34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,32,109,101,116,104 +,111,100,61,34,103,101,116,34,32,97,99,116,105,111,110,61,34,108,105,110,107,32, +114,101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,32,32,61,32,100,111 +,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,116,121,112, +101,61,34,105,109,97,103,101,47,120,45,105,99,111,110,34,32,47,62,99,101,108,108 +,112,97,100,100,105,110,103,61,34,48,34,32,99,101,108,108,115,112,46,99,115,115, +34,32,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34,32,60,47,97,62,60, +47,108,105,62,60,108,105,62,60,97,32,104,114,101,102,61,34,34,32,119,105,100,116 +,104,61,34,49,34,32,104,101,105,103,104,116,61,34,49,34,34,62,60,97,32,104,114, +101,102,61,34,104,116,116,112,58,47,47,119,119,119,46,115,116,121,108,101,61,34, +100,105,115,112,108,97,121,58,110,111,110,101,59,34,62,97,108,116,101,114,110,97 +,116,101,34,32,116,121,112,101,61,34,97,112,112,108,105,45,47,47,87,51,67,47,47, +68,84,68,32,88,72,84,77,76,32,49,46,48,32,101,108,108,115,112,97,99,105,110,103, +61,34,48,34,32,99,101,108,108,112,97,100,32,116,121,112,101,61,34,104,105,100, +100,101,110,34,32,118,97,108,117,101,61,34,47,97,62,38,110,98,115,112,59,60,115, +112,97,110,32,114,111,108,101,61,34,115,10,60,105,110,112,117,116,32,116,121,112 +,101,61,34,104,105,100,100,101,110,34,32,108,97,110,103,117,97,103,101,61,34,74, +97,118,97,83,99,114,105,112,116,34,32,32,100,111,99,117,109,101,110,116,46,103, +101,116,69,108,101,109,101,110,116,115,66,103,61,34,48,34,32,99,101,108,108,115, +112,97,99,105,110,103,61,34,48,34,32,121,112,101,61,34,116,101,120,116,47,99,115 +,115,34,32,109,101,100,105,97,61,34,116,121,112,101,61,39,116,101,120,116,47,106 +,97,118,97,115,99,114,105,112,116,39,119,105,116,104,32,116,104,101,32,101,120, +99,101,112,116,105,111,110,32,111,102,32,121,112,101,61,34,116,101,120,116,47,99 +,115,115,34,32,114,101,108,61,34,115,116,32,104,101,105,103,104,116,61,34,49,34, +32,119,105,100,116,104,61,34,49,34,32,61,39,43,101,110,99,111,100,101,85,82,73, +67,111,109,112,111,110,101,110,116,40,60,108,105,110,107,32,114,101,108,61,34,97 +,108,116,101,114,110,97,116,101,34,32,10,98,111,100,121,44,32,116,114,44,32,105, +110,112,117,116,44,32,116,101,120,116,109,101,116,97,32,110,97,109,101,61,34,114 +,111,98,111,116,115,34,32,99,111,110,109,101,116,104,111,100,61,34,112,111,115, +116,34,32,97,99,116,105,111,110,61,34,62,10,60,97,32,104,114,101,102,61,34,104, +116,116,112,58,47,47,119,119,119,46,99,115,115,34,32,114,101,108,61,34,115,116, +121,108,101,115,104,101,101,116,34,32,60,47,100,105,118,62,60,47,100,105,118,62, +60,100,105,118,32,99,108,97,115,115,108,97,110,103,117,97,103,101,61,34,106,97, +118,97,115,99,114,105,112,116,34,62,97,114,105,97,45,104,105,100,100,101,110,61, +34,116,114,117,101,34,62,194,183,60,114,105,112,116,34,32,116,121,112,101,61,34, +116,101,120,116,47,106,97,118,97,115,108,61,48,59,125,41,40,41,59,10,40,102,117, +110,99,116,105,111,110,40,41,123,98,97,99,107,103,114,111,117,110,100,45,105,109 +,97,103,101,58,32,117,114,108,40,47,97,62,60,47,108,105,62,60,108,105,62,60,97, +32,104,114,101,102,61,34,104,9,9,60,108,105,62,60,97,32,104,114,101,102,61,34, +104,116,116,112,58,47,47,97,116,111,114,34,32,97,114,105,97,45,104,105,100,100, +101,110,61,34,116,114,117,62,32,60,97,32,104,114,101,102,61,34,104,116,116,112, +58,47,47,119,119,119,46,108,97,110,103,117,97,103,101,61,34,106,97,118,97,115,99 +,114,105,112,116,34,32,47,111,112,116,105,111,110,62,10,60,111,112,116,105,111, +110,32,118,97,108,117,101,47,100,105,118,62,60,47,100,105,118,62,60,100,105,118, +32,99,108,97,115,115,61,114,97,116,111,114,34,32,97,114,105,97,45,104,105,100, +100,101,110,61,34,116,114,101,61,40,110,101,119,32,68,97,116,101,41,46,103,101, +116,84,105,109,101,40,41,112,111,114,116,117,103,117,195,170,115,32,40,100,111, +32,66,114,97,115,105,108,41,208,190,209,128,208,179,208,176,208,189,208,184,208, +183,208,176,209,134,208,184,208,184,208,178,208,190,208,183,208,188,208,190,208, +182,208,189,208,190,209,129,209,130,209,140,208,190,208,177,209,128,208,176,208, +183,208,190,208,178,208,176,208,189,208,184,209,143,209,128,208,181,208,179,208, +184,209,129,209,130,209,128,208,176,209,134,208,184,208,184,208,178,208,190,208, +183,208,188,208,190,208,182,208,189,208,190,209,129,209,130,208,184,208,190,208, +177,209,143,208,183,208,176,209,130,208,181,208,187,209,140,208,189,208,176,60, +33,68,79,67,84,89,80,69,32,104,116,109,108,32,80,85,66,76,73,67,32,34,110,116,45 +,84,121,112,101,34,32,99,111,110,116,101,110,116,61,34,116,101,120,116,47,60,109 +,101,116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,67,111,110,116,101, +114,97,110,115,105,116,105,111,110,97,108,47,47,69,78,34,32,34,104,116,116,112, +58,60,104,116,109,108,32,120,109,108,110,115,61,34,104,116,116,112,58,47,47,119, +119,119,45,47,47,87,51,67,47,47,68,84,68,32,88,72,84,77,76,32,49,46,48,32,84,68, +84,68,47,120,104,116,109,108,49,45,116,114,97,110,115,105,116,105,111,110,97,108 +,47,47,119,119,119,46,119,51,46,111,114,103,47,84,82,47,120,104,116,109,108,49, +47,112,101,32,61,32,39,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116, +39,59,60,109,101,116,97,32,110,97,109,101,61,34,100,101,115,99,114,105,112,116, +105,111,110,112,97,114,101,110,116,78,111,100,101,46,105,110,115,101,114,116,66, +101,102,111,114,101,60,105,110,112,117,116,32,116,121,112,101,61,34,104,105,100, +100,101,110,34,32,110,97,106,115,34,32,116,121,112,101,61,34,116,101,120,116,47, +106,97,118,97,115,99,114,105,40,100,111,99,117,109,101,110,116,41,46,114,101,97, +100,121,40,102,117,110,99,116,105,115,99,114,105,112,116,32,116,121,112,101,61, +34,116,101,120,116,47,106,97,118,97,115,105,109,97,103,101,34,32,99,111,110,116, +101,110,116,61,34,104,116,116,112,58,47,47,85,65,45,67,111,109,112,97,116,105,98 +,108,101,34,32,99,111,110,116,101,110,116,61,116,109,108,59,32,99,104,97,114,115 +,101,116,61,117,116,102,45,56,34,32,47,62,10,108,105,110,107,32,114,101,108,61, +34,115,104,111,114,116,99,117,116,32,105,99,111,110,60,108,105,110,107,32,114, +101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,32,60,47,115,99,114,105 +,112,116,62,10,60,115,99,114,105,112,116,32,116,121,112,101,61,61,32,100,111,99, +117,109,101,110,116,46,99,114,101,97,116,101,69,108,101,109,101,110,60,97,32,116 +,97,114,103,101,116,61,34,95,98,108,97,110,107,34,32,104,114,101,102,61,32,100, +111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,115,66,105, +110,112,117,116,32,116,121,112,101,61,34,116,101,120,116,34,32,110,97,109,101,61 +,97,46,116,121,112,101,32,61,32,39,116,101,120,116,47,106,97,118,97,115,99,114, +105,110,112,117,116,32,116,121,112,101,61,34,104,105,100,100,101,110,34,32,110, +97,109,101,104,116,109,108,59,32,99,104,97,114,115,101,116,61,117,116,102,45,56, +34,32,47,62,100,116,100,34,62,10,60,104,116,109,108,32,120,109,108,110,115,61,34 +,104,116,116,112,45,47,47,87,51,67,47,47,68,84,68,32,72,84,77,76,32,52,46,48,49, +32,84,101,110,116,115,66,121,84,97,103,78,97,109,101,40,39,115,99,114,105,112, +116,39,41,105,110,112,117,116,32,116,121,112,101,61,34,104,105,100,100,101,110, +34,32,110,97,109,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120, +116,47,106,97,118,97,115,34,32,115,116,121,108,101,61,34,100,105,115,112,108,97, +121,58,110,111,110,101,59,34,62,100,111,99,117,109,101,110,116,46,103,101,116,69 +,108,101,109,101,110,116,66,121,73,100,40,61,100,111,99,117,109,101,110,116,46, +99,114,101,97,116,101,69,108,101,109,101,110,116,40,39,32,116,121,112,101,61,39, +116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,39,105,110,112,117,116, +32,116,121,112,101,61,34,116,101,120,116,34,32,110,97,109,101,61,34,100,46,103, +101,116,69,108,101,109,101,110,116,115,66,121,84,97,103,78,97,109,101,40,115,110 +,105,99,97,108,34,32,104,114,101,102,61,34,104,116,116,112,58,47,47,119,119,119, +46,67,47,47,68,84,68,32,72,84,77,76,32,52,46,48,49,32,84,114,97,110,115,105,116, +60,115,116,121,108,101,32,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34 +,62,10,10,60,115,116,121,108,101,32,116,121,112,101,61,34,116,101,120,116,47,99, +115,115,34,62,105,111,110,97,108,46,100,116,100,34,62,10,60,104,116,109,108,32, +120,109,108,110,115,61,104,116,116,112,45,101,113,117,105,118,61,34,67,111,110, +116,101,110,116,45,84,121,112,101,100,105,110,103,61,34,48,34,32,99,101,108,108, +115,112,97,99,105,110,103,61,34,48,34,104,116,109,108,59,32,99,104,97,114,115, +101,116,61,117,116,102,45,56,34,32,47,62,10,32,115,116,121,108,101,61,34,100,105 +,115,112,108,97,121,58,110,111,110,101,59,34,62,60,60,108,105,62,60,97,32,104, +114,101,102,61,34,104,116,116,112,58,47,47,119,119,119,46,32,116,121,112,101,61, +39,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,39,62,208,180,208,181 +,209,143,209,130,208,181,208,187,209,140,208,189,208,190,209,129,209,130,208,184 +,209,129,208,190,208,190,209,130,208,178,208,181,209,130,209,129,209,130,208,178 +,208,184,208,184,208,191,209,128,208,190,208,184,208,183,208,178,208,190,208,180 +,209,129,209,130,208,178,208,176,208,177,208,181,208,183,208,190,208,191,208,176 +,209,129,208,189,208,190,209,129,209,130,208,184,224,164,170,224,165,129,224,164 +,184,224,165,141,224,164,164,224,164,191,224,164,149,224,164,190,224,164,149,224 +,164,190,224,164,130,224,164,151,224,165,141,224,164,176,224,165,135,224,164,184 +,224,164,137,224,164,168,224,165,141,224,164,185,224,165,139,224,164,130,224,164 +,168,224,165,135,224,164,181,224,164,191,224,164,167,224,164,190,224,164,168,224 +,164,184,224,164,173,224,164,190,224,164,171,224,164,191,224,164,149,224,165,141 +,224,164,184,224,164,191,224,164,130,224,164,151,224,164,184,224,165,129,224,164 +,176,224,164,149,224,165,141,224,164,183,224,164,191,224,164,164,224,164,149,224 +,165,137,224,164,170,224,165,128,224,164,176,224,164,190,224,164,135,224,164,159 +,224,164,181,224,164,191,224,164,156,224,165,141,224,164,158,224,164,190,224,164 +,170,224,164,168,224,164,149,224,164,190,224,164,176,224,165,141,224,164,176,224 +,164,181,224,164,190,224,164,136,224,164,184,224,164,149,224,165,141,224,164,176 +,224,164,191,224,164,175,224,164,164,224,164,190 +}; diff --git a/packages/runtime/vendor/brotli/c/common/platform.c b/packages/runtime/vendor/brotli/c/common/platform.c new file mode 100644 index 000000000..1e047ced7 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/platform.c @@ -0,0 +1,19 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "platform.h" + +/* Default brotli_alloc_func */ +void* BrotliDefaultAllocFunc(void* opaque, size_t size) { + BROTLI_UNUSED(opaque); + return malloc(size); +} + +/* Default brotli_free_func */ +void BrotliDefaultFreeFunc(void* opaque, void* address) { + BROTLI_UNUSED(opaque); + free(address); +} diff --git a/packages/runtime/vendor/brotli/c/common/platform.h b/packages/runtime/vendor/brotli/c/common/platform.h new file mode 100644 index 000000000..b59f9b809 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/platform.h @@ -0,0 +1,680 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Macros for compiler / platform specific features and build options. + + Build options are: + * BROTLI_BUILD_32_BIT disables 64-bit optimizations + * BROTLI_BUILD_64_BIT forces to use 64-bit optimizations + * BROTLI_BUILD_BIG_ENDIAN forces to use big-endian optimizations + * BROTLI_BUILD_ENDIAN_NEUTRAL disables endian-aware optimizations + * BROTLI_BUILD_LITTLE_ENDIAN forces to use little-endian optimizations + * BROTLI_BUILD_NO_RBIT disables "rbit" optimization for ARM CPUs + * BROTLI_BUILD_NO_UNALIGNED_READ_FAST forces off the fast-unaligned-read + optimizations (mainly for testing purposes) + * BROTLI_DEBUG dumps file name and line number when decoder detects stream + or memory error + * BROTLI_ENABLE_LOG enables asserts and dumps various state information + * BROTLI_ENABLE_DUMP overrides default "dump" behaviour +*/ + +#ifndef BROTLI_COMMON_PLATFORM_H_ +#define BROTLI_COMMON_PLATFORM_H_ + +#include /* IWYU pragma: export memcmp, memcpy, memset */ +#include /* IWYU pragma: export exit, free, malloc */ +#include /* should include endian.h for us */ + +#include /* IWYU pragma: export */ +#include /* IWYU pragma: export */ + +#if BROTLI_MSVC_VERSION_CHECK(18, 0, 0) +#include +#endif + +#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG) +#include +#include +#endif + +/* The following macros were borrowed from https://github.com/nemequ/hedley + * with permission of original author - Evan Nemerson */ + +/* >>> >>> >>> hedley macros */ + +/* Define "BROTLI_PREDICT_TRUE" and "BROTLI_PREDICT_FALSE" macros for capable + compilers. + +To apply compiler hint, enclose the branching condition into macros, like this: + + if (BROTLI_PREDICT_TRUE(zero == 0)) { + // main execution path + } else { + // compiler should place this code outside of main execution path + } + +OR: + + if (BROTLI_PREDICT_FALSE(something_rare_or_unexpected_happens)) { + // compiler should place this code outside of main execution path + } + +*/ +#if BROTLI_GNUC_HAS_BUILTIN(__builtin_expect, 3, 0, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ + BROTLI_SUNPRO_VERSION_CHECK(5, 15, 0) || \ + BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ + BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ + BROTLI_TI_VERSION_CHECK(7, 3, 0) || \ + BROTLI_TINYC_VERSION_CHECK(0, 9, 27) +#define BROTLI_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#define BROTLI_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#else +#define BROTLI_PREDICT_FALSE(x) (x) +#define BROTLI_PREDICT_TRUE(x) (x) +#endif + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__cplusplus) +#define BROTLI_RESTRICT restrict +#elif BROTLI_GNUC_VERSION_CHECK(3, 1, 0) || \ + BROTLI_MSVC_VERSION_CHECK(14, 0, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ + BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ + BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ + BROTLI_PGI_VERSION_CHECK(17, 10, 0) || \ + BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ + BROTLI_IAR_VERSION_CHECK(8, 0, 0) || \ + (BROTLI_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus)) +#define BROTLI_RESTRICT __restrict +#elif BROTLI_SUNPRO_VERSION_CHECK(5, 3, 0) && !defined(__cplusplus) +#define BROTLI_RESTRICT _Restrict +#else +#define BROTLI_RESTRICT +#endif + +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) +#define BROTLI_MAYBE_INLINE inline +#elif defined(__GNUC_STDC_INLINE__) || defined(__GNUC_GNU_INLINE__) || \ + BROTLI_ARM_VERSION_CHECK(6, 2, 0) +#define BROTLI_MAYBE_INLINE __inline__ +#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0) || \ + BROTLI_ARM_VERSION_CHECK(4, 1, 0) || BROTLI_TI_VERSION_CHECK(8, 0, 0) +#define BROTLI_MAYBE_INLINE __inline +#else +#define BROTLI_MAYBE_INLINE +#endif + +#if BROTLI_GNUC_HAS_ATTRIBUTE(always_inline, 4, 0, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ + BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ + BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ + BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ + (BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +#define BROTLI_INLINE BROTLI_MAYBE_INLINE __attribute__((__always_inline__)) +#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0) +#define BROTLI_INLINE BROTLI_MAYBE_INLINE __forceinline +#elif BROTLI_TI_VERSION_CHECK(7, 0, 0) && defined(__cplusplus) +#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0) +#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("inline=forced") +#else +#define BROTLI_INLINE BROTLI_MAYBE_INLINE +#endif + +#if BROTLI_GNUC_HAS_ATTRIBUTE(noinline, 4, 0, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ + BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ + BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \ + BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ + (BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +#define BROTLI_NOINLINE __attribute__((__noinline__)) +#elif BROTLI_MSVC_VERSION_CHECK(13, 10, 0) +#define BROTLI_NOINLINE __declspec(noinline) +#elif BROTLI_PGI_VERSION_CHECK(10, 2, 0) +#define BROTLI_NOINLINE _Pragma("noinline") +#elif BROTLI_TI_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) +#define BROTLI_NOINLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0) +#define BROTLI_NOINLINE _Pragma("inline=never") +#else +#define BROTLI_NOINLINE +#endif + +/* <<< <<< <<< end of hedley macros. */ + +#if BROTLI_GNUC_HAS_ATTRIBUTE(unused, 2, 7, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) +#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE __attribute__ ((unused)) +#else +#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE +#endif + +#if BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0) +#define BROTLI_ALIGNED(N) __attribute__((aligned(N))) +#else +#define BROTLI_ALIGNED(N) +#endif + +#if (defined(__ARM_ARCH) && (__ARM_ARCH == 7)) || \ + (defined(M_ARM) && (M_ARM == 7)) +#define BROTLI_TARGET_ARMV7 +#endif /* ARMv7 */ + +#if (defined(__ARM_ARCH) && (__ARM_ARCH == 8)) || \ + defined(__aarch64__) || defined(__ARM64_ARCH_8__) +#define BROTLI_TARGET_ARMV8_ANY + +#if defined(__ARM_32BIT_STATE) +#define BROTLI_TARGET_ARMV8_32 +#elif defined(__ARM_64BIT_STATE) +#define BROTLI_TARGET_ARMV8_64 +#endif + +#endif /* ARMv8 */ + +#if defined(__ARM_NEON__) || defined(__ARM_NEON) +#define BROTLI_TARGET_NEON +#endif + +#if defined(__i386) || defined(_M_IX86) +#define BROTLI_TARGET_X86 +#endif + +#if defined(__x86_64__) || defined(_M_X64) +#define BROTLI_TARGET_X64 +#endif + +#if defined(__PPC64__) +#define BROTLI_TARGET_POWERPC64 +#endif + +#if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64 +#define BROTLI_TARGET_RISCV64 +#endif + +#if defined(__loongarch_lp64) +#define BROTLI_TARGET_LOONGARCH64 +#endif + +/* This does not seem to be an indicator of z/Architecture (64-bit); neither + that allows to use unaligned loads. */ +#if defined(__s390x__) +#define BROTLI_TARGET_S390X +#endif + +#if defined(__mips64) +#define BROTLI_TARGET_MIPS64 +#endif + +#if defined(BROTLI_TARGET_X64) || defined(BROTLI_TARGET_ARMV8_64) || \ + defined(BROTLI_TARGET_POWERPC64) || defined(BROTLI_TARGET_RISCV64) || \ + defined(BROTLI_TARGET_LOONGARCH64) || defined(BROTLI_TARGET_MIPS64) +#define BROTLI_TARGET_64_BITS 1 +#else +#define BROTLI_TARGET_64_BITS 0 +#endif + +#if defined(BROTLI_BUILD_64_BIT) +#define BROTLI_64_BITS 1 +#elif defined(BROTLI_BUILD_32_BIT) +#define BROTLI_64_BITS 0 +#else +#define BROTLI_64_BITS BROTLI_TARGET_64_BITS +#endif + +#if (BROTLI_64_BITS) +#define brotli_reg_t uint64_t +#else +#define brotli_reg_t uint32_t +#endif + +#if defined(BROTLI_BUILD_BIG_ENDIAN) +#define BROTLI_BIG_ENDIAN 1 +#elif defined(BROTLI_BUILD_LITTLE_ENDIAN) +#define BROTLI_LITTLE_ENDIAN 1 +#elif defined(BROTLI_BUILD_ENDIAN_NEUTRAL) +/* Just break elif chain. */ +#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define BROTLI_LITTLE_ENDIAN 1 +#elif defined(_WIN32) || defined(BROTLI_TARGET_X64) +/* Win32 & x64 can currently always be assumed to be little endian */ +#define BROTLI_LITTLE_ENDIAN 1 +#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define BROTLI_BIG_ENDIAN 1 +/* Likely target platform is iOS / OSX. */ +#elif defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) +#define BROTLI_LITTLE_ENDIAN 1 +#elif defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN) +#define BROTLI_BIG_ENDIAN 1 +#endif + +#if !defined(BROTLI_LITTLE_ENDIAN) +#define BROTLI_LITTLE_ENDIAN 0 +#endif + +#if !defined(BROTLI_BIG_ENDIAN) +#define BROTLI_BIG_ENDIAN 0 +#endif + +#if defined(BROTLI_BUILD_NO_UNALIGNED_READ_FAST) +#define BROTLI_UNALIGNED_READ_FAST (!!0) +#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \ + defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) || \ + defined(BROTLI_TARGET_RISCV64) || defined(BROTLI_TARGET_LOONGARCH64) +/* These targets are known to generate efficient code for unaligned reads + * (e.g. a single instruction, not multiple 1-byte loads, shifted and or'd + * together). */ +#define BROTLI_UNALIGNED_READ_FAST (!!1) +#else +#define BROTLI_UNALIGNED_READ_FAST (!!0) +#endif + +/* Portable unaligned memory access: read / write values via memcpy. */ +#if !defined(BROTLI_USE_PACKED_FOR_UNALIGNED) +#if defined(__mips__) && (!defined(__mips_isa_rev) || __mips_isa_rev < 6) +#define BROTLI_USE_PACKED_FOR_UNALIGNED 1 +#else +#define BROTLI_USE_PACKED_FOR_UNALIGNED 0 +#endif +#endif /* defined(BROTLI_USE_PACKED_FOR_UNALIGNED) */ + +#if BROTLI_USE_PACKED_FOR_UNALIGNED + +typedef union BrotliPackedValue { + uint16_t u16; + uint32_t u32; + uint64_t u64; + size_t szt; +} __attribute__ ((packed)) BrotliPackedValue; + +static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) { + const BrotliPackedValue* address = (const BrotliPackedValue*)p; + return address->u16; +} +static BROTLI_INLINE uint32_t BrotliUnalignedRead32(const void* p) { + const BrotliPackedValue* address = (const BrotliPackedValue*)p; + return address->u32; +} +static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) { + const BrotliPackedValue* address = (const BrotliPackedValue*)p; + return address->u64; +} +static BROTLI_INLINE size_t BrotliUnalignedReadSizeT(const void* p) { + const BrotliPackedValue* address = (const BrotliPackedValue*)p; + return address->szt; +} +static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) { + BrotliPackedValue* address = (BrotliPackedValue*)p; + address->u64 = v; +} + +#else /* not BROTLI_USE_PACKED_FOR_UNALIGNED */ + +static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) { + uint16_t t; + memcpy(&t, p, sizeof t); + return t; +} +static BROTLI_INLINE uint32_t BrotliUnalignedRead32(const void* p) { + uint32_t t; + memcpy(&t, p, sizeof t); + return t; +} +static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) { + uint64_t t; + memcpy(&t, p, sizeof t); + return t; +} +static BROTLI_INLINE size_t BrotliUnalignedReadSizeT(const void* p) { + size_t t; + memcpy(&t, p, sizeof t); + return t; +} +static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) { + memcpy(p, &v, sizeof v); +} + +#endif /* BROTLI_USE_PACKED_FOR_UNALIGNED */ + +#if BROTLI_GNUC_HAS_BUILTIN(__builtin_bswap16, 4, 3, 0) +#define BROTLI_BSWAP16(V) ((uint16_t)__builtin_bswap16(V)) +#else +#define BROTLI_BSWAP16(V) ((uint16_t)( \ + (((V) & 0xFFU) << 8) | \ + (((V) >> 8) & 0xFFU))) +#endif + +#if BROTLI_GNUC_HAS_BUILTIN(__builtin_bswap32, 4, 3, 0) +#define BROTLI_BSWAP32(V) ((uint32_t)__builtin_bswap32(V)) +#else +#define BROTLI_BSWAP32(V) ((uint32_t)( \ + (((V) & 0xFFU) << 24) | (((V) & 0xFF00U) << 8) | \ + (((V) >> 8) & 0xFF00U) | (((V) >> 24) & 0xFFU))) +#endif + +#if BROTLI_GNUC_HAS_BUILTIN(__builtin_bswap64, 4, 3, 0) +#define BROTLI_BSWAP64(V) ((uint64_t)__builtin_bswap64(V)) +#else +#define BROTLI_BSWAP64(V) ((uint64_t)( \ + (((V) & 0xFFU) << 56) | (((V) & 0xFF00U) << 40) | \ + (((V) & 0xFF0000U) << 24) | (((V) & 0xFF000000U) << 8) | \ + (((V) >> 8) & 0xFF000000U) | (((V) >> 24) & 0xFF0000U) | \ + (((V) >> 40) & 0xFF00U) | (((V) >> 56) & 0xFFU))) +#endif + +#if BROTLI_LITTLE_ENDIAN +/* Straight endianness. Just read / write values. */ +#define BROTLI_UNALIGNED_LOAD16LE BrotliUnalignedRead16 +#define BROTLI_UNALIGNED_LOAD32LE BrotliUnalignedRead32 +#define BROTLI_UNALIGNED_LOAD64LE BrotliUnalignedRead64 +#define BROTLI_UNALIGNED_STORE64LE BrotliUnalignedWrite64 +#elif BROTLI_BIG_ENDIAN /* BROTLI_LITTLE_ENDIAN */ +static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) { + uint16_t value = BrotliUnalignedRead16(p); + return BROTLI_BSWAP16(value); +} +static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) { + uint32_t value = BrotliUnalignedRead32(p); + return BROTLI_BSWAP32(value); +} +static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) { + uint64_t value = BrotliUnalignedRead64(p); + return BROTLI_BSWAP64(value); +} +static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) { + uint64_t value = BROTLI_BSWAP64(v); + BrotliUnalignedWrite64(p, value); +} +#else /* BROTLI_LITTLE_ENDIAN */ +/* Read / store values byte-wise; hopefully compiler will understand. */ +static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) { + const uint8_t* in = (const uint8_t*)p; + return (uint16_t)(in[0] | (in[1] << 8)); +} +static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) { + const uint8_t* in = (const uint8_t*)p; + uint32_t value = (uint32_t)(in[0]); + value |= (uint32_t)(in[1]) << 8; + value |= (uint32_t)(in[2]) << 16; + value |= (uint32_t)(in[3]) << 24; + return value; +} +static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) { + const uint8_t* in = (const uint8_t*)p; + uint64_t value = (uint64_t)(in[0]); + value |= (uint64_t)(in[1]) << 8; + value |= (uint64_t)(in[2]) << 16; + value |= (uint64_t)(in[3]) << 24; + value |= (uint64_t)(in[4]) << 32; + value |= (uint64_t)(in[5]) << 40; + value |= (uint64_t)(in[6]) << 48; + value |= (uint64_t)(in[7]) << 56; + return value; +} +static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) { + uint8_t* out = (uint8_t*)p; + out[0] = (uint8_t)v; + out[1] = (uint8_t)(v >> 8); + out[2] = (uint8_t)(v >> 16); + out[3] = (uint8_t)(v >> 24); + out[4] = (uint8_t)(v >> 32); + out[5] = (uint8_t)(v >> 40); + out[6] = (uint8_t)(v >> 48); + out[7] = (uint8_t)(v >> 56); +} +#endif /* BROTLI_LITTLE_ENDIAN */ + +static BROTLI_INLINE void* BROTLI_UNALIGNED_LOAD_PTR(const void* p) { + void* v; + memcpy(&v, p, sizeof(void*)); + return v; +} + +static BROTLI_INLINE void BROTLI_UNALIGNED_STORE_PTR(void* p, const void* v) { + memcpy(p, &v, sizeof(void*)); +} + +/* BROTLI_IS_CONSTANT macros returns true for compile-time constants. */ +#if BROTLI_GNUC_HAS_BUILTIN(__builtin_constant_p, 3, 0, 1) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) +#define BROTLI_IS_CONSTANT(x) (!!__builtin_constant_p(x)) +#else +#define BROTLI_IS_CONSTANT(x) (!!0) +#endif + +#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) +#define BROTLI_HAS_UBFX (!!1) +#else +#define BROTLI_HAS_UBFX (!!0) +#endif + +#if defined(BROTLI_ENABLE_LOG) +#define BROTLI_LOG(x) printf x +#else +#define BROTLI_LOG(x) +#endif + +#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG) +#define BROTLI_ENABLE_DUMP_DEFAULT 1 +#define BROTLI_DCHECK(x) assert(x) +#else +#define BROTLI_ENABLE_DUMP_DEFAULT 0 +#define BROTLI_DCHECK(x) +#endif + +#if !defined(BROTLI_ENABLE_DUMP) +#define BROTLI_ENABLE_DUMP BROTLI_ENABLE_DUMP_DEFAULT +#endif + +#if BROTLI_ENABLE_DUMP +static BROTLI_INLINE void BrotliDump(const char* f, int l, const char* fn) { + fprintf(stderr, "%s:%d (%s)\n", f, l, fn); + fflush(stderr); +} +#define BROTLI_DUMP() BrotliDump(__FILE__, __LINE__, __FUNCTION__) +#else +#define BROTLI_DUMP() (void)(0) +#endif + +/* BrotliRBit assumes brotli_reg_t fits native CPU register type. */ +#if (BROTLI_64_BITS == BROTLI_TARGET_64_BITS) +/* TODO(eustas): add appropriate icc/sunpro/arm/ibm/ti checks. */ +#if (BROTLI_GNUC_VERSION_CHECK(3, 0, 0) || defined(__llvm__)) && \ + !defined(BROTLI_BUILD_NO_RBIT) +#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) +/* TODO(eustas): detect ARMv6T2 and enable this code for it. */ +static BROTLI_INLINE brotli_reg_t BrotliRBit(brotli_reg_t input) { + brotli_reg_t output; + __asm__("rbit %0, %1\n" : "=r"(output) : "r"(input)); + return output; +} +#define BROTLI_RBIT(x) BrotliRBit(x) +#endif /* armv7 / armv8 */ +#endif /* gcc || clang */ +#endif /* brotli_reg_t is native */ +#if !defined(BROTLI_RBIT) +static BROTLI_INLINE void BrotliRBit(void) { /* Should break build if used. */ } +#endif /* BROTLI_RBIT */ + +#define BROTLI_REPEAT_4(X) {X; X; X; X;} +#define BROTLI_REPEAT_5(X) {X; X; X; X; X;} +#define BROTLI_REPEAT_6(X) {X; X; X; X; X; X;} + +#define BROTLI_UNUSED(X) (void)(X) + +#define BROTLI_MIN_MAX(T) \ + static BROTLI_INLINE T brotli_min_ ## T (T a, T b) { return a < b ? a : b; } \ + static BROTLI_INLINE T brotli_max_ ## T (T a, T b) { return a > b ? a : b; } +BROTLI_MIN_MAX(double) BROTLI_MIN_MAX(float) BROTLI_MIN_MAX(int) +BROTLI_MIN_MAX(size_t) BROTLI_MIN_MAX(uint32_t) BROTLI_MIN_MAX(uint8_t) +#undef BROTLI_MIN_MAX +#define BROTLI_MIN(T, A, B) (brotli_min_ ## T((A), (B))) +#define BROTLI_MAX(T, A, B) (brotli_max_ ## T((A), (B))) + +#define BROTLI_SWAP(T, A, I, J) { \ + T __brotli_swap_tmp = (A)[(I)]; \ + (A)[(I)] = (A)[(J)]; \ + (A)[(J)] = __brotli_swap_tmp; \ +} + +#if BROTLI_64_BITS +#if BROTLI_GNUC_HAS_BUILTIN(__builtin_ctzll, 3, 4, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) +#define BROTLI_TZCNT64 __builtin_ctzll +#elif BROTLI_MSVC_VERSION_CHECK(18, 0, 0) +#if defined(BROTLI_TARGET_X64) && !defined(_M_ARM64EC) +#define BROTLI_TZCNT64 _tzcnt_u64 +#else /* BROTLI_TARGET_X64 */ +static BROTLI_INLINE uint32_t BrotliBsf64Msvc(uint64_t x) { + uint32_t lsb; + _BitScanForward64(&lsb, x); + return lsb; +} +#define BROTLI_TZCNT64 BrotliBsf64Msvc +#endif /* BROTLI_TARGET_X64 */ +#endif /* __builtin_ctzll */ +#endif /* BROTLI_64_BITS */ + +#if BROTLI_GNUC_HAS_BUILTIN(__builtin_clz, 3, 4, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) +#define BROTLI_BSR32(x) (31u ^ (uint32_t)__builtin_clz(x)) +#elif BROTLI_MSVC_VERSION_CHECK(18, 0, 0) +static BROTLI_INLINE uint32_t BrotliBsr32Msvc(uint32_t x) { + unsigned long msb; + _BitScanReverse(&msb, x); + return (uint32_t)msb; +} +#define BROTLI_BSR32 BrotliBsr32Msvc +#endif /* __builtin_clz */ + +/* Default brotli_alloc_func */ +BROTLI_COMMON_API void* BrotliDefaultAllocFunc(void* opaque, size_t size); + +/* Default brotli_free_func */ +BROTLI_COMMON_API void BrotliDefaultFreeFunc(void* opaque, void* address); + +/* Circular logical rotates. */ +static BROTLI_INLINE uint16_t BrotliRotateRight16(uint16_t const value, + size_t count) { + count &= 0x0F; /* for fickle pattern recognition */ + return (value >> count) | (uint16_t)(value << ((0U - count) & 0x0F)); +} +static BROTLI_INLINE uint32_t BrotliRotateRight32(uint32_t const value, + size_t count) { + count &= 0x1F; /* for fickle pattern recognition */ + return (value >> count) | (uint32_t)(value << ((0U - count) & 0x1F)); +} +static BROTLI_INLINE uint64_t BrotliRotateRight64(uint64_t const value, + size_t count) { + count &= 0x3F; /* for fickle pattern recognition */ + return (value >> count) | (uint64_t)(value << ((0U - count) & 0x3F)); +} + +BROTLI_UNUSED_FUNCTION void BrotliSuppressUnusedFunctions(void) { + BROTLI_UNUSED(&BrotliSuppressUnusedFunctions); + BROTLI_UNUSED(&BrotliUnalignedRead16); + BROTLI_UNUSED(&BrotliUnalignedRead32); + BROTLI_UNUSED(&BrotliUnalignedRead64); + BROTLI_UNUSED(&BrotliUnalignedReadSizeT); + BROTLI_UNUSED(&BrotliUnalignedWrite64); + BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD16LE); + BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD32LE); + BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD64LE); + BROTLI_UNUSED(&BROTLI_UNALIGNED_STORE64LE); + BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD_PTR); + BROTLI_UNUSED(&BROTLI_UNALIGNED_STORE_PTR); + BROTLI_UNUSED(&BrotliRBit); + BROTLI_UNUSED(&brotli_min_double); + BROTLI_UNUSED(&brotli_max_double); + BROTLI_UNUSED(&brotli_min_float); + BROTLI_UNUSED(&brotli_max_float); + BROTLI_UNUSED(&brotli_min_int); + BROTLI_UNUSED(&brotli_max_int); + BROTLI_UNUSED(&brotli_min_size_t); + BROTLI_UNUSED(&brotli_max_size_t); + BROTLI_UNUSED(&brotli_min_uint32_t); + BROTLI_UNUSED(&brotli_max_uint32_t); + BROTLI_UNUSED(&brotli_min_uint8_t); + BROTLI_UNUSED(&brotli_max_uint8_t); + BROTLI_UNUSED(&BrotliDefaultAllocFunc); + BROTLI_UNUSED(&BrotliDefaultFreeFunc); + BROTLI_UNUSED(&BrotliRotateRight16); + BROTLI_UNUSED(&BrotliRotateRight32); + BROTLI_UNUSED(&BrotliRotateRight64); +#if BROTLI_ENABLE_DUMP + BROTLI_UNUSED(&BrotliDump); +#endif + +#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) && \ + !defined(_M_ARM64EC) +/* _mm_prefetch() is not defined outside of x86/x64 */ +/* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ +#include +#define PREFETCH_L1(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) +#define PREFETCH_L2(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T1) +#elif BROTLI_GNUC_HAS_BUILTIN(__builtin_prefetch, 3, 1, 0) +#define PREFETCH_L1(ptr) \ + __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) +#define PREFETCH_L2(ptr) \ + __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */) +#elif defined(__aarch64__) +#define PREFETCH_L1(ptr) \ + do { \ + __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr))); \ + } while (0) +#define PREFETCH_L2(ptr) \ + do { \ + __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr))); \ + } while (0) +#else +#define PREFETCH_L1(ptr) \ + do { \ + (void)(ptr); \ + } while (0) /* disabled */ +#define PREFETCH_L2(ptr) \ + do { \ + (void)(ptr); \ + } while (0) /* disabled */ +#endif + +/* The SIMD matchers are only faster at certain quality levels. */ +#if defined(_M_X64) && defined(BROTLI_TZCNT64) +#define BROTLI_MAX_SIMD_QUALITY 7 +#elif defined(BROTLI_TZCNT64) +#define BROTLI_MAX_SIMD_QUALITY 6 +#endif +} + +#if defined(_MSC_VER) +#define BROTLI_CRASH() __debugbreak(), (void)abort() +#elif BROTLI_GNUC_HAS_BUILTIN(__builtin_trap, 3, 0, 0) +#define BROTLI_CRASH() (void)__builtin_trap() +#else +#define BROTLI_CRASH() (void)abort() +#endif + +/* Make BROTLI_TEST=0 act same as undefined. */ +#if defined(BROTLI_TEST) && ((1-BROTLI_TEST-1) == 0) +#undef BROTLI_TEST +#endif + +#if BROTLI_GNUC_HAS_ATTRIBUTE(model, 3, 0, 3) +#define BROTLI_MODEL(M) __attribute__((model(M))) +#else +#define BROTLI_MODEL(M) /* M */ +#endif + +#if BROTLI_GNUC_HAS_ATTRIBUTE(cold, 4, 3, 0) +#define BROTLI_COLD __attribute__((cold)) +#else +#define BROTLI_COLD /* cold */ +#endif + +#endif /* BROTLI_COMMON_PLATFORM_H_ */ diff --git a/packages/runtime/vendor/brotli/c/common/shared_dictionary.c b/packages/runtime/vendor/brotli/c/common/shared_dictionary.c new file mode 100644 index 000000000..cef4e20fc --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/shared_dictionary.c @@ -0,0 +1,517 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Shared Dictionary definition and utilities. */ + +#include + +#include "dictionary.h" +#include "platform.h" +#include "shared_dictionary_internal.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if defined(BROTLI_EXPERIMENTAL) + +#define BROTLI_NUM_ENCODED_LENGTHS (SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH \ + - SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH + 1) + +/* Max allowed by spec */ +#define BROTLI_MAX_SIZE_BITS 15u + +/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ +static BROTLI_BOOL ReadBool(const uint8_t* encoded, size_t size, size_t* pos, + BROTLI_BOOL* result) { + uint8_t value; + size_t position = *pos; + if (position >= size) return BROTLI_FALSE; /* past file end */ + value = encoded[position++]; + if (value > 1) return BROTLI_FALSE; /* invalid bool */ + *result = TO_BROTLI_BOOL(value); + *pos = position; + return BROTLI_TRUE; /* success */ +} + +/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ +static BROTLI_BOOL ReadUint8(const uint8_t* encoded, size_t size, size_t* pos, + uint8_t* result) { + size_t position = *pos; + if (position + sizeof(uint8_t) > size) return BROTLI_FALSE; + *result = encoded[position++]; + *pos = position; + return BROTLI_TRUE; +} + +/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ +static BROTLI_BOOL ReadUint16(const uint8_t* encoded, size_t size, size_t* pos, + uint16_t* result) { + size_t position = *pos; + if (position + sizeof(uint16_t) > size) return BROTLI_FALSE; + *result = BROTLI_UNALIGNED_LOAD16LE(&encoded[position]); + position += 2; + *pos = position; + return BROTLI_TRUE; +} + +/* Reads a varint into a uint32_t, and returns error if it's too large */ +/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */ +static BROTLI_BOOL ReadVarint32(const uint8_t* encoded, size_t size, + size_t* pos, uint32_t* result) { + int num = 0; + uint8_t byte; + *result = 0; + for (;;) { + if (*pos >= size) return BROTLI_FALSE; + byte = encoded[(*pos)++]; + if (num == 4 && byte > 15) return BROTLI_FALSE; + *result |= (uint32_t)(byte & 127) << (num * 7); + if (byte < 128) return BROTLI_TRUE; + num++; + } +} + +/* Returns the total length of word list. */ +static size_t BrotliSizeBitsToOffsets(const uint8_t* size_bits_by_length, + uint32_t* offsets_by_length) { + uint32_t pos = 0; + uint32_t i; + for (i = 0; i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; i++) { + offsets_by_length[i] = pos; + if (size_bits_by_length[i] != 0) { + pos += i << size_bits_by_length[i]; + } + } + return pos; +} + +static BROTLI_BOOL ParseWordList(size_t size, const uint8_t* encoded, + size_t* pos, BrotliDictionary* out) { + size_t offset; + size_t i; + size_t position = *pos; + if (position + BROTLI_NUM_ENCODED_LENGTHS > size) { + return BROTLI_FALSE; + } + + memset(out->size_bits_by_length, 0, SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH); + memcpy(out->size_bits_by_length + SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH, + &encoded[position], BROTLI_NUM_ENCODED_LENGTHS); + for (i = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; + i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; i++) { + if (out->size_bits_by_length[i] > BROTLI_MAX_SIZE_BITS) { + return BROTLI_FALSE; + } + } + position += BROTLI_NUM_ENCODED_LENGTHS; + offset = BrotliSizeBitsToOffsets( + out->size_bits_by_length, out->offsets_by_length); + + out->data = &encoded[position]; + out->data_size = offset; + position += offset; + if (position > size) return BROTLI_FALSE; + *pos = position; + return BROTLI_TRUE; +} + +/* Computes the cutOffTransforms of a BrotliTransforms which already has the + transforms data correctly filled in. */ +static void ComputeCutoffTransforms(BrotliTransforms* transforms) { + uint32_t i; + for (i = 0; i < BROTLI_TRANSFORMS_MAX_CUT_OFF + 1; i++) { + transforms->cutOffTransforms[i] = -1; + } + for (i = 0; i < transforms->num_transforms; i++) { + const uint8_t* prefix = BROTLI_TRANSFORM_PREFIX(transforms, i); + uint8_t type = BROTLI_TRANSFORM_TYPE(transforms, i); + const uint8_t* suffix = BROTLI_TRANSFORM_SUFFIX(transforms, i); + if (type <= BROTLI_TRANSFORM_OMIT_LAST_9 && *prefix == 0 && *suffix == 0 && + transforms->cutOffTransforms[type] == -1) { + transforms->cutOffTransforms[type] = (int16_t)i; + } + } +} + +static BROTLI_BOOL ParsePrefixSuffixTable(size_t size, const uint8_t* encoded, + size_t* pos, BrotliTransforms* out, uint16_t* out_table, + size_t* out_table_size) { + size_t position = *pos; + size_t offset = 0; + size_t stringlet_count = 0; /* NUM_PREFIX_SUFFIX */ + size_t data_length = 0; + + /* PREFIX_SUFFIX_LENGTH */ + if (!ReadUint16(encoded, size, &position, &out->prefix_suffix_size)) { + return BROTLI_FALSE; + } + data_length = out->prefix_suffix_size; + + /* Must at least have space for null terminator. */ + if (data_length < 1) return BROTLI_FALSE; + out->prefix_suffix = &encoded[position]; + if (position + data_length >= size) return BROTLI_FALSE; + while (BROTLI_TRUE) { + /* STRING_LENGTH */ + size_t stringlet_len = encoded[position + offset]; + out_table[stringlet_count] = (uint16_t)offset; + stringlet_count++; + offset++; + if (stringlet_len == 0) { + if (offset == data_length) { + break; + } else { + return BROTLI_FALSE; + } + } + if (stringlet_count > 255) return BROTLI_FALSE; + offset += stringlet_len; + if (offset >= data_length) return BROTLI_FALSE; + } + + position += data_length; + *pos = position; + *out_table_size = (uint16_t)stringlet_count; + return BROTLI_TRUE; +} + +static BROTLI_BOOL ParseTransformsList(size_t size, const uint8_t* encoded, + size_t* pos, BrotliTransforms* out, uint16_t* prefix_suffix_table, + size_t* prefix_suffix_count) { + uint32_t i; + BROTLI_BOOL has_params = BROTLI_FALSE; + BROTLI_BOOL prefix_suffix_ok = BROTLI_FALSE; + size_t position = *pos; + size_t stringlet_cnt = 0; + if (position >= size) return BROTLI_FALSE; + + prefix_suffix_ok = ParsePrefixSuffixTable( + size, encoded, &position, out, prefix_suffix_table, &stringlet_cnt); + if (!prefix_suffix_ok) return BROTLI_FALSE; + out->prefix_suffix_map = prefix_suffix_table; + *prefix_suffix_count = stringlet_cnt; + + out->num_transforms = encoded[position++]; + out->transforms = &encoded[position]; + position += (size_t)out->num_transforms * 3; + if (position > size) return BROTLI_FALSE; + /* Check for errors and read extra parameters. */ + for (i = 0; i < out->num_transforms; i++) { + uint8_t prefix_id = BROTLI_TRANSFORM_PREFIX_ID(out, i); + uint8_t type = BROTLI_TRANSFORM_TYPE(out, i); + uint8_t suffix_id = BROTLI_TRANSFORM_SUFFIX_ID(out, i); + if (prefix_id >= stringlet_cnt) return BROTLI_FALSE; + if (type >= BROTLI_NUM_TRANSFORM_TYPES) return BROTLI_FALSE; + if (suffix_id >= stringlet_cnt) return BROTLI_FALSE; + if (type == BROTLI_TRANSFORM_SHIFT_FIRST || + type == BROTLI_TRANSFORM_SHIFT_ALL) { + has_params = BROTLI_TRUE; + } + } + if (has_params) { + out->params = &encoded[position]; + position += (size_t)out->num_transforms * 2; + if (position > size) return BROTLI_FALSE; + for (i = 0; i < out->num_transforms; i++) { + uint8_t type = BROTLI_TRANSFORM_TYPE(out, i); + if (type != BROTLI_TRANSFORM_SHIFT_FIRST && + type != BROTLI_TRANSFORM_SHIFT_ALL) { + if (out->params[i * 2] != 0 || out->params[i * 2 + 1] != 0) { + return BROTLI_FALSE; + } + } + } + } else { + out->params = NULL; + } + ComputeCutoffTransforms(out); + *pos = position; + return BROTLI_TRUE; +} + +static BROTLI_BOOL DryParseDictionary(const uint8_t* encoded, + size_t size, uint32_t* num_prefix, BROTLI_BOOL* is_custom_static_dict) { + size_t pos = 0; + uint32_t chunk_size = 0; + uint8_t num_word_lists; + uint8_t num_transform_lists; + *is_custom_static_dict = BROTLI_FALSE; + *num_prefix = 0; + + /* Skip magic header bytes. */ + pos += 2; + + /* LZ77_DICTIONARY_LENGTH */ + if (!ReadVarint32(encoded, size, &pos, &chunk_size)) return BROTLI_FALSE; + if (chunk_size != 0) { + /* This limitation is not specified but the 32-bit Brotli decoder for now */ + if (chunk_size > 1073741823) return BROTLI_FALSE; + *num_prefix = 1; + if (pos + chunk_size > size) return BROTLI_FALSE; + pos += chunk_size; + } + + if (!ReadUint8(encoded, size, &pos, &num_word_lists)) { + return BROTLI_FALSE; + } + if (!ReadUint8(encoded, size, &pos, &num_transform_lists)) { + return BROTLI_FALSE; + } + + if (num_word_lists > 0 || num_transform_lists > 0) { + *is_custom_static_dict = BROTLI_TRUE; + } + + return BROTLI_TRUE; +} + +static BROTLI_BOOL ParseDictionary(const uint8_t* encoded, size_t size, + BrotliSharedDictionary* dict) { + uint32_t i; + size_t pos = 0; + uint32_t chunk_size = 0; + size_t total_prefix_suffix_count = 0; + size_t transform_list_start[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; + uint16_t temporary_prefix_suffix_table[256]; + + /* Skip magic header bytes. */ + pos += 2; + + /* LZ77_DICTIONARY_LENGTH */ + if (!ReadVarint32(encoded, size, &pos, &chunk_size)) return BROTLI_FALSE; + if (chunk_size != 0) { + if (pos + chunk_size > size) return BROTLI_FALSE; + dict->prefix_size[dict->num_prefix] = chunk_size; + dict->prefix[dict->num_prefix] = &encoded[pos]; + dict->num_prefix++; + /* LZ77_DICTIONARY_LENGTH bytes. */ + pos += chunk_size; + } + + /* NUM_WORD_LISTS */ + if (!ReadUint8(encoded, size, &pos, &dict->num_word_lists)) { + return BROTLI_FALSE; + } + if (dict->num_word_lists > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) { + return BROTLI_FALSE; + } + + if (dict->num_word_lists != 0) { + dict->words_instances = (BrotliDictionary*)dict->alloc_func( + dict->memory_manager_opaque, + dict->num_word_lists * sizeof(*dict->words_instances)); + if (!dict->words_instances) return BROTLI_FALSE; /* OOM */ + } + for (i = 0; i < dict->num_word_lists; i++) { + if (!ParseWordList(size, encoded, &pos, &dict->words_instances[i])) { + return BROTLI_FALSE; + } + } + + /* NUM_TRANSFORM_LISTS */ + if (!ReadUint8(encoded, size, &pos, &dict->num_transform_lists)) { + return BROTLI_FALSE; + } + if (dict->num_transform_lists > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) { + return BROTLI_FALSE; + } + + if (dict->num_transform_lists != 0) { + dict->transforms_instances = (BrotliTransforms*)dict->alloc_func( + dict->memory_manager_opaque, + dict->num_transform_lists * sizeof(*dict->transforms_instances)); + if (!dict->transforms_instances) return BROTLI_FALSE; /* OOM */ + } + for (i = 0; i < dict->num_transform_lists; i++) { + BROTLI_BOOL ok = BROTLI_FALSE; + size_t prefix_suffix_count = 0; + transform_list_start[i] = pos; + dict->transforms_instances[i].prefix_suffix_map = + temporary_prefix_suffix_table; + ok = ParseTransformsList( + size, encoded, &pos, &dict->transforms_instances[i], + temporary_prefix_suffix_table, &prefix_suffix_count); + if (!ok) return BROTLI_FALSE; + total_prefix_suffix_count += prefix_suffix_count; + } + if (total_prefix_suffix_count != 0) { + dict->prefix_suffix_maps = (uint16_t*)dict->alloc_func( + dict->memory_manager_opaque, + total_prefix_suffix_count * sizeof(*dict->prefix_suffix_maps)); + if (!dict->prefix_suffix_maps) return BROTLI_FALSE; /* OOM */ + } + total_prefix_suffix_count = 0; + for (i = 0; i < dict->num_transform_lists; i++) { + size_t prefix_suffix_count = 0; + size_t position = transform_list_start[i]; + uint16_t* prefix_suffix_map = + &dict->prefix_suffix_maps[total_prefix_suffix_count]; + BROTLI_BOOL ok = ParsePrefixSuffixTable( + size, encoded, &position, &dict->transforms_instances[i], + prefix_suffix_map, &prefix_suffix_count); + if (!ok) return BROTLI_FALSE; + dict->transforms_instances[i].prefix_suffix_map = prefix_suffix_map; + total_prefix_suffix_count += prefix_suffix_count; + } + + if (dict->num_word_lists != 0 || dict->num_transform_lists != 0) { + if (!ReadUint8(encoded, size, &pos, &dict->num_dictionaries)) { + return BROTLI_FALSE; + } + if (dict->num_dictionaries == 0 || + dict->num_dictionaries > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) { + return BROTLI_FALSE; + } + for (i = 0; i < dict->num_dictionaries; i++) { + uint8_t words_index; + uint8_t transforms_index; + if (!ReadUint8(encoded, size, &pos, &words_index)) { + return BROTLI_FALSE; + } + if (words_index > dict->num_word_lists) return BROTLI_FALSE; + if (!ReadUint8(encoded, size, &pos, &transforms_index)) { + return BROTLI_FALSE; + } + if (transforms_index > dict->num_transform_lists) return BROTLI_FALSE; + dict->words[i] = words_index == dict->num_word_lists ? + BrotliGetDictionary() : &dict->words_instances[words_index]; + dict->transforms[i] = transforms_index == dict->num_transform_lists ? + BrotliGetTransforms(): &dict->transforms_instances[transforms_index]; + } + /* CONTEXT_ENABLED */ + if (!ReadBool(encoded, size, &pos, &dict->context_based)) { + return BROTLI_FALSE; + } + + /* CONTEXT_MAP */ + if (dict->context_based) { + for (i = 0; i < SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS; i++) { + if (!ReadUint8(encoded, size, &pos, &dict->context_map[i])) { + return BROTLI_FALSE; + } + if (dict->context_map[i] >= dict->num_dictionaries) { + return BROTLI_FALSE; + } + } + } + } else { + dict->context_based = BROTLI_FALSE; + dict->num_dictionaries = 1; + dict->words[0] = BrotliGetDictionary(); + dict->transforms[0] = BrotliGetTransforms(); + } + + return BROTLI_TRUE; +} + +/* Decodes shared dictionary and verifies correctness. + Returns BROTLI_TRUE if dictionary is valid, BROTLI_FALSE otherwise. + The BrotliSharedDictionary must already have been initialized. If the + BrotliSharedDictionary already contains data, compound dictionaries + will be appended, but an error will be returned if it already has + custom words or transforms. + TODO(lode): link to RFC for shared brotli once published. */ +static BROTLI_BOOL DecodeSharedDictionary( + const uint8_t* encoded, size_t size, BrotliSharedDictionary* dict) { + uint32_t num_prefix = 0; + BROTLI_BOOL is_custom_static_dict = BROTLI_FALSE; + BROTLI_BOOL has_custom_static_dict = + dict->num_word_lists > 0 || dict->num_transform_lists > 0; + + /* Check magic header bytes. */ + if (size < 2) return BROTLI_FALSE; + if (encoded[0] != 0x91 || encoded[1] != 0) return BROTLI_FALSE; + + if (!DryParseDictionary(encoded, size, &num_prefix, &is_custom_static_dict)) { + return BROTLI_FALSE; + } + + if (num_prefix + dict->num_prefix > SHARED_BROTLI_MAX_COMPOUND_DICTS) { + return BROTLI_FALSE; + } + + /* Cannot combine different static dictionaries, only prefix dictionaries */ + if (has_custom_static_dict && is_custom_static_dict) return BROTLI_FALSE; + + return ParseDictionary(encoded, size, dict); +} + +#endif /* BROTLI_EXPERIMENTAL */ + +void BrotliSharedDictionaryDestroyInstance( + BrotliSharedDictionary* dict) { + if (!dict) { + return; + } else { + brotli_free_func free_func = dict->free_func; + void* opaque = dict->memory_manager_opaque; + /* Cleanup. */ + free_func(opaque, dict->words_instances); + free_func(opaque, dict->transforms_instances); + free_func(opaque, dict->prefix_suffix_maps); + /* Self-destruction. */ + free_func(opaque, dict); + } +} + +BROTLI_BOOL BrotliSharedDictionaryAttach( + BrotliSharedDictionary* dict, BrotliSharedDictionaryType type, + size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]) { + if (!dict) { + return BROTLI_FALSE; + } +#if defined(BROTLI_EXPERIMENTAL) + if (type == BROTLI_SHARED_DICTIONARY_SERIALIZED) { + return DecodeSharedDictionary(data, data_size, dict); + } +#endif /* BROTLI_EXPERIMENTAL */ + if (type == BROTLI_SHARED_DICTIONARY_RAW) { + if (dict->num_prefix >= SHARED_BROTLI_MAX_COMPOUND_DICTS) { + return BROTLI_FALSE; + } + dict->prefix_size[dict->num_prefix] = data_size; + dict->prefix[dict->num_prefix] = data; + dict->num_prefix++; + return BROTLI_TRUE; + } + return BROTLI_FALSE; +} + +BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + BrotliSharedDictionary* dict = 0; + if (!alloc_func && !free_func) { + dict = (BrotliSharedDictionary*)malloc(sizeof(BrotliSharedDictionary)); + } else if (alloc_func && free_func) { + dict = (BrotliSharedDictionary*)alloc_func( + opaque, sizeof(BrotliSharedDictionary)); + } + if (dict == 0) { + return 0; + } + + /* TODO(eustas): explicitly initialize all the fields? */ + memset(dict, 0, sizeof(BrotliSharedDictionary)); + + dict->context_based = BROTLI_FALSE; + dict->num_dictionaries = 1; + dict->num_word_lists = 0; + dict->num_transform_lists = 0; + + dict->words[0] = BrotliGetDictionary(); + dict->transforms[0] = BrotliGetTransforms(); + + dict->alloc_func = alloc_func ? alloc_func : BrotliDefaultAllocFunc; + dict->free_func = free_func ? free_func : BrotliDefaultFreeFunc; + dict->memory_manager_opaque = opaque; + + return dict; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/common/shared_dictionary_internal.h b/packages/runtime/vendor/brotli/c/common/shared_dictionary_internal.h new file mode 100644 index 000000000..c6a90fa74 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/shared_dictionary_internal.h @@ -0,0 +1,75 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* (Transparent) Shared Dictionary definition. */ + +#ifndef BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_ +#define BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_ + +#include + +#include "dictionary.h" +#include "platform.h" +#include "transform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +struct BrotliSharedDictionaryStruct { + /* LZ77 prefixes (compound dictionary). */ + uint32_t num_prefix; /* max SHARED_BROTLI_MAX_COMPOUND_DICTS */ + size_t prefix_size[SHARED_BROTLI_MAX_COMPOUND_DICTS]; + const uint8_t* prefix[SHARED_BROTLI_MAX_COMPOUND_DICTS]; + + /* If set, the context map is used to select word and transform list from 64 + contexts, if not set, the context map is not used and only words[0] and + transforms[0] are to be used. */ + BROTLI_BOOL context_based; + + uint8_t context_map[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; + + /* Amount of word_list+transform_list combinations. */ + uint8_t num_dictionaries; + + /* Must use num_dictionaries values. */ + const BrotliDictionary* words[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; + + /* Must use num_dictionaries values. */ + const BrotliTransforms* transforms[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; + + /* Amount of custom word lists. May be 0 if only Brotli's built-in is used */ + uint8_t num_word_lists; + + /* Contents of the custom words lists. Must be NULL if num_word_lists is 0. */ + BrotliDictionary* words_instances; + + /* Amount of custom transform lists. May be 0 if only Brotli's built-in is + used */ + uint8_t num_transform_lists; + + /* Contents of the custom transform lists. Must be NULL if num_transform_lists + is 0. */ + BrotliTransforms* transforms_instances; + + /* Concatenated prefix_suffix_maps of the custom transform lists. Must be NULL + if num_transform_lists is 0. */ + uint16_t* prefix_suffix_maps; + + /* Memory management */ + brotli_alloc_func alloc_func; + brotli_free_func free_func; + void* memory_manager_opaque; +}; + +typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionaryInternal; +#define BrotliSharedDictionary BrotliSharedDictionaryInternal + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_ */ diff --git a/packages/runtime/vendor/brotli/c/common/static_init.h b/packages/runtime/vendor/brotli/c/common/static_init.h new file mode 100644 index 000000000..12744de8f --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/static_init.h @@ -0,0 +1,56 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* + Central point for static initialization. + + In case of "lazy" mode `BrotliXxxLazyStaticInit` is not provided by the + library. Embedder is responsible for providing it. This function should call + `BrotliXxxLazyStaticInitInner` on the first invocation. This function should + not return until execution of `BrotliXxxLazyStaticInitInner` is finished. + In C or before C++11 it is possible to call `BrotliXxxLazyStaticInitInner` + on start-up path and then `BrotliEncoderLazyStaticInit` is could be no-op; + another option is to use available thread execution controls to meet the + requirements. For possible C++11 implementation see static_init_lazy.cc. +*/ + +#ifndef THIRD_PARTY_BROTLI_COMMON_STATIC_INIT_H_ +#define THIRD_PARTY_BROTLI_COMMON_STATIC_INIT_H_ + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* Static data is "initialized" in compile time. */ +#define BROTLI_STATIC_INIT_NONE 0 +/* Static data is initialized before "main". */ +#define BROTLI_STATIC_INIT_EARLY 1 +/* Static data is initialized when first encoder is created. */ +#define BROTLI_STATIC_INIT_LAZY 2 + +#define BROTLI_STATIC_INIT_DEFAULT BROTLI_STATIC_INIT_NONE + +#if !defined(BROTLI_STATIC_INIT) +#define BROTLI_STATIC_INIT BROTLI_STATIC_INIT_DEFAULT +#endif + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) && \ + (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_EARLY) && \ + (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_LAZY) +#error Invalid value for BROTLI_STATIC_INIT +#endif + +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_EARLY) +#if defined(BROTLI_EXTERNAL_DICTIONARY_DATA) +#error BROTLI_STATIC_INIT_EARLY will fail with BROTLI_EXTERNAL_DICTIONARY_DATA +#endif +#endif /* BROTLI_STATIC_INIT */ + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif // THIRD_PARTY_BROTLI_COMMON_STATIC_INIT_H_ diff --git a/packages/runtime/vendor/brotli/c/common/transform.c b/packages/runtime/vendor/brotli/c/common/transform.c new file mode 100644 index 000000000..968b91238 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/transform.c @@ -0,0 +1,293 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "platform.h" +#include "transform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* RFC 7932 transforms string data */ +static const BROTLI_MODEL("small") char kPrefixSuffix[217] = + "\1 \2, \10 of the \4 of \2s \1.\5 and \4 " +/* 0x _0 _2 __5 _E _3 _6 _8 _E */ + "in \1\"\4 to \2\">\1\n\2. \1]\5 for \3 a \6 " +/* 2x _3_ _5 _A_ _D_ _F _2 _4 _A _E */ + "that \1\'\6 with \6 from \4 by \1(\6. T" +/* 4x _5_ _7 _E _5 _A _C */ + "he \4 on \4 as \4 is \4ing \2\n\t\1:\3ed " +/* 6x _3 _8 _D _2 _7_ _ _A _C */ + "\2=\"\4 at \3ly \1,\2=\'\5.com/\7. This \5" +/* 8x _0 _ _3 _8 _C _E _ _1 _7 _F */ + " not \3er \3al \4ful \4ive \5less \4es" +/* Ax _5 _9 _D _2 _7 _D */ + "t \4ize \2\xc2\xa0\4ous \5 the \2e "; /* \0 - implicit trailing zero. */ +/* Cx _2 _7___ ___ _A _F _5 _8 */ + +static const BROTLI_MODEL("small") uint16_t kPrefixSuffixMap[50] = { + 0x00, 0x02, 0x05, 0x0E, 0x13, 0x16, 0x18, 0x1E, 0x23, 0x25, + 0x2A, 0x2D, 0x2F, 0x32, 0x34, 0x3A, 0x3E, 0x45, 0x47, 0x4E, + 0x55, 0x5A, 0x5C, 0x63, 0x68, 0x6D, 0x72, 0x77, 0x7A, 0x7C, + 0x80, 0x83, 0x88, 0x8C, 0x8E, 0x91, 0x97, 0x9F, 0xA5, 0xA9, + 0xAD, 0xB2, 0xB7, 0xBD, 0xC2, 0xC7, 0xCA, 0xCF, 0xD5, 0xD8 +}; + +/* RFC 7932 transforms */ +static const BROTLI_MODEL("small") uint8_t kTransformsData[] = { + 49, BROTLI_TRANSFORM_IDENTITY, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 0, + 0, BROTLI_TRANSFORM_IDENTITY, 0, + 49, BROTLI_TRANSFORM_OMIT_FIRST_1, 49, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0, + 49, BROTLI_TRANSFORM_IDENTITY, 47, + 0, BROTLI_TRANSFORM_IDENTITY, 49, + 4, BROTLI_TRANSFORM_IDENTITY, 0, + 49, BROTLI_TRANSFORM_IDENTITY, 3, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 6, + 49, BROTLI_TRANSFORM_OMIT_FIRST_2, 49, + 49, BROTLI_TRANSFORM_OMIT_LAST_1, 49, + 1, BROTLI_TRANSFORM_IDENTITY, 0, + 49, BROTLI_TRANSFORM_IDENTITY, 1, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0, + 49, BROTLI_TRANSFORM_IDENTITY, 7, + 49, BROTLI_TRANSFORM_IDENTITY, 9, + 48, BROTLI_TRANSFORM_IDENTITY, 0, + 49, BROTLI_TRANSFORM_IDENTITY, 8, + 49, BROTLI_TRANSFORM_IDENTITY, 5, + 49, BROTLI_TRANSFORM_IDENTITY, 10, + 49, BROTLI_TRANSFORM_IDENTITY, 11, + 49, BROTLI_TRANSFORM_OMIT_LAST_3, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 13, + 49, BROTLI_TRANSFORM_IDENTITY, 14, + 49, BROTLI_TRANSFORM_OMIT_FIRST_3, 49, + 49, BROTLI_TRANSFORM_OMIT_LAST_2, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 15, + 49, BROTLI_TRANSFORM_IDENTITY, 16, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 12, + 5, BROTLI_TRANSFORM_IDENTITY, 49, + 0, BROTLI_TRANSFORM_IDENTITY, 1, + 49, BROTLI_TRANSFORM_OMIT_FIRST_4, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 18, + 49, BROTLI_TRANSFORM_IDENTITY, 17, + 49, BROTLI_TRANSFORM_IDENTITY, 19, + 49, BROTLI_TRANSFORM_IDENTITY, 20, + 49, BROTLI_TRANSFORM_OMIT_FIRST_5, 49, + 49, BROTLI_TRANSFORM_OMIT_FIRST_6, 49, + 47, BROTLI_TRANSFORM_IDENTITY, 49, + 49, BROTLI_TRANSFORM_OMIT_LAST_4, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 22, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 23, + 49, BROTLI_TRANSFORM_IDENTITY, 24, + 49, BROTLI_TRANSFORM_IDENTITY, 25, + 49, BROTLI_TRANSFORM_OMIT_LAST_7, 49, + 49, BROTLI_TRANSFORM_OMIT_LAST_1, 26, + 49, BROTLI_TRANSFORM_IDENTITY, 27, + 49, BROTLI_TRANSFORM_IDENTITY, 28, + 0, BROTLI_TRANSFORM_IDENTITY, 12, + 49, BROTLI_TRANSFORM_IDENTITY, 29, + 49, BROTLI_TRANSFORM_OMIT_FIRST_9, 49, + 49, BROTLI_TRANSFORM_OMIT_FIRST_7, 49, + 49, BROTLI_TRANSFORM_OMIT_LAST_6, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 21, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1, + 49, BROTLI_TRANSFORM_OMIT_LAST_8, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 31, + 49, BROTLI_TRANSFORM_IDENTITY, 32, + 47, BROTLI_TRANSFORM_IDENTITY, 3, + 49, BROTLI_TRANSFORM_OMIT_LAST_5, 49, + 49, BROTLI_TRANSFORM_OMIT_LAST_9, 49, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 8, + 5, BROTLI_TRANSFORM_IDENTITY, 21, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 0, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 10, + 49, BROTLI_TRANSFORM_IDENTITY, 30, + 0, BROTLI_TRANSFORM_IDENTITY, 5, + 35, BROTLI_TRANSFORM_IDENTITY, 49, + 47, BROTLI_TRANSFORM_IDENTITY, 2, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 17, + 49, BROTLI_TRANSFORM_IDENTITY, 36, + 49, BROTLI_TRANSFORM_IDENTITY, 33, + 5, BROTLI_TRANSFORM_IDENTITY, 0, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 21, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5, + 49, BROTLI_TRANSFORM_IDENTITY, 37, + 0, BROTLI_TRANSFORM_IDENTITY, 30, + 49, BROTLI_TRANSFORM_IDENTITY, 38, + 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 0, + 49, BROTLI_TRANSFORM_IDENTITY, 39, + 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 49, + 49, BROTLI_TRANSFORM_IDENTITY, 34, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 8, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12, + 0, BROTLI_TRANSFORM_IDENTITY, 21, + 49, BROTLI_TRANSFORM_IDENTITY, 40, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12, + 49, BROTLI_TRANSFORM_IDENTITY, 41, + 49, BROTLI_TRANSFORM_IDENTITY, 42, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 17, + 49, BROTLI_TRANSFORM_IDENTITY, 43, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 10, + 0, BROTLI_TRANSFORM_IDENTITY, 34, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33, + 49, BROTLI_TRANSFORM_IDENTITY, 44, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 5, + 45, BROTLI_TRANSFORM_IDENTITY, 49, + 0, BROTLI_TRANSFORM_IDENTITY, 33, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 30, + 49, BROTLI_TRANSFORM_IDENTITY, 46, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 1, + 49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33, + 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 30, + 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 1, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 33, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 21, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 12, + 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 5, + 49, BROTLI_TRANSFORM_UPPERCASE_ALL, 34, + 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 12, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30, + 0, BROTLI_TRANSFORM_UPPERCASE_ALL, 34, + 0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34, +}; + +static const BROTLI_MODEL("small") +BrotliTransforms kBrotliTransforms = { + sizeof(kPrefixSuffix), + (const uint8_t*)kPrefixSuffix, + kPrefixSuffixMap, + sizeof(kTransformsData) / (3 * sizeof(kTransformsData[0])), + kTransformsData, + NULL, /* no extra parameters */ + {0, 12, 27, 23, 42, 63, 56, 48, 59, 64} +}; + +const BrotliTransforms* BrotliGetTransforms(void) { + return &kBrotliTransforms; +} + +static int ToUpperCase(uint8_t* p) { + if (p[0] < 0xC0) { + if (p[0] >= 'a' && p[0] <= 'z') { + p[0] ^= 32; + } + return 1; + } + /* An overly simplified uppercasing model for UTF-8. */ + if (p[0] < 0xE0) { + p[1] ^= 32; + return 2; + } + /* An arbitrary transform for three byte characters. */ + p[2] ^= 5; + return 3; +} + +static int Shift(uint8_t* word, int word_len, uint16_t parameter) { + /* Limited sign extension: scalar < (1 << 24). */ + uint32_t scalar = + (parameter & 0x7FFFu) + (0x1000000u - (parameter & 0x8000u)); + if (word[0] < 0x80) { + /* 1-byte rune / 0sssssss / 7 bit scalar (ASCII). */ + scalar += (uint32_t)word[0]; + word[0] = (uint8_t)(scalar & 0x7Fu); + return 1; + } else if (word[0] < 0xC0) { + /* Continuation / 10AAAAAA. */ + return 1; + } else if (word[0] < 0xE0) { + /* 2-byte rune / 110sssss AAssssss / 11 bit scalar. */ + if (word_len < 2) return 1; + scalar += (uint32_t)((word[1] & 0x3Fu) | ((word[0] & 0x1Fu) << 6u)); + word[0] = (uint8_t)(0xC0 | ((scalar >> 6u) & 0x1F)); + word[1] = (uint8_t)((word[1] & 0xC0) | (scalar & 0x3F)); + return 2; + } else if (word[0] < 0xF0) { + /* 3-byte rune / 1110ssss AAssssss BBssssss / 16 bit scalar. */ + if (word_len < 3) return word_len; + scalar += (uint32_t)((word[2] & 0x3Fu) | ((word[1] & 0x3Fu) << 6u) | + ((word[0] & 0x0Fu) << 12u)); + word[0] = (uint8_t)(0xE0 | ((scalar >> 12u) & 0x0F)); + word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 6u) & 0x3F)); + word[2] = (uint8_t)((word[2] & 0xC0) | (scalar & 0x3F)); + return 3; + } else if (word[0] < 0xF8) { + /* 4-byte rune / 11110sss AAssssss BBssssss CCssssss / 21 bit scalar. */ + if (word_len < 4) return word_len; + scalar += (uint32_t)((word[3] & 0x3Fu) | ((word[2] & 0x3Fu) << 6u) | + ((word[1] & 0x3Fu) << 12u) | ((word[0] & 0x07u) << 18u)); + word[0] = (uint8_t)(0xF0 | ((scalar >> 18u) & 0x07)); + word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 12u) & 0x3F)); + word[2] = (uint8_t)((word[2] & 0xC0) | ((scalar >> 6u) & 0x3F)); + word[3] = (uint8_t)((word[3] & 0xC0) | (scalar & 0x3F)); + return 4; + } + return 1; +} + +int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len, + const BrotliTransforms* transforms, int transform_idx) { + int idx = 0; + const uint8_t* prefix = BROTLI_TRANSFORM_PREFIX(transforms, transform_idx); + uint8_t type = BROTLI_TRANSFORM_TYPE(transforms, transform_idx); + const uint8_t* suffix = BROTLI_TRANSFORM_SUFFIX(transforms, transform_idx); + { + int prefix_len = *prefix++; + while (prefix_len--) { dst[idx++] = *prefix++; } + } + { + const int t = type; + int i = 0; + if (t <= BROTLI_TRANSFORM_OMIT_LAST_9) { + len -= t; + } else if (t >= BROTLI_TRANSFORM_OMIT_FIRST_1 + && t <= BROTLI_TRANSFORM_OMIT_FIRST_9) { + int skip = t - (BROTLI_TRANSFORM_OMIT_FIRST_1 - 1); + word += skip; + len -= skip; + } + while (i < len) { dst[idx++] = word[i++]; } + if (t == BROTLI_TRANSFORM_UPPERCASE_FIRST) { + ToUpperCase(&dst[idx - len]); + } else if (t == BROTLI_TRANSFORM_UPPERCASE_ALL) { + uint8_t* uppercase = &dst[idx - len]; + while (len > 0) { + int step = ToUpperCase(uppercase); + uppercase += step; + len -= step; + } + } else if (t == BROTLI_TRANSFORM_SHIFT_FIRST) { + uint16_t param = (uint16_t)(transforms->params[transform_idx * 2] + + (transforms->params[transform_idx * 2 + 1] << 8u)); + Shift(&dst[idx - len], len, param); + } else if (t == BROTLI_TRANSFORM_SHIFT_ALL) { + uint16_t param = (uint16_t)(transforms->params[transform_idx * 2] + + (transforms->params[transform_idx * 2 + 1] << 8u)); + uint8_t* shift = &dst[idx - len]; + while (len > 0) { + int step = Shift(shift, len, param); + shift += step; + len -= step; + } + } + } + { + int suffix_len = *suffix++; + while (suffix_len--) { dst[idx++] = *suffix++; } + return idx; + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/common/transform.h b/packages/runtime/vendor/brotli/c/common/transform.h new file mode 100644 index 000000000..c6176d834 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/transform.h @@ -0,0 +1,84 @@ +/* transforms is a part of ABI, but not API. + + It means that there are some functions that are supposed to be in "common" + library, but header itself is not placed into include/brotli. This way, + aforementioned functions will be available only to brotli internals. + */ + +#ifndef BROTLI_COMMON_TRANSFORM_H_ +#define BROTLI_COMMON_TRANSFORM_H_ + +#include "platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +enum BrotliWordTransformType { + BROTLI_TRANSFORM_IDENTITY = 0, + BROTLI_TRANSFORM_OMIT_LAST_1 = 1, + BROTLI_TRANSFORM_OMIT_LAST_2 = 2, + BROTLI_TRANSFORM_OMIT_LAST_3 = 3, + BROTLI_TRANSFORM_OMIT_LAST_4 = 4, + BROTLI_TRANSFORM_OMIT_LAST_5 = 5, + BROTLI_TRANSFORM_OMIT_LAST_6 = 6, + BROTLI_TRANSFORM_OMIT_LAST_7 = 7, + BROTLI_TRANSFORM_OMIT_LAST_8 = 8, + BROTLI_TRANSFORM_OMIT_LAST_9 = 9, + BROTLI_TRANSFORM_UPPERCASE_FIRST = 10, + BROTLI_TRANSFORM_UPPERCASE_ALL = 11, + BROTLI_TRANSFORM_OMIT_FIRST_1 = 12, + BROTLI_TRANSFORM_OMIT_FIRST_2 = 13, + BROTLI_TRANSFORM_OMIT_FIRST_3 = 14, + BROTLI_TRANSFORM_OMIT_FIRST_4 = 15, + BROTLI_TRANSFORM_OMIT_FIRST_5 = 16, + BROTLI_TRANSFORM_OMIT_FIRST_6 = 17, + BROTLI_TRANSFORM_OMIT_FIRST_7 = 18, + BROTLI_TRANSFORM_OMIT_FIRST_8 = 19, + BROTLI_TRANSFORM_OMIT_FIRST_9 = 20, + BROTLI_TRANSFORM_SHIFT_FIRST = 21, + BROTLI_TRANSFORM_SHIFT_ALL = 22, + BROTLI_NUM_TRANSFORM_TYPES /* Counts transforms, not a transform itself. */ +}; + +#define BROTLI_TRANSFORMS_MAX_CUT_OFF BROTLI_TRANSFORM_OMIT_LAST_9 + +typedef struct BrotliTransforms { + uint16_t prefix_suffix_size; + /* Last character must be null, so prefix_suffix_size must be at least 1. */ + const uint8_t* prefix_suffix; + const uint16_t* prefix_suffix_map; + uint32_t num_transforms; + /* Each entry is a [prefix_id, transform, suffix_id] triplet. */ + const uint8_t* transforms; + /* Shift for BROTLI_TRANSFORM_SHIFT_FIRST and BROTLI_TRANSFORM_SHIFT_ALL, + must be NULL if and only if no such transforms are present. */ + const uint8_t* params; + /* Indices of transforms like ["", BROTLI_TRANSFORM_OMIT_LAST_#, ""]. + 0-th element corresponds to ["", BROTLI_TRANSFORM_IDENTITY, ""]. + -1, if cut-off transform does not exist. */ + int16_t cutOffTransforms[BROTLI_TRANSFORMS_MAX_CUT_OFF + 1]; +} BrotliTransforms; + +/* T is BrotliTransforms*; result is uint8_t. */ +#define BROTLI_TRANSFORM_PREFIX_ID(T, I) ((T)->transforms[((I) * 3) + 0]) +#define BROTLI_TRANSFORM_TYPE(T, I) ((T)->transforms[((I) * 3) + 1]) +#define BROTLI_TRANSFORM_SUFFIX_ID(T, I) ((T)->transforms[((I) * 3) + 2]) + +/* T is BrotliTransforms*; result is const uint8_t*. */ +#define BROTLI_TRANSFORM_PREFIX(T, I) (&(T)->prefix_suffix[ \ + (T)->prefix_suffix_map[BROTLI_TRANSFORM_PREFIX_ID(T, I)]]) +#define BROTLI_TRANSFORM_SUFFIX(T, I) (&(T)->prefix_suffix[ \ + (T)->prefix_suffix_map[BROTLI_TRANSFORM_SUFFIX_ID(T, I)]]) + +BROTLI_COMMON_API const BrotliTransforms* BrotliGetTransforms(void); + +BROTLI_COMMON_API int BrotliTransformDictionaryWord( + uint8_t* dst, const uint8_t* word, int len, + const BrotliTransforms* transforms, int transform_idx); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_COMMON_TRANSFORM_H_ */ diff --git a/packages/runtime/vendor/brotli/c/common/version.h b/packages/runtime/vendor/brotli/c/common/version.h new file mode 100644 index 000000000..0d6fcc50a --- /dev/null +++ b/packages/runtime/vendor/brotli/c/common/version.h @@ -0,0 +1,51 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Version definition. */ + +#ifndef BROTLI_COMMON_VERSION_H_ +#define BROTLI_COMMON_VERSION_H_ + +/* Compose 3 components into a single number. In a hexadecimal representation + B and C components occupy exactly 3 digits. */ +#define BROTLI_MAKE_HEX_VERSION(A, B, C) ((A << 24) | (B << 12) | C) + +/* Those macros should only be used when library is compiled together with + the client. If library is dynamically linked, use BrotliDecoderVersion and + BrotliEncoderVersion methods. */ + +#define BROTLI_VERSION_MAJOR 1 +#define BROTLI_VERSION_MINOR 2 +#define BROTLI_VERSION_PATCH 0 + +#define BROTLI_VERSION BROTLI_MAKE_HEX_VERSION( \ + BROTLI_VERSION_MAJOR, BROTLI_VERSION_MINOR, BROTLI_VERSION_PATCH) + +/* This macro is used by build system to produce Libtool-friendly soname. See + https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html + Version evolution rules: + - interfaces added (or change is compatible) -> current+1:0:age+1 + - interfaces removed (or changed is incompatible) -> current+1:0:0 + - interfaces not changed -> current:revision+1:age + */ + +#define BROTLI_ABI_CURRENT 3 +#define BROTLI_ABI_REVISION 0 +#define BROTLI_ABI_AGE 2 + +#if BROTLI_VERSION_MAJOR != (BROTLI_ABI_CURRENT - BROTLI_ABI_AGE) +#error ABI/API version inconsistency +#endif + +#if BROTLI_VERSION_MINOR != BROTLI_ABI_AGE +#error ABI/API version inconsistency +#endif + +#if BROTLI_VERSION_PATCH != BROTLI_ABI_REVISION +#error ABI/API version inconsistency +#endif + +#endif /* BROTLI_COMMON_VERSION_H_ */ diff --git a/packages/runtime/vendor/brotli/c/dec/bit_reader.c b/packages/runtime/vendor/brotli/c/dec/bit_reader.c new file mode 100644 index 000000000..fae034fd8 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/bit_reader.c @@ -0,0 +1,77 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Bit reading helpers */ + +#include "bit_reader.h" + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +const BROTLI_MODEL("small") +brotli_reg_t kBrotliBitMask[33] = { 0x00000000, + 0x00000001, 0x00000003, 0x00000007, 0x0000000F, + 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, + 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, + 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, + 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, + 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, + 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF +}; + +void BrotliInitBitReader(BrotliBitReader* const br) { + br->val_ = 0; + br->bit_pos_ = 0; +} + +BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br) { + size_t aligned_read_mask = (sizeof(br->val_) >> 1) - 1; + /* Fixing alignment after unaligned BrotliFillWindow would result accumulator + overflow. If unalignment is caused by BrotliSafeReadBits, then there is + enough space in accumulator to fix alignment. */ + if (BROTLI_UNALIGNED_READ_FAST) { + aligned_read_mask = 0; + } + if (BrotliGetAvailableBits(br) == 0) { + br->val_ = 0; + if (!BrotliPullByte(br)) { + return BROTLI_FALSE; + } + } + + while ((((size_t)br->next_in) & aligned_read_mask) != 0) { + if (!BrotliPullByte(br)) { + /* If we consumed all the input, we don't care about the alignment. */ + return BROTLI_TRUE; + } + } + return BROTLI_TRUE; +} + +BROTLI_BOOL BrotliSafeReadBits32Slow(BrotliBitReader* const br, + brotli_reg_t n_bits, brotli_reg_t* val) { + brotli_reg_t low_val; + brotli_reg_t high_val; + BrotliBitReaderState memento; + BROTLI_DCHECK(n_bits <= 32); + BROTLI_DCHECK(n_bits > 24); + BrotliBitReaderSaveState(br, &memento); + if (!BrotliSafeReadBits(br, 16, &low_val) || + !BrotliSafeReadBits(br, n_bits - 16, &high_val)) { + BrotliBitReaderRestoreState(br, &memento); + return BROTLI_FALSE; + } + *val = low_val | (high_val << 16); + return BROTLI_TRUE; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/dec/bit_reader.h b/packages/runtime/vendor/brotli/c/dec/bit_reader.h new file mode 100644 index 000000000..ef6b047ae --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/bit_reader.h @@ -0,0 +1,419 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Bit reading helpers */ + +#ifndef BROTLI_DEC_BIT_READER_H_ +#define BROTLI_DEC_BIT_READER_H_ + +#include "../common/constants.h" +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define BROTLI_SHORT_FILL_BIT_WINDOW_READ (sizeof(brotli_reg_t) >> 1) + +/* 162 bits + 7 bytes */ +#define BROTLI_FAST_INPUT_SLACK 28 + +BROTLI_INTERNAL extern const brotli_reg_t kBrotliBitMask[33]; + +static BROTLI_INLINE brotli_reg_t BitMask(brotli_reg_t n) { + if (BROTLI_IS_CONSTANT(n) || BROTLI_HAS_UBFX) { + /* Masking with this expression turns to a single + "Unsigned Bit Field Extract" UBFX instruction on ARM. */ + return ~(~((brotli_reg_t)0) << n); + } else { + return kBrotliBitMask[n]; + } +} + +typedef struct { + brotli_reg_t val_; /* pre-fetched bits */ + brotli_reg_t bit_pos_; /* current bit-reading position in val_ */ + const uint8_t* next_in; /* the byte we're reading from */ + const uint8_t* guard_in; /* position from which "fast-path" is prohibited */ + const uint8_t* last_in; /* == next_in + avail_in */ +} BrotliBitReader; + +typedef struct { + brotli_reg_t val_; + brotli_reg_t bit_pos_; + const uint8_t* next_in; + size_t avail_in; +} BrotliBitReaderState; + +/* Initializes the BrotliBitReader fields. */ +BROTLI_INTERNAL void BrotliInitBitReader(BrotliBitReader* br); + +/* Ensures that accumulator is not empty. + May consume up to sizeof(brotli_reg_t) - 1 bytes of input. + Returns BROTLI_FALSE if data is required but there is no input available. + For !BROTLI_UNALIGNED_READ_FAST this function also prepares bit reader for + aligned reading. */ +BROTLI_INTERNAL BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* br); + +/* Fallback for BrotliSafeReadBits32. Extracted as noninlined method to unburden + the main code-path. Never called for RFC brotli streams, required only for + "large-window" mode and other extensions. */ +BROTLI_INTERNAL BROTLI_NOINLINE BROTLI_BOOL BrotliSafeReadBits32Slow( + BrotliBitReader* br, brotli_reg_t n_bits, brotli_reg_t* val); + +static BROTLI_INLINE size_t +BrotliBitReaderGetAvailIn(BrotliBitReader* const br) { + return (size_t)(br->last_in - br->next_in); +} + +static BROTLI_INLINE void BrotliBitReaderSaveState( + BrotliBitReader* const from, BrotliBitReaderState* to) { + to->val_ = from->val_; + to->bit_pos_ = from->bit_pos_; + to->next_in = from->next_in; + to->avail_in = BrotliBitReaderGetAvailIn(from); +} + +static BROTLI_INLINE void BrotliBitReaderSetInput( + BrotliBitReader* const br, const uint8_t* next_in, size_t avail_in) { + br->next_in = next_in; + br->last_in = (avail_in == 0) ? next_in : (next_in + avail_in); + if (avail_in + 1 > BROTLI_FAST_INPUT_SLACK) { + br->guard_in = next_in + (avail_in + 1 - BROTLI_FAST_INPUT_SLACK); + } else { + br->guard_in = next_in; + } +} + +static BROTLI_INLINE void BrotliBitReaderRestoreState( + BrotliBitReader* const to, BrotliBitReaderState* from) { + to->val_ = from->val_; + to->bit_pos_ = from->bit_pos_; + to->next_in = from->next_in; + BrotliBitReaderSetInput(to, from->next_in, from->avail_in); +} + +static BROTLI_INLINE brotli_reg_t BrotliGetAvailableBits( + const BrotliBitReader* br) { + return br->bit_pos_; +} + +/* Returns amount of unread bytes the bit reader still has buffered from the + BrotliInput, including whole bytes in br->val_. Result is capped with + maximal ring-buffer size (larger number won't be utilized anyway). */ +static BROTLI_INLINE size_t BrotliGetRemainingBytes(BrotliBitReader* br) { + static const size_t kCap = (size_t)1 << BROTLI_LARGE_MAX_WBITS; + size_t avail_in = BrotliBitReaderGetAvailIn(br); + if (avail_in > kCap) return kCap; + return avail_in + (BrotliGetAvailableBits(br) >> 3); +} + +/* Checks if there is at least |num| bytes left in the input ring-buffer + (excluding the bits remaining in br->val_). */ +static BROTLI_INLINE BROTLI_BOOL BrotliCheckInputAmount( + BrotliBitReader* const br) { + return TO_BROTLI_BOOL(br->next_in < br->guard_in); +} + +/* Load more bits into accumulator. */ +static BROTLI_INLINE brotli_reg_t BrotliBitReaderLoadBits(brotli_reg_t val, + brotli_reg_t new_bits, + brotli_reg_t count, + brotli_reg_t offset) { + BROTLI_DCHECK( + !((val >> offset) & ~new_bits & ~(~((brotli_reg_t)0) << count))); + (void)count; + return val | (new_bits << offset); +} + +/* Guarantees that there are at least |n_bits| + 1 bits in accumulator. + Precondition: accumulator contains at least 1 bit. + |n_bits| should be in the range [1..24] for regular build. For portable + non-64-bit little-endian build only 16 bits are safe to request. */ +static BROTLI_INLINE void BrotliFillBitWindow( + BrotliBitReader* const br, brotli_reg_t n_bits) { +#if (BROTLI_64_BITS) + if (BROTLI_UNALIGNED_READ_FAST && BROTLI_IS_CONSTANT(n_bits) && + (n_bits <= 8)) { + brotli_reg_t bit_pos = br->bit_pos_; + if (bit_pos <= 8) { + br->val_ = BrotliBitReaderLoadBits(br->val_, + BROTLI_UNALIGNED_LOAD64LE(br->next_in), 56, bit_pos); + br->bit_pos_ = bit_pos + 56; + br->next_in += 7; + } + } else if (BROTLI_UNALIGNED_READ_FAST && BROTLI_IS_CONSTANT(n_bits) && + (n_bits <= 16)) { + brotli_reg_t bit_pos = br->bit_pos_; + if (bit_pos <= 16) { + br->val_ = BrotliBitReaderLoadBits(br->val_, + BROTLI_UNALIGNED_LOAD64LE(br->next_in), 48, bit_pos); + br->bit_pos_ = bit_pos + 48; + br->next_in += 6; + } + } else { + brotli_reg_t bit_pos = br->bit_pos_; + if (bit_pos <= 32) { + br->val_ = BrotliBitReaderLoadBits(br->val_, + (uint64_t)BROTLI_UNALIGNED_LOAD32LE(br->next_in), 32, bit_pos); + br->bit_pos_ = bit_pos + 32; + br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ; + } + } +#else + if (BROTLI_UNALIGNED_READ_FAST && BROTLI_IS_CONSTANT(n_bits) && + (n_bits <= 8)) { + brotli_reg_t bit_pos = br->bit_pos_; + if (bit_pos <= 8) { + br->val_ = BrotliBitReaderLoadBits(br->val_, + BROTLI_UNALIGNED_LOAD32LE(br->next_in), 24, bit_pos); + br->bit_pos_ = bit_pos + 24; + br->next_in += 3; + } + } else { + brotli_reg_t bit_pos = br->bit_pos_; + if (bit_pos <= 16) { + br->val_ = BrotliBitReaderLoadBits(br->val_, + (uint32_t)BROTLI_UNALIGNED_LOAD16LE(br->next_in), 16, bit_pos); + br->bit_pos_ = bit_pos + 16; + br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ; + } + } +#endif +} + +/* Mostly like BrotliFillBitWindow, but guarantees only 16 bits and reads no + more than BROTLI_SHORT_FILL_BIT_WINDOW_READ bytes of input. */ +static BROTLI_INLINE void BrotliFillBitWindow16(BrotliBitReader* const br) { + BrotliFillBitWindow(br, 17); +} + +/* Tries to pull one byte of input to accumulator. + Returns BROTLI_FALSE if there is no input available. */ +static BROTLI_INLINE BROTLI_BOOL BrotliPullByte(BrotliBitReader* const br) { + if (br->next_in == br->last_in) { + return BROTLI_FALSE; + } + br->val_ = BrotliBitReaderLoadBits(br->val_, + (brotli_reg_t)*br->next_in, 8, br->bit_pos_); + br->bit_pos_ += 8; + ++br->next_in; + return BROTLI_TRUE; +} + +/* Returns currently available bits. + The number of valid bits could be calculated by BrotliGetAvailableBits. */ +static BROTLI_INLINE brotli_reg_t BrotliGetBitsUnmasked( + BrotliBitReader* const br) { + return br->val_; +} + +/* Like BrotliGetBits, but does not mask the result. + The result contains at least 16 valid bits. */ +static BROTLI_INLINE brotli_reg_t BrotliGet16BitsUnmasked( + BrotliBitReader* const br) { + BrotliFillBitWindow(br, 16); + return (brotli_reg_t)BrotliGetBitsUnmasked(br); +} + +/* Returns the specified number of bits from |br| without advancing bit + position. */ +static BROTLI_INLINE brotli_reg_t BrotliGetBits( + BrotliBitReader* const br, brotli_reg_t n_bits) { + BrotliFillBitWindow(br, n_bits); + return BrotliGetBitsUnmasked(br) & BitMask(n_bits); +} + +/* Tries to peek the specified amount of bits. Returns BROTLI_FALSE, if there + is not enough input. */ +static BROTLI_INLINE BROTLI_BOOL BrotliSafeGetBits( + BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { + while (BrotliGetAvailableBits(br) < n_bits) { + if (!BrotliPullByte(br)) { + return BROTLI_FALSE; + } + } + *val = BrotliGetBitsUnmasked(br) & BitMask(n_bits); + return BROTLI_TRUE; +} + +/* Advances the bit pos by |n_bits|. */ +static BROTLI_INLINE void BrotliDropBits( + BrotliBitReader* const br, brotli_reg_t n_bits) { + br->bit_pos_ -= n_bits; + br->val_ >>= n_bits; +} + +/* Make sure that there are no spectre bits in accumulator. + This is important for the cases when some bytes are skipped + (i.e. never placed into accumulator). */ +static BROTLI_INLINE void BrotliBitReaderNormalize(BrotliBitReader* br) { + /* Actually, it is enough to normalize when br->bit_pos_ == 0 */ + if (br->bit_pos_ < (sizeof(brotli_reg_t) << 3u)) { + br->val_ &= (((brotli_reg_t)1) << br->bit_pos_) - 1; + } +} + +static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) { + brotli_reg_t unused_bytes = BrotliGetAvailableBits(br) >> 3; + brotli_reg_t unused_bits = unused_bytes << 3; + br->next_in = + (unused_bytes == 0) ? br->next_in : (br->next_in - unused_bytes); + br->bit_pos_ -= unused_bits; + BrotliBitReaderNormalize(br); +} + +/* Reads the specified number of bits from |br| and advances the bit pos. + Precondition: accumulator MUST contain at least |n_bits|. */ +static BROTLI_INLINE void BrotliTakeBits(BrotliBitReader* const br, + brotli_reg_t n_bits, + brotli_reg_t* val) { + *val = BrotliGetBitsUnmasked(br) & BitMask(n_bits); + BROTLI_LOG(("[BrotliTakeBits] %d %d %d val: %6x\n", + (int)BrotliBitReaderGetAvailIn(br), (int)br->bit_pos_, + (int)n_bits, (int)*val)); + BrotliDropBits(br, n_bits); +} + +/* Reads the specified number of bits from |br| and advances the bit pos. + Assumes that there is enough input to perform BrotliFillBitWindow. + Up to 24 bits are allowed to be requested from this method. */ +static BROTLI_INLINE brotli_reg_t BrotliReadBits24( + BrotliBitReader* const br, brotli_reg_t n_bits) { + BROTLI_DCHECK(n_bits <= 24); + if (BROTLI_64_BITS || (n_bits <= 16)) { + brotli_reg_t val; + BrotliFillBitWindow(br, n_bits); + BrotliTakeBits(br, n_bits, &val); + return val; + } else { + brotli_reg_t low_val; + brotli_reg_t high_val; + BrotliFillBitWindow(br, 16); + BrotliTakeBits(br, 16, &low_val); + BrotliFillBitWindow(br, 8); + BrotliTakeBits(br, n_bits - 16, &high_val); + return low_val | (high_val << 16); + } +} + +/* Same as BrotliReadBits24, but allows reading up to 32 bits. */ +static BROTLI_INLINE brotli_reg_t BrotliReadBits32( + BrotliBitReader* const br, brotli_reg_t n_bits) { + BROTLI_DCHECK(n_bits <= 32); + if (BROTLI_64_BITS || (n_bits <= 16)) { + brotli_reg_t val; + BrotliFillBitWindow(br, n_bits); + BrotliTakeBits(br, n_bits, &val); + return val; + } else { + brotli_reg_t low_val; + brotli_reg_t high_val; + BrotliFillBitWindow(br, 16); + BrotliTakeBits(br, 16, &low_val); + BrotliFillBitWindow(br, 16); + BrotliTakeBits(br, n_bits - 16, &high_val); + return low_val | (high_val << 16); + } +} + +/* Tries to read the specified amount of bits. Returns BROTLI_FALSE, if there + is not enough input. |n_bits| MUST be positive. + Up to 24 bits are allowed to be requested from this method. */ +static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits( + BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { + BROTLI_DCHECK(n_bits <= 24); + while (BrotliGetAvailableBits(br) < n_bits) { + if (!BrotliPullByte(br)) { + return BROTLI_FALSE; + } + } + BrotliTakeBits(br, n_bits, val); + return BROTLI_TRUE; +} + +/* Same as BrotliSafeReadBits, but allows reading up to 32 bits. */ +static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits32( + BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { + BROTLI_DCHECK(n_bits <= 32); + if (BROTLI_64_BITS || (n_bits <= 24)) { + while (BrotliGetAvailableBits(br) < n_bits) { + if (!BrotliPullByte(br)) { + return BROTLI_FALSE; + } + } + BrotliTakeBits(br, n_bits, val); + return BROTLI_TRUE; + } else { + return BrotliSafeReadBits32Slow(br, n_bits, val); + } +} + +/* Advances the bit reader position to the next byte boundary and verifies + that any skipped bits are set to zero. */ +static BROTLI_INLINE BROTLI_BOOL BrotliJumpToByteBoundary(BrotliBitReader* br) { + brotli_reg_t pad_bits_count = BrotliGetAvailableBits(br) & 0x7; + brotli_reg_t pad_bits = 0; + if (pad_bits_count != 0) { + BrotliTakeBits(br, pad_bits_count, &pad_bits); + } + BrotliBitReaderNormalize(br); + return TO_BROTLI_BOOL(pad_bits == 0); +} + +static BROTLI_INLINE void BrotliDropBytes(BrotliBitReader* br, size_t num) { + /* Check detour is legal: accumulator must to be empty. */ + BROTLI_DCHECK(br->bit_pos_ == 0); + BROTLI_DCHECK(br->val_ == 0); + br->next_in += num; +} + +/* Copies remaining input bytes stored in the bit reader to the output. Value + |num| may not be larger than BrotliGetRemainingBytes. The bit reader must be + warmed up again after this. */ +static BROTLI_INLINE void BrotliCopyBytes(uint8_t* dest, + BrotliBitReader* br, size_t num) { + while (BrotliGetAvailableBits(br) >= 8 && num > 0) { + *dest = (uint8_t)BrotliGetBitsUnmasked(br); + BrotliDropBits(br, 8); + ++dest; + --num; + } + BrotliBitReaderNormalize(br); + if (num > 0) { + memcpy(dest, br->next_in, num); + BrotliDropBytes(br, num); + } +} + +BROTLI_UNUSED_FUNCTION void BrotliBitReaderSuppressUnusedFunctions(void) { + BROTLI_UNUSED(&BrotliBitReaderSuppressUnusedFunctions); + + BROTLI_UNUSED(&BrotliBitReaderGetAvailIn); + BROTLI_UNUSED(&BrotliBitReaderLoadBits); + BROTLI_UNUSED(&BrotliBitReaderRestoreState); + BROTLI_UNUSED(&BrotliBitReaderSaveState); + BROTLI_UNUSED(&BrotliBitReaderSetInput); + BROTLI_UNUSED(&BrotliBitReaderUnload); + BROTLI_UNUSED(&BrotliCheckInputAmount); + BROTLI_UNUSED(&BrotliCopyBytes); + BROTLI_UNUSED(&BrotliFillBitWindow16); + BROTLI_UNUSED(&BrotliGet16BitsUnmasked); + BROTLI_UNUSED(&BrotliGetBits); + BROTLI_UNUSED(&BrotliGetRemainingBytes); + BROTLI_UNUSED(&BrotliJumpToByteBoundary); + BROTLI_UNUSED(&BrotliReadBits24); + BROTLI_UNUSED(&BrotliReadBits32); + BROTLI_UNUSED(&BrotliSafeGetBits); + BROTLI_UNUSED(&BrotliSafeReadBits); + BROTLI_UNUSED(&BrotliSafeReadBits32); +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_DEC_BIT_READER_H_ */ diff --git a/packages/runtime/vendor/brotli/c/dec/decode.c b/packages/runtime/vendor/brotli/c/dec/decode.c new file mode 100644 index 000000000..e87e7f5ea --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/decode.c @@ -0,0 +1,2964 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include + +#include "../common/constants.h" +#include "../common/context.h" +#include "../common/dictionary.h" +#include "../common/platform.h" +#include "../common/shared_dictionary_internal.h" +#include "../common/transform.h" +#include "../common/version.h" +#include "bit_reader.h" +#include "huffman.h" +#include "prefix.h" +#include "state.h" +#include "static_init.h" + +#if defined(BROTLI_TARGET_NEON) +#include +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define BROTLI_FAILURE(CODE) (BROTLI_DUMP(), CODE) + +#define BROTLI_LOG_UINT(name) \ + BROTLI_LOG(("[%s] %s = %lu\n", __func__, #name, (unsigned long)(name))) +#define BROTLI_LOG_ARRAY_INDEX(array_name, idx) \ + BROTLI_LOG(("[%s] %s[%lu] = %lu\n", __func__, #array_name, \ + (unsigned long)(idx), (unsigned long)array_name[idx])) + +#define HUFFMAN_TABLE_BITS 8U +#define HUFFMAN_TABLE_MASK 0xFF + +/* We need the slack region for the following reasons: + - doing up to two 16-byte copies for fast backward copying + - inserting transformed dictionary word: + 255 prefix + 32 base + 255 suffix */ +static const brotli_reg_t kRingBufferWriteAheadSlack = 542; + +static const BROTLI_MODEL("small") +uint8_t kCodeLengthCodeOrder[BROTLI_CODE_LENGTH_CODES] = { + 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15, +}; + +/* Static prefix code for the complex code length code lengths. */ +static const BROTLI_MODEL("small") +uint8_t kCodeLengthPrefixLength[16] = { + 2, 2, 2, 3, 2, 2, 2, 4, 2, 2, 2, 3, 2, 2, 2, 4, +}; + +static const BROTLI_MODEL("small") +uint8_t kCodeLengthPrefixValue[16] = { + 0, 4, 3, 2, 0, 4, 3, 1, 0, 4, 3, 2, 0, 4, 3, 5, +}; + +BROTLI_BOOL BrotliDecoderSetParameter( + BrotliDecoderState* state, BrotliDecoderParameter p, uint32_t value) { + if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE; + switch (p) { + case BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION: + state->canny_ringbuffer_allocation = !!value ? 0 : 1; + return BROTLI_TRUE; + + case BROTLI_DECODER_PARAM_LARGE_WINDOW: + state->large_window = TO_BROTLI_BOOL(!!value); + return BROTLI_TRUE; + + default: return BROTLI_FALSE; + } +} + +BrotliDecoderState* BrotliDecoderCreateInstance( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + BrotliDecoderState* state = 0; + if (!BrotliDecoderEnsureStaticInit()) { + BROTLI_DUMP(); + return 0; + } + if (!alloc_func && !free_func) { + state = (BrotliDecoderState*)malloc(sizeof(BrotliDecoderState)); + } else if (alloc_func && free_func) { + state = (BrotliDecoderState*)alloc_func(opaque, sizeof(BrotliDecoderState)); + } + if (state == 0) { + BROTLI_DUMP(); + return 0; + } + if (!BrotliDecoderStateInit(state, alloc_func, free_func, opaque)) { + BROTLI_DUMP(); + if (!alloc_func && !free_func) { + free(state); + } else if (alloc_func && free_func) { + free_func(opaque, state); + } + return 0; + } + return state; +} + +/* Deinitializes and frees BrotliDecoderState instance. */ +void BrotliDecoderDestroyInstance(BrotliDecoderState* state) { + if (!state) { + return; + } else { + brotli_free_func free_func = state->free_func; + void* opaque = state->memory_manager_opaque; + BrotliDecoderStateCleanup(state); + free_func(opaque, state); + } +} + +/* Saves error code and converts it to BrotliDecoderResult. */ +static BROTLI_NOINLINE BrotliDecoderResult SaveErrorCode( + BrotliDecoderState* s, BrotliDecoderErrorCode e, size_t consumed_input) { + s->error_code = (int)e; + s->used_input += consumed_input; + if ((s->buffer_length != 0) && (s->br.next_in == s->br.last_in)) { + /* If internal buffer is depleted at last, reset it. */ + s->buffer_length = 0; + } + switch (e) { + case BROTLI_DECODER_SUCCESS: + return BROTLI_DECODER_RESULT_SUCCESS; + + case BROTLI_DECODER_NEEDS_MORE_INPUT: + return BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; + + case BROTLI_DECODER_NEEDS_MORE_OUTPUT: + return BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + default: + return BROTLI_DECODER_RESULT_ERROR; + } +} + +/* Decodes WBITS by reading 1 - 7 bits, or 0x11 for "Large Window Brotli". + Precondition: bit-reader accumulator has at least 8 bits. */ +static BrotliDecoderErrorCode DecodeWindowBits(BrotliDecoderState* s, + BrotliBitReader* br) { + brotli_reg_t n; + BROTLI_BOOL large_window = s->large_window; + s->large_window = BROTLI_FALSE; + BrotliTakeBits(br, 1, &n); + if (n == 0) { + s->window_bits = 16; + return BROTLI_DECODER_SUCCESS; + } + BrotliTakeBits(br, 3, &n); + if (n != 0) { + s->window_bits = (17u + n) & 63u; + return BROTLI_DECODER_SUCCESS; + } + BrotliTakeBits(br, 3, &n); + if (n == 1) { + if (large_window) { + BrotliTakeBits(br, 1, &n); + if (n == 1) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS); + } + s->large_window = BROTLI_TRUE; + return BROTLI_DECODER_SUCCESS; + } else { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS); + } + } + if (n != 0) { + s->window_bits = (8u + n) & 63u; + return BROTLI_DECODER_SUCCESS; + } + s->window_bits = 17; + return BROTLI_DECODER_SUCCESS; +} + +static BROTLI_INLINE void memmove16(uint8_t* dst, uint8_t* src) { +#if defined(BROTLI_TARGET_NEON) + vst1q_u8(dst, vld1q_u8(src)); +#else + uint32_t buffer[4]; + memcpy(buffer, src, 16); + memcpy(dst, buffer, 16); +#endif +} + +/* Decodes a number in the range [0..255], by reading 1 - 11 bits. */ +static BROTLI_NOINLINE BrotliDecoderErrorCode DecodeVarLenUint8( + BrotliDecoderState* s, BrotliBitReader* br, brotli_reg_t* value) { + brotli_reg_t bits; + switch (s->substate_decode_uint8) { + case BROTLI_STATE_DECODE_UINT8_NONE: + if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, 1, &bits))) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (bits == 0) { + *value = 0; + return BROTLI_DECODER_SUCCESS; + } + /* Fall through. */ + + case BROTLI_STATE_DECODE_UINT8_SHORT: + if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, 3, &bits))) { + s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_SHORT; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (bits == 0) { + *value = 1; + s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE; + return BROTLI_DECODER_SUCCESS; + } + /* Use output value as a temporary storage. It MUST be persisted. */ + *value = bits; + /* Fall through. */ + + case BROTLI_STATE_DECODE_UINT8_LONG: + if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, *value, &bits))) { + s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_LONG; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + *value = ((brotli_reg_t)1U << *value) + bits; + s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE; + return BROTLI_DECODER_SUCCESS; + + default: + return + BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ + } +} + +/* Decodes a metablock length and flags by reading 2 - 31 bits. */ +static BrotliDecoderErrorCode BROTLI_NOINLINE DecodeMetaBlockLength( + BrotliDecoderState* s, BrotliBitReader* br) { + brotli_reg_t bits; + int i; + for (;;) { + switch (s->substate_metablock_header) { + case BROTLI_STATE_METABLOCK_HEADER_NONE: + if (!BrotliSafeReadBits(br, 1, &bits)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + s->is_last_metablock = bits ? 1 : 0; + s->meta_block_remaining_len = 0; + s->is_uncompressed = 0; + s->is_metadata = 0; + if (!s->is_last_metablock) { + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NIBBLES; + break; + } + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_EMPTY; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_HEADER_EMPTY: + if (!BrotliSafeReadBits(br, 1, &bits)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (bits) { + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; + return BROTLI_DECODER_SUCCESS; + } + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NIBBLES; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_HEADER_NIBBLES: + if (!BrotliSafeReadBits(br, 2, &bits)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + s->size_nibbles = (uint8_t)(bits + 4); + s->loop_counter = 0; + if (bits == 3) { + s->is_metadata = 1; + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_RESERVED; + break; + } + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_SIZE; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_HEADER_SIZE: + i = s->loop_counter; + for (; i < (int)s->size_nibbles; ++i) { + if (!BrotliSafeReadBits(br, 4, &bits)) { + s->loop_counter = i; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (i + 1 == (int)s->size_nibbles && s->size_nibbles > 4 && + bits == 0) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE); + } + s->meta_block_remaining_len |= (int)(bits << (i * 4)); + } + s->substate_metablock_header = + BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED: + if (!s->is_last_metablock) { + if (!BrotliSafeReadBits(br, 1, &bits)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + s->is_uncompressed = bits ? 1 : 0; + } + ++s->meta_block_remaining_len; + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; + return BROTLI_DECODER_SUCCESS; + + case BROTLI_STATE_METABLOCK_HEADER_RESERVED: + if (!BrotliSafeReadBits(br, 1, &bits)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (bits != 0) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_RESERVED); + } + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_BYTES; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_HEADER_BYTES: + if (!BrotliSafeReadBits(br, 2, &bits)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (bits == 0) { + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; + return BROTLI_DECODER_SUCCESS; + } + s->size_nibbles = (uint8_t)bits; + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_METADATA; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_HEADER_METADATA: + i = s->loop_counter; + for (; i < (int)s->size_nibbles; ++i) { + if (!BrotliSafeReadBits(br, 8, &bits)) { + s->loop_counter = i; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (i + 1 == (int)s->size_nibbles && s->size_nibbles > 1 && + bits == 0) { + return BROTLI_FAILURE( + BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE); + } + s->meta_block_remaining_len |= (int)(bits << (i * 8)); + } + ++s->meta_block_remaining_len; + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; + return BROTLI_DECODER_SUCCESS; + + default: + return + BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ + } + } +} + +/* Decodes the Huffman code. + This method doesn't read data from the bit reader, BUT drops the amount of + bits that correspond to the decoded symbol. + bits MUST contain at least 15 (BROTLI_HUFFMAN_MAX_CODE_LENGTH) valid bits. */ +static BROTLI_INLINE brotli_reg_t DecodeSymbol(brotli_reg_t bits, + const HuffmanCode* table, + BrotliBitReader* br) { + BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); + BROTLI_HC_ADJUST_TABLE_INDEX(table, bits & HUFFMAN_TABLE_MASK); + if (BROTLI_HC_FAST_LOAD_BITS(table) > HUFFMAN_TABLE_BITS) { + brotli_reg_t nbits = BROTLI_HC_FAST_LOAD_BITS(table) - HUFFMAN_TABLE_BITS; + BrotliDropBits(br, HUFFMAN_TABLE_BITS); + BROTLI_HC_ADJUST_TABLE_INDEX(table, + BROTLI_HC_FAST_LOAD_VALUE(table) + + ((bits >> HUFFMAN_TABLE_BITS) & BitMask(nbits))); + } + BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(table)); + return BROTLI_HC_FAST_LOAD_VALUE(table); +} + +/* Reads and decodes the next Huffman code from bit-stream. + This method peeks 16 bits of input and drops 0 - 15 of them. */ +static BROTLI_INLINE brotli_reg_t ReadSymbol(const HuffmanCode* table, + BrotliBitReader* br) { + return DecodeSymbol(BrotliGet16BitsUnmasked(br), table, br); +} + +/* Same as DecodeSymbol, but it is known that there is less than 15 bits of + input are currently available. */ +static BROTLI_NOINLINE BROTLI_BOOL SafeDecodeSymbol( + const HuffmanCode* table, BrotliBitReader* br, brotli_reg_t* result) { + brotli_reg_t val; + brotli_reg_t available_bits = BrotliGetAvailableBits(br); + BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); + if (available_bits == 0) { + if (BROTLI_HC_FAST_LOAD_BITS(table) == 0) { + *result = BROTLI_HC_FAST_LOAD_VALUE(table); + return BROTLI_TRUE; + } + return BROTLI_FALSE; /* No valid bits at all. */ + } + val = BrotliGetBitsUnmasked(br); + BROTLI_HC_ADJUST_TABLE_INDEX(table, val & HUFFMAN_TABLE_MASK); + if (BROTLI_HC_FAST_LOAD_BITS(table) <= HUFFMAN_TABLE_BITS) { + if (BROTLI_HC_FAST_LOAD_BITS(table) <= available_bits) { + BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(table)); + *result = BROTLI_HC_FAST_LOAD_VALUE(table); + return BROTLI_TRUE; + } else { + return BROTLI_FALSE; /* Not enough bits for the first level. */ + } + } + if (available_bits <= HUFFMAN_TABLE_BITS) { + return BROTLI_FALSE; /* Not enough bits to move to the second level. */ + } + + /* Speculatively drop HUFFMAN_TABLE_BITS. */ + val = (val & BitMask(BROTLI_HC_FAST_LOAD_BITS(table))) >> HUFFMAN_TABLE_BITS; + available_bits -= HUFFMAN_TABLE_BITS; + BROTLI_HC_ADJUST_TABLE_INDEX(table, BROTLI_HC_FAST_LOAD_VALUE(table) + val); + if (available_bits < BROTLI_HC_FAST_LOAD_BITS(table)) { + return BROTLI_FALSE; /* Not enough bits for the second level. */ + } + + BrotliDropBits(br, HUFFMAN_TABLE_BITS + BROTLI_HC_FAST_LOAD_BITS(table)); + *result = BROTLI_HC_FAST_LOAD_VALUE(table); + return BROTLI_TRUE; +} + +static BROTLI_INLINE BROTLI_BOOL SafeReadSymbol( + const HuffmanCode* table, BrotliBitReader* br, brotli_reg_t* result) { + brotli_reg_t val; + if (BROTLI_PREDICT_TRUE(BrotliSafeGetBits(br, 15, &val))) { + *result = DecodeSymbol(val, table, br); + return BROTLI_TRUE; + } + return SafeDecodeSymbol(table, br, result); +} + +/* Makes a look-up in first level Huffman table. Peeks 8 bits. */ +static BROTLI_INLINE void PreloadSymbol(int safe, + const HuffmanCode* table, + BrotliBitReader* br, + brotli_reg_t* bits, + brotli_reg_t* value) { + if (safe) { + return; + } + BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); + BROTLI_HC_ADJUST_TABLE_INDEX(table, BrotliGetBits(br, HUFFMAN_TABLE_BITS)); + *bits = BROTLI_HC_FAST_LOAD_BITS(table); + *value = BROTLI_HC_FAST_LOAD_VALUE(table); +} + +/* Decodes the next Huffman code using data prepared by PreloadSymbol. + Reads 0 - 15 bits. Also peeks 8 following bits. */ +static BROTLI_INLINE brotli_reg_t ReadPreloadedSymbol(const HuffmanCode* table, + BrotliBitReader* br, + brotli_reg_t* bits, + brotli_reg_t* value) { + brotli_reg_t result = *value; + if (BROTLI_PREDICT_FALSE(*bits > HUFFMAN_TABLE_BITS)) { + brotli_reg_t val = BrotliGet16BitsUnmasked(br); + const HuffmanCode* ext = table + (val & HUFFMAN_TABLE_MASK) + *value; + brotli_reg_t mask = BitMask((*bits - HUFFMAN_TABLE_BITS)); + BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(ext); + BrotliDropBits(br, HUFFMAN_TABLE_BITS); + BROTLI_HC_ADJUST_TABLE_INDEX(ext, (val >> HUFFMAN_TABLE_BITS) & mask); + BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(ext)); + result = BROTLI_HC_FAST_LOAD_VALUE(ext); + } else { + BrotliDropBits(br, *bits); + } + PreloadSymbol(0, table, br, bits, value); + return result; +} + +/* Reads up to limit symbols from br and copies them into ringbuffer, + starting from pos. Caller must ensure that there is enough space + for the write. Returns the amount of symbols actually copied. */ +static BROTLI_INLINE int BrotliCopyPreloadedSymbolsToU8(const HuffmanCode* table, + BrotliBitReader* br, + brotli_reg_t* bits, + brotli_reg_t* value, + uint8_t* ringbuffer, + int pos, + const int limit) { + /* Calculate range where CheckInputAmount is always true. + Start with the number of bytes we can read. */ + int64_t new_lim = br->guard_in - br->next_in; + /* Convert to bits, since symbols use variable number of bits. */ + new_lim *= 8; + /* At most 15 bits per symbol, so this is safe. */ + new_lim /= 15; + const int kMaximalOverread = 4; + int pos_limit = limit; + int copies = 0; + if ((new_lim - kMaximalOverread) <= limit) { + // Safe cast, since new_lim is already < num_steps + pos_limit = (int)(new_lim - kMaximalOverread); + } + if (pos_limit < 0) { + pos_limit = 0; + } + copies = pos_limit; + pos_limit += pos; + /* Fast path, caller made sure it is safe to write, + we verified that is is safe to read. */ + for (; pos < pos_limit; pos++) { + BROTLI_DCHECK(BrotliCheckInputAmount(br)); + ringbuffer[pos] = (uint8_t)ReadPreloadedSymbol(table, br, bits, value); + BROTLI_LOG_ARRAY_INDEX(ringbuffer, pos); + } + /* Do the remainder, caller made sure it is safe to write, + we need to bverify that it is safe to read. */ + while (BrotliCheckInputAmount(br) && copies < limit) { + ringbuffer[pos] = (uint8_t)ReadPreloadedSymbol(table, br, bits, value); + BROTLI_LOG_ARRAY_INDEX(ringbuffer, pos); + pos++; + copies++; + } + return copies; +} + +static BROTLI_INLINE brotli_reg_t Log2Floor(brotli_reg_t x) { + brotli_reg_t result = 0; + while (x) { + x >>= 1; + ++result; + } + return result; +} + +/* Reads (s->symbol + 1) symbols. + Totally 1..4 symbols are read, 1..11 bits each. + The list of symbols MUST NOT contain duplicates. */ +static BrotliDecoderErrorCode ReadSimpleHuffmanSymbols( + brotli_reg_t alphabet_size_max, brotli_reg_t alphabet_size_limit, + BrotliDecoderState* s) { + /* max_bits == 1..11; symbol == 0..3; 1..44 bits will be read. */ + BrotliBitReader* br = &s->br; + BrotliMetablockHeaderArena* h = &s->arena.header; + brotli_reg_t max_bits = Log2Floor(alphabet_size_max - 1); + brotli_reg_t i = h->sub_loop_counter; + brotli_reg_t num_symbols = h->symbol; + while (i <= num_symbols) { + brotli_reg_t v; + if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, max_bits, &v))) { + h->sub_loop_counter = i; + h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_READ; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (v >= alphabet_size_limit) { + return + BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET); + } + h->symbols_lists_array[i] = (uint16_t)v; + BROTLI_LOG_UINT(h->symbols_lists_array[i]); + ++i; + } + + for (i = 0; i < num_symbols; ++i) { + brotli_reg_t k = i + 1; + for (; k <= num_symbols; ++k) { + if (h->symbols_lists_array[i] == h->symbols_lists_array[k]) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME); + } + } + } + + return BROTLI_DECODER_SUCCESS; +} + +/* Process single decoded symbol code length: + A) reset the repeat variable + B) remember code length (if it is not 0) + C) extend corresponding index-chain + D) reduce the Huffman space + E) update the histogram */ +static BROTLI_INLINE void ProcessSingleCodeLength(brotli_reg_t code_len, + brotli_reg_t* symbol, brotli_reg_t* repeat, brotli_reg_t* space, + brotli_reg_t* prev_code_len, uint16_t* symbol_lists, + uint16_t* code_length_histo, int* next_symbol) { + *repeat = 0; + if (code_len != 0) { /* code_len == 1..15 */ + symbol_lists[next_symbol[code_len]] = (uint16_t)(*symbol); + next_symbol[code_len] = (int)(*symbol); + *prev_code_len = code_len; + *space -= 32768U >> code_len; + code_length_histo[code_len]++; + BROTLI_LOG(("[ReadHuffmanCode] code_length[%d] = %d\n", + (int)*symbol, (int)code_len)); + } + (*symbol)++; +} + +/* Process repeated symbol code length. + A) Check if it is the extension of previous repeat sequence; if the decoded + value is not BROTLI_REPEAT_PREVIOUS_CODE_LENGTH, then it is a new + symbol-skip + B) Update repeat variable + C) Check if operation is feasible (fits alphabet) + D) For each symbol do the same operations as in ProcessSingleCodeLength + + PRECONDITION: code_len == BROTLI_REPEAT_PREVIOUS_CODE_LENGTH or + code_len == BROTLI_REPEAT_ZERO_CODE_LENGTH */ +static BROTLI_INLINE void ProcessRepeatedCodeLength(brotli_reg_t code_len, + brotli_reg_t repeat_delta, brotli_reg_t alphabet_size, brotli_reg_t* symbol, + brotli_reg_t* repeat, brotli_reg_t* space, brotli_reg_t* prev_code_len, + brotli_reg_t* repeat_code_len, uint16_t* symbol_lists, + uint16_t* code_length_histo, int* next_symbol) { + brotli_reg_t old_repeat; + brotli_reg_t extra_bits = 3; /* for BROTLI_REPEAT_ZERO_CODE_LENGTH */ + brotli_reg_t new_len = 0; /* for BROTLI_REPEAT_ZERO_CODE_LENGTH */ + if (code_len == BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) { + new_len = *prev_code_len; + extra_bits = 2; + } + if (*repeat_code_len != new_len) { + *repeat = 0; + *repeat_code_len = new_len; + } + old_repeat = *repeat; + if (*repeat > 0) { + *repeat -= 2; + *repeat <<= extra_bits; + } + *repeat += repeat_delta + 3U; + repeat_delta = *repeat - old_repeat; + if (*symbol + repeat_delta > alphabet_size) { + BROTLI_DUMP(); + *symbol = alphabet_size; + *space = 0xFFFFF; + return; + } + BROTLI_LOG(("[ReadHuffmanCode] code_length[%d..%d] = %d\n", + (int)*symbol, (int)(*symbol + repeat_delta - 1), (int)*repeat_code_len)); + if (*repeat_code_len != 0) { + brotli_reg_t last = *symbol + repeat_delta; + int next = next_symbol[*repeat_code_len]; + do { + symbol_lists[next] = (uint16_t)*symbol; + next = (int)*symbol; + } while (++(*symbol) != last); + next_symbol[*repeat_code_len] = next; + *space -= repeat_delta << (15 - *repeat_code_len); + code_length_histo[*repeat_code_len] = + (uint16_t)(code_length_histo[*repeat_code_len] + repeat_delta); + } else { + *symbol += repeat_delta; + } +} + +/* Reads and decodes symbol codelengths. */ +static BrotliDecoderErrorCode ReadSymbolCodeLengths( + brotli_reg_t alphabet_size, BrotliDecoderState* s) { + BrotliBitReader* br = &s->br; + BrotliMetablockHeaderArena* h = &s->arena.header; + brotli_reg_t symbol = h->symbol; + brotli_reg_t repeat = h->repeat; + brotli_reg_t space = h->space; + brotli_reg_t prev_code_len = h->prev_code_len; + brotli_reg_t repeat_code_len = h->repeat_code_len; + uint16_t* symbol_lists = h->symbol_lists; + uint16_t* code_length_histo = h->code_length_histo; + int* next_symbol = h->next_symbol; + if (!BrotliWarmupBitReader(br)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + while (symbol < alphabet_size && space > 0) { + const HuffmanCode* p = h->table; + brotli_reg_t code_len; + BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(p); + if (!BrotliCheckInputAmount(br)) { + h->symbol = symbol; + h->repeat = repeat; + h->prev_code_len = prev_code_len; + h->repeat_code_len = repeat_code_len; + h->space = space; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + BrotliFillBitWindow16(br); + BROTLI_HC_ADJUST_TABLE_INDEX(p, BrotliGetBitsUnmasked(br) & + BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH)); + BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p)); /* Use 1..5 bits. */ + code_len = BROTLI_HC_FAST_LOAD_VALUE(p); /* code_len == 0..17 */ + if (code_len < BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) { + ProcessSingleCodeLength(code_len, &symbol, &repeat, &space, + &prev_code_len, symbol_lists, code_length_histo, next_symbol); + } else { /* code_len == 16..17, extra_bits == 2..3 */ + brotli_reg_t extra_bits = + (code_len == BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) ? 2 : 3; + brotli_reg_t repeat_delta = + BrotliGetBitsUnmasked(br) & BitMask(extra_bits); + BrotliDropBits(br, extra_bits); + ProcessRepeatedCodeLength(code_len, repeat_delta, alphabet_size, + &symbol, &repeat, &space, &prev_code_len, &repeat_code_len, + symbol_lists, code_length_histo, next_symbol); + } + } + h->space = space; + return BROTLI_DECODER_SUCCESS; +} + +static BrotliDecoderErrorCode SafeReadSymbolCodeLengths( + brotli_reg_t alphabet_size, BrotliDecoderState* s) { + BrotliBitReader* br = &s->br; + BrotliMetablockHeaderArena* h = &s->arena.header; + BROTLI_BOOL get_byte = BROTLI_FALSE; + while (h->symbol < alphabet_size && h->space > 0) { + const HuffmanCode* p = h->table; + brotli_reg_t code_len; + brotli_reg_t available_bits; + brotli_reg_t bits = 0; + BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(p); + if (get_byte && !BrotliPullByte(br)) return BROTLI_DECODER_NEEDS_MORE_INPUT; + get_byte = BROTLI_FALSE; + available_bits = BrotliGetAvailableBits(br); + if (available_bits != 0) { + bits = (uint32_t)BrotliGetBitsUnmasked(br); + } + BROTLI_HC_ADJUST_TABLE_INDEX(p, + bits & BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH)); + if (BROTLI_HC_FAST_LOAD_BITS(p) > available_bits) { + get_byte = BROTLI_TRUE; + continue; + } + code_len = BROTLI_HC_FAST_LOAD_VALUE(p); /* code_len == 0..17 */ + if (code_len < BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) { + BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p)); + ProcessSingleCodeLength(code_len, &h->symbol, &h->repeat, &h->space, + &h->prev_code_len, h->symbol_lists, h->code_length_histo, + h->next_symbol); + } else { /* code_len == 16..17, extra_bits == 2..3 */ + brotli_reg_t extra_bits = code_len - 14U; + brotli_reg_t repeat_delta = (bits >> BROTLI_HC_FAST_LOAD_BITS(p)) & + BitMask(extra_bits); + if (available_bits < BROTLI_HC_FAST_LOAD_BITS(p) + extra_bits) { + get_byte = BROTLI_TRUE; + continue; + } + BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p) + extra_bits); + ProcessRepeatedCodeLength(code_len, repeat_delta, alphabet_size, + &h->symbol, &h->repeat, &h->space, &h->prev_code_len, + &h->repeat_code_len, h->symbol_lists, h->code_length_histo, + h->next_symbol); + } + } + return BROTLI_DECODER_SUCCESS; +} + +/* Reads and decodes 15..18 codes using static prefix code. + Each code is 2..4 bits long. In total 30..72 bits are used. */ +static BrotliDecoderErrorCode ReadCodeLengthCodeLengths(BrotliDecoderState* s) { + BrotliBitReader* br = &s->br; + BrotliMetablockHeaderArena* h = &s->arena.header; + brotli_reg_t num_codes = h->repeat; + brotli_reg_t space = h->space; + brotli_reg_t i = h->sub_loop_counter; + for (; i < BROTLI_CODE_LENGTH_CODES; ++i) { + const uint8_t code_len_idx = kCodeLengthCodeOrder[i]; + brotli_reg_t ix; + brotli_reg_t v; + if (BROTLI_PREDICT_FALSE(!BrotliSafeGetBits(br, 4, &ix))) { + brotli_reg_t available_bits = BrotliGetAvailableBits(br); + if (available_bits != 0) { + ix = BrotliGetBitsUnmasked(br) & 0xF; + } else { + ix = 0; + } + if (kCodeLengthPrefixLength[ix] > available_bits) { + h->sub_loop_counter = i; + h->repeat = num_codes; + h->space = space; + h->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + } + v = kCodeLengthPrefixValue[ix]; + BrotliDropBits(br, kCodeLengthPrefixLength[ix]); + h->code_length_code_lengths[code_len_idx] = (uint8_t)v; + BROTLI_LOG_ARRAY_INDEX(h->code_length_code_lengths, code_len_idx); + if (v != 0) { + space = space - (32U >> v); + ++num_codes; + ++h->code_length_histo[v]; + if (space - 1U >= 32U) { + /* space is 0 or wrapped around. */ + break; + } + } + } + if (!(num_codes == 1 || space == 0)) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_CL_SPACE); + } + return BROTLI_DECODER_SUCCESS; +} + +/* Decodes the Huffman tables. + There are 2 scenarios: + A) Huffman code contains only few symbols (1..4). Those symbols are read + directly; their code lengths are defined by the number of symbols. + For this scenario 4 - 49 bits will be read. + + B) 2-phase decoding: + B.1) Small Huffman table is decoded; it is specified with code lengths + encoded with predefined entropy code. 32 - 74 bits are used. + B.2) Decoded table is used to decode code lengths of symbols in resulting + Huffman table. In worst case 3520 bits are read. */ +static BrotliDecoderErrorCode ReadHuffmanCode(brotli_reg_t alphabet_size_max, + brotli_reg_t alphabet_size_limit, + HuffmanCode* table, + brotli_reg_t* opt_table_size, + BrotliDecoderState* s) { + BrotliBitReader* br = &s->br; + BrotliMetablockHeaderArena* h = &s->arena.header; + /* State machine. */ + for (;;) { + switch (h->substate_huffman) { + case BROTLI_STATE_HUFFMAN_NONE: + if (!BrotliSafeReadBits(br, 2, &h->sub_loop_counter)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + BROTLI_LOG_UINT(h->sub_loop_counter); + /* The value is used as follows: + 1 for simple code; + 0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */ + if (h->sub_loop_counter != 1) { + h->space = 32; + h->repeat = 0; /* num_codes */ + memset(&h->code_length_histo[0], 0, sizeof(h->code_length_histo[0]) * + (BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1)); + memset(&h->code_length_code_lengths[0], 0, + sizeof(h->code_length_code_lengths)); + h->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX; + continue; + } + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_SIMPLE_SIZE: + /* Read symbols, codes & code lengths directly. */ + if (!BrotliSafeReadBits(br, 2, &h->symbol)) { /* num_symbols */ + h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_SIZE; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + h->sub_loop_counter = 0; + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_SIMPLE_READ: { + BrotliDecoderErrorCode result = + ReadSimpleHuffmanSymbols(alphabet_size_max, alphabet_size_limit, s); + if (result != BROTLI_DECODER_SUCCESS) { + return result; + } + } + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_SIMPLE_BUILD: { + brotli_reg_t table_size; + if (h->symbol == 3) { + brotli_reg_t bits; + if (!BrotliSafeReadBits(br, 1, &bits)) { + h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_BUILD; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + h->symbol += bits; + } + BROTLI_LOG_UINT(h->symbol); + table_size = BrotliBuildSimpleHuffmanTable(table, HUFFMAN_TABLE_BITS, + h->symbols_lists_array, + (uint32_t)h->symbol); + if (opt_table_size) { + *opt_table_size = table_size; + } + h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE; + return BROTLI_DECODER_SUCCESS; + } + + /* Decode Huffman-coded code lengths. */ + case BROTLI_STATE_HUFFMAN_COMPLEX: { + brotli_reg_t i; + BrotliDecoderErrorCode result = ReadCodeLengthCodeLengths(s); + if (result != BROTLI_DECODER_SUCCESS) { + return result; + } + BrotliBuildCodeLengthsHuffmanTable(h->table, + h->code_length_code_lengths, + h->code_length_histo); + memset(&h->code_length_histo[0], 0, sizeof(h->code_length_histo)); + for (i = 0; i <= BROTLI_HUFFMAN_MAX_CODE_LENGTH; ++i) { + h->next_symbol[i] = (int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); + h->symbol_lists[h->next_symbol[i]] = 0xFFFF; + } + + h->symbol = 0; + h->prev_code_len = BROTLI_INITIAL_REPEATED_CODE_LENGTH; + h->repeat = 0; + h->repeat_code_len = 0; + h->space = 32768; + h->substate_huffman = BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS; + } + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS: { + brotli_reg_t table_size; + BrotliDecoderErrorCode result = ReadSymbolCodeLengths( + alphabet_size_limit, s); + if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) { + result = SafeReadSymbolCodeLengths(alphabet_size_limit, s); + } + if (result != BROTLI_DECODER_SUCCESS) { + return result; + } + + if (h->space != 0) { + BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", (int)h->space)); + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE); + } + table_size = BrotliBuildHuffmanTable( + table, HUFFMAN_TABLE_BITS, h->symbol_lists, h->code_length_histo); + if (opt_table_size) { + *opt_table_size = table_size; + } + h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE; + return BROTLI_DECODER_SUCCESS; + } + + default: + return + BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ + } + } +} + +/* Decodes a block length by reading 3..39 bits. */ +static BROTLI_INLINE brotli_reg_t ReadBlockLength(const HuffmanCode* table, + BrotliBitReader* br) { + brotli_reg_t code; + brotli_reg_t nbits; + code = ReadSymbol(table, br); + nbits = _kBrotliPrefixCodeRanges[code].nbits; /* nbits == 2..24 */ + return _kBrotliPrefixCodeRanges[code].offset + BrotliReadBits24(br, nbits); +} + +/* WARNING: if state is not BROTLI_STATE_READ_BLOCK_LENGTH_NONE, then + reading can't be continued with ReadBlockLength. */ +static BROTLI_INLINE BROTLI_BOOL SafeReadBlockLength( + BrotliDecoderState* s, brotli_reg_t* result, const HuffmanCode* table, + BrotliBitReader* br) { + brotli_reg_t index; + if (s->substate_read_block_length == BROTLI_STATE_READ_BLOCK_LENGTH_NONE) { + if (!SafeReadSymbol(table, br, &index)) { + return BROTLI_FALSE; + } + } else { + index = s->block_length_index; + } + { + brotli_reg_t bits; + brotli_reg_t nbits = _kBrotliPrefixCodeRanges[index].nbits; + brotli_reg_t offset = _kBrotliPrefixCodeRanges[index].offset; + if (!BrotliSafeReadBits(br, nbits, &bits)) { + s->block_length_index = index; + s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX; + return BROTLI_FALSE; + } + *result = offset + bits; + s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE; + return BROTLI_TRUE; + } +} + +/* Transform: + 1) initialize list L with values 0, 1,... 255 + 2) For each input element X: + 2.1) let Y = L[X] + 2.2) remove X-th element from L + 2.3) prepend Y to L + 2.4) append Y to output + + In most cases max(Y) <= 7, so most of L remains intact. + To reduce the cost of initialization, we reuse L, remember the upper bound + of Y values, and reinitialize only first elements in L. + + Most of input values are 0 and 1. To reduce number of branches, we replace + inner for loop with do-while. */ +static BROTLI_NOINLINE void InverseMoveToFrontTransform( + uint8_t* v, brotli_reg_t v_len, BrotliDecoderState* state) { + /* Reinitialize elements that could have been changed. */ + brotli_reg_t i = 1; + brotli_reg_t upper_bound = state->mtf_upper_bound; + uint32_t* mtf = &state->mtf[1]; /* Make mtf[-1] addressable. */ + uint8_t* mtf_u8 = (uint8_t*)mtf; + /* Load endian-aware constant. */ + const uint8_t b0123[4] = {0, 1, 2, 3}; + uint32_t pattern; + memcpy(&pattern, &b0123, 4); + + /* Initialize list using 4 consequent values pattern. */ + mtf[0] = pattern; + do { + pattern += 0x04040404; /* Advance all 4 values by 4. */ + mtf[i] = pattern; + i++; + } while (i <= upper_bound); + + /* Transform the input. */ + upper_bound = 0; + for (i = 0; i < v_len; ++i) { + int index = v[i]; + uint8_t value = mtf_u8[index]; + upper_bound |= v[i]; + v[i] = value; + mtf_u8[-1] = value; + do { + index--; + mtf_u8[index + 1] = mtf_u8[index]; + } while (index >= 0); + } + /* Remember amount of elements to be reinitialized. */ + state->mtf_upper_bound = upper_bound >> 2; +} + +/* Decodes a series of Huffman table using ReadHuffmanCode function. */ +static BrotliDecoderErrorCode HuffmanTreeGroupDecode( + HuffmanTreeGroup* group, BrotliDecoderState* s) { + BrotliMetablockHeaderArena* h = &s->arena.header; + if (h->substate_tree_group != BROTLI_STATE_TREE_GROUP_LOOP) { + h->next = group->codes; + h->htree_index = 0; + h->substate_tree_group = BROTLI_STATE_TREE_GROUP_LOOP; + } + while (h->htree_index < group->num_htrees) { + brotli_reg_t table_size; + BrotliDecoderErrorCode result = ReadHuffmanCode(group->alphabet_size_max, + group->alphabet_size_limit, h->next, &table_size, s); + if (result != BROTLI_DECODER_SUCCESS) return result; + group->htrees[h->htree_index] = h->next; + h->next += table_size; + ++h->htree_index; + } + h->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE; + return BROTLI_DECODER_SUCCESS; +} + +/* Decodes a context map. + Decoding is done in 4 phases: + 1) Read auxiliary information (6..16 bits) and allocate memory. + In case of trivial context map, decoding is finished at this phase. + 2) Decode Huffman table using ReadHuffmanCode function. + This table will be used for reading context map items. + 3) Read context map items; "0" values could be run-length encoded. + 4) Optionally, apply InverseMoveToFront transform to the resulting map. */ +static BrotliDecoderErrorCode DecodeContextMap(brotli_reg_t context_map_size, + brotli_reg_t* num_htrees, + uint8_t** context_map_arg, + BrotliDecoderState* s) { + BrotliBitReader* br = &s->br; + BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS; + BrotliMetablockHeaderArena* h = &s->arena.header; + + switch ((int)h->substate_context_map) { + case BROTLI_STATE_CONTEXT_MAP_NONE: + result = DecodeVarLenUint8(s, br, num_htrees); + if (result != BROTLI_DECODER_SUCCESS) { + return result; + } + (*num_htrees)++; + h->context_index = 0; + BROTLI_LOG_UINT(context_map_size); + BROTLI_LOG_UINT(*num_htrees); + *context_map_arg = + (uint8_t*)BROTLI_DECODER_ALLOC(s, (size_t)context_map_size); + if (*context_map_arg == 0) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP); + } + if (*num_htrees <= 1) { + memset(*context_map_arg, 0, (size_t)context_map_size); + return BROTLI_DECODER_SUCCESS; + } + h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_READ_PREFIX; + /* Fall through. */ + + case BROTLI_STATE_CONTEXT_MAP_READ_PREFIX: { + brotli_reg_t bits; + /* In next stage ReadHuffmanCode uses at least 4 bits, so it is safe + to peek 4 bits ahead. */ + if (!BrotliSafeGetBits(br, 5, &bits)) { + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if ((bits & 1) != 0) { /* Use RLE for zeros. */ + h->max_run_length_prefix = (bits >> 1) + 1; + BrotliDropBits(br, 5); + } else { + h->max_run_length_prefix = 0; + BrotliDropBits(br, 1); + } + BROTLI_LOG_UINT(h->max_run_length_prefix); + h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_HUFFMAN; + } + /* Fall through. */ + + case BROTLI_STATE_CONTEXT_MAP_HUFFMAN: { + brotli_reg_t alphabet_size = *num_htrees + h->max_run_length_prefix; + result = ReadHuffmanCode(alphabet_size, alphabet_size, + h->context_map_table, NULL, s); + if (result != BROTLI_DECODER_SUCCESS) return result; + h->code = 0xFFFF; + h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_DECODE; + } + /* Fall through. */ + + case BROTLI_STATE_CONTEXT_MAP_DECODE: { + brotli_reg_t context_index = h->context_index; + brotli_reg_t max_run_length_prefix = h->max_run_length_prefix; + uint8_t* context_map = *context_map_arg; + brotli_reg_t code = h->code; + BROTLI_BOOL skip_preamble = (code != 0xFFFF); + while (context_index < context_map_size || skip_preamble) { + if (!skip_preamble) { + if (!SafeReadSymbol(h->context_map_table, br, &code)) { + h->code = 0xFFFF; + h->context_index = context_index; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + BROTLI_LOG_UINT(code); + + if (code == 0) { + context_map[context_index++] = 0; + continue; + } + if (code > max_run_length_prefix) { + context_map[context_index++] = + (uint8_t)(code - max_run_length_prefix); + continue; + } + } else { + skip_preamble = BROTLI_FALSE; + } + /* RLE sub-stage. */ + { + brotli_reg_t reps; + if (!BrotliSafeReadBits(br, code, &reps)) { + h->code = code; + h->context_index = context_index; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + reps += (brotli_reg_t)1U << code; + BROTLI_LOG_UINT(reps); + if (context_index + reps > context_map_size) { + return + BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT); + } + do { + context_map[context_index++] = 0; + } while (--reps); + } + } + } + /* Fall through. */ + + case BROTLI_STATE_CONTEXT_MAP_TRANSFORM: { + brotli_reg_t bits; + if (!BrotliSafeReadBits(br, 1, &bits)) { + h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_TRANSFORM; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + if (bits != 0) { + InverseMoveToFrontTransform(*context_map_arg, context_map_size, s); + } + h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE; + return BROTLI_DECODER_SUCCESS; + } + + default: + return + BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ + } +} + +/* Decodes a command or literal and updates block type ring-buffer. + Reads 3..54 bits. */ +static BROTLI_INLINE BROTLI_BOOL DecodeBlockTypeAndLength( + int safe, BrotliDecoderState* s, int tree_type) { + brotli_reg_t max_block_type = s->num_block_types[tree_type]; + const HuffmanCode* type_tree = &s->block_type_trees[ + tree_type * BROTLI_HUFFMAN_MAX_SIZE_258]; + const HuffmanCode* len_tree = &s->block_len_trees[ + tree_type * BROTLI_HUFFMAN_MAX_SIZE_26]; + BrotliBitReader* br = &s->br; + brotli_reg_t* ringbuffer = &s->block_type_rb[tree_type * 2]; + brotli_reg_t block_type; + if (max_block_type <= 1) { + return BROTLI_FALSE; + } + + /* Read 0..15 + 3..39 bits. */ + if (!safe) { + block_type = ReadSymbol(type_tree, br); + s->block_length[tree_type] = ReadBlockLength(len_tree, br); + } else { + BrotliBitReaderState memento; + BrotliBitReaderSaveState(br, &memento); + if (!SafeReadSymbol(type_tree, br, &block_type)) return BROTLI_FALSE; + if (!SafeReadBlockLength(s, &s->block_length[tree_type], len_tree, br)) { + s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE; + BrotliBitReaderRestoreState(br, &memento); + return BROTLI_FALSE; + } + } + + if (block_type == 1) { + block_type = ringbuffer[1] + 1; + } else if (block_type == 0) { + block_type = ringbuffer[0]; + } else { + block_type -= 2; + } + if (block_type >= max_block_type) { + block_type -= max_block_type; + } + ringbuffer[0] = ringbuffer[1]; + ringbuffer[1] = block_type; + return BROTLI_TRUE; +} + +static BROTLI_INLINE void DetectTrivialLiteralBlockTypes( + BrotliDecoderState* s) { + size_t i; + for (i = 0; i < 8; ++i) s->trivial_literal_contexts[i] = 0; + for (i = 0; i < s->num_block_types[0]; i++) { + size_t offset = i << BROTLI_LITERAL_CONTEXT_BITS; + size_t error = 0; + size_t sample = s->context_map[offset]; + size_t j; + for (j = 0; j < (1u << BROTLI_LITERAL_CONTEXT_BITS);) { + /* NOLINTNEXTLINE(bugprone-macro-repeated-side-effects) */ + BROTLI_REPEAT_4({ error |= s->context_map[offset + j++] ^ sample; }) + } + if (error == 0) { + s->trivial_literal_contexts[i >> 5] |= 1u << (i & 31); + } + } +} + +static BROTLI_INLINE void PrepareLiteralDecoding(BrotliDecoderState* s) { + uint8_t context_mode; + size_t trivial; + brotli_reg_t block_type = s->block_type_rb[1]; + brotli_reg_t context_offset = block_type << BROTLI_LITERAL_CONTEXT_BITS; + s->context_map_slice = s->context_map + context_offset; + trivial = s->trivial_literal_contexts[block_type >> 5]; + s->trivial_literal_context = (trivial >> (block_type & 31)) & 1; + s->literal_htree = s->literal_hgroup.htrees[s->context_map_slice[0]]; + context_mode = s->context_modes[block_type] & 3; + s->context_lookup = BROTLI_CONTEXT_LUT(context_mode); +} + +/* Decodes the block type and updates the state for literal context. + Reads 3..54 bits. */ +static BROTLI_INLINE BROTLI_BOOL DecodeLiteralBlockSwitchInternal( + int safe, BrotliDecoderState* s) { + if (!DecodeBlockTypeAndLength(safe, s, 0)) { + return BROTLI_FALSE; + } + PrepareLiteralDecoding(s); + return BROTLI_TRUE; +} + +static void BROTLI_NOINLINE DecodeLiteralBlockSwitch(BrotliDecoderState* s) { + DecodeLiteralBlockSwitchInternal(0, s); +} + +static BROTLI_BOOL BROTLI_NOINLINE SafeDecodeLiteralBlockSwitch( + BrotliDecoderState* s) { + return DecodeLiteralBlockSwitchInternal(1, s); +} + +/* Block switch for insert/copy length. + Reads 3..54 bits. */ +static BROTLI_INLINE BROTLI_BOOL DecodeCommandBlockSwitchInternal( + int safe, BrotliDecoderState* s) { + if (!DecodeBlockTypeAndLength(safe, s, 1)) { + return BROTLI_FALSE; + } + s->htree_command = s->insert_copy_hgroup.htrees[s->block_type_rb[3]]; + return BROTLI_TRUE; +} + +static void BROTLI_NOINLINE DecodeCommandBlockSwitch(BrotliDecoderState* s) { + DecodeCommandBlockSwitchInternal(0, s); +} + +static BROTLI_BOOL BROTLI_NOINLINE SafeDecodeCommandBlockSwitch( + BrotliDecoderState* s) { + return DecodeCommandBlockSwitchInternal(1, s); +} + +/* Block switch for distance codes. + Reads 3..54 bits. */ +static BROTLI_INLINE BROTLI_BOOL DecodeDistanceBlockSwitchInternal( + int safe, BrotliDecoderState* s) { + if (!DecodeBlockTypeAndLength(safe, s, 2)) { + return BROTLI_FALSE; + } + s->dist_context_map_slice = s->dist_context_map + + (s->block_type_rb[5] << BROTLI_DISTANCE_CONTEXT_BITS); + s->dist_htree_index = s->dist_context_map_slice[s->distance_context]; + return BROTLI_TRUE; +} + +static void BROTLI_NOINLINE DecodeDistanceBlockSwitch(BrotliDecoderState* s) { + DecodeDistanceBlockSwitchInternal(0, s); +} + +static BROTLI_BOOL BROTLI_NOINLINE SafeDecodeDistanceBlockSwitch( + BrotliDecoderState* s) { + return DecodeDistanceBlockSwitchInternal(1, s); +} + +static size_t UnwrittenBytes(const BrotliDecoderState* s, BROTLI_BOOL wrap) { + size_t pos = wrap && s->pos > s->ringbuffer_size ? + (size_t)s->ringbuffer_size : (size_t)(s->pos); + size_t partial_pos_rb = (s->rb_roundtrips * (size_t)s->ringbuffer_size) + pos; + return partial_pos_rb - s->partial_pos_out; +} + +/* Dumps output. + Returns BROTLI_DECODER_NEEDS_MORE_OUTPUT only if there is more output to push + and either ring-buffer is as big as window size, or |force| is true. */ +static BrotliDecoderErrorCode BROTLI_NOINLINE WriteRingBuffer( + BrotliDecoderState* s, size_t* available_out, uint8_t** next_out, + size_t* total_out, BROTLI_BOOL force) { + uint8_t* start = + s->ringbuffer + (s->partial_pos_out & (size_t)s->ringbuffer_mask); + size_t to_write = UnwrittenBytes(s, BROTLI_TRUE); + size_t num_written = *available_out; + if (num_written > to_write) { + num_written = to_write; + } + if (s->meta_block_remaining_len < 0) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1); + } + if (next_out && !*next_out) { + *next_out = start; + } else { + if (next_out) { + memcpy(*next_out, start, num_written); + *next_out += num_written; + } + } + *available_out -= num_written; + BROTLI_LOG_UINT(to_write); + BROTLI_LOG_UINT(num_written); + s->partial_pos_out += num_written; + if (total_out) { + *total_out = s->partial_pos_out; + } + if (num_written < to_write) { + if (s->ringbuffer_size == (1 << s->window_bits) || force) { + return BROTLI_DECODER_NEEDS_MORE_OUTPUT; + } else { + return BROTLI_DECODER_SUCCESS; + } + } + /* Wrap ring buffer only if it has reached its maximal size. */ + if (s->ringbuffer_size == (1 << s->window_bits) && + s->pos >= s->ringbuffer_size) { + s->pos -= s->ringbuffer_size; + s->rb_roundtrips++; + s->should_wrap_ringbuffer = (size_t)s->pos != 0 ? 1 : 0; + } + return BROTLI_DECODER_SUCCESS; +} + +static void BROTLI_NOINLINE WrapRingBuffer(BrotliDecoderState* s) { + if (s->should_wrap_ringbuffer) { + memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)s->pos); + s->should_wrap_ringbuffer = 0; + } +} + +/* Allocates ring-buffer. + + s->ringbuffer_size MUST be updated by BrotliCalculateRingBufferSize before + this function is called. + + Last two bytes of ring-buffer are initialized to 0, so context calculation + could be done uniformly for the first two and all other positions. */ +static BROTLI_BOOL BROTLI_NOINLINE BrotliEnsureRingBuffer( + BrotliDecoderState* s) { + uint8_t* old_ringbuffer = s->ringbuffer; + if (s->ringbuffer_size == s->new_ringbuffer_size) { + return BROTLI_TRUE; + } + + s->ringbuffer = (uint8_t*)BROTLI_DECODER_ALLOC(s, + (size_t)(s->new_ringbuffer_size) + kRingBufferWriteAheadSlack); + if (s->ringbuffer == 0) { + /* Restore previous value. */ + s->ringbuffer = old_ringbuffer; + return BROTLI_FALSE; + } + s->ringbuffer[s->new_ringbuffer_size - 2] = 0; + s->ringbuffer[s->new_ringbuffer_size - 1] = 0; + + if (!!old_ringbuffer) { + memcpy(s->ringbuffer, old_ringbuffer, (size_t)s->pos); + BROTLI_DECODER_FREE(s, old_ringbuffer); + } + + s->ringbuffer_size = s->new_ringbuffer_size; + s->ringbuffer_mask = s->new_ringbuffer_size - 1; + s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size; + + return BROTLI_TRUE; +} + +static BrotliDecoderErrorCode BROTLI_NOINLINE +SkipMetadataBlock(BrotliDecoderState* s) { + BrotliBitReader* br = &s->br; + int nbytes; + + if (s->meta_block_remaining_len == 0) { + return BROTLI_DECODER_SUCCESS; + } + + BROTLI_DCHECK((BrotliGetAvailableBits(br) & 7) == 0); + + /* Drain accumulator. */ + if (BrotliGetAvailableBits(br) >= 8) { + uint8_t buffer[8]; + nbytes = (int)(BrotliGetAvailableBits(br)) >> 3; + BROTLI_DCHECK(nbytes <= 8); + if (nbytes > s->meta_block_remaining_len) { + nbytes = s->meta_block_remaining_len; + } + BrotliCopyBytes(buffer, br, (size_t)nbytes); + if (s->metadata_chunk_func) { + s->metadata_chunk_func(s->metadata_callback_opaque, buffer, + (size_t)nbytes); + } + s->meta_block_remaining_len -= nbytes; + if (s->meta_block_remaining_len == 0) { + return BROTLI_DECODER_SUCCESS; + } + } + + /* Direct access to metadata is possible. */ + nbytes = (int)BrotliGetRemainingBytes(br); + if (nbytes > s->meta_block_remaining_len) { + nbytes = s->meta_block_remaining_len; + } + if (nbytes > 0) { + if (s->metadata_chunk_func) { + s->metadata_chunk_func(s->metadata_callback_opaque, br->next_in, + (size_t)nbytes); + } + BrotliDropBytes(br, (size_t)nbytes); + s->meta_block_remaining_len -= nbytes; + if (s->meta_block_remaining_len == 0) { + return BROTLI_DECODER_SUCCESS; + } + } + + BROTLI_DCHECK(BrotliGetRemainingBytes(br) == 0); + + return BROTLI_DECODER_NEEDS_MORE_INPUT; +} + +static BrotliDecoderErrorCode BROTLI_NOINLINE CopyUncompressedBlockToOutput( + size_t* available_out, uint8_t** next_out, size_t* total_out, + BrotliDecoderState* s) { + /* TODO(eustas): avoid allocation for single uncompressed block. */ + if (!BrotliEnsureRingBuffer(s)) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1); + } + + /* State machine */ + for (;;) { + switch (s->substate_uncompressed) { + case BROTLI_STATE_UNCOMPRESSED_NONE: { + int nbytes = (int)BrotliGetRemainingBytes(&s->br); + if (nbytes > s->meta_block_remaining_len) { + nbytes = s->meta_block_remaining_len; + } + if (s->pos + nbytes > s->ringbuffer_size) { + nbytes = s->ringbuffer_size - s->pos; + } + /* Copy remaining bytes from s->br.buf_ to ring-buffer. */ + BrotliCopyBytes(&s->ringbuffer[s->pos], &s->br, (size_t)nbytes); + s->pos += nbytes; + s->meta_block_remaining_len -= nbytes; + if (s->pos < 1 << s->window_bits) { + if (s->meta_block_remaining_len == 0) { + return BROTLI_DECODER_SUCCESS; + } + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE; + } + /* Fall through. */ + + case BROTLI_STATE_UNCOMPRESSED_WRITE: { + BrotliDecoderErrorCode result; + result = WriteRingBuffer( + s, available_out, next_out, total_out, BROTLI_FALSE); + if (result != BROTLI_DECODER_SUCCESS) { + return result; + } + if (s->ringbuffer_size == 1 << s->window_bits) { + s->max_distance = s->max_backward_distance; + } + s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_NONE; + break; + } + } + } + BROTLI_DCHECK(0); /* Unreachable */ +} + +static BROTLI_BOOL AttachCompoundDictionary( + BrotliDecoderState* state, const uint8_t* data, size_t size) { + BrotliDecoderCompoundDictionary* addon = state->compound_dictionary; + if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE; + if (!addon) { + addon = (BrotliDecoderCompoundDictionary*)BROTLI_DECODER_ALLOC( + state, sizeof(BrotliDecoderCompoundDictionary)); + if (!addon) return BROTLI_FALSE; + addon->num_chunks = 0; + addon->total_size = 0; + addon->br_length = 0; + addon->br_copied = 0; + addon->block_bits = -1; + addon->chunk_offsets[0] = 0; + state->compound_dictionary = addon; + } + if (addon->num_chunks == 15) return BROTLI_FALSE; + addon->chunks[addon->num_chunks] = data; + addon->num_chunks++; + addon->total_size += (int)size; + addon->chunk_offsets[addon->num_chunks] = addon->total_size; + return BROTLI_TRUE; +} + +static void EnsureCompoundDictionaryInitialized(BrotliDecoderState* state) { + BrotliDecoderCompoundDictionary* addon = state->compound_dictionary; + /* 256 = (1 << 8) slots in block map. */ + int block_bits = 8; + int cursor = 0; + int index = 0; + if (addon->block_bits != -1) return; + while (((addon->total_size - 1) >> block_bits) != 0) block_bits++; + block_bits -= 8; + addon->block_bits = block_bits; + while (cursor < addon->total_size) { + while (addon->chunk_offsets[index + 1] < cursor) index++; + addon->block_map[cursor >> block_bits] = (uint8_t)index; + cursor += 1 << block_bits; + } +} + +static BROTLI_BOOL InitializeCompoundDictionaryCopy(BrotliDecoderState* s, + int address, int length) { + BrotliDecoderCompoundDictionary* addon = s->compound_dictionary; + int index; + EnsureCompoundDictionaryInitialized(s); + index = addon->block_map[address >> addon->block_bits]; + while (address >= addon->chunk_offsets[index + 1]) index++; + if (addon->total_size < address + length) return BROTLI_FALSE; + /* Update the recent distances cache. */ + s->dist_rb[s->dist_rb_idx & 3] = s->distance_code; + ++s->dist_rb_idx; + s->meta_block_remaining_len -= length; + addon->br_index = index; + addon->br_offset = address - addon->chunk_offsets[index]; + addon->br_length = length; + addon->br_copied = 0; + return BROTLI_TRUE; +} + +static int GetCompoundDictionarySize(BrotliDecoderState* s) { + return s->compound_dictionary ? s->compound_dictionary->total_size : 0; +} + +static int CopyFromCompoundDictionary(BrotliDecoderState* s, int pos) { + BrotliDecoderCompoundDictionary* addon = s->compound_dictionary; + int orig_pos = pos; + while (addon->br_length != addon->br_copied) { + uint8_t* copy_dst = &s->ringbuffer[pos]; + const uint8_t* copy_src = + addon->chunks[addon->br_index] + addon->br_offset; + int space = s->ringbuffer_size - pos; + int rem_chunk_length = (addon->chunk_offsets[addon->br_index + 1] - + addon->chunk_offsets[addon->br_index]) - addon->br_offset; + int length = addon->br_length - addon->br_copied; + if (length > rem_chunk_length) length = rem_chunk_length; + if (length > space) length = space; + memcpy(copy_dst, copy_src, (size_t)length); + pos += length; + addon->br_offset += length; + addon->br_copied += length; + if (length == rem_chunk_length) { + addon->br_index++; + addon->br_offset = 0; + } + if (pos == s->ringbuffer_size) break; + } + return pos - orig_pos; +} + +BROTLI_BOOL BrotliDecoderAttachDictionary( + BrotliDecoderState* state, BrotliSharedDictionaryType type, + size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]) { + brotli_reg_t i; + brotli_reg_t num_prefix_before = state->dictionary->num_prefix; + if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE; + if (!BrotliSharedDictionaryAttach(state->dictionary, type, data_size, data)) { + return BROTLI_FALSE; + } + for (i = num_prefix_before; i < state->dictionary->num_prefix; i++) { + if (!AttachCompoundDictionary( + state, state->dictionary->prefix[i], + state->dictionary->prefix_size[i])) { + return BROTLI_FALSE; + } + } + return BROTLI_TRUE; +} + +/* Calculates the smallest feasible ring buffer. + + If we know the data size is small, do not allocate more ring buffer + size than needed to reduce memory usage. + + When this method is called, metablock size and flags MUST be decoded. */ +static void BROTLI_NOINLINE BrotliCalculateRingBufferSize( + BrotliDecoderState* s) { + int window_size = 1 << s->window_bits; + int new_ringbuffer_size = window_size; + /* We need at least 2 bytes of ring buffer size to get the last two + bytes for context from there */ + int min_size = s->ringbuffer_size ? s->ringbuffer_size : 1024; + int output_size; + + /* If maximum is already reached, no further extension is retired. */ + if (s->ringbuffer_size == window_size) { + return; + } + + /* Metadata blocks does not touch ring buffer. */ + if (s->is_metadata) { + return; + } + + if (!s->ringbuffer) { + output_size = 0; + } else { + output_size = s->pos; + } + output_size += s->meta_block_remaining_len; + min_size = min_size < output_size ? output_size : min_size; + + if (!!s->canny_ringbuffer_allocation) { + /* Reduce ring buffer size to save memory when server is unscrupulous. + In worst case memory usage might be 1.5x bigger for a short period of + ring buffer reallocation. */ + while ((new_ringbuffer_size >> 1) >= min_size) { + new_ringbuffer_size >>= 1; + } + } + + s->new_ringbuffer_size = new_ringbuffer_size; +} + +/* Reads 1..256 2-bit context modes. */ +static BrotliDecoderErrorCode ReadContextModes(BrotliDecoderState* s) { + BrotliBitReader* br = &s->br; + int i = s->loop_counter; + + while (i < (int)s->num_block_types[0]) { + brotli_reg_t bits; + if (!BrotliSafeReadBits(br, 2, &bits)) { + s->loop_counter = i; + return BROTLI_DECODER_NEEDS_MORE_INPUT; + } + s->context_modes[i] = (uint8_t)bits; + BROTLI_LOG_ARRAY_INDEX(s->context_modes, i); + i++; + } + return BROTLI_DECODER_SUCCESS; +} + +static BROTLI_INLINE void TakeDistanceFromRingBuffer(BrotliDecoderState* s) { + int offset = s->distance_code - 3; + if (s->distance_code <= 3) { + /* Compensate double distance-ring-buffer roll for dictionary items. */ + s->distance_context = 1 >> s->distance_code; + s->distance_code = s->dist_rb[(s->dist_rb_idx - offset) & 3]; + s->dist_rb_idx -= s->distance_context; + } else { + int index_delta = 3; + int delta; + int base = s->distance_code - 10; + if (s->distance_code < 10) { + base = s->distance_code - 4; + } else { + index_delta = 2; + } + /* Unpack one of six 4-bit values. */ + delta = ((0x605142 >> (4 * base)) & 0xF) - 3; + s->distance_code = s->dist_rb[(s->dist_rb_idx + index_delta) & 0x3] + delta; + if (s->distance_code <= 0) { + /* A huge distance will cause a BROTLI_FAILURE() soon. + This is a little faster than failing here. */ + s->distance_code = 0x7FFFFFFF; + } + } +} + +static BROTLI_INLINE BROTLI_BOOL SafeReadBits( + BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { + if (n_bits != 0) { + return BrotliSafeReadBits(br, n_bits, val); + } else { + *val = 0; + return BROTLI_TRUE; + } +} + +static BROTLI_INLINE BROTLI_BOOL SafeReadBits32( + BrotliBitReader* const br, brotli_reg_t n_bits, brotli_reg_t* val) { + if (n_bits != 0) { + return BrotliSafeReadBits32(br, n_bits, val); + } else { + *val = 0; + return BROTLI_TRUE; + } +} + +/* + RFC 7932 Section 4 with "..." shortenings and "[]" emendations. + + Each distance ... is represented with a pair ... + The distance code is encoded using a prefix code... The number of extra bits + can be 0..24... Two additional parameters: NPOSTFIX (0..3), and ... + NDIRECT (0..120) ... are encoded in the meta-block header... + + The first 16 distance symbols ... reference past distances... ring buffer ... + Next NDIRECT distance symbols ... represent distances from 1 to NDIRECT... + [For] distance symbols 16 + NDIRECT and greater ... the number of extra bits + ... is given by the following formula: + + [ xcode = dcode - NDIRECT - 16 ] + ndistbits = 1 + [ xcode ] >> (NPOSTFIX + 1) + + ... +*/ + +/* + RFC 7932 Section 9.2 with "..." shortenings and "[]" emendations. + + ... to get the actual value of the parameter NDIRECT, left-shift this + four-bit number by NPOSTFIX bits ... +*/ + +/* Remaining formulas from RFC 7932 Section 4 could be rewritten as following: + + alphabet_size = 16 + NDIRECT + (max_distbits << (NPOSTFIX + 1)) + + half = ((xcode >> NPOSTFIX) & 1) << ndistbits + postfix = xcode & ((1 << NPOSTFIX) - 1) + range_start = 2 * (1 << ndistbits - 1 - 1) + + distance = (range_start + half + extra) << NPOSTFIX + postfix + NDIRECT + 1 + + NB: ndistbits >= 1 -> range_start >= 0 + NB: range_start has factor 2, as the range is covered by 2 "halves" + NB: extra -1 offset in range_start formula covers the absence of + ndistbits = 0 case + NB: when NPOSTFIX = 0, NDIRECT is not greater than 15 + + In other words, xcode has the following binary structure - XXXHPPP: + - XXX represent the number of extra distance bits + - H selects upper / lower range of distances + - PPP represent "postfix" + + "Regular" distance encoding has NPOSTFIX = 0; omitting the postfix part + simplifies distance calculation. + + Using NPOSTFIX > 0 allows cheaper encoding of regular structures, e.g. where + most of distances have the same reminder of division by 2/4/8. For example, + the table of int32_t values that come from different sources; if it is likely + that 3 highest bytes of values from the same source are the same, then + copy distance often looks like 4x + y. + + Distance calculation could be rewritten to: + + ndistbits = NDISTBITS(NDIRECT, NPOSTFIX)[dcode] + distance = OFFSET(NDIRECT, NPOSTFIX)[dcode] + extra << NPOSTFIX + + NDISTBITS and OFFSET could be pre-calculated, as NDIRECT and NPOSTFIX could + change only once per meta-block. +*/ + +/* Calculates distance lookup table. + NB: it is possible to have all 64 tables precalculated. */ +static void CalculateDistanceLut(BrotliDecoderState* s) { + BrotliMetablockBodyArena* b = &s->arena.body; + brotli_reg_t npostfix = s->distance_postfix_bits; + brotli_reg_t ndirect = s->num_direct_distance_codes; + brotli_reg_t alphabet_size_limit = s->distance_hgroup.alphabet_size_limit; + brotli_reg_t postfix = (brotli_reg_t)1u << npostfix; + brotli_reg_t j; + brotli_reg_t bits = 1; + brotli_reg_t half = 0; + + /* Skip short codes. */ + brotli_reg_t i = BROTLI_NUM_DISTANCE_SHORT_CODES; + + /* Fill direct codes. */ + for (j = 0; j < ndirect; ++j) { + b->dist_extra_bits[i] = 0; + b->dist_offset[i] = j + 1; + ++i; + } + + /* Fill regular distance codes. */ + while (i < alphabet_size_limit) { + brotli_reg_t base = ndirect + ((((2 + half) << bits) - 4) << npostfix) + 1; + /* Always fill the complete group. */ + for (j = 0; j < postfix; ++j) { + b->dist_extra_bits[i] = (uint8_t)bits; + b->dist_offset[i] = base + j; + ++i; + } + bits = bits + half; + half = half ^ 1; + } +} + +/* Precondition: s->distance_code < 0. */ +static BROTLI_INLINE BROTLI_BOOL ReadDistanceInternal( + int safe, BrotliDecoderState* s, BrotliBitReader* br) { + BrotliMetablockBodyArena* b = &s->arena.body; + brotli_reg_t code; + brotli_reg_t bits; + BrotliBitReaderState memento; + HuffmanCode* distance_tree = s->distance_hgroup.htrees[s->dist_htree_index]; + if (!safe) { + code = ReadSymbol(distance_tree, br); + } else { + BrotliBitReaderSaveState(br, &memento); + if (!SafeReadSymbol(distance_tree, br, &code)) { + return BROTLI_FALSE; + } + } + --s->block_length[2]; + /* Convert the distance code to the actual distance by possibly + looking up past distances from the s->dist_rb. */ + s->distance_context = 0; + if ((code & ~0xFu) == 0) { + s->distance_code = (int)code; + TakeDistanceFromRingBuffer(s); + return BROTLI_TRUE; + } + if (!safe) { + bits = BrotliReadBits32(br, b->dist_extra_bits[code]); + } else { + if (!SafeReadBits32(br, b->dist_extra_bits[code], &bits)) { + ++s->block_length[2]; + BrotliBitReaderRestoreState(br, &memento); + return BROTLI_FALSE; + } + } + s->distance_code = + (int)(b->dist_offset[code] + (bits << s->distance_postfix_bits)); + return BROTLI_TRUE; +} + +static BROTLI_INLINE void ReadDistance( + BrotliDecoderState* s, BrotliBitReader* br) { + ReadDistanceInternal(0, s, br); +} + +static BROTLI_INLINE BROTLI_BOOL SafeReadDistance( + BrotliDecoderState* s, BrotliBitReader* br) { + return ReadDistanceInternal(1, s, br); +} + +static BROTLI_INLINE BROTLI_BOOL ReadCommandInternal( + int safe, BrotliDecoderState* s, BrotliBitReader* br, int* insert_length) { + brotli_reg_t cmd_code; + brotli_reg_t insert_len_extra = 0; + brotli_reg_t copy_length; + CmdLutElement v; + BrotliBitReaderState memento; + if (!safe) { + cmd_code = ReadSymbol(s->htree_command, br); + } else { + BrotliBitReaderSaveState(br, &memento); + if (!SafeReadSymbol(s->htree_command, br, &cmd_code)) { + return BROTLI_FALSE; + } + } + v = kCmdLut[cmd_code]; + s->distance_code = v.distance_code; + s->distance_context = v.context; + s->dist_htree_index = s->dist_context_map_slice[s->distance_context]; + *insert_length = v.insert_len_offset; + if (!safe) { + if (BROTLI_PREDICT_FALSE(v.insert_len_extra_bits != 0)) { + insert_len_extra = BrotliReadBits24(br, v.insert_len_extra_bits); + } + copy_length = BrotliReadBits24(br, v.copy_len_extra_bits); + } else { + if (!SafeReadBits(br, v.insert_len_extra_bits, &insert_len_extra) || + !SafeReadBits(br, v.copy_len_extra_bits, ©_length)) { + BrotliBitReaderRestoreState(br, &memento); + return BROTLI_FALSE; + } + } + s->copy_length = (int)copy_length + v.copy_len_offset; + --s->block_length[1]; + *insert_length += (int)insert_len_extra; + return BROTLI_TRUE; +} + +static BROTLI_INLINE void ReadCommand( + BrotliDecoderState* s, BrotliBitReader* br, int* insert_length) { + ReadCommandInternal(0, s, br, insert_length); +} + +static BROTLI_INLINE BROTLI_BOOL SafeReadCommand( + BrotliDecoderState* s, BrotliBitReader* br, int* insert_length) { + return ReadCommandInternal(1, s, br, insert_length); +} + +static BROTLI_INLINE BROTLI_BOOL CheckInputAmount( + int safe, BrotliBitReader* const br) { + if (safe) { + return BROTLI_TRUE; + } + return BrotliCheckInputAmount(br); +} + +#define BROTLI_SAFE(METHOD) \ + { \ + if (safe) { \ + if (!Safe##METHOD) { \ + result = BROTLI_DECODER_NEEDS_MORE_INPUT; \ + goto saveStateAndReturn; \ + } \ + } else { \ + METHOD; \ + } \ + } + +static BROTLI_INLINE BrotliDecoderErrorCode ProcessCommandsInternal( + int safe, BrotliDecoderState* s) { + int pos = s->pos; + int i = s->loop_counter; + BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS; + BrotliBitReader* br = &s->br; + int compound_dictionary_size = GetCompoundDictionarySize(s); + + if (!CheckInputAmount(safe, br)) { + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + goto saveStateAndReturn; + } + if (!safe) { + BROTLI_UNUSED(BrotliWarmupBitReader(br)); + } + + /* Jump into state machine. */ + if (s->state == BROTLI_STATE_COMMAND_BEGIN) { + goto CommandBegin; + } else if (s->state == BROTLI_STATE_COMMAND_INNER) { + goto CommandInner; + } else if (s->state == BROTLI_STATE_COMMAND_POST_DECODE_LITERALS) { + goto CommandPostDecodeLiterals; + } else if (s->state == BROTLI_STATE_COMMAND_POST_WRAP_COPY) { + goto CommandPostWrapCopy; + } else { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE); /* COV_NF_LINE */ + } + +CommandBegin: + if (safe) { + s->state = BROTLI_STATE_COMMAND_BEGIN; + } + if (!CheckInputAmount(safe, br)) { + s->state = BROTLI_STATE_COMMAND_BEGIN; + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + goto saveStateAndReturn; + } + if (BROTLI_PREDICT_FALSE(s->block_length[1] == 0)) { + BROTLI_SAFE(DecodeCommandBlockSwitch(s)); + goto CommandBegin; + } + /* Read the insert/copy length in the command. */ + BROTLI_SAFE(ReadCommand(s, br, &i)); + BROTLI_LOG(("[ProcessCommandsInternal] pos = %d insert = %d copy = %d\n", + pos, i, s->copy_length)); + if (i == 0) { + goto CommandPostDecodeLiterals; + } + s->meta_block_remaining_len -= i; + +CommandInner: + if (safe) { + s->state = BROTLI_STATE_COMMAND_INNER; + } + /* Read the literals in the command. */ + if (s->trivial_literal_context) { + brotli_reg_t bits; + brotli_reg_t value; + PreloadSymbol(safe, s->literal_htree, br, &bits, &value); + if (!safe) { + // This is a hottest part of the decode, so we copy the loop below + // and optimize it by calculating the number of steps where all checks + // evaluate to false (ringbuffer size/block size/input size). + // Since all checks are loop invariant, we just need to find + // minimal number of iterations for a simple loop, and run + // the full version for the remainder. + int num_steps = i - 1; + if (num_steps > 0 && ((brotli_reg_t)(num_steps) > s->block_length[0])) { + // Safe cast, since block_length < steps + num_steps = (int)s->block_length[0]; + } + if (s->ringbuffer_size >= pos && + (s->ringbuffer_size - pos) <= num_steps) { + num_steps = s->ringbuffer_size - pos - 1; + } + if (num_steps < 0) { + num_steps = 0; + } + num_steps = BrotliCopyPreloadedSymbolsToU8(s->literal_htree, br, &bits, + &value, s->ringbuffer, pos, + num_steps); + pos += num_steps; + s->block_length[0] -= (brotli_reg_t)num_steps; + i -= num_steps; + do { + if (!CheckInputAmount(safe, br)) { + s->state = BROTLI_STATE_COMMAND_INNER; + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + goto saveStateAndReturn; + } + if (BROTLI_PREDICT_FALSE(s->block_length[0] == 0)) { + goto NextLiteralBlock; + } + BrotliCopyPreloadedSymbolsToU8(s->literal_htree, br, &bits, &value, + s->ringbuffer, pos, 1); + --s->block_length[0]; + BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos); + ++pos; + if (BROTLI_PREDICT_FALSE(pos == s->ringbuffer_size)) { + s->state = BROTLI_STATE_COMMAND_INNER_WRITE; + --i; + goto saveStateAndReturn; + } + } while (--i != 0); + } else { /* safe */ + do { + if (BROTLI_PREDICT_FALSE(s->block_length[0] == 0)) { + goto NextLiteralBlock; + } + brotli_reg_t literal; + if (!SafeReadSymbol(s->literal_htree, br, &literal)) { + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + goto saveStateAndReturn; + } + s->ringbuffer[pos] = (uint8_t)literal; + --s->block_length[0]; + BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos); + ++pos; + if (BROTLI_PREDICT_FALSE(pos == s->ringbuffer_size)) { + s->state = BROTLI_STATE_COMMAND_INNER_WRITE; + --i; + goto saveStateAndReturn; + } + } while (--i != 0); + } + } else { + uint8_t p1 = s->ringbuffer[(pos - 1) & s->ringbuffer_mask]; + uint8_t p2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask]; + do { + const HuffmanCode* hc; + uint8_t context; + if (!CheckInputAmount(safe, br)) { + s->state = BROTLI_STATE_COMMAND_INNER; + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + goto saveStateAndReturn; + } + if (BROTLI_PREDICT_FALSE(s->block_length[0] == 0)) { + goto NextLiteralBlock; + } + context = BROTLI_CONTEXT(p1, p2, s->context_lookup); + BROTLI_LOG_UINT(context); + hc = s->literal_hgroup.htrees[s->context_map_slice[context]]; + p2 = p1; + if (!safe) { + p1 = (uint8_t)ReadSymbol(hc, br); + } else { + brotli_reg_t literal; + if (!SafeReadSymbol(hc, br, &literal)) { + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + goto saveStateAndReturn; + } + p1 = (uint8_t)literal; + } + s->ringbuffer[pos] = p1; + --s->block_length[0]; + BROTLI_LOG_UINT(s->context_map_slice[context]); + BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos & s->ringbuffer_mask); + ++pos; + if (BROTLI_PREDICT_FALSE(pos == s->ringbuffer_size)) { + s->state = BROTLI_STATE_COMMAND_INNER_WRITE; + --i; + goto saveStateAndReturn; + } + } while (--i != 0); + } + BROTLI_LOG_UINT(s->meta_block_remaining_len); + if (BROTLI_PREDICT_FALSE(s->meta_block_remaining_len <= 0)) { + s->state = BROTLI_STATE_METABLOCK_DONE; + goto saveStateAndReturn; + } + +CommandPostDecodeLiterals: + if (safe) { + s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS; + } + if (s->distance_code >= 0) { + /* Implicit distance case. */ + s->distance_context = s->distance_code ? 0 : 1; + --s->dist_rb_idx; + s->distance_code = s->dist_rb[s->dist_rb_idx & 3]; + } else { + /* Read distance code in the command, unless it was implicitly zero. */ + if (BROTLI_PREDICT_FALSE(s->block_length[2] == 0)) { + BROTLI_SAFE(DecodeDistanceBlockSwitch(s)); + } + BROTLI_SAFE(ReadDistance(s, br)); + } + BROTLI_LOG(("[ProcessCommandsInternal] pos = %d distance = %d\n", + pos, s->distance_code)); + if (s->max_distance != s->max_backward_distance) { + s->max_distance = + (pos < s->max_backward_distance) ? pos : s->max_backward_distance; + } + i = s->copy_length; + /* Apply copy of LZ77 back-reference, or static dictionary reference if + the distance is larger than the max LZ77 distance */ + if (s->distance_code > s->max_distance) { + /* The maximum allowed distance is BROTLI_MAX_ALLOWED_DISTANCE = 0x7FFFFFFC. + With this choice, no signed overflow can occur after decoding + a special distance code (e.g., after adding 3 to the last distance). */ + if (s->distance_code > BROTLI_MAX_ALLOWED_DISTANCE) { + BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " + "len: %d bytes left: %d\n", + pos, s->distance_code, i, s->meta_block_remaining_len)); + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DISTANCE); + } + if (s->distance_code - s->max_distance - 1 < compound_dictionary_size) { + int address = compound_dictionary_size - + (s->distance_code - s->max_distance); + if (!InitializeCompoundDictionaryCopy(s, address, i)) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY); + } + pos += CopyFromCompoundDictionary(s, pos); + if (pos >= s->ringbuffer_size) { + s->state = BROTLI_STATE_COMMAND_POST_WRITE_1; + goto saveStateAndReturn; + } + } else if (i >= SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH && + i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH) { + uint8_t p1 = s->ringbuffer[(pos - 1) & s->ringbuffer_mask]; + uint8_t p2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask]; + uint8_t dict_id = s->dictionary->context_based ? + s->dictionary->context_map[BROTLI_CONTEXT(p1, p2, s->context_lookup)] + : 0; + const BrotliDictionary* words = s->dictionary->words[dict_id]; + const BrotliTransforms* transforms = s->dictionary->transforms[dict_id]; + int offset = (int)words->offsets_by_length[i]; + brotli_reg_t shift = words->size_bits_by_length[i]; + int address = + s->distance_code - s->max_distance - 1 - compound_dictionary_size; + int mask = (int)BitMask(shift); + int word_idx = address & mask; + int transform_idx = address >> shift; + /* Compensate double distance-ring-buffer roll. */ + s->dist_rb_idx += s->distance_context; + offset += word_idx * i; + /* If the distance is out of bound, select a next static dictionary if + there exist multiple. */ + if ((transform_idx >= (int)transforms->num_transforms || + words->size_bits_by_length[i] == 0) && + s->dictionary->num_dictionaries > 1) { + uint8_t dict_id2; + int dist_remaining = address - + (int)(((1u << shift) & ~1u)) * (int)transforms->num_transforms; + for (dict_id2 = 0; dict_id2 < s->dictionary->num_dictionaries; + dict_id2++) { + const BrotliDictionary* words2 = s->dictionary->words[dict_id2]; + if (dict_id2 != dict_id && words2->size_bits_by_length[i] != 0) { + const BrotliTransforms* transforms2 = + s->dictionary->transforms[dict_id2]; + brotli_reg_t shift2 = words2->size_bits_by_length[i]; + int num = (int)((1u << shift2) & ~1u) * + (int)transforms2->num_transforms; + if (dist_remaining < num) { + dict_id = dict_id2; + words = words2; + transforms = transforms2; + address = dist_remaining; + shift = shift2; + mask = (int)BitMask(shift); + word_idx = address & mask; + transform_idx = address >> shift; + offset = (int)words->offsets_by_length[i] + word_idx * i; + break; + } + dist_remaining -= num; + } + } + } + if (BROTLI_PREDICT_FALSE(words->size_bits_by_length[i] == 0)) { + BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " + "len: %d bytes left: %d\n", + pos, s->distance_code, i, s->meta_block_remaining_len)); + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DICTIONARY); + } + if (BROTLI_PREDICT_FALSE(!words->data)) { + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET); + } + if (transform_idx < (int)transforms->num_transforms) { + const uint8_t* word = &words->data[offset]; + int len = i; + if (transform_idx == transforms->cutOffTransforms[0]) { + memcpy(&s->ringbuffer[pos], word, (size_t)len); + BROTLI_LOG(("[ProcessCommandsInternal] dictionary word: [%.*s]\n", + len, word)); + } else { + len = BrotliTransformDictionaryWord(&s->ringbuffer[pos], word, len, + transforms, transform_idx); + BROTLI_LOG(("[ProcessCommandsInternal] dictionary word: [%.*s]," + " transform_idx = %d, transformed: [%.*s]\n", + i, word, transform_idx, len, &s->ringbuffer[pos])); + if (len == 0 && s->distance_code <= 120) { + BROTLI_LOG(("Invalid length-0 dictionary word after transform\n")); + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_TRANSFORM); + } + } + pos += len; + s->meta_block_remaining_len -= len; + if (pos >= s->ringbuffer_size) { + s->state = BROTLI_STATE_COMMAND_POST_WRITE_1; + goto saveStateAndReturn; + } + } else { + BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " + "len: %d bytes left: %d\n", + pos, s->distance_code, i, s->meta_block_remaining_len)); + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_TRANSFORM); + } + } else { + BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " + "len: %d bytes left: %d\n", + pos, s->distance_code, i, s->meta_block_remaining_len)); + return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DICTIONARY); + } + } else { + int src_start = (pos - s->distance_code) & s->ringbuffer_mask; + uint8_t* copy_dst = &s->ringbuffer[pos]; + uint8_t* copy_src = &s->ringbuffer[src_start]; + int dst_end = pos + i; + int src_end = src_start + i; + /* Update the recent distances cache. */ + s->dist_rb[s->dist_rb_idx & 3] = s->distance_code; + ++s->dist_rb_idx; + s->meta_block_remaining_len -= i; + /* There are 32+ bytes of slack in the ring-buffer allocation. + Also, we have 16 short codes, that make these 16 bytes irrelevant + in the ring-buffer. Let's copy over them as a first guess. */ + memmove16(copy_dst, copy_src); + if (src_end > pos && dst_end > src_start) { + /* Regions intersect. */ + goto CommandPostWrapCopy; + } + if (dst_end >= s->ringbuffer_size || src_end >= s->ringbuffer_size) { + /* At least one region wraps. */ + goto CommandPostWrapCopy; + } + pos += i; + if (i > 16) { + if (i > 32) { + memcpy(copy_dst + 16, copy_src + 16, (size_t)(i - 16)); + } else { + /* This branch covers about 45% cases. + Fixed size short copy allows more compiler optimizations. */ + memmove16(copy_dst + 16, copy_src + 16); + } + } + } + BROTLI_LOG_UINT(s->meta_block_remaining_len); + if (s->meta_block_remaining_len <= 0) { + /* Next metablock, if any. */ + s->state = BROTLI_STATE_METABLOCK_DONE; + goto saveStateAndReturn; + } else { + goto CommandBegin; + } +CommandPostWrapCopy: + { + int wrap_guard = s->ringbuffer_size - pos; + while (--i >= 0) { + s->ringbuffer[pos] = + s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask]; + ++pos; + if (BROTLI_PREDICT_FALSE(--wrap_guard == 0)) { + s->state = BROTLI_STATE_COMMAND_POST_WRITE_2; + goto saveStateAndReturn; + } + } + } + if (s->meta_block_remaining_len <= 0) { + /* Next metablock, if any. */ + s->state = BROTLI_STATE_METABLOCK_DONE; + goto saveStateAndReturn; + } else { + goto CommandBegin; + } + +NextLiteralBlock: + BROTLI_SAFE(DecodeLiteralBlockSwitch(s)); + goto CommandInner; + +saveStateAndReturn: + s->pos = pos; + s->loop_counter = i; + return result; +} + +#undef BROTLI_SAFE + +static BROTLI_NOINLINE BrotliDecoderErrorCode ProcessCommands( + BrotliDecoderState* s) { + return ProcessCommandsInternal(0, s); +} + +static BROTLI_NOINLINE BrotliDecoderErrorCode SafeProcessCommands( + BrotliDecoderState* s) { + return ProcessCommandsInternal(1, s); +} + +BrotliDecoderResult BrotliDecoderDecompress( + size_t encoded_size, + const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], + size_t* decoded_size, + uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]) { + BrotliDecoderState s; + BrotliDecoderResult result; + size_t total_out = 0; + size_t available_in = encoded_size; + const uint8_t* next_in = encoded_buffer; + size_t available_out = *decoded_size; + uint8_t* next_out = decoded_buffer; + if (!BrotliDecoderStateInit(&s, 0, 0, 0)) { + return BROTLI_DECODER_RESULT_ERROR; + } + result = BrotliDecoderDecompressStream( + &s, &available_in, &next_in, &available_out, &next_out, &total_out); + *decoded_size = total_out; + BrotliDecoderStateCleanup(&s); + if (result != BROTLI_DECODER_RESULT_SUCCESS) { + result = BROTLI_DECODER_RESULT_ERROR; + } + return result; +} + +/* Invariant: input stream is never overconsumed: + - invalid input implies that the whole stream is invalid -> any amount of + input could be read and discarded + - when result is "needs more input", then at least one more byte is REQUIRED + to complete decoding; all input data MUST be consumed by decoder, so + client could swap the input buffer + - when result is "needs more output" decoder MUST ensure that it doesn't + hold more than 7 bits in bit reader; this saves client from swapping input + buffer ahead of time + - when result is "success" decoder MUST return all unused data back to input + buffer; this is possible because the invariant is held on enter */ +BrotliDecoderResult BrotliDecoderDecompressStream( + BrotliDecoderState* s, size_t* available_in, const uint8_t** next_in, + size_t* available_out, uint8_t** next_out, size_t* total_out) { + BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS; + BrotliBitReader* br = &s->br; + size_t input_size = *available_in; +#define BROTLI_SAVE_ERROR_CODE(code) \ + SaveErrorCode(s, (code), input_size - *available_in) + /* Ensure that |total_out| is set, even if no data will ever be pushed out. */ + if (total_out) { + *total_out = s->partial_pos_out; + } + /* Do not try to process further in a case of unrecoverable error. */ + if ((int)s->error_code < 0) { + return BROTLI_DECODER_RESULT_ERROR; + } + if (*available_out && (!next_out || !*next_out)) { + return BROTLI_SAVE_ERROR_CODE( + BROTLI_FAILURE(BROTLI_DECODER_ERROR_INVALID_ARGUMENTS)); + } + if (!*available_out) next_out = 0; + if (s->buffer_length == 0) { /* Just connect bit reader to input stream. */ + BrotliBitReaderSetInput(br, *next_in, *available_in); + } else { + /* At least one byte of input is required. More than one byte of input may + be required to complete the transaction -> reading more data must be + done in a loop -> do it in a main loop. */ + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + BrotliBitReaderSetInput(br, &s->buffer.u8[0], s->buffer_length); + } + /* State machine */ + for (;;) { + if (result != BROTLI_DECODER_SUCCESS) { + /* Error, needs more input/output. */ + if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) { + if (s->ringbuffer != 0) { /* Pro-actively push output. */ + BrotliDecoderErrorCode intermediate_result = WriteRingBuffer(s, + available_out, next_out, total_out, BROTLI_TRUE); + /* WriteRingBuffer checks s->meta_block_remaining_len validity. */ + if ((int)intermediate_result < 0) { + result = intermediate_result; + break; + } + } + if (s->buffer_length != 0) { /* Used with internal buffer. */ + if (br->next_in == br->last_in) { + /* Successfully finished read transaction. + Accumulator contains less than 8 bits, because internal buffer + is expanded byte-by-byte until it is enough to complete read. */ + s->buffer_length = 0; + /* Switch to input stream and restart. */ + result = BROTLI_DECODER_SUCCESS; + BrotliBitReaderSetInput(br, *next_in, *available_in); + continue; + } else if (*available_in != 0) { + /* Not enough data in buffer, but can take one more byte from + input stream. */ + result = BROTLI_DECODER_SUCCESS; + BROTLI_DCHECK(s->buffer_length < 8); + s->buffer.u8[s->buffer_length] = **next_in; + s->buffer_length++; + BrotliBitReaderSetInput(br, &s->buffer.u8[0], s->buffer_length); + (*next_in)++; + (*available_in)--; + /* Retry with more data in buffer. */ + continue; + } + /* Can't finish reading and no more input. */ + break; + } else { /* Input stream doesn't contain enough input. */ + /* Copy tail to internal buffer and return. */ + *next_in = br->next_in; + *available_in = BrotliBitReaderGetAvailIn(br); + while (*available_in) { + s->buffer.u8[s->buffer_length] = **next_in; + s->buffer_length++; + (*next_in)++; + (*available_in)--; + } + break; + } + /* Unreachable. */ + } + + /* Fail or needs more output. */ + + if (s->buffer_length != 0) { + /* Just consumed the buffered input and produced some output. Otherwise + it would result in "needs more input". Reset internal buffer. */ + s->buffer_length = 0; + } else { + /* Using input stream in last iteration. When decoder switches to input + stream it has less than 8 bits in accumulator, so it is safe to + return unused accumulator bits there. */ + BrotliBitReaderUnload(br); + *available_in = BrotliBitReaderGetAvailIn(br); + *next_in = br->next_in; + } + break; + } + switch (s->state) { + case BROTLI_STATE_UNINITED: + /* Prepare to the first read. */ + if (!BrotliWarmupBitReader(br)) { + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + break; + } + /* Decode window size. */ + result = DecodeWindowBits(s, br); /* Reads 1..8 bits. */ + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + if (s->large_window) { + s->state = BROTLI_STATE_LARGE_WINDOW_BITS; + break; + } + s->state = BROTLI_STATE_INITIALIZE; + break; + + case BROTLI_STATE_LARGE_WINDOW_BITS: { + brotli_reg_t bits; + if (!BrotliSafeReadBits(br, 6, &bits)) { + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + break; + } + s->window_bits = bits & 63u; + if (s->window_bits < BROTLI_LARGE_MIN_WBITS || + s->window_bits > BROTLI_LARGE_MAX_WBITS) { + result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS); + break; + } + s->state = BROTLI_STATE_INITIALIZE; + } + /* Fall through. */ + + case BROTLI_STATE_INITIALIZE: + BROTLI_LOG_UINT(s->window_bits); + /* Maximum distance, see section 9.1. of the spec. */ + s->max_backward_distance = (1 << s->window_bits) - BROTLI_WINDOW_GAP; + + /* Allocate memory for both block_type_trees and block_len_trees. */ + s->block_type_trees = (HuffmanCode*)BROTLI_DECODER_ALLOC(s, + sizeof(HuffmanCode) * 3 * + (BROTLI_HUFFMAN_MAX_SIZE_258 + BROTLI_HUFFMAN_MAX_SIZE_26)); + if (s->block_type_trees == 0) { + result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES); + break; + } + s->block_len_trees = + s->block_type_trees + 3 * BROTLI_HUFFMAN_MAX_SIZE_258; + + s->state = BROTLI_STATE_METABLOCK_BEGIN; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_BEGIN: + BrotliDecoderStateMetablockBegin(s); + BROTLI_LOG_UINT(s->pos); + s->state = BROTLI_STATE_METABLOCK_HEADER; + /* Fall through. */ + + case BROTLI_STATE_METABLOCK_HEADER: + result = DecodeMetaBlockLength(s, br); /* Reads 2 - 31 bits. */ + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + BROTLI_LOG_UINT(s->is_last_metablock); + BROTLI_LOG_UINT(s->meta_block_remaining_len); + BROTLI_LOG_UINT(s->is_metadata); + BROTLI_LOG_UINT(s->is_uncompressed); + if (s->is_metadata || s->is_uncompressed) { + if (!BrotliJumpToByteBoundary(br)) { + result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_PADDING_1); + break; + } + } + if (s->is_metadata) { + s->state = BROTLI_STATE_METADATA; + if (s->metadata_start_func) { + s->metadata_start_func(s->metadata_callback_opaque, + (size_t)s->meta_block_remaining_len); + } + break; + } + if (s->meta_block_remaining_len == 0) { + s->state = BROTLI_STATE_METABLOCK_DONE; + break; + } + BrotliCalculateRingBufferSize(s); + if (s->is_uncompressed) { + s->state = BROTLI_STATE_UNCOMPRESSED; + break; + } + s->state = BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER; + /* Fall through. */ + + case BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER: { + BrotliMetablockHeaderArena* h = &s->arena.header; + s->loop_counter = 0; + /* Initialize compressed metablock header arena. */ + h->sub_loop_counter = 0; + /* Make small negative indexes addressable. */ + h->symbol_lists = + &h->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1]; + h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE; + h->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE; + h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE; + s->state = BROTLI_STATE_HUFFMAN_CODE_0; + } + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_CODE_0: + if (s->loop_counter >= 3) { + s->state = BROTLI_STATE_METABLOCK_HEADER_2; + break; + } + /* Reads 1..11 bits. */ + result = DecodeVarLenUint8(s, br, &s->num_block_types[s->loop_counter]); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + s->num_block_types[s->loop_counter]++; + BROTLI_LOG_UINT(s->num_block_types[s->loop_counter]); + if (s->num_block_types[s->loop_counter] < 2) { + s->loop_counter++; + break; + } + s->state = BROTLI_STATE_HUFFMAN_CODE_1; + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_CODE_1: { + brotli_reg_t alphabet_size = s->num_block_types[s->loop_counter] + 2; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_258; + result = ReadHuffmanCode(alphabet_size, alphabet_size, + &s->block_type_trees[tree_offset], NULL, s); + if (result != BROTLI_DECODER_SUCCESS) break; + s->state = BROTLI_STATE_HUFFMAN_CODE_2; + } + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_CODE_2: { + brotli_reg_t alphabet_size = BROTLI_NUM_BLOCK_LEN_SYMBOLS; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; + result = ReadHuffmanCode(alphabet_size, alphabet_size, + &s->block_len_trees[tree_offset], NULL, s); + if (result != BROTLI_DECODER_SUCCESS) break; + s->state = BROTLI_STATE_HUFFMAN_CODE_3; + } + /* Fall through. */ + + case BROTLI_STATE_HUFFMAN_CODE_3: { + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; + if (!SafeReadBlockLength(s, &s->block_length[s->loop_counter], + &s->block_len_trees[tree_offset], br)) { + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + break; + } + BROTLI_LOG_UINT(s->block_length[s->loop_counter]); + s->loop_counter++; + s->state = BROTLI_STATE_HUFFMAN_CODE_0; + break; + } + + case BROTLI_STATE_UNCOMPRESSED: { + result = CopyUncompressedBlockToOutput( + available_out, next_out, total_out, s); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + s->state = BROTLI_STATE_METABLOCK_DONE; + break; + } + + case BROTLI_STATE_METADATA: + result = SkipMetadataBlock(s); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + s->state = BROTLI_STATE_METABLOCK_DONE; + break; + + case BROTLI_STATE_METABLOCK_HEADER_2: { + brotli_reg_t bits; + if (!BrotliSafeReadBits(br, 6, &bits)) { + result = BROTLI_DECODER_NEEDS_MORE_INPUT; + break; + } + s->distance_postfix_bits = bits & BitMask(2); + bits >>= 2; + s->num_direct_distance_codes = bits << s->distance_postfix_bits; + BROTLI_LOG_UINT(s->num_direct_distance_codes); + BROTLI_LOG_UINT(s->distance_postfix_bits); + s->context_modes = + (uint8_t*)BROTLI_DECODER_ALLOC(s, (size_t)s->num_block_types[0]); + if (s->context_modes == 0) { + result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES); + break; + } + s->loop_counter = 0; + s->state = BROTLI_STATE_CONTEXT_MODES; + } + /* Fall through. */ + + case BROTLI_STATE_CONTEXT_MODES: + result = ReadContextModes(s); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + s->state = BROTLI_STATE_CONTEXT_MAP_1; + /* Fall through. */ + + case BROTLI_STATE_CONTEXT_MAP_1: + result = DecodeContextMap( + s->num_block_types[0] << BROTLI_LITERAL_CONTEXT_BITS, + &s->num_literal_htrees, &s->context_map, s); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + DetectTrivialLiteralBlockTypes(s); + s->state = BROTLI_STATE_CONTEXT_MAP_2; + /* Fall through. */ + + case BROTLI_STATE_CONTEXT_MAP_2: { + brotli_reg_t npostfix = s->distance_postfix_bits; + brotli_reg_t ndirect = s->num_direct_distance_codes; + brotli_reg_t distance_alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( + npostfix, ndirect, BROTLI_MAX_DISTANCE_BITS); + brotli_reg_t distance_alphabet_size_limit = distance_alphabet_size_max; + BROTLI_BOOL allocation_success = BROTLI_TRUE; + if (s->large_window) { + BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit( + BROTLI_MAX_ALLOWED_DISTANCE, (uint32_t)npostfix, + (uint32_t)ndirect); + distance_alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( + npostfix, ndirect, BROTLI_LARGE_MAX_DISTANCE_BITS); + distance_alphabet_size_limit = limit.max_alphabet_size; + } + result = DecodeContextMap( + s->num_block_types[2] << BROTLI_DISTANCE_CONTEXT_BITS, + &s->num_dist_htrees, &s->dist_context_map, s); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + allocation_success &= BrotliDecoderHuffmanTreeGroupInit( + s, &s->literal_hgroup, BROTLI_NUM_LITERAL_SYMBOLS, + BROTLI_NUM_LITERAL_SYMBOLS, s->num_literal_htrees); + allocation_success &= BrotliDecoderHuffmanTreeGroupInit( + s, &s->insert_copy_hgroup, BROTLI_NUM_COMMAND_SYMBOLS, + BROTLI_NUM_COMMAND_SYMBOLS, s->num_block_types[1]); + allocation_success &= BrotliDecoderHuffmanTreeGroupInit( + s, &s->distance_hgroup, distance_alphabet_size_max, + distance_alphabet_size_limit, s->num_dist_htrees); + if (!allocation_success) { + return BROTLI_SAVE_ERROR_CODE( + BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS)); + } + s->loop_counter = 0; + s->state = BROTLI_STATE_TREE_GROUP; + } + /* Fall through. */ + + case BROTLI_STATE_TREE_GROUP: { + HuffmanTreeGroup* hgroup = NULL; + switch (s->loop_counter) { + case 0: hgroup = &s->literal_hgroup; break; + case 1: hgroup = &s->insert_copy_hgroup; break; + case 2: hgroup = &s->distance_hgroup; break; + default: return BROTLI_SAVE_ERROR_CODE(BROTLI_FAILURE( + BROTLI_DECODER_ERROR_UNREACHABLE)); /* COV_NF_LINE */ + } + result = HuffmanTreeGroupDecode(hgroup, s); + if (result != BROTLI_DECODER_SUCCESS) break; + s->loop_counter++; + if (s->loop_counter < 3) { + break; + } + s->state = BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY; + } + /* Fall through. */ + + case BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY: + PrepareLiteralDecoding(s); + s->dist_context_map_slice = s->dist_context_map; + s->htree_command = s->insert_copy_hgroup.htrees[0]; + if (!BrotliEnsureRingBuffer(s)) { + result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2); + break; + } + CalculateDistanceLut(s); + s->state = BROTLI_STATE_COMMAND_BEGIN; + /* Fall through. */ + + case BROTLI_STATE_COMMAND_BEGIN: + /* Fall through. */ + case BROTLI_STATE_COMMAND_INNER: + /* Fall through. */ + case BROTLI_STATE_COMMAND_POST_DECODE_LITERALS: + /* Fall through. */ + case BROTLI_STATE_COMMAND_POST_WRAP_COPY: + result = ProcessCommands(s); + if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) { + result = SafeProcessCommands(s); + } + break; + + case BROTLI_STATE_COMMAND_INNER_WRITE: + /* Fall through. */ + case BROTLI_STATE_COMMAND_POST_WRITE_1: + /* Fall through. */ + case BROTLI_STATE_COMMAND_POST_WRITE_2: + result = WriteRingBuffer( + s, available_out, next_out, total_out, BROTLI_FALSE); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + WrapRingBuffer(s); + if (s->ringbuffer_size == 1 << s->window_bits) { + s->max_distance = s->max_backward_distance; + } + if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) { + BrotliDecoderCompoundDictionary* addon = s->compound_dictionary; + if (addon && (addon->br_length != addon->br_copied)) { + s->pos += CopyFromCompoundDictionary(s, s->pos); + if (s->pos >= s->ringbuffer_size) continue; + } + if (s->meta_block_remaining_len == 0) { + /* Next metablock, if any. */ + s->state = BROTLI_STATE_METABLOCK_DONE; + } else { + s->state = BROTLI_STATE_COMMAND_BEGIN; + } + break; + } else if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_2) { + s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY; + } else { /* BROTLI_STATE_COMMAND_INNER_WRITE */ + if (s->loop_counter == 0) { + if (s->meta_block_remaining_len == 0) { + s->state = BROTLI_STATE_METABLOCK_DONE; + } else { + s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS; + } + break; + } + s->state = BROTLI_STATE_COMMAND_INNER; + } + break; + + case BROTLI_STATE_METABLOCK_DONE: + if (s->meta_block_remaining_len < 0) { + result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2); + break; + } + BrotliDecoderStateCleanupAfterMetablock(s); + if (!s->is_last_metablock) { + s->state = BROTLI_STATE_METABLOCK_BEGIN; + break; + } + if (!BrotliJumpToByteBoundary(br)) { + result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_PADDING_2); + break; + } + if (s->buffer_length == 0) { + BrotliBitReaderUnload(br); + *available_in = BrotliBitReaderGetAvailIn(br); + *next_in = br->next_in; + } + s->state = BROTLI_STATE_DONE; + /* Fall through. */ + + case BROTLI_STATE_DONE: + if (s->ringbuffer != 0) { + result = WriteRingBuffer( + s, available_out, next_out, total_out, BROTLI_TRUE); + if (result != BROTLI_DECODER_SUCCESS) { + break; + } + } + return BROTLI_SAVE_ERROR_CODE(result); + } + } + return BROTLI_SAVE_ERROR_CODE(result); +#undef BROTLI_SAVE_ERROR_CODE +} + +BROTLI_BOOL BrotliDecoderHasMoreOutput(const BrotliDecoderState* s) { + /* After unrecoverable error remaining output is considered nonsensical. */ + if ((int)s->error_code < 0) { + return BROTLI_FALSE; + } + return TO_BROTLI_BOOL( + s->ringbuffer != 0 && UnwrittenBytes(s, BROTLI_FALSE) != 0); +} + +const uint8_t* BrotliDecoderTakeOutput(BrotliDecoderState* s, size_t* size) { + uint8_t* result = 0; + size_t available_out = *size ? *size : 1u << 24; + size_t requested_out = available_out; + BrotliDecoderErrorCode status; + if ((s->ringbuffer == 0) || ((int)s->error_code < 0)) { + *size = 0; + return 0; + } + WrapRingBuffer(s); + status = WriteRingBuffer(s, &available_out, &result, 0, BROTLI_TRUE); + /* Either WriteRingBuffer returns those "success" codes... */ + if (status == BROTLI_DECODER_SUCCESS || + status == BROTLI_DECODER_NEEDS_MORE_OUTPUT) { + *size = requested_out - available_out; + } else { + /* ... or stream is broken. Normally this should be caught by + BrotliDecoderDecompressStream, this is just a safeguard. */ + if ((int)status < 0) SaveErrorCode(s, status, 0); + *size = 0; + result = 0; + } + return result; +} + +BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* s) { + return TO_BROTLI_BOOL(s->state != BROTLI_STATE_UNINITED || + BrotliGetAvailableBits(&s->br) != 0); +} + +BROTLI_BOOL BrotliDecoderIsFinished(const BrotliDecoderState* s) { + return TO_BROTLI_BOOL(s->state == BROTLI_STATE_DONE) && + !BrotliDecoderHasMoreOutput(s); +} + +BrotliDecoderErrorCode BrotliDecoderGetErrorCode(const BrotliDecoderState* s) { + return (BrotliDecoderErrorCode)s->error_code; +} + +const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c) { + switch (c) { +#define BROTLI_ERROR_CODE_CASE_(PREFIX, NAME, CODE) \ + case BROTLI_DECODER ## PREFIX ## NAME: return #PREFIX #NAME; +#define BROTLI_NOTHING_ + BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE_CASE_, BROTLI_NOTHING_) +#undef BROTLI_ERROR_CODE_CASE_ +#undef BROTLI_NOTHING_ + default: return "INVALID"; + } +} + +uint32_t BrotliDecoderVersion(void) { + return BROTLI_VERSION; +} + +void BrotliDecoderSetMetadataCallbacks( + BrotliDecoderState* state, + brotli_decoder_metadata_start_func start_func, + brotli_decoder_metadata_chunk_func chunk_func, void* opaque) { + state->metadata_start_func = start_func; + state->metadata_chunk_func = chunk_func; + state->metadata_callback_opaque = opaque; +} + +/* Escalate internal functions visibility; for testing purposes only. */ +#if defined(BROTLI_TEST) +BROTLI_BOOL BrotliSafeReadSymbolForTest( + const HuffmanCode*, BrotliBitReader*, brotli_reg_t*); +BROTLI_BOOL BrotliSafeReadSymbolForTest( + const HuffmanCode* table, BrotliBitReader* br, brotli_reg_t* result) { + return SafeReadSymbol(table, br, result); +} +void BrotliInverseMoveToFrontTransformForTest( + uint8_t*, brotli_reg_t, BrotliDecoderState*); +void BrotliInverseMoveToFrontTransformForTest( + uint8_t* v, brotli_reg_t l, BrotliDecoderState* s) { + InverseMoveToFrontTransform(v, l, s); +} +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/dec/huffman.c b/packages/runtime/vendor/brotli/c/dec/huffman.c new file mode 100644 index 000000000..06486cf81 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/huffman.c @@ -0,0 +1,339 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Utilities for building Huffman decoding tables. */ + +#include "huffman.h" + +#include "../common/constants.h" +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define BROTLI_REVERSE_BITS_MAX 8 + +#if defined(BROTLI_RBIT) +#define BROTLI_REVERSE_BITS_BASE \ + ((sizeof(brotli_reg_t) << 3) - BROTLI_REVERSE_BITS_MAX) +#else +#define BROTLI_REVERSE_BITS_BASE 0 +static BROTLI_MODEL("small") +uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; +#endif /* BROTLI_RBIT */ + +#define BROTLI_REVERSE_BITS_LOWEST \ + ((brotli_reg_t)1 << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE)) + +/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX), + where reverse(value, len) is the bit-wise reversal of the len least + significant bits of value. */ +static BROTLI_INLINE brotli_reg_t BrotliReverseBits(brotli_reg_t num) { +#if defined(BROTLI_RBIT) + return BROTLI_RBIT(num); +#else + return kReverseBits[num]; +#endif +} + +/* Stores code in table[0], table[step], table[2*step], ..., table[end] */ +/* Assumes that end is an integer multiple of step */ +static BROTLI_INLINE void ReplicateValue(HuffmanCode* table, + int step, int end, + HuffmanCode code) { + do { + end -= step; + table[end] = code; + } while (end > 0); +} + +/* Returns the table width of the next 2nd level table. |count| is the histogram + of bit lengths for the remaining symbols, |len| is the code length of the + next processed symbol. */ +static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count, + int len, int root_bits) { + int left = 1 << (len - root_bits); + while (len < BROTLI_HUFFMAN_MAX_CODE_LENGTH) { + left -= count[len]; + if (left <= 0) break; + ++len; + left <<= 1; + } + return len - root_bits; +} + +void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table, + const uint8_t* const code_lengths, + uint16_t* count) { + HuffmanCode code; /* current table entry */ + int symbol; /* symbol index in original or sorted table */ + brotli_reg_t key; /* prefix code */ + brotli_reg_t key_step; /* prefix code addend */ + int step; /* step size to replicate values in current table */ + int table_size; /* size of current table */ + int sorted[BROTLI_CODE_LENGTH_CODES]; /* symbols sorted by code length */ + /* offsets in sorted table for each length */ + int offset[BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1]; + int bits; + int bits_count; + BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <= + BROTLI_REVERSE_BITS_MAX); + BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH == 5); + + /* Generate offsets into sorted symbol table by code length. */ + symbol = -1; + bits = 1; + /* BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH == 5 */ + BROTLI_REPEAT_5({ + symbol += count[bits]; + offset[bits] = symbol; + bits++; + }); + /* Symbols with code length 0 are placed after all other symbols. */ + offset[0] = BROTLI_CODE_LENGTH_CODES - 1; + + /* Sort symbols by length, by symbol order within each length. */ + symbol = BROTLI_CODE_LENGTH_CODES; + do { + BROTLI_REPEAT_6({ + symbol--; + sorted[offset[code_lengths[symbol]]--] = symbol; + }); + } while (symbol != 0); + + table_size = 1 << BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH; + + /* Special case: all symbols but one have 0 code length. */ + if (offset[0] == 0) { + code = ConstructHuffmanCode(0, (uint16_t)sorted[0]); + for (key = 0; key < (brotli_reg_t)table_size; ++key) { + table[key] = code; + } + return; + } + + /* Fill in table. */ + key = 0; + key_step = BROTLI_REVERSE_BITS_LOWEST; + symbol = 0; + bits = 1; + step = 2; + do { + for (bits_count = count[bits]; bits_count != 0; --bits_count) { + code = ConstructHuffmanCode((uint8_t)bits, (uint16_t)sorted[symbol++]); + ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code); + key += key_step; + } + step <<= 1; + key_step >>= 1; + } while (++bits <= BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH); +} + +uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table, + int root_bits, + const uint16_t* const symbol_lists, + uint16_t* count) { + HuffmanCode code; /* current table entry */ + HuffmanCode* table; /* next available space in table */ + int len; /* current code length */ + int symbol; /* symbol index in original or sorted table */ + brotli_reg_t key; /* prefix code */ + brotli_reg_t key_step; /* prefix code addend */ + brotli_reg_t sub_key; /* 2nd level table prefix code */ + brotli_reg_t sub_key_step; /* 2nd level table prefix code addend */ + int step; /* step size to replicate values in current table */ + int table_bits; /* key length of current table */ + int table_size; /* size of current table */ + int total_size; /* sum of root table size and 2nd level table sizes */ + int max_length = -1; + int bits; + int bits_count; + + BROTLI_DCHECK(root_bits <= BROTLI_REVERSE_BITS_MAX); + BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH - root_bits <= + BROTLI_REVERSE_BITS_MAX); + + while (symbol_lists[max_length] == 0xFFFF) max_length--; + max_length += BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1; + + table = root_table; + table_bits = root_bits; + table_size = 1 << table_bits; + total_size = table_size; + + /* Fill in the root table. Reduce the table size to if possible, + and create the repetitions by memcpy. */ + if (table_bits > max_length) { + table_bits = max_length; + table_size = 1 << table_bits; + } + key = 0; + key_step = BROTLI_REVERSE_BITS_LOWEST; + bits = 1; + step = 2; + do { + symbol = bits - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); + for (bits_count = count[bits]; bits_count != 0; --bits_count) { + symbol = symbol_lists[symbol]; + code = ConstructHuffmanCode((uint8_t)bits, (uint16_t)symbol); + ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code); + key += key_step; + } + step <<= 1; + key_step >>= 1; + } while (++bits <= table_bits); + + /* If root_bits != table_bits then replicate to fill the remaining slots. */ + while (total_size != table_size) { + memcpy(&table[table_size], &table[0], + (size_t)table_size * sizeof(table[0])); + table_size <<= 1; + } + + /* Fill in 2nd level tables and add pointers to root table. */ + key_step = BROTLI_REVERSE_BITS_LOWEST >> (root_bits - 1); + sub_key = (BROTLI_REVERSE_BITS_LOWEST << 1); + sub_key_step = BROTLI_REVERSE_BITS_LOWEST; + for (len = root_bits + 1, step = 2; len <= max_length; ++len) { + symbol = len - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); + for (; count[len] != 0; --count[len]) { + if (sub_key == (BROTLI_REVERSE_BITS_LOWEST << 1U)) { + table += table_size; + table_bits = NextTableBitSize(count, len, root_bits); + table_size = 1 << table_bits; + total_size += table_size; + sub_key = BrotliReverseBits(key); + key += key_step; + root_table[sub_key] = ConstructHuffmanCode( + (uint8_t)(table_bits + root_bits), + (uint16_t)(((size_t)(table - root_table)) - sub_key)); + sub_key = 0; + } + symbol = symbol_lists[symbol]; + code = ConstructHuffmanCode((uint8_t)(len - root_bits), (uint16_t)symbol); + ReplicateValue( + &table[BrotliReverseBits(sub_key)], step, table_size, code); + sub_key += sub_key_step; + } + step <<= 1; + sub_key_step >>= 1; + } + return (uint32_t)total_size; +} + +uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table, + int root_bits, + uint16_t* val, + uint32_t num_symbols) { + uint32_t table_size = 1; + const uint32_t goal_size = 1U << root_bits; + switch (num_symbols) { + case 0: + table[0] = ConstructHuffmanCode(0, val[0]); + break; + case 1: + if (val[1] > val[0]) { + table[0] = ConstructHuffmanCode(1, val[0]); + table[1] = ConstructHuffmanCode(1, val[1]); + } else { + table[0] = ConstructHuffmanCode(1, val[1]); + table[1] = ConstructHuffmanCode(1, val[0]); + } + table_size = 2; + break; + case 2: + table[0] = ConstructHuffmanCode(1, val[0]); + table[2] = ConstructHuffmanCode(1, val[0]); + if (val[2] > val[1]) { + table[1] = ConstructHuffmanCode(2, val[1]); + table[3] = ConstructHuffmanCode(2, val[2]); + } else { + table[1] = ConstructHuffmanCode(2, val[2]); + table[3] = ConstructHuffmanCode(2, val[1]); + } + table_size = 4; + break; + case 3: { + int i, k; + for (i = 0; i < 3; ++i) { + for (k = i + 1; k < 4; ++k) { + if (val[k] < val[i]) { + uint16_t t = val[k]; + val[k] = val[i]; + val[i] = t; + } + } + } + table[0] = ConstructHuffmanCode(2, val[0]); + table[2] = ConstructHuffmanCode(2, val[1]); + table[1] = ConstructHuffmanCode(2, val[2]); + table[3] = ConstructHuffmanCode(2, val[3]); + table_size = 4; + break; + } + case 4: { + if (val[3] < val[2]) { + uint16_t t = val[3]; + val[3] = val[2]; + val[2] = t; + } + table[0] = ConstructHuffmanCode(1, val[0]); + table[1] = ConstructHuffmanCode(2, val[1]); + table[2] = ConstructHuffmanCode(1, val[0]); + table[3] = ConstructHuffmanCode(3, val[2]); + table[4] = ConstructHuffmanCode(1, val[0]); + table[5] = ConstructHuffmanCode(2, val[1]); + table[6] = ConstructHuffmanCode(1, val[0]); + table[7] = ConstructHuffmanCode(3, val[3]); + table_size = 8; + break; + } + } + while (table_size != goal_size) { + memcpy(&table[table_size], &table[0], + (size_t)table_size * sizeof(table[0])); + table_size <<= 1; + } + return goal_size; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/dec/huffman.h b/packages/runtime/vendor/brotli/c/dec/huffman.h new file mode 100644 index 000000000..53daf600e --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/huffman.h @@ -0,0 +1,120 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Utilities for building Huffman decoding tables. */ + +#ifndef BROTLI_DEC_HUFFMAN_H_ +#define BROTLI_DEC_HUFFMAN_H_ + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define BROTLI_HUFFMAN_MAX_CODE_LENGTH 15 + +/* BROTLI_NUM_BLOCK_LEN_SYMBOLS == 26 */ +#define BROTLI_HUFFMAN_MAX_SIZE_26 396 +/* BROTLI_MAX_BLOCK_TYPE_SYMBOLS == 258 */ +#define BROTLI_HUFFMAN_MAX_SIZE_258 632 +/* BROTLI_MAX_CONTEXT_MAP_SYMBOLS == 272 */ +#define BROTLI_HUFFMAN_MAX_SIZE_272 646 + +#define BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH 5 + +#if ((defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_32)) && \ + BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0)) +#define BROTLI_HUFFMAN_CODE_FAST_LOAD +#endif + +#if !defined(BROTLI_HUFFMAN_CODE_FAST_LOAD) +/* Do not create this struct directly - use the ConstructHuffmanCode + * constructor below! */ +typedef struct { + uint8_t bits; /* number of bits used for this symbol */ + uint16_t value; /* symbol value or table offset */ +} HuffmanCode; + +static BROTLI_INLINE HuffmanCode ConstructHuffmanCode(const uint8_t bits, + const uint16_t value) { + HuffmanCode h; + h.bits = bits; + h.value = value; + return h; +} + +/* Please use the following macros to optimize HuffmanCode accesses in hot + * paths. + * + * For example, assuming |table| contains a HuffmanCode pointer: + * + * BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table); + * BROTLI_HC_ADJUST_TABLE_INDEX(table, index_into_table); + * *bits = BROTLI_HC_GET_BITS(table); + * *value = BROTLI_HC_GET_VALUE(table); + * BROTLI_HC_ADJUST_TABLE_INDEX(table, offset); + * *bits2 = BROTLI_HC_GET_BITS(table); + * *value2 = BROTLI_HC_GET_VALUE(table); + * + */ + +#define BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(H) +#define BROTLI_HC_ADJUST_TABLE_INDEX(H, V) H += (V) + +/* These must be given a HuffmanCode pointer! */ +#define BROTLI_HC_FAST_LOAD_BITS(H) (H->bits) +#define BROTLI_HC_FAST_LOAD_VALUE(H) (H->value) + +#else /* BROTLI_HUFFMAN_CODE_FAST_LOAD */ + +typedef BROTLI_ALIGNED(4) uint32_t HuffmanCode; + +static BROTLI_INLINE HuffmanCode ConstructHuffmanCode(const uint8_t bits, + const uint16_t value) { + return (HuffmanCode) ((value & 0xFFFF) << 16) | (bits & 0xFF); +} + +#define BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(H) uint32_t __fastload_##H = (*H) +#define BROTLI_HC_ADJUST_TABLE_INDEX(H, V) H += (V); __fastload_##H = (*H) + +/* These must be given a HuffmanCode pointer! */ +#define BROTLI_HC_FAST_LOAD_BITS(H) ((__fastload_##H) & 0xFF) +#define BROTLI_HC_FAST_LOAD_VALUE(H) ((__fastload_##H) >> 16) +#endif /* BROTLI_HUFFMAN_CODE_FAST_LOAD */ + +/* Builds Huffman lookup table assuming code lengths are in symbol order. */ +BROTLI_INTERNAL void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table, + const uint8_t* const code_lengths, uint16_t* count); + +/* Builds Huffman lookup table assuming code lengths are in symbol order. + Returns size of resulting table. */ +BROTLI_INTERNAL uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table, + int root_bits, const uint16_t* const symbol_lists, uint16_t* count); + +/* Builds a simple Huffman table. The |num_symbols| parameter is to be + interpreted as follows: 0 means 1 symbol, 1 means 2 symbols, + 2 means 3 symbols, 3 means 4 symbols with lengths [2, 2, 2, 2], + 4 means 4 symbols with lengths [1, 2, 3, 3]. */ +BROTLI_INTERNAL uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table, + int root_bits, uint16_t* symbols, uint32_t num_symbols); + +/* Contains a collection of Huffman trees with the same alphabet size. */ +/* alphabet_size_limit is needed due to simple codes, since + log2(alphabet_size_max) could be greater than log2(alphabet_size_limit). */ +typedef struct { + HuffmanCode** htrees; + HuffmanCode* codes; + uint16_t alphabet_size_max; + uint16_t alphabet_size_limit; + uint16_t num_htrees; +} HuffmanTreeGroup; + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_DEC_HUFFMAN_H_ */ diff --git a/packages/runtime/vendor/brotli/c/dec/prefix.c b/packages/runtime/vendor/brotli/c/dec/prefix.c new file mode 100644 index 000000000..ce3998f24 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/prefix.c @@ -0,0 +1,67 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "prefix.h" + +#include "../common/platform.h" /* IWYU pragma: keep */ +#include "../common/static_init.h" + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +#include "../common/constants.h" +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_NONE) +/* Embed kCmdLut. */ +#include "prefix_inc.h" +#else +BROTLI_COLD BROTLI_BOOL BrotliDecoderInitCmdLut(CmdLutElement* items) { + static const uint8_t kInsertLengthExtraBits[24] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0C, 0x0E, 0x18}; + static const uint8_t kCopyLengthExtraBits[24] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x18}; + static const uint8_t kCellPos[11] = {0, 1, 0, 1, 8, 9, 2, 16, 10, 17, 18}; + + uint16_t insert_length_offsets[24]; + uint16_t copy_length_offsets[24]; + insert_length_offsets[0] = 0; + copy_length_offsets[0] = 2; + for (size_t i = 0; i < 23; ++i) { + insert_length_offsets[i + 1] = + insert_length_offsets[i] + (uint16_t)(1u << kInsertLengthExtraBits[i]); + copy_length_offsets[i + 1] = + copy_length_offsets[i] + (uint16_t)(1u << kCopyLengthExtraBits[i]); + } + + for (size_t symbol = 0; symbol < BROTLI_NUM_COMMAND_SYMBOLS; ++symbol) { + CmdLutElement* item = items + symbol; + const size_t cell_idx = symbol >> 6; + const size_t cell_pos = kCellPos[cell_idx]; + const size_t copy_code = ((cell_pos << 3) & 0x18) + (symbol & 0x7); + const uint16_t copy_len_offset = copy_length_offsets[copy_code]; + const size_t insert_code = (cell_pos & 0x18) + ((symbol >> 3) & 0x7); + item->copy_len_extra_bits = kCopyLengthExtraBits[copy_code]; + item->context = (copy_len_offset > 4) ? 3 : ((uint8_t)copy_len_offset - 2); + item->copy_len_offset = copy_len_offset; + item->distance_code = (cell_idx >= 2) ? -1 : 0; + item->insert_len_extra_bits = kInsertLengthExtraBits[insert_code]; + item->insert_len_offset = insert_length_offsets[insert_code]; + } + return BROTLI_TRUE; +} + +BROTLI_MODEL("small") +CmdLutElement kCmdLut[BROTLI_NUM_COMMAND_SYMBOLS]; +#endif /* BROTLI_STATIC_INIT */ + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/dec/prefix.h b/packages/runtime/vendor/brotli/c/dec/prefix.h new file mode 100644 index 000000000..f45b8be40 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/prefix.h @@ -0,0 +1,43 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Lookup tables to map prefix codes to value ranges. This is used during + decoding of the block lengths, literal insertion lengths and copy lengths. */ + +#ifndef BROTLI_DEC_PREFIX_H_ +#define BROTLI_DEC_PREFIX_H_ + +#include "../common/constants.h" +#include "../common/platform.h" /* IWYU pragma: keep */ +#include "../common/static_init.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct CmdLutElement { + uint8_t insert_len_extra_bits; + uint8_t copy_len_extra_bits; + int8_t distance_code; + uint8_t context; + uint16_t insert_len_offset; + uint16_t copy_len_offset; +} CmdLutElement; + +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_NONE) +BROTLI_INTERNAL extern const BROTLI_MODEL("small") + CmdLutElement kCmdLut[BROTLI_NUM_COMMAND_SYMBOLS]; +#else +BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderInitCmdLut(CmdLutElement* items); +BROTLI_INTERNAL extern BROTLI_MODEL("small") + CmdLutElement kCmdLut[BROTLI_NUM_COMMAND_SYMBOLS]; +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_DEC_PREFIX_H_ */ diff --git a/packages/runtime/vendor/brotli/c/dec/prefix_inc.h b/packages/runtime/vendor/brotli/c/dec/prefix_inc.h new file mode 100644 index 000000000..91d81b846 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/prefix_inc.h @@ -0,0 +1,707 @@ +const BROTLI_MODEL("small") +CmdLutElement kCmdLut[BROTLI_NUM_COMMAND_SYMBOLS] = { + { 0x00, 0x00, 0, 0x00, 0x0000, 0x0002 }, + { 0x00, 0x00, 0, 0x01, 0x0000, 0x0003 }, + { 0x00, 0x00, 0, 0x02, 0x0000, 0x0004 }, + { 0x00, 0x00, 0, 0x03, 0x0000, 0x0005 }, + { 0x00, 0x00, 0, 0x03, 0x0000, 0x0006 }, + { 0x00, 0x00, 0, 0x03, 0x0000, 0x0007 }, + { 0x00, 0x00, 0, 0x03, 0x0000, 0x0008 }, + { 0x00, 0x00, 0, 0x03, 0x0000, 0x0009 }, + { 0x00, 0x00, 0, 0x00, 0x0001, 0x0002 }, + { 0x00, 0x00, 0, 0x01, 0x0001, 0x0003 }, + { 0x00, 0x00, 0, 0x02, 0x0001, 0x0004 }, + { 0x00, 0x00, 0, 0x03, 0x0001, 0x0005 }, + { 0x00, 0x00, 0, 0x03, 0x0001, 0x0006 }, + { 0x00, 0x00, 0, 0x03, 0x0001, 0x0007 }, + { 0x00, 0x00, 0, 0x03, 0x0001, 0x0008 }, + { 0x00, 0x00, 0, 0x03, 0x0001, 0x0009 }, + { 0x00, 0x00, 0, 0x00, 0x0002, 0x0002 }, + { 0x00, 0x00, 0, 0x01, 0x0002, 0x0003 }, + { 0x00, 0x00, 0, 0x02, 0x0002, 0x0004 }, + { 0x00, 0x00, 0, 0x03, 0x0002, 0x0005 }, + { 0x00, 0x00, 0, 0x03, 0x0002, 0x0006 }, + { 0x00, 0x00, 0, 0x03, 0x0002, 0x0007 }, + { 0x00, 0x00, 0, 0x03, 0x0002, 0x0008 }, + { 0x00, 0x00, 0, 0x03, 0x0002, 0x0009 }, + { 0x00, 0x00, 0, 0x00, 0x0003, 0x0002 }, + { 0x00, 0x00, 0, 0x01, 0x0003, 0x0003 }, + { 0x00, 0x00, 0, 0x02, 0x0003, 0x0004 }, + { 0x00, 0x00, 0, 0x03, 0x0003, 0x0005 }, + { 0x00, 0x00, 0, 0x03, 0x0003, 0x0006 }, + { 0x00, 0x00, 0, 0x03, 0x0003, 0x0007 }, + { 0x00, 0x00, 0, 0x03, 0x0003, 0x0008 }, + { 0x00, 0x00, 0, 0x03, 0x0003, 0x0009 }, + { 0x00, 0x00, 0, 0x00, 0x0004, 0x0002 }, + { 0x00, 0x00, 0, 0x01, 0x0004, 0x0003 }, + { 0x00, 0x00, 0, 0x02, 0x0004, 0x0004 }, + { 0x00, 0x00, 0, 0x03, 0x0004, 0x0005 }, + { 0x00, 0x00, 0, 0x03, 0x0004, 0x0006 }, + { 0x00, 0x00, 0, 0x03, 0x0004, 0x0007 }, + { 0x00, 0x00, 0, 0x03, 0x0004, 0x0008 }, + { 0x00, 0x00, 0, 0x03, 0x0004, 0x0009 }, + { 0x00, 0x00, 0, 0x00, 0x0005, 0x0002 }, + { 0x00, 0x00, 0, 0x01, 0x0005, 0x0003 }, + { 0x00, 0x00, 0, 0x02, 0x0005, 0x0004 }, + { 0x00, 0x00, 0, 0x03, 0x0005, 0x0005 }, + { 0x00, 0x00, 0, 0x03, 0x0005, 0x0006 }, + { 0x00, 0x00, 0, 0x03, 0x0005, 0x0007 }, + { 0x00, 0x00, 0, 0x03, 0x0005, 0x0008 }, + { 0x00, 0x00, 0, 0x03, 0x0005, 0x0009 }, + { 0x01, 0x00, 0, 0x00, 0x0006, 0x0002 }, + { 0x01, 0x00, 0, 0x01, 0x0006, 0x0003 }, + { 0x01, 0x00, 0, 0x02, 0x0006, 0x0004 }, + { 0x01, 0x00, 0, 0x03, 0x0006, 0x0005 }, + { 0x01, 0x00, 0, 0x03, 0x0006, 0x0006 }, + { 0x01, 0x00, 0, 0x03, 0x0006, 0x0007 }, + { 0x01, 0x00, 0, 0x03, 0x0006, 0x0008 }, + { 0x01, 0x00, 0, 0x03, 0x0006, 0x0009 }, + { 0x01, 0x00, 0, 0x00, 0x0008, 0x0002 }, + { 0x01, 0x00, 0, 0x01, 0x0008, 0x0003 }, + { 0x01, 0x00, 0, 0x02, 0x0008, 0x0004 }, + { 0x01, 0x00, 0, 0x03, 0x0008, 0x0005 }, + { 0x01, 0x00, 0, 0x03, 0x0008, 0x0006 }, + { 0x01, 0x00, 0, 0x03, 0x0008, 0x0007 }, + { 0x01, 0x00, 0, 0x03, 0x0008, 0x0008 }, + { 0x01, 0x00, 0, 0x03, 0x0008, 0x0009 }, + { 0x00, 0x01, 0, 0x03, 0x0000, 0x000a }, + { 0x00, 0x01, 0, 0x03, 0x0000, 0x000c }, + { 0x00, 0x02, 0, 0x03, 0x0000, 0x000e }, + { 0x00, 0x02, 0, 0x03, 0x0000, 0x0012 }, + { 0x00, 0x03, 0, 0x03, 0x0000, 0x0016 }, + { 0x00, 0x03, 0, 0x03, 0x0000, 0x001e }, + { 0x00, 0x04, 0, 0x03, 0x0000, 0x0026 }, + { 0x00, 0x04, 0, 0x03, 0x0000, 0x0036 }, + { 0x00, 0x01, 0, 0x03, 0x0001, 0x000a }, + { 0x00, 0x01, 0, 0x03, 0x0001, 0x000c }, + { 0x00, 0x02, 0, 0x03, 0x0001, 0x000e }, + { 0x00, 0x02, 0, 0x03, 0x0001, 0x0012 }, + { 0x00, 0x03, 0, 0x03, 0x0001, 0x0016 }, + { 0x00, 0x03, 0, 0x03, 0x0001, 0x001e }, + { 0x00, 0x04, 0, 0x03, 0x0001, 0x0026 }, + { 0x00, 0x04, 0, 0x03, 0x0001, 0x0036 }, + { 0x00, 0x01, 0, 0x03, 0x0002, 0x000a }, + { 0x00, 0x01, 0, 0x03, 0x0002, 0x000c }, + { 0x00, 0x02, 0, 0x03, 0x0002, 0x000e }, + { 0x00, 0x02, 0, 0x03, 0x0002, 0x0012 }, + { 0x00, 0x03, 0, 0x03, 0x0002, 0x0016 }, + { 0x00, 0x03, 0, 0x03, 0x0002, 0x001e }, + { 0x00, 0x04, 0, 0x03, 0x0002, 0x0026 }, + { 0x00, 0x04, 0, 0x03, 0x0002, 0x0036 }, + { 0x00, 0x01, 0, 0x03, 0x0003, 0x000a }, + { 0x00, 0x01, 0, 0x03, 0x0003, 0x000c }, + { 0x00, 0x02, 0, 0x03, 0x0003, 0x000e }, + { 0x00, 0x02, 0, 0x03, 0x0003, 0x0012 }, + { 0x00, 0x03, 0, 0x03, 0x0003, 0x0016 }, + { 0x00, 0x03, 0, 0x03, 0x0003, 0x001e }, + { 0x00, 0x04, 0, 0x03, 0x0003, 0x0026 }, + { 0x00, 0x04, 0, 0x03, 0x0003, 0x0036 }, + { 0x00, 0x01, 0, 0x03, 0x0004, 0x000a }, + { 0x00, 0x01, 0, 0x03, 0x0004, 0x000c }, + { 0x00, 0x02, 0, 0x03, 0x0004, 0x000e }, + { 0x00, 0x02, 0, 0x03, 0x0004, 0x0012 }, + { 0x00, 0x03, 0, 0x03, 0x0004, 0x0016 }, + { 0x00, 0x03, 0, 0x03, 0x0004, 0x001e }, + { 0x00, 0x04, 0, 0x03, 0x0004, 0x0026 }, + { 0x00, 0x04, 0, 0x03, 0x0004, 0x0036 }, + { 0x00, 0x01, 0, 0x03, 0x0005, 0x000a }, + { 0x00, 0x01, 0, 0x03, 0x0005, 0x000c }, + { 0x00, 0x02, 0, 0x03, 0x0005, 0x000e }, + { 0x00, 0x02, 0, 0x03, 0x0005, 0x0012 }, + { 0x00, 0x03, 0, 0x03, 0x0005, 0x0016 }, + { 0x00, 0x03, 0, 0x03, 0x0005, 0x001e }, + { 0x00, 0x04, 0, 0x03, 0x0005, 0x0026 }, + { 0x00, 0x04, 0, 0x03, 0x0005, 0x0036 }, + { 0x01, 0x01, 0, 0x03, 0x0006, 0x000a }, + { 0x01, 0x01, 0, 0x03, 0x0006, 0x000c }, + { 0x01, 0x02, 0, 0x03, 0x0006, 0x000e }, + { 0x01, 0x02, 0, 0x03, 0x0006, 0x0012 }, + { 0x01, 0x03, 0, 0x03, 0x0006, 0x0016 }, + { 0x01, 0x03, 0, 0x03, 0x0006, 0x001e }, + { 0x01, 0x04, 0, 0x03, 0x0006, 0x0026 }, + { 0x01, 0x04, 0, 0x03, 0x0006, 0x0036 }, + { 0x01, 0x01, 0, 0x03, 0x0008, 0x000a }, + { 0x01, 0x01, 0, 0x03, 0x0008, 0x000c }, + { 0x01, 0x02, 0, 0x03, 0x0008, 0x000e }, + { 0x01, 0x02, 0, 0x03, 0x0008, 0x0012 }, + { 0x01, 0x03, 0, 0x03, 0x0008, 0x0016 }, + { 0x01, 0x03, 0, 0x03, 0x0008, 0x001e }, + { 0x01, 0x04, 0, 0x03, 0x0008, 0x0026 }, + { 0x01, 0x04, 0, 0x03, 0x0008, 0x0036 }, + { 0x00, 0x00, -1, 0x00, 0x0000, 0x0002 }, + { 0x00, 0x00, -1, 0x01, 0x0000, 0x0003 }, + { 0x00, 0x00, -1, 0x02, 0x0000, 0x0004 }, + { 0x00, 0x00, -1, 0x03, 0x0000, 0x0005 }, + { 0x00, 0x00, -1, 0x03, 0x0000, 0x0006 }, + { 0x00, 0x00, -1, 0x03, 0x0000, 0x0007 }, + { 0x00, 0x00, -1, 0x03, 0x0000, 0x0008 }, + { 0x00, 0x00, -1, 0x03, 0x0000, 0x0009 }, + { 0x00, 0x00, -1, 0x00, 0x0001, 0x0002 }, + { 0x00, 0x00, -1, 0x01, 0x0001, 0x0003 }, + { 0x00, 0x00, -1, 0x02, 0x0001, 0x0004 }, + { 0x00, 0x00, -1, 0x03, 0x0001, 0x0005 }, + { 0x00, 0x00, -1, 0x03, 0x0001, 0x0006 }, + { 0x00, 0x00, -1, 0x03, 0x0001, 0x0007 }, + { 0x00, 0x00, -1, 0x03, 0x0001, 0x0008 }, + { 0x00, 0x00, -1, 0x03, 0x0001, 0x0009 }, + { 0x00, 0x00, -1, 0x00, 0x0002, 0x0002 }, + { 0x00, 0x00, -1, 0x01, 0x0002, 0x0003 }, + { 0x00, 0x00, -1, 0x02, 0x0002, 0x0004 }, + { 0x00, 0x00, -1, 0x03, 0x0002, 0x0005 }, + { 0x00, 0x00, -1, 0x03, 0x0002, 0x0006 }, + { 0x00, 0x00, -1, 0x03, 0x0002, 0x0007 }, + { 0x00, 0x00, -1, 0x03, 0x0002, 0x0008 }, + { 0x00, 0x00, -1, 0x03, 0x0002, 0x0009 }, + { 0x00, 0x00, -1, 0x00, 0x0003, 0x0002 }, + { 0x00, 0x00, -1, 0x01, 0x0003, 0x0003 }, + { 0x00, 0x00, -1, 0x02, 0x0003, 0x0004 }, + { 0x00, 0x00, -1, 0x03, 0x0003, 0x0005 }, + { 0x00, 0x00, -1, 0x03, 0x0003, 0x0006 }, + { 0x00, 0x00, -1, 0x03, 0x0003, 0x0007 }, + { 0x00, 0x00, -1, 0x03, 0x0003, 0x0008 }, + { 0x00, 0x00, -1, 0x03, 0x0003, 0x0009 }, + { 0x00, 0x00, -1, 0x00, 0x0004, 0x0002 }, + { 0x00, 0x00, -1, 0x01, 0x0004, 0x0003 }, + { 0x00, 0x00, -1, 0x02, 0x0004, 0x0004 }, + { 0x00, 0x00, -1, 0x03, 0x0004, 0x0005 }, + { 0x00, 0x00, -1, 0x03, 0x0004, 0x0006 }, + { 0x00, 0x00, -1, 0x03, 0x0004, 0x0007 }, + { 0x00, 0x00, -1, 0x03, 0x0004, 0x0008 }, + { 0x00, 0x00, -1, 0x03, 0x0004, 0x0009 }, + { 0x00, 0x00, -1, 0x00, 0x0005, 0x0002 }, + { 0x00, 0x00, -1, 0x01, 0x0005, 0x0003 }, + { 0x00, 0x00, -1, 0x02, 0x0005, 0x0004 }, + { 0x00, 0x00, -1, 0x03, 0x0005, 0x0005 }, + { 0x00, 0x00, -1, 0x03, 0x0005, 0x0006 }, + { 0x00, 0x00, -1, 0x03, 0x0005, 0x0007 }, + { 0x00, 0x00, -1, 0x03, 0x0005, 0x0008 }, + { 0x00, 0x00, -1, 0x03, 0x0005, 0x0009 }, + { 0x01, 0x00, -1, 0x00, 0x0006, 0x0002 }, + { 0x01, 0x00, -1, 0x01, 0x0006, 0x0003 }, + { 0x01, 0x00, -1, 0x02, 0x0006, 0x0004 }, + { 0x01, 0x00, -1, 0x03, 0x0006, 0x0005 }, + { 0x01, 0x00, -1, 0x03, 0x0006, 0x0006 }, + { 0x01, 0x00, -1, 0x03, 0x0006, 0x0007 }, + { 0x01, 0x00, -1, 0x03, 0x0006, 0x0008 }, + { 0x01, 0x00, -1, 0x03, 0x0006, 0x0009 }, + { 0x01, 0x00, -1, 0x00, 0x0008, 0x0002 }, + { 0x01, 0x00, -1, 0x01, 0x0008, 0x0003 }, + { 0x01, 0x00, -1, 0x02, 0x0008, 0x0004 }, + { 0x01, 0x00, -1, 0x03, 0x0008, 0x0005 }, + { 0x01, 0x00, -1, 0x03, 0x0008, 0x0006 }, + { 0x01, 0x00, -1, 0x03, 0x0008, 0x0007 }, + { 0x01, 0x00, -1, 0x03, 0x0008, 0x0008 }, + { 0x01, 0x00, -1, 0x03, 0x0008, 0x0009 }, + { 0x00, 0x01, -1, 0x03, 0x0000, 0x000a }, + { 0x00, 0x01, -1, 0x03, 0x0000, 0x000c }, + { 0x00, 0x02, -1, 0x03, 0x0000, 0x000e }, + { 0x00, 0x02, -1, 0x03, 0x0000, 0x0012 }, + { 0x00, 0x03, -1, 0x03, 0x0000, 0x0016 }, + { 0x00, 0x03, -1, 0x03, 0x0000, 0x001e }, + { 0x00, 0x04, -1, 0x03, 0x0000, 0x0026 }, + { 0x00, 0x04, -1, 0x03, 0x0000, 0x0036 }, + { 0x00, 0x01, -1, 0x03, 0x0001, 0x000a }, + { 0x00, 0x01, -1, 0x03, 0x0001, 0x000c }, + { 0x00, 0x02, -1, 0x03, 0x0001, 0x000e }, + { 0x00, 0x02, -1, 0x03, 0x0001, 0x0012 }, + { 0x00, 0x03, -1, 0x03, 0x0001, 0x0016 }, + { 0x00, 0x03, -1, 0x03, 0x0001, 0x001e }, + { 0x00, 0x04, -1, 0x03, 0x0001, 0x0026 }, + { 0x00, 0x04, -1, 0x03, 0x0001, 0x0036 }, + { 0x00, 0x01, -1, 0x03, 0x0002, 0x000a }, + { 0x00, 0x01, -1, 0x03, 0x0002, 0x000c }, + { 0x00, 0x02, -1, 0x03, 0x0002, 0x000e }, + { 0x00, 0x02, -1, 0x03, 0x0002, 0x0012 }, + { 0x00, 0x03, -1, 0x03, 0x0002, 0x0016 }, + { 0x00, 0x03, -1, 0x03, 0x0002, 0x001e }, + { 0x00, 0x04, -1, 0x03, 0x0002, 0x0026 }, + { 0x00, 0x04, -1, 0x03, 0x0002, 0x0036 }, + { 0x00, 0x01, -1, 0x03, 0x0003, 0x000a }, + { 0x00, 0x01, -1, 0x03, 0x0003, 0x000c }, + { 0x00, 0x02, -1, 0x03, 0x0003, 0x000e }, + { 0x00, 0x02, -1, 0x03, 0x0003, 0x0012 }, + { 0x00, 0x03, -1, 0x03, 0x0003, 0x0016 }, + { 0x00, 0x03, -1, 0x03, 0x0003, 0x001e }, + { 0x00, 0x04, -1, 0x03, 0x0003, 0x0026 }, + { 0x00, 0x04, -1, 0x03, 0x0003, 0x0036 }, + { 0x00, 0x01, -1, 0x03, 0x0004, 0x000a }, + { 0x00, 0x01, -1, 0x03, 0x0004, 0x000c }, + { 0x00, 0x02, -1, 0x03, 0x0004, 0x000e }, + { 0x00, 0x02, -1, 0x03, 0x0004, 0x0012 }, + { 0x00, 0x03, -1, 0x03, 0x0004, 0x0016 }, + { 0x00, 0x03, -1, 0x03, 0x0004, 0x001e }, + { 0x00, 0x04, -1, 0x03, 0x0004, 0x0026 }, + { 0x00, 0x04, -1, 0x03, 0x0004, 0x0036 }, + { 0x00, 0x01, -1, 0x03, 0x0005, 0x000a }, + { 0x00, 0x01, -1, 0x03, 0x0005, 0x000c }, + { 0x00, 0x02, -1, 0x03, 0x0005, 0x000e }, + { 0x00, 0x02, -1, 0x03, 0x0005, 0x0012 }, + { 0x00, 0x03, -1, 0x03, 0x0005, 0x0016 }, + { 0x00, 0x03, -1, 0x03, 0x0005, 0x001e }, + { 0x00, 0x04, -1, 0x03, 0x0005, 0x0026 }, + { 0x00, 0x04, -1, 0x03, 0x0005, 0x0036 }, + { 0x01, 0x01, -1, 0x03, 0x0006, 0x000a }, + { 0x01, 0x01, -1, 0x03, 0x0006, 0x000c }, + { 0x01, 0x02, -1, 0x03, 0x0006, 0x000e }, + { 0x01, 0x02, -1, 0x03, 0x0006, 0x0012 }, + { 0x01, 0x03, -1, 0x03, 0x0006, 0x0016 }, + { 0x01, 0x03, -1, 0x03, 0x0006, 0x001e }, + { 0x01, 0x04, -1, 0x03, 0x0006, 0x0026 }, + { 0x01, 0x04, -1, 0x03, 0x0006, 0x0036 }, + { 0x01, 0x01, -1, 0x03, 0x0008, 0x000a }, + { 0x01, 0x01, -1, 0x03, 0x0008, 0x000c }, + { 0x01, 0x02, -1, 0x03, 0x0008, 0x000e }, + { 0x01, 0x02, -1, 0x03, 0x0008, 0x0012 }, + { 0x01, 0x03, -1, 0x03, 0x0008, 0x0016 }, + { 0x01, 0x03, -1, 0x03, 0x0008, 0x001e }, + { 0x01, 0x04, -1, 0x03, 0x0008, 0x0026 }, + { 0x01, 0x04, -1, 0x03, 0x0008, 0x0036 }, + { 0x02, 0x00, -1, 0x00, 0x000a, 0x0002 }, + { 0x02, 0x00, -1, 0x01, 0x000a, 0x0003 }, + { 0x02, 0x00, -1, 0x02, 0x000a, 0x0004 }, + { 0x02, 0x00, -1, 0x03, 0x000a, 0x0005 }, + { 0x02, 0x00, -1, 0x03, 0x000a, 0x0006 }, + { 0x02, 0x00, -1, 0x03, 0x000a, 0x0007 }, + { 0x02, 0x00, -1, 0x03, 0x000a, 0x0008 }, + { 0x02, 0x00, -1, 0x03, 0x000a, 0x0009 }, + { 0x02, 0x00, -1, 0x00, 0x000e, 0x0002 }, + { 0x02, 0x00, -1, 0x01, 0x000e, 0x0003 }, + { 0x02, 0x00, -1, 0x02, 0x000e, 0x0004 }, + { 0x02, 0x00, -1, 0x03, 0x000e, 0x0005 }, + { 0x02, 0x00, -1, 0x03, 0x000e, 0x0006 }, + { 0x02, 0x00, -1, 0x03, 0x000e, 0x0007 }, + { 0x02, 0x00, -1, 0x03, 0x000e, 0x0008 }, + { 0x02, 0x00, -1, 0x03, 0x000e, 0x0009 }, + { 0x03, 0x00, -1, 0x00, 0x0012, 0x0002 }, + { 0x03, 0x00, -1, 0x01, 0x0012, 0x0003 }, + { 0x03, 0x00, -1, 0x02, 0x0012, 0x0004 }, + { 0x03, 0x00, -1, 0x03, 0x0012, 0x0005 }, + { 0x03, 0x00, -1, 0x03, 0x0012, 0x0006 }, + { 0x03, 0x00, -1, 0x03, 0x0012, 0x0007 }, + { 0x03, 0x00, -1, 0x03, 0x0012, 0x0008 }, + { 0x03, 0x00, -1, 0x03, 0x0012, 0x0009 }, + { 0x03, 0x00, -1, 0x00, 0x001a, 0x0002 }, + { 0x03, 0x00, -1, 0x01, 0x001a, 0x0003 }, + { 0x03, 0x00, -1, 0x02, 0x001a, 0x0004 }, + { 0x03, 0x00, -1, 0x03, 0x001a, 0x0005 }, + { 0x03, 0x00, -1, 0x03, 0x001a, 0x0006 }, + { 0x03, 0x00, -1, 0x03, 0x001a, 0x0007 }, + { 0x03, 0x00, -1, 0x03, 0x001a, 0x0008 }, + { 0x03, 0x00, -1, 0x03, 0x001a, 0x0009 }, + { 0x04, 0x00, -1, 0x00, 0x0022, 0x0002 }, + { 0x04, 0x00, -1, 0x01, 0x0022, 0x0003 }, + { 0x04, 0x00, -1, 0x02, 0x0022, 0x0004 }, + { 0x04, 0x00, -1, 0x03, 0x0022, 0x0005 }, + { 0x04, 0x00, -1, 0x03, 0x0022, 0x0006 }, + { 0x04, 0x00, -1, 0x03, 0x0022, 0x0007 }, + { 0x04, 0x00, -1, 0x03, 0x0022, 0x0008 }, + { 0x04, 0x00, -1, 0x03, 0x0022, 0x0009 }, + { 0x04, 0x00, -1, 0x00, 0x0032, 0x0002 }, + { 0x04, 0x00, -1, 0x01, 0x0032, 0x0003 }, + { 0x04, 0x00, -1, 0x02, 0x0032, 0x0004 }, + { 0x04, 0x00, -1, 0x03, 0x0032, 0x0005 }, + { 0x04, 0x00, -1, 0x03, 0x0032, 0x0006 }, + { 0x04, 0x00, -1, 0x03, 0x0032, 0x0007 }, + { 0x04, 0x00, -1, 0x03, 0x0032, 0x0008 }, + { 0x04, 0x00, -1, 0x03, 0x0032, 0x0009 }, + { 0x05, 0x00, -1, 0x00, 0x0042, 0x0002 }, + { 0x05, 0x00, -1, 0x01, 0x0042, 0x0003 }, + { 0x05, 0x00, -1, 0x02, 0x0042, 0x0004 }, + { 0x05, 0x00, -1, 0x03, 0x0042, 0x0005 }, + { 0x05, 0x00, -1, 0x03, 0x0042, 0x0006 }, + { 0x05, 0x00, -1, 0x03, 0x0042, 0x0007 }, + { 0x05, 0x00, -1, 0x03, 0x0042, 0x0008 }, + { 0x05, 0x00, -1, 0x03, 0x0042, 0x0009 }, + { 0x05, 0x00, -1, 0x00, 0x0062, 0x0002 }, + { 0x05, 0x00, -1, 0x01, 0x0062, 0x0003 }, + { 0x05, 0x00, -1, 0x02, 0x0062, 0x0004 }, + { 0x05, 0x00, -1, 0x03, 0x0062, 0x0005 }, + { 0x05, 0x00, -1, 0x03, 0x0062, 0x0006 }, + { 0x05, 0x00, -1, 0x03, 0x0062, 0x0007 }, + { 0x05, 0x00, -1, 0x03, 0x0062, 0x0008 }, + { 0x05, 0x00, -1, 0x03, 0x0062, 0x0009 }, + { 0x02, 0x01, -1, 0x03, 0x000a, 0x000a }, + { 0x02, 0x01, -1, 0x03, 0x000a, 0x000c }, + { 0x02, 0x02, -1, 0x03, 0x000a, 0x000e }, + { 0x02, 0x02, -1, 0x03, 0x000a, 0x0012 }, + { 0x02, 0x03, -1, 0x03, 0x000a, 0x0016 }, + { 0x02, 0x03, -1, 0x03, 0x000a, 0x001e }, + { 0x02, 0x04, -1, 0x03, 0x000a, 0x0026 }, + { 0x02, 0x04, -1, 0x03, 0x000a, 0x0036 }, + { 0x02, 0x01, -1, 0x03, 0x000e, 0x000a }, + { 0x02, 0x01, -1, 0x03, 0x000e, 0x000c }, + { 0x02, 0x02, -1, 0x03, 0x000e, 0x000e }, + { 0x02, 0x02, -1, 0x03, 0x000e, 0x0012 }, + { 0x02, 0x03, -1, 0x03, 0x000e, 0x0016 }, + { 0x02, 0x03, -1, 0x03, 0x000e, 0x001e }, + { 0x02, 0x04, -1, 0x03, 0x000e, 0x0026 }, + { 0x02, 0x04, -1, 0x03, 0x000e, 0x0036 }, + { 0x03, 0x01, -1, 0x03, 0x0012, 0x000a }, + { 0x03, 0x01, -1, 0x03, 0x0012, 0x000c }, + { 0x03, 0x02, -1, 0x03, 0x0012, 0x000e }, + { 0x03, 0x02, -1, 0x03, 0x0012, 0x0012 }, + { 0x03, 0x03, -1, 0x03, 0x0012, 0x0016 }, + { 0x03, 0x03, -1, 0x03, 0x0012, 0x001e }, + { 0x03, 0x04, -1, 0x03, 0x0012, 0x0026 }, + { 0x03, 0x04, -1, 0x03, 0x0012, 0x0036 }, + { 0x03, 0x01, -1, 0x03, 0x001a, 0x000a }, + { 0x03, 0x01, -1, 0x03, 0x001a, 0x000c }, + { 0x03, 0x02, -1, 0x03, 0x001a, 0x000e }, + { 0x03, 0x02, -1, 0x03, 0x001a, 0x0012 }, + { 0x03, 0x03, -1, 0x03, 0x001a, 0x0016 }, + { 0x03, 0x03, -1, 0x03, 0x001a, 0x001e }, + { 0x03, 0x04, -1, 0x03, 0x001a, 0x0026 }, + { 0x03, 0x04, -1, 0x03, 0x001a, 0x0036 }, + { 0x04, 0x01, -1, 0x03, 0x0022, 0x000a }, + { 0x04, 0x01, -1, 0x03, 0x0022, 0x000c }, + { 0x04, 0x02, -1, 0x03, 0x0022, 0x000e }, + { 0x04, 0x02, -1, 0x03, 0x0022, 0x0012 }, + { 0x04, 0x03, -1, 0x03, 0x0022, 0x0016 }, + { 0x04, 0x03, -1, 0x03, 0x0022, 0x001e }, + { 0x04, 0x04, -1, 0x03, 0x0022, 0x0026 }, + { 0x04, 0x04, -1, 0x03, 0x0022, 0x0036 }, + { 0x04, 0x01, -1, 0x03, 0x0032, 0x000a }, + { 0x04, 0x01, -1, 0x03, 0x0032, 0x000c }, + { 0x04, 0x02, -1, 0x03, 0x0032, 0x000e }, + { 0x04, 0x02, -1, 0x03, 0x0032, 0x0012 }, + { 0x04, 0x03, -1, 0x03, 0x0032, 0x0016 }, + { 0x04, 0x03, -1, 0x03, 0x0032, 0x001e }, + { 0x04, 0x04, -1, 0x03, 0x0032, 0x0026 }, + { 0x04, 0x04, -1, 0x03, 0x0032, 0x0036 }, + { 0x05, 0x01, -1, 0x03, 0x0042, 0x000a }, + { 0x05, 0x01, -1, 0x03, 0x0042, 0x000c }, + { 0x05, 0x02, -1, 0x03, 0x0042, 0x000e }, + { 0x05, 0x02, -1, 0x03, 0x0042, 0x0012 }, + { 0x05, 0x03, -1, 0x03, 0x0042, 0x0016 }, + { 0x05, 0x03, -1, 0x03, 0x0042, 0x001e }, + { 0x05, 0x04, -1, 0x03, 0x0042, 0x0026 }, + { 0x05, 0x04, -1, 0x03, 0x0042, 0x0036 }, + { 0x05, 0x01, -1, 0x03, 0x0062, 0x000a }, + { 0x05, 0x01, -1, 0x03, 0x0062, 0x000c }, + { 0x05, 0x02, -1, 0x03, 0x0062, 0x000e }, + { 0x05, 0x02, -1, 0x03, 0x0062, 0x0012 }, + { 0x05, 0x03, -1, 0x03, 0x0062, 0x0016 }, + { 0x05, 0x03, -1, 0x03, 0x0062, 0x001e }, + { 0x05, 0x04, -1, 0x03, 0x0062, 0x0026 }, + { 0x05, 0x04, -1, 0x03, 0x0062, 0x0036 }, + { 0x00, 0x05, -1, 0x03, 0x0000, 0x0046 }, + { 0x00, 0x05, -1, 0x03, 0x0000, 0x0066 }, + { 0x00, 0x06, -1, 0x03, 0x0000, 0x0086 }, + { 0x00, 0x07, -1, 0x03, 0x0000, 0x00c6 }, + { 0x00, 0x08, -1, 0x03, 0x0000, 0x0146 }, + { 0x00, 0x09, -1, 0x03, 0x0000, 0x0246 }, + { 0x00, 0x0a, -1, 0x03, 0x0000, 0x0446 }, + { 0x00, 0x18, -1, 0x03, 0x0000, 0x0846 }, + { 0x00, 0x05, -1, 0x03, 0x0001, 0x0046 }, + { 0x00, 0x05, -1, 0x03, 0x0001, 0x0066 }, + { 0x00, 0x06, -1, 0x03, 0x0001, 0x0086 }, + { 0x00, 0x07, -1, 0x03, 0x0001, 0x00c6 }, + { 0x00, 0x08, -1, 0x03, 0x0001, 0x0146 }, + { 0x00, 0x09, -1, 0x03, 0x0001, 0x0246 }, + { 0x00, 0x0a, -1, 0x03, 0x0001, 0x0446 }, + { 0x00, 0x18, -1, 0x03, 0x0001, 0x0846 }, + { 0x00, 0x05, -1, 0x03, 0x0002, 0x0046 }, + { 0x00, 0x05, -1, 0x03, 0x0002, 0x0066 }, + { 0x00, 0x06, -1, 0x03, 0x0002, 0x0086 }, + { 0x00, 0x07, -1, 0x03, 0x0002, 0x00c6 }, + { 0x00, 0x08, -1, 0x03, 0x0002, 0x0146 }, + { 0x00, 0x09, -1, 0x03, 0x0002, 0x0246 }, + { 0x00, 0x0a, -1, 0x03, 0x0002, 0x0446 }, + { 0x00, 0x18, -1, 0x03, 0x0002, 0x0846 }, + { 0x00, 0x05, -1, 0x03, 0x0003, 0x0046 }, + { 0x00, 0x05, -1, 0x03, 0x0003, 0x0066 }, + { 0x00, 0x06, -1, 0x03, 0x0003, 0x0086 }, + { 0x00, 0x07, -1, 0x03, 0x0003, 0x00c6 }, + { 0x00, 0x08, -1, 0x03, 0x0003, 0x0146 }, + { 0x00, 0x09, -1, 0x03, 0x0003, 0x0246 }, + { 0x00, 0x0a, -1, 0x03, 0x0003, 0x0446 }, + { 0x00, 0x18, -1, 0x03, 0x0003, 0x0846 }, + { 0x00, 0x05, -1, 0x03, 0x0004, 0x0046 }, + { 0x00, 0x05, -1, 0x03, 0x0004, 0x0066 }, + { 0x00, 0x06, -1, 0x03, 0x0004, 0x0086 }, + { 0x00, 0x07, -1, 0x03, 0x0004, 0x00c6 }, + { 0x00, 0x08, -1, 0x03, 0x0004, 0x0146 }, + { 0x00, 0x09, -1, 0x03, 0x0004, 0x0246 }, + { 0x00, 0x0a, -1, 0x03, 0x0004, 0x0446 }, + { 0x00, 0x18, -1, 0x03, 0x0004, 0x0846 }, + { 0x00, 0x05, -1, 0x03, 0x0005, 0x0046 }, + { 0x00, 0x05, -1, 0x03, 0x0005, 0x0066 }, + { 0x00, 0x06, -1, 0x03, 0x0005, 0x0086 }, + { 0x00, 0x07, -1, 0x03, 0x0005, 0x00c6 }, + { 0x00, 0x08, -1, 0x03, 0x0005, 0x0146 }, + { 0x00, 0x09, -1, 0x03, 0x0005, 0x0246 }, + { 0x00, 0x0a, -1, 0x03, 0x0005, 0x0446 }, + { 0x00, 0x18, -1, 0x03, 0x0005, 0x0846 }, + { 0x01, 0x05, -1, 0x03, 0x0006, 0x0046 }, + { 0x01, 0x05, -1, 0x03, 0x0006, 0x0066 }, + { 0x01, 0x06, -1, 0x03, 0x0006, 0x0086 }, + { 0x01, 0x07, -1, 0x03, 0x0006, 0x00c6 }, + { 0x01, 0x08, -1, 0x03, 0x0006, 0x0146 }, + { 0x01, 0x09, -1, 0x03, 0x0006, 0x0246 }, + { 0x01, 0x0a, -1, 0x03, 0x0006, 0x0446 }, + { 0x01, 0x18, -1, 0x03, 0x0006, 0x0846 }, + { 0x01, 0x05, -1, 0x03, 0x0008, 0x0046 }, + { 0x01, 0x05, -1, 0x03, 0x0008, 0x0066 }, + { 0x01, 0x06, -1, 0x03, 0x0008, 0x0086 }, + { 0x01, 0x07, -1, 0x03, 0x0008, 0x00c6 }, + { 0x01, 0x08, -1, 0x03, 0x0008, 0x0146 }, + { 0x01, 0x09, -1, 0x03, 0x0008, 0x0246 }, + { 0x01, 0x0a, -1, 0x03, 0x0008, 0x0446 }, + { 0x01, 0x18, -1, 0x03, 0x0008, 0x0846 }, + { 0x06, 0x00, -1, 0x00, 0x0082, 0x0002 }, + { 0x06, 0x00, -1, 0x01, 0x0082, 0x0003 }, + { 0x06, 0x00, -1, 0x02, 0x0082, 0x0004 }, + { 0x06, 0x00, -1, 0x03, 0x0082, 0x0005 }, + { 0x06, 0x00, -1, 0x03, 0x0082, 0x0006 }, + { 0x06, 0x00, -1, 0x03, 0x0082, 0x0007 }, + { 0x06, 0x00, -1, 0x03, 0x0082, 0x0008 }, + { 0x06, 0x00, -1, 0x03, 0x0082, 0x0009 }, + { 0x07, 0x00, -1, 0x00, 0x00c2, 0x0002 }, + { 0x07, 0x00, -1, 0x01, 0x00c2, 0x0003 }, + { 0x07, 0x00, -1, 0x02, 0x00c2, 0x0004 }, + { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0005 }, + { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0006 }, + { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0007 }, + { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0008 }, + { 0x07, 0x00, -1, 0x03, 0x00c2, 0x0009 }, + { 0x08, 0x00, -1, 0x00, 0x0142, 0x0002 }, + { 0x08, 0x00, -1, 0x01, 0x0142, 0x0003 }, + { 0x08, 0x00, -1, 0x02, 0x0142, 0x0004 }, + { 0x08, 0x00, -1, 0x03, 0x0142, 0x0005 }, + { 0x08, 0x00, -1, 0x03, 0x0142, 0x0006 }, + { 0x08, 0x00, -1, 0x03, 0x0142, 0x0007 }, + { 0x08, 0x00, -1, 0x03, 0x0142, 0x0008 }, + { 0x08, 0x00, -1, 0x03, 0x0142, 0x0009 }, + { 0x09, 0x00, -1, 0x00, 0x0242, 0x0002 }, + { 0x09, 0x00, -1, 0x01, 0x0242, 0x0003 }, + { 0x09, 0x00, -1, 0x02, 0x0242, 0x0004 }, + { 0x09, 0x00, -1, 0x03, 0x0242, 0x0005 }, + { 0x09, 0x00, -1, 0x03, 0x0242, 0x0006 }, + { 0x09, 0x00, -1, 0x03, 0x0242, 0x0007 }, + { 0x09, 0x00, -1, 0x03, 0x0242, 0x0008 }, + { 0x09, 0x00, -1, 0x03, 0x0242, 0x0009 }, + { 0x0a, 0x00, -1, 0x00, 0x0442, 0x0002 }, + { 0x0a, 0x00, -1, 0x01, 0x0442, 0x0003 }, + { 0x0a, 0x00, -1, 0x02, 0x0442, 0x0004 }, + { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0005 }, + { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0006 }, + { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0007 }, + { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0008 }, + { 0x0a, 0x00, -1, 0x03, 0x0442, 0x0009 }, + { 0x0c, 0x00, -1, 0x00, 0x0842, 0x0002 }, + { 0x0c, 0x00, -1, 0x01, 0x0842, 0x0003 }, + { 0x0c, 0x00, -1, 0x02, 0x0842, 0x0004 }, + { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0005 }, + { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0006 }, + { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0007 }, + { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0008 }, + { 0x0c, 0x00, -1, 0x03, 0x0842, 0x0009 }, + { 0x0e, 0x00, -1, 0x00, 0x1842, 0x0002 }, + { 0x0e, 0x00, -1, 0x01, 0x1842, 0x0003 }, + { 0x0e, 0x00, -1, 0x02, 0x1842, 0x0004 }, + { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0005 }, + { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0006 }, + { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0007 }, + { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0008 }, + { 0x0e, 0x00, -1, 0x03, 0x1842, 0x0009 }, + { 0x18, 0x00, -1, 0x00, 0x5842, 0x0002 }, + { 0x18, 0x00, -1, 0x01, 0x5842, 0x0003 }, + { 0x18, 0x00, -1, 0x02, 0x5842, 0x0004 }, + { 0x18, 0x00, -1, 0x03, 0x5842, 0x0005 }, + { 0x18, 0x00, -1, 0x03, 0x5842, 0x0006 }, + { 0x18, 0x00, -1, 0x03, 0x5842, 0x0007 }, + { 0x18, 0x00, -1, 0x03, 0x5842, 0x0008 }, + { 0x18, 0x00, -1, 0x03, 0x5842, 0x0009 }, + { 0x02, 0x05, -1, 0x03, 0x000a, 0x0046 }, + { 0x02, 0x05, -1, 0x03, 0x000a, 0x0066 }, + { 0x02, 0x06, -1, 0x03, 0x000a, 0x0086 }, + { 0x02, 0x07, -1, 0x03, 0x000a, 0x00c6 }, + { 0x02, 0x08, -1, 0x03, 0x000a, 0x0146 }, + { 0x02, 0x09, -1, 0x03, 0x000a, 0x0246 }, + { 0x02, 0x0a, -1, 0x03, 0x000a, 0x0446 }, + { 0x02, 0x18, -1, 0x03, 0x000a, 0x0846 }, + { 0x02, 0x05, -1, 0x03, 0x000e, 0x0046 }, + { 0x02, 0x05, -1, 0x03, 0x000e, 0x0066 }, + { 0x02, 0x06, -1, 0x03, 0x000e, 0x0086 }, + { 0x02, 0x07, -1, 0x03, 0x000e, 0x00c6 }, + { 0x02, 0x08, -1, 0x03, 0x000e, 0x0146 }, + { 0x02, 0x09, -1, 0x03, 0x000e, 0x0246 }, + { 0x02, 0x0a, -1, 0x03, 0x000e, 0x0446 }, + { 0x02, 0x18, -1, 0x03, 0x000e, 0x0846 }, + { 0x03, 0x05, -1, 0x03, 0x0012, 0x0046 }, + { 0x03, 0x05, -1, 0x03, 0x0012, 0x0066 }, + { 0x03, 0x06, -1, 0x03, 0x0012, 0x0086 }, + { 0x03, 0x07, -1, 0x03, 0x0012, 0x00c6 }, + { 0x03, 0x08, -1, 0x03, 0x0012, 0x0146 }, + { 0x03, 0x09, -1, 0x03, 0x0012, 0x0246 }, + { 0x03, 0x0a, -1, 0x03, 0x0012, 0x0446 }, + { 0x03, 0x18, -1, 0x03, 0x0012, 0x0846 }, + { 0x03, 0x05, -1, 0x03, 0x001a, 0x0046 }, + { 0x03, 0x05, -1, 0x03, 0x001a, 0x0066 }, + { 0x03, 0x06, -1, 0x03, 0x001a, 0x0086 }, + { 0x03, 0x07, -1, 0x03, 0x001a, 0x00c6 }, + { 0x03, 0x08, -1, 0x03, 0x001a, 0x0146 }, + { 0x03, 0x09, -1, 0x03, 0x001a, 0x0246 }, + { 0x03, 0x0a, -1, 0x03, 0x001a, 0x0446 }, + { 0x03, 0x18, -1, 0x03, 0x001a, 0x0846 }, + { 0x04, 0x05, -1, 0x03, 0x0022, 0x0046 }, + { 0x04, 0x05, -1, 0x03, 0x0022, 0x0066 }, + { 0x04, 0x06, -1, 0x03, 0x0022, 0x0086 }, + { 0x04, 0x07, -1, 0x03, 0x0022, 0x00c6 }, + { 0x04, 0x08, -1, 0x03, 0x0022, 0x0146 }, + { 0x04, 0x09, -1, 0x03, 0x0022, 0x0246 }, + { 0x04, 0x0a, -1, 0x03, 0x0022, 0x0446 }, + { 0x04, 0x18, -1, 0x03, 0x0022, 0x0846 }, + { 0x04, 0x05, -1, 0x03, 0x0032, 0x0046 }, + { 0x04, 0x05, -1, 0x03, 0x0032, 0x0066 }, + { 0x04, 0x06, -1, 0x03, 0x0032, 0x0086 }, + { 0x04, 0x07, -1, 0x03, 0x0032, 0x00c6 }, + { 0x04, 0x08, -1, 0x03, 0x0032, 0x0146 }, + { 0x04, 0x09, -1, 0x03, 0x0032, 0x0246 }, + { 0x04, 0x0a, -1, 0x03, 0x0032, 0x0446 }, + { 0x04, 0x18, -1, 0x03, 0x0032, 0x0846 }, + { 0x05, 0x05, -1, 0x03, 0x0042, 0x0046 }, + { 0x05, 0x05, -1, 0x03, 0x0042, 0x0066 }, + { 0x05, 0x06, -1, 0x03, 0x0042, 0x0086 }, + { 0x05, 0x07, -1, 0x03, 0x0042, 0x00c6 }, + { 0x05, 0x08, -1, 0x03, 0x0042, 0x0146 }, + { 0x05, 0x09, -1, 0x03, 0x0042, 0x0246 }, + { 0x05, 0x0a, -1, 0x03, 0x0042, 0x0446 }, + { 0x05, 0x18, -1, 0x03, 0x0042, 0x0846 }, + { 0x05, 0x05, -1, 0x03, 0x0062, 0x0046 }, + { 0x05, 0x05, -1, 0x03, 0x0062, 0x0066 }, + { 0x05, 0x06, -1, 0x03, 0x0062, 0x0086 }, + { 0x05, 0x07, -1, 0x03, 0x0062, 0x00c6 }, + { 0x05, 0x08, -1, 0x03, 0x0062, 0x0146 }, + { 0x05, 0x09, -1, 0x03, 0x0062, 0x0246 }, + { 0x05, 0x0a, -1, 0x03, 0x0062, 0x0446 }, + { 0x05, 0x18, -1, 0x03, 0x0062, 0x0846 }, + { 0x06, 0x01, -1, 0x03, 0x0082, 0x000a }, + { 0x06, 0x01, -1, 0x03, 0x0082, 0x000c }, + { 0x06, 0x02, -1, 0x03, 0x0082, 0x000e }, + { 0x06, 0x02, -1, 0x03, 0x0082, 0x0012 }, + { 0x06, 0x03, -1, 0x03, 0x0082, 0x0016 }, + { 0x06, 0x03, -1, 0x03, 0x0082, 0x001e }, + { 0x06, 0x04, -1, 0x03, 0x0082, 0x0026 }, + { 0x06, 0x04, -1, 0x03, 0x0082, 0x0036 }, + { 0x07, 0x01, -1, 0x03, 0x00c2, 0x000a }, + { 0x07, 0x01, -1, 0x03, 0x00c2, 0x000c }, + { 0x07, 0x02, -1, 0x03, 0x00c2, 0x000e }, + { 0x07, 0x02, -1, 0x03, 0x00c2, 0x0012 }, + { 0x07, 0x03, -1, 0x03, 0x00c2, 0x0016 }, + { 0x07, 0x03, -1, 0x03, 0x00c2, 0x001e }, + { 0x07, 0x04, -1, 0x03, 0x00c2, 0x0026 }, + { 0x07, 0x04, -1, 0x03, 0x00c2, 0x0036 }, + { 0x08, 0x01, -1, 0x03, 0x0142, 0x000a }, + { 0x08, 0x01, -1, 0x03, 0x0142, 0x000c }, + { 0x08, 0x02, -1, 0x03, 0x0142, 0x000e }, + { 0x08, 0x02, -1, 0x03, 0x0142, 0x0012 }, + { 0x08, 0x03, -1, 0x03, 0x0142, 0x0016 }, + { 0x08, 0x03, -1, 0x03, 0x0142, 0x001e }, + { 0x08, 0x04, -1, 0x03, 0x0142, 0x0026 }, + { 0x08, 0x04, -1, 0x03, 0x0142, 0x0036 }, + { 0x09, 0x01, -1, 0x03, 0x0242, 0x000a }, + { 0x09, 0x01, -1, 0x03, 0x0242, 0x000c }, + { 0x09, 0x02, -1, 0x03, 0x0242, 0x000e }, + { 0x09, 0x02, -1, 0x03, 0x0242, 0x0012 }, + { 0x09, 0x03, -1, 0x03, 0x0242, 0x0016 }, + { 0x09, 0x03, -1, 0x03, 0x0242, 0x001e }, + { 0x09, 0x04, -1, 0x03, 0x0242, 0x0026 }, + { 0x09, 0x04, -1, 0x03, 0x0242, 0x0036 }, + { 0x0a, 0x01, -1, 0x03, 0x0442, 0x000a }, + { 0x0a, 0x01, -1, 0x03, 0x0442, 0x000c }, + { 0x0a, 0x02, -1, 0x03, 0x0442, 0x000e }, + { 0x0a, 0x02, -1, 0x03, 0x0442, 0x0012 }, + { 0x0a, 0x03, -1, 0x03, 0x0442, 0x0016 }, + { 0x0a, 0x03, -1, 0x03, 0x0442, 0x001e }, + { 0x0a, 0x04, -1, 0x03, 0x0442, 0x0026 }, + { 0x0a, 0x04, -1, 0x03, 0x0442, 0x0036 }, + { 0x0c, 0x01, -1, 0x03, 0x0842, 0x000a }, + { 0x0c, 0x01, -1, 0x03, 0x0842, 0x000c }, + { 0x0c, 0x02, -1, 0x03, 0x0842, 0x000e }, + { 0x0c, 0x02, -1, 0x03, 0x0842, 0x0012 }, + { 0x0c, 0x03, -1, 0x03, 0x0842, 0x0016 }, + { 0x0c, 0x03, -1, 0x03, 0x0842, 0x001e }, + { 0x0c, 0x04, -1, 0x03, 0x0842, 0x0026 }, + { 0x0c, 0x04, -1, 0x03, 0x0842, 0x0036 }, + { 0x0e, 0x01, -1, 0x03, 0x1842, 0x000a }, + { 0x0e, 0x01, -1, 0x03, 0x1842, 0x000c }, + { 0x0e, 0x02, -1, 0x03, 0x1842, 0x000e }, + { 0x0e, 0x02, -1, 0x03, 0x1842, 0x0012 }, + { 0x0e, 0x03, -1, 0x03, 0x1842, 0x0016 }, + { 0x0e, 0x03, -1, 0x03, 0x1842, 0x001e }, + { 0x0e, 0x04, -1, 0x03, 0x1842, 0x0026 }, + { 0x0e, 0x04, -1, 0x03, 0x1842, 0x0036 }, + { 0x18, 0x01, -1, 0x03, 0x5842, 0x000a }, + { 0x18, 0x01, -1, 0x03, 0x5842, 0x000c }, + { 0x18, 0x02, -1, 0x03, 0x5842, 0x000e }, + { 0x18, 0x02, -1, 0x03, 0x5842, 0x0012 }, + { 0x18, 0x03, -1, 0x03, 0x5842, 0x0016 }, + { 0x18, 0x03, -1, 0x03, 0x5842, 0x001e }, + { 0x18, 0x04, -1, 0x03, 0x5842, 0x0026 }, + { 0x18, 0x04, -1, 0x03, 0x5842, 0x0036 }, + { 0x06, 0x05, -1, 0x03, 0x0082, 0x0046 }, + { 0x06, 0x05, -1, 0x03, 0x0082, 0x0066 }, + { 0x06, 0x06, -1, 0x03, 0x0082, 0x0086 }, + { 0x06, 0x07, -1, 0x03, 0x0082, 0x00c6 }, + { 0x06, 0x08, -1, 0x03, 0x0082, 0x0146 }, + { 0x06, 0x09, -1, 0x03, 0x0082, 0x0246 }, + { 0x06, 0x0a, -1, 0x03, 0x0082, 0x0446 }, + { 0x06, 0x18, -1, 0x03, 0x0082, 0x0846 }, + { 0x07, 0x05, -1, 0x03, 0x00c2, 0x0046 }, + { 0x07, 0x05, -1, 0x03, 0x00c2, 0x0066 }, + { 0x07, 0x06, -1, 0x03, 0x00c2, 0x0086 }, + { 0x07, 0x07, -1, 0x03, 0x00c2, 0x00c6 }, + { 0x07, 0x08, -1, 0x03, 0x00c2, 0x0146 }, + { 0x07, 0x09, -1, 0x03, 0x00c2, 0x0246 }, + { 0x07, 0x0a, -1, 0x03, 0x00c2, 0x0446 }, + { 0x07, 0x18, -1, 0x03, 0x00c2, 0x0846 }, + { 0x08, 0x05, -1, 0x03, 0x0142, 0x0046 }, + { 0x08, 0x05, -1, 0x03, 0x0142, 0x0066 }, + { 0x08, 0x06, -1, 0x03, 0x0142, 0x0086 }, + { 0x08, 0x07, -1, 0x03, 0x0142, 0x00c6 }, + { 0x08, 0x08, -1, 0x03, 0x0142, 0x0146 }, + { 0x08, 0x09, -1, 0x03, 0x0142, 0x0246 }, + { 0x08, 0x0a, -1, 0x03, 0x0142, 0x0446 }, + { 0x08, 0x18, -1, 0x03, 0x0142, 0x0846 }, + { 0x09, 0x05, -1, 0x03, 0x0242, 0x0046 }, + { 0x09, 0x05, -1, 0x03, 0x0242, 0x0066 }, + { 0x09, 0x06, -1, 0x03, 0x0242, 0x0086 }, + { 0x09, 0x07, -1, 0x03, 0x0242, 0x00c6 }, + { 0x09, 0x08, -1, 0x03, 0x0242, 0x0146 }, + { 0x09, 0x09, -1, 0x03, 0x0242, 0x0246 }, + { 0x09, 0x0a, -1, 0x03, 0x0242, 0x0446 }, + { 0x09, 0x18, -1, 0x03, 0x0242, 0x0846 }, + { 0x0a, 0x05, -1, 0x03, 0x0442, 0x0046 }, + { 0x0a, 0x05, -1, 0x03, 0x0442, 0x0066 }, + { 0x0a, 0x06, -1, 0x03, 0x0442, 0x0086 }, + { 0x0a, 0x07, -1, 0x03, 0x0442, 0x00c6 }, + { 0x0a, 0x08, -1, 0x03, 0x0442, 0x0146 }, + { 0x0a, 0x09, -1, 0x03, 0x0442, 0x0246 }, + { 0x0a, 0x0a, -1, 0x03, 0x0442, 0x0446 }, + { 0x0a, 0x18, -1, 0x03, 0x0442, 0x0846 }, + { 0x0c, 0x05, -1, 0x03, 0x0842, 0x0046 }, + { 0x0c, 0x05, -1, 0x03, 0x0842, 0x0066 }, + { 0x0c, 0x06, -1, 0x03, 0x0842, 0x0086 }, + { 0x0c, 0x07, -1, 0x03, 0x0842, 0x00c6 }, + { 0x0c, 0x08, -1, 0x03, 0x0842, 0x0146 }, + { 0x0c, 0x09, -1, 0x03, 0x0842, 0x0246 }, + { 0x0c, 0x0a, -1, 0x03, 0x0842, 0x0446 }, + { 0x0c, 0x18, -1, 0x03, 0x0842, 0x0846 }, + { 0x0e, 0x05, -1, 0x03, 0x1842, 0x0046 }, + { 0x0e, 0x05, -1, 0x03, 0x1842, 0x0066 }, + { 0x0e, 0x06, -1, 0x03, 0x1842, 0x0086 }, + { 0x0e, 0x07, -1, 0x03, 0x1842, 0x00c6 }, + { 0x0e, 0x08, -1, 0x03, 0x1842, 0x0146 }, + { 0x0e, 0x09, -1, 0x03, 0x1842, 0x0246 }, + { 0x0e, 0x0a, -1, 0x03, 0x1842, 0x0446 }, + { 0x0e, 0x18, -1, 0x03, 0x1842, 0x0846 }, + { 0x18, 0x05, -1, 0x03, 0x5842, 0x0046 }, + { 0x18, 0x05, -1, 0x03, 0x5842, 0x0066 }, + { 0x18, 0x06, -1, 0x03, 0x5842, 0x0086 }, + { 0x18, 0x07, -1, 0x03, 0x5842, 0x00c6 }, + { 0x18, 0x08, -1, 0x03, 0x5842, 0x0146 }, + { 0x18, 0x09, -1, 0x03, 0x5842, 0x0246 }, + { 0x18, 0x0a, -1, 0x03, 0x5842, 0x0446 }, + { 0x18, 0x18, -1, 0x03, 0x5842, 0x0846 }, +}; diff --git a/packages/runtime/vendor/brotli/c/dec/state.c b/packages/runtime/vendor/brotli/c/dec/state.c new file mode 100644 index 000000000..dcf61b9eb --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/state.c @@ -0,0 +1,186 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "state.h" + +#include "../common/dictionary.h" +#include "../common/platform.h" +#include "huffman.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#ifdef BROTLI_REPORTING +/* When BROTLI_REPORTING is defined extra reporting module have to be linked. */ +void BrotliDecoderOnStart(const BrotliDecoderState* s); +void BrotliDecoderOnFinish(const BrotliDecoderState* s); +#define BROTLI_DECODER_ON_START(s) BrotliDecoderOnStart(s); +#define BROTLI_DECODER_ON_FINISH(s) BrotliDecoderOnFinish(s); +#else +#if !defined(BROTLI_DECODER_ON_START) +#define BROTLI_DECODER_ON_START(s) (void)(s); +#endif +#if !defined(BROTLI_DECODER_ON_FINISH) +#define BROTLI_DECODER_ON_FINISH(s) (void)(s); +#endif +#endif + +BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s, + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + BROTLI_DECODER_ON_START(s); + if (!alloc_func) { + s->alloc_func = BrotliDefaultAllocFunc; + s->free_func = BrotliDefaultFreeFunc; + s->memory_manager_opaque = 0; + } else { + s->alloc_func = alloc_func; + s->free_func = free_func; + s->memory_manager_opaque = opaque; + } + + s->error_code = 0; /* BROTLI_DECODER_NO_ERROR */ + + BrotliInitBitReader(&s->br); + s->state = BROTLI_STATE_UNINITED; + s->large_window = 0; + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; + s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_NONE; + s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE; + s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE; + + s->buffer_length = 0; + s->loop_counter = 0; + s->pos = 0; + s->rb_roundtrips = 0; + s->partial_pos_out = 0; + s->used_input = 0; + + s->block_type_trees = NULL; + s->block_len_trees = NULL; + s->ringbuffer = NULL; + s->ringbuffer_size = 0; + s->new_ringbuffer_size = 0; + s->ringbuffer_mask = 0; + + s->context_map = NULL; + s->context_modes = NULL; + s->dist_context_map = NULL; + s->context_map_slice = NULL; + s->dist_context_map_slice = NULL; + + s->literal_hgroup.codes = NULL; + s->literal_hgroup.htrees = NULL; + s->insert_copy_hgroup.codes = NULL; + s->insert_copy_hgroup.htrees = NULL; + s->distance_hgroup.codes = NULL; + s->distance_hgroup.htrees = NULL; + + s->is_last_metablock = 0; + s->is_uncompressed = 0; + s->is_metadata = 0; + s->should_wrap_ringbuffer = 0; + s->canny_ringbuffer_allocation = 1; + + s->window_bits = 0; + s->max_distance = 0; + s->dist_rb[0] = 16; + s->dist_rb[1] = 15; + s->dist_rb[2] = 11; + s->dist_rb[3] = 4; + s->dist_rb_idx = 0; + s->block_type_trees = NULL; + s->block_len_trees = NULL; + + s->mtf_upper_bound = 63; + + s->compound_dictionary = NULL; + s->dictionary = + BrotliSharedDictionaryCreateInstance(alloc_func, free_func, opaque); + if (!s->dictionary) return BROTLI_FALSE; + + s->metadata_start_func = NULL; + s->metadata_chunk_func = NULL; + s->metadata_callback_opaque = 0; + + return BROTLI_TRUE; +} + +void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) { + s->meta_block_remaining_len = 0; + s->block_length[0] = BROTLI_BLOCK_SIZE_CAP; + s->block_length[1] = BROTLI_BLOCK_SIZE_CAP; + s->block_length[2] = BROTLI_BLOCK_SIZE_CAP; + s->num_block_types[0] = 1; + s->num_block_types[1] = 1; + s->num_block_types[2] = 1; + s->block_type_rb[0] = 1; + s->block_type_rb[1] = 0; + s->block_type_rb[2] = 1; + s->block_type_rb[3] = 0; + s->block_type_rb[4] = 1; + s->block_type_rb[5] = 0; + s->context_map = NULL; + s->context_modes = NULL; + s->dist_context_map = NULL; + s->context_map_slice = NULL; + s->literal_htree = NULL; + s->dist_context_map_slice = NULL; + s->dist_htree_index = 0; + s->context_lookup = NULL; + s->literal_hgroup.codes = NULL; + s->literal_hgroup.htrees = NULL; + s->insert_copy_hgroup.codes = NULL; + s->insert_copy_hgroup.htrees = NULL; + s->distance_hgroup.codes = NULL; + s->distance_hgroup.htrees = NULL; +} + +void BrotliDecoderStateCleanupAfterMetablock(BrotliDecoderState* s) { + BROTLI_DECODER_FREE(s, s->context_modes); + BROTLI_DECODER_FREE(s, s->context_map); + BROTLI_DECODER_FREE(s, s->dist_context_map); + BROTLI_DECODER_FREE(s, s->literal_hgroup.htrees); + BROTLI_DECODER_FREE(s, s->insert_copy_hgroup.htrees); + BROTLI_DECODER_FREE(s, s->distance_hgroup.htrees); +} + +void BrotliDecoderStateCleanup(BrotliDecoderState* s) { + BrotliDecoderStateCleanupAfterMetablock(s); + + BROTLI_DECODER_ON_FINISH(s); + + BROTLI_DECODER_FREE(s, s->compound_dictionary); + BrotliSharedDictionaryDestroyInstance(s->dictionary); + s->dictionary = NULL; + BROTLI_DECODER_FREE(s, s->ringbuffer); + BROTLI_DECODER_FREE(s, s->block_type_trees); +} + +BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit(BrotliDecoderState* s, + HuffmanTreeGroup* group, brotli_reg_t alphabet_size_max, + brotli_reg_t alphabet_size_limit, brotli_reg_t ntrees) { + /* 376 = 256 (1-st level table) + 4 + 7 + 15 + 31 + 63 (2-nd level mix-tables) + This number is discovered "unlimited" "enough" calculator; it is actually + a wee bigger than required in several cases (especially for alphabets with + less than 16 symbols). */ + const size_t max_table_size = alphabet_size_limit + 376; + const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size; + const size_t htree_size = sizeof(HuffmanCode*) * ntrees; + /* Pointer alignment is, hopefully, wider than sizeof(HuffmanCode). */ + HuffmanCode** p = (HuffmanCode**)BROTLI_DECODER_ALLOC(s, + code_size + htree_size); + group->alphabet_size_max = (uint16_t)alphabet_size_max; + group->alphabet_size_limit = (uint16_t)alphabet_size_limit; + group->num_htrees = (uint16_t)ntrees; + group->htrees = p; + group->codes = p ? (HuffmanCode*)(&p[ntrees]) : NULL; + return !!p; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/dec/state.h b/packages/runtime/vendor/brotli/c/dec/state.h new file mode 100644 index 000000000..aa371fba2 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/state.h @@ -0,0 +1,396 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Brotli state for partial streaming decoding. */ + +#ifndef BROTLI_DEC_STATE_H_ +#define BROTLI_DEC_STATE_H_ + +#include "../common/constants.h" +#include "../common/platform.h" +#include +#include +#include "bit_reader.h" +#include "huffman.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* Graphviz diagram that describes state transitions: + +digraph States { + graph [compound=true] + concentrate=true + node [shape="box"] + + UNINITED -> {LARGE_WINDOW_BITS -> INITIALIZE} + subgraph cluster_metablock_workflow { + style="rounded" + label=< METABLOCK CYCLE > + METABLOCK_BEGIN -> METABLOCK_HEADER + METABLOCK_HEADER:sw -> METADATA + METABLOCK_HEADER:s -> UNCOMPRESSED + METABLOCK_HEADER:se -> METABLOCK_DONE:ne + METADATA:s -> METABLOCK_DONE:w + UNCOMPRESSED:s -> METABLOCK_DONE:n + METABLOCK_DONE:e -> METABLOCK_BEGIN:e [constraint="false"] + } + INITIALIZE -> METABLOCK_BEGIN + METABLOCK_DONE -> DONE + + subgraph cluster_compressed_metablock { + style="rounded" + label=< COMPRESSED METABLOCK > + + subgraph cluster_command { + style="rounded" + label=< HOT LOOP > + + _METABLOCK_DONE_PORT_ [shape=point style=invis] + + { + // Set different shape for nodes returning from "compressed metablock". + node [shape=invhouse]; CMD_INNER CMD_POST_DECODE_LITERALS; + CMD_POST_WRAP_COPY; CMD_INNER_WRITE; CMD_POST_WRITE_1; + } + + CMD_BEGIN -> CMD_INNER -> CMD_POST_DECODE_LITERALS -> CMD_POST_WRAP_COPY + + // IO ("write") nodes are not in the hot loop! + CMD_INNER_WRITE [style=dashed] + CMD_INNER -> CMD_INNER_WRITE + CMD_POST_WRITE_1 [style=dashed] + CMD_POST_DECODE_LITERALS -> CMD_POST_WRITE_1 + CMD_POST_WRITE_2 [style=dashed] + CMD_POST_WRAP_COPY -> CMD_POST_WRITE_2 + + CMD_POST_WRITE_1 -> CMD_BEGIN:s [constraint="false"] + CMD_INNER_WRITE -> {CMD_INNER CMD_POST_DECODE_LITERALS} + [constraint="false"] + CMD_BEGIN:ne -> CMD_POST_DECODE_LITERALS [constraint="false"] + CMD_POST_WRAP_COPY -> CMD_BEGIN [constraint="false"] + CMD_POST_DECODE_LITERALS -> CMD_BEGIN:ne [constraint="false"] + CMD_POST_WRITE_2 -> CMD_POST_WRAP_COPY [constraint="false"] + {rank=same; CMD_BEGIN; CMD_INNER; CMD_POST_DECODE_LITERALS; + CMD_POST_WRAP_COPY} + {rank=same; CMD_INNER_WRITE; CMD_POST_WRITE_1; CMD_POST_WRITE_2} + + {CMD_INNER CMD_POST_DECODE_LITERALS CMD_POST_WRAP_COPY} -> + _METABLOCK_DONE_PORT_ [style=invis] + {CMD_INNER_WRITE CMD_POST_WRITE_1} -> _METABLOCK_DONE_PORT_ + [constraint="false" style=invis] + } + + BEFORE_COMPRESSED_METABLOCK_HEADER:s -> HUFFMAN_CODE_0:n + HUFFMAN_CODE_0 -> HUFFMAN_CODE_1 -> HUFFMAN_CODE_2 -> HUFFMAN_CODE_3 + HUFFMAN_CODE_0 -> METABLOCK_HEADER_2 -> CONTEXT_MODES -> CONTEXT_MAP_1 + CONTEXT_MAP_1 -> CONTEXT_MAP_2 -> TREE_GROUP + TREE_GROUP -> BEFORE_COMPRESSED_METABLOCK_BODY:e + BEFORE_COMPRESSED_METABLOCK_BODY:s -> CMD_BEGIN:n + + HUFFMAN_CODE_3:e -> HUFFMAN_CODE_0:ne [constraint="false"] + {rank=same; HUFFMAN_CODE_0; HUFFMAN_CODE_1; HUFFMAN_CODE_2; HUFFMAN_CODE_3} + {rank=same; METABLOCK_HEADER_2; CONTEXT_MODES; CONTEXT_MAP_1; CONTEXT_MAP_2; + TREE_GROUP} + } + METABLOCK_HEADER:e -> BEFORE_COMPRESSED_METABLOCK_HEADER:n + + _METABLOCK_DONE_PORT_ -> METABLOCK_DONE:se + [constraint="false" ltail=cluster_command] + + UNINITED [shape=Mdiamond]; + DONE [shape=Msquare]; +} + + + */ + +typedef enum { + BROTLI_STATE_UNINITED, + BROTLI_STATE_LARGE_WINDOW_BITS, + BROTLI_STATE_INITIALIZE, + BROTLI_STATE_METABLOCK_BEGIN, + BROTLI_STATE_METABLOCK_HEADER, + BROTLI_STATE_METABLOCK_HEADER_2, + BROTLI_STATE_CONTEXT_MODES, + BROTLI_STATE_COMMAND_BEGIN, + BROTLI_STATE_COMMAND_INNER, + BROTLI_STATE_COMMAND_POST_DECODE_LITERALS, + BROTLI_STATE_COMMAND_POST_WRAP_COPY, + BROTLI_STATE_UNCOMPRESSED, + BROTLI_STATE_METADATA, + BROTLI_STATE_COMMAND_INNER_WRITE, + BROTLI_STATE_METABLOCK_DONE, + BROTLI_STATE_COMMAND_POST_WRITE_1, + BROTLI_STATE_COMMAND_POST_WRITE_2, + BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER, + BROTLI_STATE_HUFFMAN_CODE_0, + BROTLI_STATE_HUFFMAN_CODE_1, + BROTLI_STATE_HUFFMAN_CODE_2, + BROTLI_STATE_HUFFMAN_CODE_3, + BROTLI_STATE_CONTEXT_MAP_1, + BROTLI_STATE_CONTEXT_MAP_2, + BROTLI_STATE_TREE_GROUP, + BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY, + BROTLI_STATE_DONE +} BrotliRunningState; + +typedef enum { + BROTLI_STATE_METABLOCK_HEADER_NONE, + BROTLI_STATE_METABLOCK_HEADER_EMPTY, + BROTLI_STATE_METABLOCK_HEADER_NIBBLES, + BROTLI_STATE_METABLOCK_HEADER_SIZE, + BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED, + BROTLI_STATE_METABLOCK_HEADER_RESERVED, + BROTLI_STATE_METABLOCK_HEADER_BYTES, + BROTLI_STATE_METABLOCK_HEADER_METADATA +} BrotliRunningMetablockHeaderState; + +typedef enum { + BROTLI_STATE_UNCOMPRESSED_NONE, + BROTLI_STATE_UNCOMPRESSED_WRITE +} BrotliRunningUncompressedState; + +typedef enum { + BROTLI_STATE_TREE_GROUP_NONE, + BROTLI_STATE_TREE_GROUP_LOOP +} BrotliRunningTreeGroupState; + +typedef enum { + BROTLI_STATE_CONTEXT_MAP_NONE, + BROTLI_STATE_CONTEXT_MAP_READ_PREFIX, + BROTLI_STATE_CONTEXT_MAP_HUFFMAN, + BROTLI_STATE_CONTEXT_MAP_DECODE, + BROTLI_STATE_CONTEXT_MAP_TRANSFORM +} BrotliRunningContextMapState; + +typedef enum { + BROTLI_STATE_HUFFMAN_NONE, + BROTLI_STATE_HUFFMAN_SIMPLE_SIZE, + BROTLI_STATE_HUFFMAN_SIMPLE_READ, + BROTLI_STATE_HUFFMAN_SIMPLE_BUILD, + BROTLI_STATE_HUFFMAN_COMPLEX, + BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS +} BrotliRunningHuffmanState; + +typedef enum { + BROTLI_STATE_DECODE_UINT8_NONE, + BROTLI_STATE_DECODE_UINT8_SHORT, + BROTLI_STATE_DECODE_UINT8_LONG +} BrotliRunningDecodeUint8State; + +typedef enum { + BROTLI_STATE_READ_BLOCK_LENGTH_NONE, + BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX +} BrotliRunningReadBlockLengthState; + +/* BrotliDecoderState addon, used for Compound Dictionary functionality. */ +typedef struct BrotliDecoderCompoundDictionary { + int num_chunks; + int total_size; + int br_index; + int br_offset; + int br_length; + int br_copied; + const uint8_t* chunks[16]; + int chunk_offsets[16]; + int block_bits; + uint8_t block_map[256]; +} BrotliDecoderCompoundDictionary; + +typedef struct BrotliMetablockHeaderArena { + BrotliRunningTreeGroupState substate_tree_group; + BrotliRunningContextMapState substate_context_map; + BrotliRunningHuffmanState substate_huffman; + + brotli_reg_t sub_loop_counter; + + brotli_reg_t repeat_code_len; + brotli_reg_t prev_code_len; + + /* For ReadHuffmanCode. */ + brotli_reg_t symbol; + brotli_reg_t repeat; + brotli_reg_t space; + + /* Huffman table for "histograms". */ + HuffmanCode table[32]; + /* List of heads of symbol chains. */ + uint16_t* symbol_lists; + /* Storage from symbol_lists. */ + uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 + + BROTLI_NUM_COMMAND_SYMBOLS]; + /* Tails of symbol chains. */ + int next_symbol[32]; + uint8_t code_length_code_lengths[BROTLI_CODE_LENGTH_CODES]; + /* Population counts for the code lengths. */ + uint16_t code_length_histo[16]; + /* TODO(eustas): +2 bytes padding */ + + /* For HuffmanTreeGroupDecode. */ + int htree_index; + HuffmanCode* next; + + /* For DecodeContextMap. */ + brotli_reg_t context_index; + brotli_reg_t max_run_length_prefix; + brotli_reg_t code; + HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272]; +} BrotliMetablockHeaderArena; + +typedef struct BrotliMetablockBodyArena { + uint8_t dist_extra_bits[544]; + brotli_reg_t dist_offset[544]; +} BrotliMetablockBodyArena; + +struct BrotliDecoderStateStruct { + BrotliRunningState state; + + /* This counter is reused for several disjoint loops. */ + int loop_counter; + + BrotliBitReader br; + + brotli_alloc_func alloc_func; + brotli_free_func free_func; + void* memory_manager_opaque; + + /* Temporary storage for remaining input. Brotli stream format is designed in + a way, that 64 bits are enough to make progress in decoding. */ + union { + uint64_t u64; + uint8_t u8[8]; + } buffer; + brotli_reg_t buffer_length; + + int pos; + int max_backward_distance; + int max_distance; + int ringbuffer_size; + int ringbuffer_mask; + int dist_rb_idx; + int dist_rb[4]; + int error_code; + int meta_block_remaining_len; + + uint8_t* ringbuffer; + uint8_t* ringbuffer_end; + HuffmanCode* htree_command; + const uint8_t* context_lookup; + uint8_t* context_map_slice; + uint8_t* dist_context_map_slice; + + /* This ring buffer holds a few past copy distances that will be used by + some special distance codes. */ + HuffmanTreeGroup literal_hgroup; + HuffmanTreeGroup insert_copy_hgroup; + HuffmanTreeGroup distance_hgroup; + HuffmanCode* block_type_trees; + HuffmanCode* block_len_trees; + /* This is true if the literal context map histogram type always matches the + block type. It is then not needed to keep the context (faster decoding). */ + int trivial_literal_context; + /* Distance context is actual after command is decoded and before distance is + computed. After distance computation it is used as a temporary variable. */ + int distance_context; + brotli_reg_t block_length[3]; + brotli_reg_t block_length_index; + brotli_reg_t num_block_types[3]; + brotli_reg_t block_type_rb[6]; + brotli_reg_t distance_postfix_bits; + brotli_reg_t num_direct_distance_codes; + brotli_reg_t num_dist_htrees; + uint8_t* dist_context_map; + HuffmanCode* literal_htree; + + /* For partial write operations. */ + size_t rb_roundtrips; /* how many times we went around the ring-buffer */ + size_t partial_pos_out; /* how much output to the user in total */ + + /* For InverseMoveToFrontTransform. */ + brotli_reg_t mtf_upper_bound; + uint32_t mtf[64 + 1]; + + int copy_length; + int distance_code; + + uint8_t dist_htree_index; + /* TODO(eustas): +3 bytes padding */ + + /* Less used attributes are at the end of this struct. */ + + brotli_decoder_metadata_start_func metadata_start_func; + brotli_decoder_metadata_chunk_func metadata_chunk_func; + void* metadata_callback_opaque; + + /* For reporting. */ + uint64_t used_input; /* how many bytes of input are consumed */ + + /* States inside function calls. */ + BrotliRunningMetablockHeaderState substate_metablock_header; + BrotliRunningUncompressedState substate_uncompressed; + BrotliRunningDecodeUint8State substate_decode_uint8; + BrotliRunningReadBlockLengthState substate_read_block_length; + + int new_ringbuffer_size; + /* TODO(eustas): +4 bytes padding */ + + unsigned int is_last_metablock : 1; + unsigned int is_uncompressed : 1; + unsigned int is_metadata : 1; + unsigned int should_wrap_ringbuffer : 1; + unsigned int canny_ringbuffer_allocation : 1; + unsigned int large_window : 1; + unsigned int window_bits : 6; + unsigned int size_nibbles : 8; + /* TODO(eustas): +12 bits padding */ + + brotli_reg_t num_literal_htrees; + uint8_t* context_map; + uint8_t* context_modes; + + BrotliSharedDictionary* dictionary; + BrotliDecoderCompoundDictionary* compound_dictionary; + + uint32_t trivial_literal_contexts[8]; /* 256 bits */ + + union { + BrotliMetablockHeaderArena header; + BrotliMetablockBodyArena body; + } arena; +}; + +typedef struct BrotliDecoderStateStruct BrotliDecoderStateInternal; +#define BrotliDecoderState BrotliDecoderStateInternal + +BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s, + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); +BROTLI_INTERNAL void BrotliDecoderStateCleanup(BrotliDecoderState* s); +BROTLI_INTERNAL void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s); +BROTLI_INTERNAL void BrotliDecoderStateCleanupAfterMetablock( + BrotliDecoderState* s); +BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit( + BrotliDecoderState* s, HuffmanTreeGroup* group, + brotli_reg_t alphabet_size_max, brotli_reg_t alphabet_size_limit, + brotli_reg_t ntrees); + +#define BROTLI_DECODER_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L) + +#define BROTLI_DECODER_FREE(S, X) { \ + S->free_func(S->memory_manager_opaque, X); \ + X = NULL; \ +} + +/* Literal/Command/Distance block size maximum; same as maximum metablock size; + used as block size when there is no block switching. */ +#define BROTLI_BLOCK_SIZE_CAP (1U << 24) + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_DEC_STATE_H_ */ diff --git a/packages/runtime/vendor/brotli/c/dec/static_init.c b/packages/runtime/vendor/brotli/c/dec/static_init.c new file mode 100644 index 000000000..1345052c5 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/static_init.c @@ -0,0 +1,53 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "static_init.h" + +#include "../common/platform.h" +#include "../common/static_init.h" + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +#include "../common/dictionary.h" +#include "prefix.h" +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +static BROTLI_BOOL DoBrotliDecoderStaticInit(void) { + BROTLI_BOOL ok = BrotliDecoderInitCmdLut(kCmdLut); + if (!ok) return BROTLI_FALSE; + return BROTLI_TRUE; +} +#endif /* BROTLI_STATIC_INIT_NONE */ + +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_EARLY) +static BROTLI_BOOL kEarlyInitOk; +static __attribute__((constructor)) void BrotliDecoderStaticInitEarly(void) { + kEarlyInitOk = DoBrotliDecoderStaticInit(); +} +#elif (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_LAZY) +static BROTLI_BOOL kLazyInitOk; +void BrotliDecoderLazyStaticInitInner(void) { + kLazyInitOk = DoBrotliDecoderStaticInit(); +} +#endif /* BROTLI_STATIC_INIT_EARLY */ + +BROTLI_BOOL BrotliDecoderEnsureStaticInit(void) { +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_NONE) + return BROTLI_TRUE; +#elif (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_EARLY) + return kEarlyInitOk; +#else + return kLazyInitOk; +#endif +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/dec/static_init.h b/packages/runtime/vendor/brotli/c/dec/static_init.h new file mode 100644 index 000000000..4ae063ef5 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/dec/static_init.h @@ -0,0 +1,30 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Central point for static initialization. */ + +#ifndef THIRD_PARTY_BROTLI_DEC_STATIC_INIT_H_ +#define THIRD_PARTY_BROTLI_DEC_STATIC_INIT_H_ + +#include "../common/platform.h" +#include "../common/static_init.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_LAZY) +BROTLI_INTERNAL void BrotliDecoderLazyStaticInitInner(void); +BROTLI_INTERNAL void BrotliDecoderLazyStaticInit(void); +#endif /* BROTLI_STATIC_INIT */ + +BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderEnsureStaticInit(void); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif // THIRD_PARTY_BROTLI_DEC_STATIC_INIT_H_ diff --git a/packages/runtime/vendor/brotli/c/enc/backward_references.c b/packages/runtime/vendor/brotli/c/enc/backward_references.c new file mode 100644 index 000000000..5d1c0ed63 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/backward_references.c @@ -0,0 +1,231 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function to find backward reference copies. */ + +#include "backward_references.h" + +#include "../common/constants.h" +#include "../common/context.h" +#include "../common/platform.h" +#include "command.h" +#include "compound_dictionary.h" +#include "encoder_dict.h" +#include "hash.h" +#include "params.h" +#include "quality.h" /* IWYU pragma: keep for inc */ + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance, + size_t max_distance, + const int* dist_cache) { + if (distance <= max_distance) { + size_t distance_plus_3 = distance + 3; + size_t offset0 = distance_plus_3 - (size_t)dist_cache[0]; + size_t offset1 = distance_plus_3 - (size_t)dist_cache[1]; + if (distance == (size_t)dist_cache[0]) { + return 0; + } else if (distance == (size_t)dist_cache[1]) { + return 1; + } else if (offset0 < 7) { + return (0x9750468 >> (4 * offset0)) & 0xF; + } else if (offset1 < 7) { + return (0xFDB1ACE >> (4 * offset1)) & 0xF; + } else if (distance == (size_t)dist_cache[2]) { + return 2; + } else if (distance == (size_t)dist_cache[3]) { + return 3; + } + } + return distance + BROTLI_NUM_DISTANCE_SHORT_CODES - 1; +} + +#define EXPAND_CAT(a, b) CAT(a, b) +#define CAT(a, b) a ## b +#define FN(X) EXPAND_CAT(X, HASHER()) +#define EXPORT_FN(X) EXPAND_CAT(X, EXPAND_CAT(PREFIX(), HASHER())) + +#define PREFIX() N +#define ENABLE_COMPOUND_DICTIONARY 0 + +#define HASHER() H2 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H3 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H4 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H5 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H6 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H40 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H41 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H42 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H54 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H35 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H55 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H65 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#if defined(BROTLI_MAX_SIMD_QUALITY) +#define HASHER() H58 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER + +#define HASHER() H68 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#endif + +#undef ENABLE_COMPOUND_DICTIONARY +#undef PREFIX +#define PREFIX() D +#define ENABLE_COMPOUND_DICTIONARY 1 + +#define HASHER() H5 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#define HASHER() H6 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#define HASHER() H40 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#define HASHER() H41 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#define HASHER() H42 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#define HASHER() H55 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#define HASHER() H65 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#if defined(BROTLI_MAX_SIMD_QUALITY) +#define HASHER() H58 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#define HASHER() H68 +/* NOLINTNEXTLINE(build/include) */ +#include "backward_references_inc.h" +#undef HASHER +#endif + +#undef ENABLE_COMPOUND_DICTIONARY +#undef PREFIX + +#undef EXPORT_FN +#undef FN +#undef CAT +#undef EXPAND_CAT + +void BrotliCreateBackwardReferences(size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + Hasher* hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals) { + if (params->dictionary.compound.num_chunks != 0) { + switch (params->hasher.type) { +#define CASE_(N) \ + case N: \ + CreateBackwardReferencesDH ## N(num_bytes, \ + position, ringbuffer, ringbuffer_mask, \ + literal_context_lut, params, hasher, dist_cache, \ + last_insert_len, commands, num_commands, num_literals); \ + return; + CASE_(5) + CASE_(6) +#if defined(BROTLI_MAX_SIMD_QUALITY) + CASE_(58) + CASE_(68) +#endif + CASE_(40) + CASE_(41) + CASE_(42) + CASE_(55) + CASE_(65) +#undef CASE_ + default: + BROTLI_DCHECK(BROTLI_FALSE); + break; + } + } + + switch (params->hasher.type) { +#define CASE_(N) \ + case N: \ + CreateBackwardReferencesNH ## N(num_bytes, \ + position, ringbuffer, ringbuffer_mask, \ + literal_context_lut, params, hasher, dist_cache, \ + last_insert_len, commands, num_commands, num_literals); \ + return; + FOR_GENERIC_HASHERS(CASE_) +#undef CASE_ + default: + BROTLI_DCHECK(BROTLI_FALSE); + break; + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/backward_references.h b/packages/runtime/vendor/brotli/c/enc/backward_references.h new file mode 100644 index 000000000..17f0c8495 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/backward_references.h @@ -0,0 +1,36 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function to find backward reference copies. */ + +#ifndef BROTLI_ENC_BACKWARD_REFERENCES_H_ +#define BROTLI_ENC_BACKWARD_REFERENCES_H_ + +#include "../common/context.h" +#include "../common/platform.h" +#include "command.h" +#include "hash.h" +#include "params.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* "commands" points to the next output command to write to, "*num_commands" is + initially the total amount of commands output by previous + CreateBackwardReferences calls, and must be incremented by the amount written + by this call. */ +BROTLI_INTERNAL void BrotliCreateBackwardReferences(size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + Hasher* hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_BACKWARD_REFERENCES_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/backward_references_hq.c b/packages/runtime/vendor/brotli/c/enc/backward_references_hq.c new file mode 100644 index 000000000..ab8b12187 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/backward_references_hq.c @@ -0,0 +1,939 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function to find backward reference copies. */ + +#include "backward_references_hq.h" + +#include "../common/constants.h" +#include "../common/context.h" +#include "../common/platform.h" +#include "command.h" +#include "compound_dictionary.h" +#include "encoder_dict.h" +#include "fast_log.h" +#include "find_match_length.h" +#include "hash.h" +#include "literal_cost.h" +#include "memory.h" +#include "params.h" +#include "prefix.h" +#include "quality.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* BrotliCalculateDistanceCodeLimit(BROTLI_MAX_ALLOWED_DISTANCE, 3, 120). */ +#define BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE 544 + +static const float kInfinity = 1.7e38f; /* ~= 2 ^ 127 */ + +static const BROTLI_MODEL("small") uint32_t kDistanceCacheIndex[] = { + 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, +}; +static const BROTLI_MODEL("small") int kDistanceCacheOffset[] = { + 0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3 +}; + +void BrotliInitZopfliNodes(ZopfliNode* array, size_t length) { + ZopfliNode stub; + size_t i; + stub.length = 1; + stub.distance = 0; + stub.dcode_insert_length = 0; + stub.u.cost = kInfinity; + for (i = 0; i < length; ++i) array[i] = stub; +} + +static BROTLI_INLINE uint32_t ZopfliNodeCopyLength(const ZopfliNode* self) { + return self->length & 0x1FFFFFF; +} + +static BROTLI_INLINE uint32_t ZopfliNodeLengthCode(const ZopfliNode* self) { + const uint32_t modifier = self->length >> 25; + return ZopfliNodeCopyLength(self) + 9u - modifier; +} + +static BROTLI_INLINE uint32_t ZopfliNodeCopyDistance(const ZopfliNode* self) { + return self->distance; +} + +static BROTLI_INLINE uint32_t ZopfliNodeDistanceCode(const ZopfliNode* self) { + const uint32_t short_code = self->dcode_insert_length >> 27; + return short_code == 0 ? + ZopfliNodeCopyDistance(self) + BROTLI_NUM_DISTANCE_SHORT_CODES - 1 : + short_code - 1; +} + +static BROTLI_INLINE uint32_t ZopfliNodeCommandLength(const ZopfliNode* self) { + return ZopfliNodeCopyLength(self) + (self->dcode_insert_length & 0x7FFFFFF); +} + +/* Temporary data for ZopfliCostModelSetFromCommands. */ +typedef struct ZopfliCostModelArena { + uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS]; + uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS]; + uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE]; + float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS]; +} ZopfliCostModelArena; + +/* Histogram based cost model for zopflification. */ +typedef struct ZopfliCostModel { + /* The insert and copy length symbols. */ + float cost_cmd_[BROTLI_NUM_COMMAND_SYMBOLS]; + float* cost_dist_; + uint32_t distance_histogram_size; + /* Cumulative costs of literals per position in the stream. */ + float* literal_costs_; + float min_cost_cmd_; + size_t num_bytes_; + + /* Temporary data. */ + union { + size_t literal_histograms[3 * 256]; + ZopfliCostModelArena arena; + }; +} ZopfliCostModel; + +static void InitZopfliCostModel( + MemoryManager* m, ZopfliCostModel* self, const BrotliDistanceParams* dist, + size_t num_bytes) { + self->num_bytes_ = num_bytes; + self->literal_costs_ = BROTLI_ALLOC(m, float, num_bytes + 2); + self->cost_dist_ = BROTLI_ALLOC(m, float, dist->alphabet_size_limit); + self->distance_histogram_size = dist->alphabet_size_limit; + if (BROTLI_IS_OOM(m)) return; +} + +static void CleanupZopfliCostModel(MemoryManager* m, ZopfliCostModel* self) { + BROTLI_FREE(m, self->literal_costs_); + BROTLI_FREE(m, self->cost_dist_); +} + +static void SetCost(const uint32_t* histogram, size_t histogram_size, + BROTLI_BOOL literal_histogram, float* cost) { + size_t sum = 0; + size_t missing_symbol_sum; + float log2sum; + float missing_symbol_cost; + size_t i; + for (i = 0; i < histogram_size; i++) { + sum += histogram[i]; + } + log2sum = (float)FastLog2(sum); + missing_symbol_sum = sum; + if (!literal_histogram) { + for (i = 0; i < histogram_size; i++) { + if (histogram[i] == 0) missing_symbol_sum++; + } + } + missing_symbol_cost = (float)FastLog2(missing_symbol_sum) + 2; + for (i = 0; i < histogram_size; i++) { + if (histogram[i] == 0) { + cost[i] = missing_symbol_cost; + continue; + } + + /* Shannon bits for this symbol. */ + cost[i] = log2sum - (float)FastLog2(histogram[i]); + + /* Cannot be coded with less than 1 bit */ + if (cost[i] < 1) cost[i] = 1; + } +} + +static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self, + size_t position, + const uint8_t* ringbuffer, + size_t ringbuffer_mask, + const Command* commands, + size_t num_commands, + size_t last_insert_len) { + ZopfliCostModelArena* arena = &self->arena; + size_t pos = position - last_insert_len; + float min_cost_cmd = kInfinity; + size_t i; + float* cost_cmd = self->cost_cmd_; + + memset(arena->histogram_literal, 0, sizeof(arena->histogram_literal)); + memset(arena->histogram_cmd, 0, sizeof(arena->histogram_cmd)); + memset(arena->histogram_dist, 0, sizeof(arena->histogram_dist)); + + for (i = 0; i < num_commands; i++) { + size_t inslength = commands[i].insert_len_; + size_t copylength = CommandCopyLen(&commands[i]); + size_t distcode = commands[i].dist_prefix_ & 0x3FF; + size_t cmdcode = commands[i].cmd_prefix_; + size_t j; + + arena->histogram_cmd[cmdcode]++; + if (cmdcode >= 128) arena->histogram_dist[distcode]++; + + for (j = 0; j < inslength; j++) { + arena->histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++; + } + + pos += inslength + copylength; + } + + SetCost(arena->histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE, + arena->cost_literal); + SetCost(arena->histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE, + cost_cmd); + SetCost(arena->histogram_dist, self->distance_histogram_size, BROTLI_FALSE, + self->cost_dist_); + + for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) { + min_cost_cmd = BROTLI_MIN(float, min_cost_cmd, cost_cmd[i]); + } + self->min_cost_cmd_ = min_cost_cmd; + + { + float* literal_costs = self->literal_costs_; + float literal_carry = 0.0; + size_t num_bytes = self->num_bytes_; + literal_costs[0] = 0.0; + for (i = 0; i < num_bytes; ++i) { + literal_carry += + arena->cost_literal[ringbuffer[(position + i) & ringbuffer_mask]]; + literal_costs[i + 1] = literal_costs[i] + literal_carry; + literal_carry -= literal_costs[i + 1] - literal_costs[i]; + } + } +} + +static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self, + size_t position, + const uint8_t* ringbuffer, + size_t ringbuffer_mask) { + float* literal_costs = self->literal_costs_; + float literal_carry = 0.0; + float* cost_dist = self->cost_dist_; + float* cost_cmd = self->cost_cmd_; + size_t num_bytes = self->num_bytes_; + size_t i; + BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask, + ringbuffer, self->literal_histograms, + &literal_costs[1]); + literal_costs[0] = 0.0; + for (i = 0; i < num_bytes; ++i) { + literal_carry += literal_costs[i + 1]; + literal_costs[i + 1] = literal_costs[i] + literal_carry; + literal_carry -= literal_costs[i + 1] - literal_costs[i]; + } + for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) { + cost_cmd[i] = (float)FastLog2(11 + (uint32_t)i); + } + for (i = 0; i < self->distance_histogram_size; ++i) { + cost_dist[i] = (float)FastLog2(20 + (uint32_t)i); + } + self->min_cost_cmd_ = (float)FastLog2(11); +} + +static BROTLI_INLINE float ZopfliCostModelGetCommandCost( + const ZopfliCostModel* self, uint16_t cmdcode) { + return self->cost_cmd_[cmdcode]; +} + +static BROTLI_INLINE float ZopfliCostModelGetDistanceCost( + const ZopfliCostModel* self, size_t distcode) { + return self->cost_dist_[distcode]; +} + +static BROTLI_INLINE float ZopfliCostModelGetLiteralCosts( + const ZopfliCostModel* self, size_t from, size_t to) { + return self->literal_costs_[to] - self->literal_costs_[from]; +} + +static BROTLI_INLINE float ZopfliCostModelGetMinCostCmd( + const ZopfliCostModel* self) { + return self->min_cost_cmd_; +} + +/* REQUIRES: len >= 2, start_pos <= pos */ +/* REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity */ +/* Maintains the "ZopfliNode array invariant". */ +static BROTLI_INLINE void UpdateZopfliNode(ZopfliNode* nodes, size_t pos, + size_t start_pos, size_t len, size_t len_code, size_t dist, + size_t short_code, float cost) { + ZopfliNode* next = &nodes[pos + len]; + next->length = (uint32_t)(len | ((len + 9u - len_code) << 25)); + next->distance = (uint32_t)dist; + next->dcode_insert_length = (uint32_t)( + (short_code << 27) | (pos - start_pos)); + next->u.cost = cost; +} + +typedef struct PosData { + size_t pos; + int distance_cache[4]; + float costdiff; + float cost; +} PosData; + +/* Maintains the smallest 8 cost difference together with their positions */ +typedef struct StartPosQueue { + PosData q_[8]; + size_t idx_; +} StartPosQueue; + +static BROTLI_INLINE void InitStartPosQueue(StartPosQueue* self) { + self->idx_ = 0; +} + +static size_t StartPosQueueSize(const StartPosQueue* self) { + return BROTLI_MIN(size_t, self->idx_, 8); +} + +static void StartPosQueuePush(StartPosQueue* self, const PosData* posdata) { + size_t offset = ~(self->idx_++) & 7; + size_t len = StartPosQueueSize(self); + size_t i; + PosData* q = self->q_; + q[offset] = *posdata; + /* Restore the sorted order. In the list of |len| items at most |len - 1| + adjacent element comparisons / swaps are required. */ + for (i = 1; i < len; ++i) { + if (q[offset & 7].costdiff > q[(offset + 1) & 7].costdiff) { + BROTLI_SWAP(PosData, q, offset & 7, (offset + 1) & 7); + } + ++offset; + } +} + +static const PosData* StartPosQueueAt(const StartPosQueue* self, size_t k) { + return &self->q_[(k - self->idx_) & 7]; +} + +/* Returns the minimum possible copy length that can improve the cost of any */ +/* future position. */ +static size_t ComputeMinimumCopyLength(const float start_cost, + const ZopfliNode* nodes, + const size_t num_bytes, + const size_t pos) { + /* Compute the minimum possible cost of reaching any future position. */ + float min_cost = start_cost; + size_t len = 2; + size_t next_len_bucket = 4; + size_t next_len_offset = 10; + while (pos + len <= num_bytes && nodes[pos + len].u.cost <= min_cost) { + /* We already reached (pos + len) with no more cost than the minimum + possible cost of reaching anything from this pos, so there is no point in + looking for lengths <= len. */ + ++len; + if (len == next_len_offset) { + /* We reached the next copy length code bucket, so we add one more + extra bit to the minimum cost. */ + min_cost += 1.0f; + next_len_offset += next_len_bucket; + next_len_bucket *= 2; + } + } + return len; +} + +/* REQUIRES: nodes[pos].cost < kInfinity + REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ +static uint32_t ComputeDistanceShortcut(const size_t block_start, + const size_t pos, + const size_t max_backward_limit, + const size_t gap, + const ZopfliNode* nodes) { + const size_t c_len = ZopfliNodeCopyLength(&nodes[pos]); + const size_t i_len = nodes[pos].dcode_insert_length & 0x7FFFFFF; + const size_t dist = ZopfliNodeCopyDistance(&nodes[pos]); + /* Since |block_start + pos| is the end position of the command, the copy part + starts from |block_start + pos - c_len|. Distances that are greater than + this or greater than |max_backward_limit| + |gap| are static dictionary + references, and do not update the last distances. + Also distance code 0 (last distance) does not update the last distances. */ + if (pos == 0) { + return 0; + } else if (dist + c_len <= block_start + pos + gap && + dist <= max_backward_limit + gap && + ZopfliNodeDistanceCode(&nodes[pos]) > 0) { + return (uint32_t)pos; + } else { + return nodes[pos - c_len - i_len].u.shortcut; + } +} + +/* Fills in dist_cache[0..3] with the last four distances (as defined by + Section 4. of the Spec) that would be used at (block_start + pos) if we + used the shortest path of commands from block_start, computed from + nodes[0..pos]. The last four distances at block_start are in + starting_dist_cache[0..3]. + REQUIRES: nodes[pos].cost < kInfinity + REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */ +static void ComputeDistanceCache(const size_t pos, + const int* starting_dist_cache, + const ZopfliNode* nodes, + int* dist_cache) { + int idx = 0; + size_t p = nodes[pos].u.shortcut; + while (idx < 4 && p > 0) { + const size_t i_len = nodes[p].dcode_insert_length & 0x7FFFFFF; + const size_t c_len = ZopfliNodeCopyLength(&nodes[p]); + const size_t dist = ZopfliNodeCopyDistance(&nodes[p]); + dist_cache[idx++] = (int)dist; + /* Because of prerequisite, p >= c_len + i_len >= 2. */ + p = nodes[p - c_len - i_len].u.shortcut; + } + for (; idx < 4; ++idx) { + dist_cache[idx] = *starting_dist_cache++; + } +} + +/* Maintains "ZopfliNode array invariant" and pushes node to the queue, if it + is eligible. */ +static void EvaluateNode( + const size_t block_start, const size_t pos, const size_t max_backward_limit, + const size_t gap, const int* starting_dist_cache, + const ZopfliCostModel* model, StartPosQueue* queue, ZopfliNode* nodes) { + /* Save cost, because ComputeDistanceCache invalidates it. */ + float node_cost = nodes[pos].u.cost; + nodes[pos].u.shortcut = ComputeDistanceShortcut( + block_start, pos, max_backward_limit, gap, nodes); + if (node_cost <= ZopfliCostModelGetLiteralCosts(model, 0, pos)) { + PosData posdata; + posdata.pos = pos; + posdata.cost = node_cost; + posdata.costdiff = node_cost - + ZopfliCostModelGetLiteralCosts(model, 0, pos); + ComputeDistanceCache( + pos, starting_dist_cache, nodes, posdata.distance_cache); + StartPosQueuePush(queue, &posdata); + } +} + +/* Returns longest copy length. */ +static size_t UpdateNodes( + const size_t num_bytes, const size_t block_start, const size_t pos, + const uint8_t* ringbuffer, const size_t ringbuffer_mask, + const BrotliEncoderParams* params, const size_t max_backward_limit, + const int* starting_dist_cache, const size_t num_matches, + const BackwardMatch* matches, const ZopfliCostModel* model, + StartPosQueue* queue, ZopfliNode* nodes) { + const size_t stream_offset = params->stream_offset; + const size_t cur_ix = block_start + pos; + const size_t cur_ix_masked = cur_ix & ringbuffer_mask; + const size_t max_distance = BROTLI_MIN(size_t, cur_ix, max_backward_limit); + const size_t dictionary_start = BROTLI_MIN(size_t, + cur_ix + stream_offset, max_backward_limit); + const size_t max_len = num_bytes - pos; + const size_t max_zopfli_len = MaxZopfliLen(params); + const size_t max_iters = MaxZopfliCandidates(params); + size_t min_len; + size_t result = 0; + size_t k; + const CompoundDictionary* addon = ¶ms->dictionary.compound; + size_t gap = addon->total_size; + + BROTLI_DCHECK(cur_ix_masked + max_len <= ringbuffer_mask); + + EvaluateNode(block_start + stream_offset, pos, max_backward_limit, gap, + starting_dist_cache, model, queue, nodes); + + { + const PosData* posdata = StartPosQueueAt(queue, 0); + float min_cost = (posdata->cost + ZopfliCostModelGetMinCostCmd(model) + + ZopfliCostModelGetLiteralCosts(model, posdata->pos, pos)); + min_len = ComputeMinimumCopyLength(min_cost, nodes, num_bytes, pos); + } + + /* Go over the command starting positions in order of increasing cost + difference. */ + for (k = 0; k < max_iters && k < StartPosQueueSize(queue); ++k) { + const PosData* posdata = StartPosQueueAt(queue, k); + const size_t start = posdata->pos; + const uint16_t inscode = GetInsertLengthCode(pos - start); + const float start_costdiff = posdata->costdiff; + const float base_cost = start_costdiff + (float)GetInsertExtra(inscode) + + ZopfliCostModelGetLiteralCosts(model, 0, pos); + + /* Look for last distance matches using the distance cache from this + starting position. */ + size_t best_len = min_len - 1; + size_t j = 0; + for (; j < BROTLI_NUM_DISTANCE_SHORT_CODES && best_len < max_len; ++j) { + const size_t idx = kDistanceCacheIndex[j]; + const size_t backward = + (size_t)(posdata->distance_cache[idx] + kDistanceCacheOffset[j]); + size_t prev_ix = cur_ix - backward; + size_t len = 0; + uint8_t continuation = ringbuffer[cur_ix_masked + best_len]; + if (cur_ix_masked + best_len > ringbuffer_mask) { + break; + } + if (BROTLI_PREDICT_FALSE(backward > dictionary_start + gap)) { + /* Word dictionary -> ignore. */ + continue; + } + if (backward <= max_distance) { + /* Regular backward reference. */ + if (prev_ix >= cur_ix) { + continue; + } + + prev_ix &= ringbuffer_mask; + if (prev_ix + best_len > ringbuffer_mask || + continuation != ringbuffer[prev_ix + best_len]) { + continue; + } + len = FindMatchLengthWithLimit(&ringbuffer[prev_ix], + &ringbuffer[cur_ix_masked], + max_len); + } else if (backward > dictionary_start) { + size_t d = 0; + size_t offset; + size_t limit; + const uint8_t* source; + offset = dictionary_start + 1 + addon->total_size - 1; + while (offset >= backward + addon->chunk_offsets[d + 1]) d++; + source = addon->chunk_source[d]; + offset = offset - addon->chunk_offsets[d] - backward; + limit = addon->chunk_offsets[d + 1] - addon->chunk_offsets[d] - offset; + limit = limit > max_len ? max_len : limit; + if (best_len >= limit || + continuation != source[offset + best_len]) { + continue; + } + len = FindMatchLengthWithLimit(&source[offset], + &ringbuffer[cur_ix_masked], + limit); + } else { + /* "Gray" area. It is addressable by decoder, but this encoder + instance does not have that data -> should not touch it. */ + continue; + } + { + const float dist_cost = base_cost + + ZopfliCostModelGetDistanceCost(model, j); + size_t l; + for (l = best_len + 1; l <= len; ++l) { + const uint16_t copycode = GetCopyLengthCode(l); + const uint16_t cmdcode = + CombineLengthCodes(inscode, copycode, j == 0); + const float cost = (cmdcode < 128 ? base_cost : dist_cost) + + (float)GetCopyExtra(copycode) + + ZopfliCostModelGetCommandCost(model, cmdcode); + if (cost < nodes[pos + l].u.cost) { + UpdateZopfliNode(nodes, pos, start, l, l, backward, j + 1, cost); + result = BROTLI_MAX(size_t, result, l); + } + best_len = l; + } + } + } + + /* At higher iterations look only for new last distance matches, since + looking only for new command start positions with the same distances + does not help much. */ + if (k >= 2) continue; + + { + /* Loop through all possible copy lengths at this position. */ + size_t len = min_len; + for (j = 0; j < num_matches; ++j) { + BackwardMatch match = matches[j]; + size_t dist = match.distance; + BROTLI_BOOL is_dictionary_match = + TO_BROTLI_BOOL(dist > dictionary_start + gap); + /* We already tried all possible last distance matches, so we can use + normal distance code here. */ + size_t dist_code = dist + BROTLI_NUM_DISTANCE_SHORT_CODES - 1; + uint16_t dist_symbol; + uint32_t distextra; + uint32_t distnumextra; + float dist_cost; + size_t max_match_len; + PrefixEncodeCopyDistance( + dist_code, params->dist.num_direct_distance_codes, + params->dist.distance_postfix_bits, &dist_symbol, &distextra); + distnumextra = dist_symbol >> 10; + dist_cost = base_cost + (float)distnumextra + + ZopfliCostModelGetDistanceCost(model, dist_symbol & 0x3FF); + + /* Try all copy lengths up until the maximum copy length corresponding + to this distance. If the distance refers to the static dictionary, or + the maximum length is long enough, try only one maximum length. */ + max_match_len = BackwardMatchLength(&match); + if (len < max_match_len && + (is_dictionary_match || max_match_len > max_zopfli_len)) { + len = max_match_len; + } + for (; len <= max_match_len; ++len) { + const size_t len_code = + is_dictionary_match ? BackwardMatchLengthCode(&match) : len; + const uint16_t copycode = GetCopyLengthCode(len_code); + const uint16_t cmdcode = CombineLengthCodes(inscode, copycode, 0); + const float cost = dist_cost + (float)GetCopyExtra(copycode) + + ZopfliCostModelGetCommandCost(model, cmdcode); + if (cost < nodes[pos + len].u.cost) { + UpdateZopfliNode(nodes, pos, start, len, len_code, dist, 0, cost); + result = BROTLI_MAX(size_t, result, len); + } + } + } + } + } + return result; +} + +static size_t ComputeShortestPathFromNodes(size_t num_bytes, + ZopfliNode* nodes) { + size_t index = num_bytes; + size_t num_commands = 0; + while ((nodes[index].dcode_insert_length & 0x7FFFFFF) == 0 && + nodes[index].length == 1) --index; + nodes[index].u.next = BROTLI_UINT32_MAX; + while (index != 0) { + size_t len = ZopfliNodeCommandLength(&nodes[index]); + index -= len; + nodes[index].u.next = (uint32_t)len; + num_commands++; + } + return num_commands; +} + +/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */ +void BrotliZopfliCreateCommands(const size_t num_bytes, + const size_t block_start, const ZopfliNode* nodes, int* dist_cache, + size_t* last_insert_len, const BrotliEncoderParams* params, + Command* commands, size_t* num_literals) { + const size_t stream_offset = params->stream_offset; + const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); + size_t pos = 0; + uint32_t offset = nodes[0].u.next; + size_t i; + size_t gap = params->dictionary.compound.total_size; + for (i = 0; offset != BROTLI_UINT32_MAX; i++) { + const ZopfliNode* next = &nodes[pos + offset]; + size_t copy_length = ZopfliNodeCopyLength(next); + size_t insert_length = next->dcode_insert_length & 0x7FFFFFF; + pos += insert_length; + offset = next->u.next; + if (i == 0) { + insert_length += *last_insert_len; + *last_insert_len = 0; + } + { + size_t distance = ZopfliNodeCopyDistance(next); + size_t len_code = ZopfliNodeLengthCode(next); + size_t dictionary_start = BROTLI_MIN(size_t, + block_start + pos + stream_offset, max_backward_limit); + BROTLI_BOOL is_dictionary = + TO_BROTLI_BOOL(distance > dictionary_start + gap); + size_t dist_code = ZopfliNodeDistanceCode(next); + InitCommand(&commands[i], ¶ms->dist, insert_length, + copy_length, (int)len_code - (int)copy_length, dist_code); + + if (!is_dictionary && dist_code > 0) { + dist_cache[3] = dist_cache[2]; + dist_cache[2] = dist_cache[1]; + dist_cache[1] = dist_cache[0]; + dist_cache[0] = (int)distance; + } + } + + *num_literals += insert_length; + pos += copy_length; + } + *last_insert_len += num_bytes - pos; +} + +static size_t ZopfliIterate(size_t num_bytes, size_t position, + const uint8_t* ringbuffer, size_t ringbuffer_mask, + const BrotliEncoderParams* params, const size_t gap, const int* dist_cache, + const ZopfliCostModel* model, const uint32_t* num_matches, + const BackwardMatch* matches, ZopfliNode* nodes) { + const size_t stream_offset = params->stream_offset; + const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); + const size_t max_zopfli_len = MaxZopfliLen(params); + StartPosQueue queue; + size_t cur_match_pos = 0; + size_t i; + nodes[0].length = 0; + nodes[0].u.cost = 0; + InitStartPosQueue(&queue); + for (i = 0; i + 3 < num_bytes; i++) { + size_t skip = UpdateNodes(num_bytes, position, i, ringbuffer, + ringbuffer_mask, params, max_backward_limit, dist_cache, + num_matches[i], &matches[cur_match_pos], model, &queue, nodes); + if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0; + cur_match_pos += num_matches[i]; + if (num_matches[i] == 1 && + BackwardMatchLength(&matches[cur_match_pos - 1]) > max_zopfli_len) { + skip = BROTLI_MAX(size_t, + BackwardMatchLength(&matches[cur_match_pos - 1]), skip); + } + if (skip > 1) { + skip--; + while (skip) { + i++; + if (i + 3 >= num_bytes) break; + EvaluateNode(position + stream_offset, i, max_backward_limit, gap, + dist_cache, model, &queue, nodes); + cur_match_pos += num_matches[i]; + skip--; + } + } + } + return ComputeShortestPathFromNodes(num_bytes, nodes); +} + +static void MergeMatches(BackwardMatch* dst, + BackwardMatch* src1, size_t len1, BackwardMatch* src2, size_t len2) { + while (len1 > 0 && len2 > 0) { + size_t l1 = BackwardMatchLength(src1); + size_t l2 = BackwardMatchLength(src2); + if (l1 < l2 || ((l1 == l2) && (src1->distance < src2->distance))) { + *dst++ = *src1++; + len1--; + } else { + *dst++ = *src2++; + len2--; + } + } + while (len1-- > 0) *dst++ = *src1++; + while (len2-- > 0) *dst++ = *src2++; +} + +/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */ +size_t BrotliZopfliComputeShortestPath(MemoryManager* m, size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + const int* dist_cache, Hasher* hasher, ZopfliNode* nodes) { + const size_t stream_offset = params->stream_offset; + const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); + const size_t max_zopfli_len = MaxZopfliLen(params); + StartPosQueue queue; + BackwardMatch* BROTLI_RESTRICT matches = + BROTLI_ALLOC(m, BackwardMatch, 2 * (MAX_NUM_MATCHES_H10 + 64)); + const size_t store_end = num_bytes >= StoreLookaheadH10() ? + position + num_bytes - StoreLookaheadH10() + 1 : position; + size_t i; + const CompoundDictionary* addon = ¶ms->dictionary.compound; + size_t gap = addon->total_size; + size_t lz_matches_offset = + (addon->num_chunks != 0) ? (MAX_NUM_MATCHES_H10 + 128) : 0; + ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) || BROTLI_IS_NULL(matches)) { + return 0; + } + nodes[0].length = 0; + nodes[0].u.cost = 0; + InitZopfliCostModel(m, model, ¶ms->dist, num_bytes); + if (BROTLI_IS_OOM(m)) return 0; + ZopfliCostModelSetFromLiteralCosts( + model, position, ringbuffer, ringbuffer_mask); + InitStartPosQueue(&queue); + for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) { + const size_t pos = position + i; + const size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit); + const size_t dictionary_start = BROTLI_MIN(size_t, + pos + stream_offset, max_backward_limit); + size_t skip; + size_t num_matches; + int dict_id = 0; + if (params->dictionary.contextual.context_based) { + uint8_t p1 = pos >= 1 ? + ringbuffer[(size_t)(pos - 1) & ringbuffer_mask] : 0; + uint8_t p2 = pos >= 2 ? + ringbuffer[(size_t)(pos - 2) & ringbuffer_mask] : 0; + dict_id = params->dictionary.contextual.context_map[ + BROTLI_CONTEXT(p1, p2, literal_context_lut)]; + } + num_matches = FindAllMatchesH10(&hasher->privat._H10, + params->dictionary.contextual.dict[dict_id], + ringbuffer, ringbuffer_mask, pos, num_bytes - i, max_distance, + dictionary_start + gap, params, &matches[lz_matches_offset]); + if (addon->num_chunks != 0) { + size_t cd_matches = LookupAllCompoundDictionaryMatches(addon, + ringbuffer, ringbuffer_mask, pos, 3, num_bytes - i, + dictionary_start, params->dist.max_distance, + &matches[lz_matches_offset - 64], 64); + MergeMatches(matches, &matches[lz_matches_offset - 64], cd_matches, + &matches[lz_matches_offset], num_matches); + num_matches += cd_matches; + } + if (num_matches > 0 && + BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) { + matches[0] = matches[num_matches - 1]; + num_matches = 1; + } + skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, + params, max_backward_limit, dist_cache, num_matches, matches, model, + &queue, nodes); + if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0; + if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) { + skip = BROTLI_MAX(size_t, BackwardMatchLength(&matches[0]), skip); + } + if (skip > 1) { + /* Add the tail of the copy to the hasher. */ + StoreRangeH10(&hasher->privat._H10, + ringbuffer, ringbuffer_mask, pos + 1, BROTLI_MIN( + size_t, pos + skip, store_end)); + skip--; + while (skip) { + i++; + if (i + HashTypeLengthH10() - 1 >= num_bytes) break; + EvaluateNode(position + stream_offset, i, max_backward_limit, gap, + dist_cache, model, &queue, nodes); + skip--; + } + } + } + CleanupZopfliCostModel(m, model); + BROTLI_FREE(m, model); + BROTLI_FREE(m, matches); + return ComputeShortestPathFromNodes(num_bytes, nodes); +} + +void BrotliCreateZopfliBackwardReferences(MemoryManager* m, size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + Hasher* hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals) { + ZopfliNode* nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) return; + BrotliInitZopfliNodes(nodes, num_bytes + 1); + *num_commands += BrotliZopfliComputeShortestPath(m, num_bytes, + position, ringbuffer, ringbuffer_mask, literal_context_lut, params, + dist_cache, hasher, nodes); + if (BROTLI_IS_OOM(m)) return; + BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache, + last_insert_len, params, commands, num_literals); + BROTLI_FREE(m, nodes); +} + +void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + Hasher* hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals) { + const size_t stream_offset = params->stream_offset; + const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); + uint32_t* num_matches = BROTLI_ALLOC(m, uint32_t, num_bytes); + size_t matches_size = 4 * num_bytes; + const size_t store_end = num_bytes >= StoreLookaheadH10() ? + position + num_bytes - StoreLookaheadH10() + 1 : position; + size_t cur_match_pos = 0; + size_t i; + size_t orig_num_literals; + size_t orig_last_insert_len; + int orig_dist_cache[4]; + size_t orig_num_commands; + ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1); + ZopfliNode* nodes; + BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size); + const CompoundDictionary* addon = ¶ms->dictionary.compound; + size_t gap = addon->total_size; + size_t shadow_matches = + (addon->num_chunks != 0) ? (MAX_NUM_MATCHES_H10 + 128) : 0; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) || + BROTLI_IS_NULL(num_matches) || BROTLI_IS_NULL(matches)) { + return; + } + for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) { + const size_t pos = position + i; + size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit); + size_t dictionary_start = BROTLI_MIN(size_t, + pos + stream_offset, max_backward_limit); + size_t max_length = num_bytes - i; + size_t num_found_matches; + size_t cur_match_end; + size_t j; + int dict_id = 0; + if (params->dictionary.contextual.context_based) { + uint8_t p1 = pos >= 1 ? + ringbuffer[(size_t)(pos - 1) & ringbuffer_mask] : 0; + uint8_t p2 = pos >= 2 ? + ringbuffer[(size_t)(pos - 2) & ringbuffer_mask] : 0; + dict_id = params->dictionary.contextual.context_map[ + BROTLI_CONTEXT(p1, p2, literal_context_lut)]; + } + /* Ensure that we have enough free slots. */ + BROTLI_ENSURE_CAPACITY(m, BackwardMatch, matches, matches_size, + cur_match_pos + MAX_NUM_MATCHES_H10 + shadow_matches); + if (BROTLI_IS_OOM(m)) return; + num_found_matches = FindAllMatchesH10(&hasher->privat._H10, + params->dictionary.contextual.dict[dict_id], + ringbuffer, ringbuffer_mask, pos, max_length, + max_distance, dictionary_start + gap, params, + &matches[cur_match_pos + shadow_matches]); + if (addon->num_chunks != 0) { + size_t cd_matches = LookupAllCompoundDictionaryMatches(addon, + ringbuffer, ringbuffer_mask, pos, 3, max_length, + dictionary_start, params->dist.max_distance, + &matches[cur_match_pos + shadow_matches - 64], 64); + MergeMatches(&matches[cur_match_pos], + &matches[cur_match_pos + shadow_matches - 64], cd_matches, + &matches[cur_match_pos + shadow_matches], num_found_matches); + num_found_matches += cd_matches; + } + cur_match_end = cur_match_pos + num_found_matches; + for (j = cur_match_pos; j + 1 < cur_match_end; ++j) { + BROTLI_DCHECK(BackwardMatchLength(&matches[j]) <= + BackwardMatchLength(&matches[j + 1])); + } + num_matches[i] = (uint32_t)num_found_matches; + if (num_found_matches > 0) { + const size_t match_len = BackwardMatchLength(&matches[cur_match_end - 1]); + if (match_len > MAX_ZOPFLI_LEN_QUALITY_11) { + const size_t skip = match_len - 1; + matches[cur_match_pos++] = matches[cur_match_end - 1]; + num_matches[i] = 1; + /* Add the tail of the copy to the hasher. */ + StoreRangeH10(&hasher->privat._H10, + ringbuffer, ringbuffer_mask, pos + 1, + BROTLI_MIN(size_t, pos + match_len, store_end)); + memset(&num_matches[i + 1], 0, skip * sizeof(num_matches[0])); + i += skip; + } else { + cur_match_pos = cur_match_end; + } + } + } + orig_num_literals = *num_literals; + orig_last_insert_len = *last_insert_len; + memcpy(orig_dist_cache, dist_cache, 4 * sizeof(dist_cache[0])); + orig_num_commands = *num_commands; + nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) return; + InitZopfliCostModel(m, model, ¶ms->dist, num_bytes); + if (BROTLI_IS_OOM(m)) return; + for (i = 0; i < 2; i++) { + BrotliInitZopfliNodes(nodes, num_bytes + 1); + if (i == 0) { + ZopfliCostModelSetFromLiteralCosts( + model, position, ringbuffer, ringbuffer_mask); + } else { + ZopfliCostModelSetFromCommands(model, position, ringbuffer, + ringbuffer_mask, commands, *num_commands - orig_num_commands, + orig_last_insert_len); + } + *num_commands = orig_num_commands; + *num_literals = orig_num_literals; + *last_insert_len = orig_last_insert_len; + memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0])); + *num_commands += ZopfliIterate(num_bytes, position, ringbuffer, + ringbuffer_mask, params, gap, dist_cache, model, num_matches, matches, + nodes); + BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache, + last_insert_len, params, commands, num_literals); + } + CleanupZopfliCostModel(m, model); + BROTLI_FREE(m, model); + BROTLI_FREE(m, nodes); + BROTLI_FREE(m, matches); + BROTLI_FREE(m, num_matches); +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/backward_references_hq.h b/packages/runtime/vendor/brotli/c/enc/backward_references_hq.h new file mode 100644 index 000000000..4aa270674 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/backward_references_hq.h @@ -0,0 +1,92 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function to find backward reference copies. */ + +#ifndef BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_ +#define BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_ + +#include "../common/context.h" +#include "../common/platform.h" +#include "command.h" +#include "hash.h" +#include "memory.h" +#include "params.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +BROTLI_INTERNAL void BrotliCreateZopfliBackwardReferences(MemoryManager* m, + size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + Hasher* hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals); + +BROTLI_INTERNAL void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, + size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + Hasher* hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals); + +typedef struct ZopfliNode { + /* Best length to get up to this byte (not including this byte itself) + highest 7 bit is used to reconstruct the length code. */ + uint32_t length; + /* Distance associated with the length. */ + uint32_t distance; + /* Number of literal inserts before this copy; highest 5 bits contain + distance short code + 1 (or zero if no short code). */ + uint32_t dcode_insert_length; + + /* This union holds information used by dynamic-programming. During forward + pass |cost| it used to store the goal function. When node is processed its + |cost| is invalidated in favor of |shortcut|. On path back-tracing pass + |next| is assigned the offset to next node on the path. */ + union { + /* Smallest cost to get to this byte from the beginning, as found so far. */ + float cost; + /* Offset to the next node on the path. Equals to command_length() of the + next node on the path. For last node equals to BROTLI_UINT32_MAX */ + uint32_t next; + /* Node position that provides next distance for distance cache. */ + uint32_t shortcut; + } u; +} ZopfliNode; + +BROTLI_INTERNAL void BrotliInitZopfliNodes(ZopfliNode* array, size_t length); + +/* Computes the shortest path of commands from position to at most + position + num_bytes. + + On return, path->size() is the number of commands found and path[i] is the + length of the i-th command (copy length plus insert length). + Note that the sum of the lengths of all commands can be less than num_bytes. + + On return, the nodes[0..num_bytes] array will have the following + "ZopfliNode array invariant": + For each i in [1..num_bytes], if nodes[i].cost < kInfinity, then + (1) nodes[i].copy_length() >= 2 + (2) nodes[i].command_length() <= i and + (3) nodes[i - nodes[i].command_length()].cost < kInfinity */ +BROTLI_INTERNAL size_t BrotliZopfliComputeShortestPath( + MemoryManager* m, size_t num_bytes, + size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + const int* dist_cache, Hasher* hasher, ZopfliNode* nodes); + +BROTLI_INTERNAL void BrotliZopfliCreateCommands( + const size_t num_bytes, const size_t block_start, const ZopfliNode* nodes, + int* dist_cache, size_t* last_insert_len, const BrotliEncoderParams* params, + Command* commands, size_t* num_literals); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/backward_references_inc.h b/packages/runtime/vendor/brotli/c/enc/backward_references_inc.h new file mode 100644 index 000000000..752c12e9f --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/backward_references_inc.h @@ -0,0 +1,189 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: EXPORT_FN, FN */ + +static BROTLI_NOINLINE void EXPORT_FN(CreateBackwardReferences)( + size_t num_bytes, size_t position, + const uint8_t* ringbuffer, size_t ringbuffer_mask, + ContextLut literal_context_lut, const BrotliEncoderParams* params, + Hasher* hasher, int* dist_cache, size_t* last_insert_len, + Command* commands, size_t* num_commands, size_t* num_literals) { + HASHER()* privat = &hasher->privat.FN(_); + /* Set maximum distance, see section 9.1. of the spec. */ + const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); + const size_t position_offset = params->stream_offset; + + const Command* const orig_commands = commands; + size_t insert_length = *last_insert_len; + const size_t pos_end = position + num_bytes; + const size_t store_end = num_bytes >= FN(StoreLookahead)() ? + position + num_bytes - FN(StoreLookahead)() + 1 : position; + + /* For speed up heuristics for random data. */ + const size_t random_heuristics_window_size = + LiteralSpreeLengthForSparseSearch(params); + size_t apply_random_heuristics = position + random_heuristics_window_size; + const size_t gap = params->dictionary.compound.total_size; + + /* Minimum score to accept a backward reference. */ + const score_t kMinScore = BROTLI_SCORE_BASE + 100; + + FN(PrepareDistanceCache)(privat, dist_cache); + + while (position + FN(HashTypeLength)() < pos_end) { + size_t max_length = pos_end - position; + size_t max_distance = BROTLI_MIN(size_t, position, max_backward_limit); + size_t dictionary_start = BROTLI_MIN(size_t, + position + position_offset, max_backward_limit); + HasherSearchResult sr; + int dict_id = 0; + uint8_t p1 = 0; + uint8_t p2 = 0; + if (params->dictionary.contextual.context_based) { + p1 = position >= 1 ? + ringbuffer[(size_t)(position - 1) & ringbuffer_mask] : 0; + p2 = position >= 2 ? + ringbuffer[(size_t)(position - 2) & ringbuffer_mask] : 0; + dict_id = params->dictionary.contextual.context_map[ + BROTLI_CONTEXT(p1, p2, literal_context_lut)]; + } + sr.len = 0; + sr.len_code_delta = 0; + sr.distance = 0; + sr.score = kMinScore; + FN(FindLongestMatch)(privat, params->dictionary.contextual.dict[dict_id], + ringbuffer, ringbuffer_mask, dist_cache, position, max_length, + max_distance, dictionary_start + gap, params->dist.max_distance, &sr); + if (ENABLE_COMPOUND_DICTIONARY) { + LookupCompoundDictionaryMatch(¶ms->dictionary.compound, ringbuffer, + ringbuffer_mask, dist_cache, position, max_length, + dictionary_start, params->dist.max_distance, &sr); + } + if (sr.score > kMinScore) { + /* Found a match. Let's look for something even better ahead. */ + int delayed_backward_references_in_row = 0; + --max_length; + for (;; --max_length) { + const score_t cost_diff_lazy = 175; + HasherSearchResult sr2; + sr2.len = params->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH ? + BROTLI_MIN(size_t, sr.len - 1, max_length) : 0; + sr2.len_code_delta = 0; + sr2.distance = 0; + sr2.score = kMinScore; + max_distance = BROTLI_MIN(size_t, position + 1, max_backward_limit); + dictionary_start = BROTLI_MIN(size_t, + position + 1 + position_offset, max_backward_limit); + if (params->dictionary.contextual.context_based) { + p2 = p1; + p1 = ringbuffer[position & ringbuffer_mask]; + dict_id = params->dictionary.contextual.context_map[ + BROTLI_CONTEXT(p1, p2, literal_context_lut)]; + } + FN(FindLongestMatch)(privat, + params->dictionary.contextual.dict[dict_id], + ringbuffer, ringbuffer_mask, dist_cache, position + 1, max_length, + max_distance, dictionary_start + gap, params->dist.max_distance, + &sr2); + if (ENABLE_COMPOUND_DICTIONARY) { + LookupCompoundDictionaryMatch( + ¶ms->dictionary.compound, ringbuffer, + ringbuffer_mask, dist_cache, position + 1, max_length, + dictionary_start, params->dist.max_distance, &sr2); + } + if (sr2.score >= sr.score + cost_diff_lazy) { + /* Ok, let's just write one byte for now and start a match from the + next byte. */ + ++position; + ++insert_length; + sr = sr2; + if (++delayed_backward_references_in_row < 4 && + position + FN(HashTypeLength)() < pos_end) { + continue; + } + } + break; + } + apply_random_heuristics = + position + 2 * sr.len + random_heuristics_window_size; + dictionary_start = BROTLI_MIN(size_t, + position + position_offset, max_backward_limit); + { + /* The first 16 codes are special short-codes, + and the minimum offset is 1. */ + size_t distance_code = ComputeDistanceCode( + sr.distance, dictionary_start + gap, dist_cache); + if ((sr.distance <= (dictionary_start + gap)) && distance_code > 0) { + dist_cache[3] = dist_cache[2]; + dist_cache[2] = dist_cache[1]; + dist_cache[1] = dist_cache[0]; + dist_cache[0] = (int)sr.distance; + FN(PrepareDistanceCache)(privat, dist_cache); + } + InitCommand(commands++, ¶ms->dist, insert_length, + sr.len, sr.len_code_delta, distance_code); + } + *num_literals += insert_length; + insert_length = 0; + /* Put the hash keys into the table, if there are enough bytes left. + Depending on the hasher implementation, it can push all positions + in the given range or only a subset of them. + Avoid hash poisoning with RLE data. */ + { + size_t range_start = position + 2; + size_t range_end = BROTLI_MIN(size_t, position + sr.len, store_end); + if (sr.distance < (sr.len >> 2)) { + range_start = BROTLI_MIN(size_t, range_end, BROTLI_MAX(size_t, + range_start, position + sr.len - (sr.distance << 2))); + } + FN(StoreRange)(privat, ringbuffer, ringbuffer_mask, range_start, + range_end); + } + position += sr.len; + } else { + ++insert_length; + ++position; + /* If we have not seen matches for a long time, we can skip some + match lookups. Unsuccessful match lookups are very very expensive + and this kind of a heuristic speeds up compression quite + a lot. */ + if (position > apply_random_heuristics) { + /* Going through uncompressible data, jump. */ + if (position > + apply_random_heuristics + 4 * random_heuristics_window_size) { + /* It is quite a long time since we saw a copy, so we assume + that this data is not compressible, and store hashes less + often. Hashes of non compressible data are less likely to + turn out to be useful in the future, too, so we store less of + them to not to flood out the hash table of good compressible + data. */ + const size_t kMargin = + BROTLI_MAX(size_t, FN(StoreLookahead)() - 1, 4); + size_t pos_jump = + BROTLI_MIN(size_t, position + 16, pos_end - kMargin); + for (; position < pos_jump; position += 4) { + FN(Store)(privat, ringbuffer, ringbuffer_mask, position); + insert_length += 4; + } + } else { + const size_t kMargin = + BROTLI_MAX(size_t, FN(StoreLookahead)() - 1, 2); + size_t pos_jump = + BROTLI_MIN(size_t, position + 8, pos_end - kMargin); + for (; position < pos_jump; position += 2) { + FN(Store)(privat, ringbuffer, ringbuffer_mask, position); + insert_length += 2; + } + } + } + } + } + insert_length += pos_end - position; + *last_insert_len = insert_length; + *num_commands += (size_t)(commands - orig_commands); +} diff --git a/packages/runtime/vendor/brotli/c/enc/bit_cost.c b/packages/runtime/vendor/brotli/c/enc/bit_cost.c new file mode 100644 index 000000000..059f8d996 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/bit_cost.c @@ -0,0 +1,60 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions to estimate the bit cost of Huffman trees. */ + +#include "bit_cost.h" + +#include "../common/platform.h" +#include "fast_log.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +double BrotliBitsEntropy(const uint32_t* population, size_t size) { + size_t sum = 0; + double retval = 0; + const uint32_t* population_end = population + size; + size_t p; + if (size & 1) { + goto odd_number_of_elements_left; + } + while (population < population_end) { + p = *population++; + sum += p; + retval -= (double)p * FastLog2(p); + odd_number_of_elements_left: + p = *population++; + sum += p; + retval -= (double)p * FastLog2(p); + } + if (sum) retval += (double)sum * FastLog2(sum); + + if (retval < (double)sum) { + /* TODO(eustas): consider doing that per-symbol? */ + /* At least one bit per literal is needed. */ + retval = (double)sum; + } + + return retval; +} + +#define FN(X) X ## Literal +#include "bit_cost_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Command +#include "bit_cost_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Distance +#include "bit_cost_inc.h" /* NOLINT(build/include) */ +#undef FN + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/bit_cost.h b/packages/runtime/vendor/brotli/c/enc/bit_cost.h new file mode 100644 index 000000000..90313c4c3 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/bit_cost.h @@ -0,0 +1,32 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions to estimate the bit cost of Huffman trees. */ + +#ifndef BROTLI_ENC_BIT_COST_H_ +#define BROTLI_ENC_BIT_COST_H_ + +#include "../common/platform.h" +#include "histogram.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +BROTLI_INTERNAL double BrotliBitsEntropy( + const uint32_t* population, size_t size); +BROTLI_INTERNAL double BrotliPopulationCostLiteral( + const HistogramLiteral* histogram); +BROTLI_INTERNAL double BrotliPopulationCostCommand( + const HistogramCommand* histogram); +BROTLI_INTERNAL double BrotliPopulationCostDistance( + const HistogramDistance* histogram); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_BIT_COST_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/bit_cost_inc.h b/packages/runtime/vendor/brotli/c/enc/bit_cost_inc.h new file mode 100644 index 000000000..8e67f2503 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/bit_cost_inc.h @@ -0,0 +1,127 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN */ + +#define HistogramType FN(Histogram) + +double FN(BrotliPopulationCost)(const HistogramType* histogram) { + static const double kOneSymbolHistogramCost = 12; + static const double kTwoSymbolHistogramCost = 20; + static const double kThreeSymbolHistogramCost = 28; + static const double kFourSymbolHistogramCost = 37; + const size_t data_size = FN(HistogramDataSize)(); + int count = 0; + size_t s[5]; + double bits = 0.0; + size_t i; + if (histogram->total_count_ == 0) { + return kOneSymbolHistogramCost; + } + for (i = 0; i < data_size; ++i) { + if (histogram->data_[i] > 0) { + s[count] = i; + ++count; + if (count > 4) break; + } + } + if (count == 1) { + return kOneSymbolHistogramCost; + } + if (count == 2) { + return (kTwoSymbolHistogramCost + (double)histogram->total_count_); + } + if (count == 3) { + const uint32_t histo0 = histogram->data_[s[0]]; + const uint32_t histo1 = histogram->data_[s[1]]; + const uint32_t histo2 = histogram->data_[s[2]]; + const uint32_t histomax = + BROTLI_MAX(uint32_t, histo0, BROTLI_MAX(uint32_t, histo1, histo2)); + return (kThreeSymbolHistogramCost + + 2 * (histo0 + histo1 + histo2) - histomax); + } + if (count == 4) { + uint32_t histo[4]; + uint32_t h23; + uint32_t histomax; + for (i = 0; i < 4; ++i) { + histo[i] = histogram->data_[s[i]]; + } + /* Sort */ + for (i = 0; i < 4; ++i) { + size_t j; + for (j = i + 1; j < 4; ++j) { + if (histo[j] > histo[i]) { + BROTLI_SWAP(uint32_t, histo, j, i); + } + } + } + h23 = histo[2] + histo[3]; + histomax = BROTLI_MAX(uint32_t, h23, histo[0]); + return (kFourSymbolHistogramCost + + 3 * h23 + 2 * (histo[0] + histo[1]) - histomax); + } + + { + /* In this loop we compute the entropy of the histogram and simultaneously + build a simplified histogram of the code length codes where we use the + zero repeat code 17, but we don't use the non-zero repeat code 16. */ + size_t max_depth = 1; + uint32_t depth_histo[BROTLI_CODE_LENGTH_CODES] = { 0 }; + const double log2total = FastLog2(histogram->total_count_); + for (i = 0; i < data_size;) { + if (histogram->data_[i] > 0) { + /* Compute -log2(P(symbol)) = -log2(count(symbol)/total_count) = + = log2(total_count) - log2(count(symbol)) */ + double log2p = log2total - FastLog2(histogram->data_[i]); + /* Approximate the bit depth by round(-log2(P(symbol))) */ + size_t depth = (size_t)(log2p + 0.5); + bits += histogram->data_[i] * log2p; + if (depth > 15) { + depth = 15; + } + if (depth > max_depth) { + max_depth = depth; + } + ++depth_histo[depth]; + ++i; + } else { + /* Compute the run length of zeros and add the appropriate number of 0 + and 17 code length codes to the code length code histogram. */ + uint32_t reps = 1; + size_t k; + for (k = i + 1; k < data_size && histogram->data_[k] == 0; ++k) { + ++reps; + } + i += reps; + if (i == data_size) { + /* Don't add any cost for the last zero run, since these are encoded + only implicitly. */ + break; + } + if (reps < 3) { + depth_histo[0] += reps; + } else { + reps -= 2; + while (reps > 0) { + ++depth_histo[BROTLI_REPEAT_ZERO_CODE_LENGTH]; + /* Add the 3 extra bits for the 17 code length code. */ + bits += 3; + reps >>= 3; + } + } + } + } + /* Add the estimated encoding cost of the code length code histogram. */ + bits += (double)(18 + 2 * max_depth); + /* Add the entropy of the code length code histogram. */ + bits += BrotliBitsEntropy(depth_histo, BROTLI_CODE_LENGTH_CODES); + } + return bits; +} + +#undef HistogramType diff --git a/packages/runtime/vendor/brotli/c/enc/block_encoder_inc.h b/packages/runtime/vendor/brotli/c/enc/block_encoder_inc.h new file mode 100644 index 000000000..8cbd5eac6 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/block_encoder_inc.h @@ -0,0 +1,34 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2014 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN */ + +#define HistogramType FN(Histogram) + +/* Creates entropy codes for all block types and stores them to the bit + stream. */ +static void FN(BuildAndStoreEntropyCodes)(MemoryManager* m, BlockEncoder* self, + const HistogramType* histograms, const size_t histograms_size, + const size_t alphabet_size, HuffmanTree* tree, + size_t* storage_ix, uint8_t* storage) { + const size_t table_size = histograms_size * self->histogram_length_; + self->depths_ = BROTLI_ALLOC(m, uint8_t, table_size); + self->bits_ = BROTLI_ALLOC(m, uint16_t, table_size); + if (BROTLI_IS_OOM(m)) return; + + { + size_t i; + for (i = 0; i < histograms_size; ++i) { + size_t ix = i * self->histogram_length_; + BuildAndStoreHuffmanTree(&histograms[i].data_[0], self->histogram_length_, + alphabet_size, tree, &self->depths_[ix], &self->bits_[ix], + storage_ix, storage); + } + } +} + +#undef HistogramType diff --git a/packages/runtime/vendor/brotli/c/enc/block_splitter.c b/packages/runtime/vendor/brotli/c/enc/block_splitter.c new file mode 100644 index 000000000..13e924f09 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/block_splitter.c @@ -0,0 +1,214 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Block split point selection utilities. */ + +#include "block_splitter.h" + +#include "../common/platform.h" +#include "bit_cost.h" +#include "cluster.h" +#include "command.h" +#include "fast_log.h" +#include "histogram.h" +#include "memory.h" +#include "quality.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static const size_t kMaxLiteralHistograms = 100; +static const size_t kMaxCommandHistograms = 50; +static const double kLiteralBlockSwitchCost = 28.1; +static const double kCommandBlockSwitchCost = 13.5; +static const double kDistanceBlockSwitchCost = 14.6; +static const size_t kLiteralStrideLength = 70; +static const size_t kCommandStrideLength = 40; +static const size_t kDistanceStrideLength = 40; +static const size_t kSymbolsPerLiteralHistogram = 544; +static const size_t kSymbolsPerCommandHistogram = 530; +static const size_t kSymbolsPerDistanceHistogram = 544; +static const size_t kMinLengthForBlockSplitting = 128; +static const size_t kIterMulForRefining = 2; +static const size_t kMinItersForRefining = 100; + +static size_t CountLiterals(const Command* cmds, const size_t num_commands) { + /* Count how many we have. */ + size_t total_length = 0; + size_t i; + for (i = 0; i < num_commands; ++i) { + total_length += cmds[i].insert_len_; + } + return total_length; +} + +static void CopyLiteralsToByteArray(const Command* cmds, + const size_t num_commands, + const uint8_t* data, + const size_t offset, + const size_t mask, + uint8_t* literals) { + size_t pos = 0; + size_t from_pos = offset & mask; + size_t i; + for (i = 0; i < num_commands; ++i) { + size_t insert_len = cmds[i].insert_len_; + if (from_pos + insert_len > mask) { + size_t head_size = mask + 1 - from_pos; + memcpy(literals + pos, data + from_pos, head_size); + from_pos = 0; + pos += head_size; + insert_len -= head_size; + } + if (insert_len > 0) { + memcpy(literals + pos, data + from_pos, insert_len); + pos += insert_len; + } + from_pos = (from_pos + insert_len + CommandCopyLen(&cmds[i])) & mask; + } +} + +static BROTLI_INLINE uint32_t MyRand(uint32_t* seed) { + /* Initial seed should be 7. In this case, loop length is (1 << 29). */ + *seed *= 16807U; + return *seed; +} + +static BROTLI_INLINE double BitCost(size_t count) { + return count == 0 ? -2.0 : FastLog2(count); +} + +#define HISTOGRAMS_PER_BATCH 64 +#define CLUSTERS_PER_BATCH 16 + +#define FN(X) X ## Literal +#define DataType uint8_t +/* NOLINTNEXTLINE(build/include) */ +#include "block_splitter_inc.h" +#undef DataType +#undef FN + +#define FN(X) X ## Command +#define DataType uint16_t +/* NOLINTNEXTLINE(build/include) */ +#include "block_splitter_inc.h" +#undef FN + +#define FN(X) X ## Distance +/* NOLINTNEXTLINE(build/include) */ +#include "block_splitter_inc.h" +#undef DataType +#undef FN + +void BrotliInitBlockSplit(BlockSplit* self) { + self->num_types = 0; + self->num_blocks = 0; + self->types = 0; + self->lengths = 0; + self->types_alloc_size = 0; + self->lengths_alloc_size = 0; +} + +void BrotliDestroyBlockSplit(MemoryManager* m, BlockSplit* self) { + BROTLI_FREE(m, self->types); + BROTLI_FREE(m, self->lengths); +} + +/* Extracts literals, command distance and prefix codes, then applies + * SplitByteVector to create partitioning. */ +void BrotliSplitBlock(MemoryManager* m, + const Command* cmds, + const size_t num_commands, + const uint8_t* data, + const size_t pos, + const size_t mask, + const BrotliEncoderParams* params, + BlockSplit* literal_split, + BlockSplit* insert_and_copy_split, + BlockSplit* dist_split) { + { + size_t literals_count = CountLiterals(cmds, num_commands); + uint8_t* literals = BROTLI_ALLOC(m, uint8_t, literals_count); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(literals)) return; + /* Create a continuous array of literals. */ + CopyLiteralsToByteArray(cmds, num_commands, data, pos, mask, literals); + /* Create the block split on the array of literals. + * Literal histograms can have alphabet size up to 256. + * Though, to accommodate context modeling, less than half of maximum size + * is allowed. */ + SplitByteVectorLiteral( + m, literals, literals_count, + kSymbolsPerLiteralHistogram, kMaxLiteralHistograms, + kLiteralStrideLength, kLiteralBlockSwitchCost, params, + literal_split); + if (BROTLI_IS_OOM(m)) return; + BROTLI_FREE(m, literals); + /* NB: this might be a good place for injecting extra splitting without + * increasing encoder complexity; however, output partition would be less + * optimal than one produced with forced splitting inside + * SplitByteVector (FindBlocks / ClusterBlocks). */ + } + + { + /* Compute prefix codes for commands. */ + uint16_t* insert_and_copy_codes = BROTLI_ALLOC(m, uint16_t, num_commands); + size_t i; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(insert_and_copy_codes)) return; + for (i = 0; i < num_commands; ++i) { + insert_and_copy_codes[i] = cmds[i].cmd_prefix_; + } + /* Create the block split on the array of command prefixes. */ + SplitByteVectorCommand( + m, insert_and_copy_codes, num_commands, + kSymbolsPerCommandHistogram, kMaxCommandHistograms, + kCommandStrideLength, kCommandBlockSwitchCost, params, + insert_and_copy_split); + if (BROTLI_IS_OOM(m)) return; + /* TODO(eustas): reuse for distances? */ + BROTLI_FREE(m, insert_and_copy_codes); + } + + { + /* Create a continuous array of distance prefixes. */ + uint16_t* distance_prefixes = BROTLI_ALLOC(m, uint16_t, num_commands); + size_t j = 0; + size_t i; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(distance_prefixes)) return; + for (i = 0; i < num_commands; ++i) { + const Command* cmd = &cmds[i]; + if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) { + distance_prefixes[j++] = cmd->dist_prefix_ & 0x3FF; + } + } + /* Create the block split on the array of distance prefixes. */ + SplitByteVectorDistance( + m, distance_prefixes, j, + kSymbolsPerDistanceHistogram, kMaxCommandHistograms, + kDistanceStrideLength, kDistanceBlockSwitchCost, params, + dist_split); + if (BROTLI_IS_OOM(m)) return; + BROTLI_FREE(m, distance_prefixes); + } +} + +#if defined(BROTLI_TEST) +size_t BrotliCountLiteralsForTest(const Command*, size_t); +size_t BrotliCountLiteralsForTest(const Command* cmds, size_t num_commands) { + return CountLiterals(cmds, num_commands); +} +void BrotliCopyLiteralsToByteArrayForTest( + const Command*, size_t, const uint8_t*, size_t, size_t, uint8_t*); +void BrotliCopyLiteralsToByteArrayForTest(const Command* cmds, + size_t num_commands, const uint8_t* data, size_t offset, size_t mask, + uint8_t* literals) { + CopyLiteralsToByteArray(cmds, num_commands, data, offset, mask, literals); +} +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/block_splitter.h b/packages/runtime/vendor/brotli/c/enc/block_splitter.h new file mode 100644 index 000000000..c927794eb --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/block_splitter.h @@ -0,0 +1,49 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Block split point selection utilities. */ + +#ifndef BROTLI_ENC_BLOCK_SPLITTER_H_ +#define BROTLI_ENC_BLOCK_SPLITTER_H_ + +#include "../common/platform.h" +#include "command.h" +#include "memory.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct BlockSplit { + size_t num_types; /* Amount of distinct types */ + size_t num_blocks; /* Amount of values in types and length */ + uint8_t* types; + uint32_t* lengths; + + size_t types_alloc_size; + size_t lengths_alloc_size; +} BlockSplit; + +BROTLI_INTERNAL void BrotliInitBlockSplit(BlockSplit* self); +BROTLI_INTERNAL void BrotliDestroyBlockSplit(MemoryManager* m, + BlockSplit* self); + +BROTLI_INTERNAL void BrotliSplitBlock(MemoryManager* m, + const Command* cmds, + const size_t num_commands, + const uint8_t* data, + const size_t offset, + const size_t mask, + const BrotliEncoderParams* params, + BlockSplit* literal_split, + BlockSplit* insert_and_copy_split, + BlockSplit* dist_split); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_BLOCK_SPLITTER_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/block_splitter_inc.h b/packages/runtime/vendor/brotli/c/enc/block_splitter_inc.h new file mode 100644 index 000000000..5e34cb9c9 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/block_splitter_inc.h @@ -0,0 +1,487 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN, DataType */ + +#define HistogramType FN(Histogram) + +static void FN(InitialEntropyCodes)(const DataType* data, size_t length, + size_t stride, + size_t num_histograms, + HistogramType* histograms) { + uint32_t seed = 7; + size_t block_length = length / num_histograms; + size_t i; + FN(ClearHistograms)(histograms, num_histograms); + for (i = 0; i < num_histograms; ++i) { + size_t pos = length * i / num_histograms; + if (i != 0) { + pos += MyRand(&seed) % block_length; + } + if (pos + stride >= length) { + pos = length - stride - 1; + } + FN(HistogramAddVector)(&histograms[i], data + pos, stride); + } +} + +static void FN(RandomSample)(uint32_t* seed, + const DataType* data, + size_t length, + size_t stride, + HistogramType* sample) { + size_t pos = 0; + if (stride >= length) { + stride = length; + } else { + pos = MyRand(seed) % (length - stride + 1); + } + FN(HistogramAddVector)(sample, data + pos, stride); +} + +static void FN(RefineEntropyCodes)(const DataType* data, size_t length, + size_t stride, + size_t num_histograms, + HistogramType* histograms, + HistogramType* tmp) { + size_t iters = + kIterMulForRefining * length / stride + kMinItersForRefining; + uint32_t seed = 7; + size_t iter; + iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms; + for (iter = 0; iter < iters; ++iter) { + FN(HistogramClear)(tmp); + FN(RandomSample)(&seed, data, length, stride, tmp); + FN(HistogramAddHistogram)(&histograms[iter % num_histograms], tmp); + } +} + +/* Assigns a block id from the range [0, num_histograms) to each data element + in data[0..length) and fills in block_id[0..length) with the assigned values. + Returns the number of blocks, i.e. one plus the number of block switches. */ +static size_t FN(FindBlocks)(const DataType* data, const size_t length, + const double block_switch_bitcost, + const size_t num_histograms, + const HistogramType* histograms, + double* insert_cost, + double* cost, + uint8_t* switch_signal, + uint8_t* block_id) { + const size_t alphabet_size = FN(HistogramDataSize)(); + const size_t bitmap_len = (num_histograms + 7) >> 3; + size_t num_blocks = 1; + size_t byte_ix; + size_t i; + size_t j; + BROTLI_DCHECK(num_histograms <= 256); + + /* Trivial case: single histogram -> single block type. */ + if (num_histograms <= 1) { + for (i = 0; i < length; ++i) { + block_id[i] = 0; + } + return 1; + } + + /* Fill bitcost for each symbol of all histograms. + * Non-existing symbol cost: 2 + log2(total_count). + * Regular symbol cost: -log2(symbol_count / total_count). */ + memset(insert_cost, 0, + sizeof(insert_cost[0]) * alphabet_size * num_histograms); + for (i = 0; i < num_histograms; ++i) { + insert_cost[i] = FastLog2((uint32_t)histograms[i].total_count_); + } + for (i = alphabet_size; i != 0;) { + /* Reverse order to use the 0-th row as a temporary storage. */ + --i; + for (j = 0; j < num_histograms; ++j) { + insert_cost[i * num_histograms + j] = + insert_cost[j] - BitCost(histograms[j].data_[i]); + } + } + + /* After each iteration of this loop, cost[k] will contain the difference + between the minimum cost of arriving at the current byte position using + entropy code k, and the minimum cost of arriving at the current byte + position. This difference is capped at the block switch cost, and if it + reaches block switch cost, it means that when we trace back from the last + position, we need to switch here. */ + memset(cost, 0, sizeof(cost[0]) * num_histograms); + memset(switch_signal, 0, sizeof(switch_signal[0]) * length * bitmap_len); + for (byte_ix = 0; byte_ix < length; ++byte_ix) { + size_t ix = byte_ix * bitmap_len; + size_t symbol = data[byte_ix]; + size_t insert_cost_ix = symbol * num_histograms; + double min_cost = 1e99; + double block_switch_cost = block_switch_bitcost; + static const size_t prologue_length = 2000; + static const double multiplier = 0.07 / 2000; + size_t k; + for (k = 0; k < num_histograms; ++k) { + /* We are coding the symbol with entropy code k. */ + cost[k] += insert_cost[insert_cost_ix + k]; + if (cost[k] < min_cost) { + min_cost = cost[k]; + block_id[byte_ix] = (uint8_t)k; + } + } + /* More blocks for the beginning. */ + if (byte_ix < prologue_length) { + block_switch_cost *= 0.77 + multiplier * (double)byte_ix; + } + for (k = 0; k < num_histograms; ++k) { + cost[k] -= min_cost; + if (cost[k] >= block_switch_cost) { + const uint8_t mask = (uint8_t)(1u << (k & 7)); + cost[k] = block_switch_cost; + BROTLI_DCHECK((k >> 3) < bitmap_len); + switch_signal[ix + (k >> 3)] |= mask; + } + } + } + + byte_ix = length - 1; + { /* Trace back from the last position and switch at the marked places. */ + size_t ix = byte_ix * bitmap_len; + uint8_t cur_id = block_id[byte_ix]; + while (byte_ix > 0) { + const uint8_t mask = (uint8_t)(1u << (cur_id & 7)); + BROTLI_DCHECK(((size_t)cur_id >> 3) < bitmap_len); + --byte_ix; + ix -= bitmap_len; + if (switch_signal[ix + (cur_id >> 3)] & mask) { + if (cur_id != block_id[byte_ix]) { + cur_id = block_id[byte_ix]; + ++num_blocks; + } + } + block_id[byte_ix] = cur_id; + } + } + return num_blocks; +} + +static size_t FN(RemapBlockIds)(uint8_t* block_ids, const size_t length, + uint16_t* new_id, const size_t num_histograms) { + static const uint16_t kInvalidId = 256; + uint16_t next_id = 0; + size_t i; + for (i = 0; i < num_histograms; ++i) { + new_id[i] = kInvalidId; + } + for (i = 0; i < length; ++i) { + BROTLI_DCHECK(block_ids[i] < num_histograms); + if (new_id[block_ids[i]] == kInvalidId) { + new_id[block_ids[i]] = next_id++; + } + } + for (i = 0; i < length; ++i) { + block_ids[i] = (uint8_t)new_id[block_ids[i]]; + BROTLI_DCHECK(block_ids[i] < num_histograms); + } + BROTLI_DCHECK(next_id <= num_histograms); + return next_id; +} + +static void FN(BuildBlockHistograms)(const DataType* data, const size_t length, + const uint8_t* block_ids, + const size_t num_histograms, + HistogramType* histograms) { + size_t i; + FN(ClearHistograms)(histograms, num_histograms); + for (i = 0; i < length; ++i) { + FN(HistogramAdd)(&histograms[block_ids[i]], data[i]); + } +} + +/* Given the initial partitioning build partitioning with limited number + * of histograms (and block types). */ +static void FN(ClusterBlocks)(MemoryManager* m, + const DataType* data, const size_t length, + const size_t num_blocks, + uint8_t* block_ids, + BlockSplit* split) { + uint32_t* histogram_symbols = BROTLI_ALLOC(m, uint32_t, num_blocks); + uint32_t* u32 = + BROTLI_ALLOC(m, uint32_t, num_blocks + 4 * HISTOGRAMS_PER_BATCH); + const size_t expected_num_clusters = CLUSTERS_PER_BATCH * + (num_blocks + HISTOGRAMS_PER_BATCH - 1) / HISTOGRAMS_PER_BATCH; + size_t all_histograms_size = 0; + size_t all_histograms_capacity = expected_num_clusters; + HistogramType* all_histograms = + BROTLI_ALLOC(m, HistogramType, all_histograms_capacity); + size_t cluster_size_size = 0; + size_t cluster_size_capacity = expected_num_clusters; + uint32_t* cluster_size = BROTLI_ALLOC(m, uint32_t, cluster_size_capacity); + size_t num_clusters = 0; + HistogramType* histograms = BROTLI_ALLOC(m, HistogramType, + BROTLI_MIN(size_t, num_blocks, HISTOGRAMS_PER_BATCH)); + size_t max_num_pairs = + HISTOGRAMS_PER_BATCH * HISTOGRAMS_PER_BATCH / 2; + size_t pairs_capacity = max_num_pairs + 1; + HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity); + size_t pos = 0; + uint32_t* clusters; + size_t num_final_clusters; + static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX; + uint32_t* new_index; + size_t i; + uint32_t* BROTLI_RESTRICT const sizes = + u32 ? (u32 + 0 * HISTOGRAMS_PER_BATCH) : NULL; + uint32_t* BROTLI_RESTRICT const new_clusters = + u32 ? (u32 + 1 * HISTOGRAMS_PER_BATCH) : NULL; + uint32_t* BROTLI_RESTRICT const symbols = + u32 ? (u32 + 2 * HISTOGRAMS_PER_BATCH) : NULL; + uint32_t* BROTLI_RESTRICT const remap = + u32 ? (u32 + 3 * HISTOGRAMS_PER_BATCH) : NULL; + uint32_t* BROTLI_RESTRICT const block_lengths = + u32 ? (u32 + 4 * HISTOGRAMS_PER_BATCH) : NULL; + /* TODO(eustas): move to arena? */ + HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 2); + + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histogram_symbols) || + BROTLI_IS_NULL(u32) || BROTLI_IS_NULL(all_histograms) || + BROTLI_IS_NULL(cluster_size) || BROTLI_IS_NULL(histograms) || + BROTLI_IS_NULL(pairs) || BROTLI_IS_NULL(tmp)) { + return; + } + + memset(u32, 0, (num_blocks + 4 * HISTOGRAMS_PER_BATCH) * sizeof(uint32_t)); + + /* Calculate block lengths (convert repeating values -> series length). */ + { + size_t block_idx = 0; + for (i = 0; i < length; ++i) { + BROTLI_DCHECK(block_idx < num_blocks); + ++block_lengths[block_idx]; + if (i + 1 == length || block_ids[i] != block_ids[i + 1]) { + ++block_idx; + } + } + BROTLI_DCHECK(block_idx == num_blocks); + } + + /* Pre-cluster blocks (cluster batches). */ + for (i = 0; i < num_blocks; i += HISTOGRAMS_PER_BATCH) { + const size_t num_to_combine = + BROTLI_MIN(size_t, num_blocks - i, HISTOGRAMS_PER_BATCH); + size_t num_new_clusters; + size_t j; + for (j = 0; j < num_to_combine; ++j) { + size_t k; + size_t block_length = block_lengths[i + j]; + FN(HistogramClear)(&histograms[j]); + for (k = 0; k < block_length; ++k) { + FN(HistogramAdd)(&histograms[j], data[pos++]); + } + histograms[j].bit_cost_ = FN(BrotliPopulationCost)(&histograms[j]); + new_clusters[j] = (uint32_t)j; + symbols[j] = (uint32_t)j; + sizes[j] = 1; + } + num_new_clusters = FN(BrotliHistogramCombine)( + histograms, tmp, sizes, symbols, new_clusters, pairs, num_to_combine, + num_to_combine, HISTOGRAMS_PER_BATCH, max_num_pairs); + BROTLI_ENSURE_CAPACITY(m, HistogramType, all_histograms, + all_histograms_capacity, all_histograms_size + num_new_clusters); + BROTLI_ENSURE_CAPACITY(m, uint32_t, cluster_size, + cluster_size_capacity, cluster_size_size + num_new_clusters); + if (BROTLI_IS_OOM(m)) return; + for (j = 0; j < num_new_clusters; ++j) { + all_histograms[all_histograms_size++] = histograms[new_clusters[j]]; + cluster_size[cluster_size_size++] = sizes[new_clusters[j]]; + remap[new_clusters[j]] = (uint32_t)j; + } + for (j = 0; j < num_to_combine; ++j) { + histogram_symbols[i + j] = (uint32_t)num_clusters + remap[symbols[j]]; + } + num_clusters += num_new_clusters; + BROTLI_DCHECK(num_clusters == cluster_size_size); + BROTLI_DCHECK(num_clusters == all_histograms_size); + } + BROTLI_FREE(m, histograms); + + /* Final clustering. */ + max_num_pairs = + BROTLI_MIN(size_t, 64 * num_clusters, (num_clusters / 2) * num_clusters); + if (pairs_capacity < max_num_pairs + 1) { + BROTLI_FREE(m, pairs); + pairs = BROTLI_ALLOC(m, HistogramPair, max_num_pairs + 1); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(pairs)) return; + } + clusters = BROTLI_ALLOC(m, uint32_t, num_clusters); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(clusters)) return; + for (i = 0; i < num_clusters; ++i) { + clusters[i] = (uint32_t)i; + } + num_final_clusters = FN(BrotliHistogramCombine)( + all_histograms, tmp, cluster_size, histogram_symbols, clusters, pairs, + num_clusters, num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES, + max_num_pairs); + BROTLI_FREE(m, pairs); + BROTLI_FREE(m, cluster_size); + + /* Assign blocks to final histograms. */ + new_index = BROTLI_ALLOC(m, uint32_t, num_clusters); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_index)) return; + for (i = 0; i < num_clusters; ++i) new_index[i] = kInvalidIndex; + pos = 0; + { + uint32_t next_index = 0; + for (i = 0; i < num_blocks; ++i) { + size_t j; + uint32_t best_out; + double best_bits; + FN(HistogramClear)(tmp); + for (j = 0; j < block_lengths[i]; ++j) { + FN(HistogramAdd)(tmp, data[pos++]); + } + /* Among equally good histograms prefer last used. */ + /* TODO(eustas): should we give a block-switch discount here? */ + best_out = (i == 0) ? histogram_symbols[0] : histogram_symbols[i - 1]; + best_bits = FN(BrotliHistogramBitCostDistance)( + tmp, &all_histograms[best_out], tmp + 1); + for (j = 0; j < num_final_clusters; ++j) { + const double cur_bits = FN(BrotliHistogramBitCostDistance)( + tmp, &all_histograms[clusters[j]], tmp + 1); + if (cur_bits < best_bits) { + best_bits = cur_bits; + best_out = clusters[j]; + } + } + histogram_symbols[i] = best_out; + if (new_index[best_out] == kInvalidIndex) { + new_index[best_out] = next_index++; + } + } + } + BROTLI_FREE(m, tmp); + BROTLI_FREE(m, clusters); + BROTLI_FREE(m, all_histograms); + BROTLI_ENSURE_CAPACITY( + m, uint8_t, split->types, split->types_alloc_size, num_blocks); + BROTLI_ENSURE_CAPACITY( + m, uint32_t, split->lengths, split->lengths_alloc_size, num_blocks); + if (BROTLI_IS_OOM(m)) return; + + /* Rewrite final assignment to block-split. There might be less blocks + * than |num_blocks| due to clustering. */ + { + uint32_t cur_length = 0; + size_t block_idx = 0; + uint8_t max_type = 0; + for (i = 0; i < num_blocks; ++i) { + cur_length += block_lengths[i]; + if (i + 1 == num_blocks || + histogram_symbols[i] != histogram_symbols[i + 1]) { + const uint8_t id = (uint8_t)new_index[histogram_symbols[i]]; + split->types[block_idx] = id; + split->lengths[block_idx] = cur_length; + max_type = BROTLI_MAX(uint8_t, max_type, id); + cur_length = 0; + ++block_idx; + } + } + split->num_blocks = block_idx; + split->num_types = (size_t)max_type + 1; + } + BROTLI_FREE(m, new_index); + BROTLI_FREE(m, u32); + BROTLI_FREE(m, histogram_symbols); +} + +/* Create BlockSplit (partitioning) given the limits, estimates and "effort" + * parameters. + * + * NB: max_histograms is often less than number of histograms allowed by format; + * this is done intentionally, to save some "space" for context-aware + * clustering (here entropy is estimated for context-free symbols). */ +static void FN(SplitByteVector)(MemoryManager* m, + const DataType* data, const size_t length, + const size_t symbols_per_histogram, + const size_t max_histograms, + const size_t sampling_stride_length, + const double block_switch_cost, + const BrotliEncoderParams* params, + BlockSplit* split) { + const size_t data_size = FN(HistogramDataSize)(); + HistogramType* histograms; + HistogramType* tmp; + /* Calculate number of histograms; initial estimate is one histogram per + * specified amount of symbols; however, this value is capped. */ + size_t num_histograms = length / symbols_per_histogram + 1; + if (num_histograms > max_histograms) { + num_histograms = max_histograms; + } + + /* Corner case: no input. */ + if (length == 0) { + split->num_types = 1; + return; + } + + if (length < kMinLengthForBlockSplitting) { + BROTLI_ENSURE_CAPACITY(m, uint8_t, + split->types, split->types_alloc_size, split->num_blocks + 1); + BROTLI_ENSURE_CAPACITY(m, uint32_t, + split->lengths, split->lengths_alloc_size, split->num_blocks + 1); + if (BROTLI_IS_OOM(m)) return; + split->num_types = 1; + split->types[split->num_blocks] = 0; + split->lengths[split->num_blocks] = (uint32_t)length; + split->num_blocks++; + return; + } + histograms = BROTLI_ALLOC(m, HistogramType, num_histograms + 1); + tmp = histograms + num_histograms; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histograms)) return; + /* Find good entropy codes. */ + FN(InitialEntropyCodes)(data, length, + sampling_stride_length, + num_histograms, histograms); + FN(RefineEntropyCodes)(data, length, + sampling_stride_length, + num_histograms, histograms, tmp); + { + /* Find a good path through literals with the good entropy codes. */ + uint8_t* block_ids = BROTLI_ALLOC(m, uint8_t, length); + size_t num_blocks = 0; + const size_t bitmaplen = (num_histograms + 7) >> 3; + double* insert_cost = BROTLI_ALLOC(m, double, data_size * num_histograms); + double* cost = BROTLI_ALLOC(m, double, num_histograms); + uint8_t* switch_signal = BROTLI_ALLOC(m, uint8_t, length * bitmaplen); + uint16_t* new_id = BROTLI_ALLOC(m, uint16_t, num_histograms); + const size_t iters = params->quality < HQ_ZOPFLIFICATION_QUALITY ? 3 : 10; + size_t i; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(block_ids) || + BROTLI_IS_NULL(insert_cost) || BROTLI_IS_NULL(cost) || + BROTLI_IS_NULL(switch_signal) || BROTLI_IS_NULL(new_id)) { + return; + } + for (i = 0; i < iters; ++i) { + num_blocks = FN(FindBlocks)(data, length, + block_switch_cost, + num_histograms, histograms, + insert_cost, cost, switch_signal, + block_ids); + num_histograms = FN(RemapBlockIds)(block_ids, length, + new_id, num_histograms); + FN(BuildBlockHistograms)(data, length, block_ids, + num_histograms, histograms); + } + BROTLI_FREE(m, insert_cost); + BROTLI_FREE(m, cost); + BROTLI_FREE(m, switch_signal); + BROTLI_FREE(m, new_id); + BROTLI_FREE(m, histograms); + FN(ClusterBlocks)(m, data, length, num_blocks, block_ids, split); + if (BROTLI_IS_OOM(m)) return; + BROTLI_FREE(m, block_ids); + } +} + +#undef HistogramType diff --git a/packages/runtime/vendor/brotli/c/enc/brotli_bit_stream.c b/packages/runtime/vendor/brotli/c/enc/brotli_bit_stream.c new file mode 100644 index 000000000..1e9e946fe --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/brotli_bit_stream.c @@ -0,0 +1,1337 @@ +/* Copyright 2014 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Brotli bit stream functions to support the low level format. There are no + compression algorithms here, just the right ordering of bits to match the + specs. */ + +#include "brotli_bit_stream.h" + +#include "../common/constants.h" +#include "../common/context.h" +#include "../common/platform.h" +#include "entropy_encode.h" +#include "entropy_encode_static.h" +#include "fast_log.h" +#include "histogram.h" +#include "memory.h" +#include "write_bits.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define MAX_HUFFMAN_TREE_SIZE (2 * BROTLI_NUM_COMMAND_SYMBOLS + 1) +/* The maximum size of Huffman dictionary for distances assuming that + NPOSTFIX = 0 and NDIRECT = 0. */ +#define MAX_SIMPLE_DISTANCE_ALPHABET_SIZE \ + BROTLI_DISTANCE_ALPHABET_SIZE(0, 0, BROTLI_LARGE_MAX_DISTANCE_BITS) +/* MAX_SIMPLE_DISTANCE_ALPHABET_SIZE == 140 */ + +static BROTLI_INLINE uint32_t BlockLengthPrefixCode(uint32_t len) { + uint32_t code = (len >= 177) ? (len >= 753 ? 20 : 14) : (len >= 41 ? 7 : 0); + while (code < (BROTLI_NUM_BLOCK_LEN_SYMBOLS - 1) && + len >= _kBrotliPrefixCodeRanges[code + 1].offset) ++code; + return code; +} + +static BROTLI_INLINE void GetBlockLengthPrefixCode(uint32_t len, size_t* code, + uint32_t* n_extra, uint32_t* extra) { + *code = BlockLengthPrefixCode(len); + *n_extra = _kBrotliPrefixCodeRanges[*code].nbits; + *extra = len - _kBrotliPrefixCodeRanges[*code].offset; +} + +typedef struct BlockTypeCodeCalculator { + size_t last_type; + size_t second_last_type; +} BlockTypeCodeCalculator; + +static void InitBlockTypeCodeCalculator(BlockTypeCodeCalculator* self) { + self->last_type = 1; + self->second_last_type = 0; +} + +static BROTLI_INLINE size_t NextBlockTypeCode( + BlockTypeCodeCalculator* calculator, uint8_t type) { + size_t type_code = (type == calculator->last_type + 1) ? 1u : + (type == calculator->second_last_type) ? 0u : type + 2u; + calculator->second_last_type = calculator->last_type; + calculator->last_type = type; + return type_code; +} + +/* |nibblesbits| represents the 2 bits to encode MNIBBLES (0-3) + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) */ +static void BrotliEncodeMlen(size_t length, uint64_t* bits, + size_t* numbits, uint64_t* nibblesbits) { + size_t lg = (length == 1) ? 1 : Log2FloorNonZero((uint32_t)(length - 1)) + 1; + size_t mnibbles = (lg < 16 ? 16 : (lg + 3)) / 4; + BROTLI_DCHECK(length > 0); + BROTLI_DCHECK(length <= (1 << 24)); + BROTLI_DCHECK(lg <= 24); + *nibblesbits = mnibbles - 4; + *numbits = mnibbles * 4; + *bits = length - 1; +} + +static BROTLI_INLINE void StoreCommandExtra( + const Command* cmd, size_t* storage_ix, uint8_t* storage) { + uint32_t copylen_code = CommandCopyLenCode(cmd); + uint16_t inscode = GetInsertLengthCode(cmd->insert_len_); + uint16_t copycode = GetCopyLengthCode(copylen_code); + uint32_t insnumextra = GetInsertExtra(inscode); + uint64_t insextraval = cmd->insert_len_ - GetInsertBase(inscode); + uint64_t copyextraval = copylen_code - GetCopyBase(copycode); + uint64_t bits = (copyextraval << insnumextra) | insextraval; + BrotliWriteBits( + insnumextra + GetCopyExtra(copycode), bits, storage_ix, storage); +} + +/* Data structure that stores almost everything that is needed to encode each + block switch command. */ +typedef struct BlockSplitCode { + BlockTypeCodeCalculator type_code_calculator; + uint8_t type_depths[BROTLI_MAX_BLOCK_TYPE_SYMBOLS]; + uint16_t type_bits[BROTLI_MAX_BLOCK_TYPE_SYMBOLS]; + uint8_t length_depths[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; + uint16_t length_bits[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; +} BlockSplitCode; + +/* Stores a number between 0 and 255. */ +static void StoreVarLenUint8(size_t n, size_t* storage_ix, uint8_t* storage) { + if (n == 0) { + BrotliWriteBits(1, 0, storage_ix, storage); + } else { + size_t nbits = Log2FloorNonZero(n); + BrotliWriteBits(1, 1, storage_ix, storage); + BrotliWriteBits(3, nbits, storage_ix, storage); + BrotliWriteBits(nbits, n - ((size_t)1 << nbits), storage_ix, storage); + } +} + +/* Stores the compressed meta-block header. + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) */ +static void StoreCompressedMetaBlockHeader(BROTLI_BOOL is_final_block, + size_t length, + size_t* storage_ix, + uint8_t* storage) { + uint64_t lenbits; + size_t nlenbits; + uint64_t nibblesbits; + + /* Write ISLAST bit. */ + BrotliWriteBits(1, (uint64_t)is_final_block, storage_ix, storage); + /* Write ISEMPTY bit. */ + if (is_final_block) { + BrotliWriteBits(1, 0, storage_ix, storage); + } + + BrotliEncodeMlen(length, &lenbits, &nlenbits, &nibblesbits); + BrotliWriteBits(2, nibblesbits, storage_ix, storage); + BrotliWriteBits(nlenbits, lenbits, storage_ix, storage); + + if (!is_final_block) { + /* Write ISUNCOMPRESSED bit. */ + BrotliWriteBits(1, 0, storage_ix, storage); + } +} + +/* Stores the uncompressed meta-block header. + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) */ +static void BrotliStoreUncompressedMetaBlockHeader(size_t length, + size_t* storage_ix, + uint8_t* storage) { + uint64_t lenbits; + size_t nlenbits; + uint64_t nibblesbits; + + /* Write ISLAST bit. + Uncompressed block cannot be the last one, so set to 0. */ + BrotliWriteBits(1, 0, storage_ix, storage); + BrotliEncodeMlen(length, &lenbits, &nlenbits, &nibblesbits); + BrotliWriteBits(2, nibblesbits, storage_ix, storage); + BrotliWriteBits(nlenbits, lenbits, storage_ix, storage); + /* Write ISUNCOMPRESSED bit. */ + BrotliWriteBits(1, 1, storage_ix, storage); +} + +static void BrotliStoreHuffmanTreeOfHuffmanTreeToBitMask( + const int num_codes, const uint8_t* code_length_bitdepth, + size_t* storage_ix, uint8_t* storage) { + static const BROTLI_MODEL("small") + uint8_t kStorageOrder[BROTLI_CODE_LENGTH_CODES] = { + 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15 + }; + /* The bit lengths of the Huffman code over the code length alphabet + are compressed with the following static Huffman code: + Symbol Code + ------ ---- + 0 00 + 1 1110 + 2 110 + 3 01 + 4 10 + 5 1111 */ + static const BROTLI_MODEL("small") + uint8_t kHuffmanBitLengthHuffmanCodeSymbols[6] = { + 0, 7, 3, 2, 1, 15 + }; + static const BROTLI_MODEL("small") + uint8_t kHuffmanBitLengthHuffmanCodeBitLengths[6] = { + 2, 4, 3, 2, 2, 4 + }; + + size_t skip_some = 0; /* skips none. */ + + /* Throw away trailing zeros: */ + size_t codes_to_store = BROTLI_CODE_LENGTH_CODES; + if (num_codes > 1) { + for (; codes_to_store > 0; --codes_to_store) { + if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) { + break; + } + } + } + if (code_length_bitdepth[kStorageOrder[0]] == 0 && + code_length_bitdepth[kStorageOrder[1]] == 0) { + skip_some = 2; /* skips two. */ + if (code_length_bitdepth[kStorageOrder[2]] == 0) { + skip_some = 3; /* skips three. */ + } + } + BrotliWriteBits(2, skip_some, storage_ix, storage); + { + size_t i; + for (i = skip_some; i < codes_to_store; ++i) { + size_t l = code_length_bitdepth[kStorageOrder[i]]; + BrotliWriteBits(kHuffmanBitLengthHuffmanCodeBitLengths[l], + kHuffmanBitLengthHuffmanCodeSymbols[l], storage_ix, storage); + } + } +} + +static void BrotliStoreHuffmanTreeToBitMask( + const size_t huffman_tree_size, const uint8_t* huffman_tree, + const uint8_t* huffman_tree_extra_bits, const uint8_t* code_length_bitdepth, + const uint16_t* code_length_bitdepth_symbols, + size_t* BROTLI_RESTRICT storage_ix, uint8_t* BROTLI_RESTRICT storage) { + size_t i; + for (i = 0; i < huffman_tree_size; ++i) { + size_t ix = huffman_tree[i]; + BrotliWriteBits(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix], + storage_ix, storage); + /* Extra bits */ + switch (ix) { + case BROTLI_REPEAT_PREVIOUS_CODE_LENGTH: + BrotliWriteBits(2, huffman_tree_extra_bits[i], storage_ix, storage); + break; + case BROTLI_REPEAT_ZERO_CODE_LENGTH: + BrotliWriteBits(3, huffman_tree_extra_bits[i], storage_ix, storage); + break; + } + } +} + +static void StoreSimpleHuffmanTree(const uint8_t* depths, + size_t symbols[4], + size_t num_symbols, + size_t max_bits, + size_t* storage_ix, uint8_t* storage) { + /* value of 1 indicates a simple Huffman code */ + BrotliWriteBits(2, 1, storage_ix, storage); + BrotliWriteBits(2, num_symbols - 1, storage_ix, storage); /* NSYM - 1 */ + + { + /* Sort */ + size_t i; + for (i = 0; i < num_symbols; i++) { + size_t j; + for (j = i + 1; j < num_symbols; j++) { + if (depths[symbols[j]] < depths[symbols[i]]) { + BROTLI_SWAP(size_t, symbols, j, i); + } + } + } + } + + if (num_symbols == 2) { + BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); + } else if (num_symbols == 3) { + BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); + } else { + BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[3], storage_ix, storage); + /* tree-select */ + BrotliWriteBits(1, depths[symbols[0]] == 1 ? 1 : 0, storage_ix, storage); + } +} + +/* num = alphabet size + depths = symbol depths */ +void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num, + HuffmanTree* tree, + size_t* storage_ix, uint8_t* storage) { + /* Write the Huffman tree into the brotli-representation. + The command alphabet is the largest, so this allocation will fit all + alphabets. */ + /* TODO(eustas): fix me */ + uint8_t huffman_tree[BROTLI_NUM_COMMAND_SYMBOLS]; + uint8_t huffman_tree_extra_bits[BROTLI_NUM_COMMAND_SYMBOLS]; + size_t huffman_tree_size = 0; + uint8_t code_length_bitdepth[BROTLI_CODE_LENGTH_CODES] = { 0 }; + uint16_t code_length_bitdepth_symbols[BROTLI_CODE_LENGTH_CODES]; + uint32_t huffman_tree_histogram[BROTLI_CODE_LENGTH_CODES] = { 0 }; + size_t i; + int num_codes = 0; + size_t code = 0; + + BROTLI_DCHECK(num <= BROTLI_NUM_COMMAND_SYMBOLS); + + BrotliWriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree, + huffman_tree_extra_bits); + + /* Calculate the statistics of the Huffman tree in brotli-representation. */ + for (i = 0; i < huffman_tree_size; ++i) { + ++huffman_tree_histogram[huffman_tree[i]]; + } + + for (i = 0; i < BROTLI_CODE_LENGTH_CODES; ++i) { + if (huffman_tree_histogram[i]) { + if (num_codes == 0) { + code = i; + num_codes = 1; + } else if (num_codes == 1) { + num_codes = 2; + break; + } + } + } + + /* Calculate another Huffman tree to use for compressing both the + earlier Huffman tree with. */ + BrotliCreateHuffmanTree(huffman_tree_histogram, BROTLI_CODE_LENGTH_CODES, + 5, tree, code_length_bitdepth); + BrotliConvertBitDepthsToSymbols(code_length_bitdepth, + BROTLI_CODE_LENGTH_CODES, + code_length_bitdepth_symbols); + + /* Now, we have all the data, let's start storing it */ + BrotliStoreHuffmanTreeOfHuffmanTreeToBitMask(num_codes, code_length_bitdepth, + storage_ix, storage); + + if (num_codes == 1) { + code_length_bitdepth[code] = 0; + } + + /* Store the real Huffman tree now. */ + BrotliStoreHuffmanTreeToBitMask(huffman_tree_size, + huffman_tree, + huffman_tree_extra_bits, + code_length_bitdepth, + code_length_bitdepth_symbols, + storage_ix, storage); +} + +/* Builds a Huffman tree from histogram[0:length] into depth[0:length] and + bits[0:length] and stores the encoded tree to the bit stream. */ +static void BuildAndStoreHuffmanTree(const uint32_t* histogram, + const size_t histogram_length, + const size_t alphabet_size, + HuffmanTree* tree, + uint8_t* depth, + uint16_t* bits, + size_t* storage_ix, + uint8_t* storage) { + size_t count = 0; + size_t s4[4] = { 0 }; + size_t i; + size_t max_bits = 0; + for (i = 0; i < histogram_length; i++) { + if (histogram[i]) { + if (count < 4) { + s4[count] = i; + } else if (count > 4) { + break; + } + count++; + } + } + + { + size_t max_bits_counter = alphabet_size - 1; + while (max_bits_counter) { + max_bits_counter >>= 1; + ++max_bits; + } + } + + if (count <= 1) { + BrotliWriteBits(4, 1, storage_ix, storage); + BrotliWriteBits(max_bits, s4[0], storage_ix, storage); + depth[s4[0]] = 0; + bits[s4[0]] = 0; + return; + } + + memset(depth, 0, histogram_length * sizeof(depth[0])); + BrotliCreateHuffmanTree(histogram, histogram_length, 15, tree, depth); + BrotliConvertBitDepthsToSymbols(depth, histogram_length, bits); + + if (count <= 4) { + StoreSimpleHuffmanTree(depth, s4, count, max_bits, storage_ix, storage); + } else { + BrotliStoreHuffmanTree(depth, histogram_length, tree, storage_ix, storage); + } +} + +static BROTLI_INLINE BROTLI_BOOL SortHuffmanTree( + const HuffmanTree* v0, const HuffmanTree* v1) { + return TO_BROTLI_BOOL(v0->total_count_ < v1->total_count_); +} + +void BrotliBuildAndStoreHuffmanTreeFast(HuffmanTree* tree, + const uint32_t* histogram, + const size_t histogram_total, + const size_t max_bits, + uint8_t* depth, uint16_t* bits, + size_t* storage_ix, + uint8_t* storage) { + size_t count = 0; + size_t symbols[4] = { 0 }; + size_t length = 0; + size_t total = histogram_total; + while (total != 0) { + if (histogram[length]) { + if (count < 4) { + symbols[count] = length; + } + ++count; + total -= histogram[length]; + } + ++length; + } + + if (count <= 1) { + BrotliWriteBits(4, 1, storage_ix, storage); + BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); + depth[symbols[0]] = 0; + bits[symbols[0]] = 0; + return; + } + + memset(depth, 0, length * sizeof(depth[0])); + { + uint32_t count_limit; + for (count_limit = 1; ; count_limit *= 2) { + HuffmanTree* node = tree; + size_t l; + for (l = length; l != 0;) { + --l; + if (histogram[l]) { + if (BROTLI_PREDICT_TRUE(histogram[l] >= count_limit)) { + InitHuffmanTree(node, histogram[l], -1, (int16_t)l); + } else { + InitHuffmanTree(node, count_limit, -1, (int16_t)l); + } + ++node; + } + } + { + const int n = (int)(node - tree); + HuffmanTree sentinel; + int i = 0; /* Points to the next leaf node. */ + int j = n + 1; /* Points to the next non-leaf node. */ + int k; + + SortHuffmanTreeItems(tree, (size_t)n, SortHuffmanTree); + /* The nodes are: + [0, n): the sorted leaf nodes that we start with. + [n]: we add a sentinel here. + [n + 1, 2n): new parent nodes are added here, starting from + (n+1). These are naturally in ascending order. + [2n]: we add a sentinel at the end as well. + There will be (2n+1) elements at the end. */ + InitHuffmanTree(&sentinel, BROTLI_UINT32_MAX, -1, -1); + *node++ = sentinel; + *node++ = sentinel; + + for (k = n - 1; k > 0; --k) { + int left, right; + if (tree[i].total_count_ <= tree[j].total_count_) { + left = i; + ++i; + } else { + left = j; + ++j; + } + if (tree[i].total_count_ <= tree[j].total_count_) { + right = i; + ++i; + } else { + right = j; + ++j; + } + /* The sentinel node becomes the parent node. */ + node[-1].total_count_ = + tree[left].total_count_ + tree[right].total_count_; + node[-1].index_left_ = (int16_t)left; + node[-1].index_right_or_value_ = (int16_t)right; + /* Add back the last sentinel node. */ + *node++ = sentinel; + } + if (BrotliSetDepth(2 * n - 1, tree, depth, 14)) { + /* We need to pack the Huffman tree in 14 bits. If this was not + successful, add fake entities to the lowest values and retry. */ + break; + } + } + } + } + BrotliConvertBitDepthsToSymbols(depth, length, bits); + if (count <= 4) { + size_t i; + /* value of 1 indicates a simple Huffman code */ + BrotliWriteBits(2, 1, storage_ix, storage); + BrotliWriteBits(2, count - 1, storage_ix, storage); /* NSYM - 1 */ + + /* Sort */ + for (i = 0; i < count; i++) { + size_t j; + for (j = i + 1; j < count; j++) { + if (depth[symbols[j]] < depth[symbols[i]]) { + BROTLI_SWAP(size_t, symbols, j, i); + } + } + } + + if (count == 2) { + BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); + } else if (count == 3) { + BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); + } else { + BrotliWriteBits(max_bits, symbols[0], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[1], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[2], storage_ix, storage); + BrotliWriteBits(max_bits, symbols[3], storage_ix, storage); + /* tree-select */ + BrotliWriteBits(1, depth[symbols[0]] == 1 ? 1 : 0, storage_ix, storage); + } + } else { + uint8_t previous_value = 8; + size_t i; + /* Complex Huffman Tree */ + StoreStaticCodeLengthCode(storage_ix, storage); + + /* Actual RLE coding. */ + for (i = 0; i < length;) { + const uint8_t value = depth[i]; + size_t reps = 1; + size_t k; + for (k = i + 1; k < length && depth[k] == value; ++k) { + ++reps; + } + i += reps; + if (value == 0) { + BrotliWriteBits(kZeroRepsDepth[reps], kZeroRepsBits[reps], + storage_ix, storage); + } else { + if (previous_value != value) { + BrotliWriteBits(kCodeLengthDepth[value], kCodeLengthBits[value], + storage_ix, storage); + --reps; + } + if (reps < 3) { + while (reps != 0) { + reps--; + BrotliWriteBits(kCodeLengthDepth[value], kCodeLengthBits[value], + storage_ix, storage); + } + } else { + reps -= 3; + BrotliWriteBits(kNonZeroRepsDepth[reps], kNonZeroRepsBits[reps], + storage_ix, storage); + } + previous_value = value; + } + } + } +} + +static size_t IndexOf(const uint8_t* v, size_t v_size, uint8_t value) { + size_t i = 0; + for (; i < v_size; ++i) { + if (v[i] == value) return i; + } + return i; +} + +static void MoveToFront(uint8_t* v, size_t index) { + uint8_t value = v[index]; + size_t i; + for (i = index; i != 0; --i) { + v[i] = v[i - 1]; + } + v[0] = value; +} + +static void MoveToFrontTransform(const uint32_t* BROTLI_RESTRICT v_in, + const size_t v_size, + uint32_t* v_out) { + size_t i; + uint8_t mtf[256]; + uint32_t max_value; + if (v_size == 0) { + return; + } + max_value = v_in[0]; + for (i = 1; i < v_size; ++i) { + if (v_in[i] > max_value) max_value = v_in[i]; + } + BROTLI_DCHECK(max_value < 256u); + for (i = 0; i <= max_value; ++i) { + mtf[i] = (uint8_t)i; + } + { + size_t mtf_size = max_value + 1; + for (i = 0; i < v_size; ++i) { + size_t index = IndexOf(mtf, mtf_size, (uint8_t)v_in[i]); + BROTLI_DCHECK(index < mtf_size); + v_out[i] = (uint32_t)index; + MoveToFront(mtf, index); + } + } +} + +/* Finds runs of zeros in v[0..in_size) and replaces them with a prefix code of + the run length plus extra bits (lower 9 bits is the prefix code and the rest + are the extra bits). Non-zero values in v[] are shifted by + *max_length_prefix. Will not create prefix codes bigger than the initial + value of *max_run_length_prefix. The prefix code of run length L is simply + Log2Floor(L) and the number of extra bits is the same as the prefix code. */ +static void RunLengthCodeZeros(const size_t in_size, + uint32_t* BROTLI_RESTRICT v, size_t* BROTLI_RESTRICT out_size, + uint32_t* BROTLI_RESTRICT max_run_length_prefix) { + uint32_t max_reps = 0; + size_t i; + uint32_t max_prefix; + for (i = 0; i < in_size;) { + uint32_t reps = 0; + for (; i < in_size && v[i] != 0; ++i) ; + for (; i < in_size && v[i] == 0; ++i) { + ++reps; + } + max_reps = BROTLI_MAX(uint32_t, reps, max_reps); + } + max_prefix = max_reps > 0 ? Log2FloorNonZero(max_reps) : 0; + max_prefix = BROTLI_MIN(uint32_t, max_prefix, *max_run_length_prefix); + *max_run_length_prefix = max_prefix; + *out_size = 0; + for (i = 0; i < in_size;) { + BROTLI_DCHECK(*out_size <= i); + if (v[i] != 0) { + v[*out_size] = v[i] + *max_run_length_prefix; + ++i; + ++(*out_size); + } else { + uint32_t reps = 1; + size_t k; + for (k = i + 1; k < in_size && v[k] == 0; ++k) { + ++reps; + } + i += reps; + while (reps != 0) { + if (reps < (2u << max_prefix)) { + uint32_t run_length_prefix = Log2FloorNonZero(reps); + const uint32_t extra_bits = reps - (1u << run_length_prefix); + v[*out_size] = run_length_prefix + (extra_bits << 9); + ++(*out_size); + break; + } else { + const uint32_t extra_bits = (1u << max_prefix) - 1u; + v[*out_size] = max_prefix + (extra_bits << 9); + reps -= (2u << max_prefix) - 1u; + ++(*out_size); + } + } + } + } +} + +#define SYMBOL_BITS 9 + +typedef struct EncodeContextMapArena { + uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; + uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; + uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; +} EncodeContextMapArena; + +static void EncodeContextMap(MemoryManager* m, + EncodeContextMapArena* arena, + const uint32_t* context_map, + size_t context_map_size, + size_t num_clusters, + HuffmanTree* tree, + size_t* storage_ix, uint8_t* storage) { + size_t i; + uint32_t* rle_symbols; + uint32_t max_run_length_prefix = 6; + size_t num_rle_symbols = 0; + uint32_t* BROTLI_RESTRICT const histogram = arena->histogram; + static const uint32_t kSymbolMask = (1u << SYMBOL_BITS) - 1u; + uint8_t* BROTLI_RESTRICT const depths = arena->depths; + uint16_t* BROTLI_RESTRICT const bits = arena->bits; + + StoreVarLenUint8(num_clusters - 1, storage_ix, storage); + + if (num_clusters == 1) { + return; + } + + rle_symbols = BROTLI_ALLOC(m, uint32_t, context_map_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(rle_symbols)) return; + MoveToFrontTransform(context_map, context_map_size, rle_symbols); + RunLengthCodeZeros(context_map_size, rle_symbols, + &num_rle_symbols, &max_run_length_prefix); + memset(histogram, 0, sizeof(arena->histogram)); + for (i = 0; i < num_rle_symbols; ++i) { + ++histogram[rle_symbols[i] & kSymbolMask]; + } + { + BROTLI_BOOL use_rle = TO_BROTLI_BOOL(max_run_length_prefix > 0); + BrotliWriteBits(1, (uint64_t)use_rle, storage_ix, storage); + if (use_rle) { + BrotliWriteBits(4, max_run_length_prefix - 1, storage_ix, storage); + } + } + BuildAndStoreHuffmanTree(histogram, num_clusters + max_run_length_prefix, + num_clusters + max_run_length_prefix, + tree, depths, bits, storage_ix, storage); + for (i = 0; i < num_rle_symbols; ++i) { + const uint32_t rle_symbol = rle_symbols[i] & kSymbolMask; + const uint32_t extra_bits_val = rle_symbols[i] >> SYMBOL_BITS; + BrotliWriteBits(depths[rle_symbol], bits[rle_symbol], storage_ix, storage); + if (rle_symbol > 0 && rle_symbol <= max_run_length_prefix) { + BrotliWriteBits(rle_symbol, extra_bits_val, storage_ix, storage); + } + } + BrotliWriteBits(1, 1, storage_ix, storage); /* use move-to-front */ + BROTLI_FREE(m, rle_symbols); +} + +/* Stores the block switch command with index block_ix to the bit stream. */ +static BROTLI_INLINE void StoreBlockSwitch(BlockSplitCode* code, + const uint32_t block_len, + const uint8_t block_type, + BROTLI_BOOL is_first_block, + size_t* storage_ix, + uint8_t* storage) { + size_t typecode = NextBlockTypeCode(&code->type_code_calculator, block_type); + size_t lencode; + uint32_t len_nextra; + uint32_t len_extra; + if (!is_first_block) { + BrotliWriteBits(code->type_depths[typecode], code->type_bits[typecode], + storage_ix, storage); + } + GetBlockLengthPrefixCode(block_len, &lencode, &len_nextra, &len_extra); + + BrotliWriteBits(code->length_depths[lencode], code->length_bits[lencode], + storage_ix, storage); + BrotliWriteBits(len_nextra, len_extra, storage_ix, storage); +} + +/* Builds a BlockSplitCode data structure from the block split given by the + vector of block types and block lengths and stores it to the bit stream. */ +static void BuildAndStoreBlockSplitCode(const uint8_t* types, + const uint32_t* lengths, + const size_t num_blocks, + const size_t num_types, + HuffmanTree* tree, + BlockSplitCode* code, + size_t* storage_ix, + uint8_t* storage) { + uint32_t type_histo[BROTLI_MAX_BLOCK_TYPE_SYMBOLS]; + uint32_t length_histo[BROTLI_NUM_BLOCK_LEN_SYMBOLS]; + size_t i; + BlockTypeCodeCalculator type_code_calculator; + memset(type_histo, 0, (num_types + 2) * sizeof(type_histo[0])); + memset(length_histo, 0, sizeof(length_histo)); + InitBlockTypeCodeCalculator(&type_code_calculator); + for (i = 0; i < num_blocks; ++i) { + size_t type_code = NextBlockTypeCode(&type_code_calculator, types[i]); + if (i != 0) ++type_histo[type_code]; + ++length_histo[BlockLengthPrefixCode(lengths[i])]; + } + StoreVarLenUint8(num_types - 1, storage_ix, storage); + if (num_types > 1) { /* TODO(eustas): else? could StoreBlockSwitch occur? */ + BuildAndStoreHuffmanTree(&type_histo[0], num_types + 2, num_types + 2, tree, + &code->type_depths[0], &code->type_bits[0], + storage_ix, storage); + BuildAndStoreHuffmanTree(&length_histo[0], BROTLI_NUM_BLOCK_LEN_SYMBOLS, + BROTLI_NUM_BLOCK_LEN_SYMBOLS, + tree, &code->length_depths[0], + &code->length_bits[0], storage_ix, storage); + StoreBlockSwitch(code, lengths[0], types[0], 1, storage_ix, storage); + } +} + +/* Stores a context map where the histogram type is always the block type. */ +static void StoreTrivialContextMap(EncodeContextMapArena* arena, + size_t num_types, + size_t context_bits, + HuffmanTree* tree, + size_t* storage_ix, + uint8_t* storage) { + StoreVarLenUint8(num_types - 1, storage_ix, storage); + if (num_types > 1) { + size_t repeat_code = context_bits - 1u; + size_t repeat_bits = (1u << repeat_code) - 1u; + size_t alphabet_size = num_types + repeat_code; + uint32_t* BROTLI_RESTRICT const histogram = arena->histogram; + uint8_t* BROTLI_RESTRICT const depths = arena->depths; + uint16_t* BROTLI_RESTRICT const bits = arena->bits; + size_t i; + memset(histogram, 0, alphabet_size * sizeof(histogram[0])); + /* Write RLEMAX. */ + BrotliWriteBits(1, 1, storage_ix, storage); + BrotliWriteBits(4, repeat_code - 1, storage_ix, storage); + histogram[repeat_code] = (uint32_t)num_types; + histogram[0] = 1; + for (i = context_bits; i < alphabet_size; ++i) { + histogram[i] = 1; + } + BuildAndStoreHuffmanTree(histogram, alphabet_size, alphabet_size, + tree, depths, bits, storage_ix, storage); + for (i = 0; i < num_types; ++i) { + size_t code = (i == 0 ? 0 : i + context_bits - 1); + BrotliWriteBits(depths[code], bits[code], storage_ix, storage); + BrotliWriteBits( + depths[repeat_code], bits[repeat_code], storage_ix, storage); + BrotliWriteBits(repeat_code, repeat_bits, storage_ix, storage); + } + /* Write IMTF (inverse-move-to-front) bit. */ + BrotliWriteBits(1, 1, storage_ix, storage); + } +} + +/* Manages the encoding of one block category (literal, command or distance). */ +typedef struct BlockEncoder { + size_t histogram_length_; + size_t num_block_types_; + const uint8_t* block_types_; /* Not owned. */ + const uint32_t* block_lengths_; /* Not owned. */ + size_t num_blocks_; + BlockSplitCode block_split_code_; + size_t block_ix_; + size_t block_len_; + size_t entropy_ix_; + uint8_t* depths_; + uint16_t* bits_; +} BlockEncoder; + +static void InitBlockEncoder(BlockEncoder* self, size_t histogram_length, + size_t num_block_types, const uint8_t* block_types, + const uint32_t* block_lengths, const size_t num_blocks) { + self->histogram_length_ = histogram_length; + self->num_block_types_ = num_block_types; + self->block_types_ = block_types; + self->block_lengths_ = block_lengths; + self->num_blocks_ = num_blocks; + InitBlockTypeCodeCalculator(&self->block_split_code_.type_code_calculator); + self->block_ix_ = 0; + self->block_len_ = num_blocks == 0 ? 0 : block_lengths[0]; + self->entropy_ix_ = 0; + self->depths_ = 0; + self->bits_ = 0; +} + +static void CleanupBlockEncoder(MemoryManager* m, BlockEncoder* self) { + BROTLI_FREE(m, self->depths_); + BROTLI_FREE(m, self->bits_); +} + +/* Creates entropy codes of block lengths and block types and stores them + to the bit stream. */ +static void BuildAndStoreBlockSwitchEntropyCodes(BlockEncoder* self, + HuffmanTree* tree, size_t* storage_ix, uint8_t* storage) { + BuildAndStoreBlockSplitCode(self->block_types_, self->block_lengths_, + self->num_blocks_, self->num_block_types_, tree, &self->block_split_code_, + storage_ix, storage); +} + +/* Stores the next symbol with the entropy code of the current block type. + Updates the block type and block length at block boundaries. */ +static void StoreSymbol(BlockEncoder* self, size_t symbol, size_t* storage_ix, + uint8_t* storage) { + if (self->block_len_ == 0) { + size_t block_ix = ++self->block_ix_; + uint32_t block_len = self->block_lengths_[block_ix]; + uint8_t block_type = self->block_types_[block_ix]; + self->block_len_ = block_len; + self->entropy_ix_ = block_type * self->histogram_length_; + StoreBlockSwitch(&self->block_split_code_, block_len, block_type, 0, + storage_ix, storage); + } + --self->block_len_; + { + size_t ix = self->entropy_ix_ + symbol; + BrotliWriteBits(self->depths_[ix], self->bits_[ix], storage_ix, storage); + } +} + +/* Stores the next symbol with the entropy code of the current block type and + context value. + Updates the block type and block length at block boundaries. */ +static void StoreSymbolWithContext(BlockEncoder* self, size_t symbol, + size_t context, const uint32_t* context_map, size_t* storage_ix, + uint8_t* storage, const size_t context_bits) { + if (self->block_len_ == 0) { + size_t block_ix = ++self->block_ix_; + uint32_t block_len = self->block_lengths_[block_ix]; + uint8_t block_type = self->block_types_[block_ix]; + self->block_len_ = block_len; + self->entropy_ix_ = (size_t)block_type << context_bits; + StoreBlockSwitch(&self->block_split_code_, block_len, block_type, 0, + storage_ix, storage); + } + --self->block_len_; + { + size_t histo_ix = context_map[self->entropy_ix_ + context]; + size_t ix = histo_ix * self->histogram_length_ + symbol; + BrotliWriteBits(self->depths_[ix], self->bits_[ix], storage_ix, storage); + } +} + +#define FN(X) X ## Literal +/* NOLINTNEXTLINE(build/include) */ +#include "block_encoder_inc.h" +#undef FN + +#define FN(X) X ## Command +/* NOLINTNEXTLINE(build/include) */ +#include "block_encoder_inc.h" +#undef FN + +#define FN(X) X ## Distance +/* NOLINTNEXTLINE(build/include) */ +#include "block_encoder_inc.h" +#undef FN + +static void JumpToByteBoundary(size_t* storage_ix, uint8_t* storage) { + *storage_ix = (*storage_ix + 7u) & ~7u; + storage[*storage_ix >> 3] = 0; +} + +typedef struct StoreMetablockArena { + BlockEncoder literal_enc; + BlockEncoder command_enc; + BlockEncoder distance_enc; + EncodeContextMapArena context_map_arena; +} StoreMetablockArena; + +void BrotliStoreMetaBlock(MemoryManager* m, + const uint8_t* input, size_t start_pos, size_t length, size_t mask, + uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last, + const BrotliEncoderParams* params, ContextType literal_context_mode, + const Command* commands, size_t n_commands, const MetaBlockSplit* mb, + size_t* storage_ix, uint8_t* storage) { + + size_t pos = start_pos; + size_t i; + uint32_t num_distance_symbols = params->dist.alphabet_size_max; + uint32_t num_effective_distance_symbols = params->dist.alphabet_size_limit; + HuffmanTree* tree; + ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); + StoreMetablockArena* arena = NULL; + BlockEncoder* literal_enc = NULL; + BlockEncoder* command_enc = NULL; + BlockEncoder* distance_enc = NULL; + const BrotliDistanceParams* dist = ¶ms->dist; + BROTLI_DCHECK( + num_effective_distance_symbols <= BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS); + + StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); + + tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE); + arena = BROTLI_ALLOC(m, StoreMetablockArena, 1); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree) || BROTLI_IS_NULL(arena)) return; + literal_enc = &arena->literal_enc; + command_enc = &arena->command_enc; + distance_enc = &arena->distance_enc; + InitBlockEncoder(literal_enc, BROTLI_NUM_LITERAL_SYMBOLS, + mb->literal_split.num_types, mb->literal_split.types, + mb->literal_split.lengths, mb->literal_split.num_blocks); + InitBlockEncoder(command_enc, BROTLI_NUM_COMMAND_SYMBOLS, + mb->command_split.num_types, mb->command_split.types, + mb->command_split.lengths, mb->command_split.num_blocks); + InitBlockEncoder(distance_enc, num_effective_distance_symbols, + mb->distance_split.num_types, mb->distance_split.types, + mb->distance_split.lengths, mb->distance_split.num_blocks); + + BuildAndStoreBlockSwitchEntropyCodes(literal_enc, tree, storage_ix, storage); + BuildAndStoreBlockSwitchEntropyCodes(command_enc, tree, storage_ix, storage); + BuildAndStoreBlockSwitchEntropyCodes(distance_enc, tree, storage_ix, storage); + + BrotliWriteBits(2, dist->distance_postfix_bits, storage_ix, storage); + BrotliWriteBits( + 4, dist->num_direct_distance_codes >> dist->distance_postfix_bits, + storage_ix, storage); + for (i = 0; i < mb->literal_split.num_types; ++i) { + BrotliWriteBits(2, literal_context_mode, storage_ix, storage); + } + + if (mb->literal_context_map_size == 0) { + StoreTrivialContextMap( + &arena->context_map_arena, mb->literal_histograms_size, + BROTLI_LITERAL_CONTEXT_BITS, tree, storage_ix, storage); + } else { + EncodeContextMap(m, &arena->context_map_arena, + mb->literal_context_map, mb->literal_context_map_size, + mb->literal_histograms_size, tree, storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + } + + if (mb->distance_context_map_size == 0) { + StoreTrivialContextMap( + &arena->context_map_arena, mb->distance_histograms_size, + BROTLI_DISTANCE_CONTEXT_BITS, tree, storage_ix, storage); + } else { + EncodeContextMap(m, &arena->context_map_arena, + mb->distance_context_map, mb->distance_context_map_size, + mb->distance_histograms_size, tree, storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + } + + BuildAndStoreEntropyCodesLiteral(m, literal_enc, mb->literal_histograms, + mb->literal_histograms_size, BROTLI_NUM_LITERAL_SYMBOLS, tree, + storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + BuildAndStoreEntropyCodesCommand(m, command_enc, mb->command_histograms, + mb->command_histograms_size, BROTLI_NUM_COMMAND_SYMBOLS, tree, + storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + BuildAndStoreEntropyCodesDistance(m, distance_enc, mb->distance_histograms, + mb->distance_histograms_size, num_distance_symbols, tree, + storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + BROTLI_FREE(m, tree); + + for (i = 0; i < n_commands; ++i) { + const Command cmd = commands[i]; + size_t cmd_code = cmd.cmd_prefix_; + StoreSymbol(command_enc, cmd_code, storage_ix, storage); + StoreCommandExtra(&cmd, storage_ix, storage); + if (mb->literal_context_map_size == 0) { + size_t j; + for (j = cmd.insert_len_; j != 0; --j) { + StoreSymbol(literal_enc, input[pos & mask], storage_ix, storage); + ++pos; + } + } else { + size_t j; + for (j = cmd.insert_len_; j != 0; --j) { + size_t context = + BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut); + uint8_t literal = input[pos & mask]; + StoreSymbolWithContext(literal_enc, literal, context, + mb->literal_context_map, storage_ix, storage, + BROTLI_LITERAL_CONTEXT_BITS); + prev_byte2 = prev_byte; + prev_byte = literal; + ++pos; + } + } + pos += CommandCopyLen(&cmd); + if (CommandCopyLen(&cmd)) { + prev_byte2 = input[(pos - 2) & mask]; + prev_byte = input[(pos - 1) & mask]; + if (cmd.cmd_prefix_ >= 128) { + size_t dist_code = cmd.dist_prefix_ & 0x3FF; + uint32_t distnumextra = cmd.dist_prefix_ >> 10; + uint64_t distextra = cmd.dist_extra_; + if (mb->distance_context_map_size == 0) { + StoreSymbol(distance_enc, dist_code, storage_ix, storage); + } else { + size_t context = CommandDistanceContext(&cmd); + StoreSymbolWithContext(distance_enc, dist_code, context, + mb->distance_context_map, storage_ix, storage, + BROTLI_DISTANCE_CONTEXT_BITS); + } + BrotliWriteBits(distnumextra, distextra, storage_ix, storage); + } + } + } + CleanupBlockEncoder(m, distance_enc); + CleanupBlockEncoder(m, command_enc); + CleanupBlockEncoder(m, literal_enc); + BROTLI_FREE(m, arena); + if (is_last) { + JumpToByteBoundary(storage_ix, storage); + } +} + +static void BuildHistograms(const uint8_t* input, + size_t start_pos, + size_t mask, + const Command* commands, + size_t n_commands, + HistogramLiteral* lit_histo, + HistogramCommand* cmd_histo, + HistogramDistance* dist_histo) { + size_t pos = start_pos; + size_t i; + for (i = 0; i < n_commands; ++i) { + const Command cmd = commands[i]; + size_t j; + HistogramAddCommand(cmd_histo, cmd.cmd_prefix_); + for (j = cmd.insert_len_; j != 0; --j) { + HistogramAddLiteral(lit_histo, input[pos & mask]); + ++pos; + } + pos += CommandCopyLen(&cmd); + if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) { + HistogramAddDistance(dist_histo, cmd.dist_prefix_ & 0x3FF); + } + } +} + +static void StoreDataWithHuffmanCodes(const uint8_t* input, + size_t start_pos, + size_t mask, + const Command* commands, + size_t n_commands, + const uint8_t* lit_depth, + const uint16_t* lit_bits, + const uint8_t* cmd_depth, + const uint16_t* cmd_bits, + const uint8_t* dist_depth, + const uint16_t* dist_bits, + size_t* storage_ix, + uint8_t* storage) { + size_t pos = start_pos; + size_t i; + for (i = 0; i < n_commands; ++i) { + const Command cmd = commands[i]; + const size_t cmd_code = cmd.cmd_prefix_; + size_t j; + BrotliWriteBits( + cmd_depth[cmd_code], cmd_bits[cmd_code], storage_ix, storage); + StoreCommandExtra(&cmd, storage_ix, storage); + for (j = cmd.insert_len_; j != 0; --j) { + const uint8_t literal = input[pos & mask]; + BrotliWriteBits( + lit_depth[literal], lit_bits[literal], storage_ix, storage); + ++pos; + } + pos += CommandCopyLen(&cmd); + if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) { + const size_t dist_code = cmd.dist_prefix_ & 0x3FF; + const uint32_t distnumextra = cmd.dist_prefix_ >> 10; + const uint32_t distextra = cmd.dist_extra_; + BrotliWriteBits(dist_depth[dist_code], dist_bits[dist_code], + storage_ix, storage); + BrotliWriteBits(distnumextra, distextra, storage_ix, storage); + } + } +} + +/* TODO(eustas): pull alloc/dealloc to caller? */ +typedef struct MetablockArena { + HistogramLiteral lit_histo; + HistogramCommand cmd_histo; + HistogramDistance dist_histo; + /* TODO(eustas): merge bits and depth? */ + uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS]; + uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS]; + uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS]; + uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS]; + uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE]; + uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE]; + HuffmanTree tree[MAX_HUFFMAN_TREE_SIZE]; +} MetablockArena; + +void BrotliStoreMetaBlockTrivial(MemoryManager* m, + const uint8_t* input, size_t start_pos, size_t length, size_t mask, + BROTLI_BOOL is_last, const BrotliEncoderParams* params, + const Command* commands, size_t n_commands, + size_t* storage_ix, uint8_t* storage) { + MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1); + uint32_t num_distance_symbols = params->dist.alphabet_size_max; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; + + StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); + + HistogramClearLiteral(&arena->lit_histo); + HistogramClearCommand(&arena->cmd_histo); + HistogramClearDistance(&arena->dist_histo); + + BuildHistograms(input, start_pos, mask, commands, n_commands, + &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo); + + BrotliWriteBits(13, 0, storage_ix, storage); + + BuildAndStoreHuffmanTree(arena->lit_histo.data_, BROTLI_NUM_LITERAL_SYMBOLS, + BROTLI_NUM_LITERAL_SYMBOLS, arena->tree, + arena->lit_depth, arena->lit_bits, + storage_ix, storage); + BuildAndStoreHuffmanTree(arena->cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS, + BROTLI_NUM_COMMAND_SYMBOLS, arena->tree, + arena->cmd_depth, arena->cmd_bits, + storage_ix, storage); + BuildAndStoreHuffmanTree(arena->dist_histo.data_, + MAX_SIMPLE_DISTANCE_ALPHABET_SIZE, + num_distance_symbols, arena->tree, + arena->dist_depth, arena->dist_bits, + storage_ix, storage); + StoreDataWithHuffmanCodes(input, start_pos, mask, commands, + n_commands, arena->lit_depth, arena->lit_bits, + arena->cmd_depth, arena->cmd_bits, + arena->dist_depth, arena->dist_bits, + storage_ix, storage); + BROTLI_FREE(m, arena); + if (is_last) { + JumpToByteBoundary(storage_ix, storage); + } +} + +void BrotliStoreMetaBlockFast(MemoryManager* m, + const uint8_t* input, size_t start_pos, size_t length, size_t mask, + BROTLI_BOOL is_last, const BrotliEncoderParams* params, + const Command* commands, size_t n_commands, + size_t* storage_ix, uint8_t* storage) { + MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1); + uint32_t num_distance_symbols = params->dist.alphabet_size_max; + uint32_t distance_alphabet_bits = + Log2FloorNonZero(num_distance_symbols - 1) + 1; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; + + StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); + + BrotliWriteBits(13, 0, storage_ix, storage); + + if (n_commands <= 128) { + uint32_t histogram[BROTLI_NUM_LITERAL_SYMBOLS] = { 0 }; + size_t pos = start_pos; + size_t num_literals = 0; + size_t i; + for (i = 0; i < n_commands; ++i) { + const Command cmd = commands[i]; + size_t j; + for (j = cmd.insert_len_; j != 0; --j) { + ++histogram[input[pos & mask]]; + ++pos; + } + num_literals += cmd.insert_len_; + pos += CommandCopyLen(&cmd); + } + BrotliBuildAndStoreHuffmanTreeFast(arena->tree, histogram, num_literals, + /* max_bits = */ 8, + arena->lit_depth, arena->lit_bits, + storage_ix, storage); + StoreStaticCommandHuffmanTree(storage_ix, storage); + StoreStaticDistanceHuffmanTree(storage_ix, storage); + StoreDataWithHuffmanCodes(input, start_pos, mask, commands, + n_commands, arena->lit_depth, arena->lit_bits, + kStaticCommandCodeDepth, + kStaticCommandCodeBits, + kStaticDistanceCodeDepth, + kStaticDistanceCodeBits, + storage_ix, storage); + } else { + HistogramClearLiteral(&arena->lit_histo); + HistogramClearCommand(&arena->cmd_histo); + HistogramClearDistance(&arena->dist_histo); + BuildHistograms(input, start_pos, mask, commands, n_commands, + &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo); + BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->lit_histo.data_, + arena->lit_histo.total_count_, + /* max_bits = */ 8, + arena->lit_depth, arena->lit_bits, + storage_ix, storage); + BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->cmd_histo.data_, + arena->cmd_histo.total_count_, + /* max_bits = */ 10, + arena->cmd_depth, arena->cmd_bits, + storage_ix, storage); + BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->dist_histo.data_, + arena->dist_histo.total_count_, + /* max_bits = */ + distance_alphabet_bits, + arena->dist_depth, arena->dist_bits, + storage_ix, storage); + StoreDataWithHuffmanCodes(input, start_pos, mask, commands, + n_commands, arena->lit_depth, arena->lit_bits, + arena->cmd_depth, arena->cmd_bits, + arena->dist_depth, arena->dist_bits, + storage_ix, storage); + } + + BROTLI_FREE(m, arena); + + if (is_last) { + JumpToByteBoundary(storage_ix, storage); + } +} + +/* This is for storing uncompressed blocks (simple raw storage of + bytes-as-bytes). */ +void BrotliStoreUncompressedMetaBlock(BROTLI_BOOL is_final_block, + const uint8_t* BROTLI_RESTRICT input, + size_t position, size_t mask, + size_t len, + size_t* BROTLI_RESTRICT storage_ix, + uint8_t* BROTLI_RESTRICT storage) { + size_t masked_pos = position & mask; + BrotliStoreUncompressedMetaBlockHeader(len, storage_ix, storage); + JumpToByteBoundary(storage_ix, storage); + + if (masked_pos + len > mask + 1) { + size_t len1 = mask + 1 - masked_pos; + memcpy(&storage[*storage_ix >> 3], &input[masked_pos], len1); + *storage_ix += len1 << 3; + len -= len1; + masked_pos = 0; + } + memcpy(&storage[*storage_ix >> 3], &input[masked_pos], len); + *storage_ix += len << 3; + + /* We need to clear the next 4 bytes to continue to be + compatible with BrotliWriteBits. */ + BrotliWriteBitsPrepareStorage(*storage_ix, storage); + + /* Since the uncompressed block itself may not be the final block, add an + empty one after this. */ + if (is_final_block) { + BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ + BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ + JumpToByteBoundary(storage_ix, storage); + } +} + +#if defined(BROTLI_TEST) +void BrotliGetBlockLengthPrefixCodeForTest(uint32_t len, size_t* code, + uint32_t* n_extra, uint32_t* extra); +void BrotliGetBlockLengthPrefixCodeForTest(uint32_t len, size_t* code, + uint32_t* n_extra, uint32_t* extra) { + GetBlockLengthPrefixCode(len, code, n_extra, extra); +} +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/brotli_bit_stream.h b/packages/runtime/vendor/brotli/c/enc/brotli_bit_stream.h new file mode 100644 index 000000000..7b72bdcee --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/brotli_bit_stream.h @@ -0,0 +1,83 @@ +/* Copyright 2014 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions to convert brotli-related data structures into the + brotli bit stream. The functions here operate under + assumption that there is enough space in the storage, i.e., there are + no out-of-range checks anywhere. + + These functions do bit addressing into a byte array. The byte array + is called "storage" and the index to the bit is called storage_ix + in function arguments. */ + +#ifndef BROTLI_ENC_BROTLI_BIT_STREAM_H_ +#define BROTLI_ENC_BROTLI_BIT_STREAM_H_ + +#include "../common/context.h" +#include "../common/platform.h" +#include "command.h" +#include "entropy_encode.h" +#include "memory.h" +#include "metablock.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* All Store functions here will use a storage_ix, which is always the bit + position for the current storage. */ + +BROTLI_INTERNAL void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num, + HuffmanTree* tree, size_t* storage_ix, uint8_t* storage); + +BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast( + HuffmanTree* tree, const uint32_t* histogram, const size_t histogram_total, + const size_t max_bits, uint8_t* depth, uint16_t* bits, size_t* storage_ix, + uint8_t* storage); + +/* REQUIRES: length > 0 */ +/* REQUIRES: length <= (1 << 24) */ +BROTLI_INTERNAL void BrotliStoreMetaBlock(MemoryManager* m, + const uint8_t* input, size_t start_pos, size_t length, size_t mask, + uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last, + const BrotliEncoderParams* params, ContextType literal_context_mode, + const Command* commands, size_t n_commands, const MetaBlockSplit* mb, + size_t* storage_ix, uint8_t* storage); + +/* Stores the meta-block without doing any block splitting, just collects + one histogram per block category and uses that for entropy coding. + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) */ +BROTLI_INTERNAL void BrotliStoreMetaBlockTrivial(MemoryManager* m, + const uint8_t* input, size_t start_pos, size_t length, size_t mask, + BROTLI_BOOL is_last, const BrotliEncoderParams* params, + const Command* commands, size_t n_commands, + size_t* storage_ix, uint8_t* storage); + +/* Same as above, but uses static prefix codes for histograms with a only a few + symbols, and uses static code length prefix codes for all other histograms. + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) */ +BROTLI_INTERNAL void BrotliStoreMetaBlockFast(MemoryManager* m, + const uint8_t* input, size_t start_pos, size_t length, size_t mask, + BROTLI_BOOL is_last, const BrotliEncoderParams* params, + const Command* commands, size_t n_commands, + size_t* storage_ix, uint8_t* storage); + +/* This is for storing uncompressed blocks (simple raw storage of + bytes-as-bytes). + REQUIRES: length > 0 + REQUIRES: length <= (1 << 24) */ +BROTLI_INTERNAL void BrotliStoreUncompressedMetaBlock( + BROTLI_BOOL is_final_block, const uint8_t* BROTLI_RESTRICT input, + size_t position, size_t mask, size_t len, + size_t* BROTLI_RESTRICT storage_ix, uint8_t* BROTLI_RESTRICT storage); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_BROTLI_BIT_STREAM_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/cluster.c b/packages/runtime/vendor/brotli/c/enc/cluster.c new file mode 100644 index 000000000..4bf94ab76 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/cluster.c @@ -0,0 +1,55 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions for clustering similar histograms together. */ + +#include "cluster.h" + +#include "../common/platform.h" +#include "bit_cost.h" /* BrotliPopulationCost */ +#include "fast_log.h" +#include "histogram.h" +#include "memory.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static BROTLI_INLINE BROTLI_BOOL HistogramPairIsLess( + const HistogramPair* p1, const HistogramPair* p2) { + if (p1->cost_diff != p2->cost_diff) { + return TO_BROTLI_BOOL(p1->cost_diff > p2->cost_diff); + } + return TO_BROTLI_BOOL((p1->idx2 - p1->idx1) > (p2->idx2 - p2->idx1)); +} + +/* Returns entropy reduction of the context map when we combine two clusters. */ +static BROTLI_INLINE double ClusterCostDiff(size_t size_a, size_t size_b) { + size_t size_c = size_a + size_b; + return (double)size_a * FastLog2(size_a) + + (double)size_b * FastLog2(size_b) - + (double)size_c * FastLog2(size_c); +} + +#define CODE(X) X + +#define FN(X) X ## Literal +#include "cluster_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Command +#include "cluster_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Distance +#include "cluster_inc.h" /* NOLINT(build/include) */ +#undef FN + +#undef CODE + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/cluster.h b/packages/runtime/vendor/brotli/c/enc/cluster.h new file mode 100644 index 000000000..6519116c4 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/cluster.h @@ -0,0 +1,47 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions for clustering similar histograms together. */ + +#ifndef BROTLI_ENC_CLUSTER_H_ +#define BROTLI_ENC_CLUSTER_H_ + +#include "../common/platform.h" +#include "histogram.h" +#include "memory.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct HistogramPair { + uint32_t idx1; + uint32_t idx2; + double cost_combo; + double cost_diff; +} HistogramPair; + +#define CODE(X) /* Declaration */; + +#define FN(X) X ## Literal +#include "cluster_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Command +#include "cluster_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Distance +#include "cluster_inc.h" /* NOLINT(build/include) */ +#undef FN + +#undef CODE + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_CLUSTER_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/cluster_inc.h b/packages/runtime/vendor/brotli/c/enc/cluster_inc.h new file mode 100644 index 000000000..d6215ef06 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/cluster_inc.h @@ -0,0 +1,325 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN, CODE */ + +#define HistogramType FN(Histogram) + +/* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if + it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */ +BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)( + const HistogramType* out, HistogramType* tmp, const uint32_t* cluster_size, + uint32_t idx1, uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs, + size_t* num_pairs) CODE({ + BROTLI_BOOL is_good_pair = BROTLI_FALSE; + HistogramPair p; + p.idx1 = p.idx2 = 0; + p.cost_diff = p.cost_combo = 0; + if (idx1 == idx2) { + return; + } + if (idx2 < idx1) { + uint32_t t = idx2; + idx2 = idx1; + idx1 = t; + } + p.idx1 = idx1; + p.idx2 = idx2; + p.cost_diff = 0.5 * ClusterCostDiff(cluster_size[idx1], cluster_size[idx2]); + p.cost_diff -= out[idx1].bit_cost_; + p.cost_diff -= out[idx2].bit_cost_; + + if (out[idx1].total_count_ == 0) { + p.cost_combo = out[idx2].bit_cost_; + is_good_pair = BROTLI_TRUE; + } else if (out[idx2].total_count_ == 0) { + p.cost_combo = out[idx1].bit_cost_; + is_good_pair = BROTLI_TRUE; + } else { + double threshold = *num_pairs == 0 ? 1e99 : + BROTLI_MAX(double, 0.0, pairs[0].cost_diff); + double cost_combo; + *tmp = out[idx1]; + FN(HistogramAddHistogram)(tmp, &out[idx2]); + cost_combo = FN(BrotliPopulationCost)(tmp); + if (cost_combo < threshold - p.cost_diff) { + p.cost_combo = cost_combo; + is_good_pair = BROTLI_TRUE; + } + } + if (is_good_pair) { + p.cost_diff += p.cost_combo; + if (*num_pairs > 0 && HistogramPairIsLess(&pairs[0], &p)) { + /* Replace the top of the queue if needed. */ + if (*num_pairs < max_num_pairs) { + pairs[*num_pairs] = pairs[0]; + ++(*num_pairs); + } + pairs[0] = p; + } else if (*num_pairs < max_num_pairs) { + pairs[*num_pairs] = p; + ++(*num_pairs); + } + } +}) + +BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out, + HistogramType* tmp, + uint32_t* cluster_size, + uint32_t* symbols, + uint32_t* clusters, + HistogramPair* pairs, + size_t num_clusters, + size_t symbols_size, + size_t max_clusters, + size_t max_num_pairs) CODE({ + double cost_diff_threshold = 0.0; + size_t min_cluster_size = 1; + size_t num_pairs = 0; + + { + /* We maintain a vector of histogram pairs, with the property that the pair + with the maximum bit cost reduction is the first. */ + size_t idx1; + for (idx1 = 0; idx1 < num_clusters; ++idx1) { + size_t idx2; + for (idx2 = idx1 + 1; idx2 < num_clusters; ++idx2) { + FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, clusters[idx1], + clusters[idx2], max_num_pairs, &pairs[0], &num_pairs); + } + } + } + + while (num_clusters > min_cluster_size) { + uint32_t best_idx1; + uint32_t best_idx2; + size_t i; + if (pairs[0].cost_diff >= cost_diff_threshold) { + cost_diff_threshold = 1e99; + min_cluster_size = max_clusters; + continue; + } + /* Take the best pair from the top of heap. */ + best_idx1 = pairs[0].idx1; + best_idx2 = pairs[0].idx2; + FN(HistogramAddHistogram)(&out[best_idx1], &out[best_idx2]); + out[best_idx1].bit_cost_ = pairs[0].cost_combo; + cluster_size[best_idx1] += cluster_size[best_idx2]; + for (i = 0; i < symbols_size; ++i) { + if (symbols[i] == best_idx2) { + symbols[i] = best_idx1; + } + } + for (i = 0; i < num_clusters; ++i) { + if (clusters[i] == best_idx2) { + memmove(&clusters[i], &clusters[i + 1], + (num_clusters - i - 1) * sizeof(clusters[0])); + break; + } + } + --num_clusters; + { + /* Remove pairs intersecting the just combined best pair. */ + size_t copy_to_idx = 0; + for (i = 0; i < num_pairs; ++i) { + HistogramPair* p = &pairs[i]; + if (p->idx1 == best_idx1 || p->idx2 == best_idx1 || + p->idx1 == best_idx2 || p->idx2 == best_idx2) { + /* Remove invalid pair from the queue. */ + continue; + } + if (HistogramPairIsLess(&pairs[0], p)) { + /* Replace the top of the queue if needed. */ + HistogramPair front = pairs[0]; + pairs[0] = *p; + pairs[copy_to_idx] = front; + } else { + pairs[copy_to_idx] = *p; + } + ++copy_to_idx; + } + num_pairs = copy_to_idx; + } + + /* Push new pairs formed with the combined histogram to the heap. */ + for (i = 0; i < num_clusters; ++i) { + FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, best_idx1, + clusters[i], max_num_pairs, &pairs[0], &num_pairs); + } + } + return num_clusters; +}) + +/* What is the bit cost of moving histogram from cur_symbol to candidate. */ +BROTLI_INTERNAL double FN(BrotliHistogramBitCostDistance)( + const HistogramType* histogram, const HistogramType* candidate, + HistogramType* tmp) CODE({ + if (histogram->total_count_ == 0) { + return 0.0; + } else { + *tmp = *histogram; + FN(HistogramAddHistogram)(tmp, candidate); + return FN(BrotliPopulationCost)(tmp) - candidate->bit_cost_; + } +}) + +/* Find the best 'out' histogram for each of the 'in' histograms. + When called, clusters[0..num_clusters) contains the unique values from + symbols[0..in_size), but this property is not preserved in this function. + Note: we assume that out[]->bit_cost_ is already up-to-date. */ +BROTLI_INTERNAL void FN(BrotliHistogramRemap)(const HistogramType* in, + size_t in_size, const uint32_t* clusters, size_t num_clusters, + HistogramType* out, HistogramType* tmp, uint32_t* symbols) CODE({ + size_t i; + for (i = 0; i < in_size; ++i) { + uint32_t best_out = i == 0 ? symbols[0] : symbols[i - 1]; + double best_bits = + FN(BrotliHistogramBitCostDistance)(&in[i], &out[best_out], tmp); + size_t j; + for (j = 0; j < num_clusters; ++j) { + const double cur_bits = + FN(BrotliHistogramBitCostDistance)(&in[i], &out[clusters[j]], tmp); + if (cur_bits < best_bits) { + best_bits = cur_bits; + best_out = clusters[j]; + } + } + symbols[i] = best_out; + } + + /* Recompute each out based on raw and symbols. */ + for (i = 0; i < num_clusters; ++i) { + FN(HistogramClear)(&out[clusters[i]]); + } + for (i = 0; i < in_size; ++i) { + FN(HistogramAddHistogram)(&out[symbols[i]], &in[i]); + } +}) + +/* Reorders elements of the out[0..length) array and changes values in + symbols[0..length) array in the following way: + * when called, symbols[] contains indexes into out[], and has N unique + values (possibly N < length) + * on return, symbols'[i] = f(symbols[i]) and + out'[symbols'[i]] = out[symbols[i]], for each 0 <= i < length, + where f is a bijection between the range of symbols[] and [0..N), and + the first occurrences of values in symbols'[i] come in consecutive + increasing order. + Returns N, the number of unique values in symbols[]. */ +BROTLI_INTERNAL size_t FN(BrotliHistogramReindex)(MemoryManager* m, + HistogramType* out, uint32_t* symbols, size_t length) CODE({ + static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX; + uint32_t* new_index = BROTLI_ALLOC(m, uint32_t, length); + uint32_t next_index; + HistogramType* tmp; + size_t i; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_index)) return 0; + for (i = 0; i < length; ++i) { + new_index[i] = kInvalidIndex; + } + next_index = 0; + for (i = 0; i < length; ++i) { + if (new_index[symbols[i]] == kInvalidIndex) { + new_index[symbols[i]] = next_index; + ++next_index; + } + } + /* TODO(eustas): by using idea of "cycle-sort" we can avoid allocation of + tmp and reduce the number of copying by the factor of 2. */ + tmp = BROTLI_ALLOC(m, HistogramType, next_index); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return 0; + next_index = 0; + for (i = 0; i < length; ++i) { + if (new_index[symbols[i]] == next_index) { + tmp[next_index] = out[symbols[i]]; + ++next_index; + } + symbols[i] = new_index[symbols[i]]; + } + BROTLI_FREE(m, new_index); + for (i = 0; i < next_index; ++i) { + out[i] = tmp[i]; + } + BROTLI_FREE(m, tmp); + return next_index; +}) + +BROTLI_INTERNAL void FN(BrotliClusterHistograms)( + MemoryManager* m, const HistogramType* in, const size_t in_size, + size_t max_histograms, HistogramType* out, size_t* out_size, + uint32_t* histogram_symbols) CODE({ + uint32_t* cluster_size = BROTLI_ALLOC(m, uint32_t, in_size); + uint32_t* clusters = BROTLI_ALLOC(m, uint32_t, in_size); + size_t num_clusters = 0; + const size_t max_input_histograms = 64; + size_t pairs_capacity = max_input_histograms * max_input_histograms / 2; + /* For the first pass of clustering, we allow all pairs. */ + HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity + 1); + /* TODO(eustas): move to "persistent" arena? */ + HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 1); + size_t i; + + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(cluster_size) || + BROTLI_IS_NULL(clusters) || BROTLI_IS_NULL(pairs)|| BROTLI_IS_NULL(tmp)) { + return; + } + + for (i = 0; i < in_size; ++i) { + cluster_size[i] = 1; + } + + for (i = 0; i < in_size; ++i) { + out[i] = in[i]; + out[i].bit_cost_ = FN(BrotliPopulationCost)(&in[i]); + histogram_symbols[i] = (uint32_t)i; + } + + for (i = 0; i < in_size; i += max_input_histograms) { + size_t num_to_combine = + BROTLI_MIN(size_t, in_size - i, max_input_histograms); + size_t num_new_clusters; + size_t j; + for (j = 0; j < num_to_combine; ++j) { + clusters[num_clusters + j] = (uint32_t)(i + j); + } + num_new_clusters = + FN(BrotliHistogramCombine)(out, tmp, cluster_size, + &histogram_symbols[i], + &clusters[num_clusters], pairs, + num_to_combine, num_to_combine, + max_histograms, pairs_capacity); + num_clusters += num_new_clusters; + } + + { + /* For the second pass, we limit the total number of histogram pairs. + After this limit is reached, we only keep searching for the best pair. */ + size_t max_num_pairs = BROTLI_MIN(size_t, + 64 * num_clusters, (num_clusters / 2) * num_clusters); + BROTLI_ENSURE_CAPACITY( + m, HistogramPair, pairs, pairs_capacity, max_num_pairs + 1); + if (BROTLI_IS_OOM(m)) return; + + /* Collapse similar histograms. */ + num_clusters = FN(BrotliHistogramCombine)(out, tmp, cluster_size, + histogram_symbols, clusters, + pairs, num_clusters, in_size, + max_histograms, max_num_pairs); + } + BROTLI_FREE(m, pairs); + BROTLI_FREE(m, cluster_size); + /* Find the optimal map from original histograms to the final ones. */ + FN(BrotliHistogramRemap)(in, in_size, clusters, num_clusters, + out, tmp, histogram_symbols); + BROTLI_FREE(m, tmp); + BROTLI_FREE(m, clusters); + /* Convert the context map to a canonical form. */ + *out_size = FN(BrotliHistogramReindex)(m, out, histogram_symbols, in_size); + if (BROTLI_IS_OOM(m)) return; +}) + +#undef HistogramType diff --git a/packages/runtime/vendor/brotli/c/enc/command.c b/packages/runtime/vendor/brotli/c/enc/command.c new file mode 100644 index 000000000..699fa8098 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/command.c @@ -0,0 +1,28 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "command.h" + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +const uint32_t kBrotliInsBase[BROTLI_NUM_INS_COPY_CODES] = { + 0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, + 34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114, 6210, 22594}; +const uint32_t kBrotliInsExtra[BROTLI_NUM_INS_COPY_CODES] = { + 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 12, 14, 24}; +const uint32_t kBrotliCopyBase[BROTLI_NUM_INS_COPY_CODES] = { + 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 18, + 22, 30, 38, 54, 70, 102, 134, 198, 326, 582, 1094, 2118}; +const uint32_t kBrotliCopyExtra[BROTLI_NUM_INS_COPY_CODES] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24}; + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/command.h b/packages/runtime/vendor/brotli/c/enc/command.h new file mode 100644 index 000000000..c1cb208e6 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/command.h @@ -0,0 +1,189 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* This class models a sequence of literals and a backward reference copy. */ + +#ifndef BROTLI_ENC_COMMAND_H_ +#define BROTLI_ENC_COMMAND_H_ + +#include "../common/constants.h" +#include "../common/platform.h" +#include "fast_log.h" +#include "params.h" +#include "prefix.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +BROTLI_INTERNAL extern const BROTLI_MODEL("small") +uint32_t kBrotliInsBase[BROTLI_NUM_INS_COPY_CODES]; +BROTLI_INTERNAL extern const BROTLI_MODEL("small") +uint32_t kBrotliInsExtra[BROTLI_NUM_INS_COPY_CODES]; +BROTLI_INTERNAL extern const BROTLI_MODEL("small") +uint32_t kBrotliCopyBase[BROTLI_NUM_INS_COPY_CODES]; +BROTLI_INTERNAL extern const BROTLI_MODEL("small") +uint32_t kBrotliCopyExtra[BROTLI_NUM_INS_COPY_CODES]; + +static BROTLI_INLINE uint16_t GetInsertLengthCode(size_t insertlen) { + if (insertlen < 6) { + return (uint16_t)insertlen; + } else if (insertlen < 130) { + uint32_t nbits = Log2FloorNonZero(insertlen - 2) - 1u; + return (uint16_t)((nbits << 1) + ((insertlen - 2) >> nbits) + 2); + } else if (insertlen < 2114) { + return (uint16_t)(Log2FloorNonZero(insertlen - 66) + 10); + } else if (insertlen < 6210) { + return 21u; + } else if (insertlen < 22594) { + return 22u; + } else { + return 23u; + } +} + +static BROTLI_INLINE uint16_t GetCopyLengthCode(size_t copylen) { + if (copylen < 10) { + return (uint16_t)(copylen - 2); + } else if (copylen < 134) { + uint32_t nbits = Log2FloorNonZero(copylen - 6) - 1u; + return (uint16_t)((nbits << 1) + ((copylen - 6) >> nbits) + 4); + } else if (copylen < 2118) { + return (uint16_t)(Log2FloorNonZero(copylen - 70) + 12); + } else { + return 23u; + } +} + +static BROTLI_INLINE uint16_t CombineLengthCodes( + uint16_t inscode, uint16_t copycode, BROTLI_BOOL use_last_distance) { + uint16_t bits64 = + (uint16_t)((copycode & 0x7u) | ((inscode & 0x7u) << 3u)); + if (use_last_distance && inscode < 8u && copycode < 16u) { + return (copycode < 8u) ? bits64 : (bits64 | 64u); + } else { + /* Specification: 5 Encoding of ... (last table) */ + /* offset = 2 * index, where index is in range [0..8] */ + uint32_t offset = 2u * ((copycode >> 3u) + 3u * (inscode >> 3u)); + /* All values in specification are K * 64, + where K = [2, 3, 6, 4, 5, 8, 7, 9, 10], + i + 1 = [1, 2, 3, 4, 5, 6, 7, 8, 9], + K - i - 1 = [1, 1, 3, 0, 0, 2, 0, 1, 2] = D. + All values in D require only 2 bits to encode. + Magic constant is shifted 6 bits left, to avoid final multiplication. */ + offset = (offset << 5u) + 0x40u + ((0x520D40u >> offset) & 0xC0u); + return (uint16_t)(offset | bits64); + } +} + +static BROTLI_INLINE void GetLengthCode(size_t insertlen, size_t copylen, + BROTLI_BOOL use_last_distance, + uint16_t* code) { + uint16_t inscode = GetInsertLengthCode(insertlen); + uint16_t copycode = GetCopyLengthCode(copylen); + *code = CombineLengthCodes(inscode, copycode, use_last_distance); +} + +static BROTLI_INLINE uint32_t GetInsertBase(uint16_t inscode) { + return kBrotliInsBase[inscode]; +} + +static BROTLI_INLINE uint32_t GetInsertExtra(uint16_t inscode) { + return kBrotliInsExtra[inscode]; +} + +static BROTLI_INLINE uint32_t GetCopyBase(uint16_t copycode) { + return kBrotliCopyBase[copycode]; +} + +static BROTLI_INLINE uint32_t GetCopyExtra(uint16_t copycode) { + return kBrotliCopyExtra[copycode]; +} + +typedef struct Command { + uint32_t insert_len_; + /* Stores copy_len in low 25 bits and copy_code - copy_len in high 7 bit. */ + uint32_t copy_len_; + /* Stores distance extra bits. */ + uint32_t dist_extra_; + uint16_t cmd_prefix_; + /* Stores distance code in low 10 bits + and number of extra bits in high 6 bits. */ + uint16_t dist_prefix_; +} Command; + +/* distance_code is e.g. 0 for same-as-last short code, or 16 for offset 1. */ +static BROTLI_INLINE void InitCommand(Command* self, + const BrotliDistanceParams* dist, size_t insertlen, + size_t copylen, int copylen_code_delta, size_t distance_code) { + /* Don't rely on signed int representation, use honest casts. */ + uint32_t delta = (uint8_t)((int8_t)copylen_code_delta); + self->insert_len_ = (uint32_t)insertlen; + self->copy_len_ = (uint32_t)(copylen | (delta << 25)); + /* The distance prefix and extra bits are stored in this Command as if + npostfix and ndirect were 0, they are only recomputed later after the + clustering if needed. */ + PrefixEncodeCopyDistance( + distance_code, dist->num_direct_distance_codes, + dist->distance_postfix_bits, &self->dist_prefix_, &self->dist_extra_); + GetLengthCode( + insertlen, (size_t)((int)copylen + copylen_code_delta), + TO_BROTLI_BOOL((self->dist_prefix_ & 0x3FF) == 0), &self->cmd_prefix_); +} + +static BROTLI_INLINE void InitInsertCommand(Command* self, size_t insertlen) { + self->insert_len_ = (uint32_t)insertlen; + self->copy_len_ = 4 << 25; + self->dist_extra_ = 0; + self->dist_prefix_ = BROTLI_NUM_DISTANCE_SHORT_CODES; + GetLengthCode(insertlen, 4, BROTLI_FALSE, &self->cmd_prefix_); +} + +static BROTLI_INLINE uint32_t CommandRestoreDistanceCode( + const Command* self, const BrotliDistanceParams* dist) { + if ((self->dist_prefix_ & 0x3FFu) < + BROTLI_NUM_DISTANCE_SHORT_CODES + dist->num_direct_distance_codes) { + return self->dist_prefix_ & 0x3FFu; + } else { + uint32_t dcode = self->dist_prefix_ & 0x3FFu; + uint32_t nbits = self->dist_prefix_ >> 10; + uint32_t extra = self->dist_extra_; + uint32_t postfix_mask = (1U << dist->distance_postfix_bits) - 1U; + uint32_t hcode = (dcode - dist->num_direct_distance_codes - + BROTLI_NUM_DISTANCE_SHORT_CODES) >> + dist->distance_postfix_bits; + uint32_t lcode = (dcode - dist->num_direct_distance_codes - + BROTLI_NUM_DISTANCE_SHORT_CODES) & postfix_mask; + uint32_t offset = ((2U + (hcode & 1U)) << nbits) - 4U; + return ((offset + extra) << dist->distance_postfix_bits) + lcode + + dist->num_direct_distance_codes + BROTLI_NUM_DISTANCE_SHORT_CODES; + } +} + +static BROTLI_INLINE uint32_t CommandDistanceContext(const Command* self) { + uint32_t r = self->cmd_prefix_ >> 6; + uint32_t c = self->cmd_prefix_ & 7; + if ((r == 0 || r == 2 || r == 4 || r == 7) && (c <= 2)) { + return c; + } + return 3; +} + +static BROTLI_INLINE uint32_t CommandCopyLen(const Command* self) { + return self->copy_len_ & 0x1FFFFFF; +} + +static BROTLI_INLINE uint32_t CommandCopyLenCode(const Command* self) { + uint32_t modifier = self->copy_len_ >> 25; + int32_t delta = (int8_t)((uint8_t)(modifier | ((modifier & 0x40) << 1))); + return (uint32_t)((int32_t)(self->copy_len_ & 0x1FFFFFF) + delta); +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_COMMAND_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/compound_dictionary.c b/packages/runtime/vendor/brotli/c/enc/compound_dictionary.c new file mode 100644 index 000000000..bf323a4b3 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/compound_dictionary.c @@ -0,0 +1,205 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "compound_dictionary.h" + +#include "../common/platform.h" +#include +#include "memory.h" + +static PreparedDictionary* CreatePreparedDictionaryWithParams(MemoryManager* m, + const uint8_t* source, size_t source_size, uint32_t bucket_bits, + uint32_t slot_bits, uint32_t hash_bits, uint16_t bucket_limit) { + /* Step 1: create "bloated" hasher. */ + uint32_t num_slots = 1u << slot_bits; + uint32_t num_buckets = 1u << bucket_bits; + uint32_t hash_shift = 64u - bucket_bits; + uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits); + uint32_t slot_mask = num_slots - 1; + size_t alloc_size = (sizeof(uint32_t) << slot_bits) + + (sizeof(uint32_t) << slot_bits) + + (sizeof(uint16_t) << bucket_bits) + + (sizeof(uint32_t) << bucket_bits) + + (sizeof(uint32_t) * source_size); + uint8_t* flat = NULL; + PreparedDictionary* result = NULL; + uint16_t* num = NULL; + uint32_t* bucket_heads = NULL; + uint32_t* next_bucket = NULL; + uint32_t* slot_offsets = NULL; + uint16_t* heads = NULL; + uint32_t* items = NULL; + uint8_t** source_ref = NULL; + uint32_t i; + uint32_t* slot_size = NULL; + uint32_t* slot_limit = NULL; + uint32_t total_items = 0; + if (slot_bits > 16) return NULL; + if (slot_bits > bucket_bits) return NULL; + if (bucket_bits - slot_bits >= 16) return NULL; + + flat = BROTLI_ALLOC(m, uint8_t, alloc_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(flat)) return NULL; + + slot_size = (uint32_t*)flat; + slot_limit = (uint32_t*)(&slot_size[num_slots]); + num = (uint16_t*)(&slot_limit[num_slots]); + bucket_heads = (uint32_t*)(&num[num_buckets]); + next_bucket = (uint32_t*)(&bucket_heads[num_buckets]); + memset(num, 0, num_buckets * sizeof(num[0])); + + /* TODO(eustas): apply custom "store" order. */ + for (i = 0; i + 7 < source_size; ++i) { + const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(&source[i]) & hash_mask) * + kPreparedDictionaryHashMul64Long; + const uint32_t key = (uint32_t)(h >> hash_shift); + uint16_t count = num[key]; + next_bucket[i] = (count == 0) ? ((uint32_t)(-1)) : bucket_heads[key]; + bucket_heads[key] = i; + count++; + if (count > bucket_limit) count = bucket_limit; + num[key] = count; + } + + /* Step 2: find slot limits. */ + for (i = 0; i < num_slots; ++i) { + BROTLI_BOOL overflow = BROTLI_FALSE; + slot_limit[i] = bucket_limit; + while (BROTLI_TRUE) { + uint32_t limit = slot_limit[i]; + size_t j; + uint32_t count = 0; + overflow = BROTLI_FALSE; + for (j = i; j < num_buckets; j += num_slots) { + uint32_t size = num[j]; + /* Last chain may span behind 64K limit; overflow happens only if + we are about to use 0xFFFF+ as item offset. */ + if (count >= 0xFFFF) { + overflow = BROTLI_TRUE; + break; + } + if (size > limit) size = limit; + count += size; + } + if (!overflow) { + slot_size[i] = count; + total_items += count; + break; + } + slot_limit[i]--; + } + } + + /* Step 3: transfer data to "slim" hasher. */ + alloc_size = sizeof(PreparedDictionary) + (sizeof(uint32_t) << slot_bits) + + (sizeof(uint16_t) << bucket_bits) + (sizeof(uint32_t) * total_items) + + sizeof(uint8_t*); + + result = (PreparedDictionary*)BROTLI_ALLOC(m, uint8_t, alloc_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(result)) { + BROTLI_FREE(m, flat); + return NULL; + } + slot_offsets = (uint32_t*)(&result[1]); + heads = (uint16_t*)(&slot_offsets[num_slots]); + items = (uint32_t*)(&heads[num_buckets]); + source_ref = (uint8_t**)(&items[total_items]); + + result->magic = kLeanPreparedDictionaryMagic; + result->num_items = total_items; + result->source_size = (uint32_t)source_size; + result->hash_bits = hash_bits; + result->bucket_bits = bucket_bits; + result->slot_bits = slot_bits; + BROTLI_UNALIGNED_STORE_PTR(source_ref, source); + + total_items = 0; + for (i = 0; i < num_slots; ++i) { + slot_offsets[i] = total_items; + total_items += slot_size[i]; + slot_size[i] = 0; + } + for (i = 0; i < num_buckets; ++i) { + uint32_t slot = i & slot_mask; + uint32_t count = num[i]; + uint32_t pos; + size_t j; + size_t cursor = slot_size[slot]; + if (count > slot_limit[slot]) count = slot_limit[slot]; + if (count == 0) { + heads[i] = 0xFFFF; + continue; + } + heads[i] = (uint16_t)cursor; + cursor += slot_offsets[slot]; + slot_size[slot] += count; + pos = bucket_heads[i]; + for (j = 0; j < count; j++) { + items[cursor++] = pos; + pos = next_bucket[pos]; + } + items[cursor - 1] |= 0x80000000; + } + + BROTLI_FREE(m, flat); + return result; +} + +PreparedDictionary* CreatePreparedDictionary(MemoryManager* m, + const uint8_t* source, size_t source_size) { + uint32_t bucket_bits = 17; + uint32_t slot_bits = 7; + uint32_t hash_bits = 40; + uint16_t bucket_limit = 32; + size_t volume = 16u << bucket_bits; + /* Tune parameters to fit dictionary size. */ + while (volume < source_size && bucket_bits < 22) { + bucket_bits++; + slot_bits++; + volume <<= 1; + } + return CreatePreparedDictionaryWithParams(m, + source, source_size, bucket_bits, slot_bits, hash_bits, bucket_limit); +} + +void DestroyPreparedDictionary(MemoryManager* m, + PreparedDictionary* dictionary) { + if (!dictionary) return; + BROTLI_FREE(m, dictionary); +} + +BROTLI_BOOL AttachPreparedDictionary( + CompoundDictionary* compound, const PreparedDictionary* dictionary) { + size_t length = 0; + size_t index = 0; + + if (compound->num_chunks == SHARED_BROTLI_MAX_COMPOUND_DICTS) { + return BROTLI_FALSE; + } + + if (!dictionary) return BROTLI_FALSE; + + length = dictionary->source_size; + index = compound->num_chunks; + compound->total_size += length; + compound->chunks[index] = dictionary; + compound->chunk_offsets[index + 1] = compound->total_size; + { + uint32_t* slot_offsets = (uint32_t*)(&dictionary[1]); + uint16_t* heads = (uint16_t*)(&slot_offsets[(size_t)1u << dictionary->slot_bits]); + uint32_t* items = (uint32_t*)(&heads[(size_t)1u << dictionary->bucket_bits]); + const void* tail = (void*)&items[dictionary->num_items]; + if (dictionary->magic == kPreparedDictionaryMagic) { + compound->chunk_source[index] = (const uint8_t*)tail; + } else { + /* dictionary->magic == kLeanPreparedDictionaryMagic */ + compound->chunk_source[index] = + (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail); + } + } + compound->num_chunks++; + return BROTLI_TRUE; +} diff --git a/packages/runtime/vendor/brotli/c/enc/compound_dictionary.h b/packages/runtime/vendor/brotli/c/enc/compound_dictionary.h new file mode 100644 index 000000000..686f92fda --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/compound_dictionary.h @@ -0,0 +1,71 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#ifndef BROTLI_ENC_PREPARED_DICTIONARY_H_ +#define BROTLI_ENC_PREPARED_DICTIONARY_H_ + +#include "../common/platform.h" +#include +#include "memory.h" + +/* "Fat" prepared dictionary, could be cooked outside of C implementation, + * e.g. on Java side. LZ77 data is copied inside PreparedDictionary struct. */ +static const uint32_t kPreparedDictionaryMagic = 0xDEBCEDE0; + +static const uint32_t kSharedDictionaryMagic = 0xDEBCEDE1; + +static const uint32_t kManagedDictionaryMagic = 0xDEBCEDE2; + +/* "Lean" prepared dictionary. LZ77 data is referenced. It is the responsibility + * of caller of "prepare dictionary" to keep the LZ77 data while prepared + * dictionary is in use. */ +static const uint32_t kLeanPreparedDictionaryMagic = 0xDEBCEDE3; + +static const uint64_t kPreparedDictionaryHashMul64Long = + BROTLI_MAKE_UINT64_T(0x1FE35A7Bu, 0xD3579BD3u); + +typedef struct PreparedDictionary { + uint32_t magic; + uint32_t num_items; + uint32_t source_size; + uint32_t hash_bits; + uint32_t bucket_bits; + uint32_t slot_bits; + + /* --- Dynamic size members --- */ + + /* uint32_t slot_offsets[1 << slot_bits]; */ + /* uint16_t heads[1 << bucket_bits]; */ + /* uint32_t items[variable]; */ + + /* [maybe] uint8_t* source_ref, depending on magic. */ + /* [maybe] uint8_t source[source_size], depending on magic. */ +} PreparedDictionary; + +BROTLI_INTERNAL PreparedDictionary* CreatePreparedDictionary(MemoryManager* m, + const uint8_t* source, size_t source_size); + +BROTLI_INTERNAL void DestroyPreparedDictionary(MemoryManager* m, + PreparedDictionary* dictionary); + +typedef struct CompoundDictionary { + /* LZ77 prefix, compound dictionary */ + size_t num_chunks; + size_t total_size; + /* Client instances. */ + const PreparedDictionary* chunks[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; + const uint8_t* chunk_source[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; + size_t chunk_offsets[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; + + size_t num_prepared_instances_; + /* Owned instances. */ + PreparedDictionary* prepared_instances_[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1]; +} CompoundDictionary; + +BROTLI_INTERNAL BROTLI_BOOL AttachPreparedDictionary( + CompoundDictionary* compound, const PreparedDictionary* dictionary); + +#endif /* BROTLI_ENC_PREPARED_DICTIONARY */ diff --git a/packages/runtime/vendor/brotli/c/enc/compress_fragment.c b/packages/runtime/vendor/brotli/c/enc/compress_fragment.c new file mode 100644 index 000000000..14fec4fbf --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/compress_fragment.c @@ -0,0 +1,790 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function for fast encoding of an input fragment, independently from the input + history. This function uses one-pass processing: when we find a backward + match, we immediately emit the corresponding command and literal codes to + the bit stream. + + Adapted from the CompressFragment() function in + https://github.com/google/snappy/blob/master/snappy.cc */ + +#include "compress_fragment.h" + +#include "../common/constants.h" +#include "../common/platform.h" +#include "brotli_bit_stream.h" +#include "entropy_encode.h" +#include "fast_log.h" +#include "find_match_length.h" +#include "hash_base.h" +#include "write_bits.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define MAX_DISTANCE (long)BROTLI_MAX_BACKWARD_LIMIT(18) + +static BROTLI_INLINE uint32_t Hash(const uint8_t* p, size_t shift) { + const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(p) << 24) * kHashMul32; + return (uint32_t)(h >> shift); +} + +static BROTLI_INLINE uint32_t HashBytesAtOffset( + uint64_t v, int offset, size_t shift) { + BROTLI_DCHECK(offset >= 0); + BROTLI_DCHECK(offset <= 3); + { + const uint64_t h = ((v >> (8 * offset)) << 24) * kHashMul32; + return (uint32_t)(h >> shift); + } +} + +static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2) { + return TO_BROTLI_BOOL( + BrotliUnalignedRead32(p1) == BrotliUnalignedRead32(p2) && + p1[4] == p2[4]); +} + +/* Builds a literal prefix code into "depths" and "bits" based on the statistics + of the "input" string and stores it into the bit stream. + Note that the prefix code here is built from the pre-LZ77 input, therefore + we can only approximate the statistics of the actual literal stream. + Moreover, for long inputs we build a histogram from a sample of the input + and thus have to assign a non-zero depth for each literal. + Returns estimated compression ratio millibytes/char for encoding given input + with generated code. */ +static size_t BuildAndStoreLiteralPrefixCode(BrotliOnePassArena* s, + const uint8_t* input, + const size_t input_size, + uint8_t depths[256], + uint16_t bits[256], + size_t* storage_ix, + uint8_t* storage) { + uint32_t* BROTLI_RESTRICT const histogram = s->histogram; + size_t histogram_total; + size_t i; + memset(histogram, 0, sizeof(s->histogram)); + + if (input_size < (1 << 15)) { + for (i = 0; i < input_size; ++i) { + ++histogram[input[i]]; + } + histogram_total = input_size; + for (i = 0; i < 256; ++i) { + /* We weigh the first 11 samples with weight 3 to account for the + balancing effect of the LZ77 phase on the histogram. */ + const uint32_t adjust = 2 * BROTLI_MIN(uint32_t, histogram[i], 11u); + histogram[i] += adjust; + histogram_total += adjust; + } + } else { + static const size_t kSampleRate = 29; + for (i = 0; i < input_size; i += kSampleRate) { + ++histogram[input[i]]; + } + histogram_total = (input_size + kSampleRate - 1) / kSampleRate; + for (i = 0; i < 256; ++i) { + /* We add 1 to each population count to avoid 0 bit depths (since this is + only a sample and we don't know if the symbol appears or not), and we + weigh the first 11 samples with weight 3 to account for the balancing + effect of the LZ77 phase on the histogram (more frequent symbols are + more likely to be in backward references instead as literals). */ + const uint32_t adjust = 1 + 2 * BROTLI_MIN(uint32_t, histogram[i], 11u); + histogram[i] += adjust; + histogram_total += adjust; + } + } + BrotliBuildAndStoreHuffmanTreeFast(s->tree, histogram, histogram_total, + /* max_bits = */ 8, + depths, bits, storage_ix, storage); + { + size_t literal_ratio = 0; + for (i = 0; i < 256; ++i) { + if (histogram[i]) literal_ratio += histogram[i] * depths[i]; + } + /* Estimated encoding ratio, millibytes per symbol. */ + return (literal_ratio * 125) / histogram_total; + } +} + +/* Builds a command and distance prefix code (each 64 symbols) into "depth" and + "bits" based on "histogram" and stores it into the bit stream. */ +static void BuildAndStoreCommandPrefixCode(BrotliOnePassArena* s, + size_t* storage_ix, uint8_t* storage) { + const uint32_t* const histogram = s->cmd_histo; + uint8_t* const depth = s->cmd_depth; + uint16_t* const bits = s->cmd_bits; + uint8_t* BROTLI_RESTRICT const tmp_depth = s->tmp_depth; + uint16_t* BROTLI_RESTRICT const tmp_bits = s->tmp_bits; + /* TODO(eustas): do only once on initialization. */ + memset(tmp_depth, 0, BROTLI_NUM_COMMAND_SYMBOLS); + + BrotliCreateHuffmanTree(histogram, 64, 15, s->tree, depth); + BrotliCreateHuffmanTree(&histogram[64], 64, 14, s->tree, &depth[64]); + /* We have to jump through a few hoops here in order to compute + the command bits because the symbols are in a different order than in + the full alphabet. This looks complicated, but having the symbols + in this order in the command bits saves a few branches in the Emit* + functions. */ + memcpy(tmp_depth, depth, 24); + memcpy(tmp_depth + 24, depth + 40, 8); + memcpy(tmp_depth + 32, depth + 24, 8); + memcpy(tmp_depth + 40, depth + 48, 8); + memcpy(tmp_depth + 48, depth + 32, 8); + memcpy(tmp_depth + 56, depth + 56, 8); + BrotliConvertBitDepthsToSymbols(tmp_depth, 64, tmp_bits); + memcpy(bits, tmp_bits, 48); + memcpy(bits + 24, tmp_bits + 32, 16); + memcpy(bits + 32, tmp_bits + 48, 16); + memcpy(bits + 40, tmp_bits + 24, 16); + memcpy(bits + 48, tmp_bits + 40, 16); + memcpy(bits + 56, tmp_bits + 56, 16); + BrotliConvertBitDepthsToSymbols(&depth[64], 64, &bits[64]); + { + /* Create the bit length array for the full command alphabet. */ + size_t i; + memset(tmp_depth, 0, 64); /* only 64 first values were used */ + memcpy(tmp_depth, depth, 8); + memcpy(tmp_depth + 64, depth + 8, 8); + memcpy(tmp_depth + 128, depth + 16, 8); + memcpy(tmp_depth + 192, depth + 24, 8); + memcpy(tmp_depth + 384, depth + 32, 8); + for (i = 0; i < 8; ++i) { + tmp_depth[128 + 8 * i] = depth[40 + i]; + tmp_depth[256 + 8 * i] = depth[48 + i]; + tmp_depth[448 + 8 * i] = depth[56 + i]; + } + /* TODO(eustas): could/should full-length machinery be avoided? */ + BrotliStoreHuffmanTree( + tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS, s->tree, storage_ix, storage); + } + BrotliStoreHuffmanTree(&depth[64], 64, s->tree, storage_ix, storage); +} + +/* REQUIRES: insertlen < 6210 */ +static BROTLI_INLINE void EmitInsertLen(size_t insertlen, + const uint8_t depth[128], + const uint16_t bits[128], + uint32_t histo[128], + size_t* storage_ix, + uint8_t* storage) { + if (insertlen < 6) { + const size_t code = insertlen + 40; + BrotliWriteBits(depth[code], bits[code], storage_ix, storage); + ++histo[code]; + } else if (insertlen < 130) { + const size_t tail = insertlen - 2; + const uint32_t nbits = Log2FloorNonZero(tail) - 1u; + const size_t prefix = tail >> nbits; + const size_t inscode = (nbits << 1) + prefix + 42; + BrotliWriteBits(depth[inscode], bits[inscode], storage_ix, storage); + BrotliWriteBits(nbits, tail - (prefix << nbits), storage_ix, storage); + ++histo[inscode]; + } else if (insertlen < 2114) { + const size_t tail = insertlen - 66; + const uint32_t nbits = Log2FloorNonZero(tail); + const size_t code = nbits + 50; + BrotliWriteBits(depth[code], bits[code], storage_ix, storage); + BrotliWriteBits(nbits, tail - ((size_t)1 << nbits), storage_ix, storage); + ++histo[code]; + } else { + BrotliWriteBits(depth[61], bits[61], storage_ix, storage); + BrotliWriteBits(12, insertlen - 2114, storage_ix, storage); + ++histo[61]; + } +} + +static BROTLI_INLINE void EmitLongInsertLen(size_t insertlen, + const uint8_t depth[128], + const uint16_t bits[128], + uint32_t histo[128], + size_t* storage_ix, + uint8_t* storage) { + if (insertlen < 22594) { + BrotliWriteBits(depth[62], bits[62], storage_ix, storage); + BrotliWriteBits(14, insertlen - 6210, storage_ix, storage); + ++histo[62]; + } else { + BrotliWriteBits(depth[63], bits[63], storage_ix, storage); + BrotliWriteBits(24, insertlen - 22594, storage_ix, storage); + ++histo[63]; + } +} + +static BROTLI_INLINE void EmitCopyLen(size_t copylen, + const uint8_t depth[128], + const uint16_t bits[128], + uint32_t histo[128], + size_t* storage_ix, + uint8_t* storage) { + if (copylen < 10) { + BrotliWriteBits( + depth[copylen + 14], bits[copylen + 14], storage_ix, storage); + ++histo[copylen + 14]; + } else if (copylen < 134) { + const size_t tail = copylen - 6; + const uint32_t nbits = Log2FloorNonZero(tail) - 1u; + const size_t prefix = tail >> nbits; + const size_t code = (nbits << 1) + prefix + 20; + BrotliWriteBits(depth[code], bits[code], storage_ix, storage); + BrotliWriteBits(nbits, tail - (prefix << nbits), storage_ix, storage); + ++histo[code]; + } else if (copylen < 2118) { + const size_t tail = copylen - 70; + const uint32_t nbits = Log2FloorNonZero(tail); + const size_t code = nbits + 28; + BrotliWriteBits(depth[code], bits[code], storage_ix, storage); + BrotliWriteBits(nbits, tail - ((size_t)1 << nbits), storage_ix, storage); + ++histo[code]; + } else { + BrotliWriteBits(depth[39], bits[39], storage_ix, storage); + BrotliWriteBits(24, copylen - 2118, storage_ix, storage); + ++histo[39]; + } +} + +static BROTLI_INLINE void EmitCopyLenLastDistance(size_t copylen, + const uint8_t depth[128], + const uint16_t bits[128], + uint32_t histo[128], + size_t* storage_ix, + uint8_t* storage) { + if (copylen < 12) { + BrotliWriteBits(depth[copylen - 4], bits[copylen - 4], storage_ix, storage); + ++histo[copylen - 4]; + } else if (copylen < 72) { + const size_t tail = copylen - 8; + const uint32_t nbits = Log2FloorNonZero(tail) - 1; + const size_t prefix = tail >> nbits; + const size_t code = (nbits << 1) + prefix + 4; + BrotliWriteBits(depth[code], bits[code], storage_ix, storage); + BrotliWriteBits(nbits, tail - (prefix << nbits), storage_ix, storage); + ++histo[code]; + } else if (copylen < 136) { + const size_t tail = copylen - 8; + const size_t code = (tail >> 5) + 30; + BrotliWriteBits(depth[code], bits[code], storage_ix, storage); + BrotliWriteBits(5, tail & 31, storage_ix, storage); + BrotliWriteBits(depth[64], bits[64], storage_ix, storage); + ++histo[code]; + ++histo[64]; + } else if (copylen < 2120) { + const size_t tail = copylen - 72; + const uint32_t nbits = Log2FloorNonZero(tail); + const size_t code = nbits + 28; + BrotliWriteBits(depth[code], bits[code], storage_ix, storage); + BrotliWriteBits(nbits, tail - ((size_t)1 << nbits), storage_ix, storage); + BrotliWriteBits(depth[64], bits[64], storage_ix, storage); + ++histo[code]; + ++histo[64]; + } else { + BrotliWriteBits(depth[39], bits[39], storage_ix, storage); + BrotliWriteBits(24, copylen - 2120, storage_ix, storage); + BrotliWriteBits(depth[64], bits[64], storage_ix, storage); + ++histo[39]; + ++histo[64]; + } +} + +static BROTLI_INLINE void EmitDistance(size_t distance, + const uint8_t depth[128], + const uint16_t bits[128], + uint32_t histo[128], + size_t* storage_ix, uint8_t* storage) { + const size_t d = distance + 3; + const uint32_t nbits = Log2FloorNonZero(d) - 1u; + const size_t prefix = (d >> nbits) & 1; + const size_t offset = (2 + prefix) << nbits; + const size_t distcode = 2 * (nbits - 1) + prefix + 80; + BrotliWriteBits(depth[distcode], bits[distcode], storage_ix, storage); + BrotliWriteBits(nbits, d - offset, storage_ix, storage); + ++histo[distcode]; +} + +static BROTLI_INLINE void EmitLiterals(const uint8_t* input, const size_t len, + const uint8_t depth[256], + const uint16_t bits[256], + size_t* storage_ix, uint8_t* storage) { + size_t j; + for (j = 0; j < len; j++) { + const uint8_t lit = input[j]; + BrotliWriteBits(depth[lit], bits[lit], storage_ix, storage); + } +} + +/* REQUIRES: len <= 1 << 24. */ +static void BrotliStoreMetaBlockHeader( + size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix, + uint8_t* storage) { + size_t nibbles = 6; + /* ISLAST */ + BrotliWriteBits(1, 0, storage_ix, storage); + if (len <= (1U << 16)) { + nibbles = 4; + } else if (len <= (1U << 20)) { + nibbles = 5; + } + BrotliWriteBits(2, nibbles - 4, storage_ix, storage); + BrotliWriteBits(nibbles * 4, len - 1, storage_ix, storage); + /* ISUNCOMPRESSED */ + BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage); +} + +static void UpdateBits(size_t n_bits, uint32_t bits, size_t pos, + uint8_t* array) { + while (n_bits > 0) { + size_t byte_pos = pos >> 3; + size_t n_unchanged_bits = pos & 7; + size_t n_changed_bits = BROTLI_MIN(size_t, n_bits, 8 - n_unchanged_bits); + size_t total_bits = n_unchanged_bits + n_changed_bits; + uint32_t mask = + (~((1u << total_bits) - 1u)) | ((1u << n_unchanged_bits) - 1u); + uint32_t unchanged_bits = array[byte_pos] & mask; + uint32_t changed_bits = bits & ((1u << n_changed_bits) - 1u); + array[byte_pos] = + (uint8_t)((changed_bits << n_unchanged_bits) | unchanged_bits); + n_bits -= n_changed_bits; + bits >>= n_changed_bits; + pos += n_changed_bits; + } +} + +static void RewindBitPosition(const size_t new_storage_ix, + size_t* storage_ix, uint8_t* storage) { + const size_t bitpos = new_storage_ix & 7; + const size_t mask = (1u << bitpos) - 1; + storage[new_storage_ix >> 3] &= (uint8_t)mask; + *storage_ix = new_storage_ix; +} + +static BROTLI_BOOL ShouldMergeBlock(BrotliOnePassArena* s, + const uint8_t* data, size_t len, const uint8_t* depths) { + uint32_t* BROTLI_RESTRICT const histo = s->histogram; + static const size_t kSampleRate = 43; + size_t i; + memset(histo, 0, sizeof(s->histogram)); + for (i = 0; i < len; i += kSampleRate) { + ++histo[data[i]]; + } + { + const size_t total = (len + kSampleRate - 1) / kSampleRate; + double r = (FastLog2(total) + 0.5) * (double)total + 200; + for (i = 0; i < 256; ++i) { + r -= (double)histo[i] * (depths[i] + FastLog2(histo[i])); + } + return TO_BROTLI_BOOL(r >= 0.0); + } +} + +/* Acceptable loss for uncompressible speedup is 2% */ +#define MIN_RATIO 980 + +static BROTLI_INLINE BROTLI_BOOL ShouldUseUncompressedMode( + const uint8_t* metablock_start, const uint8_t* next_emit, + const size_t insertlen, const size_t literal_ratio) { + const size_t compressed = (size_t)(next_emit - metablock_start); + if (compressed * 50 > insertlen) { + return BROTLI_FALSE; + } else { + return TO_BROTLI_BOOL(literal_ratio > MIN_RATIO); + } +} + +static void EmitUncompressedMetaBlock(const uint8_t* begin, const uint8_t* end, + const size_t storage_ix_start, + size_t* storage_ix, uint8_t* storage) { + const size_t len = (size_t)(end - begin); + RewindBitPosition(storage_ix_start, storage_ix, storage); + BrotliStoreMetaBlockHeader(len, 1, storage_ix, storage); + *storage_ix = (*storage_ix + 7u) & ~7u; + memcpy(&storage[*storage_ix >> 3], begin, len); + *storage_ix += len << 3; + storage[*storage_ix >> 3] = 0; +} + +static BROTLI_MODEL("small") uint32_t kCmdHistoSeed[128] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, +}; + +static BROTLI_INLINE void BrotliCompressFragmentFastImpl( + BrotliOnePassArena* s, const uint8_t* input, size_t input_size, + BROTLI_BOOL is_last, int* table, size_t table_bits, + size_t* storage_ix, uint8_t* storage) { + uint8_t* BROTLI_RESTRICT const cmd_depth = s->cmd_depth; + uint16_t* BROTLI_RESTRICT const cmd_bits = s->cmd_bits; + uint32_t* BROTLI_RESTRICT const cmd_histo = s->cmd_histo; + uint8_t* BROTLI_RESTRICT const lit_depth = s->lit_depth; + uint16_t* BROTLI_RESTRICT const lit_bits = s->lit_bits; + const uint8_t* ip_end; + + /* "next_emit" is a pointer to the first byte that is not covered by a + previous copy. Bytes between "next_emit" and the start of the next copy or + the end of the input will be emitted as literal bytes. */ + const uint8_t* next_emit = input; + /* Save the start of the first block for position and distance computations. + */ + const uint8_t* base_ip = input; + + static const size_t kFirstBlockSize = 3 << 15; + static const size_t kMergeBlockSize = 1 << 16; + + const size_t kInputMarginBytes = BROTLI_WINDOW_GAP; + const size_t kMinMatchLen = 5; + + const uint8_t* metablock_start = input; + size_t block_size = BROTLI_MIN(size_t, input_size, kFirstBlockSize); + size_t total_block_size = block_size; + /* Save the bit position of the MLEN field of the meta-block header, so that + we can update it later if we decide to extend this meta-block. */ + size_t mlen_storage_ix = *storage_ix + 3; + + size_t literal_ratio; + + const uint8_t* ip; + int last_distance; + + const size_t shift = 64u - table_bits; + + BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage); + /* No block splits, no contexts. */ + BrotliWriteBits(13, 0, storage_ix, storage); + + literal_ratio = BuildAndStoreLiteralPrefixCode( + s, input, block_size, s->lit_depth, s->lit_bits, storage_ix, storage); + + { + /* Store the pre-compressed command and distance prefix codes. */ + size_t i; + for (i = 0; i + 7 < s->cmd_code_numbits; i += 8) { + BrotliWriteBits(8, s->cmd_code[i >> 3], storage_ix, storage); + } + } + BrotliWriteBits(s->cmd_code_numbits & 7, + s->cmd_code[s->cmd_code_numbits >> 3], storage_ix, storage); + + emit_commands: + /* Initialize the command and distance histograms. We will gather + statistics of command and distance codes during the processing + of this block and use it to update the command and distance + prefix codes for the next block. */ + memcpy(s->cmd_histo, kCmdHistoSeed, sizeof(kCmdHistoSeed)); + + /* "ip" is the input pointer. */ + ip = input; + last_distance = -1; + ip_end = input + block_size; + + if (BROTLI_PREDICT_TRUE(block_size >= kInputMarginBytes)) { + /* For the last block, we need to keep a 16 bytes margin so that we can be + sure that all distances are at most window size - 16. + For all other blocks, we only need to keep a margin of 5 bytes so that + we don't go over the block size with a copy. */ + const size_t len_limit = BROTLI_MIN(size_t, block_size - kMinMatchLen, + input_size - kInputMarginBytes); + const uint8_t* ip_limit = input + len_limit; + + uint32_t next_hash; + for (next_hash = Hash(++ip, shift); ; ) { + /* Step 1: Scan forward in the input looking for a 5-byte-long match. + If we get close to exhausting the input then goto emit_remainder. + + Heuristic match skipping: If 32 bytes are scanned with no matches + found, start looking only at every other byte. If 32 more bytes are + scanned, look at every third byte, etc.. When a match is found, + immediately go back to looking at every byte. This is a small loss + (~5% performance, ~0.1% density) for compressible data due to more + bookkeeping, but for non-compressible data (such as JPEG) it's a huge + win since the compressor quickly "realizes" the data is incompressible + and doesn't bother looking for matches everywhere. + + The "skip" variable keeps track of how many bytes there are since the + last match; dividing it by 32 (i.e. right-shifting by five) gives the + number of bytes to move ahead for each iteration. */ + uint32_t skip = 32; + + const uint8_t* next_ip = ip; + const uint8_t* candidate; + BROTLI_DCHECK(next_emit < ip); +trawl: + do { + uint32_t hash = next_hash; + uint32_t bytes_between_hash_lookups = skip++ >> 5; + BROTLI_DCHECK(hash == Hash(next_ip, shift)); + ip = next_ip; + next_ip = ip + bytes_between_hash_lookups; + if (BROTLI_PREDICT_FALSE(next_ip > ip_limit)) { + goto emit_remainder; + } + next_hash = Hash(next_ip, shift); + candidate = ip - last_distance; + if (IsMatch(ip, candidate)) { + if (BROTLI_PREDICT_TRUE(candidate < ip)) { + table[hash] = (int)(ip - base_ip); + break; + } + } + candidate = base_ip + table[hash]; + BROTLI_DCHECK(candidate >= base_ip); + BROTLI_DCHECK(candidate < ip); + + table[hash] = (int)(ip - base_ip); + } while (BROTLI_PREDICT_TRUE(!IsMatch(ip, candidate))); + + /* Check copy distance. If candidate is not feasible, continue search. + Checking is done outside of hot loop to reduce overhead. */ + if (ip - candidate > MAX_DISTANCE) goto trawl; + + /* Step 2: Emit the found match together with the literal bytes from + "next_emit" to the bit stream, and then see if we can find a next match + immediately afterwards. Repeat until we find no match for the input + without emitting some literal bytes. */ + + { + /* We have a 5-byte match at ip, and we need to emit bytes in + [next_emit, ip). */ + const uint8_t* base = ip; + size_t matched = 5 + FindMatchLengthWithLimit( + candidate + 5, ip + 5, (size_t)(ip_end - ip) - 5); + int distance = (int)(base - candidate); /* > 0 */ + size_t insert = (size_t)(base - next_emit); + ip += matched; + BROTLI_LOG(("[CompressFragment] pos = %d insert = %lu copy = %d\n", + (int)(next_emit - base_ip), (unsigned long)insert, 2)); + BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); + if (BROTLI_PREDICT_TRUE(insert < 6210)) { + EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, + storage_ix, storage); + } else if (ShouldUseUncompressedMode(metablock_start, next_emit, insert, + literal_ratio)) { + EmitUncompressedMetaBlock(metablock_start, base, mlen_storage_ix - 3, + storage_ix, storage); + input_size -= (size_t)(base - input); + input = base; + next_emit = input; + goto next_block; + } else { + EmitLongInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, + storage_ix, storage); + } + EmitLiterals(next_emit, insert, lit_depth, lit_bits, + storage_ix, storage); + if (distance == last_distance) { + BrotliWriteBits(cmd_depth[64], cmd_bits[64], storage_ix, storage); + ++cmd_histo[64]; + } else { + EmitDistance((size_t)distance, cmd_depth, cmd_bits, + cmd_histo, storage_ix, storage); + last_distance = distance; + } + EmitCopyLenLastDistance(matched, cmd_depth, cmd_bits, cmd_histo, + storage_ix, storage); + BROTLI_LOG(("[CompressFragment] pos = %d distance = %d\n" + "[CompressFragment] pos = %d insert = %d copy = %d\n" + "[CompressFragment] pos = %d distance = %d\n", + (int)(base - base_ip), (int)distance, + (int)(base - base_ip) + 2, 0, (int)matched - 2, + (int)(base - base_ip) + 2, (int)distance)); + + next_emit = ip; + if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { + goto emit_remainder; + } + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some positions + within the last copy. */ + { + uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); + uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift); + uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift); + table[prev_hash] = (int)(ip - base_ip - 3); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift); + table[prev_hash] = (int)(ip - base_ip - 2); + prev_hash = HashBytesAtOffset(input_bytes, 2, shift); + table[prev_hash] = (int)(ip - base_ip - 1); + + candidate = base_ip + table[cur_hash]; + table[cur_hash] = (int)(ip - base_ip); + } + } + + while (IsMatch(ip, candidate)) { + /* We have a 5-byte match at ip, and no need to emit any literal bytes + prior to ip. */ + const uint8_t* base = ip; + size_t matched = 5 + FindMatchLengthWithLimit( + candidate + 5, ip + 5, (size_t)(ip_end - ip) - 5); + if (ip - candidate > MAX_DISTANCE) break; + ip += matched; + last_distance = (int)(base - candidate); /* > 0 */ + BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); + EmitCopyLen(matched, cmd_depth, cmd_bits, cmd_histo, + storage_ix, storage); + EmitDistance((size_t)last_distance, cmd_depth, cmd_bits, + cmd_histo, storage_ix, storage); + BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n" + "[CompressFragment] pos = %d distance = %d\n", + (int)(base - base_ip), 0, (int)matched, + (int)(base - base_ip), (int)last_distance)); + + next_emit = ip; + if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { + goto emit_remainder; + } + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some positions + within the last copy. */ + { + uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); + uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift); + uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift); + table[prev_hash] = (int)(ip - base_ip - 3); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift); + table[prev_hash] = (int)(ip - base_ip - 2); + prev_hash = HashBytesAtOffset(input_bytes, 2, shift); + table[prev_hash] = (int)(ip - base_ip - 1); + + candidate = base_ip + table[cur_hash]; + table[cur_hash] = (int)(ip - base_ip); + } + } + + next_hash = Hash(++ip, shift); + } + } + + emit_remainder: + BROTLI_DCHECK(next_emit <= ip_end); + input += block_size; + input_size -= block_size; + block_size = BROTLI_MIN(size_t, input_size, kMergeBlockSize); + + /* Decide if we want to continue this meta-block instead of emitting the + last insert-only command. */ + if (input_size > 0 && + total_block_size + block_size <= (1 << 20) && + ShouldMergeBlock(s, input, block_size, lit_depth)) { + BROTLI_DCHECK(total_block_size > (1 << 16)); + /* Update the size of the current meta-block and continue emitting commands. + We can do this because the current size and the new size both have 5 + nibbles. */ + total_block_size += block_size; + UpdateBits(20, (uint32_t)(total_block_size - 1), mlen_storage_ix, storage); + goto emit_commands; + } + + /* Emit the remaining bytes as literals. */ + if (next_emit < ip_end) { + const size_t insert = (size_t)(ip_end - next_emit); + BROTLI_LOG(("[CompressFragment] pos = %d insert = %lu copy = %d\n", + (int)(next_emit - base_ip), (unsigned long)insert, 2)); + if (BROTLI_PREDICT_TRUE(insert < 6210)) { + EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, + storage_ix, storage); + EmitLiterals(next_emit, insert, lit_depth, lit_bits, storage_ix, storage); + } else if (ShouldUseUncompressedMode(metablock_start, next_emit, insert, + literal_ratio)) { + EmitUncompressedMetaBlock(metablock_start, ip_end, mlen_storage_ix - 3, + storage_ix, storage); + } else { + EmitLongInsertLen(insert, cmd_depth, cmd_bits, cmd_histo, + storage_ix, storage); + EmitLiterals(next_emit, insert, lit_depth, lit_bits, + storage_ix, storage); + } + } + next_emit = ip_end; + +next_block: + /* If we have more data, write a new meta-block header and prefix codes and + then continue emitting commands. */ + if (input_size > 0) { + metablock_start = input; + block_size = BROTLI_MIN(size_t, input_size, kFirstBlockSize); + total_block_size = block_size; + /* Save the bit position of the MLEN field of the meta-block header, so that + we can update it later if we decide to extend this meta-block. */ + mlen_storage_ix = *storage_ix + 3; + BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage); + /* No block splits, no contexts. */ + BrotliWriteBits(13, 0, storage_ix, storage); + literal_ratio = BuildAndStoreLiteralPrefixCode( + s, input, block_size, lit_depth, lit_bits, storage_ix, storage); + BuildAndStoreCommandPrefixCode(s, storage_ix, storage); + goto emit_commands; + } + + if (!is_last) { + /* If this is not the last block, update the command and distance prefix + codes for the next block and store the compressed forms. */ + s->cmd_code[0] = 0; + s->cmd_code_numbits = 0; + BuildAndStoreCommandPrefixCode(s, &s->cmd_code_numbits, s->cmd_code); + } +} + +#define FOR_TABLE_BITS_(X) X(9) X(11) X(13) X(15) + +#define BAKE_METHOD_PARAM_(B) \ +static BROTLI_NOINLINE void BrotliCompressFragmentFastImpl ## B( \ + BrotliOnePassArena* s, const uint8_t* input, size_t input_size, \ + BROTLI_BOOL is_last, int* table, size_t* storage_ix, uint8_t* storage) { \ + BrotliCompressFragmentFastImpl(s, input, input_size, is_last, table, B, \ + storage_ix, storage); \ +} +FOR_TABLE_BITS_(BAKE_METHOD_PARAM_) +#undef BAKE_METHOD_PARAM_ + +void BrotliCompressFragmentFast( + BrotliOnePassArena* s, const uint8_t* input, size_t input_size, + BROTLI_BOOL is_last, int* table, size_t table_size, + size_t* storage_ix, uint8_t* storage) { + const size_t initial_storage_ix = *storage_ix; + const size_t table_bits = Log2FloorNonZero(table_size); + + if (input_size == 0) { + BROTLI_DCHECK(is_last); + BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ + BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ + *storage_ix = (*storage_ix + 7u) & ~7u; + return; + } + + switch (table_bits) { +#define CASE_(B) \ + case B: \ + BrotliCompressFragmentFastImpl ## B( \ + s, input, input_size, is_last, table, storage_ix, storage);\ + break; + FOR_TABLE_BITS_(CASE_) +#undef CASE_ + default: BROTLI_DCHECK(0); break; + } + + /* If output is larger than single uncompressed block, rewrite it. */ + if (*storage_ix - initial_storage_ix > 31 + (input_size << 3)) { + EmitUncompressedMetaBlock(input, input + input_size, initial_storage_ix, + storage_ix, storage); + } + + if (is_last) { + BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ + BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ + *storage_ix = (*storage_ix + 7u) & ~7u; + } +} + +#undef FOR_TABLE_BITS_ + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/compress_fragment.h b/packages/runtime/vendor/brotli/c/enc/compress_fragment.h new file mode 100644 index 000000000..462f366b3 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/compress_fragment.h @@ -0,0 +1,84 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function for fast encoding of an input fragment, independently from the input + history. This function uses one-pass processing: when we find a backward + match, we immediately emit the corresponding command and literal codes to + the bit stream. */ + +#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_H_ +#define BROTLI_ENC_COMPRESS_FRAGMENT_H_ + +#include "../common/constants.h" +#include "../common/platform.h" +#include "entropy_encode.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct BrotliOnePassArena { + uint8_t lit_depth[256]; + uint16_t lit_bits[256]; + + /* Command and distance prefix codes (each 64 symbols, stored back-to-back) + used for the next block. The command prefix code is over a smaller alphabet + with the following 64 symbols: + 0 - 15: insert length code 0, copy length code 0 - 15, same distance + 16 - 39: insert length code 0, copy length code 0 - 23 + 40 - 63: insert length code 0 - 23, copy length code 0 + Note that symbols 16 and 40 represent the same code in the full alphabet, + but we do not use either of them. */ + uint8_t cmd_depth[128]; + uint16_t cmd_bits[128]; + uint32_t cmd_histo[128]; + + /* The compressed form of the command and distance prefix codes for the next + block. */ + uint8_t cmd_code[512]; + size_t cmd_code_numbits; + + HuffmanTree tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1]; + uint32_t histogram[256]; + uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS]; + uint16_t tmp_bits[64]; +} BrotliOnePassArena; + +/* Compresses "input" string to the "*storage" buffer as one or more complete + meta-blocks, and updates the "*storage_ix" bit position. + + If "is_last" is 1, emits an additional empty last meta-block. + + "cmd_depth" and "cmd_bits" contain the command and distance prefix codes + (see comment in encode.h) used for the encoding of this input fragment. + If "is_last" is 0, they are updated to reflect the statistics + of this input fragment, to be used for the encoding of the next fragment. + + "*cmd_code_numbits" is the number of bits of the compressed representation + of the command and distance prefix codes, and "cmd_code" is an array of + at least "(*cmd_code_numbits + 7) >> 3" size that contains the compressed + command and distance prefix codes. If "is_last" is 0, these are also + updated to represent the updated "cmd_depth" and "cmd_bits". + + REQUIRES: "input_size" is greater than zero, or "is_last" is 1. + REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24). + REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. + REQUIRES: "table_size" is an odd (9, 11, 13, 15) power of two + OUTPUT: maximal copy distance <= |input_size| + OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ +BROTLI_INTERNAL void BrotliCompressFragmentFast(BrotliOnePassArena* s, + const uint8_t* input, + size_t input_size, + BROTLI_BOOL is_last, + int* table, size_t table_size, + size_t* storage_ix, + uint8_t* storage); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_COMPRESS_FRAGMENT_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/compress_fragment_two_pass.c b/packages/runtime/vendor/brotli/c/enc/compress_fragment_two_pass.c new file mode 100644 index 000000000..01e4794d8 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/compress_fragment_two_pass.c @@ -0,0 +1,647 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function for fast encoding of an input fragment, independently from the input + history. This function uses two-pass processing: in the first pass we save + the found backward matches and literal bytes into a buffer, and in the + second pass we emit them into the bit stream using prefix codes built based + on the actual command and literal byte histograms. */ + +#include "compress_fragment_two_pass.h" + +#include "../common/constants.h" +#include "../common/platform.h" +#include "bit_cost.h" +#include "brotli_bit_stream.h" +#include "entropy_encode.h" +#include "fast_log.h" +#include "find_match_length.h" +#include "hash_base.h" +#include "write_bits.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define MAX_DISTANCE (long)BROTLI_MAX_BACKWARD_LIMIT(18) + +static BROTLI_INLINE uint32_t Hash(const uint8_t* p, + size_t shift, size_t length) { + const uint64_t h = + (BROTLI_UNALIGNED_LOAD64LE(p) << ((8 - length) * 8)) * kHashMul32; + return (uint32_t)(h >> shift); +} + +static BROTLI_INLINE uint32_t HashBytesAtOffset(uint64_t v, size_t offset, + size_t shift, size_t length) { + BROTLI_DCHECK(offset <= 8 - length); + { + const uint64_t h = ((v >> (8 * offset)) << ((8 - length) * 8)) * kHashMul32; + return (uint32_t)(h >> shift); + } +} + +static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2, + size_t length) { + if (BrotliUnalignedRead32(p1) == BrotliUnalignedRead32(p2)) { + if (length == 4) return BROTLI_TRUE; + return TO_BROTLI_BOOL(p1[4] == p2[4] && p1[5] == p2[5]); + } + return BROTLI_FALSE; +} + +/* Builds a command and distance prefix code (each 64 symbols) into "depth" and + "bits" based on "histogram" and stores it into the bit stream. */ +static void BuildAndStoreCommandPrefixCode(BrotliTwoPassArena* s, + size_t* storage_ix, + uint8_t* storage) { + /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */ + /* TODO(eustas): initialize once. */ + memset(s->tmp_depth, 0, sizeof(s->tmp_depth)); + BrotliCreateHuffmanTree(s->cmd_histo, 64, 15, s->tmp_tree, s->cmd_depth); + BrotliCreateHuffmanTree(&s->cmd_histo[64], 64, 14, s->tmp_tree, + &s->cmd_depth[64]); + /* We have to jump through a few hoops here in order to compute + the command bits because the symbols are in a different order than in + the full alphabet. This looks complicated, but having the symbols + in this order in the command bits saves a few branches in the Emit* + functions. */ + memcpy(s->tmp_depth, s->cmd_depth + 24, 24); + memcpy(s->tmp_depth + 24, s->cmd_depth, 8); + memcpy(s->tmp_depth + 32, s->cmd_depth + 48, 8); + memcpy(s->tmp_depth + 40, s->cmd_depth + 8, 8); + memcpy(s->tmp_depth + 48, s->cmd_depth + 56, 8); + memcpy(s->tmp_depth + 56, s->cmd_depth + 16, 8); + BrotliConvertBitDepthsToSymbols(s->tmp_depth, 64, s->tmp_bits); + memcpy(s->cmd_bits, s->tmp_bits + 24, 16); + memcpy(s->cmd_bits + 8, s->tmp_bits + 40, 16); + memcpy(s->cmd_bits + 16, s->tmp_bits + 56, 16); + memcpy(s->cmd_bits + 24, s->tmp_bits, 48); + memcpy(s->cmd_bits + 48, s->tmp_bits + 32, 16); + memcpy(s->cmd_bits + 56, s->tmp_bits + 48, 16); + BrotliConvertBitDepthsToSymbols(&s->cmd_depth[64], 64, &s->cmd_bits[64]); + { + /* Create the bit length array for the full command alphabet. */ + size_t i; + memset(s->tmp_depth, 0, 64); /* only 64 first values were used */ + memcpy(s->tmp_depth, s->cmd_depth + 24, 8); + memcpy(s->tmp_depth + 64, s->cmd_depth + 32, 8); + memcpy(s->tmp_depth + 128, s->cmd_depth + 40, 8); + memcpy(s->tmp_depth + 192, s->cmd_depth + 48, 8); + memcpy(s->tmp_depth + 384, s->cmd_depth + 56, 8); + for (i = 0; i < 8; ++i) { + s->tmp_depth[128 + 8 * i] = s->cmd_depth[i]; + s->tmp_depth[256 + 8 * i] = s->cmd_depth[8 + i]; + s->tmp_depth[448 + 8 * i] = s->cmd_depth[16 + i]; + } + BrotliStoreHuffmanTree(s->tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS, + s->tmp_tree, storage_ix, storage); + } + BrotliStoreHuffmanTree(&s->cmd_depth[64], 64, s->tmp_tree, storage_ix, + storage); +} + +static BROTLI_INLINE void EmitInsertLen( + uint32_t insertlen, uint32_t** commands) { + if (insertlen < 6) { + **commands = insertlen; + } else if (insertlen < 130) { + const uint32_t tail = insertlen - 2; + const uint32_t nbits = Log2FloorNonZero(tail) - 1u; + const uint32_t prefix = tail >> nbits; + const uint32_t inscode = (nbits << 1) + prefix + 2; + const uint32_t extra = tail - (prefix << nbits); + **commands = inscode | (extra << 8); + } else if (insertlen < 2114) { + const uint32_t tail = insertlen - 66; + const uint32_t nbits = Log2FloorNonZero(tail); + const uint32_t code = nbits + 10; + const uint32_t extra = tail - (1u << nbits); + **commands = code | (extra << 8); + } else if (insertlen < 6210) { + const uint32_t extra = insertlen - 2114; + **commands = 21 | (extra << 8); + } else if (insertlen < 22594) { + const uint32_t extra = insertlen - 6210; + **commands = 22 | (extra << 8); + } else { + const uint32_t extra = insertlen - 22594; + **commands = 23 | (extra << 8); + } + ++(*commands); +} + +static BROTLI_INLINE void EmitCopyLen(size_t copylen, uint32_t** commands) { + if (copylen < 10) { + **commands = (uint32_t)(copylen + 38); + } else if (copylen < 134) { + const size_t tail = copylen - 6; + const size_t nbits = Log2FloorNonZero(tail) - 1; + const size_t prefix = tail >> nbits; + const size_t code = (nbits << 1) + prefix + 44; + const size_t extra = tail - (prefix << nbits); + **commands = (uint32_t)(code | (extra << 8)); + } else if (copylen < 2118) { + const size_t tail = copylen - 70; + const size_t nbits = Log2FloorNonZero(tail); + const size_t code = nbits + 52; + const size_t extra = tail - ((size_t)1 << nbits); + **commands = (uint32_t)(code | (extra << 8)); + } else { + const size_t extra = copylen - 2118; + **commands = (uint32_t)(63 | (extra << 8)); + } + ++(*commands); +} + +static BROTLI_INLINE void EmitCopyLenLastDistance( + size_t copylen, uint32_t** commands) { + if (copylen < 12) { + **commands = (uint32_t)(copylen + 20); + ++(*commands); + } else if (copylen < 72) { + const size_t tail = copylen - 8; + const size_t nbits = Log2FloorNonZero(tail) - 1; + const size_t prefix = tail >> nbits; + const size_t code = (nbits << 1) + prefix + 28; + const size_t extra = tail - (prefix << nbits); + **commands = (uint32_t)(code | (extra << 8)); + ++(*commands); + } else if (copylen < 136) { + const size_t tail = copylen - 8; + const size_t code = (tail >> 5) + 54; + const size_t extra = tail & 31; + **commands = (uint32_t)(code | (extra << 8)); + ++(*commands); + **commands = 64; + ++(*commands); + } else if (copylen < 2120) { + const size_t tail = copylen - 72; + const size_t nbits = Log2FloorNonZero(tail); + const size_t code = nbits + 52; + const size_t extra = tail - ((size_t)1 << nbits); + **commands = (uint32_t)(code | (extra << 8)); + ++(*commands); + **commands = 64; + ++(*commands); + } else { + const size_t extra = copylen - 2120; + **commands = (uint32_t)(63 | (extra << 8)); + ++(*commands); + **commands = 64; + ++(*commands); + } +} + +static BROTLI_INLINE void EmitDistance(uint32_t distance, uint32_t** commands) { + uint32_t d = distance + 3; + uint32_t nbits = Log2FloorNonZero(d) - 1; + const uint32_t prefix = (d >> nbits) & 1; + const uint32_t offset = (2 + prefix) << nbits; + const uint32_t distcode = 2 * (nbits - 1) + prefix + 80; + uint32_t extra = d - offset; + **commands = distcode | (extra << 8); + ++(*commands); +} + +/* REQUIRES: len <= 1 << 24. */ +static void BrotliStoreMetaBlockHeader( + size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix, + uint8_t* storage) { + size_t nibbles = 6; + /* ISLAST */ + BrotliWriteBits(1, 0, storage_ix, storage); + if (len <= (1U << 16)) { + nibbles = 4; + } else if (len <= (1U << 20)) { + nibbles = 5; + } + BrotliWriteBits(2, nibbles - 4, storage_ix, storage); + BrotliWriteBits(nibbles * 4, len - 1, storage_ix, storage); + /* ISUNCOMPRESSED */ + BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage); +} + +static BROTLI_INLINE void CreateCommands(const uint8_t* input, + size_t block_size, size_t input_size, const uint8_t* base_ip, int* table, + size_t table_bits, size_t min_match, + uint8_t** literals, uint32_t** commands) { + /* "ip" is the input pointer. */ + const uint8_t* ip = input; + const size_t shift = 64u - table_bits; + const uint8_t* ip_end = input + block_size; + /* "next_emit" is a pointer to the first byte that is not covered by a + previous copy. Bytes between "next_emit" and the start of the next copy or + the end of the input will be emitted as literal bytes. */ + const uint8_t* next_emit = input; + + int last_distance = -1; + const size_t kInputMarginBytes = BROTLI_WINDOW_GAP; + + if (BROTLI_PREDICT_TRUE(block_size >= kInputMarginBytes)) { + /* For the last block, we need to keep a 16 bytes margin so that we can be + sure that all distances are at most window size - 16. + For all other blocks, we only need to keep a margin of 5 bytes so that + we don't go over the block size with a copy. */ + const size_t len_limit = BROTLI_MIN(size_t, block_size - min_match, + input_size - kInputMarginBytes); + const uint8_t* ip_limit = input + len_limit; + + uint32_t next_hash; + for (next_hash = Hash(++ip, shift, min_match); ; ) { + /* Step 1: Scan forward in the input looking for a 6-byte-long match. + If we get close to exhausting the input then goto emit_remainder. + + Heuristic match skipping: If 32 bytes are scanned with no matches + found, start looking only at every other byte. If 32 more bytes are + scanned, look at every third byte, etc.. When a match is found, + immediately go back to looking at every byte. This is a small loss + (~5% performance, ~0.1% density) for compressible data due to more + bookkeeping, but for non-compressible data (such as JPEG) it's a huge + win since the compressor quickly "realizes" the data is incompressible + and doesn't bother looking for matches everywhere. + + The "skip" variable keeps track of how many bytes there are since the + last match; dividing it by 32 (ie. right-shifting by five) gives the + number of bytes to move ahead for each iteration. */ + uint32_t skip = 32; + + const uint8_t* next_ip = ip; + const uint8_t* candidate; + + BROTLI_DCHECK(next_emit < ip); +trawl: + do { + uint32_t hash = next_hash; + uint32_t bytes_between_hash_lookups = skip++ >> 5; + ip = next_ip; + BROTLI_DCHECK(hash == Hash(ip, shift, min_match)); + next_ip = ip + bytes_between_hash_lookups; + if (BROTLI_PREDICT_FALSE(next_ip > ip_limit)) { + goto emit_remainder; + } + next_hash = Hash(next_ip, shift, min_match); + candidate = ip - last_distance; + if (IsMatch(ip, candidate, min_match)) { + if (BROTLI_PREDICT_TRUE(candidate < ip)) { + table[hash] = (int)(ip - base_ip); + break; + } + } + candidate = base_ip + table[hash]; + BROTLI_DCHECK(candidate >= base_ip); + BROTLI_DCHECK(candidate < ip); + + table[hash] = (int)(ip - base_ip); + } while (BROTLI_PREDICT_TRUE(!IsMatch(ip, candidate, min_match))); + + /* Check copy distance. If candidate is not feasible, continue search. + Checking is done outside of hot loop to reduce overhead. */ + if (ip - candidate > MAX_DISTANCE) goto trawl; + + /* Step 2: Emit the found match together with the literal bytes from + "next_emit", and then see if we can find a next match immediately + afterwards. Repeat until we find no match for the input + without emitting some literal bytes. */ + + { + /* We have a 6-byte match at ip, and we need to emit bytes in + [next_emit, ip). */ + const uint8_t* base = ip; + size_t matched = min_match + FindMatchLengthWithLimit( + candidate + min_match, ip + min_match, + (size_t)(ip_end - ip) - min_match); + int distance = (int)(base - candidate); /* > 0 */ + int insert = (int)(base - next_emit); + ip += matched; + BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); + EmitInsertLen((uint32_t)insert, commands); + BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n", + (int)(next_emit - base_ip), insert, 2)); + memcpy(*literals, next_emit, (size_t)insert); + *literals += insert; + if (distance == last_distance) { + **commands = 64; + ++(*commands); + } else { + EmitDistance((uint32_t)distance, commands); + last_distance = distance; + } + EmitCopyLenLastDistance(matched, commands); + BROTLI_LOG(("[CompressFragment] pos = %d distance = %d\n" + "[CompressFragment] pos = %d insert = %d copy = %d\n" + "[CompressFragment] pos = %d distance = %d\n", + (int)(base - base_ip), (int)distance, + (int)(base - base_ip) + 2, 0, (int)matched - 2, + (int)(base - base_ip) + 2, (int)distance)); + + next_emit = ip; + if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { + goto emit_remainder; + } + { + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some + positions within the last copy. */ + uint64_t input_bytes; + uint32_t cur_hash; + uint32_t prev_hash; + if (min_match == 4) { + input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); + cur_hash = HashBytesAtOffset(input_bytes, 3, shift, min_match); + prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 3); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 2); + prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 1); + } else { + input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5); + prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 5); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 4); + prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 3); + input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2); + cur_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); + prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 2); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 1); + } + + candidate = base_ip + table[cur_hash]; + table[cur_hash] = (int)(ip - base_ip); + } + } + + while (ip - candidate <= MAX_DISTANCE && + IsMatch(ip, candidate, min_match)) { + /* We have a 6-byte match at ip, and no need to emit any + literal bytes prior to ip. */ + const uint8_t* base = ip; + size_t matched = min_match + FindMatchLengthWithLimit( + candidate + min_match, ip + min_match, + (size_t)(ip_end - ip) - min_match); + ip += matched; + last_distance = (int)(base - candidate); /* > 0 */ + BROTLI_DCHECK(0 == memcmp(base, candidate, matched)); + EmitCopyLen(matched, commands); + EmitDistance((uint32_t)last_distance, commands); + BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n" + "[CompressFragment] pos = %d distance = %d\n", + (int)(base - base_ip), 0, (int)matched, + (int)(base - base_ip), (int)last_distance)); + + next_emit = ip; + if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) { + goto emit_remainder; + } + { + /* We could immediately start working at ip now, but to improve + compression we first update "table" with the hashes of some + positions within the last copy. */ + uint64_t input_bytes; + uint32_t cur_hash; + uint32_t prev_hash; + if (min_match == 4) { + input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3); + cur_hash = HashBytesAtOffset(input_bytes, 3, shift, min_match); + prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 3); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 2); + prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 1); + } else { + input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5); + prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 5); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 4); + prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 3); + input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2); + cur_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match); + prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 2); + prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match); + table[prev_hash] = (int)(ip - base_ip - 1); + } + + candidate = base_ip + table[cur_hash]; + table[cur_hash] = (int)(ip - base_ip); + } + } + + next_hash = Hash(++ip, shift, min_match); + } + } + +emit_remainder: + BROTLI_DCHECK(next_emit <= ip_end); + /* Emit the remaining bytes as literals. */ + if (next_emit < ip_end) { + const uint32_t insert = (uint32_t)(ip_end - next_emit); + EmitInsertLen(insert, commands); + BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n", + (int)(next_emit - base_ip), insert, 2)); + memcpy(*literals, next_emit, insert); + *literals += insert; + } +} + +static void StoreCommands(BrotliTwoPassArena* s, + const uint8_t* literals, const size_t num_literals, + const uint32_t* commands, const size_t num_commands, + size_t* storage_ix, uint8_t* storage) { + static const BROTLI_MODEL("small") uint32_t kNumExtraBits[128] = { + 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 6, 7, 8, 9, 10, 12, 14, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, + }; + static const BROTLI_MODEL("small") uint32_t kInsertOffset[24] = { + 0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, + 34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114, 6210, 22594, + }; + + size_t i; + memset(s->lit_histo, 0, sizeof(s->lit_histo)); + /* TODO(eustas): is that necessary? */ + memset(s->cmd_depth, 0, sizeof(s->cmd_depth)); + /* TODO(eustas): is that necessary? */ + memset(s->cmd_bits, 0, sizeof(s->cmd_bits)); + memset(s->cmd_histo, 0, sizeof(s->cmd_histo)); + for (i = 0; i < num_literals; ++i) { + ++s->lit_histo[literals[i]]; + } + BrotliBuildAndStoreHuffmanTreeFast(s->tmp_tree, s->lit_histo, num_literals, + /* max_bits = */ 8, s->lit_depth, + s->lit_bits, storage_ix, storage); + + for (i = 0; i < num_commands; ++i) { + const uint32_t code = commands[i] & 0xFF; + BROTLI_DCHECK(code < 128); + ++s->cmd_histo[code]; + } + s->cmd_histo[1] += 1; + s->cmd_histo[2] += 1; + s->cmd_histo[64] += 1; + s->cmd_histo[84] += 1; + BuildAndStoreCommandPrefixCode(s, storage_ix, storage); + + for (i = 0; i < num_commands; ++i) { + const uint32_t cmd = commands[i]; + const uint32_t code = cmd & 0xFF; + const uint32_t extra = cmd >> 8; + BROTLI_DCHECK(code < 128); + BrotliWriteBits(s->cmd_depth[code], s->cmd_bits[code], storage_ix, storage); + BrotliWriteBits(kNumExtraBits[code], extra, storage_ix, storage); + if (code < 24) { + const uint32_t insert = kInsertOffset[code] + extra; + uint32_t j; + for (j = 0; j < insert; ++j) { + const uint8_t lit = *literals; + BrotliWriteBits(s->lit_depth[lit], s->lit_bits[lit], storage_ix, + storage); + ++literals; + } + } + } +} + +/* Acceptable loss for uncompressible speedup is 2% */ +#define MIN_RATIO 0.98 +#define SAMPLE_RATE 43 + +static BROTLI_BOOL ShouldCompress(BrotliTwoPassArena* s, + const uint8_t* input, size_t input_size, size_t num_literals) { + double corpus_size = (double)input_size; + if ((double)num_literals < MIN_RATIO * corpus_size) { + return BROTLI_TRUE; + } else { + const double max_total_bit_cost = corpus_size * 8 * MIN_RATIO / SAMPLE_RATE; + size_t i; + memset(s->lit_histo, 0, sizeof(s->lit_histo)); + for (i = 0; i < input_size; i += SAMPLE_RATE) { + ++s->lit_histo[input[i]]; + } + return TO_BROTLI_BOOL( + BrotliBitsEntropy(s->lit_histo, 256) < max_total_bit_cost); + } +} + +static void RewindBitPosition(const size_t new_storage_ix, + size_t* storage_ix, uint8_t* storage) { + const size_t bitpos = new_storage_ix & 7; + const size_t mask = (1u << bitpos) - 1; + storage[new_storage_ix >> 3] &= (uint8_t)mask; + *storage_ix = new_storage_ix; +} + +static void EmitUncompressedMetaBlock(const uint8_t* input, size_t input_size, + size_t* storage_ix, uint8_t* storage) { + BrotliStoreMetaBlockHeader(input_size, 1, storage_ix, storage); + *storage_ix = (*storage_ix + 7u) & ~7u; + memcpy(&storage[*storage_ix >> 3], input, input_size); + *storage_ix += input_size << 3; + storage[*storage_ix >> 3] = 0; +} + +static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl( + BrotliTwoPassArena* s, const uint8_t* input, size_t input_size, + BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, + int* table, size_t table_bits, size_t min_match, + size_t* storage_ix, uint8_t* storage) { + /* Save the start of the first block for position and distance computations. + */ + const uint8_t* base_ip = input; + BROTLI_UNUSED(is_last); + + while (input_size > 0) { + size_t block_size = + BROTLI_MIN(size_t, input_size, kCompressFragmentTwoPassBlockSize); + uint32_t* commands = command_buf; + uint8_t* literals = literal_buf; + size_t num_literals; + CreateCommands(input, block_size, input_size, base_ip, table, + table_bits, min_match, &literals, &commands); + num_literals = (size_t)(literals - literal_buf); + if (ShouldCompress(s, input, block_size, num_literals)) { + const size_t num_commands = (size_t)(commands - command_buf); + BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage); + /* No block splits, no contexts. */ + BrotliWriteBits(13, 0, storage_ix, storage); + StoreCommands(s, literal_buf, num_literals, command_buf, num_commands, + storage_ix, storage); + } else { + /* Since we did not find many backward references and the entropy of + the data is close to 8 bits, we can simply emit an uncompressed block. + This makes compression speed of uncompressible data about 3x faster. */ + EmitUncompressedMetaBlock(input, block_size, storage_ix, storage); + } + input += block_size; + input_size -= block_size; + } +} + +#define FOR_TABLE_BITS_(X) \ + X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) + +#define BAKE_METHOD_PARAM_(B) \ +static BROTLI_NOINLINE void BrotliCompressFragmentTwoPassImpl ## B( \ + BrotliTwoPassArena* s, const uint8_t* input, size_t input_size, \ + BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, \ + int* table, size_t* storage_ix, uint8_t* storage) { \ + size_t min_match = (B <= 15) ? 4 : 6; \ + BrotliCompressFragmentTwoPassImpl(s, input, input_size, is_last, command_buf,\ + literal_buf, table, B, min_match, storage_ix, storage); \ +} +FOR_TABLE_BITS_(BAKE_METHOD_PARAM_) +#undef BAKE_METHOD_PARAM_ + +void BrotliCompressFragmentTwoPass( + BrotliTwoPassArena* s, const uint8_t* input, size_t input_size, + BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, + int* table, size_t table_size, size_t* storage_ix, uint8_t* storage) { + const size_t initial_storage_ix = *storage_ix; + const size_t table_bits = Log2FloorNonZero(table_size); + switch (table_bits) { +#define CASE_(B) \ + case B: \ + BrotliCompressFragmentTwoPassImpl ## B( \ + s, input, input_size, is_last, command_buf, \ + literal_buf, table, storage_ix, storage); \ + break; + FOR_TABLE_BITS_(CASE_) +#undef CASE_ + default: BROTLI_DCHECK(0); break; + } + + /* If output is larger than single uncompressed block, rewrite it. */ + if (*storage_ix - initial_storage_ix > 31 + (input_size << 3)) { + RewindBitPosition(initial_storage_ix, storage_ix, storage); + EmitUncompressedMetaBlock(input, input_size, storage_ix, storage); + } + + if (is_last) { + BrotliWriteBits(1, 1, storage_ix, storage); /* islast */ + BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */ + *storage_ix = (*storage_ix + 7u) & ~7u; + } +} + +#undef FOR_TABLE_BITS_ + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/compress_fragment_two_pass.h b/packages/runtime/vendor/brotli/c/enc/compress_fragment_two_pass.h new file mode 100644 index 000000000..e98b5e0e2 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/compress_fragment_two_pass.h @@ -0,0 +1,70 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function for fast encoding of an input fragment, independently from the input + history. This function uses two-pass processing: in the first pass we save + the found backward matches and literal bytes into a buffer, and in the + second pass we emit them into the bit stream using prefix codes built based + on the actual command and literal byte histograms. */ + +#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ +#define BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ + +#include "../common/constants.h" +#include "../common/platform.h" +#include "entropy_encode.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* TODO(eustas): turn to macro. */ +static const size_t kCompressFragmentTwoPassBlockSize = 1 << 17; + +typedef struct BrotliTwoPassArena { + uint32_t lit_histo[256]; + uint8_t lit_depth[256]; + uint16_t lit_bits[256]; + + uint32_t cmd_histo[128]; + uint8_t cmd_depth[128]; + uint16_t cmd_bits[128]; + + /* BuildAndStoreCommandPrefixCode */ + HuffmanTree tmp_tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1]; + uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS]; + uint16_t tmp_bits[64]; +} BrotliTwoPassArena; + +/* Compresses "input" string to the "*storage" buffer as one or more complete + meta-blocks, and updates the "*storage_ix" bit position. + + If "is_last" is 1, emits an additional empty last meta-block. + + REQUIRES: "input_size" is greater than zero, or "is_last" is 1. + REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24). + REQUIRES: "command_buf" and "literal_buf" point to at least + kCompressFragmentTwoPassBlockSize long arrays. + REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. + REQUIRES: "table_size" is a power of two + OUTPUT: maximal copy distance <= |input_size| + OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ +BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(BrotliTwoPassArena* s, + const uint8_t* input, + size_t input_size, + BROTLI_BOOL is_last, + uint32_t* command_buf, + uint8_t* literal_buf, + int* table, + size_t table_size, + size_t* storage_ix, + uint8_t* storage); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/dictionary_hash.c b/packages/runtime/vendor/brotli/c/enc/dictionary_hash.c new file mode 100644 index 000000000..193f7a41c --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/dictionary_hash.c @@ -0,0 +1,145 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Hash table on the 4-byte prefixes of static dictionary words. */ + +#include "dictionary_hash.h" + +#include "../common/platform.h" /* IWYU pragma: keep */ +#include "../common/static_init.h" + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +#include "../common/dictionary.h" +#include "hash_base.h" +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +BROTLI_BOOL BROTLI_COLD BrotliEncoderInitDictionaryHash( + const BrotliDictionary* dict, uint16_t* words, uint8_t* lengths) { + size_t global_idx = 0; + size_t len; + size_t i; + static const uint8_t frozen_idx[1688] = {0, 0, 8, 164, 32, 56, 31, 191, 36, 4, +128, 81, 68, 132, 145, 129, 0, 0, 0, 28, 0, 8, 1, 1, 64, 3, 1, 0, 0, 0, 0, 0, 4, +64, 1, 2, 128, 0, 132, 49, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 36, 152, +0, 0, 0, 0, 128, 8, 0, 0, 128, 0, 0, 8, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, +0, 0, 0, 1, 0, 64, 133, 0, 32, 0, 0, 128, 1, 0, 0, 0, 0, 4, 4, 4, 32, 16, 130, +0, 128, 8, 0, 0, 0, 0, 0, 64, 0, 64, 0, 160, 0, 148, 53, 0, 0, 0, 0, 0, 128, 0, +130, 0, 0, 0, 8, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 32, 1, 32, 129, 0, 12, 0, +1, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 32, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, +0, 0, 2, 0, 0, 0, 0, 0, 32, 0, 0, 0, 2, 66, 128, 0, 0, 16, 0, 0, 0, 0, 64, 1, 6, +128, 8, 0, 192, 24, 32, 0, 0, 8, 4, 128, 128, 2, 160, 0, 160, 0, 64, 0, 0, 2, 0, +0, 0, 0, 0, 0, 0, 0, 0, 32, 1, 0, 0, 64, 0, 0, 0, 0, 0, 0, 32, 0, 66, 0, 2, 0, +4, 0, 8, 0, 2, 0, 0, 33, 8, 0, 0, 0, 8, 0, 128, 162, 4, 128, 0, 2, 33, 0, 160, +0, 8, 0, 64, 0, 160, 0, 129, 4, 0, 0, 32, 0, 0, 32, 0, 2, 0, 0, 0, 0, 0, 0, 128, +0, 0, 0, 0, 0, 64, 10, 0, 0, 0, 0, 32, 64, 0, 0, 0, 0, 0, 16, 0, 16, 16, 0, 0, +80, 2, 0, 0, 0, 0, 8, 0, 0, 16, 0, 8, 0, 0, 0, 8, 64, 128, 0, 0, 0, 8, 208, 0, +0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 32, 0, 8, 0, 128, 0, 0, 0, 1, 0, 0, 0, +16, 8, 1, 136, 0, 0, 36, 0, 64, 9, 0, 1, 32, 8, 0, 64, 64, 131, 16, 224, 32, 4, +0, 4, 5, 160, 0, 131, 0, 4, 96, 0, 0, 184, 192, 0, 177, 205, 96, 0, 0, 0, 0, 2, +0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 128, 0, 0, 8, 0, 0, 0, 0, 1, 4, 0, 1, +0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 64, 69, 0, 0, 8, 2, 66, 32, 64, 0, 0, 0, +0, 0, 1, 0, 128, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 16, 0, 0, 4, 128, 64, +0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 8, 0, 0, 130, 16, 64, 128, 2, 64, 0, 0, 0, 128, +2, 192, 64, 0, 65, 0, 0, 0, 16, 0, 0, 0, 32, 4, 2, 2, 76, 0, 0, 0, 4, 72, 52, +131, 44, 76, 0, 0, 0, 0, 64, 1, 16, 148, 4, 0, 16, 10, 64, 0, 2, 0, 1, 0, 128, +64, 68, 0, 0, 0, 0, 0, 64, 144, 0, 8, 0, 2, 0, 0, 0, 0, 0, 0, 3, 64, 0, 0, 0, 0, +1, 128, 0, 0, 32, 66, 0, 0, 0, 40, 0, 18, 0, 0, 0, 0, 0, 33, 0, 0, 32, 0, 0, 32, +0, 128, 4, 64, 145, 140, 0, 0, 0, 128, 0, 2, 0, 0, 20, 0, 80, 38, 0, 0, 32, 0, +32, 64, 4, 4, 0, 4, 0, 0, 0, 129, 4, 0, 0, 144, 17, 32, 130, 16, 132, 24, 134, +0, 0, 64, 2, 5, 50, 8, 194, 33, 1, 68, 117, 1, 8, 32, 161, 54, 0, 130, 34, 0, 0, +0, 64, 128, 0, 0, 2, 0, 0, 0, 0, 32, 1, 0, 0, 0, 3, 14, 0, 0, 0, 0, 0, 16, 4, 0, +0, 0, 0, 0, 0, 0, 0, 96, 1, 24, 18, 0, 1, 128, 24, 0, 64, 0, 4, 0, 16, 128, 0, +64, 0, 0, 0, 64, 0, 8, 0, 0, 0, 0, 0, 66, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 16, 0, 64, 2, 0, 0, 0, 0, 6, 0, 8, 8, 2, 0, 64, 0, 0, 0, 0, 128, 2, +2, 12, 64, 0, 64, 0, 8, 0, 128, 32, 0, 0, 10, 0, 0, 32, 0, 128, 32, 33, 8, 136, +0, 96, 64, 0, 0, 0, 0, 0, 64, 4, 16, 4, 8, 0, 0, 0, 16, 0, 2, 0, 0, 1, 128, 0, +64, 16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 2, 0, 16, 0, 4, 0, 8, 0, 0, 0, 0, 0, +20, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 136, 0, 0, 0, 0, 0, 8, 0, +0, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, +0, 0, 4, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 2, 128, 0, 0, 0, 8, 2, 0, 0, 128, 0, 16, 2, 0, 0, 4, 0, 32, 0, 0, 1, +4, 64, 64, 0, 4, 0, 1, 0, 16, 0, 32, 68, 4, 4, 65, 10, 0, 20, 37, 18, 1, 148, 0, +32, 128, 3, 8, 0, 64, 0, 0, 0, 0, 0, 0, 4, 0, 16, 1, 128, 0, 0, 0, 128, 16, 0, +0, 0, 0, 1, 128, 0, 0, 128, 64, 128, 64, 0, 130, 0, 164, 8, 0, 0, 1, 64, 128, 0, +18, 0, 2, 150, 0, 8, 0, 0, 64, 0, 81, 0, 0, 16, 128, 2, 8, 36, 32, 129, 4, 144, +13, 0, 0, 3, 8, 1, 0, 2, 0, 0, 64, 0, 5, 0, 1, 34, 1, 32, 2, 16, 128, 128, 128, +0, 0, 0, 2, 0, 4, 18, 8, 12, 34, 32, 192, 6, 64, 224, 33, 0, 0, 137, 72, 64, 0, +24, 8, 128, 128, 0, 16, 0, 32, 128, 128, 132, 8, 0, 0, 16, 0, 64, 0, 0, 4, 0, 0, +16, 0, 4, 128, 64, 0, 0, 1, 0, 4, 64, 32, 144, 130, 2, 128, 0, 192, 0, 64, 82, +64, 1, 32, 128, 128, 2, 0, 84, 0, 32, 0, 44, 24, 72, 80, 32, 16, 0, 0, 44, 16, +96, 64, 1, 72, 131, 0, 0, 0, 16, 0, 0, 165, 0, 129, 2, 49, 48, 64, 64, 12, 64, +176, 64, 84, 8, 128, 20, 64, 213, 136, 104, 1, 41, 15, 83, 170, 0, 0, 41, 1, 64, +64, 0, 193, 64, 64, 8, 0, 128, 0, 0, 64, 8, 64, 8, 1, 16, 0, 8, 0, 0, 2, 1, 128, +28, 84, 141, 97, 0, 0, 68, 0, 0, 129, 8, 0, 16, 8, 32, 0, 64, 0, 0, 0, 24, 0, 0, +0, 192, 0, 8, 128, 0, 0, 0, 0, 0, 64, 0, 1, 0, 0, 0, 0, 40, 1, 128, 64, 0, 4, 2, +32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 32, 8, 0, 32, 0, 0, 0, 16, 17, 0, +2, 4, 0, 0, 33, 128, 2, 0, 0, 0, 0, 129, 0, 2, 0, 0, 0, 36, 0, 32, 2, 0, 0, 0, +0, 0, 0, 32, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 32, 64, 0, 0, 0, 0, 0, 0, +32, 0, 0, 32, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 16, 0, 0, 0, 0, 0, 0, 0, +1, 0, 136, 0, 0, 24, 192, 128, 3, 0, 17, 18, 2, 0, 66, 0, 4, 24, 0, 9, 208, 167, +0, 144, 20, 64, 0, 130, 64, 0, 2, 16, 136, 8, 74, 32, 0, 168, 0, 65, 32, 8, 12, +1, 3, 1, 64, 180, 3, 0, 64, 0, 8, 0, 0, 32, 65, 0, 4, 16, 4, 16, 68, 32, 64, 36, +32, 24, 33, 1, 128, 0, 0, 8, 0, 32, 64, 81, 0, 1, 10, 19, 8, 0, 0, 4, 5, 144, 0, +0, 8, 128, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 0, 0, 80, 1, 0, 0, +33, 0, 32, 66, 4, 2, 0, 1, 43, 2, 0, 0, 4, 32, 16, 0, 64, 0, 3, 32, 0, 2, 64, +64, 116, 0, 65, 52, 64, 0, 17, 64, 192, 96, 8, 10, 8, 2, 4, 0, 17, 64, 0, 4, 0, +0, 4, 128, 0, 0, 9, 0, 0, 130, 2, 0, 192, 0, 48, 128, 64, 0, 96, 0, 64, 0, 1, +16, 32, 0, 1, 32, 6, 128, 2, 32, 0, 12, 0, 0, 48, 32, 8, 0, 0, 128, 0, 18, 0, +0, 28, 24, 41, 16, 5, 32, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 1, 0, 0, 0, 16, 0, 0, 0, 0, 64, 0, 0, 0, 0, 8, 0, 0, 0, 0, 16, 128, +0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0}; + + memset(lengths, 0, BROTLI_ENC_NUM_HASH_BUCKETS); + + for (len = BROTLI_MAX_DICTIONARY_WORD_LENGTH; + len >= BROTLI_MIN_DICTIONARY_WORD_LENGTH; --len) { + size_t length_lt_8 = len < 8 ? 1 : 0; + size_t n = 1u << dict->size_bits_by_length[len]; + const uint8_t* dict_words = dict->data + dict->offsets_by_length[len]; + for (i = 0; i < n; ++i) { + size_t j = n - 1 - i; + const uint8_t* word = dict_words + len * j; + const uint32_t key = Hash14(word); + size_t idx = (key << 1) + length_lt_8; + if ((lengths[idx] & 0x80) == 0) { + BROTLI_BOOL is_final = TO_BROTLI_BOOL(frozen_idx[global_idx / 8] & + (1u << (global_idx % 8))); + words[idx] = (uint16_t)j; + lengths[idx] = (uint8_t)(len + (is_final ? 0x80 : 0)); + } + global_idx++; + } + } + for (i = 0; i < BROTLI_ENC_NUM_HASH_BUCKETS; ++i) { + lengths[i] &= 0x7F; + } + + return BROTLI_TRUE; +} + +BROTLI_MODEL("small") +uint16_t kStaticDictionaryHashWords[BROTLI_ENC_NUM_HASH_BUCKETS]; +BROTLI_MODEL("small") +uint8_t kStaticDictionaryHashLengths[BROTLI_ENC_NUM_HASH_BUCKETS]; + +#else /* BROTLI_STATIC_INIT */ + +/* Embed kStaticDictionaryHashWords and kStaticDictionaryHashLengths. */ +#include "dictionary_hash_inc.h" + +#endif /* BROTLI_STATIC_INIT */ + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/dictionary_hash.h b/packages/runtime/vendor/brotli/c/enc/dictionary_hash.h new file mode 100644 index 000000000..dfddc017d --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/dictionary_hash.h @@ -0,0 +1,45 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Hash table on the 4-byte prefixes of static dictionary words. */ + +#ifndef BROTLI_ENC_DICTIONARY_HASH_H_ +#define BROTLI_ENC_DICTIONARY_HASH_H_ + +#include "../common/platform.h" +#include "../common/static_init.h" + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +#include "../common/dictionary.h" +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* Bucket is (Hash14 * 2 + length_lt_8); in other words we reserve 2 buckets + for each hash - one for shorter words and one for longer words. */ +#define BROTLI_ENC_NUM_HASH_BUCKETS 32768 + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +BROTLI_BOOL BROTLI_INTERNAL BrotliEncoderInitDictionaryHash( + const BrotliDictionary* dictionary, uint16_t* words, uint8_t* lengths); +BROTLI_INTERNAL extern BROTLI_MODEL("small") uint16_t + kStaticDictionaryHashWords[BROTLI_ENC_NUM_HASH_BUCKETS]; +BROTLI_INTERNAL extern BROTLI_MODEL("small") uint8_t + kStaticDictionaryHashLengths[BROTLI_ENC_NUM_HASH_BUCKETS]; +#else +BROTLI_INTERNAL extern const BROTLI_MODEL("small") uint16_t + kStaticDictionaryHashWords[BROTLI_ENC_NUM_HASH_BUCKETS]; +BROTLI_INTERNAL extern const BROTLI_MODEL("small") uint8_t + kStaticDictionaryHashLengths[BROTLI_ENC_NUM_HASH_BUCKETS]; +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_DICTIONARY_HASH_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/dictionary_hash_inc.h b/packages/runtime/vendor/brotli/c/enc/dictionary_hash_inc.h new file mode 100644 index 000000000..89e04f0f4 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/dictionary_hash_inc.h @@ -0,0 +1,1829 @@ +const BROTLI_MODEL("small") +uint16_t kStaticDictionaryHashWords[BROTLI_ENC_NUM_HASH_BUCKETS] = { +1002,0,0,0,0,0,0,0,0,683,0,0,0,0,0,0,0,1265,0,0,0,0,0,1431,0,0,0,0,0,0,40,0,0,0, +0,155,8,741,0,624,0,0,0,0,0,0,0,0,0,0,0,0,66,503,0,0,0,451,0,0,0,0,0,0,0,835,70, +0,0,539,0,0,0,0,0,0,0,0,0,113,0,0,0,0,718,0,0,0,0,0,0,520,0,1070,0,0,0,0,0,1515, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,610,0,0,750,0,0,0,307,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,964,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,999,0,0,0,0,0,0,0,0, +645,75,0,649,52,282,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1621,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,211,225,0,0,687,718,0,0,110,0,58,0,0,0,0,0,0,345,0,0,301,0,0, +0,203,0,0,1154,674,1949,0,0,0,0,0,0,0,0,0,259,0,0,0,0,0,0,0,1275,0,0,0,1231,254, +0,0,0,0,0,0,0,277,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,0,0,800,0,0,0,29, +116,100,490,0,0,0,0,0,1641,0,543,0,0,0,0,41,181,0,657,0,0,202,25,0,0,0,0,0,0,0, +0,0,0,423,0,0,0,113,0,0,0,927,963,0,976,0,206,0,0,0,0,0,0,0,0,0,2002,0,0,0,0,0, +0,0,0,0,0,0,696,0,1170,0,0,0,0,226,13,0,769,678,551,0,0,0,0,0,0,57,0,0,0,10,188, +0,0,0,624,0,0,0,0,0,0,0,0,0,1941,130,0,0,0,0,378,269,0,0,528,0,1146,0,0,0,1105, +0,1616,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,656,0,1940,0,0,0,0,0,173,0,0,0,0,0,0,0,0,0, +0,0,457,342,810,0,0,0,0,620,0,0,0,0,0,0,0,967,95,447,406,0,0,0,477,0,1268,944, +1941,0,0,0,629,0,0,0,0,0,375,0,0,0,1636,0,0,0,0,774,0,1,1034,0,0,0,0,0,824,0,0, +0,0,0,118,0,0,560,296,0,0,0,0,0,0,0,0,1009,894,0,0,0,0,0,0,0,0,0,0,0,0,0,1474, +366,0,0,0,0,0,0,0,0,0,79,1723,0,0,200,0,0,0,0,0,0,0,0,1759,372,0,16,0,943,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,258,0,0,900,1839,707,30,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2004,0,0,10,115,0,50,0,0,0,0,0,0,0,0,0,0,520,1,0,738,98,482,0,0,0,0, +0,0,0,0,0,0,701,2,0,0,0,0,0,0,0,0,557,0,0,0,0,0,0,0,0,0,347,0,0,0,0,572,0,0,0,0, +0,0,0,0,0,832,0,0,797,809,0,0,0,0,0,0,0,0,0,0,0,528,0,0,0,861,0,0,294,0,0,0,109, +0,0,0,0,0,0,0,0,1187,290,266,0,0,0,0,49,50,748,0,0,466,399,0,0,0,0,0,0,0,378,0, +519,0,0,0,0,0,0,0,0,0,0,0,0,667,351,902,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,180, +0,0,869,0,0,0,0,0,0,0,260,0,0,0,0,0,0,0,0,0,0,523,36,0,0,587,510,809,29,260,0,0, +0,0,0,0,0,0,570,0,565,0,1464,0,0,0,0,0,0,10,0,0,787,399,380,200,0,0,0,0,516,0, +844,887,0,0,0,0,0,0,0,44,0,0,0,305,1655,0,0,0,0,0,0,0,0,0,0,0,0,0,0,786,10,0,0, +0,0,0,0,0,0,0,2031,0,0,0,0,0,684,0,0,0,0,0,1480,0,0,0,27,0,0,0,395,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,813,511,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,0,0,0,206, +496,0,0,0,0,0,909,0,891,0,0,0,0,0,0,0,0,0,687,0,0,0,1342,0,0,0,0,0,0,0,0,0,0, +160,41,0,0,0,0,0,0,0,0,0,0,0,1718,778,0,0,0,0,0,0,0,0,0,0,1610,0,0,0,0,0,115,0, +0,0,0,314,294,0,0,0,983,178,193,0,0,0,0,0,0,0,0,0,174,0,0,0,0,0,0,0,0,0,0,848, +1796,0,0,0,0,0,0,221,0,687,1660,0,0,0,0,262,0,0,179,0,0,0,0,0,66,0,773,0,352,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,0,152,0,0,1197,0,0,0,0,0,0,0,0,0,0,0,0,560,0,0, +564,0,0,0,797,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,556,0,819,0,0,0,0,0,0,0,0,719,544, +637,5,0,0,0,0,0,0,0,0,0,0,0,101,0,1441,0,0,0,893,0,0,0,0,0,0,0,0,0,238,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1296,0,0,969,1729,314,60,0,0,0,0,0,1144,0,1147,0,0,0,0,0, +0,0,0,0,0,437,1853,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,828,0,176,0,0,0,0,0,0,434,39,0, +0,0,0,0,159,0,0,0,902,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,270,0,0,0,0,801,556,0,0, +0,0,0,0,0,416,19,197,369,0,0,0,0,0,0,0,0,0,28,34,0,757,0,0,898,1553,0,721,0,0,0, +0,1012,0,0,0,0,1102,0,898,183,0,0,0,0,0,0,0,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,277,0,0,0,435,0,0,0,0,0,1311,0,0,0,0, +0,0,211,437,0,0,0,28,0,0,750,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2012,0,702, +0,808,0,0,0,0,739,166,0,0,0,0,0,0,719,170,500,0,0,0,0,0,0,0,0,1500,327,0,0,450, +0,0,0,1318,0,0,0,1602,0,0,331,754,0,0,0,0,0,1368,0,0,557,0,0,0,799,850,0,0,0,0, +0,0,0,0,908,0,0,0,0,0,19,62,459,0,0,0,0,0,0,0,0,0,0,0,0,1802,0,0,0,0,0,0,0,0,0, +1397,0,0,0,0,120,238,0,0,0,0,0,0,0,0,0,0,0,1324,0,0,0,0,0,0,0,0,602,201,0,0,164, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,615,0,0,0,0,0,0,0,0,0,0,0,0,0,1243,0,0,0,0,968,0,0, +0,0,0,0,882,0,0,0,907,329,100,0,0,0,0,0,0,0,0,0,0,0,176,26,9,0,0,265,256,0,0,0, +0,0,0,0,0,0,643,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,610,0,0,0,0,973,2001,0, +0,0,0,0,0,522,0,0,0,0,0,0,0,0,0,0,0,553,0,0,0,0,0,0,1582,0,1578,0,0,0,0,0,0,0,0, +0,0,0,795,0,0,0,432,0,0,0,0,0,0,84,126,0,0,0,0,790,0,377,64,0,1529,0,0,0,0,530, +1857,539,1104,0,0,0,0,0,0,0,0,0,0,0,0,977,0,0,0,34,0,0,0,0,0,0,0,0,0,0,0,24,26, +0,0,918,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,183,379,0,0,0,0,0,0,0,792, +0,0,0,0,0,0,0,0,0,1920,0,0,0,0,0,0,0,0,0,771,0,0,0,1979,0,901,254,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,0,0,0,0,0,440,37,0, +508,0,0,0,513,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,533,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,752,920,0,1048,0,153,0, +0,391,0,0,1952,0,0,0,0,0,0,0,0,0,0,126,0,0,0,0,640,0,483,69,1616,0,0,0,0,0,734, +0,0,0,0,0,0,480,0,495,0,472,0,0,0,0,0,0,0,0,874,229,0,0,0,0,948,0,0,0,0,0,0,0,0, +1009,748,0,555,0,0,0,0,0,0,193,0,653,0,0,0,0,0,0,0,0,0,0,984,0,0,0,172,0,0,0,0, +0,0,0,0,83,1568,0,0,384,0,0,0,0,0,0,0,164,880,0,0,0,0,0,0,0,0,0,0,0,367,121,0,0, +828,0,0,0,0,0,0,0,1541,0,0,0,0,0,0,0,343,0,0,0,0,0,0,0,0,561,57,0,0,0,0,0,0,0, +926,0,0,0,0,827,0,194,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0, +0,0,0,896,1249,0,0,0,0,0,1614,0,0,0,860,0,0,0,0,0,0,0,0,964,102,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,899,0,569,0,0,0,0,795,2045,0,0,0, +0,0,0,104,52,0,0,0,0,0,604,0,0,0,0,779,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0, +494,0,677,0,0,0,0,0,0,0,508,0,0,0,0,0,0,0,0,0,1014,0,957,0,0,630,310,0,0,0,570, +0,0,449,0,64,537,0,0,0,0,0,0,0,244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,702,1650,49,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,338,0,0,0,0,1279,0,0,0,0,0,0,0,896,0,0, +178,0,0,0,0,0,0,0,0,0,0,0,0,0,808,695,0,0,0,0,539,1117,0,0,0,0,0,0,0,0,257,0, +1003,0,0,0,1,448,0,516,0,0,960,0,125,4,0,1268,30,748,0,0,852,0,0,0,6,0,0,848, +236,1385,862,1811,0,0,0,0,698,803,0,0,0,0,0,0,0,610,992,0,0,878,0,1847,0,0,0,0, +0,0,0,383,0,1404,0,0,0,0,986,0,347,0,0,0,0,0,0,0,0,0,0,0,592,572,0,1411,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,606,0,0,0,0,0,0, +0,0,0,0,0,0,0,1829,0,0,0,0,0,0,0,0,0,0,0,0,700,748,0,0,0,0,0,0,365,0,0,127,0,0, +83,198,0,0,0,0,0,0,864,55,0,0,0,0,726,1752,0,0,0,0,0,0,0,0,0,0,0,0,0,1066,0,764, +0,0,0,0,683,0,550,309,0,0,874,1212,0,0,0,1364,0,986,381,723,0,0,0,1573,0,0,0,0, +0,1025,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1559,0,0,0,0,493,133,0,0,0,0,148, +119,0,0,0,0,0,0,537,14,541,0,635,126,0,0,0,495,0,0,0,0,861,998,1009,0,0,0,0,0,0, +0,359,368,0,0,0,0,304,1577,0,0,0,0,0,1107,0,0,0,0,0,929,0,0,0,1142,0,0,0,0,289, +175,0,432,0,219,0,0,0,0,0,785,0,0,595,0,0,0,0,0,0,0,0,0,0,0,0,0,80,0,0,0,0,0,0, +931,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1323,0,0,0,0,290,0,559,1751,127,0,0,0, +934,1167,0,963,0,260,0,0,0,573,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +580,1689,0,0,0,0,0,0,0,0,0,1164,0,0,982,1922,0,63,0,0,0,0,0,793,0,0,0,0,0,0,0,0, +0,0,0,0,0,67,790,0,0,0,0,0,0,0,0,0,0,391,443,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,271,0,0,0,0,0,0,0,0,0,0,0,1140,0,0,0,0,340,300,0,897,0,0,0,0,0,0, +0,0,0,0,890,0,0,0,0,818,321,53,0,0,0,0,0,0,0,0,0,468,0,243,0,870,0,0,0,1765,121, +0,0,0,180,518,0,822,419,634,0,0,0,0,0,0,0,0,0,898,0,0,0,0,454,36,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,806,0,0,0,0,0,0,0,0,0,0,0,0,1326,0,104,0,0,0,0,0,0,0, +0,0,260,0,0,0,0,0,0,0,0,0,0,0,0,542,45,0,0,263,1516,42,0,0,0,0,0,468,0,1005,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,288,87,0,0,0,0,0,0,0,0,502,988,133,0,0,0,0,0,0, +141,0,0,872,1842,0,0,0,0,0,0,0,0,261,619,0,0,0,0,189,246,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,678,0,0,0,0,0,0,0,0,0,0,0,0,285,35,0,517,0,0,0,0,0,0,0,0,0,0, +540,214,667,0,74,0,0,125,0,0,0,0,0,761,131,0,0,0,0,0,0,0,0,0,0,0,0,0,333,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1338,94,0,0,0,0,0,0,0,0,0,0,0,0,449,0,646,103, +86,641,2028,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,869,87,277,117,39,0,0,0,0,0,0,0,0,938, +297,0,0,0,0,558,464,0,0,0,0,0,0,0,0,0,0,731,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1608,0, +0,0,0,0,0,0,1429,0,0,733,1010,0,0,338,1656,0,0,0,1038,979,2010,0,0,0,0,0,0,0, +1005,0,0,121,0,0,0,219,20,0,0,0,0,0,0,872,1440,0,0,0,683,0,1070,0,0,522,0,0,0,0, +439,669,0,0,0,0,0,0,0,0,1245,0,0,0,0,0,1218,0,0,547,233,0,0,0,0,0,0,0,0,0,482,0, +0,0,0,0,0,0,886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,795,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,371,0,0,0,0,0,0,0,0,0,0,0,0,0,622,0,625,0,0,0,339,29,0,0,338,0,0,0, +0,130,0,0,0,0,0,0,0,0,0,307,0,0,0,0,0,0,0,0,0,0,2044,0,0,0,0,0,0,0,0,308,770,0, +0,0,0,0,1266,0,0,0,0,0,0,0,0,0,400,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,690,739,0,0, +0,0,0,0,0,990,0,0,0,1831,0,0,0,0,0,0,0,0,0,0,0,0,0,613,0,0,0,0,0,0,0,0,0,0,0,0, +0,763,0,878,0,0,0,977,0,100,0,0,0,0,0,0,0,0,0,463,0,0,0,0,623,318,0,0,296,463, +137,0,0,454,0,0,0,1527,58,0,0,0,0,0,0,0,18,48,0,0,0,0,0,729,0,0,0,442,0,0,0,0, +40,449,0,853,0,0,0,0,0,0,227,0,0,0,0,0,0,1491,0,0,0,0,0,0,0,0,0,0,161,55,0,450, +0,1174,62,0,207,0,0,0,0,0,0,0,0,869,0,0,0,0,80,213,0,0,0,0,0,0,0,0,0,0,354,820, +0,0,747,0,0,0,954,0,0,1073,0,556,0,0,0,692,0,191,0,804,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,831,162,0,0,35,0,0,0,0,0,0,0,0,1235,0,0,0,0,0,1234,0,0, +0,0,0,0,0,0,0,0,96,0,0,0,0,0,0,0,149,0,0,0,902,204,0,0,833,0,287,366,0,0,0,0,0, +0,992,2020,0,0,0,0,0,0,0,0,0,0,0,356,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,784,0,0,567, +630,0,0,0,539,0,0,27,0,0,0,0,0,0,0,0,0,0,755,0,0,0,0,0,0,0,0,0,0,0,0,814,0,0,0, +0,0,0,0,0,0,0,0,0,0,987,0,0,255,761,194,0,1086,0,0,0,0,0,0,1016,0,0,1396,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,562,271,913,0,0,0,0,0,0,0,0,320,153,45,475,0,0, +0,0,0,0,0,713,0,327,0,0,0,0,0,0,604,552,3,359,0,0,0,0,853,80,0,0,0,0,0,0,0,2016, +6,887,0,0,0,0,975,0,961,0,0,0,0,0,916,1891,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,100,101,390,708,0,0,0,587,983,512,0,0,0,0,0,0,0,0,0,0,0,645,0,0,0,851,0,0,0, +0,0,498,140,217,0,0,0,1448,0,0,0,0,0,0,0,0,0,905,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +643,105,0,792,0,0,0,0,0,0,0,0,0,0,0,0,56,0,0,0,0,0,0,0,0,0,0,535,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1748,0,0,0,0,0,754,0,0,0,0,0,0,0,0,0,0,0,0,91,0,0,1565,0,91,792, +939,3,370,0,0,0,0,95,0,0,0,0,551,7,619,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1150,0, +0,0,0,0,0,0,0,0,0,0,0,0,671,0,0,0,0,0,888,368,149,0,0,105,1134,0,983,0,0,458,31, +0,643,0,0,0,312,0,740,0,0,0,1642,0,0,0,0,0,0,0,236,0,0,0,0,0,0,0,59,68,0,0,0,0, +0,867,795,0,0,0,0,970,1977,0,0,0,0,0,0,0,1148,0,775,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,970,0,0,0,0,0,0,0,0,0,665,71,0,0,0,0,827,0,0,0,0,0,0,0,0,0, +0,479,0,0,0,0,0,0,0,0,99,607,0,0,0,0,0,0,0,1960,0,0,0,793,0,0,871,41,0,0,241,94, +0,0,0,0,209,0,0,1497,0,0,0,0,0,0,0,0,0,98,0,0,0,463,0,0,0,0,291,0,0,0,0,0,0,0,0, +0,0,984,0,0,0,0,0,205,0,0,0,0,0,0,205,42,0,801,0,0,0,0,0,635,0,0,533,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,371,0,1282,0,0,0,825,0,0,0,0,0,0,0,0,0,357,879,467,0,317,0,0, +0,0,0,0,0,924,0,0,0,0,849,1795,0,0,0,0,895,1799,43,0,0,0,0,0,0,0,0,0,0,1820,0,0, +0,0,0,0,0,525,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,0,493,0,174,417,0,0, +0,0,0,583,733,0,0,0,0,0,0,481,215,0,0,0,0,477,0,0,0,0,0,0,0,0,308,0,0,0,0,0,0,0, +0,297,126,0,0,361,1551,0,0,0,0,0,0,871,1807,0,0,0,0,0,1307,0,685,0,0,0,0,0,0,0, +797,0,858,0,565,0,0,0,0,0,0,0,0,0,0,0,0,434,252,826,0,0,0,0,0,0,791,0,0,0,0,509, +231,178,601,0,0,0,0,0,0,0,0,43,1591,0,0,0,0,0,1683,0,0,0,0,45,0,0,0,0,0,0,0,0,0, +0,1120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,556,494,0,398,0,0,0,1030,0,0,0,0,0,0, +168,0,0,0,0,0,0,0,0,0,0,973,0,642,0,0,0,0,0,0,0,0,0,1615,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,378,594,0,1093,0,679,112,0,0,0,0,1492,540,1374,714, +1486,0,0,0,0,825,1511,0,0,0,0,0,0,0,0,0,0,0,0,0,952,0,0,736,143,0,700,0,1540,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1557,0,0,0,860,990,0,0,0,807,0,0,0,0,0,131, +515,0,646,0,0,0,0,117,728,508,121,0,0,0,0,0,0,357,0,0,0,0,0,0,237,0,0,0,0,0,0,0, +0,0,1784,0,0,0,0,0,0,0,0,0,0,0,713,348,1536,0,738,0,0,0,0,0,0,0,434,0,0,0,0,0,0, +366,1877,39,0,0,0,0,0,0,580,0,0,0,0,0,0,0,0,0,0,0,0,0,0,873,0,0,0,0,171,0,625, +550,107,343,943,0,0,0,0,0,0,0,768,0,0,0,0,0,0,0,799,0,0,0,894,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1673,0,0,0,0,0,0,0,0,0,0,0,1052,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +272,0,441,0,0,3,9,0,0,0,1182,0,1346,0,0,0,0,0,0,0,0,682,0,0,1004,24,0,0,968,0,0, +0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,0,0,0,578, +474,0,0,0,0,0,0,0,0,0,0,0,0,0,0,113,530,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,556,0,0,0,0,0,0,16,1317,0,0,97,0,0,0,703,0,0,0,0,0,0,0,0,892,0,0,0,1571,0,0, +426,186,0,1101,0,0,0,0,0,0,0,0,937,585,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,644,291, +0,0,0,0,749,0,162,0,0,381,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,762,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,628,21,0,0,0,0,0,0,0,0,919,0,0,0,0,0,0,0,0,0, +633,0,0,0,0,332,0,0,0,0,0,0,0,0,0,1489,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,832,398,0,645,0,0,0,13,0,0,0,0,0,0,0,0,0,0,20,0,800,0,0,0,0,0,0,0,0,0, +0,0,0,0,1993,0,0,0,0,769,0,0,0,665,0,0,0,0,0,0,0,0,0,0,1426,0,0,0,0,60,0,0,0, +641,1874,0,644,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1757,0,0,0,0,0,937,0,1652,0,654,0, +0,0,0,0,0,0,527,0,0,0,0,0,0,0,0,0,0,0,0,0,226,0,0,0,0,0,1486,0,0,0,0,0,0,0,0,0, +0,0,325,0,0,0,0,0,0,0,1345,0,0,91,0,404,0,0,0,0,0,0,0,0,0,0,0,0,973,0,0,0,0,0,0, +0,1176,0,549,0,0,0,0,0,0,0,0,0,0,976,0,0,0,0,0,21,0,0,0,0,0,51,0,0,0,0,314,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,198,6,0,1093,0,0,0,0,0,0,0,0,0, +0,0,0,0,1776,0,0,0,0,0,1528,0,419,0,0,0,0,0,0,0,0,76,138,0,0,0,0,638,29,0,0,0,0, +0,0,0,1418,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1710,0,0,0,0,0, +0,0,0,0,0,0,0,532,23,0,0,0,0,0,0,0,862,0,0,946,592,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,70,0,0,0,0,0,0,0,0,0,812,0,0,0,76,0,0,988,0,442,0,0,0,896,0,0,0,0,0,0, +483,0,0,0,0,1709,0,0,0,0,0,0,119,0,0,0,117,0,309,0,0,0,0,0,596,976,0,0,0,0,0,0, +0,0,0,0,0,768,0,0,0,0,0,0,0,0,0,518,0,0,0,0,0,0,0,0,0,0,0,0,0,0,863,0,0,0,24, +145,1020,0,0,1984,0,0,0,0,0,0,0,658,0,0,0,0,0,0,0,0,0,0,106,1827,0,1010,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,582,87,0,0,0,0,0,0,0,267,0,0,0,703,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,496,0,0,0,0,1121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,561,0,0,0,0,0, +0,0,760,0,0,154,0,0,0,255,0,419,323,0,0,0,0,0,368,0,0,0,0,0,0,0,0,0,0,522,0,0,0, +0,0,0,0,551,562,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,0,0,0,0, +0,0,0,284,525,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,958,0,0,594,0,0,0,0,0,0,6,479,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,61,0,0,0,0,0,0,0,820,1641,0,1556,0,0,0,0,0,0,0,302,0,0, +0,0,0,148,0,0,676,0,0,0,0,0,0,1674,0,0,0,0,0,0,178,0,0,0,0,0,0,0,94,389,0,0,0,0, +91,8,0,0,0,0,0,0,0,0,0,0,112,0,0,0,0,0,0,0,0,0,0,747,0,0,0,0,0,0,0,1746,0,0,0,0, +0,24,0,1352,158,1530,0,0,718,130,280,1401,0,0,0,0,0,1946,8,0,0,0,0,1607,0,0,0,0, +0,0,882,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,417,0,0,0,1597,633,433,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,234,0,0,0,0,0,0,0,0,680,1950,0,0,0,0,249,5,0,0,0, +0,0,0,0,0,0,1216,0,1773,0,0,0,0,0,0,0,0,0,0,0,0,0,0,509,180,0,0,0,0,0,0,0,1002, +0,0,0,0,0,0,0,0,0,0,0,0,0,931,0,0,0,0,0,0,0,0,747,943,0,1837,0,0,0,0,0,0,0,641, +0,0,0,0,280,0,0,0,5,0,0,0,0,0,72,545,0,0,0,0,0,0,0,0,0,742,0,0,254,151,872,0,0, +0,0,0,0,0,0,0,0,0,0,921,0,0,517,833,0,1680,0,0,436,251,584,0,0,0,0,0,0,0,0,0,0, +0,24,500,0,0,0,0,0,0,0,0,195,1775,514,389,0,0,0,0,0,0,0,743,0,0,0,0,0,0,292,0,0, +0,227,1283,774,1805,0,0,0,0,0,0,0,0,0,0,119,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,913, +1910,0,0,0,1826,490,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1162,700,30, +0,0,0,721,839,0,0,0,617,0,0,0,0,0,0,0,0,0,169,428,0,0,0,0,0,1648,637,1205,0,0,0, +1596,0,0,4,266,0,0,0,0,0,0,0,0,0,0,0,862,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0, +0,279,157,391,604,0,0,713,945,877,973,0,0,0,0,0,0,0,0,0,0,0,0,0,0,859,567,628, +1846,0,0,0,0,0,0,0,0,0,762,0,0,191,0,0,0,0,298,0,0,767,909,0,0,0,0,0,0,0,795,0, +0,301,0,0,1970,0,0,0,0,0,0,0,0,0,1236,0,0,0,0,0,0,644,369,15,0,160,71,0,0,0,0,0, +1447,0,0,0,0,0,0,0,0,735,1255,76,0,0,0,0,0,0,0,0,0,0,474,0,0,0,0,0,0,0,0,0,0, +841,0,0,0,0,0,0,0,0,0,0,836,0,0,0,0,0,1622,0,0,735,0,0,0,0,1601,804,1390,394,0, +0,0,0,0,0,96,0,289,0,0,35,688,0,0,0,667,0,513,0,0,0,0,0,0,0,2034,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,704,0,1524,0,1078,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,306, +0,0,0,0,0,0,0,431,0,1196,0,0,54,0,15,1448,0,1418,0,0,0,0,0,0,0,0,0,907,0,0,0,0, +0,0,194,1767,0,0,0,0,0,840,0,900,0,0,0,0,0,0,0,0,0,0,0,1436,0,0,0,0,642,1560,0, +0,0,0,0,0,94,386,0,0,0,0,0,0,0,0,0,0,830,416,0,0,20,731,0,0,0,0,0,0,0,0,697,0,0, +662,0,0,0,0,0,0,0,0,0,861,0,0,0,0,0,0,0,871,671,864,0,928,7,0,332,0,0,0,0,1055, +0,0,0,0,0,0,986,0,0,0,0,0,44,76,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,175,518, +831,1108,0,0,0,836,0,1852,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,843,1804,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,246,0,0,0,610,202,0,0,36,0,0,0,240,654,13,0,0,0,0,0,0,0, +0,391,0,403,0,0,0,0,0,0,0,0,0,0,75,0,366,815,0,0,631,0,0,0,0,0,0,0,0,345,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,952,0,0,0,0,0,0,0,0,0,0,0,673,35,662,0,287,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5,34,0,0,0,0,0,0,0,0,151,0,427,0,0,382,0,0,0,329,0,0,279,0,0,0, +0,0,0,0,0,0,0,906,0,0,366,843,0,1443,0,1372,992,0,36,123,0,649,0,0,0,0,0,767,0, +1018,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,995,0,0,0,0,0,0,0,72,368,0,0,1345,0,0,0, +589,0,0,0,0,0,0,0,0,0,1988,0,0,220,541,0,0,0,686,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,32,196,0,0,0,0,0,0,0,0,0,0,0,0,0,381,0,0,0,0,0,0,0,0,0,1452,0, +0,0,616,0,0,0,0,0,0,0,0,0,1229,0,0,0,0,0,0,0,0,0,0,667,120,0,0,0,0,0,0,0,1146,0, +0,0,0,0,0,0,0,0,0,0,352,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0,0,935,0,1050,0, +147,88,0,0,923,0,0,0,0,0,934,0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,341,222,0,0,0,0,0,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0, +637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1159,0,0,0,847,0,0,0,0,0,0,683,0,867,944,0,0, +0,0,0,1809,0,0,0,0,0,0,0,0,0,0,395,170,0,0,0,0,0,0,0,0,0,0,618,535,0,1625,0,0,0, +0,0,0,0,0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,778,0,0,0,0,0,46,0,2032,0,0,37, +1458,0,938,363,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,314,0,0,0,0,0,0,889,0,0,0,0,0,0,0, +0,0,0,0,462,0,0,0,0,525,0,0,23,0,0,0,0,0,0,0,0,0,0,0,676,0,0,0,0,0,0,0,0,0,0,0, +0,498,725,0,0,0,0,7,0,0,0,0,773,0,0,0,164,0,0,0,0,0,0,0,0,936,583,659,1462,0, +220,0,0,0,0,803,0,0,544,119,0,0,0,0,0,0,0,0,0,0,0,181,176,0,1192,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1878,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,0,0,0, +944,0,0,0,0,0,0,0,273,0,0,0,0,0,855,0,0,0,0,5,127,0,0,0,0,0,0,0,0,752,230,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,162,0,654,48,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197, +0,0,0,0,0,0,0,963,0,0,0,0,0,0,0,0,0,0,858,0,0,0,0,0,0,0,0,0,0,676,1978,0,0,102, +972,0,0,0,0,0,0,0,361,0,461,0,0,0,472,0,0,0,0,0,0,0,0,0,0,0,0,0,0,747,905,0,0,0, +155,0,0,0,0,0,0,0,0,0,0,319,163,0,0,0,0,0,0,0,0,0,848,0,0,36,631,0,0,0,0,0,1769, +0,0,0,0,0,144,0,0,0,0,0,0,0,0,0,0,369,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,555,247,0,0, +996,0,0,189,0,0,0,0,0,0,0,0,0,0,280,0,0,0,0,0,0,0,0,0,0,0,526,746,0,0,345,0,0,0, +1017,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,651,428,0,0,0,1162,230,327,546,792,0,0,0, +1203,0,0,0,0,0,0,0,0,0,672,189,0,0,0,0,0,0,99,0,0,0,298,0,0,0,0,0,0,555,397,0,0, +0,0,0,1157,0,0,0,0,0,0,0,0,0,0,398,1523,0,366,0,0,787,0,0,0,282,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,157,0,941,0,0,0,0,0,1336,0,0,116,0,0,0,0,0,0,787,0,0,0,0,0,0,0,0,0, +0,170,160,0,1815,0,0,0,0,0,866,0,0,0,0,0,0,0,0,0,689,0,0,0,0,820,0,498,108,0,0, +0,1119,0,0,0,244,609,1005,0,581,0,0,0,0,0,895,0,0,0,1898,0,0,0,0,0,926,0,0,0,0, +0,0,0,0,0,0,0,0,0,538,496,294,301,0,0,0,18,0,0,757,0,0,0,0,0,1263,0,820,0,722,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2028,0,0,0,0,124,1875,0,0,0,881,0,0,0,1348, +0,0,0,0,0,0,0,911,0,954,0,0,0,0,414,0,0,0,0,517,0,0,0,0,0,816,0,0,0,0,0,0,0,0, +713,0,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,593,150,0,0,0,0, +0,553,0,0,0,0,0,0,0,0,0,0,108,0,0,0,0,420,0,0,0,0,0,0,0,0,0,0,0,1777,0,0,55,493, +0,0,81,0,321,980,0,0,0,0,0,0,0,0,0,0,0,0,0,0,362,112,0,74,0,0,0,0,0,0,0,625,0,0, +0,0,0,0,377,16,0,0,61,281,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,1031,0,0,0,0,0,0,51,0, +0,0,0,0,0,0,211,309,15,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,789,173,0,439,9,648, +0,0,294,0,0,0,0,0,0,0,374,8,0,1099,0,0,0,0,0,0,0,575,0,0,0,518,0,0,0,702,0,0,0, +0,0,0,87,0,0,0,438,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,464,122,0,0,0,1802,0,0,0,0, +0,0,499,0,0,0,87,476,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,840,283,0,0,0,0,1620,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,609,1160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600, +323,372,0,0,0,0,471,722,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0, +477,1304,0,1774,0,0,88,0,438,12,0,0,0,0,0,0,0,0,671,997,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,639,22,0,0,782,681,0,0,0,0,0,0,0,0,0,0,1013,664,0,942,0,1349,0,0,0,0,0,0,0, +0,0,0,0,0,356,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,215,289,0,1975, +109,450,0,0,0,0,0,0,0,0,0,0,705,0,0,664,0,0,0,0,0,0,0,1238,0,0,318,0,0,0,0,0,0, +0,0,0,0,0,0,0,960,1872,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103,0,0,0,0,0,0,0,0,0,239, +777,0,26,0,0,0,0,0,0,0,0,0,0,0,0,375,414,0,17,0,0,0,1350,0,955,0,0,0,0,0,0,0,0, +887,960,0,0,0,0,0,0,0,0,0,0,708,710,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,919,0,0,0, +0,502,280,7,45,0,0,0,0,777,0,0,0,0,410,0,1110,0,0,0,0,0,0,414,341,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,787,0,0,0,436,0,0,0,0,0,0,0,1707,613,377,96,0,0,0,0,451, +0,0,0,0,0,0,0,0,0,0,0,0,0,680,0,483,916,0,0,0,0,0,0,937,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,739,0,0,0,0,0,0,0,0,82,0,0,663,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,128,0,0,0,0,0,0,0,0,1087,0,0,0,0,0,0,0,503,0,0,0,0,0,0,9,113,104,324,0,460,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,935,702,434,485,1014,949,423,0,900, +0,0,0,0,0,0,0,2018,574,0,0,0,0,0,0,0,0,0,0,0,0,1206,0,0,0,0,0,0,0,0,38,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1022,0,0,0,0,143,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2029,0,0,0,0,0,0,0,0,0,0,0,0,523,0,0,0,0,0,0,625,0,0,425,37,0,0,0,1943,0,0,0, +0,0,765,0,0,0,0,0,0,0,0,0,0,551,0,0,0,0,0,0,0,0,0,0,0,0,168,0,0,1010,0,0,1994,0, +0,0,91,0,0,0,0,532,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1884,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,240,15,0,0,0,1227,0,1534,0,0,0,0,0,0,0,0,0,0,0,0,0,0,392,0, +0,0,0,0,0,0,0,0,0,0,0,655,562,395,0,0,0,501,1019,0,0,0,0,509,267,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1099,0,0,0,0,0,0,948,0,0,0,0,0,0,0, +462,114,0,0,258,404,0,1717,0,0,0,0,82,1061,0,724,0,0,0,0,0,1133,0,0,0,0,0,0, +1021,841,0,1021,0,0,0,0,0,0,0,0,0,0,488,373,37,0,0,0,0,564,0,0,0,0,0,513,0,0,0, +825,0,0,899,0,0,778,0,0,12,1417,0,1116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,545,0,5, +0,0,0,0,0,0,0,192,0,0,763,0,0,0,0,0,0,0,755,759,0,0,0,0,0,0,0,0,0,370,0,1237,0, +0,0,0,0,0,298,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0, +0,0,0,0,814,991,0,757,57,0,0,0,0,0,0,0,0,0,540,0,0,0,0,608,0,0,0,0,0,0,0,0,1014, +0,0,0,902,0,0,0,0,553,1668,0,0,0,0,0,0,0,0,0,559,60,0,0,0,0,0,511,0,0,675,0,0, +156,0,0,0,0,0,0,709,0,698,0,0,0,1745,0,0,0,0,0,0,0,0,0,714,0,0,0,0,0,0,0,0,206, +8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,776,0,0,0,0,0,0,0,0,0,1272,0,0, +0,0,0,1059,0,0,0,0,0,0,406,0,0,0,0,0,0,0,0,0,0,947,0,0,0,0,0,0,168,0,0,0,0,0,0, +870,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,554,0,0,0,0,784,908,0,0,0,0,0,0, +0,396,358,0,0,0,0,0,0,0,0,2,228,0,0,0,0,0,0,0,0,0,0,0,845,14,0,716,1820,594,0, +81,1428,0,161,0,782,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,998,0, +0,0,0,0,0,0,0,0,0,0,0,1043,0,1496,0,0,0,0,0,0,0,0,781,0,0,0,0,0,0,0,817,1114,0, +1814,958,0,0,0,0,812,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,236,643,0,0,0,0,0,0,0,0,0,1172,0,0,0,0,0,0,0,0,0,1338,0,0,0, +0,0,0,0,0,0,0,0,54,0,0,0,256,0,0,351,0,955,1885,0,469,0,0,0,1270,0,744,0,313,0, +0,0,0,0,0,0,0,402,969,0,0,0,0,0,0,50,0,0,0,0,572,0,0,0,0,847,0,0,0,0,0,0,0,248, +43,0,369,0,0,0,0,0,0,0,0,0,0,0,0,0,766,0,363,0,0,0,0,0,0,0,0,0,0,0,678,0,0,409, +258,82,249,0,0,0,0,0,0,0,0,0,0,0,0,32,393,0,788,0,0,0,1281,509,1968,0,0,0,0,39, +291,0,0,0,589,0,0,54,1059,0,0,0,0,0,0,824,0,0,0,0,0,0,0,0,0,0,1005,0,1598,0,0,0, +0,0,919,0,0,0,0,0,0,0,0,52,132,0,0,0,0,0,328,0,0,0,0,173,0,0,0,0,0,65,1411,0,0, +0,0,0,0,0,0,0,0,442,0,842,0,0,0,0,0,0,0,0,0,534,0,0,0,0,0,0,0,0,0,0,0,0,0,845, +210,0,0,0,0,0,0,0,0,892,0,0,223,0,0,0,0,529,0,0,0,807,0,137,218,0,1444,0,0,0,0, +0,332,661,0,0,0,0,0,0,0,76,1517,0,0,0,0,0,0,0,0,0,0,0,418,0,0,0,0,0,0,0,0,481, +379,0,0,0,0,0,149,18,0,0,0,0,0,0,0,0,742,304,142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,799,925,195,51,0,0,0,0,688,0,0,0,0,697,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1169,751,0,0,0,452,929,0,221,0,1437,0,0,0,0,955,1251,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,132,0,0,0,0,0,865,0,0,0,0,0,0,0,767, +672,42,0,0,0,1050,0,0,0,0,0,0,0,0,368,44,0,0,0,0,0,0,0,570,29,0,0,0,0,0,0,227,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,522,0,0,0,0,0,0,0,1529,0,0,0,0,0,0,739,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1667,0,0,0,0,0,0,132,511,0,138,208,1020,0,0,23,565,0,344,0,0,0, +0,0,922,0,0,0,0,0,0,0,240,0,0,415,171,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,402,0,0,754,31,716,0,982,731,0,0,0,0,0,0,0,888,0,0,0,803,847,0,0,823, +0,0,0,0,0,0,785,0,0,2,0,0,0,0,0,0,0,532,0,0,681,0,0,314,0,384,684,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,649,447,0,1818,1007,0,321,0,66,360,0,0,0,385,0,0,0,0,0,0, +0,900,73,254,0,0,0,0,683,1959,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,86,0,0,725,0,0,0,0,0,196,0,0,0,0,0,831,0,0,0,0,723,0,0,0,0,0,994,627,0,0, +0,0,0,0,0,0,0,0,764,66,0,0,0,0,205,36,0,0,0,0,0,0,0,950,0,0,0,887,111,0,0,831, +388,165,0,0,0,0,0,155,0,0,0,0,0,0,0,0,0,0,0,0,0,0,780,755,0,0,0,0,898,146,0,0,0, +0,0,0,0,45,7,0,0,0,0,0,0,0,0,607,0,0,0,0,0,0,65,0,0,0,0,0,0,0,0,0,88,0,0,0,0,0, +621,600,0,367,0,0,0,0,0,0,0,561,0,559,0,585,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +287,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,672,157,0,0,0,0,714,0,0,0, +0,0,456,0,925,0,0,0,0,0,0,0,0,19,0,0,0,0,1473,0,0,0,0,0,0,0,0,0,0,113,0,0,0,0,0, +0,0,0,0,0,0,0,0,69,463,0,0,82,193,2,471,0,0,0,0,633,0,0,0,0,0,0,1148,129,1392, +542,803,0,0,0,0,0,0,0,0,0,0,0,0,438,0,0,0,0,0,0,875,0,0,0,0,0,237,0,0,0,0,0,0,0, +65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,563,0,0,0,9,444,0,0,43,1260,0,0,0,0,0,0, +971,0,0,699,0,0,0,0,0,1116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,829,242,0, +0,593,0,0,0,0,0,0,0,0,201,36,224,0,0,0,0,0,0,1430,0,1806,0,523,0,0,212,1889,0,0, +0,827,0,0,0,0,0,2043,136,242,0,0,0,0,0,0,284,148,10,0,0,0,0,0,0,1249,0,0,0,807, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,0,0,0,494,0,0,0,0,0,0,0,0,1510,0,0,0,0,0, +0,0,0,0,0,505,1306,0,0,764,268,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,384,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1703,0,0,0,0,159,964,583,0,0,0, +0,0,0,515,0,0,854,0,0,0,0,0,0,0,0,0,0,0,0,1123,0,0,0,0,0,0,0,136,0,0,0,0,0,1782, +0,0,44,1287,0,0,0,0,0,732,0,0,0,0,313,679,0,0,316,0,0,0,0,595,0,0,0,0,0,0,753, +147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,137,0,0,0,0,414,0,1762,0,0,0,0,0,0,0,0, +0,0,0,599,0,0,0,0,0,0,0,0,0,1749,0,0,0,1627,0,488,0,0,0,0,0,83,0,0,0,0,676,0,0, +1639,0,0,0,0,0,0,0,0,0,278,0,0,0,0,0,0,97,0,14,1085,0,0,0,0,0,0,781,388,0,849, +59,229,0,0,0,0,0,1115,0,0,0,0,108,0,0,0,0,700,0,0,0,0,0,0,0,0,0,1414,0,0,0,0,0, +0,0,0,0,0,0,0,0,660,737,1035,0,0,0,0,0,0,521,690,0,0,0,0,0,0,0,0,0,0,0,0,272,0, +0,0,0,0,0,0,0,0,0,1744,0,0,0,0,0,0,128,733,0,0,277,0,0,0,0,0,0,0,0,0,4,0,0,0,0, +0,0,0,0,0,0,0,0,0,936,1981,40,0,0,0,0,0,0,0,0,775,0,0,0,0,0,0,0,0,0,306,0,0,0,0, +0,0,0,979,0,0,0,0,0,611,0,0,0,0,0,178,0,0,0,1969,0,0,0,0,0,0,0,664,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,390,0,0,0,1510,0,0,0,0,0,0,0,0,0,0,0,493,0,0,37,0,0,0,0,724,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,1537,0,0,168,473,0,0,0,105,0,0,0,0, +627,438,0,0,0,0,0,0,0,0,0,0,11,1256,0,0,0,1626,0,779,0,0,0,0,25,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,308,0,0,0,0,0,741,0,671,0,0,0,0,649,150,0,0,99,521,0,0,3,339,0,0,0, +543,0,0,0,0,0,0,0,0,0,1358,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,234,155, +0,0,0,0,0,0,0,1628,0,766,0,0,0,0,0,0,0,0,0,0,0,0,0,829,0,0,0,1445,0,0,0,486,0,0, +0,0,2,1635,0,0,0,0,558,0,0,0,0,0,0,0,0,0,0,1461,0,0,0,0,0,599,0,0,0,0,0,0,0,0,0, +1376,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,0,0,0,0,0,0,447,0,0,66,1432,0,0,0,0, +0,0,307,0,413,609,0,0,0,930,0,0,0,0,21,939,0,0,0,0,0,962,4,651,0,0,0,0,15,579,0, +0,0,0,0,597,0,0,0,0,0,981,0,0,0,545,0,0,0,0,0,0,0,1558,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,800,17,0,0,17,0,907,0,0,0,110,0,0,0,53,458,0,1983,0,0,0,0,0,0,0,0,0,0,443,0, +0,0,0,0,0,0,0,0,0,0,924,1844,0,1232,0,0,0,0,70,519,0,993,0,0,0,0,0,0,14,530,0, +907,0,0,0,0,0,733,0,0,0,0,0,0,0,0,55,0,188,531,56,0,0,1693,0,0,0,0,0,0,0,0,441, +0,192,928,0,0,0,0,0,241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1525,0,259,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,512,185,0,464,1603,0,0,0,0,0,0,0,0,0,0,0,1113, +284,720,0,0,722,0,0,0,0,0,13,0,0,0,0,0,0,0,4,289,43,0,0,0,0,0,0,1694,0,0,0,0, +193,0,0,0,0,409,0,0,0,0,0,0,0,0,0,0,0,0,308,0,0,1863,0,0,0,0,0,0,0,0,0,790,0,0, +745,1002,0,0,0,0,0,0,0,0,0,289,68,477,13,0,0,0,0,0,0,0,0,0,0,609,0,0,0,0,0,0,0, +0,0,0,0,367,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,528,0,0,0,0,0,0,0,0,0,694,58, +548,0,0,0,0,0,0,687,0,0,0,0,1749,0,0,0,0,0,0,0,0,1004,661,0,0,0,0,0,0,445,0,0,0, +74,0,0,0,0,213,0,0,0,0,0,0,0,0,0,0,0,0,0,834,0,0,189,1672,0,0,0,0,0,0,0,1548, +192,0,0,0,0,0,0,0,0,0,0,0,0,0,32,751,0,78,0,0,0,0,0,0,544,1602,105,473,0,0,0,0, +0,0,156,1949,0,1779,0,0,0,0,0,0,0,0,0,0,0,763,0,0,0,0,0,0,0,0,29,0,0,0,0,0,0,0, +0,0,0,883,0,0,0,0,0,0,0,488,0,617,0,0,50,0,694,1518,785,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,546,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,1016,0,0,0,577,0,0,0,0,0,0, +184,935,114,720,0,0,100,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,95,14,0,969,0,0,0,0,0,0,0, +727,0,1021,0,0,0,0,0,1190,0,0,0,0,0,0,0,0,0,0,0,0,0,153,0,0,0,0,0,0,0,0,0,798,0, +587,0,0,695,42,0,1929,141,957,0,465,7,908,0,0,450,148,0,0,0,1166,0,0,0,0,0,0,0, +0,0,0,0,0,253,0,1003,0,0,0,0,0,0,0,0,0,0,0,46,0,0,879,0,806,0,1868,0,0,0,0,0, +1846,0,0,0,730,0,0,0,0,0,0,0,965,0,0,0,0,506,0,0,0,10,0,0,0,22,0,0,0,0,0,0,0,0, +0,0,0,0,0,960,296,0,0,0,0,0,0,0,0,0,0,0,587,0,0,0,0,20,0,0,0,32,982,0,0,0,0,0,0, +0,0,0,0,941,0,0,0,0,435,0,0,0,0,0,0,71,419,0,0,0,0,0,0,688,740,94,345,0,0,679, +582,0,0,0,0,0,0,0,945,0,0,0,0,0,0,0,0,0,0,0,0,539,0,684,1993,0,0,0,659,0,583,0, +803,0,704,0,0,0,0,0,198,181,347,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,481,405,203,0,0,99,826,0,0,0,0,0,0,0,492,0,408,0,0,0,0,0,0,0,0,0,0,4,0,0, +0,0,665,349,137,0,0,0,0,612,1270,0,0,0,0,0,371,0,0,0,826,0,0,0,0,21,1535,858, +374,0,0,0,0,0,0,311,0,0,0,991,1968,0,0,0,0,494,1647,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,769,0,0,0,0,0,642,0,0,157,123,0,0,0,1435,0,0,0,0,0,0,0,0,0,0,79,0,0,0, +0,0,0,1425,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,393,486,1690,0,0,0,0, +0,0,0,0,0,0,0,0,756,184,0,0,0,1382,0,0,0,175,0,1493,0,1007,0,0,0,0,0,0,0,0,0,0, +0,219,0,0,0,0,515,99,0,851,0,0,0,0,0,1278,0,0,0,0,0,0,0,1000,982,0,762,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,910,1819,0,0,0,0,0,0,906,0,0,0,0,0,0,0,0,0,0,1730,0,0, +0,0,0,0,0,0,0,0,0,1185,0,0,0,0,0,0,0,0,40,0,0,0,147,0,0,0,0,0,0,0,0,0,0,0,0,0, +650,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,30,0,553,0,0,20,597,0,1614,0,0,0,0,0,327, +49,0,0,0,0,0,0,0,78,0,0,786,134,0,0,0,12,496,0,0,0,0,0,0,0,0,0,0,42,204,0,614,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,247,0,0,0,0,942,0,0,2023,0,0,0,0, +0,0,67,285,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1309,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,532,0,0,0,0,0,0,0, +1692,0,0,0,0,55,1704,0,0,0,0,988,0,0,0,223,0,0,0,0,0,0,0,57,1123,0,0,0,0,0,1764, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2015,0,0,0,1599,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,0,0,534,0,0,0,0,0,0,0,0,0,0,0, +0,0,504,621,1248,321,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1397,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,441,75,0,0,0,0,0,0,0,0,0,0,841,0,0,0,0,0,693,0,650,314,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,913,0,0,0,0,0,0,0,0,0,0,0,0,0,0,880,0,475,0, +0,1016,179,602,111,329,0,0,0,1864,0,0,0,0,846,1888,0,0,780,0,0,0,82,0,0,0,0,821, +0,0,0,0,0,0,0,0,0,0,0,956,112,0,0,0,261,455,0,0,0,0,0,0,337,385,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,184,1865,0,0,721,16,0,486,0,0,0,265,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,621,0,0,0,0,0,0,0,0,234,0,0,815,0,0,743, +1987,205,197,0,0,0,0,0,0,0,0,0,314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,219,452,589,0, +176,333,0,0,0,0,0,0,0,1110,47,0,0,0,0,0,0,0,0,0,0,0,864,0,0,300,0,1237,0,0,0,0, +0,0,0,0,0,0,0,1685,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,135,395,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,631,0,0,0,0,0,0,835,0,0,0,606,459,0,979,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,612,0,0,0,0,0,0,0,0,158,372,0,854,0,0,0,0,0, +0,0,1492,0,0,0,833,0,0,0,0,0,0,0,1739,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +195,0,0,0,0,0,0,0,0,730,1997,0,0,0,0,0,0,0,0,61,0,0,0,0,0,0,0,266,751,0,0,0,0,0, +0,0,821,0,0,0,715,0,0,0,868,0,959,0,0,0,0,0,0,0,0,0,0,0,1053,0,0,0,950,0,1081,0, +1595,0,0,0,0,59,0,0,0,0,0,0,0,0,0,0,47,684,0,0,0,0,0,0,1606,0,777,0,1020,0,0,0, +1094,0,0,0,0,0,0,0,350,0,0,0,0,0,0,242,1812,0,0,0,967,0,0,0,473,286,0,0,0,0,0,0, +798,629,222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,513,337,306,0,0,0,0,0,0,0,0,0, +146,0,0,1646,0,0,0,0,0,465,0,0,0,525,0,0,0,0,0,0,299,165,0,0,0,0,0,0,0,1064,0,0, +0,0,0,596,0,0,0,0,0,0,0,0,0,0,0,0,0,0,238,1741,0,1233,451,1824,0,0,0,0,733,495, +0,0,0,0,0,1204,0,0,0,559,341,0,224,21,0,0,0,0,0,0,0,0,97,1446,0,0,0,0,0,0,0,729, +0,0,565,727,0,1948,0,0,0,519,0,0,0,0,0,0,0,0,0,1193,0,0,0,0,0,0,790,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,323,2,201,0,0,59,0,0,34,0,896,961,0,1285,0,0,46,0,479,0,0, +0,0,549,0,663,0,0,0,0,0,783,65,682,0,0,0,0,0,11,0,0,0,0,0,522,0,0,0,52,0,0,0,0, +0,383,0,0,0,0,0,0,0,0,127,0,0,0,0,0,397,194,0,0,635,0,0,0,0,0,0,0,0,0,0,975,0,0, +0,0,0,0,0,0,0,0,116,0,51,0,0,858,0,1075,535,448,0,0,0,0,0,610,0,0,0,0,0,0,0,0,0, +0,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,267,673,319,94,92,0,551,0,0,218, +1406,69,256,0,0,952,1980,0,833,0,0,0,0,0,0,0,0,0,0,0,0,39,0,0,0,0,0,0,0,81,0,0, +0,352,634,0,0,0,0,0,618,0,0,0,0,0,0,73,339,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,759, +0,0,0,0,0,0,0,0,0,0,0,0,0,1075,0,0,0,0,0,0,482,649,0,0,0,0,0,0,0,0,386,336,0,0, +0,1035,0,0,0,0,0,0,0,0,0,0,0,924,0,73,0,0,0,0,0,1971,0,0,0,0,0,0,0,0,0,1344,0, +501,0,0,0,0,0,0,0,0,46,799,0,0,0,0,0,0,0,276,0,0,0,0,0,0,0,770,0,0,0,0,0,0,0,0, +0,0,0,0,0,158,0,0,0,0,0,1432,0,0,0,0,0,0,0,0,0,0,25,0,0,2001,0,0,0,0,0,0,0,0,0, +0,0,0,0,478,0,0,0,0,0,0,91,1461,211,602,0,0,0,0,0,0,0,0,0,1068,0,0,124,567,0,0, +0,1006,0,0,0,0,0,0,0,0,0,735,812,0,0,323,0,0,0,304,0,0,0,0,0,0,0,0,0,148,0,0,0, +0,0,0,0,0,0,523,0,0,144,730,0,0,981,0,0,111,0,0,132,0,0,0,0,0,0,890,0,0,0,0,0, +444,0,1787,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,2041,932,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,937,0,995,0,0,255,0,0,138,863,965,0,0,631,0,0,0,0,1394,16,652,0,0,0,0,0,0, +0,0,0,0,0,0,0,897,0,321,0,0,0,0,0,922,0,619,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,844,0,0,0,0,0,0,1659,0,1100,0,0,0,1173,0,1930,268,251,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,390,711,0,0,0,0,0,0,0,0,0,0,0,0,0,744,0,0,0,0,0,0,0,0,0,624,0,0,0, +1998,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1125,0,0,0,594,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,268,0,0,0,0,0,0,0,563,0,0,0,0,0,0,0,0,2,39,0,0,0,1332,0,0,0,0,0, +0,0,508,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,796,0,0,0,0,527,0,0,0,0,98,0,0,576,0, +0,0,0,0,122,0,276,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,645,0,0,0,0, +0,0,0,0,0,0,0,290,0,0,762,1292,0,0,0,1315,0,1955,0,0,0,0,0,0,0,0,0,0,210,131,0, +0,0,0,797,0,38,0,11,488,0,936,0,441,0,0,0,0,0,595,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +991,0,0,0,0,0,0,0,0,0,0,0,653,0,523,0,0,0,903,0,0,0,0,0,0,0,0,0,0,0,0,80,0,0,0, +0,0,0,0,0,0,432,0,0,314,0,0,0,0,232,1368,534,0,0,0,0,0,27,0,0,0,12,0,0,0,0,0,0, +0,0,0,264,736,0,1657,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1117,0,127,0,0,0,1208,0,1294, +0,0,0,0,364,0,0,0,0,0,125,1334,0,0,0,0,0,0,0,0,0,0,0,0,0,0,792,0,0,0,0,0,0,0, +849,699,0,0,0,0,0,968,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1446, +124,397,0,0,0,0,0,0,0,0,0,0,0,641,0,0,0,0,0,0,0,0,0,0,0,0,127,346,0,0,517,75,0, +0,0,0,0,0,0,0,83,0,0,0,0,0,0,1031,0,0,0,0,0,0,0,1470,0,954,0,0,345,304,410,0,0, +0,0,734,0,0,0,0,0,1822,0,0,0,1798,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,161, +1865,69,0,0,0,0,0,0,922,0,0,0,0,0,0,0,0,0,0,0,541,0,627,0,0,0,0,0,0,0,0,0,166,0, +0,0,0,0,0,0,0,0,849,0,0,0,0,0,0,0,717,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600,0,0,0,0,0, +0,654,0,0,188,273,0,0,0,543,0,410,87,0,0,941,0,0,186,250,0,1785,0,0,0,0,0,1339, +462,961,0,780,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,529,0,0,0,0,0,0,474,1276,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,24,948,0,0,0,0,657,753,0,0,0,0,941,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,706,985,837,0,1861,0,0,0,0,0,0,0,0,0,0,0,0,0,0,292,933,0,0,0,0,0, +0,0,0,0,767,0,0,0,0,0,0,0,641,0,0,0,1233,114,0,883,0,274,2008,0,1794,285,0,0, +571,0,0,0,0,0,0,0,0,0,0,823,960,16,617,0,431,0,0,0,0,0,0,0,0,0,0,567,0,401,0,2, +781,424,33,0,2006,0,0,274,0,0,1882,0,794,0,0,0,1848,0,0,0,0,0,0,448,47,0,0,0, +1199,0,0,0,0,0,0,0,0,417,0,0,0,0,0,0,0,0,0,0,295,0,0,0,0,0,0,0,1019,0,0,0,0,0,0, +0,0,0,0,0,0,0,620,0,0,0,0,464,0,0,0,0,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,442,0,930,0,0,0,0,0,516,68,0,0,0,0,0,1128,104,0,0,0,0,0,0,0,0,787,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,491,0,0,0,0,0,0,711,0,0,9,0,101,441,0,0,0,0,0,0,0,0, +0,0,160,396,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,679,326,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,1128,0,0,0,0,0,737,0,1796,0,0,0,0,0,0,0,0,0,0,0,0,338,574,0,0, +0,0,0,1096,491,405,0,0,0,0,0,1081,0,0,0,0,0,0,0,0,0,0,0,0,0,1676,0,1207,0,0,0,0, +0,0,969,354,0,0,0,0,598,0,297,0,0,0,0,0,0,0,0,1772,751,0,37,0,0,1828,0,0,0,0,0, +0,0,0,0,257,191,582,0,0,0,0,0,0,790,0,0,0,0,0,47,0,0,0,0,0,0,0,449,306,1011,0,0, +0,0,0,299,0,0,0,0,0,0,837,0,0,0,0,0,0,10,329,0,0,0,0,0,1320,0,0,0,0,0,0,158,657, +0,1191,0,0,0,0,0,0,7,0,974,1939,0,1665,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,288, +66,0,0,0,0,494,175,0,1643,0,0,0,0,0,0,0,0,570,750,719,0,0,0,0,0,0,0,0,0,0,0,0,0, +13,0,0,1247,0,0,221,356,0,0,0,0,0,0,0,0,0,0,694,1809,0,0,0,0,0,0,0,411,0,44,31, +0,0,0,0,669,0,673,0,0,0,0,0,0,0,0,0,1303,704,299,0,0,0,275,0,0,216,1761,0,0,0,0, +0,0,0,0,0,0,0,1319,0,0,428,0,0,0,0,0,0,0,0,0,0,514,0,0,0,0,0,0,49,55,102,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,364,0,0,0,0,379,0,921,971,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1258,0,0,0,1058,0,0,0,0,0,656,0,0,0,0,0,144,0,0,0,0,0,0,0,0,0,0, +0,1373,10,605,0,0,0,0,0,0,0,838,0,1012,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,365,0,0, +0,0,0,0,0,0,0,340,0,0,0,0,0,810,0,0,0,0,0,0,495,0,0,0,0,0,0,0,0,0,261,0,535,248, +0,358,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,567,445,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,697,0,0,0,1336,0,0,0,0,0,0,0,0,917,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,972,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,351,0,0,0,0,0,0,0,0,0,0, +0,0,0,286,0,0,56,438,0,0,0,0,0,1950,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,738,0,0,0,0,0, +0,0,0,0,0,969,2047,0,0,0,0,0,0,0,818,0,0,0,0,0,0,0,866,0,0,0,0,0,0,0,1467,0,0,0, +0,0,0,0,0,0,0,0,0,0,972,0,355,0,0,0,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,267,189,104,0,0,0,0,1613,0,0,0,0,0,0,0,116,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,886,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,863,0,0,0,0,0, +0,0,1953,450,1773,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,381,0,0,0,0,0,0,0, +0,0,0,0,0,1142,0,1189,0,0,0,663,0,0,0,0,0,0,0,846,0,0,528,0,393,378,0,0,0,0,0,0, +325,899,680,1880,0,1770,0,0,0,0,0,648,0,0,0,0,0,0,185,167,0,2046,0,0,0,0,0,0, +249,1645,0,152,0,0,0,1733,0,0,0,0,0,1006,0,0,0,0,0,420,0,0,0,832,0,0,0,0,0,351, +0,0,0,0,6,40,0,0,60,0,0,0,0,1354,745,724,0,0,0,0,0,0,0,0,772,1951,275,108,639,0, +0,0,0,0,0,0,0,0,500,1758,0,0,0,0,0,0,0,0,0,0,0,1886,711,205,0,0,965,865,0,0,0, +534,0,0,0,0,691,0,0,0,237,443,0,878,0,0,0,0,0,1410,0,0,0,0,0,0,0,0,0,0,0,0,0, +995,0,0,0,0,0,0,0,0,0,0,0,0,0,578,0,0,0,0,881,0,0,0,0,0,0,0,0,822,0,923,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,924,0,0,0,665,0,0,0,0,0,1901,0,0,0,0,0,950,498,93, +0,0,0,1451,0,0,0,0,0,747,828,788,400,184,0,198,0,0,0,0,0,0,0,0,0,0,0,994,0,0,0, +0,0,0,0,0,615,320,0,0,0,978,843,905,0,0,0,0,0,0,0,0,850,974,0,0,0,0,6,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,509,0,0,0,0,0,273,0,0,0,0,0,0,0,0,0,0,0,0,0, +201,0,0,0,1041,0,0,0,1040,0,0,0,0,0,0,0,0,0,693,234,774,0,336,0,1399,22,0,805, +802,777,167,789,0,0,1705,0,0,0,0,0,0,0,0,0,0,0,10,13,11,0,0,204,264,0,0,56,0,0, +1917,0,470,0,0,0,0,0,0,0,0,0,0,0,1198,0,0,0,0,0,0,0,0,0,0,1015,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,715,0,0,1002,0,0,0,298,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,867,0,0,724,0,0,0,0,0,0,0,0,0,0,0,0,768,0,0,0,0,0,1066,0,0,0,0,67,0,174,948, +0,0,0,0,0,0,0,0,0,0,0,0,0,764,0,0,0,0,75,137,0,756,0,0,0,0,0,0,1008,842,643,0,0, +0,67,0,0,0,0,0,0,0,0,0,0,0,135,821,0,0,0,0,0,0,0,0,736,0,389,355,0,0,786,0,0,0, +0,0,0,2044,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1030,0,0,0,1083,0,0,0,0,0, +1226,0,0,0,0,356,319,8,389,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,474,0,0,0,427, +0,413,0,730,0,0,0,0,0,373,0,0,0,0,0,0,0,0,0,799,0,0,0,1793,0,0,0,322,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,290,2,0,0,0,0,0,0,0,0,0,0,672, +699,1860,0,0,0,737,0,0,0,1612,0,0,0,0,0,0,0,0,0,0,0,145,124,884,0,0,0,0,0,387,0, +0,0,0,0,0,0,0,0,0,0,679,0,550,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1305,0,0,0,0,0,0,0, +576,0,0,0,0,0,0,0,686,0,607,0,0,37,0,0,0,0,0,0,0,0,0,101,1726,0,0,0,0,0,958,0,0, +0,903,0,0,0,0,147,0,0,0,0,0,0,0,0,0,0,0,367,0,0,0,0,690,0,705,273,0,0,887,0,0,0, +0,0,0,0,0,0,0,0,90,0,0,0,0,0,0,0,908,0,0,0,0,0,0,0,1261,0,0,497,1235,0,429,0,0, +0,0,904,0,12,125,0,0,0,841,0,0,0,0,0,860,946,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,768,0,770,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,271,0,0,0,0,0,0,0,719,0,699,581,0,0,0,0,0,0,0,0,0,0,862,304,0,631,0,0,0,0,880, +1513,0,0,0,0,0,981,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,434,0,0,0,0,0,550,0,0,476,930, +824,553,0,0,452,0,151,0,0,0,0,0,0,772,0,292,135,0,0,0,0,0,0,0,504,0,0,1089,0,0, +0,0,0,0,0,0,0,0,0,783,0,0,0,0,0,0,206,393,0,0,0,0,0,0,0,0,232,912,0,0,0,0,0,977, +0,0,716,98,0,0,0,0,0,733,0,0,0,0,0,0,0,0,19,0,0,0,0,668,0,360,0,0,0,0,0,0,656,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,726,0,0,0,0,0,0,0,0,0,0,0,0,72,0,0,1269,0,0,463,0, +0,0,0,0,0,1454,0,1287,245,0,989,0,0,0,0,0,0,0,0,0,107,164,0,0,0,0,0,0,0,1061,0, +0,0,0,2,484,0,0,0,0,0,0,0,1127,0,0,0,0,0,0,0,460,0,0,0,0,0,932,0,0,0,0,0,0,0, +588,625,0,0,0,0,76,92,0,0,0,0,0,0,0,0,0,0,0,0,0,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +763,0,622,0,0,0,253,0,546,0,0,110,0,256,916,0,0,35,212,0,0,746,0,0,0,150,0,0, +1466,0,0,0,1299,0,0,0,0,0,0,0,0,0,1518,0,0,0,0,0,0,0,0,0,0,0,0,0,1229,0,0,0,816, +0,0,0,0,0,0,159,0,0,0,0,0,734,869,126,1716,0,0,0,0,0,0,202,232,0,0,0,0,212,0,0, +0,0,0,111,1003,0,0,0,0,0,0,0,0,0,0,0,1712,0,0,216,0,0,0,0,516,0,0,0,0,0,650,0,0, +0,0,57,99,0,0,0,0,300,574,0,0,0,0,1023,0,0,302,0,1871,0,728,252,0,0,461,0,0,0, +323,0,0,0,0,0,0,775,461,0,0,0,0,0,0,172,0,0,464,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,73,727,0,1023,0,0,0,0,0,0,0,0,0,0,577,0,0,0,0,0,0,0,0,1037,0,0,0,0,0,0, +0,0,280,677,0,0,0,0,0,0,0,0,0,0,0,799,0,0,0,0,159,0,446,1730,0,0,0,0,0,0,0,0,0, +395,0,0,0,0,145,0,0,0,0,0,0,0,20,0,0,426,608,0,0,0,0,0,977,0,250,0,0,0,0,0,100, +0,0,0,0,1982,0,0,0,0,0,476,0,0,0,0,0,0,594,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,447,0,0,0,0,526,0,0,14,1124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,188,0,0,0,0,0,0,0,0,362,301,0,0,0,1743,0,178,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,872,0,831,0,0,208,202,0,0,0,0,0,0,0,1954,0, +0,0,0,516,872,0,0,313,224,0,0,24,0,11,546,0,0,0,1937,242,241,46,0,0,0,830,1273, +0,0,0,0,0,0,0,825,327,1006,0,0,0,0,0,1580,516,366,0,0,0,0,0,1736,0,0,0,0,0,0,0, +0,0,0,0,1935,0,826,0,0,0,0,139,331,0,0,0,0,0,0,0,0,0,0,0,288,0,916,0,0,0,0,0, +1888,0,0,0,0,0,0,0,1471,0,1570,0,394,0,0,0,0,0,0,0,1931,0,1719,0,658,228,0,0,0, +0,0,374,0,0,0,0,735,0,0,0,0,0,0,323,498,0,1063,0,0,0,0,155,0,0,0,0,0,0,0,0,906, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,616, +902,0,0,0,0,0,692,0,0,0,0,0,0,823,0,0,0,305,0,0,0,0,0,0,0,681,0,0,0,0,0,214, +1004,0,0,0,0,0,0,0,23,0,0,1703,0,0,0,0,0,0,0,0,0,1443,0,0,19,714,0,0,0,0,64,737, +0,0,345,1758,0,0,579,47,0,0,539,139,0,0,0,0,388,0,0,0,0,253,0,0,0,0,0,0,252,0, +745,0,0,0,0,0,0,0,0,0,0,0,504,107,0,871,0,0,0,229,0,0,0,0,0,903,0,0,71,0,0,549, +6,47,0,0,0,0,0,0,0,0,0,980,865,705,0,0,0,161,0,0,0,0,143,1331,0,0,0,1388,33,724, +0,0,0,19,0,0,0,395,0,0,0,0,0,846,210,0,0,0,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,695,937,497,0,0,0,0,0,718,0,0,0,0,0,0,0,1581,0, +0,0,0,0,0,161,49,0,0,0,0,0,0,0,0,0,597,0,0,0,1094,0,0,0,811,908,0,0,0,0,0,0,0,0, +0,0,1471,0,0,0,0,0,0,0,0,0,0,42,1935,0,0,0,2014,66,2007,0,0,586,0,0,0,0,0,0,0,0, +0,28,1077,0,0,0,1221,0,0,62,0,0,0,0,0,0,0,0,0,0,1766,0,0,0,0,0,0,0,0,0,0,0,0,25, +0,499,1388,0,0,97,10,0,0,0,0,0,481,0,0,0,0,0,0,0,0,0,0,37,134,155,486,0,1442,0, +0,0,0,0,591,0,0,0,0,0,0,310,1173,0,0,0,0,409,1156,0,0,0,482,0,0,263,926,0,0,0,0, +0,0,0,0,0,0,0,0,0,804,0,0,0,0,0,0,0,0,0,0,0,0,0,1265,0,415,0,348,0,0,0,1012,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,165,1803,0,0,0,0,0,0,0,408, +0,0,0,0,0,0,257,1321,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1138,0,0,0,249,0, +0,0,576,0,0,0,0,231,0,0,0,288,0,0,0,0,0,0,0,0,0,433,1487,569,1678,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,0,0,0,0,0,779,538,0,0,0,413,0,0,0, +0,0,0,0,0,0,0,495,0,0,0,0,0,191,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,530,567, +0,0,0,0,0,1484,0,0,0,0,0,0,815,609,0,0,0,0,0,484,0,0,0,0,0,0,0,0,0,0,900,0,0,0, +0,1335,0,1724,0,0,0,0,0,0,0,0,0,0,0,640,0,0,0,0,0,0,0,0,0,0,0,1831,0,0,0,0,0,0, +0,0,0,0,0,0,0,474,0,0,0,0,0,0,0,0,0,1103,0,1504,655,1034,0,0,0,0,0,305,0,0,0,0, +0,0,0,0,0,1236,0,0,429,217,0,0,0,0,739,278,0,0,0,0,0,0,0,708,0,0,0,0,0,1840,233, +0,0,0,0,0,0,0,0,2017,0,0,0,0,0,1488,0,0,0,1590,0,0,0,0,0,1800,28,0,0,0,0,0,0,0, +0,0,45,0,36,0,22,1442,378,0,0,0,0,0,0,1507,0,0,0,0,0,0,0,0,0,0,39,0,0,1054,725, +1955,0,2036,0,0,0,0,0,0,0,0,0,0,896,1871,0,0,0,0,0,0,0,0,0,0,805,0,0,0,0,2046,0, +0,0,0,17,712,0,617,55,320,271,0,0,0,0,0,0,0,0,0,445,0,184,103,0,0,0,0,0,0,0,0, +659,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,676,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +337,0,0,0,506,0,0,0,0,0,843,77,0,458,0,0,0,0,0,1420,382,109,142,330,0,0,0,0,0,0, +0,0,0,0,0,0,87,0,0,0,492,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1239,0,0,0,0,0,0, +211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1049,0,321,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1985,0,0,122,0,0,234,0,0,0,1098,0,0,0,0,0,0,549,253,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,522,131,0,0,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,507,0,0,0,0,811,630,0,0,0,343, +0,0,0,0,0,448,591,455,0,1381,0,0,0,0,0,0,0,575,0,0,0,0,0,1175,0,0,0,0,0,0,0,0,0, +653,0,0,0,1761,0,1198,0,0,0,0,297,1127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,678,0,0, +164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,0,45,0,0,0,0,0,121,0,0,0,0,0,0, +0,0,125,0,0,0,1622,0,0,0,0,0,721,145,0,0,0,970,792,0,0,0,715,0,0,0,0,0,1999,0,0, +74,531,0,0,65,0,0,0,105,220,0,0,0,0,0,0,0,960,0,0,0,0,0,0,428,19,0,0,401,96,0,0, +0,0,0,1595,116,0,1021,0,0,0,0,0,750,1961,0,0,148,0,0,0,0,0,0,0,0,0,0,0,0,0,75,0, +0,1383,0,0,0,0,0,0,0,0,0,0,0,0,0,0,779,0,0,0,0,0,0,0,0,598,0,424,0,0,0,0,0,0,0, +1222,0,0,0,876,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,0,0,0,0,187,0,8,0,0,0,0,0, +0,0,429,0,685,0,0,0,0,0,0,0,0,0,0,0,132,472,0,0,0,0,0,0,0,0,0,938,0,0,874,0,0,0, +0,0,774,0,0,0,0,0,92,0,0,0,0,0,0,830,701,0,0,0,0,0,426,350,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,603,59,0,0,0,0,0,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0,0,0,441,163,4,0, +0,0,0,0,0,0,0,0,806,0,0,0,0,0,0,233,0,0,0,0,1994,0,1739,0,0,393,0,47,1038,0,0,0, +309,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,363,0,0,0,175,0,0,0,0,0,0,0,666, +0,0,1675,0,1600,0,0,0,808,0,0,0,0,0,0,0,0,0,0,0,280,54,0,0,0,0,0,0,0,0,421,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,0,0,103,254,0,262,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,805,0,0,0,0,0,0,0,0,0,1630,0,0,0,0,0,0,0,0,0,0,0,0,0,671,972,989,0,0, +0,0,0,0,0,889,0,0,0,1382,0,0,0,0,0,0,0,775,0,0,0,0,0,0,0,0,0,0,388,202,0,0,0,0, +16,560,0,0,0,841,0,0,566,0,0,0,938,0,0,0,0,0,0,0,0,0,0,912,0,0,0,1361,0,0,0,0,0, +0,618,236,0,1854,0,0,318,190,0,1376,0,0,0,0,0,0,0,349,0,0,0,0,951,1972,0,0,0,0, +0,0,344,0,0,0,0,0,0,0,0,850,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,910,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,163,85,0,487,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,145,0,83,0,0,1013,0,0,0,1922,0,0,169,557,66,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1193,82,0,352,454,57,0,0,1333,396,107,0,370,0,0,0,0,0,0,0,0,0,204,0,0,0, +0,0,1706,0,0,0,0,0,0,0,0,0,0,0,0,394,1204,0,0,0,0,0,1007,0,0,0,1696,0,1519,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,981,0,0,0,0,1072,0,0,0,712,0,1629,0,0,0,0,0,0,0,728,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1271,0,0,0,1608,16,0,0,0,0,485,0,0,0,0,0,0, +153,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1991,0,0,0,0,0,0,0,0,52,0,21,0, +0,0,0,0,0,0,0,0,819,0,0,0,0,0,917,0,0,0,0,784,0,0,0,0,135,0,0,0,0,0,454,0,0,0,0, +0,0,0,0,0,852,1719,0,0,0,0,0,852,0,0,0,0,0,952,0,0,0,0,568,0,0,0,0,0,448,0,0,0, +67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1826,657,0,729,666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +669,0,0,0,0,0,0,0,402,0,0,152,0,0,0,0,912,0,0,0,0,0,0,51,320,0,445,0,0,0,0,308, +0,0,0,0,0,386,0,0,239,0,0,130,83,0,143,0,348,0,0,0,0,0,0,0,958,0,0,0,0,0,210,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,430,0,0,0,0,0,0,0,0,0,0,0,0,7,213,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,801,0,0,0,0,0,0,0,0,0,936,0,108,0,0, +0,0,0,0,0,0,0,885,587,219,398,364,0,1165,0,0,342,241,303,0,0,0,0,0,0,0,0,0,0, +1454,0,0,0,0,0,0,0,0,0,0,254,562,0,786,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1294,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,493,216,0,0,0,0,219,341,0,0,0,0,0, +0,0,0,0,0,130,1734,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,701,604,0,0,879,0,195, +666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1669,0,0,0,1791,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1228,0,0,0,0,0,623,0,0,0,0,0,0,0,798,0,0,0,0,0,0,0,0,0,0,0,0,84, +122,0,0,0,837,0,0,0,0,0,0,1013,0,0,577,0,0,0,460,932,0,0,0,0,0,0,0,0,0,0,0,31, +131,0,0,0,605,0,0,0,1246,0,0,0,0,68,278,165,307,781,0,0,0,0,0,0,33,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,1113,0,0,720,1953,203,0,0,0,0,0,0,0,425,326,0,0,0,0,0, +0,0,0,0,0,241,1316,0,0,0,0,0,416,0,0,0,1300,0,847,0,0,662,358,0,0,0,0,839,1823, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,654,1522,0,0,0,0,0,0,163,0,0,0,0,0,314,978,0,0,0, +601,0,0,0,0,0,946,434,0,0,0,402,411,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,1467, +410,0,0,0,0,0,0,0,0,0,0,0,0,0,0,483,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,0,70,0,0,0,0,1405,0,0,0,0,0,0,108,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,777,0,0,0,0,0,747,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,505,0,326,0,0,164,628,654,0,0,0, +37,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,668,152,0,0,0,0,0,0,0,0,0,0,0,581, +0,0,0,0,44,126,89,0,0,0,0,0,0,0,0,1531,0,0,0,0,0,0,0,0,203,1167,0,0,0,0,0,0,0,0, +531,1232,0,0,0,0,0,943,0,670,231,880,0,1617,0,0,0,1957,0,0,0,0,0,0,0,975,0,0,0, +0,0,0,0,0,0,0,0,242,0,0,0,0,0,0,0,0,0,421,0,0,14,834,0,0,0,0,0,0,0,0,0,0,0,0, +465,0,0,0,0,0,834,688,413,855,0,0,0,590,0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,0,0,0, +0,45,169,0,0,0,0,0,0,0,0,0,0,0,198,0,0,565,585,0,0,0,0,0,0,0,0,0,0,0,0,0,691,0, +0,0,593,0,0,0,0,0,0,0,0,0,913,116,0,0,0,0,1360,0,0,0,802,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,673,308,0,709,1006,1895,0,228,0,0,0,1840,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,608,0,0,0,0,0,0,0,0,0,1573,0,2039,136,540,0,0,0,0,0,0,0, +897,0,0,938,1878,0,0,0,0,0,0,0,0,0,1469,0,999,0,299,0,0,0,0,0,0,0,578,0,0,0,0,0, +456,0,0,0,1679,163,693,0,0,0,0,0,0,48,755,0,0,0,0,0,0,0,0,0,0,0,0,338,0,0,0,0, +1091,0,0,0,0,695,0,0,1464,0,0,0,0,0,975,0,0,335,0,0,1979,0,0,0,0,269,1566,630, +396,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1815,634,0,0,0,966,0,0,0,0,0,0,0,9, +412,0,958,0,0,579,382,0,212,0,0,0,0,965,681,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,655, +0,0,0,0,67,0,0,0,0,0,0,751,0,0,0,0,423,231,0,0,1016,300,0,0,0,0,100,237,0,0,0, +1370,0,0,0,1208,0,0,0,0,0,1219,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,199,0,0,427,0,0, +0,0,949,665,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,712,0,0,0,0,0,1186,0,0,0,0,0,0,0,0,0,0,295,312,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +151,0,0,0,0,588,4,0,0,0,0,0,414,104,0,0,757,263,0,561,0,0,0,320,0,0,0,0,0,0,0,0, +0,0,0,225,0,0,0,0,37,817,0,974,0,0,0,0,0,0,0,0,0,0,0,0,0,2026,131,235,16,0,590, +1157,0,0,0,0,0,0,0,0,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,390,0,0,0,0, +0,0,0,1144,0,0,0,464,0,0,0,0,0,0,0,0,0,0,0,0,204,407,303,1218,0,0,0,0,5,325,0,0, +0,0,12,800,0,1783,0,0,0,0,0,0,0,0,0,0,504,621,0,0,0,0,0,0,0,0,0,920,0,376,0,0,0, +0,0,218,580,0,768,454,0,0,0,0,0,0,0,0,0,0,0,0,676,0,0,0,0,0,0,164,0,0,0,0,0,0,0, +0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,120,285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,343, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,0,0,1812,0,0,8,0,0,0,21,1125,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1327,0,0,0,0,575,1598,0,0,0,0,0,0,0,0,0,895,0,0,0,959,0,0, +0,0,0,1759,173,0,0,0,0,266,261,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1427,0,0,300,1033,0,0,0,0,0,0,0,0,0,0,0,584,0,0,0,0,52,734, +0,0,217,239,0,1129,0,0,0,0,0,0,0,0,732,20,0,0,0,0,0,0,0,0,0,0,0,418,0,0,0,613,0, +0,0,0,0,0,0,0,0,632,0,0,85,984,0,0,0,0,909,694,7,1109,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,167,0,0,0,0,280,62,0,0,33,0,0,359,186,980,0,0,0,0,0,0,0,0,0,0,0,585,0,0,0, +211,0,0,336,145,0,1130,0,873,0,0,840,263,0,0,0,0,0,0,0,0,0,916,0,0,0,0,0,0,0,0, +0,0,155,0,0,0,461,97,0,0,0,0,0,1356,0,0,0,0,0,0,0,593,0,0,0,0,0,1392,0,0,0,0, +126,0,0,0,0,1179,0,0,0,0,0,162,0,0,0,0,0,765,0,187,0,1286,0,0,0,0,0,0,0,0,0,635, +0,0,23,215,0,0,0,1306,0,0,97,716,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,657,0, +0,0,0,0,0,0,0,299,0,0,0,0,0,0,134,0,0,0,0,0,0,0,0,0,0,0,658,1082,0,0,0,0,0,2002, +0,0,0,0,0,0,833,248,0,0,0,0,0,1654,0,0,531,0,0,0,0,0,0,634,0,0,0,0,0,0,0,0,0, +853,573,249,0,0,0,0,0,0,0,0,527,0,0,0,0,1419,0,0,0,0,0,0,20,49,0,0,0,992,0,0,0, +728,0,0,0,0,0,0,0,0,0,0,0,0,497,1579,0,0,0,0,62,268,0,0,0,0,0,0,0,1201,0,0,0,0, +0,0,0,0,0,0,0,0,495,193,0,0,0,0,106,0,0,859,0,0,23,0,0,0,0,0,0,0,813,925,0,0, +223,613,953,0,0,0,0,0,0,0,0,666,0,0,0,0,0,0,0,0,0,670,0,0,40,216,0,0,0,0,0,0, +259,0,0,0,440,1114,0,0,0,0,0,0,0,0,74,475,0,0,188,139,0,797,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1572,0,0,0,0,39,0,0,0,0,0,0,0,0,0,0,0,0,1594,0,0,0,0,0,0,0,290,0,232, +0,0,887,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,521,14,0,0,0,0,0,741,0,0,0,992,0, +0,0,0,0,0,0,0,111,0,0,425,0,0,0,0,0,789,0,0,0,1593,0,1768,0,0,233,0,0,0,0,943,0, +0,0,0,0,0,0,955,225,245,0,0,0,0,0,0,241,0,0,0,0,1943,0,0,0,1284,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,709,0,0,0,0,0,0,554,0,0,0,0,0,0,0,0,1564,0,0,0, +443,0,0,0,0,0,0,280,0,0,0,0,0,0,0,0,729,0,0,0,348,0,0,0,0,0,0,0,758,848,298,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,829,1422,189,121,0,0,632,812,0,0,556,0,0,0,0,0,436,172, +530,844,232,984,0,0,0,0,0,0,0,0,0,0,147,0,0,0,0,0,0,0,0,537,0,0,0,0,0,859,0,0, +842,0,0,0,0,0,0,0,0,0,0,1291,0,0,0,0,0,0,0,0,0,0,0,1482,612,392,0,0,0,262,31,0, +0,0,0,0,0,0,0,0,0,753,549,0,0,0,0,0,0,696,0,0,0,0,0,0,0,834,0,0,0,0,0,771,0,0,0, +0,0,0,0,0,0,0,0,0,0,921,0,0,0,674,0,0,0,0,0,0,0,0,0,0,308,444,0,0,0,0,0,0,805, +180,0,0,278,271,0,0,214,505,0,1215,0,0,0,0,0,0,387,271,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,1645,42,92,0,459,0,0,330,1557,0,0,0,0,0,0,0,0,113,18,0,0,0, +1742,0,0,0,965,0,0,0,0,0,0,0,0,0,0,0,0,0,182,0,0,65,0,0,0,0,0,0,0,0,0,0,0,0,973, +0,0,0,0,0,328,0,0,588,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1786, +0,0,962,1985,0,0,0,308,508,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,588,0,0,0,0,0,0,614,793,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,290,0,0,0,0,0,0,0,0,0,0,1136,0,0,0,0,0,0,0,0,0,0,796,719,0,0, +326,210,0,0,0,701,758,472,0,0,0,1947,278,1079,0,0,0,0,0,0,497,41,0,0,634,46,961, +0,810,524,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,532,0,997,0,0,0,0,0,0,0,0,0,0,0,1301,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1298,0,671,0,0,0,306,0,0,0,0,0,0,0,0,0,0, +693,1823,0,0,0,759,0,0,0,0,0,1932,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88,182,0,0,0,1964, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,521,0,0,0,0,0,0,424,857,0,0,0,0,671,328,0, +529,0,0,0,0,0,716,0,1509,80,67,0,0,0,0,59,141,0,0,0,0,0,0,783,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1498,0,0,0,0,343,430,803,1183,677, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1357,53,0,0,0,0,590,0,0,0,0,0,0,0,0,0,0, +0,0,0,329,0,0,0,0,0,0,0,469,0,0,0,0,0,0,0,0,0,0,460,0,0,1743,0,0,963,340,0,0,0, +0,0,1603,0,0,250,0,0,0,0,0,646,218,0,1794,0,0,0,571,0,455,0,0,0,1012,0,0,0,0,0, +0,0,0,0,0,0,0,597,161,0,349,0,524,0,0,0,0,0,0,0,0,0,0,0,0,322,432,0,0,0,0,0,0, +325,223,0,0,0,0,0,566,0,0,0,1394,481,436,0,48,457,610,756,618,0,0,0,755,0,1217, +0,0,0,0,0,197,0,0,0,0,0,0,0,0,0,0,0,0,0,0,544,492,107,414,0,0,0,0,0,0,0,0,0,0,0, +1007,0,0,0,0,5,0,0,1580,0,0,0,0,0,0,0,0,0,0,0,0,0,673,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1843,0,0,0,0,0,0,0,0,0,165,0,0,0,0,0,0,809,885,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,498,0,0,0,306,9,0,0,0,0,0,0,0,437,721,146,0,0,0,0,0,0,0,0,0,0,0,177,0,0,0,0, +0,0,0,1377,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,959,0,0,0,1928,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1435,0,481,0,0,0,0,0,0,142,84,0,0,0,0,0, +1015,0,0,0,315,0,0,0,0,0,0,759,0,0,0,0,0,0,0,0,712,0,0,0,1722,0,0,0,0,0,0,0,0,0, +0,0,0,222,0,985,1414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1273, +538,706,0,0,0,0,0,0,0,0,115,0,0,0,0,0,0,0,0,0,0,1781,0,0,0,0,0,431,97,665,42, +237,0,0,0,264,0,0,213,0,0,0,0,0,0,0,455,0,0,0,906,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +624,0,574,0,0,0,0,0,0,0,0,0,0,0,0,354,0,0,0,1558,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68, +235,723,1813,0,0,0,957,0,830,0,0,0,0,0,0,0,0,0,0,0,0,23,0,0,496,0,0,0,0,0,0,0, +547,239,88,0,0,0,0,0,0,0,0,0,1310,0,0,0,0,0,0,0,0,80,1076,0,0,118,0,0,0,479,274, +0,0,0,0,0,0,0,0,0,0,0,497,0,0,669,261,0,0,0,0,13,0,0,0,0,0,0,791,250,642,0,0,0, +1429,939,949,0,0,0,0,0,0,0,0,0,0,0,0,0,818,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,982,330,0,0,0,0,545,0,0,0,0,0,0,947,0,1188,0,0,0,0,0,904,0,0,0,0,0,1372,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,693,377,0,0,0,0,0,0,0,0,0,0,0,0,0,0,695,0,0, +713,386,0,0,0,0,128,1575,0,0,0,0,0,0,424,893,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,904,0,0,0,0,0,552,322,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,1808,49,0,0,0,0, +1832,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,421,0,0,442,415,0,0,289, +0,0,0,0,0,206,110,0,0,0,0,0,205,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +19,1539,0,0,0,0,0,1340,0,1194,0,0,0,0,0,0,0,0,549,0,0,0,0,0,0,0,0,1720,0,0,0,0, +0,0,0,0,0,319,0,0,0,0,112,1180,0,0,0,0,0,0,0,0,0,0,0,967,0,0,0,0,0,0,0,0,0,1940, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,735,0,0,0,0,0,0,0,0,0,897,132,0,0,0,0,0,0,0, +0,0,0,38,838,0,0,0,379,218,8,660,1017,0,0,0,0,0,0,111,387,647,877,0,0,53,790,0, +0,0,0,0,0,0,0,458,0,0,0,0,0,0,954,0,0,0,394,0,1367,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,882,0,0,0,0,0,0,0,1409,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,124,342,199,0,0,0,0, +0,0,0,0,0,0,724,628,0,0,0,0,804,266,0,0,0,0,0,208,0,79,0,0,0,0,0,0,0,0,741,0,0, +0,0,0,0,0,0,0,0,606,0,1494,821,1553,0,0,135,405,0,0,178,100,0,0,0,0,0,0,0,0,0,0, +0,0,0,481,0,0,0,1378,0,0,0,0,0,0,0,0,0,0,0,0,0,791,33,1227,857,0,467,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,447,0,0,0,0,0,0,86,128,0,0,0,0,0,0,587,0,0,0,692,1018,0, +195,0,0,0,0,0,0,0,1546,0,0,0,0,0,0,0,0,0,0,0,684,0,0,345,0,0,0,0,0,0,365,0,1683, +0,0,472,0,433,0,0,0,0,0,0,0,28,0,0,0,997,0,705,3,0,0,0,0,0,0,0,0,0,229,0,0,0,0, +102,0,0,0,0,866,1022,0,0,0,0,0,0,0,0,0,55,0,115,0,0,0,0,933,0,0,0,0,0,0,0,702,0, +0,0,0,0,0,0,1728,26,484,0,0,0,185,618,417,0,803,0,0,0,0,0,0,0,0,0,0,0,1262,0,0, +0,0,0,0,0,0,0,0,0,0,0,633,0,0,0,0,0,0,0,0,0,0,0,0,0,479,262,0,0,0,0,0,0,830,0,0, +0,0,26,70,0,0,0,0,0,0,0,0,217,0,640,51,0,0,360,1586,0,0,0,0,0,652,0,0,0,0,0,766, +0,0,0,0,298,737,0,0,0,0,0,0,0,0,0,0,655,222,906,0,0,1013,991,2009,0,0,0,0,503,0, +0,0,216,154,0,0,0,716,0,844,0,0,0,0,621,252,0,0,0,0,748,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,103,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,576,0,0,0,648,0,0,0,331,0,0,0, +0,0,0,0,0,0,0,0,0,632,0,0,0,518,107,0,0,0,0,0,0,0,0,851,0,0,0,0,504,0,0,0,0,0,0, +0,0,0,0,0,0,7,883,0,0,0,0,0,0,0,922,0,0,0,0,0,0,0,0,91,993,0,0,0,0,0,0,200,131, +10,0,0,0,0,0,0,0,0,0,0,0,0,0,365,1433,0,0,0,0,28,103,0,0,798,1013,0,0,0,0,0,0,0, +0,39,1925,0,853,0,0,271,519,0,0,0,0,338,0,0,300,470,419,0,0,0,0,0,0,836,0,0,0,0, +0,0,1937,0,0,0,0,0,393,0,0,357,0,0,0,0,0,703,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,387,0,0,0,0,0,0,75,708,453,1351,0,303,0,0,772,0,0,0,0,0,0,0,0,749,0,0, +0,0,0,0,0,0,0,0,0,0,0,1065,0,0,717,226,0,0,0,0,0,890,431,626,0,0,0,0,706,0,0,0, +51,698,0,0,0,0,0,0,0,0,0,0,0,828,0,0,17,0,0,0,0,1929,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,871,498,0,101,1793,0,0,0,0,0,0,435,0, +0,0,0,0,966,0,129,1644,0,0,0,0,0,0,0,0,0,0,0,0,0,997,502,0,0,0,0,0,0,0,0,0,0,0, +0,823,0,1927,0,0,0,0,98,1756,0,0,0,0,0,0,0,0,0,0,0,0,8,0,160,1046,0,492,0,0,0,0, +0,0,129,45,0,0,0,0,0,0,353,558,0,0,0,0,0,785,0,0,0,1145,189,0,0,0,26,353,0,0,0, +0,0,2024,0,0,0,606,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,855,0,0,0,0,0,0,0,0,0,0,0, +0,0,2011,0,0,5,4,0,0,461,764,0,0,0,1449,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1445,0,0, +0,1168,0,0,0,233,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,216,0,0,0,286,0,0,0, +3,0,0,0,723,536,0,0,0,0,0,285,0,0,0,560,0,0,0,0,0,690,0,0,0,0,0,1246,0,0,63,0, +33,0,0,0,0,0,520,1862,0,0,0,0,0,0,0,0,0,0,0,0,630,0,0,0,0,554,0,0,0,0,0,1001,0, +0,0,0,0,446,0,0,0,0,0,0,0,1313,0,0,837,636,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278, +0,0,0,0,0,0,0,0,868,0,0,0,0,1010,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1231,0,304,0,506,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,93,1408,794, +843,704,0,285,114,485,898,145,0,19,2035,0,0,0,1933,0,0,0,0,0,0,0,1728,0,0,0,0,0, +0,0,0,746,0,0,0,0,0,0,0,995,1964,0,0,0,0,0,0,0,0,0,0,0,1550,0,874,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1018,0,0,0,814,126,0,0,1264,0,0,814,955,0,0,0,0,0,0, +0,981,0,0,0,0,0,0,0,0,915,56,0,0,100,0,0,0,0,0,0,0,0,0,638,0,0,0,0,738,0,0,0,0, +0,0,0,0,0,758,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1112,0,0,214,0,0,0,133,0,196, +168,0,0,0,0,0,1152,0,1245,0,0,538,169,871,1816,0,0,413,133,0,0,0,978,0,0,43,93, +371,0,0,0,0,0,0,526,25,0,754,335,0,0,0,0,182,0,0,0,0,0,0,0,0,0,0,0,39,601,0,0,0, +0,0,0,0,181,370,0,0,1652,358,0,0,0,0,0,0,0,0,0,176,286,0,788,0,0,0,0,0,1223,780, +254,1003,896,0,0,0,1447,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,744,0,0,0,0,0,126,0, +41,788,0,0,0,629,0,0,0,0,0,0,0,0,0,0,0,293,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,420,37,1900,0,0,0,0,542,1570,957,0,0,0,0,0,0, +0,373,31,0,0,0,0,125,325,0,0,0,0,0,0,323,0,0,1547,0,0,0,0,0,0,0,0,0,0,0,0,0, +1216,0,0,0,0,0,0,198,1905,629,15,0,0,0,0,0,0,20,75,543,1353,0,0,0,533,0,0,6,0,0, +0,0,0,0,538,0,0,0,0,0,0,0,0,0,0,0,338,0,0,0,0,11,0,0,0,284,659,0,989,0,0,0,0,0, +0,0,0,0,848,0,0,507,0,0,0,0,0,0,0,0,188,991,884,0,0,0,0,60,959,0,0,0,0,0,1653,0, +0,922,337,0,638,0,0,500,0,0,0,0,0,0,0,0,0,0,0,166,0,0,0,0,0,0,0,0,0,0,0,0,418,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,760,0,0,0,0,0,0,1277,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,770,0,0,0,0,0,0,0,243,89,0,0,0,0,0,0,0,0,0,1396,0, +560,0,0,3,1658,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,586,0,0,1271,0,0,0,505,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1947, +41,445,0,0,0,0,0,0,0,0,57,189,0,0,371,0,0,0,0,552,0,883,0,923,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,875,0,0,0,1788,49,0,0,0,0,0, +0,0,0,0,0,0,661,0,0,1945,0,0,0,0,0,794,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1135,0,0,0,745,0,0,0,0,0,0,0,84,0,0,0,0,0,0,0,410,0,976,0,0,0,0,0,703,0,0, +0,0,0,0,187,322,0,0,0,227,0,0,0,0,560,0,31,1395,0,0,0,0,0,466,0,0,0,0,643,167,0, +0,0,1428,0,412,0,0,0,0,0,0,0,0,0,1118,562,0,0,0,0,0,256,0,0,0,0,0,0,1771,0,0,0, +0,0,1190,132,0,66,0,0,0,0,0,0,0,0,0,0,317,0,0,0,63,0,0,0,0,0,0,0,1475,0,0,0,0,0, +0,0,288,0,0,0,0,608,0,0,0,0,0,0,0,0,1225,0,1189,0,0,0,0,0,0,0,1468,0,0,0,0,0, +689,120,0,0,0,0,0,0,0,1,0,329,0,0,0,0,226,0,0,0,0,0,1855,0,0,461,0,0,0,0,1346,0, +0,0,0,0,85,0,0,299,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1171,0,0, +0,980,0,0,0,0,0,0,0,0,637,279,0,0,0,0,0,293,0,0,0,0,528,17,0,0,0,0,5,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,0,0,0,601,0,0,0,0,0,0,779,0, +196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1322,737,752,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,412,192,80,0,0,8,1470,0,0,0,0,0,0,0,0,0,873,0,0,0,0,0,835,0,0,0,0,256, +38,986,0,0,0,0,0,0,0,0,0,91,257,278,911,0,0,0,0,0,0,0,0,749,151,0,0,0,0,0,0,0,0, +0,0,0,0,989,0,0,990,0,0,90,194,0,0,0,0,0,425,0,0,0,0,0,774,0,0,0,0,0,0,0,0,0,0, +646,827,752,0,0,0,662,0,22,21,0,0,0,0,0,0,95,239,0,0,0,431,0,0,0,0,0,874,0,0, +265,65,0,0,0,1350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1887,0,0,0,0,0,0,0,809, +0,696,0,1074,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,630,0,0,802,0,0,0,56,776,0, +970,0,0,797,0,0,0,0,0,400,0,0,1951,0,0,41,0,11,118,0,0,0,0,0,0,0,0,251,615,0,0, +0,1044,0,0,0,0,0,0,0,0,0,0,0,225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,370,0,0,0,0, +104,48,209,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,930,0,0,0,0, +0,0,0,0,0,0,0,1286,0,759,0,120,385,0,0,0,429,0,0,0,0,0,0,0,0,820,0,0,0,0,0,0, +199,0,10,151,0,0,0,761,365,0,0,0,0,0,0,0,0,0,46,1086,0,0,0,0,11,1624,58,344,0,0, +1008,1868,0,0,0,888,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,711,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,440,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,914,1913,0,958,0,885,0,0,0,0,0,0,0,0,0,0,0, +0,0,847,276,0,302,65,0,0,0,510,0,1514,0,0,0,0,0,0,152,291,0,0,0,0,0,0,0,0,0,0,0, +0,282,589,0,0,0,0,0,0,0,0,0,0,0,0,0,130,0,0,463,42,0,0,0,0,0,372,0,0,0,0,0,0,0, +0,0,680,0,0,0,0,0,0,0,0,977,1997,0,0,0,810,0,0,0,0,0,0,0,0,0,1390,0,0,0,644,0,0, +867,982,0,0,0,0,0,0,0,540,0,123,0,0,0,1978,0,0,0,0,789,623,0,1723,0,1220,0,0,0, +0,0,0,0,480,0,0,0,0,0,0,0,0,0,0,0,888,0,0,0,0,0,0,0,0,0,0,0,0,299,1995,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,788,179,0,0,0,0,0,0,431,156,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1373,39,80,196,0,0,507,0,0,0,646,0,0,0,0, +0,1214,0,0,0,0,926,0,0,0,1,114,0,0,0,0,0,446,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,490,0,0,0,491,0,1584,0,0,507,250,0,0,0,158, +10,362,1,0,0,0,0,0,0,0,0,0,408,228,860,480,0,779,0,0,0,557,0,0,142,197,0,0,0,0, +0,0,0,0,0,0,0,1490,11,378,316,1057,0,0,18,579,299,1546,0,177,0,0,0,0,0,0,0,0,0, +411,0,0,0,0,727,439,0,0,0,0,0,1528,0,0,0,0,0,0,58,0,482,0,0,0,505,1952,0,0,0,0, +0,0,0,0,0,0,0,242,0,0,0,0,0,0,0,953,0,0,0,0,802,0,0,0,0,0,0,0,0,0,0,290,0,0,791, +52,0,0,0,0,0,0,0,0,0,0,0,112,0,0,0,0,0,1028,0,0,138,0,0,0,0,1811,0,0,0,0,0,0, +934,1821,0,0,0,0,371,38,0,0,0,1296,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,723,0,0,0,0,0, +0,0,0,0,0,0,0,0,1330,0,0,0,0,0,0,0,1255,296,109,0,0,0,0,0,660,0,0,0,0,270,591,0, +0,0,0,0,0,0,1090,81,0,0,0,0,391,0,0,0,0,249,322,0,0,0,0,0,0,0,1412,0,0,0,0,0,0, +0,0,0,0,526,632,0,0,0,0,0,0,235,144,0,0,0,0,0,940,0,0,0,52,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,309,196,0,0,0,0,0,1912,0,1290,0,686,0,0,625,0,0,0,0,0,0,0,0,0,0,0,412,0, +0,0,0,43,0,0,0,0,11,967,758,0,0,0,0,0,0,0,0,0,0,0,0,0,0,220,0,0,0,0,0,0,0,0,0,0, +873,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,890,0,0,2,0,0,0,0,0,0,0,0,1774, +393,263,0,0,0,0,0,0,818,456,0,0,251,178,393,97,0,0,0,0,0,674,168,0,0,0,0,0,0,0, +159,1639,0,0,0,0,0,0,0,0,59,934,0,191,0,0,0,0,346,165,0,877,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,128,0,0,0,0,0,0,1297,0,0,0,0,0,0,164,0,0,0,15,132,241,1073,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,324,53,0,0,910,0,0,0,0,0,0,0,0,734,705, +217,73,0,0,0,0,0,0,0,0,636,389,0,1409,0,0,0,0,0,893,0,0,0,0,21,0,0,0,0,0,0,0,0, +0,0,0,0,0,721,0,0,0,959,0,0,0,0,1433,0,0,0,0,0,0,0,0,0,0,0,0,174,189,0,0,0,0,0, +0,0,0,0,0,22,2,0,0,815,354,0,0,0,0,425,0,411,60,13,1611,0,0,0,0,0,0,0,0,0,0,0,0, +0,1478,596,0,0,398,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,1159,0,0,0,0,0, +592,223,0,0,0,0,0,0,0,245,64,0,0,0,0,278,0,604,0,0,1502,265,0,0,0,0,0,0,0,310, +1763,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,1356,0,0,0,0,0,0,0, +0,505,0,0,0,0,0,0,0,1000,0,0,966,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,839,0,0,0,0,0,0, +0,0,0,0,0,0,0,637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,590,0,0,0,0,280,0,0,0,1386,0,0,0, +281,0,1064,0,0,0,0,0,917,0,0,15,555,0,0,1014,1883,0,0,0,965,0,0,117,33,0,0,0, +801,0,0,0,0,0,877,0,824,0,0,0,0,0,0,0,0,0,0,0,365,0,0,0,0,0,0,774,7,0,430,0,0, +231,360,0,0,0,0,0,0,0,0,822,740,0,0,929,1485,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,852,0,0,0,0,17,0,0,0,0,0,0,1001,0,0,0,0,35,831,0,0,384,457,0,0,0,1351,0,27, +0,0,984,0,264,552,0,401,0,0,0,710,0,1211,0,0,11,205,0,0,0,0,0,0,0,0,0,0,0,0,5, +579,0,717,0,0,1011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,805,0,0,0,0,0,0,0,0,0,0,0,489,0, +0,0,1024,0,0,0,0,0,0,0,0,0,892,0,0,0,0,0,0,0,0,0,0,0,0,473,0,0,0,659,864,0,0,0, +0,0,0,152,819,0,51,0,0,0,0,0,0,0,0,0,0,130,0,0,0,0,0,229,0,0,0,0,674,0,0,0,0,0, +0,0,0,0,770,52,79,0,0,0,1666,0,409,0,0,0,0,0,0,0,195,0,688,0,0,0,0,0,0,0,0,0,0, +0,889,174,160,0,0,0,0,0,0,0,0,0,0,0,0,0,872,0,918,569,268,0,0,0,1224,0,1361,0,0, +0,0,0,0,0,0,0,374,0,0,0,0,0,731,0,0,0,0,190,0,0,0,0,0,0,0,202,506,444,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,835,0,17,1526,0,0,0,0,0,477,0,0, +994,1374,76,0,0,0,0,0,0,0,355,287,0,1389,0,0,0,0,0,0,455,384,0,0,0,264,0,0,0,0, +0,0,0,0,0,0,0,0,1001,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,851,175,359,0,0,0,0,0,0,0, +0,287,740,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,857,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +819,1402,0,0,0,0,0,0,174,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1649, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,655,573,0,0,0,0,0,0,0,0,128,351,0,0,0,0,0,0, +0,0,0,0,0,918,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,687,0,0,0,0,0,0,0,0,0,1525, +0,0,0,1009,0,0,0,0,0,0,0,340,0,0,0,0,0,0,0,0,0,0,861,0,176,0,0,0,0,0,0,0,0,0,96, +985,0,615,0,0,0,0,0,0,0,1919,0,0,0,0,0,1131,0,0,0,0,0,0,0,247,0,0,0,0,27,23,0,0, +0,0,0,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1015,0,0,0,0,0,1088,0,0, +0,0,0,1585,0,0,0,0,227,0,0,0,478,360,0,0,0,95,0,0,0,0,0,0,699,0,0,0,26,0,0,0,0, +1119,0,0,0,739,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,741,67,0,0,0,0,0,0,464,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,0,96,0,0,0,26,342,0,0,0,0,0,0,203,0,0,449,0, +0,0,0,0,0,0,0,0,0,256,311,0,0,0,0,0,0,758,0,0,0,0,0,0,0,0,827,0,0,0,0,581,64,0, +1047,0,0,0,0,0,288,0,0,0,0,0,1375,0,0,0,0,0,0,0,0,0,0,0,1309,0,0,0,0,0,0,0,0, +376,12,0,0,0,0,0,154,0,1520,0,1753,95,502,0,0,0,0,0,0,0,269,291,1197,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,1341,0,1017,0,0,0,0,0,0,0, +0,857,1810,533,0,0,1453,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,836,211,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,19,0,156,0,0,0,0,1009,0,0,0,0,0,0,0,0,0,0,0,0,0,820,0,0, +0,0,0,0,0,0,0,228,0,0,0,1131,0,1276,0,0,0,0,0,0,0,0,0,0,0,0,849,1792,0,0,389, +291,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,525,0,0, +0,453,0,0,0,0,666,0,0,0,422,0,355,0,0,0,0,165,0,260,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,865,0,0,0,0,0,0,0,1625,0,0,0,234,0,1383,0,0,0,0,0,0,0,0,306,0,0,0,802,1921, +0,0,0,0,0,0,180,0,0,0,0,1312,814,0,0,0,0,0,0,0,0,0,0,707,0,0,0,1493,11,61,733,0, +0,0,341,0,0,0,98,0,0,0,0,0,0,0,0,0,0,0,1014,0,0,0,0,0,0,0,142,102,0,0,30,0,0, +823,0,1045,0,0,0,1930,0,1512,0,0,0,0,0,0,0,87,0,1243,245,0,0,0,0,0,0,0,48,68,0, +0,0,0,0,0,0,0,126,77,625,938,0,0,351,0,0,0,174,1668,0,707,0,0,0,0,0,0,0,0,0,0,0, +403,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,282,0,0,0,0,0,0,8,44,0,0,363,115,0,0,0,0,0,0, +0,0,0,0,0,0,545,761,0,0,835,1254,0,0,0,0,930,1936,0,0,0,0,0,0,0,0,653,0,0,0,0,0, +344,0,0,1483,673,185,0,0,460,93,753,478,0,0,0,0,0,1020,0,0,0,0,0,0,0,103,0,0,0, +499,0,0,0,0,0,0,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,968,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,3,0,0,0,0,399,0,0,0,0,224,563,0,0,0,0,0,704,0,0,0,0,0,0,0,0,0,0,0, +1559,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,861,0,0,0,0,946,333,746,0,0,0,0,0, +0,0,910,0,0,0,0,0,0,0,0,0,0,0,0,0,652,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1393,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1514,0,0,0,0,201,0,510,717,0,0,528,0,0,0,0, +20,0,0,0,1251,0,0,0,1163,0,0,0,307,0,0,0,0,0,1091,0,0,0,0,0,0,0,0,0,0,0,429,0,0, +0,881,0,0,0,0,0,621,0,0,0,0,0,0,0,736,0,348,0,868,0,0,0,0,433,0,0,0,771,1495,0, +0,0,0,215,0,0,0,0,0,124,0,0,0,0,0,0,0,0,0,0,0,55,0,0,0,0,0,0,0,112,62,0,856,270, +0,572,0,0,0,0,939,0,0,0,0,0,0,0,352,0,0,0,0,0,0,0,0,0,647,0,0,0,0,10,0,0,0,0,0, +0,0,220,0,0,0,0,0,0,0,0,0,0,0,0,0,464,0,0,109,0,0,0,1746,0,0,0,515,0,0,0,566,0, +0,0,0,0,0,67,40,0,0,722,992,0,0,923,0,0,0,0,0,0,1145,0,0,0,0,0,0,0,0,0,0,0,568, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,0,0,0,0,645,0,0,328,0,0,0,0,0,0,0,0,0,0,0,0, +1363,0,0,0,0,0,1280,0,0,0,0,0,0,0,0,0,0,7,28,360,162,0,0,0,0,0,0,0,0,0,0,0,764, +0,0,833,862,0,856,0,0,0,0,0,0,736,92,0,0,948,1944,0,1479,63,590,0,0,0,1521,0,0, +0,709,0,0,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,483,0,0,0,0,1213, +0,0,0,0,29,1022,0,1712,0,466,0,0,0,0,0,0,0,0,0,0,0,0,0,731,0,0,0,0,0,0,171,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,241,0,0,0,0,0,0,0,0,0,0,0,964,2005,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1100,0,0,0,954,0,0,0,0,0,0,0,0,0,1958,0,0,34,549,994,0,0,449, +137,850,0,0,670,146,0,0,0,0,518,159,0,0,0,0,0,0,0,0,151,0,0,1027,0,0,0,0,0,0,0, +0,0,0,983,0,0,0,0,993,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,501,0,0,0, +0,0,0,0,0,0,452,0,0,0,0,0,0,0,0,0,0,233,149,0,0,0,0,0,0,0,0,582,0,0,0,801,0,0,0, +0,0,0,70,0,0,369,0,36,0,0,0,0,0,0,0,204,721,430,241,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1817,16,1078,1021,0,0, +406,0,0,0,0,0,69,0,0,0,0,0,1830,0,0,0,824,0,0,0,0,0,0,0,0,0,826,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,816,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,717,1845,0,423,0,0, +0,0,0,0,0,0,510,0,0,1048,0,0,0,618,0,0,0,520,0,0,0,0,990,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,321,0,0,0,0,0,0,0,1135,0,0,921,0,0,0,24,397,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,856,0,0,0,139,282,981,0,288,0,0,0,1890,651,56,0,0,0,0,0,0,0, +0,261,0,0,0,0,0,0,0,0,0,0,0,617,1403,0,1205,0,0,563,0,0,0,0,0,0,0,0,333,0,0,0,0, +0,369,0,0,0,0,0,0,0,0,0,622,0,0,0,1407,0,0,0,0,0,0,0,0,0,0,0,0,624,160,0,363,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,619,0,174,292,0,0,656,616,0,0,0,685,0,0,0,0,0,0,0,0,0,0,0,0,0,647,0,0,0,631,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1267,0,0,0,1797,0,0,0,1684,0,0,469,0,531, +1230,73,0,0,0,0,0,0,0,0,0,268,0,0,0,0,0,102,558,109,65,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,595,0,0,0,0,0,374,1832,0,0,0,0,0,0,16,0,405,6,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,881,0,1495,0,0,0,0,0,0,0,0,0,142,0,0,0,0,0,0,0,0,0,0,21,466,23, +257,0,0,0,0,0,0,77,404,0,0,0,0,0,0,712,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,860, +1848,0,0,652,629,0,0,0,0,13,377,0,1842,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1501,0, +0,0,1906,0,0,0,0,0,0,0,0,0,0,0,0,0,491,234,171,0,0,0,0,631,1186,0,0,0,0,0,0,0,0, +0,0,0,0,931,0,170,0,0,0,0,0,0,0,0,0,0,1587,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +765,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,424,0,0,714,0,0,0,0,685,0,0,0,0,0, +0,285,0,0,0,0,0,0,429,0,0,0,0,0,0,0,0,0,0,71,18,0,0,0,0,0,0,0,0,0,0,116,828,0,0, +0,0,0,0,289,0,0,0,0,0,0,0,0,675,0,0,0,1424,0,0,0,0,0,647,0,0,0,1334,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,36,209,0,0,0,0,0,0,0,342,0,0,0,928,0,0,0,0,0,1838,118,856,654, +318,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,915,895,454,0,0,513,1425,0,0, +0,0,0,0,791,0,153,0,0,0,0,0,0,796,909,445,345,0,0,0,0,0,0,0,0,578,0,0,0,1387,0, +0,0,555,0,0,0,0,0,0,766,0,0,0,0,0,0,0,0,0,0,541,0,0,0,0,0,0,0,0,0,0,0,0,0,880,0, +0,0,0,0,1506,0,0,983,0,768,0,0,0,0,584,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,737, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,30,426,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,462,0,0,0,385,0,398,0,0,0,0,0,0, +0,0,0,347,0,0,0,0,125,1259,644,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,469,0,0,0,0,0, +1367,0,0,0,0,0,0,0,0,0,0,0,719,0,0,0,0,0,0,0,0,0,0,0,0,0,1423,0,0,0,0,0,0,0,0,0, +749,0,0,0,0,546,645,0,0,0,0,0,0,277,0,0,1275,0,0,0,0,0,0,0,453,536,555,0,0,987, +1107,0,0,90,0,0,0,0,0,0,0,0,860,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +257,0,1768,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1071,0,0,0,0,0,0,0,0,0,0,0,0,0,83, +0,835,0,0,0,0,0,0,0,2006,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,696,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,95,1718,0,0,0,0,0,0,0,26,0,550,0,0,0,0,0,901,0,0,0,0,0, +0,822,0,0,122,0,0,0,807,0,0,0,0,0,262,0,620,601,34,0,0,170,0,0,0,0,537,0,0,0,0, +0,0,0,0,0,332,0,0,208,1909,182,261,0,0,0,1721,0,0,0,0,0,933,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,1609,0,895,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,812,0,0,942,1916,0,0,0,0, +0,0,0,778,0,0,0,137,0,1314,0,0,0,0,0,0,0,1661,0,0,0,0,0,0,0,1591,0,0,0,0,0,0, +820,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,89,0,1160,230,6,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,63,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1740,0,0,177, +170,0,1961,0,0,0,0,0,0,0,0,0,0,0,0,91,0,17,44,0,0,0,0,0,0,0,0,0,270,0,296,0,0,0, +0,0,0,0,1523,0,0,0,0,0,0,0,0,0,0,757,7,0,0,0,0,0,0,0,0,0,0,530,588,0,0,0,0,0,0, +0,0,0,786,0,0,0,0,0,580,627,88,447,57,0,0,0,0,0,0,0,0,845,735,0,0,0,0,0,31,15,0, +460,521,12,424,0,0,0,1302,0,0,0,0,0,0,0,595,0,0,0,13,548,97,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1472,452,1767,0,0,0,0,0,0,0,0,0,0,115,0,0,0,0,0,0,1543,0,1111,0,0,0,0, +1,0,359,488,0,267,0,0,0,1983,0,0,0,0,0,0,0,1155,0,1575,0,1438,31,0,0,377,101,0, +0,0,0,0,0,0,0,0,0,0,0,0,476,0,0,0,0,0,0,0,0,2023,0,0,0,0,0,1836,0,0,0,0,35,843, +0,0,0,0,0,0,0,554,0,0,0,536,625,207,0,1371,0,0,0,424,785,336,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,896,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,750,0,0,0,0,238,0,0, +0,0,0,383,0,0,0,0,0,0,0,0,603,725,11,0,0,0,0,0,0,0,0,0,476,0,0,0,0,0,1552,0,0,0, +0,0,0,0,680,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,435,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1431,0,0,13,112,0,0,356,0,0,0,0,0,0,0,0,0,0,1963,0,0,0,1244,18,0,0,0,0,0,0,867, +0,0,0,0,0,0,50,708,73,592,0,502,0,0,0,0,0,0,161,347,0,0,0,0,470,33,0,246,571,10, +0,465,614,0,237,0,0,0,0,0,24,18,0,506,0,0,0,0,0,0,33,309,0,0,0,0,0,0,0,0,0,0, +140,0,0,0,0,1056,0,0,0,1704,0,0,0,0,0,0,0,1036,0,0,0,0,0,0,0,0,0,1315,432,86, +264,524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,0,0,0,0,0,123,927,0,0,957,1149,0,0, +0,0,0,778,0,502,196,0,0,0,0,1312,0,0,0,0,0,0,0,855,0,0,0,0,0,0,0,0,0,0,45,1400, +0,0,0,1003,0,0,0,0,0,1097,0,0,0,0,0,0,0,0,545,612,0,0,0,0,0,0,0,0,0,0,0,0,54,0, +0,0,0,172,0,0,0,1029,0,0,0,0,0,0,0,0,0,568,0,0,0,732,617,0,0,974,94,989,733,0,0, +0,0,0,0,1789,0,0,665,2015,0,0,0,0,0,0,806,287,0,0,0,0,0,1539,0,0,0,0,0,0,0,0,0, +0,182,1563,0,0,0,0,0,0,0,0,0,484,0,0,0,0,0,1623,0,0,0,0,0,0,0,0,878,1833,0,1569, +0,0,0,0,0,0,0,0,93,0,715,994,0,0,0,0,0,63,0,591,0,0,0,0,0,0,0,749,0,0,0,0,547, +366,0,0,0,1747,0,0,0,0,0,0,0,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1463,0,772, +893,0,0,0,48,0,0,941,0,0,690,1785,106,440,0,0,0,0,0,0,0,0,0,0,32,0,332,216,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,852,0, +0,416,564,0,918,0,1764,0,0,3,0,0,274,0,0,0,0,501,0,0,0,0,0,0,0,851,743,0,49,0, +879,0,0,47,0,0,0,0,0,0,865,0,1202,0,0,0,0,0,0,47,272,0,0,0,0,0,0,0,0,0,0,0,1455, +0,0,0,0,891,1911,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,761,0,0,0,0,0,0,0,0,0,407,0, +183,0,0,490,0,0,0,0,0,0,0,35,731,0,0,0,0,0,0,0,819,0,0,0,0,0,0,0,0,0,0,0,0,0, +575,0,0,0,0,45,818,0,0,77,222,0,0,0,0,849,1880,0,0,0,633,0,1308,0,0,0,0,0,0,0,0, +0,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,817,0,0,0,0,0,0,0,0,0,882,0,0,0,914,0,0,0,0, +0,0,0,0,0,0,865,0,0,426,399,58,0,0,0,0,0,0,538,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,876,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,566,0,63,12,0,0,0, +0,0,0,0,0,0,0,0,0,0,3,114,0,0,0,0,0,0,0,0,576,0,0,0,0,0,0,0,0,933,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,692,0,0,0,0,0,0,0,0,0,0,0,0,752,0,0,0,0, +0,0,0,0,375,0,1011,0,0,96,0,0,0,0,0,0,0,0,0,148,0,0,0,0,0,0,0,0,0,0,0,337,56, +666,0,246,394,0,0,0,0,0,0,0,0,437,0,0,0,506,0,0,0,0,1003,0,1163,0,328,0,0,0,0,0, +0,0,0,1000,0,0,0,0,0,744,101,0,0,0,0,0,726,0,0,176,0,146,9,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,839,0,0,0,0,0,0,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,246,1931,29,0,0,1771,0,0,0,0,0,846,6,157,0,0,0,0,0,0,0,0,0,875,0,0,477, +773,177,639,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1747,0,0,0,0,158,873,0,659,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,391,0,0,0,0,0,0,0,0,0,0,0,0,668,883,0,78,628,0,0,0, +0,0,0,0,0,0,0,0,0,1460,0,962,0,0,0,0,0,460,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,199,0, +0,0,388,474,0,271,0,333,608,0,0,0,0,0,0,49,0,988,0,707,617,0,0,0,0,0,0,0,756,0, +0,0,0,0,1583,0,0,0,0,0,0,0,0,0,0,285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,344,0,0,0,0,0, +0,0,0,515,1709,0,0,0,0,0,0,0,0,404,0,0,0,0,500,0,0,0,0,0,0,0,0,0,68,216,0,0,0,0, +0,0,0,488,353,0,0,177,236,0,0,458,490,0,0,0,0,0,0,756,1504,0,757,0,1735,0,0,108, +598,0,0,0,0}; +const BROTLI_MODEL("small") +uint8_t kStaticDictionaryHashLengths[BROTLI_ENC_NUM_HASH_BUCKETS] = { +8,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,12,0,0,0,0,4,22,5,0, +4,0,0,0,0,0,0,0,0,0,0,0,0,14,6,0,0,0,5,0,0,0,0,0,0,0,7,13,0,0,4,0,0,0,0,0,0,0,0, +0,6,0,0,0,0,8,0,0,0,0,0,0,7,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,4,0,0,0,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,10,4,0,5,13,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,8,7,0,0,9,0,8,0,0,0,0,0,0,6,0, +0,9,0,0,0,11,0,0,6,8,7,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,6,8,0,0,0,0,0, +0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,9,0,0,0,8,4,13,7,0,0,0,0,0, +7,0,5,0,0,0,0,8,5,0,5,0,0,8,7,0,0,0,0,0,0,0,0,0,0,9,0,0,0,8,0,0,0,10,4,0,5,0,4, +0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,8,7,0,4,9,4,0,0,0,0,0,0, +9,0,0,0,8,5,0,0,0,6,0,0,0,0,0,0,0,0,0,7,18,0,0,0,0,4,9,0,0,4,0,6,0,0,0,6,0,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,5,8,7,0,0,0, +0,9,0,0,0,0,0,0,0,8,6,10,6,0,0,0,4,0,6,8,6,0,0,0,4,0,0,0,0,0,5,0,0,0,6,0,0,0,0, +10,0,12,7,0,0,0,0,0,4,0,0,0,0,0,5,0,0,8,7,0,0,0,0,0,0,0,0,9,5,0,0,0,0,0,0,0,0,0, +0,0,0,0,6,11,0,0,0,0,0,0,0,0,0,8,7,0,0,10,0,0,0,0,0,0,0,0,6,10,0,17,0,8,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,8,6,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7,0,0,11,4,0,5,0,0,0,0,0,0,0,0,0,0,10,5,0,6,8,5,0,0,0,0,0,0,0,0,0,0,11,5,0,0,0, +0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,9,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,8,7,0,0,0,0,0, +0,0,0,0,0,0,5,0,0,0,6,0,0,10,0,0,0,20,0,0,0,0,0,0,0,0,6,9,5,0,0,0,0,10,4,8,0,0, +4,13,0,0,0,0,0,0,0,9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,4,8,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,12,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,12,5,0,0,10,4,10,7,13, +0,0,0,0,0,0,0,0,6,0,6,0,6,0,0,0,0,0,0,19,0,0,4,12,6,9,0,0,0,0,4,0,4,11,0,0,0,0, +0,0,0,12,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,4,0,0,0,0,0,0,0,0,0,6,0,0,0,0, +0,5,0,0,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,9,6,0,0,0,0,0,4,0,4,0,0,0,0,0,0,0,0,0,4,0,0,0, +6,0,0,0,0,0,0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,6,0,0, +0,0,0,5,0,0,0,0,14,4,0,0,0,4,12,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,6,0, +0,0,0,0,0,12,0,9,6,0,0,0,0,13,0,0,5,0,0,0,0,0,4,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,13,0,9,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,5,0,0,0,6,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,8,7,8,4,0,0,0,0,0,0,0,0,0,0,0,7,0,7,0,0,0,4,0, +0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,6,8,4,0,0,0,0,0,6,0,7,0, +0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,7,0,0,0,0,0,0,9,5,0,0, +0,0,0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,9,4,0,0,0,0,0,0,0,4, +12,5,11,0,0,0,0,0,0,0,0,0,8,7,0,5,0,0,8,7,0,5,0,0,0,0,8,0,0,0,0,7,0,4,10,0,0,0, +0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +13,5,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,0,14,5,0,0,0,7,0,0,10,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,6,0,4,0,5,0,0,0,0,8,5,0,0,0,0,0,0,9,5,9,0,0,0,0,0,0,0,0,6,9,0, +0,4,0,0,0,7,0,0,0,6,0,0,10,4,0,0,0,0,0,6,0,0,10,0,0,0,8,5,0,0,0,0,0,0,0,0,10,0, +0,0,0,0,18,4,12,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,8,7,0,0,0, +0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,4,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, +0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,8,0,0,0,0,0,0,6,0,0,0,4,10,5,0,0,0,0,0,0,0,0,0,0, +0,4,8,7,0,0,8,6,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0, +0,0,0,8,6,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0, +0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,8,7,0,0,0,0,8,0,12,6,0,6,0,0,0,0,9,7,11,7,0,0,0, +0,0,0,0,0,0,0,0,0,11,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,10,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0, +0,0,0,6,0,0,0,7,0,4,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,14,0,0,0,0,0,8,4,0,4,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,20,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,12,5,0,7,0,5,0,0,10,0,0,7,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,6,0,4,9,7,0,0,0, +0,0,7,0,0,0,0,0,0,10,0,9,0,9,0,0,0,0,0,0,0,0,4,9,0,0,0,0,6,0,0,0,0,0,0,0,0,11,4, +0,6,0,0,0,0,0,0,8,0,8,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,0,0,0,0,0,0,0,0,13,6,0,0,11, +0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,6,18,0,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0, +0,5,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,6,0,0,0,0,9,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,11, +4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,4,0,0,0,0,8, +6,0,0,0,0,0,0,9,6,0,0,0,0,0,4,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0, +0,6,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,6,0,6,0,0,10,6,0,0,0,7,0,0,8,0,8,7,0, +0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,0,0,0,0,5,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0, +0,0,0,8,7,0,0,0,0,0,0,0,0,12,0,12,0,0,0,11,6,0,5,0,0,12,0,12,5,0,7,11,6,0,0,11, +0,0,0,12,0,0,4,12,7,8,6,0,0,0,0,8,5,0,0,0,0,0,0,0,4,11,0,0,6,0,7,0,0,0,0,0,0,0, +5,0,6,0,0,0,0,8,0,10,0,0,0,0,0,0,0,0,0,0,0,9,7,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0, +0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,0,0,10,0,0,5,0,0,12,6,0,0,0,0,0,0,10,6,0,0,0,0,8, +6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,5,0,0,0,0,11,0,10,6,0,0,8,6,0,0,0,6,0,7,10,6,0, +0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,10,7,0,0,0,0, +10,6,0,0,0,0,0,0,8,5,11,0,8,4,0,0,0,4,0,0,0,0,9,4,8,0,0,0,0,0,0,0,11,6,0,0,0,0, +10,7,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,7,0,0,0,0,9,6,0,5,0,7,0,0,0,0,0,7,0,0,11,0,0, +0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, +0,0,0,0,13,0,8,6,13,0,0,0,11,7,0,7,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,6,0,0,9,6,0,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0, +0,0,0,0,0,5,9,0,0,0,0,0,0,0,0,0,0,4,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,9,7,0,7,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0, +5,11,5,0,0,0,0,0,0,0,0,0,4,0,7,0,6,0,0,0,6,20,0,0,0,10,7,0,5,14,4,0,0,0,0,0,0,0, +0,0,6,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0, +0,0,6,0,4,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0,0,11,6,15,0,0,0,0,0, +10,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,7,0,0,0,0,0,0,0,0,9,7,13,0,0,0,0,0, +0,7,0,0,8,6,0,0,0,0,0,0,0,0,9,4,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,5,0,0,0,0,0,0,0,0,0,0,0,0,8,5,0,4,0,0,0,0,0,0,0,0,0,0,12,6,8,0,12,0,0,7,0,0,0, +0,0,5,10,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7, +14,0,0,0,0,0,0,0,0,0,0,0,0,5,0,5,8,7,10,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,18,6, +14,7,0,0,0,0,0,0,0,0,11,6,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,11,7,0,0,10,7,0,0,0,6,8,6,0,0,0,0,0,0,0,6,0,0, +19,0,0,0,9,5,0,0,0,0,0,0,11,7,0,0,0,7,0,6,0,0,11,0,0,0,0,4,8,0,0,0,0,0,0,0,0,6, +0,0,0,0,0,6,0,0,8,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,7, +0,7,0,0,0,7,15,0,0,5,0,0,0,0,10,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,7,0,0, +0,0,0,0,0,0,9,6,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +11,7,0,0,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0, +0,0,5,0,4,0,0,0,4,0,4,0,0,0,0,0,0,0,0,0,6,0,0,0,0,11,6,0,0,8,5,14,0,0,4,0,0,0,7, +17,0,0,0,0,0,0,0,13,5,0,0,0,0,0,5,0,0,0,5,0,0,0,0,16,6,0,4,0,0,0,0,0,0,12,0,0,0, +0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,5,0,5,0,6,10,0,12,0,0,0,0,0,0,0,0,7,0,0,0,0,8,4, +0,0,0,0,0,0,0,0,0,0,8,7,0,0,8,0,0,0,8,0,0,6,0,7,0,0,0,5,0,6,0,4,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,22,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0, +0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,18,0,0,0,9,4,0,0,8,0,9,7,0,0,0,0,0,0,8,6,0,0,0,0, +0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,9,7,0,0,0,6,0,0,14,0,0,0,0, +0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,7,10,4, +0,6,0,0,0,0,0,0,8,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,9,6,0,0,0,0,0,0, +0,0,11,6,12,7,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,0,9,6,11,6,0,0,0,0,9,5,0,0,0,0,0,0, +0,6,8,5,0,0,0,0,8,0,10,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9, +5,10,7,0,0,0,5,8,7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,4,8,7,0,0,0,6,0,0, +0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,22, +0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,5,0,0,0,0,0,0,0, +0,0,0,0,0,17,0,0,6,0,6,12,4,19,6,0,0,0,0,16,0,0,0,0,7,15,7,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,4,10,4,0,0,8,7,0,7,0,0,9, +4,0,6,0,0,0,4,0,5,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,7,10,0,0,0,0,0,11,7,0,0, +0,0,12,6,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8, +0,0,0,0,0,0,0,0,0,10,4,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,8,7,0,0, +0,0,0,0,0,6,0,0,0,4,0,0,11,4,0,0,12,7,0,0,0,0,9,0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0, +4,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,9,4,0,6,0,0,0,0,0,4, +0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0,0,0,5,0,0,0,0,0,0,0,0,0,7,9,6,0,7,0, +0,0,0,0,0,0,6,0,0,0,0,8,6,0,0,0,0,10,6,11,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,5,0,4,8,0,0,0,0,0,9,7,0,0,0,0,0,0, +13,5,0,0,0,0,8,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,5,0,0,11,7,0,0,0,0,0,0,8,6,0, +0,0,0,0,7,0,4,0,0,0,0,0,0,0,5,0,6,0,5,0,0,0,0,0,0,0,0,0,0,0,0,10,4,9,0,0,0,0,0, +0,4,0,0,0,0,10,5,10,7,0,0,0,0,0,0,0,0,16,7,0,0,0,0,0,7,0,0,0,0,11,0,0,0,0,0,0,0, +0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,5,0,4,0,0,0,7,0,0,0,0,0,0,13,0,0, +0,0,0,0,0,0,0,0,7,0,4,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,13,7,0,7,0,4,16,0,0,0,0,6,8,7,9,7,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,6,0,0,8,5,0,4,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,11,7,0,0,11, +0,0,0,0,0,9,5,0,4,0,0,0,0,9,7,8,6,0,0,0,0,0,0,10,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0, +0,7,0,0,0,0,0,0,0,0,0,0,0,4,10,6,0,7,0,0,0,0,0,0,0,5,0,0,0,0,0,0,10,7,10,0,0,0, +0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,7,0,6,8,7,12,4,0,0,0,0,0,0,0,5,14, +0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0, +6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0,0,20,4,0,0,0,7,0,6,0,0,0,0,0,0,0,0,8,0, +0,6,15,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,12,0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5,0,0,0,0,0,0,8,6,0,0,18,0,0,0,10,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,9,6,0, +6,0,0,0,0,0,0,0,0,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,9,0,9,0,0,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,9,5,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,10,0,0,0,0,7,0,0,0,0,0,0,0,0,0,7,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,0,8,0,0,0,16,0,0,0,0,0,0,0, +0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,8,0,0,0,11,0,0,0,0,0,0,0,0,0,0, +6,0,0,0,0,11,0,0,0,9,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,7,0,7,0,6, +0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6, +0,0,0,0,0,0,0,6,0,0,18,0,8,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,7,0,4,0,0,0, +0,0,0,0,0,0,0,8,0,0,0,0,0,16,0,0,0,0,0,16,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,18,0,0,0,0,0,0,0,0,0,9,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,4,0, +0,0,0,0,0,0,0,9,4,0,0,0,0,12,5,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,12,5,0,0,0,0,0,0,0,5,0,0,10,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,9,0,0,0,11,0,0,6,0,6,0,0, +0,7,0,0,0,0,0,0,8,0,0,0,0,6,0,0,0,0,0,0,19,0,0,0,12,0,9,0,0,0,0,0,10,7,0,0,0,0, +0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,16,7,12, +0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,6,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,10,5,0,0,0,0,0,0,0,4,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,0,4,0,0,9,0,0,0,8,0,12,4,0,0,0,0, +0,4,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,5,0, +0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,8,6,0,6,0,0,0,0,0,0, +0,4,0,0,0,0,0,6,0,0,9,0,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,10,6,0,0,0,0,8, +6,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,7,0,6, +10,7,0,0,10,5,11,6,0,0,0,0,0,7,16,0,0,0,0,6,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5,0,0,0,7,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0, +0,0,0,0,0,8,7,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,12,7,0,7,0,0,0, +0,0,0,0,6,0,0,0,0,9,0,0,0,23,0,0,0,0,0,10,5,0,0,0,0,0,0,0,0,0,4,0,0,11,7,10,0,0, +0,0,0,0,0,0,0,0,0,0,6,0,0,8,7,0,7,0,0,8,7,8,0,0,0,0,0,0,0,0,0,0,0,14,5,0,0,0,0, +0,0,0,0,18,6,8,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,11,0,0,0,9,7,12,6,0,0,0,0,0,0,0,0, +0,0,12,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,7,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,6,8,7,0,0,0,6,10,0,0,0,9,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,6, +10,7,0,0,0,7,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0, +0,0,0,8,7,8,6,0,0,11,7,10,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,4,8,7,0,0,0,0,0,0,0,0, +0,5,0,0,13,0,0,0,0,5,0,0,9,7,0,0,0,0,0,0,0,4,0,0,11,0,0,7,0,0,0,0,0,0,0,0,0,6,0, +0,0,0,0,0,12,7,19,0,8,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,10,6,8,0,0,0,0,0,0,0,0,0,0, +6,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,7,0,0,12,0,0,0,0,6,9,6, +14,0,0,0,0,0,0,6,0,5,0,0,8,7,0,0,0,6,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,4,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,0, +7,0,0,10,0,9,7,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,12,6,0,0,0,0,0,5,0,6,0,0,0,0, +0,0,0,0,0,0,0,6,0,0,0,0,9,7,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,11,7,0,0,13,7, +0,0,0,0,0,0,0,0,12,0,0,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,6,11,5,0,5,13,0,8,0, +0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,11,5, +9,6,0,0,0,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,10,0,0,0,8,5,0,0,9,0,0,0,8,7,9,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,0, +0,11,0,13,6,0,0,9,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0, +0,0,0,0,0,5,21,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,5,0,0,0,0,0,0,0,0,10,0,8,0, +0,6,0,0,0,4,0,0,9,0,0,0,0,0,0,0,0,0,0,4,0,0,8,6,0,6,0,7,10,0,8,4,0,4,0,0,0,0,0, +5,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,6,12,0,0,7,0,0,0,5,0,0, +0,0,0,0,0,0,0,6,0,0,8,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +15,7,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,24,7,0,0,0,0,0,0,0,0,0, +7,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,4,12,0,0,7,0,0,0,0,0,5,0,0,0,0,0,0,0,0,15,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,8,0,0,0, +0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,9,0,9,6, +0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,8,4,0,7,0,0,0,0,0,0,0,0, +22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,4,0,7,0,0,21,7,0,7,9,6,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,8,0,0,6, +0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,23,0,0,0,0,7,0,0,0, +4,0,0,0,0,0,0,0,0,9,4,11,7,0,5,0,0,0,0,11,0,0,4,20,0,0,0,0,0,0,0,0,0,0,0,11,5,0, +7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +21,0,0,0,0,0,0,7,0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,11,6,0,0,0,0,0,0,0,0,9,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,5,0,4,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0, +0,0,0,10,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,7,0,0,11,7,0,0,0,0,0,0,0,4, +0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,8,7,0, +0,0,0,0,0,0,0,0,6,0,0,21,6,0,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,14,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,8,0,0,7,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0, +0,0,0,8,7,0,0,11,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0,7,13,7,10,4,0, +0,0,6,0,0,0,0,0,0,0,0,0,5,10,0,0,0,0,0,0,5,0,0,0,7,0,0,0,0,0,0,8,4,0,0,0,0,0,6, +0,0,0,0,0,0,0,0,0,0,12,7,0,6,0,0,10,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0, +0,0,0,0,7,0,0,8,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,10,5,0,6,0,0,0,0,0,4,0,0,0,0, +0,0,0,0,0,4,0,0,0,0,9,0,11,4,0,0,0,6,0,0,0,5,12,7,0,5,0,0,0,0,0,4,0,0,0,7,0,0,0, +0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,13,6,10,0,0,0,17,0,0,4,0,0,0,0,0,6,0,4,0,5,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,11,7,0,0,0,7,0,0,0,6,0,0,0,0,0,0, +0,6,0,4,0,0,0,0,8,0,0,0,0,5,0,0,0,0,0,4,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,9,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,0,0, +0,0,7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,16,4,0,0,11,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +8,7,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,8,6,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10, +7,0,0,0,0,0,0,9,0,0,0,0,0,0,0,12,5,10,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0, +5,18,7,0,0,14,0,0,0,0,0,0,0,9,4,0,7,0,0,0,0,0,0,0,5,0,0,0,6,0,0,0,6,0,0,0,0,0,0, +8,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0,7,0,0,0,0,0,0,11,0,0,0, +10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,14,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +11,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,14,6,0,0,0,0,11,4,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,10,7,0,6,0,0,9,0,9,5,0,0,0,0,0, +0,0,0,10,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,8,5,0,0,0,0,0,0,0,0,0,0,11,4,0,6, +0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,12,4,0,6,8,6,0,0,0,0,0,0,0,0,0,0,8,0,0,5,0,0,0,0,0,0,0,7,0,0,13,0,0,0,0,0,0,0, +0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,12,7,0,6,0,0,0, +0,0,0,0,0,0,0,0,0,13,4,0,7,0,0,0,7,0,7,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,0,0,0,0, +9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,10,6,21,5,0,0,0,0,8,0,0,0,0,4,0, +7,0,0,0,0,0,0,11,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,4,0,0,0,0,0,0, +0,7,9,6,11,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,7,10,0,0,0,0,0,0,6,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,19,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,9,4,10,4,0,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,9,7,9,7,10,4,0,7,0,0,0,0,0,0,0,6,12,0, +0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0, +0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,8,0, +0,0,0,0,0,5,0,0,8,7,0,0,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0, +0,0,0,0,4,0,0,8,0,0,6,0,0,0,7,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,7,9,7,0,0,0,4,8,0,0,0,0,6,11,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,10,0,0,0,0,0,0,0,13,4,0,0, +12,6,0,6,0,0,0,0,8,7,0,7,0,0,0,0,0,6,0,0,0,0,0,0,12,6,0,4,0,0,0,0,0,0,0,0,0,0,9, +7,22,0,0,0,0,4,0,0,0,0,0,6,0,0,0,4,0,0,9,0,0,6,0,0,24,7,0,7,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,10,6,0,5,0,0,0,0,0,0,0,7,0,0,8,0,0,0,0,0,0,0,10,5,0,0,0,0,0,0,0,0,0,7,0, +7,0,0,0,0,0,0,13,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0, +0,0,0,0,0,7,12,0,9,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,8,0,0,0,0,0,0,0,0,4,0,0,0,7,0, +0,0,0,8,7,0,0,0,0,0,0,0,0,0,4,18,0,0,0,0,0,10,0,0,5,0,0,11,0,0,0,0,0,0,5,0,6,0, +0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0, +4,0,0,0,0,0,0,10,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0, +0,0,0,5,8,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,20,7,0,0,0,0,0,0,0,0,0,0,0,4,9,0,12, +6,8,0,14,7,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,10,0,0, +0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,9,6,0,7,12,0,0,0,0,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7, +0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,4,0,0,9,0, +12,6,0,5,0,0,0,6,0,4,0,6,0,0,0,0,0,0,0,0,10,7,0,0,0,0,0,0,8,0,0,0,0,4,0,0,0,0, +10,0,0,0,0,0,0,0,8,6,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,5,0,0,0,0,0,0,0,0,0,0,0, +6,0,0,12,6,20,5,0,0,0,0,0,0,0,0,0,0,0,0,9,5,0,5,0,0,0,6,13,7,0,0,0,0,15,6,0,0,0, +6,0,0,13,7,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,5,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0, +10,6,0,0,0,0,0,6,0,0,0,0,9,0,0,0,0,0,19,6,0,0,0,0,0,0,0,0,0,0,13,0,11,0,0,0,0,0, +0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,0,10,0,0,6,0,0,0,0,8,0,0, +0,9,0,15,4,0,6,0,0,0,0,0,6,12,0,0,0,0,0,0,0,14,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, +0,0,0,0,0,8,7,0,0,0,0,0,6,10,0,0,0,0,0,0,0,0,7,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,7,10,5,0,0,0,0,8,0,0,0,0,4,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6,12,0,0,0,10,7,0,5,0,6,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,6,0,4,0,0,0,0,0,7,0,0,0,0,0,0,0,4,9,6,0,0,0,7,0,0,0,0,0,0,0,0,8,6,0,0, +0,0,0,0,0,4,12,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,7,0, +0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,12,6,0,6,9,4,0,0,8,4,0,6, +0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,8,0,0,6,13,4,0,5,8,0,0,0,0,0,0,0,8,0,0,0,10,5,0,0,9,0,0,0,0,0,0,6,0,0, +24,0,0,0,0,0,0,0,8,0,0,7,0,0,12,0,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0, +6,8,0,10,0,9,7,0,0,0,5,0,0,0,0,0,0,0,4,8,5,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,4,0,0,0,0,0,6,0,0,0,0,0,5,0,0,0,0,8,0,0, +0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,10,4,0,0,0,0,0,0,0,6,0,0,0,4,20,0,0,7, +10,6,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,9,6,0,0,0,0,0,0,0,4, +12,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,9,4,0,5,0,0, +0,0,0,0,0,6,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6,9,0,0,0,0,7,0,0,0,0,0,6,0,5,0,0,0,0,0,0,0,0,9,0,0,0, +0,6,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,17,7,0,0,13,6,14,6,0,0,0,0, +8,0,0,0,0,0,0,7,12,7,8,7,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,4,0,0,0,0,0,4,0, +0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,12,4,0,0,10,7,0,0,0, +0,0,0,10,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,0,0,6, +0,0,0,0,0,0,0,0,8,7,12,0,0,0,0,0,0,6,0,6,0,4,0,0,18,6,0,0,0,6,0,0,0,0,0,6,10,6, +0,0,0,0,0,0,8,7,14,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19, +0,0,0,8,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,8,7,0,0,10,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, +0,0,9,4,8,0,0,0,0,0,0,4,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,0, +0,6,0,0,9,7,0,0,0,0,0,5,0,0,0,0,8,7,0,0,14,0,0,0,0,6,0,0,0,0,0,0,9,6,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,5,0,7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0, +0,0,0,6,0,0,0,6,0,4,0,0,0,0,0,4,0,0,0,0,12,0,0,7,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0, +0,12,0,16,6,0,0,0,0,0,0,11,7,0,4,8,7,0,0,0,0,0,6,0,0,0,0,16,0,0,0,0,6,0,0,0,0,0, +0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,10,7,0,0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0, +0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,13,4,0,0,10,0,0,0,0,0,0,0,0,0,19,0,0,0, +0,0,0,0,0,0,0,0,0,0,8,6,22,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0, +5,0,0,0,0,0,5,0,0,0,0,0,5,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +4,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0,0,18,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,14,7,0,0,11,5,0,0,0,5,0,0,0,0,12,5,0,0,0,0,0,0,0,0,0,0,24,6,0,0, +0,7,0,4,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,7,0,4,0,0,0,0,8,7,0,0, +9,6,0,0,14,5,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,12,6,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,7,0,0,0,5,0,0, +0,0,12,7,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,6,0,0,13,7,0,0,0,0,0,0,14,0,11,4,0, +0,0,4,0,0,0,0,14,5,0,0,0,0,0,5,11,5,0,0,0,0,22,5,0,0,0,0,0,7,0,0,0,0,0,4,0,0,0, +4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,17,0,10,0,0,0,8,0,0,0,19, +5,18,7,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,10,6,0,6,0,0,0,0,10,4,0,4,0, +0,0,0,0,0,14,7,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,0,9,6,12,0,0,6,0,0,0,0,0,0,0,0, +12,0,10,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,4,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,13,0,9,7,0,0,0,0,0,0,0,0,0,0,0,7,9,7,0,0,8,0,0,0,0,0, +22,0,0,0,0,0,0,0,23,6,14,0,0,0,0,0,0,7,0,0,0,0,11,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0, +0,0,10,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,8,5,0,0,0,0,0,0,0,0,0,7,11,6,21,0,0,0,0,0, +0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0, +0,0,0,0,0,0,0,4,9,7,0,0,0,0,0,0,12,0,0,0,0,7,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,9, +0,0,0,20,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,11,7,0,0,0,0,0,0,0,6,15,0,0, +0,0,0,0,0,0,0,0,0,0,0,12,4,0,5,0,0,0,0,0,0,11,7,17,6,0,0,0,0,0,0,15,6,0,7,0,0,0, +0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,6,0,5, +0,0,11,0,11,7,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0, +17,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0,8,7,9,6,0,0,14,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0, +8,7,0,4,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0, +0,0,0,5,0,4,0,0,8,7,0,6,12,5,0,7,18,7,0,0,8,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0, +10,0,11,0,0,0,0,0,0,0,0,0,0,0,9,0,0,4,0,6,0,7,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0, +7,0,0,0,0,8,0,0,0,15,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,0,0, +0,0,6,0,0,0,0,23,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,5,0,0,0,0,0,0,8,6,0,0, +0,0,0,0,12,7,9,7,0,0,10,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,9,0,8,7,0,0,0, +6,0,6,0,4,0,5,0,0,0,0,0,5,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,7,10,5,0,0,11,6,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,4,9,7,0, +0,0,0,11,7,0,0,0,0,0,5,0,0,0,7,0,0,0,0,23,6,11,4,0,0,0,0,0,0,9,0,0,0,10,6,0,0,0, +0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,10,6,0,0,0,7,0,0, +0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13, +6,11,7,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0,6,0,0,0,5,0,6,0,6,0,0,0,0,0,0,0,0,0,0, +0,6,0,0,0,0,8,7,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,4,10,0,8,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,10,6,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0, +0,0,0,0,0,0,10,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,11,6,0,4,0,0,14,5,0,7,0,0,0,0,0,6,16,0,0,0,0,0,0,0,10,0,0,7,15,0,0,0,11,7,0,0, +0,0,0,0,0,0,0,0,8,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,5,0,0,0, +0,8,0,0,6,0,0,0,0,0,0,9,5,0,0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,6,0, +0,0,0,0,0,0,7,0,0,0,0,15,7,0,0,0,0,8,0,0,0,14,0,0,0,0,0,0,0,16,7,0,0,0,0,0,7,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,12,6,11,7, +9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13, +7,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,12,0,10,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,8,0,0,5,8,7,10,6,0,0,0,7,0,0,0,0,12,6, +0,0,9,0,0,0,12,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,10,0,0,0,10,5,0,0,0,0,0,0,9,6, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,6,0,0,9,5,0,4,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,9,0,0,5,0,0,8,7,8, +6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,10,0,9,4,0,0,0,0,0,0,0,6, +11,0,0,0,0,0,0,0,0,0,0,0,8,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,8,7,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0, +0,0,0,10,0,0,0,8,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0, +0,0,8,4,0,5,0,0,0,0,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,8,5,0,0,0, +0,0,0,0,7,0,0,0,6,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,0,6,0,7,0,0,0,0, +20,0,0,0,0,0,0,0,0,0,0,7,9,0,0,0,0,0,0,6,0,6,0,7,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0, +0,0,0,14,7,0,0,0,5,0,0,22,4,10,0,0,0,0,0,0,4,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,11,5,13,0,0,0,0,0,0,0,0,0,8,0,0,7,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,10,7,0, +0,0,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,7,0,7,14,6,0,0,0,0,9,5, +0,0,0,0,0,6,0,0,0,5,10,0,8,6,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,6,0,0,8,4,0,6,0, +0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7, +14,0,0,5,0,0,18,0,8,4,0,6,0,0,20,0,13,0,0,0,0,7,0,4,0,0,0,0,0,4,8,4,0,0,0,0,0,6, +0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,0,14,0,0,0,0,0,9,7,0,0,9,0,0,0,0, +0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,20,0,14,0,0,4,0,6,8,5,0,0,0,0,0,7,0,0,0,0,0,0, +0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,10,4,12,7,0,6,0,0,9,7,10,5, +0,0,8,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,18,0,0,0,14,7,0,0,0,0,0,4, +0,0,0,0,0,0,17,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,4,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0, +0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,8,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,5,0,7,0,0,0,0,0, +7,0,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,8,5,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,5,0, +0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,23,0,0,7,0,0,0,0,0,0, +0,0,0,0,0,0,0,4,0,0,0,0,0,0,12,7,8,4,0,0,0,0,0,0,0,0,0,6,0,0,9,5,0,0,0,7,0,0,0, +0,0,0,0,0,0,4,10,0,0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,5,0,0,18,7, +0,0,8,0,0,5,0,0,10,0,0,0,0,0,0,6,0,0,0,0,0,5,0,7,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0, +6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,6,0,0,10,0,0,5,10,4,0,0,12,0,0,0,0, +6,22,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,7,0,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,6,0,7,0,0,0,6,0,6,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0, +0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,16,6,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,12,7,0,0,0,0,9,0,0,0,0,6,0,0,11,0,0,0,0,0,13,0,9,6,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,0,0,10,7,0,0,0,7,0,6,0, +0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,11,0,15,0,22,7,0,4,0,6,0,0,0,0,0,7,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0, +18,0,0,0,0,0,0,0,0,0,14,0,0,4,0,0,0,0,8,7,9,0,0,0,0,0,9,0,0,0,14,0,0,0,0,0,0,0, +0,0,11,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,7,0,0,0,6,0,6,0,0,0,0,8,0,0,0,0, +0,11,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,9,4,0,0,0,0,0,4,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,8,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0, +0,0,0,0,0,0,8,6,0,0,9,5,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,5,0, +0,10,6,9,0,0,0,0,6,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0, +11,7,12,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,4,0,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0, +0,0,0,0,6,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,5,0,0,10,6, +0,0,0,4,0,7,13,0,0,4,0,0,11,4,0,6,0,0,0,0,0,6,8,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,5,0,0,0,0,12,6,0,0,0,0, +11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,11,5,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8, +7,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0,6,17,0,9,0,10,6,0,6,12,0,0,4,0,0,0, +0,0,0,0,0,0,0,8,5,12,7,0,4,0,0,0,0,0,0,0,0,0,0,11,0,9,0,10,6,11,5,0,7,0,0,8,0,0, +7,0,4,0,0,0,7,0,0,0,0,0,0,8,6,0,0,0,6,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,6,0, +0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,11,0,0,0,0,6,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,10,0,0,0,0,0,8,6,0,0,0,0,0,6,12,0,0,0,0,0, +0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,6,0,0,16,0,11,5,0,0,0,0,0, +0,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,9,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,10, +7,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,0,9,5,0,0,0,0,8,0,9,0,0, +0,0,0,0,0,0,7,10,0,13,0,0,6,0,0,0,0,0,0,0,0,0,6,9,4,0,0,0,0,0,0,10,0,0,0,0,0,10, +0,0,0,0,0,0,0,10,6,11,0,0,0,0,0,9,0,0,0,0,0,0,4,0,0,0,0,0,0,10,5,0,0,0,0,0,6,0, +0,0,0,0,0,18,4,0,7,0,0,0,0,0,0,24,0,8,6,0,7,0,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0, +0,8,5,0,0,0,0,10,7,0,6,0,0,0,0,0,0,0,0,8,5,10,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0, +6,0,0,8,7,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,4,0,5,15,0,0,0,0,7,0,7,0,0,0,0, +0,0,0,0,0,6,10,5,0,0,0,6,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,12,0,0,0,0,0,0,0,0, +0,0,5,0,0,0,0,0,0,14,4,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,11,0,10,4,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,7,0,0,0, +0,0,0,0,0,0,0,0,7,13,7,0,0,0,0,0,0,0,5,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0, +0,0,0,0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,8,0,10,6,0,4,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0, +0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,9,7,0,0,0,0,0,6, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,0,6,0,0,0, +0,0,0,0,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0,0,0,5,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,11,0,0,0,0,6,0,0,0,0,0,0,0,6, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0, +6,0,0,0,0,0,0,0,6,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0, +0,0,0,0,0,0,0,6,0,6,0,0,0,5,0,0,0,0,0,0,0,5,0,0,10,0,11,5,0,0,0,0,0,0,14,7,9,7, +0,6,0,0,0,0,0,4,0,0,0,0,0,0,11,7,0,6,0,0,0,0,0,0,9,7,0,4,0,0,0,7,0,0,0,0,0,5,0, +0,0,0,0,5,0,0,0,7,0,0,0,0,0,5,0,0,0,0,17,5,0,0,8,0,0,0,0,6,9,4,0,0,0,0,0,0,0,0, +8,7,11,7,9,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,6,9,5,0,0,8,6,0,0,0,5,0, +0,0,0,9,0,0,0,9,6,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0, +0,0,0,0,4,0,0,0,0,10,0,0,0,0,0,0,0,0,4,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,4,0,0,0,5,0,0,0,0,0,7,0,0,0,0,0,7,13,5,0,0,0,7,0,0,0,0,0,7,9,6,11,7,0,7,0,0,0, +0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,8,5,0,0,0,5,9,4,0,0,0,0,0,0,0,0,8,4,0,0,0,0, +24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0, +0,0,0,0,6,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0,5,11,6,0,4,0,7,20,0,8,5,9,5,9,0,0,6, +0,0,0,0,0,0,0,0,0,0,0,7,23,5,0,0,8,4,0,0,10,0,0,6,0,5,0,0,0,0,0,0,0,0,0,0,0,7,0, +0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,9,0,0,0, +10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0, +6,0,0,0,0,14,0,18,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,9,6,0,4,0,0,0,0,0,0,8,4, +11,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,12,0,10,7,0,0,10,0,0,0,0, +0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,6,0,0,0,0,8, +6,10,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,4,0,6,0,4,0,0,0,0,0,5,0,0, +0,0,0,0,0,0,0,7,0,0,0,7,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,17,7,11,0,0,0,0,0,0,0,0,0,0,4,12,6,0,0,0,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0, +0,5,12,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,6,0,0,20,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,4, +0,0,0,5,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,6,0,4,13,0,0,7,0,0,0,0,0,0, +0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,12,6,0,7,0,0,0,0,10,0,23,6,0,0, +0,4,0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +10,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,11,0,9,7,0,0, +0,0,0,0,0,0,0,0,9,7,0,4,0,0,0,0,8,7,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, +0,0,0,0,0,6,0,0,10,7,10,5,0,0,8,0,8,0,0,0,0,0,0,4,0,5,10,0,0,0,0,0,0,0,9,0,0,6, +0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,11,7,0,0,0,0,0,0,0,0,9,4,0,0,0,0,0,6,0,0,8, +7,0,0,0,0,0,5,0,0,0,0,0,0,0,0,10,0,0,0,0,5,0,4,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,24,7,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,6,0,0,9,0,0,0,0,0,0,7,0,6,13,0,8, +0,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,6,0,0,0,0,8,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0, +4,0,0,0,0,0,4,0,0,0,0,0,0,0,6,8,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,4,0,4,0,0,0,5,0,7,0,0,10,0,10,7,0,0,12,5,0,0,9,0,0,0,10,0, +0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0, +12,0,0,0,0,0,8,5,13,6,0,0,0,0,0,0,9,4,0,0,0,0,8,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0, +0,0,6,0,0,14,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,17,6,0,0,0,0,12,6,0,0,0,0,8,0,0,7,0, +7,0,4,9,0,0,6,0,0,0,6,0,0,0,0,0,0,8,7,0,0,0,0,0,0,11,0,0,4,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,18,7,0,4,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,6,0,0,0,0,0, +0,0,0,12,5,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,8,0,11,7,0,0,0,0,0,0,0,0,0,4,0,0,0,0, +11,0,0,0,0,0,0,0,21,0,0,6,10,0,0,0,0,0,9,0,10,0,0,0,0,0,11,0,0,0,0,6,0,0,0,0,0, +5,0,0,0,0,0,0,10,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,4,0,0,23,7,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,9,7,0,0,0,7, +0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,6,0,0, +11,6,0,0,0,0,0,0,0,6,0,0,0,0,10,7,0,0,9,4,0,0,11,0,8,5,0,0,0,7,8,5,22,0,0,0,9,6, +0,0,0,0,0,0,0,6,10,4,0,0,0,0,0,7,9,4,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0, +0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,7,0,6,0,0,0,0,0,7,0,0,0,0,0,0,0,6,0,6,0,4,0,0, +0,0,0,0,0,7,0,7,0,4,13,0,0,0,0,0,8,0,0,0,0,7,0,0,0,0,0,0,11,6,0,7,0,0,0,0,9,0,0, +0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,8,0,0,0,0,0,8,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,6,0,0,0,0,13,5,8,0,0, +0,0,0,0,0,14,0,0,6,0,0,0,0,0,0,0,0,0,7,0,0,17,6,0,0,0,0,13,4,0,0,9,6,0,0,10,5,0, +0,10,5,0,0,0,0,13,0,0,0,0,6,0,0,0,0,0,0,10,0,12,0,0,0,0,0,0,0,0,0,0,0,8,4,0,4,0, +0,0,4,0,0,0,0,0,4,0,0,12,0,0,5,9,4,0,0,0,0,0,0,0,0,0,5,8,5,0,0,0,7,0,0,0,0,8,7, +0,0,0,6,12,5,0,0,0,5,0,0,0,5,0,0,0,0,0,4,12,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,7,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0, +0,9,6,0,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,4,11,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0, +0,0,0,0,12,7,0,0,0,7,10,7,0,0,11,0,0,0,0,0,0,0,0,0,11,7,0,0,0,6,0,0,11,0,0,0,0, +0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,22,0,10,7,0,0,8,5,0,0,0,0,0,5,0,0,0,0,0,0, +0,0,0,0,9,6,8,7,0,6,0,0,0,0,0,5,0,0,0,0,0,0,8,7,0,0,0,0,9,7,0,0,0,6,0,0,8,7,0,0, +0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,4,0,5,0,0,0,4,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,6,0,0,0,0,0,0,0,4,0,0,0,0,0,0,9, +6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,5,0,0,0,0,14,0,0,0, +9,0,0,0,0,0,0,0,0,0,9,7,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,12,0,0,0,0,0,12,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,10,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,6,0,0,0,0,0,0,9,6,0,0,0,0,0,6,0,0,0,0,0, +0,0,0,0,0,9,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0, +0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,6,0,7,12,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0, +0,7,0,0,8,6,0,0,0,0,10,7,0,0,0,0,0,0,0,6,0,0,0,0,0,6,12,0,0,0,0,0,0,0,0,6,0,0,0, +0,0,6,0,0,0,6,0,0,0,0,0,6,16,0,0,0,0,0,0,0,0,0,9,0,17,0,14,7,8,0,0,0,0,0,0,6,0, +0,0,0,0,0,0,0,0,0,11,0,0,6,8,7,0,6,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,0,0, +9,0,0,0,0,7,0,0,0,0,11,5,0,4,9,6,8,0,0,0,0,0,0,0,0,0,10,0,11,7,0,0,0,0,0,0,0,0, +9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11, +0,0,0,12,0,0,0,0,0,10,5,0,4,0,0,0,0,0,7,10,6,11,6,0,0,0,0,0,0,0,0,0,0,0,0,17,0, +0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,8,0,0,4,0,0,0,6,0,0,0, +0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,4,0,0,0,0,9,6,0,0,0,4,0,0,0,0,0,4,10,7,0,7,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0, +0,0,0,0,0,0,6,0,0,0,6,0,6,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,18,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,13,0,0,0,0,0,10,0,0,0,0,0,0,0,0,4, +0,0,0,6,0,0,0,0,0,4,8,0,0,0,11,7,0,0,0,4,0,0,0,0,0,7,0,0,8,5,0,0,16,0,0,0,13,6, +0,0,0,0,0,0,0,6,0,0,0,0,20,0,11,6,0,0,8,7,0,0,0,0,0,6,17,0,8,0,0,0,0,0,8,7,0,0, +9,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0, +0,0,4,0,7,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,8, +0,8,0,0,0,0,0,0,0,11,0,8,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,6,0,0,9,0, +0,0,0,0,8,0,0,0,0,0,18,0,0,0,0,0,0,4,9,0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,9,6,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,7,0,0,0,0,0,0,0,0, +0,4,0,0,0,0,0,0,14,0,0,0,0,7,0,6,0,0,8,0,20,7,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,8,0,0,0,14,0,0,0,0,0,0,0,8,0,0,7,0,6,0,0,0,7,0,0,0,0,0,0,0,0, +0,0,0,4,12,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,10,6,0, +5,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,8,4,0,0,0,0,0,0,0,4,0,0,0,7,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,12,7,0, +0,0,0,13,6,0,0,0,7,0,0,8,0,0,0,8,0,0,0,0,0,0,0,0,0,0,5,0,0,0,7,0,0,0,0,0,0,11,5, +0,6,0,0,8,5,0,7,0,0,0,0,0,0,0,7,0,0,0,0,8,6,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,4,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +14,0,10,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,19,0,0,4,0,0,0,7, +0,0,11,5,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,16,0,10,5,18,0,0,7,9,6,0,5,0,0,0,0,0, +0,0,0,0,5,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,5,0,0,0,7,0,6,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,4,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,23,0,0,0,0,5,0,0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,14,0,20,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0, +11,0,0,0,0,7,0,0,0,0,15,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,7,0,0,0,0, +0,4,0,0,0,0,10,0,0,0,0,0,9,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,10,0,11,6,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,5,0,0,11,0,0,0,0,7,0,0,0,0,0,0,8,7,0, +4,0,0,0,0,11,0,0,0,0,0,11,0,0,5,0,0,8,7,0,4,0,7,0,0,0,0,0,0,0,6,0,0,0,0,0,4,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,10,5,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,6,0,5,0,0,0,0,0,0,0, +0,0,4,11,5,10,7,0,7,0,0,9,6,9,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,9,4,0,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8,6,0,0,0,0,11,7,0,0,0,0,0,0,0,0,0,0,11,7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,8,5,0,0,8,0,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0, +10,7,0,0,0,6,0,0,0,0,0,0,8,0,0,6,0,0,0,6,10,0,0,0,0,0,0,0,0,0,0,0,8,5,0,0,0,6,0, +0,0,6,0,0,0,0,9,5,8,5,8,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0, +0,8,7,10,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,0,5,0,0,0,6,0,7,0,0, +10,5,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,11,0,0,0,0,0,13,4, +0,0,0,4,0,0,0,0,0,5,8,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,7,14,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,7,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,5,0,0,15,6,10,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,14,6,10,0,0,0,0,0,0,0,0,6,0, +0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,6,0,5,11,4,0,6,0,0,0,7,0,0,0,0, +0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,5,0,0,8,5,0,0,0,0,0,0,0,0,0,0, +0,0,10,0,0,0,0,0,9,6,9,4,0,0,0,4,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,8,5,0, +0,0,0,0,0,0,0,0,0,0,4,0,0,11,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0, +0,0,0,7,12,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0, +4,9,6,0,4,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,6,0, +7,8,6,0,0,0,0,0,0,0,4,0,0,9,6,0,0,0,0,0,0,0,0,0,6,0,5,0,4,0,0,0,0,0,0,0,5,0,0,0, +0,0,5,0,0,0,7,12,7,0,0,0,0,0,0,18,4,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,6,0,0,0, +0,12,0,0,7,0,0,0,0,0,7,0,0,13,0,0,6,0,0,0,0,8,7,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,7,10,5,0,0,8,0,0,0,0,0,0,0,8,6,0,7,0,0,8,4,0,4,0,0,0,0,10,4,0,0,14,0, +0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,17,0,0,0,0,0,0,6,0,0,0,0,8,6,0,0,10,5,0,0,0,0,8, +6,0,0,0,6,0,0,0,7,0,0,0,0,0,6,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,12,0,0,0,0,6, +8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, +0,0,0,6,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,4,24,0,0, +0,0,0,12,6,0,0,10,6,0,5,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,17,7,0,5,0,0,0, +0,0,0,0,0,0,0,0,0,0,6,11,5,9,0,8,7,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,10,7,0,0,0,0,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,5,8,7,0,0,0, +0,8,5,0,0,0,0,10,7,0,7,0,0,0,0,0,0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,6,0,4,0,0,0,0, +0,6,12,0,8,7,0,0,0,0,0,0,0,0,0,0,16,0,10,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,22,0,0,0, +0,0,0,0,0,0,0,0,0,0,13,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,22,0,0,6,0,0,21,0,0,0,22,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,7,8,0,0,0,0,6,14,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,6,0,0,0,0,0,0, +0,0,0,0,0,6,0,0,0,0,8,5,0,0,11,7,0,6,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0, +6,0,0,0,5,0,0,0,0,0,0,0,0,0,4,0,0,8,7,0,0,0,0,8,5,11,7,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,0,0,0,0,8,5,0,0,10,0,0,4,13,7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,0,0,13,6, +0,6,0,7,0,0,8,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,15,0,0,0,10,7,0,0,0,0,0, +7,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,19,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,6,0,5, +0,7,0,0,0,0,0,0,0,0,0,6,0,0,11,4,0,0,0,6,0,0,13,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,8,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,12,6,0,0,0,0, +0,7,0,0,0,0,0,0,11,7,0,0,0,0,0,6,0,0,10,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,5,11,6, +0,0,0,0,0,0,0,0,10,0,0,0,0,6,0,0,0,0,0,0,8,7,0,0,0,5,0,0,0,5,0,0,0,0,0,0,0,0,0, +0,0,0,8,7,0,0,0,0,9,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,10,0, +0,6,0,0,13,0,0,0,0,0,0,0,9,6,0,0,8,6,8,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,0, +0,9,7,0,0,0,0,0,0,11,0,0,0,10,7,0,0,0,0,0,0,0,0,9,6,0,0,12,4,0,4,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,0,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,5,0,0, +9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0, +16,0,0,4,0,0,0,0,0,7,0,0,0,6,0,6,0,0,11,0,0,0,0,5,0,0,0,0,0,0,0,4,8,5,0,0,0,0,0, +0,14,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0, +0,0,8,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0, +0,0,0,0,6,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,14,7,0,0,9,7,0,0,11,0,0,0,0,0,10, +4,11,5,13,6,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,5,0,0,0,0,0,4,0,0,9,0,0,0,0, +0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,6,12,5,0,0,0,6,14,0,0,0,0,0,0,0,0,0,0,4,9,4, +0,0,0,0,0,5,0,0,0,0,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,5,0,0, +0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,11,6,0,0,13,7,0,0,13,6,0,7,0,0,0,0,0,0,8,6,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,10,6,0,4,0,0,12,6,0,0,0,0,0,0,0,0,10,6, +0,0,0,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,6,0, +0,0,0,0,7,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,6,0, +0,0,7,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0, +0,0,0,5,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0, +0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,8,7,0,0,8,5,0,0,0,4,9,5,0,0,0,7,10,6,0,0, +0,0,0,0,9,7,0,0,8,5,8,0,8,4,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,5,0,5,0,0,0,0,0,0,0, +0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,4,0,0,0,0,0,0,0,0,0, +0,11,7,0,0,0,7,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,5,0,0,0,7,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,9,7,0,0,0,0,8,5,0,4,0,0,0,0,0,6,0,6,14, +6,0,0,0,0,9,6,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6,0,0,0,0,14,7,9,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,16, +0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,14,0, +0,6,0,0,8,6,0,0,0,0,0,6,0,0,12,0,0,0,0,0,8,5,0,7,11,0,0,5,0,4,0,0,0,6,0,0,0,0,0, +0,0,0,0,0,0,0,9,6,0,4,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,10,5,0,0,0,0, +0,4,0,0,0,7,11,6,0,4,8,5,9,5,0,0,0,5,0,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8,5,14,7,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,16,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,9,6,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,9,0,0,0,12,5,0,0,0,0,0,0,0,4,10,5,0,0,0,0,0,0,0,0,0,0,0,6,0, +0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,4,0,0,0,6,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,10,4,0,0,0,0,0,5,0,0,0,4, +0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,8,0,10,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,10,7,0,0,0,0,0,0,0,0,15,0,0,0, +0,0,0,0,0,0,0,7,0,0,0,0,0,7,10,7,9,7,0,0,0,7,0,0,8,0,0,0,0,0,0,0,9,0,0,0,8,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,8,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,7,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,15,7,12,6,0,0,0,7,0,5,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,5,0,0,0,0, +0,0,0,6,9,5,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,9,7,0,0,14,0,0,0,11,7,0,0,0,0,0, +0,0,0,0,0,0,4,0,0,11,7,0,0,0,0,8,0,0,0,0,0,0,6,8,7,0,0,0,7,10,4,0,0,0,0,0,0,0,0, +0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,10,0,0,0,0,0,0, +6,0,6,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,11,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7,0,0,10,7,0,0,0,0,9,7,0,0,0,0,0,0,13,7,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,12,0, +0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,9,6,0,0,11,0,0, +0,0,0,14,4,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,7,0,0, +0,0,0,6,0,7,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,0,20, +7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8,0,0,0,0,0,0,0,0,0,11,5,0,0,0,0,0,0,0,0,0,0,10,4,0,0,0,5,8,5,10,4,0,0,0,0,0, +0,13,6,9,7,0,0,10,7,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,6,0,0,0,7,0,6,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,10,7,0,0, +0,0,0,0,0,0,0,0,12,4,0,0,0,0,8,7,0,0,0,0,0,7,0,6,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0, +0,0,0,0,6,0,6,9,6,0,0,12,5,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,5,8,7,9,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,11, +4,0,0,0,0,0,0,8,0,0,0,10,7,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,0, +0,0,0,0,0,5,0,6,0,0,10,0,14,0,0,0,0,0,0,0,23,0,0,0,12,0,10,5,0,0,0,0,0,0,0,0,0, +5,0,0,0,0,8,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,22,0,8,0,0,0,0,6,0,0,0,0,0,0,0,5,0,0, +0,0,0,0,0,6,18,4,0,0,0,7,10,6,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0, +0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,7,10,0,0,0,0,0,0,6,0,0,0,0,11,5,0,0,0,0,0,0,0,0, +15,0,8,6,0,0,13,7,0,0,0,0,0,7,0,0,0,0,0,7,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,9,5,9, +0,0,6,8,6,0,0,0,0,10,0,0,0,18,5,0,0,0,5,0,7,0,0,0,0,8,6,0,0,0,0,9,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,14,0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,6,0,0,0,5,0, +0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,8,5,0,0,0,0,0,0,0,0,9,0,0,0,0,4,0,0,0,0,0,0,0,0, +0,0,0,0,20,5,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,9,5,0,0,0,0,0,0,8,4,24,0,0,0,0,0,0, +0,0,0,0,0,0,0,9,7,0,0,0,0,10,5,0,0,8,5,0,0,0,0,0,0,0,0,12,7,0,6,0,0,10,6,0,0,0, +0,14,0,0,4,9,5,0,0,0,0,0,0,9,0,0,0,0,0,0,6,0,0,0,0,0,4,0,0,8,0,0,0,0,0,11,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,8,5,11,7,0,4,0,0,10,0,0,0,0, +0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,11,6,0,0,0,0,0,5,14,6,0,0,0,0,10,0,0, +0,13,4,0,0,0,0,0,0,0,0,0,0,0,6,0,0,10,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,7,12,0,10,6,0,0,0,0,0,0,10,0,0,0,0,0,10,0,9, +7,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,0,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,0,9,7,0,0,0, +0,0,0,0,0,0,0,0,0,24,0,11,7,0,7,0,0,0,0,0,0,8,6,0,0,0,0,0,0,8,7,0,0,0,0,0,5,0,0, +0,6,9,0,0,0,23,5,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,7,0,0,0, +0,0,0,0,0,0,0,0,0,0,6,0,0,18,4,0,0,11,7,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, +0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,9,0,0,0,11,0,0,0,23,0,0, +0,10,4,0,0,0,0,0,7,0,0,0,7,0,0,0,0,0,4,0,0,0,0,0,7,0,0,19,0,11,0,0,0,0,0,12,7,0, +0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,5,0,0,0,0,0,5,0,0,0,0,0,5,0,0,0,0,0,0,0,6,0,0, +9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,4,0,0,0,0,10,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,22,0,8,7,10,4,11,0,13,5,8,7,9,0,8,7,0,0,0,7,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0, +0,8,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,4,0,0,0,4,11,0,0,6,0,0,8,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,5,0,0, +20,0,0,0,0,0,0,0,0,0,11,0,0,0,0,5,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7,0,0,14,0,0,0,9,0,13,7,0,0,0,0,0,6,0,7,0,0,8,6,10,6,0,0,8,6,0,0,0,6,0, +0,12,6,9,0,0,0,0,0,0,5,9,0,12,4,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,8,5,0,0,0,0,0, +0,0,4,8,0,0,6,8,0,0,0,0,0,0,0,0,0,13,6,0,7,0,0,0,0,0,6,8,7,8,6,0,0,0,7,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,18,0,11,4,0,0,0,5,0,0,0,0,0,0,0,0,0,0, +0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,14, +6,0,0,0,0,12,7,8,0,0,0,0,0,0,0,8,7,0,0,0,0,10,4,0,0,0,0,0,0,10,0,0,6,0,0,0,0,0, +0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,15,6,9,7,0,0,0,0,0,0,15,6,11,7,0,0,0,7,0,0,21,0,0, +0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,17,6,0,0,10,5,0,5,0,0,0,0,0,0,0,0,0,7, +0,0,10,0,0,0,0,0,0,0,0,4,11,5,0,0,0,0,16,7,0,0,0,0,0,6,0,0,8,7,0,4,0,0,10,0,0,0, +0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0, +0,0,0,10,4,0,0,0,0,0,0,0,0,0,6,0,5,0,0,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0, +0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,6,10,7,0,0,0,0,0,0,0,0,8,4,0,0,10,0,0,0,0,4,0,6,0,6,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,7,17,0,0,0,0,0, +0,0,0,0,0,0,10,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6,0,0,0,5,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,0,4,0,0,0,0,0,6,0,0,0,0,0,0,10,5,0,0, +0,5,0,0,0,0,9,0,19,7,0,0,0,0,0,7,0,0,0,0,10,6,0,0,0,6,0,5,0,0,0,0,0,0,0,0,0,6,8, +0,0,0,0,0,11,0,0,0,0,0,0,6,0,0,0,0,0,7,9,0,15,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,0,0, +0,0,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,0,9,0,0,0,0,0,0,0,0,6,0,7,0,0,0,0,0,0,0,6,0,0, +0,0,0,6,10,0,0,0,0,0,0,0,23,0,14,0,0,0,0,7,0,0,0,0,0,7,0,0,9,0,0,0,0,7,0,0,0,0, +0,6,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4,0,0,0, +0,0,0,0,0,9,5,0,0,0,0,0,4,0,0,0,0,9,5,0,0,0,0,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,10,0,0,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,6,11,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,14,7,0,0,12,7,0,0,0, +0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,8,6,10,0,0,0,0,0,0,0,0,0,10,7,8,5,0,0,0,0,0,0, +0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,5,0,0,9,5,0,0,0,0,0,5,0,0,0,0,0,4,0,0,0, +0,0,0,0,0,0,0,12,4,11,0,0,0,9,0,11,7,0,0,0,0,0,0,10,6,0,0,0,6,0,0,0,0,15,5,0,0, +11,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,4,0,4,0,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,8,0,0,0,19,7,0,4,0,0,9,0,0,0,0,0,10,0, +0,6,0,0,13,0,12,6,0,0,0,0,0,0,0,0,10,7,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,13,7,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,4,9,0,0,0,10,0,0,0,0,0,0,0, +0,5,0,0,0,0,0,0,10,0,23,6,0,0,0,6,8,0,0,0,0,0,0,0,0,0,17,7,0,0,0,0,11,6,22,5,0, +0,9,6,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,5,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,4,11,0,9,4,0,0, +0,7,0,7,0,0,0,0,0,0,12,4,0,0,0,0,0,0,0,0,0,0,0,0,11,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +4,0,0,11,5,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,8,6,0,0,0,4,0,0,0,0, +0,0,0,0,0,7,0,0,0,4,0,0,10,4,0,0,0,0,0,0,0,7,0,7,0,0,0,6,0,0,0,0,8,6,0,6,0,6,0, +0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,0,0,0,0,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6,22,6,12,0,0,6,0,0,0,6,0,0,0,0,0,7,0,0,0,0,11,0,0,0, +9,7,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,6,0,0,0,6,0,6,0,0,8,7,0,0,0,4,9,7,19,0,0,0,0,0,0,0,0,0,9,6,10,6,0,6,0,0,0, +4,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,6,16,7,10,6,0,0,23,6,11,7,0,4,0,0,0,0,0,0,0,0,0, +5,0,0,0,0,10,7,0,0,0,0,0,7,0,0,0,0,0,0,15,0,10,0,0,0,14,6,0,0,0,0,0,0,0,0,0,0,0, +5,0,0,0,0,0,0,0,5,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,5,0,0,11,5,0,0,0,0,0,0,0,0,0,0, +0,4,0,0,0,0,0,6,0,0,10,0,0,0,0,7,0,0,0,0,0,0,10,6,0,0,0,0,8,4,0,0,0,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,7,12,5,0,0,0,0, +0,6,0,0,0,0,9,6,0,0,0,0,0,0,0,6,9,0,0,0,0,6,0,0,0,0,8,7,0,0,0,0,0,0,0,6,0,0,0,0, +0,0,0,0,0,0,10,5,0,0,0,0,0,0,8,6,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8,5,0,0,0,0,0,7,0,7,0,4,0,0,10,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,5,0,0,0,0,13, +7,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7,0,0,13,0,0,0,0,0,0,0,0,7,10,5,0,0,0,0,0,0,9,7,0,0,8,6,9, +5,0,0,0,0,0,6,12,0,0,0,0,0,0,0,18,6,0,0,0,0,0,0,0,0,19,7,0,4,0,0,0,0,9,5,0,5,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,7,0,0,0,0,0,0,14,0,0,0,23,7,8,7,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,22,0,0,7,0,0,0,0,0,0,0,0,9,7,8,4,0, +0,0,0,0,0,0,0,8,5,0,6,0,0,0,0,0,6,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0, +8,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,12,5,0,0,0,0,0,0,0,0,0,0,8,6,0,0,11,7,0,0,0, +0,12,0,8,6,19,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,11,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,11,7,0,0,0,0,0,4,10,0,0,0,0,0,0,0,8,7,0,0,0,0,14,0,8,0,0,6,10,0,0, +0,0,0,0,0,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,6,0,0,0,0, +0,0,0,0,13,0,0,0,0,0,0,0,11,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0, +0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,5,0,0,0,6,0,0,0,5,0,7,0,0,0, +0,0,6,0,0,21,7,0,0,9,6,0,0,0,6,0,0,13,7,0,0,0,5,0,0,0,0,0,4,0,6,0,0,0,0,0,0,0,0, +0,0,0,4,0,0,0,0,0,0,11,5,0,6,0,0,10,5,0,0,0,0,0,0,0,0,9,6,0,0,8,7,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,9,0,0,0,0,0,0,6,0,0,0,0,15,4,0,0,12,7,0,0,0,6, +0,7,0,0,8,0,9,5,0,4,0,0,0,6,0,6,0,0,23,4,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,4,0,0,8, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,0,0,0,0,0,0, +7,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,12,6,0,0,0,0,0,0,10,7,0,7,0,0,0,0,0,0,0,0,0,0, +9,0,0,0,0,0,8,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,11,5,0,0,0,6,0,6,0,0,0,0,0,0,0,6,0, +4,0,0,0,0,0,0,0,0,0,0,0,5,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,8,7,0,0,0,6,0,6,0, +0,0,0,0,0,0,0,0,5,0,0,0,0,0,5,0,0,0,0,11,0,0,0,0,0,0,0,10,5,9,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,23,7,0,0,0,0,0,7,0,0,10,6,18,0,0,0, +0,0,0,0,8,7,0,6,0,0,0,0,0,0,8,5,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0, +0,0,0,0,0,6,0,0,0,4,12,7,0,0,0,0,0,0,0,0,10,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,13,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0, +11,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0, +0,0,0,0,6,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,10,0,11,0,0,0,0,0,0,0,0,0, +17,5,0,4,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,0,8,7,0,0,0,0,0,0,0, +0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0, +10,0,0,0,8,6,0,0,0,7,0,0,0,0,0,0,8,0,0,0,14,0,0,0,0,7,0,0,0,4,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,9,4,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0, +10,0,0,0,16,5,0,0,0,0,0,0,8,0,0,4,0,0,0,0,0,0,0,0,0,0,9,6,0,0,0,0,0,0,10,0,0,0, +0,0,0,0,0,5,0,0,0,0,12,5,0,7,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,7,0, +0,0,0,0,0,0,0,12,6,0,0,0,0,0,7,0,6,0,6,12,6,0,0,0,0,0,0,0,4,8,7,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,6,0,6,0,0,0,0,0,0,0,0,10,6,8,0,0, +6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +16,0,8,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,23,5,0,0,0,7,0,6,0, +0,0,0,0,0,0,0,0,0,0,0,10,6,0,0,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,14,0,0,0,0,7,0,0,0,4,17,5,0,0,0,0,11,0,9,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,6,0,0,0,5,0,7,0,0,0,0,0,0,0,0,8,0,0,0, +12,6,0,0,0,0,0,0,13,0,0,0,0,7,9,0,0,0,0,0,0,0,0,0,0,5,0,0,0,7,10,7,12,0,0,0,9,0, +0,0,14,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,15,6,0,0,23,0,0,7,0,6,0,0,0,7,0,6, +0,0,0,0,0,0,0,6,0,6,9,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,8,7,9,4,0,0,10,0,0,0,10, +6,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,23,0,0,6,0,0,0,0,0,0,9,4, +0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0,0,9,6,0,0,0,0,8,6,0,0,0,0,0,0,0,0,12,0,0, +0,0,0,8,0,0,6,11,6,0,0,8,7,8,5,0,0,0,0,0,5,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0, +10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0, +7,0,0,0,0,9,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,8,0,0,0,0,6,12,5,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,10,0,10, +7,0,0,8,0,0,0,0,4,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0, +0,0,4,0,0,0,0,0,4,0,0,0,0,0,0,0,6,0,6,0,5,0,0,0,0,8,0,0,0,10,7,0,0,0,0,10,0,0,0, +0,0,13,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,19,7,0,4,12,0,8,0,0,0,0,6,0,0,0,0, +0,0,0,6,0,0,0,0,0,0,0,0,0,4,0,0,0,0,18,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0, +0,14,0,0,4,0,0,0,6,0,0,0,6,0,0,0,7,0,0,0,0,0,0,10,4,0,0,9,7,0,0,11,0,0,0,0,0,0, +7,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,4,0,0,12,0,0,0, +0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,22,5,9,7,0,0,0,0,0,0,0,0,0, +0,0,6,0,0,9,6,0,5,0,0,0,0,0,0,10,5,0,0,8,6,0,6,10,5,0,0,0,6,0,0,0,6,0,0,20,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,0,17,4,0,7,0,6, +0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10, +0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0, +0,0,7,0,0,8,6,12,0,0,7,18,7,0,0,8,4,0,0,0,0,9,6,0,0,0,0,0,0,0,0,13,0,0,6,0,0,0, +0,0,0,0,0,0,0,10,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,0, +0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,8,5,0,0,0,0,0,0,0,0,12,0,0,0,8,0,0,0,0,0,0, +4,0,0,10,0,16,0,0,0,0,0,0,0,12,7,10,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,16,6,10,0,0,5,0,0,0,0,0,6,0,0,0,0, +0,7,0,0,0,7,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,5,8,7,0,7,0,0,0,0,0,0,0,0,8,0,0,6,0,0,0,6,0,0,0,4,0,0,0,0, +8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,7,0,0,8,0,0,0, +9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,13,5,0,5,0,0,0,7,8,4,0,0,0,0,0,0,0, +0,12,0,0,0,0,0,0,0,0,0,0,0,8,6,0,6,0,0,11,0,0,0,0,0,0,0,0,6,0,0,0,0,0,4,0,0,0,0, +0,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,10,7,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,11,6,0,0,10,6,0,0, +0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, +0,0,0,6,0,0,0,7,0,0,9,0,8,7,11,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,9,6,10,5,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,10,7,0,0,0,0,0,0,11,0,9,6,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,0,6,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,15,5,12,5, +0,0,0,0,0,0,12,7,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,12,6,0, +0,0,0,24,4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,10,4,0,0,0,0,10,7,0,0,0,0,0,0,0,0,0,0,0,0,9,0,11,0,0,0,0,0,0,0,0,0,0,6, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5, +0,0,8,0,0,0,0,7,0,0,0,0,0,0,10,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,0,0, +0,0,0,0,0,14,7,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,4,0,0,0,6,0,0,0,0,0,6,0,0,0,6,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,7,20,7,11,4,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,7,9,6,0,0,12,7,0,0,0,0,0,0,10,0,12,0, +0,0,0,0,0,4,9,6,13,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,0,5,0,0,0,0,0,0,8,0,0,0,0,0,0, +0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,6,0,0,11,0,9,0,0,0,0,4,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,5,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, +0,4,0,5,0,0,0,0,0,0,0,0,0,4,0,0,0,0,9,7,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0, +0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6, +0,0,0,0,8,7,0,0,0,0,0,0,12,0,0,6,0,0,0,0,0,0,0,6,8,4,0,0,10,7,0,0,10,0,0,0,0,0, +0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,7,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,4,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,7,0,0,0,0,0,0,0,5, +0,4,0,0,0,0,0,6,0,0,0,0,0,0,8,0,0,6,0,0,0,6,0,0,0,0,0,7,0,5,8,4,0,0,9,0,0,0,0,4, +0,0,0,0,0,0,0,0,0,5,0,0,15,6,8,6,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,9,6,0,0,0,0,0,0,0,7,0,0,0,4,0, +6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,9,5,0,6,12,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,8,7,0,6,0,0,0,0,0,0,0,0,0,0,0,0,11,0,12,7,0,0,0,0, +0,0,0,0,0,5,0,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,11,4,0,0,0,0,0,0,0,0,0,0,10, +7,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,7,8,7,9,6,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,5,12,0, +10,5,12,6,0,0,0,7,0,0,0,0,0,0,0,5,0,0,0,5,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7, +11,7,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,6,0,7,0,0,0,0,8,0,8,5,0,6,0,0,0,6,0,0,0, +0,0,0,0,6,0,6,0,6,9,0,0,5,17,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,7,0,0, +0,0,0,7,0,0,0,0,16,5,0,0,0,0,0,0,0,4,0,0,0,5,11,5,0,7,0,0,0,4,8,7,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,7,0,0,0,0,12,0,0,0, +0,0,12,0,0,0,0,0,0,0,0,4,10,4,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,6,0,0,0,0,0,0,0,4,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,20,5,0,0, +10,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,12,0,0,0,0,0,0,6,0,0,0,0,0,0,9,4,10,7,0,4,0,0, +0,0,0,0,10,6,0,0,0,0,8,4,0,7,8,6,0,6,8,0,10,0,0,0,0,0,13,5,0,6,0,0,0,0,0,0,22,4, +0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,6,10, +5,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,10,4,0,0,10,7,0,0,0,0,0,5,0, +5,8,0,0,0,0,6,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,10,7,0,0,0,4,0,0,0,0,0,6,0,0, +0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,7,0,0,0,6,0,0,0,0,0,0,0,0,0, +4,0,0,0,4,10,0,0,6,13,7,8,0,0,0,0,0,0,7,0,0,12,7,0,0,0,0,0,0,10,5,0,0,0,0,0,6,0, +0,0,0,0,0,0,0,0,0,13,7,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,0,0,0,0,0,8,6,0,6, +0,0,0,0,0,0,0,0,12,0,8,4,0,0,0,0,0,4,0,4,0,0,0,0,0,0,0,5,0,0,0,0,12,5,0,0,0,7,0, +0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,10,0,0,0,20,0,0,5,0,0,10, +7,11,7,0,0,0,0,0,0,0,0,0,0,17,0,9,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,10,7,0,4,0,6,0,0,24,0,0,5,0,0,0,0,8,0,0, +0,0,0,0,0,10,5,0,4,0,6,0,0,8,0,0,0,0,0,0,4,0,6,0,0,0,0,0,0,9,5,0,0,0,0,0,0,0,0, +0,0,0,6,0,0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,4,0,7, +0,0,13,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0, +17,7,0,0,11,6,0,0,0,0,12,6,0,0,0,6,0,6,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,5,0,0,0,6,0,0,0,0,0,0,0,0,0,0,10,0,0,4,8,6,0,0,0, +0,0,0,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,9,5,0,7,18,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,0,0,0,8,0,0,0, +0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0, +0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,4,0,6,0,0,9,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0, +0,0,0,8,7,10,0,8,5,0,0,0,0,0,0,0,0,9,0,0,0,10,0,0,0,0,6,0,7,0,4,0,0,0,0,0,0,0,0, +8,0,0,0,0,0,8,4,0,0,0,0,0,5,0,0,10,0,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +4,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,6,11,0,0, +7,0,0,0,0,0,6,10,5,0,0,0,0,0,0,0,0,0,5,0,0,9,5,12,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,6,0,0,0,0,13,6,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0, +0,0,0,8,4,0,6,12,0,0,0,0,0,0,0,0,0,0,0,0,6,0,6,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,11,4,0,0,0,6,14,0,11,0,9,6,0,0,0,0,0,0,22,0,12,0,8,6,0,0,0,0,0,0,0,6,0, +0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0, +10,7,0,0,0,0,0,0,0,0,9,0,0,0,0,4,0,0,0,0,0,0,0,0,0,5,11,0,0,0,0,0,0,0,8,6,0,0,9, +7,0,0,12,4,0,0,0,0,0,0,12,6,0,6,0,7,0,0,8,5,0,0,0,0}; diff --git a/packages/runtime/vendor/brotli/c/enc/encode.c b/packages/runtime/vendor/brotli/c/enc/encode.c new file mode 100644 index 000000000..ba21aa2ec --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/encode.c @@ -0,0 +1,2021 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Implementation of Brotli compressor. */ + +#include + +#include "../common/constants.h" +#include "../common/context.h" +#include "../common/platform.h" +#include +#include "../common/version.h" +#include "backward_references_hq.h" +#include "backward_references.h" +#include "bit_cost.h" +#include "brotli_bit_stream.h" +#include "command.h" +#include "compound_dictionary.h" +#include "compress_fragment_two_pass.h" +#include "compress_fragment.h" +#include "dictionary_hash.h" +#include "encoder_dict.h" +#include "fast_log.h" +#include "hash.h" +#include "histogram.h" +#include "memory.h" +#include "metablock.h" +#include "params.h" +#include "quality.h" +#include "ringbuffer.h" +#include "state.h" +#include "static_init.h" +#include "utf8_util.h" +#include "write_bits.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define COPY_ARRAY(dst, src) memcpy(dst, src, sizeof(src)); + +static size_t InputBlockSize(BrotliEncoderState* s) { + return (size_t)1 << s->params.lgblock; +} + +static uint64_t UnprocessedInputSize(BrotliEncoderState* s) { + return s->input_pos_ - s->last_processed_pos_; +} + +static size_t RemainingInputBlockSize(BrotliEncoderState* s) { + const uint64_t delta = UnprocessedInputSize(s); + size_t block_size = InputBlockSize(s); + if (delta >= block_size) return 0; + return block_size - (size_t)delta; +} + +BROTLI_BOOL BrotliEncoderSetParameter( + BrotliEncoderState* state, BrotliEncoderParameter p, uint32_t value) { + /* Changing parameters on the fly is not implemented yet. */ + if (state->is_initialized_) return BROTLI_FALSE; + /* TODO(eustas): Validate/clamp parameters here. */ + switch (p) { + case BROTLI_PARAM_MODE: + state->params.mode = (BrotliEncoderMode)value; + return BROTLI_TRUE; + + case BROTLI_PARAM_QUALITY: + state->params.quality = (int)value; + return BROTLI_TRUE; + + case BROTLI_PARAM_LGWIN: + state->params.lgwin = (int)value; + return BROTLI_TRUE; + + case BROTLI_PARAM_LGBLOCK: + state->params.lgblock = (int)value; + return BROTLI_TRUE; + + case BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: + if ((value != 0) && (value != 1)) return BROTLI_FALSE; + state->params.disable_literal_context_modeling = TO_BROTLI_BOOL(!!value); + return BROTLI_TRUE; + + case BROTLI_PARAM_SIZE_HINT: + state->params.size_hint = value; + return BROTLI_TRUE; + + case BROTLI_PARAM_LARGE_WINDOW: + state->params.large_window = TO_BROTLI_BOOL(!!value); + return BROTLI_TRUE; + + case BROTLI_PARAM_NPOSTFIX: + state->params.dist.distance_postfix_bits = value; + return BROTLI_TRUE; + + case BROTLI_PARAM_NDIRECT: + state->params.dist.num_direct_distance_codes = value; + return BROTLI_TRUE; + + case BROTLI_PARAM_STREAM_OFFSET: + if (value > (1u << 30)) return BROTLI_FALSE; + state->params.stream_offset = value; + return BROTLI_TRUE; + + default: return BROTLI_FALSE; + } +} + +/* Wraps 64-bit input position to 32-bit ring-buffer position preserving + "not-a-first-lap" feature. */ +static uint32_t WrapPosition(uint64_t position) { + uint32_t result = (uint32_t)position; + uint64_t gb = position >> 30; + if (gb > 2) { + /* Wrap every 2GiB; The first 3GB are continuous. */ + result = (result & ((1u << 30) - 1)) | ((uint32_t)((gb - 1) & 1) + 1) << 30; + } + return result; +} + +static uint8_t* GetBrotliStorage(BrotliEncoderState* s, size_t size) { + MemoryManager* m = &s->memory_manager_; + if (s->storage_size_ < size) { + BROTLI_FREE(m, s->storage_); + s->storage_ = BROTLI_ALLOC(m, uint8_t, size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->storage_)) return NULL; + s->storage_size_ = size; + } + return s->storage_; +} + +static size_t HashTableSize(size_t max_table_size, size_t input_size) { + size_t htsize = 256; + while (htsize < max_table_size && htsize < input_size) { + htsize <<= 1; + } + return htsize; +} + +static int* GetHashTable(BrotliEncoderState* s, int quality, + size_t input_size, size_t* table_size) { + /* Use smaller hash table when input.size() is smaller, since we + fill the table, incurring O(hash table size) overhead for + compression, and if the input is short, we won't need that + many hash table entries anyway. */ + MemoryManager* m = &s->memory_manager_; + const size_t max_table_size = MaxHashTableSize(quality); + size_t htsize = HashTableSize(max_table_size, input_size); + int* table; + BROTLI_DCHECK(max_table_size >= 256); + if (quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { + /* Only odd shifts are supported by fast-one-pass. */ + if ((htsize & 0xAAAAA) == 0) { + htsize <<= 1; + } + } + + if (htsize <= sizeof(s->small_table_) / sizeof(s->small_table_[0])) { + table = s->small_table_; + } else { + if (htsize > s->large_table_size_) { + s->large_table_size_ = htsize; + BROTLI_FREE(m, s->large_table_); + s->large_table_ = BROTLI_ALLOC(m, int, htsize); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->large_table_)) return 0; + } + table = s->large_table_; + } + + *table_size = htsize; + memset(table, 0, htsize * sizeof(*table)); + return table; +} + +static void EncodeWindowBits(int lgwin, BROTLI_BOOL large_window, + uint16_t* last_bytes, uint8_t* last_bytes_bits) { + if (large_window) { + *last_bytes = (uint16_t)(((lgwin & 0x3F) << 8) | 0x11); + *last_bytes_bits = 14; + } else { + if (lgwin == 16) { + *last_bytes = 0; + *last_bytes_bits = 1; + } else if (lgwin == 17) { + *last_bytes = 1; + *last_bytes_bits = 7; + } else if (lgwin > 17) { + *last_bytes = (uint16_t)(((lgwin - 17) << 1) | 0x01); + *last_bytes_bits = 4; + } else { + *last_bytes = (uint16_t)(((lgwin - 8) << 4) | 0x01); + *last_bytes_bits = 7; + } + } +} + +/* TODO(eustas): move to compress_fragment.c? */ +/* Initializes the command and distance prefix codes for the first block. */ +static void InitCommandPrefixCodes(BrotliOnePassArena* s) { + static const BROTLI_MODEL("small") uint8_t kDefaultCommandDepths[128] = { + 0, 4, 4, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 0, 0, 0, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, + 7, 7, 10, 10, 10, 10, 10, 10, 0, 4, 4, 5, 5, 5, 6, 6, + 7, 8, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 10, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + }; + static const BROTLI_MODEL("small") uint16_t kDefaultCommandBits[128] = { + 0, 0, 8, 9, 3, 35, 7, 71, + 39, 103, 23, 47, 175, 111, 239, 31, + 0, 0, 0, 4, 12, 2, 10, 6, + 13, 29, 11, 43, 27, 59, 87, 55, + 15, 79, 319, 831, 191, 703, 447, 959, + 0, 14, 1, 25, 5, 21, 19, 51, + 119, 159, 95, 223, 479, 991, 63, 575, + 127, 639, 383, 895, 255, 767, 511, 1023, + 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 27, 59, 7, 39, 23, 55, 30, 1, 17, 9, 25, 5, 0, 8, 4, 12, + 2, 10, 6, 21, 13, 29, 3, 19, 11, 15, 47, 31, 95, 63, 127, 255, + 767, 2815, 1791, 3839, 511, 2559, 1535, 3583, 1023, 3071, 2047, 4095, + }; + static const BROTLI_MODEL("small") uint8_t kDefaultCommandCode[] = { + 0xff, 0x77, 0xd5, 0xbf, 0xe7, 0xde, 0xea, 0x9e, 0x51, 0x5d, 0xde, 0xc6, + 0x70, 0x57, 0xbc, 0x58, 0x58, 0x58, 0xd8, 0xd8, 0x58, 0xd5, 0xcb, 0x8c, + 0xea, 0xe0, 0xc3, 0x87, 0x1f, 0x83, 0xc1, 0x60, 0x1c, 0x67, 0xb2, 0xaa, + 0x06, 0x83, 0xc1, 0x60, 0x30, 0x18, 0xcc, 0xa1, 0xce, 0x88, 0x54, 0x94, + 0x46, 0xe1, 0xb0, 0xd0, 0x4e, 0xb2, 0xf7, 0x04, 0x00, + }; + static const size_t kDefaultCommandCodeNumBits = 448; + COPY_ARRAY(s->cmd_depth, kDefaultCommandDepths); + COPY_ARRAY(s->cmd_bits, kDefaultCommandBits); + + /* Initialize the pre-compressed form of the command and distance prefix + codes. */ + COPY_ARRAY(s->cmd_code, kDefaultCommandCode); + s->cmd_code_numbits = kDefaultCommandCodeNumBits; +} + +/* TODO(eustas): avoid FP calculations. */ +static double EstimateEntropy(const uint32_t* population, size_t size) { + size_t total = 0; + double result = 0; + for (size_t i = 0; i < size; ++i) { + uint32_t p = population[i]; + total += p; + result += (double)p * FastLog2(p); + } + result = (double)total * FastLog2(total) - result; + return result; +} + +/* Decide about the context map based on the ability of the prediction + ability of the previous byte UTF8-prefix on the next byte. The + prediction ability is calculated as Shannon entropy. Here we need + Shannon entropy instead of 'BrotliBitsEntropy' since the prefix will be + encoded with the remaining 6 bits of the following byte, and + BrotliBitsEntropy will assume that symbol to be stored alone using Huffman + coding. */ +static void ChooseContextMap(int quality, + uint32_t* bigram_histo, + size_t* num_literal_contexts, + const uint32_t** literal_context_map) { + static const BROTLI_MODEL("small") + uint32_t kStaticContextMapContinuation[64] = { + 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + static const BROTLI_MODEL("small") + uint32_t kStaticContextMapSimpleUTF8[64] = { + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + uint32_t monogram_histo[3] = { 0 }; + uint32_t two_prefix_histo[6] = { 0 }; + size_t total; + size_t i; + double entropy[4]; + for (i = 0; i < 9; ++i) { + monogram_histo[i % 3] += bigram_histo[i]; + two_prefix_histo[i % 6] += bigram_histo[i]; + } + entropy[1] = EstimateEntropy(monogram_histo, 3); + entropy[2] = (EstimateEntropy(two_prefix_histo, 3) + + EstimateEntropy(two_prefix_histo + 3, 3)); + entropy[3] = 0; + for (i = 0; i < 3; ++i) { + entropy[3] += EstimateEntropy(bigram_histo + 3 * i, 3); + } + + total = monogram_histo[0] + monogram_histo[1] + monogram_histo[2]; + BROTLI_DCHECK(total != 0); + entropy[0] = 1.0 / (double)total; + entropy[1] *= entropy[0]; + entropy[2] *= entropy[0]; + entropy[3] *= entropy[0]; + + if (quality < MIN_QUALITY_FOR_HQ_CONTEXT_MODELING) { + /* 3 context models is a bit slower, don't use it at lower qualities. */ + entropy[3] = entropy[1] * 10; + } + /* If expected savings by symbol are less than 0.2 bits, skip the + context modeling -- in exchange for faster decoding speed. */ + if (entropy[1] - entropy[2] < 0.2 && + entropy[1] - entropy[3] < 0.2) { + *num_literal_contexts = 1; + } else if (entropy[2] - entropy[3] < 0.02) { + *num_literal_contexts = 2; + *literal_context_map = kStaticContextMapSimpleUTF8; + } else { + *num_literal_contexts = 3; + *literal_context_map = kStaticContextMapContinuation; + } +} + +/* Decide if we want to use a more complex static context map containing 13 + context values, based on the entropy reduction of histograms over the + first 5 bits of literals. */ +static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input, + size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint, + size_t* num_literal_contexts, const uint32_t** literal_context_map, + uint32_t* arena) { + static const BROTLI_MODEL("small") + uint32_t kStaticContextMapComplexUTF8[64] = { + 11, 11, 12, 12, /* 0 special */ + 0, 0, 0, 0, /* 4 lf */ + 1, 1, 9, 9, /* 8 space */ + 2, 2, 2, 2, /* !, first after space/lf and after something else. */ + 1, 1, 1, 1, /* " */ + 8, 3, 3, 3, /* % */ + 1, 1, 1, 1, /* ({[ */ + 2, 2, 2, 2, /* }]) */ + 8, 4, 4, 4, /* :; */ + 8, 7, 4, 4, /* . */ + 8, 0, 0, 0, /* > */ + 3, 3, 3, 3, /* [0..9] */ + 5, 5, 10, 5, /* [A-Z] */ + 5, 5, 10, 5, + 6, 6, 6, 6, /* [a-z] */ + 6, 6, 6, 6, + }; + BROTLI_UNUSED(quality); + /* Try the more complex static context map only for long data. */ + if (size_hint < (1 << 20)) { + return BROTLI_FALSE; + } else { + const size_t end_pos = start_pos + length; + /* To make entropy calculations faster, we collect histograms + over the 5 most significant bits of literals. One histogram + without context and 13 additional histograms for each context value. */ + uint32_t* BROTLI_RESTRICT const combined_histo = arena; + uint32_t* BROTLI_RESTRICT const context_histo = arena + 32; + uint32_t total = 0; + double entropy[3]; + size_t i; + ContextLut utf8_lut = BROTLI_CONTEXT_LUT(CONTEXT_UTF8); + memset(arena, 0, sizeof(arena[0]) * 32 * (BROTLI_MAX_STATIC_CONTEXTS + 1)); + for (; start_pos + 64 <= end_pos; start_pos += 4096) { + const size_t stride_end_pos = start_pos + 64; + uint8_t prev2 = input[start_pos & mask]; + uint8_t prev1 = input[(start_pos + 1) & mask]; + size_t pos; + /* To make the analysis of the data faster we only examine 64 byte long + strides at every 4kB intervals. */ + for (pos = start_pos + 2; pos < stride_end_pos; ++pos) { + const uint8_t literal = input[pos & mask]; + const uint8_t context = (uint8_t)kStaticContextMapComplexUTF8[ + BROTLI_CONTEXT(prev1, prev2, utf8_lut)]; + ++total; + ++combined_histo[literal >> 3]; + ++context_histo[(context << 5) + (literal >> 3)]; + prev2 = prev1; + prev1 = literal; + } + } + entropy[1] = EstimateEntropy(combined_histo, 32); + entropy[2] = 0; + for (i = 0; i < BROTLI_MAX_STATIC_CONTEXTS; ++i) { + entropy[2] += EstimateEntropy(context_histo + (i << 5), 32); + } + entropy[0] = 1.0 / (double)total; + entropy[1] *= entropy[0]; + entropy[2] *= entropy[0]; + /* The triggering heuristics below were tuned by compressing the individual + files of the silesia corpus. If we skip this kind of context modeling + for not very well compressible input (i.e. entropy using context modeling + is 60% of maximal entropy) or if expected savings by symbol are less + than 0.2 bits, then in every case when it triggers, the final compression + ratio is improved. Note however that this heuristics might be too strict + for some cases and could be tuned further. */ + if (entropy[2] > 3.0 || entropy[1] - entropy[2] < 0.2) { + return BROTLI_FALSE; + } else { + *num_literal_contexts = BROTLI_MAX_STATIC_CONTEXTS; + *literal_context_map = kStaticContextMapComplexUTF8; + return BROTLI_TRUE; + } + } +} + +static void DecideOverLiteralContextModeling(const uint8_t* input, + size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint, + size_t* num_literal_contexts, const uint32_t** literal_context_map, + uint32_t* arena) { + if (quality < MIN_QUALITY_FOR_CONTEXT_MODELING || length < 64) { + return; + } else if (ShouldUseComplexStaticContextMap( + input, start_pos, length, mask, quality, size_hint, + num_literal_contexts, literal_context_map, arena)) { + /* Context map was already set, nothing else to do. */ + } else { + /* Gather bi-gram data of the UTF8 byte prefixes. To make the analysis of + UTF8 data faster we only examine 64 byte long strides at every 4kB + intervals. */ + const size_t end_pos = start_pos + length; + uint32_t* BROTLI_RESTRICT const bigram_prefix_histo = arena; + memset(bigram_prefix_histo, 0, sizeof(arena[0]) * 9); + for (; start_pos + 64 <= end_pos; start_pos += 4096) { + static const int lut[4] = { 0, 0, 1, 2 }; + const size_t stride_end_pos = start_pos + 64; + int prev = lut[input[start_pos & mask] >> 6] * 3; + size_t pos; + for (pos = start_pos + 1; pos < stride_end_pos; ++pos) { + const uint8_t literal = input[pos & mask]; + ++bigram_prefix_histo[prev + lut[literal >> 6]]; + prev = lut[literal >> 6] * 3; + } + } + ChooseContextMap(quality, &bigram_prefix_histo[0], num_literal_contexts, + literal_context_map); + } +} + +static BROTLI_BOOL ShouldCompress( + const uint8_t* data, const size_t mask, const uint64_t last_flush_pos, + const size_t bytes, const size_t num_literals, const size_t num_commands) { + /* TODO(eustas): find more precise minimal block overhead. */ + if (bytes <= 2) return BROTLI_FALSE; + if (num_commands < (bytes >> 8) + 2) { + if ((double)num_literals > 0.99 * (double)bytes) { + uint32_t literal_histo[256] = { 0 }; + static const uint32_t kSampleRate = 13; + static const double kInvSampleRate = 1.0 / 13.0; + static const double kMinEntropy = 7.92; + const double bit_cost_threshold = + (double)bytes * kMinEntropy * kInvSampleRate; + size_t t = (bytes + kSampleRate - 1) / kSampleRate; + uint32_t pos = (uint32_t)last_flush_pos; + size_t i; + for (i = 0; i < t; i++) { + ++literal_histo[data[pos & mask]]; + pos += kSampleRate; + } + if (BrotliBitsEntropy(literal_histo, 256) > bit_cost_threshold) { + return BROTLI_FALSE; + } + } + } + return BROTLI_TRUE; +} + +/* Chooses the literal context mode for a metablock */ +static ContextType ChooseContextMode(const BrotliEncoderParams* params, + const uint8_t* data, const size_t pos, const size_t mask, + const size_t length) { + /* We only do the computation for the option of something else than + CONTEXT_UTF8 for the highest qualities */ + if (params->quality >= MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING && + !BrotliIsMostlyUTF8(data, pos, mask, length, kMinUTF8Ratio)) { + return CONTEXT_SIGNED; + } + return CONTEXT_UTF8; +} + +static void WriteMetaBlockInternal(MemoryManager* m, + const uint8_t* data, + const size_t mask, + const uint64_t last_flush_pos, + const size_t bytes, + const BROTLI_BOOL is_last, + ContextType literal_context_mode, + const BrotliEncoderParams* params, + const uint8_t prev_byte, + const uint8_t prev_byte2, + const size_t num_literals, + const size_t num_commands, + Command* commands, + const int* saved_dist_cache, + int* dist_cache, + size_t* storage_ix, + uint8_t* storage) { + const uint32_t wrapped_last_flush_pos = WrapPosition(last_flush_pos); + uint16_t last_bytes; + uint8_t last_bytes_bits; + ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); + BrotliEncoderParams block_params = *params; + + if (bytes == 0) { + /* Write the ISLAST and ISEMPTY bits. */ + BrotliWriteBits(2, 3, storage_ix, storage); + *storage_ix = (*storage_ix + 7u) & ~7u; + return; + } + + if (!ShouldCompress(data, mask, last_flush_pos, bytes, + num_literals, num_commands)) { + /* Restore the distance cache, as its last update by + CreateBackwardReferences is now unused. */ + memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0])); + BrotliStoreUncompressedMetaBlock(is_last, data, + wrapped_last_flush_pos, mask, bytes, + storage_ix, storage); + return; + } + + BROTLI_DCHECK(*storage_ix <= 14); + last_bytes = (uint16_t)((storage[1] << 8) | storage[0]); + last_bytes_bits = (uint8_t)(*storage_ix); + if (params->quality <= MAX_QUALITY_FOR_STATIC_ENTROPY_CODES) { + BrotliStoreMetaBlockFast(m, data, wrapped_last_flush_pos, + bytes, mask, is_last, params, + commands, num_commands, + storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + } else if (params->quality < MIN_QUALITY_FOR_BLOCK_SPLIT) { + BrotliStoreMetaBlockTrivial(m, data, wrapped_last_flush_pos, + bytes, mask, is_last, params, + commands, num_commands, + storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + } else { + MetaBlockSplit mb; + InitMetaBlockSplit(&mb); + if (params->quality < MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING) { + size_t num_literal_contexts = 1; + const uint32_t* literal_context_map = NULL; + if (!params->disable_literal_context_modeling) { + /* TODO(eustas): pull to higher level and reuse. */ + uint32_t* arena = + BROTLI_ALLOC(m, uint32_t, 32 * (BROTLI_MAX_STATIC_CONTEXTS + 1)); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; + DecideOverLiteralContextModeling( + data, wrapped_last_flush_pos, bytes, mask, params->quality, + params->size_hint, &num_literal_contexts, + &literal_context_map, arena); + BROTLI_FREE(m, arena); + } + BrotliBuildMetaBlockGreedy(m, data, wrapped_last_flush_pos, mask, + prev_byte, prev_byte2, literal_context_lut, num_literal_contexts, + literal_context_map, commands, num_commands, &mb); + if (BROTLI_IS_OOM(m)) return; + } else { + BrotliBuildMetaBlock(m, data, wrapped_last_flush_pos, mask, &block_params, + prev_byte, prev_byte2, + commands, num_commands, + literal_context_mode, + &mb); + if (BROTLI_IS_OOM(m)) return; + } + if (params->quality >= MIN_QUALITY_FOR_OPTIMIZE_HISTOGRAMS) { + /* The number of distance symbols effectively used for distance + histograms. It might be less than distance alphabet size + for "Large Window Brotli" (32-bit). */ + BrotliOptimizeHistograms(block_params.dist.alphabet_size_limit, &mb); + } + BrotliStoreMetaBlock(m, data, wrapped_last_flush_pos, bytes, mask, + prev_byte, prev_byte2, + is_last, + &block_params, + literal_context_mode, + commands, num_commands, + &mb, + storage_ix, storage); + if (BROTLI_IS_OOM(m)) return; + DestroyMetaBlockSplit(m, &mb); + } + if (bytes + 4 < (*storage_ix >> 3)) { + /* Restore the distance cache and last byte. */ + memcpy(dist_cache, saved_dist_cache, 4 * sizeof(dist_cache[0])); + storage[0] = (uint8_t)last_bytes; + storage[1] = (uint8_t)(last_bytes >> 8); + *storage_ix = last_bytes_bits; + BrotliStoreUncompressedMetaBlock(is_last, data, + wrapped_last_flush_pos, mask, + bytes, storage_ix, storage); + } +} + +static void ChooseDistanceParams(BrotliEncoderParams* params) { + uint32_t distance_postfix_bits = 0; + uint32_t num_direct_distance_codes = 0; + + if (params->quality >= MIN_QUALITY_FOR_NONZERO_DISTANCE_PARAMS) { + uint32_t ndirect_msb; + if (params->mode == BROTLI_MODE_FONT) { + distance_postfix_bits = 1; + num_direct_distance_codes = 12; + } else { + distance_postfix_bits = params->dist.distance_postfix_bits; + num_direct_distance_codes = params->dist.num_direct_distance_codes; + } + ndirect_msb = (num_direct_distance_codes >> distance_postfix_bits) & 0x0F; + if (distance_postfix_bits > BROTLI_MAX_NPOSTFIX || + num_direct_distance_codes > BROTLI_MAX_NDIRECT || + (ndirect_msb << distance_postfix_bits) != num_direct_distance_codes) { + distance_postfix_bits = 0; + num_direct_distance_codes = 0; + } + } + + BrotliInitDistanceParams(¶ms->dist, distance_postfix_bits, + num_direct_distance_codes, params->large_window); +} + +static BROTLI_BOOL EnsureInitialized(BrotliEncoderState* s) { + MemoryManager* m = &s->memory_manager_; + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + if (s->is_initialized_) return BROTLI_TRUE; + + s->last_bytes_bits_ = 0; + s->last_bytes_ = 0; + s->flint_ = BROTLI_FLINT_DONE; + s->remaining_metadata_bytes_ = BROTLI_UINT32_MAX; + + SanitizeParams(&s->params); + s->params.lgblock = ComputeLgBlock(&s->params); + ChooseDistanceParams(&s->params); + + if (s->params.stream_offset != 0) { + s->flint_ = BROTLI_FLINT_NEEDS_2_BYTES; + /* Poison the distance cache. -16 +- 3 is still less than zero (invalid). */ + s->dist_cache_[0] = -16; + s->dist_cache_[1] = -16; + s->dist_cache_[2] = -16; + s->dist_cache_[3] = -16; + memcpy(s->saved_dist_cache_, s->dist_cache_, sizeof(s->saved_dist_cache_)); + } + + RingBufferSetup(&s->params, &s->ringbuffer_); + + /* Initialize last byte with stream header. */ + { + int lgwin = s->params.lgwin; + if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || + s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { + lgwin = BROTLI_MAX(int, lgwin, 18); + } + if (s->params.stream_offset == 0) { + EncodeWindowBits(lgwin, s->params.large_window, + &s->last_bytes_, &s->last_bytes_bits_); + } else { + /* Bigger values have the same effect, but could cause overflows. */ + s->params.stream_offset = BROTLI_MIN(size_t, + s->params.stream_offset, BROTLI_MAX_BACKWARD_LIMIT(lgwin)); + } + } + + if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { + s->one_pass_arena_ = BROTLI_ALLOC(m, BrotliOnePassArena, 1); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + InitCommandPrefixCodes(s->one_pass_arena_); + } else if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { + s->two_pass_arena_ = BROTLI_ALLOC(m, BrotliTwoPassArena, 1); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } + + s->is_initialized_ = BROTLI_TRUE; + return BROTLI_TRUE; +} + +static void BrotliEncoderInitParams(BrotliEncoderParams* params) { + params->mode = BROTLI_DEFAULT_MODE; + params->large_window = BROTLI_FALSE; + params->quality = BROTLI_DEFAULT_QUALITY; + params->lgwin = BROTLI_DEFAULT_WINDOW; + params->lgblock = 0; + params->stream_offset = 0; + params->size_hint = 0; + params->disable_literal_context_modeling = BROTLI_FALSE; + BrotliInitSharedEncoderDictionary(¶ms->dictionary); + params->dist.distance_postfix_bits = 0; + params->dist.num_direct_distance_codes = 0; + params->dist.alphabet_size_max = + BROTLI_DISTANCE_ALPHABET_SIZE(0, 0, BROTLI_MAX_DISTANCE_BITS); + params->dist.alphabet_size_limit = params->dist.alphabet_size_max; + params->dist.max_distance = BROTLI_MAX_DISTANCE; +} + +static void BrotliEncoderCleanupParams(MemoryManager* m, + BrotliEncoderParams* params) { + BrotliCleanupSharedEncoderDictionary(m, ¶ms->dictionary); +} + +#ifdef BROTLI_REPORTING +/* When BROTLI_REPORTING is defined extra reporting module have to be linked. */ +void BrotliEncoderOnStart(const BrotliEncoderState* s); +void BrotliEncoderOnFinish(const BrotliEncoderState* s); +#define BROTLI_ENCODER_ON_START(s) BrotliEncoderOnStart(s); +#define BROTLI_ENCODER_ON_FINISH(s) BrotliEncoderOnFinish(s); +#else +#if !defined(BROTLI_ENCODER_ON_START) +#define BROTLI_ENCODER_ON_START(s) (void)(s); +#endif +#if !defined(BROTLI_ENCODER_ON_FINISH) +#define BROTLI_ENCODER_ON_FINISH(s) (void)(s); +#endif +#endif + +static void BrotliEncoderInitState(BrotliEncoderState* s) { + BROTLI_ENCODER_ON_START(s); + BrotliEncoderInitParams(&s->params); + s->input_pos_ = 0; + s->num_commands_ = 0; + s->num_literals_ = 0; + s->last_insert_len_ = 0; + s->last_flush_pos_ = 0; + s->last_processed_pos_ = 0; + s->prev_byte_ = 0; + s->prev_byte2_ = 0; + s->storage_size_ = 0; + s->storage_ = 0; + HasherInit(&s->hasher_); + s->large_table_ = NULL; + s->large_table_size_ = 0; + s->one_pass_arena_ = NULL; + s->two_pass_arena_ = NULL; + s->command_buf_ = NULL; + s->literal_buf_ = NULL; + s->total_in_ = 0; + s->next_out_ = NULL; + s->available_out_ = 0; + s->total_out_ = 0; + s->stream_state_ = BROTLI_STREAM_PROCESSING; + s->is_last_block_emitted_ = BROTLI_FALSE; + s->is_initialized_ = BROTLI_FALSE; + + RingBufferInit(&s->ringbuffer_); + + s->commands_ = 0; + s->cmd_alloc_size_ = 0; + + /* Initialize distance cache. */ + s->dist_cache_[0] = 4; + s->dist_cache_[1] = 11; + s->dist_cache_[2] = 15; + s->dist_cache_[3] = 16; + /* Save the state of the distance cache in case we need to restore it for + emitting an uncompressed block. */ + memcpy(s->saved_dist_cache_, s->dist_cache_, sizeof(s->saved_dist_cache_)); +} + +BrotliEncoderState* BrotliEncoderCreateInstance( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + BROTLI_BOOL healthy = BrotliEncoderEnsureStaticInit(); + if (!healthy) { + return 0; + } + BrotliEncoderState* state = (BrotliEncoderState*)BrotliBootstrapAlloc( + sizeof(BrotliEncoderState), alloc_func, free_func, opaque); + if (state == NULL) { + /* BROTLI_DUMP(); */ + return 0; + } + BrotliInitMemoryManager( + &state->memory_manager_, alloc_func, free_func, opaque); + BrotliEncoderInitState(state); + return state; +} + +static void BrotliEncoderCleanupState(BrotliEncoderState* s) { + MemoryManager* m = &s->memory_manager_; + + BROTLI_ENCODER_ON_FINISH(s); + + if (BROTLI_IS_OOM(m)) { + BrotliWipeOutMemoryManager(m); + return; + } + + BROTLI_FREE(m, s->storage_); + BROTLI_FREE(m, s->commands_); + RingBufferFree(m, &s->ringbuffer_); + DestroyHasher(m, &s->hasher_); + BROTLI_FREE(m, s->large_table_); + BROTLI_FREE(m, s->one_pass_arena_); + BROTLI_FREE(m, s->two_pass_arena_); + BROTLI_FREE(m, s->command_buf_); + BROTLI_FREE(m, s->literal_buf_); + BrotliEncoderCleanupParams(m, &s->params); +} + +/* Deinitializes and frees BrotliEncoderState instance. */ +void BrotliEncoderDestroyInstance(BrotliEncoderState* state) { + if (!state) { + return; + } else { + BrotliEncoderCleanupState(state); + BrotliBootstrapFree(state, &state->memory_manager_); + } +} + +/* + Copies the given input data to the internal ring buffer of the compressor. + No processing of the data occurs at this time and this function can be + called multiple times before calling WriteBrotliData() to process the + accumulated input. At most input_block_size() bytes of input data can be + copied to the ring buffer, otherwise the next WriteBrotliData() will fail. + */ +static void CopyInputToRingBuffer(BrotliEncoderState* s, + const size_t input_size, + const uint8_t* input_buffer) { + RingBuffer* ringbuffer_ = &s->ringbuffer_; + MemoryManager* m = &s->memory_manager_; + RingBufferWrite(m, input_buffer, input_size, ringbuffer_); + if (BROTLI_IS_OOM(m)) return; + s->input_pos_ += input_size; + + /* TL;DR: If needed, initialize 7 more bytes in the ring buffer to make the + hashing not depend on uninitialized data. This makes compression + deterministic and it prevents uninitialized memory warnings in Valgrind. + Even without erasing, the output would be valid (but nondeterministic). + + Background information: The compressor stores short (at most 8 bytes) + substrings of the input already read in a hash table, and detects + repetitions by looking up such substrings in the hash table. If it + can find a substring, it checks whether the substring is really there + in the ring buffer (or it's just a hash collision). Should the hash + table become corrupt, this check makes sure that the output is + still valid, albeit the compression ratio would be bad. + + The compressor populates the hash table from the ring buffer as it's + reading new bytes from the input. However, at the last few indexes of + the ring buffer, there are not enough bytes to build full-length + substrings from. Since the hash table always contains full-length + substrings, we overwrite with zeros here to make sure that those + substrings will contain zeros at the end instead of uninitialized + data. + + Please note that erasing is not necessary (because the + memory region is already initialized since he ring buffer + has a `tail' that holds a copy of the beginning,) so we + skip erasing if we have already gone around at least once in + the ring buffer. + + Only clear during the first round of ring-buffer writes. On + subsequent rounds data in the ring-buffer would be affected. */ + if (ringbuffer_->pos_ <= ringbuffer_->mask_) { + /* This is the first time when the ring buffer is being written. + We clear 7 bytes just after the bytes that have been copied from + the input buffer. + + The ring-buffer has a "tail" that holds a copy of the beginning, + but only once the ring buffer has been fully written once, i.e., + pos <= mask. For the first time, we need to write values + in this tail (where index may be larger than mask), so that + we have exactly defined behavior and don't read uninitialized + memory. Due to performance reasons, hashing reads data using a + LOAD64, which can go 7 bytes beyond the bytes written in the + ring-buffer. */ + memset(ringbuffer_->buffer_ + ringbuffer_->pos_, 0, 7); + } +} + +/* Marks all input as processed. + Returns true if position wrapping occurs. */ +static BROTLI_BOOL UpdateLastProcessedPos(BrotliEncoderState* s) { + uint32_t wrapped_last_processed_pos = WrapPosition(s->last_processed_pos_); + uint32_t wrapped_input_pos = WrapPosition(s->input_pos_); + s->last_processed_pos_ = s->input_pos_; + return TO_BROTLI_BOOL(wrapped_input_pos < wrapped_last_processed_pos); +} + +static void ExtendLastCommand(BrotliEncoderState* s, uint32_t* bytes, + uint32_t* wrapped_last_processed_pos) { + Command* last_command = &s->commands_[s->num_commands_ - 1]; + const uint8_t* data = s->ringbuffer_.buffer_; + const uint32_t mask = s->ringbuffer_.mask_; + uint64_t max_backward_distance = + (((uint64_t)1) << s->params.lgwin) - BROTLI_WINDOW_GAP; + uint64_t last_copy_len = last_command->copy_len_ & 0x1FFFFFF; + uint64_t last_processed_pos = s->last_processed_pos_ - last_copy_len; + uint64_t max_distance = last_processed_pos < max_backward_distance ? + last_processed_pos : max_backward_distance; + uint64_t cmd_dist = (uint64_t)s->dist_cache_[0]; + uint32_t distance_code = CommandRestoreDistanceCode(last_command, + &s->params.dist); + const CompoundDictionary* dict = &s->params.dictionary.compound; + size_t compound_dictionary_size = dict->total_size; + if (distance_code < BROTLI_NUM_DISTANCE_SHORT_CODES || + distance_code - (BROTLI_NUM_DISTANCE_SHORT_CODES - 1) == cmd_dist) { + if (cmd_dist <= max_distance) { + while (*bytes != 0 && data[*wrapped_last_processed_pos & mask] == + data[(*wrapped_last_processed_pos - cmd_dist) & mask]) { + last_command->copy_len_++; + (*bytes)--; + (*wrapped_last_processed_pos)++; + } + } else { + if ((cmd_dist - max_distance - 1) < compound_dictionary_size && + last_copy_len < cmd_dist - max_distance) { + size_t address = + compound_dictionary_size - (size_t)(cmd_dist - max_distance) + + (size_t)last_copy_len; + size_t br_index = 0; + size_t br_offset; + const uint8_t* chunk; + size_t chunk_length; + while (address >= dict->chunk_offsets[br_index + 1]) br_index++; + br_offset = address - dict->chunk_offsets[br_index]; + chunk = dict->chunk_source[br_index]; + chunk_length = + dict->chunk_offsets[br_index + 1] - dict->chunk_offsets[br_index]; + while (*bytes != 0 && data[*wrapped_last_processed_pos & mask] == + chunk[br_offset]) { + last_command->copy_len_++; + (*bytes)--; + (*wrapped_last_processed_pos)++; + if (++br_offset == chunk_length) { + br_index++; + br_offset = 0; + if (br_index != dict->num_chunks) { + chunk = dict->chunk_source[br_index]; + chunk_length = dict->chunk_offsets[br_index + 1] - + dict->chunk_offsets[br_index]; + } else { + break; + } + } + } + } + } + /* The copy length is at most the metablock size, and thus expressible. */ + GetLengthCode(last_command->insert_len_, + (size_t)((int)(last_command->copy_len_ & 0x1FFFFFF) + + (int)(last_command->copy_len_ >> 25)), + TO_BROTLI_BOOL((last_command->dist_prefix_ & 0x3FF) == 0), + &last_command->cmd_prefix_); + } +} + +/* + Processes the accumulated input data and sets |*out_size| to the length of + the new output meta-block, or to zero if no new output meta-block has been + created (in this case the processed input data is buffered internally). + If |*out_size| is positive, |*output| points to the start of the output + data. If |is_last| or |force_flush| is BROTLI_TRUE, an output meta-block is + always created. However, until |is_last| is BROTLI_TRUE encoder may retain up + to 7 bits of the last byte of output. Byte-alignment could be enforced by + emitting an empty meta-data block. + Returns BROTLI_FALSE if the size of the input data is larger than + input_block_size(). + */ +static BROTLI_BOOL EncodeData( + BrotliEncoderState* s, const BROTLI_BOOL is_last, + const BROTLI_BOOL force_flush, size_t* out_size, uint8_t** output) { + const uint64_t delta = UnprocessedInputSize(s); + uint32_t bytes = (uint32_t)delta; + uint32_t wrapped_last_processed_pos = WrapPosition(s->last_processed_pos_); + uint8_t* data; + uint32_t mask; + MemoryManager* m = &s->memory_manager_; + ContextType literal_context_mode; + ContextLut literal_context_lut; + BROTLI_BOOL fast_compress = + s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || + s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY; + + data = s->ringbuffer_.buffer_; + mask = s->ringbuffer_.mask_; + + if (delta == 0) { /* No new input; still might want to flush or finish. */ + if (!data) { /* No input has been processed so far. */ + if (is_last) { /* Emit complete finalized stream. */ + BROTLI_DCHECK(s->last_bytes_bits_ <= 14); + s->last_bytes_ |= (uint16_t)(3u << s->last_bytes_bits_); + s->last_bytes_bits_ = (uint8_t)(s->last_bytes_bits_ + 2u); + s->tiny_buf_.u8[0] = (uint8_t)s->last_bytes_; + s->tiny_buf_.u8[1] = (uint8_t)(s->last_bytes_ >> 8); + *output = s->tiny_buf_.u8; + *out_size = (s->last_bytes_bits_ + 7u) >> 3u; + return BROTLI_TRUE; + } else { /* No data, not last -> no-op. */ + *out_size = 0; + return BROTLI_TRUE; + } + } else { + /* Fast compress performs flush every block -> flush is no-op. */ + if (!is_last && (!force_flush || fast_compress)) { /* Another no-op. */ + *out_size = 0; + return BROTLI_TRUE; + } + } + } + BROTLI_DCHECK(data); + + if (s->params.quality > s->params.dictionary.max_quality) return BROTLI_FALSE; + /* Adding more blocks after "last" block is forbidden. */ + if (s->is_last_block_emitted_) return BROTLI_FALSE; + if (is_last) s->is_last_block_emitted_ = BROTLI_TRUE; + + if (delta > InputBlockSize(s)) { + return BROTLI_FALSE; + } + if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY && + !s->command_buf_) { + s->command_buf_ = + BROTLI_ALLOC(m, uint32_t, kCompressFragmentTwoPassBlockSize); + s->literal_buf_ = + BROTLI_ALLOC(m, uint8_t, kCompressFragmentTwoPassBlockSize); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->command_buf_) || + BROTLI_IS_NULL(s->literal_buf_)) { + return BROTLI_FALSE; + } + } + + if (fast_compress) { + uint8_t* storage; + size_t storage_ix = s->last_bytes_bits_; + size_t table_size; + int* table; + + storage = GetBrotliStorage(s, 2 * bytes + 503); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + storage[0] = (uint8_t)s->last_bytes_; + storage[1] = (uint8_t)(s->last_bytes_ >> 8); + table = GetHashTable(s, s->params.quality, bytes, &table_size); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { + BrotliCompressFragmentFast( + s->one_pass_arena_, &data[wrapped_last_processed_pos & mask], + bytes, is_last, + table, table_size, + &storage_ix, storage); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } else { + BrotliCompressFragmentTwoPass( + s->two_pass_arena_, &data[wrapped_last_processed_pos & mask], + bytes, is_last, + s->command_buf_, s->literal_buf_, + table, table_size, + &storage_ix, storage); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } + s->last_bytes_ = (uint16_t)(storage[storage_ix >> 3]); + s->last_bytes_bits_ = storage_ix & 7u; + UpdateLastProcessedPos(s); + *output = &storage[0]; + *out_size = storage_ix >> 3; + return BROTLI_TRUE; + } + + { + /* Theoretical max number of commands is 1 per 2 bytes. */ + size_t newsize = s->num_commands_ + bytes / 2 + 1; + if (newsize > s->cmd_alloc_size_) { + Command* new_commands; + /* Reserve a bit more memory to allow merging with a next block + without reallocation: that would impact speed. */ + newsize += (bytes / 4) + 16; + s->cmd_alloc_size_ = newsize; + new_commands = BROTLI_ALLOC(m, Command, newsize); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_commands)) return BROTLI_FALSE; + if (s->commands_) { + memcpy(new_commands, s->commands_, sizeof(Command) * s->num_commands_); + BROTLI_FREE(m, s->commands_); + } + s->commands_ = new_commands; + } + } + + InitOrStitchToPreviousBlock(m, &s->hasher_, data, mask, &s->params, + wrapped_last_processed_pos, bytes, is_last); + + literal_context_mode = ChooseContextMode( + &s->params, data, WrapPosition(s->last_flush_pos_), + mask, (size_t)(s->input_pos_ - s->last_flush_pos_)); + literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); + + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + + if (s->num_commands_ && s->last_insert_len_ == 0) { + ExtendLastCommand(s, &bytes, &wrapped_last_processed_pos); + } + + if (s->params.quality == ZOPFLIFICATION_QUALITY) { + BROTLI_DCHECK(s->params.hasher.type == 10); + BrotliCreateZopfliBackwardReferences(m, bytes, wrapped_last_processed_pos, + data, mask, literal_context_lut, &s->params, + &s->hasher_, s->dist_cache_, + &s->last_insert_len_, &s->commands_[s->num_commands_], + &s->num_commands_, &s->num_literals_); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } else if (s->params.quality == HQ_ZOPFLIFICATION_QUALITY) { + BROTLI_DCHECK(s->params.hasher.type == 10); + BrotliCreateHqZopfliBackwardReferences(m, bytes, wrapped_last_processed_pos, + data, mask, literal_context_lut, &s->params, + &s->hasher_, s->dist_cache_, + &s->last_insert_len_, &s->commands_[s->num_commands_], + &s->num_commands_, &s->num_literals_); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } else { + BrotliCreateBackwardReferences(bytes, wrapped_last_processed_pos, + data, mask, literal_context_lut, &s->params, + &s->hasher_, s->dist_cache_, + &s->last_insert_len_, &s->commands_[s->num_commands_], + &s->num_commands_, &s->num_literals_); + } + + { + const size_t max_length = MaxMetablockSize(&s->params); + const size_t max_literals = max_length / 8; + const size_t max_commands = max_length / 8; + const size_t processed_bytes = (size_t)(s->input_pos_ - s->last_flush_pos_); + /* If maximal possible additional block doesn't fit metablock, flush now. */ + /* TODO(eustas): Postpone decision until next block arrives? */ + const BROTLI_BOOL next_input_fits_metablock = TO_BROTLI_BOOL( + processed_bytes + InputBlockSize(s) <= max_length); + /* If block splitting is not used, then flush as soon as there is some + amount of commands / literals produced. */ + const BROTLI_BOOL should_flush = TO_BROTLI_BOOL( + s->params.quality < MIN_QUALITY_FOR_BLOCK_SPLIT && + s->num_literals_ + s->num_commands_ >= MAX_NUM_DELAYED_SYMBOLS); + if (!is_last && !force_flush && !should_flush && + next_input_fits_metablock && + s->num_literals_ < max_literals && + s->num_commands_ < max_commands) { + /* Merge with next input block. Everything will happen later. */ + if (UpdateLastProcessedPos(s)) { + HasherReset(&s->hasher_); + } + *out_size = 0; + return BROTLI_TRUE; + } + } + + /* Create the last insert-only command. */ + if (s->last_insert_len_ > 0) { + InitInsertCommand(&s->commands_[s->num_commands_++], s->last_insert_len_); + s->num_literals_ += s->last_insert_len_; + s->last_insert_len_ = 0; + } + + if (!is_last && s->input_pos_ == s->last_flush_pos_) { + /* We have no new input data and we don't have to finish the stream, so + nothing to do. */ + *out_size = 0; + return BROTLI_TRUE; + } + BROTLI_DCHECK(s->input_pos_ >= s->last_flush_pos_); + BROTLI_DCHECK(s->input_pos_ > s->last_flush_pos_ || is_last); + BROTLI_DCHECK(s->input_pos_ - s->last_flush_pos_ <= 1u << 24); + { + const uint32_t metablock_size = + (uint32_t)(s->input_pos_ - s->last_flush_pos_); + uint8_t* storage = GetBrotliStorage(s, 2 * metablock_size + 503); + size_t storage_ix = s->last_bytes_bits_; + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + storage[0] = (uint8_t)s->last_bytes_; + storage[1] = (uint8_t)(s->last_bytes_ >> 8); + WriteMetaBlockInternal( + m, data, mask, s->last_flush_pos_, metablock_size, is_last, + literal_context_mode, &s->params, s->prev_byte_, s->prev_byte2_, + s->num_literals_, s->num_commands_, s->commands_, s->saved_dist_cache_, + s->dist_cache_, &storage_ix, storage); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + s->last_bytes_ = (uint16_t)(storage[storage_ix >> 3]); + s->last_bytes_bits_ = storage_ix & 7u; + s->last_flush_pos_ = s->input_pos_; + if (UpdateLastProcessedPos(s)) { + HasherReset(&s->hasher_); + } + if (s->last_flush_pos_ > 0) { + s->prev_byte_ = data[((uint32_t)s->last_flush_pos_ - 1) & mask]; + } + if (s->last_flush_pos_ > 1) { + s->prev_byte2_ = data[(uint32_t)(s->last_flush_pos_ - 2) & mask]; + } + s->num_commands_ = 0; + s->num_literals_ = 0; + /* Save the state of the distance cache in case we need to restore it for + emitting an uncompressed block. */ + memcpy(s->saved_dist_cache_, s->dist_cache_, sizeof(s->saved_dist_cache_)); + *output = &storage[0]; + *out_size = storage_ix >> 3; + return BROTLI_TRUE; + } +} + +/* Dumps remaining output bits and metadata header to |header|. + Returns number of produced bytes. + REQUIRED: |header| should be 8-byte aligned and at least 16 bytes long. + REQUIRED: |block_size| <= (1 << 24). */ +static size_t WriteMetadataHeader( + BrotliEncoderState* s, const size_t block_size, uint8_t* header) { + size_t storage_ix; + storage_ix = s->last_bytes_bits_; + header[0] = (uint8_t)s->last_bytes_; + header[1] = (uint8_t)(s->last_bytes_ >> 8); + s->last_bytes_ = 0; + s->last_bytes_bits_ = 0; + + BrotliWriteBits(1, 0, &storage_ix, header); + BrotliWriteBits(2, 3, &storage_ix, header); + BrotliWriteBits(1, 0, &storage_ix, header); + if (block_size == 0) { + BrotliWriteBits(2, 0, &storage_ix, header); + } else { + uint32_t nbits = (block_size == 1) ? 1 : + (Log2FloorNonZero((uint32_t)block_size - 1) + 1); + uint32_t nbytes = (nbits + 7) / 8; + BrotliWriteBits(2, nbytes, &storage_ix, header); + BrotliWriteBits(8 * nbytes, block_size - 1, &storage_ix, header); + } + return (storage_ix + 7u) >> 3; +} + +size_t BrotliEncoderMaxCompressedSize(size_t input_size) { + /* [window bits / empty metadata] + N * [uncompressed] + [last empty] */ + size_t num_large_blocks = input_size >> 14; + size_t overhead = 2 + (4 * num_large_blocks) + 3 + 1; + size_t result = input_size + overhead; + if (input_size == 0) return 2; + return (result < input_size) ? 0 : result; +} + +/* Wraps data to uncompressed brotli stream with minimal window size. + |output| should point at region with at least BrotliEncoderMaxCompressedSize + addressable bytes. + Returns the length of stream. */ +static size_t MakeUncompressedStream( + const uint8_t* input, size_t input_size, uint8_t* output) { + size_t size = input_size; + size_t result = 0; + size_t offset = 0; + if (input_size == 0) { + output[0] = 6; + return 1; + } + output[result++] = 0x21; /* window bits = 10, is_last = false */ + output[result++] = 0x03; /* empty metadata, padding */ + while (size > 0) { + uint32_t nibbles = 0; + uint32_t chunk_size; + uint32_t bits; + chunk_size = (size > (1u << 24)) ? (1u << 24) : (uint32_t)size; + if (chunk_size > (1u << 16)) nibbles = (chunk_size > (1u << 20)) ? 2 : 1; + bits = + (nibbles << 1) | ((chunk_size - 1) << 3) | (1u << (19 + 4 * nibbles)); + output[result++] = (uint8_t)bits; + output[result++] = (uint8_t)(bits >> 8); + output[result++] = (uint8_t)(bits >> 16); + if (nibbles == 2) output[result++] = (uint8_t)(bits >> 24); + memcpy(&output[result], &input[offset], chunk_size); + result += chunk_size; + offset += chunk_size; + size -= chunk_size; + } + output[result++] = 3; + return result; +} + +BROTLI_BOOL BrotliEncoderCompress( + int quality, int lgwin, BrotliEncoderMode mode, size_t input_size, + const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)], + size_t* encoded_size, + uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]) { + BrotliEncoderState* s; + size_t out_size = *encoded_size; + const uint8_t* input_start = input_buffer; + uint8_t* output_start = encoded_buffer; + size_t max_out_size = BrotliEncoderMaxCompressedSize(input_size); + if (out_size == 0) { + /* Output buffer needs at least one byte. */ + return BROTLI_FALSE; + } + if (input_size == 0) { + /* Handle the special case of empty input. */ + *encoded_size = 1; + *encoded_buffer = 6; + return BROTLI_TRUE; + } + + s = BrotliEncoderCreateInstance(0, 0, 0); + if (!s) { + return BROTLI_FALSE; + } else { + size_t available_in = input_size; + const uint8_t* next_in = input_buffer; + size_t available_out = *encoded_size; + uint8_t* next_out = encoded_buffer; + size_t total_out = 0; + BROTLI_BOOL result = BROTLI_FALSE; + /* TODO(eustas): check that parameters are sane. */ + BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, (uint32_t)quality); + BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, (uint32_t)lgwin); + BrotliEncoderSetParameter(s, BROTLI_PARAM_MODE, (uint32_t)mode); + BrotliEncoderSetParameter(s, BROTLI_PARAM_SIZE_HINT, (uint32_t)input_size); + if (lgwin > BROTLI_MAX_WINDOW_BITS) { + BrotliEncoderSetParameter(s, BROTLI_PARAM_LARGE_WINDOW, BROTLI_TRUE); + } + result = BrotliEncoderCompressStream(s, BROTLI_OPERATION_FINISH, + &available_in, &next_in, &available_out, &next_out, &total_out); + if (!BrotliEncoderIsFinished(s)) result = 0; + *encoded_size = total_out; + BrotliEncoderDestroyInstance(s); + if (!result || (max_out_size && *encoded_size > max_out_size)) { + goto fallback; + } + return BROTLI_TRUE; + } +fallback: + *encoded_size = 0; + if (!max_out_size) return BROTLI_FALSE; + if (out_size >= max_out_size) { + *encoded_size = + MakeUncompressedStream(input_start, input_size, output_start); + return BROTLI_TRUE; + } + return BROTLI_FALSE; +} + +static void InjectBytePaddingBlock(BrotliEncoderState* s) { + uint32_t seal = s->last_bytes_; + size_t seal_bits = s->last_bytes_bits_; + uint8_t* destination; + s->last_bytes_ = 0; + s->last_bytes_bits_ = 0; + /* is_last = 0, data_nibbles = 11, reserved = 0, meta_nibbles = 00 */ + seal |= 0x6u << seal_bits; + seal_bits += 6; + /* If we have already created storage, then append to it. + Storage is valid until next block is being compressed. */ + if (s->next_out_) { + destination = s->next_out_ + s->available_out_; + } else { + destination = s->tiny_buf_.u8; + s->next_out_ = destination; + } + destination[0] = (uint8_t)seal; + if (seal_bits > 8) destination[1] = (uint8_t)(seal >> 8); + if (seal_bits > 16) destination[2] = (uint8_t)(seal >> 16); + s->available_out_ += (seal_bits + 7) >> 3; +} + +/* Fills the |total_out|, if it is not NULL. */ +static void SetTotalOut(BrotliEncoderState* s, size_t* total_out) { + if (total_out) { + /* Saturating conversion uint64_t -> size_t */ + size_t result = (size_t)-1; + if (s->total_out_ < result) { + result = (size_t)s->total_out_; + } + *total_out = result; + } +} + +/* Injects padding bits or pushes compressed data to output. + Returns false if nothing is done. */ +static BROTLI_BOOL InjectFlushOrPushOutput(BrotliEncoderState* s, + size_t* available_out, uint8_t** next_out, size_t* total_out) { + if (s->stream_state_ == BROTLI_STREAM_FLUSH_REQUESTED && + s->last_bytes_bits_ != 0) { + InjectBytePaddingBlock(s); + return BROTLI_TRUE; + } + + if (s->available_out_ != 0 && *available_out != 0) { + size_t copy_output_size = + BROTLI_MIN(size_t, s->available_out_, *available_out); + memcpy(*next_out, s->next_out_, copy_output_size); + *next_out += copy_output_size; + *available_out -= copy_output_size; + s->next_out_ += copy_output_size; + s->available_out_ -= copy_output_size; + s->total_out_ += copy_output_size; + SetTotalOut(s, total_out); + return BROTLI_TRUE; + } + + return BROTLI_FALSE; +} + +static void CheckFlushComplete(BrotliEncoderState* s) { + if (s->stream_state_ == BROTLI_STREAM_FLUSH_REQUESTED && + s->available_out_ == 0) { + s->stream_state_ = BROTLI_STREAM_PROCESSING; + s->next_out_ = 0; + } +} + +static BROTLI_BOOL BrotliEncoderCompressStreamFast( + BrotliEncoderState* s, BrotliEncoderOperation op, size_t* available_in, + const uint8_t** next_in, size_t* available_out, uint8_t** next_out, + size_t* total_out) { + const size_t block_size_limit = (size_t)1 << s->params.lgwin; + const size_t buf_size = BROTLI_MIN(size_t, kCompressFragmentTwoPassBlockSize, + BROTLI_MIN(size_t, *available_in, block_size_limit)); + uint32_t* tmp_command_buf = NULL; + uint32_t* command_buf = NULL; + uint8_t* tmp_literal_buf = NULL; + uint8_t* literal_buf = NULL; + MemoryManager* m = &s->memory_manager_; + if (s->params.quality != FAST_ONE_PASS_COMPRESSION_QUALITY && + s->params.quality != FAST_TWO_PASS_COMPRESSION_QUALITY) { + return BROTLI_FALSE; + } + if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { + if (!s->command_buf_ && buf_size == kCompressFragmentTwoPassBlockSize) { + s->command_buf_ = + BROTLI_ALLOC(m, uint32_t, kCompressFragmentTwoPassBlockSize); + s->literal_buf_ = + BROTLI_ALLOC(m, uint8_t, kCompressFragmentTwoPassBlockSize); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(s->command_buf_) || + BROTLI_IS_NULL(s->literal_buf_)) { + return BROTLI_FALSE; + } + } + if (s->command_buf_) { + command_buf = s->command_buf_; + literal_buf = s->literal_buf_; + } else { + tmp_command_buf = BROTLI_ALLOC(m, uint32_t, buf_size); + tmp_literal_buf = BROTLI_ALLOC(m, uint8_t, buf_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp_command_buf) || + BROTLI_IS_NULL(tmp_literal_buf)) { + return BROTLI_FALSE; + } + command_buf = tmp_command_buf; + literal_buf = tmp_literal_buf; + } + } + + while (BROTLI_TRUE) { + if (InjectFlushOrPushOutput(s, available_out, next_out, total_out)) { + continue; + } + + /* Compress block only when internal output buffer is empty, stream is not + finished, there is no pending flush request, and there is either + additional input or pending operation. */ + if (s->available_out_ == 0 && + s->stream_state_ == BROTLI_STREAM_PROCESSING && + (*available_in != 0 || op != BROTLI_OPERATION_PROCESS)) { + size_t block_size = BROTLI_MIN(size_t, block_size_limit, *available_in); + BROTLI_BOOL is_last = + (*available_in == block_size) && (op == BROTLI_OPERATION_FINISH); + BROTLI_BOOL force_flush = + (*available_in == block_size) && (op == BROTLI_OPERATION_FLUSH); + size_t max_out_size = 2 * block_size + 503; + BROTLI_BOOL inplace = BROTLI_TRUE; + uint8_t* storage = NULL; + size_t storage_ix = s->last_bytes_bits_; + size_t table_size; + int* table; + + if (force_flush && block_size == 0) { + s->stream_state_ = BROTLI_STREAM_FLUSH_REQUESTED; + continue; + } + if (max_out_size <= *available_out) { + storage = *next_out; + } else { + inplace = BROTLI_FALSE; + storage = GetBrotliStorage(s, max_out_size); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } + storage[0] = (uint8_t)s->last_bytes_; + storage[1] = (uint8_t)(s->last_bytes_ >> 8); + table = GetHashTable(s, s->params.quality, block_size, &table_size); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + + if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { + BrotliCompressFragmentFast(s->one_pass_arena_, *next_in, block_size, + is_last, table, table_size, &storage_ix, storage); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } else { + BrotliCompressFragmentTwoPass(s->two_pass_arena_, *next_in, block_size, + is_last, command_buf, literal_buf, table, table_size, + &storage_ix, storage); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + } + if (block_size != 0) { + *next_in += block_size; + *available_in -= block_size; + s->total_in_ += block_size; + } + if (inplace) { + size_t out_bytes = storage_ix >> 3; + BROTLI_DCHECK(out_bytes <= *available_out); + BROTLI_DCHECK((storage_ix & 7) == 0 || out_bytes < *available_out); + *next_out += out_bytes; + *available_out -= out_bytes; + s->total_out_ += out_bytes; + SetTotalOut(s, total_out); + } else { + size_t out_bytes = storage_ix >> 3; + s->next_out_ = storage; + s->available_out_ = out_bytes; + } + s->last_bytes_ = (uint16_t)(storage[storage_ix >> 3]); + s->last_bytes_bits_ = storage_ix & 7u; + + if (force_flush) s->stream_state_ = BROTLI_STREAM_FLUSH_REQUESTED; + if (is_last) s->stream_state_ = BROTLI_STREAM_FINISHED; + continue; + } + break; + } + BROTLI_FREE(m, tmp_command_buf); + BROTLI_FREE(m, tmp_literal_buf); + CheckFlushComplete(s); + return BROTLI_TRUE; +} + +static BROTLI_BOOL ProcessMetadata( + BrotliEncoderState* s, size_t* available_in, const uint8_t** next_in, + size_t* available_out, uint8_t** next_out, size_t* total_out) { + if (*available_in > (1u << 24)) return BROTLI_FALSE; + /* Switch to metadata block workflow, if required. */ + if (s->stream_state_ == BROTLI_STREAM_PROCESSING) { + s->remaining_metadata_bytes_ = (uint32_t)*available_in; + s->stream_state_ = BROTLI_STREAM_METADATA_HEAD; + } + if (s->stream_state_ != BROTLI_STREAM_METADATA_HEAD && + s->stream_state_ != BROTLI_STREAM_METADATA_BODY) { + return BROTLI_FALSE; + } + + while (BROTLI_TRUE) { + if (InjectFlushOrPushOutput(s, available_out, next_out, total_out)) { + continue; + } + if (s->available_out_ != 0) break; + + if (s->input_pos_ != s->last_flush_pos_) { + BROTLI_BOOL result = EncodeData(s, BROTLI_FALSE, BROTLI_TRUE, + &s->available_out_, &s->next_out_); + if (!result) return BROTLI_FALSE; + continue; + } + + if (s->stream_state_ == BROTLI_STREAM_METADATA_HEAD) { + s->next_out_ = s->tiny_buf_.u8; + s->available_out_ = + WriteMetadataHeader(s, s->remaining_metadata_bytes_, s->next_out_); + s->stream_state_ = BROTLI_STREAM_METADATA_BODY; + continue; + } else { + /* Exit workflow only when there is no more input and no more output. + Otherwise client may continue producing empty metadata blocks. */ + if (s->remaining_metadata_bytes_ == 0) { + s->remaining_metadata_bytes_ = BROTLI_UINT32_MAX; + s->stream_state_ = BROTLI_STREAM_PROCESSING; + break; + } + if (*available_out) { + /* Directly copy input to output. */ + uint32_t copy = (uint32_t)BROTLI_MIN( + size_t, s->remaining_metadata_bytes_, *available_out); + memcpy(*next_out, *next_in, copy); + *next_in += copy; + *available_in -= copy; + s->total_in_ += copy; /* not actually data input, though */ + s->remaining_metadata_bytes_ -= copy; + *next_out += copy; + *available_out -= copy; + } else { + /* This guarantees progress in "TakeOutput" workflow. */ + uint32_t copy = BROTLI_MIN(uint32_t, s->remaining_metadata_bytes_, 16); + s->next_out_ = s->tiny_buf_.u8; + memcpy(s->next_out_, *next_in, copy); + *next_in += copy; + *available_in -= copy; + s->total_in_ += copy; /* not actually data input, though */ + s->remaining_metadata_bytes_ -= copy; + s->available_out_ = copy; + } + continue; + } + } + + return BROTLI_TRUE; +} + +static void UpdateSizeHint(BrotliEncoderState* s, size_t available_in) { + if (s->params.size_hint == 0) { + uint64_t delta = UnprocessedInputSize(s); + uint64_t tail = available_in; + uint32_t limit = 1u << 30; + uint32_t total; + if ((delta >= limit) || (tail >= limit) || ((delta + tail) >= limit)) { + total = limit; + } else { + total = (uint32_t)(delta + tail); + } + s->params.size_hint = total; + } +} + +BROTLI_BOOL BrotliEncoderCompressStream( + BrotliEncoderState* s, BrotliEncoderOperation op, size_t* available_in, + const uint8_t** next_in, size_t* available_out, uint8_t** next_out, + size_t* total_out) { + if (!EnsureInitialized(s)) return BROTLI_FALSE; + + /* Unfinished metadata block; check requirements. */ + if (s->remaining_metadata_bytes_ != BROTLI_UINT32_MAX) { + if (*available_in != s->remaining_metadata_bytes_) return BROTLI_FALSE; + if (op != BROTLI_OPERATION_EMIT_METADATA) return BROTLI_FALSE; + } + + if (op == BROTLI_OPERATION_EMIT_METADATA) { + UpdateSizeHint(s, 0); /* First data metablock might be emitted here. */ + return ProcessMetadata( + s, available_in, next_in, available_out, next_out, total_out); + } + + if (s->stream_state_ == BROTLI_STREAM_METADATA_HEAD || + s->stream_state_ == BROTLI_STREAM_METADATA_BODY) { + return BROTLI_FALSE; + } + + if (s->stream_state_ != BROTLI_STREAM_PROCESSING && *available_in != 0) { + return BROTLI_FALSE; + } + if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || + s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { + return BrotliEncoderCompressStreamFast(s, op, available_in, next_in, + available_out, next_out, total_out); + } + while (BROTLI_TRUE) { + size_t remaining_block_size = RemainingInputBlockSize(s); + /* Shorten input to flint size. */ + if (s->flint_ >= 0 && remaining_block_size > (size_t)s->flint_) { + remaining_block_size = (size_t)s->flint_; + } + + if (remaining_block_size != 0 && *available_in != 0) { + size_t copy_input_size = + BROTLI_MIN(size_t, remaining_block_size, *available_in); + CopyInputToRingBuffer(s, copy_input_size, *next_in); + *next_in += copy_input_size; + *available_in -= copy_input_size; + s->total_in_ += copy_input_size; + if (s->flint_ > 0) s->flint_ = (int8_t)(s->flint_ - (int)copy_input_size); + continue; + } + + if (InjectFlushOrPushOutput(s, available_out, next_out, total_out)) { + /* Exit the "emit flint" workflow. */ + if (s->flint_ == BROTLI_FLINT_WAITING_FOR_FLUSHING) { + CheckFlushComplete(s); + if (s->stream_state_ == BROTLI_STREAM_PROCESSING) { + s->flint_ = BROTLI_FLINT_DONE; + } + } + continue; + } + + /* Compress data only when internal output buffer is empty, stream is not + finished and there is no pending flush request. */ + if (s->available_out_ == 0 && + s->stream_state_ == BROTLI_STREAM_PROCESSING) { + if (remaining_block_size == 0 || op != BROTLI_OPERATION_PROCESS) { + BROTLI_BOOL is_last = TO_BROTLI_BOOL( + (*available_in == 0) && op == BROTLI_OPERATION_FINISH); + BROTLI_BOOL force_flush = TO_BROTLI_BOOL( + (*available_in == 0) && op == BROTLI_OPERATION_FLUSH); + BROTLI_BOOL result; + /* Force emitting (uncompressed) piece containing flint. */ + if (!is_last && s->flint_ == 0) { + s->flint_ = BROTLI_FLINT_WAITING_FOR_FLUSHING; + force_flush = BROTLI_TRUE; + } + UpdateSizeHint(s, *available_in); + result = EncodeData(s, is_last, force_flush, + &s->available_out_, &s->next_out_); + if (!result) return BROTLI_FALSE; + if (force_flush) s->stream_state_ = BROTLI_STREAM_FLUSH_REQUESTED; + if (is_last) s->stream_state_ = BROTLI_STREAM_FINISHED; + continue; + } + } + break; + } + CheckFlushComplete(s); + return BROTLI_TRUE; +} + +BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* s) { + return TO_BROTLI_BOOL(s->stream_state_ == BROTLI_STREAM_FINISHED && + !BrotliEncoderHasMoreOutput(s)); +} + +BROTLI_BOOL BrotliEncoderHasMoreOutput(BrotliEncoderState* s) { + return TO_BROTLI_BOOL(s->available_out_ != 0); +} + +const uint8_t* BrotliEncoderTakeOutput(BrotliEncoderState* s, size_t* size) { + size_t consumed_size = s->available_out_; + uint8_t* result = s->next_out_; + if (*size) { + consumed_size = BROTLI_MIN(size_t, *size, s->available_out_); + } + if (consumed_size) { + s->next_out_ += consumed_size; + s->available_out_ -= consumed_size; + s->total_out_ += consumed_size; + CheckFlushComplete(s); + *size = consumed_size; + } else { + *size = 0; + result = 0; + } + return result; +} + +uint32_t BrotliEncoderVersion(void) { + return BROTLI_VERSION; +} + +BrotliEncoderPreparedDictionary* BrotliEncoderPrepareDictionary( + BrotliSharedDictionaryType type, size_t size, + const uint8_t data[BROTLI_ARRAY_PARAM(size)], int quality, + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + ManagedDictionary* managed_dictionary = NULL; + BROTLI_BOOL type_is_known = BROTLI_FALSE; + type_is_known |= (type == BROTLI_SHARED_DICTIONARY_RAW); +#if defined(BROTLI_EXPERIMENTAL) + type_is_known |= (type == BROTLI_SHARED_DICTIONARY_SERIALIZED); +#endif /* BROTLI_EXPERIMENTAL */ + if (!type_is_known) { + return NULL; + } + managed_dictionary = + BrotliCreateManagedDictionary(alloc_func, free_func, opaque); + if (managed_dictionary == NULL) { + return NULL; + } + if (type == BROTLI_SHARED_DICTIONARY_RAW) { + managed_dictionary->dictionary = (uint32_t*)CreatePreparedDictionary( + &managed_dictionary->memory_manager_, data, size); + } +#if defined(BROTLI_EXPERIMENTAL) + if (type == BROTLI_SHARED_DICTIONARY_SERIALIZED) { + SharedEncoderDictionary* dict = (SharedEncoderDictionary*)BrotliAllocate( + &managed_dictionary->memory_manager_, sizeof(SharedEncoderDictionary)); + managed_dictionary->dictionary = (uint32_t*)dict; + if (dict != NULL) { + BROTLI_BOOL ok = BrotliInitCustomSharedEncoderDictionary( + &managed_dictionary->memory_manager_, data, size, quality, dict); + if (!ok) { + BrotliFree(&managed_dictionary->memory_manager_, dict); + managed_dictionary->dictionary = NULL; + } + } + } +#else /* BROTLI_EXPERIMENTAL */ + (void)quality; +#endif /* BROTLI_EXPERIMENTAL */ + if (managed_dictionary->dictionary == NULL) { + BrotliDestroyManagedDictionary(managed_dictionary); + return NULL; + } + return (BrotliEncoderPreparedDictionary*)managed_dictionary; +} + +void BROTLI_COLD BrotliEncoderDestroyPreparedDictionary( + BrotliEncoderPreparedDictionary* dictionary) { + ManagedDictionary* dict = (ManagedDictionary*)dictionary; + if (!dictionary) return; + /* First field of dictionary structs. */ + /* Only managed dictionaries are eligible for destruction by this method. */ + if (dict->magic != kManagedDictionaryMagic) { + return; + } + if (dict->dictionary == NULL) { + /* This should never ever happen. */ + } else if (*dict->dictionary == kLeanPreparedDictionaryMagic) { + DestroyPreparedDictionary( + &dict->memory_manager_, (PreparedDictionary*)dict->dictionary); + } else if (*dict->dictionary == kSharedDictionaryMagic) { + BrotliCleanupSharedEncoderDictionary(&dict->memory_manager_, + (SharedEncoderDictionary*)dict->dictionary); + BrotliFree(&dict->memory_manager_, dict->dictionary); + } else { + /* There is also kPreparedDictionaryMagic, but such instances should be + * constructed and destroyed by different means. */ + } + dict->dictionary = NULL; + BrotliDestroyManagedDictionary(dict); +} + +BROTLI_BOOL BROTLI_COLD BrotliEncoderAttachPreparedDictionary( + BrotliEncoderState* state, + const BrotliEncoderPreparedDictionary* dictionary) { + /* First field of dictionary structs */ + const BrotliEncoderPreparedDictionary* dict = dictionary; + uint32_t magic = *((const uint32_t*)dict); + SharedEncoderDictionary* current = NULL; + if (magic == kManagedDictionaryMagic) { + /* Unwrap managed dictionary. */ + ManagedDictionary* managed_dictionary = (ManagedDictionary*)dict; + magic = *managed_dictionary->dictionary; + dict = (BrotliEncoderPreparedDictionary*)managed_dictionary->dictionary; + } + current = &state->params.dictionary; + if (magic == kPreparedDictionaryMagic || + magic == kLeanPreparedDictionaryMagic) { + const PreparedDictionary* prepared = (const PreparedDictionary*)dict; + if (!AttachPreparedDictionary(¤t->compound, prepared)) { + return BROTLI_FALSE; + } + } else if (magic == kSharedDictionaryMagic) { + const SharedEncoderDictionary* attached = + (const SharedEncoderDictionary*)dict; + BROTLI_BOOL was_default = !current->contextual.context_based && + current->contextual.num_dictionaries == 1 && + current->contextual.dict[0]->hash_table_words == + kStaticDictionaryHashWords && + current->contextual.dict[0]->hash_table_lengths == + kStaticDictionaryHashLengths; + BROTLI_BOOL new_default = !attached->contextual.context_based && + attached->contextual.num_dictionaries == 1 && + attached->contextual.dict[0]->hash_table_words == + kStaticDictionaryHashWords && + attached->contextual.dict[0]->hash_table_lengths == + kStaticDictionaryHashLengths; + size_t i; + if (state->is_initialized_) return BROTLI_FALSE; + current->max_quality = + BROTLI_MIN(int, current->max_quality, attached->max_quality); + for (i = 0; i < attached->compound.num_chunks; i++) { + if (!AttachPreparedDictionary(¤t->compound, + attached->compound.chunks[i])) { + return BROTLI_FALSE; + } + } + if (!new_default) { + if (!was_default) return BROTLI_FALSE; + /* Copy by value, but then set num_instances_ to 0 because their memory + is managed by attached, not by current */ + current->contextual = attached->contextual; + current->contextual.num_instances_ = 0; + } + } else { + return BROTLI_FALSE; + } + return BROTLI_TRUE; +} + +size_t BROTLI_COLD BrotliEncoderEstimatePeakMemoryUsage(int quality, int lgwin, + size_t input_size) { + BrotliEncoderParams params; + size_t memory_manager_slots = BROTLI_ENCODER_MEMORY_MANAGER_SLOTS; + size_t memory_manager_size = memory_manager_slots * sizeof(void*); + BrotliEncoderInitParams(¶ms); + params.quality = quality; + params.lgwin = lgwin; + params.size_hint = input_size; + params.large_window = lgwin > BROTLI_MAX_WINDOW_BITS; + SanitizeParams(¶ms); + params.lgblock = ComputeLgBlock(¶ms); + ChooseHasher(¶ms, ¶ms.hasher); + if (params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || + params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { + size_t state_size = sizeof(BrotliEncoderState); + size_t block_size = BROTLI_MIN(size_t, input_size, ((size_t)1ul << params.lgwin)); + size_t hash_table_size = + HashTableSize(MaxHashTableSize(params.quality), block_size); + size_t hash_size = + (hash_table_size < (1u << 10)) ? 0 : sizeof(int) * hash_table_size; + size_t cmdbuf_size = params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY ? + 5 * BROTLI_MIN(size_t, block_size, 1ul << 17) : 0; + if (params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { + state_size += sizeof(BrotliOnePassArena); + } else { + state_size += sizeof(BrotliTwoPassArena); + } + return hash_size + cmdbuf_size + state_size; + } else { + size_t short_ringbuffer_size = (size_t)1 << params.lgblock; + int ringbuffer_bits = ComputeRbBits(¶ms); + size_t ringbuffer_size = input_size < short_ringbuffer_size ? + input_size : ((size_t)1u << ringbuffer_bits) + short_ringbuffer_size; + size_t hash_size[4] = {0}; + size_t metablock_size = + BROTLI_MIN(size_t, input_size, MaxMetablockSize(¶ms)); + size_t inputblock_size = + BROTLI_MIN(size_t, input_size, (size_t)1 << params.lgblock); + size_t cmdbuf_size = metablock_size * 2 + inputblock_size * 6; + size_t outbuf_size = metablock_size * 2 + 503; + size_t histogram_size = 0; + HasherSize(¶ms, BROTLI_TRUE, input_size, hash_size); + if (params.quality < MIN_QUALITY_FOR_BLOCK_SPLIT) { + cmdbuf_size = BROTLI_MIN(size_t, cmdbuf_size, + MAX_NUM_DELAYED_SYMBOLS * sizeof(Command) + inputblock_size * 12); + } + if (params.quality >= MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING) { + /* Only a very rough estimation, based on enwik8. */ + histogram_size = 200 << 20; + } else if (params.quality >= MIN_QUALITY_FOR_BLOCK_SPLIT) { + size_t literal_histograms = + BROTLI_MIN(size_t, metablock_size / 6144, 256); + size_t command_histograms = + BROTLI_MIN(size_t, metablock_size / 6144, 256); + size_t distance_histograms = + BROTLI_MIN(size_t, metablock_size / 6144, 256); + histogram_size = literal_histograms * sizeof(HistogramLiteral) + + command_histograms * sizeof(HistogramCommand) + + distance_histograms * sizeof(HistogramDistance); + } + return (memory_manager_size + ringbuffer_size + + hash_size[0] + hash_size[1] + hash_size[2] + hash_size[3] + + cmdbuf_size + + outbuf_size + + histogram_size); + } +} +size_t BROTLI_COLD BrotliEncoderGetPreparedDictionarySize( + const BrotliEncoderPreparedDictionary* prepared_dictionary) { + /* First field of dictionary structs */ + const BrotliEncoderPreparedDictionary* prepared = prepared_dictionary; + uint32_t magic = *((const uint32_t*)prepared); + size_t overhead = 0; + if (magic == kManagedDictionaryMagic) { + const ManagedDictionary* managed = (const ManagedDictionary*)prepared; + overhead = sizeof(ManagedDictionary); + magic = *managed->dictionary; + prepared = (const BrotliEncoderPreparedDictionary*)managed->dictionary; + } + + if (magic == kPreparedDictionaryMagic) { + const PreparedDictionary* dictionary = + (const PreparedDictionary*)prepared; + /* Keep in sync with step 3 of CreatePreparedDictionary */ + return sizeof(PreparedDictionary) + dictionary->source_size + + (sizeof(uint32_t) << dictionary->slot_bits) + + (sizeof(uint16_t) << dictionary->bucket_bits) + + (sizeof(uint32_t) * dictionary->num_items) + overhead; + } else if (magic == kLeanPreparedDictionaryMagic) { + const PreparedDictionary* dictionary = + (const PreparedDictionary*)prepared; + /* Keep in sync with step 3 of CreatePreparedDictionary */ + return sizeof(PreparedDictionary) + sizeof(uint8_t*) + + (sizeof(uint32_t) << dictionary->slot_bits) + + (sizeof(uint16_t) << dictionary->bucket_bits) + + (sizeof(uint32_t) * dictionary->num_items) + overhead; + } else if (magic == kSharedDictionaryMagic) { + const SharedEncoderDictionary* dictionary = + (const SharedEncoderDictionary*)prepared; + const CompoundDictionary* compound = &dictionary->compound; + const ContextualEncoderDictionary* contextual = &dictionary->contextual; + size_t result = sizeof(*dictionary); + size_t i; + size_t num_instances; + const BrotliEncoderDictionary* instances; + for (i = 0; i < compound->num_prepared_instances_; i++) { + size_t size = BrotliEncoderGetPreparedDictionarySize( + (const BrotliEncoderPreparedDictionary*) + compound->prepared_instances_[i]); + if (!size) return 0; /* error */ + result += size; + } + if (contextual->context_based) { + num_instances = contextual->num_instances_; + instances = contextual->instances_; + result += sizeof(*instances) * num_instances; + } else { + num_instances = 1; + instances = &contextual->instance_; + } + for (i = 0; i < num_instances; i++) { + const BrotliEncoderDictionary* dict = &instances[i]; + result += dict->trie.pool_capacity * sizeof(BrotliTrieNode); + if (dict->hash_table_data_words_) { + result += sizeof(kStaticDictionaryHashWords); + } + if (dict->hash_table_data_lengths_) { + result += sizeof(kStaticDictionaryHashLengths); + } + if (dict->buckets_data_) { + result += sizeof(*dict->buckets_data_) * dict->buckets_alloc_size_; + } + if (dict->dict_words_data_) { + result += sizeof(*dict->dict_words) * dict->dict_words_alloc_size_; + } + if (dict->words_instance_) { + result += sizeof(*dict->words_instance_); + /* data_size not added here: it is never allocated by the + SharedEncoderDictionary, instead it always points to the file + already loaded in memory. So if the caller wants to include + this memory as well, add the size of the loaded dictionary + file to this. */ + } + } + return result + overhead; + } + return 0; /* error */ +} + +#if defined(BROTLI_TEST) +size_t BrotliMakeUncompressedStreamForTest(const uint8_t*, size_t, uint8_t*); +size_t BrotliMakeUncompressedStreamForTest( + const uint8_t* input, size_t input_size, uint8_t* output) { + return MakeUncompressedStream(input, input_size, output); +} +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/encoder_dict.c b/packages/runtime/vendor/brotli/c/enc/encoder_dict.c new file mode 100644 index 000000000..f75ee5984 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/encoder_dict.c @@ -0,0 +1,642 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "encoder_dict.h" + +#include "../common/dictionary.h" +#include "../common/platform.h" +#include +#include "../common/shared_dictionary_internal.h" +#include "../common/transform.h" +#include +#include "compound_dictionary.h" +#include "dictionary_hash.h" +#include "hash_base.h" +#include "hash.h" +#include "memory.h" +#include "quality.h" +#include "static_dict_lut.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define NUM_HASH_BITS 15u +#define NUM_HASH_BUCKETS (1u << NUM_HASH_BITS) + +static void BrotliTrieInit(BrotliTrie* trie) { + trie->pool_capacity = 0; + trie->pool_size = 0; + trie->pool = 0; + + /* Set up the root node */ + trie->root.single = 0; + trie->root.len_ = 0; + trie->root.idx_ = 0; + trie->root.sub = 0; +} + +static void BrotliTrieFree(MemoryManager* m, BrotliTrie* trie) { + BrotliFree(m, trie->pool); +} + +/* Initializes to RFC 7932 static dictionary / transforms. */ +static void InitEncoderDictionary(BrotliEncoderDictionary* dict) { + dict->words = BrotliGetDictionary(); + dict->num_transforms = (uint32_t)BrotliGetTransforms()->num_transforms; + + dict->hash_table_words = kStaticDictionaryHashWords; + dict->hash_table_lengths = kStaticDictionaryHashLengths; + dict->buckets = kStaticDictionaryBuckets; + dict->dict_words = kStaticDictionaryWords; + + dict->cutoffTransformsCount = kCutoffTransformsCount; + dict->cutoffTransforms = kCutoffTransforms; + + dict->parent = 0; + + dict->hash_table_data_words_ = 0; + dict->hash_table_data_lengths_ = 0; + dict->buckets_alloc_size_ = 0; + dict->buckets_data_ = 0; + dict->dict_words_alloc_size_ = 0; + dict->dict_words_data_ = 0; + dict->words_instance_ = 0; + dict->has_words_heavy = BROTLI_FALSE; + BrotliTrieInit(&dict->trie); +} + +static void BrotliDestroyEncoderDictionary(MemoryManager* m, + BrotliEncoderDictionary* dict) { + BrotliFree(m, dict->hash_table_data_words_); + BrotliFree(m, dict->hash_table_data_lengths_); + BrotliFree(m, dict->buckets_data_); + BrotliFree(m, dict->dict_words_data_); + BrotliFree(m, dict->words_instance_); + BrotliTrieFree(m, &dict->trie); +} + +#if defined(BROTLI_EXPERIMENTAL) +/* Word length must be at least 4 bytes */ +static uint32_t Hash(const uint8_t* data, int bits) { + uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> (32 - bits); +} + +/* Theoretical max possible word size after transform */ +#define kTransformedBufferSize \ + (256 + 256 + SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH) + +/* To be safe buffer must have at least kTransformedBufferSize */ +static void TransformedDictionaryWord(uint32_t word_idx, int len, int transform, + const BrotliTransforms* transforms, + const BrotliEncoderDictionary* dict, + uint8_t* buffer, size_t* size) { + const uint8_t* dict_word = &dict->words->data[ + dict->words->offsets_by_length[len] + (uint32_t)len * word_idx]; + *size = (size_t)BrotliTransformDictionaryWord(buffer, dict_word, len, + transforms, transform); +} + +static DictWord MakeDictWord(uint8_t len, uint8_t transform, uint16_t idx) { + DictWord result; + result.len = len; + result.transform = transform; + result.idx = idx; + return result; +} + +static uint32_t BrotliTrieAlloc(MemoryManager* m, size_t num, BrotliTrie* trie, + BrotliTrieNode** keep) { + uint32_t result; + uint32_t keep_index = 0; + if (keep && *keep != &trie->root) { + /* Optional node to keep, since address may change after re-allocating */ + keep_index = (uint32_t)(*keep - trie->pool); + } + if (trie->pool_size == 0) { + /* Have a placeholder node in the front. We do not want the result to be 0, + it must be at least 1, 0 represents "null pointer" */ + trie->pool_size = 1; + } + BROTLI_ENSURE_CAPACITY(m, BrotliTrieNode, trie->pool, trie->pool_capacity, + trie->pool_size + num); + if (BROTLI_IS_OOM(m)) return 0; + /* Init the new nodes to empty */ + memset(trie->pool + trie->pool_size, 0, sizeof(*trie->pool) * num); + result = (uint32_t)trie->pool_size; + trie->pool_size += num; + if (keep && *keep != &trie->root) { + *keep = trie->pool + keep_index; + } + return result; +} + +/** + * len and idx: payload for last node + * word, size: the string + * index: position in the string + */ +static BROTLI_BOOL BrotliTrieNodeAdd(MemoryManager* m, uint8_t len, + uint32_t idx, const uint8_t* word, size_t size, int index, + BrotliTrieNode* node, BrotliTrie* trie) { + BrotliTrieNode* child = 0; + uint8_t c; + if ((size_t)index == size) { + if (!node->len_ || idx < node->idx_) { + node->len_ = len; + node->idx_ = idx; + } + return BROTLI_TRUE; + } + c = word[index]; + if (node->single && c != node->c) { + BrotliTrieNode old = trie->pool[node->sub]; + uint32_t new_nodes = BrotliTrieAlloc(m, 32, trie, &node); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + node->single = 0; + node->sub = new_nodes; + trie->pool[node->sub + (node->c >> 4)].sub = new_nodes + 16; + trie->pool[trie->pool[node->sub + (node->c >> 4)].sub + (node->c & 15)] = + old; + } + if (!node->sub) { + uint32_t new_node = BrotliTrieAlloc(m, 1, trie, &node); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + node->single = 1; + node->c = c; + node->sub = new_node; + } + if (node->single) { + child = &trie->pool[node->sub]; + } else { + if (!trie->pool[node->sub + (c >> 4)].sub) { + uint32_t new_nodes = BrotliTrieAlloc(m, 16, trie, &node); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + trie->pool[node->sub + (c >> 4)].sub = new_nodes; + } + child = &trie->pool[trie->pool[node->sub + (c >> 4)].sub + (c & 15)]; + } + return BrotliTrieNodeAdd(m, len, idx, word, size, index + 1, child, trie); +} + +static BROTLI_BOOL BrotliTrieAdd(MemoryManager* m, uint8_t len, uint32_t idx, + const uint8_t* word, size_t size, BrotliTrie* trie) { + return BrotliTrieNodeAdd(m, len, idx, word, size, 0, &trie->root, trie); +} + +const BrotliTrieNode* BrotliTrieSub(const BrotliTrie* trie, + const BrotliTrieNode* node, uint8_t c) { + BrotliTrieNode* temp_node; + if (node->single) { + if (node->c == c) return &trie->pool[node->sub]; + return 0; + } + if (!node->sub) return 0; + temp_node = &trie->pool[node->sub + (c >> 4)]; + if (!temp_node->sub) return 0; + return &trie->pool[temp_node->sub + (c & 15)]; +} + +static const BrotliTrieNode* BrotliTrieFind(const BrotliTrie* trie, + const uint8_t* word, size_t size) { + const BrotliTrieNode* node = &trie->root; + size_t i; + for (i = 0; i < size; i++) { + node = BrotliTrieSub(trie, node, word[i]); + if (!node) return 0; + } + return node; +} + +static BROTLI_BOOL BuildDictionaryLut(MemoryManager* m, + const BrotliTransforms* transforms, + BrotliEncoderDictionary* dict) { + uint32_t i; + DictWord* dict_words; + uint16_t* buckets; + DictWord** words_by_hash; + size_t* words_by_hash_size; + size_t* words_by_hash_capacity; + BrotliTrie dedup; + uint8_t word[kTransformedBufferSize]; + size_t word_size; + size_t total = 0; + uint8_t l; + uint16_t idx; + + BrotliTrieInit(&dedup); + + words_by_hash = (DictWord**)BrotliAllocate(m, + sizeof(*words_by_hash) * NUM_HASH_BUCKETS); + words_by_hash_size = (size_t*)BrotliAllocate(m, + sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS); + words_by_hash_capacity = (size_t*)BrotliAllocate(m, + sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + memset(words_by_hash, 0, sizeof(*words_by_hash) * NUM_HASH_BUCKETS); + memset(words_by_hash_size, 0, sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS); + memset(words_by_hash_capacity, 0, + sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS); + + if (transforms->num_transforms > 0) { + for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; + l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) { + uint16_t n = dict->words->size_bits_by_length[l] ? + (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u; + for (idx = 0; idx < n; ++idx) { + uint32_t key; + /* First transform (usually identity) */ + TransformedDictionaryWord(idx, l, 0, transforms, dict, word, + &word_size); + /* Cannot hash words smaller than 4 bytes */ + if (word_size < 4) { + /* Break instead of continue, all next words of this length will have + same length after transform */ + break; + } + if (!BrotliTrieAdd(m, 0, idx, word, word_size, &dedup)) { + return BROTLI_FALSE; + } + key = Hash(word, NUM_HASH_BITS); + BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key], + words_by_hash_capacity[key], words_by_hash_size[key], + MakeDictWord(l, 0, idx)); + ++total; + } + } + } + + /* These LUT transforms only supported if no custom transforms. This is + ok, we will use the heavy trie instead. */ + if (transforms == BrotliGetTransforms()) { + for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; + l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) { + uint16_t n = dict->words->size_bits_by_length[l] ? + (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u; + for (idx = 0; idx < n; ++idx) { + int k; + BROTLI_BOOL is_ascii = BROTLI_TRUE; + size_t offset = dict->words->offsets_by_length[l] + (size_t)l * idx; + const uint8_t* data = &dict->words->data[offset]; + for (k = 0; k < l; ++k) { + if (data[k] >= 128) is_ascii = BROTLI_FALSE; + } + if (data[0] < 128) { + int transform = 9; /* {empty, uppercase first, empty} */ + uint32_t ix = idx + (uint32_t)transform * n; + const BrotliTrieNode* it; + TransformedDictionaryWord(idx, l, transform, transforms, + dict, word, &word_size); + it = BrotliTrieFind(&dedup, word, word_size); + if (!it || it->idx_ > ix) { + uint32_t key = Hash(word, NUM_HASH_BITS); + if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) { + return BROTLI_FALSE; + } + BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key], + words_by_hash_capacity[key], words_by_hash_size[key], + MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_FIRST, idx)); + ++total; + } + } + if (is_ascii) { + int transform = 44; /* {empty, uppercase all, empty} */ + uint32_t ix = idx + (uint32_t)transform * n; + const BrotliTrieNode* it; + TransformedDictionaryWord(idx, l, transform, transforms, + dict, word, &word_size); + it = BrotliTrieFind(&dedup, word, word_size); + if (!it || it->idx_ > ix) { + uint32_t key = Hash(word, NUM_HASH_BITS); + if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) { + return BROTLI_FALSE; + } + BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key], + words_by_hash_capacity[key], words_by_hash_size[key], + MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_ALL, idx)); + ++total; + } + } + } + } + } + + dict_words = (DictWord*)BrotliAllocate(m, + sizeof(*dict->dict_words) * (total + 1)); + buckets = (uint16_t*)BrotliAllocate(m, + sizeof(*dict->buckets) * NUM_HASH_BUCKETS); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + dict->dict_words_alloc_size_ = total + 1; + dict->dict_words = dict->dict_words_data_ = dict_words; + dict->buckets_alloc_size_ = NUM_HASH_BUCKETS; + dict->buckets = dict->buckets_data_ = buckets; + + /* Unused; makes offsets start from 1. */ + dict_words[0] = MakeDictWord(0, 0, 0); + total = 1; + for (i = 0; i < NUM_HASH_BUCKETS; ++i) { + size_t num_words = words_by_hash_size[i]; + if (num_words > 0) { + buckets[i] = (uint16_t)(total); + memcpy(&dict_words[total], &words_by_hash[i][0], + sizeof(dict_words[0]) * num_words); + total += num_words; + dict_words[total - 1].len |= 0x80; + } else { + buckets[i] = 0; + } + } + + for (i = 0; i < NUM_HASH_BUCKETS; ++i) { + BrotliFree(m, words_by_hash[i]); + } + BrotliFree(m, words_by_hash); + BrotliFree(m, words_by_hash_size); + BrotliFree(m, words_by_hash_capacity); + BrotliTrieFree(m, &dedup); + + return BROTLI_TRUE; +} + +static void BuildDictionaryHashTable(uint16_t* hash_table_words, + uint8_t* hash_table_lengths, const BrotliDictionary* dict) { + int j, len; + /* The order of the loops is such that in case of collision, words with + shorter length are preferred, and in case of same length, words with + smaller index. There is only a single word per bucket. */ + /* TODO(lode): consider adding optional user-supplied frequency_map to use + for preferred words instead, this can make the encoder better for + quality 9 and below without affecting the decoder */ + memset(hash_table_words, 0, sizeof(kStaticDictionaryHashWords)); + memset(hash_table_lengths, 0, sizeof(kStaticDictionaryHashLengths)); + for (len = SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; + len >= SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; --len) { + const size_t num_words = dict->size_bits_by_length[len] ? + (1u << dict->size_bits_by_length[len]) : 0; + for (j = (int)num_words - 1; j >= 0; --j) { + size_t offset = dict->offsets_by_length[len] + + (size_t)len * (size_t)j; + const uint8_t* word = &dict->data[offset]; + const uint32_t key = Hash(word, 14); + int idx = (int)(key << 1) + (len < 8 ? 1 : 0); + BROTLI_DCHECK(idx < (int)NUM_HASH_BUCKETS); + hash_table_words[idx] = (uint16_t)j; + hash_table_lengths[idx] = (uint8_t)len; + } + } +} + +static BROTLI_BOOL GenerateWordsHeavy(MemoryManager* m, + const BrotliTransforms* transforms, + BrotliEncoderDictionary* dict) { + int i, j, l; + for (j = (int)transforms->num_transforms - 1; j >= 0 ; --j) { + for (l = 0; l < 32; l++) { + int num = (int)((1u << dict->words->size_bits_by_length[l]) & ~1u); + for (i = 0; i < num; i++) { + uint8_t transformed[kTransformedBufferSize]; + size_t size; + TransformedDictionaryWord( + (uint32_t)i, l, j, transforms, dict, transformed, &size); + if (size < 4) continue; + if (!BrotliTrieAdd(m, (uint8_t)l, (uint32_t)(i + num * j), + transformed, size, &dict->trie)) { + return BROTLI_FALSE; + } + } + } + } + return BROTLI_TRUE; +} + +/* Computes cutoffTransformsCount (in count) and cutoffTransforms (in data) for + the custom transforms, where possible within the limits of the + cutoffTransforms encoding. The fast encoder uses this to do fast lookup for + transforms that remove the N last characters (OmitLast). */ +static void ComputeCutoffTransforms( + const BrotliTransforms* transforms, + uint32_t* count, uint64_t* data) { + int i; + /* The encoding in a 64-bit integer of transform N in the data is: (N << 2) + + ((cutoffTransforms >> (N * 6)) & 0x3F), so for example the identity + transform code must be 0-63, for N=1 the transform code must be 4-67, ..., + for N=9 it must be 36-99. + TODO(lode): consider a simple flexible uint8_t[10] instead of the uint64_t + for the cutoff transforms, so that shared dictionaries can have the + OmitLast transforms anywhere without loss. */ + *count = 0; + *data = 0; + for (i = 0; i < BROTLI_TRANSFORMS_MAX_CUT_OFF + 1; i++) { + int idx = transforms->cutOffTransforms[i]; + if (idx == -1) break; /* Not found */ + if (idx < (i << 2)) break; /* Too small for the encoding */ + if (idx >= (i << 2) + 64) break; /* Too large for the encoding */ + (*count)++; + *data |= (uint64_t)(((uint64_t)idx - + ((uint64_t)i << 2u)) << ((uint64_t)i * 6u)); + } +} + +static BROTLI_BOOL ComputeDictionary(MemoryManager* m, int quality, + const BrotliTransforms* transforms, + BrotliEncoderDictionary* current) { + int default_words = current->words == BrotliGetDictionary(); + int default_transforms = transforms == BrotliGetTransforms(); + + if (default_words && default_transforms) { + /* hashes are already set to Brotli defaults */ + return BROTLI_TRUE; + } + + current->hash_table_data_words_ = (uint16_t*)BrotliAllocate( + m, sizeof(kStaticDictionaryHashWords)); + current->hash_table_data_lengths_ = (uint8_t*)BrotliAllocate( + m, sizeof(kStaticDictionaryHashLengths)); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + current->hash_table_words = current->hash_table_data_words_; + current->hash_table_lengths = current->hash_table_data_lengths_; + + BuildDictionaryHashTable(current->hash_table_data_words_, + current->hash_table_data_lengths_, current->words); + + ComputeCutoffTransforms(transforms, + ¤t->cutoffTransformsCount, ¤t->cutoffTransforms); + + /* Only compute the data for slow encoder if the requested quality is high + enough to need it */ + if (quality >= ZOPFLIFICATION_QUALITY) { + if (!BuildDictionaryLut(m, transforms, current)) return BROTLI_FALSE; + + /* For the built-in Brotli transforms, there is a hard-coded function to + handle all transforms, but for custom transforms, we use the following + large hammer instead */ + current->has_words_heavy = !default_transforms; + if (current->has_words_heavy) { + if (!GenerateWordsHeavy(m, transforms, current)) return BROTLI_FALSE; + } + } + + return BROTLI_TRUE; +} +#endif /* BROTLI_EXPERIMENTAL */ + +void BrotliInitSharedEncoderDictionary(SharedEncoderDictionary* dict) { + dict->magic = kSharedDictionaryMagic; + + dict->compound.num_chunks = 0; + dict->compound.total_size = 0; + dict->compound.chunk_offsets[0] = 0; + dict->compound.num_prepared_instances_ = 0; + + dict->contextual.context_based = 0; + dict->contextual.num_dictionaries = 1; + dict->contextual.instances_ = 0; + dict->contextual.num_instances_ = 1; /* The instance_ field */ + dict->contextual.dict[0] = &dict->contextual.instance_; + InitEncoderDictionary(&dict->contextual.instance_); + dict->contextual.instance_.parent = &dict->contextual; + + dict->max_quality = BROTLI_MAX_QUALITY; +} + +#if defined(BROTLI_EXPERIMENTAL) +/* TODO(eustas): make sure that tooling will warn user if not all the cutoff + transforms are available (for low-quality encoder). */ +static BROTLI_BOOL InitCustomSharedEncoderDictionary( + MemoryManager* m, const BrotliSharedDictionary* decoded_dict, + int quality, SharedEncoderDictionary* dict) { + ContextualEncoderDictionary* contextual; + CompoundDictionary* compound; + BrotliEncoderDictionary* instances; + int i; + BrotliInitSharedEncoderDictionary(dict); + + contextual = &dict->contextual; + compound = &dict->compound; + + for (i = 0; i < (int)decoded_dict->num_prefix; i++) { + PreparedDictionary* prepared = CreatePreparedDictionary(m, + decoded_dict->prefix[i], decoded_dict->prefix_size[i]); + AttachPreparedDictionary(compound, prepared); + /* remember for cleanup */ + compound->prepared_instances_[ + compound->num_prepared_instances_++] = prepared; + } + + dict->max_quality = quality; + contextual->context_based = decoded_dict->context_based; + if (decoded_dict->context_based) { + memcpy(contextual->context_map, decoded_dict->context_map, + SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS); + } + + contextual->num_dictionaries = decoded_dict->num_dictionaries; + contextual->num_instances_ = decoded_dict->num_dictionaries; + if (contextual->num_instances_ == 1) { + instances = &contextual->instance_; + } else { + contextual->instances_ = (BrotliEncoderDictionary*) + BrotliAllocate(m, sizeof(*contextual->instances_) * + contextual->num_instances_); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + instances = contextual->instances_; + } + for (i = 0; i < (int)contextual->num_instances_; i++) { + BrotliEncoderDictionary* current = &instances[i]; + InitEncoderDictionary(current); + current->parent = &dict->contextual; + if (decoded_dict->words[i] == BrotliGetDictionary()) { + current->words = BrotliGetDictionary(); + } else { + current->words_instance_ = (BrotliDictionary*)BrotliAllocate( + m, sizeof(BrotliDictionary)); + if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; + *current->words_instance_ = *decoded_dict->words[i]; + current->words = current->words_instance_; + } + current->num_transforms = + (uint32_t)decoded_dict->transforms[i]->num_transforms; + if (!ComputeDictionary( + m, quality, decoded_dict->transforms[i], current)) { + return BROTLI_FALSE; + } + + contextual->dict[i] = current; + } + + return BROTLI_TRUE; /* success */ +} + +BROTLI_BOOL BrotliInitCustomSharedEncoderDictionary( + MemoryManager* m, const uint8_t* encoded_dict, size_t size, + int quality, SharedEncoderDictionary* dict) { + BROTLI_BOOL success = BROTLI_FALSE; + BrotliSharedDictionary* decoded_dict = BrotliSharedDictionaryCreateInstance( + m->alloc_func, m->free_func, m->opaque); + if (!decoded_dict) { /* OOM */ + return BROTLI_FALSE; + } + success = BrotliSharedDictionaryAttach( + decoded_dict, BROTLI_SHARED_DICTIONARY_SERIALIZED, size, encoded_dict); + if (success) { + success = InitCustomSharedEncoderDictionary(m, + decoded_dict, quality, dict); + } + BrotliSharedDictionaryDestroyInstance(decoded_dict); + return success; +} +#endif /* BROTLI_EXPERIMENTAL */ + +void BrotliCleanupSharedEncoderDictionary(MemoryManager* m, + SharedEncoderDictionary* dict) { + size_t i; + for (i = 0; i < dict->compound.num_prepared_instances_; i++) { + DestroyPreparedDictionary(m, + (PreparedDictionary*)dict->compound.prepared_instances_[i]); + } + if (dict->contextual.num_instances_ == 1) { + BrotliDestroyEncoderDictionary(m, &dict->contextual.instance_); + } else if (dict->contextual.num_instances_ > 1) { + for (i = 0; i < dict->contextual.num_instances_; i++) { + BrotliDestroyEncoderDictionary(m, &dict->contextual.instances_[i]); + } + BrotliFree(m, dict->contextual.instances_); + } +} + +ManagedDictionary* BrotliCreateManagedDictionary( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + ManagedDictionary* result = (ManagedDictionary*)BrotliBootstrapAlloc( + sizeof(ManagedDictionary), alloc_func, free_func, opaque); + if (result == NULL) return NULL; + + result->magic = kManagedDictionaryMagic; + BrotliInitMemoryManager( + &result->memory_manager_, alloc_func, free_func, opaque); + result->dictionary = NULL; + + return result; +} + +void BrotliDestroyManagedDictionary(ManagedDictionary* dictionary) { + if (!dictionary) return; + BrotliBootstrapFree(dictionary, &dictionary->memory_manager_); +} + +/* Escalate internal functions visibility; for testing purposes only. */ +#if defined(BROTLI_TEST) +void BrotliInitEncoderDictionaryForTest(BrotliEncoderDictionary*); +void BrotliInitEncoderDictionaryForTest(BrotliEncoderDictionary* d) { + InitEncoderDictionary(d); +} +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/encoder_dict.h b/packages/runtime/vendor/brotli/c/enc/encoder_dict.h new file mode 100644 index 000000000..76f037977 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/encoder_dict.h @@ -0,0 +1,155 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#ifndef BROTLI_ENC_ENCODER_DICT_H_ +#define BROTLI_ENC_ENCODER_DICT_H_ + +#include "../common/dictionary.h" +#include +#include "../common/platform.h" +#include "compound_dictionary.h" +#include "memory.h" +#include "static_dict_lut.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* +Dictionary hierarchy for Encoder: +-SharedEncoderDictionary +--CompoundDictionary +---PreparedDictionary [up to 15x] + = prefix dictionary with precomputed hashes +--ContextualEncoderDictionary +---BrotliEncoderDictionary [up to 64x] + = for each context, precomputed static dictionary with words + transforms + +Dictionary hierarchy from common: similar, but without precomputed hashes +-BrotliSharedDictionary +--BrotliDictionary [up to 64x] +--BrotliTransforms [up to 64x] +--const uint8_t* prefix [up to 15x]: compound dictionaries +*/ + +typedef struct BrotliTrieNode { + uint8_t single; /* if 1, sub is a single node for c instead of 256 */ + uint8_t c; + uint8_t len_; /* untransformed length */ + uint32_t idx_; /* word index + num words * transform index */ + uint32_t sub; /* index of sub node(s) in the pool */ +} BrotliTrieNode; + +typedef struct BrotliTrie { + BrotliTrieNode* pool; + size_t pool_capacity; + size_t pool_size; + BrotliTrieNode root; +} BrotliTrie; + +#if defined(BROTLI_EXPERIMENTAL) +BROTLI_INTERNAL const BrotliTrieNode* BrotliTrieSub(const BrotliTrie* trie, + const BrotliTrieNode* node, uint8_t c); +#endif /* BROTLI_EXPERIMENTAL */ + +/* Dictionary data (words and transforms) for 1 possible context */ +typedef struct BrotliEncoderDictionary { + const BrotliDictionary* words; + uint32_t num_transforms; + + /* cut off for fast encoder */ + uint32_t cutoffTransformsCount; + uint64_t cutoffTransforms; + + /* from dictionary_hash.h, for fast encoder */ + const uint16_t* hash_table_words; + const uint8_t* hash_table_lengths; + + /* from static_dict_lut.h, for slow encoder */ + const uint16_t* buckets; + const DictWord* dict_words; + /* Heavy version, for use by slow encoder when there are custom transforms. + Contains every possible transformed dictionary word in a trie. It encodes + about as fast as the non-heavy encoder but consumes a lot of memory and + takes time to build. */ + BrotliTrie trie; + BROTLI_BOOL has_words_heavy; + + /* Reference to other dictionaries. */ + const struct ContextualEncoderDictionary* parent; + + /* Allocated memory, used only when not using the Brotli defaults */ + uint16_t* hash_table_data_words_; + uint8_t* hash_table_data_lengths_; + size_t buckets_alloc_size_; + uint16_t* buckets_data_; + size_t dict_words_alloc_size_; + DictWord* dict_words_data_; + BrotliDictionary* words_instance_; +} BrotliEncoderDictionary; + +/* Dictionary data for all 64 contexts */ +typedef struct ContextualEncoderDictionary { + BROTLI_BOOL context_based; + uint8_t num_dictionaries; + uint8_t context_map[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; + const BrotliEncoderDictionary* dict[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS]; + + /* If num_instances_ is 1, instance_ is used, else dynamic allocation with + instances_ is used. */ + size_t num_instances_; + BrotliEncoderDictionary instance_; + BrotliEncoderDictionary* instances_; +} ContextualEncoderDictionary; + +typedef struct SharedEncoderDictionary { + /* Magic value to distinguish this struct from PreparedDictionary for + certain external usages. */ + uint32_t magic; + + /* LZ77 prefix, compound dictionary */ + CompoundDictionary compound; + + /* Custom static dictionary (optionally context-based) */ + ContextualEncoderDictionary contextual; + + /* The maximum quality the dictionary was computed for */ + int max_quality; +} SharedEncoderDictionary; + +typedef struct ManagedDictionary { + uint32_t magic; + MemoryManager memory_manager_; + uint32_t* dictionary; +} ManagedDictionary; + +/* Initializes to the brotli built-in dictionary */ +BROTLI_INTERNAL void BrotliInitSharedEncoderDictionary( + SharedEncoderDictionary* dict); + +#if defined(BROTLI_EXPERIMENTAL) +/* Initializes to shared dictionary that will be parsed from + encoded_dict. Requires that you keep the encoded_dict buffer + around, parts of data will point to it. */ +BROTLI_INTERNAL BROTLI_BOOL BrotliInitCustomSharedEncoderDictionary( + MemoryManager* m, const uint8_t* encoded_dict, size_t size, + int quality, SharedEncoderDictionary* dict); +#endif /* BROTLI_EXPERIMENTAL */ + +BROTLI_INTERNAL void BrotliCleanupSharedEncoderDictionary( + MemoryManager* m, SharedEncoderDictionary* dict); + +BROTLI_INTERNAL ManagedDictionary* BrotliCreateManagedDictionary( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); + +BROTLI_INTERNAL void BrotliDestroyManagedDictionary( + ManagedDictionary* dictionary); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_ENCODER_DICT_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/entropy_encode.c b/packages/runtime/vendor/brotli/c/enc/entropy_encode.c new file mode 100644 index 000000000..1b4eab97f --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/entropy_encode.c @@ -0,0 +1,501 @@ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Entropy encoding (Huffman) utilities. */ + +#include "entropy_encode.h" + +#include "../common/constants.h" +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +const BROTLI_MODEL("small") size_t kBrotliShellGaps[] = {132, 57, 23, 10, 4, 1}; + +BROTLI_BOOL BrotliSetDepth( + int p0, HuffmanTree* pool, uint8_t* depth, int max_depth) { + int stack[16]; + int level = 0; + int p = p0; + BROTLI_DCHECK(max_depth <= 15); + stack[0] = -1; + while (BROTLI_TRUE) { + if (pool[p].index_left_ >= 0) { + level++; + if (level > max_depth) return BROTLI_FALSE; + stack[level] = pool[p].index_right_or_value_; + p = pool[p].index_left_; + continue; + } else { + depth[pool[p].index_right_or_value_] = (uint8_t)level; + } + while (level >= 0 && stack[level] == -1) level--; + if (level < 0) return BROTLI_TRUE; + p = stack[level]; + stack[level] = -1; + } +} + +/* Sort the root nodes, least popular first. */ +static BROTLI_INLINE BROTLI_BOOL SortHuffmanTree( + const HuffmanTree* v0, const HuffmanTree* v1) { + if (v0->total_count_ != v1->total_count_) { + return TO_BROTLI_BOOL(v0->total_count_ < v1->total_count_); + } + return TO_BROTLI_BOOL(v0->index_right_or_value_ > v1->index_right_or_value_); +} + +/* This function will create a Huffman tree. + + The catch here is that the tree cannot be arbitrarily deep. + Brotli specifies a maximum depth of 15 bits for "code trees" + and 7 bits for "code length code trees." + + count_limit is the value that is to be faked as the minimum value + and this minimum value is raised until the tree matches the + maximum length requirement. + + This algorithm is not of excellent performance for very long data blocks, + especially when population counts are longer than 2**tree_limit, but + we are not planning to use this with extremely long blocks. + + See http://en.wikipedia.org/wiki/Huffman_coding */ +void BrotliCreateHuffmanTree(const uint32_t* data, + const size_t length, + const int tree_limit, + HuffmanTree* tree, + uint8_t* depth) { + uint32_t count_limit; + HuffmanTree sentinel; + InitHuffmanTree(&sentinel, BROTLI_UINT32_MAX, -1, -1); + /* For block sizes below 64 kB, we never need to do a second iteration + of this loop. Probably all of our block sizes will be smaller than + that, so this loop is mostly of academic interest. If we actually + would need this, we would be better off with the Katajainen algorithm. */ + for (count_limit = 1; ; count_limit *= 2) { + size_t n = 0; + size_t i; + size_t j; + size_t k; + for (i = length; i != 0;) { + --i; + if (data[i]) { + const uint32_t count = BROTLI_MAX(uint32_t, data[i], count_limit); + InitHuffmanTree(&tree[n++], count, -1, (int16_t)i); + } + } + + if (n == 1) { + depth[tree[0].index_right_or_value_] = 1; /* Only one element. */ + break; + } + + SortHuffmanTreeItems(tree, n, SortHuffmanTree); + + /* The nodes are: + [0, n): the sorted leaf nodes that we start with. + [n]: we add a sentinel here. + [n + 1, 2n): new parent nodes are added here, starting from + (n+1). These are naturally in ascending order. + [2n]: we add a sentinel at the end as well. + There will be (2n+1) elements at the end. */ + tree[n] = sentinel; + tree[n + 1] = sentinel; + + i = 0; /* Points to the next leaf node. */ + j = n + 1; /* Points to the next non-leaf node. */ + for (k = n - 1; k != 0; --k) { + size_t left, right; + if (tree[i].total_count_ <= tree[j].total_count_) { + left = i; + ++i; + } else { + left = j; + ++j; + } + if (tree[i].total_count_ <= tree[j].total_count_) { + right = i; + ++i; + } else { + right = j; + ++j; + } + + { + /* The sentinel node becomes the parent node. */ + size_t j_end = 2 * n - k; + tree[j_end].total_count_ = + tree[left].total_count_ + tree[right].total_count_; + tree[j_end].index_left_ = (int16_t)left; + tree[j_end].index_right_or_value_ = (int16_t)right; + + /* Add back the last sentinel node. */ + tree[j_end + 1] = sentinel; + } + } + if (BrotliSetDepth((int)(2 * n - 1), &tree[0], depth, tree_limit)) { + /* We need to pack the Huffman tree in tree_limit bits. If this was not + successful, add fake entities to the lowest values and retry. */ + break; + } + } +} + +static void Reverse(uint8_t* v, size_t start, size_t end) { + --end; + while (start < end) { + uint8_t tmp = v[start]; + v[start] = v[end]; + v[end] = tmp; + ++start; + --end; + } +} + +static void BrotliWriteHuffmanTreeRepetitions( + const uint8_t previous_value, + const uint8_t value, + size_t repetitions, + size_t* tree_size, + uint8_t* tree, + uint8_t* extra_bits_data) { + BROTLI_DCHECK(repetitions > 0); + if (previous_value != value) { + tree[*tree_size] = value; + extra_bits_data[*tree_size] = 0; + ++(*tree_size); + --repetitions; + } + if (repetitions == 7) { + tree[*tree_size] = value; + extra_bits_data[*tree_size] = 0; + ++(*tree_size); + --repetitions; + } + if (repetitions < 3) { + size_t i; + for (i = 0; i < repetitions; ++i) { + tree[*tree_size] = value; + extra_bits_data[*tree_size] = 0; + ++(*tree_size); + } + } else { + size_t start = *tree_size; + repetitions -= 3; + while (BROTLI_TRUE) { + tree[*tree_size] = BROTLI_REPEAT_PREVIOUS_CODE_LENGTH; + extra_bits_data[*tree_size] = repetitions & 0x3; + ++(*tree_size); + repetitions >>= 2; + if (repetitions == 0) { + break; + } + --repetitions; + } + Reverse(tree, start, *tree_size); + Reverse(extra_bits_data, start, *tree_size); + } +} + +static void BrotliWriteHuffmanTreeRepetitionsZeros( + size_t repetitions, + size_t* tree_size, + uint8_t* tree, + uint8_t* extra_bits_data) { + if (repetitions == 11) { + tree[*tree_size] = 0; + extra_bits_data[*tree_size] = 0; + ++(*tree_size); + --repetitions; + } + if (repetitions < 3) { + size_t i; + for (i = 0; i < repetitions; ++i) { + tree[*tree_size] = 0; + extra_bits_data[*tree_size] = 0; + ++(*tree_size); + } + } else { + size_t start = *tree_size; + repetitions -= 3; + while (BROTLI_TRUE) { + tree[*tree_size] = BROTLI_REPEAT_ZERO_CODE_LENGTH; + extra_bits_data[*tree_size] = repetitions & 0x7; + ++(*tree_size); + repetitions >>= 3; + if (repetitions == 0) { + break; + } + --repetitions; + } + Reverse(tree, start, *tree_size); + Reverse(extra_bits_data, start, *tree_size); + } +} + +void BrotliOptimizeHuffmanCountsForRle(size_t length, uint32_t* counts, + uint8_t* good_for_rle) { + size_t nonzero_count = 0; + size_t stride; + size_t limit; + size_t sum; + const size_t streak_limit = 1240; + /* Let's make the Huffman code more compatible with RLE encoding. */ + size_t i; + for (i = 0; i < length; i++) { + if (counts[i]) { + ++nonzero_count; + } + } + if (nonzero_count < 16) { + return; + } + while (length != 0 && counts[length - 1] == 0) { + --length; + } + if (length == 0) { + return; /* All zeros. */ + } + /* Now counts[0..length - 1] does not have trailing zeros. */ + { + size_t nonzeros = 0; + uint32_t smallest_nonzero = 1 << 30; + for (i = 0; i < length; ++i) { + if (counts[i] != 0) { + ++nonzeros; + if (smallest_nonzero > counts[i]) { + smallest_nonzero = counts[i]; + } + } + } + if (nonzeros < 5) { + /* Small histogram will model it well. */ + return; + } + if (smallest_nonzero < 4) { + size_t zeros = length - nonzeros; + if (zeros < 6) { + for (i = 1; i < length - 1; ++i) { + if (counts[i - 1] != 0 && counts[i] == 0 && counts[i + 1] != 0) { + counts[i] = 1; + } + } + } + } + if (nonzeros < 28) { + return; + } + } + /* 2) Let's mark all population counts that already can be encoded + with an RLE code. */ + memset(good_for_rle, 0, length); + { + /* Let's not spoil any of the existing good RLE codes. + Mark any seq of 0's that is longer as 5 as a good_for_rle. + Mark any seq of non-0's that is longer as 7 as a good_for_rle. */ + uint32_t symbol = counts[0]; + size_t step = 0; + for (i = 0; i <= length; ++i) { + if (i == length || counts[i] != symbol) { + if ((symbol == 0 && step >= 5) || + (symbol != 0 && step >= 7)) { + size_t k; + for (k = 0; k < step; ++k) { + good_for_rle[i - k - 1] = 1; + } + } + step = 1; + if (i != length) { + symbol = counts[i]; + } + } else { + ++step; + } + } + } + /* 3) Let's replace those population counts that lead to more RLE codes. + Math here is in 24.8 fixed point representation. */ + stride = 0; + limit = 256 * (counts[0] + counts[1] + counts[2]) / 3 + 420; + sum = 0; + for (i = 0; i <= length; ++i) { + if (i == length || good_for_rle[i] || + (i != 0 && good_for_rle[i - 1]) || + (256 * counts[i] - limit + streak_limit) >= 2 * streak_limit) { + if (stride >= 4 || (stride >= 3 && sum == 0)) { + size_t k; + /* The stride must end, collapse what we have, if we have enough (4). */ + size_t count = (sum + stride / 2) / stride; + if (count == 0) { + count = 1; + } + if (sum == 0) { + /* Don't make an all zeros stride to be upgraded to ones. */ + count = 0; + } + for (k = 0; k < stride; ++k) { + /* We don't want to change value at counts[i], + that is already belonging to the next stride. Thus - 1. */ + counts[i - k - 1] = (uint32_t)count; + } + } + stride = 0; + sum = 0; + if (i < length - 2) { + /* All interesting strides have a count of at least 4, */ + /* at least when non-zeros. */ + limit = 256 * (counts[i] + counts[i + 1] + counts[i + 2]) / 3 + 420; + } else if (i < length) { + limit = 256 * counts[i]; + } else { + limit = 0; + } + } + ++stride; + if (i != length) { + sum += counts[i]; + if (stride >= 4) { + limit = (256 * sum + stride / 2) / stride; + } + if (stride == 4) { + limit += 120; + } + } + } +} + +static void DecideOverRleUse(const uint8_t* depth, const size_t length, + BROTLI_BOOL* use_rle_for_non_zero, + BROTLI_BOOL* use_rle_for_zero) { + size_t total_reps_zero = 0; + size_t total_reps_non_zero = 0; + size_t count_reps_zero = 1; + size_t count_reps_non_zero = 1; + size_t i; + for (i = 0; i < length;) { + const uint8_t value = depth[i]; + size_t reps = 1; + size_t k; + for (k = i + 1; k < length && depth[k] == value; ++k) { + ++reps; + } + if (reps >= 3 && value == 0) { + total_reps_zero += reps; + ++count_reps_zero; + } + if (reps >= 4 && value != 0) { + total_reps_non_zero += reps; + ++count_reps_non_zero; + } + i += reps; + } + *use_rle_for_non_zero = + TO_BROTLI_BOOL(total_reps_non_zero > count_reps_non_zero * 2); + *use_rle_for_zero = TO_BROTLI_BOOL(total_reps_zero > count_reps_zero * 2); +} + +void BrotliWriteHuffmanTree(const uint8_t* depth, + size_t length, + size_t* tree_size, + uint8_t* tree, + uint8_t* extra_bits_data) { + uint8_t previous_value = BROTLI_INITIAL_REPEATED_CODE_LENGTH; + size_t i; + BROTLI_BOOL use_rle_for_non_zero = BROTLI_FALSE; + BROTLI_BOOL use_rle_for_zero = BROTLI_FALSE; + + /* Throw away trailing zeros. */ + size_t new_length = length; + for (i = 0; i < length; ++i) { + if (depth[length - i - 1] == 0) { + --new_length; + } else { + break; + } + } + + /* First gather statistics on if it is a good idea to do RLE. */ + if (length > 50) { + /* Find RLE coding for longer codes. + Shorter codes seem not to benefit from RLE. */ + DecideOverRleUse(depth, new_length, + &use_rle_for_non_zero, &use_rle_for_zero); + } + + /* Actual RLE coding. */ + for (i = 0; i < new_length;) { + const uint8_t value = depth[i]; + size_t reps = 1; + if ((value != 0 && use_rle_for_non_zero) || + (value == 0 && use_rle_for_zero)) { + size_t k; + for (k = i + 1; k < new_length && depth[k] == value; ++k) { + ++reps; + } + } + if (value == 0) { + BrotliWriteHuffmanTreeRepetitionsZeros( + reps, tree_size, tree, extra_bits_data); + } else { + BrotliWriteHuffmanTreeRepetitions(previous_value, + value, reps, tree_size, + tree, extra_bits_data); + previous_value = value; + } + i += reps; + } +} + +static uint16_t BrotliReverseBits(size_t num_bits, uint16_t bits) { + static const size_t BROTLI_MODEL("small") kLut[16] = + { /* Pre-reversed 4-bit values. */ + 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E, + 0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F + }; + size_t retval = kLut[bits & 0x0F]; + size_t i; + for (i = 4; i < num_bits; i += 4) { + retval <<= 4; + bits = (uint16_t)(bits >> 4); + retval |= kLut[bits & 0x0F]; + } + retval >>= ((0 - num_bits) & 0x03); + return (uint16_t)retval; +} + +/* 0..15 are values for bits */ +#define MAX_HUFFMAN_BITS 16 + +void BrotliConvertBitDepthsToSymbols(const uint8_t* depth, + size_t len, + uint16_t* bits) { + /* In Brotli, all bit depths are [1..15] + 0 bit depth means that the symbol does not exist. */ + uint16_t bl_count[MAX_HUFFMAN_BITS] = { 0 }; + uint16_t next_code[MAX_HUFFMAN_BITS]; + size_t i; + int code = 0; + for (i = 0; i < len; ++i) { + ++bl_count[depth[i]]; + } + bl_count[0] = 0; + next_code[0] = 0; + for (i = 1; i < MAX_HUFFMAN_BITS; ++i) { + code = (code + bl_count[i - 1]) << 1; + next_code[i] = (uint16_t)code; + } + for (i = 0; i < len; ++i) { + if (depth[i]) { + bits[i] = BrotliReverseBits(depth[i], next_code[depth[i]]++); + } + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/entropy_encode.h b/packages/runtime/vendor/brotli/c/enc/entropy_encode.h new file mode 100644 index 000000000..0470c16e0 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/entropy_encode.h @@ -0,0 +1,121 @@ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Entropy encoding (Huffman) utilities. */ + +#ifndef BROTLI_ENC_ENTROPY_ENCODE_H_ +#define BROTLI_ENC_ENTROPY_ENCODE_H_ + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* A node of a Huffman tree. */ +typedef struct HuffmanTree { + uint32_t total_count_; + int16_t index_left_; + int16_t index_right_or_value_; +} HuffmanTree; + +static BROTLI_INLINE void InitHuffmanTree(HuffmanTree* self, uint32_t count, + int16_t left, int16_t right) { + self->total_count_ = count; + self->index_left_ = left; + self->index_right_or_value_ = right; +} + +/* Returns 1 is assignment of depths succeeded, otherwise 0. */ +BROTLI_INTERNAL BROTLI_BOOL BrotliSetDepth( + int p, HuffmanTree* pool, uint8_t* depth, int max_depth); + +/* This function will create a Huffman tree. + + The (data,length) contains the population counts. + The tree_limit is the maximum bit depth of the Huffman codes. + + The depth contains the tree, i.e., how many bits are used for + the symbol. + + The actual Huffman tree is constructed in the tree[] array, which has to + be at least 2 * length + 1 long. + + See http://en.wikipedia.org/wiki/Huffman_coding */ +BROTLI_INTERNAL void BrotliCreateHuffmanTree(const uint32_t* data, + const size_t length, + const int tree_limit, + HuffmanTree* tree, + uint8_t* depth); + +/* Change the population counts in a way that the consequent + Huffman tree compression, especially its RLE-part will be more + likely to compress this data more efficiently. + + length contains the size of the histogram. + counts contains the population counts. + good_for_rle is a buffer of at least length size */ +BROTLI_INTERNAL void BrotliOptimizeHuffmanCountsForRle( + size_t length, uint32_t* counts, uint8_t* good_for_rle); + +/* Write a Huffman tree from bit depths into the bit-stream representation + of a Huffman tree. The generated Huffman tree is to be compressed once + more using a Huffman tree */ +BROTLI_INTERNAL void BrotliWriteHuffmanTree(const uint8_t* depth, + size_t length, + size_t* tree_size, + uint8_t* tree, + uint8_t* extra_bits_data); + +/* Get the actual bit values for a tree of bit depths. */ +BROTLI_INTERNAL void BrotliConvertBitDepthsToSymbols(const uint8_t* depth, + size_t len, + uint16_t* bits); + +BROTLI_INTERNAL extern BROTLI_MODEL("small") const size_t kBrotliShellGaps[6]; +/* Input size optimized Shell sort. */ +typedef BROTLI_BOOL (*HuffmanTreeComparator)( + const HuffmanTree*, const HuffmanTree*); +static BROTLI_INLINE void SortHuffmanTreeItems(HuffmanTree* items, + const size_t n, HuffmanTreeComparator comparator) { + if (n < 13) { + /* Insertion sort. */ + size_t i; + for (i = 1; i < n; ++i) { + HuffmanTree tmp = items[i]; + size_t k = i; + size_t j = i - 1; + while (comparator(&tmp, &items[j])) { + items[k] = items[j]; + k = j; + if (!j--) break; + } + items[k] = tmp; + } + return; + } else { + /* Shell sort. */ + int g = n < 57 ? 2 : 0; + for (; g < 6; ++g) { + size_t gap = kBrotliShellGaps[g]; + size_t i; + for (i = gap; i < n; ++i) { + size_t j = i; + HuffmanTree tmp = items[i]; + for (; j >= gap && comparator(&tmp, &items[j - gap]); j -= gap) { + items[j] = items[j - gap]; + } + items[j] = tmp; + } + } + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_ENTROPY_ENCODE_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/entropy_encode_static.h b/packages/runtime/vendor/brotli/c/enc/entropy_encode_static.h new file mode 100644 index 000000000..5056eaa13 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/entropy_encode_static.h @@ -0,0 +1,548 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Static entropy codes used for faster meta-block encoding. */ + +#ifndef BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_ +#define BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_ + +#include "../common/constants.h" +#include "../common/platform.h" +#include "write_bits.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static const BROTLI_MODEL("small") uint8_t kCodeLengthDepth[18] = { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 0, 4, 4, +}; + +static const BROTLI_MODEL("small") +uint8_t kStaticCommandCodeDepth[BROTLI_NUM_COMMAND_SYMBOLS] = { + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +}; + +static const BROTLI_MODEL("small") +uint8_t kStaticDistanceCodeDepth[64] = { + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +/* GENERATED CODE START */ +static const BROTLI_MODEL("small") +uint32_t kCodeLengthBits[18] = { + 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 15, 31, 0, 11, 7, +}; + +static BROTLI_INLINE void StoreStaticCodeLengthCode( + size_t* storage_ix, uint8_t* storage) { + BrotliWriteBits( + 40, BROTLI_MAKE_UINT64_T(0x0000FFu, 0x55555554u), storage_ix, storage); +} + +static const BROTLI_MODEL("small") +uint64_t kZeroRepsBits[BROTLI_NUM_COMMAND_SYMBOLS] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000007, 0x00000017, 0x00000027, + 0x00000037, 0x00000047, 0x00000057, 0x00000067, 0x00000077, 0x00000770, + 0x00000b87, 0x00001387, 0x00001b87, 0x00002387, 0x00002b87, 0x00003387, + 0x00003b87, 0x00000397, 0x00000b97, 0x00001397, 0x00001b97, 0x00002397, + 0x00002b97, 0x00003397, 0x00003b97, 0x000003a7, 0x00000ba7, 0x000013a7, + 0x00001ba7, 0x000023a7, 0x00002ba7, 0x000033a7, 0x00003ba7, 0x000003b7, + 0x00000bb7, 0x000013b7, 0x00001bb7, 0x000023b7, 0x00002bb7, 0x000033b7, + 0x00003bb7, 0x000003c7, 0x00000bc7, 0x000013c7, 0x00001bc7, 0x000023c7, + 0x00002bc7, 0x000033c7, 0x00003bc7, 0x000003d7, 0x00000bd7, 0x000013d7, + 0x00001bd7, 0x000023d7, 0x00002bd7, 0x000033d7, 0x00003bd7, 0x000003e7, + 0x00000be7, 0x000013e7, 0x00001be7, 0x000023e7, 0x00002be7, 0x000033e7, + 0x00003be7, 0x000003f7, 0x00000bf7, 0x000013f7, 0x00001bf7, 0x000023f7, + 0x00002bf7, 0x000033f7, 0x00003bf7, 0x0001c387, 0x0005c387, 0x0009c387, + 0x000dc387, 0x0011c387, 0x0015c387, 0x0019c387, 0x001dc387, 0x0001cb87, + 0x0005cb87, 0x0009cb87, 0x000dcb87, 0x0011cb87, 0x0015cb87, 0x0019cb87, + 0x001dcb87, 0x0001d387, 0x0005d387, 0x0009d387, 0x000dd387, 0x0011d387, + 0x0015d387, 0x0019d387, 0x001dd387, 0x0001db87, 0x0005db87, 0x0009db87, + 0x000ddb87, 0x0011db87, 0x0015db87, 0x0019db87, 0x001ddb87, 0x0001e387, + 0x0005e387, 0x0009e387, 0x000de387, 0x0011e387, 0x0015e387, 0x0019e387, + 0x001de387, 0x0001eb87, 0x0005eb87, 0x0009eb87, 0x000deb87, 0x0011eb87, + 0x0015eb87, 0x0019eb87, 0x001deb87, 0x0001f387, 0x0005f387, 0x0009f387, + 0x000df387, 0x0011f387, 0x0015f387, 0x0019f387, 0x001df387, 0x0001fb87, + 0x0005fb87, 0x0009fb87, 0x000dfb87, 0x0011fb87, 0x0015fb87, 0x0019fb87, + 0x001dfb87, 0x0001c397, 0x0005c397, 0x0009c397, 0x000dc397, 0x0011c397, + 0x0015c397, 0x0019c397, 0x001dc397, 0x0001cb97, 0x0005cb97, 0x0009cb97, + 0x000dcb97, 0x0011cb97, 0x0015cb97, 0x0019cb97, 0x001dcb97, 0x0001d397, + 0x0005d397, 0x0009d397, 0x000dd397, 0x0011d397, 0x0015d397, 0x0019d397, + 0x001dd397, 0x0001db97, 0x0005db97, 0x0009db97, 0x000ddb97, 0x0011db97, + 0x0015db97, 0x0019db97, 0x001ddb97, 0x0001e397, 0x0005e397, 0x0009e397, + 0x000de397, 0x0011e397, 0x0015e397, 0x0019e397, 0x001de397, 0x0001eb97, + 0x0005eb97, 0x0009eb97, 0x000deb97, 0x0011eb97, 0x0015eb97, 0x0019eb97, + 0x001deb97, 0x0001f397, 0x0005f397, 0x0009f397, 0x000df397, 0x0011f397, + 0x0015f397, 0x0019f397, 0x001df397, 0x0001fb97, 0x0005fb97, 0x0009fb97, + 0x000dfb97, 0x0011fb97, 0x0015fb97, 0x0019fb97, 0x001dfb97, 0x0001c3a7, + 0x0005c3a7, 0x0009c3a7, 0x000dc3a7, 0x0011c3a7, 0x0015c3a7, 0x0019c3a7, + 0x001dc3a7, 0x0001cba7, 0x0005cba7, 0x0009cba7, 0x000dcba7, 0x0011cba7, + 0x0015cba7, 0x0019cba7, 0x001dcba7, 0x0001d3a7, 0x0005d3a7, 0x0009d3a7, + 0x000dd3a7, 0x0011d3a7, 0x0015d3a7, 0x0019d3a7, 0x001dd3a7, 0x0001dba7, + 0x0005dba7, 0x0009dba7, 0x000ddba7, 0x0011dba7, 0x0015dba7, 0x0019dba7, + 0x001ddba7, 0x0001e3a7, 0x0005e3a7, 0x0009e3a7, 0x000de3a7, 0x0011e3a7, + 0x0015e3a7, 0x0019e3a7, 0x001de3a7, 0x0001eba7, 0x0005eba7, 0x0009eba7, + 0x000deba7, 0x0011eba7, 0x0015eba7, 0x0019eba7, 0x001deba7, 0x0001f3a7, + 0x0005f3a7, 0x0009f3a7, 0x000df3a7, 0x0011f3a7, 0x0015f3a7, 0x0019f3a7, + 0x001df3a7, 0x0001fba7, 0x0005fba7, 0x0009fba7, 0x000dfba7, 0x0011fba7, + 0x0015fba7, 0x0019fba7, 0x001dfba7, 0x0001c3b7, 0x0005c3b7, 0x0009c3b7, + 0x000dc3b7, 0x0011c3b7, 0x0015c3b7, 0x0019c3b7, 0x001dc3b7, 0x0001cbb7, + 0x0005cbb7, 0x0009cbb7, 0x000dcbb7, 0x0011cbb7, 0x0015cbb7, 0x0019cbb7, + 0x001dcbb7, 0x0001d3b7, 0x0005d3b7, 0x0009d3b7, 0x000dd3b7, 0x0011d3b7, + 0x0015d3b7, 0x0019d3b7, 0x001dd3b7, 0x0001dbb7, 0x0005dbb7, 0x0009dbb7, + 0x000ddbb7, 0x0011dbb7, 0x0015dbb7, 0x0019dbb7, 0x001ddbb7, 0x0001e3b7, + 0x0005e3b7, 0x0009e3b7, 0x000de3b7, 0x0011e3b7, 0x0015e3b7, 0x0019e3b7, + 0x001de3b7, 0x0001ebb7, 0x0005ebb7, 0x0009ebb7, 0x000debb7, 0x0011ebb7, + 0x0015ebb7, 0x0019ebb7, 0x001debb7, 0x0001f3b7, 0x0005f3b7, 0x0009f3b7, + 0x000df3b7, 0x0011f3b7, 0x0015f3b7, 0x0019f3b7, 0x001df3b7, 0x0001fbb7, + 0x0005fbb7, 0x0009fbb7, 0x000dfbb7, 0x0011fbb7, 0x0015fbb7, 0x0019fbb7, + 0x001dfbb7, 0x0001c3c7, 0x0005c3c7, 0x0009c3c7, 0x000dc3c7, 0x0011c3c7, + 0x0015c3c7, 0x0019c3c7, 0x001dc3c7, 0x0001cbc7, 0x0005cbc7, 0x0009cbc7, + 0x000dcbc7, 0x0011cbc7, 0x0015cbc7, 0x0019cbc7, 0x001dcbc7, 0x0001d3c7, + 0x0005d3c7, 0x0009d3c7, 0x000dd3c7, 0x0011d3c7, 0x0015d3c7, 0x0019d3c7, + 0x001dd3c7, 0x0001dbc7, 0x0005dbc7, 0x0009dbc7, 0x000ddbc7, 0x0011dbc7, + 0x0015dbc7, 0x0019dbc7, 0x001ddbc7, 0x0001e3c7, 0x0005e3c7, 0x0009e3c7, + 0x000de3c7, 0x0011e3c7, 0x0015e3c7, 0x0019e3c7, 0x001de3c7, 0x0001ebc7, + 0x0005ebc7, 0x0009ebc7, 0x000debc7, 0x0011ebc7, 0x0015ebc7, 0x0019ebc7, + 0x001debc7, 0x0001f3c7, 0x0005f3c7, 0x0009f3c7, 0x000df3c7, 0x0011f3c7, + 0x0015f3c7, 0x0019f3c7, 0x001df3c7, 0x0001fbc7, 0x0005fbc7, 0x0009fbc7, + 0x000dfbc7, 0x0011fbc7, 0x0015fbc7, 0x0019fbc7, 0x001dfbc7, 0x0001c3d7, + 0x0005c3d7, 0x0009c3d7, 0x000dc3d7, 0x0011c3d7, 0x0015c3d7, 0x0019c3d7, + 0x001dc3d7, 0x0001cbd7, 0x0005cbd7, 0x0009cbd7, 0x000dcbd7, 0x0011cbd7, + 0x0015cbd7, 0x0019cbd7, 0x001dcbd7, 0x0001d3d7, 0x0005d3d7, 0x0009d3d7, + 0x000dd3d7, 0x0011d3d7, 0x0015d3d7, 0x0019d3d7, 0x001dd3d7, 0x0001dbd7, + 0x0005dbd7, 0x0009dbd7, 0x000ddbd7, 0x0011dbd7, 0x0015dbd7, 0x0019dbd7, + 0x001ddbd7, 0x0001e3d7, 0x0005e3d7, 0x0009e3d7, 0x000de3d7, 0x0011e3d7, + 0x0015e3d7, 0x0019e3d7, 0x001de3d7, 0x0001ebd7, 0x0005ebd7, 0x0009ebd7, + 0x000debd7, 0x0011ebd7, 0x0015ebd7, 0x0019ebd7, 0x001debd7, 0x0001f3d7, + 0x0005f3d7, 0x0009f3d7, 0x000df3d7, 0x0011f3d7, 0x0015f3d7, 0x0019f3d7, + 0x001df3d7, 0x0001fbd7, 0x0005fbd7, 0x0009fbd7, 0x000dfbd7, 0x0011fbd7, + 0x0015fbd7, 0x0019fbd7, 0x001dfbd7, 0x0001c3e7, 0x0005c3e7, 0x0009c3e7, + 0x000dc3e7, 0x0011c3e7, 0x0015c3e7, 0x0019c3e7, 0x001dc3e7, 0x0001cbe7, + 0x0005cbe7, 0x0009cbe7, 0x000dcbe7, 0x0011cbe7, 0x0015cbe7, 0x0019cbe7, + 0x001dcbe7, 0x0001d3e7, 0x0005d3e7, 0x0009d3e7, 0x000dd3e7, 0x0011d3e7, + 0x0015d3e7, 0x0019d3e7, 0x001dd3e7, 0x0001dbe7, 0x0005dbe7, 0x0009dbe7, + 0x000ddbe7, 0x0011dbe7, 0x0015dbe7, 0x0019dbe7, 0x001ddbe7, 0x0001e3e7, + 0x0005e3e7, 0x0009e3e7, 0x000de3e7, 0x0011e3e7, 0x0015e3e7, 0x0019e3e7, + 0x001de3e7, 0x0001ebe7, 0x0005ebe7, 0x0009ebe7, 0x000debe7, 0x0011ebe7, + 0x0015ebe7, 0x0019ebe7, 0x001debe7, 0x0001f3e7, 0x0005f3e7, 0x0009f3e7, + 0x000df3e7, 0x0011f3e7, 0x0015f3e7, 0x0019f3e7, 0x001df3e7, 0x0001fbe7, + 0x0005fbe7, 0x0009fbe7, 0x000dfbe7, 0x0011fbe7, 0x0015fbe7, 0x0019fbe7, + 0x001dfbe7, 0x0001c3f7, 0x0005c3f7, 0x0009c3f7, 0x000dc3f7, 0x0011c3f7, + 0x0015c3f7, 0x0019c3f7, 0x001dc3f7, 0x0001cbf7, 0x0005cbf7, 0x0009cbf7, + 0x000dcbf7, 0x0011cbf7, 0x0015cbf7, 0x0019cbf7, 0x001dcbf7, 0x0001d3f7, + 0x0005d3f7, 0x0009d3f7, 0x000dd3f7, 0x0011d3f7, 0x0015d3f7, 0x0019d3f7, + 0x001dd3f7, 0x0001dbf7, 0x0005dbf7, 0x0009dbf7, 0x000ddbf7, 0x0011dbf7, + 0x0015dbf7, 0x0019dbf7, 0x001ddbf7, 0x0001e3f7, 0x0005e3f7, 0x0009e3f7, + 0x000de3f7, 0x0011e3f7, 0x0015e3f7, 0x0019e3f7, 0x001de3f7, 0x0001ebf7, + 0x0005ebf7, 0x0009ebf7, 0x000debf7, 0x0011ebf7, 0x0015ebf7, 0x0019ebf7, + 0x001debf7, 0x0001f3f7, 0x0005f3f7, 0x0009f3f7, 0x000df3f7, 0x0011f3f7, + 0x0015f3f7, 0x0019f3f7, 0x001df3f7, 0x0001fbf7, 0x0005fbf7, 0x0009fbf7, + 0x000dfbf7, 0x0011fbf7, 0x0015fbf7, 0x0019fbf7, 0x001dfbf7, 0x00e1c387, + 0x02e1c387, 0x04e1c387, 0x06e1c387, 0x08e1c387, 0x0ae1c387, 0x0ce1c387, + 0x0ee1c387, 0x00e5c387, 0x02e5c387, 0x04e5c387, 0x06e5c387, 0x08e5c387, + 0x0ae5c387, 0x0ce5c387, 0x0ee5c387, 0x00e9c387, 0x02e9c387, 0x04e9c387, + 0x06e9c387, 0x08e9c387, 0x0ae9c387, 0x0ce9c387, 0x0ee9c387, 0x00edc387, + 0x02edc387, 0x04edc387, 0x06edc387, 0x08edc387, 0x0aedc387, 0x0cedc387, + 0x0eedc387, 0x00f1c387, 0x02f1c387, 0x04f1c387, 0x06f1c387, 0x08f1c387, + 0x0af1c387, 0x0cf1c387, 0x0ef1c387, 0x00f5c387, 0x02f5c387, 0x04f5c387, + 0x06f5c387, 0x08f5c387, 0x0af5c387, 0x0cf5c387, 0x0ef5c387, 0x00f9c387, + 0x02f9c387, 0x04f9c387, 0x06f9c387, 0x08f9c387, 0x0af9c387, 0x0cf9c387, + 0x0ef9c387, 0x00fdc387, 0x02fdc387, 0x04fdc387, 0x06fdc387, 0x08fdc387, + 0x0afdc387, 0x0cfdc387, 0x0efdc387, 0x00e1cb87, 0x02e1cb87, 0x04e1cb87, + 0x06e1cb87, 0x08e1cb87, 0x0ae1cb87, 0x0ce1cb87, 0x0ee1cb87, 0x00e5cb87, + 0x02e5cb87, 0x04e5cb87, 0x06e5cb87, 0x08e5cb87, 0x0ae5cb87, 0x0ce5cb87, + 0x0ee5cb87, 0x00e9cb87, 0x02e9cb87, 0x04e9cb87, 0x06e9cb87, 0x08e9cb87, + 0x0ae9cb87, 0x0ce9cb87, 0x0ee9cb87, 0x00edcb87, 0x02edcb87, 0x04edcb87, + 0x06edcb87, 0x08edcb87, 0x0aedcb87, 0x0cedcb87, 0x0eedcb87, 0x00f1cb87, + 0x02f1cb87, 0x04f1cb87, 0x06f1cb87, 0x08f1cb87, 0x0af1cb87, 0x0cf1cb87, + 0x0ef1cb87, 0x00f5cb87, 0x02f5cb87, 0x04f5cb87, 0x06f5cb87, 0x08f5cb87, + 0x0af5cb87, 0x0cf5cb87, 0x0ef5cb87, 0x00f9cb87, 0x02f9cb87, 0x04f9cb87, + 0x06f9cb87, 0x08f9cb87, +}; + +static const BROTLI_MODEL("small") +uint32_t kZeroRepsDepth[BROTLI_NUM_COMMAND_SYMBOLS] = { + 0, 4, 8, 7, 7, 7, 7, 7, 7, 7, 7, 11, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +}; + +static const BROTLI_MODEL("small") +uint64_t kNonZeroRepsBits[BROTLI_NUM_COMMAND_SYMBOLS] = { + 0x0000000b, 0x0000001b, 0x0000002b, 0x0000003b, 0x000002cb, 0x000006cb, + 0x00000acb, 0x00000ecb, 0x000002db, 0x000006db, 0x00000adb, 0x00000edb, + 0x000002eb, 0x000006eb, 0x00000aeb, 0x00000eeb, 0x000002fb, 0x000006fb, + 0x00000afb, 0x00000efb, 0x0000b2cb, 0x0001b2cb, 0x0002b2cb, 0x0003b2cb, + 0x0000b6cb, 0x0001b6cb, 0x0002b6cb, 0x0003b6cb, 0x0000bacb, 0x0001bacb, + 0x0002bacb, 0x0003bacb, 0x0000becb, 0x0001becb, 0x0002becb, 0x0003becb, + 0x0000b2db, 0x0001b2db, 0x0002b2db, 0x0003b2db, 0x0000b6db, 0x0001b6db, + 0x0002b6db, 0x0003b6db, 0x0000badb, 0x0001badb, 0x0002badb, 0x0003badb, + 0x0000bedb, 0x0001bedb, 0x0002bedb, 0x0003bedb, 0x0000b2eb, 0x0001b2eb, + 0x0002b2eb, 0x0003b2eb, 0x0000b6eb, 0x0001b6eb, 0x0002b6eb, 0x0003b6eb, + 0x0000baeb, 0x0001baeb, 0x0002baeb, 0x0003baeb, 0x0000beeb, 0x0001beeb, + 0x0002beeb, 0x0003beeb, 0x0000b2fb, 0x0001b2fb, 0x0002b2fb, 0x0003b2fb, + 0x0000b6fb, 0x0001b6fb, 0x0002b6fb, 0x0003b6fb, 0x0000bafb, 0x0001bafb, + 0x0002bafb, 0x0003bafb, 0x0000befb, 0x0001befb, 0x0002befb, 0x0003befb, + 0x002cb2cb, 0x006cb2cb, 0x00acb2cb, 0x00ecb2cb, 0x002db2cb, 0x006db2cb, + 0x00adb2cb, 0x00edb2cb, 0x002eb2cb, 0x006eb2cb, 0x00aeb2cb, 0x00eeb2cb, + 0x002fb2cb, 0x006fb2cb, 0x00afb2cb, 0x00efb2cb, 0x002cb6cb, 0x006cb6cb, + 0x00acb6cb, 0x00ecb6cb, 0x002db6cb, 0x006db6cb, 0x00adb6cb, 0x00edb6cb, + 0x002eb6cb, 0x006eb6cb, 0x00aeb6cb, 0x00eeb6cb, 0x002fb6cb, 0x006fb6cb, + 0x00afb6cb, 0x00efb6cb, 0x002cbacb, 0x006cbacb, 0x00acbacb, 0x00ecbacb, + 0x002dbacb, 0x006dbacb, 0x00adbacb, 0x00edbacb, 0x002ebacb, 0x006ebacb, + 0x00aebacb, 0x00eebacb, 0x002fbacb, 0x006fbacb, 0x00afbacb, 0x00efbacb, + 0x002cbecb, 0x006cbecb, 0x00acbecb, 0x00ecbecb, 0x002dbecb, 0x006dbecb, + 0x00adbecb, 0x00edbecb, 0x002ebecb, 0x006ebecb, 0x00aebecb, 0x00eebecb, + 0x002fbecb, 0x006fbecb, 0x00afbecb, 0x00efbecb, 0x002cb2db, 0x006cb2db, + 0x00acb2db, 0x00ecb2db, 0x002db2db, 0x006db2db, 0x00adb2db, 0x00edb2db, + 0x002eb2db, 0x006eb2db, 0x00aeb2db, 0x00eeb2db, 0x002fb2db, 0x006fb2db, + 0x00afb2db, 0x00efb2db, 0x002cb6db, 0x006cb6db, 0x00acb6db, 0x00ecb6db, + 0x002db6db, 0x006db6db, 0x00adb6db, 0x00edb6db, 0x002eb6db, 0x006eb6db, + 0x00aeb6db, 0x00eeb6db, 0x002fb6db, 0x006fb6db, 0x00afb6db, 0x00efb6db, + 0x002cbadb, 0x006cbadb, 0x00acbadb, 0x00ecbadb, 0x002dbadb, 0x006dbadb, + 0x00adbadb, 0x00edbadb, 0x002ebadb, 0x006ebadb, 0x00aebadb, 0x00eebadb, + 0x002fbadb, 0x006fbadb, 0x00afbadb, 0x00efbadb, 0x002cbedb, 0x006cbedb, + 0x00acbedb, 0x00ecbedb, 0x002dbedb, 0x006dbedb, 0x00adbedb, 0x00edbedb, + 0x002ebedb, 0x006ebedb, 0x00aebedb, 0x00eebedb, 0x002fbedb, 0x006fbedb, + 0x00afbedb, 0x00efbedb, 0x002cb2eb, 0x006cb2eb, 0x00acb2eb, 0x00ecb2eb, + 0x002db2eb, 0x006db2eb, 0x00adb2eb, 0x00edb2eb, 0x002eb2eb, 0x006eb2eb, + 0x00aeb2eb, 0x00eeb2eb, 0x002fb2eb, 0x006fb2eb, 0x00afb2eb, 0x00efb2eb, + 0x002cb6eb, 0x006cb6eb, 0x00acb6eb, 0x00ecb6eb, 0x002db6eb, 0x006db6eb, + 0x00adb6eb, 0x00edb6eb, 0x002eb6eb, 0x006eb6eb, 0x00aeb6eb, 0x00eeb6eb, + 0x002fb6eb, 0x006fb6eb, 0x00afb6eb, 0x00efb6eb, 0x002cbaeb, 0x006cbaeb, + 0x00acbaeb, 0x00ecbaeb, 0x002dbaeb, 0x006dbaeb, 0x00adbaeb, 0x00edbaeb, + 0x002ebaeb, 0x006ebaeb, 0x00aebaeb, 0x00eebaeb, 0x002fbaeb, 0x006fbaeb, + 0x00afbaeb, 0x00efbaeb, 0x002cbeeb, 0x006cbeeb, 0x00acbeeb, 0x00ecbeeb, + 0x002dbeeb, 0x006dbeeb, 0x00adbeeb, 0x00edbeeb, 0x002ebeeb, 0x006ebeeb, + 0x00aebeeb, 0x00eebeeb, 0x002fbeeb, 0x006fbeeb, 0x00afbeeb, 0x00efbeeb, + 0x002cb2fb, 0x006cb2fb, 0x00acb2fb, 0x00ecb2fb, 0x002db2fb, 0x006db2fb, + 0x00adb2fb, 0x00edb2fb, 0x002eb2fb, 0x006eb2fb, 0x00aeb2fb, 0x00eeb2fb, + 0x002fb2fb, 0x006fb2fb, 0x00afb2fb, 0x00efb2fb, 0x002cb6fb, 0x006cb6fb, + 0x00acb6fb, 0x00ecb6fb, 0x002db6fb, 0x006db6fb, 0x00adb6fb, 0x00edb6fb, + 0x002eb6fb, 0x006eb6fb, 0x00aeb6fb, 0x00eeb6fb, 0x002fb6fb, 0x006fb6fb, + 0x00afb6fb, 0x00efb6fb, 0x002cbafb, 0x006cbafb, 0x00acbafb, 0x00ecbafb, + 0x002dbafb, 0x006dbafb, 0x00adbafb, 0x00edbafb, 0x002ebafb, 0x006ebafb, + 0x00aebafb, 0x00eebafb, 0x002fbafb, 0x006fbafb, 0x00afbafb, 0x00efbafb, + 0x002cbefb, 0x006cbefb, 0x00acbefb, 0x00ecbefb, 0x002dbefb, 0x006dbefb, + 0x00adbefb, 0x00edbefb, 0x002ebefb, 0x006ebefb, 0x00aebefb, 0x00eebefb, + 0x002fbefb, 0x006fbefb, 0x00afbefb, 0x00efbefb, 0x0b2cb2cb, 0x1b2cb2cb, + 0x2b2cb2cb, 0x3b2cb2cb, 0x0b6cb2cb, 0x1b6cb2cb, 0x2b6cb2cb, 0x3b6cb2cb, + 0x0bacb2cb, 0x1bacb2cb, 0x2bacb2cb, 0x3bacb2cb, 0x0becb2cb, 0x1becb2cb, + 0x2becb2cb, 0x3becb2cb, 0x0b2db2cb, 0x1b2db2cb, 0x2b2db2cb, 0x3b2db2cb, + 0x0b6db2cb, 0x1b6db2cb, 0x2b6db2cb, 0x3b6db2cb, 0x0badb2cb, 0x1badb2cb, + 0x2badb2cb, 0x3badb2cb, 0x0bedb2cb, 0x1bedb2cb, 0x2bedb2cb, 0x3bedb2cb, + 0x0b2eb2cb, 0x1b2eb2cb, 0x2b2eb2cb, 0x3b2eb2cb, 0x0b6eb2cb, 0x1b6eb2cb, + 0x2b6eb2cb, 0x3b6eb2cb, 0x0baeb2cb, 0x1baeb2cb, 0x2baeb2cb, 0x3baeb2cb, + 0x0beeb2cb, 0x1beeb2cb, 0x2beeb2cb, 0x3beeb2cb, 0x0b2fb2cb, 0x1b2fb2cb, + 0x2b2fb2cb, 0x3b2fb2cb, 0x0b6fb2cb, 0x1b6fb2cb, 0x2b6fb2cb, 0x3b6fb2cb, + 0x0bafb2cb, 0x1bafb2cb, 0x2bafb2cb, 0x3bafb2cb, 0x0befb2cb, 0x1befb2cb, + 0x2befb2cb, 0x3befb2cb, 0x0b2cb6cb, 0x1b2cb6cb, 0x2b2cb6cb, 0x3b2cb6cb, + 0x0b6cb6cb, 0x1b6cb6cb, 0x2b6cb6cb, 0x3b6cb6cb, 0x0bacb6cb, 0x1bacb6cb, + 0x2bacb6cb, 0x3bacb6cb, 0x0becb6cb, 0x1becb6cb, 0x2becb6cb, 0x3becb6cb, + 0x0b2db6cb, 0x1b2db6cb, 0x2b2db6cb, 0x3b2db6cb, 0x0b6db6cb, 0x1b6db6cb, + 0x2b6db6cb, 0x3b6db6cb, 0x0badb6cb, 0x1badb6cb, 0x2badb6cb, 0x3badb6cb, + 0x0bedb6cb, 0x1bedb6cb, 0x2bedb6cb, 0x3bedb6cb, 0x0b2eb6cb, 0x1b2eb6cb, + 0x2b2eb6cb, 0x3b2eb6cb, 0x0b6eb6cb, 0x1b6eb6cb, 0x2b6eb6cb, 0x3b6eb6cb, + 0x0baeb6cb, 0x1baeb6cb, 0x2baeb6cb, 0x3baeb6cb, 0x0beeb6cb, 0x1beeb6cb, + 0x2beeb6cb, 0x3beeb6cb, 0x0b2fb6cb, 0x1b2fb6cb, 0x2b2fb6cb, 0x3b2fb6cb, + 0x0b6fb6cb, 0x1b6fb6cb, 0x2b6fb6cb, 0x3b6fb6cb, 0x0bafb6cb, 0x1bafb6cb, + 0x2bafb6cb, 0x3bafb6cb, 0x0befb6cb, 0x1befb6cb, 0x2befb6cb, 0x3befb6cb, + 0x0b2cbacb, 0x1b2cbacb, 0x2b2cbacb, 0x3b2cbacb, 0x0b6cbacb, 0x1b6cbacb, + 0x2b6cbacb, 0x3b6cbacb, 0x0bacbacb, 0x1bacbacb, 0x2bacbacb, 0x3bacbacb, + 0x0becbacb, 0x1becbacb, 0x2becbacb, 0x3becbacb, 0x0b2dbacb, 0x1b2dbacb, + 0x2b2dbacb, 0x3b2dbacb, 0x0b6dbacb, 0x1b6dbacb, 0x2b6dbacb, 0x3b6dbacb, + 0x0badbacb, 0x1badbacb, 0x2badbacb, 0x3badbacb, 0x0bedbacb, 0x1bedbacb, + 0x2bedbacb, 0x3bedbacb, 0x0b2ebacb, 0x1b2ebacb, 0x2b2ebacb, 0x3b2ebacb, + 0x0b6ebacb, 0x1b6ebacb, 0x2b6ebacb, 0x3b6ebacb, 0x0baebacb, 0x1baebacb, + 0x2baebacb, 0x3baebacb, 0x0beebacb, 0x1beebacb, 0x2beebacb, 0x3beebacb, + 0x0b2fbacb, 0x1b2fbacb, 0x2b2fbacb, 0x3b2fbacb, 0x0b6fbacb, 0x1b6fbacb, + 0x2b6fbacb, 0x3b6fbacb, 0x0bafbacb, 0x1bafbacb, 0x2bafbacb, 0x3bafbacb, + 0x0befbacb, 0x1befbacb, 0x2befbacb, 0x3befbacb, 0x0b2cbecb, 0x1b2cbecb, + 0x2b2cbecb, 0x3b2cbecb, 0x0b6cbecb, 0x1b6cbecb, 0x2b6cbecb, 0x3b6cbecb, + 0x0bacbecb, 0x1bacbecb, 0x2bacbecb, 0x3bacbecb, 0x0becbecb, 0x1becbecb, + 0x2becbecb, 0x3becbecb, 0x0b2dbecb, 0x1b2dbecb, 0x2b2dbecb, 0x3b2dbecb, + 0x0b6dbecb, 0x1b6dbecb, 0x2b6dbecb, 0x3b6dbecb, 0x0badbecb, 0x1badbecb, + 0x2badbecb, 0x3badbecb, 0x0bedbecb, 0x1bedbecb, 0x2bedbecb, 0x3bedbecb, + 0x0b2ebecb, 0x1b2ebecb, 0x2b2ebecb, 0x3b2ebecb, 0x0b6ebecb, 0x1b6ebecb, + 0x2b6ebecb, 0x3b6ebecb, 0x0baebecb, 0x1baebecb, 0x2baebecb, 0x3baebecb, + 0x0beebecb, 0x1beebecb, 0x2beebecb, 0x3beebecb, 0x0b2fbecb, 0x1b2fbecb, + 0x2b2fbecb, 0x3b2fbecb, 0x0b6fbecb, 0x1b6fbecb, 0x2b6fbecb, 0x3b6fbecb, + 0x0bafbecb, 0x1bafbecb, 0x2bafbecb, 0x3bafbecb, 0x0befbecb, 0x1befbecb, + 0x2befbecb, 0x3befbecb, 0x0b2cb2db, 0x1b2cb2db, 0x2b2cb2db, 0x3b2cb2db, + 0x0b6cb2db, 0x1b6cb2db, 0x2b6cb2db, 0x3b6cb2db, 0x0bacb2db, 0x1bacb2db, + 0x2bacb2db, 0x3bacb2db, 0x0becb2db, 0x1becb2db, 0x2becb2db, 0x3becb2db, + 0x0b2db2db, 0x1b2db2db, 0x2b2db2db, 0x3b2db2db, 0x0b6db2db, 0x1b6db2db, + 0x2b6db2db, 0x3b6db2db, 0x0badb2db, 0x1badb2db, 0x2badb2db, 0x3badb2db, + 0x0bedb2db, 0x1bedb2db, 0x2bedb2db, 0x3bedb2db, 0x0b2eb2db, 0x1b2eb2db, + 0x2b2eb2db, 0x3b2eb2db, 0x0b6eb2db, 0x1b6eb2db, 0x2b6eb2db, 0x3b6eb2db, + 0x0baeb2db, 0x1baeb2db, 0x2baeb2db, 0x3baeb2db, 0x0beeb2db, 0x1beeb2db, + 0x2beeb2db, 0x3beeb2db, 0x0b2fb2db, 0x1b2fb2db, 0x2b2fb2db, 0x3b2fb2db, + 0x0b6fb2db, 0x1b6fb2db, 0x2b6fb2db, 0x3b6fb2db, 0x0bafb2db, 0x1bafb2db, + 0x2bafb2db, 0x3bafb2db, 0x0befb2db, 0x1befb2db, 0x2befb2db, 0x3befb2db, + 0x0b2cb6db, 0x1b2cb6db, 0x2b2cb6db, 0x3b2cb6db, 0x0b6cb6db, 0x1b6cb6db, + 0x2b6cb6db, 0x3b6cb6db, 0x0bacb6db, 0x1bacb6db, 0x2bacb6db, 0x3bacb6db, + 0x0becb6db, 0x1becb6db, 0x2becb6db, 0x3becb6db, 0x0b2db6db, 0x1b2db6db, + 0x2b2db6db, 0x3b2db6db, 0x0b6db6db, 0x1b6db6db, 0x2b6db6db, 0x3b6db6db, + 0x0badb6db, 0x1badb6db, 0x2badb6db, 0x3badb6db, 0x0bedb6db, 0x1bedb6db, + 0x2bedb6db, 0x3bedb6db, 0x0b2eb6db, 0x1b2eb6db, 0x2b2eb6db, 0x3b2eb6db, + 0x0b6eb6db, 0x1b6eb6db, 0x2b6eb6db, 0x3b6eb6db, 0x0baeb6db, 0x1baeb6db, + 0x2baeb6db, 0x3baeb6db, +}; + +static const BROTLI_MODEL("small") +uint32_t kNonZeroRepsDepth[BROTLI_NUM_COMMAND_SYMBOLS] = { + 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, +}; + +static const BROTLI_MODEL("small") +uint16_t kStaticCommandCodeBits[BROTLI_NUM_COMMAND_SYMBOLS] = { + 0, 256, 128, 384, 64, 320, 192, 448, + 32, 288, 160, 416, 96, 352, 224, 480, + 16, 272, 144, 400, 80, 336, 208, 464, + 48, 304, 176, 432, 112, 368, 240, 496, + 8, 264, 136, 392, 72, 328, 200, 456, + 40, 296, 168, 424, 104, 360, 232, 488, + 24, 280, 152, 408, 88, 344, 216, 472, + 56, 312, 184, 440, 120, 376, 248, 504, + 4, 260, 132, 388, 68, 324, 196, 452, + 36, 292, 164, 420, 100, 356, 228, 484, + 20, 276, 148, 404, 84, 340, 212, 468, + 52, 308, 180, 436, 116, 372, 244, 500, + 12, 268, 140, 396, 76, 332, 204, 460, + 44, 300, 172, 428, 108, 364, 236, 492, + 28, 284, 156, 412, 92, 348, 220, 476, + 60, 316, 188, 444, 124, 380, 252, 508, + 2, 258, 130, 386, 66, 322, 194, 450, + 34, 290, 162, 418, 98, 354, 226, 482, + 18, 274, 146, 402, 82, 338, 210, 466, + 50, 306, 178, 434, 114, 370, 242, 498, + 10, 266, 138, 394, 74, 330, 202, 458, + 42, 298, 170, 426, 106, 362, 234, 490, + 26, 282, 154, 410, 90, 346, 218, 474, + 58, 314, 186, 442, 122, 378, 250, 506, + 6, 262, 134, 390, 70, 326, 198, 454, + 38, 294, 166, 422, 102, 358, 230, 486, + 22, 278, 150, 406, 86, 342, 214, 470, + 54, 310, 182, 438, 118, 374, 246, 502, + 14, 270, 142, 398, 78, 334, 206, 462, + 46, 302, 174, 430, 110, 366, 238, 494, + 30, 286, 158, 414, 94, 350, 222, 478, + 62, 318, 190, 446, 126, 382, 254, 510, + 1, 257, 129, 385, 65, 321, 193, 449, + 33, 289, 161, 417, 97, 353, 225, 481, + 17, 273, 145, 401, 81, 337, 209, 465, + 49, 305, 177, 433, 113, 369, 241, 497, + 9, 265, 137, 393, 73, 329, 201, 457, + 41, 297, 169, 425, 105, 361, 233, 489, + 25, 281, 153, 409, 89, 345, 217, 473, + 57, 313, 185, 441, 121, 377, 249, 505, + 5, 261, 133, 389, 69, 325, 197, 453, + 37, 293, 165, 421, 101, 357, 229, 485, + 21, 277, 149, 405, 85, 341, 213, 469, + 53, 309, 181, 437, 117, 373, 245, 501, + 13, 269, 141, 397, 77, 333, 205, 461, + 45, 301, 173, 429, 109, 365, 237, 493, + 29, 285, 157, 413, 93, 349, 221, 477, + 61, 317, 189, 445, 125, 381, 253, 509, + 3, 259, 131, 387, 67, 323, 195, 451, + 35, 291, 163, 419, 99, 355, 227, 483, + 19, 275, 147, 403, 83, 339, 211, 467, + 51, 307, 179, 435, 115, 371, 243, 499, + 11, 267, 139, 395, 75, 331, 203, 459, + 43, 299, 171, 427, 107, 363, 235, 491, + 27, 283, 155, 411, 91, 347, 219, 475, + 59, 315, 187, 443, 123, 379, 251, 507, + 7, 1031, 519, 1543, 263, 1287, 775, 1799, + 135, 1159, 647, 1671, 391, 1415, 903, 1927, + 71, 1095, 583, 1607, 327, 1351, 839, 1863, + 199, 1223, 711, 1735, 455, 1479, 967, 1991, + 39, 1063, 551, 1575, 295, 1319, 807, 1831, + 167, 1191, 679, 1703, 423, 1447, 935, 1959, + 103, 1127, 615, 1639, 359, 1383, 871, 1895, + 231, 1255, 743, 1767, 487, 1511, 999, 2023, + 23, 1047, 535, 1559, 279, 1303, 791, 1815, + 151, 1175, 663, 1687, 407, 1431, 919, 1943, + 87, 1111, 599, 1623, 343, 1367, 855, 1879, + 215, 1239, 727, 1751, 471, 1495, 983, 2007, + 55, 1079, 567, 1591, 311, 1335, 823, 1847, + 183, 1207, 695, 1719, 439, 1463, 951, 1975, + 119, 1143, 631, 1655, 375, 1399, 887, 1911, + 247, 1271, 759, 1783, 503, 1527, 1015, 2039, + 15, 1039, 527, 1551, 271, 1295, 783, 1807, + 143, 1167, 655, 1679, 399, 1423, 911, 1935, + 79, 1103, 591, 1615, 335, 1359, 847, 1871, + 207, 1231, 719, 1743, 463, 1487, 975, 1999, + 47, 1071, 559, 1583, 303, 1327, 815, 1839, + 175, 1199, 687, 1711, 431, 1455, 943, 1967, + 111, 1135, 623, 1647, 367, 1391, 879, 1903, + 239, 1263, 751, 1775, 495, 1519, 1007, 2031, + 31, 1055, 543, 1567, 287, 1311, 799, 1823, + 159, 1183, 671, 1695, 415, 1439, 927, 1951, + 95, 1119, 607, 1631, 351, 1375, 863, 1887, + 223, 1247, 735, 1759, 479, 1503, 991, 2015, + 63, 1087, 575, 1599, 319, 1343, 831, 1855, + 191, 1215, 703, 1727, 447, 1471, 959, 1983, + 127, 1151, 639, 1663, 383, 1407, 895, 1919, + 255, 1279, 767, 1791, 511, 1535, 1023, 2047, +}; + +static BROTLI_INLINE void StoreStaticCommandHuffmanTree( + size_t* storage_ix, uint8_t* storage) { + BrotliWriteBits( + 56, BROTLI_MAKE_UINT64_T(0x926244U, 0x16307003U), storage_ix, storage); + BrotliWriteBits(3, 0x00000000U, storage_ix, storage); +} + +static const BROTLI_MODEL("small") uint16_t kStaticDistanceCodeBits[64] = { + 0, 32, 16, 48, 8, 40, 24, 56, 4, 36, 20, 52, 12, 44, 28, 60, + 2, 34, 18, 50, 10, 42, 26, 58, 6, 38, 22, 54, 14, 46, 30, 62, + 1, 33, 17, 49, 9, 41, 25, 57, 5, 37, 21, 53, 13, 45, 29, 61, + 3, 35, 19, 51, 11, 43, 27, 59, 7, 39, 23, 55, 15, 47, 31, 63, +}; + +static BROTLI_INLINE void StoreStaticDistanceHuffmanTree( + size_t* storage_ix, uint8_t* storage) { + BrotliWriteBits(28, 0x0369DC03u, storage_ix, storage); +} +/* GENERATED CODE END */ + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/fast_log.c b/packages/runtime/vendor/brotli/c/enc/fast_log.c new file mode 100644 index 000000000..7b7ac0295 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/fast_log.c @@ -0,0 +1,105 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "fast_log.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* ", ".join(["%.16ff" % x for x in [0.0]+[log2(x) for x in range(1, 256)]]) */ +const BROTLI_MODEL("small") double kBrotliLog2Table[BROTLI_LOG2_TABLE_SIZE] = { + 0.0000000000000000f, 0.0000000000000000f, 1.0000000000000000f, + 1.5849625007211563f, 2.0000000000000000f, 2.3219280948873622f, + 2.5849625007211561f, 2.8073549220576042f, 3.0000000000000000f, + 3.1699250014423126f, 3.3219280948873626f, 3.4594316186372978f, + 3.5849625007211565f, 3.7004397181410922f, 3.8073549220576037f, + 3.9068905956085187f, 4.0000000000000000f, 4.0874628412503400f, + 4.1699250014423122f, 4.2479275134435852f, 4.3219280948873626f, + 4.3923174227787607f, 4.4594316186372973f, 4.5235619560570131f, + 4.5849625007211570f, 4.6438561897747244f, 4.7004397181410926f, + 4.7548875021634691f, 4.8073549220576037f, 4.8579809951275728f, + 4.9068905956085187f, 4.9541963103868758f, 5.0000000000000000f, + 5.0443941193584534f, 5.0874628412503400f, 5.1292830169449664f, + 5.1699250014423122f, 5.2094533656289501f, 5.2479275134435852f, + 5.2854022188622487f, 5.3219280948873626f, 5.3575520046180838f, + 5.3923174227787607f, 5.4262647547020979f, 5.4594316186372973f, + 5.4918530963296748f, 5.5235619560570131f, 5.5545888516776376f, + 5.5849625007211570f, 5.6147098441152083f, 5.6438561897747244f, + 5.6724253419714961f, 5.7004397181410926f, 5.7279204545631996f, + 5.7548875021634691f, 5.7813597135246599f, 5.8073549220576046f, + 5.8328900141647422f, 5.8579809951275719f, 5.8826430493618416f, + 5.9068905956085187f, 5.9307373375628867f, 5.9541963103868758f, + 5.9772799234999168f, 6.0000000000000000f, 6.0223678130284544f, + 6.0443941193584534f, 6.0660891904577721f, 6.0874628412503400f, + 6.1085244567781700f, 6.1292830169449672f, 6.1497471195046822f, + 6.1699250014423122f, 6.1898245588800176f, 6.2094533656289510f, + 6.2288186904958804f, 6.2479275134435861f, 6.2667865406949019f, + 6.2854022188622487f, 6.3037807481771031f, 6.3219280948873617f, + 6.3398500028846252f, 6.3575520046180847f, 6.3750394313469254f, + 6.3923174227787598f, 6.4093909361377026f, 6.4262647547020979f, + 6.4429434958487288f, 6.4594316186372982f, 6.4757334309663976f, + 6.4918530963296748f, 6.5077946401986964f, 6.5235619560570131f, + 6.5391588111080319f, 6.5545888516776376f, 6.5698556083309478f, + 6.5849625007211561f, 6.5999128421871278f, 6.6147098441152092f, + 6.6293566200796095f, 6.6438561897747253f, 6.6582114827517955f, + 6.6724253419714952f, 6.6865005271832185f, 6.7004397181410917f, + 6.7142455176661224f, 6.7279204545631988f, 6.7414669864011465f, + 6.7548875021634691f, 6.7681843247769260f, 6.7813597135246599f, + 6.7944158663501062f, 6.8073549220576037f, 6.8201789624151887f, + 6.8328900141647422f, 6.8454900509443757f, 6.8579809951275719f, + 6.8703647195834048f, 6.8826430493618416f, 6.8948177633079437f, + 6.9068905956085187f, 6.9188632372745955f, 6.9307373375628867f, + 6.9425145053392399f, 6.9541963103868758f, 6.9657842846620879f, + 6.9772799234999168f, 6.9886846867721664f, 7.0000000000000000f, + 7.0112272554232540f, 7.0223678130284544f, 7.0334230015374501f, + 7.0443941193584534f, 7.0552824355011898f, 7.0660891904577721f, + 7.0768155970508317f, 7.0874628412503400f, 7.0980320829605272f, + 7.1085244567781700f, 7.1189410727235076f, 7.1292830169449664f, + 7.1395513523987937f, 7.1497471195046822f, 7.1598713367783891f, + 7.1699250014423130f, 7.1799090900149345f, 7.1898245588800176f, + 7.1996723448363644f, 7.2094533656289492f, 7.2191685204621621f, + 7.2288186904958804f, 7.2384047393250794f, 7.2479275134435861f, + 7.2573878426926521f, 7.2667865406949019f, 7.2761244052742384f, + 7.2854022188622487f, 7.2946207488916270f, 7.3037807481771031f, + 7.3128829552843557f, 7.3219280948873617f, 7.3309168781146177f, + 7.3398500028846243f, 7.3487281542310781f, 7.3575520046180847f, + 7.3663222142458151f, 7.3750394313469254f, 7.3837042924740528f, + 7.3923174227787607f, 7.4008794362821844f, 7.4093909361377026f, + 7.4178525148858991f, 7.4262647547020979f, 7.4346282276367255f, + 7.4429434958487288f, 7.4512111118323299f, 7.4594316186372973f, + 7.4676055500829976f, 7.4757334309663976f, 7.4838157772642564f, + 7.4918530963296748f, 7.4998458870832057f, 7.5077946401986964f, + 7.5156998382840436f, 7.5235619560570131f, 7.5313814605163119f, + 7.5391588111080319f, 7.5468944598876373f, 7.5545888516776376f, + 7.5622424242210728f, 7.5698556083309478f, 7.5774288280357487f, + 7.5849625007211561f, 7.5924570372680806f, 7.5999128421871278f, + 7.6073303137496113f, 7.6147098441152075f, 7.6220518194563764f, + 7.6293566200796095f, 7.6366246205436488f, 7.6438561897747244f, + 7.6510516911789290f, 7.6582114827517955f, 7.6653359171851765f, + 7.6724253419714952f, 7.6794800995054464f, 7.6865005271832185f, + 7.6934869574993252f, 7.7004397181410926f, 7.7073591320808825f, + 7.7142455176661224f, 7.7210991887071856f, 7.7279204545631996f, + 7.7347096202258392f, 7.7414669864011465f, 7.7481928495894596f, + 7.7548875021634691f, 7.7615512324444795f, 7.7681843247769260f, + 7.7747870596011737f, 7.7813597135246608f, 7.7879025593914317f, + 7.7944158663501062f, 7.8008998999203047f, 7.8073549220576037f, + 7.8137811912170374f, 7.8201789624151887f, 7.8265484872909159f, + 7.8328900141647422f, 7.8392037880969445f, 7.8454900509443757f, + 7.8517490414160571f, 7.8579809951275719f, 7.8641861446542798f, + 7.8703647195834048f, 7.8765169465650002f, 7.8826430493618425f, + 7.8887432488982601f, 7.8948177633079446f, 7.9008668079807496f, + 7.9068905956085187f, 7.9128893362299619f, 7.9188632372745955f, + 7.9248125036057813f, 7.9307373375628867f, 7.9366379390025719f, + 7.9425145053392399f, 7.9483672315846778f, 7.9541963103868758f, + 7.9600019320680806f, 7.9657842846620870f, 7.9715435539507720f, + 7.9772799234999168f, 7.9829935746943104f, 7.9886846867721664f, + 7.9943534368588578f +}; + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/fast_log.h b/packages/runtime/vendor/brotli/c/enc/fast_log.h new file mode 100644 index 000000000..a245443ff --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/fast_log.h @@ -0,0 +1,66 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Utilities for fast computation of logarithms. */ + +#ifndef BROTLI_ENC_FAST_LOG_H_ +#define BROTLI_ENC_FAST_LOG_H_ + +#include + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static BROTLI_INLINE uint32_t Log2FloorNonZero(size_t n) { +#if defined(BROTLI_BSR32) + return BROTLI_BSR32((uint32_t)n); +#else + uint32_t result = 0; + while (n >>= 1) result++; + return result; +#endif +} + +#define BROTLI_LOG2_TABLE_SIZE 256 + +/* A lookup table for small values of log2(int) to be used in entropy + computation. */ +BROTLI_INTERNAL extern const BROTLI_MODEL("small") +double kBrotliLog2Table[BROTLI_LOG2_TABLE_SIZE]; + +/* Visual Studio 2012 and Android API levels < 18 do not have the log2() + * function defined, so we use log() and a multiplication instead. */ +#if !defined(BROTLI_HAVE_LOG2) +#if ((defined(_MSC_VER) && _MSC_VER <= 1700) || \ + (defined(__ANDROID_API__) && __ANDROID_API__ < 18)) +#define BROTLI_HAVE_LOG2 0 +#else +#define BROTLI_HAVE_LOG2 1 +#endif +#endif + +#define LOG_2_INV 1.4426950408889634 + +/* Faster logarithm for small integers, with the property of log2(0) == 0. */ +static BROTLI_INLINE double FastLog2(size_t v) { + if (v < BROTLI_LOG2_TABLE_SIZE) { + return kBrotliLog2Table[v]; + } +#if !(BROTLI_HAVE_LOG2) + return log((double)v) * LOG_2_INV; +#else + return log2((double)v); +#endif +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_FAST_LOG_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/find_match_length.h b/packages/runtime/vendor/brotli/c/enc/find_match_length.h new file mode 100644 index 000000000..096be9331 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/find_match_length.h @@ -0,0 +1,70 @@ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Function to find maximal matching prefixes of strings. */ + +#ifndef BROTLI_ENC_FIND_MATCH_LENGTH_H_ +#define BROTLI_ENC_FIND_MATCH_LENGTH_H_ + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* Separate implementation for little-endian 64-bit targets, for speed. */ +#if defined(BROTLI_TZCNT64) && BROTLI_64_BITS && BROTLI_LITTLE_ENDIAN +static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1, + const uint8_t* s2, + size_t limit) { + const uint8_t *s1_orig = s1; + for (; limit >= 8; limit -= 8) { + uint64_t x = BROTLI_UNALIGNED_LOAD64LE(s2) ^ + BROTLI_UNALIGNED_LOAD64LE(s1); + s2 += 8; + if (x != 0) { + size_t matching_bits = (size_t)BROTLI_TZCNT64(x); + return (size_t)(s1 - s1_orig) + (matching_bits >> 3); + } + s1 += 8; + } + while (limit && *s1 == *s2) { + limit--; + ++s2; + ++s1; + } + return (size_t)(s1 - s1_orig); +} +#else +static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1, + const uint8_t* s2, + size_t limit) { + size_t matched = 0; + const uint8_t* s2_limit = s2 + limit; + const uint8_t* s2_ptr = s2; + /* Find out how long the match is. We loop over the data 32 bits at a + time until we find a 32-bit block that doesn't match; then we find + the first non-matching bit and use that to calculate the total + length of the match. */ + while (s2_ptr <= s2_limit - 4 && + BrotliUnalignedRead32(s2_ptr) == + BrotliUnalignedRead32(s1 + matched)) { + s2_ptr += 4; + matched += 4; + } + while ((s2_ptr < s2_limit) && (s1[matched] == *s2_ptr)) { + ++s2_ptr; + ++matched; + } + return matched; +} +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_FIND_MATCH_LENGTH_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/hash.h b/packages/runtime/vendor/brotli/c/enc/hash.h new file mode 100644 index 000000000..d86fa1895 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash.h @@ -0,0 +1,735 @@ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. */ + +#ifndef BROTLI_ENC_HASH_H_ +#define BROTLI_ENC_HASH_H_ + +#include "../common/constants.h" +#include "../common/dictionary.h" +#include "../common/platform.h" +#include "compound_dictionary.h" +#include "encoder_dict.h" +#include "fast_log.h" +#include "find_match_length.h" +#include "hash_base.h" +#include "matching_tag_mask.h" +#include "memory.h" +#include "params.h" +#include "quality.h" +#include "static_dict.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct { + /** + * Dynamically allocated areas; regular hasher uses one or two allocations; + * "composite" hasher uses up to 4 allocations. + */ + void* extra[4]; + + /** + * False before the first invocation of HasherSetup (where "extra" memory) + * is allocated. + */ + BROTLI_BOOL is_setup_; + + size_t dict_num_lookups; + size_t dict_num_matches; + + BrotliHasherParams params; + + /** + * False if hasher needs to be "prepared" before use (before the first + * invocation of HasherSetup or after HasherReset). "preparation" is hasher + * data initialization (using input ringbuffer). + */ + BROTLI_BOOL is_prepared_; +} HasherCommon; + +#define score_t size_t + +static const uint32_t kCutoffTransformsCount = 10; +/* 0, 12, 27, 23, 42, 63, 56, 48, 59, 64 */ +/* 0+0, 4+8, 8+19, 12+11, 16+26, 20+43, 24+32, 28+20, 32+27, 36+28 */ +static const uint64_t kCutoffTransforms = + BROTLI_MAKE_UINT64_T(0x071B520A, 0xDA2D3200); + +typedef struct HasherSearchResult { + size_t len; + size_t distance; + score_t score; + int len_code_delta; /* == len_code - len */ +} HasherSearchResult; + +static BROTLI_INLINE void PrepareDistanceCache( + int* BROTLI_RESTRICT distance_cache, const int num_distances) { + if (num_distances > 4) { + int last_distance = distance_cache[0]; + distance_cache[4] = last_distance - 1; + distance_cache[5] = last_distance + 1; + distance_cache[6] = last_distance - 2; + distance_cache[7] = last_distance + 2; + distance_cache[8] = last_distance - 3; + distance_cache[9] = last_distance + 3; + if (num_distances > 10) { + int next_last_distance = distance_cache[1]; + distance_cache[10] = next_last_distance - 1; + distance_cache[11] = next_last_distance + 1; + distance_cache[12] = next_last_distance - 2; + distance_cache[13] = next_last_distance + 2; + distance_cache[14] = next_last_distance - 3; + distance_cache[15] = next_last_distance + 3; + } + } +} + +#define BROTLI_LITERAL_BYTE_SCORE 135 +#define BROTLI_DISTANCE_BIT_PENALTY 30 +/* Score must be positive after applying maximal penalty. */ +#define BROTLI_SCORE_BASE (BROTLI_DISTANCE_BIT_PENALTY * 8 * sizeof(size_t)) + +/* Usually, we always choose the longest backward reference. This function + allows for the exception of that rule. + + If we choose a backward reference that is further away, it will + usually be coded with more bits. We approximate this by assuming + log2(distance). If the distance can be expressed in terms of the + last four distances, we use some heuristic constants to estimate + the bits cost. For the first up to four literals we use the bit + cost of the literals from the literal cost model, after that we + use the average bit cost of the cost model. + + This function is used to sometimes discard a longer backward reference + when it is not much longer and the bit cost for encoding it is more + than the saved literals. + + backward_reference_offset MUST be positive. */ +static BROTLI_INLINE score_t BackwardReferenceScore( + size_t copy_length, size_t backward_reference_offset) { + return BROTLI_SCORE_BASE + BROTLI_LITERAL_BYTE_SCORE * (score_t)copy_length - + BROTLI_DISTANCE_BIT_PENALTY * Log2FloorNonZero(backward_reference_offset); +} + +static BROTLI_INLINE score_t BackwardReferenceScoreUsingLastDistance( + size_t copy_length) { + return BROTLI_LITERAL_BYTE_SCORE * (score_t)copy_length + + BROTLI_SCORE_BASE + 15; +} + +static BROTLI_INLINE score_t BackwardReferencePenaltyUsingLastDistance( + size_t distance_short_code) { + return (score_t)39 + ((0x1CA10 >> (distance_short_code & 0xE)) & 0xE); +} + +static BROTLI_INLINE BROTLI_BOOL TestStaticDictionaryItem( + const BrotliEncoderDictionary* dictionary, size_t len, size_t word_idx, + const uint8_t* data, size_t max_length, size_t max_backward, + size_t max_distance, HasherSearchResult* out) { + size_t offset; + size_t matchlen; + size_t backward; + score_t score; + offset = dictionary->words->offsets_by_length[len] + len * word_idx; + if (len > max_length) { + return BROTLI_FALSE; + } + + matchlen = + FindMatchLengthWithLimit(data, &dictionary->words->data[offset], len); + if (matchlen + dictionary->cutoffTransformsCount <= len || matchlen == 0) { + return BROTLI_FALSE; + } + { + size_t cut = len - matchlen; + size_t transform_id = (cut << 2) + + (size_t)((dictionary->cutoffTransforms >> (cut * 6)) & 0x3F); + backward = max_backward + 1 + word_idx + + (transform_id << dictionary->words->size_bits_by_length[len]); + } + if (backward > max_distance) { + return BROTLI_FALSE; + } + score = BackwardReferenceScore(matchlen, backward); + if (score < out->score) { + return BROTLI_FALSE; + } + out->len = matchlen; + out->len_code_delta = (int)len - (int)matchlen; + out->distance = backward; + out->score = score; + return BROTLI_TRUE; +} + +static BROTLI_INLINE void SearchInStaticDictionary( + const BrotliEncoderDictionary* dictionary, + HasherCommon* common, const uint8_t* data, size_t max_length, + size_t max_backward, size_t max_distance, + HasherSearchResult* out, BROTLI_BOOL shallow) { + size_t key; + size_t i; + if (common->dict_num_matches < (common->dict_num_lookups >> 7)) { + return; + } + key = Hash14(data) << 1; + for (i = 0; i < (shallow ? 1u : 2u); ++i, ++key) { + common->dict_num_lookups++; + if (dictionary->hash_table_lengths[key] != 0) { + BROTLI_BOOL item_matches = TestStaticDictionaryItem( + dictionary, dictionary->hash_table_lengths[key], + dictionary->hash_table_words[key], data, + max_length, max_backward, max_distance, out); + if (item_matches) { + common->dict_num_matches++; + } + } + } +} + +typedef struct BackwardMatch { + uint32_t distance; + uint32_t length_and_code; +} BackwardMatch; + +static BROTLI_INLINE void InitBackwardMatch(BackwardMatch* self, + size_t dist, size_t len) { + self->distance = (uint32_t)dist; + self->length_and_code = (uint32_t)(len << 5); +} + +static BROTLI_INLINE void InitDictionaryBackwardMatch(BackwardMatch* self, + size_t dist, size_t len, size_t len_code) { + self->distance = (uint32_t)dist; + self->length_and_code = + (uint32_t)((len << 5) | (len == len_code ? 0 : len_code)); +} + +static BROTLI_INLINE size_t BackwardMatchLength(const BackwardMatch* self) { + return self->length_and_code >> 5; +} + +static BROTLI_INLINE size_t BackwardMatchLengthCode(const BackwardMatch* self) { + size_t code = self->length_and_code & 31; + return code ? code : BackwardMatchLength(self); +} + +#define EXPAND_CAT(a, b) CAT(a, b) +#define CAT(a, b) a ## b +#define FN(X) EXPAND_CAT(X, HASHER()) + +#define HASHER() H10 +#define BUCKET_BITS 17 +#define MAX_TREE_SEARCH_DEPTH 64 +#define MAX_TREE_COMP_LENGTH 128 +#include "hash_to_binary_tree_inc.h" /* NOLINT(build/include) */ +#undef MAX_TREE_SEARCH_DEPTH +#undef MAX_TREE_COMP_LENGTH +#undef BUCKET_BITS +#undef HASHER +/* MAX_NUM_MATCHES == 64 + MAX_TREE_SEARCH_DEPTH */ +#define MAX_NUM_MATCHES_H10 128 + +/* For BUCKET_SWEEP_BITS == 0, enabling the dictionary lookup makes compression + a little faster (0.5% - 1%) and it compresses 0.15% better on small text + and HTML inputs. */ + +#define HASHER() H2 +#define BUCKET_BITS 16 +#define BUCKET_SWEEP_BITS 0 +#define HASH_LEN 5 +#define USE_DICTIONARY 1 +#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ +#undef BUCKET_SWEEP_BITS +#undef USE_DICTIONARY +#undef HASHER + +#define HASHER() H3 +#define BUCKET_SWEEP_BITS 1 +#define USE_DICTIONARY 0 +#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ +#undef USE_DICTIONARY +#undef BUCKET_SWEEP_BITS +#undef BUCKET_BITS +#undef HASHER + +#define HASHER() H4 +#define BUCKET_BITS 17 +#define BUCKET_SWEEP_BITS 2 +#define USE_DICTIONARY 1 +#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ +#undef USE_DICTIONARY +#undef HASH_LEN +#undef BUCKET_SWEEP_BITS +#undef BUCKET_BITS +#undef HASHER + +#define HASHER() H5 +#include "hash_longest_match_inc.h" /* NOLINT(build/include) */ +#undef HASHER + +#define HASHER() H6 +#include "hash_longest_match64_inc.h" /* NOLINT(build/include) */ +#undef HASHER + +#if defined(BROTLI_MAX_SIMD_QUALITY) +#define HASHER() H58 +#include "hash_longest_match_simd_inc.h" /* NOLINT(build/include) */ +#undef HASHER + +#define HASHER() H68 +#include "hash_longest_match64_simd_inc.h" /* NOLINT(build/include) */ +#undef HASHER +#endif + +#define BUCKET_BITS 15 + +#define NUM_LAST_DISTANCES_TO_CHECK 4 +#define NUM_BANKS 1 +#define BANK_BITS 16 +#define HASHER() H40 +#include "hash_forgetful_chain_inc.h" /* NOLINT(build/include) */ +#undef HASHER +#undef NUM_LAST_DISTANCES_TO_CHECK + +#define NUM_LAST_DISTANCES_TO_CHECK 10 +#define HASHER() H41 +#include "hash_forgetful_chain_inc.h" /* NOLINT(build/include) */ +#undef HASHER +#undef NUM_LAST_DISTANCES_TO_CHECK +#undef NUM_BANKS +#undef BANK_BITS + +#define NUM_LAST_DISTANCES_TO_CHECK 16 +#define NUM_BANKS 512 +#define BANK_BITS 9 +#define HASHER() H42 +#include "hash_forgetful_chain_inc.h" /* NOLINT(build/include) */ +#undef HASHER +#undef NUM_LAST_DISTANCES_TO_CHECK +#undef NUM_BANKS +#undef BANK_BITS + +#undef BUCKET_BITS + +#define HASHER() H54 +#define BUCKET_BITS 20 +#define BUCKET_SWEEP_BITS 2 +#define HASH_LEN 7 +#define USE_DICTIONARY 0 +#include "hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */ +#undef USE_DICTIONARY +#undef HASH_LEN +#undef BUCKET_SWEEP_BITS +#undef BUCKET_BITS +#undef HASHER + +/* fast large window hashers */ + +#define HASHER() HROLLING_FAST +#define CHUNKLEN 32 +#define JUMP 4 +#define NUMBUCKETS 16777216 +#define MASK ((NUMBUCKETS * 64) - 1) +#include "hash_rolling_inc.h" /* NOLINT(build/include) */ +#undef JUMP +#undef HASHER + + +#define HASHER() HROLLING +#define JUMP 1 +#include "hash_rolling_inc.h" /* NOLINT(build/include) */ +#undef MASK +#undef NUMBUCKETS +#undef JUMP +#undef CHUNKLEN +#undef HASHER + +#define HASHER() H35 +#define HASHER_A H3 +#define HASHER_B HROLLING_FAST +#include "hash_composite_inc.h" /* NOLINT(build/include) */ +#undef HASHER_A +#undef HASHER_B +#undef HASHER + +#define HASHER() H55 +#define HASHER_A H54 +#define HASHER_B HROLLING_FAST +#include "hash_composite_inc.h" /* NOLINT(build/include) */ +#undef HASHER_A +#undef HASHER_B +#undef HASHER + +#define HASHER() H65 +#define HASHER_A H6 +#define HASHER_B HROLLING +#include "hash_composite_inc.h" /* NOLINT(build/include) */ +#undef HASHER_A +#undef HASHER_B +#undef HASHER + +#undef FN +#undef CAT +#undef EXPAND_CAT + +#if defined(BROTLI_MAX_SIMD_QUALITY) +#define FOR_SIMPLE_HASHERS(H) \ + H(2) H(3) H(4) H(5) H(6) H(40) H(41) H(42) H(54) H(58) H(68) +#else +#define FOR_SIMPLE_HASHERS(H) \ + H(2) H(3) H(4) H(5) H(6) H(40) H(41) H(42) H(54) +#endif +#define FOR_COMPOSITE_HASHERS(H) H(35) H(55) H(65) +#define FOR_GENERIC_HASHERS(H) FOR_SIMPLE_HASHERS(H) FOR_COMPOSITE_HASHERS(H) +#define FOR_ALL_HASHERS(H) FOR_GENERIC_HASHERS(H) H(10) + +typedef struct { + HasherCommon common; + + union { +#define MEMBER_(N) \ + H ## N _H ## N; + FOR_ALL_HASHERS(MEMBER_) +#undef MEMBER_ + } privat; +} Hasher; + +/* MUST be invoked before any other method. */ +static BROTLI_INLINE void HasherInit(Hasher* hasher) { + hasher->common.is_setup_ = BROTLI_FALSE; + hasher->common.extra[0] = NULL; + hasher->common.extra[1] = NULL; + hasher->common.extra[2] = NULL; + hasher->common.extra[3] = NULL; +} + +static BROTLI_INLINE void DestroyHasher(MemoryManager* m, Hasher* hasher) { + if (hasher->common.extra[0] != NULL) BROTLI_FREE(m, hasher->common.extra[0]); + if (hasher->common.extra[1] != NULL) BROTLI_FREE(m, hasher->common.extra[1]); + if (hasher->common.extra[2] != NULL) BROTLI_FREE(m, hasher->common.extra[2]); + if (hasher->common.extra[3] != NULL) BROTLI_FREE(m, hasher->common.extra[3]); +} + +static BROTLI_INLINE void HasherReset(Hasher* hasher) { + hasher->common.is_prepared_ = BROTLI_FALSE; +} + +static BROTLI_INLINE void HasherSize(const BrotliEncoderParams* params, + BROTLI_BOOL one_shot, const size_t input_size, size_t* alloc_size) { + switch (params->hasher.type) { +#define SIZE_(N) \ + case N: \ + HashMemAllocInBytesH ## N(params, one_shot, input_size, alloc_size); \ + break; + FOR_ALL_HASHERS(SIZE_) +#undef SIZE_ + default: + break; + } +} + +static BROTLI_INLINE void HasherSetup(MemoryManager* m, Hasher* hasher, + BrotliEncoderParams* params, const uint8_t* data, size_t position, + size_t input_size, BROTLI_BOOL is_last) { + BROTLI_BOOL one_shot = (position == 0 && is_last); + if (!hasher->common.is_setup_) { + size_t alloc_size[4] = {0}; + size_t i; + ChooseHasher(params, ¶ms->hasher); + hasher->common.params = params->hasher; + hasher->common.dict_num_lookups = 0; + hasher->common.dict_num_matches = 0; + HasherSize(params, one_shot, input_size, alloc_size); + for (i = 0; i < 4; ++i) { + if (alloc_size[i] == 0) continue; + hasher->common.extra[i] = BROTLI_ALLOC(m, uint8_t, alloc_size[i]); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra[i])) return; + } + switch (hasher->common.params.type) { +#define INITIALIZE_(N) \ + case N: \ + InitializeH ## N(&hasher->common, \ + &hasher->privat._H ## N, params); \ + break; + FOR_ALL_HASHERS(INITIALIZE_); +#undef INITIALIZE_ + default: + break; + } + HasherReset(hasher); + hasher->common.is_setup_ = BROTLI_TRUE; + } + + if (!hasher->common.is_prepared_) { + switch (hasher->common.params.type) { +#define PREPARE_(N) \ + case N: \ + PrepareH ## N( \ + &hasher->privat._H ## N, \ + one_shot, input_size, data); \ + break; + FOR_ALL_HASHERS(PREPARE_) +#undef PREPARE_ + default: break; + } + hasher->common.is_prepared_ = BROTLI_TRUE; + } +} + +static BROTLI_INLINE void InitOrStitchToPreviousBlock( + MemoryManager* m, Hasher* hasher, const uint8_t* data, size_t mask, + BrotliEncoderParams* params, size_t position, size_t input_size, + BROTLI_BOOL is_last) { + HasherSetup(m, hasher, params, data, position, input_size, is_last); + if (BROTLI_IS_OOM(m)) return; + switch (hasher->common.params.type) { +#define INIT_(N) \ + case N: \ + StitchToPreviousBlockH ## N( \ + &hasher->privat._H ## N, \ + input_size, position, data, mask); \ + break; + FOR_ALL_HASHERS(INIT_) +#undef INIT_ + default: break; + } +} + +/* NB: when seamless dictionary-ring-buffer copies are implemented, don't forget + to add proper guards for non-zero-BROTLI_PARAM_STREAM_OFFSET. */ +static BROTLI_INLINE void FindCompoundDictionaryMatch( + const PreparedDictionary* self, const uint8_t* BROTLI_RESTRICT data, + const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache, + const size_t cur_ix, const size_t max_length, const size_t distance_offset, + const size_t max_distance, HasherSearchResult* BROTLI_RESTRICT out) { + const uint32_t source_size = self->source_size; + const size_t boundary = distance_offset - source_size; + const uint32_t hash_bits = self->hash_bits; + const uint32_t bucket_bits = self->bucket_bits; + const uint32_t slot_bits = self->slot_bits; + + const uint32_t hash_shift = 64u - bucket_bits; + const uint32_t slot_mask = (~((uint32_t)0U)) >> (32 - slot_bits); + const uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits); + + const uint32_t* slot_offsets = (uint32_t*)(&self[1]); + const uint16_t* heads = (uint16_t*)(&slot_offsets[(size_t)1u << slot_bits]); + const uint32_t* items = (uint32_t*)(&heads[(size_t)1u << bucket_bits]); + const uint8_t* source = NULL; + + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + score_t best_score = out->score; + size_t best_len = out->len; + size_t i; + const uint64_t h = + (BROTLI_UNALIGNED_LOAD64LE(&data[cur_ix_masked]) & hash_mask) * + kPreparedDictionaryHashMul64Long; + const uint32_t key = (uint32_t)(h >> hash_shift); + const uint32_t slot = key & slot_mask; + const uint32_t head = heads[key]; + const uint32_t* BROTLI_RESTRICT chain = &items[slot_offsets[slot] + head]; + uint32_t item = (head == 0xFFFF) ? 1 : 0; + + const void* tail = (void*)&items[self->num_items]; + if (self->magic == kPreparedDictionaryMagic) { + source = (const uint8_t*)tail; + } else { + /* kLeanPreparedDictionaryMagic */ + source = (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail); + } + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + for (i = 0; i < 4; ++i) { + const size_t distance = (size_t)distance_cache[i]; + size_t offset; + size_t limit; + size_t len; + if (distance <= boundary || distance > distance_offset) continue; + offset = distance_offset - distance; + limit = source_size - offset; + limit = limit > max_length ? max_length : limit; + len = FindMatchLengthWithLimit(&source[offset], &data[cur_ix_masked], + limit); + if (len >= 2) { + score_t score = BackwardReferenceScoreUsingLastDistance(len); + if (best_score < score) { + if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); + if (best_score < score) { + best_score = score; + if (len > best_len) best_len = len; + out->len = len; + out->len_code_delta = 0; + out->distance = distance; + out->score = best_score; + } + } + } + } + /* we require matches of len >4, so increase best_len to 3, so we can compare + * 4 bytes all the time. */ + if (best_len < 3) { + best_len = 3; + } + while (item == 0) { + size_t offset; + size_t distance; + size_t limit; + item = *chain; + chain++; + offset = item & 0x7FFFFFFF; + item &= 0x80000000; + distance = distance_offset - offset; + limit = source_size - offset; + limit = (limit > max_length) ? max_length : limit; + if (distance > max_distance) continue; + if (cur_ix_masked + best_len > ring_buffer_mask || best_len >= limit || + /* compare 4 bytes ending at best_len + 1 */ + BrotliUnalignedRead32(&data[cur_ix_masked + best_len - 3]) != + BrotliUnalignedRead32(&source[offset + best_len - 3])) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&source[offset], + &data[cur_ix_masked], + limit); + if (len >= 4) { + score_t score = BackwardReferenceScore(len, distance); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->len_code_delta = 0; + out->distance = distance; + out->score = best_score; + } + } + } + } +} + +/* NB: when seamless dictionary-ring-buffer copies are implemented, don't forget + to add proper guards for non-zero-BROTLI_PARAM_STREAM_OFFSET. */ +static BROTLI_INLINE size_t FindAllCompoundDictionaryMatches( + const PreparedDictionary* self, const uint8_t* BROTLI_RESTRICT data, + const size_t ring_buffer_mask, const size_t cur_ix, const size_t min_length, + const size_t max_length, const size_t distance_offset, + const size_t max_distance, BackwardMatch* matches, size_t match_limit) { + const uint32_t source_size = self->source_size; + const uint32_t hash_bits = self->hash_bits; + const uint32_t bucket_bits = self->bucket_bits; + const uint32_t slot_bits = self->slot_bits; + + const uint32_t hash_shift = 64u - bucket_bits; + const uint32_t slot_mask = (~((uint32_t)0U)) >> (32 - slot_bits); + const uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits); + + const uint32_t* slot_offsets = (uint32_t*)(&self[1]); + const uint16_t* heads = (uint16_t*)(&slot_offsets[(size_t)1u << slot_bits]); + const uint32_t* items = (uint32_t*)(&heads[(size_t)1u << bucket_bits]); + const uint8_t* source = NULL; + + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + size_t best_len = min_length; + const uint64_t h = + (BROTLI_UNALIGNED_LOAD64LE(&data[cur_ix_masked]) & hash_mask) * + kPreparedDictionaryHashMul64Long; + const uint32_t key = (uint32_t)(h >> hash_shift); + const uint32_t slot = key & slot_mask; + const uint32_t head = heads[key]; + const uint32_t* BROTLI_RESTRICT chain = &items[slot_offsets[slot] + head]; + uint32_t item = (head == 0xFFFF) ? 1 : 0; + size_t found = 0; + + const void* tail = (void*)&items[self->num_items]; + if (self->magic == kPreparedDictionaryMagic) { + source = (const uint8_t*)tail; + } else { + /* kLeanPreparedDictionaryMagic */ + source = (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail); + } + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + while (item == 0) { + size_t offset; + size_t distance; + size_t limit; + size_t len; + item = *chain; + chain++; + offset = item & 0x7FFFFFFF; + item &= 0x80000000; + distance = distance_offset - offset; + limit = source_size - offset; + limit = (limit > max_length) ? max_length : limit; + if (distance > max_distance) continue; + if (cur_ix_masked + best_len > ring_buffer_mask || + best_len >= limit || + data[cur_ix_masked + best_len] != source[offset + best_len]) { + continue; + } + len = FindMatchLengthWithLimit( + &source[offset], &data[cur_ix_masked], limit); + if (len > best_len) { + best_len = len; + InitBackwardMatch(matches++, distance, len); + found++; + if (found == match_limit) break; + } + } + return found; +} + +static BROTLI_INLINE void LookupCompoundDictionaryMatch( + const CompoundDictionary* addon, const uint8_t* BROTLI_RESTRICT data, + const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache, + const size_t cur_ix, const size_t max_length, + const size_t max_ring_buffer_distance, const size_t max_distance, + HasherSearchResult* sr) { + size_t base_offset = max_ring_buffer_distance + 1 + addon->total_size - 1; + size_t d; + for (d = 0; d < addon->num_chunks; ++d) { + /* Only one prepared dictionary type is currently supported. */ + FindCompoundDictionaryMatch( + (const PreparedDictionary*)addon->chunks[d], data, ring_buffer_mask, + distance_cache, cur_ix, max_length, + base_offset - addon->chunk_offsets[d], max_distance, sr); + } +} + +static BROTLI_INLINE size_t LookupAllCompoundDictionaryMatches( + const CompoundDictionary* addon, const uint8_t* BROTLI_RESTRICT data, + const size_t ring_buffer_mask, const size_t cur_ix, size_t min_length, + const size_t max_length, const size_t max_ring_buffer_distance, + const size_t max_distance, BackwardMatch* matches, + size_t match_limit) { + size_t base_offset = max_ring_buffer_distance + 1 + addon->total_size - 1; + size_t d; + size_t total_found = 0; + for (d = 0; d < addon->num_chunks; ++d) { + /* Only one prepared dictionary type is currently supported. */ + total_found += FindAllCompoundDictionaryMatches( + (const PreparedDictionary*)addon->chunks[d], data, ring_buffer_mask, + cur_ix, min_length, max_length, base_offset - addon->chunk_offsets[d], + max_distance, matches + total_found, match_limit - total_found); + if (total_found == match_limit) break; + if (total_found > 0) { + min_length = BackwardMatchLength(&matches[total_found - 1]); + } + } + return total_found; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_HASH_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/hash_base.h b/packages/runtime/vendor/brotli/c/enc/hash_base.h new file mode 100644 index 000000000..edf1ef020 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_base.h @@ -0,0 +1,38 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Basic common hash functions / constants. */ + +#ifndef THIRD_PARTY_BROTLI_ENC_HASH_BASE_H_ +#define THIRD_PARTY_BROTLI_ENC_HASH_BASE_H_ + +#include "../common/platform.h" + +/* kHashMul32 multiplier has these properties: + * The multiplier must be odd. Otherwise we may lose the highest bit. + * No long streaks of ones or zeros. + * There is no effort to ensure that it is a prime, the oddity is enough + for this use. + * The number has been tuned heuristically against compression benchmarks. */ +static const uint32_t kHashMul32 = 0x1E35A7BD; +static const uint64_t kHashMul64 = + BROTLI_MAKE_UINT64_T(0x1FE35A7Bu, 0xD3579BD3u); + +static BROTLI_INLINE uint32_t Hash14(const uint8_t* data) { + uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> (32 - 14); +} + +static BROTLI_INLINE uint32_t Hash15(const uint8_t* data) { + uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> (32 - 15); +} + +#endif // THIRD_PARTY_BROTLI_ENC_HASH_BASE_H_ diff --git a/packages/runtime/vendor/brotli/c/enc/hash_composite_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_composite_inc.h new file mode 100644 index 000000000..3923bc72c --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_composite_inc.h @@ -0,0 +1,140 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2018 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN, HASHER_A, HASHER_B */ + +/* Composite hasher: This hasher allows to combine two other hashers, HASHER_A + and HASHER_B. */ + +#define HashComposite HASHER() + +#define FN_A(X) EXPAND_CAT(X, HASHER_A) +#define FN_B(X) EXPAND_CAT(X, HASHER_B) + +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { + size_t a = FN_A(HashTypeLength)(); + size_t b = FN_B(HashTypeLength)(); + return a > b ? a : b; +} + +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { + size_t a = FN_A(StoreLookahead)(); + size_t b = FN_B(StoreLookahead)(); + return a > b ? a : b; +} + +typedef struct HashComposite { + HASHER_A ha; + HASHER_B hb; + HasherCommon ha_common; + HasherCommon hb_common; + + /* Shortcuts. */ + HasherCommon* common; + + BROTLI_BOOL fresh; + const BrotliEncoderParams* params; +} HashComposite; + +static void FN(Initialize)(HasherCommon* common, + HashComposite* BROTLI_RESTRICT self, const BrotliEncoderParams* params) { + self->common = common; + + self->ha_common = *self->common; + self->hb_common = *self->common; + self->fresh = BROTLI_TRUE; + self->params = params; + /* TODO(lode): Initialize of the hashers is deferred to Prepare (and params + remembered here) because we don't get the one_shot and input_size params + here that are needed to know the memory size of them. Instead provide + those params to all hashers FN(Initialize) */ +} + +static void FN(Prepare)( + HashComposite* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + if (self->fresh) { + self->fresh = BROTLI_FALSE; + self->ha_common.extra[0] = self->common->extra[0]; + self->ha_common.extra[1] = self->common->extra[1]; + self->ha_common.extra[2] = NULL; + self->ha_common.extra[3] = NULL; + self->hb_common.extra[0] = self->common->extra[2]; + self->hb_common.extra[1] = self->common->extra[3]; + self->hb_common.extra[2] = NULL; + self->hb_common.extra[3] = NULL; + + FN_A(Initialize)(&self->ha_common, &self->ha, self->params); + FN_B(Initialize)(&self->hb_common, &self->hb, self->params); + } + FN_A(Prepare)(&self->ha, one_shot, input_size, data); + FN_B(Prepare)(&self->hb, one_shot, input_size, data); +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + size_t alloc_size_a[4] = {0}; + size_t alloc_size_b[4] = {0}; + FN_A(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_a); + FN_B(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_b); + /* Should never happen. */ + if (alloc_size_a[2] != 0 || alloc_size_a[3] != 0) exit(EXIT_FAILURE); + if (alloc_size_b[2] != 0 || alloc_size_b[3] != 0) exit(EXIT_FAILURE); + alloc_size[0] = alloc_size_a[0]; + alloc_size[1] = alloc_size_a[1]; + alloc_size[2] = alloc_size_b[0]; + alloc_size[3] = alloc_size_b[1]; +} + +static BROTLI_INLINE void FN(Store)(HashComposite* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { + FN_A(Store)(&self->ha, data, mask, ix); + FN_B(Store)(&self->hb, data, mask, ix); +} + +static BROTLI_INLINE void FN(StoreRange)( + HashComposite* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, + const size_t mask, const size_t ix_start, + const size_t ix_end) { + FN_A(StoreRange)(&self->ha, data, mask, ix_start, ix_end); + FN_B(StoreRange)(&self->hb, data, mask, ix_start, ix_end); +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashComposite* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ring_buffer_mask) { + FN_A(StitchToPreviousBlock)(&self->ha, num_bytes, position, + ringbuffer, ring_buffer_mask); + FN_B(StitchToPreviousBlock)(&self->hb, num_bytes, position, + ringbuffer, ring_buffer_mask); +} + +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashComposite* BROTLI_RESTRICT self, int* BROTLI_RESTRICT distance_cache) { + FN_A(PrepareDistanceCache)(&self->ha, distance_cache); + FN_B(PrepareDistanceCache)(&self->hb, distance_cache); +} + +static BROTLI_INLINE void FN(FindLongestMatch)( + HashComposite* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, + const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, + const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + FN_A(FindLongestMatch)(&self->ha, dictionary, data, ring_buffer_mask, + distance_cache, cur_ix, max_length, max_backward, dictionary_distance, + max_distance, out); + FN_B(FindLongestMatch)(&self->hb, dictionary, data, ring_buffer_mask, + distance_cache, cur_ix, max_length, max_backward, dictionary_distance, + max_distance, out); +} + +#undef HashComposite diff --git a/packages/runtime/vendor/brotli/c/enc/hash_forgetful_chain_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_forgetful_chain_inc.h new file mode 100644 index 000000000..c44276fa4 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_forgetful_chain_inc.h @@ -0,0 +1,305 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN, BUCKET_BITS, NUM_BANKS, BANK_BITS, + NUM_LAST_DISTANCES_TO_CHECK */ + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + Hashes are stored in chains which are bucketed to groups. Group of chains + share a storage "bank". When more than "bank size" chain nodes are added, + oldest nodes are replaced; this way several chains may share a tail. */ + +#define HashForgetfulChain HASHER() + +#define BANK_SIZE (1 << BANK_BITS) + +/* Number of hash buckets. */ +#define BUCKET_SIZE (1 << BUCKET_BITS) + +#define CAPPED_CHAINS 0 + +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; } + +/* HashBytes is the function that chooses the bucket to place the address in.*/ +static BROTLI_INLINE size_t FN(HashBytes)(const uint8_t* BROTLI_RESTRICT data) { + const uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> (32 - BUCKET_BITS); +} + +typedef struct FN(Slot) { + uint16_t delta; + uint16_t next; +} FN(Slot); + +typedef struct FN(Bank) { + FN(Slot) slots[BANK_SIZE]; +} FN(Bank); + +typedef struct HashForgetfulChain { + uint16_t free_slot_idx[NUM_BANKS]; /* Up to 1KiB. Move to dynamic? */ + size_t max_hops; + + /* Shortcuts. */ + void* extra[2]; + HasherCommon* common; + + /* --- Dynamic size members --- */ + + /* uint32_t addr[BUCKET_SIZE]; */ + + /* uint16_t head[BUCKET_SIZE]; */ + + /* Truncated hash used for quick rejection of "distance cache" candidates. */ + /* uint8_t tiny_hash[65536];*/ + + /* FN(Bank) banks[NUM_BANKS]; */ +} HashForgetfulChain; + +static uint32_t* FN(Addr)(void* extra) { + return (uint32_t*)extra; +} + +static uint16_t* FN(Head)(void* extra) { + return (uint16_t*)(&FN(Addr)(extra)[BUCKET_SIZE]); +} + +static uint8_t* FN(TinyHash)(void* extra) { + return (uint8_t*)(&FN(Head)(extra)[BUCKET_SIZE]); +} + +static FN(Bank)* FN(Banks)(void* extra) { + return (FN(Bank)*)(extra); +} + +static void FN(Initialize)( + HasherCommon* common, HashForgetfulChain* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + self->common = common; + self->extra[0] = common->extra[0]; + self->extra[1] = common->extra[1]; + + self->max_hops = (params->quality > 6 ? 7u : 8u) << (params->quality - 4); +} + +static void FN(Prepare)( + HashForgetfulChain* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]); + uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]); + uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra[0]); + /* Partial preparation is 100 times slower (per socket). */ + size_t partial_prepare_threshold = BUCKET_SIZE >> 6; + if (one_shot && input_size <= partial_prepare_threshold) { + size_t i; + for (i = 0; i < input_size; ++i) { + size_t bucket = FN(HashBytes)(&data[i]); + /* See InitEmpty comment. */ + addr[bucket] = 0xCCCCCCCC; + head[bucket] = 0xCCCC; + } + } else { + /* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position + processed by hasher never reaches 3GB + 64M; this makes all new chains + to be terminated after the first node. */ + memset(addr, 0xCC, sizeof(uint32_t) * BUCKET_SIZE); + memset(head, 0, sizeof(uint16_t) * BUCKET_SIZE); + } + memset(tiny_hash, 0, sizeof(uint8_t) * 65536); + memset(self->free_slot_idx, 0, sizeof(self->free_slot_idx)); +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + BROTLI_UNUSED(params); + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE + + sizeof(uint16_t) * BUCKET_SIZE + sizeof(uint8_t) * 65536; + alloc_size[1] = sizeof(FN(Bank)) * NUM_BANKS; +} + +/* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend + node to corresponding chain; also update tiny_hash for current position. */ +static BROTLI_INLINE void FN(Store)(HashForgetfulChain* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { + uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]); + uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]); + uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra[0]); + FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra[1]); + const size_t key = FN(HashBytes)(&data[ix & mask]); + const size_t bank = key & (NUM_BANKS - 1); + const size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1); + size_t delta = ix - addr[key]; + tiny_hash[(uint16_t)ix] = (uint8_t)key; + if (delta > 0xFFFF) delta = CAPPED_CHAINS ? 0 : 0xFFFF; + banks[bank].slots[idx].delta = (uint16_t)delta; + banks[bank].slots[idx].next = head[key]; + addr[key] = (uint32_t)ix; + head[key] = (uint16_t)idx; +} + +static BROTLI_INLINE void FN(StoreRange)( + HashForgetfulChain* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + size_t i; + for (i = ix_start; i < ix_end; ++i) { + FN(Store)(self, data, mask, i); + } +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashForgetfulChain* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ring_buffer_mask) { + if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + FN(Store)(self, ringbuffer, ring_buffer_mask, position - 3); + FN(Store)(self, ringbuffer, ring_buffer_mask, position - 2); + FN(Store)(self, ringbuffer, ring_buffer_mask, position - 1); + } +} + +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashForgetfulChain* BROTLI_RESTRICT self, + int* BROTLI_RESTRICT distance_cache) { + BROTLI_UNUSED(self); + PrepareDistanceCache(distance_cache, NUM_LAST_DISTANCES_TO_CHECK); +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + + REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke FN(PrepareDistanceCache) once. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +static BROTLI_INLINE void FN(FindLongestMatch)( + HashForgetfulChain* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, + const int* BROTLI_RESTRICT distance_cache, + const size_t cur_ix, const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]); + uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]); + uint8_t* BROTLI_RESTRICT tiny_hashes = FN(TinyHash)(self->extra[0]); + FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra[1]); + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + /* Don't accept a short copy from far away. */ + score_t min_score = out->score; + score_t best_score = out->score; + size_t best_len = out->len; + size_t i; + const size_t key = FN(HashBytes)(&data[cur_ix_masked]); + const uint8_t tiny_hash = (uint8_t)(key); + out->len = 0; + out->len_code_delta = 0; + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + /* Try last distance first. */ + for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) { + const size_t backward = (size_t)distance_cache[i]; + size_t prev_ix = (cur_ix - backward); + /* For distance code 0 we want to consider 2-byte matches. */ + if (i > 0 && tiny_hashes[(uint16_t)prev_ix] != tiny_hash) continue; + if (prev_ix >= cur_ix || backward > max_backward) { + continue; + } + prev_ix &= ring_buffer_mask; + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 2) { + score_t score = BackwardReferenceScoreUsingLastDistance(len); + if (best_score < score) { + if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + } + /* we require matches of len >4, so increase best_len to 3, so we can compare + * 4 bytes all the time. */ + if (best_len < 3) { + best_len = 3; + } + { + const size_t bank = key & (NUM_BANKS - 1); + size_t backward = 0; + size_t hops = self->max_hops; + size_t delta = cur_ix - addr[key]; + size_t slot = head[key]; + while (hops--) { + size_t prev_ix; + size_t last = slot; + backward += delta; + if (backward > max_backward || (CAPPED_CHAINS && !delta)) break; + prev_ix = (cur_ix - backward) & ring_buffer_mask; + slot = banks[bank].slots[last].next; + delta = banks[bank].slots[last].delta; + if (cur_ix_masked + best_len > ring_buffer_mask || + prev_ix + best_len > ring_buffer_mask || + /* compare 4 bytes ending at best_len + 1 */ + BrotliUnalignedRead32(&data[cur_ix_masked + best_len - 3]) != + BrotliUnalignedRead32(&data[prev_ix + best_len - 3])) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 4) { + /* Comparing for >= 3 does not change the semantics, but just saves + for a few unnecessary binary logarithms in backward reference + score, since we are not interested in such short matches. */ + score_t score = BackwardReferenceScore(len, backward); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + FN(Store)(self, data, ring_buffer_mask, cur_ix); + } + if (out->score == min_score) { + SearchInStaticDictionary(dictionary, + self->common, &data[cur_ix_masked], max_length, dictionary_distance, + max_distance, out, BROTLI_FALSE); + } +} + +#undef BANK_SIZE +#undef BUCKET_SIZE +#undef CAPPED_CHAINS + +#undef HashForgetfulChain diff --git a/packages/runtime/vendor/brotli/c/enc/hash_longest_match64_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_longest_match64_inc.h new file mode 100644 index 000000000..bdbb30aa5 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_longest_match64_inc.h @@ -0,0 +1,279 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN */ + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + This is a hash map of fixed size (bucket_size_) to a ring buffer of + fixed size (block_size_). The ring buffer contains the last block_size_ + index positions of the given hash key in the compressed data. */ + +#define HashLongestMatch HASHER() + +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 8; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 8; } + +/* HashBytes is the function that chooses the bucket to place the address in. */ +static BROTLI_INLINE size_t FN(HashBytes)(const uint8_t* BROTLI_RESTRICT data, + uint64_t hash_mul) { + const uint64_t h = BROTLI_UNALIGNED_LOAD64LE(data) * hash_mul; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return (size_t)(h >> (64 - 15)); +} + +typedef struct HashLongestMatch { + /* Number of hash buckets. */ + size_t bucket_size_; + /* Only block_size_ newest backward references are kept, + and the older are forgotten. */ + size_t block_size_; + /* Hash multiplier tuned to match length. */ + uint64_t hash_mul_; + /* Mask for accessing entries in a block (in a ring-buffer manner). */ + uint32_t block_mask_; + + int block_bits_; + int num_last_distances_to_check_; + + /* Shortcuts. */ + HasherCommon* common_; + + /* --- Dynamic size members --- */ + + /* Number of entries in a particular bucket. */ + uint16_t* num_; /* uint16_t[bucket_size]; */ + + /* Buckets containing block_size_ of backward references. */ + uint32_t* buckets_; /* uint32_t[bucket_size * block_size]; */ +} HashLongestMatch; + +static void FN(Initialize)( + HasherCommon* common, HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + self->common_ = common; + + BROTLI_UNUSED(params); + self->hash_mul_ = kHashMul64 << (64 - 5 * 8); + BROTLI_DCHECK(common->params.bucket_bits == 15); + self->bucket_size_ = (size_t)1 << common->params.bucket_bits; + self->block_bits_ = common->params.block_bits; + self->block_size_ = (size_t)1 << common->params.block_bits; + self->block_mask_ = (uint32_t)(self->block_size_ - 1); + self->num_last_distances_to_check_ = + common->params.num_last_distances_to_check; + self->num_ = (uint16_t*)common->extra[0]; + self->buckets_ = (uint32_t*)common->extra[1]; +} + +static void FN(Prepare)( + HashLongestMatch* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + uint16_t* BROTLI_RESTRICT num = self->num_; + /* Partial preparation is 100 times slower (per socket). */ + size_t partial_prepare_threshold = self->bucket_size_ >> 6; + if (one_shot && input_size <= partial_prepare_threshold) { + size_t i; + for (i = 0; i < input_size; ++i) { + const size_t key = FN(HashBytes)(&data[i], self->hash_mul_); + num[key] = 0; + } + } else { + memset(num, 0, self->bucket_size_ * sizeof(num[0])); + } +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + size_t bucket_size = (size_t)1 << params->hasher.bucket_bits; + size_t block_size = (size_t)1 << params->hasher.block_bits; + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + alloc_size[0] = sizeof(uint16_t) * bucket_size; + alloc_size[1] = sizeof(uint32_t) * bucket_size * block_size; +} + +/* Look at 4 bytes at &data[ix & mask]. + Compute a hash from these, and store the value of ix at that position. */ +static BROTLI_INLINE void FN(Store)( + HashLongestMatch* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, + const size_t mask, const size_t ix) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + const size_t key = FN(HashBytes)(&data[ix & mask], self->hash_mul_); + const size_t minor_ix = num[key] & self->block_mask_; + const size_t offset = minor_ix + (key << self->block_bits_); + ++num[key]; + buckets[offset] = (uint32_t)ix; +} + +static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + size_t i; + for (i = ix_start; i < ix_end; ++i) { + FN(Store)(self, data, mask, i); + } +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashLongestMatch* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ringbuffer_mask) { + if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); + } +} + +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashLongestMatch* BROTLI_RESTRICT self, + int* BROTLI_RESTRICT distance_cache) { + PrepareDistanceCache(distance_cache, self->num_last_distances_to_check_); +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + + REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke FN(PrepareDistanceCache) once. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +static BROTLI_INLINE void FN(FindLongestMatch)( + HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, + const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, + const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + /* Don't accept a short copy from far away. */ + score_t min_score = out->score; + score_t best_score = out->score; + size_t best_len = out->len; + size_t i; + /* Precalculate the hash key and prefetch the bucket. */ + const size_t key = FN(HashBytes)(&data[cur_ix_masked], self->hash_mul_); + uint32_t* BROTLI_RESTRICT bucket = &buckets[key << self->block_bits_]; + PREFETCH_L1(bucket); + if (self->block_bits_ > 4) PREFETCH_L1(bucket + 16); + out->len = 0; + out->len_code_delta = 0; + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + /* Try last distance first. */ + for (i = 0; i < (size_t)self->num_last_distances_to_check_; ++i) { + const size_t backward = (size_t)distance_cache[i]; + size_t prev_ix = (size_t)(cur_ix - backward); + if (prev_ix >= cur_ix) { + continue; + } + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + continue; + } + prev_ix &= ring_buffer_mask; + + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 3 || (len == 2 && i < 2)) { + /* Comparing for >= 2 does not change the semantics, but just saves for + a few unnecessary binary logarithms in backward reference score, + since we are not interested in such short matches. */ + score_t score = BackwardReferenceScoreUsingLastDistance(len); + if (best_score < score) { + if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + } + /* we require matches of len >4, so increase best_len to 3, so we can compare + * 4 bytes all the time. */ + if (best_len < 3) { + best_len = 3; + } + { + const size_t down = + (num[key] > self->block_size_) ? + (num[key] - self->block_size_) : 0u; + const uint32_t first4 = BrotliUnalignedRead32(data + cur_ix_masked); + const size_t max_length_m4 = max_length - 4; + i = num[key]; + for (; i > down;) { + size_t prev_ix = bucket[--i & self->block_mask_]; + uint32_t current4; + const size_t backward = cur_ix - prev_ix; + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + break; + } + prev_ix &= ring_buffer_mask; + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + /* compare 4 bytes ending at best_len + 1 */ + BrotliUnalignedRead32(&data[cur_ix_masked + best_len - 3]) != + BrotliUnalignedRead32(&data[prev_ix + best_len - 3])) { + continue; + } + current4 = BrotliUnalignedRead32(data + prev_ix); + if (first4 != current4) continue; + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix + 4], + &data[cur_ix_masked + 4], + max_length_m4) + 4; + const score_t score = BackwardReferenceScore(len, backward); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix; + ++num[key]; + } + if (min_score == out->score) { + SearchInStaticDictionary(dictionary, + self->common_, &data[cur_ix_masked], max_length, dictionary_distance, + max_distance, out, BROTLI_FALSE); + } +} + +#undef HashLongestMatch diff --git a/packages/runtime/vendor/brotli/c/enc/hash_longest_match64_simd_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_longest_match64_simd_inc.h new file mode 100644 index 000000000..12bd8cffd --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_longest_match64_simd_inc.h @@ -0,0 +1,304 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN */ + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + This is a hash map of fixed size (bucket_size_) to a ring buffer of + fixed size (block_size_). The ring buffer contains the last block_size_ + index positions of the given hash key in the compressed data. */ + +#define HashLongestMatch HASHER() + +#define TAG_HASH_BITS 8 +#define TAG_HASH_MASK ((1 << TAG_HASH_BITS) - 1) + +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 8; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 8; } + +/* HashBytes is the function that chooses the bucket to place the address in. */ +static BROTLI_INLINE size_t FN(HashBytes)(const uint8_t* BROTLI_RESTRICT data, + uint64_t hash_mul) { + const uint64_t h = BROTLI_UNALIGNED_LOAD64LE(data) * hash_mul; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return (size_t)(h >> (64 - 15 - TAG_HASH_BITS)); +} + +typedef struct HashLongestMatch { + /* Number of hash buckets. */ + size_t bucket_size_; + /* Only block_size_ newest backward references are kept, + and the older are forgotten. */ + size_t block_size_; + /* Hash multiplier tuned to match length. */ + uint64_t hash_mul_; + /* Mask for accessing entries in a block (in a ring-buffer manner). */ + uint32_t block_mask_; + + int block_bits_; + int num_last_distances_to_check_; + + /* Shortcuts. */ + HasherCommon* common_; + + /* --- Dynamic size members --- */ + + /* Number of entries in a particular bucket. */ + uint16_t* num_; /* uint16_t[bucket_size]; */ + + uint8_t* tags_; + + /* Buckets containing block_size_ of backward references. */ + uint32_t* buckets_; /* uint32_t[bucket_size * block_size]; */ +} HashLongestMatch; + +static void FN(Initialize)( + HasherCommon* common, HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + self->common_ = common; + + BROTLI_UNUSED(params); + self->hash_mul_ = kHashMul64 << (64 - 5 * 8); + BROTLI_DCHECK(common->params.bucket_bits == 15); + self->bucket_size_ = (size_t)1 << common->params.bucket_bits; + self->block_bits_ = common->params.block_bits; + self->block_size_ = (size_t)1 << common->params.block_bits; + self->block_mask_ = (uint32_t)(self->block_size_ - 1); + self->num_last_distances_to_check_ = + common->params.num_last_distances_to_check; + self->num_ = (uint16_t*)common->extra[0]; + self->tags_ = (uint8_t*)common->extra[1]; + self->buckets_ = (uint32_t*)common->extra[2]; +} + +static void FN(Prepare)( + HashLongestMatch* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + uint16_t* BROTLI_RESTRICT num = self->num_; + /* Partial preparation is 100 times slower (per socket). */ + size_t partial_prepare_threshold = self->bucket_size_ >> 6; + if (one_shot && input_size <= partial_prepare_threshold) { + size_t i; + for (i = 0; i < input_size; ++i) { + const size_t hash = FN(HashBytes)(&data[i], self->hash_mul_); + const size_t key = hash >> TAG_HASH_BITS; + num[key] = 65535; + } + } else { + /* Set all the bytes of num to 255, which makes each uint16_t 65535. */ + memset(num, 255, self->bucket_size_ * sizeof(num[0])); + } +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + size_t bucket_size = (size_t)1 << params->hasher.bucket_bits; + size_t block_size = (size_t)1 << params->hasher.block_bits; + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + alloc_size[0] = sizeof(uint16_t) * bucket_size; + alloc_size[1] = sizeof(uint8_t) * bucket_size * block_size; + alloc_size[2] = sizeof(uint32_t) * bucket_size * block_size; +} + +/* Look at 4 bytes at &data[ix & mask]. + Compute a hash from these, and store the value of ix at that position. */ +static BROTLI_INLINE void FN(Store)( + HashLongestMatch* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, + const size_t mask, const size_t ix) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint8_t* BROTLI_RESTRICT tags = self->tags_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + const size_t hash = FN(HashBytes)(&data[ix & mask], self->hash_mul_); + const size_t key = hash >> TAG_HASH_BITS; + const uint8_t tag = hash & TAG_HASH_MASK; + const size_t minor_ix = num[key] & self->block_mask_; + const size_t offset = minor_ix + (key << self->block_bits_); + --num[key]; + buckets[offset] = (uint32_t)ix; + tags[offset] = tag; +} + +static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + size_t i; + for (i = ix_start; i < ix_end; ++i) { + FN(Store)(self, data, mask, i); + } +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashLongestMatch* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ringbuffer_mask) { + if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); + } +} + +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashLongestMatch* BROTLI_RESTRICT self, + int* BROTLI_RESTRICT distance_cache) { + PrepareDistanceCache(distance_cache, self->num_last_distances_to_check_); +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + + REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke FN(PrepareDistanceCache) once. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +static BROTLI_INLINE void FN(FindLongestMatch)( + HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, + const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, + const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + uint8_t* BROTLI_RESTRICT tags = self->tags_; + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + /* Don't accept a short copy from far away. */ + score_t min_score = out->score; + score_t best_score = out->score; + size_t best_len = out->len; + size_t i; + /* Precalculate the hash key and prefetch the bucket. */ + const size_t hash = FN(HashBytes)(&data[cur_ix_masked], self->hash_mul_); + const size_t key = hash >> TAG_HASH_BITS; + uint32_t* BROTLI_RESTRICT bucket = &buckets[key << self->block_bits_]; + uint8_t* BROTLI_RESTRICT tag_bucket = &tags[key << self->block_bits_]; + PREFETCH_L1(bucket); + PREFETCH_L1(tag_bucket); + if (self->block_bits_ > 4) PREFETCH_L1(bucket + 16); + out->len = 0; + out->len_code_delta = 0; + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + /* Try last distance first. */ + for (i = 0; i < (size_t)self->num_last_distances_to_check_; ++i) { + const size_t backward = (size_t)distance_cache[i]; + size_t prev_ix = (size_t)(cur_ix - backward); + if (prev_ix >= cur_ix) { + continue; + } + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + continue; + } + prev_ix &= ring_buffer_mask; + + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 3 || (len == 2 && i < 2)) { + /* Comparing for >= 2 does not change the semantics, but just saves for + a few unnecessary binary logarithms in backward reference score, + since we are not interested in such short matches. */ + score_t score = BackwardReferenceScoreUsingLastDistance(len); + if (best_score < score) { + if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + } + /* we require matches of len >4, so increase best_len to 3, so we can compare + * 4 bytes all the time. */ + if (best_len < 3) { + best_len = 3; + } + { + const uint8_t tag = hash & TAG_HASH_MASK; + const uint32_t first4 = BrotliUnalignedRead32(data + cur_ix_masked); + const size_t max_length_m4 = max_length - 4; + const size_t head = (num[key] + 1) & self->block_mask_; + uint64_t matches = + GetMatchingTagMask(self->block_size_ / 16, tag, tag_bucket, head); + /* Mask off any matches from uninitialized tags. */ + uint16_t n = 65535 - num[key]; + uint64_t block_has_unused_slots = self->block_size_ > n; + uint64_t mask = (block_has_unused_slots << (n & (64 - 1))) - 1; + matches &= mask; + for (; matches > 0; matches &= (matches - 1)) { + const size_t rb_index = + (head + (size_t)BROTLI_TZCNT64(matches)) & self->block_mask_; + size_t prev_ix = bucket[rb_index]; + uint32_t current4; + const size_t backward = cur_ix - prev_ix; + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + break; + } + prev_ix &= ring_buffer_mask; + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + /* compare 4 bytes ending at best_len + 1 */ + BrotliUnalignedRead32(&data[cur_ix_masked + best_len - 3]) != + BrotliUnalignedRead32(&data[prev_ix + best_len - 3])) { + continue; + } + current4 = BrotliUnalignedRead32(data + prev_ix); + if (first4 != current4) continue; + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix + 4], + &data[cur_ix_masked + 4], + max_length_m4) + 4; + const score_t score = BackwardReferenceScore(len, backward); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix; + tag_bucket[num[key] & self->block_mask_] = tag; + --num[key]; + } + if (min_score == out->score) { + SearchInStaticDictionary(dictionary, + self->common_, &data[cur_ix_masked], max_length, dictionary_distance, + max_distance, out, BROTLI_FALSE); + } +} + +#undef HashLongestMatch diff --git a/packages/runtime/vendor/brotli/c/enc/hash_longest_match_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_longest_match_inc.h new file mode 100644 index 000000000..58e88b3a6 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_longest_match_inc.h @@ -0,0 +1,277 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN */ + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + This is a hash map of fixed size (bucket_size_) to a ring buffer of + fixed size (block_size_). The ring buffer contains the last block_size_ + index positions of the given hash key in the compressed data. */ + +#define HashLongestMatch HASHER() + +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; } + +/* HashBytes is the function that chooses the bucket to place the address in. */ +static uint32_t FN(HashBytes)( + const uint8_t* BROTLI_RESTRICT data, const int shift) { + uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return (uint32_t)(h >> shift); +} + +typedef struct HashLongestMatch { + /* Number of hash buckets. */ + size_t bucket_size_; + /* Only block_size_ newest backward references are kept, + and the older are forgotten. */ + size_t block_size_; + /* Left-shift for computing hash bucket index from hash value. */ + int hash_shift_; + /* Mask for accessing entries in a block (in a ring-buffer manner). */ + uint32_t block_mask_; + + int block_bits_; + int num_last_distances_to_check_; + + /* Shortcuts. */ + HasherCommon* common_; + + /* --- Dynamic size members --- */ + + /* Number of entries in a particular bucket. */ + uint16_t* num_; /* uint16_t[bucket_size]; */ + + /* Buckets containing block_size_ of backward references. */ + uint32_t* buckets_; /* uint32_t[bucket_size * block_size]; */ +} HashLongestMatch; + +static void FN(Initialize)( + HasherCommon* common, HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + self->common_ = common; + + BROTLI_UNUSED(params); + self->hash_shift_ = 32 - common->params.bucket_bits; + self->bucket_size_ = (size_t)1 << common->params.bucket_bits; + self->block_size_ = (size_t)1 << common->params.block_bits; + self->block_mask_ = (uint32_t)(self->block_size_ - 1); + self->num_ = (uint16_t*)common->extra[0]; + self->buckets_ = (uint32_t*)common->extra[1]; + self->block_bits_ = common->params.block_bits; + self->num_last_distances_to_check_ = + common->params.num_last_distances_to_check; +} + +static void FN(Prepare)( + HashLongestMatch* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + uint16_t* BROTLI_RESTRICT num = self->num_; + /* Partial preparation is 100 times slower (per socket). */ + size_t partial_prepare_threshold = self->bucket_size_ >> 6; + if (one_shot && input_size <= partial_prepare_threshold) { + size_t i; + for (i = 0; i < input_size; ++i) { + const uint32_t key = FN(HashBytes)(&data[i], self->hash_shift_); + num[key] = 0; + } + } else { + memset(num, 0, self->bucket_size_ * sizeof(num[0])); + } +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + size_t bucket_size = (size_t)1 << params->hasher.bucket_bits; + size_t block_size = (size_t)1 << params->hasher.block_bits; + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + alloc_size[0] = sizeof(uint16_t) * bucket_size; + alloc_size[1] = sizeof(uint32_t) * bucket_size * block_size; +} + +/* Look at 4 bytes at &data[ix & mask]. + Compute a hash from these, and store the value of ix at that position. */ +static BROTLI_INLINE void FN(Store)( + HashLongestMatch* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, + const size_t mask, const size_t ix) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + const uint32_t key = FN(HashBytes)(&data[ix & mask], self->hash_shift_); + const size_t minor_ix = num[key] & self->block_mask_; + const size_t offset = minor_ix + (key << self->block_bits_); + ++num[key]; + buckets[offset] = (uint32_t)ix; +} + +static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + size_t i; + for (i = ix_start; i < ix_end; ++i) { + FN(Store)(self, data, mask, i); + } +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashLongestMatch* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ringbuffer_mask) { + if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); + } +} + +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashLongestMatch* BROTLI_RESTRICT self, + int* BROTLI_RESTRICT distance_cache) { + PrepareDistanceCache(distance_cache, self->num_last_distances_to_check_); +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + + REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke FN(PrepareDistanceCache) once. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +static BROTLI_INLINE void FN(FindLongestMatch)( + HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, + const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, + const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + /* Don't accept a short copy from far away. */ + score_t min_score = out->score; + score_t best_score = out->score; + size_t best_len = out->len; + size_t i; + /* Precalculate the hash key and prefetch the bucket. */ + const uint32_t key = + FN(HashBytes)(&data[cur_ix_masked], self->hash_shift_); + uint32_t* BROTLI_RESTRICT bucket = &buckets[key << self->block_bits_]; + PREFETCH_L1(bucket); + if (self->block_bits_ > 4) PREFETCH_L1(bucket + 16); + out->len = 0; + out->len_code_delta = 0; + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + /* Try last distance first. */ + for (i = 0; i < (size_t)self->num_last_distances_to_check_; ++i) { + const size_t backward = (size_t)distance_cache[i]; + size_t prev_ix = (size_t)(cur_ix - backward); + if (prev_ix >= cur_ix) { + continue; + } + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + continue; + } + prev_ix &= ring_buffer_mask; + + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 3 || (len == 2 && i < 2)) { + /* Comparing for >= 2 does not change the semantics, but just saves for + a few unnecessary binary logarithms in backward reference score, + since we are not interested in such short matches. */ + score_t score = BackwardReferenceScoreUsingLastDistance(len); + if (best_score < score) { + if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + } + /* we require matches of len >4, so increase best_len to 3, so we can compare + * 4 bytes all the time. */ + if (best_len < 3) { + best_len = 3; + } + { + const size_t down = + (num[key] > self->block_size_) ? (num[key] - self->block_size_) : 0u; + for (i = num[key]; i > down;) { + size_t prev_ix = bucket[--i & self->block_mask_]; + const size_t backward = cur_ix - prev_ix; + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + break; + } + prev_ix &= ring_buffer_mask; + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + /* compare 4 bytes ending at best_len + 1 */ + BrotliUnalignedRead32(&data[cur_ix_masked + best_len - 3]) != + BrotliUnalignedRead32(&data[prev_ix + best_len - 3])) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 4) { + /* Comparing for >= 3 does not change the semantics, but just saves + for a few unnecessary binary logarithms in backward reference + score, since we are not interested in such short matches. */ + score_t score = BackwardReferenceScore(len, backward); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix; + ++num[key]; + } + if (min_score == out->score) { + SearchInStaticDictionary(dictionary, + self->common_, &data[cur_ix_masked], max_length, dictionary_distance, + max_distance, out, BROTLI_FALSE); + } +} + +#undef HashLongestMatch diff --git a/packages/runtime/vendor/brotli/c/enc/hash_longest_match_quickly_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_longest_match_quickly_inc.h new file mode 100644 index 000000000..57ed586fe --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_longest_match_quickly_inc.h @@ -0,0 +1,270 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN, BUCKET_BITS, BUCKET_SWEEP_BITS, HASH_LEN, + USE_DICTIONARY + */ + +#define HashLongestMatchQuickly HASHER() + +#define BUCKET_SIZE (1 << BUCKET_BITS) +#define BUCKET_MASK (BUCKET_SIZE - 1) +#define BUCKET_SWEEP (1 << BUCKET_SWEEP_BITS) +#define BUCKET_SWEEP_MASK ((BUCKET_SWEEP - 1) << 3) + +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 8; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 8; } + +/* HashBytes is the function that chooses the bucket to place + the address in. The HashLongestMatch and HashLongestMatchQuickly + classes have separate, different implementations of hashing. */ +static uint32_t FN(HashBytes)(const uint8_t* data) { + const uint64_t h = ((BROTLI_UNALIGNED_LOAD64LE(data) << (64 - 8 * HASH_LEN)) * + kHashMul64); + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return (uint32_t)(h >> (64 - BUCKET_BITS)); +} + +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + + This is a hash map of fixed size (BUCKET_SIZE). */ +typedef struct HashLongestMatchQuickly { + /* Shortcuts. */ + HasherCommon* common; + + /* --- Dynamic size members --- */ + + uint32_t* buckets_; /* uint32_t[BUCKET_SIZE]; */ +} HashLongestMatchQuickly; + +static void FN(Initialize)( + HasherCommon* common, HashLongestMatchQuickly* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + self->common = common; + + BROTLI_UNUSED(params); + self->buckets_ = (uint32_t*)common->extra[0]; +} + +static void FN(Prepare)( + HashLongestMatchQuickly* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + /* Partial preparation is 100 times slower (per socket). */ + size_t partial_prepare_threshold = BUCKET_SIZE >> 5; + if (one_shot && input_size <= partial_prepare_threshold) { + size_t i; + for (i = 0; i < input_size; ++i) { + const uint32_t key = FN(HashBytes)(&data[i]); + if (BUCKET_SWEEP == 1) { + buckets[key] = 0; + } else { + uint32_t j; + for (j = 0; j < BUCKET_SWEEP; ++j) { + buckets[(key + (j << 3)) & BUCKET_MASK] = 0; + } + } + } + } else { + /* It is not strictly necessary to fill this buffer here, but + not filling will make the results of the compression stochastic + (but correct). This is because random data would cause the + system to find accidentally good backward references here and there. */ + memset(buckets, 0, sizeof(uint32_t) * BUCKET_SIZE); + } +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + BROTLI_UNUSED(params); + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE; +} + +/* Look at 5 bytes at &data[ix & mask]. + Compute a hash from these, and store the value somewhere within + [ix .. ix+3]. */ +static BROTLI_INLINE void FN(Store)( + HashLongestMatchQuickly* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { + const uint32_t key = FN(HashBytes)(&data[ix & mask]); + if (BUCKET_SWEEP == 1) { + self->buckets_[key] = (uint32_t)ix; + } else { + /* Wiggle the value with the bucket sweep range. */ + const uint32_t off = ix & BUCKET_SWEEP_MASK; + self->buckets_[(key + off) & BUCKET_MASK] = (uint32_t)ix; + } +} + +static BROTLI_INLINE void FN(StoreRange)( + HashLongestMatchQuickly* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + size_t i; + for (i = ix_start; i < ix_end; ++i) { + FN(Store)(self, data, mask, i); + } +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashLongestMatchQuickly* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, + const uint8_t* ringbuffer, size_t ringbuffer_mask) { + if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); + } +} + +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashLongestMatchQuickly* BROTLI_RESTRICT self, + int* BROTLI_RESTRICT distance_cache) { + BROTLI_UNUSED(self); + BROTLI_UNUSED(distance_cache); +} + +/* Find a longest backward match of &data[cur_ix & ring_buffer_mask] + up to the length of max_length and stores the position cur_ix in the + hash table. + + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +static BROTLI_INLINE void FN(FindLongestMatch)( + HashLongestMatchQuickly* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, + const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache, + const size_t cur_ix, const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + const size_t best_len_in = out->len; + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + /* TODO: compare 4 bytes at once (and set the minimum best len to 4) */ + int compare_char = data[cur_ix_masked + best_len_in]; + size_t key = FN(HashBytes)(&data[cur_ix_masked]); + size_t key_out; + score_t min_score = out->score; + score_t best_score = out->score; + size_t best_len = best_len_in; + size_t cached_backward = (size_t)distance_cache[0]; + size_t prev_ix = cur_ix - cached_backward; + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + out->len_code_delta = 0; + if (prev_ix < cur_ix) { + prev_ix &= (uint32_t)ring_buffer_mask; + if (compare_char == data[prev_ix + best_len]) { + const size_t len = FindMatchLengthWithLimit( + &data[prev_ix], &data[cur_ix_masked], max_length); + if (len >= 4) { + const score_t score = BackwardReferenceScoreUsingLastDistance(len); + if (best_score < score) { + out->len = len; + out->distance = cached_backward; + out->score = score; + if (BUCKET_SWEEP == 1) { + buckets[key] = (uint32_t)cur_ix; + return; + } else { + best_len = len; + best_score = score; + compare_char = data[cur_ix_masked + len]; + } + } + } + } + } + if (BUCKET_SWEEP == 1) { + size_t backward; + size_t len; + /* Only one to look for, don't bother to prepare for a loop. */ + prev_ix = buckets[key]; + buckets[key] = (uint32_t)cur_ix; + backward = cur_ix - prev_ix; + prev_ix &= (uint32_t)ring_buffer_mask; + if (compare_char != data[prev_ix + best_len_in]) { + return; + } + if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) { + return; + } + len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 4) { + const score_t score = BackwardReferenceScore(len, backward); + if (best_score < score) { + out->len = len; + out->distance = backward; + out->score = score; + return; + } + } + } else { + size_t keys[BUCKET_SWEEP]; + size_t i; + for (i = 0; i < BUCKET_SWEEP; ++i) { + keys[i] = (key + (i << 3)) & BUCKET_MASK; + } + key_out = keys[(cur_ix & BUCKET_SWEEP_MASK) >> 3]; + for (i = 0; i < BUCKET_SWEEP; ++i) { + size_t len; + size_t backward; + prev_ix = buckets[keys[i]]; + backward = cur_ix - prev_ix; + prev_ix &= (uint32_t)ring_buffer_mask; + if (compare_char != data[prev_ix + best_len]) { + continue; + } + if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) { + continue; + } + len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 4) { + const score_t score = BackwardReferenceScore(len, backward); + if (best_score < score) { + best_len = len; + out->len = len; + compare_char = data[cur_ix_masked + len]; + best_score = score; + out->score = score; + out->distance = backward; + } + } + } + } + if (USE_DICTIONARY && min_score == out->score) { + SearchInStaticDictionary(dictionary, + self->common, &data[cur_ix_masked], max_length, dictionary_distance, + max_distance, out, BROTLI_TRUE); + } + if (BUCKET_SWEEP != 1) { + buckets[key_out] = (uint32_t)cur_ix; + } +} + +#undef BUCKET_SWEEP_MASK +#undef BUCKET_SWEEP +#undef BUCKET_MASK +#undef BUCKET_SIZE + +#undef HashLongestMatchQuickly diff --git a/packages/runtime/vendor/brotli/c/enc/hash_longest_match_simd_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_longest_match_simd_inc.h new file mode 100644 index 000000000..79b341ae5 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_longest_match_simd_inc.h @@ -0,0 +1,278 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2010 Google Inc. All Rights Reserved. + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ +/* template parameters: FN */ +/* A (forgetful) hash table to the data seen by the compressor, to + help create backward references to previous data. + This is a hash map of fixed size (bucket_size_) to a ring buffer of + fixed size (block_size_). The ring buffer contains the last block_size_ + index positions of the given hash key in the compressed data. */ +#define HashLongestMatch HASHER() +#define TAG_HASH_BITS 8 +#define TAG_HASH_MASK ((1 << TAG_HASH_BITS) - 1) +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; } +/* HashBytes is the function that chooses the bucket to place the address in. */ +static uint32_t FN(HashBytes)( + const uint8_t* BROTLI_RESTRICT data, const int shift) { + uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return (uint32_t)(h >> shift); +} +typedef struct HashLongestMatch { + /* Number of hash buckets. */ + size_t bucket_size_; + /* Only block_size_ newest backward references are kept, + and the older are forgotten. */ + size_t block_size_; + /* Left-shift for computing hash bucket index from hash value. */ + int hash_shift_; + /* Mask for accessing entries in a block (in a ring-buffer manner). */ + uint32_t block_mask_; + int block_bits_; + int num_last_distances_to_check_; + /* Shortcuts. */ + HasherCommon* common_; + /* --- Dynamic size members --- */ + /* Number of entries in a particular bucket. */ + uint16_t* num_; /* uint16_t[bucket_size]; */ + uint8_t* tags_; + /* Buckets containing block_size_ of backward references. */ + uint32_t* buckets_; /* uint32_t[bucket_size * block_size]; */ +} HashLongestMatch; +static void FN(Initialize)( + HasherCommon* common, HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + self->common_ = common; + BROTLI_UNUSED(params); + self->hash_shift_ = 32 - common->params.bucket_bits - TAG_HASH_BITS; + self->bucket_size_ = (size_t)1 << common->params.bucket_bits; + self->block_size_ = (size_t)1 << common->params.block_bits; + self->block_mask_ = (uint32_t)(self->block_size_ - 1); + self->num_ = (uint16_t*)common->extra[0]; + self->tags_ = (uint8_t*)common->extra[1]; + self->buckets_ = (uint32_t*)common->extra[2]; + self->block_bits_ = common->params.block_bits; + self->num_last_distances_to_check_ = + common->params.num_last_distances_to_check; +} +static void FN(Prepare)( + HashLongestMatch* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + uint16_t* BROTLI_RESTRICT num = self->num_; + /* Partial preparation is 100 times slower (per socket). */ + size_t partial_prepare_threshold = self->bucket_size_ >> 6; + if (one_shot && input_size <= partial_prepare_threshold) { + size_t i; + for (i = 0; i < input_size; ++i) { + const uint32_t hash = FN(HashBytes)(&data[i], self->hash_shift_); + const uint32_t key = hash >> TAG_HASH_BITS; + num[key] = 65535; + } + } else { + /* Set all the bytes of num to 255, which makes each uint16_t 65535. */ + memset(num, 255, self->bucket_size_ * sizeof(num[0])); + } +} +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + size_t bucket_size = (size_t)1 << params->hasher.bucket_bits; + size_t block_size = (size_t)1 << params->hasher.block_bits; + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + alloc_size[0] = sizeof(uint16_t) * bucket_size; + alloc_size[1] = sizeof(uint8_t) * bucket_size * block_size; + alloc_size[2] = sizeof(uint32_t) * bucket_size * block_size; +} +/* Look at 4 bytes at &data[ix & mask]. + Compute a hash from these, and store the value of ix at that position. */ +static BROTLI_INLINE void FN(Store)( + HashLongestMatch* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, + const size_t mask, const size_t ix) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint8_t* BROTLI_RESTRICT tags = self->tags_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + const size_t hash = FN(HashBytes)(&data[ix & mask], self->hash_shift_); + const size_t key = hash >> TAG_HASH_BITS; + const uint8_t tag = hash & TAG_HASH_MASK; + const size_t minor_ix = num[key] & self->block_mask_; + const size_t offset = minor_ix + (key << self->block_bits_); + --num[key]; + buckets[offset] = (uint32_t)ix; + tags[offset] = tag; +} +static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + size_t i; + for (i = ix_start; i < ix_end; ++i) { + FN(Store)(self, data, mask, i); + } +} +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashLongestMatch* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ringbuffer_mask) { + if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) { + /* Prepare the hashes for three last bytes of the last write. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2); + FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1); + } +} +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashLongestMatch* BROTLI_RESTRICT self, + int* BROTLI_RESTRICT distance_cache) { + PrepareDistanceCache(distance_cache, self->num_last_distances_to_check_); +} + +/* Find a longest backward match of &data[cur_ix] up to the length of + max_length and stores the position cur_ix in the hash table. + REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache + values; if this method is invoked repeatedly with the same distance + cache values, it is enough to invoke FN(PrepareDistanceCache) once. + Does not look for matches longer than max_length. + Does not look for matches further away than max_backward. + Writes the best match into |out|. + |out|->score is updated only if a better match is found. */ +static BROTLI_INLINE void FN(FindLongestMatch)( + HashLongestMatch* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, + const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, + const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + uint16_t* BROTLI_RESTRICT num = self->num_; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + uint8_t* BROTLI_RESTRICT tags = self->tags_; + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + /* Don't accept a short copy from far away. */ + score_t min_score = out->score; + score_t best_score = out->score; + size_t best_len = out->len; + size_t i; + /* Precalculate the hash key and prefetch the bucket. */ + const uint32_t hash = + FN(HashBytes)(&data[cur_ix_masked], self->hash_shift_); + const uint32_t key = hash >> TAG_HASH_BITS; + uint32_t* BROTLI_RESTRICT bucket = &buckets[key << self->block_bits_]; + uint8_t* BROTLI_RESTRICT tag_bucket = &tags[key << self->block_bits_]; + PREFETCH_L1(bucket); + PREFETCH_L1(tag_bucket); + if (self->block_bits_ > 4) PREFETCH_L1(bucket + 16); + out->len = 0; + out->len_code_delta = 0; + + BROTLI_DCHECK(cur_ix_masked + max_length <= ring_buffer_mask); + + /* Try last distance first. */ + for (i = 0; i < (size_t)self->num_last_distances_to_check_; ++i) { + const size_t backward = (size_t)distance_cache[i]; + size_t prev_ix = (size_t)(cur_ix - backward); + if (prev_ix >= cur_ix) { + continue; + } + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + continue; + } + prev_ix &= ring_buffer_mask; + + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 3 || (len == 2 && i < 2)) { + /* Comparing for >= 2 does not change the semantics, but just saves for + a few unnecessary binary logarithms in backward reference score, + since we are not interested in such short matches. */ + score_t score = BackwardReferenceScoreUsingLastDistance(len); + if (best_score < score) { + if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + } + /* we require matches of len >4, so increase best_len to 3, so we can compare + * 4 bytes all the time. */ + if (best_len < 3) { + best_len = 3; + } + { + const uint8_t tag = hash & TAG_HASH_MASK; + const size_t head = (num[key] + 1) & self->block_mask_; + uint64_t matches = + GetMatchingTagMask(self->block_size_ / 16, tag, tag_bucket, head); + /* Mask off any matches from uninitialized tags. */ + uint16_t n = 65535 - num[key]; + uint64_t block_has_unused_slots = self->block_size_ > n; + uint64_t mask = (block_has_unused_slots << (n & (64 - 1))) - 1; + matches &= mask; + for (; matches > 0; matches &= (matches - 1)) { + const size_t rb_index = + (head + (size_t)BROTLI_TZCNT64(matches)) & self->block_mask_; + size_t prev_ix = bucket[rb_index]; + const size_t backward = cur_ix - prev_ix; + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + break; + } + prev_ix &= ring_buffer_mask; + if (cur_ix_masked + best_len > ring_buffer_mask) { + break; + } + if (prev_ix + best_len > ring_buffer_mask || + /* compare 4 bytes ending at best_len + 1 */ + BrotliUnalignedRead32(&data[cur_ix_masked + best_len - 3]) != + BrotliUnalignedRead32(&data[prev_ix + best_len - 3])) { + continue; + } + { + const size_t len = FindMatchLengthWithLimit(&data[prev_ix], + &data[cur_ix_masked], + max_length); + if (len >= 4) { + /* Comparing for >= 3 does not change the semantics, but just saves + for a few unnecessary binary logarithms in backward reference + score, since we are not interested in such short matches. */ + score_t score = BackwardReferenceScore(len, backward); + if (best_score < score) { + best_score = score; + best_len = len; + out->len = best_len; + out->distance = backward; + out->score = best_score; + } + } + } + } + bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix; + tag_bucket[num[key] & self->block_mask_] = tag; + --num[key]; + } + if (min_score == out->score) { + SearchInStaticDictionary(dictionary, + self->common_, &data[cur_ix_masked], max_length, dictionary_distance, + max_distance, out, BROTLI_FALSE); + } +} +#undef HashLongestMatch diff --git a/packages/runtime/vendor/brotli/c/enc/hash_rolling_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_rolling_inc.h new file mode 100644 index 000000000..4c7a6b199 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_rolling_inc.h @@ -0,0 +1,212 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2018 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN, JUMP, NUMBUCKETS, MASK, CHUNKLEN */ +/* NUMBUCKETS / (MASK + 1) = probability of storing and using hash code. */ +/* JUMP = skip bytes for speedup */ + +/* Rolling hash for long distance long string matches. Stores one position + per bucket, bucket key is computed over a long region. */ + +#define HashRolling HASHER() + +static const uint32_t FN(kRollingHashMul32) = 69069; +static const uint32_t FN(kInvalidPos) = 0xffffffff; + +/* This hasher uses a longer forward length, but returning a higher value here + will hurt compression by the main hasher when combined with a composite + hasher. The hasher tests for forward itself instead. */ +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; } + +/* Computes a code from a single byte. A lookup table of 256 values could be + used, but simply adding 1 works about as good. */ +static uint32_t FN(HashByte)(uint8_t byte) { + return (uint32_t)byte + 1u; +} + +static uint32_t FN(HashRollingFunctionInitial)(uint32_t state, uint8_t add, + uint32_t factor) { + return (uint32_t)(factor * state + FN(HashByte)(add)); +} + +static uint32_t FN(HashRollingFunction)(uint32_t state, uint8_t add, + uint8_t rem, uint32_t factor, + uint32_t factor_remove) { + return (uint32_t)(factor * state + + FN(HashByte)(add) - factor_remove * FN(HashByte)(rem)); +} + +typedef struct HashRolling { + uint32_t state; + uint32_t* table; + size_t next_ix; + + uint32_t chunk_len; + uint32_t factor; + uint32_t factor_remove; +} HashRolling; + +static void FN(Initialize)( + HasherCommon* common, HashRolling* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + size_t i; + self->state = 0; + self->next_ix = 0; + + self->factor = FN(kRollingHashMul32); + + /* Compute the factor of the oldest byte to remove: factor**steps modulo + 0xffffffff (the multiplications rely on 32-bit overflow) */ + self->factor_remove = 1; + for (i = 0; i < CHUNKLEN; i += JUMP) { + self->factor_remove *= self->factor; + } + + self->table = (uint32_t*)common->extra[0]; + for (i = 0; i < NUMBUCKETS; i++) { + self->table[i] = FN(kInvalidPos); + } + + BROTLI_UNUSED(params); +} + +static void FN(Prepare)(HashRolling* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + size_t i; + /* Too small size, cannot use this hasher. */ + if (input_size < CHUNKLEN) return; + self->state = 0; + for (i = 0; i < CHUNKLEN; i += JUMP) { + self->state = FN(HashRollingFunctionInitial)( + self->state, data[i], self->factor); + } + BROTLI_UNUSED(one_shot); +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + BROTLI_UNUSED(params); + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + alloc_size[0] = NUMBUCKETS * sizeof(uint32_t); +} + +static BROTLI_INLINE void FN(Store)(HashRolling* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) { + BROTLI_UNUSED(self); + BROTLI_UNUSED(data); + BROTLI_UNUSED(mask); + BROTLI_UNUSED(ix); +} + +static BROTLI_INLINE void FN(StoreRange)(HashRolling* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + BROTLI_UNUSED(self); + BROTLI_UNUSED(data); + BROTLI_UNUSED(mask); + BROTLI_UNUSED(ix_start); + BROTLI_UNUSED(ix_end); +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashRolling* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ring_buffer_mask) { + /* In this case we must re-initialize the hasher from scratch from the + current position. */ + size_t position_masked; + size_t available = num_bytes; + if ((position & (JUMP - 1)) != 0) { + size_t diff = JUMP - (position & (JUMP - 1)); + available = (diff > available) ? 0 : (available - diff); + position += diff; + } + position_masked = position & ring_buffer_mask; + /* wrapping around ringbuffer not handled. */ + if (available > ring_buffer_mask - position_masked) { + available = ring_buffer_mask - position_masked; + } + + FN(Prepare)(self, BROTLI_FALSE, available, + ringbuffer + (position & ring_buffer_mask)); + self->next_ix = position; + BROTLI_UNUSED(num_bytes); +} + +static BROTLI_INLINE void FN(PrepareDistanceCache)( + HashRolling* BROTLI_RESTRICT self, + int* BROTLI_RESTRICT distance_cache) { + BROTLI_UNUSED(self); + BROTLI_UNUSED(distance_cache); +} + +static BROTLI_INLINE void FN(FindLongestMatch)( + HashRolling* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask, + const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix, + const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const size_t max_distance, + HasherSearchResult* BROTLI_RESTRICT out) { + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + size_t pos; + + if ((cur_ix & (JUMP - 1)) != 0) return; + + /* Not enough lookahead */ + if (max_length < CHUNKLEN) return; + + for (pos = self->next_ix; pos <= cur_ix; pos += JUMP) { + uint32_t code = self->state & MASK; + + uint8_t rem = data[pos & ring_buffer_mask]; + uint8_t add = data[(pos + CHUNKLEN) & ring_buffer_mask]; + size_t found_ix = FN(kInvalidPos); + + self->state = FN(HashRollingFunction)( + self->state, add, rem, self->factor, self->factor_remove); + + if (code < NUMBUCKETS) { + found_ix = self->table[code]; + self->table[code] = (uint32_t)pos; + if (pos == cur_ix && found_ix != FN(kInvalidPos)) { + /* The cast to 32-bit makes backward distances up to 4GB work even + if cur_ix is above 4GB, despite using 32-bit values in the table. */ + size_t backward = (uint32_t)(cur_ix - found_ix); + if (backward <= max_backward) { + const size_t found_ix_masked = found_ix & ring_buffer_mask; + const size_t len = FindMatchLengthWithLimit(&data[found_ix_masked], + &data[cur_ix_masked], + max_length); + if (len >= 4 && len > out->len) { + score_t score = BackwardReferenceScore(len, backward); + if (score > out->score) { + out->len = len; + out->distance = backward; + out->score = score; + out->len_code_delta = 0; + } + } + } + } + } + } + + self->next_ix = cur_ix + JUMP; + + /* NOTE: this hasher does not search in the dictionary. It is used as + backup-hasher, the main hasher already searches in it. */ + BROTLI_UNUSED(dictionary); + BROTLI_UNUSED(distance_cache); + BROTLI_UNUSED(dictionary_distance); + BROTLI_UNUSED(max_distance); +} + +#undef HashRolling diff --git a/packages/runtime/vendor/brotli/c/enc/hash_to_binary_tree_inc.h b/packages/runtime/vendor/brotli/c/enc/hash_to_binary_tree_inc.h new file mode 100644 index 000000000..a639d2d4b --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/hash_to_binary_tree_inc.h @@ -0,0 +1,330 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN, BUCKET_BITS, MAX_TREE_COMP_LENGTH, + MAX_TREE_SEARCH_DEPTH */ + +/* A (forgetful) hash table where each hash bucket contains a binary tree of + sequences whose first 4 bytes share the same hash code. + Each sequence is MAX_TREE_COMP_LENGTH long and is identified by its starting + position in the input data. The binary tree is sorted by the lexicographic + order of the sequences, and it is also a max-heap with respect to the + starting positions. */ + +#define HashToBinaryTree HASHER() + +#define BUCKET_SIZE (1 << BUCKET_BITS) + +static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; } +static BROTLI_INLINE size_t FN(StoreLookahead)(void) { + return MAX_TREE_COMP_LENGTH; +} + +static uint32_t FN(HashBytes)(const uint8_t* BROTLI_RESTRICT data) { + uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32; + /* The higher bits contain more mixture from the multiplication, + so we take our results from there. */ + return h >> (32 - BUCKET_BITS); +} + +typedef struct HashToBinaryTree { + /* The window size minus 1 */ + size_t window_mask_; + + /* Hash table that maps the 4-byte hashes of the sequence to the last + position where this hash was found, which is the root of the binary + tree of sequences that share this hash bucket. */ + uint32_t* buckets_; /* uint32_t[BUCKET_SIZE]; */ + + /* A position used to mark a non-existent sequence, i.e. a tree is empty if + its root is at invalid_pos_ and a node is a leaf if both its children + are at invalid_pos_. */ + uint32_t invalid_pos_; + + /* --- Dynamic size members --- */ + + /* The union of the binary trees of each hash bucket. The root of the tree + corresponding to a hash is a sequence starting at buckets_[hash] and + the left and right children of a sequence starting at pos are + forest_[2 * pos] and forest_[2 * pos + 1]. */ + uint32_t* forest_; /* uint32_t[2 * num_nodes] */ +} HashToBinaryTree; + +static void FN(Initialize)( + HasherCommon* common, HashToBinaryTree* BROTLI_RESTRICT self, + const BrotliEncoderParams* params) { + self->buckets_ = (uint32_t*)common->extra[0]; + self->forest_ = (uint32_t*)common->extra[1]; + + self->window_mask_ = (1u << params->lgwin) - 1u; + self->invalid_pos_ = (uint32_t)(0 - self->window_mask_); +} + +static void FN(Prepare) + (HashToBinaryTree* BROTLI_RESTRICT self, BROTLI_BOOL one_shot, + size_t input_size, const uint8_t* BROTLI_RESTRICT data) { + uint32_t invalid_pos = self->invalid_pos_; + uint32_t i; + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + BROTLI_UNUSED(data); + BROTLI_UNUSED(one_shot); + BROTLI_UNUSED(input_size); + for (i = 0; i < BUCKET_SIZE; i++) { + buckets[i] = invalid_pos; + } +} + +static BROTLI_INLINE void FN(HashMemAllocInBytes)( + const BrotliEncoderParams* params, BROTLI_BOOL one_shot, + size_t input_size, size_t* alloc_size) { + size_t num_nodes = (size_t)1 << params->lgwin; + if (one_shot && input_size < num_nodes) { + num_nodes = input_size; + } + alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE; + alloc_size[1] = 2 * sizeof(uint32_t) * num_nodes; +} + +static BROTLI_INLINE size_t FN(LeftChildIndex)( + HashToBinaryTree* BROTLI_RESTRICT self, + const size_t pos) { + return 2 * (pos & self->window_mask_); +} + +static BROTLI_INLINE size_t FN(RightChildIndex)( + HashToBinaryTree* BROTLI_RESTRICT self, + const size_t pos) { + return 2 * (pos & self->window_mask_) + 1; +} + +/* Stores the hash of the next 4 bytes and in a single tree-traversal, the + hash bucket's binary tree is searched for matches and is re-rooted at the + current position. + + If less than MAX_TREE_COMP_LENGTH data is available, the hash bucket of the + current position is searched for matches, but the state of the hash table + is not changed, since we can not know the final sorting order of the + current (incomplete) sequence. + + This function must be called with increasing cur_ix positions. */ +static BROTLI_INLINE BackwardMatch* FN(StoreAndFindMatches)( + HashToBinaryTree* BROTLI_RESTRICT self, const uint8_t* BROTLI_RESTRICT data, + const size_t cur_ix, const size_t ring_buffer_mask, const size_t max_length, + const size_t max_backward, size_t* const BROTLI_RESTRICT best_len, + BackwardMatch* BROTLI_RESTRICT matches) { + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + const size_t max_comp_len = + BROTLI_MIN(size_t, max_length, MAX_TREE_COMP_LENGTH); + const BROTLI_BOOL should_reroot_tree = + TO_BROTLI_BOOL(max_length >= MAX_TREE_COMP_LENGTH); + const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]); + uint32_t* BROTLI_RESTRICT buckets = self->buckets_; + uint32_t* BROTLI_RESTRICT forest = self->forest_; + size_t prev_ix = buckets[key]; + /* The forest index of the rightmost node of the left subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + size_t node_left = FN(LeftChildIndex)(self, cur_ix); + /* The forest index of the leftmost node of the right subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + size_t node_right = FN(RightChildIndex)(self, cur_ix); + /* The match length of the rightmost node of the left subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + size_t best_len_left = 0; + /* The match length of the leftmost node of the right subtree of the new + root, updated as we traverse and re-root the tree of the hash bucket. */ + size_t best_len_right = 0; + size_t depth_remaining; + if (should_reroot_tree) { + buckets[key] = (uint32_t)cur_ix; + } + for (depth_remaining = MAX_TREE_SEARCH_DEPTH; ; --depth_remaining) { + const size_t backward = cur_ix - prev_ix; + const size_t prev_ix_masked = prev_ix & ring_buffer_mask; + if (backward == 0 || backward > max_backward || depth_remaining == 0) { + if (should_reroot_tree) { + forest[node_left] = self->invalid_pos_; + forest[node_right] = self->invalid_pos_; + } + break; + } + { + const size_t cur_len = BROTLI_MIN(size_t, best_len_left, best_len_right); + size_t len; + BROTLI_DCHECK(cur_len <= MAX_TREE_COMP_LENGTH); + len = cur_len + + FindMatchLengthWithLimit(&data[cur_ix_masked + cur_len], + &data[prev_ix_masked + cur_len], + max_length - cur_len); + BROTLI_DCHECK( + 0 == memcmp(&data[cur_ix_masked], &data[prev_ix_masked], len)); + if (matches && len > *best_len) { + *best_len = len; + InitBackwardMatch(matches++, backward, len); + } + if (len >= max_comp_len) { + if (should_reroot_tree) { + forest[node_left] = forest[FN(LeftChildIndex)(self, prev_ix)]; + forest[node_right] = forest[FN(RightChildIndex)(self, prev_ix)]; + } + break; + } + if (data[cur_ix_masked + len] > data[prev_ix_masked + len]) { + best_len_left = len; + if (should_reroot_tree) { + forest[node_left] = (uint32_t)prev_ix; + } + node_left = FN(RightChildIndex)(self, prev_ix); + prev_ix = forest[node_left]; + } else { + best_len_right = len; + if (should_reroot_tree) { + forest[node_right] = (uint32_t)prev_ix; + } + node_right = FN(LeftChildIndex)(self, prev_ix); + prev_ix = forest[node_right]; + } + } + } + return matches; +} + +/* Finds all backward matches of &data[cur_ix & ring_buffer_mask] up to the + length of max_length and stores the position cur_ix in the hash table. + + Sets *num_matches to the number of matches found, and stores the found + matches in matches[0] to matches[*num_matches - 1]. The matches will be + sorted by strictly increasing length and (non-strictly) increasing + distance. */ +static BROTLI_INLINE size_t FN(FindAllMatches)( + HashToBinaryTree* BROTLI_RESTRICT self, + const BrotliEncoderDictionary* dictionary, + const uint8_t* BROTLI_RESTRICT data, + const size_t ring_buffer_mask, const size_t cur_ix, + const size_t max_length, const size_t max_backward, + const size_t dictionary_distance, const BrotliEncoderParams* params, + BackwardMatch* matches) { + BackwardMatch* const orig_matches = matches; + const size_t cur_ix_masked = cur_ix & ring_buffer_mask; + size_t best_len = 1; + const size_t short_match_max_backward = + params->quality != HQ_ZOPFLIFICATION_QUALITY ? 16 : 64; + size_t stop = cur_ix - short_match_max_backward; + uint32_t dict_matches[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1]; + size_t i; + if (cur_ix < short_match_max_backward) { stop = 0; } + for (i = cur_ix - 1; i > stop && best_len <= 2; --i) { + size_t prev_ix = i; + const size_t backward = cur_ix - prev_ix; + if (BROTLI_PREDICT_FALSE(backward > max_backward)) { + break; + } + prev_ix &= ring_buffer_mask; + if (data[cur_ix_masked] != data[prev_ix] || + data[cur_ix_masked + 1] != data[prev_ix + 1]) { + continue; + } + { + const size_t len = + FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked], + max_length); + if (len > best_len) { + best_len = len; + InitBackwardMatch(matches++, backward, len); + } + } + } + if (best_len < max_length) { + matches = FN(StoreAndFindMatches)(self, data, cur_ix, + ring_buffer_mask, max_length, max_backward, &best_len, matches); + } + for (i = 0; i <= BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN; ++i) { + dict_matches[i] = kInvalidMatch; + } + { + size_t minlen = BROTLI_MAX(size_t, 4, best_len + 1); + if (BrotliFindAllStaticDictionaryMatches(dictionary, + &data[cur_ix_masked], minlen, max_length, &dict_matches[0])) { + size_t maxlen = BROTLI_MIN( + size_t, BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN, max_length); + size_t l; + for (l = minlen; l <= maxlen; ++l) { + uint32_t dict_id = dict_matches[l]; + if (dict_id < kInvalidMatch) { + size_t distance = dictionary_distance + (dict_id >> 5) + 1; + if (distance <= params->dist.max_distance) { + InitDictionaryBackwardMatch(matches++, distance, l, dict_id & 31); + } + } + } + } + } + return (size_t)(matches - orig_matches); +} + +/* Stores the hash of the next 4 bytes and re-roots the binary tree at the + current sequence, without returning any matches. + REQUIRES: ix + MAX_TREE_COMP_LENGTH <= end-of-current-block */ +static BROTLI_INLINE void FN(Store)(HashToBinaryTree* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, + const size_t mask, const size_t ix) { + /* Maximum distance is window size - 16, see section 9.1. of the spec. */ + const size_t max_backward = self->window_mask_ - BROTLI_WINDOW_GAP + 1; + FN(StoreAndFindMatches)(self, data, ix, mask, MAX_TREE_COMP_LENGTH, + max_backward, NULL, NULL); +} + +static BROTLI_INLINE void FN(StoreRange)(HashToBinaryTree* BROTLI_RESTRICT self, + const uint8_t* BROTLI_RESTRICT data, const size_t mask, + const size_t ix_start, const size_t ix_end) { + size_t i = ix_start; + size_t j = ix_start; + if (ix_start + 63 <= ix_end) { + i = ix_end - 63; + } + if (ix_start + 512 <= i) { + for (; j < i; j += 8) { + FN(Store)(self, data, mask, j); + } + } + for (; i < ix_end; ++i) { + FN(Store)(self, data, mask, i); + } +} + +static BROTLI_INLINE void FN(StitchToPreviousBlock)( + HashToBinaryTree* BROTLI_RESTRICT self, + size_t num_bytes, size_t position, const uint8_t* ringbuffer, + size_t ringbuffer_mask) { + if (num_bytes >= FN(HashTypeLength)() - 1 && + position >= MAX_TREE_COMP_LENGTH) { + /* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher. + These could not be calculated before, since they require knowledge + of both the previous and the current block. */ + const size_t i_start = position - MAX_TREE_COMP_LENGTH + 1; + const size_t i_end = BROTLI_MIN(size_t, position, i_start + num_bytes); + size_t i; + for (i = i_start; i < i_end; ++i) { + /* Maximum distance is window size - 16, see section 9.1. of the spec. + Furthermore, we have to make sure that we don't look further back + from the start of the next block than the window size, otherwise we + could access already overwritten areas of the ring-buffer. */ + const size_t max_backward = + self->window_mask_ - BROTLI_MAX(size_t, + BROTLI_WINDOW_GAP - 1, + position - i); + /* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the + end of the current block and that we have at least + MAX_TREE_COMP_LENGTH tail in the ring-buffer. */ + FN(StoreAndFindMatches)(self, ringbuffer, i, ringbuffer_mask, + MAX_TREE_COMP_LENGTH, max_backward, NULL, NULL); + } + } +} + +#undef BUCKET_SIZE + +#undef HashToBinaryTree diff --git a/packages/runtime/vendor/brotli/c/enc/histogram.c b/packages/runtime/vendor/brotli/c/enc/histogram.c new file mode 100644 index 000000000..43e4b38e3 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/histogram.c @@ -0,0 +1,101 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Build per-context histograms of literals, commands and distance codes. */ + +#include "histogram.h" + +#include "../common/context.h" +#include "../common/platform.h" +#include "block_splitter.h" +#include "command.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct BlockSplitIterator { + const BlockSplit* split_; /* Not owned. */ + size_t idx_; + size_t type_; + size_t length_; +} BlockSplitIterator; + +static void InitBlockSplitIterator(BlockSplitIterator* self, + const BlockSplit* split) { + self->split_ = split; + self->idx_ = 0; + self->type_ = 0; + self->length_ = split->lengths ? split->lengths[0] : 0; +} + +static void BlockSplitIteratorNext(BlockSplitIterator* self) { + if (self->length_ == 0) { + ++self->idx_; + self->type_ = self->split_->types[self->idx_]; + self->length_ = self->split_->lengths[self->idx_]; + } + --self->length_; +} + +void BrotliBuildHistogramsWithContext( + const Command* cmds, const size_t num_commands, + const BlockSplit* literal_split, const BlockSplit* insert_and_copy_split, + const BlockSplit* dist_split, const uint8_t* ringbuffer, size_t start_pos, + size_t mask, uint8_t prev_byte, uint8_t prev_byte2, + const ContextType* context_modes, HistogramLiteral* literal_histograms, + HistogramCommand* insert_and_copy_histograms, + HistogramDistance* copy_dist_histograms) { + size_t pos = start_pos; + BlockSplitIterator literal_it; + BlockSplitIterator insert_and_copy_it; + BlockSplitIterator dist_it; + size_t i; + + InitBlockSplitIterator(&literal_it, literal_split); + InitBlockSplitIterator(&insert_and_copy_it, insert_and_copy_split); + InitBlockSplitIterator(&dist_it, dist_split); + for (i = 0; i < num_commands; ++i) { + const Command* cmd = &cmds[i]; + size_t j; + BlockSplitIteratorNext(&insert_and_copy_it); + HistogramAddCommand(&insert_and_copy_histograms[insert_and_copy_it.type_], + cmd->cmd_prefix_); + /* TODO(eustas): unwrap iterator blocks. */ + for (j = cmd->insert_len_; j != 0; --j) { + size_t context; + BlockSplitIteratorNext(&literal_it); + context = literal_it.type_; + if (context_modes) { + ContextLut lut = BROTLI_CONTEXT_LUT(context_modes[context]); + context = (context << BROTLI_LITERAL_CONTEXT_BITS) + + BROTLI_CONTEXT(prev_byte, prev_byte2, lut); + } + HistogramAddLiteral(&literal_histograms[context], + ringbuffer[pos & mask]); + prev_byte2 = prev_byte; + prev_byte = ringbuffer[pos & mask]; + ++pos; + } + pos += CommandCopyLen(cmd); + if (CommandCopyLen(cmd)) { + prev_byte2 = ringbuffer[(pos - 2) & mask]; + prev_byte = ringbuffer[(pos - 1) & mask]; + if (cmd->cmd_prefix_ >= 128) { + size_t context; + BlockSplitIteratorNext(&dist_it); + context = (dist_it.type_ << BROTLI_DISTANCE_CONTEXT_BITS) + + CommandDistanceContext(cmd); + HistogramAddDistance(©_dist_histograms[context], + cmd->dist_prefix_ & 0x3FF); + } + } + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/histogram.h b/packages/runtime/vendor/brotli/c/enc/histogram.h new file mode 100644 index 000000000..a2accc331 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/histogram.h @@ -0,0 +1,60 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Models the histograms of literals, commands and distance codes. */ + +#ifndef BROTLI_ENC_HISTOGRAM_H_ +#define BROTLI_ENC_HISTOGRAM_H_ + +#include "../common/constants.h" +#include "../common/context.h" +#include "../common/platform.h" +#include "block_splitter.h" +#include "command.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* The distance symbols effectively used by "Large Window Brotli" (32-bit). */ +#define BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS 544 + +#define FN(X) X ## Literal +#define DATA_SIZE BROTLI_NUM_LITERAL_SYMBOLS +#define DataType uint8_t +#include "histogram_inc.h" /* NOLINT(build/include) */ +#undef DataType +#undef DATA_SIZE +#undef FN + +#define FN(X) X ## Command +#define DataType uint16_t +#define DATA_SIZE BROTLI_NUM_COMMAND_SYMBOLS +#include "histogram_inc.h" /* NOLINT(build/include) */ +#undef DATA_SIZE +#undef FN + +#define FN(X) X ## Distance +#define DATA_SIZE BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS +#include "histogram_inc.h" /* NOLINT(build/include) */ +#undef DataType +#undef DATA_SIZE +#undef FN + +BROTLI_INTERNAL void BrotliBuildHistogramsWithContext( + const Command* cmds, const size_t num_commands, + const BlockSplit* literal_split, const BlockSplit* insert_and_copy_split, + const BlockSplit* dist_split, const uint8_t* ringbuffer, size_t pos, + size_t mask, uint8_t prev_byte, uint8_t prev_byte2, + const ContextType* context_modes, HistogramLiteral* literal_histograms, + HistogramCommand* insert_and_copy_histograms, + HistogramDistance* copy_dist_histograms); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_HISTOGRAM_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/histogram_inc.h b/packages/runtime/vendor/brotli/c/enc/histogram_inc.h new file mode 100644 index 000000000..50eaf7468 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/histogram_inc.h @@ -0,0 +1,51 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: Histogram, DATA_SIZE, DataType */ + +/* A simple container for histograms of data in blocks. */ + +typedef struct FN(Histogram) { + uint32_t data_[DATA_SIZE]; + size_t total_count_; + double bit_cost_; +} FN(Histogram); + +static BROTLI_INLINE void FN(HistogramClear)(FN(Histogram)* self) { + memset(self->data_, 0, sizeof(self->data_)); + self->total_count_ = 0; + self->bit_cost_ = HUGE_VAL; +} + +static BROTLI_INLINE void FN(ClearHistograms)( + FN(Histogram)* array, size_t length) { + size_t i; + for (i = 0; i < length; ++i) FN(HistogramClear)(array + i); +} + +static BROTLI_INLINE void FN(HistogramAdd)(FN(Histogram)* self, size_t val) { + ++self->data_[val]; + ++self->total_count_; +} + +static BROTLI_INLINE void FN(HistogramAddVector)(FN(Histogram)* self, + const DataType* p, size_t n) { + self->total_count_ += n; + n += 1; + while (--n) ++self->data_[*p++]; +} + +static BROTLI_INLINE void FN(HistogramAddHistogram)(FN(Histogram)* self, + const FN(Histogram)* v) { + size_t i; + self->total_count_ += v->total_count_; + for (i = 0; i < DATA_SIZE; ++i) { + self->data_[i] += v->data_[i]; + } +} + +static BROTLI_INLINE size_t FN(HistogramDataSize)(void) { return DATA_SIZE; } diff --git a/packages/runtime/vendor/brotli/c/enc/literal_cost.c b/packages/runtime/vendor/brotli/c/enc/literal_cost.c new file mode 100644 index 000000000..c4e207d09 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/literal_cost.c @@ -0,0 +1,178 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Literal cost model to allow backward reference replacement to be efficient. +*/ + +#include "literal_cost.h" + +#include "../common/platform.h" +#include "fast_log.h" +#include "utf8_util.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static size_t UTF8Position(size_t last, size_t c, size_t clamp) { + if (c < 128) { + return 0; /* Next one is the 'Byte 1' again. */ + } else if (c >= 192) { /* Next one is the 'Byte 2' of utf-8 encoding. */ + return BROTLI_MIN(size_t, 1, clamp); + } else { + /* Let's decide over the last byte if this ends the sequence. */ + if (last < 0xE0) { + return 0; /* Completed two or three byte coding. */ + } else { /* Next one is the 'Byte 3' of utf-8 encoding. */ + return BROTLI_MIN(size_t, 2, clamp); + } + } +} + +static size_t DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask, + const uint8_t* data) { + size_t counts[3] = { 0 }; + size_t max_utf8 = 1; /* should be 2, but 1 compresses better. */ + size_t last_c = 0; + size_t i; + for (i = 0; i < len; ++i) { + size_t c = data[(pos + i) & mask]; + ++counts[UTF8Position(last_c, c, 2)]; + last_c = c; + } + if (counts[2] < 500) { + max_utf8 = 1; + } + if (counts[1] + counts[2] < 25) { + max_utf8 = 0; + } + return max_utf8; +} + +static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask, + const uint8_t* data, + size_t* histogram, float* cost) { + /* max_utf8 is 0 (normal ASCII single byte modeling), + 1 (for 2-byte UTF-8 modeling), or 2 (for 3-byte UTF-8 modeling). */ + const size_t max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data); + size_t window_half = 495; + size_t in_window = BROTLI_MIN(size_t, window_half, len); + size_t in_window_utf8[3] = { 0 }; + size_t i; + memset(histogram, 0, 3 * 256 * sizeof(histogram[0])); + + { /* Bootstrap histograms. */ + size_t last_c = 0; + size_t utf8_pos = 0; + for (i = 0; i < in_window; ++i) { + size_t c = data[(pos + i) & mask]; + ++histogram[256 * utf8_pos + c]; + ++in_window_utf8[utf8_pos]; + utf8_pos = UTF8Position(last_c, c, max_utf8); + last_c = c; + } + } + + /* Compute bit costs with sliding window. */ + for (i = 0; i < len; ++i) { + if (i >= window_half) { + /* Remove a byte in the past. */ + size_t c = + i < window_half + 1 ? 0 : data[(pos + i - window_half - 1) & mask]; + size_t last_c = + i < window_half + 2 ? 0 : data[(pos + i - window_half - 2) & mask]; + size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8); + --histogram[256 * utf8_pos2 + data[(pos + i - window_half) & mask]]; + --in_window_utf8[utf8_pos2]; + } + if (i + window_half < len) { + /* Add a byte in the future. */ + size_t c = data[(pos + i + window_half - 1) & mask]; + size_t last_c = data[(pos + i + window_half - 2) & mask]; + size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8); + ++histogram[256 * utf8_pos2 + data[(pos + i + window_half) & mask]]; + ++in_window_utf8[utf8_pos2]; + } + { + size_t c = i < 1 ? 0 : data[(pos + i - 1) & mask]; + size_t last_c = i < 2 ? 0 : data[(pos + i - 2) & mask]; + size_t utf8_pos = UTF8Position(last_c, c, max_utf8); + size_t masked_pos = (pos + i) & mask; + size_t histo = histogram[256 * utf8_pos + data[masked_pos]]; + static const size_t prologue_length = 2000; + static const double multiplier = 0.35 / 2000; + double lit_cost; + if (histo == 0) { + histo = 1; + } + lit_cost = FastLog2(in_window_utf8[utf8_pos]) - FastLog2(histo); + lit_cost += 0.02905; + if (lit_cost < 1.0) { + lit_cost *= 0.5; + lit_cost += 0.5; + } + /* Make the first bytes more expensive -- seems to help, not sure why. + Perhaps because the entropy source is changing its properties + rapidly in the beginning of the file, perhaps because the beginning + of the data is a statistical "anomaly". */ + if (i < prologue_length) { + lit_cost += 0.35 + multiplier * (double)i; + } + cost[i] = (float)lit_cost; + } + } +} + +void BrotliEstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask, + const uint8_t* data, + size_t* histogram, float* cost) { + if (BrotliIsMostlyUTF8(data, pos, mask, len, kMinUTF8Ratio)) { + EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, histogram, cost); + return; + } else { + size_t window_half = 2000; + size_t in_window = BROTLI_MIN(size_t, window_half, len); + size_t i; + memset(histogram, 0, 256 * sizeof(histogram[0])); + + /* Bootstrap histogram. */ + for (i = 0; i < in_window; ++i) { + ++histogram[data[(pos + i) & mask]]; + } + + /* Compute bit costs with sliding window. */ + for (i = 0; i < len; ++i) { + size_t histo; + if (i >= window_half) { + /* Remove a byte in the past. */ + --histogram[data[(pos + i - window_half) & mask]]; + --in_window; + } + if (i + window_half < len) { + /* Add a byte in the future. */ + ++histogram[data[(pos + i + window_half) & mask]]; + ++in_window; + } + histo = histogram[data[(pos + i) & mask]]; + if (histo == 0) { + histo = 1; + } + { + double lit_cost = FastLog2(in_window) - FastLog2(histo); + lit_cost += 0.029; + if (lit_cost < 1.0) { + lit_cost *= 0.5; + lit_cost += 0.5; + } + cost[i] = (float)lit_cost; + } + } + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/literal_cost.h b/packages/runtime/vendor/brotli/c/enc/literal_cost.h new file mode 100644 index 000000000..eb3f33836 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/literal_cost.h @@ -0,0 +1,30 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Literal cost model to allow backward reference replacement to be efficient. +*/ + +#ifndef BROTLI_ENC_LITERAL_COST_H_ +#define BROTLI_ENC_LITERAL_COST_H_ + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* Estimates how many bits the literals in the interval [pos, pos + len) in the + ring-buffer (data, mask) will take entropy coded and writes these estimates + to the cost[0..len) array. */ +BROTLI_INTERNAL void BrotliEstimateBitCostsForLiterals( + size_t pos, size_t len, size_t mask, const uint8_t* data, size_t* histogram, + float* cost); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_LITERAL_COST_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/matching_tag_mask.h b/packages/runtime/vendor/brotli/c/enc/matching_tag_mask.h new file mode 100644 index 000000000..8a1be1d71 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/matching_tag_mask.h @@ -0,0 +1,69 @@ +#ifndef THIRD_PARTY_BROTLI_ENC_MATCHING_TAG_MASK_H_ +#define THIRD_PARTY_BROTLI_ENC_MATCHING_TAG_MASK_H_ + +#include "../common/platform.h" + +#if defined(__SSE2__) || defined(_M_AMD64) || \ + (defined(_M_IX86) && defined(_M_IX86_FP) && (_M_IX86_FP >= 2)) +#define SUPPORTS_SSE_2 +#endif + +#if defined(SUPPORTS_SSE_2) +#include +#endif + + +static BROTLI_INLINE uint64_t GetMatchingTagMask( + size_t chunk_count, const uint8_t tag, + const uint8_t* BROTLI_RESTRICT tag_bucket, const size_t head) { + uint64_t matches = 0; +#if defined(SUPPORTS_SSE_2) + const __m128i comparison_mask = _mm_set1_epi8((char)tag); + size_t i; + for (i = 0; i < chunk_count && i < 4; i++) { + const __m128i chunk = + _mm_loadu_si128((const __m128i*)(const void*)(tag_bucket + 16 * i)); + const __m128i equal_mask = _mm_cmpeq_epi8(chunk, comparison_mask); + matches |= (uint64_t)_mm_movemask_epi8(equal_mask) << 16 * i; + } +#else + const int chunk_size = sizeof(size_t); + const size_t shift_amount = ((chunk_size * 8) - chunk_size); + const size_t xFF = ~((size_t)0); + const size_t x01 = xFF / 0xFF; + const size_t x80 = x01 << 7; + const size_t splat_char = tag * x01; + int i = ((int)chunk_count * 16) - chunk_size; + BROTLI_DCHECK((sizeof(size_t) == 4) || (sizeof(size_t) == 8)); +#if BROTLI_LITTLE_ENDIAN + const size_t extractMagic = (xFF / 0x7F) >> chunk_size; + do { + size_t chunk = BrotliUnalignedReadSizeT(&tag_bucket[i]); + chunk ^= splat_char; + chunk = (((chunk | x80) - x01) | chunk) & x80; + matches <<= chunk_size; + matches |= (chunk * extractMagic) >> shift_amount; + i -= chunk_size; + } while (i >= 0); +#else + const size_t msb = xFF ^ (xFF >> 1); + const size_t extractMagic = (msb / 0x1FF) | msb; + do { + size_t chunk = BrotliUnalignedReadSizeT(&tag_bucket[i]); + chunk ^= splat_char; + chunk = (((chunk | x80) - x01) | chunk) & x80; + matches <<= chunk_size; + matches |= ((chunk >> 7) * extractMagic) >> shift_amount; + i -= chunk_size; + } while (i >= 0); +#endif + matches = ~matches; +#endif + if (chunk_count == 1) return BrotliRotateRight16((uint16_t)matches, head); + if (chunk_count == 2) return BrotliRotateRight32((uint32_t)matches, head); + return BrotliRotateRight64(matches, head); +} + +#undef SUPPORTS_SSE_2 + +#endif // THIRD_PARTY_BROTLI_ENC_MATCHING_TAG_MASK_H_ diff --git a/packages/runtime/vendor/brotli/c/enc/memory.c b/packages/runtime/vendor/brotli/c/enc/memory.c new file mode 100644 index 000000000..67f81ffde --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/memory.c @@ -0,0 +1,189 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Algorithms for distributing the literals and commands of a metablock between + block types and contexts. */ + +#include "memory.h" + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define MAX_NEW_ALLOCATED (BROTLI_ENCODER_MEMORY_MANAGER_SLOTS >> 2) +#define MAX_NEW_FREED (BROTLI_ENCODER_MEMORY_MANAGER_SLOTS >> 2) +#define MAX_PERM_ALLOCATED (BROTLI_ENCODER_MEMORY_MANAGER_SLOTS >> 1) + +#define PERM_ALLOCATED_OFFSET 0 +#define NEW_ALLOCATED_OFFSET MAX_PERM_ALLOCATED +#define NEW_FREED_OFFSET (MAX_PERM_ALLOCATED + MAX_NEW_ALLOCATED) + +void BrotliInitMemoryManager( + MemoryManager* m, brotli_alloc_func alloc_func, brotli_free_func free_func, + void* opaque) { + if (!alloc_func) { + m->alloc_func = BrotliDefaultAllocFunc; + m->free_func = BrotliDefaultFreeFunc; + m->opaque = 0; + } else { + m->alloc_func = alloc_func; + m->free_func = free_func; + m->opaque = opaque; + } +#if !defined(BROTLI_ENCODER_EXIT_ON_OOM) + m->is_oom = BROTLI_FALSE; + m->perm_allocated = 0; + m->new_allocated = 0; + m->new_freed = 0; +#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ +} + +#if defined(BROTLI_ENCODER_EXIT_ON_OOM) + +void* BrotliAllocate(MemoryManager* m, size_t n) { + void* result = m->alloc_func(m->opaque, n); + if (!result) exit(EXIT_FAILURE); + return result; +} + +void BrotliFree(MemoryManager* m, void* p) { + m->free_func(m->opaque, p); +} + +void BrotliWipeOutMemoryManager(MemoryManager* m) { + BROTLI_UNUSED(m); +} + +#else /* BROTLI_ENCODER_EXIT_ON_OOM */ + +static void SortPointers(void** items, const size_t n) { + /* Shell sort. */ + /* TODO(eustas): fine-tune for "many slots" case */ + static const size_t gaps[] = {23, 10, 4, 1}; + int g = 0; + for (; g < 4; ++g) { + size_t gap = gaps[g]; + size_t i; + for (i = gap; i < n; ++i) { + size_t j = i; + void* tmp = items[i]; + for (; j >= gap && tmp < items[j - gap]; j -= gap) { + items[j] = items[j - gap]; + } + items[j] = tmp; + } + } +} + +static size_t Annihilate(void** a, size_t a_len, void** b, size_t b_len) { + size_t a_read_index = 0; + size_t b_read_index = 0; + size_t a_write_index = 0; + size_t b_write_index = 0; + size_t annihilated = 0; + while (a_read_index < a_len && b_read_index < b_len) { + if (a[a_read_index] == b[b_read_index]) { + a_read_index++; + b_read_index++; + annihilated++; + } else if (a[a_read_index] < b[b_read_index]) { + a[a_write_index++] = a[a_read_index++]; + } else { + b[b_write_index++] = b[b_read_index++]; + } + } + while (a_read_index < a_len) a[a_write_index++] = a[a_read_index++]; + while (b_read_index < b_len) b[b_write_index++] = b[b_read_index++]; + return annihilated; +} + +static void CollectGarbagePointers(MemoryManager* m) { + size_t annihilated; + SortPointers(m->pointers + NEW_ALLOCATED_OFFSET, m->new_allocated); + SortPointers(m->pointers + NEW_FREED_OFFSET, m->new_freed); + annihilated = Annihilate( + m->pointers + NEW_ALLOCATED_OFFSET, m->new_allocated, + m->pointers + NEW_FREED_OFFSET, m->new_freed); + m->new_allocated -= annihilated; + m->new_freed -= annihilated; + + if (m->new_freed != 0) { + annihilated = Annihilate( + m->pointers + PERM_ALLOCATED_OFFSET, m->perm_allocated, + m->pointers + NEW_FREED_OFFSET, m->new_freed); + m->perm_allocated -= annihilated; + m->new_freed -= annihilated; + BROTLI_DCHECK(m->new_freed == 0); + } + + if (m->new_allocated != 0) { + BROTLI_DCHECK(m->perm_allocated + m->new_allocated <= MAX_PERM_ALLOCATED); + memcpy(m->pointers + PERM_ALLOCATED_OFFSET + m->perm_allocated, + m->pointers + NEW_ALLOCATED_OFFSET, + sizeof(void*) * m->new_allocated); + m->perm_allocated += m->new_allocated; + m->new_allocated = 0; + SortPointers(m->pointers + PERM_ALLOCATED_OFFSET, m->perm_allocated); + } +} + +void* BrotliAllocate(MemoryManager* m, size_t n) { + void* result = m->alloc_func(m->opaque, n); + if (!result) { + m->is_oom = BROTLI_TRUE; + return NULL; + } + if (m->new_allocated == MAX_NEW_ALLOCATED) CollectGarbagePointers(m); + m->pointers[NEW_ALLOCATED_OFFSET + (m->new_allocated++)] = result; + return result; +} + +void BrotliFree(MemoryManager* m, void* p) { + if (!p) return; + m->free_func(m->opaque, p); + if (m->new_freed == MAX_NEW_FREED) CollectGarbagePointers(m); + m->pointers[NEW_FREED_OFFSET + (m->new_freed++)] = p; +} + +void BrotliWipeOutMemoryManager(MemoryManager* m) { + size_t i; + CollectGarbagePointers(m); + /* Now all unfreed pointers are in perm-allocated list. */ + for (i = 0; i < m->perm_allocated; ++i) { + m->free_func(m->opaque, m->pointers[PERM_ALLOCATED_OFFSET + i]); + } + m->perm_allocated = 0; +} + +#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ + +void* BrotliBootstrapAlloc(size_t size, + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + if (!alloc_func && !free_func) { + return malloc(size); + } else if (alloc_func && free_func) { + return alloc_func(opaque, size); + } + return NULL; +} + +void BrotliBootstrapFree(void* address, MemoryManager* m) { + if (!address) { + /* Should not happen! */ + return; + } else { + /* Copy values, as those would be freed. */ + brotli_free_func free_func = m->free_func; + void* opaque = m->opaque; + free_func(opaque, address); + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/memory.h b/packages/runtime/vendor/brotli/c/enc/memory.h new file mode 100644 index 000000000..d8374815b --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/memory.h @@ -0,0 +1,127 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Macros for memory management. */ + +#ifndef BROTLI_ENC_MEMORY_H_ +#define BROTLI_ENC_MEMORY_H_ + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if !defined(BROTLI_ENCODER_CLEANUP_ON_OOM) && \ + !defined(BROTLI_ENCODER_EXIT_ON_OOM) +#define BROTLI_ENCODER_EXIT_ON_OOM +#endif + +#if !defined(BROTLI_ENCODER_EXIT_ON_OOM) +#if defined(BROTLI_EXPERIMENTAL) +#define BROTLI_ENCODER_MEMORY_MANAGER_SLOTS (48*1024) +#else /* BROTLI_EXPERIMENTAL */ +#define BROTLI_ENCODER_MEMORY_MANAGER_SLOTS 256 +#endif /* BROTLI_EXPERIMENTAL */ +#else /* BROTLI_ENCODER_EXIT_ON_OOM */ +#define BROTLI_ENCODER_MEMORY_MANAGER_SLOTS 0 +#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ + +typedef struct MemoryManager { + brotli_alloc_func alloc_func; + brotli_free_func free_func; + void* opaque; +#if !defined(BROTLI_ENCODER_EXIT_ON_OOM) + BROTLI_BOOL is_oom; + size_t perm_allocated; + size_t new_allocated; + size_t new_freed; + void* pointers[BROTLI_ENCODER_MEMORY_MANAGER_SLOTS]; +#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ +} MemoryManager; + +BROTLI_INTERNAL void BrotliInitMemoryManager( + MemoryManager* m, brotli_alloc_func alloc_func, brotli_free_func free_func, + void* opaque); + +BROTLI_INTERNAL void* BrotliAllocate(MemoryManager* m, size_t n); +#define BROTLI_ALLOC(M, T, N) \ + ((N) > 0 ? ((T*)BrotliAllocate((M), (N) * sizeof(T))) : NULL) + +BROTLI_INTERNAL void BrotliFree(MemoryManager* m, void* p); +#define BROTLI_FREE(M, P) { \ + BrotliFree((M), (P)); \ + P = NULL; \ +} + +#if defined(BROTLI_ENCODER_EXIT_ON_OOM) +#define BROTLI_IS_OOM(M) (!!0) +#else /* BROTLI_ENCODER_EXIT_ON_OOM */ +#define BROTLI_IS_OOM(M) (!!(M)->is_oom) +#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ + +/* +BROTLI_IS_NULL is a fake check, BROTLI_IS_OOM does the heavy lifting. +The only purpose of it is to explain static analyzers the state of things. +NB: use ONLY together with BROTLI_IS_OOM + AND ONLY for allocations in the current scope. + */ +#if defined(__clang_analyzer__) && !defined(BROTLI_ENCODER_EXIT_ON_OOM) +#define BROTLI_IS_NULL(A) ((A) == nullptr) +#else /* defined(__clang_analyzer__) */ +#define BROTLI_IS_NULL(A) (!!0) +#endif /* defined(__clang_analyzer__) */ + +BROTLI_INTERNAL void BrotliWipeOutMemoryManager(MemoryManager* m); + +/* +Dynamically grows array capacity to at least the requested size +M: MemoryManager +T: data type +A: array +C: capacity +R: requested size +*/ +#define BROTLI_ENSURE_CAPACITY(M, T, A, C, R) { \ + if (C < (R)) { \ + size_t _new_size = (C == 0) ? (R) : C; \ + T* new_array; \ + while (_new_size < (R)) _new_size *= 2; \ + new_array = BROTLI_ALLOC((M), T, _new_size); \ + if (!BROTLI_IS_OOM(M) && !BROTLI_IS_NULL(new_array) && C != 0) \ + memcpy(new_array, A, C * sizeof(T)); \ + BROTLI_FREE((M), A); \ + A = new_array; \ + C = _new_size; \ + } \ +} + +/* +Appends value and dynamically grows array capacity when needed +M: MemoryManager +T: data type +A: array +C: array capacity +S: array size +V: value to append +*/ +#define BROTLI_ENSURE_CAPACITY_APPEND(M, T, A, C, S, V) { \ + (S)++; \ + BROTLI_ENSURE_CAPACITY(M, T, A, C, S); \ + A[(S) - 1] = (V); \ +} + +/* "Bootstrap" allocations are not tracked by memory manager; should be used + only to allocate MemoryManager itself (or structure containing it). */ +BROTLI_INTERNAL void* BrotliBootstrapAlloc(size_t size, + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); +BROTLI_INTERNAL void BrotliBootstrapFree(void* address, MemoryManager* m); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_MEMORY_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/metablock.c b/packages/runtime/vendor/brotli/c/enc/metablock.c new file mode 100644 index 000000000..dfe8120c8 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/metablock.c @@ -0,0 +1,675 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Algorithms for distributing the literals and commands of a metablock between + block types and contexts. */ + +#include "metablock.h" + +#include "../common/constants.h" +#include "../common/context.h" +#include "../common/platform.h" +#include "bit_cost.h" +#include "block_splitter.h" +#include "cluster.h" +#include "command.h" +#include "entropy_encode.h" +#include "histogram.h" +#include "memory.h" +#include "params.h" +#include "prefix.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +void BrotliInitDistanceParams(BrotliDistanceParams* dist_params, + uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window) { + uint32_t alphabet_size_max; + uint32_t alphabet_size_limit; + uint32_t max_distance; + + dist_params->distance_postfix_bits = npostfix; + dist_params->num_direct_distance_codes = ndirect; + + alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( + npostfix, ndirect, BROTLI_MAX_DISTANCE_BITS); + alphabet_size_limit = alphabet_size_max; + max_distance = ndirect + (1U << (BROTLI_MAX_DISTANCE_BITS + npostfix + 2)) - + (1U << (npostfix + 2)); + + if (large_window) { + BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit( + BROTLI_MAX_ALLOWED_DISTANCE, npostfix, ndirect); + alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( + npostfix, ndirect, BROTLI_LARGE_MAX_DISTANCE_BITS); + alphabet_size_limit = limit.max_alphabet_size; + max_distance = limit.max_distance; + } + + dist_params->alphabet_size_max = alphabet_size_max; + dist_params->alphabet_size_limit = alphabet_size_limit; + dist_params->max_distance = max_distance; +} + +static void RecomputeDistancePrefixes(Command* cmds, + size_t num_commands, + const BrotliDistanceParams* orig_params, + const BrotliDistanceParams* new_params) { + size_t i; + + if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits && + orig_params->num_direct_distance_codes == + new_params->num_direct_distance_codes) { + return; + } + + for (i = 0; i < num_commands; ++i) { + Command* cmd = &cmds[i]; + if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) { + PrefixEncodeCopyDistance(CommandRestoreDistanceCode(cmd, orig_params), + new_params->num_direct_distance_codes, + new_params->distance_postfix_bits, + &cmd->dist_prefix_, + &cmd->dist_extra_); + } + } +} + +static BROTLI_BOOL ComputeDistanceCost(const Command* cmds, + size_t num_commands, + const BrotliDistanceParams* orig_params, + const BrotliDistanceParams* new_params, + double* cost, + HistogramDistance* tmp) { + size_t i; + BROTLI_BOOL equal_params = BROTLI_FALSE; + uint16_t dist_prefix; + uint32_t dist_extra; + double extra_bits = 0.0; + HistogramClearDistance(tmp); + + if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits && + orig_params->num_direct_distance_codes == + new_params->num_direct_distance_codes) { + equal_params = BROTLI_TRUE; + } + + for (i = 0; i < num_commands; i++) { + const Command* cmd = &cmds[i]; + if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) { + if (equal_params) { + dist_prefix = cmd->dist_prefix_; + } else { + uint32_t distance = CommandRestoreDistanceCode(cmd, orig_params); + if (distance > new_params->max_distance) { + return BROTLI_FALSE; + } + PrefixEncodeCopyDistance(distance, + new_params->num_direct_distance_codes, + new_params->distance_postfix_bits, + &dist_prefix, + &dist_extra); + } + HistogramAddDistance(tmp, dist_prefix & 0x3FF); + extra_bits += dist_prefix >> 10; + } + } + + *cost = BrotliPopulationCostDistance(tmp) + extra_bits; + return BROTLI_TRUE; +} + +void BrotliBuildMetaBlock(MemoryManager* m, + const uint8_t* ringbuffer, + const size_t pos, + const size_t mask, + BrotliEncoderParams* params, + uint8_t prev_byte, + uint8_t prev_byte2, + Command* cmds, + size_t num_commands, + ContextType literal_context_mode, + MetaBlockSplit* mb) { + /* Histogram ids need to fit in one byte. */ + static const size_t kMaxNumberOfHistograms = 256; + HistogramDistance* distance_histograms; + HistogramLiteral* literal_histograms; + ContextType* literal_context_modes = NULL; + size_t literal_histograms_size; + size_t distance_histograms_size; + size_t i; + size_t literal_context_multiplier = 1; + uint32_t npostfix; + uint32_t ndirect_msb = 0; + BROTLI_BOOL check_orig = BROTLI_TRUE; + double best_dist_cost = 1e99; + BrotliDistanceParams orig_params = params->dist; + BrotliDistanceParams new_params = params->dist; + HistogramDistance* tmp = BROTLI_ALLOC(m, HistogramDistance, 1); + + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return; + + for (npostfix = 0; npostfix <= BROTLI_MAX_NPOSTFIX; npostfix++) { + for (; ndirect_msb < 16; ndirect_msb++) { + uint32_t ndirect = ndirect_msb << npostfix; + BROTLI_BOOL skip; + double dist_cost; + BrotliInitDistanceParams(&new_params, npostfix, ndirect, + params->large_window); + if (npostfix == orig_params.distance_postfix_bits && + ndirect == orig_params.num_direct_distance_codes) { + check_orig = BROTLI_FALSE; + } + skip = !ComputeDistanceCost( + cmds, num_commands, &orig_params, &new_params, &dist_cost, tmp); + if (skip || (dist_cost > best_dist_cost)) { + break; + } + best_dist_cost = dist_cost; + params->dist = new_params; + } + if (ndirect_msb > 0) ndirect_msb--; + ndirect_msb /= 2; + } + if (check_orig) { + double dist_cost; + ComputeDistanceCost(cmds, num_commands, &orig_params, &orig_params, + &dist_cost, tmp); + if (dist_cost < best_dist_cost) { + /* NB: currently unused; uncomment when more param tuning is added. */ + /* best_dist_cost = dist_cost; */ + params->dist = orig_params; + } + } + BROTLI_FREE(m, tmp); + RecomputeDistancePrefixes(cmds, num_commands, &orig_params, ¶ms->dist); + + BrotliSplitBlock(m, cmds, num_commands, + ringbuffer, pos, mask, params, + &mb->literal_split, + &mb->command_split, + &mb->distance_split); + if (BROTLI_IS_OOM(m)) return; + + if (!params->disable_literal_context_modeling) { + literal_context_multiplier = 1 << BROTLI_LITERAL_CONTEXT_BITS; + literal_context_modes = + BROTLI_ALLOC(m, ContextType, mb->literal_split.num_types); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(literal_context_modes)) return; + for (i = 0; i < mb->literal_split.num_types; ++i) { + literal_context_modes[i] = literal_context_mode; + } + } + + literal_histograms_size = + mb->literal_split.num_types * literal_context_multiplier; + literal_histograms = + BROTLI_ALLOC(m, HistogramLiteral, literal_histograms_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(literal_histograms)) return; + ClearHistogramsLiteral(literal_histograms, literal_histograms_size); + + distance_histograms_size = + mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS; + distance_histograms = + BROTLI_ALLOC(m, HistogramDistance, distance_histograms_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(distance_histograms)) return; + ClearHistogramsDistance(distance_histograms, distance_histograms_size); + + BROTLI_DCHECK(mb->command_histograms == 0); + mb->command_histograms_size = mb->command_split.num_types; + mb->command_histograms = + BROTLI_ALLOC(m, HistogramCommand, mb->command_histograms_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->command_histograms)) return; + ClearHistogramsCommand(mb->command_histograms, mb->command_histograms_size); + + BrotliBuildHistogramsWithContext(cmds, num_commands, + &mb->literal_split, &mb->command_split, &mb->distance_split, + ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_modes, + literal_histograms, mb->command_histograms, distance_histograms); + BROTLI_FREE(m, literal_context_modes); + + BROTLI_DCHECK(mb->literal_context_map == 0); + mb->literal_context_map_size = + mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS; + mb->literal_context_map = + BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->literal_context_map)) return; + + BROTLI_DCHECK(mb->literal_histograms == 0); + mb->literal_histograms_size = mb->literal_context_map_size; + mb->literal_histograms = + BROTLI_ALLOC(m, HistogramLiteral, mb->literal_histograms_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->literal_histograms)) return; + + BrotliClusterHistogramsLiteral(m, literal_histograms, literal_histograms_size, + kMaxNumberOfHistograms, mb->literal_histograms, + &mb->literal_histograms_size, mb->literal_context_map); + if (BROTLI_IS_OOM(m)) return; + BROTLI_FREE(m, literal_histograms); + + if (params->disable_literal_context_modeling) { + /* Distribute assignment to all contexts. */ + for (i = mb->literal_split.num_types; i != 0;) { + size_t j = 0; + i--; + for (; j < (1 << BROTLI_LITERAL_CONTEXT_BITS); j++) { + mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] = + mb->literal_context_map[i]; + } + } + } + + BROTLI_DCHECK(mb->distance_context_map == 0); + mb->distance_context_map_size = + mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS; + mb->distance_context_map = + BROTLI_ALLOC(m, uint32_t, mb->distance_context_map_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->distance_context_map)) return; + + BROTLI_DCHECK(mb->distance_histograms == 0); + mb->distance_histograms_size = mb->distance_context_map_size; + mb->distance_histograms = + BROTLI_ALLOC(m, HistogramDistance, mb->distance_histograms_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->distance_histograms)) return; + + BrotliClusterHistogramsDistance(m, distance_histograms, + mb->distance_context_map_size, + kMaxNumberOfHistograms, + mb->distance_histograms, + &mb->distance_histograms_size, + mb->distance_context_map); + if (BROTLI_IS_OOM(m)) return; + BROTLI_FREE(m, distance_histograms); +} + +#define FN(X) X ## Literal +#include "metablock_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Command +#include "metablock_inc.h" /* NOLINT(build/include) */ +#undef FN + +#define FN(X) X ## Distance +#include "metablock_inc.h" /* NOLINT(build/include) */ +#undef FN + +/* Greedy block splitter for one block category (literal, command or distance). + Gathers histograms for all context buckets. */ +typedef struct ContextBlockSplitter { + /* Alphabet size of particular block category. */ + size_t alphabet_size_; + size_t num_contexts_; + size_t max_block_types_; + /* We collect at least this many symbols for each block. */ + size_t min_block_size_; + /* We merge histograms A and B if + entropy(A+B) < entropy(A) + entropy(B) + split_threshold_, + where A is the current histogram and B is the histogram of the last or the + second last block type. */ + double split_threshold_; + + size_t num_blocks_; + BlockSplit* split_; /* not owned */ + HistogramLiteral* histograms_; /* not owned */ + size_t* histograms_size_; /* not owned */ + + /* The number of symbols that we want to collect before deciding on whether + or not to merge the block with a previous one or emit a new block. */ + size_t target_block_size_; + /* The number of symbols in the current histogram. */ + size_t block_size_; + /* Offset of the current histogram. */ + size_t curr_histogram_ix_; + /* Offset of the histograms of the previous two block types. */ + size_t last_histogram_ix_[2]; + /* Entropy of the previous two block types. */ + double last_entropy_[2 * BROTLI_MAX_STATIC_CONTEXTS]; + /* The number of times we merged the current block with the last one. */ + size_t merge_last_count_; +} ContextBlockSplitter; + +static void InitContextBlockSplitter( + MemoryManager* m, ContextBlockSplitter* self, size_t alphabet_size, + size_t num_contexts, size_t min_block_size, double split_threshold, + size_t num_symbols, BlockSplit* split, HistogramLiteral** histograms, + size_t* histograms_size) { + size_t max_num_blocks = num_symbols / min_block_size + 1; + size_t max_num_types; + BROTLI_DCHECK(num_contexts <= BROTLI_MAX_STATIC_CONTEXTS); + + self->alphabet_size_ = alphabet_size; + self->num_contexts_ = num_contexts; + self->max_block_types_ = BROTLI_MAX_NUMBER_OF_BLOCK_TYPES / num_contexts; + self->min_block_size_ = min_block_size; + self->split_threshold_ = split_threshold; + self->num_blocks_ = 0; + self->split_ = split; + self->histograms_size_ = histograms_size; + self->target_block_size_ = min_block_size; + self->block_size_ = 0; + self->curr_histogram_ix_ = 0; + self->merge_last_count_ = 0; + + /* We have to allocate one more histogram than the maximum number of block + types for the current histogram when the meta-block is too big. */ + max_num_types = + BROTLI_MIN(size_t, max_num_blocks, self->max_block_types_ + 1); + BROTLI_ENSURE_CAPACITY(m, uint8_t, + split->types, split->types_alloc_size, max_num_blocks); + BROTLI_ENSURE_CAPACITY(m, uint32_t, + split->lengths, split->lengths_alloc_size, max_num_blocks); + if (BROTLI_IS_OOM(m)) return; + split->num_blocks = max_num_blocks; + if (BROTLI_IS_OOM(m)) return; + BROTLI_DCHECK(*histograms == 0); + *histograms_size = max_num_types * num_contexts; + *histograms = BROTLI_ALLOC(m, HistogramLiteral, *histograms_size); + self->histograms_ = *histograms; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(*histograms)) return; + /* Clear only current histogram. */ + ClearHistogramsLiteral(&self->histograms_[0], num_contexts); + self->last_histogram_ix_[0] = self->last_histogram_ix_[1] = 0; +} + +/* Does either of three things: + (1) emits the current block with a new block type; + (2) emits the current block with the type of the second last block; + (3) merges the current block with the last block. */ +static void ContextBlockSplitterFinishBlock( + ContextBlockSplitter* self, MemoryManager* m, BROTLI_BOOL is_final) { + BlockSplit* split = self->split_; + const size_t num_contexts = self->num_contexts_; + double* last_entropy = self->last_entropy_; + HistogramLiteral* histograms = self->histograms_; + + if (self->block_size_ < self->min_block_size_) { + self->block_size_ = self->min_block_size_; + } + if (self->num_blocks_ == 0) { + size_t i; + /* Create first block. */ + split->lengths[0] = (uint32_t)self->block_size_; + split->types[0] = 0; + + for (i = 0; i < num_contexts; ++i) { + last_entropy[i] = + BrotliBitsEntropy(histograms[i].data_, self->alphabet_size_); + last_entropy[num_contexts + i] = last_entropy[i]; + } + ++self->num_blocks_; + ++split->num_types; + self->curr_histogram_ix_ += num_contexts; + if (self->curr_histogram_ix_ < *self->histograms_size_) { + ClearHistogramsLiteral( + &self->histograms_[self->curr_histogram_ix_], self->num_contexts_); + } + self->block_size_ = 0; + } else if (self->block_size_ > 0) { + /* Try merging the set of histograms for the current block type with the + respective set of histograms for the last and second last block types. + Decide over the split based on the total reduction of entropy across + all contexts. */ + double entropy[BROTLI_MAX_STATIC_CONTEXTS]; + HistogramLiteral* combined_histo = + BROTLI_ALLOC(m, HistogramLiteral, 2 * num_contexts); + double combined_entropy[2 * BROTLI_MAX_STATIC_CONTEXTS]; + double diff[2] = { 0.0 }; + size_t i; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(combined_histo)) return; + for (i = 0; i < num_contexts; ++i) { + size_t curr_histo_ix = self->curr_histogram_ix_ + i; + size_t j; + entropy[i] = BrotliBitsEntropy(histograms[curr_histo_ix].data_, + self->alphabet_size_); + for (j = 0; j < 2; ++j) { + size_t jx = j * num_contexts + i; + size_t last_histogram_ix = self->last_histogram_ix_[j] + i; + combined_histo[jx] = histograms[curr_histo_ix]; + HistogramAddHistogramLiteral(&combined_histo[jx], + &histograms[last_histogram_ix]); + combined_entropy[jx] = BrotliBitsEntropy( + &combined_histo[jx].data_[0], self->alphabet_size_); + diff[j] += combined_entropy[jx] - entropy[i] - last_entropy[jx]; + } + } + + if (split->num_types < self->max_block_types_ && + diff[0] > self->split_threshold_ && + diff[1] > self->split_threshold_) { + /* Create new block. */ + split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; + split->types[self->num_blocks_] = (uint8_t)split->num_types; + self->last_histogram_ix_[1] = self->last_histogram_ix_[0]; + self->last_histogram_ix_[0] = split->num_types * num_contexts; + for (i = 0; i < num_contexts; ++i) { + last_entropy[num_contexts + i] = last_entropy[i]; + last_entropy[i] = entropy[i]; + } + ++self->num_blocks_; + ++split->num_types; + self->curr_histogram_ix_ += num_contexts; + if (self->curr_histogram_ix_ < *self->histograms_size_) { + ClearHistogramsLiteral( + &self->histograms_[self->curr_histogram_ix_], self->num_contexts_); + } + self->block_size_ = 0; + self->merge_last_count_ = 0; + self->target_block_size_ = self->min_block_size_; + } else if (diff[1] < diff[0] - 20.0) { + /* Combine this block with second last block. */ + split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; + split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2]; + BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1); + for (i = 0; i < num_contexts; ++i) { + histograms[self->last_histogram_ix_[0] + i] = + combined_histo[num_contexts + i]; + last_entropy[num_contexts + i] = last_entropy[i]; + last_entropy[i] = combined_entropy[num_contexts + i]; + HistogramClearLiteral(&histograms[self->curr_histogram_ix_ + i]); + } + ++self->num_blocks_; + self->block_size_ = 0; + self->merge_last_count_ = 0; + self->target_block_size_ = self->min_block_size_; + } else { + /* Combine this block with last block. */ + split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_; + for (i = 0; i < num_contexts; ++i) { + histograms[self->last_histogram_ix_[0] + i] = combined_histo[i]; + last_entropy[i] = combined_entropy[i]; + if (split->num_types == 1) { + last_entropy[num_contexts + i] = last_entropy[i]; + } + HistogramClearLiteral(&histograms[self->curr_histogram_ix_ + i]); + } + self->block_size_ = 0; + if (++self->merge_last_count_ > 1) { + self->target_block_size_ += self->min_block_size_; + } + } + BROTLI_FREE(m, combined_histo); + } + if (is_final) { + *self->histograms_size_ = split->num_types * num_contexts; + split->num_blocks = self->num_blocks_; + } +} + +/* Adds the next symbol to the current block type and context. When the + current block reaches the target size, decides on merging the block. */ +static void ContextBlockSplitterAddSymbol( + ContextBlockSplitter* self, MemoryManager* m, + size_t symbol, size_t context) { + HistogramAddLiteral(&self->histograms_[self->curr_histogram_ix_ + context], + symbol); + ++self->block_size_; + if (self->block_size_ == self->target_block_size_) { + ContextBlockSplitterFinishBlock(self, m, /* is_final = */ BROTLI_FALSE); + if (BROTLI_IS_OOM(m)) return; + } +} + +static void MapStaticContexts(MemoryManager* m, + size_t num_contexts, + const uint32_t* static_context_map, + MetaBlockSplit* mb) { + size_t i; + BROTLI_DCHECK(mb->literal_context_map == 0); + mb->literal_context_map_size = + mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS; + mb->literal_context_map = + BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(mb->literal_context_map)) return; + + for (i = 0; i < mb->literal_split.num_types; ++i) { + uint32_t offset = (uint32_t)(i * num_contexts); + size_t j; + for (j = 0; j < (1u << BROTLI_LITERAL_CONTEXT_BITS); ++j) { + mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] = + offset + static_context_map[j]; + } + } +} + +typedef struct GreedyMetablockArena { + union { + BlockSplitterLiteral plain; + ContextBlockSplitter ctx; + } lit_blocks; + BlockSplitterCommand cmd_blocks; + BlockSplitterDistance dist_blocks; +} GreedyMetablockArena; + +static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal( + MemoryManager* m, GreedyMetablockArena* arena, const uint8_t* ringbuffer, + size_t pos, size_t mask, uint8_t prev_byte, uint8_t prev_byte2, + ContextLut literal_context_lut, const size_t num_contexts, + const uint32_t* static_context_map, const Command* commands, + size_t n_commands, MetaBlockSplit* mb) { + size_t num_literals = 0; + size_t i; + for (i = 0; i < n_commands; ++i) { + num_literals += commands[i].insert_len_; + } + + if (num_contexts == 1) { + InitBlockSplitterLiteral(m, &arena->lit_blocks.plain, 256, 512, 400.0, + num_literals, &mb->literal_split, &mb->literal_histograms, + &mb->literal_histograms_size); + } else { + InitContextBlockSplitter(m, &arena->lit_blocks.ctx, 256, num_contexts, 512, + 400.0, num_literals, &mb->literal_split, &mb->literal_histograms, + &mb->literal_histograms_size); + } + if (BROTLI_IS_OOM(m)) return; + InitBlockSplitterCommand(m, &arena->cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, + 1024, 500.0, n_commands, &mb->command_split, &mb->command_histograms, + &mb->command_histograms_size); + if (BROTLI_IS_OOM(m)) return; + InitBlockSplitterDistance(m, &arena->dist_blocks, 64, 512, 100.0, n_commands, + &mb->distance_split, &mb->distance_histograms, + &mb->distance_histograms_size); + if (BROTLI_IS_OOM(m)) return; + + for (i = 0; i < n_commands; ++i) { + const Command cmd = commands[i]; + size_t j; + BlockSplitterAddSymbolCommand(&arena->cmd_blocks, cmd.cmd_prefix_); + for (j = cmd.insert_len_; j != 0; --j) { + uint8_t literal = ringbuffer[pos & mask]; + if (num_contexts == 1) { + BlockSplitterAddSymbolLiteral(&arena->lit_blocks.plain, literal); + } else { + size_t context = + BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut); + ContextBlockSplitterAddSymbol(&arena->lit_blocks.ctx, m, literal, + static_context_map[context]); + if (BROTLI_IS_OOM(m)) return; + } + prev_byte2 = prev_byte; + prev_byte = literal; + ++pos; + } + pos += CommandCopyLen(&cmd); + if (CommandCopyLen(&cmd)) { + prev_byte2 = ringbuffer[(pos - 2) & mask]; + prev_byte = ringbuffer[(pos - 1) & mask]; + if (cmd.cmd_prefix_ >= 128) { + BlockSplitterAddSymbolDistance( + &arena->dist_blocks, cmd.dist_prefix_ & 0x3FF); + } + } + } + + if (num_contexts == 1) { + BlockSplitterFinishBlockLiteral( + &arena->lit_blocks.plain, /* is_final = */ BROTLI_TRUE); + } else { + ContextBlockSplitterFinishBlock( + &arena->lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE); + if (BROTLI_IS_OOM(m)) return; + } + BlockSplitterFinishBlockCommand( + &arena->cmd_blocks, /* is_final = */ BROTLI_TRUE); + BlockSplitterFinishBlockDistance( + &arena->dist_blocks, /* is_final = */ BROTLI_TRUE); + + if (num_contexts > 1) { + MapStaticContexts(m, num_contexts, static_context_map, mb); + } +} + +void BrotliBuildMetaBlockGreedy(MemoryManager* m, + const uint8_t* ringbuffer, + size_t pos, + size_t mask, + uint8_t prev_byte, + uint8_t prev_byte2, + ContextLut literal_context_lut, + size_t num_contexts, + const uint32_t* static_context_map, + const Command* commands, + size_t n_commands, + MetaBlockSplit* mb) { + GreedyMetablockArena* arena = BROTLI_ALLOC(m, GreedyMetablockArena, 1); + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return; + if (num_contexts == 1) { + BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask, + prev_byte, prev_byte2, literal_context_lut, 1, NULL, commands, + n_commands, mb); + } else { + BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask, + prev_byte, prev_byte2, literal_context_lut, num_contexts, + static_context_map, commands, n_commands, mb); + } + BROTLI_FREE(m, arena); +} + +void BrotliOptimizeHistograms(uint32_t num_distance_codes, + MetaBlockSplit* mb) { + uint8_t good_for_rle[BROTLI_NUM_COMMAND_SYMBOLS]; + size_t i; + for (i = 0; i < mb->literal_histograms_size; ++i) { + BrotliOptimizeHuffmanCountsForRle(256, mb->literal_histograms[i].data_, + good_for_rle); + } + for (i = 0; i < mb->command_histograms_size; ++i) { + BrotliOptimizeHuffmanCountsForRle(BROTLI_NUM_COMMAND_SYMBOLS, + mb->command_histograms[i].data_, + good_for_rle); + } + for (i = 0; i < mb->distance_histograms_size; ++i) { + BrotliOptimizeHuffmanCountsForRle(num_distance_codes, + mb->distance_histograms[i].data_, + good_for_rle); + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/metablock.h b/packages/runtime/vendor/brotli/c/enc/metablock.h new file mode 100644 index 000000000..e7d10526d --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/metablock.h @@ -0,0 +1,106 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Algorithms for distributing the literals and commands of a metablock between + block types and contexts. */ + +#ifndef BROTLI_ENC_METABLOCK_H_ +#define BROTLI_ENC_METABLOCK_H_ + +#include "../common/context.h" +#include "../common/platform.h" +#include "block_splitter.h" +#include "command.h" +#include "histogram.h" +#include "memory.h" +#include "params.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define BROTLI_MAX_STATIC_CONTEXTS 13 + +typedef struct MetaBlockSplit { + BlockSplit literal_split; + BlockSplit command_split; + BlockSplit distance_split; + uint32_t* literal_context_map; + size_t literal_context_map_size; + uint32_t* distance_context_map; + size_t distance_context_map_size; + HistogramLiteral* literal_histograms; + size_t literal_histograms_size; + HistogramCommand* command_histograms; + size_t command_histograms_size; + HistogramDistance* distance_histograms; + size_t distance_histograms_size; +} MetaBlockSplit; + +static BROTLI_INLINE void InitMetaBlockSplit(MetaBlockSplit* mb) { + BrotliInitBlockSplit(&mb->literal_split); + BrotliInitBlockSplit(&mb->command_split); + BrotliInitBlockSplit(&mb->distance_split); + mb->literal_context_map = 0; + mb->literal_context_map_size = 0; + mb->distance_context_map = 0; + mb->distance_context_map_size = 0; + mb->literal_histograms = 0; + mb->literal_histograms_size = 0; + mb->command_histograms = 0; + mb->command_histograms_size = 0; + mb->distance_histograms = 0; + mb->distance_histograms_size = 0; +} + +static BROTLI_INLINE void DestroyMetaBlockSplit( + MemoryManager* m, MetaBlockSplit* mb) { + BrotliDestroyBlockSplit(m, &mb->literal_split); + BrotliDestroyBlockSplit(m, &mb->command_split); + BrotliDestroyBlockSplit(m, &mb->distance_split); + BROTLI_FREE(m, mb->literal_context_map); + BROTLI_FREE(m, mb->distance_context_map); + BROTLI_FREE(m, mb->literal_histograms); + BROTLI_FREE(m, mb->command_histograms); + BROTLI_FREE(m, mb->distance_histograms); +} + +/* Uses the slow shortest-path block splitter and does context clustering. + The distance parameters are dynamically selected based on the commands + which get recomputed under the new distance parameters. The new distance + parameters are stored into *params. */ +BROTLI_INTERNAL void BrotliBuildMetaBlock(MemoryManager* m, + const uint8_t* ringbuffer, + const size_t pos, + const size_t mask, + BrotliEncoderParams* params, + uint8_t prev_byte, + uint8_t prev_byte2, + Command* cmds, + size_t num_commands, + ContextType literal_context_mode, + MetaBlockSplit* mb); + +/* Uses a fast greedy block splitter that tries to merge current block with the + last or the second last block and uses a static context clustering which + is the same for all block types. */ +BROTLI_INTERNAL void BrotliBuildMetaBlockGreedy( + MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask, + uint8_t prev_byte, uint8_t prev_byte2, ContextLut literal_context_lut, + size_t num_contexts, const uint32_t* static_context_map, + const Command* commands, size_t n_commands, MetaBlockSplit* mb); + +BROTLI_INTERNAL void BrotliOptimizeHistograms(uint32_t num_distance_codes, + MetaBlockSplit* mb); + +BROTLI_INTERNAL void BrotliInitDistanceParams(BrotliDistanceParams* params, + uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_METABLOCK_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/metablock_inc.h b/packages/runtime/vendor/brotli/c/enc/metablock_inc.h new file mode 100644 index 000000000..e7a7bc821 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/metablock_inc.h @@ -0,0 +1,185 @@ +/* NOLINT(build/header_guard) */ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* template parameters: FN */ + +#define HistogramType FN(Histogram) + +/* Greedy block splitter for one block category (literal, command or distance). +*/ +typedef struct FN(BlockSplitter) { + /* Alphabet size of particular block category. */ + size_t alphabet_size_; + /* We collect at least this many symbols for each block. */ + size_t min_block_size_; + /* We merge histograms A and B if + entropy(A+B) < entropy(A) + entropy(B) + split_threshold_, + where A is the current histogram and B is the histogram of the last or the + second last block type. */ + double split_threshold_; + + size_t num_blocks_; + BlockSplit* split_; /* not owned */ + HistogramType* histograms_; /* not owned */ + size_t* histograms_size_; /* not owned */ + + /* Temporary storage for BlockSplitterFinishBlock. */ + HistogramType combined_histo[2]; + + /* The number of symbols that we want to collect before deciding on whether + or not to merge the block with a previous one or emit a new block. */ + size_t target_block_size_; + /* The number of symbols in the current histogram. */ + size_t block_size_; + /* Offset of the current histogram. */ + size_t curr_histogram_ix_; + /* Offset of the histograms of the previous two block types. */ + size_t last_histogram_ix_[2]; + /* Entropy of the previous two block types. */ + double last_entropy_[2]; + /* The number of times we merged the current block with the last one. */ + size_t merge_last_count_; +} FN(BlockSplitter); + +static void FN(InitBlockSplitter)( + MemoryManager* m, FN(BlockSplitter)* self, size_t alphabet_size, + size_t min_block_size, double split_threshold, size_t num_symbols, + BlockSplit* split, HistogramType** histograms, size_t* histograms_size) { + size_t max_num_blocks = num_symbols / min_block_size + 1; + /* We have to allocate one more histogram than the maximum number of block + types for the current histogram when the meta-block is too big. */ + size_t max_num_types = + BROTLI_MIN(size_t, max_num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + 1); + self->alphabet_size_ = alphabet_size; + self->min_block_size_ = min_block_size; + self->split_threshold_ = split_threshold; + self->num_blocks_ = 0; + self->split_ = split; + self->histograms_size_ = histograms_size; + self->target_block_size_ = min_block_size; + self->block_size_ = 0; + self->curr_histogram_ix_ = 0; + self->merge_last_count_ = 0; + BROTLI_ENSURE_CAPACITY(m, uint8_t, + split->types, split->types_alloc_size, max_num_blocks); + BROTLI_ENSURE_CAPACITY(m, uint32_t, + split->lengths, split->lengths_alloc_size, max_num_blocks); + if (BROTLI_IS_OOM(m)) return; + self->split_->num_blocks = max_num_blocks; + BROTLI_DCHECK(*histograms == 0); + *histograms_size = max_num_types; + *histograms = BROTLI_ALLOC(m, HistogramType, *histograms_size); + self->histograms_ = *histograms; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(*histograms)) return; + /* Clear only current histogram. */ + FN(HistogramClear)(&self->histograms_[0]); + self->last_histogram_ix_[0] = self->last_histogram_ix_[1] = 0; +} + +/* Does either of three things: + (1) emits the current block with a new block type; + (2) emits the current block with the type of the second last block; + (3) merges the current block with the last block. */ +static void FN(BlockSplitterFinishBlock)( + FN(BlockSplitter)* self, BROTLI_BOOL is_final) { + BlockSplit* split = self->split_; + double* last_entropy = self->last_entropy_; + HistogramType* histograms = self->histograms_; + self->block_size_ = + BROTLI_MAX(size_t, self->block_size_, self->min_block_size_); + if (self->num_blocks_ == 0) { + /* Create first block. */ + split->lengths[0] = (uint32_t)self->block_size_; + split->types[0] = 0; + last_entropy[0] = + BrotliBitsEntropy(histograms[0].data_, self->alphabet_size_); + last_entropy[1] = last_entropy[0]; + ++self->num_blocks_; + ++split->num_types; + ++self->curr_histogram_ix_; + if (self->curr_histogram_ix_ < *self->histograms_size_) + FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); + self->block_size_ = 0; + } else if (self->block_size_ > 0) { + double entropy = BrotliBitsEntropy( + histograms[self->curr_histogram_ix_].data_, self->alphabet_size_); + double combined_entropy[2]; + double diff[2]; + size_t j; + for (j = 0; j < 2; ++j) { + size_t last_histogram_ix = self->last_histogram_ix_[j]; + self->combined_histo[j] = histograms[self->curr_histogram_ix_]; + FN(HistogramAddHistogram)(&self->combined_histo[j], + &histograms[last_histogram_ix]); + combined_entropy[j] = BrotliBitsEntropy( + &self->combined_histo[j].data_[0], self->alphabet_size_); + diff[j] = combined_entropy[j] - entropy - last_entropy[j]; + } + + if (split->num_types < BROTLI_MAX_NUMBER_OF_BLOCK_TYPES && + diff[0] > self->split_threshold_ && + diff[1] > self->split_threshold_) { + /* Create new block. */ + split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; + split->types[self->num_blocks_] = (uint8_t)split->num_types; + self->last_histogram_ix_[1] = self->last_histogram_ix_[0]; + self->last_histogram_ix_[0] = (uint8_t)split->num_types; + last_entropy[1] = last_entropy[0]; + last_entropy[0] = entropy; + ++self->num_blocks_; + ++split->num_types; + ++self->curr_histogram_ix_; + if (self->curr_histogram_ix_ < *self->histograms_size_) + FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); + self->block_size_ = 0; + self->merge_last_count_ = 0; + self->target_block_size_ = self->min_block_size_; + } else if (diff[1] < diff[0] - 20.0) { + /* Combine this block with second last block. */ + split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; + split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2]; + BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1); + histograms[self->last_histogram_ix_[0]] = self->combined_histo[1]; + last_entropy[1] = last_entropy[0]; + last_entropy[0] = combined_entropy[1]; + ++self->num_blocks_; + self->block_size_ = 0; + FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); + self->merge_last_count_ = 0; + self->target_block_size_ = self->min_block_size_; + } else { + /* Combine this block with last block. */ + split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_; + histograms[self->last_histogram_ix_[0]] = self->combined_histo[0]; + last_entropy[0] = combined_entropy[0]; + if (split->num_types == 1) { + last_entropy[1] = last_entropy[0]; + } + self->block_size_ = 0; + FN(HistogramClear)(&histograms[self->curr_histogram_ix_]); + if (++self->merge_last_count_ > 1) { + self->target_block_size_ += self->min_block_size_; + } + } + } + if (is_final) { + *self->histograms_size_ = split->num_types; + split->num_blocks = self->num_blocks_; + } +} + +/* Adds the next symbol to the current histogram. When the current histogram + reaches the target size, decides on merging the block. */ +static void FN(BlockSplitterAddSymbol)(FN(BlockSplitter)* self, size_t symbol) { + FN(HistogramAdd)(&self->histograms_[self->curr_histogram_ix_], symbol); + ++self->block_size_; + if (self->block_size_ == self->target_block_size_) { + FN(BlockSplitterFinishBlock)(self, /* is_final = */ BROTLI_FALSE); + } +} + +#undef HistogramType diff --git a/packages/runtime/vendor/brotli/c/enc/params.h b/packages/runtime/vendor/brotli/c/enc/params.h new file mode 100644 index 000000000..fd1de0096 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/params.h @@ -0,0 +1,46 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Parameters for the Brotli encoder with chosen quality levels. */ + +#ifndef BROTLI_ENC_PARAMS_H_ +#define BROTLI_ENC_PARAMS_H_ + +#include +#include "encoder_dict.h" + +typedef struct BrotliHasherParams { + int type; + int bucket_bits; + int block_bits; + int num_last_distances_to_check; +} BrotliHasherParams; + +typedef struct BrotliDistanceParams { + uint32_t distance_postfix_bits; + uint32_t num_direct_distance_codes; + uint32_t alphabet_size_max; + uint32_t alphabet_size_limit; + size_t max_distance; +} BrotliDistanceParams; + +/* Encoding parameters */ +typedef struct BrotliEncoderParams { + BrotliEncoderMode mode; + int quality; + int lgwin; + int lgblock; + size_t stream_offset; + size_t size_hint; + BROTLI_BOOL disable_literal_context_modeling; + BROTLI_BOOL large_window; + BrotliHasherParams hasher; + BrotliDistanceParams dist; + /* TODO(eustas): rename to BrotliShared... */ + SharedEncoderDictionary dictionary; +} BrotliEncoderParams; + +#endif /* BROTLI_ENC_PARAMS_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/prefix.h b/packages/runtime/vendor/brotli/c/enc/prefix.h new file mode 100644 index 000000000..24c0afc17 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/prefix.h @@ -0,0 +1,52 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Functions for encoding of integers into prefix codes the amount of extra + bits, and the actual values of the extra bits. */ + +#ifndef BROTLI_ENC_PREFIX_H_ +#define BROTLI_ENC_PREFIX_H_ + +#include "../common/constants.h" +#include "../common/platform.h" +#include "fast_log.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* Here distance_code is an intermediate code, i.e. one of the special codes or + the actual distance increased by BROTLI_NUM_DISTANCE_SHORT_CODES - 1. */ +static BROTLI_INLINE void PrefixEncodeCopyDistance(size_t distance_code, + size_t num_direct_codes, + size_t postfix_bits, + uint16_t* code, + uint32_t* extra_bits) { + if (distance_code < BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes) { + *code = (uint16_t)distance_code; + *extra_bits = 0; + return; + } else { + size_t dist = ((size_t)1 << (postfix_bits + 2u)) + + (distance_code - BROTLI_NUM_DISTANCE_SHORT_CODES - num_direct_codes); + size_t bucket = Log2FloorNonZero(dist) - 1; + size_t postfix_mask = (1u << postfix_bits) - 1; + size_t postfix = dist & postfix_mask; + size_t prefix = (dist >> bucket) & 1; + size_t offset = (2 + prefix) << bucket; + size_t nbits = bucket - postfix_bits; + *code = (uint16_t)((nbits << 10) | + (BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes + + ((2 * (nbits - 1) + prefix) << postfix_bits) + postfix)); + *extra_bits = (uint32_t)((dist - offset) >> postfix_bits); + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_PREFIX_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/quality.h b/packages/runtime/vendor/brotli/c/enc/quality.h new file mode 100644 index 000000000..45d67bf40 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/quality.h @@ -0,0 +1,209 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Constants and formulas that affect speed-ratio trade-offs and thus define + quality levels. */ + +#ifndef BROTLI_ENC_QUALITY_H_ +#define BROTLI_ENC_QUALITY_H_ + +#include "../common/platform.h" +#include +#include "params.h" + +#define FAST_ONE_PASS_COMPRESSION_QUALITY 0 +#define FAST_TWO_PASS_COMPRESSION_QUALITY 1 +#define ZOPFLIFICATION_QUALITY 10 +#define HQ_ZOPFLIFICATION_QUALITY 11 + +#define MAX_QUALITY_FOR_STATIC_ENTROPY_CODES 2 +#define MIN_QUALITY_FOR_BLOCK_SPLIT 4 +#define MIN_QUALITY_FOR_NONZERO_DISTANCE_PARAMS 4 +#define MIN_QUALITY_FOR_OPTIMIZE_HISTOGRAMS 4 +#define MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH 5 +#define MIN_QUALITY_FOR_CONTEXT_MODELING 5 +#define MIN_QUALITY_FOR_HQ_CONTEXT_MODELING 7 +#define MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING 10 + +/* For quality below MIN_QUALITY_FOR_BLOCK_SPLIT there is no block splitting, + so we buffer at most this much literals and commands. */ +#define MAX_NUM_DELAYED_SYMBOLS 0x2FFF + +/* Returns hash-table size for quality levels 0 and 1. */ +static BROTLI_INLINE size_t MaxHashTableSize(int quality) { + return quality == FAST_ONE_PASS_COMPRESSION_QUALITY ? 1 << 15 : 1 << 17; +} + +/* The maximum length for which the zopflification uses distinct distances. */ +#define MAX_ZOPFLI_LEN_QUALITY_10 150 +#define MAX_ZOPFLI_LEN_QUALITY_11 325 + +/* Do not thoroughly search when a long copy is found. */ +#define BROTLI_LONG_COPY_QUICK_STEP 16384 + +static BROTLI_INLINE size_t MaxZopfliLen(const BrotliEncoderParams* params) { + return params->quality <= 10 ? + MAX_ZOPFLI_LEN_QUALITY_10 : + MAX_ZOPFLI_LEN_QUALITY_11; +} + +/* Number of best candidates to evaluate to expand Zopfli chain. */ +static BROTLI_INLINE size_t MaxZopfliCandidates( + const BrotliEncoderParams* params) { + return params->quality <= 10 ? 1 : 5; +} + +static BROTLI_INLINE void SanitizeParams(BrotliEncoderParams* params) { + params->quality = BROTLI_MIN(int, BROTLI_MAX_QUALITY, + BROTLI_MAX(int, BROTLI_MIN_QUALITY, params->quality)); + if (params->quality <= MAX_QUALITY_FOR_STATIC_ENTROPY_CODES) { + params->large_window = BROTLI_FALSE; + } + if (params->lgwin < BROTLI_MIN_WINDOW_BITS) { + params->lgwin = BROTLI_MIN_WINDOW_BITS; + } else { + int max_lgwin = params->large_window ? BROTLI_LARGE_MAX_WINDOW_BITS : + BROTLI_MAX_WINDOW_BITS; + if (params->lgwin > max_lgwin) params->lgwin = max_lgwin; + } +} + +/* Returns optimized lg_block value. */ +static BROTLI_INLINE int ComputeLgBlock(const BrotliEncoderParams* params) { + int lgblock = params->lgblock; + if (params->quality == FAST_ONE_PASS_COMPRESSION_QUALITY || + params->quality == FAST_TWO_PASS_COMPRESSION_QUALITY) { + lgblock = params->lgwin; + } else if (params->quality < MIN_QUALITY_FOR_BLOCK_SPLIT) { + lgblock = 14; + } else if (lgblock == 0) { + lgblock = 16; + if (params->quality >= 9 && params->lgwin > lgblock) { + lgblock = BROTLI_MIN(int, 18, params->lgwin); + } + } else { + lgblock = BROTLI_MIN(int, BROTLI_MAX_INPUT_BLOCK_BITS, + BROTLI_MAX(int, BROTLI_MIN_INPUT_BLOCK_BITS, lgblock)); + } + return lgblock; +} + +/* Returns log2 of the size of main ring buffer area. + Allocate at least lgwin + 1 bits for the ring buffer so that the newly + added block fits there completely and we still get lgwin bits and at least + read_block_size_bits + 1 bits because the copy tail length needs to be + smaller than ring-buffer size. */ +static BROTLI_INLINE int ComputeRbBits(const BrotliEncoderParams* params) { + return 1 + BROTLI_MAX(int, params->lgwin, params->lgblock); +} + +static BROTLI_INLINE size_t MaxMetablockSize( + const BrotliEncoderParams* params) { + int bits = + BROTLI_MIN(int, ComputeRbBits(params), BROTLI_MAX_INPUT_BLOCK_BITS); + return (size_t)1 << bits; +} + +/* When searching for backward references and have not seen matches for a long + time, we can skip some match lookups. Unsuccessful match lookups are very + expensive and this kind of a heuristic speeds up compression quite a lot. + At first 8 byte strides are taken and every second byte is put to hasher. + After 4x more literals stride by 16 bytes, every put 4-th byte to hasher. + Applied only to qualities 2 to 9. */ +static BROTLI_INLINE size_t LiteralSpreeLengthForSparseSearch( + const BrotliEncoderParams* params) { + return params->quality < 9 ? 64 : 512; +} + +/* Quality to hasher mapping: + + - q02: h02 (longest_match_quickly), b16, l5 + + - q03: h03 (longest_match_quickly), b17, l5 + + - q04: h04 (longest_match_quickly), b17, l5 + - q04: h54 (longest_match_quickly), b20, l7 | for large files + + - q05: h58 (longest_match_simd ), b14, l4 + - q05: h68 (longest_match64_simd ), b15, l5 | for large files + - q05: h40 (forgetful_chain ), b15, l4 | for small window + + - q06: h58 (longest_match_simd ), b14, l4 + - q06: h68 (longest_match64_simd ), b15, l5 | for large files + - q06: h40 (forgetful_chain ), b15, l4 | for small window + + - q07: h58 (longest_match_simd ), b15, l4 + - q07: h68 (longest_match64_simd ), b15, l5 | for large files + - q07: h41 (forgetful_chain ), b15, l4 | for small window + + - q08: h05 (longest_match ), b15, l4 + - q08: h06 (longest_match64 ), b15, l5 | for large files + - q08: h41 (forgetful_chain ), b15, l4 | for small window + + - q09: h05 (longest_match ), b15, l4 + - q09: h06 (longest_match64 ), b15, l5 | for large files + - q09: h42 (forgetful_chain ), b15, l4 | for small window + + - q10: t10 (to_binary_tree ), b17, l128 + + - q11: t10 (to_binary_tree ), b17, l128 + + Where "q" is quality, "h" is hasher type, "b" is bucket bits, + "l" is source len. */ +static BROTLI_INLINE void ChooseHasher(const BrotliEncoderParams* params, + BrotliHasherParams* hparams) { + if (params->quality > 9) { + hparams->type = 10; + } else if (params->quality == 4 && params->size_hint >= (1 << 20)) { + hparams->type = 54; + } else if (params->quality < 5) { + hparams->type = params->quality; + } else if (params->lgwin <= 16) { + hparams->type = params->quality < 7 ? 40 : params->quality < 9 ? 41 : 42; + } else if (params->size_hint >= (1 << 20) && params->lgwin >= 19) { +#if defined(BROTLI_MAX_SIMD_QUALITY) + hparams->type = params->quality <= BROTLI_MAX_SIMD_QUALITY ? 68 : 6; +#else + hparams->type = 6; +#endif + hparams->block_bits = params->quality - 1; + hparams->bucket_bits = 15; + hparams->num_last_distances_to_check = + params->quality < 7 ? 4 : params->quality < 9 ? 10 : 16; + } else { + /* TODO(eustas): often previous setting (H6) is faster and denser; consider + adding an option to use it. */ +#if defined(BROTLI_MAX_SIMD_QUALITY) + hparams->type = params->quality <= BROTLI_MAX_SIMD_QUALITY ? 58 : 5; +#else + hparams->type = 5; +#endif + hparams->block_bits = params->quality - 1; + hparams->bucket_bits = params->quality < 7 ? 14 : 15; + hparams->num_last_distances_to_check = + params->quality < 7 ? 4 : params->quality < 9 ? 10 : 16; + } + + if (params->lgwin > 24) { + /* Different hashers for large window brotli: not for qualities <= 2, + these are too fast for large window. Not for qualities >= 10: their + hasher already works well with large window. So the changes are: + H3 --> H35: for quality 3. + H54 --> H55: for quality 4 with size hint > 1MB + H6/H68 --> H65: for qualities 5, 6, 7, 8, 9. */ + if (hparams->type == 3) { + hparams->type = 35; + } + if (hparams->type == 54) { + hparams->type = 55; + } + if (hparams->type == 6 || hparams->type == 68) { + hparams->type = 65; + } + } +} + +#endif /* BROTLI_ENC_QUALITY_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/ringbuffer.h b/packages/runtime/vendor/brotli/c/enc/ringbuffer.h new file mode 100644 index 000000000..5bb75ed7c --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/ringbuffer.h @@ -0,0 +1,165 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Sliding window over the input data. */ + +#ifndef BROTLI_ENC_RINGBUFFER_H_ +#define BROTLI_ENC_RINGBUFFER_H_ + +#include "../common/platform.h" +#include "memory.h" +#include "params.h" +#include "quality.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* A RingBuffer(window_bits, tail_bits) contains `1 << window_bits' bytes of + data in a circular manner: writing a byte writes it to: + `position() % (1 << window_bits)'. + For convenience, the RingBuffer array contains another copy of the + first `1 << tail_bits' bytes: + buffer_[i] == buffer_[i + (1 << window_bits)], if i < (1 << tail_bits), + and another copy of the last two bytes: + buffer_[-1] == buffer_[(1 << window_bits) - 1] and + buffer_[-2] == buffer_[(1 << window_bits) - 2]. */ +typedef struct RingBuffer { + /* Size of the ring-buffer is (1 << window_bits) + tail_size_. */ + const uint32_t size_; + const uint32_t mask_; + const uint32_t tail_size_; + const uint32_t total_size_; + + uint32_t cur_size_; + /* Position to write in the ring buffer. */ + uint32_t pos_; + /* The actual ring buffer containing the copy of the last two bytes, the data, + and the copy of the beginning as a tail. */ + uint8_t* data_; + /* The start of the ring-buffer. */ + uint8_t* buffer_; +} RingBuffer; + +static BROTLI_INLINE void RingBufferInit(RingBuffer* rb) { + rb->cur_size_ = 0; + rb->pos_ = 0; + rb->data_ = 0; + rb->buffer_ = 0; +} + +static BROTLI_INLINE void RingBufferSetup( + const BrotliEncoderParams* params, RingBuffer* rb) { + int window_bits = ComputeRbBits(params); + int tail_bits = params->lgblock; + *(uint32_t*)&rb->size_ = 1u << window_bits; + *(uint32_t*)&rb->mask_ = (1u << window_bits) - 1; + *(uint32_t*)&rb->tail_size_ = 1u << tail_bits; + *(uint32_t*)&rb->total_size_ = rb->size_ + rb->tail_size_; +} + +static BROTLI_INLINE void RingBufferFree(MemoryManager* m, RingBuffer* rb) { + BROTLI_FREE(m, rb->data_); +} + +/* Allocates or re-allocates data_ to the given length + plus some slack + region before and after. Fills the slack regions with zeros. */ +static BROTLI_INLINE void RingBufferInitBuffer( + MemoryManager* m, const uint32_t buflen, RingBuffer* rb) { + static const size_t kSlackForEightByteHashingEverywhere = 7; + uint8_t* new_data = BROTLI_ALLOC( + m, uint8_t, 2 + buflen + kSlackForEightByteHashingEverywhere); + size_t i; + if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_data)) return; + if (rb->data_) { + memcpy(new_data, rb->data_, + 2 + rb->cur_size_ + kSlackForEightByteHashingEverywhere); + BROTLI_FREE(m, rb->data_); + } + rb->data_ = new_data; + rb->cur_size_ = buflen; + rb->buffer_ = rb->data_ + 2; + rb->buffer_[-2] = rb->buffer_[-1] = 0; + for (i = 0; i < kSlackForEightByteHashingEverywhere; ++i) { + rb->buffer_[rb->cur_size_ + i] = 0; + } +} + +static BROTLI_INLINE void RingBufferWriteTail( + const uint8_t* bytes, size_t n, RingBuffer* rb) { + const size_t masked_pos = rb->pos_ & rb->mask_; + if (BROTLI_PREDICT_FALSE(masked_pos < rb->tail_size_)) { + /* Just fill the tail buffer with the beginning data. */ + const size_t p = rb->size_ + masked_pos; + memcpy(&rb->buffer_[p], bytes, + BROTLI_MIN(size_t, n, rb->tail_size_ - masked_pos)); + } +} + +/* Push bytes into the ring buffer. */ +static BROTLI_INLINE void RingBufferWrite( + MemoryManager* m, const uint8_t* bytes, size_t n, RingBuffer* rb) { + if (rb->pos_ == 0 && n < rb->tail_size_) { + /* Special case for the first write: to process the first block, we don't + need to allocate the whole ring-buffer and we don't need the tail + either. However, we do this memory usage optimization only if the + first write is less than the tail size, which is also the input block + size, otherwise it is likely that other blocks will follow and we + will need to reallocate to the full size anyway. */ + rb->pos_ = (uint32_t)n; + RingBufferInitBuffer(m, rb->pos_, rb); + if (BROTLI_IS_OOM(m)) return; + memcpy(rb->buffer_, bytes, n); + return; + } + if (rb->cur_size_ < rb->total_size_) { + /* Lazily allocate the full buffer. */ + RingBufferInitBuffer(m, rb->total_size_, rb); + if (BROTLI_IS_OOM(m)) return; + /* Initialize the last two bytes to zero, so that we don't have to worry + later when we copy the last two bytes to the first two positions. */ + rb->buffer_[rb->size_ - 2] = 0; + rb->buffer_[rb->size_ - 1] = 0; + /* Initialize tail; might be touched by "best_len++" optimization when + ring buffer is "full". */ + rb->buffer_[rb->size_] = 241; + } + { + const size_t masked_pos = rb->pos_ & rb->mask_; + /* The length of the writes is limited so that we do not need to worry + about a write */ + RingBufferWriteTail(bytes, n, rb); + if (BROTLI_PREDICT_TRUE(masked_pos + n <= rb->size_)) { + /* A single write fits. */ + memcpy(&rb->buffer_[masked_pos], bytes, n); + } else { + /* Split into two writes. + Copy into the end of the buffer, including the tail buffer. */ + memcpy(&rb->buffer_[masked_pos], bytes, + BROTLI_MIN(size_t, n, rb->total_size_ - masked_pos)); + /* Copy into the beginning of the buffer */ + memcpy(&rb->buffer_[0], bytes + (rb->size_ - masked_pos), + n - (rb->size_ - masked_pos)); + } + } + { + BROTLI_BOOL not_first_lap = (rb->pos_ & (1u << 31)) != 0; + uint32_t rb_pos_mask = (1u << 31) - 1; + rb->buffer_[-2] = rb->buffer_[rb->size_ - 2]; + rb->buffer_[-1] = rb->buffer_[rb->size_ - 1]; + rb->pos_ = (rb->pos_ & rb_pos_mask) + (uint32_t)(n & rb_pos_mask); + if (not_first_lap) { + /* Wrap, but preserve not-a-first-lap feature. */ + rb->pos_ |= 1u << 31; + } + } +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_RINGBUFFER_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/state.h b/packages/runtime/vendor/brotli/c/enc/state.h new file mode 100644 index 000000000..325f9f3dd --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/state.h @@ -0,0 +1,104 @@ +/* Copyright 2022 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Encoder state. */ + +#ifndef BROTLI_ENC_STATE_H_ +#define BROTLI_ENC_STATE_H_ + +#include "../common/constants.h" +#include "../common/platform.h" +#include "command.h" +#include "compress_fragment.h" +#include "compress_fragment_two_pass.h" +#include "hash.h" +#include "memory.h" +#include "params.h" +#include "ringbuffer.h" + +typedef enum BrotliEncoderStreamState { + /* Default state. */ + BROTLI_STREAM_PROCESSING = 0, + /* Intermediate state; after next block is emitted, byte-padding should be + performed before getting back to default state. */ + BROTLI_STREAM_FLUSH_REQUESTED = 1, + /* Last metablock was produced; no more input is acceptable. */ + BROTLI_STREAM_FINISHED = 2, + /* Flushing compressed block and writing meta-data block header. */ + BROTLI_STREAM_METADATA_HEAD = 3, + /* Writing metadata block body. */ + BROTLI_STREAM_METADATA_BODY = 4 +} BrotliEncoderStreamState; + +typedef enum BrotliEncoderFlintState { + BROTLI_FLINT_NEEDS_2_BYTES = 2, + BROTLI_FLINT_NEEDS_1_BYTE = 1, + BROTLI_FLINT_WAITING_FOR_PROCESSING = 0, + BROTLI_FLINT_WAITING_FOR_FLUSHING = -1, + BROTLI_FLINT_DONE = -2 +} BrotliEncoderFlintState; + +typedef struct BrotliEncoderStateStruct { + BrotliEncoderParams params; + + MemoryManager memory_manager_; + + uint64_t input_pos_; + RingBuffer ringbuffer_; + size_t cmd_alloc_size_; + Command* commands_; + size_t num_commands_; + size_t num_literals_; + size_t last_insert_len_; + uint64_t last_flush_pos_; + uint64_t last_processed_pos_; + int dist_cache_[BROTLI_NUM_DISTANCE_SHORT_CODES]; + int saved_dist_cache_[4]; + uint16_t last_bytes_; + uint8_t last_bytes_bits_; + /* "Flint" is a tiny uncompressed block emitted before the continuation + block to unwire literal context from previous data. Despite being int8_t, + field is actually BrotliEncoderFlintState enum. */ + int8_t flint_; + uint8_t prev_byte_; + uint8_t prev_byte2_; + size_t storage_size_; + uint8_t* storage_; + + Hasher hasher_; + + /* Hash table for FAST_ONE_PASS_COMPRESSION_QUALITY mode. */ + int small_table_[1 << 10]; /* 4KiB */ + int* large_table_; /* Allocated only when needed */ + size_t large_table_size_; + + BrotliOnePassArena* one_pass_arena_; + BrotliTwoPassArena* two_pass_arena_; + + /* Command and literal buffers for FAST_TWO_PASS_COMPRESSION_QUALITY. */ + uint32_t* command_buf_; + uint8_t* literal_buf_; + + uint64_t total_in_; + uint8_t* next_out_; + size_t available_out_; + uint64_t total_out_; + /* Temporary buffer for padding flush bits or metadata block header / body. */ + union { + uint64_t u64[2]; + uint8_t u8[16]; + } tiny_buf_; + uint32_t remaining_metadata_bytes_; + BrotliEncoderStreamState stream_state_; + + BROTLI_BOOL is_last_block_emitted_; + BROTLI_BOOL is_initialized_; +} BrotliEncoderStateStruct; + +typedef struct BrotliEncoderStateStruct BrotliEncoderStateInternal; +#define BrotliEncoderState BrotliEncoderStateInternal + +#endif // BROTLI_ENC_STATE_H_ diff --git a/packages/runtime/vendor/brotli/c/enc/static_dict.c b/packages/runtime/vendor/brotli/c/enc/static_dict.c new file mode 100644 index 000000000..854674600 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/static_dict.c @@ -0,0 +1,536 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "static_dict.h" + +#include "../common/dictionary.h" +#include "../common/platform.h" +#include "../common/transform.h" +#include "encoder_dict.h" +#include "find_match_length.h" +#include "hash_base.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static BROTLI_INLINE void AddMatch(size_t distance, size_t len, size_t len_code, + uint32_t* matches) { + uint32_t match = (uint32_t)((distance << 5) + len_code); + matches[len] = BROTLI_MIN(uint32_t, matches[len], match); +} + +static BROTLI_INLINE size_t DictMatchLength(const BrotliDictionary* dictionary, + const uint8_t* data, + size_t id, + size_t len, + size_t maxlen) { + const size_t offset = dictionary->offsets_by_length[len] + len * id; + return FindMatchLengthWithLimit(&dictionary->data[offset], data, + BROTLI_MIN(size_t, len, maxlen)); +} + +static BROTLI_INLINE BROTLI_BOOL IsMatch(const BrotliDictionary* dictionary, + DictWord w, const uint8_t* data, size_t max_length) { + if (w.len > max_length) { + return BROTLI_FALSE; + } else { + const size_t offset = dictionary->offsets_by_length[w.len] + + (size_t)w.len * (size_t)w.idx; + const uint8_t* dict = &dictionary->data[offset]; + if (w.transform == 0) { + /* Match against base dictionary word. */ + return + TO_BROTLI_BOOL(FindMatchLengthWithLimit(dict, data, w.len) == w.len); + } else if (w.transform == 10) { + /* Match against uppercase first transform. + Note that there are only ASCII uppercase words in the lookup table. */ + return TO_BROTLI_BOOL(dict[0] >= 'a' && dict[0] <= 'z' && + (dict[0] ^ 32) == data[0] && + FindMatchLengthWithLimit(&dict[1], &data[1], w.len - 1u) == + w.len - 1u); + } else { + /* Match against uppercase all transform. + Note that there are only ASCII uppercase words in the lookup table. */ + size_t i; + for (i = 0; i < w.len; ++i) { + if (dict[i] >= 'a' && dict[i] <= 'z') { + if ((dict[i] ^ 32) != data[i]) return BROTLI_FALSE; + } else { + if (dict[i] != data[i]) return BROTLI_FALSE; + } + } + return BROTLI_TRUE; + } + } +} + +/* Finds matches for a single static dictionary */ +static BROTLI_BOOL BrotliFindAllStaticDictionaryMatchesFor( + const BrotliEncoderDictionary* dictionary, const uint8_t* data, + size_t min_length, size_t max_length, uint32_t* matches) { + BROTLI_BOOL has_found_match = BROTLI_FALSE; +#if defined(BROTLI_EXPERIMENTAL) + if (dictionary->has_words_heavy) { + const BrotliTrieNode* node = &dictionary->trie.root; + size_t l = 0; + while (node && l < max_length) { + uint8_t c; + if (l >= min_length && node->len_) { + AddMatch(node->idx_, l, node->len_, matches); + has_found_match = BROTLI_TRUE; + } + c = data[l++]; + node = BrotliTrieSub(&dictionary->trie, node, c); + } + return has_found_match; + } +#endif /* BROTLI_EXPERIMENTAL */ + { + size_t offset = dictionary->buckets[Hash15(data)]; + BROTLI_BOOL end = !offset; + while (!end) { + DictWord w = dictionary->dict_words[offset++]; + const size_t l = w.len & 0x1F; + const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; + const size_t id = w.idx; + end = !!(w.len & 0x80); + w.len = (uint8_t)l; + if (w.transform == 0) { + const size_t matchlen = + DictMatchLength(dictionary->words, data, id, l, max_length); + const uint8_t* s; + size_t minlen; + size_t maxlen; + size_t len; + /* Transform "" + BROTLI_TRANSFORM_IDENTITY + "" */ + if (matchlen == l) { + AddMatch(id, l, l, matches); + has_found_match = BROTLI_TRUE; + } + /* Transforms "" + BROTLI_TRANSFORM_OMIT_LAST_1 + "" and + "" + BROTLI_TRANSFORM_OMIT_LAST_1 + "ing " */ + if (matchlen >= l - 1) { + AddMatch(id + 12 * n, l - 1, l, matches); + if (l + 2 < max_length && + data[l - 1] == 'i' && data[l] == 'n' && data[l + 1] == 'g' && + data[l + 2] == ' ') { + AddMatch(id + 49 * n, l + 3, l, matches); + } + has_found_match = BROTLI_TRUE; + } + /* Transform "" + BROTLI_TRANSFORM_OMIT_LAST_# + "" (# = 2 .. 9) */ + minlen = min_length; + if (l > 9) minlen = BROTLI_MAX(size_t, minlen, l - 9); + maxlen = BROTLI_MIN(size_t, matchlen, l - 2); + for (len = minlen; len <= maxlen; ++len) { + size_t cut = l - len; + size_t transform_id = (cut << 2) + + (size_t)((dictionary->cutoffTransforms >> (cut * 6)) & 0x3F); + AddMatch(id + transform_id * n, len, l, matches); + has_found_match = BROTLI_TRUE; + } + if (matchlen < l || l + 6 >= max_length) { + continue; + } + s = &data[l]; + /* Transforms "" + BROTLI_TRANSFORM_IDENTITY + */ + if (s[0] == ' ') { + AddMatch(id + n, l + 1, l, matches); + if (s[1] == 'a') { + if (s[2] == ' ') { + AddMatch(id + 28 * n, l + 3, l, matches); + } else if (s[2] == 's') { + if (s[3] == ' ') AddMatch(id + 46 * n, l + 4, l, matches); + } else if (s[2] == 't') { + if (s[3] == ' ') AddMatch(id + 60 * n, l + 4, l, matches); + } else if (s[2] == 'n') { + if (s[3] == 'd' && s[4] == ' ') { + AddMatch(id + 10 * n, l + 5, l, matches); + } + } + } else if (s[1] == 'b') { + if (s[2] == 'y' && s[3] == ' ') { + AddMatch(id + 38 * n, l + 4, l, matches); + } + } else if (s[1] == 'i') { + if (s[2] == 'n') { + if (s[3] == ' ') AddMatch(id + 16 * n, l + 4, l, matches); + } else if (s[2] == 's') { + if (s[3] == ' ') AddMatch(id + 47 * n, l + 4, l, matches); + } + } else if (s[1] == 'f') { + if (s[2] == 'o') { + if (s[3] == 'r' && s[4] == ' ') { + AddMatch(id + 25 * n, l + 5, l, matches); + } + } else if (s[2] == 'r') { + if (s[3] == 'o' && s[4] == 'm' && s[5] == ' ') { + AddMatch(id + 37 * n, l + 6, l, matches); + } + } + } else if (s[1] == 'o') { + if (s[2] == 'f') { + if (s[3] == ' ') AddMatch(id + 8 * n, l + 4, l, matches); + } else if (s[2] == 'n') { + if (s[3] == ' ') AddMatch(id + 45 * n, l + 4, l, matches); + } + } else if (s[1] == 'n') { + if (s[2] == 'o' && s[3] == 't' && s[4] == ' ') { + AddMatch(id + 80 * n, l + 5, l, matches); + } + } else if (s[1] == 't') { + if (s[2] == 'h') { + if (s[3] == 'e') { + if (s[4] == ' ') AddMatch(id + 5 * n, l + 5, l, matches); + } else if (s[3] == 'a') { + if (s[4] == 't' && s[5] == ' ') { + AddMatch(id + 29 * n, l + 6, l, matches); + } + } + } else if (s[2] == 'o') { + if (s[3] == ' ') AddMatch(id + 17 * n, l + 4, l, matches); + } + } else if (s[1] == 'w') { + if (s[2] == 'i' && s[3] == 't' && s[4] == 'h' && s[5] == ' ') { + AddMatch(id + 35 * n, l + 6, l, matches); + } + } + } else if (s[0] == '"') { + AddMatch(id + 19 * n, l + 1, l, matches); + if (s[1] == '>') { + AddMatch(id + 21 * n, l + 2, l, matches); + } + } else if (s[0] == '.') { + AddMatch(id + 20 * n, l + 1, l, matches); + if (s[1] == ' ') { + AddMatch(id + 31 * n, l + 2, l, matches); + if (s[2] == 'T' && s[3] == 'h') { + if (s[4] == 'e') { + if (s[5] == ' ') AddMatch(id + 43 * n, l + 6, l, matches); + } else if (s[4] == 'i') { + if (s[5] == 's' && s[6] == ' ') { + AddMatch(id + 75 * n, l + 7, l, matches); + } + } + } + } + } else if (s[0] == ',') { + AddMatch(id + 76 * n, l + 1, l, matches); + if (s[1] == ' ') { + AddMatch(id + 14 * n, l + 2, l, matches); + } + } else if (s[0] == '\n') { + AddMatch(id + 22 * n, l + 1, l, matches); + if (s[1] == '\t') { + AddMatch(id + 50 * n, l + 2, l, matches); + } + } else if (s[0] == ']') { + AddMatch(id + 24 * n, l + 1, l, matches); + } else if (s[0] == '\'') { + AddMatch(id + 36 * n, l + 1, l, matches); + } else if (s[0] == ':') { + AddMatch(id + 51 * n, l + 1, l, matches); + } else if (s[0] == '(') { + AddMatch(id + 57 * n, l + 1, l, matches); + } else if (s[0] == '=') { + if (s[1] == '"') { + AddMatch(id + 70 * n, l + 2, l, matches); + } else if (s[1] == '\'') { + AddMatch(id + 86 * n, l + 2, l, matches); + } + } else if (s[0] == 'a') { + if (s[1] == 'l' && s[2] == ' ') { + AddMatch(id + 84 * n, l + 3, l, matches); + } + } else if (s[0] == 'e') { + if (s[1] == 'd') { + if (s[2] == ' ') AddMatch(id + 53 * n, l + 3, l, matches); + } else if (s[1] == 'r') { + if (s[2] == ' ') AddMatch(id + 82 * n, l + 3, l, matches); + } else if (s[1] == 's') { + if (s[2] == 't' && s[3] == ' ') { + AddMatch(id + 95 * n, l + 4, l, matches); + } + } + } else if (s[0] == 'f') { + if (s[1] == 'u' && s[2] == 'l' && s[3] == ' ') { + AddMatch(id + 90 * n, l + 4, l, matches); + } + } else if (s[0] == 'i') { + if (s[1] == 'v') { + if (s[2] == 'e' && s[3] == ' ') { + AddMatch(id + 92 * n, l + 4, l, matches); + } + } else if (s[1] == 'z') { + if (s[2] == 'e' && s[3] == ' ') { + AddMatch(id + 100 * n, l + 4, l, matches); + } + } + } else if (s[0] == 'l') { + if (s[1] == 'e') { + if (s[2] == 's' && s[3] == 's' && s[4] == ' ') { + AddMatch(id + 93 * n, l + 5, l, matches); + } + } else if (s[1] == 'y') { + if (s[2] == ' ') AddMatch(id + 61 * n, l + 3, l, matches); + } + } else if (s[0] == 'o') { + if (s[1] == 'u' && s[2] == 's' && s[3] == ' ') { + AddMatch(id + 106 * n, l + 4, l, matches); + } + } + } else { + /* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and + is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL) + transform. */ + const BROTLI_BOOL is_all_caps = + TO_BROTLI_BOOL(w.transform != BROTLI_TRANSFORM_UPPERCASE_FIRST); + const uint8_t* s; + if (!IsMatch(dictionary->words, w, data, max_length)) { + continue; + } + /* Transform "" + kUppercase{First,All} + "" */ + AddMatch(id + (is_all_caps ? 44 : 9) * n, l, l, matches); + has_found_match = BROTLI_TRUE; + if (l + 1 >= max_length) { + continue; + } + /* Transforms "" + kUppercase{First,All} + */ + s = &data[l]; + if (s[0] == ' ') { + AddMatch(id + (is_all_caps ? 68 : 4) * n, l + 1, l, matches); + } else if (s[0] == '"') { + AddMatch(id + (is_all_caps ? 87 : 66) * n, l + 1, l, matches); + if (s[1] == '>') { + AddMatch(id + (is_all_caps ? 97 : 69) * n, l + 2, l, matches); + } + } else if (s[0] == '.') { + AddMatch(id + (is_all_caps ? 101 : 79) * n, l + 1, l, matches); + if (s[1] == ' ') { + AddMatch(id + (is_all_caps ? 114 : 88) * n, l + 2, l, matches); + } + } else if (s[0] == ',') { + AddMatch(id + (is_all_caps ? 112 : 99) * n, l + 1, l, matches); + if (s[1] == ' ') { + AddMatch(id + (is_all_caps ? 107 : 58) * n, l + 2, l, matches); + } + } else if (s[0] == '\'') { + AddMatch(id + (is_all_caps ? 94 : 74) * n, l + 1, l, matches); + } else if (s[0] == '(') { + AddMatch(id + (is_all_caps ? 113 : 78) * n, l + 1, l, matches); + } else if (s[0] == '=') { + if (s[1] == '"') { + AddMatch(id + (is_all_caps ? 105 : 104) * n, l + 2, l, matches); + } else if (s[1] == '\'') { + AddMatch(id + (is_all_caps ? 116 : 108) * n, l + 2, l, matches); + } + } + } + } + } + /* Transforms with prefixes " " and "." */ + if (max_length >= 5 && (data[0] == ' ' || data[0] == '.')) { + BROTLI_BOOL is_space = TO_BROTLI_BOOL(data[0] == ' '); + size_t offset = dictionary->buckets[Hash15(&data[1])]; + BROTLI_BOOL end = !offset; + while (!end) { + DictWord w = dictionary->dict_words[offset++]; + const size_t l = w.len & 0x1F; + const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; + const size_t id = w.idx; + end = !!(w.len & 0x80); + w.len = (uint8_t)l; + if (w.transform == 0) { + const uint8_t* s; + if (!IsMatch(dictionary->words, w, &data[1], max_length - 1)) { + continue; + } + /* Transforms " " + BROTLI_TRANSFORM_IDENTITY + "" and + "." + BROTLI_TRANSFORM_IDENTITY + "" */ + AddMatch(id + (is_space ? 6 : 32) * n, l + 1, l, matches); + has_found_match = BROTLI_TRUE; + if (l + 2 >= max_length) { + continue; + } + /* Transforms " " + BROTLI_TRANSFORM_IDENTITY + and + "." + BROTLI_TRANSFORM_IDENTITY + + */ + s = &data[l + 1]; + if (s[0] == ' ') { + AddMatch(id + (is_space ? 2 : 77) * n, l + 2, l, matches); + } else if (s[0] == '(') { + AddMatch(id + (is_space ? 89 : 67) * n, l + 2, l, matches); + } else if (is_space) { + if (s[0] == ',') { + AddMatch(id + 103 * n, l + 2, l, matches); + if (s[1] == ' ') { + AddMatch(id + 33 * n, l + 3, l, matches); + } + } else if (s[0] == '.') { + AddMatch(id + 71 * n, l + 2, l, matches); + if (s[1] == ' ') { + AddMatch(id + 52 * n, l + 3, l, matches); + } + } else if (s[0] == '=') { + if (s[1] == '"') { + AddMatch(id + 81 * n, l + 3, l, matches); + } else if (s[1] == '\'') { + AddMatch(id + 98 * n, l + 3, l, matches); + } + } + } + } else if (is_space) { + /* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and + is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL) + transform. */ + const BROTLI_BOOL is_all_caps = + TO_BROTLI_BOOL(w.transform != BROTLI_TRANSFORM_UPPERCASE_FIRST); + const uint8_t* s; + if (!IsMatch(dictionary->words, w, &data[1], max_length - 1)) { + continue; + } + /* Transforms " " + kUppercase{First,All} + "" */ + AddMatch(id + (is_all_caps ? 85 : 30) * n, l + 1, l, matches); + has_found_match = BROTLI_TRUE; + if (l + 2 >= max_length) { + continue; + } + /* Transforms " " + kUppercase{First,All} + */ + s = &data[l + 1]; + if (s[0] == ' ') { + AddMatch(id + (is_all_caps ? 83 : 15) * n, l + 2, l, matches); + } else if (s[0] == ',') { + if (!is_all_caps) { + AddMatch(id + 109 * n, l + 2, l, matches); + } + if (s[1] == ' ') { + AddMatch(id + (is_all_caps ? 111 : 65) * n, l + 3, l, matches); + } + } else if (s[0] == '.') { + AddMatch(id + (is_all_caps ? 115 : 96) * n, l + 2, l, matches); + if (s[1] == ' ') { + AddMatch(id + (is_all_caps ? 117 : 91) * n, l + 3, l, matches); + } + } else if (s[0] == '=') { + if (s[1] == '"') { + AddMatch(id + (is_all_caps ? 110 : 118) * n, l + 3, l, matches); + } else if (s[1] == '\'') { + AddMatch(id + (is_all_caps ? 119 : 120) * n, l + 3, l, matches); + } + } + } + } + } + if (max_length >= 6) { + /* Transforms with prefixes "e ", "s ", ", " and "\xC2\xA0" */ + if ((data[1] == ' ' && + (data[0] == 'e' || data[0] == 's' || data[0] == ',')) || + (data[0] == 0xC2 && data[1] == 0xA0)) { + size_t offset = dictionary->buckets[Hash15(&data[2])]; + BROTLI_BOOL end = !offset; + while (!end) { + DictWord w = dictionary->dict_words[offset++]; + const size_t l = w.len & 0x1F; + const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; + const size_t id = w.idx; + end = !!(w.len & 0x80); + w.len = (uint8_t)l; + if (w.transform == 0 && + IsMatch(dictionary->words, w, &data[2], max_length - 2)) { + if (data[0] == 0xC2) { + AddMatch(id + 102 * n, l + 2, l, matches); + has_found_match = BROTLI_TRUE; + } else if (l + 2 < max_length && data[l + 2] == ' ') { + size_t t = data[0] == 'e' ? 18 : (data[0] == 's' ? 7 : 13); + AddMatch(id + t * n, l + 3, l, matches); + has_found_match = BROTLI_TRUE; + } + } + } + } + } + if (max_length >= 9) { + /* Transforms with prefixes " the " and ".com/" */ + if ((data[0] == ' ' && data[1] == 't' && data[2] == 'h' && + data[3] == 'e' && data[4] == ' ') || + (data[0] == '.' && data[1] == 'c' && data[2] == 'o' && + data[3] == 'm' && data[4] == '/')) { + size_t offset = dictionary->buckets[Hash15(&data[5])]; + BROTLI_BOOL end = !offset; + while (!end) { + DictWord w = dictionary->dict_words[offset++]; + const size_t l = w.len & 0x1F; + const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l]; + const size_t id = w.idx; + end = !!(w.len & 0x80); + w.len = (uint8_t)l; + if (w.transform == 0 && + IsMatch(dictionary->words, w, &data[5], max_length - 5)) { + AddMatch(id + (data[0] == ' ' ? 41 : 72) * n, l + 5, l, matches); + has_found_match = BROTLI_TRUE; + if (l + 5 < max_length) { + const uint8_t* s = &data[l + 5]; + if (data[0] == ' ') { + if (l + 8 < max_length && + s[0] == ' ' && s[1] == 'o' && s[2] == 'f' && s[3] == ' ') { + AddMatch(id + 62 * n, l + 9, l, matches); + if (l + 12 < max_length && + s[4] == 't' && s[5] == 'h' && s[6] == 'e' && s[7] == ' ') { + AddMatch(id + 73 * n, l + 13, l, matches); + } + } + } + } + } + } + } + } + return has_found_match; +} + +/* Finds matches for one or more dictionaries, if multiple are present + in the contextual dictionary */ +BROTLI_BOOL BrotliFindAllStaticDictionaryMatches( + const BrotliEncoderDictionary* dictionary, const uint8_t* data, + size_t min_length, size_t max_length, uint32_t* matches) { + BROTLI_BOOL has_found_match = + BrotliFindAllStaticDictionaryMatchesFor( + dictionary, data, min_length, max_length, matches); + + if (!!dictionary->parent && dictionary->parent->num_dictionaries > 1) { + uint32_t matches2[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1]; + int l; + const BrotliEncoderDictionary* dictionary2 = dictionary->parent->dict[0]; + if (dictionary2 == dictionary) { + dictionary2 = dictionary->parent->dict[1]; + } + + for (l = 0; l < BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1; l++) { + matches2[l] = kInvalidMatch; + } + + has_found_match |= BrotliFindAllStaticDictionaryMatchesFor( + dictionary2, data, min_length, max_length, matches2); + + for (l = 0; l < BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1; l++) { + if (matches2[l] != kInvalidMatch) { + uint32_t dist = (uint32_t)(matches2[l] >> 5); + uint32_t len_code = matches2[l] & 31; + uint32_t skipdist = (uint32_t)((uint32_t)(1 << dictionary->words-> + size_bits_by_length[len_code]) & ~1u) * + (uint32_t)dictionary->num_transforms; + /* TODO(lode): check for dist overflow */ + dist += skipdist; + AddMatch(dist, (size_t)l, len_code, matches); + } + } + } + return has_found_match; +} +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/static_dict.h b/packages/runtime/vendor/brotli/c/enc/static_dict.h new file mode 100644 index 000000000..bc69b1591 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/static_dict.h @@ -0,0 +1,39 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Class to model the static dictionary. */ + +#ifndef BROTLI_ENC_STATIC_DICT_H_ +#define BROTLI_ENC_STATIC_DICT_H_ + +#include "../common/platform.h" + +#include "encoder_dict.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN 37 +static const uint32_t kInvalidMatch = 0xFFFFFFF; + +/* Matches data against static dictionary words, and for each length l, + for which a match is found, updates matches[l] to be the minimum possible + (distance << 5) + len_code. + Returns 1 if matches have been found, otherwise 0. + Prerequisites: + matches array is at least BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1 long + all elements are initialized to kInvalidMatch */ +BROTLI_INTERNAL BROTLI_BOOL BrotliFindAllStaticDictionaryMatches( + const BrotliEncoderDictionary* dictionary, + const uint8_t* data, size_t min_length, size_t max_length, + uint32_t* matches); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_STATIC_DICT_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/static_dict_lut.c b/packages/runtime/vendor/brotli/c/enc/static_dict_lut.c new file mode 100644 index 000000000..109366f60 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/static_dict_lut.c @@ -0,0 +1,224 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Lookup table for static dictionary and transforms. */ + +#include "static_dict_lut.h" + +#include "../common/platform.h" /* IWYU pragma: keep */ +#include "../common/static_init.h" + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +#include "../common/dictionary.h" +#include "../common/transform.h" +#include "hash_base.h" +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) + +/* TODO(eustas): deal with largest bucket(s). Not it contains 163 items. */ +static BROTLI_BOOL BROTLI_COLD DoBrotliEncoderInitStaticDictionaryLut( + const BrotliDictionary* dict, uint16_t* buckets, DictWord* words, + void* arena) { + DictWord* slots = (DictWord*)arena; + uint16_t* heads = (uint16_t*)(slots + BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS); + uint16_t* counts = heads + BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS; + uint16_t* prev = counts + BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS; + size_t next_slot = 0; + uint8_t transformed_word[24]; + uint8_t transformed_other_word[24]; + size_t l; + size_t pos; + size_t i; + + memset(counts, 0, BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS * sizeof(uint16_t)); + memset(heads, 0, BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS * sizeof(uint16_t)); + memset(prev, 0, BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS * sizeof(uint16_t)); + + for (l = 4; l <= 24; ++l) { + size_t n = 1u << dict->size_bits_by_length[l]; + const uint8_t* dict_words = dict->data + dict->offsets_by_length[l]; + for (i = 0; i < n; ++i) { + const uint8_t* dict_word = dict_words + l * i; + uint32_t key = Hash15(dict_word); + slots[next_slot].len = (uint8_t)l; + slots[next_slot].transform = BROTLI_TRANSFORM_IDENTITY; + slots[next_slot].idx = (uint16_t)i; + prev[next_slot] = heads[key]; + heads[key] = (uint16_t)next_slot; + counts[key]++; + ++next_slot; + } + for (i = 0; i < n; ++i) { + uint32_t key; + uint32_t prefix; + BROTLI_BOOL found; + size_t curr; + const uint8_t* dict_word = dict_words + l * i; + if (dict_word[0] < 'a' || dict_word[0] > 'z') continue; + memcpy(transformed_word, dict_word, l); + transformed_word[0] = transformed_word[0] - 32; + key = Hash15(transformed_word); + prefix = BROTLI_UNALIGNED_LOAD32LE(transformed_word) & ~0x20202020u; + found = BROTLI_FALSE; + curr = heads[key]; + while (curr != 0) { + const uint8_t* other_word; + uint32_t other_prefix; + if (slots[curr].len != l) break; + other_word = dict_words + l * slots[curr].idx; + other_prefix = BROTLI_UNALIGNED_LOAD32LE(other_word) & ~0x20202020u; + if (prefix == other_prefix) { + if (memcmp(transformed_word, other_word, l) == 0) { + found = BROTLI_TRUE; + break; + } + } + curr = prev[curr]; + } + if (found) continue; + slots[next_slot].len = (uint8_t)l; + slots[next_slot].transform = BROTLI_TRANSFORM_UPPERCASE_FIRST; + slots[next_slot].idx = (uint16_t)i; + prev[next_slot] = heads[key]; + heads[key] = (uint16_t)next_slot; + counts[key]++; + ++next_slot; + } + for (i = 0; i < n; ++i) { + const uint8_t* dict_word = dict_words + l * i; + BROTLI_BOOL is_ascii = BROTLI_TRUE; + BROTLI_BOOL has_lower = BROTLI_FALSE; + size_t k; + uint32_t prefix; + uint32_t key; + size_t curr; + BROTLI_BOOL found; + for (k = 0; k < l; ++k) { + if (dict_word[k] >= 128) is_ascii = BROTLI_FALSE; + if (k > 0 && dict_word[k] >= 'a' && dict_word[k] <= 'z') + has_lower = BROTLI_TRUE; + } + if (!is_ascii || !has_lower) continue; + memcpy(transformed_word, dict_word, l); + prefix = BROTLI_UNALIGNED_LOAD32LE(transformed_word) & ~0x20202020u; + for (k = 0; k < l; ++k) { + if (transformed_word[k] >= 'a' && transformed_word[k] <= 'z') { + transformed_word[k] = transformed_word[k] - 32; + } + } + key = Hash15(transformed_word); + found = BROTLI_FALSE; + curr = heads[key]; + while (curr != 0) { + const uint8_t* other_word; + uint32_t other_prefix; + if (slots[curr].len != l) break; + other_word = dict_words + l * slots[curr].idx; + other_prefix = BROTLI_UNALIGNED_LOAD32LE(other_word) & ~0x20202020u; + if (prefix == other_prefix) { + if (slots[curr].transform == BROTLI_TRANSFORM_IDENTITY) { + if (memcmp(transformed_word, other_word, l) == 0) { + found = BROTLI_TRUE; + break; + } + } else if (slots[curr].transform == + BROTLI_TRANSFORM_UPPERCASE_FIRST) { + if ((transformed_word[0] == (other_word[0] - 32)) && + memcmp(transformed_word + 1, other_word + 1, l - 1) == 0) { + found = BROTLI_TRUE; + break; + } + } else { + for (k = 0; k < l; ++k) { + if (other_word[k] >= 'a' && other_word[k] <= 'z') { + transformed_other_word[k] = other_word[k] - 32; + } else { + transformed_other_word[k] = other_word[k]; + } + } + if (memcmp(transformed_word, transformed_other_word, l) == 0) { + found = BROTLI_TRUE; + break; + } + } + } + curr = prev[curr]; + } + if (found) { + continue; + } + slots[next_slot].len = (uint8_t)l; + slots[next_slot].transform = BROTLI_TRANSFORM_UPPERCASE_ALL; + slots[next_slot].idx = (uint16_t)i; + prev[next_slot] = heads[key]; + heads[key] = (uint16_t)next_slot; + counts[key]++; + ++next_slot; + } + } + + if (next_slot != 31704) return BROTLI_FALSE; + pos = 0; + /* Unused; makes offsets start from 1. */ + words[pos].len = 0; + words[pos].transform = 0; + words[pos].idx = 0; + pos++; + for (i = 0; i < BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS; ++i) { + size_t num_words = counts[i]; + size_t curr; + if (num_words == 0) { + buckets[i] = 0; + continue; + } + buckets[i] = (uint16_t)pos; + curr = heads[i]; + pos += num_words; + for (size_t k = 0; k < num_words; ++k) { + words[pos - 1 - k] = slots[curr]; + curr = prev[curr]; + } + words[pos - 1].len |= 0x80; + } + return BROTLI_TRUE; +} + +BROTLI_BOOL BrotliEncoderInitStaticDictionaryLut( + const BrotliDictionary* dict, uint16_t* buckets, DictWord* words) { + size_t arena_size = + BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS * + (sizeof(uint16_t) + sizeof(DictWord)) + + BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS * 2 * sizeof(uint16_t); + void* arena = malloc(arena_size); + BROTLI_BOOL ok; + if (arena == NULL) { + return BROTLI_FALSE; + } + ok = DoBrotliEncoderInitStaticDictionaryLut(dict, buckets, words, arena); + free(arena); + return ok; +} + +BROTLI_MODEL("small") +uint16_t kStaticDictionaryBuckets[BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS]; +BROTLI_MODEL("small") +DictWord kStaticDictionaryWords[BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS]; + +#else /* BROTLI_STATIC_INIT */ + +/* Embed kStaticDictionaryBuckets and kStaticDictionaryWords. */ +#include "static_dict_lut_inc.h" + +#endif /* BROTLI_STATIC_INIT */ + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/static_dict_lut.h b/packages/runtime/vendor/brotli/c/enc/static_dict_lut.h new file mode 100644 index 000000000..078f55424 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/static_dict_lut.h @@ -0,0 +1,49 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Lookup table for static dictionary and transforms. */ + +#ifndef BROTLI_ENC_STATIC_DICT_LUT_H_ +#define BROTLI_ENC_STATIC_DICT_LUT_H_ + +#include "../common/dictionary.h" +#include "../common/platform.h" +#include "../common/static_init.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct DictWord { + /* Highest bit is used to indicate end of bucket. */ + uint8_t len; + uint8_t transform; + uint16_t idx; +} DictWord; + +/* Buckets are Hash15 results. */ +#define BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS 32768 +#define BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS 31705 + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +BROTLI_INTERNAL BROTLI_BOOL BrotliEncoderInitStaticDictionaryLut( + const BrotliDictionary* dictionary, uint16_t* buckets, DictWord* words); +BROTLI_INTERNAL extern BROTLI_MODEL("small") uint16_t + kStaticDictionaryBuckets[BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS]; +BROTLI_INTERNAL extern BROTLI_MODEL("small") DictWord + kStaticDictionaryWords[BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS]; +#else +BROTLI_INTERNAL extern const BROTLI_MODEL("small") uint16_t + kStaticDictionaryBuckets[BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS]; +BROTLI_INTERNAL extern const BROTLI_MODEL("small") DictWord + kStaticDictionaryWords[BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS]; +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_STATIC_DICT_LUT_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/static_dict_lut_inc.h b/packages/runtime/vendor/brotli/c/enc/static_dict_lut_inc.h new file mode 100644 index 000000000..fea97f8d7 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/static_dict_lut_inc.h @@ -0,0 +1,5830 @@ +const BROTLI_MODEL("small") +uint16_t kStaticDictionaryBuckets[BROTLI_ENC_STATIC_DICT_LUT_NUM_BUCKETS] = { +1,0,0,0,0,0,0,0,0,3,6,0,0,0,0,0,20,0,0,0,21,0,22,0,0,0,0,0,0,0,0,23,0,0,25,0,29, +0,53,0,0,0,0,0,0,55,0,0,0,0,0,0,61,76,0,0,0,94,0,0,0,0,0,0,96,0,97,0,98,0,0,0,0, +0,0,0,99,101,106,108,0,0,0,0,0,110,0,111,112,0,113,118,124,0,0,0,0,0,125,128,0,0 +,0,0,129,0,0,131,0,0,0,0,0,0,132,0,0,135,0,0,0,137,0,0,0,0,0,138,139,0,0,0,0,0,0 +,0,142,143,144,0,0,0,0,0,145,0,0,0,146,149,151,152,0,0,153,0,0,0,0,0,0,0,0,0,0,0 +,0,0,0,0,154,0,0,0,0,0,0,155,0,0,0,0,160,182,0,0,0,0,0,0,183,0,0,0,188,189,0,0, +192,0,0,0,0,0,0,194,0,0,0,0,0,0,0,0,197,202,209,0,0,210,0,224,0,0,0,225,0,0,0,0, +0,0,0,0,0,0,231,0,0,0,232,0,240,0,0,242,0,0,0,0,0,0,0,0,0,0,0,244,0,0,0,246,0,0, +249,251,253,0,0,0,0,0,258,0,0,261,263,0,0,0,267,0,0,268,0,269,0,0,0,0,0,0,0,0,0, +271,0,0,0,0,0,0,272,0,273,0,277,0,278,286,0,0,0,0,287,0,289,290,291,0,0,0,295,0, +0,296,297,0,0,0,0,0,0,0,0,0,0,298,0,0,0,299,0,0,305,0,324,0,0,0,0,0,327,0,328, +329,0,0,0,0,336,0,0,340,0,341,342,343,0,0,346,0,348,0,0,0,0,0,0,349,351,0,0,355, +0,363,0,364,0,368,369,0,370,0,0,0,0,0,0,0,372,0,0,0,0,0,0,0,0,0,0,0,373,0,375,0, +0,0,0,376,377,0,0,394,395,396,0,0,398,0,0,0,0,400,0,0,408,0,0,0,0,420,0,0,0,0,0, +0,421,0,0,422,423,0,0,429,435,436,442,0,0,443,0,444,445,453,456,0,457,0,0,0,0,0, +458,0,0,0,459,0,0,0,460,0,462,463,465,0,0,0,0,0,0,466,469,0,0,0,0,0,0,470,0,0,0, +474,0,476,0,0,0,0,483,0,485,0,0,0,486,0,0,488,491,492,0,0,497,499,500,0,501,0,0, +0,505,0,0,506,0,0,0,507,0,0,0,509,0,0,0,0,511,512,519,0,0,0,0,0,0,529,530,0,0,0, +534,0,0,0,0,543,0,0,0,0,0,0,0,0,0,553,0,0,0,0,557,560,0,0,0,0,0,0,561,0,564,0,0, +0,0,0,0,565,566,0,575,0,619,0,620,0,0,623,624,0,0,0,625,0,0,626,627,0,0,628,0,0, +0,0,630,0,631,0,0,0,0,0,0,0,0,0,641,0,0,0,0,643,656,668,0,0,0,673,0,0,0,674,0,0, +0,0,0,0,0,0,682,0,687,0,690,0,693,699,700,0,0,0,0,0,0,704,705,0,0,0,0,707,710,0, +711,0,0,0,0,726,0,0,729,0,0,0,730,731,0,0,0,0,0,752,0,0,0,762,0,763,0,0,767,0,0, +0,770,774,0,0,775,0,0,0,0,0,0,0,0,0,0,776,0,0,0,777,783,0,0,0,785,788,0,0,0,0, +790,0,0,0,793,0,0,0,0,794,0,0,804,819,821,0,827,0,0,0,834,0,0,835,0,0,0,841,0, +844,0,850,851,859,0,860,0,0,0,0,0,0,0,874,0,876,0,877,890,0,0,0,0,0,0,0,0,893, +894,898,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,899,0,0,0,900,904,906,0,0,0,907,0,908,909, +0,910,0,0,0,0,911,0,0,0,0,0,916,0,0,0,922,925,0,930,0,934,0,0,0,0,0,943,0,0,944, +0,953,954,0,0,0,0,0,0,955,0,962,963,0,0,976,0,0,977,978,979,980,0,981,0,0,0,0, +984,0,0,985,0,0,987,989,991,0,0,0,0,0,0,0,0,0,992,0,0,0,993,0,0,0,0,0,0,996,0,0, +0,1000,0,0,0,0,0,1002,0,0,0,0,1005,1007,0,0,0,1009,0,0,0,1010,0,0,0,0,0,0,1011,0 +,1012,0,0,0,0,1014,1016,0,0,0,1020,0,1021,0,0,0,0,1022,0,0,0,1024,0,0,0,0,0,0, +1025,0,0,1026,1027,0,0,0,0,0,1031,0,1033,0,0,0,0,1034,0,0,0,1037,1040,0,0,0,1042 +,1043,0,0,1053,0,1054,0,0,1057,0,0,0,1058,0,0,1060,0,0,0,0,0,0,0,1061,0,0,1062,0 +,0,0,0,1063,0,0,0,0,1064,0,0,0,0,0,1065,0,0,0,0,1066,1067,0,0,0,1069,1070,1072,0 +,0,0,0,0,0,1073,0,1075,0,0,0,0,0,0,1080,1084,0,0,0,0,1088,0,0,0,0,0,0,1094,0, +1095,0,1107,0,0,0,1112,1114,0,1119,0,1122,0,0,1126,0,1129,0,1130,0,0,0,0,0,1132, +0,0,0,0,0,0,1144,0,0,1145,1146,0,1148,1149,0,0,1150,1151,0,0,0,0,1152,0,1153,0,0 +,0,0,0,1154,0,1163,0,0,0,1164,0,0,0,0,0,1165,0,1167,0,1170,0,0,0,0,0,1171,1172,0 +,0,0,0,0,0,0,0,1173,1175,1177,0,1186,0,0,0,0,0,0,0,0,0,0,1195,0,0,1221,0,0,1224, +0,0,1227,0,0,0,0,0,1228,1229,0,0,1230,0,0,0,0,0,0,0,0,0,1231,0,0,0,1233,0,0,1243 +,1244,1246,1248,0,0,0,0,1254,1255,1258,1259,0,0,0,1260,0,0,1261,0,0,0,1262,1264, +0,0,1265,0,0,0,0,0,0,0,0,0,0,0,0,1266,0,1267,0,0,0,0,1273,1274,1276,1289,0,0, +1291,1292,1293,0,0,1294,1295,1296,0,0,0,0,1302,0,1304,0,0,0,0,0,0,0,0,0,1311, +1312,0,1314,0,1316,1320,1321,0,0,0,0,0,0,0,1322,1323,1324,0,1335,0,1336,0,0,0,0, +1341,1342,0,1346,0,1357,0,0,0,1358,1360,0,0,0,0,0,0,1361,0,0,0,1362,1365,0,1366, +0,0,0,0,0,0,0,1379,0,0,0,0,0,0,0,0,0,0,0,0,1386,0,1388,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,1395,0,0,0,0,1403,0,1405,0,0,1407,0,0,0,0,0,1408,1409,0,1410,0,0,0,1412,1413, +1416,0,0,1429,1451,0,0,1454,0,0,0,0,0,0,0,1455,0,0,0,0,0,0,0,1456,0,0,0,0,1459, +1460,1461,1475,0,0,0,0,0,0,1477,0,1480,0,1481,0,0,1486,0,0,1495,0,0,0,1496,0,0, +1498,1499,1501,1520,1521,0,0,0,1526,0,0,0,0,1528,1529,0,1533,1536,0,0,0,1537, +1538,1549,0,1550,1558,1559,1572,0,1573,0,0,0,0,0,0,0,0,0,1575,0,0,0,0,0,1579,0, +1599,0,1603,0,1604,0,1605,0,0,0,0,0,1608,1610,0,0,0,0,1611,0,1615,0,1616,1618,0, +1619,0,0,1622,0,0,0,0,1634,0,0,0,1635,0,0,0,1641,0,0,0,0,0,0,0,0,0,1643,0,0,0, +1650,0,0,1652,0,0,0,0,0,1653,0,0,0,1654,0,0,0,0,1655,0,1662,0,0,1663,1664,0,0, +1668,0,0,1669,1670,0,1672,1673,0,0,0,0,0,1674,0,0,0,1675,1676,1680,0,1682,0,0, +1687,0,0,0,0,0,1704,0,0,1705,0,0,1721,0,0,0,0,1734,1735,0,0,0,0,1737,0,0,0,0, +1739,0,0,1740,0,0,0,0,0,0,0,0,0,0,1741,1743,0,0,0,0,1745,0,0,0,1749,0,0,0,1751,0 +,0,0,0,0,0,1760,0,0,0,0,1765,0,0,0,0,0,1784,0,1785,1787,0,0,0,0,1788,1789,0,0,0, +0,1790,1791,1793,0,1798,1799,0,0,0,0,1801,0,1803,1805,0,0,0,1806,1811,0,1812, +1814,0,1821,0,0,0,0,0,1822,1833,0,0,0,0,0,0,1848,0,0,0,0,0,0,1857,0,0,0,1859,0,0 +,0,0,1861,0,0,0,0,0,0,0,1866,0,1921,1925,0,0,0,1929,1930,0,0,0,0,0,0,0,0,0,1931, +0,0,0,0,1932,0,0,0,1934,0,0,0,0,0,0,0,0,1946,0,0,1948,0,0,0,0,1950,0,1957,0,1958 +,0,0,0,0,0,1965,1967,0,0,0,0,1968,0,1969,0,1971,1972,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,1973,0,0,0,0,1975,0,0,0,0,1976,1979,0,1982,0,0,0,0,1984,1988,0,0,0,0,1990, +2004,2008,0,0,0,2012,2013,0,0,0,0,0,0,0,0,0,0,2015,0,2016,2017,0,0,0,0,2021,0,0, +2025,0,0,0,0,0,2029,2036,2040,0,2042,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2043,0,0,0,0,0, +2045,0,0,0,0,0,0,0,2046,2047,0,2048,2049,0,2059,0,0,2063,0,2064,2065,0,0,2066,0, +0,0,0,0,0,2069,0,0,0,0,2070,0,2071,0,2072,0,0,0,0,2080,2082,2083,0,0,0,0,0,2085, +0,2086,2088,2089,2105,0,0,0,0,2107,0,0,2116,2117,0,2120,0,0,2122,0,0,0,0,0,2123, +0,0,2125,2127,2128,0,0,0,2130,0,0,0,2137,2139,2140,2141,0,0,0,0,0,0,0,0,0,2144, +2145,0,0,2146,2149,0,0,0,0,2150,0,0,2151,2158,0,2159,0,2160,0,0,0,0,0,0,2161, +2162,0,0,2194,2202,0,0,0,0,0,0,2205,2217,0,2220,0,2221,0,2222,2224,0,0,0,0,2237, +0,0,0,0,0,2238,0,2239,2241,0,0,2242,0,0,0,0,0,2243,0,0,0,0,0,0,2252,0,0,2253,0,0 +,0,2257,2258,0,0,0,2260,0,0,0,0,0,0,0,2262,0,2264,0,0,0,0,0,2269,2270,0,0,0,0,0, +0,0,0,0,2271,0,2273,0,0,0,0,2277,0,0,0,0,2278,0,0,0,0,2279,0,2280,0,2283,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2287,0,0,0,0,0,0,0,2289,2290,0,0,0,0,2291,0,2292,0, +0,0,2293,2295,2296,0,0,0,0,0,0,0,2298,0,0,0,0,0,2303,0,2305,0,0,2306,0,2307,0,0, +0,0,0,0,0,0,0,0,0,0,2313,2314,2315,2316,0,0,2318,0,2319,0,2322,0,0,2323,0,2324,0 +,2326,0,0,0,0,0,0,0,2335,0,2336,2338,2339,0,2340,0,0,0,2355,0,2375,0,2382,2386,0 +,2387,0,0,2394,0,0,0,0,2395,0,2397,0,0,0,0,0,2398,0,0,0,0,0,0,0,2399,2402,2404, +2408,2411,0,0,0,2413,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2415,0,0,2416,2417,2419,0,2420, +0,0,0,0,0,2425,0,0,0,2426,0,0,0,0,0,0,0,0,0,0,0,0,2427,2428,0,2429,0,0,2430,2434 +,0,2436,0,0,0,0,0,0,2441,2442,0,2445,0,0,2446,2457,0,2459,0,0,2462,0,2464,0,2477 +,0,2478,2486,0,0,0,2491,0,0,2493,0,0,2494,0,2495,0,2513,2523,0,0,0,0,2524,0,0,0, +0,0,0,2528,2529,2530,0,0,2531,0,2533,0,0,2534,2535,0,2536,2537,0,2538,0,2539, +2540,0,0,0,2545,2546,0,0,0,0,0,0,0,2548,0,0,2549,0,2550,2555,0,0,0,0,0,2557,0, +2560,0,0,0,0,0,0,0,0,0,0,0,2561,0,2576,0,0,0,0,0,0,0,0,0,2577,2578,0,0,0,2579,0, +0,0,0,0,0,0,2580,0,0,0,0,2581,0,0,0,0,2583,0,2584,0,2588,2590,0,0,0,2591,0,0,0,0 +,2593,2594,0,2595,0,2601,2602,0,0,2603,0,2605,0,0,0,2606,2607,2611,0,2615,0,0,0, +2617,0,0,0,0,0,0,0,0,0,0,0,0,0,2619,0,0,2620,0,0,0,2621,0,2623,0,2625,0,0,2628, +2629,0,0,2635,2636,2637,0,0,2639,0,0,0,2642,0,0,0,0,2643,0,2644,0,2649,0,0,0,0,0 +,0,2655,2656,0,0,2657,0,0,0,0,0,2658,0,0,0,0,0,2659,0,0,0,0,2664,2685,0,2687,0, +2688,0,0,2689,0,0,2694,0,2695,0,0,2698,0,2701,2706,0,0,0,2707,0,2709,2710,2711,0 +,0,0,2720,2730,2735,0,0,0,0,2738,2740,0,0,0,0,2747,0,0,0,0,0,0,2748,0,0,2749,0,0 +,0,0,0,2750,0,0,2752,2754,0,0,0,0,0,2758,0,0,0,0,2762,0,0,0,0,2763,0,0,0,0,0,0,0 +,2764,2767,0,0,0,0,2768,0,0,2770,0,0,0,0,0,0,0,2771,0,0,0,0,0,0,0,0,0,2772,0,0,0 +,0,0,2773,2776,0,0,2783,0,0,2784,0,2789,0,2790,0,0,0,2792,0,0,0,0,0,0,0,0,0,0, +2793,2795,0,0,0,0,0,0,2796,0,0,0,0,0,0,2797,2799,0,0,0,0,2803,0,0,0,0,2806,0, +2807,2808,2817,2819,0,0,0,0,0,2821,0,0,0,0,2822,2823,0,0,0,0,0,0,0,2824,0,0,2828 +,0,2834,0,0,0,0,0,0,2836,0,2838,0,0,2839,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2841, +0,0,0,2842,0,0,0,0,0,2843,2844,0,0,0,0,2846,0,0,2847,0,2849,0,2853,0,0,0,0,0, +2857,0,0,0,0,2858,0,2859,0,0,2860,0,2862,2868,0,0,0,0,2875,0,2876,0,0,2877,2878, +2884,2889,2890,0,0,2891,0,0,2892,0,0,0,2906,2912,0,2913,0,0,0,0,0,0,0,0,2916,0, +2934,0,0,0,0,0,2935,0,0,0,0,2939,0,2940,0,0,0,0,0,0,0,2941,0,0,0,2946,0,2949,0,0 +,2950,2954,2955,0,0,0,2959,2961,0,0,2962,0,2963,0,0,0,0,0,0,2964,2965,2966,2967, +0,0,0,0,0,0,0,2969,0,0,0,0,0,2970,2975,0,2982,2983,2984,0,0,0,0,0,2989,0,0,2990, +0,0,0,0,0,0,0,2991,0,0,0,0,0,0,0,0,2998,0,3000,3001,0,0,3002,0,0,0,3003,0,0,3012 +,0,0,3022,0,0,3024,0,0,3025,3027,0,0,0,3030,0,0,0,0,3034,3035,0,0,3036,0,3039,0, +3049,0,0,3050,0,0,0,0,0,0,3051,0,3053,0,0,0,0,3057,0,3058,0,0,0,0,0,0,0,0,3063,0 +,0,3073,3074,3078,3079,0,3080,3086,0,0,0,0,0,0,0,0,3087,0,3092,0,3095,0,3099,0,0 +,0,3100,0,3101,3102,0,3122,0,0,0,3124,0,3125,0,0,0,0,0,0,3132,3134,0,0,3136,0,0, +0,0,0,0,0,3147,0,0,3149,0,0,0,0,0,3150,3151,3152,0,0,0,0,3158,0,0,3160,0,0,3161, +0,0,3162,0,3163,3166,3168,0,0,3169,3170,0,0,3171,0,0,0,0,0,0,0,3182,0,3184,0,0, +3188,0,0,3194,0,0,0,0,0,0,3204,0,0,0,0,3209,0,0,0,0,0,0,0,0,0,0,0,3216,3217,0,0, +0,0,0,0,0,3219,0,0,3220,3222,0,3223,0,0,0,0,3224,0,3225,3226,0,3228,3233,0,3239, +3241,3242,0,0,3251,3252,3253,3255,0,0,0,0,0,0,0,0,3260,0,0,3261,0,0,0,3267,0,0,0 +,0,0,0,0,0,3271,0,0,0,3278,0,3282,0,0,0,3284,0,0,0,3285,3286,0,0,0,0,0,0,0,3287, +3292,0,0,0,0,3294,3296,0,0,3299,3300,3301,0,3302,0,0,0,0,0,3304,3306,0,0,0,0,0,0 +,3308,0,0,0,0,0,0,0,0,0,3311,0,0,0,0,0,0,0,0,3312,3314,3315,0,3318,0,0,0,0,0,0,0 +,0,3319,0,0,0,0,0,3321,0,0,0,0,0,0,0,0,0,3322,0,0,3324,3325,0,0,3326,0,0,3328, +3329,3331,0,0,3335,0,0,3337,0,3338,0,0,0,0,3343,3347,0,0,0,3348,0,0,3351,0,0,0,0 +,0,0,3354,0,0,0,0,0,0,0,0,0,0,3355,0,0,3365,3366,3367,0,0,0,0,0,0,3368,3369,0, +3370,0,0,3373,0,0,3376,0,0,3377,0,3379,3387,0,0,0,0,0,3390,0,0,0,0,0,0,0,3402,0, +3403,3436,3437,3439,0,0,3441,0,0,0,3442,0,0,3449,0,0,0,3450,0,0,0,0,0,0,0,3451,0 +,0,3452,0,3453,3456,0,3457,0,0,3458,0,3459,0,0,0,0,0,0,0,0,0,3460,0,0,3469,3470, +0,0,3475,0,0,0,3480,3487,3489,0,3490,0,0,3491,3499,0,3500,0,0,3501,0,0,0,3502,0, +3514,0,0,0,3516,3517,0,0,0,3518,0,0,0,0,3520,3521,3522,0,0,3526,3530,0,0,0,0, +3531,0,0,0,0,3536,0,0,0,0,0,0,0,3539,3541,0,0,3542,3544,0,3547,3548,0,0,3550,0, +3553,0,0,0,0,0,0,0,3554,0,3555,0,3558,0,3559,0,0,0,0,0,0,0,0,3563,0,3581,0,0,0, +3599,0,0,0,3600,0,3601,0,3602,3603,0,0,3606,3608,0,3610,3611,0,0,0,0,0,0,0,0,0, +3612,3616,3619,0,0,0,0,0,0,0,0,0,0,0,0,0,3624,3628,0,3629,3634,3635,0,0,0,0,0,0, +3636,0,3637,0,0,3638,3651,0,0,0,0,0,0,3652,3653,0,0,0,0,3656,3657,0,0,0,0,0,3658 +,0,0,0,0,3659,0,3661,3663,3664,0,3665,0,3692,0,0,0,3694,3696,0,0,0,0,0,0,0,0,0,0 +,0,0,3698,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3700,0,0,3701,0,0,0,3708,3709,0,0,0,3711 +,3712,0,0,0,0,0,3723,0,3724,3725,0,0,3726,0,0,0,0,0,0,3728,3729,0,3734,3735,3737 +,0,0,0,3743,0,3745,0,0,3746,0,0,3747,3748,0,3757,0,3759,3766,3767,0,3768,0,0,0,0 +,3769,0,0,3771,0,3774,0,0,0,0,0,0,3775,0,0,0,0,0,0,3776,0,3777,3786,0,3788,3789, +0,0,0,0,0,0,0,0,0,3791,0,3811,0,0,0,0,0,3814,3815,3816,3820,0,0,0,0,0,0,0,3821,0 +,0,3825,0,0,0,0,3835,0,0,3848,3849,0,0,0,0,3850,3851,3853,0,0,0,0,3859,0,3860, +3862,0,0,0,0,0,3863,0,0,0,0,0,0,0,0,3873,0,3874,0,3875,3886,0,3887,0,0,0,0,3892, +3913,0,3914,0,0,0,3925,3931,0,0,0,0,3934,3941,3942,0,0,0,0,3943,0,0,0,3944,0,0,0 +,0,0,3945,0,3947,0,0,0,3956,3957,0,0,0,0,0,0,0,0,0,3958,0,3959,3965,0,0,0,0,3966 +,0,0,0,3967,0,0,0,3968,3974,0,0,0,0,0,3975,3977,3978,0,0,0,0,3980,0,3985,0,0,0,0 +,0,0,0,0,3986,4011,0,0,4017,0,0,0,0,0,0,0,0,0,0,0,4018,0,0,0,0,4019,0,4023,0,0,0 +,4027,4028,0,0,0,0,0,0,0,0,4031,4034,0,0,4035,4037,4039,4040,0,0,0,0,0,4059,0, +4060,4061,0,4062,4063,4066,0,0,4072,0,0,0,0,0,0,0,0,0,0,0,0,0,4088,0,0,0,0,0, +4091,0,0,0,0,4094,4095,0,0,4096,0,0,0,0,0,4098,4099,0,0,0,4101,0,4104,0,0,0,4105 +,4108,0,4113,0,0,4115,4116,0,4126,0,0,4127,0,0,0,0,0,0,0,4128,4132,4133,0,4134,0 +,0,0,4137,0,0,4141,0,0,0,0,4144,4146,4147,0,0,0,0,4148,0,0,4311,0,0,0,4314,4329, +0,4331,4332,0,4333,0,4334,0,0,0,4335,0,4336,0,0,0,4337,0,0,0,4342,4345,4346,4350 +,0,4351,4352,0,4354,4355,0,0,4364,0,0,0,0,4369,0,0,0,4373,0,4374,0,0,0,0,4377,0, +0,0,0,4378,0,0,0,4380,0,0,0,4381,4382,0,0,0,0,0,0,0,4384,0,0,0,0,4385,0,0,0,4386 +,0,0,0,4391,4398,0,0,0,0,4407,4409,0,0,0,0,4410,0,0,4411,0,4414,4415,4418,0,4427 +,4428,4430,0,4431,0,4448,0,0,0,0,0,4449,0,0,0,4451,4452,0,4453,4454,0,4456,0,0,0 +,0,0,0,0,4459,0,4463,0,0,0,0,0,4466,0,4467,0,4469,0,0,0,0,0,0,0,0,0,0,0,0,0,4470 +,4471,0,4473,0,0,4475,0,0,0,0,4477,4478,0,0,0,4479,4481,0,4482,0,4484,0,0,0,0,0, +0,0,4486,0,0,4488,0,0,4497,0,4508,0,0,4510,4511,0,4520,4523,0,4524,0,4525,0,4527 +,0,0,4528,0,0,0,0,4530,0,4531,0,0,4532,0,0,0,4533,0,0,0,0,0,4535,0,0,0,4536,0,0, +0,0,0,4541,4543,4544,4545,4547,0,4548,0,0,0,0,4550,4551,0,4553,0,0,0,0,4562,0,0, +4571,0,0,0,4574,0,0,0,4575,0,4576,0,4577,0,0,0,4581,0,0,0,0,0,4582,0,0,4586,0,0, +0,4588,0,0,4597,0,4598,0,0,0,0,4616,4617,0,4618,0,0,0,0,4619,0,4620,0,0,4621,0, +4624,0,0,0,0,0,4625,0,0,0,0,4657,0,4659,0,4667,0,0,0,4668,4670,0,4672,0,0,0,0,0, +4673,4676,0,0,0,0,4687,0,0,0,0,4697,0,0,0,0,4699,0,4701,0,0,0,0,4702,0,0,4706,0, +0,4713,0,0,0,4714,4715,4716,0,0,0,0,0,0,0,0,0,0,0,0,4717,0,0,4720,0,4721,4729, +4735,0,0,0,4737,0,0,0,4739,0,0,0,4740,0,0,0,4741,0,0,0,0,0,4742,0,4745,4746,4747 +,0,0,0,0,0,0,0,0,4748,0,0,0,4749,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4751, +4786,0,4787,0,4788,4796,0,0,4797,4798,0,4799,4806,4807,0,0,0,0,4809,4810,0,0,0,0 +,0,0,4811,0,0,0,0,0,4812,0,4813,0,0,4815,0,4821,4822,0,0,0,0,4823,0,0,0,0,0,0,0, +0,0,0,4824,0,0,0,0,4826,0,0,0,4828,0,4829,0,0,0,4843,0,0,4847,0,4853,4855,4858,0 +,0,0,0,0,4859,0,4864,0,0,4879,0,0,0,0,4880,0,0,0,0,4881,0,4882,0,0,0,0,0,0,0,0,0 +,4883,0,0,0,0,4884,0,0,0,0,0,4886,4887,4888,4894,4896,0,4902,0,0,4905,0,0,4915,0 +,0,0,0,0,0,0,4916,4917,4919,4921,0,0,0,0,0,4926,0,0,0,0,4927,0,0,0,0,0,0,0,0, +4929,0,4930,4931,0,4938,0,4952,0,4953,4957,4960,4964,0,0,0,0,0,0,0,5019,5020, +5022,0,0,0,0,0,5023,0,0,0,5024,0,0,0,5025,0,0,0,0,5028,0,0,0,0,5029,5030,5031,0, +5033,0,0,0,0,0,0,0,0,0,5034,5035,0,5036,0,0,5037,0,0,0,0,5038,0,0,5039,0,0,0, +5041,5042,0,0,0,0,5044,5049,5054,0,5055,0,5057,0,0,0,5060,0,0,0,0,0,5063,0,5064, +5065,0,5067,0,0,0,5068,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5076,0,0,0,0,0,0, +0,5077,0,0,5078,5080,0,0,5083,0,0,0,0,0,0,0,0,5085,0,0,0,0,0,0,5098,5099,5101, +5105,5107,0,5108,0,5109,0,0,0,0,0,0,0,5110,0,0,0,0,0,5117,5118,0,5121,0,5122,0,0 +,5130,0,0,0,5137,0,0,0,5148,0,0,0,0,0,0,0,5151,5154,0,0,0,5155,0,0,5156,5159, +5161,0,0,0,0,5162,0,0,0,0,5163,5164,0,5166,0,0,0,0,0,0,0,0,0,0,5167,0,0,0,5172,0 +,0,0,0,0,0,5178,5179,0,0,5190,0,0,5191,5192,5194,0,0,5198,5201,0,0,0,0,0,5203,0, +5206,5209,0,0,0,0,0,0,5213,0,5214,5216,0,0,0,0,0,5217,0,0,0,0,0,0,0,0,5218,5219, +0,5231,0,0,5244,5249,0,5254,0,5255,0,0,5257,0,0,0,0,0,5258,0,5260,5270,0,5277,0, +0,0,0,0,0,5280,5281,5282,5283,0,0,0,0,0,5284,0,5285,0,0,0,0,0,5287,5288,0,0,0,0, +0,0,0,0,0,0,5289,5291,0,0,5294,0,0,5295,0,0,0,0,0,0,0,5304,0,0,5306,5307,5308,0, +5309,0,0,5310,0,0,0,0,5311,5312,0,5313,0,0,0,0,0,5316,0,0,0,5317,0,0,0,0,0,0,0,0 +,0,5325,0,0,0,0,0,0,5326,0,5327,5329,0,5332,0,0,0,0,5338,0,0,0,0,0,0,0,0,5340,0, +0,5341,0,0,0,5342,0,5343,5344,0,0,5345,0,0,0,0,0,0,5347,5348,0,0,0,0,0,0,0,0,0, +5349,0,5350,0,5354,0,0,0,0,5358,0,0,5359,0,0,5361,0,0,5365,0,5367,0,5373,0,0,0, +5379,0,0,0,5380,0,0,0,5382,0,5384,0,0,0,0,0,0,5385,0,0,0,0,5387,0,0,0,0,0,0,5388 +,5390,5393,0,0,0,0,0,0,0,0,0,0,0,5396,0,0,0,0,5397,5402,0,0,0,0,0,5403,0,0,0, +5404,5405,0,0,0,0,0,0,0,0,0,0,0,0,5406,0,0,0,0,5410,0,0,5411,0,5415,0,0,0,0,5416 +,5434,0,0,0,0,0,0,0,0,0,0,0,5438,0,5440,0,0,0,0,0,0,5441,5442,0,0,0,5443,5444, +5447,0,0,5448,5449,5451,0,0,0,5456,5457,0,0,0,5459,0,0,0,5461,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,5464,0,5466,0,0,5467,0,5470,0,0,5473,0,0,5474,0,0,5476,0,0,0,0,0,0,0,0 +,0,0,0,5477,0,0,0,0,0,0,0,5484,0,0,5485,5486,0,0,0,0,0,5488,0,0,0,0,0,0,0,5489,0 +,0,0,0,0,5507,0,0,0,5510,0,5511,0,0,5512,0,0,0,5513,0,5515,0,0,5516,5517,0,5518, +0,0,5522,0,0,0,0,0,5534,5535,0,0,5536,0,5538,0,0,5543,0,5544,0,0,5545,0,5547,0, +5557,0,0,5558,0,5560,5567,0,0,0,0,5568,0,0,0,5571,5573,0,5574,0,5575,0,0,0,0, +5577,0,0,5598,0,0,0,0,0,0,0,0,0,5600,5609,0,0,0,0,5610,0,0,5612,0,5624,0,5625,0, +0,0,5629,0,5641,0,5642,5643,0,0,0,0,0,0,5651,0,0,0,5652,5653,0,5661,5662,5678,0, +5679,0,0,0,0,5685,5686,0,0,0,0,0,5690,5692,0,5703,0,0,0,0,0,5706,0,0,0,0,5707,0, +0,0,0,0,0,5708,0,0,5709,0,5710,0,0,0,5712,0,5733,0,5734,5735,0,0,5744,5751,0,0,0 +,0,0,0,0,0,0,0,0,0,5752,0,5754,0,0,0,0,0,0,5757,5758,0,5760,5761,0,0,0,0,5763, +5764,5765,0,5766,0,5767,5768,0,5770,0,0,0,0,5776,5780,0,0,0,0,5782,0,0,0,0,5784, +0,0,5788,0,0,0,0,0,0,0,0,0,0,0,5797,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5799,0,0,5801, +0,0,0,5811,0,0,0,0,0,0,5816,0,0,5827,0,0,0,0,0,0,0,0,5830,5831,0,0,5832,0,0,5833 +,0,5835,5844,5845,0,5846,0,0,0,0,0,5850,0,0,0,0,0,5852,0,5855,5857,0,0,5859,0, +5861,0,0,5863,0,5865,0,0,0,5873,5875,0,0,0,5877,0,5879,0,0,0,5888,0,0,5889,5891, +0,5894,0,0,0,0,0,0,5895,0,5897,0,0,0,0,0,0,5907,0,5911,0,0,5912,0,5913,5922,5924 +,0,5927,5928,0,0,0,0,5929,5930,0,5933,0,0,0,0,5949,0,0,5951,0,0,0,0,0,0,0,0,5953 +,0,0,5954,0,5959,5960,5961,0,5964,0,0,0,5976,5978,5987,5990,0,0,0,0,0,5991,0, +5992,0,0,0,5994,5995,0,0,5996,0,0,6001,6003,0,0,0,0,6007,0,0,0,0,0,6008,0,0,6009 +,0,6010,0,0,0,6011,6015,0,6017,0,6019,0,6023,0,0,0,0,0,0,0,6025,0,0,0,0,0,0,0,0, +0,0,6026,0,6030,0,0,6032,0,0,0,6033,6038,6040,0,0,0,6041,6045,0,0,6046,0,0,6053, +0,0,6054,0,6055,0,0,0,0,0,0,6057,0,6063,0,0,0,6064,0,6066,6071,6072,0,0,0,0,0,0, +6075,6076,0,0,6077,0,0,0,0,0,0,0,0,0,6078,6079,0,0,0,0,0,0,0,0,6080,0,6083,0,0,0 +,0,0,6084,0,0,6088,0,6089,0,0,6093,6105,0,0,6107,0,6110,0,0,0,6111,6125,6126,0,0 +,0,6129,0,0,0,0,6130,0,0,0,6131,6134,0,0,0,0,0,0,6142,0,0,0,0,0,6144,0,0,6146, +6151,6153,0,6156,0,6163,0,6180,6181,0,0,0,0,0,6182,0,0,0,0,6184,6195,0,0,6206,0, +6208,0,0,6212,6213,6214,0,6215,0,0,0,6228,0,0,0,6234,0,0,0,0,0,0,6235,6240,0, +6242,6243,6244,0,6250,6255,0,0,0,0,0,6257,0,0,0,6258,6278,0,6284,0,0,0,6285,0,0, +0,0,0,0,0,0,6286,0,0,0,6320,0,0,6322,6332,0,0,0,0,0,0,0,0,6334,0,0,0,0,0,0,0, +6335,0,0,6337,0,6338,0,6339,6340,0,0,6356,6357,6369,0,0,0,6370,6371,6372,0,6373, +0,0,0,0,0,6376,0,0,0,0,0,6382,6383,6384,0,0,0,0,6386,0,6389,6397,6400,6411,0, +6414,0,0,0,0,0,0,0,6415,6416,0,0,0,0,0,0,6417,0,0,0,0,6418,0,0,0,0,0,0,0,6420,0, +6421,6423,6425,0,6429,6430,0,6433,6438,0,0,0,0,0,0,0,0,0,0,6439,6440,0,0,6441,0, +0,6444,0,0,0,0,6446,0,0,0,0,6447,6448,0,0,6450,0,0,0,6454,0,0,6455,0,6461,0,0,0, +0,0,0,6462,0,0,6463,0,6464,0,6465,6467,0,0,0,6468,0,6479,6480,0,0,0,0,0,0,0,6481 +,0,0,6485,6487,0,0,0,0,0,0,6493,0,0,0,0,0,0,0,0,6494,6495,6496,0,0,0,0,0,6498,0, +0,0,6507,6508,0,0,0,0,0,0,0,0,0,0,6511,6512,0,0,0,0,6513,0,0,0,6514,0,0,0,0,0, +6516,0,0,6517,6518,0,0,0,6519,6520,6521,0,6523,0,0,0,0,6524,6528,0,6530,0,0,6532 +,0,6578,0,0,0,6583,0,6584,0,0,0,6587,0,0,0,6590,0,6591,0,0,0,0,0,6592,0,0,0,0, +6593,6594,0,0,0,0,0,6599,6600,0,0,6601,6602,6604,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6608,0,0,0,0,0,0,0,0,6610,6611,0,6615,0,6616,6618,6620,0,6637,0,0,0,0,6639,0,0,0 +,0,6641,0,6642,0,0,0,6647,0,6660,6663,0,6664,0,6666,6669,0,6675,6676,6677,0,0,0, +0,0,0,0,0,0,6678,0,0,0,6679,0,6680,0,0,0,0,0,0,0,6693,0,0,0,0,0,0,0,0,0,6704, +6705,6706,0,0,6711,6713,0,0,0,0,0,6716,0,0,0,6717,0,6719,6724,0,0,0,0,0,0,0,0, +6725,6726,0,0,0,0,0,6728,6729,6735,0,6737,6742,0,0,6743,6750,0,6751,0,0,6752, +6753,0,0,0,0,0,0,6754,0,0,0,0,0,6756,0,0,0,0,0,0,6763,0,0,6764,6765,0,0,0,6770,0 +,0,0,6776,6780,0,6781,0,0,0,6783,0,6784,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6785,0,0,0,6792,0,0,0,6793,0,0,6802,0,0,0,0,0,6803,0,0,0,6804,0,0,0,6812,0,0, +6823,0,6824,6839,0,0,0,0,6852,0,0,6854,0,6856,6857,0,0,0,0,0,0,0,0,0,6867,0,6868 +,6870,6872,0,0,0,6873,6874,0,0,0,0,0,6875,0,0,6877,0,0,0,0,0,0,0,6878,0,0,0,6879 +,0,6880,0,0,0,0,0,0,0,0,0,0,6887,0,6888,6891,6893,0,6895,0,0,0,0,0,0,0,0,6899,0, +0,0,0,6901,0,0,0,0,6910,0,6911,0,0,6912,0,0,6913,6914,0,0,0,6915,0,0,0,6916,6919 +,0,0,0,0,0,0,6924,0,6925,0,0,0,6926,6927,6928,0,6929,0,6930,0,0,6931,6935,0,6936 +,0,0,0,0,6939,6940,6941,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6942,6948,6949,0,0,0,0,0,0 +,0,6952,6954,6963,6965,6966,0,0,6967,6968,0,0,0,0,0,0,0,0,0,6969,0,0,6970,6979,0 +,0,6980,0,0,6983,0,0,0,0,0,6984,0,0,0,0,0,0,0,6988,6990,6992,0,0,0,0,0,0,0,6995, +0,0,0,7012,0,0,0,0,0,0,0,0,0,7019,0,0,0,0,0,0,0,0,7021,0,0,7022,7023,7028,0,7030 +,7033,0,0,0,0,0,0,7038,0,0,0,0,0,0,0,0,0,0,7039,0,0,0,0,0,7046,0,7047,0,0,0,0,0, +0,0,0,0,0,0,7048,7052,0,0,0,0,0,7054,0,7060,0,0,0,0,7061,0,7065,0,0,0,0,7067, +7069,0,7070,7071,7072,0,0,7078,0,7080,7081,0,7083,0,0,0,7084,7087,7088,0,0,7090, +0,7093,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7107,0,0,7108,0,0,0,0,0,0,0,0,7110,0,7114,0 +,0,0,0,0,0,0,7115,0,7116,0,0,0,0,0,7117,0,0,7118,0,0,7124,0,7125,0,0,7126,0,0,0, +0,7128,0,0,0,0,0,7129,0,7130,0,7132,7133,0,0,7134,0,0,7139,0,7148,7150,0,0,0,0, +7152,0,0,0,7153,7156,7157,0,0,0,0,0,7158,0,0,0,0,0,0,0,0,0,0,7163,7165,7169,0, +7171,0,0,0,0,0,0,0,0,0,7172,0,7173,7181,0,0,0,0,0,7182,7185,0,0,0,0,7187,0,7201, +7204,0,0,0,0,0,7206,7207,0,0,0,0,7211,7216,0,7218,0,0,0,0,7226,7228,7230,7232, +7233,7235,7237,0,0,0,0,7238,7241,0,7242,0,0,7247,0,0,0,7266,0,0,0,0,0,0,0,7289,0 +,0,7290,7291,0,0,7292,0,7297,0,0,0,0,0,0,0,0,0,0,7300,0,7301,0,0,0,0,0,0,0,0,0,0 +,0,0,7302,0,0,0,0,7305,0,0,0,0,7307,0,7308,0,7310,0,7335,0,0,0,0,0,0,0,7337,0, +7343,7347,0,0,0,0,0,7348,0,7349,7350,7352,7354,0,0,0,0,7357,0,7358,7366,0,7367, +7368,0,0,7373,0,0,0,7374,0,0,0,0,0,0,0,7376,0,0,0,7377,0,0,0,0,0,7378,0,7379, +7380,0,0,0,0,0,7383,0,0,7386,0,0,0,0,7398,0,0,0,7399,7400,0,7401,0,0,0,0,0,0,0, +7402,0,0,0,0,0,7405,0,0,0,0,0,7406,0,0,0,0,0,0,0,0,7421,7427,7429,0,0,0,7435,0,0 +,7436,0,0,0,7437,0,0,0,0,0,0,7438,7443,0,7446,0,7448,0,0,0,0,0,0,0,0,0,0,7456,0, +0,0,0,0,7457,0,0,7461,0,0,0,0,0,7462,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7463,7466,7472, +0,7476,0,0,7490,0,7491,0,0,7493,0,0,0,7498,7499,0,0,7508,0,0,0,0,0,7512,0,0,0, +7513,7514,7516,0,0,0,0,7518,0,0,7519,7521,7522,0,0,0,7526,0,0,7529,0,0,7531,0, +7536,0,7538,0,7539,0,0,7541,7542,7546,0,0,0,0,0,7547,0,7548,0,0,0,0,0,7550,0,0, +7552,7553,0,0,0,0,0,0,0,0,0,0,7554,7563,0,7573,0,0,0,0,0,0,7574,7576,0,7578,7581 +,7583,0,0,0,7584,0,7587,0,0,0,0,0,7589,0,0,0,7594,0,0,7595,0,0,7600,7602,7610,0, +0,0,0,0,7612,0,7613,7614,0,0,7615,0,0,7616,0,7620,0,7621,7622,0,7623,0,0,0,0, +7626,0,0,0,0,7627,7629,7631,0,0,7633,0,0,0,0,0,7639,0,7640,7642,0,0,7643,0,0,0,0 +,7644,0,0,0,0,0,0,0,7645,0,0,0,0,0,7661,7662,7663,7665,0,7666,0,7667,0,7684,7688 +,7690,0,7691,0,0,0,0,0,0,7692,0,0,7700,0,7707,0,7708,0,7709,0,7721,0,0,0,7722,0, +7724,0,0,0,0,0,0,7729,7731,0,7732,0,7733,7735,0,0,0,0,0,0,0,7739,0,0,7741,7745,0 +,7748,0,0,0,7751,0,0,0,7752,0,0,0,0,0,0,0,7753,0,0,7756,0,7757,0,7759,0,7760,0,0 +,0,0,7761,7768,0,0,7769,0,0,7770,0,0,7771,0,0,7772,0,0,7773,0,0,0,0,0,7778,7783, +0,0,0,0,0,7784,7785,0,7790,0,0,0,0,7792,0,7798,0,0,0,0,0,7799,0,7810,0,0,7813,0, +7814,0,7816,0,7818,7824,7825,7826,0,7828,7830,0,0,0,7840,0,7842,0,7843,0,0,0,0, +7844,0,0,0,0,0,0,0,7846,0,0,0,0,0,7856,7857,7858,7862,0,7865,0,0,7866,0,0,7913,0 +,0,0,0,7914,0,0,7915,7917,7918,7919,0,7920,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7921, +7922,0,7924,0,0,7925,0,0,7927,0,7930,7935,0,0,7937,0,0,0,0,0,0,7939,0,7940,0,0,0 +,0,0,7941,0,0,0,0,7945,0,0,0,0,7949,0,0,0,0,0,0,0,0,7950,0,7953,0,0,0,0,0,0,0, +7968,0,0,0,0,7969,7972,7992,0,7993,0,0,0,0,0,0,0,0,0,0,0,7994,0,0,0,0,8007,8008, +0,0,0,0,0,0,0,0,0,0,0,0,8010,0,0,0,8012,0,0,0,0,0,0,0,0,8018,0,8028,8029,0,0, +8030,0,0,8032,8033,0,0,8034,8036,0,0,0,0,0,0,0,0,0,0,8037,0,0,0,8043,8052,8059, +8060,0,0,8061,0,0,0,8062,0,8063,0,8064,0,8066,8068,0,0,0,8080,8081,0,8089,0,0,0, +0,0,8092,0,0,0,0,0,0,8093,8110,0,0,0,0,0,0,0,8111,0,0,0,0,0,8112,8115,0,8117,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8120,8121,8122,8128,8129,8130,8131,0,0,8139,0,0, +8144,0,0,0,0,8145,8146,8153,0,0,0,0,0,0,0,0,8154,0,8157,8160,8162,0,8164,8165,0, +0,0,0,8166,8167,0,0,8179,0,0,0,8185,0,0,0,8186,0,0,8187,0,0,0,8188,0,0,0,0,0, +8204,0,0,0,0,8210,0,0,0,0,0,8213,0,8214,0,0,8215,0,0,0,0,0,0,8218,0,0,0,0,0,0,0, +0,0,8219,0,8221,0,0,8222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8225,0,0,0,8233,0,0, +8242,0,0,0,0,0,0,0,0,0,0,0,8247,0,8248,8252,0,8256,8257,0,0,8261,0,8264,8265,0,0 +,0,0,8267,0,0,0,8269,0,0,0,0,0,0,0,0,0,8270,0,0,0,8278,0,8279,8283,0,0,8285,8286 +,8289,8292,0,0,0,0,8293,8295,8299,8300,8301,0,0,0,0,0,0,8304,8307,0,0,0,0,0,0,0, +8321,0,0,0,8322,8323,8325,8326,8327,0,0,8332,8338,0,0,8340,0,0,0,0,0,8350,0,0, +8351,0,8354,8355,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8360,8372,0,0,0,0,0,0,0,0,8377,0,0, +0,0,8380,0,0,0,8383,0,8384,0,0,0,0,8386,8392,0,0,8394,0,0,0,0,0,0,0,8396,8397,0, +8398,0,8399,0,0,0,0,0,8400,0,8401,8410,8411,0,8412,8413,8422,0,0,0,0,8423,0,0,0, +0,8424,0,0,8425,0,0,0,0,0,0,0,8441,8442,0,0,0,0,0,0,8443,0,0,8444,0,8447,0,0,0,0 +,8451,0,8458,0,8462,0,0,8468,0,8469,0,0,0,8470,0,8473,8479,8480,0,0,0,0,8481, +8483,0,0,0,0,0,0,0,0,0,8484,0,0,8490,0,0,0,0,0,0,8491,8493,8494,0,8528,0,0,0,0,0 +,0,0,8530,0,0,0,0,0,0,0,0,8534,8538,8540,0,0,8541,0,0,8545,0,8557,0,0,8569,8570, +0,0,8571,8574,8575,8579,0,8583,0,0,0,0,8591,0,0,0,0,0,0,0,0,8606,0,8607,0,0,0,0, +0,0,0,0,0,8608,0,0,8609,0,0,0,8610,0,0,0,8611,0,0,8613,8617,8621,0,0,8622,0,8623 +,0,8624,8625,0,0,0,0,0,0,0,0,0,8637,8638,8639,8650,0,0,0,0,8652,8654,8655,0,0,0, +0,0,0,0,0,0,0,8656,0,0,0,0,0,8657,0,0,0,0,0,0,0,0,0,8658,0,0,8659,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,8660,0,0,0,0,0,0,8661,8663,8664,0,0,0,0,8665,0,8669,0, +0,0,0,0,0,0,8671,8674,0,8684,0,8686,0,0,0,8689,0,0,0,8690,0,8706,0,0,0,0,0,0,0,0 +,0,0,0,8710,0,8711,8713,8714,8724,8727,8728,8733,8736,0,8737,8739,0,0,0,0,8742, +8743,8745,8754,0,0,0,0,8756,0,0,0,0,0,0,8757,8760,0,0,0,0,0,8762,8763,8764,0, +8766,8769,8770,8773,0,8774,0,8779,0,0,0,0,8780,0,0,8781,0,0,8783,0,0,0,0,0,0,0,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8784,0,0,0,0,0,0,0,0,8785,0,0,0,0,8786,0,0,0,0,8788 +,8790,0,0,0,8803,0,8813,8814,0,0,0,0,0,8815,8816,0,0,0,0,8818,0,0,0,0,8822,8828, +8829,0,8831,0,0,0,0,8833,0,0,0,8834,0,0,0,8835,0,8836,0,0,0,8837,0,0,0,0,0,0, +8838,8839,0,0,0,0,0,0,0,0,0,0,0,8840,0,0,0,8841,0,8842,0,0,0,8846,0,0,0,0,0,0,0, +8847,0,8848,0,0,8864,0,0,8866,0,0,8870,8872,0,0,8873,8874,0,0,0,0,0,0,8875,0, +8876,0,0,0,0,8896,8900,0,0,0,0,8901,0,0,0,0,0,8904,0,8907,0,0,0,0,8911,8912,8913 +,0,0,0,8914,0,8915,0,0,0,0,0,0,0,0,0,0,0,0,8916,0,0,0,8929,0,0,0,0,0,0,0,0,0,0, +8930,0,8932,0,8943,0,0,0,8945,8947,0,0,0,0,8949,0,8950,0,8954,8957,0,0,8970,0,0, +0,0,8971,0,8996,0,0,0,0,8997,9000,0,0,0,0,9001,9002,0,9004,9009,9024,0,0,0,0,0,0 +,0,0,0,0,0,0,9027,9082,0,0,9083,9089,0,0,0,0,0,0,9090,0,0,0,9092,0,0,9093,0,9095 +,0,0,9096,9097,9101,9102,0,0,0,0,0,0,0,0,9112,0,0,0,0,0,0,9114,0,0,9120,0,9121, +9122,0,0,0,9123,9124,0,0,9125,0,0,9126,0,9127,0,0,9129,9131,0,0,0,9132,0,0,9136, +0,9144,0,0,9148,0,0,0,0,0,0,9149,0,9152,9163,0,0,9165,0,0,0,0,0,0,0,0,0,0,0,0,0, +9166,0,9169,0,0,0,0,0,0,0,9170,0,0,0,0,9172,0,9174,9175,9176,0,9177,0,0,0,0,0,0, +0,0,9186,0,9187,0,0,0,9188,9189,0,0,9190,0,0,0,0,9191,0,0,0,9193,0,0,0,0,9197, +9198,0,0,0,9208,9211,0,0,0,0,9216,9217,0,9220,0,0,0,0,9221,9222,9223,0,9224,9225 +,0,0,9227,0,9228,9229,0,0,9230,0,9232,0,9233,0,0,0,0,0,9234,9235,0,0,9237,0,0,0, +0,0,0,0,0,9238,9240,0,0,9241,0,0,0,0,9244,0,0,0,0,9247,0,0,0,0,0,0,0,0,0,0,9248, +0,0,0,9249,0,0,0,0,0,9250,0,0,0,0,9251,0,0,9252,9255,0,0,0,9256,0,0,0,0,0,0,0, +9257,0,0,9258,0,0,0,0,0,0,9259,0,0,0,0,0,9262,9263,0,0,9265,9266,0,0,0,0,0,0,0,0 +,9268,9271,0,0,0,0,0,0,0,0,0,9273,0,0,0,9276,9277,9279,0,0,0,0,0,0,0,9280,0,0, +9293,0,0,0,0,0,9297,9301,0,0,0,0,0,0,0,0,0,0,0,9308,9309,9313,9321,9322,0,9326, +9327,0,0,9477,0,9479,0,0,0,0,9482,0,0,0,9483,0,9484,0,0,0,0,0,0,0,0,0,9485,0,0, +9486,0,0,0,9489,0,0,0,0,9490,9491,0,0,0,0,9493,0,9495,9496,0,0,0,0,0,0,0,0,9500, +0,9502,0,0,0,0,0,9504,9507,0,9509,0,9511,0,0,9513,0,0,0,0,0,0,0,0,9515,0,0,0,0,0 +,0,9516,9517,0,0,0,0,9532,0,0,9533,0,0,9538,0,9539,9540,0,0,0,0,9541,0,0,0,9542, +0,0,0,0,0,0,0,0,9544,9545,0,9546,0,0,0,0,0,0,9547,9548,0,0,0,9550,0,9557,0,9558, +0,9561,0,9563,9570,0,9572,9574,9575,0,0,0,9577,9592,0,0,9596,0,0,0,9598,0,9600,0 +,9601,0,0,0,0,0,0,9608,0,9638,9639,0,0,0,0,0,0,0,9641,0,0,9643,9644,9645,9646,0, +0,0,9648,0,0,0,0,0,0,0,9650,9654,0,0,0,0,0,0,0,0,9655,0,0,0,0,0,9656,0,9657,0,0, +0,0,9658,0,0,9659,0,0,9664,0,0,9665,0,9667,9669,0,0,0,0,0,0,0,0,0,0,0,0,9671,0, +9673,9681,0,0,0,0,9682,9683,9684,0,0,0,0,9686,9698,0,0,9700,9701,9702,0,9703, +9717,0,0,0,0,9718,0,9726,0,0,0,0,9727,0,0,0,9728,0,9742,0,9744,0,0,0,9750,0,9754 +,9755,0,0,0,0,0,9756,0,9757,9768,0,9769,0,0,0,9770,9771,0,9773,0,9774,0,9775,0,0 +,0,9776,9777,9784,0,0,0,9786,0,9789,0,0,0,0,9793,9794,0,0,0,9808,0,0,0,0,0,9811, +0,0,0,0,0,0,0,0,0,0,0,0,9812,0,9820,0,9823,0,9828,0,0,0,0,9830,0,0,9833,9836,0,0 +,0,9840,0,0,0,9841,0,0,9842,0,9845,0,0,0,9847,9848,0,0,9855,0,0,0,0,0,0,9856, +9863,9865,0,0,0,0,0,0,0,0,9866,9867,9868,9873,9875,0,0,0,0,0,0,9880,0,9886,0,0,0 +,9887,0,0,9891,0,0,0,0,0,0,0,9906,9907,9908,0,0,0,9909,0,0,0,0,0,0,9910,0,0,0,0, +9913,0,0,0,0,9914,0,0,0,0,0,9922,0,0,0,0,9923,9925,0,0,0,0,0,0,9930,0,0,0,9931,0 +,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9932,0,9939,0,0,9940,9962,9966,0,9969,9970,0,0,9974 +,0,9979,9981,9982,0,0,0,9985,0,0,0,0,0,0,9987,0,0,0,0,0,0,0,9988,9993,0,0,9994,0 +,0,0,9997,0,10004,0,0,0,0,0,10007,10019,10020,10022,0,0,0,10031,0,0,0,0,0,10032, +0,0,10034,0,10036,0,0,0,0,10038,0,10039,10040,10041,10042,0,0,0,0,0,10043,0,0,0, +0,0,10045,10054,0,0,0,0,10055,0,0,10057,10058,0,0,0,0,0,0,10059,0,0,0,0,0,0,0, +10060,0,0,0,0,0,0,0,10063,0,10066,0,0,0,10070,0,10072,0,0,10076,10077,0,0,10084, +0,10087,10090,10091,0,0,0,10094,10097,0,0,0,0,0,0,10098,0,0,0,0,0,0,10103,0, +10104,0,10108,0,0,0,0,0,0,0,0,10120,0,0,0,10122,0,0,10125,0,0,0,0,10127,10128,0, +0,10134,0,10135,10136,0,10137,0,0,10147,0,10149,10150,0,0,10156,0,10158,10159, +10160,10168,0,0,10171,0,10173,0,0,0,10176,0,0,0,0,10177,0,0,0,0,10178,0,0,0,0, +10194,0,10202,0,0,10203,10204,0,10205,10206,0,10207,0,0,0,0,10209,0,0,0,0,0,0,0, +10213,0,0,0,0,0,0,10217,0,10229,0,10230,10231,0,0,10232,0,0,10237,10238,10244,0, +0,0,0,0,10250,0,10252,0,0,0,0,0,0,10255,0,0,10257,0,0,0,0,0,0,10258,0,10259,0,0, +0,0,0,0,0,0,10260,0,0,0,0,0,0,0,10284,10288,10289,0,0,0,10290,0,10296,0,0,0,0,0, +10297,0,0,0,0,0,0,10298,0,0,0,0,10299,10303,0,0,0,0,0,10306,0,0,0,10307,0,10308, +0,0,0,0,10311,0,0,0,0,0,0,0,10315,10317,0,0,0,10318,10319,0,10321,0,10326,0, +10328,0,0,0,0,10329,0,0,10331,0,10332,0,0,0,0,0,0,10334,0,0,10335,10338,0,0,0,0, +0,10339,10349,0,0,0,0,0,0,10351,0,10353,0,0,0,0,0,0,10362,0,10368,0,10369,0,0,0, +10372,10373,0,0,0,0,0,10374,0,0,0,10375,0,10376,0,0,10386,10388,10390,0,0,0,0,0, +0,0,10391,0,0,10392,10394,0,0,10396,0,10397,0,10403,0,0,0,0,0,0,0,0,10404,0, +10405,10410,0,0,10411,0,10412,0,0,0,0,0,0,0,10421,10422,10423,0,0,0,0,0,0,0,0,0, +10425,0,0,10427,0,0,10430,0,0,0,0,0,10432,0,10433,10434,0,0,0,0,10436,10437,0, +10438,0,10439,0,10444,10446,0,0,0,0,0,10448,0,0,0,0,0,10449,0,0,0,0,0,0,0,10451, +0,10453,0,0,0,10454,10457,0,0,10459,0,10469,0,0,0,0,0,10472,10481,0,0,0,0,0, +10482,10483,0,10492,0,0,0,0,0,0,0,0,0,0,10499,0,0,0,10502,0,0,10510,0,10521, +10524,0,0,10525,10526,10528,0,0,0,0,0,0,0,0,10530,0,0,0,0,10533,0,10534,0,0,0,0, +0,0,0,0,0,0,10535,10536,0,0,10544,0,10553,10556,0,10557,10559,0,0,0,0,0,10562, +10563,10564,0,10565,0,0,0,10566,0,10567,0,0,0,0,10575,0,0,10576,0,10578,0,0,0,0, +0,0,0,0,0,0,10585,10586,10587,10589,0,10590,0,0,10594,0,0,0,0,0,10598,0,0,10601, +0,0,0,10602,0,10603,0,10604,0,10605,0,0,10607,0,10626,0,10627,0,0,0,0,0,10629, +10630,10631,0,0,0,10646,0,0,0,10647,0,10650,0,10651,0,0,0,10652,10653,10655,0, +10658,0,0,10659,0,10667,0,0,0,0,10669,0,0,0,0,0,0,0,0,0,10670,0,0,0,10671,0,0,0, +0,10672,10673,0,10674,0,0,0,10676,0,0,0,0,0,0,10678,0,10682,0,0,10692,0,10697,0, +0,0,0,10698,0,0,0,10700,0,0,0,0,0,10703,0,10704,0,0,0,0,0,0,0,10705,0,10715, +10718,10720,0,0,10722,0,0,0,0,0,0,0,0,10723,0,0,0,0,10726,0,0,0,0,0,10727,10730, +10743,0,0,0,0,0,0,10744,0,0,10745,0,0,0,0,0,0,10748,0,0,0,0,10750,0,0,10752, +10753,0,0,0,10756,0,0,0,0,0,0,10758,0,0,0,10759,0,10769,0,0,10772,0,0,0,0,0,0, +10773,0,0,0,10777,0,0,10779,0,0,0,0,0,0,0,0,10780,10784,0,0,0,10789,0,0,0,10791, +0,0,0,0,0,0,0,0,0,10795,0,0,10796,0,10808,0,10809,0,0,0,10810,0,0,0,10812,0,0, +10814,0,0,0,0,0,0,0,0,0,10815,0,0,0,0,10816,10817,0,0,0,0,10819,0,10820,0,0,0,0, +10821,10822,10823,0,10826,10849,0,0,0,0,10850,0,0,10852,0,10853,0,0,10856,0,0, +10857,10858,10859,10860,0,0,0,0,0,0,10863,0,10866,10867,10872,10890,0,0,10891, +10892,0,0,0,0,0,10893,0,0,0,10896,10899,0,0,10900,10902,0,0,0,0,0,10903,0,0,0,0, +0,0,0,0,0,0,0,0,10905,0,10906,0,0,0,0,10908,10911,0,10912,0,0,10916,0,0,0,0,0, +10917,0,10918,0,0,0,10923,0,0,0,0,0,10924,0,0,10928,10929,0,0,10930,0,0,0,10932, +0,0,0,0,10939,0,0,10945,0,0,0,10947,0,0,10948,0,0,0,0,0,0,0,0,0,0,0,0,10958,0, +10960,10962,0,0,10964,0,0,0,10966,0,0,0,0,0,0,0,0,0,0,10967,0,0,0,10968,0,0,0, +10973,0,0,0,0,0,10975,0,0,0,10976,10978,0,0,10982,10984,10987,0,0,10988,0,10989, +0,0,10991,0,0,0,0,10992,0,0,0,10993,0,10995,0,0,0,10996,10997,0,0,0,10998,0, +10999,0,11001,0,0,0,0,0,0,11010,11012,0,11013,11016,11017,0,0,11019,11020,11021, +0,0,0,0,0,0,0,0,0,0,0,0,11022,0,0,11023,11029,0,0,0,0,11031,0,0,0,11034,0,0,0,0, +11055,0,0,0,0,0,11056,11060,0,0,0,0,0,0,11061,0,0,11064,11065,0,11066,0,11069,0, +11085,0,0,0,0,0,11086,0,0,0,11088,0,0,0,11094,0,0,0,11095,11096,0,0,0,0,0,0, +11097,11098,0,0,0,0,0,0,11099,0,0,11102,11108,0,0,0,11109,0,11114,11119,0,11131, +0,0,0,11142,0,0,11143,0,11146,0,11147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11148,0, +11149,11152,11153,11154,0,11156,0,11157,0,0,0,11158,0,0,11159,11160,0,0,0,0,0,0, +0,0,0,0,0,0,11163,0,0,11164,11166,0,0,0,11172,11174,0,0,0,11176,0,0,0,0,0,11182, +11183,0,0,0,11184,11187,0,0,11188,11189,0,0,0,0,0,0,11194,0,0,0,0,0,0,0,11200, +11202,0,0,0,0,0,0,11203,0,11204,0,0,0,0,0,11205,0,0,0,11206,0,11207,0,0,11209,0, +11211,0,11214,0,0,11231,0,0,0,11293,11295,0,0,11296,11297,11302,0,0,0,11307,0,0, +0,0,11309,11310,0,11311,0,0,0,11313,0,11314,0,0,0,0,11334,0,11338,0,0,0,11339,0, +0,0,0,0,11340,0,11341,11342,0,11344,0,11345,0,0,0,11348,11349,0,0,11350,0,0,0, +11355,0,0,0,0,0,0,11356,0,11357,11370,0,0,11371,0,11374,11376,0,0,0,11377,0,0, +11378,11383,0,11386,11399,0,11400,11406,0,0,0,11408,0,0,11409,11412,0,0,0,0, +11417,0,0,0,11418,0,11421,0,11426,11429,0,0,0,0,0,11430,0,11437,0,11438,0,0,0,0, +0,11440,11453,0,0,0,0,0,0,11454,0,0,0,0,11455,0,0,11456,11460,11461,11463,0, +11469,0,11473,0,0,0,0,11474,0,0,0,11475,0,11476,11477,11480,0,0,0,0,11481,0,0, +11484,0,0,11487,0,0,0,0,0,0,0,0,0,0,11497,0,0,11502,0,11509,0,0,11510,11511, +11513,0,0,0,0,0,0,0,0,0,0,11515,0,0,0,0,11516,0,11520,11521,0,0,0,0,0,0,0,0,0,0, +0,11529,11530,11531,11534,0,0,11543,0,0,0,0,0,11547,0,11548,0,0,0,0,0,11552, +11556,0,11557,0,0,11559,0,11560,0,0,0,0,0,0,11561,0,0,11563,11564,0,11565,0,0,0, +0,11567,0,0,0,11569,0,11574,0,11575,0,0,0,11577,0,11578,0,0,0,11580,11581,0,0,0, +11582,11584,0,0,0,0,0,0,0,11587,0,11588,11591,0,11595,0,0,0,0,0,0,0,0,11596,0, +11597,0,0,0,0,11598,11601,0,0,0,11602,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11603, +11604,0,11606,0,0,11608,0,0,0,0,11610,0,0,11611,0,0,0,0,11613,0,11622,0,0,0, +11623,0,0,0,0,11625,0,0,11626,11627,11628,11630,0,0,0,0,0,0,11639,0,0,11646,0, +11648,11649,0,11650,0,0,0,0,0,0,0,0,0,11651,0,0,11652,11653,11656,0,0,11677, +11679,0,0,0,0,11680,0,0,11681,0,11685,0,0,0,0,0,0,0,0,11688,0,0,0,11716,0,11719, +0,0,0,0,0,11721,0,0,11724,11743,0,0,0,0,0,0,0,0,11745,11748,11750,0,0,0,0,0, +11751,0,0,0,11752,11754,0,11755,0,0,0,0,0,0,0,11759,0,0,0,0,0,0,11760,0,0,0, +11761,0,0,0,0,0,0,11766,11767,0,11772,11773,0,11774,0,0,11775,0,11777,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,11778,11780,0,0,0,0,0,0,0,11783,0,11784,0,0,0,11785, +0,0,0,11786,0,0,0,0,11788,0,0,11789,11791,11792,0,0,0,0,11795,11834,11835,11836, +0,0,11837,0,0,0,11838,0,0,11846,11851,0,11852,0,11869,0,0,0,11871,0,0,0,11872, +11874,0,0,0,0,0,0,11875,0,11876,11877,0,0,0,0,0,0,0,0,0,0,11883,0,0,0,0,0,0,0, +11884,0,11885,0,11886,0,0,11887,0,11894,11895,11897,11909,11910,0,11912,11918,0, +0,11920,0,11922,11924,11927,11928,0,0,0,0,11929,0,11934,0,0,0,0,0,11941,11943, +11944,0,11945,0,0,0,0,11948,11949,0,0,0,0,11953,0,11954,0,11955,0,11956,0,0,0,0, +0,11957,0,0,11959,0,0,0,0,0,0,0,0,11961,0,0,0,0,0,11978,0,0,0,11979,11980,11986, +11987,0,11992,0,0,0,0,0,11993,0,0,0,11994,0,11999,12004,12005,12006,0,0,0,0,0, +12011,0,0,12012,12014,0,0,12015,0,0,12019,12028,0,0,12029,0,0,12032,12033,0,0,0, +0,12034,0,12041,12043,0,0,12044,0,0,0,0,0,0,0,12046,0,0,0,0,0,0,0,12054,12055,0, +12056,0,0,0,12060,12064,0,0,0,0,0,12065,12067,12068,0,0,0,0,0,0,0,0,12074,0,0,0, +12075,12076,0,0,0,12079,0,12081,12086,12087,0,0,12088,0,0,0,0,12089,0,12092,0,0, +0,0,12097,0,0,0,0,0,0,0,0,12098,0,0,0,0,0,0,0,0,0,0,0,0,0,12102,12103,12104, +12111,0,0,12114,12116,0,0,0,12118,0,0,0,12119,12120,12128,0,0,0,0,12130,0,0,0,0, +0,0,12131,0,0,0,12132,12134,0,0,0,0,12137,0,12139,0,12141,0,0,12142,0,0,0,12144, +0,0,0,0,0,12145,0,12148,0,12153,0,0,0,0,12154,12171,12173,0,0,0,12175,0,0,0,0, +12178,0,0,0,0,0,0,0,12183,0,0,0,0,0,0,0,0,12184,0,0,0,12186,0,0,0,0,0,12187, +12188,0,0,12189,0,12196,0,12197,0,0,12198,0,12201,0,0,0,0,12203,0,12209,0,0,0,0, +12210,12211,12212,12213,0,12217,12218,0,0,0,0,0,0,0,0,0,12222,0,0,0,0,0,0,0, +12223,0,0,12229,0,0,0,0,12233,0,0,0,0,12234,0,0,12236,12242,0,0,0,12243,0,0,0, +12244,12253,0,12254,12256,0,12257,0,0,12275,0,0,0,0,0,12277,0,0,0,0,0,12278,0, +12289,0,0,12290,0,12292,12293,0,0,12294,0,12295,0,0,12296,0,12297,0,12298,0,0,0, +0,12301,0,0,0,0,0,0,0,0,0,0,0,0,0,12309,0,12338,12340,0,0,0,0,12341,0,0,0,0,0,0, +0,0,12342,12343,0,12344,0,0,0,0,0,0,0,0,0,12345,0,0,0,0,0,0,0,0,12346,0,0,0,0, +12348,0,0,0,0,0,0,0,0,0,0,0,0,12350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12351,0,12355, +12356,12357,0,0,12367,12370,12371,0,0,0,0,0,12372,12376,0,0,0,0,0,0,0,0,12379,0, +12382,0,12383,0,0,12384,0,0,0,0,12393,0,0,12394,0,0,0,0,12398,12403,0,0,12404,0, +0,0,0,0,0,0,0,0,0,0,0,0,12410,0,0,0,12411,0,0,0,12412,0,0,0,0,12420,0,12421,0,0, +0,0,0,12423,0,12425,12429,0,0,0,12431,12432,0,0,0,0,0,0,0,0,0,0,0,0,12434,0,0,0, +0,0,12435,12436,0,0,0,0,0,0,0,0,12437,0,0,0,0,0,12438,0,0,0,0,0,0,0,0,12445,0,0, +0,12450,12451,0,0,0,0,0,0,0,0,12452,12475,0,0,12493,12494,0,0,0,12495,0,0,0,0, +12496,12502,12509,0,0,0,0,12510,0,12512,12513,0,0,0,0,12514,0,0,0,12515,0,12520, +0,0,0,12524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12527,0,0,0,12528,0,0,0,12529,0,0,0, +0,0,12530,0,12535,0,0,12536,0,12538,0,0,0,0,0,0,0,0,0,0,0,0,12540,0,12548,0,0,0, +0,0,12550,0,0,0,12551,12552,0,0,0,12554,0,0,0,0,0,0,0,0,12555,0,0,12562,0,12565, +0,12566,0,0,0,0,0,0,0,0,0,0,0,0,12569,0,0,0,12571,12574,0,0,0,0,0,0,0,12577,0,0, +0,0,0,0,0,12578,12579,12603,0,12608,0,0,12611,0,12612,0,12615,0,12625,0,0,0,0, +12627,12646,0,12648,0,0,12657,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12670,0,0,12671,0, +12673,12677,0,0,0,0,0,0,0,0,0,0,0,12679,0,12681,0,12682,12693,0,12694,0,12697,0, +12701,0,0,0,12703,12704,0,0,0,0,12707,12737,0,0,12739,0,0,12740,0,0,12742,12743, +0,0,0,0,0,0,0,0,0,12745,0,12746,12747,0,12748,0,0,12759,12767,0,0,0,0,12773,0, +12774,12778,0,0,0,0,0,0,0,12779,0,0,0,0,0,12780,12793,0,12824,0,12825,0,12836,0, +0,0,0,12839,0,12842,0,0,0,0,0,0,0,0,0,0,0,0,12843,12845,0,12846,0,0,0,0,12847,0, +0,12850,12852,12853,0,0,0,12854,0,0,0,12855,0,12856,0,12858,0,0,12859,0,12862,0, +12863,0,0,12866,0,12869,12872,12873,0,0,0,0,0,0,0,0,0,12875,0,12877,0,0,12878,0, +0,0,0,0,0,0,0,0,12884,12885,12888,0,12889,0,0,0,0,12893,0,0,0,12895,12896,12898, +0,0,0,0,0,0,0,12902,0,12909,12910,0,12926,0,12928,0,0,0,12929,0,12930,0,0,0,0, +12931,0,12932,12933,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12934,0,12942,0,0,0,0,12944, +0,0,0,0,0,0,0,0,12946,0,0,12948,0,0,12949,0,0,0,0,12950,0,0,0,0,12951,0,12952,0, +12953,0,0,0,12954,12958,12959,0,0,0,0,0,12960,12964,0,0,0,0,0,12966,0,0,0,0,0,0, +0,0,12970,0,12971,0,0,0,0,0,0,12972,0,0,12982,0,0,0,12984,12985,0,12986,12996, +12997,13001,13002,0,0,0,0,13004,0,0,13005,0,0,13007,13009,0,13017,0,0,0,13020,0, +13021,0,0,0,0,0,0,0,0,0,0,13022,0,0,0,0,0,0,0,0,13024,13027,0,0,0,0,0,13028,0,0, +13029,0,0,0,0,0,0,0,13032,0,13037,0,0,0,0,0,0,13040,0,0,13041,0,0,0,13043,13044, +13046,0,0,0,0,13047,0,0,0,0,0,0,0,13049,13054,0,13056,0,0,13060,13061,0,0,0,0,0, +13067,0,0,13068,0,13071,0,0,0,0,0,13077,13078,0,0,0,0,0,13079,13080,13081,0, +13082,0,0,0,13085,0,0,0,0,0,0,0,13086,0,13087,13088,0,0,0,0,0,13094,0,13099,0, +13100,0,0,0,13101,0,13125,13126,13128,13129,0,0,13130,0,13131,0,0,0,0,0,0,13134, +0,0,0,0,0,0,0,0,0,0,0,13150,0,13168,0,0,0,0,0,0,0,0,0,13169,0,0,13170,0,0,0,0, +13174,0,0,0,13176,0,0,0,0,0,13177,0,13178,13183,13187,0,0,0,13189,0,0,13190,0,0, +13191,0,0,13206,0,0,0,13207,0,0,0,0,0,0,0,0,0,0,13212,0,0,13219,13232,0,0,0, +13241,0,13249,13253,0,0,0,0,0,13255,13259,0,13260,13261,0,13262,0,13272,0,0,0,0, +13276,0,0,0,0,13277,13299,0,0,13301,13302,0,0,13303,0,0,13305,0,13310,0,0,0, +13311,0,0,0,0,13325,0,13328,0,0,0,13329,0,0,0,0,0,0,13330,0,0,13331,0,13335,0,0, +13342,0,0,0,0,0,13343,0,13354,0,13362,0,13366,13367,13369,0,0,13371,13372,0, +13373,13374,0,13376,0,13380,13381,13386,0,13387,13388,0,13389,13391,13395,0,0,0, +0,0,13401,13409,0,13410,0,0,0,0,13420,0,0,0,0,0,13422,0,0,0,0,13423,0,0,0,0, +13425,0,0,0,0,0,13427,0,0,0,13428,0,0,13430,13438,0,13439,0,13445,0,13448,13449, +0,0,0,0,0,0,13451,0,13457,0,0,0,0,13458,13459,0,13460,0,0,0,0,13464,13465,13466, +13470,0,13471,13472,13474,13475,0,13476,0,0,13478,13479,0,13481,0,0,0,0,13487,0, +13490,0,13493,0,0,13494,0,0,13495,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13496,13497,0, +13500,0,0,13516,13522,0,0,13525,13528,0,0,0,13530,13535,0,13537,13539,0,13540,0, +13543,0,13544,0,0,0,0,0,0,13545,0,0,0,0,0,0,13547,0,0,0,13549,13555,0,0,0,13556, +13557,0,0,0,0,0,0,0,13558,0,13563,0,0,0,0,13564,0,0,0,0,0,0,0,0,13566,0,0,0,0,0, +0,13569,0,0,13571,0,0,0,0,13573,0,0,0,0,0,0,13578,0,0,0,0,0,0,0,0,0,0,13581,0, +13586,0,13595,0,13600,0,0,0,0,0,0,0,0,13601,13603,0,13604,13605,13606,13607,0,0, +13617,13618,0,0,0,0,0,0,0,13623,0,13625,13627,0,0,0,0,0,0,0,0,13629,0,0,0,13634, +0,0,0,13638,0,0,0,0,0,0,0,0,13654,0,0,0,0,0,0,0,0,0,0,13656,0,13659,0,0,13660,0, +0,13662,0,0,0,13663,0,13664,0,0,0,0,0,13668,0,13669,13671,0,0,13672,0,0,0,0,0,0, +13675,13685,0,13686,0,0,0,13687,0,0,0,13692,13694,13697,0,0,0,13702,0,0,0,0,0, +13705,0,0,0,0,13707,0,0,0,13714,0,0,0,0,0,0,0,0,0,13715,0,13716,13717,0,0,13719, +13724,13730,13731,0,0,0,0,0,0,0,0,13732,0,0,0,0,0,0,0,13734,0,13736,0,0,13737, +13738,13747,0,13751,0,0,13752,0,0,0,13753,0,13757,0,0,13762,13763,0,13764,13765, +0,13766,0,0,13767,0,0,0,13768,0,0,0,0,0,0,0,13769,0,0,13772,0,13775,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,13776,13778,13787,0,0,0,13797,0,13798,0,13801,0,13804, +13806,0,0,0,0,13816,13817,0,0,0,0,0,0,0,0,0,0,0,0,0,13834,0,13836,0,0,13838,0,0, +13839,0,13840,0,0,0,0,13842,0,0,0,0,0,0,13843,0,0,0,0,0,0,0,0,0,13845,0,0,0,0,0, +13858,0,0,13860,0,0,13861,0,0,13862,13863,0,13868,0,13869,13870,0,0,0,0,0,0,0,0, +0,0,13872,0,0,0,0,13873,13878,0,0,0,0,0,0,0,0,0,0,13886,0,13888,13889,13890,0,0, +13891,13894,0,13897,13899,13900,13904,0,0,13906,0,0,0,13909,0,0,0,13910,0,0,0, +13911,0,0,0,0,0,13912,13917,0,0,0,0,13918,0,13919,0,0,13920,0,0,0,13921,0,0, +13922,0,0,0,0,0,0,0,13924,0,13927,0,0,0,0,0,13932,0,13933,0,13934,0,0,13935,0, +13944,0,0,0,13954,0,0,13955,0,0,0,0,13956,0,13957,0,13967,13969,0,0,0,0,0,0,0,0, +0,0,0,0,13970,13990,0,13991,13994,0,13995,0,0,0,0,13996,0,0,13999,0,0,0,14018,0, +14019,0,14021,0,0,0,0,0,0,14041,0,0,0,0,0,0,0,0,14043,0,0,0,0,14046,0,0,0,14048, +14049,0,0,0,0,0,0,0,0,0,0,14051,0,0,14052,14056,0,14063,0,14064,14066,0,0,14067, +0,0,0,0,0,0,0,0,0,14068,0,0,0,14072,0,14074,14075,0,14076,14079,14085,14086, +14087,14093,0,0,0,0,14095,0,0,0,0,0,0,14096,14097,0,0,0,0,0,0,0,14098,0,14102,0, +0,0,0,0,14103,0,0,0,14104,0,0,14105,0,0,0,14107,14108,0,0,14109,0,0,0,0,0,0,0,0, +14117,0,0,0,0,14118,0,0,0,0,14119,0,0,14120,0,0,14121,0,14122,14127,0,14128, +14136,0,0,14138,0,14140,0,0,0,14141,14142,0,0,0,0,14146,0,0,14149,0,14151,0,0,0, +14152,0,0,14153,0,0,0,0,0,0,0,0,0,14154,0,14156,14157,0,0,14159,0,14161,0,0,0,0, +14162,0,0,0,0,0,0,14163,0,0,14173,0,0,0,0,0,0,14174,0,0,14176,0,0,14178,0,0, +14179,14181,0,0,14182,14185,14187,0,14190,0,0,14197,0,0,0,0,0,0,0,0,0,0,0,0, +14198,0,0,0,0,0,0,14199,14200,0,0,0,14204,0,0,14208,0,0,0,0,0,0,0,0,0,0,0,14231, +0,0,0,0,0,0,0,0,0,14234,0,0,14235,0,0,0,14240,14241,0,0,0,14246,0,0,0,14247,0, +14250,0,0,14251,0,0,14254,0,0,14256,0,0,0,14260,0,14261,0,0,0,0,14262,14267, +14269,0,0,14277,0,0,14278,0,14279,14282,0,0,0,14283,0,0,0,14284,14285,0,0,0,0, +14286,0,0,0,14288,0,0,0,14289,0,14290,0,14293,14301,14302,14304,14305,0,14307,0, +14308,14309,0,0,0,0,0,0,0,0,0,0,0,14311,14312,0,0,14317,0,0,0,0,0,0,0,14318,0,0, +0,0,14320,0,0,0,0,14321,14322,0,0,0,0,0,14326,14329,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +14330,14331,0,0,0,0,14332,0,0,0,14333,0,0,14337,14340,0,14341,0,0,14342,0,14345, +14346,0,0,14347,0,14362,0,0,0,0,0,14364,14365,14371,0,14373,0,0,14374,0,14379,0, +14400,0,0,0,0,0,14401,0,0,14405,0,14406,0,14408,14409,0,0,0,14417,0,0,14424,0,0, +0,0,0,0,0,0,0,14430,0,0,0,14431,0,0,14435,0,14440,0,0,0,0,0,0,14442,0,0,14443,0, +0,0,0,0,14446,0,0,0,0,0,0,0,14454,0,14457,0,14460,0,0,14466,0,0,0,0,0,14467,0,0, +0,0,0,0,14469,0,14477,0,0,0,0,0,0,14478,14482,0,0,0,14483,0,0,0,14485,14486,0,0, +0,14487,14488,14489,14492,14493,14494,14495,14496,14497,0,14499,0,14501,0,0,0,0, +0,0,0,0,0,0,14502,0,14507,14512,14513,14514,0,0,0,0,0,0,0,0,0,0,0,14515,14526, +14530,0,14537,0,14544,0,14547,0,0,14548,14550,14551,0,0,14552,0,0,0,14553,0, +14554,0,0,0,0,14556,14564,0,0,14565,14566,0,0,0,0,0,0,14568,0,0,14569,0,0,0, +14571,14576,0,0,14577,14578,14579,0,0,14580,0,0,0,0,14582,0,0,0,0,0,0,0,0,0,0,0, +0,14583,0,0,0,0,0,14587,0,14588,0,0,14600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,14601,0,0,14604,14605,14611,0,14613,0,0,0,0,14615,0,0,0,0,0,0,14627,0,14628,0, +0,0,0,14631,0,14633,14634,0,0,0,0,14635,0,0,0,0,0,0,0,0,14636,0,0,14639,14642,0, +0,0,0,14644,0,0,0,0,14645,14646,0,14653,0,0,14654,0,14658,0,14661,0,0,0,14665,0, +0,0,14668,0,0,0,0,0,0,0,0,0,14669,0,0,14670,0,0,0,14680,0,0,14681,0,0,0,0,0, +14682,14683,0,0,0,0,14686,0,0,0,0,14687,14697,0,0,0,0,14699,14705,14711,0,0,0,0, +0,0,0,0,0,0,14712,0,0,0,14713,0,0,0,0,14719,0,14720,14721,14726,0,0,0,14728, +14729,0,0,0,0,14731,0,0,0,0,0,0,0,14733,14736,14737,0,0,14740,14742,0,0,0,14744, +14753,0,0,0,0,14755,14758,14760,0,0,0,0,0,14761,14762,14765,14771,0,14772,0, +14773,14774,0,0,14775,0,0,14776,0,0,0,0,14777,0,14779,0,0,14782,0,0,14785,14786, +14788,0,0,0,0,0,14795,0,0,0,0,0,0,14798,0,14803,14804,14806,0,0,0,14809,0,0,0,0, +0,0,14810,0,0,0,0,14811,0,14812,0,0,0,0,0,14815,0,0,0,0,0,0,0,0,14816,0,14818,0, +0,0,0,0,0,14819,0,14820,0,14823,0,0,0,14824,0,0,14826,14827,0,0,0,0,0,0,0,0,0,0, +0,0,14830,0,0,0,0,0,14833,0,14845,0,0,0,0,0,14846,0,0,14847,14871,0,14873,0, +14876,0,14877,14878,14880,0,0,0,0,0,14881,0,14882,14894,0,0,0,0,14895,0,14907,0, +14908,0,0,0,0,0,0,0,14911,0,0,0,0,14920,0,0,14931,0,14932,14934,14935,0,0,14936, +0,14945,0,0,0,0,0,0,0,14947,0,0,14948,14949,14951,0,0,14952,0,0,0,14964,14973,0, +0,14990,0,0,0,0,14995,0,0,14998,15001,0,0,15002,15020,0,0,0,0,0,0,15021,0,15022, +0,0,0,0,15023,0,0,15025,15029,15033,0,0,0,15034,0,0,0,15035,0,0,0,0,0,15043, +15044,0,0,0,15045,15046,15048,15050,0,15065,0,0,0,0,15066,0,0,15075,15082,15084, +0,0,15085,15086,0,0,0,0,0,0,0,0,15088,0,0,0,15089,0,0,0,0,15094,0,15096,0,15097, +0,15100,0,0,15102,0,0,0,0,0,0,0,0,15105,0,0,15106,0,15109,15113,0,0,0,15115,0, +15118,0,0,0,0,0,0,15119,0,0,15120,0,0,0,0,0,15123,15129,0,0,0,15130,0,15131,0,0, +15134,0,15135,0,0,0,15137,15138,0,0,0,0,0,0,15139,0,0,0,0,0,15140,0,0,15154, +15162,0,15169,15170,0,15175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15177,0,15178,15179,0, +0,0,0,0,15183,0,0,0,0,0,0,0,0,0,0,0,0,15185,15187,0,15194,15195,15196,0,0,0,0,0, +0,0,15204,0,0,0,0,15206,0,0,0,0,0,15207,0,0,0,0,0,0,0,0,0,15213,0,15214,0,0,0,0, +0,0,0,15232,0,0,0,0,15234,0,15238,15240,0,15248,0,0,0,0,15250,15251,0,0,0,0,0,0, +0,15252,0,0,0,15255,15262,15266,0,0,0,15267,0,0,0,15277,15279,0,0,0,15280,15281, +15282,0,0,0,0,0,15285,0,0,0,0,15289,0,0,15291,0,0,0,0,0,0,0,15296,15297,0,0, +15304,0,0,0,0,15306,0,0,0,0,0,0,15307,15308,0,15309,0,0,15311,0,0,15312,15313,0, +0,0,0,0,0,0,0,0,0,0,0,15314,15317,0,0,0,15318,15319,0,0,0,0,15320,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,15321,0,0,0,0,0,15324,0,15325,15326,0,15330,0,0,0,0,15334,0, +15335,0,15341,0,0,15342,0,0,15343,15344,0,0,0,0,15345,0,0,0,0,15347,0,0,15348, +15349,15350,0,15356,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15357,0,15358,0,0,0,0,0,0,0, +15359,15360,15364,0,15380,0,0,0,0,0,15392,0,0,15393,0,15395,0,0,0,0,0,0,0,0, +15396,0,0,15397,15398,0,0,0,0,0,0,0,0,0,15399,0,15400,0,0,0,15402,0,15405,15410, +0,0,0,0,15411,0,0,0,15412,0,15416,0,0,0,0,0,0,0,15428,0,15435,0,0,15438,0,0,0,0, +15439,0,0,0,15440,0,0,0,15441,15449,15451,0,0,0,0,0,0,0,15452,0,0,15455,0,0,0, +15456,0,0,15458,0,15460,15461,0,0,0,0,0,15462,15464,0,15465,0,0,15466,0,0,15467, +0,0,0,0,0,15468,0,0,0,0,15481,0,0,15484,0,15485,15486,0,0,0,15487,0,0,0,0,0, +15488,0,15492,15498,0,0,0,15499,0,0,0,15500,0,15501,0,0,15512,0,15522,0,0,0, +15524,0,15525,15526,0,0,15527,0,0,15545,15546,0,15548,15552,0,15553,0,0,0,15554, +0,15555,0,15557,15565,15573,15577,15578,0,15582,0,15583,0,0,0,0,0,0,0,0,0,0,0,0, +0,15586,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15588,0,0,0,0,0,15589,0,0,0,0,0,0,0,15593, +15594,0,0,0,0,15595,0,0,0,0,0,0,15596,0,0,0,15597,0,0,0,0,15600,0,0,15601,0,0,0, +0,15602,15603,0,0,0,0,0,0,15604,0,15609,0,0,15612,0,0,15613,0,0,15615,15617, +15618,0,0,15620,0,15636,15637,0,0,15649,0,0,0,0,0,0,0,15650,0,0,15651,0,0,0, +15656,0,15658,0,0,0,15664,0,0,15665,0,0,15668,0,0,0,0,0,15669,0,0,15674,0,0, +15675,0,0,0,0,15676,0,0,0,0,0,0,0,0,0,0,0,15677,0,0,0,0,15678,0,0,0,0,0,15679,0, +0,15681,0,15686,0,0,0,0,15687,0,15688,0,0,15690,0,0,0,15697,0,15699,15700,0,0,0, +0,0,0,0,0,0,15701,0,15702,15703,0,15704,0,15705,0,15707,0,15709,0,15712,15716,0, +15717,0,15718,15720,0,0,0,0,0,15724,0,0,0,15725,0,15726,0,0,0,15740,0,15745, +15746,0,0,15747,0,15748,0,0,0,0,0,15749,0,0,0,15752,0,15753,0,0,0,0,0,0,15759,0, +0,0,15765,0,0,0,0,0,0,0,0,0,15767,0,0,0,15771,0,0,15784,0,0,0,0,15785,15790, +15791,0,0,15792,0,0,0,15807,0,15811,0,0,0,0,0,0,0,0,0,0,0,0,15818,0,0,0,15819,0, +0,0,0,15821,0,0,0,0,0,15822,15824,0,0,15827,0,0,15829,15831,0,15832,0,0,15833,0, +15835,15838,15839,15843,0,0,0,0,0,0,0,0,0,0,0,15844,0,0,0,0,15845,15851,15856,0, +0,0,0,0,0,0,15858,15860,0,15861,0,0,0,15864,0,0,0,0,15865,0,0,0,0,0,0,15866,0, +15872,0,0,15876,0,0,0,0,15877,15878,15883,15885,0,0,15888,0,0,0,0,0,15889,15890, +0,0,0,0,0,0,0,0,15892,0,0,0,0,0,0,0,15893,0,0,15894,0,0,0,15895,0,15896,15897,0, +15898,15901,15902,0,15911,15915,0,15916,0,15924,15935,0,15937,0,0,0,0,0,15950,0, +0,0,0,0,0,0,15958,0,0,0,15961,0,0,15966,0,15967,0,0,15977,0,0,15978,0,0,15981, +15982,15983,0,0,0,0,0,0,0,15986,0,0,0,15990,0,15991,15995,15998,0,15999,0,16000, +0,0,0,0,16008,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16009,16011,0,16013,0,0,0,0, +0,0,0,0,16014,0,0,16015,16023,16024,16025,0,0,16026,0,16030,0,16032,0,16033,0,0, +0,0,0,0,16035,16036,16037,0,0,0,0,0,16039,0,0,0,0,16041,0,0,0,0,0,16043,16044,0, +0,16047,0,0,0,16048,0,0,16049,16050,16052,0,0,0,0,0,16055,0,0,0,0,0,0,0,0,16056, +0,0,0,0,0,0,0,16058,16060,16061,0,0,16063,0,0,16064,0,0,0,16067,16068,0,0,16069, +16078,0,0,0,16079,0,0,0,16080,0,16081,0,0,0,16088,0,0,0,0,0,0,0,0,0,0,0,16089, +16093,0,16097,0,16103,0,16104,16105,0,0,16256,0,0,16259,0,0,0,0,0,0,0,16260, +16261,0,0,16262,0,0,16263,0,16268,0,0,0,0,0,0,0,16269,0,0,16270,16273,0,16274,0, +0,0,0,16275,16276,16277,16280,0,0,0,16281,16284,0,0,0,16286,0,16289,0,0,0,0,0,0, +0,0,0,16290,0,0,0,0,16291,0,0,0,0,0,0,0,16292,0,0,0,0,0,0,0,0,16293,16295,16297, +0,16302,0,16304,0,16305,0,16306,0,0,0,0,0,0,0,0,0,0,0,0,16307,16308,16312,0,0,0, +0,0,0,16313,16315,0,16318,0,0,0,16321,0,0,0,0,0,0,0,16326,16333,16336,0,0,0,0, +16337,16340,0,0,0,0,0,16345,0,0,16346,0,0,0,0,0,0,0,0,0,16347,0,0,16348,0,0,0,0, +16349,0,0,0,16350,0,16357,0,0,0,0,16359,16360,0,0,0,0,16362,16363,16364,16365,0, +0,16366,0,0,0,0,16367,16368,0,16369,16374,0,0,0,0,0,0,0,16376,0,0,0,0,16378, +16379,0,16380,0,0,0,16381,16383,0,0,0,0,0,16390,0,0,0,16399,0,16402,16404,16406, +16407,0,0,0,16409,16411,0,0,0,0,16412,0,16413,16415,16423,0,0,0,0,0,16424,0,0,0, +16428,16434,16435,16449,0,16450,16451,0,0,0,16453,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +16454,0,0,16456,16458,0,0,16459,0,0,16460,0,0,0,0,16462,0,16463,0,0,16466,0,0,0, +0,0,16479,0,0,16480,0,16481,16484,0,0,0,0,0,0,0,0,0,0,16485,0,0,0,0,0,0,16489,0, +0,0,0,0,16491,0,0,16498,0,0,16503,0,16505,0,0,0,0,0,0,0,0,16506,0,0,0,16508, +16509,0,0,0,0,0,0,0,0,16511,16513,0,0,0,16516,0,16517,0,16519,0,16529,0,0,16531, +0,0,0,0,0,0,16534,0,0,16541,16542,0,0,0,0,0,0,0,0,0,16543,16547,16548,0,0,0, +16551,0,16552,0,0,0,16553,0,0,16558,0,0,16562,16565,0,0,0,16570,0,0,0,16573, +16585,0,0,0,16586,16587,16595,0,16596,0,16598,0,0,0,16600,0,0,0,0,0,0,0,0,0,0,0, +0,0,16601,0,0,0,0,16603,0,0,0,0,0,0,0,16604,16612,0,0,0,0,16613,0,16618,0,0,0, +16640,0,0,16641,0,0,0,0,0,0,16645,0,0,0,0,16646,0,0,0,0,0,0,16651,0,0,0,0,16653, +16654,0,0,0,16655,0,0,16656,16667,0,0,0,0,16671,0,16672,0,0,0,16673,0,0,0,0,0, +16676,0,16686,0,0,0,0,16689,0,16690,0,16692,0,16693,0,16694,0,16696,0,0,0,16705, +0,0,0,0,0,0,16707,0,0,0,16709,0,0,0,0,16711,0,16712,16713,0,0,0,16715,0,0,0,0, +16716,0,0,0,0,0,0,0,0,0,16718,16724,0,0,16726,16727,0,0,0,0,0,0,0,16728,0,16729, +0,0,16730,0,0,0,0,0,16731,0,0,0,16732,0,0,0,0,16734,16738,0,0,0,0,0,0,0,0,16743, +0,0,16745,0,0,0,0,0,16749,0,16752,0,0,0,0,16756,0,0,16758,0,16759,0,0,0,0,0, +16760,0,0,0,0,0,0,0,16762,0,16769,0,16770,0,16772,0,0,0,16777,16780,0,0,0,0,0,0, +16781,0,0,16782,0,16784,0,0,16785,16787,16792,0,0,16794,0,0,0,16798,0,0,16809,0, +0,16814,16816,16817,0,16819,0,0,0,0,0,0,0,0,0,0,16820,0,0,16836,16839,0,0,16841, +16851,16857,0,0,16858,16859,0,0,16860,0,0,0,0,0,0,0,0,16862,0,16863,0,0,0,0,0,0, +0,16864,0,0,0,0,0,0,0,16876,0,16881,16882,0,16885,16886,0,16887,0,0,0,16889, +16891,0,0,0,0,0,16894,16895,0,0,0,0,0,0,0,0,0,0,0,16897,0,16898,0,0,0,0,0,16913, +0,0,16924,16925,16926,0,0,16927,0,0,0,16937,16938,0,0,0,16940,16941,0,0,0,16942, +16945,0,16946,16949,16950,0,0,0,16952,16955,0,0,0,16965,0,16969,0,0,16975,0,0, +16976,0,0,0,0,16978,0,0,16981,0,16983,16989,0,0,0,0,16990,0,0,16991,0,0,0,16993, +0,16994,16996,17000,0,0,0,0,0,17002,17004,0,17006,0,0,17007,0,0,0,0,17008,17013, +17014,0,0,0,0,0,0,0,0,0,17021,0,17031,0,0,0,0,0,17033,17036,0,17038,0,0,17039,0, +17045,0,0,17046,17047,0,0,0,0,17048,0,17049,17050,0,17051,17053,0,17054,0,17055, +0,0,0,0,0,17063,0,0,17064,0,0,0,0,0,0,0,17065,0,0,17068,0,0,0,0,0,17072,0,0,0,0, +0,0,17073,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17074,0,17080,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,17081,17083,17084,0,0,0,17085,0,0,0,0,17092,0,0,0,0,0,0,0, +0,0,17093,0,17095,17102,0,0,0,0,0,0,17103,0,0,17105,0,17107,0,0,0,0,17114,0,0,0, +0,0,17115,17125,17127,0,0,17128,0,0,0,17129,17130,0,17131,0,0,0,0,0,17132,17135, +17145,0,0,0,0,0,0,0,0,17146,0,17147,0,17148,0,0,0,0,0,0,17149,17150,0,17151, +17153,0,17155,0,0,0,0,17163,17171,0,17174,0,0,0,0,17179,0,0,17182,17185,0,0,0,0, +0,17186,0,0,17188,0,0,0,0,0,0,0,17189,17191,0,17194,0,0,0,0,0,0,0,0,0,17195, +17196,17203,17204,0,0,17205,17217,0,0,0,0,0,17218,0,0,0,0,17219,0,17220,0,17221, +0,0,17230,0,0,0,0,0,17236,0,17238,17239,0,0,0,17241,17244,0,0,17245,0,17248,0,0, +17251,0,17252,0,0,17264,0,17266,0,0,0,17268,0,0,0,0,17271,17272,0,17273,0,17295, +0,17302,0,17305,0,0,0,17306,0,0,0,0,0,0,0,17308,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +17309,0,17310,17313,0,0,0,0,17314,17315,0,17317,0,0,0,0,17318,0,0,0,0,0,0,0, +17320,0,0,0,0,0,0,17334,0,17344,17348,0,0,0,17350,17351,0,0,17353,0,0,17354,0,0, +0,0,0,0,0,0,0,17355,0,0,0,0,0,0,17356,17357,0,0,17359,0,0,0,17371,0,17372,0,0,0, +17393,0,0,0,0,17394,0,0,0,0,0,17395,0,0,17399,0,0,0,17401,17417,0,17418,0,17419, +0,0,0,0,0,17422,17423,0,0,0,0,0,17424,0,0,0,0,0,17428,17429,17433,0,0,0,17437,0, +0,17441,0,0,17442,0,0,17453,0,0,0,0,0,0,0,0,17454,17456,17462,0,0,17466,0,0, +17468,0,0,17469,0,0,0,0,17470,0,17475,0,0,0,0,0,17479,0,0,0,17483,17484,0,17485, +0,17486,0,17491,17492,0,0,17493,0,17494,17495,0,0,0,17496,0,0,0,17497,0,0,0, +17502,0,0,0,0,0,17503,0,17505,0,17507,0,0,0,17512,17513,17514,0,0,17515,0,0,0, +17519,0,0,0,17522,0,0,17523,0,0,0,0,0,0,0,0,0,17527,0,0,0,17528,0,0,0,17534,0,0, +0,0,17536,0,0,0,17539,0,17540,17543,17549,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17556, +0,0,17558,0,17559,0,0,17560,0,0,0,17563,0,0,0,0,0,0,17564,0,0,17565,17566,0, +17567,0,0,0,0,0,0,17569,17570,0,17575,0,0,0,0,0,0,0,0,0,0,0,17581,0,0,0,17582, +17583,0,17586,0,0,17587,0,0,0,0,0,0,0,17588,0,0,0,0,17596,17597,0,0,17598,17600, +0,0,0,0,0,0,17601,0,0,0,17604,0,0,17605,0,0,17607,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,17612,0,0,17618,0,17621,17622,0,0,0,0,17623,0,0,17624,0,0,17630,0,0, +17631,17633,17634,0,0,0,0,0,0,0,17635,0,0,17636,0,0,17637,0,17638,0,17640,0,0,0, +0,0,0,0,0,0,0,17641,0,0,0,0,0,0,0,0,0,0,17643,0,0,0,0,17645,0,0,0,0,0,0,0,0, +17646,17662,0,0,0,0,0,0,0,0,0,17663,17664,0,17665,17666,0,0,0,17669,17671,17673, +0,17679,0,0,0,0,0,0,0,17684,0,0,0,17686,0,17714,0,0,17720,17722,17726,0,0,17728, +0,0,17729,0,0,0,17732,0,17733,0,17734,0,0,0,17735,0,0,0,0,17737,0,0,0,0,17739,0, +0,0,17741,17742,0,0,0,0,17743,17744,17745,0,0,0,17749,0,17750,17751,17752,17754, +17761,17762,0,17763,0,17766,0,17772,0,0,0,0,0,17775,0,0,0,0,0,0,0,17776,0,0, +17777,0,0,17778,17779,0,17782,17783,0,0,0,0,0,0,0,0,0,0,17784,0,0,0,0,0,0,0, +17821,0,0,0,17822,0,0,0,17823,17825,0,0,0,0,0,17826,17831,17832,17833,0,0,17845, +0,0,0,17846,0,0,0,17848,17850,17854,0,17855,0,0,17859,0,0,0,0,0,0,17860,17861,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17870,17871,0,0,0,0,0,0,17872,0,0,0,17879,0, +0,0,17881,17883,0,17884,0,17885,0,0,17886,0,0,17887,17891,17953,0,0,0,0,17954,0, +0,17955,0,17968,0,0,17972,0,0,0,0,0,17974,0,0,0,0,17976,17978,0,0,17983,0,0,0,0, +18003,0,0,0,0,0,18007,0,0,0,0,0,18009,0,0,0,0,0,0,0,18010,0,0,0,0,0,0,18012,0,0, +18014,0,0,0,18015,0,0,0,18016,0,18017,0,0,0,18030,0,0,0,0,0,0,0,18031,0,0,18036, +18037,18038,0,0,18049,18056,0,18057,18058,0,18059,0,0,0,0,0,0,0,0,18062,0,0,0,0, +18064,0,0,0,0,0,0,0,0,18067,0,0,0,18068,0,0,18075,0,0,18078,18093,18094,0,0,0,0, +0,0,0,0,18097,0,0,0,0,0,18098,18100,0,0,0,18108,0,18111,0,0,18112,0,18113,0,0, +18115,18116,0,18118,0,0,0,0,18121,0,0,0,0,18123,0,0,0,0,0,0,0,0,0,18124,0,0,0,0, +18125,18126,0,18127,0,0,18128,18135,0,0,0,0,0,0,0,0,0,18150,0,0,0,0,0,18151, +18152,0,0,18156,18164,0,18166,18171,0,0,0,0,0,0,0,0,0,18172,18183,0,18184,0,0,0, +0,18185,0,18187,0,0,0,0,0,18188,0,0,0,0,0,0,0,0,18189,0,0,18190,0,0,18191,18192, +0,0,18194,18195,18196,0,0,0,18197,0,18203,0,18204,0,0,0,0,18205,0,0,0,18207, +18208,0,0,18214,0,0,0,18215,18216,0,0,0,18220,0,0,18222,0,0,0,0,0,18223,0,18225, +18231,0,18234,0,18235,0,0,0,0,18240,0,0,18241,18242,0,0,0,0,0,18243,18251,0, +18253,0,18254,0,0,0,18266,0,0,0,0,0,0,18269,18270,18271,18273,18281,0,0,0,0,0,0, +0,0,0,0,0,0,18282,0,18283,0,18284,0,0,0,0,0,0,18285,0,18287,18289,0,0,18290,0,0, +0,0,18308,0,0,0,18310,0,0,0,0,0,0,0,0,0,0,0,0,18311,0,18312,18313,0,18315,0,0, +18316,18320,0,18331,0,18332,0,18336,0,0,0,0,18337,0,18340,0,0,0,0,0,0,0,0,0, +18341,0,18344,18345,0,18346,0,0,0,0,0,18348,0,18351,0,0,18356,0,0,0,0,0,0,18357, +0,0,0,0,0,18367,0,0,0,18368,0,18369,0,18370,18371,0,0,0,18437,18444,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,18445,18450,0,0,0,0,18451,0,18452,0,0,0,18453,0,0,0,0,0,18455,0, +0,0,18456,0,18457,0,18460,0,0,18461,0,0,0,0,0,0,0,0,18466,0,0,18467,0,0,0,0, +18473,0,0,0,18476,0,18477,0,0,0,18478,18479,18480,0,0,0,18485,0,0,0,18486,0,0,0, +0,0,0,18488,18490,0,0,0,0,0,0,18491,0,0,0,0,0,18495,0,0,18496,0,0,0,0,0,0,18505, +0,18521,0,18522,18523,0,0,0,18525,18526,0,0,0,0,0,18527,0,0,0,0,18532,18533,0, +18534,0,0,0,0,0,0,18535,18537,0,18538,0,0,0,0,0,0,18540,18541,18542,18543,0, +18546,0,0,0,0,18553,18556,0,0,18558,0,0,18569,18571,0,0,0,18572,0,18574,0,0,0,0, +18586,0,0,0,0,0,18588,0,0,18589,0,0,0,0,0,0,18590,0,18592,0,0,0,0,18594,0,0,0, +18596,0,0,18597,18598,0,0,18601,0,0,0,0,18602,0,0,0,18603,18604,0,18605,0,0,0,0, +18608,0,0,18611,0,0,0,0,0,0,0,0,0,18612,0,18616,0,0,18617,18619,0,0,0,18628,0,0, +0,18629,0,0,18630,0,0,0,0,0,0,0,18631,0,18632,0,0,18635,18637,0,0,0,0,0,0,18641, +18643,18648,0,18652,0,0,18653,0,18655,18656,0,0,0,18657,0,0,18666,18674,0,0,0,0, +18677,18684,18685,0,0,18686,0,0,18690,0,0,0,0,0,0,0,18695,18696,0,0,0,0,0,0,0,0, +0,0,18697,0,0,18700,0,0,0,0,0,0,18702,0,18708,0,0,18709,0,18710,0,0,18711,0, +18714,0,0,18718,0,0,0,0,0,0,18719,0,0,18722,0,18726,0,0,0,0,0,0,0,0,0,0,0,0,0, +18731,0,0,0,0,0,18739,18741,0,0,18742,0,18743,18744,18746,18748,0,18752,18753,0, +0,18754,18763,0,18765,0,0,0,18766,0,0,0,18769,0,0,0,0,0,18773,18778,18779,18781, +0,0,18784,18787,0,18788,0,18793,0,0,0,0,0,0,18795,0,0,18800,0,0,0,0,0,18801, +18804,0,0,0,0,0,0,0,18806,0,0,0,18811,18815,18816,0,0,0,0,18825,0,0,18827,18829, +0,0,18830,0,0,0,0,18831,0,0,18832,0,0,0,0,18833,0,18840,0,18841,0,18842,0,0,0,0, +18843,0,18844,0,0,0,0,0,0,18845,18846,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +18848,0,0,0,18853,18860,0,0,18862,18866,0,0,18867,18869,0,0,18874,18881,18891,0, +0,0,0,0,0,0,0,0,0,18892,0,0,0,0,0,0,0,0,18895,0,18896,0,0,0,18900,0,0,0,18901,0, +18902,18915,18916,0,0,0,0,0,0,0,0,18919,0,0,0,0,0,18920,0,0,0,18921,18929,0,0,0, +0,18930,0,0,0,0,0,0,18932,0,0,0,0,18934,18942,0,0,0,18951,18957,0,0,0,0,18958,0, +0,0,0,18959,18960,0,0,18961,0,0,18962,0,0,0,0,18963,18964,0,0,0,18965,0,18967,0, +0,0,0,0,0,0,0,0,18968,0,18969,0,18970,18973,18976,0,0,0,0,0,0,18977,0,0,0,18981, +0,0,0,18990,0,18998,0,0,0,0,0,18999,19003,0,0,19005,0,0,0,19006,0,0,0,0,0,0, +19008,19011,0,0,19018,0,0,19019,0,19024,0,19031,19032,0,19039,0,19041,19050,0,0, +0,19051,19055,19056,0,19059,19063,19064,0,0,19088,0,0,0,19093,19094,0,0,0,0, +19095,0,19096,0,0,0,19097,0,0,19098,0,19099,19100,0,0,19103,0,0,0,0,0,0,0,19111, +0,0,0,0,0,0,19112,0,0,0,19116,19117,0,19121,19122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,19123,19124,0,0,0,0,0,0,0,19125,19126,0,19128,0,0,0,0,0,0,0,0,0,0, +19129,19130,19131,19132,0,0,19146,0,0,19147,19156,19158,0,0,0,0,0,0,0,0,19182, +19185,0,0,19187,0,0,0,19193,0,0,0,0,0,19194,0,19197,0,0,0,0,19198,0,0,0,0,0,0,0, +0,0,0,19202,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19203,0,19205,19210, +0,0,0,19213,0,19218,0,0,0,19223,19229,0,0,19230,0,0,19231,19232,19233,19239,0,0, +0,0,0,19240,0,19248,19249,0,0,0,0,19254,0,19256,19258,19259,0,0,19261,0,19266,0, +0,0,19272,0,19278,19281,19282,0,0,0,0,0,0,0,0,0,0,0,0,19283,0,0,19284,0,0,19285, +19287,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19288,19291,0,19292,0,0,0,0,19297,0,19298,0,0, +0,0,19302,19303,0,0,0,0,19304,19305,0,0,0,0,19314,0,0,19315,0,0,19321,0,0,0,0,0, +0,0,19322,0,19333,0,19334,19335,0,19336,19337,0,0,0,0,0,0,0,0,0,0,0,19346,0,0, +19353,0,19354,19362,0,19366,19367,0,0,19369,0,19375,0,19377,19380,19388,0,0,0,0, +0,19389,19390,0,0,0,0,19392,0,0,0,0,0,19402,0,0,0,0,0,0,0,0,19412,0,0,19413, +19422,0,19424,0,0,0,19425,0,0,0,19428,0,0,0,0,19431,0,0,0,0,0,19432,0,0,0,0,0, +19448,19459,0,0,19461,0,19462,19463,0,19467,19474,19482,0,0,0,0,19494,0,0,0,0, +19501,0,0,0,0,0,0,0,0,0,0,19502,19504,0,0,0,0,0,0,0,19505,0,0,0,0,19506,19507,0, +0,0,19508,0,0,19511,0,0,19514,0,19515,0,19516,0,19518,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,19530,0,19537,19538,0,19543,19546,0,19547,19551,0,0,0,0,0,0,19552, +19553,0,0,0,0,0,0,0,0,0,0,0,0,19555,0,0,19556,0,0,0,0,0,0,0,0,0,0,0,0,19560, +19561,0,0,19562,0,0,0,0,0,0,19565,19567,0,19568,0,0,0,19569,19570,0,19578,0,0,0, +0,19580,0,0,0,0,19581,19584,0,0,0,0,0,0,0,19585,19586,0,0,0,19587,19588,0,19589, +0,0,0,0,0,0,19592,19593,19599,0,19600,0,0,19604,0,0,19605,0,19606,19608,19610,0, +19613,19614,0,0,0,0,0,0,19616,19617,0,0,19618,0,0,19619,0,0,0,19620,19621,19631, +0,0,19632,19634,19636,0,19643,0,0,19644,19658,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,19659,0,0,0,0,0,0,0,0,0,0,0,19675,19677,0,0,0,0,19679,0,19683,0,19684,0,0, +0,0,0,0,19687,0,0,0,0,0,0,0,0,19688,19689,19692,0,0,0,0,0,0,0,19695,19697,0,0,0, +0,0,19698,19699,0,0,19700,0,19702,0,0,19703,0,0,0,0,0,0,19704,19708,0,19710,0, +19713,0,0,0,19715,0,0,0,0,19718,0,0,0,0,0,0,0,19720,0,19722,0,0,19725,0,0,0,0,0, +0,0,0,0,0,0,0,0,19730,0,0,0,0,0,19731,0,19734,19735,19739,0,0,19740,0,19741,0,0, +0,19746,0,0,19747,0,19771,0,0,0,0,0,0,0,0,19772,19775,0,0,0,0,0,0,19778,0,0,0,0, +0,19779,0,0,19780,19790,0,19791,0,0,19792,0,0,0,19793,0,0,19796,19797,0,0,0, +19799,0,0,0,19801,0,0,0,0,19803,0,19804,0,19805,0,0,19807,0,0,0,19808,0,0,0,0,0, +0,19809,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19816,0,19821,0,19822,19830,19831,0,0, +0,19833,0,0,0,0,0,0,0,0,0,0,19838,0,0,0,0,19839,0,0,19843,0,0,0,0,19845,0,0,0,0, +19847,0,0,19848,0,19849,0,0,0,0,0,0,0,19851,0,0,0,19854,0,0,0,0,0,0,0,0,0,19864, +0,19865,0,19866,0,0,0,0,0,0,0,19868,0,0,19870,0,0,19871,0,0,19872,19873,19875,0, +19880,19882,19884,0,0,19885,19886,19888,0,0,0,0,0,0,0,0,0,0,0,0,19890,19892, +19893,0,0,19894,0,0,0,19895,0,19896,19902,0,0,19903,0,0,19905,0,0,0,19906,0, +19908,0,19909,19911,0,0,0,19913,19920,0,19938,19939,19940,0,0,0,0,0,0,0,19942,0, +19943,0,19945,0,0,0,19951,19952,19954,19960,0,19965,0,19971,0,0,0,0,0,19975,0, +19976,0,19990,0,0,19991,0,19993,0,19995,0,0,0,19998,19999,20001,0,20003,20005,0, +20011,20012,0,0,0,0,0,0,20014,0,20020,0,0,0,0,20021,0,0,0,0,0,20023,20024,0,0,0, +0,0,20025,0,0,20027,0,0,20029,0,0,20032,0,0,0,0,20044,20045,0,20048,20049,0,0, +20050,0,20052,0,0,20054,20057,0,0,0,0,0,0,0,0,0,20059,0,0,20061,0,20062,0,20064, +0,0,20066,0,0,20067,0,0,0,0,20069,0,0,0,0,0,0,20070,20071,0,0,0,0,0,0,0,0,0,0,0, +20072,0,0,20073,20074,0,0,0,0,0,20075,0,20078,0,0,0,0,20080,0,20081,0,0,0,0,0,0, +20095,0,20098,0,0,0,0,0,0,0,20107,0,0,0,0,0,0,0,0,20112,0,0,0,20113,20114,0,0,0, +20115,20123,20124,0,0,0,20131,20133,20134,0,0,0,0,20136,0,0,20137,20138,20150,0, +20152,0,0,0,20153,0,0,20154,0,0,0,20158,0,20163,0,0,20164,0,0,0,0,0,0,0,20166,0, +20168,0,20170,0,20175,0,0,20178,0,0,0,0,20223,0,0,0,0,20224,0,20226,0,0,20230,0, +20231,0,0,0,0,20232,0,0,20233,20234,0,20244,0,20247,0,0,0,0,0,0,20249,0,0,0, +20250,0,0,0,0,20251,0,20253,0,20254,0,0,0,0,20256,0,0,20264,0,0,0,0,20266,0,0,0, +20278,0,0,20279,20282,0,0,0,0,0,20283,0,20284,0,20285,0,20287,20290,0,0,0,0, +20292,0,0,0,0,20293,20297,0,0,0,0,0,0,20299,0,20300,20303,0,0,0,0,0,0,20307,0,0, +20308,0,20309,0,20310,0,0,0,0,0,0,20312,0,0,0,20314,0,0,0,0,20315,20316,0,20322, +0,0,0,0,0,0,20339,0,0,0,20342,0,0,0,0,20352,0,0,0,0,0,0,0,0,0,0,20362,0,0,20365, +0,20375,20377,0,0,0,0,0,0,0,0,0,0,0,20378,20379,0,20380,0,0,20381,0,20382,0, +20383,0,20388,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20390,20392,20393,0,0,20395,0,0,0,0,0, +20396,0,0,0,0,0,0,0,0,20398,20415,0,0,0,20417,0,0,20420,0,0,20426,20428,0,20431, +0,0,20432,0,20433,20434,20435,0,0,0,0,20440,0,0,0,0,0,20442,0,20443,0,20446,0,0, +0,0,20448,0,20451,0,0,0,0,0,0,0,0,0,20452,20453,0,0,20454,0,0,0,0,0,0,20457,0, +20458,0,0,0,20465,0,0,0,0,0,20469,0,0,0,20473,0,20476,0,0,0,0,0,0,0,0,20477,0,0, +20485,0,0,20486,0,0,20487,0,20496,0,20497,0,0,20498,0,0,0,0,0,0,0,0,0,0,20499, +20500,0,20501,0,0,0,0,0,20520,20527,0,20529,0,0,0,0,20539,0,0,20540,0,0,0,20543, +0,0,0,20546,0,0,0,0,0,20548,0,0,20563,0,0,20564,0,20566,0,0,0,0,0,20589,0,0,0,0, +20590,0,0,20593,20594,0,0,0,0,20595,0,20597,20598,0,0,0,20618,20620,0,0,0,0, +20621,0,0,0,0,20627,0,0,0,0,0,20628,0,0,0,20629,0,20630,0,0,20639,0,0,0,0,0, +20707,0,0,20709,0,0,0,20713,20714,0,0,0,0,0,20724,20725,0,0,0,0,20726,20728, +20729,0,20733,0,20734,0,20735,20736,0,20737,0,0,20744,0,20745,0,20748,0,0,20749, +0,0,0,0,0,0,0,0,20750,0,0,0,0,20754,0,0,0,20761,0,0,20763,0,0,0,0,0,0,0,20766,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,20767,0,0,0,0,20768,0,20769,20777,0,0,0,0,0,0,20785,0, +0,0,20786,20795,20801,0,20802,0,20807,0,0,20808,0,0,20810,0,0,20811,0,20812,0,0, +0,0,0,20813,0,0,20818,20820,20821,0,0,0,20822,0,20823,0,0,0,20826,0,0,0,0,0,0,0, +20829,20830,20831,0,20832,20836,0,0,20839,0,0,20840,20842,0,20843,0,20844,0, +20854,0,0,0,20855,0,0,0,0,20856,0,0,0,20869,0,0,20871,0,0,0,0,0,0,0,20873,0,0,0, +0,0,20876,0,0,0,0,0,20880,0,0,20882,0,0,0,0,20883,20884,0,0,20890,0,0,0,0,0,0,0, +0,0,20891,0,0,0,0,0,20905,0,20906,20910,0,0,20912,20915,0,0,0,0,0,20916,0,20917, +0,20919,20920,20922,0,20927,0,20928,20929,20930,0,0,20935,0,0,20939,0,0,20941,0, +0,0,20943,0,0,0,20946,20947,0,0,0,0,0,20950,0,20954,0,0,20955,20964,0,0,20967,0, +0,0,0,0,20973,20975,0,0,0,20984,0,20987,20988,0,0,0,0,0,20989,0,0,0,20995,0, +20998,0,20999,0,0,0,0,21000,21001,0,0,0,0,21008,0,21010,0,21016,0,0,0,21017, +21018,0,0,0,0,0,21021,21026,21027,21028,0,0,21029,0,0,0,0,0,21030,0,0,0,0,0,0,0, +0,0,0,0,0,0,21031,21032,0,0,0,0,0,21037,0,0,21038,0,0,0,0,0,0,0,0,0,21039,0, +21041,0,21046,21047,0,0,0,21049,21053,0,0,21057,21064,21065,0,0,21066,21067,0,0, +0,21069,0,0,0,21071,21072,0,0,21073,0,21074,0,0,21078,0,0,0,0,21079,0,0,21080, +21081,0,0,21086,21087,0,21089,0,0,0,0,0,0,0,21091,0,21093,0,21094,0,0,0,0,0,0,0, +0,21095,0,0,0,0,0,21096,0,21098,0,0,0,0,0,0,0,21099,0,0,21100,21101,21102,0,0,0, +0,0,21103,0,21104,0,0,0,0,0,21105,21108,21109,0,0,21112,21113,0,0,0,0,0,0,21115, +21122,21123,0,0,0,0,0,21125,0,0,0,0,0,0,0,0,21129,21131,0,0,21134,0,0,0,21137, +21142,0,21143,0,0,21144,0,21145,21146,0,21152,21154,21155,21156,0,0,0,21160,0,0, +0,0,0,0,21161,0,21164,0,21166,0,0,0,0,21170,0,0,0,0,21171,0,0,21172,0,21174,0, +21175,0,0,0,0,0,21176,21179,21188,0,0,0,21189,0,0,21190,0,0,0,21192,0,0,21193,0, +0,0,21198,0,21212,0,0,21213,0,0,0,0,0,0,21215,21216,0,0,21223,21225,0,21226,0,0, +0,0,21227,21228,0,0,21229,0,0,0,0,21230,21236,0,0,0,0,0,0,0,0,0,0,0,0,0,21237,0, +0,21238,21239,0,0,0,0,21256,0,0,0,0,0,21257,0,0,0,0,0,0,0,21259,0,0,0,21263,0, +21272,0,21274,0,21282,0,0,0,0,0,0,0,0,21283,0,0,0,0,0,0,0,0,21294,0,0,21297,0,0, +0,0,21298,0,0,0,21299,0,21300,21302,0,21316,0,21318,21322,21323,0,21324,0,21326, +0,0,0,21327,21328,0,0,0,21352,0,0,21354,21361,0,0,0,0,0,0,0,0,0,0,0,0,0,21362,0, +0,0,21363,0,0,0,0,0,0,0,0,0,21366,0,0,21367,21372,21374,0,0,0,21375,21377,0, +21378,0,0,0,21380,0,0,0,0,0,0,0,0,0,0,21381,0,0,0,0,0,0,21382,0,21383,0,0,21384, +0,0,21385,0,0,0,0,21389,21390,0,0,0,0,0,0,0,0,0,0,0,0,0,21397,21398,0,0,0,0,0,0, +0,0,0,0,21399,0,21400,0,0,0,0,21402,0,0,0,21403,21404,0,21405,21406,0,0,0,21407, +0,0,0,0,0,0,0,0,0,0,0,0,21408,0,0,0,0,21409,0,21421,0,21422,0,0,0,21425,21428,0, +0,0,0,21429,0,0,0,0,0,21433,0,0,0,0,0,0,0,0,0,0,21434,0,21443,0,21444,21449,0, +21452,0,21453,21454,0,0,0,21457,0,0,21458,0,0,0,21460,21461,0,0,21464,0,0,0, +21473,21478,0,0,21479,0,0,21481,21483,0,0,0,0,0,0,0,0,21484,0,0,21485,21486,0,0, +21488,0,0,0,0,0,0,21523,0,0,21525,0,0,0,0,0,0,0,21526,0,0,0,0,0,0,21529,21530,0, +0,21531,0,0,21533,0,0,21539,21564,0,21567,0,0,0,0,0,0,0,0,21575,0,0,0,0,21577,0, +0,0,0,0,21591,0,0,21604,0,0,0,0,0,0,0,0,0,21605,0,21606,0,0,21617,21618,21619, +21620,0,0,0,0,0,0,0,0,0,0,0,0,0,21623,0,0,0,0,21631,0,21635,0,0,0,0,21639,21646, +21653,21662,0,0,21663,21664,0,21666,0,0,21667,0,21670,21672,21673,0,21674,21683, +0,0,0,0,0,21684,0,21694,0,0,0,0,21695,21700,0,21703,0,21704,0,0,21709,0,0,0, +21710,0,0,0,0,0,0,0,0,21711,0,0,0,21712,0,21717,0,21730,0,0,0,21731,21733,0,0,0, +0,21737,21741,21742,0,21747,0,0,0,21749,0,0,0,0,0,0,0,0,0,0,0,0,0,21750,0,0,0,0, +0,21752,0,0,0,0,21753,0,0,0,0,0,0,21755,21756,0,21757,0,0,0,0,0,0,21760,0,0, +21763,0,0,0,0,0,0,0,0,0,21764,0,0,21766,0,0,21767,0,0,0,0,0,0,0,0,0,21773,0, +21774,0,0,21775,0,0,0,0,21776,0,0,21777,0,0,0,0,0,0,0,0,0,21780,21787,21788, +21791,0,0,0,21797,0,0,0,0,0,21805,0,0,0,0,21806,0,21807,21809,0,21810,21811,0, +21817,21819,21820,0,21823,0,21824,0,0,21825,0,0,21826,21832,0,0,0,0,0,21833, +21848,21849,0,0,21867,21870,21871,21873,0,0,0,21874,0,0,0,0,0,0,0,0,0,21875,0, +21878,0,0,0,21879,0,21881,21886,0,0,0,0,21887,0,0,21888,21894,21895,21897,0, +21901,0,21904,0,0,21906,0,0,0,21909,21910,21911,0,0,21912,0,0,21913,21914,21915, +0,21919,0,0,0,0,0,0,0,21921,0,0,21922,21933,21939,0,0,0,0,0,0,0,0,0,0,0,21944,0, +0,0,0,0,21945,0,21947,0,0,0,0,0,0,0,0,0,0,21949,0,0,0,21950,0,0,0,0,0,0,0,0,0,0, +0,0,0,21951,0,21952,0,0,0,0,0,0,0,0,0,21954,21957,0,0,0,0,21958,0,21959,0,0,0,0, +0,0,21962,21963,0,0,0,0,0,0,0,0,21964,21965,0,0,21969,21970,0,0,0,21974,0,0, +21980,21981,0,21982,0,0,0,0,0,21985,0,21988,0,21992,0,21999,0,0,0,0,0,0,22001,0, +22002,0,0,0,0,0,0,22003,0,0,0,0,0,22004,0,0,0,22008,0,22009,22015,0,0,22016,0,0, +0,22017,22019,0,0,0,0,0,0,0,0,0,22020,0,0,0,0,0,0,0,0,0,0,22021,22037,0,22039,0, +0,0,22040,0,0,0,22048,22049,0,0,22053,22055,22056,22059,0,0,22060,22061,0,0, +22064,0,0,0,0,22066,0,0,0,0,0,0,0,22073,0,0,0,22074,22075,0,0,0,0,0,0,0,22076,0, +0,0,0,22077,22084,22099,0,0,0,0,0,0,0,22104,0,0,22107,0,22108,0,22109,0,22110,0, +0,0,0,0,0,0,22111,22119,0,22120,22122,0,0,0,0,22125,0,0,0,22128,22129,0,0,0,0,0, +0,22141,0,0,0,22142,0,0,22144,22146,0,22148,22149,22151,22154,0,0,0,22162,0,0,0, +0,22164,22177,0,0,0,0,22179,0,22182,22183,0,0,22184,22188,0,0,0,0,0,0,0,0,22190, +0,22194,22201,0,0,22208,0,22209,0,22212,0,0,22215,0,22223,22231,0,0,22232,0, +22234,0,0,22235,22236,0,22237,0,22240,0,0,0,0,0,22241,0,0,0,22242,22246,22247,0, +0,0,22259,22268,0,22269,0,0,0,0,0,0,0,22270,0,0,0,0,22271,0,22272,0,22277,0,0,0, +0,0,22278,22280,22283,22286,0,0,22287,22289,0,0,22290,0,22293,0,0,0,0,0,0,0,0,0, +0,22295,0,22301,22302,0,0,0,22305,0,22308,0,0,0,0,0,0,0,0,0,0,22315,0,0,0,22317, +0,22334,0,0,0,22335,0,0,0,0,0,22336,0,22338,22344,0,22347,22349,0,22350,0,0,0,0, +0,0,0,22357,0,0,0,0,0,22358,0,0,0,0,0,0,0,0,0,0,22359,22360,0,0,0,0,0,0,0,0, +22361,22366,0,0,22369,0,22370,22373,0,0,0,0,0,22375,0,22377,0,0,0,0,0,22378,0,0, +0,0,22381,0,0,0,0,22382,0,22383,0,0,0,0,0,0,0,0,0,22391,0,0,22392,22395,22396, +22402,0,0,0,0,0,0,0,0,0,0,0,0,0,22405,0,0,22406,0,0,22408,0,0,22409,22410,0,0,0, +0,0,0,22424,0,0,0,0,22426,0,0,0,22427,0,22428,0,22432,0,22435,22442,22443,0,0,0, +0,22444,0,0,0,0,0,22446,0,22454,0,22455,0,0,0,22465,0,22470,0,22471,0,0,0,0, +22472,22473,0,22487,0,0,0,22488,0,0,0,0,22489,0,0,22499,0,0,0,0,0,0,22514,0,0, +22515,0,0,0,0,0,0,0,22516,0,0,0,22517,22520,0,0,0,22534,0,0,22535,0,0,22536,0, +22540,22553,0,22555,0,0,0,0,22561,0,0,22562,0,0,0,0,0,0,0,0,0,0,0,22566,0,0,0,0, +22567,22568,0,0,22575,0,22579,0,22582,22583,22585,0,0,0,0,0,22586,0,0,22587,0,0, +22590,0,0,0,0,0,22591,0,22592,0,0,0,0,0,22593,0,22602,0,0,22604,0,0,22609,0,0, +22618,0,0,0,0,0,0,22619,0,22624,22625,0,0,22638,0,0,0,0,0,22639,0,0,22640,0,0,0, +0,0,0,0,22644,0,22645,22647,0,0,0,0,22652,22653,0,0,0,22654,0,22655,0,0,0,22656, +0,0,0,0,0,0,0,0,0,0,22673,22675,22676,0,0,22678,22679,0,22691,0,0,0,0,0,0,0, +22693,0,0,22696,0,22699,22707,22708,0,0,0,0,0,0,0,0,22718,0,22719,0,0,0,0,22723, +0,0,0,22724,22725,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22726,22728,0,0,0,0,0,0,0,0,22729, +0,0,22731,0,0,0,0,22732,22735,22736,0,0,0,0,22739,0,22749,0,0,22751,0,0,0,0,0,0, +0,0,0,0,0,22758,0,0,0,0,0,22760,0,0,0,0,0,22764,22765,22766,0,22768,0,0,0,0,0, +22769,22770,0,0,0,0,0,0,22771,0,0,22772,22775,0,22776,22777,22780,0,0,22782, +22784,0,22787,0,22789,22796,0,0,0,0,0,22798,0,0,0,0,0,0,22802,0,22803,22804,0,0, +0,0,0,0,0,0,0,0,22805,0,0,22810,22811,22814,22816,0,22825,22826,0,22831,22833,0, +0,0,0,0,0,0,0,0,22834,0,22836,22838,0,22839,0,0,0,0,0,22840,0,22847,0,0,0,0,0, +22856,22857,0,22858,22859,0,0,22862,0,0,22864,0,0,0,0,22865,0,0,0,0,0,0,0,0,0,0, +0,22866,0,22867,22868,0,0,0,0,22869,0,22871,0,22872,0,22873,22881,22882,22884, +22885,0,0,0,0,0,0,0,22886,22887,0,22894,0,22895,0,0,0,22900,0,22901,0,0,0,0, +22904,0,0,0,0,22905,22907,0,0,0,22915,22917,0,0,22918,0,0,0,22920,0,0,0,22929, +22930,0,0,0,22941,22942,0,0,0,22943,0,0,0,22944,0,0,0,0,0,0,0,22946,0,22947,0,0, +22954,0,22956,0,0,22962,0,0,0,0,0,0,0,22963,0,0,22964,0,0,0,0,0,0,0,22965,0, +22968,0,0,0,22969,0,0,0,0,0,22970,0,22971,0,0,0,0,0,22978,0,0,22979,0,22987,0,0, +22989,0,0,0,0,0,0,22990,0,23005,0,0,0,0,0,0,0,23006,23007,23008,0,0,23023,23024, +23029,0,0,0,0,23030,0,0,0,0,0,23032,0,0,0,0,0,23035,0,0,0,0,23038,0,0,0,23048,0, +23049,23052,23053,23060,23061,0,23063,0,0,0,0,23067,23068,0,0,0,23069,23073,0,0, +0,23127,0,23128,0,0,0,0,0,23129,0,23138,23141,0,23149,0,0,23150,0,0,0,23152,0,0, +0,0,0,0,0,0,23154,0,0,0,0,23157,23159,23160,0,0,0,0,0,0,0,0,0,0,0,0,23180,0,0,0, +0,23181,0,0,23188,0,23189,0,0,0,0,0,0,0,0,0,0,0,0,23195,0,0,23196,23199,0,0,0,0, +0,0,0,0,0,23202,0,23204,0,23207,0,23209,23210,0,0,0,0,0,0,23227,23229,0,0,23230, +23234,23238,0,0,0,23245,23246,23248,0,0,0,0,23249,23254,0,0,0,23265,0,0,0,0,0,0, +0,23268,0,23276,0,0,0,0,23277,0,23297,0,23298,0,0,0,0,23299,0,23302,0,0,23303, +23312,0,0,23314,0,23320,0,0,0,0,23324,0,23325,0,23328,0,23334,0,0,0,23337,0,0,0, +0,23343,23344,23346,0,23348,0,0,0,0,0,0,0,0,23353,0,0,0,0,23355,0,23356,23358,0, +0,0,23359,23360,0,23361,0,23367,0,23369,0,0,23373,0,23378,23379,0,23382,23383,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,23387,0,0,0,0,0,0,23388,23390,0,0,23393,23398,0,0,0, +23399,0,0,0,23400,0,0,0,0,23401,0,0,0,23415,0,0,0,0,0,0,0,0,23416,0,23422,0, +23443,23444,0,0,0,0,23448,0,23454,0,0,0,0,0,0,23456,0,0,23458,23464,0,0,0,0,0,0, +23465,0,0,0,23470,23471,0,0,23472,0,0,0,23473,23496,0,0,0,0,0,0,0,0,23497,0, +23499,0,0,23502,0,0,23503,0,0,23513,0,0,23515,0,0,0,23517,0,0,0,0,23518,23519, +23521,23524,0,23525,23528,23539,0,0,0,0,0,23541,0,0,23544,0,0,23556,0,0,23557,0, +0,0,0,0,0,0,0,0,0,0,0,0,23559,0,23560,0,0,23561,0,0,23566,0,0,0,0,0,23568,23569, +23570,0,0,0,0,23571,0,23574,0,0,0,0,0,0,0,0,0,0,0,23575,0,23579,0,0,23581,0,0,0, +0,0,0,23587,0,0,0,0,0,0,0,23596,23598,0,0,0,0,23602,23606,0,0,23607,0,23608,0,0, +0,23614,23616,0,0,0,0,0,23618,0,0,23619,0,0,0,0,23621,23626,0,23627,0,0,0,0,0,0, +0,23629,0,23630,0,0,0,0,23634,0,23636,0,0,0,0,0,0,23638,0,0,0,0,23640,23667,0, +23669,0,0,0,23681,0,0,0,0,0,0,0,23682,0,23683,0,0,0,0,0,23684,0,0,0,23685,23689, +0,23693,23694,23700,0,23702,0,23709,0,0,0,0,0,0,0,23712,0,0,0,0,0,23714,0,0, +23715,0,0,0,0,23718,0,0,23720,0,0,0,0,23722,0,0,0,23726,23729,0,23741,23746,0, +23748,0,0,0,0,23749,0,0,0,0,0,23750,0,0,0,0,23751,0,23753,0,0,0,0,23757,23765,0, +0,0,23770,0,0,0,0,0,0,0,23771,0,23772,23781,0,0,23796,0,0,0,0,23798,0,23799,0,0, +0,23802,0,0,23806,0,23807,0,0,23808,0,23809,0,23819,0,0,0,23821,0,23827,0,0,0, +23829,0,0,0,0,0,0,0,23830,0,0,0,0,0,0,23832,23833,23834,23835,0,0,0,0,23837, +23838,0,0,0,0,0,23846,0,0,0,0,0,0,23847,0,0,0,0,0,23879,23881,0,0,23882,23883, +23895,0,23899,0,0,0,0,23901,0,0,0,0,0,0,23902,0,0,0,0,0,23903,23905,0,23906,0, +23907,23918,23919,23920,0,23922,0,23924,0,23927,0,23934,0,23937,23941,0,23942, +23946,0,0,0,0,0,23955,23956,23958,0,0,0,0,0,0,23959,0,23962,23965,0,23966,0,0,0, +0,23967,23968,0,0,23973,0,0,23974,0,0,0,0,23975,0,23976,0,0,0,0,0,0,0,0,0,0,0,0, +0,23977,0,0,0,0,0,0,0,0,23980,0,0,23984,0,23985,0,0,23987,0,0,23988,23990,23991, +0,0,0,0,0,0,23992,0,0,0,0,0,0,0,0,23994,0,0,0,23998,0,0,0,0,0,0,0,0,0,23999,0,0, +24003,0,24004,0,24006,0,0,0,24007,0,0,24008,0,0,0,0,0,0,0,24009,0,0,24010,0,0, +24011,0,0,24013,24014,0,0,24015,24016,24027,0,24028,24029,0,24030,0,0,0,0,0, +24033,24034,0,24035,0,0,24036,0,0,24044,0,24048,24049,24063,24067,0,24068,24070, +0,0,24071,24078,24087,0,24090,0,0,0,24095,0,24098,24101,24104,24106,0,24107,0,0, +0,24108,0,0,0,0,24110,24111,0,24113,0,0,24115,24120,0,0,0,0,0,0,24124,0,24125,0, +24126,0,24127,0,0,0,0,0,24135,0,0,24136,0,24137,24142,0,0,0,24146,0,0,24147, +24149,24154,0,24163,0,0,0,24165,24166,24167,0,0,0,0,0,0,0,0,0,0,24169,24170, +24175,0,0,0,24178,0,0,24179,0,0,24181,0,24184,24197,0,24201,24204,0,0,0,0,0,0, +24206,24212,24220,0,0,0,24224,0,0,0,0,0,0,0,0,24226,0,24234,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,24235,0,24236,0,0,0,0,0,24239,24240,24241,0,0,24248,0,0,24249,0, +24251,0,0,0,0,0,0,24253,0,24268,0,0,0,24269,0,24271,24272,0,0,0,0,24273,0,0, +24274,0,0,24279,0,0,0,0,0,0,0,24280,0,24293,24294,0,0,0,0,0,0,24296,0,0,24323,0, +0,0,24329,24330,24331,24339,0,24351,0,0,24369,24370,0,0,0,24371,0,0,0,0,24372, +24373,24374,0,0,0,0,0,24378,0,0,0,0,24379,0,24381,0,24383,24389,0,24390,0,0, +24394,24395,24400,0,0,0,24401,24402,0,24406,0,0,0,24411,0,0,0,24415,0,24416,0,0, +0,0,0,24417,0,24419,0,24422,0,24423,24428,0,24435,0,0,0,24439,0,0,0,24440,24442, +24446,0,0,0,24447,24448,24449,24452,0,0,0,0,24453,24457,0,0,24458,24459,24460,0, +24465,0,0,0,0,0,0,0,24470,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24471,0,24473, +24474,24475,24476,0,24478,0,0,0,0,24480,0,0,0,0,0,0,0,0,0,0,24481,0,0,0,0,0,0,0, +0,0,0,24482,24485,0,0,0,0,24486,0,0,0,24488,0,0,0,24494,0,0,0,0,24497,0,0,24498, +0,0,0,24499,24506,0,0,0,24507,0,0,24511,0,0,24513,24514,0,0,0,0,0,24517,0,24518, +0,24520,0,24521,24524,24525,0,0,0,0,0,24527,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24528,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24537,24539,0,24540,0,0,0,24548,0,0,0,0,0,24549, +24550,0,0,0,24553,24554,0,24555,0,24556,0,24558,0,0,0,0,0,24560,0,0,0,24561,0,0, +0,0,0,24562,0,0,0,0,0,0,0,0,0,0,0,0,0,24567,0,0,0,0,0,24569,0,0,0,24574,0,24575, +0,0,0,0,0,0,0,0,0,0,0,24577,24581,0,24584,0,0,0,0,0,24585,0,0,0,0,0,24586,0,0, +24587,0,24588,0,0,0,0,0,0,0,0,0,0,24590,24591,0,0,0,0,24592,0,0,0,0,0,0,0,24594, +0,0,0,0,0,0,0,24596,24597,0,0,0,0,24602,24603,0,0,0,0,24604,0,0,24605,0,24610,0, +0,24611,0,0,0,0,24612,24615,24616,24624,0,0,0,24627,0,24638,24639,0,0,0,0,24640, +0,0,0,24655,24656,24657,0,0,0,0,0,0,0,0,24662,0,24663,24664,0,0,0,0,0,24665,0,0, +0,0,24667,0,0,0,0,0,0,24668,24669,0,24670,24674,0,0,0,24675,0,24678,0,0,24679,0, +0,0,24681,0,24683,0,0,0,0,24684,0,24685,0,0,24686,0,0,24688,24689,0,0,0,0,24690, +24691,0,0,0,0,0,0,0,24697,0,24698,0,0,0,0,0,0,0,0,24709,0,0,0,0,0,24710,0,24712, +0,0,0,0,0,0,24713,24714,0,24715,0,24716,24718,0,24719,0,0,0,0,24720,0,0,24725,0, +0,24738,0,24749,24750,0,0,0,24752,0,0,0,24753,0,0,0,24758,0,0,0,0,0,24762,0, +24763,0,0,0,0,0,0,0,24764,0,0,0,0,0,24765,24767,24768,0,24772,0,0,0,0,24773,0,0, +0,0,24777,0,0,0,0,0,24785,0,24786,24788,0,0,0,24789,0,0,0,0,24794,24798,0,24799, +24800,0,0,0,24803,0,24804,24806,0,24807,0,0,0,24810,0,0,0,0,0,0,24827,24828,0, +24835,0,0,0,0,0,0,24836,0,0,0,0,0,24839,0,24843,24844,0,0,0,0,0,0,0,0,0,0,24847, +0,0,24848,0,0,0,0,0,0,24849,0,24850,24851,0,0,0,24852,0,24853,0,0,0,0,0,0,0,0,0, +24854,0,24855,0,0,24868,0,0,0,24883,0,0,0,24884,0,24895,24897,0,0,0,0,0,24899,0, +0,0,0,0,24900,0,24913,0,0,0,0,0,0,24914,0,0,24917,24930,24931,0,0,0,24932,0,0, +24939,0,0,24942,0,0,0,0,0,0,0,0,0,24945,24950,0,24951,0,0,24953,0,0,0,24954,0, +24959,0,0,0,24961,0,0,24962,0,24964,24968,24970,24972,0,0,0,0,0,24976,0,0,0, +24977,0,24982,0,0,24983,0,0,24984,0,0,0,24993,0,0,0,24994,0,0,25001,0,0,0,25003, +0,0,25018,0,0,25023,0,0,0,25034,0,0,25035,25036,0,25037,0,0,0,0,0,0,0,25039,0,0, +0,0,0,25040,0,0,0,0,0,0,0,25042,0,0,25043,25045,0,0,0,0,0,0,25049,0,0,25051,0, +25052,25053,0,0,25054,0,0,0,25055,0,0,0,0,25057,25059,0,0,25060,25064,0,25065, +25069,25070,0,0,0,0,25072,0,25073,0,25090,0,0,25092,25093,25101,0,0,0,0,0,0, +25105,25108,0,0,25113,0,0,25115,25116,0,0,0,0,0,0,25117,0,0,0,25120,25121,0,0,0, +0,0,0,0,25125,0,0,0,25126,0,25130,25134,0,25139,0,25143,0,0,0,25151,0,25161,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25163,0,0,0,0,0,0,0,25174,0,25175,0,25207,0,0, +0,25209,0,0,0,0,25213,0,25219,0,25223,0,25225,0,0,0,25227,0,0,0,25228,0,0,0, +25229,0,0,0,0,0,0,0,25231,25233,0,0,0,0,25237,25239,0,0,0,25243,0,0,0,25252,0, +25257,25258,0,0,0,0,25260,25265,0,25268,0,0,25273,25324,0,25325,0,25326,0,0,0,0, +0,0,0,0,25327,0,0,0,0,0,25328,0,0,0,0,0,0,25332,0,0,0,25333,0,0,0,25336,25337, +25338,0,0,25343,0,25350,0,0,0,0,0,0,0,25352,0,25354,0,25375,0,25379,0,0,0,0, +25384,0,0,0,0,0,0,0,0,0,25386,0,25388,0,25390,0,0,25399,0,0,25401,0,0,0,25402,0, +0,0,25407,0,0,0,0,0,0,0,0,0,0,0,25413,25415,0,0,25417,0,0,0,0,0,0,0,25419,0,0,0, +25421,0,0,0,25424,0,0,0,0,25433,0,0,0,0,0,0,0,0,0,25435,0,0,0,0,0,0,25436,0,0,0, +25437,0,0,25440,0,0,0,0,0,0,25442,0,0,25443,0,25446,0,0,25449,0,0,0,25450,0,0,0, +0,25452,0,25453,25454,25455,0,0,0,25456,0,25457,0,0,0,25459,0,25461,0,25468,0,0, +0,0,0,0,0,0,25469,0,0,0,0,0,25471,0,0,0,0,0,25474,0,0,0,0,0,0,0,0,25475,0,0,0,0, +25477,0,0,0,0,25483,0,0,0,0,0,25484,0,0,0,0,0,0,0,0,0,0,0,0,25485,0,25497,0,0, +25498,0,25504,0,25510,0,25512,0,0,25513,25514,0,0,0,0,0,0,25517,25518,25519,0, +25520,0,0,0,0,0,0,0,25521,0,25522,25527,25534,0,25536,0,25537,0,0,25548,25550,0, +0,25551,0,25552,0,0,0,0,0,25554,0,25555,0,25556,25557,25568,0,0,0,25570,25571,0, +0,0,0,0,0,25574,0,0,0,0,25579,0,0,0,25581,0,0,0,25582,0,0,0,0,0,0,0,0,0,25588,0, +0,0,0,25589,0,0,0,0,25590,0,25591,25592,25593,0,25594,0,0,0,25596,0,25597,25615, +0,0,0,0,0,25618,0,0,0,0,25619,25623,0,0,25629,0,0,25631,0,0,0,25635,25636,0,0, +25649,0,0,0,0,25654,0,0,0,25661,25663,0,0,25671,0,0,25678,25698,0,25699,25702, +25703,0,0,0,0,0,0,0,0,25704,0,0,0,0,0,25706,0,0,25710,0,25711,0,25712,0,25715, +25716,25717,0,0,25718,25728,25732,0,0,0,25734,0,0,0,0,0,0,0,0,0,25737,0,0,25739, +0,0,0,25740,0,25741,25745,0,25746,0,25748,25772,25778,0,0,0,0,0,25780,0,0,0,0, +25781,0,25782,25784,25785,0,0,0,25789,0,0,0,0,0,0,25797,25801,0,0,0,25808,25809, +0,0,25811,25814,25815,0,0,25817,0,0,0,0,0,0,0,0,25820,0,0,0,0,25832,25833,0,0,0, +25846,0,0,0,25847,25848,0,0,0,0,0,0,0,0,0,25849,25850,0,0,25851,0,0,25852,0, +25862,0,0,0,25863,25865,0,0,0,0,0,0,0,25867,25868,0,25869,25874,0,25875,0,25876, +25877,0,0,0,0,25878,25902,0,0,0,0,0,0,0,25903,25904,25905,0,0,0,25908,25909,0,0, +0,0,25910,0,0,0,0,0,0,0,25912,0,25913,0,0,0,0,0,0,0,0,25914,0,0,25916,0,0,0,0,0, +25917,25927,0,0,0,0,25928,0,0,25930,0,0,0,25933,0,0,25938,25942,0,0,0,0,0,0,0, +25945,0,25950,0,25956,0,0,25961,25962,0,0,25963,0,25964,25965,25966,0,0,0,0,0, +25967,0,0,0,0,25968,0,0,0,25969,25971,0,0,0,0,0,25973,25975,0,0,0,0,0,0,0,25978, +0,25981,0,0,0,25982,0,0,0,25984,0,0,0,0,0,0,0,25993,0,0,0,0,0,0,0,0,0,0,0,0,0, +26002,0,0,0,26005,0,0,0,26006,26007,0,0,26014,26015,26016,0,0,0,0,0,0,26017, +26018,26020,0,26022,26023,0,0,0,26024,26028,0,26029,26033,26034,26044,0,0,0,0,0, +26046,0,0,26047,0,0,26049,0,26050,0,26051,0,0,0,0,0,26053,0,0,0,0,26054,26059,0, +0,0,0,0,0,26060,0,26066,0,0,0,0,0,0,0,0,0,0,0,0,26067,0,26069,0,0,26071,0,0,0, +26073,0,26074,26077,0,0,0,0,26078,0,0,0,26079,0,26090,0,0,26094,0,0,0,0,0,0,0,0, +26095,0,0,0,0,0,0,0,0,0,0,0,26096,26101,0,26107,26122,0,26124,0,0,26125,0,0,0,0, +0,0,26136,26141,26155,0,0,0,0,0,0,0,0,0,26164,26166,0,0,0,26167,0,26170,26171,0, +0,26172,0,0,26174,0,0,0,0,0,0,0,0,0,0,0,0,0,26175,0,0,0,26176,26177,0,26321, +26322,0,26323,0,0,26324,0,0,0,0,0,0,0,26325,0,26331,0,0,0,0,0,0,26335,0,0,0, +26350,0,0,0,26379,0,0,26382,26383,26385,0,0,26392,26406,0,0,0,0,26411,0,0,0,0,0, +26412,0,0,26420,0,0,26423,0,26424,26426,26432,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +26435,0,26436,0,0,0,0,0,26441,0,26444,0,0,0,26446,0,0,0,0,26447,0,0,0,0,26449,0, +26450,26452,0,26453,26454,0,0,0,26455,0,0,0,26456,0,0,26458,0,0,26460,0,26463,0, +0,0,0,0,0,0,0,26464,26470,0,0,0,0,0,0,0,0,0,26473,0,0,26474,0,0,0,0,0,0,0,26475, +0,0,0,0,0,0,0,26477,0,26485,0,0,26486,0,26487,0,0,26488,26493,26494,0,0,26495,0, +26497,26504,26506,0,0,0,0,0,26507,0,0,0,0,0,26509,0,0,26510,0,0,0,0,0,0,0,0,0,0, +0,0,0,26512,0,26513,26515,0,0,0,26518,0,0,0,26519,0,26524,26526,0,0,0,26527,0, +26532,0,26533,26537,26558,0,0,0,26559,0,0,0,26571,0,0,26573,0,26588,0,26593,0,0, +0,0,0,0,26603,0,26604,0,0,0,0,0,0,0,0,0,0,26606,0,0,0,0,0,0,0,26607,26609,26611, +26614,0,0,0,26616,26620,0,26621,0,0,0,0,0,26627,0,26629,0,0,26630,0,0,26632, +26643,0,0,0,26644,0,0,0,0,0,0,0,0,0,26646,26647,0,0,0,26650,0,0,26656,0,0,0,0, +26663,26670,26671,0,0,0,26685,26686,26687,0,26689,0,0,0,0,26744,0,26745,0,26747, +26748,0,26749,26750,26751,0,0,0,0,26752,26755,0,0,0,26756,26769,0,0,0,26774,0,0, +0,0,0,26775,0,26777,26778,0,26786,0,0,0,26787,0,0,0,0,0,0,0,0,0,0,0,0,0,26788,0, +0,26789,0,0,0,0,0,26791,0,26792,26793,0,0,0,26794,0,26797,26798,0,0,0,26800,0,0, +26803,0,26804,0,0,0,0,0,0,0,0,0,26805,0,0,26808,0,0,26809,0,0,0,0,0,0,0,26812,0, +26825,0,0,0,0,0,0,0,26826,0,0,26827,26829,26834,0,0,0,0,26835,0,0,26849,0,26851, +0,0,0,0,0,0,0,0,0,26852,0,26853,26857,0,26858,0,26859,0,0,0,0,0,0,0,26876,0, +26878,26882,26883,0,0,0,0,26890,26894,0,0,0,0,26895,26896,0,0,0,0,0,26900,0,0,0, +0,0,0,0,26911,26913,26914,26915,26916,26919,0,0,0,26921,26922,0,0,26925,0,0,0, +26928,0,0,26929,26930,0,0,0,26931,0,26932,0,0,0,0,0,26933,0,0,0,0,0,0,26937,0,0, +26943,0,0,26944,0,0,0,26946,0,0,0,0,0,0,0,26956,0,26958,0,0,26963,0,0,0,0,0,0,0, +26965,0,26969,26970,26972,0,0,0,0,0,26973,0,26974,0,26978,0,26980,0,0,0,0,0,0, +26982,0,26986,26987,0,26990,0,0,0,0,27003,27006,0,0,27007,27010,27012,27013,0,0, +0,0,0,0,0,0,27014,27015,27018,0,27019,0,0,0,0,0,27025,0,0,0,27026,0,0,0,0,27029, +27030,27031,27034,0,0,27036,27037,0,0,0,27038,27042,0,0,0,27044,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,27045,0,0,0,0,0,0,0,27046,0,0,0,0,0,0,0,27047,27049,0,27050,0,0,0, +27051,27052,0,27055,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27056,27058,27059,0, +27061,0,27064,0,0,0,0,0,27069,0,0,27070,0,0,0,0,0,0,0,27072,0,0,0,0,0,0,0,0, +27076,0,0,0,0,0,27078,0,27079,0,0,0,27081,0,0,0,0,0,0,27082,0,27083,27086,0,0,0, +0,27087,0,0,0,0,0,27088,27090,0,27094,0,0,27095,0,27099,27102,0,0,0,27103,0,0,0, +0,27105,0,0,0,27106,0,0,0,0,0,0,27107,0,0,0,0,27108,27117,0,0,0,0,27118,0,0, +27124,0,27126,0,0,27130,27131,0,0,0,0,0,0,27147,0,0,0,0,27148,27149,0,0,0,0, +27150,27151,0,27152,0,27159,0,0,0,27164,0,0,0,0,0,0,0,27175,0,27189,0,0,27191,0, +27193,0,27195,0,27198,0,0,0,0,0,27200,0,0,0,0,27202,0,0,0,0,27203,0,0,27204,0,0, +27206,0,27207,0,0,0,0,27209,0,0,0,27213,0,0,27216,27219,27220,27222,27223,0, +27224,0,27225,27226,0,0,27233,0,0,0,0,27235,0,27237,0,27238,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,27239,0,27242,27243,0,27250,0,0,0,27251,0,27253,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,27254,27255,27258,0,0,0,27259,0,0,0,0,0,0,27267,0,27276,27278, +0,0,0,0,0,0,0,0,0,27296,27297,27301,0,0,0,0,0,0,27302,0,0,0,0,0,0,27312,27313,0, +0,0,0,0,27318,0,27320,0,27329,0,27330,27331,0,27332,0,0,0,0,27340,0,0,0,27348,0, +0,0,0,0,0,27350,0,27351,0,0,0,0,27355,0,0,27358,27359,27361,0,0,0,27365,0,27367, +0,27376,27378,0,0,27379,0,0,0,0,0,0,27396,0,27397,27404,0,0,0,0,0,27408,0,0,0,0, +27453,0,0,0,27456,0,0,0,27458,0,0,0,0,0,0,0,27459,0,0,0,27460,0,0,27461,0,27465, +27467,0,0,27469,0,27470,0,27471,0,27477,27482,0,0,0,0,0,0,27484,0,0,0,0,0,0, +27485,0,0,0,0,0,27493,0,27494,27502,0,0,0,0,0,0,0,0,0,0,0,0,27511,27532,0,0,0, +27533,27545,0,0,0,27546,0,0,0,0,0,0,0,0,0,0,27547,0,0,27549,27550,0,27551,0,0,0, +0,0,0,0,27555,0,0,27571,0,27573,27574,27575,27577,0,27578,0,0,27579,27585,0,0,0, +0,0,27586,0,0,27588,27589,0,0,0,0,27596,0,0,27600,0,0,0,0,0,0,0,0,0,0,0,27608,0, +0,0,0,0,0,0,0,0,0,0,27610,0,0,0,27618,0,0,27620,0,0,0,27631,0,0,27632,27634,0, +27636,27638,0,0,0,27643,0,27644,27649,0,0,0,0,0,0,0,0,0,0,0,0,0,27651,27660,0, +27661,0,0,0,0,0,0,0,27662,0,0,27664,0,27665,0,0,0,27669,0,27671,0,0,0,27673, +27674,0,0,0,27682,0,0,0,27711,0,27712,27713,27719,27720,0,0,27728,0,27729,0,0,0, +0,0,0,0,0,0,27731,0,0,27732,0,27733,0,27738,0,0,0,27742,0,0,0,27743,27744,0,0,0, +0,0,0,27745,27746,0,0,0,27747,27748,27751,27752,0,0,0,27768,27770,0,0,0,27774, +27775,0,27776,27777,0,0,27781,0,27784,0,27786,0,0,27791,0,27792,27793,27804,0, +27812,27813,0,0,0,0,0,0,0,0,27814,0,27825,0,27827,0,0,0,0,27828,27861,27862,0,0, +0,27864,0,0,0,27865,27884,0,27889,0,0,0,0,0,27890,0,27891,0,0,0,27892,0,0,0,0,0, +27897,27898,0,0,27899,0,0,0,27901,27905,0,0,27920,0,0,27921,0,27922,0,0,0,27931, +27934,0,0,0,0,0,0,0,0,0,0,27941,0,27942,0,27945,0,27947,27954,0,0,0,0,27960, +27963,0,0,0,0,0,0,0,0,27964,27965,0,0,0,27967,0,27969,27975,0,27976,27977,0, +27981,0,27983,28051,28052,0,0,0,0,0,28056,0,0,0,0,0,0,28058,28059,0,0,28061,0,0, +0,0,0,0,0,28063,0,0,0,0,0,0,28066,0,0,0,0,0,0,28069,28070,28072,0,28073,0,0, +28074,0,0,0,0,28075,0,0,0,0,0,0,0,28078,0,0,0,0,28085,0,0,0,0,28086,0,0,0,0,0,0, +28088,0,0,0,0,0,0,0,0,28090,0,28097,28114,28115,0,0,0,0,0,0,0,28116,0,0,0,0,0, +28118,0,28129,0,28131,0,0,28135,0,0,0,28140,28141,0,0,0,28146,0,0,0,0,28152,0,0, +0,0,28155,28157,28161,0,0,0,0,28166,0,28167,0,0,0,0,0,0,0,0,0,0,0,28172,0,0,0,0, +0,0,28173,0,0,28175,0,0,0,0,0,0,0,0,0,28178,28188,0,28190,0,0,0,0,0,28191,0, +28193,28206,0,0,28207,28209,0,28211,0,28213,0,0,0,28215,28216,28217,0,28222,0, +28223,28225,0,0,0,28226,0,28227,28229,28232,0,0,0,0,0,0,0,0,0,28235,0,28241,0,0, +28242,0,0,0,0,28243,0,0,0,28245,0,0,0,28248,28250,0,28251,28252,0,0,0,0,0,0, +28253,0,0,28254,28255,0,0,28256,0,0,28258,0,0,0,0,0,28259,0,0,28260,0,0,28261,0, +0,0,0,28262,28263,0,0,28264,0,0,0,28266,0,28268,28269,0,28270,28272,28274,0, +28277,28278,0,0,0,28279,0,28280,28281,28283,0,28292,0,28294,0,28297,0,0,0,0, +28299,0,0,0,0,0,28300,0,0,0,0,0,0,0,28301,0,0,0,0,0,0,0,0,0,0,0,0,0,28302,28303, +0,0,0,0,28304,0,0,28305,0,28312,0,28313,28314,0,0,0,0,0,0,28315,0,0,0,28320, +28321,0,0,28328,0,0,0,28329,28338,0,28339,0,0,28344,0,0,0,0,0,0,0,0,28347,0,0,0, +0,0,0,0,0,28348,0,0,0,0,0,28411,0,28412,28413,0,28416,0,0,0,28420,0,0,0,0,0, +28421,0,0,0,0,28423,0,0,0,28424,0,0,28428,0,0,0,0,0,28429,0,0,0,28431,28434,0, +28458,0,0,0,0,0,0,0,0,0,0,0,28464,0,0,0,0,28465,0,28467,0,0,0,0,0,0,28471,0,0,0, +0,28474,0,28480,0,28481,0,0,28485,0,0,0,0,28486,28488,0,0,28489,0,0,0,0,28492,0, +0,0,28495,0,28497,0,28499,0,0,0,0,28500,0,0,28502,28503,0,0,0,28508,0,0,0,28510, +0,0,28512,28513,28514,28521,0,28526,0,28527,28528,0,0,0,0,28529,0,0,28532,0,0, +28537,28538,0,0,0,28539,0,28548,0,28553,28554,0,0,0,0,0,0,0,0,0,0,0,0,28560, +28563,0,0,28564,0,0,0,0,28565,0,0,0,0,0,0,0,28566,28568,0,0,0,0,0,0,28569,0,0,0, +28570,0,28572,28573,0,0,0,0,28575,0,0,0,0,28576,28581,28588,0,0,28589,0,0,0, +28590,28595,0,28598,0,0,28601,0,0,28605,0,0,0,0,28614,28615,28619,0,0,0,0,0,0, +28620,0,28626,0,0,28628,0,28631,0,28632,0,0,0,0,0,0,28635,0,0,0,28637,28638,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28639,0,28643,0,0,28652,0,0,0,28662,0, +28670,28671,0,0,0,0,0,0,0,0,0,28672,28673,28675,28676,0,0,0,0,0,0,0,28691,0,0,0, +28695,0,0,0,28696,0,28697,28698,0,28705,0,28707,28708,28710,0,0,0,0,0,0,0,28711, +28728,0,0,0,28736,0,0,0,28737,0,0,0,0,0,0,0,0,0,28738,0,28739,0,28741,0,0,28742, +0,0,0,0,0,0,0,0,0,0,0,28745,0,0,0,0,0,0,28749,28750,28752,28754,28756,0,28757,0, +0,0,0,28759,28760,0,0,0,0,0,0,28762,0,0,0,28764,0,0,0,0,0,0,28766,0,28767,28768, +0,0,0,0,28769,28770,0,0,0,0,0,0,0,0,0,0,0,0,0,28771,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,28772,0,28773,0,28782,0,0,0,0,0,0,28784,0,28785,0,28786,0,0,0,28787,0,0,0, +28797,0,0,0,0,0,0,28799,0,0,28801,0,0,0,0,28802,0,28805,0,0,28806,0,0,28807,0,0, +0,0,0,0,0,28808,0,0,0,0,0,28810,28812,0,0,28816,28819,0,0,28821,0,28826,0,0,0, +28842,28852,0,0,28853,0,28854,28855,0,0,0,28857,0,0,0,28858,0,28867,28868,28869, +0,0,0,28874,28880,28882,28890,28892,0,0,0,0,0,0,0,28895,0,0,0,28898,28899,0,0,0, +28900,0,0,28904,0,28906,0,0,0,0,28907,0,0,0,0,0,0,28908,0,0,0,28910,0,28914,0,0, +0,0,0,0,0,28915,28916,28919,0,0,28920,0,28921,0,0,0,0,0,0,0,0,28924,0,0,0,0, +28926,28929,0,0,0,28930,0,28936,0,28939,0,0,0,0,28942,0,0,0,0,0,0,28956,0,0,0, +28966,0,0,0,0,28967,0,0,0,0,0,0,0,0,0,28968,0,28971,0,28975,28976,0,28982,28983, +0,0,28984,28989,28996,28997,28998,0,0,0,0,0,0,28999,0,0,0,0,0,29000,0,29001,0,0, +0,29009,0,0,29011,0,0,29021,0,0,0,0,29024,0,29025,0,0,0,0,0,29026,0,0,0,29036,0, +0,0,29037,0,0,0,0,29038,0,29045,0,29047,0,0,0,0,0,0,0,0,0,29051,0,0,0,29054, +29056,29062,0,29070,29082,0,0,0,29083,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29084,0,0, +0,0,29085,29088,0,0,0,0,0,0,0,29090,29097,0,0,0,29103,0,0,0,0,0,0,0,0,29105,0,0, +0,0,0,29107,0,29109,0,0,0,29115,0,0,29120,0,0,29138,29140,0,0,0,0,0,0,0,0,0, +29152,0,29160,29174,0,29176,0,0,29180,0,29181,0,0,0,0,0,0,0,0,29228,0,0,29229,0, +0,29230,0,0,0,0,0,0,0,0,0,0,29234,0,0,0,29241,0,29245,0,29248,0,29250,29256, +29280,0,29282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29285,0,0,29286,29291,29292,0,0,0,0, +29294,0,29295,0,0,0,0,0,29296,29297,29298,29300,0,29302,0,0,29304,29307,0,29312, +0,0,0,29322,0,0,29323,0,0,29324,29326,29328,0,29335,0,0,0,0,0,0,0,29338,29339,0, +0,0,0,0,29341,29343,0,0,0,0,29344,0,0,0,0,0,29345,0,0,0,0,29346,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,29347,29348,29349,0,0,29354,0,0,29355,0,0,0,0,0,0,0,0,29357,0,0, +0,0,29364,0,29365,0,0,0,0,0,0,0,29366,0,0,29368,0,0,0,0,0,0,0,0,29378,0,29381,0, +0,0,0,0,0,0,0,29386,0,0,0,0,0,0,29389,0,0,0,29390,0,0,29391,29397,0,29398,29412, +29414,29418,29419,0,0,0,0,0,0,0,29420,0,0,0,0,0,0,0,29423,0,0,0,29435,0,0,0, +29437,0,0,29439,0,29441,0,0,0,0,29443,0,29446,29450,29452,0,0,0,0,0,29456,0,0,0, +0,0,29461,0,0,0,29464,0,0,0,0,0,0,0,0,29468,0,29473,0,0,0,29486,0,0,0,29490,0,0, +0,29491,29492,0,0,29497,0,0,0,29498,0,29499,0,29502,29505,0,29509,0,0,0,29510,0, +0,0,29512,0,0,0,29516,0,0,0,0,0,0,0,0,29518,0,29519,0,0,0,0,0,29520,29521,29529, +0,0,0,0,0,0,0,0,29530,0,0,29531,29538,0,29540,0,0,0,29542,0,29543,29544,29547,0, +0,29548,0,0,0,29549,0,0,0,29550,0,0,29552,0,0,0,0,29558,29561,0,29562,29564,0,0, +29565,0,0,29566,0,0,0,0,0,0,0,0,0,0,29578,29584,29586,29591,0,0,0,0,29593,29594, +0,0,29597,0,0,29613,0,29614,0,29615,0,0,0,0,29616,29617,0,0,29625,0,0,0,29632,0, +0,0,0,0,0,0,29633,0,0,0,0,0,29634,29635,29637,0,29638,0,29641,29643,0,0,0,0,0,0, +29644,0,29645,0,29649,0,0,0,29650,0,29653,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29656, +29659,0,0,29660,0,0,0,29661,0,0,0,0,0,29664,0,0,0,29671,29673,0,0,0,0,0,0,0, +29675,0,29677,29679,0,0,29684,0,0,0,0,0,29685,0,0,0,29687,0,0,0,29688,0,29689, +29690,29700,0,29701,0,0,0,29702,0,29706,0,0,0,0,0,0,0,29720,0,29721,0,29727,0, +29733,29734,0,29750,29761,0,29763,0,0,0,0,0,29764,0,0,29765,0,0,0,29771,0,0,0,0, +0,0,0,0,0,0,0,0,29772,0,0,0,29773,29774,29775,0,0,0,0,0,0,0,0,0,0,0,29822,0,0,0, +29824,0,29825,0,0,0,0,0,29827,0,0,0,0,0,0,0,0,29829,0,29832,29834,0,0,29835,0,0, +29837,29838,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29843,0,0,0,0,29844,29845,0,0,0, +0,0,0,0,0,0,29849,0,0,29869,29872,29890,29905,0,0,0,0,0,29907,29921,0,29922,0,0, +29923,29926,29944,29946,0,0,0,0,0,0,0,29947,29948,0,0,0,29951,0,0,0,0,0,29953,0, +0,29956,0,29957,0,0,29962,0,0,0,0,29971,0,0,0,29972,0,0,0,0,0,29978,0,29979, +29992,30007,30008,30010,0,0,0,30013,0,0,0,0,30014,30016,0,0,0,0,0,0,0,0,0,0,0, +30017,0,0,0,0,0,30023,30031,0,0,30033,0,0,0,0,0,0,0,0,0,0,30034,0,30038,0,30039, +0,30040,0,0,0,0,0,0,30067,30068,0,0,0,30069,0,30072,0,0,0,30073,0,0,0,0,30075,0, +0,0,0,0,0,30079,0,0,30080,0,0,0,0,0,30082,0,0,0,0,0,0,0,0,0,0,0,30084,30090,0,0, +30091,0,0,0,0,30098,30118,0,30119,0,30121,30130,0,0,0,0,0,0,0,0,0,0,0,0,0,30131, +30132,30133,0,0,0,0,0,0,30135,0,0,0,0,0,0,0,0,0,0,0,30136,0,0,30137,30138,0,0,0, +30139,30146,0,0,0,0,0,30147,0,0,30148,30151,0,0,0,30168,0,30172,30173,0,0,0,0,0, +0,0,0,30180,30181,0,30192,0,0,0,0,0,0,0,30194,30196,0,0,30199,0,0,30202,0,0,0,0, +30203,0,0,0,0,0,0,0,0,0,0,30213,0,0,0,30216,0,0,30217,0,0,0,30218,0,0,0,0,30219, +0,30220,0,30222,30227,0,0,0,0,0,30231,0,0,30233,30235,0,0,0,0,30238,0,30240, +30243,30245,0,30250,30252,0,0,0,30269,0,0,30271,30272,0,0,0,30278,30280,0,0, +30282,0,30284,0,30294,0,0,0,0,30295,30296,0,0,0,0,0,30298,30299,30302,30304, +30306,0,0,0,0,0,0,30316,30317,0,0,0,30318,0,0,0,30319,0,30320,30322,30326,0,0,0, +0,0,30327,0,30332,30348,30349,0,0,30356,0,0,0,0,0,0,0,0,30357,0,30358,0,30359, +30360,0,0,30365,30366,30378,0,0,0,0,30379,0,0,30381,0,30385,0,30388,30397,0,0,0, +30401,0,0,0,0,30403,0,0,0,0,0,30404,0,0,30405,0,30406,30408,0,30409,0,30410,0,0, +0,30417,0,0,30418,30419,0,30420,0,30424,0,0,0,30427,30430,30432,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,30433,0,0,0,0,0,0,0,30436,0,30437,30438,0,30441,30442,0,0, +0,30445,0,0,0,0,30452,30456,30457,0,0,0,30458,0,30464,0,0,0,0,0,0,30467,0,30469, +0,0,0,0,0,30477,0,0,30484,0,0,0,0,0,30485,0,0,0,0,0,30486,30487,30497,30498,0,0, +0,0,0,0,0,0,0,0,30505,0,30508,0,0,0,30509,30510,0,30514,30516,0,0,0,0,0,0,0,0,0, +0,0,30523,0,30524,0,30525,0,0,0,0,30537,0,0,30538,0,0,0,0,0,30553,0,0,30555, +30556,30558,30559,30560,0,0,30561,0,30562,0,0,0,0,0,0,0,0,30563,30570,30571,0, +30586,30587,0,0,30590,0,0,30594,0,0,0,0,30611,30612,30623,30634,0,0,30636,30640, +30655,30656,0,30657,0,0,30658,30669,0,30670,0,30676,30678,0,0,0,0,0,0,0,30679,0, +0,0,0,0,0,0,0,0,0,0,30695,0,0,30698,0,0,0,0,30700,0,0,0,0,30701,0,30702,30703,0, +0,0,0,30707,0,0,0,30709,0,0,30710,30719,30729,0,0,0,0,0,0,0,0,0,30731,0,0,30733, +0,0,0,30734,0,0,0,0,0,30736,30737,0,0,0,30740,0,0,0,30743,0,30746,0,30747,30748, +0,0,30751,30752,30753,0,0,0,30754,0,0,30760,0,0,0,0,0,0,0,30763,0,30764,0,0, +30766,0,30769,30770,30771,30774,30777,0,0,30779,30780,30781,0,0,0,0,30790,0,0,0, +30792,0,0,0,0,30810,0,0,0,0,0,0,0,30812,30819,0,0,30823,30824,0,30825,0,30827,0, +0,0,0,0,0,30828,0,0,30830,0,0,0,30834,0,30835,0,30837,30838,0,30845,0,0,0,0,0, +30846,30847,0,0,30849,0,30851,0,0,0,0,0,30852,30858,0,0,30859,0,30865,0,0,30866, +0,0,30868,0,0,30869,0,0,0,30881,30883,0,0,0,0,0,30889,0,30891,0,0,0,0,30894,0, +30895,0,30897,0,30898,0,0,0,30904,30906,0,30909,0,0,0,0,0,0,30910,0,0,0,30915, +30933,30942,0,0,0,0,30943,0,0,30945,0,0,0,0,0,0,30946,0,0,30947,0,0,30955,30956, +0,0,30960,0,0,30961,30962,30966,0,0,30969,30974,0,0,0,30976,0,0,30977,0,30978, +30982,0,0,0,0,0,0,0,30994,30995,30998,0,31000,0,0,31001,0,0,31003,31005,0,0, +31006,31011,0,0,31014,0,31016,0,0,0,0,31018,0,0,31020,31023,31024,31025,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,31027,31028,31029,0,0,0,0,0,0,31032,0,0,0,0,0,0,0,0,0,0,0, +31036,31037,31038,0,0,0,31041,31043,31045,0,31047,0,0,0,31048,0,31049,0,0,0, +31053,31054,31055,0,0,31063,0,0,0,0,0,31066,0,31068,31071,0,0,0,31072,31073,0,0, +0,0,31075,0,0,31076,0,0,0,31077,31079,0,31080,0,0,0,0,0,0,0,0,0,0,31087,0,31142, +0,31144,0,0,31145,31146,31147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31149,0,31151,31152,0, +0,0,0,0,0,0,31162,31171,31174,31175,0,0,0,31176,0,0,0,0,0,0,0,31179,0,0,0,31186, +0,0,0,31192,31195,0,0,31196,0,0,0,0,0,0,0,0,31198,0,0,0,0,0,31199,0,0,0,31205,0, +0,0,0,31211,31215,0,0,0,0,31231,0,31232,0,0,0,0,0,0,0,0,0,0,31233,31236,31253,0, +31254,0,0,0,0,0,0,31255,0,0,31257,0,0,0,0,0,0,0,0,0,31258,31259,0,0,31260,0, +31261,0,0,0,0,0,31262,31263,0,0,31264,0,31266,0,31267,0,0,0,0,0,31281,0,31282,0, +31284,0,0,31285,31287,31288,0,0,31290,0,0,0,31292,31295,0,31299,0,31300,0,0,0,0, +0,31302,0,0,0,0,31303,0,0,0,0,0,0,31304,0,0,0,0,0,31305,31308,31309,31315,0, +31317,0,0,0,0,0,31323,0,31324,0,0,0,0,0,31325,31327,0,0,31331,0,0,0,0,0,31333,0, +0,0,0,0,31336,0,0,31337,0,0,0,0,0,0,31338,0,0,0,0,0,0,0,0,0,0,0,0,31339,0,0,0,0, +0,0,0,31342,0,0,0,0,31345,0,0,0,0,0,0,0,0,31347,0,0,0,0,0,0,31348,0,0,31350, +31351,0,31352,0,0,31354,0,0,0,0,31355,0,0,31356,0,0,0,0,0,0,0,0,0,0,31363,0, +31372,0,0,31373,0,0,0,0,0,0,0,0,0,31376,0,31388,0,31389,0,31392,0,31401,0,31405, +31407,31408,0,31409,0,0,0,0,0,0,31413,31415,0,0,0,31416,31418,0,0,0,0,0,0,31422, +31423,0,0,31424,0,31425,31432,0,0,0,0,0,0,0,0,0,31433,0,0,0,0,0,0,0,0,31434,0,0, +0,0,0,0,31435,0,0,0,0,31438,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31442,0,31444,0, +31448,0,0,31451,0,0,0,0,31452,0,31461,31465,0,0,31466,0,0,31467,0,0,31468,0,0,0, +31469,31473,0,31476,0,0,0,0,31489,31490,0,0,0,0,0,0,0,31492,31493,31494,0,0,0,0, +31501,31504,31505,0,0,0,0,0,0,0,0,0,31509,0,0,0,0,31510,0,0,31511,0,0,31513,0,0, +0,0,0,0,0,0,0,31514,0,31522,31536,31539,31540,0,31541,0,0,0,0,0,0,31546,31553, +31559,0,0,0,31560,31561,31562,0,0,31564,31567,0,31569,0,0,0,31570,0,0,0,0,31571, +0,0,0,0,0,0,31572,31574,31580,31581,0,0,31582,31584,31585,31586,31595,0,31596,0, +0,0,0,31597,0,31599,0,31600,31601,0,0,31603,31604,0,0,31608,31610,0,0,0,31611,0, +31615,0,0,0,0,31616,0,0,0,0,0,0,31617,0,0,0,0,0,31618,0,0,0,0,0,0,31621,0,0,0,0, +0,0,0,0,0,31622,31625,0,0,0,0,31627,0,31641,0,0,31642,0,0,31643,0,0,0,0,0,0,0,0, +0,31644,0,31646,0,0,0,0,31648,0,0,0,31652,0,0,0,31657,0,0,31676,0,0,0,0,0,0,0, +31689,31691,31692,0,31694,0,0,0,31696,0,31702,0,31703,0}; + +const BROTLI_MODEL("small") +DictWord kStaticDictionaryWords[BROTLI_ENC_STATIC_DICT_LUT_NUM_ITEMS] = { +{0,0,0},{8,0,1002},{136,0,1015},{4,0,683},{4,10,325},{138,10,125},{7,11,572},{9, +11,592},{11,11,680},{11,11,842},{11,11,924},{12,11,356},{12,11,550},{13,11,317}, +{13,11,370},{13,11,469},{13,11,471},{14,11,397},{18,11,69},{146,11,145},{134,0, +1265},{136,11,534},{134,0,1431},{11,0,138},{140,0,40},{4,0,155},{4,10,718},{7,0, +1689},{135,10,1216},{4,0,245},{5,0,151},{5,0,741},{6,0,1147},{7,0,498},{7,0,870} +,{7,0,1542},{12,0,213},{14,0,36},{14,0,391},{17,0,111},{18,0,6},{18,0,46},{18,0, +151},{19,0,36},{20,0,32},{20,0,56},{20,0,69},{20,0,102},{21,0,4},{22,0,8},{22,0, +10},{22,0,14},{150,0,31},{4,0,624},{135,0,1752},{5,10,124},{5,10,144},{6,10,548} +,{7,10,15},{7,10,153},{137,10,629},{6,0,503},{7,10,1531},{8,10,416},{9,0,586},{9 +,10,275},{10,10,100},{11,10,658},{11,10,979},{12,10,86},{13,0,468},{14,0,66},{14 +,10,207},{15,10,20},{15,10,25},{144,0,58},{5,0,603},{5,10,915},{6,10,1783},{7,0, +1212},{7,10,211},{7,10,1353},{9,0,565},{9,10,83},{10,10,376},{10,10,431},{11,10, +543},{12,10,664},{13,10,280},{13,10,428},{14,0,301},{14,10,128},{17,10,52},{145, +10,81},{4,0,492},{133,0,451},{135,0,835},{141,0,70},{132,0,539},{7,11,748},{139, +11,700},{7,11,1517},{11,11,597},{14,11,76},{14,11,335},{148,11,33},{6,0,113},{ +135,0,436},{4,10,338},{133,10,400},{136,0,718},{133,11,127},{133,11,418},{6,0, +1505},{6,11,198},{7,0,520},{11,10,892},{140,11,83},{4,10,221},{5,10,659},{5,10, +989},{7,10,697},{7,10,1211},{138,10,284},{135,0,1070},{5,11,276},{6,11,55},{135, +11,1369},{134,0,1515},{6,11,1752},{136,11,726},{138,10,507},{4,10,188},{7,10,805 +},{143,0,78},{5,10,884},{139,10,991},{133,11,764},{134,10,1653},{6,11,309},{7,11 +,331},{138,11,550},{135,11,1861},{132,11,348},{135,11,986},{135,11,1573},{12,0, +610},{13,0,431},{144,0,59},{9,11,799},{140,10,166},{134,0,1530},{132,0,750},{132 +,0,307},{133,0,964},{6,11,194},{7,11,133},{10,11,493},{10,11,570},{139,11,664},{ +5,11,24},{5,11,569},{6,11,3},{6,11,119},{6,11,143},{6,11,440},{7,11,295},{7,11, +599},{7,11,1686},{7,11,1854},{8,11,424},{9,11,43},{9,11,584},{9,11,760},{10,11, +148},{10,11,328},{11,11,159},{11,11,253},{11,11,506},{12,11,487},{12,11,531},{ +144,11,33},{136,10,760},{5,11,14},{5,11,892},{6,11,283},{7,11,234},{136,11,537}, +{135,11,1251},{4,11,126},{8,11,635},{147,11,34},{4,11,316},{135,11,1561},{6,0, +999},{6,0,1310},{137,11,861},{4,11,64},{5,11,352},{5,11,720},{6,11,368},{139,11, +359},{4,0,75},{5,0,180},{6,0,500},{7,0,58},{7,0,710},{8,10,770},{138,0,645},{133 +,0,649},{6,0,276},{7,0,282},{7,0,879},{7,0,924},{8,0,459},{9,0,599},{9,0,754},{ +11,0,574},{12,0,128},{12,0,494},{13,0,52},{13,0,301},{15,0,30},{143,0,132},{132, +0,200},{4,10,89},{5,10,489},{6,10,315},{7,10,553},{7,10,1745},{138,10,243},{135, +11,1050},{6,10,1658},{7,0,1621},{9,10,3},{10,10,154},{11,10,641},{13,10,85},{13, +10,201},{141,10,346},{6,11,175},{137,11,289},{5,11,432},{133,11,913},{6,0,225},{ +137,0,211},{7,0,718},{8,0,687},{139,0,374},{4,10,166},{133,10,505},{6,10,1670},{ +137,0,110},{8,0,58},{9,0,724},{11,0,809},{13,0,113},{145,0,72},{6,0,345},{7,0, +1247},{144,11,82},{5,11,931},{134,11,1698},{8,0,767},{8,0,803},{9,0,301},{137,0, +903},{139,0,203},{134,0,1154},{7,0,1949},{136,0,674},{134,0,259},{135,0,1275},{5 +,11,774},{6,11,1637},{6,11,1686},{134,11,1751},{134,0,1231},{7,10,445},{8,10,307 +},{8,10,704},{10,10,41},{10,10,439},{11,10,237},{11,10,622},{140,10,201},{136,0, +254},{6,11,260},{135,11,1484},{139,0,277},{135,10,1977},{4,10,189},{5,10,713},{6 +,11,573},{136,10,57},{138,10,371},{132,10,552},{134,11,344},{133,0,248},{6,11, +240},{9,0,800},{10,0,693},{11,0,482},{11,0,734},{139,0,789},{4,0,116},{4,11,292} +,{4,11,736},{5,0,95},{5,0,445},{5,11,871},{6,11,171},{6,11,1689},{7,0,1688},{7, +11,1324},{7,11,1944},{8,0,29},{9,0,272},{9,11,415},{9,11,580},{11,0,509},{11,0, +915},{14,11,230},{146,11,68},{7,0,490},{13,0,100},{143,0,75},{135,0,1641},{133,0 +,543},{7,11,209},{8,11,661},{10,11,42},{11,11,58},{12,11,58},{12,11,118},{141,11 +,32},{5,0,181},{6,11,63},{7,11,920},{136,0,41},{133,0,657},{133,11,793},{138,0, +709},{7,0,25},{8,0,202},{138,0,536},{5,11,665},{135,10,1788},{145,10,49},{9,0, +423},{140,0,89},{5,11,67},{6,11,62},{6,11,374},{135,11,1391},{8,0,113},{9,0,877} +,{9,11,790},{10,0,554},{11,0,83},{12,0,136},{12,11,47},{147,0,109},{138,10,661}, +{4,0,963},{7,10,1945},{10,0,927},{142,0,442},{133,0,976},{132,0,206},{4,11,391}, +{135,11,1169},{134,0,2002},{6,0,696},{134,0,1008},{134,0,1170},{132,11,271},{7,0 +,13},{8,0,226},{10,0,537},{11,0,570},{11,0,605},{11,0,799},{11,0,804},{12,0,85}, +{12,0,516},{12,0,623},{12,11,314},{13,0,112},{13,0,361},{14,0,77},{14,0,78},{17, +0,28},{147,0,110},{132,0,769},{134,0,1544},{4,0,551},{137,0,678},{5,10,84},{134, +10,163},{9,0,57},{9,0,459},{10,0,425},{11,0,119},{12,0,184},{12,0,371},{13,0,358 +},{145,0,51},{4,11,253},{5,0,188},{5,0,814},{5,10,410},{5,11,544},{7,11,300},{8, +0,10},{9,0,421},{9,0,729},{9,11,340},{10,0,609},{139,0,689},{134,0,624},{138,11, +321},{135,0,1941},{5,10,322},{8,10,186},{9,10,262},{10,10,187},{14,10,208},{146, +0,130},{5,11,53},{5,11,541},{6,11,94},{6,11,499},{7,11,230},{139,11,321},{133,10 +,227},{4,0,378},{4,11,920},{5,11,25},{5,11,790},{6,11,457},{135,11,853},{137,0, +269},{132,0,528},{134,0,1146},{7,10,1395},{8,10,486},{9,10,236},{9,10,878},{10, +10,218},{11,10,95},{19,10,17},{147,10,31},{7,10,2043},{8,10,672},{141,10,448},{ +134,0,1105},{134,0,1616},{134,11,1765},{140,11,163},{5,10,412},{133,11,822},{132 +,11,634},{6,0,656},{134,11,1730},{134,0,1940},{5,0,104},{6,0,173},{135,0,1631},{ +136,10,562},{6,11,36},{7,11,658},{8,11,454},{147,11,86},{5,0,457},{134,10,1771}, +{7,0,810},{8,0,138},{8,0,342},{9,0,84},{10,0,193},{11,0,883},{140,0,359},{7,10, +1190},{137,0,620},{137,10,132},{7,11,975},{137,11,789},{6,0,95},{6,0,1934},{136, +0,967},{141,11,335},{6,0,406},{10,0,409},{10,0,447},{11,0,44},{140,0,100},{4,10, +317},{135,10,1279},{132,0,477},{134,0,1268},{5,10,63},{5,10,509},{6,0,1941},{136 +,0,944},{132,0,629},{132,11,104},{4,0,246},{133,0,375},{4,10,288},{134,0,1636},{ +135,11,1614},{8,10,89},{8,10,620},{9,0,49},{10,0,774},{11,10,628},{12,10,322},{ +143,10,124},{4,0,282},{7,0,1034},{11,0,398},{11,0,634},{12,0,1},{12,0,79},{12,0, +544},{14,0,237},{17,0,10},{146,0,20},{132,0,824},{7,11,45},{9,11,542},{9,11,566} +,{138,11,728},{5,0,118},{5,0,499},{6,0,476},{6,0,665},{6,0,1176},{6,0,1196},{7,0 +,600},{7,0,888},{135,0,1096},{7,0,296},{7,0,596},{8,0,560},{8,0,586},{9,0,612},{ +11,0,304},{12,0,46},{13,0,89},{14,0,112},{145,0,122},{5,0,894},{6,0,1772},{9,0, +1009},{138,10,120},{5,11,533},{7,11,755},{138,11,780},{151,10,1},{6,0,1474},{7, +11,87},{142,11,288},{139,0,366},{137,10,461},{7,11,988},{7,11,1939},{9,11,64},{9 +,11,502},{12,11,7},{12,11,34},{13,11,12},{13,11,234},{147,11,77},{7,0,1599},{7,0 +,1723},{8,0,79},{8,0,106},{8,0,190},{8,0,302},{8,0,383},{8,0,713},{9,0,119},{9,0 +,233},{9,0,419},{9,0,471},{10,0,181},{10,0,406},{11,0,57},{11,0,85},{11,0,120},{ +11,0,177},{11,0,296},{11,0,382},{11,0,454},{11,0,758},{11,0,999},{12,0,27},{12,0 +,98},{12,0,131},{12,0,245},{12,0,312},{12,0,446},{12,0,454},{13,0,25},{13,0,98}, +{13,0,426},{13,0,508},{14,0,70},{14,0,163},{14,0,272},{14,0,277},{14,0,370},{15, +0,95},{15,0,138},{15,0,167},{17,0,38},{148,0,96},{135,10,1346},{10,0,200},{19,0, +2},{151,0,22},{135,11,141},{134,10,85},{134,0,1759},{138,0,372},{145,0,16},{4,11 +,619},{136,0,943},{139,11,88},{5,11,246},{8,11,189},{9,11,355},{9,11,512},{10,11 +,124},{10,11,453},{11,11,143},{11,11,416},{11,11,859},{141,11,341},{5,0,258},{ +134,0,719},{6,0,1798},{6,0,1839},{8,0,900},{10,0,874},{10,0,886},{12,0,698},{12, +0,732},{12,0,770},{16,0,106},{18,0,163},{18,0,170},{18,0,171},{152,0,20},{9,0, +707},{9,11,707},{11,0,326},{11,0,339},{11,11,326},{11,11,339},{12,0,423},{12,0, +502},{12,11,423},{12,11,502},{20,0,62},{148,11,62},{5,0,30},{7,0,495},{8,0,134}, +{9,0,788},{140,0,438},{133,11,678},{5,10,279},{6,10,235},{7,10,468},{8,10,446},{ +9,10,637},{10,10,717},{11,10,738},{140,10,514},{5,11,35},{6,11,287},{7,11,862},{ +7,11,1886},{138,11,179},{4,11,517},{7,0,1948},{135,0,2004},{5,10,17},{6,10,371}, +{137,10,528},{4,0,115},{5,0,669},{6,0,407},{8,0,311},{11,0,10},{141,0,5},{137,0, +381},{5,0,50},{6,0,439},{7,0,780},{135,0,1040},{136,11,667},{11,11,403},{146,11, +83},{5,0,1},{6,0,81},{138,0,520},{134,0,738},{5,0,482},{8,0,98},{9,0,172},{10,0, +360},{10,0,700},{10,0,822},{11,0,302},{11,0,778},{12,0,50},{12,0,127},{12,0,396} +,{13,0,62},{13,0,328},{14,0,122},{147,0,72},{9,11,157},{10,11,131},{140,11,72},{ +135,11,714},{135,11,539},{5,0,2},{6,0,512},{7,0,797},{7,0,1494},{8,0,253},{8,0, +589},{9,0,77},{10,0,1},{10,0,129},{10,0,225},{11,0,118},{11,0,226},{11,0,251},{ +11,0,430},{11,0,701},{11,0,974},{11,0,982},{12,0,64},{12,0,260},{12,0,488},{140, +0,690},{5,11,394},{7,11,367},{7,11,487},{7,11,857},{7,11,1713},{8,11,246},{9,11, +537},{10,11,165},{12,11,219},{140,11,561},{136,0,557},{5,10,779},{5,10,807},{6, +10,1655},{134,10,1676},{4,10,196},{5,10,558},{133,10,949},{11,11,827},{12,11,56} +,{14,11,34},{143,11,148},{137,0,347},{133,0,572},{134,0,832},{4,0,12},{7,0,504}, +{7,0,522},{7,0,809},{8,0,797},{141,0,88},{4,10,752},{133,11,449},{7,11,86},{8,11 +,103},{145,11,69},{7,11,2028},{138,11,641},{5,0,528},{6,11,1},{142,11,2},{134,0, +861},{4,10,227},{5,10,159},{5,10,409},{7,10,80},{10,0,294},{10,10,479},{12,10, +418},{14,10,50},{14,10,249},{142,10,295},{7,10,1470},{8,10,66},{8,10,137},{8,10, +761},{9,10,638},{11,10,80},{11,10,212},{11,10,368},{11,10,418},{12,10,8},{13,10, +15},{16,10,61},{17,10,59},{19,10,28},{148,10,84},{7,11,1148},{148,0,109},{6,11, +277},{7,11,1274},{7,11,1386},{7,11,1392},{12,11,129},{146,11,87},{6,11,187},{7, +11,39},{7,11,1203},{8,11,380},{8,11,542},{14,11,117},{149,11,28},{134,0,1187},{5 +,0,266},{9,0,290},{9,0,364},{10,0,293},{11,0,606},{142,0,45},{6,11,297},{7,11, +793},{139,11,938},{4,0,50},{6,0,594},{9,0,121},{10,0,49},{10,0,412},{139,0,834}, +{136,0,748},{7,11,464},{8,11,438},{11,11,105},{11,11,363},{12,11,231},{14,11,386 +},{15,11,102},{148,11,75},{132,0,466},{6,10,38},{7,10,1220},{8,10,185},{8,10,256 +},{9,10,22},{9,10,331},{10,10,738},{11,10,205},{11,10,540},{11,10,746},{13,0,399 +},{13,10,465},{14,0,337},{142,10,194},{9,0,378},{141,0,162},{137,0,519},{4,10, +159},{6,10,115},{7,10,252},{7,10,257},{7,10,1928},{8,10,69},{9,10,384},{10,10,91 +},{10,10,615},{12,10,375},{14,10,235},{18,10,117},{147,10,123},{5,10,911},{5,11, +604},{136,10,278},{132,0,667},{4,10,151},{7,10,1567},{8,0,351},{137,0,322},{134, +0,902},{133,10,990},{5,10,194},{7,10,1662},{9,10,90},{140,0,180},{4,0,869},{134, +0,1996},{134,0,813},{133,10,425},{137,11,761},{132,0,260},{133,10,971},{5,11,20} +,{6,11,298},{7,11,659},{7,11,1366},{137,11,219},{4,0,39},{5,0,36},{7,0,1843},{8, +0,407},{11,0,144},{140,0,523},{4,0,510},{10,0,587},{139,10,752},{7,0,29},{7,0,66 +},{7,0,1980},{10,0,487},{138,0,809},{9,10,662},{13,0,260},{14,0,82},{146,0,63},{ +5,10,72},{6,10,264},{7,10,21},{7,10,46},{7,10,2013},{8,10,215},{8,10,513},{10,10 +,266},{139,10,22},{134,0,570},{4,11,439},{6,0,565},{7,0,1667},{10,10,95},{11,10, +603},{12,11,242},{13,10,443},{14,10,160},{143,10,4},{134,0,1464},{134,10,431},{5 +,10,874},{6,10,1677},{9,0,372},{15,0,2},{15,10,0},{19,0,10},{147,0,18},{132,0, +787},{6,0,380},{7,10,939},{7,10,1172},{7,10,1671},{9,10,540},{10,10,696},{11,10, +265},{11,10,732},{11,10,928},{11,10,937},{12,0,399},{13,10,438},{149,0,19},{137, +0,200},{132,11,233},{132,0,516},{134,11,577},{132,0,844},{11,0,887},{14,0,365},{ +142,0,375},{132,11,482},{8,0,821},{140,0,44},{7,0,1655},{136,0,305},{5,10,682},{ +135,10,1887},{135,11,346},{132,10,696},{4,0,10},{7,0,917},{139,0,786},{5,11,795} +,{6,11,1741},{8,11,417},{137,11,782},{4,0,1016},{134,0,2031},{4,10,726},{5,0,684 +},{133,10,630},{6,0,1021},{134,0,1480},{8,10,802},{136,10,838},{134,0,27},{134,0 +,395},{135,11,622},{7,11,625},{135,11,1750},{4,11,203},{135,11,1936},{6,10,118}, +{7,10,215},{7,10,1521},{140,10,11},{132,0,813},{136,0,511},{7,10,615},{138,10, +251},{135,10,1044},{145,0,56},{133,10,225},{6,0,342},{6,0,496},{8,0,275},{137,0, +206},{4,0,909},{133,0,940},{132,0,891},{7,11,311},{9,11,308},{140,11,255},{4,10, +370},{5,10,756},{135,10,1326},{4,0,687},{134,0,1596},{134,0,1342},{6,10,1662},{7 +,10,48},{8,10,771},{10,10,116},{13,10,104},{14,10,105},{14,10,184},{15,10,168},{ +19,10,92},{148,10,68},{138,10,209},{4,11,400},{5,11,267},{135,11,232},{151,11,12 +},{6,0,41},{141,0,160},{141,11,314},{134,0,1718},{136,0,778},{142,11,261},{134,0 +,1610},{133,0,115},{132,0,294},{4,10,120},{142,0,314},{132,0,983},{5,0,193},{140 +,0,178},{138,10,429},{5,10,820},{135,10,931},{5,11,732},{6,0,994},{6,0,1051},{6, +0,1439},{135,0,174},{4,11,100},{7,11,679},{8,11,313},{138,10,199},{6,10,151},{6, +10,1675},{7,10,383},{151,10,10},{6,0,1796},{8,0,848},{8,0,867},{8,0,907},{10,0, +855},{140,0,703},{140,0,221},{4,0,122},{5,0,796},{5,0,952},{6,0,1660},{6,0,1671} +,{8,0,567},{9,0,687},{9,0,742},{10,0,686},{11,0,682},{11,0,909},{140,0,281},{5, +11,362},{5,11,443},{6,11,318},{7,11,1019},{139,11,623},{5,11,463},{136,11,296},{ +6,10,1624},{11,0,583},{12,10,422},{13,0,262},{142,10,360},{5,0,179},{7,0,1095},{ +135,0,1213},{4,10,43},{4,11,454},{5,10,344},{133,10,357},{4,0,66},{7,0,722},{135 +,0,904},{134,0,773},{5,10,888},{135,0,352},{5,11,48},{5,11,404},{6,11,557},{7,11 +,458},{8,11,597},{10,11,455},{10,11,606},{11,11,49},{11,11,548},{12,11,476},{13, +11,18},{141,11,450},{134,11,418},{132,10,711},{5,11,442},{135,11,1984},{141,0,35 +},{137,0,152},{134,0,1197},{135,11,1093},{137,11,203},{137,10,440},{10,0,592},{ +10,0,753},{12,0,317},{12,0,355},{12,0,465},{12,0,469},{12,0,560},{12,0,578},{141 +,0,243},{133,0,564},{134,0,797},{5,10,958},{133,10,987},{5,11,55},{7,11,376},{ +140,11,161},{133,11,450},{134,0,556},{134,0,819},{11,10,276},{142,10,293},{7,0, +544},{138,0,61},{4,10,65},{5,10,479},{5,10,1004},{7,10,1913},{8,0,719},{8,10,317 +},{9,10,302},{10,10,612},{141,10,22},{4,0,5},{4,10,261},{4,11,213},{5,0,498},{7, +10,510},{7,11,223},{8,0,637},{8,11,80},{137,0,521},{4,10,291},{5,0,927},{7,0,101 +},{7,11,381},{7,11,806},{7,11,820},{8,11,354},{8,11,437},{8,11,787},{9,10,515},{ +9,11,657},{10,11,58},{10,11,339},{10,11,749},{11,11,914},{12,10,152},{12,10,443} +,{12,11,162},{13,10,392},{13,11,75},{14,10,357},{14,11,106},{14,11,198},{14,11, +320},{14,11,413},{146,11,43},{6,0,1153},{7,0,1441},{136,11,747},{4,0,893},{5,0, +780},{133,0,893},{138,11,654},{133,11,692},{133,0,238},{134,11,191},{4,10,130},{ +135,10,843},{5,10,42},{5,10,879},{6,0,1296},{7,10,245},{7,10,324},{7,10,1532},{ +11,10,463},{11,10,472},{13,10,363},{144,10,52},{134,0,1729},{6,0,1999},{136,0, +969},{4,10,134},{133,10,372},{4,0,60},{7,0,941},{7,0,1800},{8,0,314},{9,0,700},{ +139,0,487},{134,0,1144},{6,11,162},{7,11,1960},{136,11,831},{132,11,706},{135,0, +1147},{138,11,426},{138,11,89},{7,0,1853},{138,0,437},{136,0,419},{135,10,1634}, +{133,0,828},{4,10,644},{5,0,806},{7,0,176},{7,0,178},{7,0,1240},{135,0,1976},{ +135,11,1877},{5,11,420},{135,11,1449},{4,0,51},{5,0,39},{6,0,4},{7,0,591},{7,0, +849},{7,0,951},{7,0,1613},{7,0,1760},{7,0,1988},{9,0,434},{10,0,754},{11,0,25},{ +139,0,37},{10,11,57},{138,11,277},{135,10,540},{132,11,204},{135,0,159},{139,11, +231},{133,0,902},{7,0,928},{7,11,366},{9,11,287},{12,11,199},{12,11,556},{140,11 +,577},{6,10,623},{136,10,789},{4,10,908},{5,10,359},{5,10,508},{6,10,1723},{7,10 +,343},{7,10,1996},{135,10,2026},{134,0,270},{4,10,341},{135,10,480},{5,11,356},{ +135,11,224},{11,11,588},{11,11,864},{11,11,968},{143,11,160},{132,0,556},{137,0, +801},{132,0,416},{142,0,372},{5,0,152},{5,0,197},{7,0,340},{7,0,867},{10,0,548}, +{10,0,581},{11,0,6},{12,0,3},{12,0,19},{14,0,110},{142,0,289},{139,0,369},{7,11, +630},{9,11,567},{11,11,150},{11,11,444},{141,11,119},{134,11,539},{7,10,1995},{8 +,10,299},{11,10,890},{140,10,674},{7,0,34},{7,0,190},{8,0,28},{8,0,141},{8,0,444 +},{8,0,811},{9,0,468},{11,0,334},{12,0,24},{12,0,386},{140,0,576},{133,0,757},{7 +,0,1553},{136,0,898},{133,0,721},{136,0,1012},{4,0,789},{5,0,647},{135,0,1102},{ +132,0,898},{4,10,238},{5,10,503},{6,10,179},{7,10,2003},{8,10,381},{8,10,473},{9 +,10,149},{10,0,183},{10,10,788},{15,10,45},{15,10,86},{20,10,110},{150,10,57},{4 +,10,121},{5,10,156},{5,10,349},{9,0,136},{10,10,605},{14,10,342},{147,0,107},{4, +11,235},{135,11,255},{4,11,194},{5,11,584},{6,11,384},{7,11,583},{10,11,761},{11 +,11,760},{139,11,851},{6,10,80},{6,10,1694},{7,10,173},{7,10,1974},{9,10,547},{ +10,10,730},{14,10,18},{150,10,39},{4,10,923},{134,10,1711},{5,0,277},{141,0,247} +,{132,0,435},{133,11,562},{134,0,1311},{5,11,191},{137,11,271},{132,10,595},{7, +11,1537},{14,11,96},{143,11,73},{5,0,437},{6,10,459},{7,0,502},{7,0,519},{7,0, +1122},{7,0,1751},{7,10,1753},{7,10,1805},{8,10,658},{9,10,1},{11,10,959},{13,10, +446},{142,0,211},{4,11,470},{5,11,473},{6,0,814},{6,11,153},{7,11,1503},{7,11, +1923},{10,11,701},{11,11,132},{11,11,168},{11,11,227},{11,11,320},{11,11,436},{ +11,11,525},{11,11,855},{12,11,41},{12,11,286},{13,11,103},{13,11,284},{14,11,255 +},{14,11,262},{15,11,117},{143,11,127},{5,0,265},{6,0,212},{135,0,28},{138,0,750 +},{133,11,327},{6,11,552},{7,11,1754},{137,11,604},{134,0,2012},{132,0,702},{5, +11,80},{6,11,405},{7,11,403},{7,11,1502},{7,11,1626},{8,11,456},{9,11,487},{9,11 +,853},{9,11,889},{10,11,309},{11,11,721},{11,11,994},{12,11,430},{141,11,165},{5 +,0,808},{135,0,2045},{5,0,166},{8,0,739},{140,0,511},{134,10,490},{4,11,453},{5, +11,887},{6,11,535},{8,11,6},{136,11,543},{4,0,119},{5,0,170},{5,0,447},{7,0,1708 +},{7,0,1889},{9,0,357},{9,0,719},{12,0,486},{140,0,596},{137,0,500},{7,10,250},{ +136,10,507},{132,10,158},{6,0,809},{134,0,1500},{4,10,140},{7,10,362},{8,10,209} +,{9,0,327},{9,10,10},{9,10,503},{9,10,614},{10,10,689},{11,0,350},{11,0,831},{11 +,10,327},{11,10,725},{12,10,252},{12,10,583},{13,0,352},{13,10,192},{14,10,269}, +{14,10,356},{148,10,50},{135,11,741},{4,0,450},{7,0,1158},{19,10,1},{19,10,26},{ +150,10,9},{6,0,597},{135,0,1318},{134,0,1602},{6,10,228},{7,10,1341},{9,10,408}, +{138,10,343},{7,0,1375},{7,0,1466},{138,0,331},{132,0,754},{132,10,557},{5,11, +101},{6,11,88},{6,11,543},{7,11,1677},{9,11,100},{10,11,677},{14,11,169},{14,11, +302},{14,11,313},{15,11,48},{143,11,84},{134,0,1368},{4,11,310},{9,11,795},{10, +11,733},{11,11,451},{12,11,249},{14,11,115},{14,11,286},{143,11,100},{132,10,548 +},{7,10,197},{8,10,142},{8,10,325},{9,10,150},{9,10,596},{10,0,557},{10,10,353}, +{11,10,74},{11,10,315},{12,10,662},{12,10,681},{14,10,423},{143,10,141},{133,11, +587},{5,0,850},{136,0,799},{10,0,908},{12,0,701},{12,0,757},{142,0,466},{4,0,62} +,{5,0,275},{6,10,399},{6,10,579},{7,10,692},{7,10,846},{7,10,1015},{7,10,1799},{ +8,10,403},{9,10,394},{10,10,133},{12,10,4},{12,10,297},{12,10,452},{16,10,81},{ +18,0,19},{18,10,25},{21,10,14},{22,10,12},{151,10,18},{7,10,1546},{11,10,299},{ +12,0,459},{142,10,407},{132,10,177},{132,11,498},{7,11,217},{8,11,140},{138,11, +610},{5,10,411},{135,10,653},{134,0,1802},{7,10,439},{10,10,727},{11,10,260},{ +139,10,684},{133,11,905},{11,11,580},{142,11,201},{134,0,1397},{5,10,208},{7,10, +753},{135,10,1528},{7,0,238},{7,0,2033},{8,0,120},{8,0,188},{8,0,659},{9,0,598}, +{10,0,466},{12,0,342},{12,0,588},{13,0,503},{14,0,246},{143,0,92},{135,11,1041}, +{4,11,456},{7,11,105},{7,11,358},{7,11,1637},{8,11,643},{139,11,483},{6,0,1318}, +{134,0,1324},{4,0,201},{5,10,242},{7,0,1744},{8,0,602},{11,0,247},{11,0,826},{ +145,0,65},{8,0,164},{146,0,62},{133,10,953},{139,10,802},{133,0,615},{7,11,1566} +,{8,11,269},{9,11,212},{9,11,718},{14,11,15},{14,11,132},{142,11,227},{133,10, +290},{132,10,380},{5,10,52},{7,10,277},{9,10,368},{139,10,791},{135,0,1243},{133 +,11,539},{11,11,919},{141,11,409},{136,0,968},{133,11,470},{134,0,882},{132,0, +907},{5,0,100},{10,0,329},{12,0,416},{149,0,29},{10,10,138},{139,10,476},{5,10, +725},{5,10,727},{6,11,91},{7,10,1811},{135,11,435},{4,11,16},{5,11,316},{5,11, +842},{6,11,370},{6,11,1778},{8,11,166},{11,11,812},{12,11,206},{12,11,351},{14, +11,418},{16,11,15},{16,11,34},{18,11,3},{19,11,3},{19,11,7},{20,11,4},{149,11,21 +},{132,0,176},{5,0,636},{5,0,998},{7,0,9},{7,0,1508},{8,0,26},{9,0,317},{9,0,358 +},{10,0,210},{10,0,292},{10,0,533},{11,0,555},{12,0,526},{12,0,607},{13,0,263},{ +13,0,459},{142,0,271},{4,10,38},{6,0,256},{7,10,307},{7,10,999},{7,10,1481},{7, +10,1732},{7,10,1738},{8,0,265},{9,10,414},{11,10,316},{12,10,52},{13,10,420},{ +147,10,100},{135,10,1296},{4,11,611},{133,11,606},{4,0,643},{142,11,21},{133,11, +715},{133,10,723},{6,0,610},{135,11,597},{10,0,127},{141,0,27},{6,0,1995},{6,0, +2001},{8,0,119},{136,0,973},{4,11,149},{138,11,368},{4,11,154},{5,10,109},{6,10, +1784},{7,10,1895},{7,11,1134},{8,11,105},{12,0,522},{12,10,296},{140,10,302},{4, +11,31},{6,11,429},{7,11,962},{9,11,458},{139,11,691},{4,11,312},{5,10,216},{7,10 +,1879},{9,10,141},{9,10,270},{9,10,679},{10,0,553},{10,10,159},{11,0,876},{11,10 +,197},{12,10,538},{12,10,559},{13,0,193},{13,0,423},{14,0,166},{14,10,144},{14, +10,167},{15,10,67},{147,0,84},{134,0,1582},{7,0,1578},{135,11,1578},{137,10,81}, +{132,11,236},{134,10,391},{134,0,795},{7,10,322},{136,10,249},{5,11,836},{5,11, +857},{6,11,1680},{7,11,59},{147,11,53},{135,0,432},{10,11,68},{139,11,494},{4,11 +,81},{139,11,867},{7,0,126},{136,0,84},{142,11,280},{5,11,282},{8,11,650},{9,11, +295},{9,11,907},{138,11,443},{136,0,790},{5,10,632},{138,10,526},{6,0,64},{12,0, +377},{13,0,309},{14,0,141},{14,0,429},{14,11,141},{142,11,429},{134,0,1529},{6,0 +,321},{7,0,1857},{7,10,948},{7,10,1042},{8,10,235},{8,10,461},{9,0,530},{9,10, +453},{10,10,354},{17,10,77},{147,0,99},{7,0,1104},{11,0,269},{11,0,539},{11,0, +627},{11,0,706},{11,0,975},{12,0,248},{12,0,434},{12,0,600},{12,0,622},{13,0,297 +},{13,0,485},{14,0,69},{14,0,409},{143,0,108},{4,10,362},{7,10,52},{7,10,303},{ +10,11,70},{12,11,26},{14,11,17},{14,11,178},{15,11,34},{149,11,12},{11,0,977},{ +141,0,507},{9,0,34},{139,0,484},{5,10,196},{6,10,486},{7,10,212},{8,10,309},{136 +,10,346},{6,0,1700},{7,0,26},{7,0,293},{7,0,382},{7,0,1026},{7,0,1087},{7,0,2027 +},{7,10,1912},{8,0,24},{8,0,114},{8,0,252},{8,0,727},{8,0,729},{9,0,30},{9,0,199 +},{9,0,231},{9,0,251},{9,0,334},{9,0,361},{9,0,712},{10,0,55},{10,0,60},{10,0, +232},{10,0,332},{10,0,384},{10,0,396},{10,0,504},{10,0,542},{10,0,652},{11,0,20} +,{11,0,48},{11,0,207},{11,0,291},{11,0,298},{11,0,342},{11,0,365},{11,0,394},{11 +,0,620},{11,0,705},{11,0,1017},{12,0,123},{12,0,340},{12,0,406},{12,0,643},{13,0 +,61},{13,0,269},{13,0,311},{13,0,319},{13,0,486},{14,0,234},{15,0,62},{15,0,85}, +{16,0,71},{18,0,119},{148,0,105},{4,11,71},{5,11,376},{7,11,119},{138,11,665},{4 +,10,686},{8,11,55},{10,0,918},{138,0,926},{138,10,625},{136,10,706},{132,11,479} +,{4,10,30},{133,10,43},{6,0,379},{7,0,270},{7,11,607},{8,0,176},{8,0,183},{8,11, +99},{9,0,432},{9,0,661},{12,0,247},{12,0,617},{18,0,125},{152,11,4},{5,0,792},{ +133,0,900},{4,11,612},{133,11,561},{4,10,220},{4,11,41},{5,11,74},{7,10,1535},{7 +,11,1627},{11,11,871},{140,11,619},{135,0,1920},{7,11,94},{11,11,329},{11,11,965 +},{12,11,241},{14,11,354},{15,11,22},{148,11,63},{9,11,209},{137,11,300},{134,0, +771},{135,0,1979},{4,0,901},{133,0,776},{142,0,254},{133,11,98},{9,11,16},{141, +11,386},{133,11,984},{4,11,182},{6,11,205},{135,11,220},{7,10,1725},{7,10,1774}, +{138,10,393},{5,10,263},{134,10,414},{4,11,42},{9,11,205},{9,11,786},{138,11,659 +},{14,0,140},{148,0,41},{6,10,178},{6,10,1750},{6,11,289},{7,11,1670},{8,0,440}, +{9,10,690},{10,0,359},{10,10,155},{10,10,373},{11,10,698},{12,11,57},{13,10,155} +,{20,10,93},{151,11,4},{4,0,37},{5,0,334},{7,0,1253},{151,11,25},{4,0,508},{4,11 +,635},{5,10,97},{137,10,393},{139,11,533},{4,0,640},{133,0,513},{134,10,1639},{ +132,11,371},{4,11,272},{7,11,836},{7,11,1651},{145,11,89},{5,11,825},{6,11,444}, +{6,11,1640},{136,11,308},{4,10,191},{7,10,934},{8,10,647},{145,10,97},{12,0,246} +,{15,0,162},{19,0,64},{20,0,8},{20,0,95},{22,0,24},{152,0,17},{4,0,533},{5,10, +165},{9,10,346},{138,10,655},{5,11,737},{139,10,885},{133,10,877},{8,10,128},{ +139,10,179},{137,11,307},{140,0,752},{133,0,920},{135,0,1048},{5,0,153},{6,0,580 +},{6,10,1663},{7,10,132},{7,10,1154},{7,10,1415},{7,10,1507},{12,10,493},{15,10, +105},{151,10,15},{5,10,459},{7,10,1073},{8,10,241},{136,10,334},{138,0,391},{135 +,0,1952},{133,11,525},{8,11,641},{11,11,388},{140,11,580},{142,0,126},{134,0,640 +},{132,0,483},{6,10,324},{6,10,520},{7,0,1616},{7,10,338},{7,10,1729},{8,10,228} +,{9,0,69},{139,10,750},{5,11,493},{134,11,528},{135,0,734},{4,11,174},{135,11, +911},{138,0,480},{9,0,495},{146,0,104},{135,10,705},{4,10,73},{6,10,612},{7,10, +927},{7,10,1330},{7,10,1822},{8,10,217},{9,0,472},{9,10,765},{9,10,766},{10,10, +408},{11,10,51},{11,10,793},{12,10,266},{15,10,158},{20,10,89},{150,10,32},{7,11 +,548},{137,11,58},{4,11,32},{5,11,215},{6,11,269},{7,11,1782},{7,11,1892},{10,11 +,16},{11,11,822},{11,11,954},{141,11,481},{132,0,874},{5,10,389},{8,10,636},{137 +,0,229},{7,11,1749},{136,11,477},{134,0,948},{5,11,308},{135,11,1088},{4,0,748}, +{139,0,1009},{136,10,21},{6,0,555},{135,0,485},{5,11,126},{8,11,297},{9,11,366}, +{9,11,445},{12,11,53},{12,11,374},{141,11,492},{7,11,1551},{139,11,361},{136,0, +193},{136,0,472},{8,0,653},{13,0,93},{147,0,14},{132,0,984},{132,11,175},{4,11, +685},{5,0,172},{134,0,1971},{149,11,8},{133,11,797},{5,10,189},{7,10,442},{7,10, +443},{8,10,281},{12,10,174},{13,0,83},{141,10,261},{134,0,1568},{133,11,565},{ +139,0,384},{133,0,260},{6,10,2},{7,0,758},{7,0,880},{7,0,1359},{7,10,1262},{7,10 +,1737},{8,10,22},{8,10,270},{8,10,612},{9,0,164},{9,0,167},{9,10,312},{9,10,436} +,{10,0,156},{10,0,588},{10,10,311},{10,10,623},{11,10,72},{11,10,330},{11,10,455 +},{12,0,101},{12,10,321},{12,10,504},{12,10,530},{12,10,543},{13,10,17},{13,10, +156},{13,10,334},{14,0,48},{15,0,70},{17,10,60},{148,10,64},{4,11,252},{7,11, +1068},{10,11,434},{11,11,228},{11,11,426},{13,11,231},{18,11,106},{148,11,87},{7 +,10,354},{10,10,410},{139,10,815},{6,0,367},{7,10,670},{7,10,1327},{8,10,411},{8 +,10,435},{9,10,653},{9,10,740},{10,10,385},{11,10,222},{11,10,324},{11,10,829},{ +140,10,611},{6,10,166},{7,0,1174},{135,10,374},{146,0,121},{132,0,828},{5,11,231 +},{138,11,509},{7,11,601},{9,11,277},{9,11,674},{10,11,178},{10,11,257},{10,11, +418},{11,11,531},{11,11,544},{11,11,585},{12,11,113},{12,11,475},{13,11,99},{142 +,11,428},{134,0,1541},{135,11,1779},{5,0,343},{134,10,398},{135,10,50},{135,11, +1683},{4,0,440},{7,0,57},{8,0,167},{8,0,375},{9,0,82},{9,0,561},{9,0,744},{9,11, +744},{138,0,620},{134,0,926},{6,10,517},{7,10,1159},{10,10,621},{139,10,192},{ +137,0,827},{8,0,194},{136,0,756},{10,10,223},{139,10,645},{7,10,64},{136,10,245} +,{4,11,399},{5,11,119},{5,11,494},{7,11,751},{137,11,556},{132,0,808},{135,0,22} +,{7,10,1763},{140,10,310},{5,0,639},{6,11,584},{7,0,1249},{139,0,896},{134,0, +1614},{135,0,860},{135,11,1121},{5,10,129},{6,10,61},{135,10,947},{4,0,102},{7,0 +,815},{7,0,1699},{139,0,964},{13,10,505},{141,10,506},{139,10,1000},{132,11,679} +,{132,0,899},{132,0,569},{5,11,694},{137,11,714},{136,0,795},{6,0,2045},{139,11, +7},{6,0,52},{9,0,104},{9,0,559},{12,0,308},{147,0,87},{4,0,301},{132,0,604},{133 +,10,637},{136,0,779},{5,11,143},{5,11,769},{6,11,1760},{7,11,682},{7,11,1992},{ +136,11,736},{137,10,590},{147,0,32},{137,11,527},{5,10,280},{135,10,1226},{134,0 +,494},{6,0,677},{6,0,682},{134,0,1044},{133,10,281},{135,10,1064},{5,11,860},{ +135,0,508},{6,11,422},{7,11,0},{7,11,1544},{9,11,577},{11,11,990},{12,11,141},{ +12,11,453},{13,11,47},{141,11,266},{134,0,1014},{5,11,515},{137,11,131},{134,0, +957},{132,11,646},{6,0,310},{7,0,1849},{8,0,72},{8,0,272},{8,0,431},{9,0,12},{9, +0,376},{10,0,563},{10,0,630},{10,0,796},{10,0,810},{11,0,367},{11,0,599},{11,0, +686},{140,0,672},{4,11,396},{7,0,570},{7,10,120},{7,11,728},{8,10,489},{9,10,319 +},{9,11,117},{10,10,820},{11,10,1004},{12,10,379},{12,10,679},{13,10,117},{13,10 +,412},{13,11,202},{14,10,25},{15,10,52},{15,10,161},{16,10,47},{20,11,51},{149, +10,2},{6,11,121},{6,11,124},{6,11,357},{7,11,1138},{7,11,1295},{8,11,162},{139, +11,655},{4,10,937},{5,10,801},{8,0,449},{136,11,449},{139,11,958},{6,0,181},{7,0 +,537},{8,0,64},{9,0,127},{10,0,496},{12,0,510},{141,0,384},{138,11,253},{4,0,244 +},{135,0,233},{133,11,237},{132,10,365},{6,0,1650},{10,0,702},{139,0,245},{5,10, +7},{139,10,774},{13,0,463},{13,11,463},{20,0,49},{148,11,49},{4,10,734},{5,10, +662},{134,10,430},{4,10,746},{135,10,1090},{5,10,360},{136,10,237},{137,0,338},{ +143,11,10},{7,11,571},{138,11,366},{134,0,1279},{9,11,513},{10,11,22},{10,11,39} +,{12,11,122},{140,11,187},{133,0,896},{146,0,178},{134,0,695},{137,0,808},{134, +11,587},{7,11,107},{7,11,838},{8,11,550},{138,11,401},{7,0,1117},{136,0,539},{4, +10,277},{5,10,608},{6,10,493},{7,10,457},{140,10,384},{133,11,768},{7,10,27},{7, +10,316},{140,0,257},{140,0,1003},{4,0,207},{5,0,586},{5,0,676},{5,10,552},{6,0, +448},{8,0,244},{11,0,1},{13,0,3},{16,0,54},{17,0,4},{146,0,13},{4,10,401},{137, +10,264},{5,0,516},{7,0,1883},{135,11,1883},{4,11,894},{140,0,960},{5,0,4},{5,0, +810},{6,0,13},{6,0,538},{6,0,1690},{6,0,1726},{7,0,499},{7,0,1819},{8,0,148},{8, +0,696},{8,0,791},{12,0,125},{143,0,9},{135,0,1268},{9,10,543},{10,10,524},{11,0, +30},{12,10,524},{14,0,315},{16,10,18},{20,10,26},{148,10,65},{4,10,205},{5,10, +623},{6,0,748},{7,10,104},{136,10,519},{11,0,542},{139,0,852},{140,0,6},{132,0, +848},{7,0,1385},{7,10,579},{9,10,41},{9,10,244},{9,10,669},{10,10,5},{11,0,582}, +{11,0,650},{11,0,901},{11,0,949},{11,10,861},{11,10,951},{11,10,980},{12,0,232}, +{12,0,236},{13,0,413},{13,0,501},{146,0,116},{4,0,945},{6,0,1811},{6,0,1845},{6, +0,1853},{6,0,1858},{8,0,862},{12,0,782},{12,0,788},{18,0,160},{148,0,117},{132, +10,717},{4,0,925},{5,0,803},{8,0,698},{138,0,828},{134,0,1416},{132,0,610},{139, +0,992},{6,0,878},{134,0,1477},{135,0,1847},{138,11,531},{137,11,539},{134,11,272 +},{133,0,383},{134,0,1404},{132,10,489},{4,11,9},{5,11,128},{7,11,368},{11,11, +480},{148,11,3},{136,0,986},{9,0,660},{138,0,347},{135,10,892},{136,11,682},{7,0 +,572},{9,0,592},{11,0,680},{12,0,356},{140,0,550},{7,0,1411},{138,11,527},{4,11, +2},{7,11,545},{135,11,894},{137,10,473},{7,10,819},{7,11,481},{9,10,26},{9,10, +392},{9,11,792},{10,10,152},{10,10,226},{11,0,64},{12,10,276},{12,10,426},{12,10 +,589},{13,10,460},{15,10,97},{19,10,48},{148,10,104},{135,10,51},{136,11,445},{ +136,11,646},{135,0,606},{132,10,674},{6,0,1829},{134,0,1830},{132,10,770},{5,10, +79},{7,10,1027},{7,10,1477},{139,10,52},{5,11,530},{142,11,113},{134,10,1666},{7 +,0,748},{139,0,700},{134,10,195},{133,10,789},{4,10,251},{4,10,688},{7,10,513},{ +7,10,1284},{9,0,87},{138,0,365},{136,11,111},{133,0,127},{6,0,198},{140,0,83},{ +133,11,556},{133,10,889},{4,10,160},{5,10,330},{7,10,1434},{136,10,174},{5,0,276 +},{6,0,55},{7,0,1369},{138,0,864},{8,11,16},{140,11,568},{6,0,1752},{136,0,726}, +{135,0,1066},{133,0,764},{6,11,186},{137,11,426},{11,0,683},{139,11,683},{6,0, +309},{7,0,331},{138,0,550},{133,10,374},{6,0,1212},{6,0,1852},{7,0,1062},{8,0, +874},{8,0,882},{138,0,936},{132,11,585},{134,0,1364},{5,10,731},{135,0,986},{6,0 +,723},{6,0,1408},{138,0,381},{135,0,1573},{134,0,1025},{4,10,626},{5,10,642},{6, +10,425},{10,10,202},{139,10,141},{4,11,93},{5,11,252},{6,11,229},{7,11,291},{9, +11,550},{139,11,644},{137,11,749},{137,11,162},{132,11,381},{135,0,1559},{6,0, +194},{7,0,133},{10,0,493},{10,0,570},{139,0,664},{5,0,24},{5,0,569},{6,0,3},{6,0 +,119},{6,0,143},{6,0,440},{7,0,295},{7,0,599},{7,0,1686},{7,0,1854},{8,0,424},{9 +,0,43},{9,0,584},{9,0,760},{10,0,148},{10,0,328},{11,0,159},{11,0,253},{11,0,506 +},{12,0,487},{140,0,531},{6,0,661},{134,0,1517},{136,10,835},{151,10,17},{5,0,14 +},{5,0,892},{6,0,283},{7,0,234},{136,0,537},{139,0,541},{4,0,126},{8,0,635},{147 +,0,34},{4,0,316},{4,0,495},{135,0,1561},{4,11,187},{5,11,184},{5,11,690},{7,11, +1869},{138,11,756},{139,11,783},{4,0,998},{137,0,861},{136,0,1009},{139,11,292}, +{5,11,21},{6,11,77},{6,11,157},{7,11,974},{7,11,1301},{7,11,1339},{7,11,1490},{7 +,11,1873},{137,11,628},{7,11,1283},{9,11,227},{9,11,499},{10,11,341},{11,11,325} +,{11,11,408},{14,11,180},{15,11,144},{18,11,47},{147,11,49},{4,0,64},{5,0,352},{ +5,0,720},{6,0,368},{139,0,359},{5,10,384},{8,10,455},{140,10,48},{5,10,264},{134 +,10,184},{7,0,1577},{10,0,304},{10,0,549},{12,0,365},{13,0,220},{13,0,240},{142, +0,33},{134,0,1107},{134,0,929},{135,0,1142},{6,0,175},{137,0,289},{5,0,432},{133 +,0,913},{5,10,633},{6,0,279},{7,0,219},{135,10,1323},{7,0,785},{7,10,359},{8,10, +243},{140,10,175},{139,0,595},{132,10,105},{8,11,398},{9,11,681},{139,11,632},{ +140,0,80},{5,0,931},{134,0,1698},{142,11,241},{134,11,20},{134,0,1323},{11,0,526 +},{11,0,939},{141,0,290},{5,0,774},{6,0,780},{6,0,1637},{6,0,1686},{6,0,1751},{8 +,0,559},{141,0,109},{141,0,127},{7,0,1167},{7,11,709},{11,0,934},{13,0,391},{145 +,0,76},{135,0,963},{6,0,260},{135,0,1484},{134,0,573},{4,10,758},{139,11,941},{ +135,10,1649},{145,11,36},{4,0,292},{137,0,580},{4,0,736},{5,0,871},{6,0,1689},{ +135,0,1944},{7,11,945},{11,11,713},{139,11,744},{134,0,1164},{135,11,937},{6,0, +1922},{7,11,1652},{9,0,982},{15,0,173},{15,0,178},{15,0,200},{18,0,189},{18,0, +207},{149,0,47},{7,0,1695},{139,10,128},{6,0,63},{135,0,920},{133,0,793},{143,11 +,134},{133,10,918},{5,0,67},{6,0,62},{6,0,374},{135,0,1391},{4,11,579},{5,11,226 +},{5,11,323},{7,11,960},{9,0,790},{140,0,47},{10,11,784},{141,11,191},{4,0,391}, +{135,0,1169},{137,0,443},{13,11,232},{146,11,35},{132,10,340},{132,0,271},{137, +11,313},{5,11,973},{137,11,659},{134,0,1140},{6,11,135},{135,11,1176},{4,0,253}, +{5,0,544},{7,0,300},{137,0,340},{5,10,985},{7,0,897},{7,10,509},{145,10,96},{138 +,11,735},{135,10,1919},{138,0,890},{5,0,818},{134,0,1122},{5,0,53},{5,0,541},{6, +0,94},{6,0,499},{7,0,230},{139,0,321},{4,0,920},{5,0,25},{5,0,790},{6,0,457},{7, +0,853},{8,0,788},{142,11,31},{132,10,247},{135,11,314},{132,0,468},{6,10,337},{7 +,0,243},{7,10,494},{8,10,27},{8,10,599},{138,10,153},{4,10,184},{5,10,390},{7,10 +,618},{7,10,1456},{139,10,710},{134,0,870},{134,0,1238},{134,0,1765},{10,0,853}, +{10,0,943},{14,0,437},{14,0,439},{14,0,443},{14,0,446},{14,0,452},{14,0,469},{14 +,0,471},{14,0,473},{16,0,93},{16,0,102},{16,0,110},{148,0,121},{4,0,605},{7,0, +518},{7,0,1282},{7,0,1918},{10,0,180},{139,0,218},{133,0,822},{4,0,634},{11,0, +916},{142,0,419},{6,11,281},{7,11,6},{8,11,282},{8,11,480},{8,11,499},{9,11,198} +,{10,11,143},{10,11,169},{10,11,211},{10,11,417},{10,11,574},{11,11,147},{11,11, +395},{12,11,75},{12,11,407},{12,11,608},{13,11,500},{142,11,251},{134,0,898},{6, +0,36},{7,0,658},{8,0,454},{150,11,48},{133,11,674},{135,11,1776},{4,11,419},{10, +10,227},{11,10,497},{11,10,709},{140,10,415},{6,10,360},{7,10,1664},{136,10,478} +,{137,0,806},{12,11,508},{14,11,102},{14,11,226},{144,11,57},{135,11,1123},{4,11 +,138},{7,11,1012},{7,11,1280},{137,11,76},{5,11,29},{140,11,638},{136,10,699},{ +134,0,1326},{132,0,104},{135,11,735},{132,10,739},{134,0,1331},{7,0,260},{135,11 +,260},{135,11,1063},{7,0,45},{9,0,542},{9,0,566},{9,10,869},{138,0,728},{4,10,67 +},{5,10,422},{7,10,1037},{7,10,1289},{7,10,1555},{9,10,741},{145,10,108},{139,0, +263},{134,0,1516},{14,0,146},{15,0,42},{16,0,23},{17,0,86},{146,0,17},{138,0,468 +},{136,0,1005},{4,11,17},{5,11,23},{7,11,995},{11,11,383},{11,11,437},{12,11,460 +},{140,11,532},{7,0,87},{142,0,288},{138,10,96},{135,11,626},{144,10,26},{7,0, +988},{7,0,1939},{9,0,64},{9,0,502},{12,0,22},{12,0,34},{13,0,12},{13,0,234},{147 +,0,77},{8,10,203},{11,10,823},{11,10,846},{12,10,482},{13,0,133},{13,10,277},{13 +,10,302},{13,10,464},{14,10,205},{142,10,221},{4,10,449},{133,10,718},{135,0,141 +},{6,0,1842},{136,0,872},{8,11,70},{12,11,171},{141,11,272},{4,10,355},{6,10,311 +},{9,10,256},{138,10,404},{132,0,619},{137,0,261},{10,10,758},{10,11,233},{139, +11,76},{5,0,246},{8,0,189},{9,0,355},{9,0,512},{10,0,124},{10,0,453},{11,0,143}, +{11,0,416},{11,0,859},{141,0,341},{134,11,442},{133,10,827},{5,10,64},{140,10, +581},{4,10,442},{7,10,1047},{7,10,1352},{135,10,1643},{134,11,1709},{5,0,678},{5 +,10,977},{6,0,305},{7,0,775},{135,0,1065},{11,11,69},{12,11,105},{12,11,117},{13 +,11,213},{14,11,13},{14,11,62},{14,11,177},{14,11,421},{15,11,19},{146,11,141},{ +137,11,309},{5,0,35},{7,0,862},{7,0,1886},{138,0,179},{136,0,285},{132,0,517},{7 +,11,976},{9,11,146},{10,11,206},{10,11,596},{13,11,218},{142,11,153},{132,10,254 +},{4,10,275},{6,0,214},{7,10,1219},{12,0,540},{140,10,376},{8,0,667},{11,0,403}, +{146,0,83},{10,11,648},{11,11,671},{12,0,74},{143,11,46},{135,0,125},{134,10, +1753},{133,0,761},{4,11,518},{6,0,912},{6,10,369},{6,10,502},{7,10,1036},{7,11, +1136},{8,10,348},{9,10,452},{10,10,26},{11,10,224},{11,10,387},{11,10,772},{12, +10,95},{12,10,629},{13,10,195},{13,10,207},{13,10,241},{14,10,260},{14,10,270},{ +143,10,140},{10,0,131},{140,0,72},{132,10,269},{5,10,480},{7,10,532},{7,10,1197} +,{7,10,1358},{8,10,291},{11,10,349},{142,10,396},{8,11,689},{137,11,863},{8,0, +333},{138,0,182},{4,11,18},{7,11,145},{7,11,444},{7,11,1278},{8,11,49},{8,11,400 +},{9,11,71},{9,11,250},{10,11,459},{12,11,160},{144,11,24},{14,11,35},{142,11, +191},{135,11,1864},{135,0,1338},{148,10,15},{14,0,94},{15,0,65},{16,0,4},{16,0, +77},{16,0,80},{145,0,5},{12,11,82},{143,11,36},{133,11,1010},{133,0,449},{133,0, +646},{7,0,86},{7,10,657},{136,0,103},{7,0,2028},{138,0,641},{136,10,533},{134,0, +1},{139,11,970},{5,11,87},{7,11,313},{7,11,1103},{10,11,112},{10,11,582},{11,11, +389},{11,11,813},{12,11,385},{13,11,286},{14,11,124},{146,11,108},{4,11,267},{ +134,0,869},{6,0,277},{7,0,1274},{7,0,1386},{146,0,87},{6,0,187},{7,0,39},{7,0, +1203},{8,0,380},{14,0,117},{149,0,28},{4,10,211},{4,10,332},{5,10,335},{6,10,238 +},{7,10,269},{7,10,811},{7,10,1797},{8,10,836},{9,10,507},{141,10,242},{4,0,785} +,{5,0,368},{6,0,297},{7,0,793},{139,0,938},{7,0,464},{8,0,558},{11,0,105},{12,0, +231},{14,0,386},{15,0,102},{148,0,75},{133,10,1009},{8,0,877},{140,0,731},{139, +11,289},{10,11,249},{139,11,209},{132,11,561},{134,0,1608},{132,11,760},{134,0, +1429},{9,11,154},{140,11,485},{5,10,228},{6,10,203},{7,10,156},{8,10,347},{137, +10,265},{7,0,1010},{11,0,733},{11,0,759},{13,0,34},{14,0,427},{146,0,45},{7,10, +1131},{135,10,1468},{136,11,255},{7,0,1656},{9,0,369},{10,0,338},{10,0,490},{11, +0,154},{11,0,545},{11,0,775},{13,0,77},{141,0,274},{133,11,621},{134,0,1038},{4, +11,368},{135,11,641},{6,0,2010},{8,0,979},{8,0,985},{10,0,951},{138,0,1011},{134 +,0,1005},{5,10,291},{5,10,318},{7,10,765},{9,10,389},{12,10,548},{147,0,121},{5, +0,20},{6,0,298},{7,0,659},{137,0,219},{7,0,1440},{11,0,854},{11,0,872},{11,0,921 +},{12,0,551},{13,0,472},{142,0,367},{5,0,490},{6,0,615},{6,0,620},{135,0,683},{6 +,0,1070},{134,0,1597},{139,0,522},{132,0,439},{136,0,669},{6,0,766},{6,0,1143},{ +6,0,1245},{10,10,525},{139,10,82},{9,11,92},{147,11,91},{6,0,668},{134,0,1218},{ +6,11,525},{9,11,876},{140,11,284},{132,0,233},{136,0,547},{132,10,422},{5,10,355 +},{145,10,0},{6,11,300},{135,11,1515},{4,0,482},{137,10,905},{4,0,886},{5,11,594 +},{135,0,346},{133,10,865},{5,10,914},{134,10,1625},{135,0,334},{5,0,795},{5,10, +234},{134,0,1741},{135,10,1383},{6,11,1641},{136,11,820},{135,0,371},{7,11,1313} +,{138,11,660},{135,10,1312},{135,0,622},{7,0,625},{135,0,1750},{135,0,339},{4,0, +203},{135,0,1936},{15,0,29},{15,11,29},{16,0,38},{144,11,38},{5,0,338},{135,0, +1256},{135,10,1493},{6,10,421},{7,10,61},{7,10,1540},{10,0,130},{138,10,501},{6, +11,389},{7,11,149},{9,11,142},{138,11,94},{137,10,341},{11,0,678},{12,0,307},{ +142,10,98},{6,11,8},{7,11,1881},{136,11,91},{135,0,2044},{6,0,770},{6,0,802},{6, +0,812},{6,10,102},{7,0,311},{7,10,72},{9,0,308},{12,0,255},{15,10,142},{147,10, +67},{151,10,30},{135,10,823},{135,0,1266},{135,11,1746},{135,10,1870},{4,0,400}, +{5,0,267},{135,0,232},{7,11,24},{11,11,542},{139,11,852},{135,11,1739},{4,11,503 +},{135,11,1661},{5,11,130},{7,11,1314},{9,11,610},{10,11,718},{11,11,601},{11,11 +,819},{11,11,946},{140,11,536},{10,11,149},{11,11,280},{142,11,336},{7,0,739},{7 +,11,1946},{8,10,48},{8,10,88},{8,10,582},{8,10,681},{9,10,373},{9,10,864},{11,0, +690},{11,10,157},{11,10,843},{148,10,27},{134,0,990},{4,10,88},{5,10,137},{5,10, +174},{5,10,777},{6,10,1664},{6,10,1725},{7,10,77},{7,10,426},{7,10,1317},{7,10, +1355},{8,10,126},{8,10,563},{9,10,523},{9,10,750},{10,10,310},{10,10,836},{11,10 +,42},{11,10,318},{11,10,731},{12,10,68},{12,10,92},{12,10,507},{12,10,692},{13, +10,81},{13,10,238},{13,10,374},{14,10,436},{18,10,138},{19,10,78},{19,10,111},{ +20,10,55},{20,10,77},{148,10,92},{141,10,418},{4,10,938},{135,0,1831},{6,0,776}, +{134,0,915},{138,10,351},{5,11,348},{6,10,1668},{6,11,522},{7,10,1499},{8,10,117 +},{9,10,314},{138,10,174},{135,10,707},{132,0,613},{133,10,403},{132,11,392},{5, +11,433},{9,11,633},{139,11,629},{133,0,763},{132,0,878},{132,0,977},{132,0,100}, +{4,10,44},{5,10,311},{6,0,463},{7,10,639},{7,10,762},{7,10,1827},{9,10,8},{9,10, +462},{148,10,83},{134,11,234},{4,10,346},{7,10,115},{9,10,180},{9,10,456},{138, +10,363},{5,0,362},{5,0,443},{6,0,318},{7,0,1019},{139,0,623},{5,0,463},{7,11,140 +},{7,11,1950},{8,0,296},{8,11,680},{11,11,817},{147,11,88},{7,11,1222},{138,11, +386},{142,0,137},{132,0,454},{6,11,5},{7,0,1914},{7,10,1051},{9,10,545},{11,11, +249},{12,11,313},{16,11,66},{145,11,26},{135,0,1527},{145,0,58},{148,11,59},{5,0 +,48},{5,0,404},{6,0,557},{7,0,458},{8,0,597},{10,0,455},{10,0,606},{11,0,49},{11 +,0,548},{12,0,476},{13,0,18},{141,0,450},{5,11,963},{134,11,1773},{133,0,729},{ +138,11,586},{5,0,442},{135,0,1984},{134,0,449},{144,0,40},{4,0,853},{7,11,180},{ +8,11,509},{136,11,792},{6,10,185},{7,10,1899},{9,10,875},{139,10,673},{134,11, +524},{4,10,327},{5,10,478},{7,10,1332},{8,10,753},{140,0,227},{5,10,1020},{5,10, +1022},{134,0,1491},{4,10,103},{133,10,401},{132,11,931},{4,10,499},{135,10,1421} +,{5,0,55},{7,0,376},{140,0,161},{133,0,450},{6,0,1174},{134,0,1562},{7,11,1837}, +{10,0,62},{141,0,400},{140,0,207},{135,0,869},{4,11,773},{5,11,618},{137,11,756} +,{132,10,96},{4,0,213},{7,0,223},{7,10,968},{136,0,80},{4,11,90},{5,11,337},{5, +11,545},{7,11,754},{9,11,186},{10,11,72},{10,11,782},{11,11,513},{11,11,577},{11 +,11,610},{11,11,889},{11,11,961},{12,11,354},{12,11,362},{12,11,461},{12,11,595} +,{13,11,79},{143,11,121},{7,0,381},{7,0,806},{7,0,820},{8,0,354},{8,0,437},{8,0, +787},{9,0,657},{10,0,58},{10,0,339},{10,0,749},{11,0,914},{12,0,162},{13,0,75},{ +14,0,106},{14,0,198},{14,0,320},{14,0,413},{146,0,43},{136,0,747},{136,0,954},{ +134,0,1073},{135,0,556},{7,11,151},{9,11,329},{139,11,254},{5,0,692},{134,0,1395 +},{6,10,563},{137,10,224},{134,0,191},{132,0,804},{9,11,187},{10,11,36},{17,11, +44},{146,11,64},{7,11,165},{7,11,919},{136,11,517},{4,11,506},{5,11,295},{7,11, +1680},{15,11,14},{144,11,5},{4,0,706},{6,0,162},{7,0,1960},{136,0,831},{135,11, +1376},{7,11,987},{9,11,688},{10,11,522},{11,11,788},{140,11,566},{150,0,35},{138 +,0,426},{135,0,1235},{135,11,1741},{7,11,389},{7,11,700},{7,11,940},{8,11,514},{ +9,11,116},{9,11,535},{10,11,118},{11,11,107},{11,11,148},{11,11,922},{12,11,254} +,{12,11,421},{142,11,238},{134,0,1234},{132,11,743},{4,10,910},{5,10,832},{135, +11,1335},{141,0,96},{135,11,185},{146,0,149},{4,0,204},{137,0,902},{4,11,784},{ +133,11,745},{136,0,833},{136,0,949},{5,11,81},{7,0,366},{7,11,146},{7,11,1342},{ +7,11,1446},{8,11,53},{8,11,561},{8,11,694},{8,11,754},{9,0,287},{9,11,97},{9,11, +115},{9,11,894},{10,11,462},{10,11,813},{11,11,230},{11,11,657},{11,11,699},{11, +11,748},{12,0,199},{12,0,556},{12,0,577},{12,11,119},{12,11,200},{12,11,283},{14 +,11,273},{145,11,15},{5,11,408},{137,11,747},{9,11,498},{140,11,181},{6,0,2020}, +{136,0,992},{5,0,356},{135,0,224},{134,0,784},{7,0,630},{8,10,528},{9,0,567},{9, +10,348},{11,0,150},{11,0,444},{141,0,119},{134,0,539},{4,10,20},{133,10,616},{ +142,0,27},{7,11,30},{8,11,86},{8,11,315},{8,11,700},{9,11,576},{9,11,858},{11,11 +,310},{11,11,888},{11,11,904},{12,11,361},{141,11,248},{138,11,839},{134,0,755}, +{134,0,1063},{7,10,1091},{135,10,1765},{134,11,428},{7,11,524},{8,11,169},{8,11, +234},{9,11,480},{138,11,646},{139,0,814},{7,11,1462},{139,11,659},{4,10,26},{5, +10,429},{6,10,245},{7,10,704},{7,10,1379},{135,10,1474},{7,11,1205},{138,11,637} +,{139,11,803},{132,10,621},{136,0,987},{4,11,266},{8,11,4},{9,11,39},{10,11,166} +,{11,11,918},{12,11,635},{20,11,10},{22,11,27},{150,11,43},{4,0,235},{135,0,255} +,{4,0,194},{5,0,584},{6,0,384},{7,0,583},{10,0,761},{11,0,760},{139,0,851},{133, +10,542},{134,0,1086},{133,10,868},{8,0,1016},{136,0,1018},{7,0,1396},{7,11,1396} +,{136,10,433},{135,10,1495},{138,10,215},{141,10,124},{7,11,157},{8,11,279},{9, +11,759},{16,11,31},{16,11,39},{16,11,75},{18,11,24},{20,11,42},{152,11,1},{5,0, +562},{134,11,604},{134,0,913},{5,0,191},{137,0,271},{4,0,470},{6,0,153},{7,0, +1503},{7,0,1923},{10,0,701},{11,0,132},{11,0,227},{11,0,320},{11,0,436},{11,0, +525},{11,0,855},{11,0,873},{12,0,41},{12,0,286},{13,0,103},{13,0,284},{14,0,255} +,{14,0,262},{15,0,117},{143,0,127},{7,0,475},{12,0,45},{147,10,112},{132,11,567} +,{137,11,859},{6,0,713},{6,0,969},{6,0,1290},{134,0,1551},{133,0,327},{6,0,552}, +{6,0,1292},{7,0,1754},{137,0,604},{4,0,223},{5,11,762},{6,0,359},{7,11,1880},{9, +11,680},{11,0,3},{11,11,798},{13,0,108},{14,0,89},{144,0,22},{5,0,80},{6,0,405}, +{7,0,403},{7,0,1502},{8,0,456},{9,0,487},{9,0,853},{9,0,889},{10,0,309},{11,0, +721},{11,0,994},{12,0,430},{141,0,165},{133,11,298},{132,10,647},{134,0,2016},{ +18,10,10},{146,11,10},{4,0,453},{5,0,887},{6,0,535},{8,0,6},{8,0,543},{136,0,826 +},{136,0,975},{10,0,961},{138,0,962},{138,10,220},{6,0,1891},{6,0,1893},{9,0,916 +},{9,0,965},{9,0,972},{12,0,801},{12,0,859},{12,0,883},{15,0,226},{149,0,51},{ +132,10,109},{135,11,267},{7,11,92},{7,11,182},{8,11,453},{9,11,204},{11,11,950}, +{12,11,94},{12,11,644},{16,11,20},{16,11,70},{16,11,90},{147,11,55},{134,10,1746 +},{6,11,71},{7,11,845},{7,11,1308},{8,11,160},{137,11,318},{5,0,101},{6,0,88},{7 +,0,263},{7,0,628},{7,0,1677},{7,11,237},{8,0,349},{8,11,664},{9,0,100},{9,11,42} +,{9,11,266},{9,11,380},{9,11,645},{10,0,677},{10,11,177},{10,11,276},{14,0,169}, +{14,0,302},{14,0,313},{15,0,48},{143,0,84},{138,11,69},{4,0,310},{7,0,708},{7,0, +996},{9,0,795},{10,0,390},{10,0,733},{11,0,451},{12,0,249},{14,0,115},{14,0,286} +,{143,0,100},{4,10,40},{5,0,587},{10,10,67},{11,10,117},{11,10,768},{139,10,935} +,{6,0,1942},{7,0,512},{136,0,983},{7,10,992},{8,10,301},{9,10,722},{12,10,63},{ +13,10,29},{14,10,161},{143,10,18},{136,11,76},{139,10,923},{134,0,645},{134,0, +851},{4,0,498},{132,11,293},{7,0,217},{8,0,140},{10,0,610},{14,11,352},{17,11,53 +},{18,11,146},{18,11,152},{19,11,11},{150,11,54},{134,0,1448},{138,11,841},{133, +0,905},{4,11,605},{7,11,518},{7,11,1282},{7,11,1918},{10,11,180},{139,11,218},{ +139,11,917},{135,10,825},{140,10,328},{4,0,456},{7,0,105},{7,0,358},{7,0,1637},{ +8,0,643},{139,0,483},{134,0,792},{6,11,96},{135,11,1426},{137,11,691},{4,11,651} +,{133,11,289},{7,11,688},{8,11,35},{9,11,511},{10,11,767},{147,11,118},{150,0,56 +},{5,0,243},{5,0,535},{6,10,204},{10,10,320},{10,10,583},{13,10,502},{14,10,72}, +{14,10,274},{14,10,312},{14,10,344},{15,10,159},{16,10,62},{16,10,69},{17,10,30} +,{18,10,42},{18,10,53},{18,10,84},{18,10,140},{19,10,68},{19,10,85},{20,10,5},{ +20,10,45},{20,10,101},{22,10,7},{150,10,20},{4,10,558},{6,10,390},{7,10,162},{7, +10,689},{9,10,360},{138,10,653},{146,11,23},{135,0,1748},{5,10,856},{6,10,1672}, +{6,10,1757},{134,10,1781},{4,11,704},{5,0,539},{5,0,754},{134,0,876},{135,11, +1078},{5,10,92},{10,10,736},{140,10,102},{5,10,590},{9,10,213},{145,0,91},{134,0 +,1565},{6,0,91},{135,0,435},{4,0,939},{140,0,792},{134,0,1399},{4,0,16},{4,11, +720},{5,0,316},{5,0,842},{5,11,306},{6,0,370},{6,0,1778},{8,0,166},{11,0,812},{ +12,0,206},{12,0,351},{14,0,418},{16,0,15},{16,0,34},{18,0,3},{19,0,3},{19,0,7},{ +20,0,4},{149,0,21},{144,0,95},{133,11,431},{132,11,234},{135,0,551},{4,0,999},{6 +,0,1966},{134,0,2042},{7,0,619},{10,0,547},{11,0,122},{12,0,601},{15,0,7},{148,0 +,20},{5,11,464},{6,11,236},{7,11,276},{7,11,696},{7,11,914},{7,11,1108},{7,11, +1448},{9,11,15},{9,11,564},{10,11,14},{12,11,565},{13,11,449},{14,11,53},{15,11, +13},{16,11,64},{145,11,41},{6,0,884},{6,0,1019},{134,0,1150},{6,11,1767},{12,11, +194},{145,11,107},{136,10,503},{133,11,840},{6,10,466},{135,0,671},{132,0,888},{ +4,0,149},{138,0,368},{4,0,154},{7,0,1134},{136,0,105},{135,0,983},{9,11,642},{11 +,11,236},{142,11,193},{4,0,31},{6,0,429},{7,0,962},{9,0,458},{139,0,691},{6,0, +643},{134,0,1102},{132,0,312},{4,11,68},{5,11,634},{6,11,386},{7,11,794},{8,11, +273},{9,11,563},{10,11,105},{10,11,171},{11,11,94},{139,11,354},{133,0,740},{135 +,0,1642},{4,11,95},{7,11,416},{8,11,211},{139,11,830},{132,0,236},{138,10,241},{ +7,11,731},{13,11,20},{143,11,11},{5,0,836},{5,0,857},{6,0,1680},{135,0,59},{10,0 +,68},{11,0,494},{152,11,6},{4,0,81},{139,0,867},{135,0,795},{133,11,689},{4,0, +1001},{5,0,282},{6,0,1932},{6,0,1977},{6,0,1987},{6,0,1992},{8,0,650},{8,0,919}, +{8,0,920},{8,0,923},{8,0,926},{8,0,927},{8,0,931},{8,0,939},{8,0,947},{8,0,956}, +{8,0,997},{9,0,907},{10,0,950},{10,0,953},{10,0,954},{10,0,956},{10,0,958},{10,0 +,959},{10,0,964},{10,0,970},{10,0,972},{10,0,973},{10,0,975},{10,0,976},{10,0, +980},{10,0,981},{10,0,984},{10,0,988},{10,0,990},{10,0,995},{10,0,999},{10,0, +1002},{10,0,1003},{10,0,1005},{10,0,1006},{10,0,1008},{10,0,1009},{10,0,1012},{ +10,0,1014},{10,0,1015},{10,0,1019},{10,0,1020},{10,0,1022},{12,0,959},{12,0,961} +,{12,0,962},{12,0,963},{12,0,964},{12,0,965},{12,0,967},{12,0,968},{12,0,969},{ +12,0,970},{12,0,971},{12,0,972},{12,0,973},{12,0,974},{12,0,975},{12,0,976},{12, +0,977},{12,0,979},{12,0,981},{12,0,982},{12,0,983},{12,0,984},{12,0,985},{12,0, +986},{12,0,987},{12,0,989},{12,0,990},{12,0,992},{12,0,993},{12,0,995},{12,0,998 +},{12,0,999},{12,0,1000},{12,0,1001},{12,0,1002},{12,0,1004},{12,0,1005},{12,0, +1006},{12,0,1007},{12,0,1008},{12,0,1009},{12,0,1010},{12,0,1011},{12,0,1012},{ +12,0,1014},{12,0,1015},{12,0,1016},{12,0,1017},{12,0,1018},{12,0,1019},{12,0, +1022},{12,0,1023},{14,0,475},{14,0,477},{14,0,478},{14,0,479},{14,0,480},{14,0, +482},{14,0,483},{14,0,484},{14,0,485},{14,0,486},{14,0,487},{14,0,488},{14,0,489 +},{14,0,490},{14,0,491},{14,0,492},{14,0,493},{14,0,494},{14,0,495},{14,0,496},{ +14,0,497},{14,0,498},{14,0,499},{14,0,500},{14,0,501},{14,0,502},{14,0,503},{14, +0,504},{14,0,506},{14,0,507},{14,0,508},{14,0,509},{14,0,510},{14,0,511},{16,0, +113},{16,0,114},{16,0,115},{16,0,117},{16,0,118},{16,0,119},{16,0,121},{16,0,122 +},{16,0,123},{16,0,124},{16,0,125},{16,0,126},{16,0,127},{18,0,242},{18,0,243},{ +18,0,244},{18,0,245},{18,0,248},{18,0,249},{18,0,250},{18,0,251},{18,0,252},{18, +0,253},{18,0,254},{18,0,255},{20,0,125},{20,0,126},{148,0,127},{7,11,1717},{7,11 +,1769},{138,11,546},{7,11,1127},{7,11,1572},{10,11,297},{10,11,422},{11,11,764}, +{11,11,810},{12,11,264},{13,11,102},{13,11,300},{13,11,484},{14,11,147},{14,11, +229},{17,11,71},{18,11,118},{147,11,120},{6,0,1148},{134,0,1586},{132,0,775},{ +135,10,954},{133,11,864},{133,11,928},{138,11,189},{135,10,1958},{6,10,549},{8, +10,34},{8,10,283},{9,10,165},{138,10,475},{5,10,652},{5,10,701},{135,10,449},{ +135,11,695},{4,10,655},{7,10,850},{17,10,75},{146,10,137},{140,11,682},{133,11, +523},{8,0,970},{136,10,670},{136,11,555},{7,11,76},{8,11,44},{9,11,884},{10,11, +580},{11,11,399},{11,11,894},{15,11,122},{18,11,144},{147,11,61},{6,10,159},{6, +10,364},{7,10,516},{7,10,1439},{137,10,518},{4,0,71},{5,0,376},{7,0,119},{138,0, +665},{141,10,151},{11,0,827},{14,0,34},{143,0,148},{133,11,518},{4,0,479},{135, +11,1787},{135,11,1852},{135,10,993},{7,0,607},{136,0,99},{134,0,1960},{132,0,793 +},{4,0,41},{5,0,74},{7,0,1627},{11,0,871},{140,0,619},{7,0,94},{11,0,329},{11,0, +965},{12,0,241},{14,0,354},{15,0,22},{148,0,63},{7,10,501},{9,10,111},{10,10,141 +},{11,10,332},{13,10,43},{13,10,429},{14,10,130},{14,10,415},{145,10,102},{9,0, +209},{137,0,300},{134,0,1497},{138,11,255},{4,11,934},{5,11,138},{136,11,610},{ +133,0,98},{6,0,1316},{10,11,804},{138,11,832},{8,11,96},{9,11,36},{10,11,607},{ +11,11,423},{11,11,442},{12,11,309},{14,11,199},{15,11,90},{145,11,110},{132,0, +463},{5,10,149},{136,10,233},{133,10,935},{4,11,652},{8,11,320},{9,11,13},{9,11, +398},{9,11,727},{10,11,75},{10,11,184},{10,11,230},{10,11,564},{10,11,569},{11, +11,973},{12,11,70},{12,11,189},{13,11,57},{13,11,257},{22,11,6},{150,11,16},{142 +,0,291},{12,10,582},{146,10,131},{136,10,801},{133,0,984},{145,11,116},{4,11,692 +},{133,11,321},{4,0,182},{6,0,205},{135,0,220},{4,0,42},{9,0,205},{9,0,786},{138 +,0,659},{6,0,801},{11,11,130},{140,11,609},{132,0,635},{5,11,345},{135,11,1016}, +{139,0,533},{132,0,371},{4,0,272},{135,0,836},{6,0,1282},{135,11,1100},{5,0,825} +,{134,0,1640},{135,11,1325},{133,11,673},{4,11,287},{133,11,1018},{135,0,357},{6 +,0,467},{137,0,879},{7,0,317},{135,0,569},{6,0,924},{134,0,1588},{5,10,406},{5, +11,34},{10,11,724},{12,11,444},{13,11,354},{18,11,32},{23,11,24},{23,11,31},{152 +,11,5},{6,0,1795},{6,0,1835},{6,0,1836},{6,0,1856},{8,0,844},{8,0,849},{8,0,854} +,{8,0,870},{8,0,887},{10,0,852},{138,0,942},{6,10,69},{135,10,117},{137,0,307},{ +4,0,944},{6,0,1799},{6,0,1825},{10,0,848},{10,0,875},{10,0,895},{10,0,899},{10,0 +,902},{140,0,773},{11,0,43},{13,0,72},{141,0,142},{135,10,1830},{134,11,382},{4, +10,432},{135,10,824},{132,11,329},{7,0,1820},{139,11,124},{133,10,826},{133,0, +525},{132,11,906},{7,11,1940},{136,11,366},{138,11,10},{4,11,123},{4,11,649},{5, +11,605},{7,11,1509},{136,11,36},{6,0,110},{135,0,1681},{133,0,493},{133,11,767}, +{4,0,174},{135,0,911},{138,11,786},{8,0,417},{137,0,782},{133,10,1000},{7,0,733} +,{137,0,583},{4,10,297},{6,10,529},{7,10,152},{7,10,713},{7,10,1845},{8,10,710}, +{8,10,717},{12,10,639},{140,10,685},{4,0,32},{5,0,215},{6,0,269},{7,0,1782},{7,0 +,1892},{10,0,16},{11,0,822},{11,0,954},{141,0,481},{4,11,273},{5,11,658},{133,11 +,995},{136,0,477},{134,11,72},{135,11,1345},{4,10,520},{5,0,308},{7,0,1088},{135 +,10,575},{133,11,589},{5,0,126},{8,0,297},{9,0,366},{140,0,374},{7,0,1551},{139, +0,361},{5,11,117},{6,11,514},{6,11,541},{7,11,1164},{7,11,1436},{8,11,220},{8,11 +,648},{10,11,688},{139,11,560},{133,11,686},{4,0,946},{6,0,1807},{8,0,871},{10,0 +,854},{10,0,870},{10,0,888},{10,0,897},{10,0,920},{12,0,722},{12,0,761},{12,0, +763},{12,0,764},{14,0,454},{14,0,465},{16,0,107},{18,0,167},{18,0,168},{146,0, +172},{132,0,175},{135,0,1307},{132,0,685},{135,11,1834},{133,0,797},{6,0,745},{6 +,0,858},{134,0,963},{133,0,565},{5,10,397},{6,10,154},{7,10,676},{7,11,196},{8, +10,443},{8,10,609},{9,10,24},{9,10,325},{10,10,35},{10,11,765},{11,10,535},{11, +10,672},{11,10,1018},{11,11,347},{11,11,552},{11,11,576},{11,11,790},{12,10,637} +,{12,11,263},{13,11,246},{13,11,270},{13,11,395},{14,11,74},{14,11,176},{14,11, +190},{14,11,398},{14,11,412},{15,11,32},{15,11,63},{16,10,30},{16,11,88},{147,11 +,105},{13,11,84},{141,11,122},{4,0,252},{7,0,1068},{10,0,434},{11,0,228},{11,0, +426},{13,0,231},{18,0,106},{148,0,87},{137,0,826},{4,11,589},{139,11,282},{5,11, +381},{135,11,1792},{132,0,791},{5,0,231},{5,10,981},{138,0,509},{7,0,601},{9,0, +277},{9,0,674},{10,0,178},{10,0,418},{10,0,571},{11,0,531},{12,0,113},{12,0,475} +,{13,0,99},{142,0,428},{4,10,56},{7,10,1791},{7,11,616},{8,10,607},{8,10,651},{ +10,11,413},{11,10,465},{11,10,835},{12,10,337},{141,10,480},{7,0,1591},{144,0,43 +},{9,10,158},{138,10,411},{135,0,1683},{8,0,289},{11,0,45},{12,0,278},{140,0,537 +},{6,11,120},{7,11,1188},{7,11,1710},{8,11,286},{9,11,667},{11,11,592},{139,11, +730},{136,10,617},{135,0,1120},{135,11,1146},{139,10,563},{4,10,369},{4,11,352}, +{135,11,687},{143,11,38},{4,0,399},{5,0,119},{5,0,494},{7,0,751},{9,0,556},{14, +11,179},{15,11,151},{150,11,11},{4,11,192},{5,11,49},{6,11,200},{6,11,293},{6,11 +,1696},{135,11,488},{4,0,398},{133,0,660},{6,10,622},{135,0,1030},{135,11,595},{ +141,0,168},{132,11,147},{7,0,973},{10,10,624},{142,10,279},{132,10,363},{132,0, +642},{133,11,934},{134,0,1615},{7,11,505},{135,11,523},{7,0,594},{7,0,851},{7,0, +1858},{9,0,411},{9,0,574},{9,0,666},{9,0,737},{10,0,346},{10,0,712},{11,0,246},{ +11,0,432},{11,0,517},{11,0,647},{11,0,679},{11,0,727},{12,0,304},{12,0,305},{12, +0,323},{12,0,483},{12,0,572},{12,0,593},{12,0,602},{13,0,95},{13,0,101},{13,0, +171},{13,0,315},{13,0,378},{13,0,425},{13,0,475},{14,0,63},{14,0,380},{14,0,384} +,{15,0,133},{18,0,112},{148,0,72},{135,0,1093},{132,0,679},{8,0,913},{10,0,903}, +{10,0,915},{10,11,438},{12,0,648},{12,0,649},{14,0,455},{144,0,112},{137,0,203}, +{134,10,292},{134,0,1492},{5,10,177},{6,10,616},{7,0,1374},{7,10,827},{8,0,540}, +{9,10,525},{138,10,656},{135,0,1486},{9,0,714},{138,10,31},{136,0,825},{134,0, +1511},{132,11,637},{134,0,952},{4,10,161},{133,10,631},{5,0,143},{5,0,769},{6,0, +1760},{7,0,682},{7,0,1992},{136,0,736},{132,0,700},{134,0,1540},{132,11,777},{9, +11,867},{138,11,837},{7,0,1557},{135,10,1684},{133,0,860},{6,0,422},{7,0,0},{7,0 +,1544},{9,0,605},{9,10,469},{9,10,709},{11,0,990},{12,0,235},{12,0,453},{12,10, +512},{13,0,47},{13,0,266},{14,10,65},{145,10,12},{10,10,229},{11,0,807},{11,10, +73},{139,10,376},{6,11,170},{7,11,1080},{8,11,395},{8,11,487},{11,11,125},{141, +11,147},{5,0,515},{137,0,131},{7,0,1605},{11,0,962},{146,0,139},{132,0,646},{4,0 +,396},{7,0,728},{9,0,117},{13,0,202},{148,0,51},{4,11,535},{6,0,121},{6,0,124},{ +6,0,357},{6,10,558},{7,0,1138},{7,0,1295},{7,10,651},{8,0,162},{8,0,508},{8,11, +618},{9,10,0},{10,10,34},{11,0,655},{139,10,1008},{135,11,1245},{138,0,357},{150 +,11,23},{133,0,237},{135,0,1784},{7,10,1832},{138,10,374},{132,0,713},{132,11,46 +},{5,11,811},{6,0,1536},{6,11,1679},{6,11,1714},{7,11,2032},{138,0,348},{11,11, +182},{142,11,195},{6,0,523},{7,0,738},{7,10,771},{7,10,1731},{9,10,405},{138,10, +421},{7,11,1458},{9,11,407},{139,11,15},{6,11,34},{7,11,69},{7,11,640},{7,11, +1089},{8,11,708},{8,11,721},{9,11,363},{9,11,643},{10,11,628},{148,11,98},{133,0 +,434},{135,0,1877},{7,0,571},{138,0,366},{5,10,881},{133,10,885},{9,0,513},{10,0 +,25},{10,0,39},{12,0,122},{140,0,187},{132,0,580},{5,10,142},{134,10,546},{132, +11,462},{137,0,873},{5,10,466},{11,10,571},{12,10,198},{13,10,283},{14,10,186},{ +15,10,21},{143,10,103},{4,10,185},{5,10,257},{5,10,839},{5,10,936},{7,0,171},{9, +10,399},{10,10,258},{10,10,395},{10,10,734},{11,10,1014},{12,10,23},{13,10,350}, +{14,10,150},{147,10,6},{134,0,625},{7,0,107},{7,0,838},{8,0,550},{138,0,401},{5, +11,73},{6,11,23},{134,11,338},{4,0,943},{6,0,1850},{12,0,713},{142,0,434},{7,10, +404},{7,10,1377},{7,10,1430},{7,10,2017},{8,10,149},{8,10,239},{8,10,512},{8,10, +793},{8,10,818},{9,10,474},{9,10,595},{10,10,122},{10,10,565},{10,10,649},{10,10 +,783},{11,0,588},{11,0,864},{11,0,936},{11,0,968},{11,10,239},{11,10,295},{11,10 +,447},{11,10,528},{11,10,639},{11,10,800},{12,0,73},{12,0,343},{12,0,394},{12,10 +,25},{12,10,157},{12,10,316},{12,10,390},{12,10,391},{12,10,395},{12,10,478},{12 +,10,503},{12,10,592},{12,10,680},{13,0,275},{13,10,50},{13,10,53},{13,10,132},{ +13,10,198},{13,10,322},{13,10,415},{13,10,511},{14,0,257},{14,10,71},{14,10,395} +,{15,0,160},{15,10,71},{15,10,136},{17,10,123},{18,10,93},{147,10,58},{133,0,768 +},{11,0,103},{142,0,0},{136,10,712},{132,0,799},{132,0,894},{7,11,725},{8,11,498 +},{139,11,268},{135,11,1798},{135,11,773},{141,11,360},{4,10,377},{152,10,13},{ +135,0,1673},{132,11,583},{134,0,1052},{133,11,220},{140,11,69},{132,11,544},{4, +10,180},{135,10,1906},{134,0,272},{4,0,441},{134,0,1421},{4,0,9},{5,0,128},{7,0, +368},{11,0,480},{148,0,3},{5,11,176},{6,11,437},{6,11,564},{11,11,181},{141,11, +183},{132,10,491},{7,0,1182},{141,11,67},{4,10,171},{6,0,1346},{138,10,234},{4, +10,586},{7,10,1186},{138,10,631},{136,0,682},{134,0,1004},{15,0,24},{143,11,24}, +{134,0,968},{4,0,2},{6,0,742},{6,0,793},{7,0,545},{7,0,894},{9,10,931},{10,10, +334},{148,10,71},{136,11,600},{133,10,765},{9,0,769},{140,0,185},{4,11,790},{5, +11,273},{134,11,394},{7,0,474},{137,0,578},{4,11,135},{6,11,127},{7,11,1185},{7, +11,1511},{8,11,613},{11,11,5},{12,11,133},{12,11,495},{12,11,586},{14,11,385},{ +15,11,118},{17,11,20},{146,11,98},{133,10,424},{5,0,530},{142,0,113},{6,11,230}, +{7,11,961},{7,11,1085},{136,11,462},{7,11,1954},{137,11,636},{136,10,714},{149, +11,6},{135,10,685},{9,10,420},{10,10,269},{10,10,285},{10,10,576},{11,10,397},{ +13,10,175},{145,10,90},{132,10,429},{5,0,556},{5,11,162},{136,11,68},{132,11,654 +},{4,11,156},{7,11,998},{7,11,1045},{7,11,1860},{9,11,48},{9,11,692},{11,11,419} +,{139,11,602},{6,0,1317},{7,11,1276},{8,0,16},{8,11,474},{9,0,825},{9,11,652},{ +140,0,568},{7,10,18},{7,10,699},{7,10,1966},{8,10,752},{9,10,273},{9,10,412},{9, +10,703},{10,10,71},{10,10,427},{10,10,508},{146,0,97},{7,11,1454},{10,0,703},{ +138,11,703},{4,10,53},{5,10,186},{135,10,752},{134,0,892},{134,0,1571},{8,10,575 +},{10,10,289},{139,10,319},{6,0,186},{137,0,426},{134,0,1101},{132,10,675},{132, +0,585},{6,0,1870},{137,0,937},{152,11,10},{9,11,197},{10,11,300},{12,11,473},{13 +,11,90},{141,11,405},{4,0,93},{5,0,252},{6,0,229},{7,0,291},{9,0,550},{139,0,644 +},{137,0,749},{6,10,209},{8,10,468},{9,0,162},{9,10,210},{11,10,36},{12,10,28},{ +12,10,630},{13,10,21},{13,10,349},{14,10,7},{145,10,13},{132,0,381},{132,11,606} +,{4,10,342},{135,10,1179},{7,11,1587},{7,11,1707},{10,11,528},{139,11,504},{12, +11,39},{13,11,265},{141,11,439},{4,10,928},{133,10,910},{7,10,1838},{7,11,1978}, +{136,11,676},{6,0,762},{6,0,796},{134,0,956},{4,10,318},{4,10,496},{7,10,856},{ +139,10,654},{137,11,242},{4,11,361},{133,11,315},{132,11,461},{132,11,472},{132, +0,857},{5,0,21},{6,0,77},{6,0,157},{7,0,974},{7,0,1301},{7,0,1339},{7,0,1490},{7 +,0,1873},{7,10,915},{8,10,247},{9,0,628},{147,10,0},{4,10,202},{5,10,382},{6,10, +454},{7,10,936},{7,10,1803},{8,10,758},{9,10,375},{9,10,895},{10,10,743},{10,10, +792},{11,10,978},{11,10,1012},{142,10,109},{7,11,617},{10,11,498},{11,11,501},{ +12,11,16},{140,11,150},{7,10,1150},{7,10,1425},{7,10,1453},{10,11,747},{140,10, +513},{133,11,155},{11,0,919},{141,0,409},{138,10,791},{10,0,633},{139,11,729},{7 +,11,163},{8,11,319},{9,11,402},{10,11,24},{10,11,681},{11,11,200},{11,11,567},{ +12,11,253},{12,11,410},{142,11,219},{5,11,475},{7,11,1780},{9,11,230},{11,11,297 +},{11,11,558},{14,11,322},{147,11,76},{6,10,445},{7,0,332},{137,10,909},{135,11, +1956},{136,11,274},{134,10,578},{135,0,1489},{135,11,1848},{5,11,944},{134,11, +1769},{132,11,144},{136,10,766},{4,0,832},{135,10,541},{8,0,398},{9,0,681},{139, +0,632},{136,0,645},{9,0,791},{10,0,93},{16,0,13},{17,0,23},{18,0,135},{19,0,12}, +{20,0,1},{20,0,12},{148,0,14},{6,11,247},{137,11,555},{134,0,20},{132,0,800},{ +135,0,1841},{139,10,983},{137,10,768},{132,10,584},{141,11,51},{4,11,620},{6,0, +1993},{138,11,280},{136,0,769},{7,11,1810},{11,0,290},{11,0,665},{11,11,866},{12 +,11,103},{13,11,495},{17,11,67},{147,11,74},{134,0,1426},{139,0,60},{4,10,326},{ +135,10,1770},{4,10,226},{7,0,1874},{137,0,641},{5,10,426},{6,0,644},{8,10,30},{9 +,10,2},{11,10,549},{147,10,122},{5,11,428},{138,11,442},{135,11,1871},{135,0, +1757},{147,10,117},{135,0,937},{135,0,1652},{6,0,654},{134,0,1476},{133,11,99},{ +135,0,527},{132,10,345},{4,10,385},{4,11,397},{7,10,265},{135,10,587},{4,0,579}, +{5,0,226},{5,0,323},{135,0,960},{134,0,1486},{8,11,502},{144,11,9},{4,10,347},{5 +,10,423},{5,10,996},{135,10,1329},{7,11,727},{146,11,73},{4,11,485},{7,10,1259}, +{7,11,353},{7,11,1523},{9,10,125},{139,10,65},{5,10,136},{6,0,325},{6,11,366},{7 +,11,1384},{7,11,1601},{136,10,644},{138,11,160},{6,0,1345},{137,11,282},{18,0,91 +},{147,0,70},{136,0,404},{4,11,157},{133,11,471},{133,0,973},{6,0,135},{135,0, +1176},{8,11,116},{11,11,551},{142,11,159},{4,0,549},{4,10,433},{133,10,719},{136 +,0,976},{5,11,160},{7,11,363},{7,11,589},{10,11,170},{141,11,55},{144,0,21},{144 +,0,51},{135,0,314},{135,10,1363},{4,11,108},{7,11,405},{10,11,491},{139,11,498}, +{146,0,4},{4,10,555},{8,10,536},{10,10,288},{139,10,1005},{135,11,1005},{6,0,281 +},{7,0,6},{8,0,282},{8,0,480},{8,0,499},{9,0,198},{10,0,143},{10,0,169},{10,0, +211},{10,0,417},{10,0,574},{11,0,147},{11,0,395},{12,0,75},{12,0,407},{12,0,608} +,{13,0,500},{142,0,251},{6,0,1093},{6,0,1405},{9,10,370},{138,10,90},{4,11,926}, +{133,11,983},{135,0,1776},{134,0,1528},{132,0,419},{132,11,538},{6,11,294},{7,11 +,1267},{136,11,624},{135,11,1772},{138,11,301},{4,10,257},{135,10,2031},{4,0,138 +},{7,0,1012},{7,0,1280},{7,10,1768},{137,0,76},{132,11,757},{5,0,29},{140,0,638} +,{7,11,655},{135,11,1844},{6,11,257},{7,0,1418},{135,11,1522},{8,11,469},{138,11 +,47},{142,11,278},{6,10,83},{6,10,1733},{135,10,1389},{11,11,204},{11,11,243},{ +140,11,293},{135,11,1875},{6,0,1710},{135,0,2038},{137,11,299},{4,0,17},{5,0,23} +,{7,0,995},{11,0,383},{11,0,437},{12,0,460},{140,0,532},{133,0,862},{137,10,696} +,{6,0,592},{138,0,946},{138,11,599},{7,10,1718},{9,10,95},{9,10,274},{10,10,279} +,{10,10,317},{10,10,420},{11,10,303},{11,10,808},{12,10,134},{12,10,367},{13,10, +149},{13,10,347},{14,10,349},{14,10,406},{18,10,22},{18,10,89},{18,10,122},{147, +10,47},{8,0,70},{12,0,171},{141,0,272},{133,10,26},{132,10,550},{137,0,812},{10, +0,233},{139,0,76},{134,0,988},{134,0,442},{136,10,822},{4,10,902},{5,10,809},{6, +10,122},{135,0,896},{5,11,150},{7,11,106},{8,11,603},{9,11,593},{9,11,634},{10, +11,44},{10,11,173},{11,11,462},{11,11,515},{13,11,216},{13,11,288},{142,11,400}, +{136,0,483},{135,10,262},{5,10,620},{134,0,1709},{4,10,34},{5,10,574},{7,10,279} +,{7,10,1624},{136,10,601},{137,10,170},{147,0,119},{12,11,108},{141,11,291},{11, +0,69},{12,0,105},{12,0,117},{13,0,213},{14,0,13},{14,0,62},{14,0,177},{14,0,421} +,{15,0,19},{146,0,141},{137,0,309},{11,11,278},{142,11,73},{7,0,608},{7,0,976},{ +9,0,146},{10,0,206},{10,0,596},{13,0,218},{142,0,153},{133,10,332},{6,10,261},{8 +,10,182},{139,10,943},{4,11,493},{144,11,55},{134,10,1721},{132,0,768},{4,10,933 +},{133,10,880},{7,11,555},{7,11,1316},{7,11,1412},{7,11,1839},{9,11,192},{9,11, +589},{11,11,241},{11,11,676},{11,11,811},{11,11,891},{12,11,140},{12,11,346},{12 +,11,479},{13,11,30},{13,11,49},{13,11,381},{14,11,188},{15,11,150},{16,11,76},{ +18,11,30},{148,11,52},{4,0,518},{135,0,1136},{6,11,568},{7,11,112},{7,11,1804},{ +8,11,362},{8,11,410},{8,11,830},{9,11,514},{11,11,649},{142,11,157},{135,11,673} +,{8,0,689},{137,0,863},{4,0,18},{4,11,625},{7,0,145},{7,0,444},{7,0,1278},{8,0, +49},{8,0,400},{9,0,71},{9,0,250},{10,0,459},{12,0,160},{144,0,24},{140,0,1020},{ +4,0,997},{6,0,1946},{6,0,1984},{134,0,1998},{6,11,16},{6,11,158},{7,11,43},{7,11 +,129},{7,11,181},{8,11,276},{8,11,377},{10,11,523},{11,11,816},{12,11,455},{13, +11,303},{142,11,135},{133,10,812},{134,0,658},{4,11,1},{7,11,1143},{7,11,1463},{ +8,11,61},{9,11,207},{9,11,390},{9,11,467},{139,11,836},{150,11,26},{140,0,106},{ +4,10,137},{6,0,1827},{7,10,1178},{7,10,1520},{7,11,1319},{10,0,931},{18,0,166},{ +148,0,114},{133,0,1010},{4,11,723},{5,11,895},{7,11,1031},{8,11,199},{8,11,340}, +{9,11,153},{9,11,215},{10,11,21},{10,11,59},{10,11,80},{10,11,224},{11,11,229},{ +11,11,652},{12,11,192},{13,11,146},{142,11,91},{132,11,295},{6,11,619},{7,11,898 +},{7,11,1092},{8,11,485},{18,11,28},{147,11,116},{137,11,51},{6,10,1661},{7,10, +1975},{7,10,2009},{135,10,2011},{5,11,309},{140,11,211},{5,0,87},{7,0,313},{7,0, +1103},{10,0,208},{10,0,582},{11,0,389},{11,0,813},{12,0,385},{13,0,286},{14,0, +124},{146,0,108},{5,11,125},{8,11,77},{138,11,15},{132,0,267},{133,0,703},{137, +11,155},{133,11,439},{11,11,164},{140,11,76},{5,10,89},{7,10,1915},{9,0,496},{9, +10,185},{9,10,235},{10,10,64},{10,10,270},{10,10,403},{10,10,469},{10,10,529},{ +10,10,590},{11,10,140},{11,10,860},{13,10,1},{13,10,422},{14,10,341},{14,10,364} +,{17,10,93},{18,10,113},{19,10,97},{147,10,113},{133,10,695},{135,0,1121},{5,10, +6},{6,10,183},{7,10,680},{7,10,978},{7,10,1013},{7,10,1055},{12,10,230},{13,10, +172},{146,10,29},{4,11,8},{7,11,1152},{7,11,1153},{7,11,1715},{9,11,374},{10,11, +478},{139,11,648},{135,11,1099},{6,10,29},{139,10,63},{4,0,561},{10,0,249},{139, +0,209},{132,0,760},{7,11,799},{138,11,511},{136,11,87},{9,0,154},{140,0,485},{ +136,0,255},{132,0,323},{140,0,419},{132,10,311},{134,10,1740},{4,0,368},{135,0, +641},{7,10,170},{8,10,90},{8,10,177},{8,10,415},{11,10,714},{142,10,281},{4,11, +69},{5,11,122},{9,11,656},{138,11,464},{5,11,849},{134,11,1633},{8,0,522},{142,0 +,328},{11,10,91},{13,10,129},{15,10,101},{145,10,125},{4,10,494},{6,10,74},{7,0, +562},{7,10,44},{8,0,551},{11,11,499},{12,10,17},{15,10,5},{148,10,11},{4,10,276} +,{133,10,296},{9,0,92},{147,0,91},{4,10,7},{5,10,90},{5,10,158},{6,10,542},{7,10 +,221},{7,10,1574},{9,10,490},{10,10,540},{11,10,443},{139,10,757},{6,0,525},{6,0 +,1976},{8,0,806},{9,0,876},{140,0,284},{5,11,859},{7,10,588},{7,11,1160},{8,11, +107},{9,10,175},{9,11,291},{9,11,439},{10,10,530},{10,11,663},{11,11,609},{140, +11,197},{7,11,168},{13,11,196},{141,11,237},{139,0,958},{133,0,594},{135,10,580} +,{7,10,88},{136,10,627},{5,10,872},{6,0,479},{6,0,562},{6,10,57},{7,0,1060},{7, +10,471},{9,10,447},{9,10,454},{141,0,6},{136,11,413},{145,11,19},{4,11,117},{6, +11,372},{7,11,1905},{142,11,323},{4,11,722},{139,11,471},{5,10,31},{6,10,614},{ +145,0,61},{8,10,330},{140,10,477},{7,10,1200},{138,10,460},{6,10,424},{135,10, +1866},{6,0,1641},{136,0,820},{6,0,1556},{134,0,1618},{9,11,5},{12,11,216},{12,11 +,294},{12,11,298},{12,11,400},{12,11,518},{13,11,229},{143,11,139},{15,11,155},{ +144,11,79},{4,0,302},{135,0,1766},{5,10,13},{134,10,142},{6,0,148},{7,0,1313},{7 +,10,116},{8,10,322},{8,10,755},{9,10,548},{10,10,714},{11,10,884},{141,10,324},{ +137,0,676},{9,11,88},{139,11,270},{5,11,12},{7,11,375},{137,11,438},{134,0,1674} +,{7,10,1472},{135,10,1554},{7,10,1071},{7,10,1541},{7,10,1767},{7,10,1806},{11,0 +,178},{11,10,162},{11,10,242},{12,10,605},{15,10,26},{144,10,44},{6,0,389},{7,0, +149},{9,0,142},{138,0,94},{140,11,71},{145,10,115},{6,0,8},{7,0,1881},{8,0,91},{ +11,11,966},{12,11,287},{13,11,342},{13,11,402},{15,11,110},{143,11,163},{4,11, +258},{136,11,639},{6,11,22},{7,11,903},{138,11,577},{133,11,681},{135,10,1111},{ +135,11,1286},{8,10,1},{9,0,112},{138,10,326},{5,10,488},{6,10,527},{7,10,489},{7 +,10,1636},{8,10,121},{8,10,144},{8,10,359},{9,10,193},{9,10,241},{9,10,336},{9, +10,882},{11,10,266},{11,10,372},{11,10,944},{12,10,401},{140,10,641},{4,11,664}, +{133,11,804},{6,0,747},{134,0,1015},{135,0,1746},{9,10,31},{10,10,244},{10,10, +699},{12,10,149},{141,10,497},{133,10,377},{135,0,24},{5,11,32},{6,0,1352},{145, +10,101},{7,0,1530},{10,0,158},{13,0,13},{13,0,137},{13,0,258},{14,0,111},{14,0, +225},{14,0,253},{14,0,304},{14,0,339},{14,0,417},{146,0,33},{4,0,503},{135,0, +1661},{5,0,130},{6,0,845},{7,0,1314},{9,0,610},{10,0,718},{11,0,601},{11,0,819}, +{11,0,946},{140,0,536},{10,0,149},{11,0,280},{142,0,336},{134,0,1401},{135,0, +1946},{8,0,663},{144,0,8},{134,0,1607},{135,10,2023},{4,11,289},{7,11,629},{7,11 +,1698},{7,11,1711},{140,11,215},{6,11,450},{136,11,109},{10,0,882},{10,0,883},{ +10,0,914},{138,0,928},{133,10,843},{136,11,705},{132,10,554},{133,10,536},{5,0, +417},{9,10,79},{11,10,625},{145,10,7},{7,11,1238},{142,11,37},{4,0,392},{135,0, +1597},{4,10,424},{5,0,433},{9,0,633},{139,0,629},{7,10,336},{136,10,785},{134,11 +,355},{6,0,234},{7,0,769},{9,0,18},{138,0,358},{4,10,896},{134,10,1777},{138,11, +323},{7,0,140},{7,0,1950},{8,0,680},{11,0,817},{147,0,88},{7,0,1222},{138,0,386} +,{139,11,908},{11,0,249},{12,0,313},{16,0,66},{145,0,26},{134,0,5},{7,10,750},{9 +,10,223},{11,10,27},{11,10,466},{12,10,624},{14,10,265},{146,10,61},{134,11,26}, +{134,0,1216},{5,0,963},{134,0,1773},{4,11,414},{5,11,467},{9,11,654},{10,11,451} +,{12,11,59},{141,11,375},{135,11,17},{4,10,603},{133,10,661},{4,10,11},{6,10,128 +},{7,10,231},{7,10,1533},{138,10,725},{135,11,955},{7,0,180},{8,0,509},{136,0, +792},{132,10,476},{132,0,1002},{133,11,538},{135,10,1807},{132,0,931},{7,0,943}, +{11,0,614},{140,0,747},{135,0,1837},{9,10,20},{10,10,324},{10,10,807},{139,10, +488},{134,0,641},{6,11,280},{10,11,502},{11,11,344},{140,11,38},{5,11,45},{7,11, +1161},{11,11,448},{11,11,880},{13,11,139},{13,11,407},{15,11,16},{17,11,95},{18, +11,66},{18,11,88},{18,11,123},{149,11,7},{9,0,280},{138,0,134},{22,0,22},{23,0,5 +},{151,0,29},{136,11,777},{4,0,90},{4,11,410},{5,0,545},{7,0,754},{7,11,521},{9, +0,186},{10,0,72},{10,0,782},{11,0,577},{11,0,610},{11,0,960},{12,0,354},{12,0, +362},{140,0,595},{135,11,1778},{5,10,112},{6,10,103},{134,10,150},{138,10,356},{ +132,0,742},{7,0,151},{9,0,329},{139,0,254},{8,0,853},{8,0,881},{8,0,911},{8,0, +912},{10,0,872},{12,0,741},{12,0,742},{152,0,18},{4,11,573},{136,11,655},{6,0, +921},{134,0,934},{9,0,187},{10,0,36},{11,0,1016},{17,0,44},{146,0,64},{7,0,833}, +{136,0,517},{4,0,506},{5,0,295},{135,0,1680},{4,10,708},{8,10,15},{9,10,50},{9, +10,386},{11,10,18},{11,10,529},{140,10,228},{4,10,563},{7,0,251},{7,0,1701},{7, +10,592},{7,10,637},{7,10,770},{8,0,436},{8,10,463},{9,10,60},{9,10,335},{9,10, +904},{10,10,73},{11,10,434},{12,10,585},{13,10,331},{18,10,110},{148,10,60},{132 +,10,502},{136,0,584},{6,10,347},{138,10,161},{7,0,987},{7,11,899},{9,0,688},{10, +0,522},{11,0,788},{12,0,137},{12,0,566},{14,0,9},{14,0,24},{14,0,64},{142,11,325 +},{4,0,214},{5,0,500},{5,10,102},{6,10,284},{7,10,1079},{7,10,1423},{7,10,1702}, +{8,10,470},{9,10,554},{9,10,723},{139,10,333},{7,10,246},{135,10,840},{6,10,10}, +{8,10,571},{9,10,739},{143,10,91},{133,10,626},{146,0,195},{134,0,1775},{7,0,389 +},{7,0,700},{7,0,940},{8,0,514},{9,0,116},{9,0,535},{10,0,118},{11,0,107},{11,0, +148},{11,0,922},{12,0,254},{12,0,421},{142,0,238},{5,10,18},{6,10,526},{13,10,24 +},{13,10,110},{19,10,5},{147,10,44},{132,0,743},{4,10,309},{5,10,462},{7,10,970} +,{7,10,1097},{139,0,292},{22,10,30},{150,10,33},{139,11,338},{135,11,1598},{7,0, +1283},{9,0,227},{11,0,325},{11,0,408},{14,0,180},{146,0,47},{4,0,953},{6,0,1805} +,{6,0,1814},{6,0,1862},{140,0,774},{6,11,611},{135,11,1733},{135,11,1464},{5,0, +81},{7,0,146},{7,0,1342},{8,0,53},{8,0,561},{8,0,694},{8,0,754},{9,0,115},{9,0, +179},{9,0,894},{10,0,462},{10,0,813},{11,0,230},{11,0,657},{11,0,699},{11,0,748} +,{12,0,119},{12,0,200},{12,0,283},{142,0,273},{5,0,408},{6,0,789},{6,0,877},{6,0 +,1253},{6,0,1413},{137,0,747},{134,10,1704},{135,11,663},{6,0,1910},{6,0,1915},{ +6,0,1923},{9,0,913},{9,0,928},{9,0,950},{9,0,954},{9,0,978},{9,0,993},{12,0,812} +,{12,0,819},{12,0,831},{12,0,833},{12,0,838},{12,0,909},{12,0,928},{12,0,931},{ +12,0,950},{15,0,186},{15,0,187},{15,0,195},{15,0,196},{15,0,209},{15,0,215},{15, +0,236},{15,0,241},{15,0,249},{15,0,253},{18,0,180},{18,0,221},{18,0,224},{18,0, +227},{18,0,229},{149,0,60},{7,0,1826},{135,0,1938},{5,10,86},{7,10,743},{9,10,85 +},{10,10,281},{10,10,432},{11,0,490},{12,10,251},{13,10,118},{14,10,378},{146,0, +143},{5,10,524},{133,10,744},{141,11,442},{10,10,107},{140,10,436},{135,11,503}, +{134,0,1162},{132,10,927},{7,0,30},{8,0,86},{8,0,315},{8,0,700},{9,0,576},{9,0, +858},{10,0,414},{11,0,310},{11,0,888},{11,0,904},{12,0,361},{12,10,670},{13,0, +248},{13,0,371},{14,0,142},{146,10,94},{134,0,721},{4,11,113},{5,11,163},{5,11, +735},{7,10,1149},{7,11,1009},{9,10,156},{9,11,9},{9,11,771},{12,11,90},{13,11, +138},{13,11,410},{143,11,128},{138,0,839},{133,10,778},{137,0,617},{133,10,502}, +{8,10,196},{10,10,283},{139,10,406},{6,0,428},{7,0,524},{8,0,169},{8,0,234},{9,0 +,480},{138,0,646},{133,10,855},{134,0,1648},{7,0,1205},{138,0,637},{4,11,935},{5 +,11,823},{135,0,1596},{5,11,269},{7,11,434},{7,11,891},{8,11,339},{9,11,702},{11 +,11,594},{11,11,718},{145,11,100},{7,11,878},{9,11,485},{141,11,264},{4,0,266},{ +6,11,1713},{8,0,4},{9,0,39},{10,0,166},{11,0,918},{12,0,635},{20,0,10},{22,0,27} +,{22,0,43},{150,0,52},{7,10,1400},{9,10,446},{138,10,45},{135,11,900},{132,0,862 +},{134,0,1554},{135,11,1033},{19,0,16},{147,11,16},{135,11,1208},{7,0,157},{136, +0,279},{6,0,604},{136,0,391},{13,10,455},{15,10,99},{15,10,129},{144,10,68},{135 +,10,172},{7,0,945},{11,0,713},{139,0,744},{4,0,973},{10,0,877},{10,0,937},{10,0, +938},{140,0,711},{139,0,1022},{132,10,568},{142,11,143},{4,0,567},{4,10,732},{ +137,0,859},{7,0,1846},{136,0,628},{136,10,733},{133,0,762},{4,10,428},{135,10, +1789},{7,10,2015},{10,0,784},{12,10,665},{141,0,191},{133,0,298},{7,0,633},{7,0, +905},{7,0,909},{7,0,1538},{9,0,767},{140,0,636},{138,10,806},{132,0,795},{139,0, +301},{135,0,1970},{5,11,625},{135,11,1617},{135,11,275},{7,11,37},{8,11,425},{8, +11,693},{9,11,720},{10,11,380},{10,11,638},{11,11,273},{11,11,307},{11,11,473},{ +12,11,61},{143,11,43},{135,11,198},{134,0,1236},{7,0,369},{12,0,644},{12,0,645}, +{144,0,90},{19,0,15},{149,0,27},{6,0,71},{6,10,1623},{6,10,1681},{7,0,845},{8,0, +160},{137,0,318},{134,0,1447},{134,0,1255},{138,0,735},{4,11,168},{136,0,76},{6, +10,1748},{8,10,715},{9,10,802},{10,10,46},{10,10,819},{13,10,308},{14,10,351},{ +14,10,363},{146,10,67},{135,11,91},{4,10,63},{5,10,347},{134,0,474},{133,10,749} +,{138,0,841},{133,10,366},{4,11,225},{134,0,836},{135,0,1622},{135,10,89},{140,0 +,735},{134,0,1601},{138,11,145},{6,0,1390},{137,0,804},{142,0,394},{6,11,15},{7, +11,70},{10,11,240},{147,11,93},{6,0,96},{135,0,1426},{4,0,651},{133,0,289},{7,10 +,977},{7,11,956},{7,11,1157},{7,11,1506},{7,11,1606},{7,11,1615},{7,11,1619},{7, +11,1736},{7,11,1775},{8,11,590},{9,11,324},{9,11,736},{9,11,774},{9,11,776},{9, +11,784},{10,11,567},{10,11,708},{11,11,518},{11,11,613},{11,11,695},{11,11,716}, +{11,11,739},{11,11,770},{11,11,771},{11,11,848},{11,11,857},{11,11,931},{11,11, +947},{12,11,326},{12,11,387},{12,11,484},{12,11,528},{12,11,552},{12,11,613},{13 +,11,189},{13,11,256},{13,11,340},{13,11,432},{13,11,436},{13,11,440},{13,11,454} +,{14,11,174},{14,11,220},{14,11,284},{14,11,390},{145,11,121},{7,0,688},{8,0,35} +,{9,0,511},{10,0,767},{147,0,118},{134,0,667},{4,0,513},{5,10,824},{133,10,941}, +{7,10,440},{8,10,230},{139,10,106},{134,0,2034},{135,11,1399},{143,11,66},{135, +11,1529},{4,11,145},{6,11,176},{7,11,395},{9,11,562},{144,11,28},{132,11,501},{ +132,0,704},{134,0,1524},{6,11,464},{135,0,1078},{6,11,509},{10,11,82},{20,11,91} +,{151,11,13},{4,0,720},{133,0,306},{133,0,431},{4,10,914},{5,10,800},{5,10,852}, +{135,0,1196},{135,11,1189},{10,0,54},{141,10,115},{7,10,564},{142,10,168},{4,10, +918},{5,0,464},{5,10,876},{6,0,236},{7,0,696},{7,0,914},{7,0,1108},{7,0,1448},{9 +,0,15},{9,0,564},{10,0,14},{12,0,565},{13,0,449},{14,0,53},{15,0,13},{16,0,64},{ +145,0,41},{6,0,1418},{134,10,1764},{4,10,92},{133,10,274},{134,0,907},{4,11,114} +,{8,10,501},{9,11,492},{13,11,462},{142,11,215},{4,11,77},{5,11,361},{6,11,139}, +{6,11,401},{6,11,404},{7,11,413},{7,11,715},{7,11,1716},{11,11,279},{12,11,179}, +{12,11,258},{13,11,244},{142,11,358},{6,0,1767},{12,0,194},{145,0,107},{134,11, +1717},{5,10,743},{142,11,329},{4,10,49},{7,10,280},{135,10,1633},{5,0,840},{7,11 +,1061},{8,11,82},{11,11,250},{12,11,420},{141,11,184},{135,11,724},{134,0,900},{ +136,10,47},{134,0,1436},{144,11,0},{5,10,272},{5,10,908},{5,10,942},{6,0,675},{7 +,0,1008},{7,0,1560},{8,10,197},{9,0,642},{9,10,47},{11,0,236},{11,10,538},{11,10 +,742},{142,0,193},{4,0,68},{5,0,628},{5,0,634},{6,0,386},{7,0,794},{8,0,273},{9, +0,563},{10,0,105},{10,0,171},{11,0,94},{139,0,354},{135,10,1911},{137,10,891},{4 +,0,95},{6,0,1297},{6,0,1604},{7,0,416},{139,0,830},{6,11,513},{135,11,1052},{7,0 +,731},{13,0,20},{143,0,11},{137,11,899},{10,0,850},{140,0,697},{4,0,662},{7,11, +1417},{12,11,382},{17,11,48},{152,11,12},{133,0,736},{132,0,861},{4,10,407},{132 +,10,560},{141,10,490},{6,11,545},{7,11,565},{7,11,1669},{10,11,114},{11,11,642}, +{140,11,618},{6,0,871},{134,0,1000},{5,0,864},{5,11,5},{10,0,648},{11,0,671},{ +143,0,46},{133,0,928},{4,10,475},{11,0,90},{11,10,35},{13,0,7},{13,10,71},{13,10 +,177},{142,10,422},{136,0,332},{135,11,192},{134,0,1055},{136,11,763},{11,0,986} +,{140,0,682},{7,0,76},{8,0,44},{9,0,884},{10,0,580},{11,0,399},{11,0,894},{143,0 +,122},{135,11,1237},{135,10,636},{6,10,222},{7,10,1620},{8,10,409},{9,10,693},{ +139,0,300},{4,11,87},{5,11,250},{10,11,601},{13,11,298},{13,11,353},{141,11,376} +,{5,0,518},{10,0,340},{11,0,175},{149,0,16},{140,0,771},{6,0,1108},{137,0,831},{ +132,0,836},{135,0,1852},{4,0,957},{6,0,1804},{8,0,842},{8,0,843},{8,0,851},{8,0, +855},{140,0,767},{135,11,814},{4,11,57},{7,11,1195},{7,11,1438},{7,11,1548},{7, +11,1835},{7,11,1904},{9,11,757},{10,11,604},{139,11,519},{133,10,882},{138,0,246 +},{4,0,934},{5,0,202},{7,11,1897},{8,0,610},{12,11,290},{13,11,80},{13,11,437},{ +145,11,74},{8,0,96},{9,0,36},{10,0,607},{10,0,804},{10,0,832},{11,0,423},{11,0, +442},{12,0,309},{14,0,199},{15,0,90},{145,0,110},{132,10,426},{6,10,58},{7,0,654 +},{7,10,745},{7,10,1969},{8,0,240},{8,10,675},{9,10,479},{9,10,731},{10,10,330}, +{10,10,593},{10,10,817},{11,10,32},{11,10,133},{11,10,221},{145,10,68},{9,0,13}, +{9,0,398},{9,0,727},{10,0,75},{10,0,184},{10,0,230},{10,0,564},{10,0,569},{11,0, +973},{12,0,70},{12,0,189},{13,0,57},{141,0,257},{4,11,209},{135,11,902},{7,0,391 +},{137,10,538},{134,0,403},{6,11,303},{7,11,335},{7,11,1437},{7,11,1668},{8,11, +553},{8,11,652},{8,11,656},{9,11,558},{11,11,743},{149,11,18},{132,11,559},{11,0 +,75},{142,0,267},{6,0,815},{141,11,2},{141,0,366},{137,0,631},{133,11,1017},{5,0 +,345},{135,0,1016},{133,11,709},{134,11,1745},{133,10,566},{6,10,48},{7,0,952},{ +9,10,139},{10,10,399},{11,10,469},{12,10,634},{141,10,223},{133,0,673},{7,11,8}, +{8,11,206},{137,0,850},{6,0,662},{149,0,35},{4,0,287},{133,0,1018},{6,10,114},{7 +,10,1224},{7,10,1556},{136,10,3},{8,10,576},{137,10,267},{4,0,884},{5,0,34},{10, +0,724},{12,0,444},{13,0,354},{18,0,32},{23,0,24},{23,0,31},{152,0,5},{133,10,933 +},{132,11,776},{138,0,151},{136,0,427},{134,0,382},{132,0,329},{9,0,846},{10,0, +827},{138,11,33},{7,11,1297},{9,0,279},{10,0,407},{14,0,84},{150,0,18},{136,11, +406},{132,0,906},{136,0,366},{134,0,843},{134,0,1443},{135,0,1372},{138,0,992},{ +4,0,123},{5,0,605},{7,0,1509},{136,0,36},{132,0,649},{8,11,175},{10,11,168},{138 +,11,573},{133,0,767},{134,0,1018},{135,11,1305},{12,10,30},{13,10,148},{14,10,87 +},{14,10,182},{16,10,42},{148,10,70},{134,11,607},{4,0,273},{5,0,658},{133,0,995 +},{6,0,72},{139,11,174},{7,10,56},{7,10,1989},{8,10,337},{8,10,738},{9,10,600},{ +10,0,483},{12,0,368},{13,10,447},{142,10,92},{5,11,784},{138,10,666},{135,0,1345 +},{139,11,882},{134,0,1293},{133,0,589},{134,0,1988},{5,0,117},{6,0,514},{6,0, +541},{7,0,1164},{7,0,1436},{8,0,220},{8,0,648},{10,0,688},{139,0,560},{136,0,379 +},{5,0,686},{7,10,866},{135,10,1163},{132,10,328},{9,11,14},{9,11,441},{10,11, +306},{139,11,9},{4,10,101},{135,10,1171},{5,10,833},{136,10,744},{5,11,161},{7, +11,839},{135,11,887},{7,0,196},{10,0,765},{11,0,347},{11,0,552},{11,0,790},{12,0 +,263},{13,0,246},{13,0,270},{13,0,395},{14,0,176},{14,0,190},{14,0,398},{14,0, +412},{15,0,32},{15,0,63},{16,0,88},{147,0,105},{6,10,9},{6,10,397},{7,10,53},{7, +10,1742},{10,10,632},{11,10,828},{140,10,146},{5,0,381},{135,0,1792},{134,0,1452 +},{135,11,429},{8,0,367},{10,0,760},{14,0,79},{20,0,17},{152,0,0},{7,0,616},{138 +,0,413},{11,10,417},{12,10,223},{140,10,265},{7,11,1611},{13,11,14},{15,11,44},{ +19,11,13},{148,11,76},{135,0,1229},{6,0,120},{7,0,1188},{7,0,1710},{8,0,286},{9, +0,667},{11,0,592},{139,0,730},{135,11,1814},{135,0,1146},{4,10,186},{5,10,157},{ +8,10,168},{138,10,6},{4,0,352},{135,0,687},{4,0,192},{5,0,49},{6,0,200},{6,0,293 +},{6,0,1696},{135,0,1151},{133,10,875},{5,10,773},{5,10,991},{6,10,1635},{134,10 +,1788},{7,10,111},{136,10,581},{6,0,935},{134,0,1151},{134,0,1050},{132,0,650},{ +132,0,147},{11,0,194},{11,11,194},{12,0,62},{12,0,88},{12,11,62},{140,11,88},{6, +0,339},{135,0,923},{134,10,1747},{7,11,643},{136,11,236},{133,0,934},{7,10,1364} +,{7,10,1907},{141,10,158},{132,10,659},{4,10,404},{135,10,675},{7,11,581},{9,11, +644},{137,11,699},{13,0,211},{14,0,133},{14,0,204},{15,0,64},{15,0,69},{15,0,114 +},{16,0,10},{19,0,23},{19,0,35},{19,0,39},{19,0,51},{19,0,71},{19,0,75},{152,0, +15},{133,10,391},{5,11,54},{135,11,1513},{5,10,540},{6,10,1697},{7,0,222},{136,0 +,341},{134,10,78},{132,11,744},{136,0,293},{137,11,701},{7,11,930},{10,11,402},{ +10,11,476},{13,11,452},{18,11,55},{147,11,104},{132,0,637},{133,10,460},{8,11,50 +},{137,11,624},{132,11,572},{134,0,1159},{4,10,199},{139,10,34},{134,0,847},{134 +,10,388},{6,11,43},{7,11,38},{8,11,248},{9,11,504},{138,11,513},{4,10,511},{6,10 +,608},{9,0,683},{9,10,333},{10,10,602},{11,10,441},{11,10,723},{11,10,976},{140, +10,357},{9,0,867},{138,0,837},{6,0,944},{135,11,326},{135,0,1809},{5,10,938},{7, +11,783},{136,10,707},{133,11,766},{133,11,363},{6,0,170},{7,0,1080},{8,0,395},{8 +,0,487},{141,0,147},{6,11,258},{140,11,409},{4,0,535},{5,11,249},{8,0,618},{148, +11,82},{6,0,1379},{149,11,15},{135,0,1625},{150,0,23},{5,11,393},{6,11,378},{7, +11,1981},{9,11,32},{9,11,591},{10,11,685},{10,11,741},{142,11,382},{133,11,788}, +{7,11,1968},{10,11,19},{139,11,911},{7,11,1401},{135,11,1476},{4,11,61},{5,11,58 +},{5,11,171},{5,11,635},{5,11,683},{5,11,700},{6,11,291},{6,11,566},{7,11,1650}, +{11,11,523},{12,11,273},{12,11,303},{15,11,39},{143,11,111},{6,10,469},{7,10, +1709},{138,10,515},{4,0,778},{134,11,589},{132,0,46},{5,0,811},{6,0,1679},{6,0, +1714},{135,0,2032},{7,0,1458},{9,0,407},{11,0,15},{12,0,651},{149,0,37},{4,10, +500},{135,0,938},{6,0,34},{7,0,69},{7,0,1089},{7,0,1281},{8,0,708},{8,0,721},{9, +0,363},{148,0,98},{10,11,231},{147,11,124},{7,11,726},{152,11,9},{5,10,68},{134, +10,383},{136,11,583},{4,11,917},{133,11,1005},{11,10,216},{139,10,340},{135,11, +1675},{8,0,441},{10,0,314},{143,0,3},{132,11,919},{4,10,337},{6,10,353},{7,10, +1934},{8,10,488},{137,10,429},{7,0,889},{7,10,1795},{8,10,259},{9,10,135},{9,10, +177},{9,10,860},{10,10,825},{11,10,115},{11,10,370},{11,10,405},{11,10,604},{12, +10,10},{12,10,667},{12,10,669},{13,10,76},{14,10,310},{15,10,76},{15,10,147},{ +148,10,23},{4,10,15},{4,11,255},{5,10,22},{5,11,302},{6,10,244},{6,11,132},{7,10 +,40},{7,10,200},{7,10,906},{7,10,1199},{7,11,128},{7,11,283},{7,11,1299},{9,10, +616},{10,10,716},{10,11,52},{10,11,514},{11,10,635},{11,10,801},{11,11,925},{12, +10,458},{13,11,92},{142,11,309},{132,0,462},{137,11,173},{135,10,1735},{5,10,598 +},{7,10,791},{8,0,525},{8,10,108},{137,10,123},{5,0,73},{6,0,23},{134,0,338},{ +132,0,676},{132,10,683},{7,0,725},{8,0,498},{139,0,268},{12,0,21},{151,0,7},{135 +,0,773},{4,10,155},{135,10,1689},{4,0,164},{5,0,730},{5,10,151},{5,10,741},{6,11 +,210},{7,10,498},{7,10,870},{7,10,1542},{12,10,213},{14,10,36},{14,10,391},{17, +10,111},{18,10,6},{18,10,46},{18,10,151},{19,10,36},{20,10,32},{20,10,56},{20,10 +,69},{20,10,102},{21,10,4},{22,10,8},{22,10,10},{22,10,14},{150,10,31},{4,10,624 +},{135,10,1752},{4,0,583},{6,11,588},{9,0,936},{15,0,214},{18,0,199},{152,0,26}, +{4,11,284},{6,11,223},{7,0,1462},{139,0,659},{133,0,220},{139,0,803},{132,0,544} +,{4,10,492},{133,10,451},{16,0,98},{148,0,119},{4,11,218},{7,11,526},{143,11,137 +},{135,10,835},{4,11,270},{5,11,192},{6,11,332},{7,11,1322},{13,10,70},{13,11,9} +,{14,11,104},{142,11,311},{132,10,539},{140,11,661},{5,0,176},{6,0,437},{6,0,564 +},{11,0,181},{141,0,183},{135,0,1192},{6,10,113},{135,10,436},{136,10,718},{135, +10,520},{135,0,1878},{140,11,196},{7,11,379},{8,11,481},{137,11,377},{5,11,1003} +,{6,11,149},{137,11,746},{8,11,262},{9,11,627},{10,11,18},{11,11,214},{11,11,404 +},{11,11,457},{11,11,780},{11,11,849},{11,11,913},{13,11,330},{13,11,401},{142, +11,200},{149,0,26},{136,11,304},{132,11,142},{135,0,944},{4,0,790},{5,0,273},{ +134,0,394},{134,0,855},{4,0,135},{6,0,127},{7,0,1185},{7,0,1511},{8,0,613},{11,0 +,5},{12,0,336},{12,0,495},{12,0,586},{12,0,660},{12,0,668},{14,0,385},{15,0,118} +,{17,0,20},{146,0,98},{6,0,230},{9,0,752},{12,10,610},{13,10,431},{16,10,59},{ +146,0,109},{7,0,1954},{135,11,925},{4,11,471},{5,11,51},{6,11,602},{8,11,484},{ +10,11,195},{140,11,159},{132,10,307},{136,11,688},{132,11,697},{7,11,812},{7,11, +1261},{7,11,1360},{9,11,632},{140,11,352},{5,0,162},{5,10,964},{136,0,68},{4,0, +654},{136,11,212},{4,0,156},{7,0,998},{7,0,1045},{7,0,1860},{9,0,48},{9,0,692},{ +11,0,419},{139,0,602},{133,11,221},{4,11,373},{5,11,283},{6,11,480},{135,11,609} +,{142,11,216},{132,0,240},{6,11,192},{9,11,793},{145,11,55},{4,10,75},{5,10,180} +,{6,10,500},{7,10,58},{7,10,710},{138,10,645},{4,11,132},{5,10,649},{5,11,69},{ +135,11,1242},{6,10,276},{7,10,282},{7,10,879},{7,10,924},{8,10,459},{9,10,599},{ +9,10,754},{11,10,574},{12,10,128},{12,10,494},{13,10,52},{13,10,301},{15,10,30}, +{143,10,132},{132,10,200},{4,11,111},{135,11,302},{9,0,197},{10,0,300},{12,0,473 +},{13,0,90},{141,0,405},{132,11,767},{6,11,42},{7,11,1416},{7,11,1590},{7,11, +2005},{8,11,131},{8,11,466},{9,11,672},{13,11,252},{148,11,103},{8,0,958},{8,0, +999},{10,0,963},{138,0,1001},{135,10,1621},{135,0,858},{4,0,606},{137,11,444},{6 +,11,44},{136,11,368},{139,11,172},{4,11,570},{133,11,120},{139,11,624},{6,10,225 +},{7,0,1978},{8,0,676},{137,10,211},{7,0,972},{8,10,687},{139,0,102},{6,11,227}, +{135,11,1589},{8,10,58},{9,10,724},{11,10,809},{13,10,113},{145,10,72},{4,0,361} +,{133,0,315},{132,0,461},{6,10,345},{135,10,1247},{132,0,472},{8,10,767},{8,10, +803},{9,10,301},{137,10,903},{135,11,1333},{135,11,477},{7,10,1949},{136,10,674} +,{6,0,905},{138,0,747},{133,0,155},{134,10,259},{7,0,163},{8,0,319},{9,0,402},{ +10,0,24},{10,0,681},{11,0,200},{12,0,253},{12,0,410},{142,0,219},{5,0,475},{6,11 +,1667},{7,0,1780},{7,11,2036},{9,0,230},{10,11,600},{11,0,297},{11,0,558},{14,0, +322},{147,0,76},{136,10,254},{6,0,848},{135,0,1956},{6,11,511},{140,11,132},{5, +11,568},{6,11,138},{135,11,1293},{6,0,631},{137,0,838},{149,0,36},{4,11,565},{8, +11,23},{136,11,827},{5,0,944},{134,0,1769},{4,0,144},{4,11,922},{5,11,1023},{6,0 +,842},{134,0,1400},{133,10,248},{9,10,800},{10,10,693},{11,10,482},{11,10,734},{ +139,10,789},{7,11,1002},{139,11,145},{4,10,116},{5,10,95},{5,10,445},{7,10,1688} +,{8,10,29},{9,10,272},{11,10,509},{139,10,915},{14,0,369},{146,0,72},{135,10, +1641},{132,11,740},{133,10,543},{140,11,116},{5,10,181},{6,0,247},{8,10,41},{137 +,0,555},{133,10,657},{136,0,996},{138,10,709},{7,0,189},{8,10,202},{138,10,536}, +{136,11,402},{4,11,716},{141,11,31},{10,0,280},{138,0,797},{9,10,423},{140,10,89 +},{8,10,113},{9,10,877},{10,10,554},{11,10,83},{12,10,136},{147,10,109},{133,10, +976},{4,10,206},{135,0,746},{136,0,526},{139,0,345},{136,0,1017},{8,11,152},{9, +11,53},{9,11,268},{9,11,901},{10,11,518},{10,11,829},{11,11,188},{13,11,74},{14, +11,46},{15,11,17},{15,11,33},{17,11,40},{18,11,36},{19,11,20},{22,11,1},{152,11, +2},{133,11,736},{136,11,532},{5,0,428},{138,0,651},{135,11,681},{135,0,1162},{7, +0,327},{8,10,226},{10,10,537},{11,10,570},{11,10,605},{11,10,799},{11,10,804},{ +12,10,85},{12,10,516},{12,10,623},{12,11,677},{13,0,230},{13,10,361},{14,10,77}, +{14,10,78},{17,0,113},{147,10,110},{4,0,792},{4,10,769},{7,0,1717},{138,0,546},{ +4,11,684},{136,11,384},{132,10,551},{134,0,1203},{9,10,57},{9,10,459},{10,10,425 +},{11,10,119},{12,10,184},{12,10,371},{13,10,358},{145,10,51},{5,0,672},{5,10, +814},{8,10,10},{9,10,421},{9,10,729},{10,10,609},{139,10,689},{138,0,189},{134, +10,624},{7,11,110},{7,11,188},{8,11,290},{8,11,591},{9,11,382},{9,11,649},{11,11 +,71},{11,11,155},{11,11,313},{12,11,5},{13,11,325},{142,11,287},{133,0,99},{6,0, +1053},{135,0,298},{7,11,360},{7,11,425},{9,11,66},{9,11,278},{138,11,644},{4,0, +397},{136,0,555},{137,10,269},{132,10,528},{4,11,900},{133,11,861},{5,11,254},{6 +,0,1157},{7,11,985},{136,11,73},{7,11,1959},{136,11,683},{12,0,398},{20,0,39},{ +21,0,11},{150,0,41},{4,0,485},{7,0,353},{135,0,1523},{6,0,366},{7,0,1384},{135,0 +,1601},{138,0,787},{137,0,282},{5,10,104},{6,10,173},{135,10,1631},{139,11,146}, +{4,0,157},{133,0,471},{134,0,941},{132,11,725},{7,0,1336},{8,10,138},{8,10,342}, +{9,10,84},{10,10,193},{11,10,883},{140,10,359},{134,11,196},{136,0,116},{133,11, +831},{134,0,787},{134,10,95},{6,10,406},{10,10,409},{10,10,447},{11,10,44},{140, +10,100},{5,0,160},{7,0,363},{7,0,589},{10,0,170},{141,0,55},{134,0,1815},{132,0, +866},{4,11,321},{6,0,889},{6,0,1067},{6,0,1183},{134,11,569},{5,11,848},{134,11, +66},{4,11,36},{6,10,1636},{7,11,1387},{10,11,205},{11,11,755},{141,11,271},{132, +0,689},{4,10,282},{7,10,1034},{9,0,820},{11,10,398},{11,10,634},{12,10,1},{12,10 +,79},{12,10,544},{14,10,237},{17,10,10},{146,10,20},{4,0,108},{7,0,804},{139,0, +498},{132,11,887},{6,0,1119},{135,11,620},{6,11,165},{138,11,388},{5,0,244},{5, +10,499},{6,10,476},{7,10,600},{7,10,888},{135,10,1096},{140,0,609},{135,0,1005}, +{4,0,412},{133,0,581},{4,11,719},{135,11,155},{7,10,296},{7,10,596},{8,10,560},{ +8,10,586},{9,10,612},{11,10,304},{12,10,46},{13,10,89},{14,10,112},{145,10,122}, +{4,0,895},{133,0,772},{142,11,307},{135,0,1898},{4,0,926},{133,0,983},{4,11,353} +,{6,11,146},{6,11,1789},{7,11,288},{7,11,990},{7,11,1348},{9,11,665},{9,11,898}, +{11,11,893},{142,11,212},{132,0,538},{133,11,532},{6,0,294},{7,0,1267},{8,0,624} +,{141,0,496},{4,11,45},{7,0,1325},{135,11,1257},{138,0,301},{7,10,1599},{7,10, +1723},{8,10,79},{8,10,106},{8,10,190},{8,10,302},{8,10,383},{8,10,713},{9,0,298} +,{9,10,119},{9,10,233},{9,10,419},{9,10,471},{10,10,181},{10,10,406},{11,10,57}, +{11,10,85},{11,10,120},{11,10,177},{11,10,296},{11,10,382},{11,10,454},{11,10, +758},{11,10,999},{12,0,291},{12,10,27},{12,10,131},{12,10,245},{12,10,312},{12, +10,446},{12,10,454},{13,0,276},{13,10,98},{13,10,426},{13,10,508},{14,0,6},{14, +10,163},{14,10,272},{14,10,277},{14,10,370},{15,10,95},{15,10,138},{15,10,167},{ +17,0,18},{17,10,38},{20,10,96},{149,0,32},{132,0,757},{134,0,1263},{4,0,820},{ +134,10,1759},{133,0,722},{136,11,816},{138,10,372},{145,10,16},{134,0,1039},{4,0 +,991},{134,0,2028},{133,10,258},{7,0,1875},{139,0,124},{6,11,559},{6,11,1691},{ +135,11,586},{5,0,324},{7,0,881},{8,10,134},{9,10,788},{140,10,438},{7,11,1823},{ +139,11,693},{6,0,1348},{134,0,1545},{134,0,911},{132,0,954},{7,10,1948},{7,10, +2004},{8,0,329},{136,0,414},{5,0,517},{6,10,439},{7,10,780},{135,10,1040},{132,0 +,816},{5,10,1},{6,10,81},{138,10,520},{5,10,482},{8,10,98},{9,0,713},{10,0,222}, +{10,10,700},{10,10,822},{11,10,302},{11,10,778},{12,10,50},{12,10,127},{12,10, +396},{13,10,62},{13,10,328},{14,10,122},{147,10,72},{137,0,33},{5,10,2},{7,10, +1494},{136,10,589},{6,10,512},{7,10,797},{8,10,253},{9,10,77},{10,10,1},{10,10, +129},{10,10,225},{10,11,108},{11,10,118},{11,10,226},{11,10,251},{11,10,430},{11 +,10,701},{11,10,974},{11,10,982},{11,11,116},{12,10,64},{12,10,260},{12,10,488}, +{140,10,690},{134,11,456},{133,11,925},{5,0,150},{7,0,106},{7,0,774},{8,0,603},{ +9,0,593},{9,0,634},{10,0,44},{10,0,173},{11,0,462},{11,0,515},{13,0,216},{13,0, +288},{142,0,400},{137,10,347},{5,0,748},{134,0,553},{12,0,108},{141,0,291},{4,10 +,12},{7,0,420},{7,10,522},{7,10,809},{8,10,797},{141,10,88},{6,11,193},{7,11,240 +},{7,11,1682},{10,11,51},{10,11,640},{11,11,410},{13,11,82},{14,11,247},{14,11, +331},{142,11,377},{133,10,528},{135,0,1777},{4,0,493},{144,0,55},{136,11,633},{ +139,0,81},{6,0,980},{136,0,321},{148,10,109},{5,10,266},{9,10,290},{9,10,364},{ +10,10,293},{11,10,606},{142,10,45},{6,0,568},{7,0,112},{7,0,1804},{8,0,362},{8,0 +,410},{8,0,830},{9,0,514},{11,0,649},{142,0,157},{4,0,74},{6,0,510},{6,10,594},{ +9,10,121},{10,10,49},{10,10,412},{139,10,834},{134,0,838},{136,10,748},{132,10, +466},{132,0,625},{135,11,1443},{4,11,237},{135,11,514},{9,10,378},{141,10,162},{ +6,0,16},{6,0,158},{7,0,43},{7,0,129},{7,0,181},{8,0,276},{8,0,377},{10,0,523},{ +11,0,816},{12,0,455},{13,0,303},{142,0,135},{135,0,281},{4,0,1},{7,0,1143},{7,0, +1463},{8,0,61},{9,0,207},{9,0,390},{9,0,467},{139,0,836},{6,11,392},{7,11,65},{ +135,11,2019},{132,10,667},{4,0,723},{5,0,895},{7,0,1031},{8,0,199},{8,0,340},{9, +0,153},{9,0,215},{10,0,21},{10,0,59},{10,0,80},{10,0,224},{10,0,838},{11,0,229}, +{11,0,652},{12,0,192},{13,0,146},{142,0,91},{132,0,295},{137,0,51},{9,11,222},{ +10,11,43},{139,11,900},{5,0,309},{140,0,211},{5,0,125},{8,0,77},{138,0,15},{136, +11,604},{138,0,789},{4,10,39},{5,0,173},{7,10,1843},{8,10,407},{11,10,144},{140, +10,523},{138,11,265},{133,0,439},{132,10,510},{7,0,648},{7,0,874},{7,10,1980},{ +10,10,487},{10,10,809},{11,0,164},{12,0,76},{146,0,9},{12,0,111},{13,10,260},{14 +,0,294},{18,10,63},{147,0,45},{133,11,549},{134,10,570},{4,0,8},{7,0,1152},{7,0, +1153},{7,0,1715},{9,0,374},{10,0,478},{139,0,648},{135,0,1099},{5,0,575},{6,0, +354},{135,0,701},{7,11,36},{8,11,201},{136,11,605},{4,10,787},{136,11,156},{6,0, +518},{149,11,13},{140,11,224},{134,0,702},{132,10,516},{5,11,724},{10,11,305},{ +11,11,151},{12,11,33},{12,11,121},{12,11,381},{17,11,3},{17,11,27},{17,11,78},{ +18,11,18},{19,11,54},{149,11,5},{4,11,523},{5,11,638},{8,0,87},{11,10,887},{14, +10,365},{142,10,375},{138,0,438},{136,10,821},{135,11,1908},{6,11,242},{7,11,227 +},{7,11,1581},{8,11,104},{9,11,113},{9,11,220},{9,11,427},{10,11,74},{10,11,239} +,{11,11,579},{11,11,1023},{13,11,4},{13,11,204},{13,11,316},{18,11,95},{148,11, +86},{4,0,69},{5,0,122},{5,0,849},{6,0,1633},{9,0,656},{138,0,464},{4,10,10},{7,0 +,1802},{139,10,786},{135,11,861},{139,0,499},{7,0,476},{7,0,1592},{138,0,87},{ +133,10,684},{4,0,840},{134,10,27},{142,0,283},{6,0,1620},{7,11,1328},{136,11,494 +},{5,0,859},{7,0,1160},{8,0,107},{9,0,291},{9,0,439},{10,0,663},{11,0,609},{140, +0,197},{7,11,1306},{8,11,505},{9,11,482},{10,11,126},{11,11,225},{12,11,347},{12 +,11,449},{13,11,19},{142,11,218},{5,11,268},{10,11,764},{12,11,120},{13,11,39},{ +145,11,127},{145,10,56},{7,11,1672},{10,11,472},{11,11,189},{143,11,51},{6,10, +342},{6,10,496},{8,10,275},{137,10,206},{133,0,600},{4,0,117},{6,0,372},{7,0, +1905},{142,0,323},{4,10,909},{5,10,940},{135,11,1471},{132,10,891},{4,0,722},{ +139,0,471},{4,11,384},{135,11,1022},{132,10,687},{9,0,5},{12,0,216},{12,0,294},{ +12,0,298},{12,0,400},{12,0,518},{13,0,229},{143,0,139},{135,11,1703},{7,11,1602} +,{10,11,698},{12,11,212},{141,11,307},{6,10,41},{141,10,160},{135,11,1077},{9,11 +,159},{11,11,28},{140,11,603},{4,0,514},{7,0,1304},{138,0,477},{134,0,1774},{9,0 +,88},{139,0,270},{5,0,12},{6,10,1718},{7,0,375},{137,0,438},{132,11,515},{136,10 +,778},{8,11,632},{8,11,697},{137,11,854},{6,0,362},{6,0,997},{146,0,51},{7,0,816 +},{7,0,1241},{9,0,283},{9,0,520},{10,0,213},{10,0,307},{10,0,463},{10,0,671},{10 +,0,746},{11,0,401},{11,0,794},{12,0,517},{18,0,107},{147,0,115},{133,10,115},{ +150,11,28},{4,11,136},{133,11,551},{142,10,314},{132,0,258},{6,0,22},{7,0,903},{ +7,0,1963},{8,0,639},{138,0,577},{5,0,681},{5,10,193},{8,0,782},{12,10,178},{13,0 +,130},{145,0,84},{9,11,17},{138,11,291},{7,11,1287},{9,11,44},{10,11,552},{10,11 +,642},{11,11,839},{12,11,274},{12,11,275},{12,11,372},{13,11,91},{142,11,125},{ +135,10,174},{4,0,664},{5,0,804},{139,0,1013},{134,0,942},{6,0,1349},{6,0,1353},{ +6,0,1450},{7,11,1518},{139,11,694},{4,10,122},{5,10,796},{5,10,952},{6,10,1660}, +{6,10,1671},{8,10,567},{9,10,687},{9,10,742},{10,10,686},{11,0,356},{11,10,682}, +{140,10,281},{5,0,32},{6,11,147},{7,11,886},{9,11,753},{138,11,268},{5,10,179},{ +7,10,1095},{135,10,1213},{4,10,66},{7,10,722},{135,10,904},{135,10,352},{9,11, +245},{138,11,137},{4,0,289},{5,11,414},{7,0,629},{7,0,1698},{7,0,1711},{140,0, +215},{6,0,1975},{135,11,1762},{6,0,450},{136,0,109},{141,10,35},{134,11,599},{ +136,0,705},{133,0,664},{134,11,1749},{11,11,402},{12,11,109},{12,11,431},{13,11, +179},{13,11,206},{14,11,175},{14,11,217},{16,11,3},{148,11,53},{135,0,1238},{134 +,11,1627},{132,11,488},{10,10,592},{10,10,753},{12,10,317},{12,10,355},{12,10, +465},{12,10,469},{12,10,560},{12,10,578},{141,0,318},{133,10,564},{132,11,83},{ +140,11,676},{6,0,1872},{6,0,1906},{6,0,1907},{9,0,934},{9,0,956},{9,0,960},{9,0, +996},{12,0,794},{12,0,876},{12,0,880},{12,0,918},{15,0,230},{18,0,234},{18,0,238 +},{21,0,38},{149,0,62},{134,10,556},{134,11,278},{137,0,103},{7,10,544},{8,10, +719},{138,10,61},{4,10,5},{5,10,498},{8,10,637},{137,10,521},{7,0,777},{12,0,229 +},{12,0,239},{12,11,229},{12,11,239},{15,0,12},{143,11,12},{6,0,26},{7,11,388},{ +7,11,644},{139,11,781},{7,11,229},{8,11,59},{9,11,190},{9,11,257},{10,11,378},{ +140,11,191},{133,10,927},{135,10,1441},{4,10,893},{5,10,780},{133,10,893},{4,0, +414},{5,0,467},{9,0,654},{10,0,451},{12,0,59},{141,0,375},{142,0,173},{135,0,17} +,{5,10,238},{135,0,1350},{135,0,955},{4,0,960},{10,0,887},{12,0,753},{18,0,161}, +{18,0,162},{152,0,19},{136,11,344},{6,10,1729},{137,11,288},{132,11,660},{4,0, +217},{4,10,60},{5,0,710},{7,0,760},{7,0,1926},{7,10,1800},{8,10,314},{9,0,428},{ +9,0,708},{9,10,700},{10,0,254},{10,0,296},{10,0,720},{11,0,109},{11,0,255},{11, +10,487},{12,0,165},{12,0,315},{13,0,107},{13,0,203},{14,0,54},{14,0,99},{14,0, +114},{14,0,388},{16,0,85},{17,0,9},{17,0,33},{20,0,25},{20,0,28},{20,0,29},{21,0 +,9},{21,0,10},{21,0,34},{150,0,17},{7,11,1035},{138,11,737},{7,11,690},{9,11,217 +},{9,11,587},{140,11,521},{6,0,919},{7,11,706},{7,11,1058},{138,11,538},{7,10, +1853},{138,10,437},{136,10,419},{6,0,280},{10,0,502},{11,0,344},{140,0,38},{5,0, +45},{7,0,1161},{11,0,448},{11,0,880},{13,0,139},{13,0,407},{15,0,16},{17,0,95},{ +18,0,66},{18,0,88},{18,0,123},{149,0,7},{11,11,92},{11,11,196},{11,11,409},{11, +11,450},{11,11,666},{11,11,777},{12,11,262},{13,11,385},{13,11,393},{15,11,115}, +{16,11,45},{145,11,82},{136,0,777},{134,11,1744},{4,0,410},{5,10,828},{135,0,521 +},{134,0,673},{7,0,1110},{7,0,1778},{7,10,176},{135,10,178},{5,10,806},{7,10, +1976},{7,11,268},{136,11,569},{4,11,733},{9,11,194},{10,11,92},{11,11,198},{12, +11,84},{12,11,87},{13,11,128},{144,11,74},{4,10,51},{5,0,341},{6,10,4},{7,0,1129 +},{7,10,591},{7,10,849},{7,10,951},{7,10,1613},{7,10,1760},{7,10,1988},{9,10,434 +},{10,10,754},{11,0,414},{11,10,25},{139,10,37},{133,10,902},{135,10,928},{135,0 +,787},{132,0,436},{134,10,270},{7,0,1587},{135,0,1707},{6,0,377},{7,0,1025},{9,0 +,613},{145,0,104},{7,11,982},{7,11,1361},{10,11,32},{143,11,56},{139,0,96},{132, +0,451},{132,10,416},{142,10,372},{5,10,152},{5,10,197},{7,10,340},{7,10,867},{7, +11,306},{10,10,548},{10,10,581},{11,10,6},{12,10,3},{12,10,19},{14,10,110},{142, +10,289},{134,0,680},{134,11,609},{7,0,483},{7,10,190},{8,10,28},{8,10,141},{8,10 +,444},{8,10,811},{9,10,468},{11,10,334},{12,10,24},{12,10,386},{140,10,576},{5, +10,757},{138,0,916},{5,10,721},{135,10,1553},{133,11,178},{134,0,937},{132,10, +898},{133,0,739},{147,0,82},{135,0,663},{146,0,128},{5,10,277},{141,10,247},{134 +,0,1087},{132,10,435},{6,11,381},{7,11,645},{7,11,694},{136,11,546},{7,0,503},{ +135,0,1885},{6,0,1965},{8,0,925},{138,0,955},{4,0,113},{5,0,163},{5,0,735},{7,0, +1009},{9,0,9},{9,0,771},{12,0,90},{13,0,138},{13,0,410},{143,0,128},{4,0,324},{ +138,0,104},{5,10,265},{6,10,212},{135,0,460},{133,11,105},{7,11,261},{7,11,1107} +,{7,11,1115},{7,11,1354},{7,11,1588},{7,11,1705},{7,11,1902},{9,11,465},{10,11, +248},{10,11,349},{10,11,647},{11,11,527},{11,11,660},{11,11,669},{12,11,529},{ +141,11,305},{5,11,438},{9,11,694},{12,11,627},{141,11,210},{152,11,11},{4,0,935} +,{133,0,823},{132,10,702},{5,0,269},{5,10,808},{7,0,434},{7,0,891},{7,10,2045},{ +8,0,339},{9,0,702},{11,0,594},{11,0,718},{145,0,100},{7,0,1014},{9,0,485},{141,0 +,264},{134,0,1713},{7,0,1810},{11,0,866},{12,0,103},{12,11,233},{141,0,495},{4,0 +,423},{10,0,949},{138,0,1013},{135,0,900},{8,11,25},{138,11,826},{5,10,166},{8, +10,739},{140,10,511},{134,0,2018},{7,11,1270},{139,11,612},{4,10,119},{5,10,170} +,{5,10,447},{7,10,1708},{7,10,1889},{9,10,357},{9,10,719},{12,10,486},{140,10, +596},{12,0,574},{140,11,574},{132,11,308},{6,0,964},{6,0,1206},{134,0,1302},{4, +10,450},{135,10,1158},{135,11,150},{136,11,649},{14,0,213},{148,0,38},{9,11,45}, +{9,11,311},{141,11,42},{134,11,521},{7,10,1375},{7,10,1466},{138,10,331},{132,10 +,754},{5,11,339},{7,11,1442},{14,11,3},{15,11,41},{147,11,66},{136,11,378},{134, +0,1022},{5,10,850},{136,10,799},{142,0,143},{135,0,2029},{134,11,1628},{8,0,523} +,{150,0,34},{5,0,625},{135,0,1617},{7,0,275},{7,10,238},{7,10,2033},{8,10,120},{ +8,10,188},{8,10,659},{9,10,598},{10,10,466},{12,10,342},{12,10,588},{13,10,503}, +{14,10,246},{143,10,92},{7,0,37},{8,0,425},{8,0,693},{9,0,720},{10,0,380},{10,0, +638},{11,0,273},{11,0,473},{12,0,61},{143,0,43},{135,11,829},{135,0,1943},{132,0 +,765},{5,11,486},{135,11,1349},{7,11,1635},{8,11,17},{10,11,217},{138,11,295},{4 +,10,201},{7,10,1744},{8,10,602},{11,10,247},{11,10,826},{145,10,65},{138,11,558} +,{11,0,551},{142,0,159},{8,10,164},{146,10,62},{139,11,176},{132,0,168},{136,0, +1010},{134,0,1994},{135,0,91},{138,0,532},{135,10,1243},{135,0,1884},{132,10,907 +},{5,10,100},{10,10,329},{12,10,416},{149,10,29},{134,11,447},{132,10,176},{5,10 +,636},{5,10,998},{7,10,9},{7,10,1508},{8,10,26},{9,10,317},{9,10,358},{10,10,210 +},{10,10,292},{10,10,533},{11,10,555},{12,10,526},{12,10,607},{13,10,263},{13,10 +,459},{142,10,271},{4,11,609},{135,11,756},{6,0,15},{7,0,70},{10,0,240},{147,0, +93},{4,11,930},{133,11,947},{134,0,1227},{134,0,1534},{133,11,939},{133,11,962}, +{5,11,651},{8,11,170},{9,11,61},{9,11,63},{10,11,23},{10,11,37},{10,11,834},{11, +11,4},{11,11,187},{11,11,281},{11,11,503},{11,11,677},{12,11,96},{12,11,130},{12 +,11,244},{14,11,5},{14,11,40},{14,11,162},{14,11,202},{146,11,133},{4,11,406},{5 +,11,579},{12,11,492},{150,11,15},{139,0,392},{6,10,610},{10,10,127},{141,10,27}, +{7,0,655},{7,0,1844},{136,10,119},{4,0,145},{6,0,176},{7,0,395},{137,0,562},{132 +,0,501},{140,11,145},{136,0,1019},{134,0,509},{139,0,267},{6,11,17},{7,11,16},{7 +,11,1001},{7,11,1982},{9,11,886},{10,11,489},{10,11,800},{11,11,782},{12,11,320} +,{13,11,467},{14,11,145},{14,11,387},{143,11,119},{145,11,17},{5,11,458},{134,0, +1099},{7,11,1983},{8,11,0},{8,11,171},{9,11,120},{9,11,732},{10,11,473},{11,11, +656},{11,11,998},{18,11,0},{18,11,2},{147,11,21},{12,11,427},{146,11,38},{10,0, +948},{138,0,968},{7,10,126},{136,10,84},{136,10,790},{4,0,114},{9,0,492},{13,0, +462},{142,0,215},{6,10,64},{12,10,377},{141,10,309},{4,0,77},{5,0,361},{6,0,139} +,{6,0,401},{6,0,404},{7,0,413},{7,0,715},{7,0,1716},{11,0,279},{12,0,179},{12,0, +258},{13,0,244},{142,0,358},{134,0,1717},{7,0,772},{7,0,1061},{7,0,1647},{7,10, +1104},{8,0,82},{11,0,250},{11,0,607},{11,10,269},{11,10,539},{11,10,627},{11,10, +706},{11,10,975},{12,0,311},{12,0,420},{12,10,248},{12,10,434},{12,10,600},{12, +10,622},{13,0,184},{13,0,367},{13,10,297},{13,10,485},{14,10,69},{14,10,409},{ +143,10,108},{135,0,724},{4,11,512},{4,11,519},{133,11,342},{134,0,1133},{145,11, +29},{11,10,977},{141,10,507},{6,0,841},{6,0,1042},{6,0,1194},{10,0,993},{140,0, +1021},{6,11,31},{7,11,491},{7,11,530},{8,11,592},{9,10,34},{11,10,484},{11,11,53 +},{11,11,779},{12,11,167},{12,11,411},{14,11,14},{14,11,136},{15,11,72},{16,11, +17},{144,11,72},{4,0,1021},{5,11,907},{134,0,2037},{6,10,1700},{7,0,373},{7,10, +293},{7,10,382},{7,10,1026},{7,10,1087},{7,10,2027},{8,0,335},{8,0,596},{8,10, +252},{8,10,727},{8,10,729},{9,0,488},{9,10,30},{9,10,199},{9,10,231},{9,10,251}, +{9,10,334},{9,10,361},{9,10,712},{10,10,55},{10,10,60},{10,10,232},{10,10,332},{ +10,10,384},{10,10,396},{10,10,504},{10,10,542},{10,10,652},{11,10,20},{11,10,48} +,{11,10,207},{11,10,291},{11,10,298},{11,10,342},{11,10,365},{11,10,394},{11,10, +620},{11,10,705},{11,10,1017},{12,10,123},{12,10,340},{12,10,406},{12,10,643},{ +13,10,61},{13,10,269},{13,10,311},{13,10,319},{13,10,486},{14,10,234},{15,10,62} +,{15,10,85},{16,10,71},{18,10,119},{148,10,105},{150,0,37},{4,11,208},{5,11,106} +,{6,11,531},{8,11,408},{9,11,188},{138,11,572},{132,0,564},{6,0,513},{135,0,1052 +},{132,0,825},{9,0,899},{140,11,441},{134,0,778},{133,11,379},{7,0,1417},{12,0, +382},{17,0,48},{152,0,12},{132,11,241},{6,10,379},{7,0,1116},{7,10,270},{8,10, +176},{8,10,183},{9,10,432},{9,10,661},{12,10,247},{12,10,617},{146,10,125},{5,10 +,792},{133,10,900},{6,0,545},{7,0,565},{7,0,1669},{10,0,114},{11,0,642},{140,0, +618},{133,0,5},{138,11,7},{132,11,259},{135,0,192},{134,0,701},{136,0,763},{135, +10,1979},{4,10,901},{133,10,776},{10,0,755},{147,0,29},{133,0,759},{4,11,173},{5 +,11,312},{5,11,512},{135,11,1285},{7,11,1603},{7,11,1691},{9,11,464},{11,11,195} +,{12,11,279},{12,11,448},{14,11,11},{147,11,102},{7,0,370},{7,0,1007},{7,0,1177} +,{135,0,1565},{135,0,1237},{4,0,87},{5,0,250},{141,0,298},{4,11,452},{5,11,583}, +{5,11,817},{6,11,433},{7,11,593},{7,11,720},{7,11,1378},{8,11,161},{9,11,284},{ +10,11,313},{139,11,886},{4,11,547},{135,11,1409},{136,11,722},{4,10,37},{5,10, +334},{135,10,1253},{132,10,508},{12,0,107},{146,0,31},{8,11,420},{139,11,193},{ +135,0,814},{135,11,409},{140,0,991},{4,0,57},{7,0,1195},{7,0,1438},{7,0,1548},{7 +,0,1835},{7,0,1904},{9,0,757},{10,0,604},{139,0,519},{132,0,540},{138,11,308},{ +132,10,533},{136,0,608},{144,11,65},{4,0,1014},{134,0,2029},{4,0,209},{5,11,1002 +},{7,0,902},{136,11,745},{134,0,2030},{6,0,303},{7,0,335},{7,0,1437},{7,0,1668}, +{8,0,553},{8,0,652},{8,0,656},{9,0,558},{11,0,743},{149,0,18},{5,11,575},{6,11, +354},{135,11,701},{4,11,239},{6,11,477},{7,11,1607},{11,11,68},{139,11,617},{132 +,0,559},{8,0,527},{18,0,60},{147,0,24},{133,10,920},{138,0,511},{133,0,1017},{ +133,0,675},{138,10,391},{7,10,1952},{139,0,156},{138,11,369},{132,11,367},{133,0 +,709},{6,0,698},{134,0,887},{142,10,126},{134,0,1745},{132,10,483},{13,11,299},{ +142,11,75},{133,0,714},{7,0,8},{136,0,206},{138,10,480},{4,11,694},{9,10,495},{ +146,10,104},{7,11,1248},{11,11,621},{139,11,702},{140,11,687},{132,0,776},{139, +10,1009},{135,0,1272},{134,0,1059},{8,10,653},{13,10,93},{147,10,14},{135,11,213 +},{136,0,406},{133,10,172},{132,0,947},{8,0,175},{10,0,168},{138,0,573},{132,0, +870},{6,0,1567},{151,11,28},{134,11,472},{5,10,260},{136,11,132},{4,11,751},{11, +11,390},{140,11,32},{4,11,409},{133,11,78},{6,11,473},{12,0,554},{145,11,105},{ +133,0,784},{8,0,908},{136,11,306},{139,0,882},{6,0,358},{7,0,1393},{7,11,1759},{ +8,0,396},{8,11,396},{10,0,263},{10,11,263},{14,0,154},{14,11,154},{16,0,48},{16, +11,48},{17,0,8},{145,11,8},{13,11,163},{13,11,180},{18,11,78},{148,11,35},{14,0, +32},{18,0,85},{20,0,2},{152,0,16},{7,0,228},{8,10,167},{8,10,375},{9,10,82},{9, +10,561},{10,0,770},{138,10,620},{132,0,845},{9,0,14},{9,0,441},{10,0,306},{139,0 +,9},{8,10,194},{8,10,756},{11,0,966},{12,0,287},{13,0,342},{13,0,402},{15,0,110} +,{143,0,163},{134,0,1578},{4,0,967},{6,0,1820},{6,0,1847},{140,0,716},{136,0,594 +},{7,0,1428},{7,0,1640},{7,0,1867},{7,11,883},{9,0,169},{9,0,182},{9,0,367},{9,0 +,478},{9,0,506},{9,0,551},{9,0,557},{9,0,648},{9,0,697},{9,0,705},{9,0,725},{9,0 +,787},{9,0,794},{10,0,198},{10,0,214},{10,0,267},{10,0,275},{10,0,456},{10,0,551 +},{10,0,561},{10,0,613},{10,0,627},{10,0,668},{10,0,675},{10,0,691},{10,0,695},{ +10,0,707},{10,0,715},{11,0,183},{11,0,201},{11,0,244},{11,0,262},{11,0,352},{11, +0,439},{11,0,493},{11,0,572},{11,0,591},{11,0,608},{11,0,611},{11,0,646},{11,0, +674},{11,0,711},{11,0,751},{11,0,761},{11,0,776},{11,0,785},{11,0,850},{11,0,853 +},{11,0,862},{11,0,865},{11,0,868},{11,0,875},{11,0,898},{11,0,902},{11,0,903},{ +11,0,910},{11,0,932},{11,0,942},{11,0,957},{11,0,967},{11,0,972},{12,0,148},{12, +0,195},{12,0,220},{12,0,237},{12,0,318},{12,0,339},{12,0,393},{12,0,445},{12,0, +450},{12,0,474},{12,0,505},{12,0,509},{12,0,533},{12,0,591},{12,0,594},{12,0,597 +},{12,0,621},{12,0,633},{12,0,642},{13,0,59},{13,0,60},{13,0,145},{13,0,239},{13 +,0,250},{13,0,329},{13,0,344},{13,0,365},{13,0,372},{13,0,387},{13,0,403},{13,0, +414},{13,0,456},{13,0,470},{13,0,478},{13,0,483},{13,0,489},{14,0,55},{14,0,57}, +{14,0,81},{14,0,90},{14,0,148},{14,0,239},{14,0,266},{14,0,321},{14,0,326},{14,0 +,327},{14,0,330},{14,0,347},{14,0,355},{14,0,401},{14,0,404},{14,0,411},{14,0, +414},{14,0,416},{14,0,420},{15,0,61},{15,0,74},{15,0,87},{15,0,88},{15,0,94},{15 +,0,96},{15,0,116},{15,0,149},{15,0,154},{16,0,50},{16,0,63},{16,0,73},{17,0,2},{ +17,0,66},{17,0,92},{17,0,103},{17,0,112},{17,0,120},{18,0,50},{18,0,54},{18,0,82 +},{18,0,86},{18,0,90},{18,0,111},{18,0,115},{18,0,156},{19,0,40},{19,0,79},{20,0 +,78},{149,0,22},{5,0,161},{135,0,839},{4,0,782},{13,11,293},{142,11,56},{133,11, +617},{139,11,50},{135,10,22},{145,0,64},{5,10,639},{7,10,1249},{139,10,896},{138 +,0,998},{135,11,2042},{4,11,546},{142,11,233},{6,0,1043},{134,0,1574},{134,0, +1496},{4,10,102},{7,10,815},{7,10,1699},{139,10,964},{12,0,781},{142,0,461},{4, +11,313},{133,11,577},{6,0,639},{6,0,1114},{137,0,817},{8,11,184},{141,11,433},{7 +,0,1814},{135,11,935},{10,0,997},{140,0,958},{4,0,812},{137,11,625},{132,10,899} +,{136,10,795},{5,11,886},{6,11,46},{6,11,1790},{7,11,14},{7,11,732},{7,11,1654}, +{8,11,95},{8,11,327},{8,11,616},{10,11,598},{10,11,769},{11,11,134},{11,11,747}, +{12,11,378},{142,11,97},{136,0,139},{6,10,52},{9,10,104},{9,10,559},{12,10,308}, +{147,10,87},{133,11,1021},{132,10,604},{132,10,301},{136,10,779},{7,0,643},{136, +0,236},{132,11,153},{134,0,1172},{147,10,32},{133,11,798},{4,11,587},{134,0,1338 +},{6,11,598},{7,11,42},{8,11,695},{10,11,212},{11,11,158},{14,11,196},{145,11,85 +},{135,10,508},{5,11,957},{5,11,1008},{135,11,249},{4,11,129},{135,11,465},{5,0, +54},{7,11,470},{7,11,1057},{7,11,1201},{9,11,755},{11,11,906},{140,11,527},{7,11 +,908},{146,11,7},{5,11,148},{136,11,450},{144,11,1},{4,0,256},{135,0,1488},{6,10 +,310},{7,10,1849},{8,10,72},{8,10,272},{8,10,431},{9,0,351},{9,10,12},{10,10,563 +},{10,10,630},{10,10,796},{10,10,810},{11,10,367},{11,10,599},{11,10,686},{140, +10,672},{6,0,1885},{6,0,1898},{6,0,1899},{140,0,955},{4,0,714},{133,0,469},{6,0, +1270},{134,0,1456},{132,0,744},{6,0,313},{7,10,537},{8,10,64},{9,10,127},{10,10, +496},{12,10,510},{141,10,384},{4,10,244},{4,11,217},{5,11,710},{7,10,233},{7,11, +1926},{9,11,428},{9,11,708},{10,11,254},{10,11,296},{10,11,720},{11,11,109},{11, +11,255},{12,11,165},{12,11,315},{13,11,107},{13,11,203},{14,11,54},{14,11,99},{ +14,11,114},{14,11,388},{16,11,85},{17,11,9},{17,11,33},{20,11,25},{20,11,28},{20 +,11,29},{21,11,9},{21,11,10},{21,11,34},{150,11,17},{138,0,402},{7,0,969},{146,0 +,55},{8,0,50},{137,0,624},{134,0,1355},{132,0,572},{134,10,1650},{10,10,702},{ +139,10,245},{10,0,847},{142,0,445},{6,0,43},{7,0,38},{8,0,248},{138,0,513},{133, +0,369},{137,10,338},{133,0,766},{133,0,363},{133,10,896},{8,11,392},{11,11,54},{ +13,11,173},{13,11,294},{148,11,7},{134,0,678},{7,11,1230},{136,11,531},{6,0,258} +,{140,0,409},{5,0,249},{148,0,82},{7,10,1117},{136,10,539},{5,0,393},{6,0,378},{ +7,0,1981},{9,0,32},{9,0,591},{10,0,685},{10,0,741},{142,0,382},{133,0,788},{134, +0,1281},{134,0,1295},{7,0,1968},{141,0,509},{4,0,61},{5,0,58},{5,0,171},{5,0,683 +},{6,0,291},{6,0,566},{7,0,1650},{11,0,523},{12,0,273},{12,0,303},{15,0,39},{143 +,0,111},{6,0,706},{134,0,1283},{134,0,589},{135,11,1433},{133,11,435},{5,10,4},{ +5,10,810},{6,10,13},{6,10,538},{6,10,1690},{6,10,1726},{7,0,1059},{7,10,1819},{8 +,10,148},{8,10,696},{8,10,791},{12,10,125},{13,0,54},{143,10,9},{135,10,1268},{5 +,11,85},{6,11,419},{7,11,134},{7,11,305},{7,11,361},{7,11,1337},{8,11,71},{140, +11,519},{137,0,824},{140,11,688},{5,11,691},{7,10,1385},{7,11,345},{9,11,94},{11 +,10,582},{11,10,650},{11,10,901},{11,10,949},{12,10,232},{12,10,236},{12,11,169} +,{13,10,413},{13,10,501},{146,10,116},{4,0,917},{133,0,1005},{5,11,183},{6,11, +582},{7,0,1598},{9,11,344},{10,11,679},{140,11,435},{4,10,925},{5,10,803},{8,10, +698},{138,10,828},{132,0,919},{135,11,511},{139,10,992},{4,0,255},{5,0,302},{6,0 +,132},{7,0,128},{7,0,283},{7,0,1299},{10,0,52},{10,0,514},{11,0,925},{13,0,92},{ +142,0,309},{134,0,1369},{135,10,1847},{134,0,328},{7,11,1993},{136,11,684},{133, +10,383},{137,0,173},{134,11,583},{134,0,1411},{5,11,704},{8,11,357},{10,11,745}, +{14,11,426},{17,11,94},{19,0,65},{147,11,57},{9,10,660},{138,10,347},{4,11,179}, +{5,11,198},{133,11,697},{7,11,347},{7,11,971},{8,11,181},{138,11,711},{141,0,442 +},{7,10,572},{9,10,592},{11,0,842},{11,0,924},{11,10,680},{12,10,356},{12,10,550 +},{13,0,317},{13,0,370},{13,0,469},{13,0,471},{14,0,397},{18,0,69},{146,0,145},{ +14,11,19},{14,11,28},{144,11,29},{136,0,534},{4,11,243},{5,11,203},{7,11,19},{7, +11,71},{7,11,113},{10,11,405},{11,11,357},{142,11,240},{6,0,210},{10,0,845},{138 +,0,862},{7,11,1351},{9,11,581},{10,11,639},{11,11,453},{140,11,584},{7,11,1450}, +{139,11,99},{10,0,892},{12,0,719},{144,0,105},{4,0,284},{6,0,223},{134,11,492},{ +5,11,134},{6,11,408},{6,11,495},{135,11,1593},{136,0,529},{137,0,807},{4,0,218}, +{7,0,526},{143,0,137},{6,0,1444},{142,11,4},{132,11,665},{4,0,270},{4,11,248},{5 +,0,192},{6,0,332},{7,0,1322},{7,11,137},{137,11,349},{140,0,661},{7,0,1517},{7, +10,748},{11,0,597},{11,10,700},{14,0,76},{14,0,335},{148,0,33},{5,11,371},{135, +11,563},{146,11,57},{133,10,127},{133,0,418},{4,11,374},{7,11,547},{7,11,1700},{ +7,11,1833},{139,11,858},{6,10,198},{140,10,83},{7,11,1812},{13,11,259},{13,11, +356},{14,11,242},{147,11,114},{5,10,276},{6,10,55},{7,0,379},{7,10,1369},{8,0, +481},{137,0,377},{138,11,286},{5,0,1003},{6,0,149},{6,10,1752},{136,10,726},{6, +11,1647},{7,11,1552},{7,11,2010},{8,0,262},{9,0,627},{9,11,494},{9,11,509},{10,0 +,18},{11,0,214},{11,0,404},{11,0,457},{11,0,780},{11,0,913},{13,0,401},{142,0, +200},{135,0,742},{136,0,304},{132,0,142},{133,10,764},{6,10,309},{7,10,331},{138 +,10,550},{135,10,1062},{6,11,123},{7,10,986},{7,11,214},{9,11,728},{10,11,157},{ +11,11,346},{11,11,662},{143,11,106},{135,10,1573},{7,0,925},{137,0,799},{4,0,471 +},{5,0,51},{6,0,602},{8,0,484},{138,0,195},{136,0,688},{132,0,697},{6,0,1169},{6 +,0,1241},{6,10,194},{7,10,133},{10,10,493},{10,10,570},{139,10,664},{140,0,751}, +{5,10,24},{5,10,569},{6,10,3},{6,10,119},{6,10,143},{6,10,440},{7,0,929},{7,10, +599},{7,10,1686},{7,10,1854},{8,10,424},{9,10,43},{9,10,584},{9,10,760},{10,0, +452},{10,10,328},{11,0,878},{11,10,159},{11,10,253},{12,10,487},{12,10,531},{144 +,0,33},{4,11,707},{13,11,106},{18,11,49},{147,11,41},{5,0,221},{5,11,588},{134, +11,393},{134,0,1437},{6,11,211},{7,11,1690},{11,11,486},{140,11,369},{5,10,14},{ +5,10,892},{6,10,283},{7,10,234},{136,10,537},{4,0,988},{136,0,955},{135,0,1251}, +{4,10,126},{8,10,635},{147,10,34},{4,10,316},{135,10,1561},{137,10,861},{4,10,64 +},{5,10,352},{5,10,720},{6,10,368},{139,10,359},{134,0,192},{4,0,132},{5,0,69},{ +135,0,1242},{7,10,1577},{10,10,304},{10,10,549},{12,10,365},{13,10,220},{13,10, +240},{142,10,33},{4,0,111},{6,11,219},{135,0,865},{5,11,582},{6,11,1646},{7,11, +99},{7,11,1962},{7,11,1986},{8,11,515},{8,11,773},{9,11,23},{9,11,491},{12,11, +620},{14,11,52},{145,11,50},{132,0,767},{7,11,568},{148,11,21},{5,11,851},{6,0, +42},{7,0,1416},{7,0,2005},{8,0,131},{8,0,466},{9,0,672},{13,0,252},{148,0,103},{ +135,0,1050},{6,10,175},{137,10,289},{5,10,432},{133,10,913},{6,0,44},{136,0,368} +,{135,11,784},{132,0,570},{133,0,120},{139,10,595},{140,0,29},{6,0,227},{135,0, +1589},{4,11,98},{7,11,1365},{9,11,422},{9,11,670},{10,11,775},{11,11,210},{13,11 +,26},{13,11,457},{141,11,476},{140,10,80},{5,10,931},{134,10,1698},{133,0,522},{ +134,0,1120},{135,0,1529},{12,0,739},{14,0,448},{142,0,467},{11,10,526},{11,10, +939},{141,10,290},{5,10,774},{6,10,1637},{6,10,1686},{134,10,1751},{6,0,1667},{ +135,0,2036},{7,10,1167},{11,10,934},{13,10,391},{145,10,76},{137,11,147},{6,10, +260},{7,10,1484},{11,11,821},{12,11,110},{12,11,153},{18,11,41},{150,11,19},{6,0 +,511},{6,10,573},{140,0,132},{5,0,568},{6,0,138},{135,0,1293},{132,0,1020},{8,0, +258},{9,0,208},{137,0,359},{4,0,565},{8,0,23},{136,0,827},{134,0,344},{4,0,922}, +{5,0,1023},{13,11,477},{14,11,120},{148,11,61},{134,0,240},{5,11,209},{6,11,30}, +{11,11,56},{139,11,305},{4,10,292},{4,10,736},{5,10,871},{6,0,171},{6,10,1689},{ +7,0,1002},{7,0,1324},{7,10,1944},{9,0,415},{9,10,580},{14,0,230},{146,0,68},{9, +11,635},{139,11,559},{4,11,150},{5,11,303},{134,11,327},{6,10,63},{135,10,920},{ +133,10,793},{8,11,192},{10,11,78},{10,11,555},{11,11,308},{13,11,359},{147,11,95 +},{135,11,786},{135,11,1712},{136,0,402},{6,0,754},{6,11,1638},{7,11,79},{7,11, +496},{9,11,138},{10,11,336},{11,11,12},{12,11,412},{12,11,440},{142,11,305},{4,0 +,716},{141,0,31},{133,0,982},{5,10,67},{6,10,62},{6,10,374},{7,10,1391},{8,0,691 +},{136,0,731},{9,10,790},{140,10,47},{139,11,556},{151,11,1},{7,11,204},{7,11, +415},{8,11,42},{10,11,85},{11,11,33},{11,11,564},{12,11,571},{149,11,1},{7,11, +610},{7,11,1501},{136,0,888},{4,10,391},{135,10,1169},{5,0,847},{9,0,840},{138,0 +,803},{137,0,823},{134,0,785},{8,0,152},{9,0,53},{9,0,268},{9,0,901},{10,0,518}, +{10,0,829},{11,0,188},{13,0,74},{14,0,46},{15,0,17},{15,0,33},{17,0,40},{18,0,36 +},{19,0,20},{22,0,1},{152,0,2},{4,11,3},{5,11,247},{5,11,644},{7,11,744},{7,11, +1207},{7,11,1225},{7,11,1909},{146,11,147},{136,0,532},{135,0,681},{132,10,271}, +{140,0,314},{140,0,677},{4,0,684},{136,0,384},{5,11,285},{9,11,67},{13,11,473},{ +143,11,82},{4,10,253},{5,10,544},{7,10,300},{137,10,340},{7,0,110},{7,0,447},{8, +0,290},{8,0,591},{9,0,382},{9,0,649},{11,0,71},{11,0,155},{11,0,313},{12,0,5},{ +13,0,325},{142,0,287},{134,0,1818},{136,0,1007},{138,0,321},{7,0,360},{7,0,425}, +{9,0,66},{9,0,278},{138,0,644},{133,10,818},{5,0,385},{5,10,541},{6,10,94},{6,10 +,499},{7,10,230},{139,10,321},{4,10,920},{5,10,25},{5,10,790},{6,10,457},{7,10, +853},{136,10,788},{4,0,900},{133,0,861},{5,0,254},{7,0,985},{136,0,73},{7,0,1959 +},{136,0,683},{134,10,1765},{133,10,822},{132,10,634},{4,11,29},{6,11,532},{7,11 +,1628},{7,11,1648},{9,11,303},{9,11,350},{10,11,433},{11,11,97},{11,11,557},{11, +11,745},{12,11,289},{12,11,335},{12,11,348},{12,11,606},{13,11,116},{13,11,233}, +{13,11,466},{14,11,181},{14,11,209},{14,11,232},{14,11,236},{14,11,300},{16,11, +41},{148,11,97},{6,10,36},{7,10,658},{8,10,454},{147,0,86},{135,11,1692},{132,0, +725},{5,11,501},{7,11,1704},{9,11,553},{11,11,520},{12,11,557},{141,11,249},{134 +,0,196},{133,0,831},{136,0,723},{7,0,1897},{13,0,80},{13,0,437},{145,0,74},{4,0, +992},{6,0,627},{136,0,994},{135,11,1294},{132,10,104},{5,0,848},{6,0,66},{136,0, +764},{4,0,36},{7,0,1387},{10,0,205},{139,0,755},{6,0,1046},{134,0,1485},{134,0, +950},{132,0,887},{14,0,450},{148,0,111},{7,0,620},{7,0,831},{9,10,542},{9,10,566 +},{138,10,728},{6,0,165},{138,0,388},{139,10,263},{4,0,719},{135,0,155},{138,10, +468},{6,11,453},{144,11,36},{134,11,129},{5,0,533},{7,0,755},{138,0,780},{134,0, +1465},{4,0,353},{6,0,146},{6,0,1789},{7,0,427},{7,0,990},{7,0,1348},{9,0,665},{9 +,0,898},{11,0,893},{142,0,212},{7,10,87},{142,10,288},{4,0,45},{135,0,1257},{7, +10,988},{7,10,1939},{9,10,64},{9,10,502},{12,0,7},{12,10,34},{13,10,12},{13,10, +234},{147,10,77},{4,0,607},{5,11,60},{6,11,504},{7,11,614},{7,11,1155},{140,11,0 +},{135,10,141},{8,11,198},{11,11,29},{140,11,534},{140,0,65},{136,0,816},{132,10 +,619},{139,0,88},{5,10,246},{8,10,189},{9,10,355},{9,10,512},{10,10,124},{10,10, +453},{11,10,143},{11,10,416},{11,10,859},{141,10,341},{4,11,379},{135,11,1397},{ +4,0,600},{137,0,621},{133,0,367},{134,0,561},{6,0,559},{134,0,1691},{6,0,585},{ +134,11,585},{135,11,1228},{4,11,118},{5,10,678},{6,11,274},{6,11,361},{7,11,75}, +{141,11,441},{135,11,1818},{137,11,841},{5,0,573},{6,0,287},{7,10,862},{7,10, +1886},{138,10,179},{132,10,517},{140,11,693},{5,11,314},{6,11,221},{7,11,419},{ +10,11,650},{11,11,396},{12,11,156},{13,11,369},{14,11,333},{145,11,47},{140,10, +540},{136,10,667},{11,10,403},{146,10,83},{5,10,761},{134,0,672},{9,0,157},{10, +10,131},{140,10,72},{6,11,460},{135,0,714},{134,0,456},{133,0,925},{5,11,682},{ +135,11,1887},{136,11,510},{136,11,475},{133,11,1016},{7,11,602},{8,11,179},{9,0, +19},{10,11,781},{140,11,126},{6,11,329},{138,11,111},{6,0,822},{134,0,1473},{144 +,11,86},{11,0,113},{139,11,113},{5,11,821},{134,11,1687},{133,10,449},{7,0,463}, +{8,10,103},{145,0,69},{7,10,2028},{138,10,641},{6,0,193},{7,0,240},{7,0,1682},{ +10,0,51},{10,0,640},{11,0,410},{13,0,82},{14,0,247},{14,0,331},{142,0,377},{6,0, +471},{11,0,411},{142,0,2},{5,11,71},{7,11,1407},{9,11,388},{9,11,704},{10,11,261 +},{10,11,619},{11,11,547},{11,11,619},{143,11,157},{136,0,633},{135,0,1148},{6,0 +,554},{7,0,1392},{7,10,1274},{7,10,1386},{7,11,2008},{9,11,337},{10,11,517},{12, +0,129},{146,10,87},{6,10,187},{7,0,803},{7,10,1203},{8,0,542},{8,10,380},{14,10, +117},{149,10,28},{6,10,297},{7,10,793},{139,10,938},{7,10,464},{8,0,438},{11,0, +363},{11,10,105},{12,10,231},{14,10,386},{15,10,102},{148,10,75},{5,11,16},{6,11 +,86},{6,11,603},{7,11,292},{7,11,561},{8,11,257},{8,11,382},{9,11,721},{9,11,778 +},{11,11,581},{140,11,466},{4,11,486},{5,11,491},{134,0,717},{132,0,875},{132,11 +,72},{6,11,265},{135,11,847},{4,0,237},{135,0,514},{6,0,392},{7,0,65},{135,0, +2019},{140,11,261},{135,11,922},{137,11,404},{7,10,1010},{11,10,733},{11,10,759} +,{12,0,563},{13,10,34},{14,0,101},{18,0,129},{146,10,45},{7,10,1656},{9,10,369}, +{10,10,338},{10,10,490},{11,10,154},{11,10,545},{11,10,775},{13,10,77},{141,10, +274},{4,0,444},{10,0,146},{140,0,9},{139,11,163},{7,0,1260},{135,0,1790},{9,0, +222},{10,0,43},{139,0,900},{137,11,234},{138,0,971},{137,0,761},{134,0,699},{136 +,11,434},{5,10,20},{6,0,1116},{6,10,298},{6,11,197},{7,0,1366},{7,10,659},{8,11, +205},{137,10,219},{132,11,490},{11,11,820},{150,11,51},{7,10,1440},{11,10,854},{ +11,10,872},{11,10,921},{12,10,551},{13,10,472},{142,10,367},{140,11,13},{132,0, +829},{4,10,439},{140,0,242},{136,10,669},{6,0,593},{6,11,452},{7,11,312},{138,11 +,219},{4,11,333},{9,11,176},{12,11,353},{141,11,187},{7,0,36},{8,0,201},{136,0, +605},{140,0,224},{132,10,233},{134,0,1430},{134,0,1806},{4,0,523},{133,0,638},{6 +,0,1889},{9,0,958},{9,0,971},{9,0,976},{12,0,796},{12,0,799},{12,0,808},{12,0, +835},{12,0,836},{12,0,914},{12,0,946},{15,0,216},{15,0,232},{18,0,183},{18,0,187 +},{18,0,194},{18,0,212},{18,0,232},{149,0,49},{132,10,482},{6,0,827},{134,0,1434 +},{135,10,346},{134,0,2043},{6,0,242},{7,0,227},{7,0,1581},{8,0,104},{9,0,113},{ +9,0,220},{9,0,427},{10,0,136},{10,0,239},{11,0,579},{11,0,1023},{13,0,4},{13,0, +204},{13,0,316},{148,0,86},{134,11,1685},{7,0,148},{8,0,284},{141,0,63},{142,0, +10},{135,11,584},{134,0,1249},{7,0,861},{135,10,334},{5,10,795},{6,10,1741},{137 +,11,70},{132,0,807},{7,11,135},{8,11,7},{8,11,62},{9,11,243},{10,11,658},{10,11, +697},{11,11,456},{139,11,756},{9,11,395},{138,11,79},{137,11,108},{147,0,94},{ +136,0,494},{135,11,631},{135,10,622},{7,0,1510},{135,10,1750},{4,10,203},{135,10 +,1936},{7,11,406},{7,11,459},{8,11,606},{139,11,726},{7,0,1306},{8,0,505},{9,0, +482},{10,0,126},{11,0,225},{12,0,347},{12,0,449},{13,0,19},{14,0,218},{142,0,435 +},{5,0,268},{10,0,764},{12,0,120},{13,0,39},{145,0,127},{142,11,68},{11,10,678}, +{140,10,307},{12,11,268},{12,11,640},{142,11,119},{135,10,2044},{133,11,612},{4, +11,372},{7,11,482},{8,11,158},{9,11,602},{9,11,615},{10,11,245},{10,11,678},{10, +11,744},{11,11,248},{139,11,806},{7,10,311},{9,10,308},{140,10,255},{4,0,384},{ +135,0,1022},{5,11,854},{135,11,1991},{135,10,1266},{4,10,400},{5,10,267},{135,10 +,232},{135,0,1703},{9,0,159},{11,0,661},{140,0,603},{4,0,964},{9,11,106},{9,11, +163},{9,11,296},{10,11,167},{10,11,172},{10,11,777},{11,11,16},{14,0,438},{14,0, +444},{14,0,456},{22,0,60},{150,0,63},{136,0,583},{132,0,515},{8,0,632},{8,0,697} +,{137,0,854},{5,11,195},{135,11,1685},{6,0,1123},{134,0,1365},{134,11,328},{7,11 +,1997},{8,11,730},{139,11,1006},{4,0,136},{133,0,551},{134,0,1782},{7,0,1287},{9 +,0,44},{10,0,552},{10,0,642},{11,0,839},{12,0,274},{12,0,275},{12,0,372},{13,0, +91},{142,0,125},{5,11,751},{11,11,797},{140,11,203},{133,0,732},{4,10,100},{7,0, +679},{7,11,821},{136,0,313},{10,0,361},{142,0,316},{134,0,595},{6,0,147},{7,0, +886},{9,0,753},{138,0,268},{5,10,362},{5,10,443},{6,10,318},{7,10,1019},{139,10, +623},{5,10,463},{136,10,296},{4,10,454},{5,11,950},{5,11,994},{134,11,351},{138, +0,137},{5,10,48},{5,10,404},{6,10,557},{7,10,458},{8,10,597},{10,10,455},{10,10, +606},{11,10,49},{11,10,548},{12,10,476},{13,10,18},{141,10,450},{133,0,414},{135 +,0,1762},{5,11,421},{135,11,47},{5,10,442},{135,10,1984},{134,0,599},{134,0,1749 +},{134,0,1627},{4,0,488},{132,11,350},{137,11,751},{132,0,83},{140,0,676},{133, +11,967},{5,10,55},{7,0,1639},{140,10,161},{4,11,473},{7,11,623},{8,11,808},{9,11 +,871},{9,11,893},{11,11,38},{11,11,431},{12,11,112},{12,11,217},{12,11,243},{12, +11,562},{12,11,683},{13,11,141},{13,11,197},{13,11,227},{13,11,406},{13,11,487}, +{14,11,156},{14,11,203},{14,11,224},{14,11,256},{18,11,58},{150,11,0},{133,10, +450},{7,11,736},{139,11,264},{134,0,278},{4,11,222},{7,11,286},{136,11,629},{135 +,10,869},{140,0,97},{144,0,14},{134,0,1085},{4,10,213},{7,10,223},{136,10,80},{7 +,0,388},{7,0,644},{139,0,781},{132,0,849},{7,0,229},{8,0,59},{9,0,190},{10,0,378 +},{140,0,191},{7,10,381},{7,10,806},{7,10,820},{8,10,354},{8,10,437},{8,10,787}, +{9,10,657},{10,10,58},{10,10,339},{10,10,749},{11,10,914},{12,10,162},{13,10,75} +,{14,10,106},{14,10,198},{14,10,320},{14,10,413},{146,10,43},{141,11,306},{136, +10,747},{134,0,1115},{8,11,146},{16,0,94},{144,0,108},{6,0,700},{6,0,817},{134,0 +,1002},{133,10,692},{4,11,465},{135,11,1663},{134,10,191},{6,0,1414},{135,11,913 +},{132,0,660},{7,0,1035},{138,0,737},{6,10,162},{7,10,1960},{136,10,831},{132,10 +,706},{7,0,690},{9,0,217},{9,0,587},{140,0,521},{138,10,426},{135,10,1235},{6,11 +,82},{7,11,138},{7,11,517},{9,11,673},{139,11,238},{138,0,272},{5,11,495},{7,11, +834},{9,11,733},{139,11,378},{134,0,1744},{132,0,1011},{7,11,828},{142,11,116},{ +4,0,733},{5,11,559},{9,0,194},{10,0,92},{11,0,198},{12,0,84},{141,0,128},{6,11, +21},{6,11,1737},{7,11,1444},{8,11,224},{10,0,57},{138,0,277},{4,10,204},{137,10, +902},{136,10,833},{7,10,366},{9,10,287},{11,0,348},{12,0,99},{12,10,199},{12,10, +556},{12,10,577},{18,0,1},{18,0,11},{147,0,4},{6,0,1981},{136,0,936},{21,0,33},{ +150,0,40},{5,11,519},{138,11,204},{5,10,356},{135,10,224},{134,0,775},{135,0,306 +},{7,10,630},{9,10,567},{11,10,150},{11,10,444},{141,10,119},{5,0,979},{134,10, +539},{133,0,611},{4,11,402},{135,11,1679},{5,0,178},{7,11,2},{8,11,323},{136,11, +479},{5,11,59},{135,11,672},{4,0,1010},{6,0,1969},{138,11,237},{133,11,412},{146 +,11,34},{7,11,1740},{146,11,48},{134,0,664},{139,10,814},{4,11,85},{135,11,549}, +{133,11,94},{133,11,457},{132,0,390},{134,0,1510},{4,10,235},{135,10,255},{4,10, +194},{5,10,584},{6,10,384},{6,11,11},{7,10,583},{7,11,187},{10,10,761},{11,10, +760},{139,10,851},{4,11,522},{139,11,802},{135,0,493},{10,11,776},{13,11,345},{ +142,11,425},{146,0,37},{4,11,52},{135,11,661},{134,0,724},{134,0,829},{133,11, +520},{133,10,562},{4,11,281},{5,11,38},{7,11,194},{7,11,668},{7,11,1893},{137,11 +,397},{5,10,191},{137,10,271},{7,0,1537},{14,0,96},{143,0,73},{4,10,470},{5,0, +473},{6,10,153},{7,10,1503},{7,10,1923},{10,10,701},{11,0,168},{11,10,132},{11, +10,227},{11,10,320},{11,10,436},{11,10,525},{11,10,855},{12,10,41},{12,10,286},{ +13,10,103},{13,10,284},{14,10,255},{14,10,262},{15,10,117},{143,10,127},{133,0, +105},{5,0,438},{9,0,694},{12,0,627},{141,0,210},{133,10,327},{6,10,552},{7,10, +1754},{137,10,604},{134,0,1256},{152,0,11},{5,11,448},{11,11,98},{139,11,524},{5 +,10,80},{6,10,405},{7,0,1626},{7,10,403},{7,10,1502},{8,10,456},{9,10,487},{9,10 +,853},{9,10,889},{10,10,309},{11,10,721},{11,10,994},{12,10,430},{13,10,165},{14 +,11,16},{146,11,44},{132,0,779},{8,0,25},{138,0,826},{4,10,453},{5,10,887},{6,10 +,535},{8,10,6},{8,10,543},{136,10,826},{137,11,461},{140,11,632},{132,0,308},{ +135,0,741},{132,0,671},{7,0,150},{8,0,649},{136,0,1020},{6,11,336},{8,11,552},{9 +,0,99},{9,11,285},{10,11,99},{139,11,568},{134,0,521},{5,0,339},{14,0,3},{15,0, +41},{15,0,166},{147,0,66},{6,11,423},{7,11,665},{7,11,1210},{9,11,218},{141,11, +222},{5,10,101},{5,11,256},{6,0,543},{6,10,88},{7,10,1677},{9,10,100},{10,10,677 +},{14,10,169},{14,10,302},{14,10,313},{15,10,48},{143,10,84},{4,10,310},{7,10, +708},{7,10,996},{9,10,795},{10,10,390},{10,10,733},{11,10,451},{12,10,249},{14, +10,115},{14,10,286},{143,10,100},{133,10,587},{13,11,417},{14,11,129},{143,11,15 +},{134,0,1358},{136,11,554},{132,10,498},{7,10,217},{8,10,140},{138,10,610},{135 +,11,989},{135,11,634},{6,0,155},{140,0,234},{135,11,462},{132,11,618},{134,0, +1628},{132,0,766},{4,11,339},{5,10,905},{135,11,259},{135,0,829},{4,11,759},{141 +,11,169},{4,10,456},{7,0,1445},{7,10,358},{7,10,1637},{8,10,643},{139,10,483},{5 +,0,486},{135,0,1349},{5,11,688},{135,11,712},{7,0,1635},{8,0,17},{10,0,217},{10, +0,295},{12,0,2},{140,11,2},{138,0,558},{150,10,56},{4,11,278},{5,11,465},{135,11 +,1367},{136,11,482},{133,10,535},{6,0,1362},{6,0,1461},{10,11,274},{10,11,625},{ +139,11,530},{5,0,599},{5,11,336},{6,11,341},{6,11,478},{6,11,1763},{136,11,386}, +{7,10,1748},{137,11,151},{134,0,1376},{133,10,539},{135,11,73},{135,11,1971},{ +139,11,283},{9,0,93},{139,0,474},{6,10,91},{135,10,435},{5,11,396},{6,0,447},{ +134,11,501},{4,10,16},{5,10,316},{5,10,842},{6,10,370},{6,10,1778},{8,10,166},{ +11,10,812},{12,10,206},{12,10,351},{14,10,418},{16,10,15},{16,10,34},{18,10,3},{ +19,10,3},{19,10,7},{20,10,4},{149,10,21},{7,0,577},{7,0,1432},{9,0,475},{9,0,505 +},{9,0,526},{9,0,609},{9,0,689},{9,0,726},{9,0,735},{9,0,738},{10,0,556},{10,0, +674},{10,0,684},{11,0,89},{11,0,202},{11,0,272},{11,0,380},{11,0,415},{11,0,505} +,{11,0,537},{11,0,550},{11,0,562},{11,0,640},{11,0,667},{11,0,688},{11,0,847},{ +11,0,927},{11,0,930},{11,0,940},{12,0,144},{12,0,325},{12,0,329},{12,0,389},{12, +0,403},{12,0,451},{12,0,515},{12,0,604},{12,0,616},{12,0,626},{13,0,66},{13,0, +131},{13,0,167},{13,0,236},{13,0,368},{13,0,411},{13,0,434},{13,0,453},{13,0,461 +},{13,0,474},{14,0,59},{14,0,60},{14,0,139},{14,0,152},{14,0,276},{14,0,353},{14 +,0,402},{15,0,28},{15,0,81},{15,0,123},{15,0,152},{18,0,136},{148,0,88},{4,11, +929},{133,11,799},{136,11,46},{142,0,307},{4,0,609},{7,0,756},{9,0,544},{11,0, +413},{144,0,25},{7,10,619},{10,0,687},{10,10,547},{11,10,122},{140,10,601},{4,0, +930},{133,0,947},{133,0,939},{142,0,21},{4,11,892},{133,11,770},{133,0,962},{5,0 +,651},{8,0,170},{9,0,61},{9,0,63},{10,0,23},{10,0,37},{10,0,834},{11,0,4},{11,0, +187},{11,0,281},{11,0,503},{11,0,677},{12,0,96},{12,0,130},{12,0,244},{14,0,5},{ +14,0,40},{14,0,162},{14,0,202},{146,0,133},{4,0,406},{5,0,579},{12,0,492},{150,0 +,15},{135,11,158},{135,0,597},{132,0,981},{132,10,888},{4,10,149},{138,10,368},{ +132,0,545},{4,10,154},{7,10,1134},{136,10,105},{135,11,2001},{134,0,1558},{4,10, +31},{6,10,429},{7,10,962},{9,10,458},{139,10,691},{132,10,312},{135,10,1642},{6, +0,17},{6,0,1304},{7,0,16},{7,0,1001},{9,0,886},{10,0,489},{10,0,800},{11,0,782}, +{12,0,320},{13,0,467},{14,0,145},{14,0,387},{143,0,119},{135,0,1982},{7,11,1461} +,{12,11,91},{145,0,17},{4,10,236},{132,11,602},{138,0,907},{136,0,110},{5,10,836 +},{5,10,857},{6,10,1680},{7,0,272},{147,0,53},{5,0,458},{7,11,1218},{136,11,303} +,{7,0,1983},{8,0,0},{8,0,171},{9,0,120},{9,0,732},{10,0,473},{10,10,68},{11,0, +656},{11,0,998},{11,10,494},{18,0,0},{18,0,2},{147,0,21},{137,11,662},{4,11,13}, +{5,11,567},{7,11,1498},{9,11,124},{11,11,521},{140,11,405},{4,10,81},{139,10,867 +},{135,11,1006},{7,11,800},{7,11,1783},{138,11,12},{5,10,282},{8,10,650},{9,0, +295},{9,10,907},{138,0,443},{132,11,735},{4,10,775},{4,11,170},{135,11,323},{6,0 +,1844},{10,0,924},{11,11,844},{12,11,104},{140,11,625},{5,11,304},{7,11,1403},{ +140,11,498},{134,0,1232},{4,0,519},{10,0,70},{12,0,26},{14,0,17},{14,0,178},{15, +0,34},{149,0,12},{132,0,993},{4,11,148},{133,11,742},{6,0,31},{7,0,491},{7,0,530 +},{8,0,592},{11,0,53},{11,0,779},{12,0,167},{12,0,411},{14,0,14},{14,0,136},{15, +0,72},{16,0,17},{144,0,72},{133,0,907},{134,0,733},{133,11,111},{4,10,71},{5,10, +376},{7,10,119},{138,10,665},{136,0,55},{8,0,430},{136,11,430},{4,0,208},{5,0, +106},{6,0,531},{8,0,408},{9,0,188},{138,0,572},{11,10,827},{12,0,56},{14,10,34}, +{143,10,148},{134,0,1693},{133,11,444},{132,10,479},{140,0,441},{9,0,449},{10,0, +192},{138,0,740},{134,0,928},{4,0,241},{7,10,607},{136,10,99},{8,11,123},{15,11, +6},{144,11,7},{6,11,285},{8,11,654},{11,11,749},{12,11,190},{12,11,327},{13,11, +120},{13,11,121},{13,11,327},{15,11,47},{146,11,40},{4,10,41},{5,10,74},{7,10, +1627},{11,10,871},{140,10,619},{7,0,1525},{11,10,329},{11,10,965},{12,10,241},{ +14,10,354},{15,10,22},{148,10,63},{132,0,259},{135,11,183},{9,10,209},{137,10, +300},{5,11,937},{135,11,100},{133,10,98},{4,0,173},{5,0,312},{5,0,512},{135,0, +1285},{141,0,185},{7,0,1603},{7,0,1691},{9,0,464},{11,0,195},{12,0,279},{12,0, +448},{14,0,11},{147,0,102},{135,0,1113},{133,10,984},{4,0,452},{5,0,583},{135,0, +720},{4,0,547},{5,0,817},{6,0,433},{7,0,593},{7,0,1378},{8,0,161},{9,0,284},{10, +0,313},{139,0,886},{4,10,182},{6,10,205},{7,10,220},{136,0,722},{150,0,13},{4,10 +,42},{9,10,205},{9,10,786},{138,10,659},{6,0,289},{7,0,1670},{12,0,57},{151,0,4} +,{132,10,635},{14,0,43},{146,0,21},{139,10,533},{135,0,1694},{8,0,420},{139,0, +193},{135,0,409},{132,10,371},{4,10,272},{135,10,836},{5,10,825},{134,10,1640},{ +5,11,251},{5,11,956},{8,11,268},{9,11,214},{146,11,142},{138,0,308},{6,0,1863},{ +141,11,37},{137,10,879},{7,10,317},{135,10,569},{132,11,294},{134,0,790},{5,0, +1002},{136,0,745},{5,11,346},{5,11,711},{136,11,390},{135,0,289},{5,0,504},{9,10 +,307},{139,0,68},{4,0,239},{6,0,477},{7,0,1607},{139,0,617},{149,0,13},{133,0, +609},{133,11,624},{5,11,783},{7,11,1998},{135,11,2047},{133,10,525},{132,0,367}, +{132,11,594},{5,10,493},{134,0,528},{4,10,174},{135,10,911},{8,10,417},{137,10, +782},{132,0,694},{7,0,548},{137,0,58},{4,10,32},{5,10,215},{6,10,269},{7,10,1782 +},{7,10,1892},{10,10,16},{11,10,822},{11,10,954},{141,10,481},{140,0,687},{7,0, +1749},{136,10,477},{132,11,569},{133,10,308},{135,10,1088},{4,0,661},{138,0,1004 +},{5,11,37},{6,11,39},{6,11,451},{7,11,218},{7,11,667},{7,11,1166},{7,11,1687},{ +8,11,662},{144,11,2},{5,10,126},{8,10,297},{9,0,445},{9,10,366},{12,0,53},{12,10 +,374},{141,0,492},{7,10,1551},{139,10,361},{148,0,74},{134,11,508},{135,0,213},{ +132,10,175},{132,10,685},{6,0,760},{6,0,834},{134,0,1248},{7,11,453},{7,11,635}, +{7,11,796},{8,11,331},{9,11,328},{9,11,330},{9,11,865},{10,11,119},{10,11,235},{ +11,11,111},{11,11,129},{11,11,240},{12,11,31},{12,11,66},{12,11,222},{12,11,269} +,{12,11,599},{12,11,689},{13,11,186},{13,11,364},{142,11,345},{7,0,1672},{139,0, +189},{133,10,797},{133,10,565},{6,0,1548},{6,11,98},{7,11,585},{135,11,702},{9,0 +,968},{15,0,192},{149,0,56},{4,10,252},{6,11,37},{7,10,1068},{7,11,299},{7,11, +1666},{8,11,195},{8,11,316},{9,11,178},{9,11,276},{9,11,339},{9,11,536},{10,10, +434},{10,11,102},{10,11,362},{10,11,785},{11,10,228},{11,10,426},{11,11,55},{11, +11,149},{11,11,773},{13,10,231},{13,11,416},{13,11,419},{14,11,38},{14,11,41},{ +14,11,210},{18,10,106},{148,10,87},{4,0,751},{11,0,390},{140,0,32},{4,0,409},{ +133,0,78},{11,11,458},{12,11,15},{140,11,432},{5,10,231},{7,0,1602},{7,10,601},{ +9,10,277},{9,10,674},{10,0,257},{10,0,698},{10,10,178},{10,10,418},{10,10,509},{ +11,0,544},{11,0,585},{11,10,531},{12,0,212},{12,10,113},{12,10,475},{13,0,307},{ +13,10,99},{142,10,428},{6,0,473},{145,0,105},{5,11,645},{6,0,1949},{143,0,156},{ +7,10,1591},{144,10,43},{135,0,1779},{135,10,1683},{4,11,290},{135,11,1356},{134, +0,763},{6,11,70},{7,11,1292},{10,11,762},{139,11,288},{142,0,29},{140,11,428},{7 +,0,883},{7,11,131},{7,11,422},{8,11,210},{140,11,573},{134,0,488},{4,10,399},{5, +10,119},{5,10,494},{7,10,751},{137,10,556},{133,0,617},{132,11,936},{139,0,50},{ +7,0,1518},{139,0,694},{137,0,785},{4,0,546},{135,0,2042},{7,11,716},{13,11,97},{ +141,11,251},{132,11,653},{145,0,22},{134,0,1016},{4,0,313},{133,0,577},{136,11, +657},{8,0,184},{141,0,433},{135,0,935},{6,0,720},{9,0,114},{146,11,80},{7,10,594 +},{7,10,851},{7,10,1858},{9,10,411},{9,10,574},{9,10,666},{9,10,737},{10,10,346} +,{10,10,712},{11,10,246},{11,10,432},{11,10,517},{11,10,647},{11,10,679},{11,10, +727},{12,0,186},{12,0,292},{12,10,304},{12,10,305},{12,10,323},{12,10,483},{12, +10,572},{12,10,593},{12,10,602},{13,10,95},{13,10,101},{13,10,171},{13,10,315},{ +13,10,378},{13,10,425},{13,10,475},{14,0,100},{14,10,63},{14,10,380},{14,10,384} +,{15,10,133},{18,0,70},{18,10,112},{148,10,72},{135,10,1093},{135,11,1836},{132, +10,679},{137,10,203},{11,0,402},{12,0,109},{12,0,431},{13,0,179},{13,0,206},{14, +0,217},{16,0,3},{148,0,53},{7,11,1368},{8,11,232},{8,11,361},{10,11,682},{138,11 +,742},{137,10,714},{5,0,886},{6,0,46},{6,0,1790},{7,0,14},{7,0,732},{7,0,1654},{ +8,0,95},{8,0,327},{8,0,616},{9,0,892},{9,11,534},{10,0,598},{10,0,769},{11,0,134 +},{11,0,747},{12,0,378},{142,0,97},{4,0,969},{136,10,825},{137,11,27},{6,0,727}, +{142,11,12},{133,0,1021},{134,0,1190},{134,11,1657},{5,10,143},{5,10,769},{6,10, +1760},{7,10,682},{7,10,1992},{136,10,736},{132,0,153},{135,11,127},{133,0,798},{ +132,0,587},{6,0,598},{7,0,42},{8,0,695},{10,0,212},{11,0,158},{14,0,196},{145,0, +85},{133,10,860},{6,0,1929},{134,0,1933},{5,0,957},{5,0,1008},{6,10,422},{7,10,0 +},{7,10,1544},{8,11,364},{9,0,577},{11,10,990},{12,0,141},{12,10,453},{13,10,47} +,{141,10,266},{134,0,1319},{4,0,129},{135,0,465},{7,0,470},{7,0,1057},{7,0,1201} +,{9,0,755},{11,0,906},{140,0,527},{7,0,908},{146,0,7},{5,0,148},{136,0,450},{5, +10,515},{137,10,131},{7,10,1605},{11,10,962},{146,10,139},{132,10,646},{134,0, +1166},{4,10,396},{7,10,728},{9,10,117},{13,10,202},{148,10,51},{6,10,121},{6,10, +124},{6,10,357},{7,10,1138},{7,10,1295},{8,10,162},{139,10,655},{14,0,374},{142, +11,374},{138,0,253},{139,0,1003},{5,11,909},{9,11,849},{138,11,805},{133,10,237} +,{7,11,525},{7,11,1579},{8,11,497},{136,11,573},{137,0,46},{132,0,879},{134,0, +806},{135,0,1868},{6,0,1837},{134,0,1846},{6,0,730},{134,0,881},{7,0,965},{7,0, +1460},{7,0,1604},{7,11,193},{7,11,397},{7,11,1105},{8,11,124},{8,11,619},{9,11, +305},{10,11,264},{11,11,40},{12,11,349},{13,11,134},{13,11,295},{14,11,155},{15, +11,120},{146,11,105},{136,0,506},{143,0,10},{4,11,262},{7,10,571},{7,10,1877},{7 +,11,342},{10,10,366},{141,11,23},{133,11,641},{9,10,513},{10,0,22},{10,10,39},{ +12,10,122},{140,10,187},{135,11,1431},{150,11,49},{4,11,99},{6,11,250},{6,11,346 +},{8,11,127},{138,11,81},{6,0,2014},{8,0,928},{10,0,960},{10,0,979},{140,0,996}, +{134,0,296},{132,11,915},{5,11,75},{9,11,517},{10,11,470},{12,11,155},{141,11, +224},{137,10,873},{4,0,854},{140,11,18},{134,0,587},{7,10,107},{7,10,838},{8,10, +550},{138,10,401},{11,0,636},{11,10,588},{11,10,864},{11,10,968},{15,0,145},{15, +10,160},{17,0,34},{19,0,50},{151,0,20},{135,11,216},{7,0,982},{10,0,32},{143,0, +56},{133,10,768},{133,11,954},{6,11,304},{7,11,1114},{8,11,418},{10,11,345},{11, +11,341},{11,11,675},{141,11,40},{9,11,410},{139,11,425},{136,0,941},{4,10,894},{ +133,0,435},{5,0,85},{6,0,419},{7,0,134},{7,0,305},{7,0,361},{7,0,1337},{8,0,71}, +{140,0,519},{140,0,688},{135,0,740},{5,0,691},{7,0,345},{9,0,94},{140,0,169},{5, +0,183},{6,0,582},{10,0,679},{140,0,435},{134,11,14},{6,0,945},{135,0,511},{134, +11,1708},{5,11,113},{6,11,243},{7,11,1865},{11,11,161},{16,11,37},{145,11,99},{ +132,11,274},{137,0,539},{6,10,272},{7,0,1993},{136,0,684},{6,0,659},{134,0,982}, +{4,10,9},{5,10,128},{7,10,368},{11,10,480},{148,10,3},{134,0,583},{132,0,803},{ +133,0,704},{4,0,179},{5,0,198},{133,0,697},{7,0,347},{7,0,971},{7,11,166},{8,0, +181},{138,0,711},{136,10,682},{4,10,2},{7,10,545},{7,10,894},{136,11,521},{135,0 +,481},{132,0,243},{5,0,203},{7,0,19},{7,0,71},{7,0,113},{10,0,405},{11,0,357},{ +142,0,240},{5,11,725},{5,11,727},{135,11,1811},{6,0,826},{137,11,304},{7,0,1450} +,{139,0,99},{133,11,654},{134,0,492},{5,0,134},{6,0,408},{6,0,495},{6,11,273},{7 +,0,1593},{10,11,188},{13,11,377},{146,11,77},{9,10,769},{140,10,185},{135,11,410 +},{142,0,4},{4,0,665},{134,11,1785},{4,0,248},{7,0,137},{137,0,349},{5,10,530},{ +142,10,113},{7,0,1270},{139,0,612},{132,11,780},{5,0,371},{135,0,563},{135,0,826 +},{6,0,1535},{23,0,21},{151,0,23},{4,0,374},{7,0,547},{7,0,1700},{7,0,1833},{139 +,0,858},{133,10,556},{7,11,612},{8,11,545},{8,11,568},{8,11,642},{9,11,717},{10, +11,541},{10,11,763},{11,11,449},{12,11,489},{13,11,153},{13,11,296},{14,11,138}, +{14,11,392},{15,11,50},{16,11,6},{16,11,12},{148,11,9},{9,0,311},{141,0,42},{8, +10,16},{140,10,568},{6,0,1968},{6,0,2027},{138,0,991},{6,0,1647},{7,0,1552},{7,0 +,2010},{9,0,494},{137,0,509},{133,11,948},{6,10,186},{137,10,426},{134,0,769},{ +134,0,642},{132,10,585},{6,0,123},{7,0,214},{9,0,728},{10,0,157},{11,0,346},{11, +0,662},{143,0,106},{142,11,381},{135,0,1435},{4,11,532},{5,11,706},{135,11,662}, +{5,11,837},{134,11,1651},{4,10,93},{5,10,252},{6,10,229},{7,10,291},{9,10,550},{ +139,10,644},{148,0,79},{137,10,749},{134,0,1425},{137,10,162},{4,11,362},{7,11, +52},{7,11,303},{140,11,166},{132,10,381},{4,11,330},{7,11,933},{7,11,2012},{136, +11,292},{135,11,767},{4,0,707},{5,0,588},{6,0,393},{13,0,106},{18,0,49},{147,0, +41},{6,0,211},{7,0,1690},{11,0,486},{140,0,369},{137,11,883},{4,11,703},{135,11, +207},{4,0,187},{5,0,184},{5,0,690},{7,0,1869},{10,0,756},{139,0,783},{132,11,571 +},{134,0,1382},{5,0,175},{6,10,77},{6,10,157},{7,10,974},{7,10,1301},{7,10,1339} +,{7,10,1490},{7,10,1873},{137,10,628},{134,0,1493},{5,11,873},{133,11,960},{134, +0,1007},{12,11,93},{12,11,501},{13,11,362},{14,11,151},{15,11,40},{15,11,59},{16 +,11,46},{17,11,25},{18,11,14},{18,11,134},{19,11,25},{19,11,69},{20,11,16},{20, +11,19},{20,11,66},{21,11,23},{21,11,25},{150,11,42},{11,10,919},{141,10,409},{ +134,0,219},{5,0,582},{6,0,1646},{7,0,99},{7,0,1962},{7,0,1986},{8,0,515},{8,0, +773},{9,0,23},{9,0,491},{12,0,620},{142,0,93},{133,0,851},{5,11,33},{134,11,470} +,{135,11,1291},{134,0,1278},{135,11,1882},{135,10,1489},{132,0,1000},{138,0,982} +,{8,0,762},{8,0,812},{137,0,910},{6,11,47},{7,11,90},{7,11,664},{7,11,830},{7,11 +,1380},{7,11,2025},{8,11,448},{136,11,828},{4,0,98},{4,0,940},{6,0,1819},{6,0, +1834},{6,0,1841},{7,0,1365},{8,0,859},{8,0,897},{8,0,918},{8,10,398},{9,0,422},{ +9,0,670},{9,10,681},{10,0,775},{10,0,894},{10,0,909},{10,0,910},{10,0,935},{11,0 +,210},{11,10,632},{12,0,750},{12,0,755},{13,0,26},{13,0,457},{13,0,476},{16,0, +100},{16,0,109},{18,0,173},{146,0,175},{9,11,417},{137,11,493},{136,10,645},{138 +,0,906},{134,0,1730},{134,10,20},{133,11,1019},{134,0,1185},{8,10,769},{138,0,40 +},{6,11,208},{137,0,147},{140,0,650},{5,0,209},{6,0,30},{11,0,56},{139,0,305},{ +132,0,553},{138,11,344},{6,11,68},{7,11,398},{7,11,448},{7,11,1629},{7,11,1813}, +{8,11,387},{8,11,442},{9,11,710},{10,11,282},{138,11,722},{5,0,597},{14,0,20},{ +142,11,20},{135,0,1614},{135,10,1757},{4,0,150},{5,0,303},{6,0,327},{135,10,937} +,{7,10,1652},{16,0,49},{144,11,49},{8,0,192},{10,0,78},{141,0,359},{135,0,786},{ +143,0,134},{6,0,1638},{7,0,79},{7,0,496},{9,0,138},{10,0,336},{11,0,12},{12,0, +412},{12,0,440},{142,0,305},{136,11,491},{4,10,579},{5,10,226},{5,10,323},{135, +10,960},{7,0,204},{7,0,415},{8,0,42},{10,0,85},{139,0,564},{132,0,614},{4,11,403 +},{5,11,441},{7,11,450},{11,11,101},{12,11,193},{141,11,430},{135,11,1927},{135, +11,1330},{4,0,3},{5,0,247},{5,0,644},{7,0,744},{7,0,1207},{7,0,1225},{7,0,1909}, +{146,0,147},{136,0,942},{4,0,1019},{134,0,2023},{5,10,973},{133,11,679},{5,0,285 +},{9,0,67},{13,0,473},{143,0,82},{7,11,328},{137,11,326},{151,0,8},{6,10,135},{ +135,10,1176},{135,11,1128},{134,0,1309},{135,11,1796},{135,10,314},{4,11,574},{7 +,11,350},{7,11,1024},{8,11,338},{9,11,677},{10,11,808},{139,11,508},{7,11,818},{ +17,11,14},{17,11,45},{18,11,75},{148,11,18},{146,10,4},{135,11,1081},{4,0,29},{6 +,0,532},{7,0,1628},{7,0,1648},{9,0,350},{10,0,433},{11,0,97},{11,0,557},{11,0, +745},{12,0,289},{12,0,335},{12,0,348},{12,0,606},{13,0,116},{13,0,233},{13,0,466 +},{14,0,181},{14,0,209},{14,0,232},{14,0,236},{14,0,300},{16,0,41},{148,0,97},{6 +,10,281},{7,0,318},{8,10,282},{8,10,480},{8,10,499},{9,10,198},{10,10,143},{10, +10,169},{10,10,211},{10,10,417},{10,10,574},{11,10,147},{11,10,395},{12,10,75},{ +12,10,407},{12,10,608},{13,10,500},{142,10,251},{135,11,1676},{135,11,2037},{135 +,0,1692},{5,0,501},{7,0,1704},{9,0,553},{11,0,520},{12,0,557},{141,0,249},{6,0, +1527},{14,0,324},{14,11,324},{15,0,55},{15,0,80},{15,11,55},{143,11,80},{135,10, +1776},{8,0,988},{137,11,297},{132,10,419},{142,0,223},{139,11,234},{7,0,1123},{ +12,0,508},{14,0,102},{14,0,226},{144,0,57},{4,10,138},{7,10,1012},{7,10,1280},{ +137,10,76},{5,10,29},{7,0,1764},{140,10,638},{134,0,2015},{134,0,1599},{138,11, +56},{6,11,306},{7,11,1140},{7,11,1340},{8,11,133},{138,11,449},{139,11,1011},{6, +10,1710},{135,10,2038},{7,11,1763},{140,11,310},{4,10,17},{5,10,23},{6,0,129},{7 +,10,995},{11,10,383},{11,10,437},{12,10,460},{140,10,532},{5,11,329},{136,11,260 +},{133,10,862},{132,0,534},{6,0,811},{135,0,626},{132,11,657},{4,0,25},{5,0,60}, +{6,0,504},{7,0,614},{7,0,1155},{12,0,0},{152,11,7},{7,0,1248},{11,0,621},{139,0, +702},{137,0,321},{8,10,70},{12,10,171},{141,10,272},{10,10,233},{139,10,76},{4,0 +,379},{6,10,442},{135,0,1397},{5,11,66},{7,11,1896},{136,11,288},{134,11,1643},{ +134,10,1709},{4,11,21},{5,11,91},{5,11,570},{5,11,648},{5,11,750},{5,11,781},{6, +11,54},{6,11,112},{6,11,402},{6,11,1732},{7,11,315},{7,11,749},{7,11,1347},{7,11 +,1900},{9,11,78},{9,11,508},{10,11,611},{11,11,510},{11,11,728},{13,11,36},{14, +11,39},{16,11,83},{17,11,124},{148,11,30},{4,0,118},{6,0,274},{6,0,361},{7,0,75} +,{141,0,441},{10,11,322},{10,11,719},{139,11,407},{147,10,119},{12,11,549},{14, +11,67},{147,11,60},{11,10,69},{12,10,105},{12,10,117},{13,10,213},{14,10,13},{14 +,10,62},{14,10,177},{14,10,421},{15,10,19},{146,10,141},{9,0,841},{137,10,309},{ +7,10,608},{7,10,976},{8,11,125},{8,11,369},{8,11,524},{9,10,146},{10,10,206},{10 +,10,596},{10,11,486},{11,11,13},{11,11,381},{11,11,736},{11,11,766},{11,11,845}, +{13,10,218},{13,11,114},{13,11,292},{14,10,153},{142,11,47},{7,11,759},{140,0, +693},{5,0,314},{6,0,221},{7,0,419},{10,0,650},{11,0,396},{12,0,156},{13,0,369},{ +14,0,333},{145,0,47},{6,11,1684},{6,11,1731},{7,11,356},{7,11,1932},{8,11,54},{8 +,11,221},{9,11,225},{9,11,356},{10,11,77},{10,11,446},{10,11,731},{12,11,404},{ +141,11,491},{132,11,375},{4,10,518},{135,10,1136},{4,0,913},{4,11,411},{11,11, +643},{140,11,115},{4,11,80},{133,11,44},{8,10,689},{137,10,863},{138,0,880},{4, +10,18},{7,10,145},{7,10,444},{7,10,1278},{8,10,49},{8,10,400},{9,10,71},{9,10, +250},{10,10,459},{12,10,160},{144,10,24},{136,0,475},{5,0,1016},{5,11,299},{135, +11,1083},{7,0,602},{8,0,179},{10,0,781},{140,0,126},{6,0,329},{138,0,111},{135,0 +,1864},{4,11,219},{7,11,1761},{137,11,86},{6,0,1888},{6,0,1892},{6,0,1901},{6,0, +1904},{9,0,953},{9,0,985},{9,0,991},{9,0,1001},{12,0,818},{12,0,846},{12,0,847}, +{12,0,861},{12,0,862},{12,0,873},{12,0,875},{12,0,877},{12,0,879},{12,0,881},{12 +,0,884},{12,0,903},{12,0,915},{12,0,926},{12,0,939},{15,0,182},{15,0,219},{15,0, +255},{18,0,191},{18,0,209},{18,0,211},{149,0,41},{5,11,328},{135,11,918},{137,0, +780},{12,0,82},{143,0,36},{133,10,1010},{5,0,821},{134,0,1687},{133,11,514},{132 +,0,956},{134,0,1180},{5,10,87},{7,10,313},{7,10,1103},{10,0,112},{10,10,582},{11 +,10,389},{11,10,813},{12,10,385},{13,10,286},{14,10,124},{146,10,108},{5,0,71},{ +7,0,1407},{9,0,704},{10,0,261},{10,0,619},{11,0,547},{11,0,619},{143,0,157},{4,0 +,531},{5,0,455},{5,11,301},{6,11,571},{14,11,49},{146,11,102},{132,10,267},{6,0, +385},{7,0,2008},{9,0,337},{138,0,517},{133,11,726},{133,11,364},{4,11,76},{7,11, +1550},{9,11,306},{9,11,430},{9,11,663},{10,11,683},{11,11,427},{11,11,753},{12, +11,334},{12,11,442},{14,11,258},{14,11,366},{143,11,131},{6,0,1865},{6,0,1879},{ +6,0,1881},{6,0,1894},{6,0,1908},{9,0,915},{9,0,926},{9,0,940},{9,0,943},{9,0,966 +},{9,0,980},{9,0,989},{9,0,1005},{9,0,1010},{12,0,813},{12,0,817},{12,0,840},{12 +,0,843},{12,0,855},{12,0,864},{12,0,871},{12,0,872},{12,0,899},{12,0,905},{12,0, +924},{15,0,171},{15,0,181},{15,0,224},{15,0,235},{15,0,251},{146,0,184},{137,11, +52},{5,0,16},{6,0,86},{6,0,603},{7,0,292},{7,0,561},{8,0,257},{8,0,382},{9,0,721 +},{9,0,778},{11,0,581},{140,0,466},{4,0,486},{5,0,491},{135,10,1121},{4,0,72},{6 +,0,265},{135,0,1300},{135,11,1183},{10,10,249},{139,10,209},{132,10,561},{137,11 +,519},{4,10,760},{4,11,656},{135,11,779},{9,10,154},{140,10,485},{135,11,1793},{ +135,11,144},{136,10,255},{133,0,621},{4,10,368},{135,10,641},{135,11,1373},{7,11 +,554},{7,11,605},{141,11,10},{137,0,234},{5,0,815},{6,0,1688},{134,0,1755},{5,11 +,838},{5,11,841},{134,11,1649},{7,0,1987},{7,0,2040},{136,0,743},{133,11,1012},{ +6,0,197},{136,0,205},{6,0,314},{134,11,314},{144,11,53},{6,11,251},{7,11,365},{7 +,11,1357},{7,11,1497},{8,11,154},{141,11,281},{133,11,340},{6,0,452},{7,0,312},{ +138,0,219},{138,0,589},{4,0,333},{9,0,176},{12,0,353},{141,0,187},{9,10,92},{147 +,10,91},{134,0,1110},{11,0,47},{139,11,495},{6,10,525},{8,10,806},{9,10,876},{ +140,10,284},{8,11,261},{9,11,144},{9,11,466},{10,11,370},{12,11,470},{13,11,144} +,{142,11,348},{137,11,897},{6,11,248},{8,0,863},{8,0,864},{8,0,868},{8,0,884},{9 +,11,546},{10,0,866},{10,0,868},{10,0,873},{10,0,911},{10,0,912},{10,0,944},{10, +11,535},{11,11,681},{12,0,727},{141,11,135},{6,0,300},{135,0,1515},{134,0,1237}, +{139,10,958},{133,10,594},{140,11,250},{134,0,1685},{134,11,567},{7,0,135},{8,0, +7},{8,0,62},{9,0,243},{10,0,658},{10,0,697},{11,0,456},{139,0,756},{9,0,395},{ +138,0,79},{6,10,1641},{136,10,820},{4,10,302},{135,10,1766},{134,11,174},{135,10 +,1313},{135,0,631},{134,10,1674},{134,11,395},{138,0,835},{7,0,406},{7,0,459},{8 +,0,606},{139,0,726},{134,11,617},{134,0,979},{6,10,389},{7,10,149},{9,10,142},{ +138,10,94},{5,11,878},{133,11,972},{6,10,8},{7,10,1881},{8,10,91},{136,11,511},{ +133,0,612},{132,11,351},{4,0,372},{7,0,482},{8,0,158},{9,0,602},{9,0,615},{10,0, +245},{10,0,678},{10,0,744},{11,0,248},{139,0,806},{5,0,854},{135,0,1991},{132,11 +,286},{135,11,344},{7,11,438},{7,11,627},{7,11,1516},{8,11,40},{9,11,56},{9,11, +294},{10,11,30},{10,11,259},{11,11,969},{146,11,148},{135,0,1492},{5,11,259},{7, +11,414},{7,11,854},{142,11,107},{135,10,1746},{6,0,833},{134,0,998},{135,10,24}, +{6,0,750},{135,0,1739},{4,10,503},{135,10,1661},{5,10,130},{7,10,1314},{9,10,610 +},{10,10,718},{11,10,601},{11,10,819},{11,10,946},{140,10,536},{10,10,149},{11, +10,280},{142,10,336},{132,11,738},{135,10,1946},{5,0,195},{135,0,1685},{7,0,1997 +},{8,0,730},{139,0,1006},{151,11,17},{133,11,866},{14,0,463},{14,0,470},{150,0, +61},{4,10,392},{5,0,751},{7,10,1597},{8,0,266},{139,0,578},{5,10,433},{9,10,633} +,{139,10,629},{135,0,821},{6,0,715},{134,0,1325},{133,11,116},{4,11,457},{134,0, +868},{134,0,959},{6,10,234},{138,11,199},{7,0,1053},{7,10,1950},{8,10,680},{11, +10,817},{147,10,88},{7,10,1222},{138,10,386},{5,0,950},{5,0,994},{6,0,351},{134, +0,1124},{134,0,1081},{6,10,5},{7,0,1595},{11,10,249},{12,10,313},{16,10,66},{145 +,10,26},{148,0,59},{5,11,527},{6,11,189},{135,11,859},{5,10,963},{6,10,1773},{11 +,11,104},{11,11,554},{15,11,60},{143,11,125},{135,0,47},{137,0,684},{134,11,116} +,{134,0,1606},{134,0,777},{7,0,1020},{8,10,509},{136,10,792},{135,0,1094},{132,0 +,350},{133,11,487},{4,11,86},{5,11,667},{5,11,753},{6,11,316},{6,11,455},{135,11 +,946},{7,0,1812},{13,0,259},{13,0,356},{14,0,242},{147,0,114},{132,10,931},{133, +0,967},{4,0,473},{7,0,623},{8,0,808},{9,0,871},{9,0,893},{11,0,38},{11,0,431},{ +12,0,112},{12,0,217},{12,0,243},{12,0,562},{12,0,663},{12,0,683},{13,0,141},{13, +0,197},{13,0,227},{13,0,406},{13,0,487},{14,0,156},{14,0,203},{14,0,224},{14,0, +256},{18,0,58},{150,0,0},{138,0,286},{7,10,943},{139,10,614},{135,10,1837},{150, +11,45},{132,0,798},{4,0,222},{7,0,286},{136,0,629},{4,11,79},{7,11,1773},{10,11, +450},{11,11,589},{13,11,332},{13,11,493},{14,11,183},{14,11,334},{14,11,362},{14 +,11,368},{14,11,376},{14,11,379},{19,11,90},{19,11,103},{19,11,127},{148,11,90}, +{4,10,90},{5,0,337},{5,10,545},{7,10,754},{9,10,186},{10,10,72},{10,10,782},{11, +0,513},{11,0,889},{11,0,961},{11,10,577},{11,10,610},{12,0,461},{12,10,354},{12, +10,362},{12,10,595},{13,0,79},{143,0,121},{141,0,306},{136,0,146},{7,0,1646},{9, +10,329},{11,10,254},{141,11,124},{4,0,465},{135,0,1663},{132,0,525},{133,11,663} +,{9,10,187},{10,0,299},{11,10,1016},{17,10,44},{146,0,74},{4,10,506},{7,0,165},{ +7,0,919},{136,10,517},{5,10,295},{135,10,1680},{133,11,846},{134,0,1064},{5,11, +378},{7,11,1402},{7,11,1414},{8,11,465},{9,11,286},{10,11,185},{10,11,562},{10, +11,635},{11,11,31},{11,11,393},{12,11,456},{13,11,312},{18,11,65},{18,11,96},{ +147,11,89},{132,0,596},{7,10,987},{9,10,688},{10,10,522},{11,10,788},{140,10,566 +},{4,11,648},{6,0,82},{6,10,1775},{7,0,138},{7,0,517},{7,0,1741},{139,0,238},{7, +0,1233},{7,10,700},{7,10,940},{8,10,514},{9,10,116},{9,10,535},{10,10,118},{11, +10,107},{11,10,148},{11,10,922},{12,10,254},{12,10,421},{142,10,238},{4,0,962},{ +6,0,1824},{8,0,894},{12,0,708},{12,0,725},{14,0,451},{20,0,94},{22,0,59},{150,0, +62},{5,11,945},{6,11,1656},{6,11,1787},{7,11,167},{8,11,824},{9,11,391},{10,11, +375},{139,11,185},{5,0,495},{7,0,834},{9,0,733},{139,0,378},{4,10,743},{135,11, +1273},{6,0,1204},{7,11,1645},{8,11,352},{137,11,249},{139,10,292},{133,0,559},{ +132,11,152},{7,10,1283},{9,0,499},{9,10,227},{10,0,341},{11,10,325},{11,10,408}, +{14,10,180},{15,0,144},{18,10,47},{147,0,49},{6,0,21},{6,0,1737},{7,0,1444},{136 +,0,224},{133,11,1006},{5,10,81},{7,0,1446},{7,10,146},{7,10,1342},{8,10,53},{8, +10,561},{8,10,694},{8,10,754},{9,0,97},{9,10,115},{9,10,894},{10,10,462},{10,10, +813},{11,10,230},{11,10,657},{11,10,699},{11,10,748},{12,10,119},{12,10,200},{12 +,10,283},{14,10,273},{145,0,15},{5,10,408},{137,10,747},{135,11,431},{135,11,832 +},{6,0,729},{134,0,953},{4,0,727},{5,11,351},{7,11,264},{8,0,565},{136,11,565},{ +134,0,1948},{5,0,519},{5,11,40},{7,11,598},{7,11,1638},{8,11,78},{9,11,166},{9, +11,640},{9,11,685},{9,11,773},{11,11,215},{13,11,65},{14,11,172},{14,11,317},{ +145,11,6},{8,11,60},{9,11,343},{139,11,769},{137,11,455},{134,0,1193},{140,0,790 +},{7,11,1951},{8,11,765},{8,11,772},{140,11,671},{7,11,108},{8,11,219},{8,11,388 +},{9,11,639},{9,11,775},{11,11,275},{140,11,464},{132,11,468},{7,10,30},{8,10,86 +},{8,10,315},{8,10,700},{9,10,576},{9,10,858},{11,10,310},{11,10,888},{11,10,904 +},{12,10,361},{141,10,248},{5,11,15},{6,11,56},{7,11,1758},{8,11,500},{9,11,730} +,{11,11,331},{13,11,150},{142,11,282},{4,0,402},{7,0,2},{8,0,323},{136,0,479},{ +138,10,839},{11,0,580},{142,0,201},{5,0,59},{135,0,672},{137,10,617},{146,0,34}, +{134,11,1886},{4,0,961},{136,0,896},{5,11,205},{6,0,1285},{6,11,438},{137,11,711 +},{134,10,428},{7,10,524},{8,10,169},{8,10,234},{9,10,480},{138,10,646},{148,0, +46},{141,0,479},{133,11,534},{6,0,2019},{134,10,1648},{4,0,85},{7,0,549},{7,10, +1205},{138,10,637},{4,0,663},{5,0,94},{7,11,235},{7,11,1475},{15,11,68},{146,11, +120},{6,11,443},{9,11,237},{9,11,571},{9,11,695},{10,11,139},{11,11,715},{12,11, +417},{141,11,421},{132,0,783},{4,0,682},{8,0,65},{9,10,39},{10,10,166},{11,10, +918},{12,10,635},{20,10,10},{22,10,27},{22,10,43},{150,10,52},{6,0,11},{135,0, +187},{132,0,522},{4,0,52},{135,0,661},{4,0,383},{133,0,520},{135,11,546},{11,0, +343},{142,0,127},{4,11,578},{7,10,157},{7,11,624},{7,11,916},{8,10,279},{10,11, +256},{11,11,87},{139,11,703},{134,10,604},{4,0,281},{5,0,38},{7,0,194},{7,0,668} +,{7,0,1893},{137,0,397},{7,10,945},{11,10,713},{139,10,744},{139,10,1022},{9,0, +635},{139,0,559},{5,11,923},{7,11,490},{12,11,553},{13,11,100},{14,11,118},{143, +11,75},{132,0,975},{132,10,567},{137,10,859},{7,10,1846},{7,11,1846},{8,10,628}, +{136,11,628},{148,0,116},{138,11,750},{14,0,51},{14,11,51},{15,11,7},{148,11,20} +,{132,0,858},{134,0,1075},{4,11,924},{133,10,762},{136,0,535},{133,0,448},{10,10 +,784},{141,10,191},{133,10,298},{7,0,610},{135,0,1501},{7,10,633},{7,10,905},{7, +10,909},{7,10,1538},{9,10,767},{140,10,636},{4,11,265},{7,11,807},{135,11,950},{ +5,11,93},{12,11,267},{144,11,26},{136,0,191},{139,10,301},{135,10,1970},{135,0, +267},{4,0,319},{5,0,699},{138,0,673},{6,0,336},{7,0,92},{7,0,182},{8,0,453},{8,0 +,552},{9,0,204},{9,0,285},{10,0,99},{11,0,568},{11,0,950},{12,0,94},{12,10,644}, +{16,0,20},{16,0,70},{16,10,90},{147,0,55},{6,0,551},{7,0,1308},{7,10,845},{7,11, +994},{8,10,160},{137,10,318},{19,11,1},{19,11,26},{150,11,9},{7,0,1406},{9,0,218 +},{141,0,222},{5,0,256},{138,0,69},{5,11,233},{5,11,320},{6,11,140},{7,11,330},{ +136,11,295},{6,0,1980},{136,0,952},{4,0,833},{137,11,678},{133,11,978},{4,11,905 +},{6,11,1701},{137,11,843},{138,10,735},{136,10,76},{17,0,39},{148,0,36},{18,0, +81},{146,11,81},{14,0,352},{17,0,53},{18,0,146},{18,0,152},{19,0,11},{150,0,54}, +{135,0,634},{138,10,841},{132,0,618},{4,0,339},{4,11,275},{7,0,259},{12,11,376}, +{145,0,73},{132,11,509},{7,11,273},{139,11,377},{4,0,759},{9,10,804},{141,0,169} +,{6,10,96},{135,10,1426},{4,10,651},{133,10,289},{7,0,1075},{8,10,35},{9,10,511} +,{10,10,767},{147,10,118},{6,0,649},{6,0,670},{136,0,482},{5,0,336},{6,0,341},{6 +,0,478},{6,0,1763},{136,0,386},{5,11,802},{7,11,2021},{8,11,805},{14,11,94},{15, +11,65},{16,11,4},{16,11,77},{16,11,80},{145,11,5},{5,11,167},{5,11,899},{6,0, +1035},{6,11,410},{137,11,777},{134,11,1705},{5,0,924},{133,0,969},{132,10,704},{ +135,0,73},{135,11,10},{135,10,1078},{5,11,11},{6,11,117},{6,11,485},{7,11,1133}, +{9,11,582},{9,11,594},{11,11,21},{11,11,818},{12,11,535},{141,11,86},{135,0,1971 +},{4,11,264},{7,11,1067},{8,11,204},{8,11,385},{139,11,953},{6,0,1458},{135,0, +1344},{5,0,396},{134,0,501},{4,10,720},{133,10,306},{4,0,929},{5,0,799},{5,10, +431},{8,0,46},{136,0,740},{7,11,646},{7,11,1730},{11,11,446},{141,11,178},{5,10, +464},{6,10,236},{7,0,276},{7,10,696},{7,10,914},{7,10,1108},{7,10,1448},{9,10,15 +},{9,10,564},{10,10,14},{12,10,565},{13,10,449},{14,10,53},{15,10,13},{16,10,64} +,{145,10,41},{4,0,892},{133,0,770},{6,10,1767},{12,10,194},{145,10,107},{135,0, +158},{5,10,840},{138,11,608},{134,0,1432},{138,11,250},{8,11,794},{9,11,400},{10 +,11,298},{142,11,228},{151,0,25},{7,11,1131},{135,11,1468},{135,0,2001},{9,10, +642},{11,10,236},{142,10,193},{4,10,68},{5,10,634},{6,10,386},{7,10,794},{8,10, +273},{9,10,563},{10,10,105},{10,10,171},{11,10,94},{139,10,354},{136,11,724},{ +132,0,478},{11,11,512},{13,11,205},{19,11,30},{22,11,36},{151,11,19},{7,0,1461}, +{140,0,91},{6,11,190},{7,11,768},{135,11,1170},{4,0,602},{4,10,95},{7,10,416},{8 +,0,211},{139,10,830},{7,10,731},{13,10,20},{143,10,11},{6,0,1068},{135,0,1872},{ +4,0,13},{5,0,567},{7,0,1498},{7,11,1023},{9,0,124},{11,0,521},{140,0,405},{135,0 +,1006},{132,0,735},{138,0,812},{4,0,170},{135,0,323},{6,11,137},{9,11,75},{9,11, +253},{10,11,194},{138,11,444},{5,0,304},{5,10,864},{7,0,1403},{10,10,648},{11,10 +,671},{143,10,46},{135,11,1180},{133,10,928},{4,0,148},{133,0,742},{11,10,986},{ +140,10,682},{133,0,523},{135,11,1743},{7,0,730},{8,10,44},{9,10,884},{10,10,580} +,{11,10,399},{11,10,894},{15,10,122},{18,0,144},{147,0,61},{5,11,760},{7,11,542} +,{8,11,135},{136,11,496},{136,0,981},{133,0,111},{10,0,132},{11,0,191},{11,0,358 +},{139,0,460},{7,11,319},{7,11,355},{7,11,763},{10,11,389},{145,11,43},{134,0, +890},{134,0,1420},{136,11,557},{133,10,518},{133,0,444},{135,0,1787},{135,10, +1852},{8,0,123},{15,0,6},{144,0,7},{6,0,2041},{10,11,38},{139,11,784},{136,0,932 +},{5,0,937},{135,0,100},{4,11,58},{5,11,286},{6,0,995},{6,11,319},{7,11,402},{7, +11,1254},{7,11,1903},{8,11,356},{140,11,408},{4,11,389},{9,11,181},{9,11,255},{ +10,11,8},{10,11,29},{10,11,816},{11,11,311},{11,11,561},{12,11,67},{141,11,181}, +{138,0,255},{4,10,934},{5,0,138},{136,10,610},{4,0,965},{10,0,863},{138,0,898},{ +10,10,804},{138,10,832},{8,10,96},{9,10,36},{10,10,607},{11,10,423},{11,10,442}, +{12,0,631},{12,10,309},{14,10,199},{15,10,90},{145,10,110},{134,0,1394},{4,0,652 +},{8,0,320},{9,10,13},{9,10,398},{9,10,727},{10,10,75},{10,10,184},{10,10,230},{ +10,10,564},{10,10,569},{11,10,973},{12,10,70},{12,10,189},{13,10,57},{13,10,257} +,{22,0,6},{150,0,16},{6,0,897},{134,0,1333},{4,0,692},{133,0,321},{133,11,373},{ +135,0,922},{5,0,619},{133,0,698},{137,10,631},{5,10,345},{135,10,1016},{9,0,957} +,{9,0,1018},{12,0,828},{12,0,844},{12,0,897},{12,0,901},{12,0,943},{15,0,180},{ +18,0,197},{18,0,200},{18,0,213},{18,0,214},{146,0,226},{5,0,917},{134,0,1659},{ +135,0,1100},{134,0,1173},{134,0,1930},{5,0,251},{5,0,956},{8,0,268},{9,0,214},{ +146,0,142},{133,10,673},{137,10,850},{4,10,287},{133,10,1018},{132,11,672},{5,0, +346},{5,0,711},{8,0,390},{11,11,752},{139,11,885},{5,10,34},{10,10,724},{12,10, +444},{13,10,354},{18,10,32},{23,10,24},{23,10,31},{152,10,5},{4,11,710},{134,11, +606},{134,0,744},{134,10,382},{133,11,145},{4,10,329},{7,11,884},{140,11,124},{4 +,11,467},{5,11,405},{134,11,544},{9,10,846},{138,10,827},{133,0,624},{9,11,372}, +{15,11,2},{19,11,10},{147,11,18},{4,11,387},{135,11,1288},{5,0,783},{7,0,1998},{ +135,0,2047},{132,10,906},{136,10,366},{135,11,550},{4,10,123},{4,10,649},{5,10, +605},{7,10,1509},{136,10,36},{134,0,1125},{132,0,594},{133,10,767},{135,11,1227} +,{136,11,467},{4,11,576},{135,11,1263},{4,0,268},{7,0,1534},{135,11,1534},{4,10, +273},{5,10,658},{5,10,995},{5,11,919},{134,11,1673},{133,0,563},{134,10,72},{135 +,10,1345},{4,11,82},{5,11,333},{5,11,904},{6,11,207},{7,11,325},{7,11,1726},{8, +11,101},{10,11,778},{139,11,220},{5,0,37},{5,10,589},{6,0,39},{6,0,451},{7,0,218 +},{7,0,667},{7,0,1166},{7,0,1687},{8,0,662},{144,0,2},{134,0,1332},{133,11,903}, +{134,0,508},{5,10,117},{6,10,514},{6,10,541},{7,10,1164},{7,10,1436},{8,10,220}, +{8,10,648},{10,10,688},{11,10,560},{140,11,147},{6,11,555},{135,11,485},{133,10, +686},{7,0,453},{7,0,635},{7,0,796},{8,0,331},{9,0,330},{9,0,865},{10,0,119},{10, +0,235},{11,0,111},{11,0,129},{11,0,240},{12,0,31},{12,0,66},{12,0,222},{12,0,269 +},{12,0,599},{12,0,684},{12,0,689},{12,0,691},{142,0,345},{135,0,1834},{4,11,705 +},{7,11,615},{138,11,251},{136,11,345},{137,0,527},{6,0,98},{7,0,702},{135,0,991 +},{7,10,196},{10,10,765},{11,0,576},{11,10,347},{11,10,552},{11,10,790},{12,10, +263},{13,10,246},{13,10,270},{13,10,395},{14,0,74},{14,10,176},{14,10,190},{14, +10,398},{14,10,412},{15,10,32},{15,10,63},{16,10,88},{147,10,105},{134,11,90},{ +13,0,84},{141,0,122},{6,0,37},{7,0,299},{7,0,1666},{8,0,195},{8,0,316},{9,0,178} +,{9,0,276},{9,0,339},{9,0,536},{10,0,102},{10,0,362},{10,0,785},{11,0,55},{11,0, +149},{11,0,773},{13,0,416},{13,0,419},{14,0,38},{14,0,41},{142,0,210},{5,10,381} +,{135,10,1792},{7,11,813},{12,11,497},{141,11,56},{7,10,616},{138,10,413},{133,0 +,645},{6,11,125},{135,11,1277},{132,0,290},{6,0,70},{7,0,1292},{10,0,762},{139,0 +,288},{6,10,120},{7,10,1188},{7,10,1710},{8,10,286},{9,10,667},{11,10,592},{139, +10,730},{135,11,1784},{7,0,1315},{135,11,1315},{134,0,1955},{135,10,1146},{7,0, +131},{7,0,422},{8,0,210},{140,0,573},{4,10,352},{135,10,687},{139,0,797},{143,0, +38},{14,0,179},{15,0,151},{150,0,11},{4,10,192},{5,10,49},{6,10,200},{6,10,293}, +{6,10,1696},{135,0,488},{132,0,936},{135,11,703},{6,11,160},{7,11,1106},{9,11, +770},{10,11,618},{11,11,112},{140,11,413},{5,0,453},{134,0,441},{135,0,595},{132 +,10,650},{132,10,147},{6,0,991},{6,0,1182},{12,11,271},{145,11,109},{133,10,934} +,{140,11,221},{132,0,653},{7,0,505},{135,0,523},{134,0,903},{135,11,479},{7,11, +304},{9,11,646},{9,11,862},{10,11,262},{11,11,696},{12,11,208},{15,11,79},{147, +11,108},{146,0,80},{135,11,981},{142,0,432},{132,0,314},{137,11,152},{7,0,1368}, +{8,0,232},{8,0,361},{10,0,682},{138,0,742},{135,11,1586},{4,11,434},{9,0,534},{ +11,11,663},{12,11,210},{13,11,166},{13,11,310},{14,11,373},{147,11,43},{7,11, +1091},{135,11,1765},{6,11,550},{135,11,652},{137,0,27},{142,0,12},{4,10,637},{5, +11,553},{7,11,766},{138,11,824},{7,11,737},{8,11,298},{136,11,452},{7,0,736},{ +139,0,264},{134,0,1657},{133,11,292},{138,11,135},{6,0,844},{134,0,1117},{135,0, +127},{9,10,867},{138,10,837},{6,0,1184},{134,0,1208},{134,0,1294},{136,0,364},{6 +,0,1415},{6,10,170},{7,0,1334},{7,11,393},{8,10,395},{8,10,487},{10,11,603},{11, +0,125},{11,11,206},{141,10,147},{137,11,748},{4,11,912},{137,11,232},{4,10,535}, +{136,10,618},{137,0,792},{7,11,1973},{136,11,716},{135,11,98},{5,0,909},{9,0,849 +},{138,0,805},{4,0,630},{132,0,699},{5,11,733},{14,11,103},{150,10,23},{12,11, +158},{18,11,8},{19,11,62},{20,11,6},{22,11,4},{23,11,2},{151,11,9},{132,0,968},{ +132,10,778},{132,10,46},{5,10,811},{6,10,1679},{6,10,1714},{135,10,2032},{6,0, +1446},{7,10,1458},{9,10,407},{139,10,15},{6,10,34},{7,0,206},{7,0,397},{7,0,621} +,{7,0,640},{7,10,1089},{8,0,124},{8,0,619},{8,10,708},{8,10,721},{9,0,305},{9,0, +643},{9,10,363},{10,0,264},{10,0,628},{11,0,40},{12,0,349},{13,0,134},{13,0,295} +,{14,0,155},{15,0,120},{18,0,105},{148,10,98},{4,0,262},{5,0,641},{135,0,342},{ +137,11,72},{4,0,99},{6,0,250},{6,0,346},{8,0,127},{138,0,81},{132,0,915},{5,0,75 +},{9,0,517},{10,0,470},{12,0,155},{141,0,224},{132,10,462},{11,11,600},{11,11, +670},{141,11,245},{142,0,83},{5,10,73},{6,10,23},{134,10,338},{6,0,1031},{139,11 +,923},{7,11,164},{7,11,1571},{9,11,107},{140,11,225},{134,0,1470},{133,0,954},{6 +,0,304},{8,0,418},{10,0,345},{11,0,341},{139,0,675},{9,0,410},{139,0,425},{4,11, +27},{5,11,484},{5,11,510},{6,11,434},{7,11,1000},{7,11,1098},{8,11,2},{136,11, +200},{134,0,734},{140,11,257},{7,10,725},{8,10,498},{139,10,268},{134,0,1822},{ +135,0,1798},{135,10,773},{132,11,460},{4,11,932},{133,11,891},{134,0,14},{132,10 +,583},{7,10,1462},{8,11,625},{139,10,659},{5,0,113},{5,10,220},{6,0,243},{6,0, +1708},{7,0,1865},{11,0,161},{16,0,37},{145,0,99},{134,11,76},{5,11,461},{135,11, +1925},{140,0,69},{8,11,92},{137,11,221},{139,10,803},{132,10,544},{4,0,274},{134 +,0,922},{132,0,541},{5,0,627},{6,10,437},{6,10,564},{11,10,181},{141,10,183},{ +135,10,1192},{4,11,763},{135,0,166},{133,11,253},{134,0,849},{9,11,73},{10,11, +110},{14,11,185},{145,11,119},{5,11,212},{12,11,35},{141,11,382},{133,0,717},{ +137,0,304},{136,0,600},{133,0,654},{6,0,273},{10,0,188},{13,0,377},{146,0,77},{4 +,10,790},{5,10,273},{134,10,394},{132,0,543},{135,0,410},{11,0,98},{11,0,524},{ +141,0,87},{132,0,941},{135,11,1175},{4,0,250},{6,10,127},{7,0,1612},{7,10,1511}, +{8,10,613},{11,0,186},{12,0,133},{12,10,495},{12,10,586},{12,10,660},{12,10,668} +,{14,10,385},{15,10,118},{17,10,20},{146,10,98},{5,11,816},{134,0,1785},{134,0, +1339},{6,10,230},{7,0,961},{7,0,1085},{7,0,1727},{7,11,1727},{136,0,462},{7,10, +1954},{137,0,636},{132,0,780},{5,11,869},{5,11,968},{6,11,1626},{8,11,734},{136, +11,784},{4,11,542},{6,11,1716},{6,11,1727},{7,11,1082},{7,11,1545},{8,11,56},{8, +11,118},{8,11,412},{8,11,564},{9,11,888},{9,11,908},{10,11,50},{10,11,423},{11, +11,685},{11,11,697},{11,11,933},{12,11,299},{13,11,126},{13,11,136},{13,11,170}, +{141,11,190},{134,11,226},{4,11,232},{9,11,202},{10,11,474},{140,11,433},{137,11 +,500},{5,0,529},{136,10,68},{132,10,654},{4,10,156},{7,10,998},{7,10,1045},{7,10 +,1860},{9,10,48},{9,10,692},{11,10,419},{139,10,602},{6,11,108},{7,0,1276},{7,11 +,1003},{7,11,1181},{8,0,474},{8,11,343},{137,0,652},{7,11,1264},{7,11,1678},{11, +11,945},{12,11,341},{12,11,471},{140,11,569},{134,11,1712},{5,0,948},{12,0,468}, +{19,0,96},{148,0,24},{4,11,133},{7,11,711},{7,11,1298},{7,11,1585},{135,11,1929} +,{6,0,753},{140,0,657},{139,0,941},{6,11,99},{7,11,1808},{145,11,57},{6,11,574}, +{7,11,428},{7,11,1250},{10,11,669},{11,11,485},{11,11,840},{12,11,300},{142,11, +250},{4,0,532},{5,0,706},{135,0,662},{5,0,837},{6,0,1651},{139,0,985},{7,0,1861} +,{9,10,197},{10,10,300},{12,10,473},{13,10,90},{141,10,405},{137,11,252},{6,11, +323},{135,11,1564},{4,0,330},{4,0,863},{7,0,933},{7,0,2012},{7,11,461},{8,0,292} +,{8,11,775},{138,11,435},{132,10,606},{4,11,655},{7,11,850},{17,11,75},{146,11, +137},{135,0,767},{7,10,1978},{136,10,676},{132,0,641},{135,11,1559},{134,0,1233} +,{137,0,242},{4,10,361},{5,10,315},{145,0,114},{137,0,883},{132,10,461},{138,0, +274},{134,0,2008},{134,0,1794},{4,0,703},{135,0,207},{4,10,472},{140,0,285},{132 +,0,571},{5,0,873},{5,0,960},{8,0,823},{8,11,577},{137,0,881},{7,0,617},{10,0,498 +},{11,0,501},{12,0,16},{140,0,150},{138,10,747},{132,0,431},{133,10,155},{7,10, +163},{8,10,319},{9,10,402},{10,10,24},{10,10,681},{11,0,283},{11,0,567},{11,10, +200},{12,10,253},{12,10,410},{142,10,219},{4,11,413},{5,11,677},{8,11,432},{140, +11,280},{5,10,475},{7,10,1780},{9,0,401},{11,10,297},{11,10,558},{14,10,322},{ +147,10,76},{6,0,781},{9,0,134},{10,0,2},{10,0,27},{10,0,333},{11,0,722},{143,0,1 +},{5,0,33},{6,0,470},{139,0,424},{135,0,2006},{7,10,1956},{140,0,783},{136,0,274 +},{135,0,1882},{132,0,794},{135,0,1848},{5,10,944},{134,10,1769},{6,0,47},{7,0, +90},{7,0,664},{7,0,830},{7,0,1380},{7,0,2025},{8,0,448},{136,0,828},{132,10,144} +,{134,0,1199},{4,11,395},{139,11,762},{135,11,1504},{9,0,417},{137,0,493},{9,11, +174},{10,11,164},{11,11,440},{11,11,841},{143,11,98},{134,11,426},{139,11,1002}, +{134,0,295},{134,0,816},{6,10,247},{137,10,555},{133,0,1019},{4,0,620},{5,11,476 +},{10,10,280},{138,10,797},{139,0,464},{5,11,76},{6,11,458},{6,11,497},{7,11,764 +},{7,11,868},{9,11,658},{10,11,594},{11,11,173},{11,11,566},{12,11,20},{12,11, +338},{141,11,200},{134,0,208},{4,11,526},{7,11,1029},{135,11,1054},{132,11,636}, +{6,11,233},{7,11,660},{7,11,1124},{17,11,31},{19,11,22},{151,11,14},{5,10,428},{ +138,0,442},{10,0,930},{140,0,778},{6,0,68},{7,0,448},{7,0,1629},{7,0,1769},{7,0, +1813},{7,10,1717},{8,0,442},{8,0,516},{9,0,710},{10,0,282},{10,0,722},{138,10, +546},{134,0,1128},{11,0,844},{12,0,104},{140,0,625},{4,11,432},{135,11,824},{138 +,10,189},{133,0,787},{133,10,99},{4,11,279},{7,11,301},{137,11,362},{4,10,397},{ +8,0,491},{136,10,555},{4,11,178},{133,11,399},{134,0,711},{144,0,9},{4,0,403},{5 +,0,441},{7,0,450},{10,0,840},{11,0,101},{12,0,193},{141,0,430},{135,11,1246},{12 +,10,398},{20,10,39},{21,10,11},{150,10,41},{4,10,485},{7,10,353},{135,10,1523},{ +6,10,366},{7,10,1384},{7,10,1601},{135,11,1912},{7,0,396},{7,11,396},{138,0,160} +,{137,10,282},{134,11,1692},{4,10,157},{5,10,471},{6,11,202},{10,11,448},{11,11, +208},{12,11,360},{17,11,117},{17,11,118},{18,11,27},{148,11,67},{133,0,679},{137 +,0,326},{136,10,116},{7,11,872},{10,11,516},{139,11,167},{132,11,224},{5,11,546} +,{7,11,35},{8,11,11},{8,11,12},{9,11,315},{9,11,533},{10,11,802},{11,11,166},{12 +,11,525},{142,11,243},{7,0,1128},{135,11,1920},{5,11,241},{8,11,242},{9,11,451}, +{10,11,667},{11,11,598},{140,11,429},{5,10,160},{6,0,737},{7,10,363},{7,10,589}, +{10,10,170},{141,10,55},{135,0,1796},{142,11,254},{4,0,574},{7,0,350},{7,0,1024} +,{8,0,338},{9,0,677},{138,0,808},{134,0,1096},{137,11,516},{4,10,108},{4,11,366} +,{7,0,405},{10,0,491},{139,10,498},{11,11,337},{142,11,303},{134,11,1736},{7,0, +1081},{140,11,364},{7,10,1005},{140,10,609},{4,10,895},{5,10,772},{135,0,1676},{ +135,0,2037},{6,0,1207},{11,11,916},{142,11,419},{14,11,140},{148,11,41},{6,11, +331},{136,11,623},{4,10,926},{5,10,983},{9,0,944},{9,0,969},{9,0,1022},{12,0,913 +},{12,0,936},{15,0,177},{143,0,193},{5,0,354},{135,11,506},{8,0,598},{9,0,664},{ +138,0,441},{4,11,640},{133,11,513},{137,0,297},{132,10,538},{6,10,294},{7,10, +1267},{136,10,624},{7,0,1772},{7,11,1888},{8,11,289},{11,11,45},{12,11,278},{140 +,11,537},{135,10,1325},{138,0,751},{141,0,37},{134,0,1828},{132,10,757},{132,11, +394},{6,0,257},{135,0,1522},{4,0,582},{7,11,1931},{137,0,191},{7,11,574},{7,11, +1719},{137,11,145},{132,11,658},{4,11,369},{138,0,790},{9,11,781},{10,11,144},{ +11,11,385},{13,11,161},{13,11,228},{13,11,268},{148,11,107},{8,0,469},{8,11,374} +,{138,0,47},{6,0,306},{7,0,1140},{7,0,1340},{8,0,133},{138,0,449},{139,0,1011},{ +7,10,1875},{139,10,124},{4,11,344},{6,11,498},{139,11,323},{137,0,299},{132,0, +837},{133,11,906},{5,0,329},{8,0,260},{138,0,10},{134,0,1320},{4,0,657},{146,0, +158},{135,0,1191},{152,0,7},{6,0,1939},{8,0,974},{138,0,996},{135,0,1665},{11,11 +,126},{139,11,287},{143,0,8},{14,11,149},{14,11,399},{143,11,57},{5,0,66},{7,0, +1896},{136,0,288},{5,10,150},{7,0,175},{8,10,603},{9,10,593},{9,10,634},{10,0, +494},{10,10,173},{11,10,462},{11,10,515},{13,10,216},{13,10,288},{142,10,400},{ +134,0,1643},{136,11,21},{4,0,21},{5,0,91},{5,0,648},{5,0,750},{5,0,781},{6,0,54} +,{6,0,112},{6,0,402},{6,0,1732},{7,0,315},{7,0,749},{7,0,1427},{7,0,1900},{9,0, +78},{9,0,508},{10,0,611},{10,0,811},{11,0,510},{11,0,728},{13,0,36},{14,0,39},{ +16,0,83},{17,0,124},{148,0,30},{4,0,668},{136,0,570},{10,0,322},{10,0,719},{139, +0,407},{135,11,1381},{136,11,193},{12,10,108},{141,10,291},{132,11,616},{136,11, +692},{8,0,125},{8,0,369},{8,0,524},{10,0,486},{11,0,13},{11,0,381},{11,0,736},{ +11,0,766},{11,0,845},{13,0,114},{13,0,292},{142,0,47},{134,0,1247},{6,0,1684},{6 +,0,1731},{7,0,356},{8,0,54},{8,0,221},{9,0,225},{9,0,356},{10,0,77},{10,0,446},{ +10,0,731},{12,0,404},{141,0,491},{135,10,1777},{4,10,493},{4,11,305},{144,10,55} +,{4,0,951},{6,0,1809},{6,0,1849},{8,0,846},{8,0,866},{8,0,899},{10,0,896},{12,0, +694},{142,0,468},{5,11,214},{7,11,603},{8,11,611},{9,11,686},{10,11,88},{11,11, +459},{11,11,496},{12,11,463},{12,11,590},{13,11,0},{142,11,214},{132,0,411},{4,0 +,80},{133,0,44},{140,11,74},{143,0,31},{6,10,568},{7,0,669},{7,10,1804},{8,10, +362},{8,10,410},{8,10,830},{9,10,514},{11,10,649},{142,10,157},{6,11,1703},{135, +0,673},{132,10,625},{134,0,1303},{5,0,299},{135,0,1083},{138,0,704},{6,0,275},{6 +,10,158},{7,0,408},{7,10,129},{7,10,181},{8,10,276},{8,10,377},{10,10,523},{11, +10,816},{12,10,455},{13,10,303},{142,10,135},{4,0,219},{7,0,367},{7,0,1713},{7,0 +,1761},{9,0,86},{9,0,537},{10,0,165},{12,0,219},{140,0,561},{4,10,1},{4,11,737}, +{6,11,317},{7,10,1143},{7,10,1463},{8,0,216},{9,10,207},{9,10,390},{9,10,467},{ +10,11,98},{11,10,836},{11,11,294},{12,11,60},{12,11,437},{13,11,64},{13,11,380}, +{142,11,430},{6,11,1758},{8,11,520},{9,11,345},{9,11,403},{142,11,350},{5,11,47} +,{10,11,242},{138,11,579},{5,11,139},{7,11,1168},{138,11,539},{135,0,1319},{4,10 +,295},{4,10,723},{5,10,895},{7,10,1031},{8,10,199},{8,10,340},{9,10,153},{9,10, +215},{10,10,21},{10,10,59},{10,10,80},{10,10,224},{10,10,838},{11,10,229},{11,10 +,652},{12,10,192},{13,10,146},{142,10,91},{140,0,428},{137,10,51},{133,0,514},{5 +,10,309},{140,10,211},{5,10,125},{6,0,1010},{8,10,77},{138,10,15},{4,0,55},{5,0, +301},{6,0,571},{142,0,49},{146,0,102},{136,11,370},{4,11,107},{7,11,613},{8,11, +358},{8,11,439},{8,11,504},{9,11,501},{10,11,383},{139,11,477},{132,11,229},{133 +,0,364},{133,10,439},{4,11,903},{135,11,1816},{11,0,379},{140,10,76},{4,0,76},{4 +,0,971},{7,0,1550},{9,0,306},{9,0,430},{9,0,663},{10,0,683},{10,0,921},{11,0,427 +},{11,0,753},{12,0,334},{12,0,442},{14,0,258},{14,0,366},{143,0,131},{137,0,52}, +{4,11,47},{6,11,373},{7,11,452},{7,11,543},{7,11,1714},{7,11,1856},{9,11,6},{11, +11,257},{139,11,391},{4,10,8},{7,10,1152},{7,10,1153},{7,10,1715},{9,10,374},{10 +,10,478},{139,10,648},{4,11,785},{133,11,368},{135,10,1099},{135,11,860},{5,11, +980},{134,11,1754},{134,0,1258},{6,0,1058},{6,0,1359},{7,11,536},{7,11,1331},{ +136,11,143},{4,0,656},{135,0,779},{136,10,87},{5,11,19},{6,11,533},{146,11,126}, +{7,0,144},{138,10,438},{5,11,395},{5,11,951},{134,11,1776},{135,0,1373},{7,0,554 +},{7,0,605},{141,0,10},{4,10,69},{5,10,122},{9,10,656},{138,10,464},{5,10,849},{ +134,10,1633},{5,0,838},{5,0,841},{134,0,1649},{133,0,1012},{139,10,499},{7,10, +476},{7,10,1592},{138,10,87},{6,0,251},{7,0,365},{7,0,1357},{7,0,1497},{8,0,154} +,{141,0,281},{132,11,441},{132,11,695},{7,11,497},{9,11,387},{147,11,81},{133,0, +340},{14,10,283},{142,11,283},{134,0,810},{135,11,1894},{139,0,495},{5,11,284},{ +6,11,49},{6,11,350},{7,11,1},{7,11,377},{7,11,1693},{8,11,18},{8,11,678},{9,11, +161},{9,11,585},{9,11,671},{9,11,839},{11,11,912},{141,11,427},{5,10,859},{7,10, +1160},{8,10,107},{9,10,291},{9,10,439},{10,10,663},{11,10,609},{140,10,197},{8,0 +,261},{9,0,144},{9,0,466},{10,0,370},{12,0,470},{13,0,144},{142,0,348},{137,0, +897},{6,0,248},{9,0,546},{10,0,535},{11,0,681},{141,0,135},{4,0,358},{135,0,1496 +},{134,0,567},{136,0,445},{4,10,117},{6,10,372},{7,10,1905},{142,10,323},{4,10, +722},{139,10,471},{6,0,697},{134,0,996},{7,11,2007},{9,11,101},{9,11,450},{10,11 +,66},{10,11,842},{11,11,536},{140,11,587},{132,0,577},{134,0,1336},{9,10,5},{12, +10,216},{12,10,294},{12,10,298},{12,10,400},{12,10,518},{13,10,229},{143,10,139} +,{6,0,174},{138,0,917},{134,10,1774},{5,10,12},{7,10,375},{9,10,88},{9,10,438},{ +11,10,270},{139,11,62},{134,11,1766},{6,11,0},{7,10,816},{7,10,1241},{7,11,84},{ +9,10,283},{9,10,520},{10,10,213},{10,10,307},{10,10,463},{10,10,671},{10,10,746} +,{11,10,401},{11,10,794},{11,11,895},{12,10,517},{17,11,11},{18,10,107},{147,10, +115},{5,0,878},{133,0,972},{6,11,1665},{7,11,256},{7,11,1388},{138,11,499},{4,10 +,258},{136,10,639},{4,11,22},{5,11,10},{6,10,22},{7,10,903},{7,10,1963},{7,11, +848},{8,11,97},{138,10,577},{5,10,681},{136,10,782},{133,11,481},{132,0,351},{4, +10,664},{5,10,804},{139,10,1013},{6,11,134},{7,11,437},{7,11,959},{9,11,37},{14, +11,285},{14,11,371},{144,11,60},{7,11,486},{8,11,155},{11,11,93},{140,11,164},{ +132,0,286},{7,0,438},{7,0,627},{7,0,1516},{8,0,40},{9,0,56},{9,0,294},{10,0,30}, +{11,0,969},{11,0,995},{146,0,148},{5,11,591},{135,11,337},{134,0,1950},{133,10, +32},{138,11,500},{5,11,380},{5,11,650},{136,11,310},{4,11,364},{7,11,1156},{7,11 +,1187},{137,11,409},{4,0,738},{134,11,482},{4,11,781},{6,11,487},{7,11,926},{8, +11,263},{139,11,500},{135,11,418},{4,10,289},{6,0,2047},{7,10,629},{7,10,1698},{ +7,10,1711},{10,0,969},{140,10,215},{6,10,450},{136,10,109},{134,0,818},{136,10, +705},{133,0,866},{4,11,94},{135,11,1265},{132,11,417},{134,0,1467},{135,10,1238} +,{4,0,972},{6,0,1851},{134,0,1857},{134,0,355},{133,0,116},{132,0,457},{135,11, +1411},{4,11,408},{4,11,741},{135,11,500},{134,10,26},{142,11,137},{5,0,527},{6,0 +,189},{7,0,859},{136,0,267},{11,0,104},{11,0,554},{15,0,60},{143,0,125},{134,0, +1613},{4,10,414},{5,10,467},{9,10,654},{10,10,451},{12,10,59},{141,10,375},{135, +10,17},{134,0,116},{135,11,541},{135,10,955},{6,11,73},{135,11,177},{133,11,576} +,{134,0,886},{133,0,487},{4,0,86},{5,0,667},{5,0,753},{6,0,316},{6,0,455},{135,0 +,946},{142,11,231},{150,0,45},{134,0,863},{134,0,1953},{6,10,280},{10,10,502},{ +11,10,344},{140,10,38},{4,0,79},{7,0,1773},{10,0,450},{11,0,589},{13,0,332},{13, +0,493},{14,0,183},{14,0,334},{14,0,362},{14,0,368},{14,0,376},{14,0,379},{19,0, +90},{19,0,103},{19,0,127},{148,0,90},{5,10,45},{7,10,1161},{11,10,448},{11,10, +880},{13,10,139},{13,10,407},{15,10,16},{17,10,95},{18,10,66},{18,10,88},{18,10, +123},{149,10,7},{136,10,777},{4,10,410},{135,10,521},{135,10,1778},{135,11,538}, +{142,0,381},{133,11,413},{134,0,1142},{6,0,1189},{136,11,495},{5,0,663},{6,0, +1962},{134,0,2003},{7,11,54},{8,11,312},{10,11,191},{10,11,614},{140,11,567},{ +132,10,436},{133,0,846},{7,10,1587},{7,10,1707},{10,0,528},{139,0,504},{5,0,378} +,{8,0,465},{9,0,286},{10,0,185},{10,0,562},{10,0,635},{11,0,31},{11,0,393},{13,0 +,312},{18,0,65},{18,0,96},{147,0,89},{6,11,468},{7,0,899},{7,11,567},{7,11,1478} +,{8,11,530},{14,0,325},{142,11,290},{7,0,1880},{9,0,680},{139,0,798},{134,0,1770 +},{132,0,648},{150,11,35},{5,0,945},{6,0,1656},{6,0,1787},{7,0,167},{8,0,824},{9 +,0,391},{10,0,375},{139,0,185},{6,11,484},{135,11,822},{134,0,2046},{7,0,1645},{ +8,0,352},{137,0,249},{132,0,152},{6,0,611},{135,0,1733},{6,11,1724},{135,11,2022 +},{133,0,1006},{141,11,96},{5,0,420},{135,0,1449},{146,11,149},{135,0,832},{135, +10,663},{133,0,351},{5,0,40},{7,0,598},{7,0,1638},{8,0,78},{9,0,166},{9,0,640},{ +9,0,685},{9,0,773},{11,0,215},{13,0,65},{14,0,172},{14,0,317},{145,0,6},{8,0,60} +,{9,0,343},{139,0,769},{134,0,1354},{132,0,724},{137,0,745},{132,11,474},{7,0, +1951},{8,0,765},{8,0,772},{140,0,671},{7,0,108},{8,0,219},{8,0,388},{9,0,775},{ +11,0,275},{140,0,464},{137,0,639},{135,10,503},{133,11,366},{5,0,15},{5,11,305}, +{6,0,56},{7,0,1758},{8,0,500},{9,0,730},{9,11,560},{11,0,331},{13,0,150},{13,11, +208},{142,0,282},{4,10,113},{5,10,163},{5,10,735},{7,10,1009},{9,10,9},{9,10,771 +},{12,10,90},{13,10,138},{13,10,410},{143,10,128},{4,10,324},{138,10,104},{135, +11,466},{142,11,27},{134,0,1886},{4,11,480},{5,0,205},{6,0,438},{6,11,167},{6,11 +,302},{6,11,1642},{7,11,130},{7,11,656},{7,11,837},{7,11,1547},{7,11,1657},{8,11 +,429},{9,0,711},{9,11,228},{10,11,643},{13,11,289},{13,11,343},{147,11,101},{134 +,0,865},{6,0,2025},{136,0,965},{7,11,278},{10,11,739},{11,11,708},{141,11,348},{ +133,0,534},{135,11,1922},{137,0,691},{4,10,935},{133,10,823},{6,0,443},{9,0,237} +,{9,0,571},{9,0,695},{10,0,139},{11,0,715},{12,0,417},{141,0,421},{5,10,269},{7, +10,434},{7,10,891},{8,10,339},{9,10,702},{11,10,594},{11,10,718},{145,10,100},{6 +,0,1555},{7,0,878},{9,10,485},{141,10,264},{134,10,1713},{7,10,1810},{11,10,866} +,{12,10,103},{141,10,495},{135,10,900},{6,0,1410},{9,11,316},{139,11,256},{4,0, +995},{135,0,1033},{132,0,578},{10,0,881},{12,0,740},{12,0,743},{140,0,759},{132, +0,822},{133,0,923},{142,10,143},{135,11,1696},{6,11,363},{7,11,1955},{136,11,725 +},{132,0,924},{133,0,665},{135,10,2029},{135,0,1901},{4,0,265},{6,0,1092},{6,0, +1417},{7,0,807},{135,0,950},{5,0,93},{12,0,267},{141,0,498},{135,0,1451},{5,11, +813},{135,11,2046},{5,10,625},{135,10,1617},{135,0,747},{6,0,788},{137,0,828},{5 +,11,712},{7,0,184},{7,11,1855},{8,10,425},{8,10,693},{9,10,720},{10,10,380},{10, +10,638},{11,0,307},{11,0,400},{11,10,473},{11,11,17},{12,10,61},{13,11,321},{15, +0,130},{144,11,67},{135,0,198},{6,11,320},{7,11,781},{7,11,1921},{9,11,55},{10, +11,186},{10,11,273},{10,11,664},{10,11,801},{11,11,996},{11,11,997},{13,11,157}, +{142,11,170},{136,11,271},{135,0,994},{7,11,103},{7,11,863},{11,11,184},{14,11, +299},{145,11,62},{11,10,551},{142,10,159},{5,0,233},{5,0,320},{6,0,140},{8,0,295 +},{8,0,615},{136,11,615},{133,0,978},{4,0,905},{6,0,1701},{137,0,843},{132,10, +168},{4,0,974},{8,0,850},{12,0,709},{12,0,768},{140,0,786},{135,10,91},{152,0,6} +,{138,10,532},{135,10,1884},{132,0,509},{6,0,1307},{135,0,273},{5,11,77},{7,11, +1455},{10,11,843},{19,11,73},{150,11,5},{132,11,458},{135,11,1420},{6,11,109},{ +138,11,382},{6,0,201},{6,11,330},{7,10,70},{7,11,1084},{10,10,240},{11,11,142},{ +147,10,93},{7,0,1041},{140,11,328},{133,11,354},{134,0,1040},{133,0,693},{134,0, +774},{139,0,234},{132,0,336},{7,0,1399},{139,10,392},{20,0,22},{148,11,22},{5,0, +802},{7,0,2021},{136,0,805},{5,0,167},{5,0,899},{6,0,410},{137,0,777},{137,0,789 +},{134,0,1705},{7,10,655},{135,10,1844},{4,10,145},{6,10,176},{7,10,395},{137,10 +,562},{132,10,501},{135,0,10},{5,0,11},{6,0,117},{6,0,485},{6,10,509},{7,0,1133} +,{9,0,582},{9,0,594},{10,0,82},{11,0,21},{11,0,818},{12,0,535},{13,0,86},{20,0, +91},{151,0,13},{4,0,264},{7,0,1067},{8,0,204},{8,0,385},{139,0,953},{139,11,737} +,{138,0,56},{134,0,1917},{133,0,470},{10,11,657},{14,11,297},{142,11,361},{135, +11,412},{7,0,1198},{7,11,1198},{8,11,556},{14,11,123},{14,11,192},{143,11,27},{7 +,11,1985},{14,11,146},{15,11,42},{16,11,23},{17,11,86},{146,11,17},{8,11,122},{ +139,0,1015},{4,10,114},{9,10,492},{13,10,462},{142,10,215},{4,10,77},{5,10,361}, +{6,10,139},{6,10,401},{6,10,404},{7,10,413},{7,10,715},{7,10,1716},{11,10,279},{ +12,10,179},{12,10,258},{13,10,244},{142,10,358},{134,10,1717},{7,10,1061},{8,10, +82},{11,10,250},{12,10,420},{141,10,184},{133,0,715},{135,10,724},{9,0,919},{9,0 +,922},{9,0,927},{9,0,933},{9,0,962},{9,0,1000},{9,0,1002},{9,0,1021},{12,0,890}, +{12,0,907},{12,0,930},{15,0,207},{15,0,228},{15,0,238},{149,0,61},{8,0,794},{9,0 +,400},{10,0,298},{142,0,228},{5,11,430},{5,11,932},{6,11,131},{7,11,417},{9,11, +522},{11,11,314},{141,11,390},{132,0,867},{4,11,507},{136,0,724},{137,11,261},{4 +,11,343},{133,11,511},{6,0,190},{7,0,768},{135,0,1170},{6,10,513},{135,10,1052}, +{7,11,455},{138,11,591},{134,0,1066},{137,10,899},{14,0,67},{147,0,60},{4,0,948} +,{18,0,174},{146,0,176},{135,0,1023},{7,10,1417},{12,10,382},{17,10,48},{152,10, +12},{134,11,575},{132,0,764},{6,10,545},{7,10,565},{7,10,1669},{10,10,114},{11, +10,642},{140,10,618},{6,0,137},{9,0,75},{9,0,253},{10,0,194},{138,0,444},{4,0, +756},{133,10,5},{7,10,192},{136,0,1008},{132,0,842},{8,10,763},{11,0,643},{140,0 +,115},{139,0,67},{133,10,759},{4,0,821},{5,0,760},{7,0,542},{7,11,580},{8,0,135} +,{136,0,496},{7,10,370},{7,10,1007},{7,10,1177},{135,10,1565},{135,10,1237},{140 +,0,736},{7,0,319},{7,0,355},{7,0,763},{10,0,389},{145,0,43},{8,11,333},{138,11, +182},{4,10,87},{5,10,250},{141,10,298},{138,0,786},{134,0,2044},{8,11,330},{140, +11,477},{135,11,1338},{132,11,125},{134,0,1030},{134,0,1083},{132,11,721},{135, +10,814},{7,11,776},{8,11,145},{147,11,56},{134,0,1226},{4,10,57},{7,10,1195},{7, +10,1438},{7,10,1548},{7,10,1835},{7,10,1904},{9,10,757},{10,10,604},{139,10,519} +,{7,11,792},{8,11,147},{10,11,821},{139,11,1021},{137,11,797},{4,0,58},{5,0,286} +,{6,0,319},{7,0,402},{7,0,1254},{7,0,1903},{8,0,356},{140,0,408},{4,0,389},{4,0, +815},{9,0,181},{9,0,255},{10,0,8},{10,0,29},{10,0,816},{11,0,311},{11,0,561},{12 +,0,67},{141,0,181},{7,11,1472},{135,11,1554},{7,11,1071},{7,11,1541},{7,11,1767} +,{7,11,1806},{7,11,1999},{9,11,248},{10,11,400},{11,11,162},{11,11,178},{11,11, +242},{12,11,605},{15,11,26},{144,11,44},{5,11,168},{5,11,930},{8,11,74},{9,11, +623},{12,11,500},{12,11,579},{13,11,41},{143,11,93},{6,11,220},{7,11,1101},{141, +11,105},{4,10,209},{5,0,474},{7,0,507},{7,10,902},{135,11,507},{132,0,427},{6,0, +413},{7,10,335},{7,10,1437},{7,10,1668},{8,10,553},{8,10,652},{8,10,656},{9,10, +558},{11,10,743},{149,10,18},{132,0,730},{6,11,19},{7,11,1413},{139,11,428},{133 +,0,373},{132,10,559},{7,11,96},{8,11,401},{137,11,896},{5,10,1017},{7,0,799},{7, +0,1972},{138,10,511},{135,0,1793},{7,11,1961},{7,11,1965},{8,11,702},{136,11,750 +},{8,11,150},{8,11,737},{140,11,366},{132,0,322},{133,10,709},{8,11,800},{9,11, +148},{9,11,872},{9,11,890},{11,11,309},{11,11,1001},{13,11,267},{141,11,323},{ +134,10,1745},{7,0,290},{136,10,206},{7,0,1651},{145,0,89},{139,0,2},{132,0,672}, +{6,0,1860},{8,0,905},{10,0,844},{10,0,846},{10,0,858},{12,0,699},{12,0,746},{140 +,0,772},{135,11,424},{133,11,547},{133,0,737},{5,11,490},{6,11,615},{6,11,620},{ +135,11,683},{6,0,746},{134,0,1612},{132,10,776},{9,11,385},{149,11,17},{133,0, +145},{135,10,1272},{7,0,884},{140,0,124},{4,0,387},{135,0,1288},{5,11,133},{136, +10,406},{136,11,187},{6,0,679},{8,11,8},{138,11,0},{135,0,550},{135,11,798},{136 +,11,685},{7,11,1086},{145,11,46},{8,10,175},{10,10,168},{138,10,573},{135,0,1305 +},{4,0,576},{135,0,1263},{6,0,686},{134,0,1563},{134,0,607},{5,0,919},{134,0, +1673},{148,0,37},{8,11,774},{10,11,670},{140,11,51},{133,10,784},{139,10,882},{4 +,0,82},{5,0,333},{5,0,904},{6,0,207},{7,0,325},{7,0,1726},{8,0,101},{10,0,778},{ +139,0,220},{135,11,371},{132,0,958},{133,0,903},{4,11,127},{5,11,350},{6,11,356} +,{8,11,426},{9,11,572},{10,11,247},{139,11,312},{140,0,147},{6,11,59},{7,11,885} +,{9,11,603},{141,11,397},{9,10,14},{9,10,441},{10,0,367},{139,10,9},{11,10,966}, +{12,10,287},{13,10,342},{13,10,402},{15,10,110},{143,10,163},{134,0,690},{132,0, +705},{7,10,1428},{7,10,1640},{7,10,1867},{9,0,651},{9,10,169},{9,10,182},{9,10, +367},{9,10,478},{9,10,506},{9,10,551},{9,10,557},{9,10,648},{9,10,697},{9,10,705 +},{9,10,725},{9,10,787},{9,10,794},{10,10,198},{10,10,214},{10,10,267},{10,10, +275},{10,10,456},{10,10,551},{10,10,561},{10,10,613},{10,10,627},{10,10,668},{10 +,10,675},{10,10,691},{10,10,695},{10,10,707},{10,10,715},{11,0,971},{11,10,183}, +{11,10,201},{11,10,262},{11,10,352},{11,10,439},{11,10,493},{11,10,572},{11,10, +591},{11,10,608},{11,10,611},{11,10,646},{11,10,674},{11,10,711},{11,10,751},{11 +,10,761},{11,10,776},{11,10,785},{11,10,850},{11,10,853},{11,10,862},{11,10,865} +,{11,10,868},{11,10,875},{11,10,898},{11,10,902},{11,10,903},{11,10,910},{11,10, +932},{11,10,942},{11,10,957},{11,10,967},{11,10,972},{12,10,148},{12,10,195},{12 +,10,220},{12,10,237},{12,10,318},{12,10,339},{12,10,393},{12,10,445},{12,10,450} +,{12,10,474},{12,10,505},{12,10,509},{12,10,533},{12,10,591},{12,10,594},{12,10, +597},{12,10,621},{12,10,633},{12,10,642},{13,0,273},{13,10,59},{13,10,60},{13,10 +,145},{13,10,239},{13,10,250},{13,10,329},{13,10,344},{13,10,365},{13,10,372},{ +13,10,387},{13,10,403},{13,10,414},{13,10,456},{13,10,470},{13,10,478},{13,10, +483},{13,10,489},{14,10,55},{14,10,57},{14,10,81},{14,10,90},{14,10,148},{14,10, +239},{14,10,266},{14,10,321},{14,10,326},{14,10,327},{14,10,330},{14,10,347},{14 +,10,355},{14,10,401},{14,10,404},{14,10,411},{14,10,414},{14,10,416},{14,10,420} +,{15,10,61},{15,10,74},{15,10,87},{15,10,88},{15,10,94},{15,10,96},{15,10,116},{ +15,10,149},{15,10,154},{16,10,50},{16,10,63},{16,10,73},{17,10,2},{17,10,66},{17 +,10,92},{17,10,103},{17,10,112},{17,10,120},{18,10,50},{18,10,54},{18,10,82},{18 +,10,86},{18,10,90},{18,10,111},{18,10,115},{18,10,156},{19,10,40},{19,10,79},{20 +,10,78},{149,10,22},{5,10,161},{7,0,887},{135,10,839},{142,11,98},{134,0,90},{ +138,11,356},{135,11,441},{6,11,111},{7,11,4},{8,11,163},{8,11,776},{138,11,566}, +{134,0,908},{134,0,1261},{7,0,813},{12,0,497},{141,0,56},{134,0,1235},{135,0,429 +},{135,11,1994},{138,0,904},{6,0,125},{7,0,1277},{137,0,772},{151,0,12},{4,0,841 +},{5,0,386},{133,11,386},{5,11,297},{135,11,1038},{6,0,860},{6,0,1069},{135,11, +309},{136,0,946},{135,10,1814},{141,11,418},{136,11,363},{10,0,768},{139,0,787}, +{22,11,30},{150,11,33},{6,0,160},{7,0,1106},{9,0,770},{11,0,112},{140,0,413},{11 +,11,216},{139,11,340},{136,10,139},{135,11,1390},{135,11,808},{132,11,280},{7,10 +,643},{8,10,236},{12,0,271},{145,0,109},{140,11,54},{4,11,421},{133,11,548},{11, +0,719},{12,0,36},{141,0,337},{7,0,581},{9,0,644},{137,0,699},{11,11,511},{13,11, +394},{14,11,298},{14,11,318},{146,11,103},{7,0,304},{9,0,646},{9,0,862},{11,0, +696},{12,0,208},{15,0,79},{147,0,108},{4,0,631},{7,0,1126},{135,0,1536},{135,11, +1527},{8,0,880},{10,0,869},{138,0,913},{5,10,54},{6,11,254},{7,0,1513},{9,11,109 +},{138,11,103},{135,0,981},{133,11,729},{132,10,744},{132,0,434},{134,0,550},{6, +11,1630},{7,0,930},{10,0,476},{10,10,402},{13,0,452},{18,10,55},{147,0,104},{5,0 +,553},{138,0,824},{136,0,452},{8,0,151},{137,10,624},{132,10,572},{132,0,772},{ +133,11,671},{133,0,292},{138,0,135},{132,11,889},{140,11,207},{6,10,43},{7,10,38 +},{8,10,248},{9,0,504},{138,10,513},{6,0,1089},{135,11,1910},{4,11,627},{133,11, +775},{135,0,783},{133,10,766},{133,10,363},{7,0,387},{135,11,387},{7,0,393},{7, +11,202},{10,0,603},{11,0,206},{11,11,362},{11,11,948},{140,11,388},{6,11,507},{7 +,11,451},{8,11,389},{12,11,490},{13,11,16},{13,11,215},{13,11,351},{18,11,132},{ +147,11,125},{4,0,912},{7,11,841},{137,0,232},{6,10,258},{140,10,409},{5,10,249}, +{148,10,82},{136,11,566},{6,0,977},{135,11,1214},{7,0,1973},{136,0,716},{135,0, +98},{133,0,733},{5,11,912},{134,11,1695},{5,10,393},{6,10,378},{7,10,1981},{9,10 +,32},{9,10,591},{10,10,685},{10,10,741},{142,10,382},{133,10,788},{7,10,1968},{ +10,0,19},{11,0,911},{141,10,509},{5,0,668},{5,11,236},{6,11,572},{8,11,492},{11, +11,618},{144,11,56},{135,11,1789},{4,0,360},{5,0,635},{5,0,700},{5,10,58},{5,10, +171},{5,10,683},{6,10,291},{6,10,566},{7,10,1650},{11,10,523},{12,10,273},{12,10 +,303},{15,10,39},{143,10,111},{133,0,901},{134,10,589},{5,11,190},{136,11,318},{ +140,0,656},{7,0,726},{152,0,9},{4,10,917},{133,10,1005},{135,10,1598},{134,11, +491},{4,10,919},{133,11,434},{137,0,72},{6,0,1269},{6,0,1566},{134,0,1621},{4,10 +,255},{5,10,302},{6,10,132},{7,10,128},{7,10,283},{7,10,1299},{9,0,463},{10,0, +595},{10,10,52},{10,10,514},{11,10,925},{13,10,92},{142,10,309},{135,0,1454},{ +134,0,1287},{9,10,173},{11,0,600},{141,0,245},{136,0,989},{7,0,164},{7,0,1571},{ +9,0,107},{140,0,225},{6,0,1061},{141,10,442},{4,0,27},{5,0,484},{5,0,510},{6,0, +434},{7,0,1000},{7,0,1098},{136,0,2},{7,11,85},{7,11,247},{8,11,585},{10,11,163} +,{138,11,316},{11,11,103},{142,11,0},{134,0,1127},{4,0,460},{134,0,852},{134,10, +210},{4,0,932},{133,0,891},{6,0,588},{147,11,83},{4,10,284},{6,10,223},{136,0, +625},{134,0,76},{8,0,92},{137,0,221},{4,11,124},{10,11,457},{11,11,121},{11,11, +169},{11,11,422},{11,11,870},{12,11,214},{13,11,389},{14,11,187},{143,11,77},{9, +11,618},{138,11,482},{4,10,218},{7,10,526},{143,10,137},{4,10,270},{5,10,192},{6 +,10,332},{7,10,1322},{13,0,9},{14,0,104},{142,0,311},{140,10,661},{135,11,1193}, +{6,11,107},{7,11,638},{7,11,1632},{137,11,396},{132,0,763},{4,0,622},{5,11,370}, +{134,11,1756},{133,0,253},{135,0,546},{5,11,204},{9,0,73},{10,0,110},{14,0,185}, +{145,0,119},{7,0,624},{7,0,916},{10,0,256},{139,0,87},{7,10,379},{8,10,481},{137 +,10,377},{5,0,212},{5,11,970},{6,11,1706},{12,0,35},{141,0,382},{5,10,1003},{6, +10,149},{137,0,746},{8,10,262},{9,10,627},{10,0,150},{11,0,849},{11,10,214},{11, +10,404},{11,10,457},{11,10,780},{11,10,913},{13,0,330},{13,10,401},{142,10,200}, +{134,0,1466},{135,11,3},{4,11,35},{5,11,121},{5,11,483},{5,11,685},{6,0,1299},{6 +,11,489},{7,11,1204},{136,11,394},{135,10,742},{4,10,142},{136,10,304},{4,11,921 +},{133,11,1007},{134,0,1518},{6,0,1229},{135,0,1175},{133,0,816},{4,10,471},{4, +11,712},{5,10,51},{6,10,602},{7,10,925},{8,10,484},{10,10,195},{140,0,159},{134, +11,1629},{5,0,869},{5,0,968},{6,0,1626},{8,0,734},{136,0,784},{4,0,542},{6,0, +1716},{6,0,1727},{7,0,1082},{7,0,1545},{8,0,56},{8,0,118},{8,0,412},{8,0,564},{8 +,10,688},{9,0,888},{9,0,908},{10,0,50},{10,0,423},{11,0,685},{11,0,697},{11,0, +933},{12,0,299},{13,0,126},{13,0,136},{13,0,170},{141,0,190},{132,10,697},{4,0, +232},{9,0,202},{10,0,474},{140,0,433},{136,0,212},{6,0,108},{7,0,1003},{7,0,1181 +},{8,0,111},{136,0,343},{5,10,221},{135,11,1255},{133,11,485},{134,0,1712},{142, +0,216},{4,11,285},{5,0,643},{5,11,317},{6,0,516},{6,11,301},{7,11,7},{8,11,153}, +{10,11,766},{11,11,468},{12,11,467},{141,11,143},{4,0,133},{7,0,711},{7,0,1298}, +{135,0,1585},{134,0,650},{135,11,512},{6,0,99},{7,0,1808},{145,0,57},{6,0,246},{ +6,0,574},{7,0,428},{9,0,793},{10,0,669},{11,0,485},{11,0,840},{12,0,300},{14,0, +250},{145,0,55},{4,10,132},{5,10,69},{135,10,1242},{136,0,1023},{4,10,111},{135, +0,302},{135,0,1871},{132,0,728},{4,10,767},{137,0,252},{6,0,461},{7,0,1590},{7, +10,1416},{7,10,2005},{8,10,131},{8,10,466},{9,10,672},{13,10,252},{148,10,103},{ +6,0,323},{135,0,1564},{7,0,461},{136,0,775},{6,10,44},{136,10,368},{139,0,172},{ +132,0,464},{4,10,570},{133,10,120},{137,11,269},{6,10,227},{135,10,1589},{6,11, +1719},{6,11,1735},{7,11,2016},{7,11,2020},{8,11,837},{137,11,852},{7,0,727},{146 +,0,73},{132,0,1023},{135,11,852},{135,10,1529},{136,0,577},{138,11,568},{134,0, +1037},{8,11,67},{138,11,419},{4,0,413},{5,0,677},{8,0,432},{140,0,280},{6,10, +1667},{7,10,2036},{7,11,967},{10,0,600},{141,11,11},{6,10,511},{140,10,132},{5, +10,568},{6,0,799},{6,10,138},{135,10,1293},{4,10,565},{8,0,159},{136,10,827},{7, +0,646},{7,0,1730},{11,0,446},{141,0,178},{4,10,922},{133,10,1023},{135,11,11},{ +132,0,395},{7,10,1002},{139,0,145},{9,0,174},{10,0,164},{11,0,440},{11,0,514},{ +11,0,841},{15,0,98},{149,0,20},{134,0,426},{10,0,608},{139,0,1002},{7,11,320},{8 +,11,51},{12,11,481},{12,11,570},{148,11,106},{4,11,445},{9,0,977},{137,0,983},{ +138,0,250},{139,0,100},{6,0,1982},{136,10,402},{133,11,239},{4,10,716},{141,10, +31},{5,0,476},{7,11,83},{7,11,1990},{8,11,130},{139,11,720},{8,10,691},{136,10, +731},{5,11,123},{6,11,530},{7,11,348},{135,11,1419},{5,0,76},{6,0,458},{6,0,497} +,{7,0,868},{9,0,658},{10,0,594},{11,0,173},{11,0,566},{12,0,20},{12,0,338},{141, +0,200},{9,11,139},{10,11,399},{11,11,469},{12,11,634},{141,11,223},{9,10,840},{ +138,10,803},{133,10,847},{11,11,223},{140,11,168},{132,11,210},{8,0,447},{9,10, +53},{9,10,268},{9,10,901},{10,10,518},{10,10,829},{11,10,188},{13,10,74},{14,10, +46},{15,10,17},{15,10,33},{17,10,40},{18,10,36},{19,10,20},{22,10,1},{152,10,2}, +{4,0,526},{7,0,1029},{135,0,1054},{19,11,59},{150,11,2},{4,0,636},{6,0,1875},{6, +0,1920},{8,10,532},{9,0,999},{12,0,807},{12,0,825},{15,0,179},{15,0,190},{146,0, +182},{6,0,1699},{7,0,660},{7,0,1124},{17,0,31},{19,0,22},{151,0,14},{135,10,681} +,{132,11,430},{140,10,677},{4,10,684},{136,10,384},{132,11,756},{133,11,213},{7, +0,188},{7,10,110},{8,10,290},{8,10,591},{9,10,382},{9,10,649},{11,10,71},{11,10, +155},{11,10,313},{12,10,5},{13,10,325},{142,10,287},{7,10,360},{7,10,425},{9,10, +66},{9,10,278},{138,10,644},{142,11,164},{4,0,279},{7,0,301},{137,0,362},{134,11 +,586},{135,0,1743},{4,0,178},{133,0,399},{4,10,900},{133,10,861},{5,10,254},{7, +10,985},{136,10,73},{133,11,108},{7,10,1959},{136,10,683},{133,11,219},{4,11,193 +},{5,11,916},{7,11,364},{10,11,398},{10,11,726},{11,11,317},{11,11,626},{12,11, +142},{12,11,288},{12,11,678},{13,11,313},{15,11,113},{18,11,114},{21,11,30},{150 +,11,53},{6,11,241},{7,11,907},{8,11,832},{9,11,342},{10,11,729},{11,11,284},{11, +11,445},{11,11,651},{11,11,863},{13,11,398},{146,11,99},{132,0,872},{134,0,831}, +{134,0,1692},{6,0,202},{6,0,1006},{9,0,832},{9,11,734},{10,0,636},{11,0,208},{12 +,0,360},{17,0,118},{18,0,27},{148,0,67},{132,10,725},{7,11,993},{138,11,666},{ +134,0,1954},{134,10,196},{7,0,872},{10,0,516},{139,0,167},{133,10,831},{4,11,562 +},{9,11,254},{139,11,879},{137,0,313},{4,0,224},{132,11,786},{8,10,723},{11,0,24 +},{140,0,170},{5,0,546},{7,0,35},{8,0,11},{8,0,12},{9,0,315},{9,0,533},{10,0,802 +},{11,0,166},{12,0,525},{142,0,243},{7,0,1937},{13,10,80},{13,10,437},{145,10,74 +},{5,0,241},{8,0,242},{9,0,451},{10,0,667},{11,0,598},{140,0,429},{150,0,46},{6, +0,1273},{137,0,830},{5,10,848},{6,10,66},{136,10,764},{6,0,825},{134,0,993},{4,0 +,1006},{4,10,36},{7,10,1387},{10,0,327},{11,10,755},{141,0,271},{134,0,1023},{ +135,0,1580},{4,0,366},{137,0,516},{132,10,887},{6,0,1736},{135,0,1891},{6,11,216 +},{7,11,901},{7,11,1343},{136,11,493},{6,10,165},{138,10,388},{7,11,341},{139,11 +,219},{4,10,719},{135,10,155},{134,0,1935},{132,0,826},{6,0,331},{6,0,1605},{8,0 +,623},{11,0,139},{139,0,171},{135,11,1734},{10,11,115},{11,11,420},{12,11,154},{ +13,11,404},{14,11,346},{15,11,54},{143,11,112},{4,10,353},{6,10,146},{6,10,1789} +,{7,0,288},{7,10,990},{7,10,1348},{9,10,665},{9,10,898},{11,10,893},{142,10,212} +,{6,0,916},{134,0,1592},{4,10,45},{7,0,1888},{135,10,1257},{5,11,1011},{136,11, +701},{139,11,596},{4,11,54},{5,11,666},{7,11,1039},{7,11,1130},{9,11,195},{138, +11,302},{134,0,1471},{134,0,1570},{132,0,394},{140,10,65},{136,10,816},{135,0, +1931},{7,0,574},{135,0,1719},{134,11,467},{132,0,658},{6,11,1669},{9,0,781},{10, +0,144},{11,0,385},{13,0,161},{13,0,228},{13,0,268},{148,0,107},{136,0,374},{135, +0,735},{4,0,344},{6,0,498},{139,0,323},{6,10,559},{6,10,1691},{7,0,586},{135,0, +1063},{137,0,155},{133,0,906},{7,11,122},{9,11,259},{10,11,84},{11,11,470},{12, +11,541},{141,11,379},{134,0,1139},{10,0,108},{139,0,116},{134,10,456},{133,10, +925},{5,11,82},{5,11,131},{7,11,1755},{8,11,31},{9,11,168},{9,11,764},{139,11, +869},{134,11,605},{5,11,278},{137,11,68},{4,11,163},{5,11,201},{5,11,307},{5,11, +310},{6,11,335},{7,11,284},{136,11,165},{135,11,1660},{6,11,33},{135,11,1244},{4 +,0,616},{136,11,483},{4,11,199},{8,0,857},{8,0,902},{8,0,910},{10,0,879},{11,11, +34},{140,0,726},{136,0,692},{6,10,193},{7,10,240},{7,10,1682},{10,10,51},{10,10, +640},{11,10,410},{13,10,82},{14,10,247},{14,10,331},{142,10,377},{6,0,823},{134, +0,983},{139,10,411},{132,0,305},{136,10,633},{138,11,203},{134,0,681},{6,11,326} +,{7,11,677},{137,11,425},{5,0,214},{7,0,603},{8,0,611},{9,0,686},{10,0,88},{11,0 +,459},{11,0,496},{12,0,463},{12,0,590},{141,0,0},{136,0,1004},{142,0,23},{134,0, +1703},{147,11,8},{145,11,56},{135,0,1443},{4,10,237},{135,10,514},{6,0,714},{145 +,0,19},{5,11,358},{7,11,473},{7,11,1184},{10,11,662},{13,11,212},{13,11,304},{13 +,11,333},{145,11,98},{4,0,737},{10,0,98},{11,0,294},{12,0,60},{12,0,437},{13,0, +64},{13,0,380},{142,0,430},{6,10,392},{7,10,65},{135,10,2019},{6,0,1758},{8,0, +520},{9,0,345},{9,0,403},{142,0,350},{5,0,47},{10,0,242},{138,0,579},{5,0,139},{ +7,0,1168},{138,0,539},{134,0,1459},{13,0,388},{141,11,388},{134,0,253},{7,10, +1260},{135,10,1790},{9,10,222},{10,0,252},{139,10,900},{140,0,745},{133,11,946}, +{4,0,107},{7,0,613},{8,0,439},{8,0,504},{9,0,501},{10,0,383},{139,0,477},{135,11 +,1485},{132,0,871},{7,11,411},{7,11,590},{8,11,631},{9,11,323},{10,11,355},{11, +11,491},{12,11,143},{12,11,402},{13,11,73},{14,11,408},{15,11,107},{146,11,71},{ +132,0,229},{132,0,903},{140,0,71},{133,0,549},{4,0,47},{6,0,373},{7,0,452},{7,0, +543},{7,0,1828},{7,0,1856},{9,0,6},{11,0,257},{139,0,391},{7,11,1467},{8,11,328} +,{10,11,544},{11,11,955},{13,11,320},{145,11,83},{5,0,980},{134,0,1754},{136,0, +865},{5,0,705},{137,0,606},{7,0,161},{8,10,201},{136,10,605},{143,11,35},{5,11, +835},{6,11,483},{140,10,224},{7,0,536},{7,0,1331},{136,0,143},{134,0,1388},{5,0, +724},{10,0,305},{11,0,151},{12,0,33},{12,0,121},{12,0,381},{17,0,3},{17,0,27},{ +17,0,78},{18,0,18},{19,0,54},{149,0,5},{4,10,523},{133,10,638},{5,0,19},{134,0, +533},{5,0,395},{5,0,951},{134,0,1776},{135,0,1908},{132,0,846},{6,10,242},{7,10, +227},{7,10,1581},{8,10,104},{9,10,113},{9,10,220},{9,10,427},{10,0,74},{10,10, +239},{11,0,663},{11,10,579},{11,10,1023},{12,0,210},{13,0,166},{13,0,310},{13,10 +,4},{13,10,204},{13,10,316},{14,0,373},{18,0,95},{19,0,43},{148,10,86},{9,11,716 +},{11,11,108},{13,11,123},{14,11,252},{19,11,38},{21,11,3},{151,11,11},{8,0,372} +,{9,0,122},{138,0,175},{132,11,677},{7,11,1374},{136,11,540},{135,10,861},{132,0 +,695},{7,0,497},{9,0,387},{147,0,81},{136,0,937},{134,0,718},{7,0,1328},{136,10, +494},{132,11,331},{5,11,747},{134,0,1581},{5,0,284},{6,0,49},{6,0,350},{7,0,1},{ +7,0,377},{7,0,1693},{8,0,18},{8,0,678},{9,0,161},{9,0,585},{9,0,671},{9,0,839},{ +11,0,912},{141,0,427},{7,10,1306},{8,10,505},{9,10,482},{10,10,126},{11,10,225}, +{12,10,347},{12,10,449},{13,10,19},{14,10,218},{142,10,435},{10,10,764},{12,10, +120},{13,10,39},{145,10,127},{4,0,597},{133,10,268},{134,0,1094},{4,0,1008},{134 +,0,1973},{132,0,811},{139,0,908},{135,0,1471},{133,11,326},{4,10,384},{135,10, +1022},{4,11,691},{7,0,1935},{7,11,1935},{8,0,324},{8,11,324},{9,11,35},{10,11, +680},{11,11,364},{12,0,42},{12,11,42},{13,11,357},{146,11,16},{135,0,2014},{6,11 +,32},{7,0,2007},{7,11,385},{7,11,757},{7,11,1916},{8,11,37},{8,11,94},{8,11,711} +,{9,0,101},{9,0,450},{9,11,541},{10,0,66},{10,0,842},{10,11,162},{10,11,795},{11 +,0,536},{11,11,989},{11,11,1010},{12,0,587},{12,11,14},{142,11,308},{139,0,586}, +{135,10,1703},{7,0,1077},{9,10,159},{11,0,28},{140,10,603},{6,0,1221},{136,10, +583},{6,11,152},{6,11,349},{6,11,1682},{7,11,1252},{8,11,112},{9,11,435},{9,11, +668},{10,11,290},{10,11,319},{10,11,815},{11,11,180},{11,11,837},{12,11,240},{13 +,11,152},{13,11,219},{142,11,158},{139,0,62},{132,10,515},{8,10,632},{8,10,697}, +{137,10,854},{134,0,1766},{132,11,581},{6,11,126},{7,11,573},{8,11,397},{142,11, +44},{150,0,28},{4,10,136},{5,10,551},{11,0,670},{150,0,25},{6,0,1665},{7,0,256}, +{7,0,1388},{138,0,499},{4,0,22},{5,0,10},{7,0,1576},{136,0,97},{134,10,1782},{5, +0,481},{7,10,1287},{9,10,44},{10,10,552},{10,10,642},{11,10,839},{12,10,274},{12 +,10,275},{12,10,372},{13,10,91},{142,10,125},{133,11,926},{7,11,1232},{137,11, +531},{6,0,134},{7,0,437},{7,0,1824},{9,0,37},{14,0,285},{142,0,371},{7,0,486},{8 +,0,155},{11,0,93},{140,0,164},{6,0,1391},{134,0,1442},{133,11,670},{133,0,591},{ +6,10,147},{7,10,886},{7,11,1957},{9,10,753},{138,10,268},{5,0,380},{5,0,650},{7, +0,1173},{136,0,310},{4,0,364},{7,0,1156},{7,0,1187},{137,0,409},{135,11,1621},{ +134,0,482},{133,11,506},{4,0,781},{6,0,487},{7,0,926},{8,0,263},{139,0,500},{138 +,10,137},{135,11,242},{139,11,96},{133,10,414},{135,10,1762},{134,0,804},{5,11, +834},{7,11,1202},{8,11,14},{9,11,481},{137,11,880},{134,10,599},{4,0,94},{135,0, +1265},{4,0,415},{132,0,417},{5,0,348},{6,0,522},{6,10,1749},{7,11,1526},{138,11, +465},{134,10,1627},{132,0,1012},{132,10,488},{4,11,357},{6,11,172},{7,11,143},{ +137,11,413},{4,10,83},{4,11,590},{146,11,76},{140,10,676},{7,11,287},{8,11,355}, +{9,11,293},{137,11,743},{134,10,278},{5,11,169},{6,0,1803},{7,11,333},{8,11,45}, +{18,0,165},{152,0,21},{12,10,97},{140,11,97},{4,0,408},{4,0,741},{135,0,500},{ +132,11,198},{7,10,388},{7,10,644},{139,10,781},{4,11,24},{5,11,140},{5,11,185},{ +7,11,1500},{11,11,565},{139,11,838},{6,0,1321},{7,10,229},{8,10,59},{9,0,257},{9 +,10,190},{10,10,378},{140,10,191},{4,11,334},{133,11,593},{135,11,1885},{134,0, +1138},{4,0,249},{6,0,73},{135,0,177},{133,0,576},{142,0,231},{137,0,288},{132,10 +,660},{7,10,1035},{138,10,737},{135,0,1487},{6,0,989},{7,10,690},{9,0,433},{9,10 +,587},{140,10,521},{7,0,1264},{7,0,1678},{11,0,945},{12,0,341},{12,0,471},{140,0 +,569},{132,11,709},{133,11,897},{5,11,224},{13,11,174},{146,11,52},{135,11,1840} +,{134,10,1744},{4,10,733},{9,10,194},{10,10,92},{11,10,198},{12,0,87},{12,10,84} +,{13,10,128},{144,0,74},{140,0,779},{135,0,538},{4,11,608},{133,11,497},{133,0, +413},{7,11,1375},{7,11,1466},{138,11,331},{136,0,495},{6,11,540},{136,11,136},{7 +,0,54},{8,0,312},{10,0,191},{10,0,614},{140,0,567},{5,11,999},{6,0,468},{7,0,567 +},{7,0,1478},{8,0,530},{142,0,290},{4,11,299},{7,10,306},{135,11,1004},{142,11, +296},{134,0,1484},{133,10,979},{6,0,609},{9,0,815},{12,11,137},{14,11,9},{14,11, +24},{142,11,64},{133,11,456},{6,0,484},{135,0,822},{133,10,178},{136,11,180},{ +132,11,755},{137,0,900},{135,0,1335},{6,0,1724},{135,0,2022},{135,11,1139},{4,10 +,390},{133,0,640},{6,0,1831},{138,11,633},{135,11,566},{4,11,890},{5,11,805},{5, +11,819},{5,11,961},{6,11,396},{6,11,1631},{6,11,1678},{7,11,1967},{7,11,2041},{9 +,11,630},{11,11,8},{11,11,1019},{12,11,176},{13,11,225},{14,11,292},{149,11,24}, +{132,0,474},{134,0,1103},{135,0,1504},{134,0,1576},{6,0,961},{6,0,1034},{140,0, +655},{11,11,514},{149,11,20},{5,0,305},{135,11,1815},{7,11,1505},{10,11,190},{10 +,11,634},{11,11,792},{12,11,358},{140,11,447},{5,11,0},{6,11,536},{7,11,604},{13 +,11,445},{145,11,126},{5,10,105},{135,0,1236},{4,0,480},{6,0,217},{6,0,302},{6,0 +,1642},{6,11,232},{6,11,412},{7,0,130},{7,0,837},{7,0,1321},{7,0,1547},{7,0,1657 +},{7,11,1074},{8,0,429},{8,11,9},{8,11,157},{8,11,786},{9,0,228},{9,11,196},{9, +11,352},{9,11,457},{10,11,337},{11,11,232},{11,11,877},{12,11,480},{12,11,546},{ +13,0,289},{13,0,343},{147,0,101},{5,10,438},{7,11,958},{9,10,694},{12,10,627},{ +13,10,210},{141,11,38},{4,11,382},{136,11,579},{7,0,278},{10,0,739},{11,0,708},{ +141,0,348},{4,11,212},{135,11,1206},{135,11,1898},{6,0,708},{6,0,1344},{152,10, +11},{137,11,768},{134,0,1840},{140,0,233},{8,10,25},{138,10,826},{5,11,655},{134 +,0,2017},{6,0,1488},{139,11,290},{132,10,308},{134,0,1590},{134,0,1800},{134,0, +1259},{6,11,231},{7,11,95},{8,11,423},{144,0,28},{133,11,300},{135,10,150},{136, +10,649},{7,11,1874},{137,11,641},{6,11,237},{7,11,611},{8,11,100},{9,11,416},{11 +,11,335},{12,11,173},{146,11,101},{137,0,45},{134,10,521},{14,11,26},{17,0,36},{ +146,11,150},{5,10,339},{7,0,1442},{14,0,22},{15,10,41},{15,10,166},{147,10,66},{ +6,11,581},{7,11,1119},{136,0,378},{134,0,1507},{147,11,117},{139,0,39},{134,0, +1054},{6,0,363},{7,0,1955},{136,0,725},{134,0,2036},{133,11,199},{6,0,1871},{9,0 +,935},{9,0,961},{9,0,1004},{9,0,1016},{12,0,805},{12,0,852},{12,0,853},{12,0,869 +},{12,0,882},{12,0,896},{12,0,906},{12,0,917},{12,0,940},{15,0,170},{15,0,176},{ +15,0,188},{15,0,201},{15,0,205},{15,0,212},{15,0,234},{15,0,244},{18,0,181},{18, +0,193},{18,0,196},{18,0,201},{18,0,202},{18,0,210},{18,0,217},{18,0,235},{18,0, +236},{18,0,237},{21,0,54},{21,0,55},{21,0,58},{21,0,59},{152,0,22},{134,10,1628} +,{137,0,805},{5,0,813},{135,0,2046},{142,11,42},{5,0,712},{6,0,1240},{11,0,17},{ +13,0,321},{144,0,67},{132,0,617},{135,10,829},{6,0,320},{7,0,781},{7,0,1921},{9, +0,55},{10,0,186},{10,0,273},{10,0,664},{10,0,801},{11,0,996},{11,0,997},{13,0, +157},{142,0,170},{136,0,271},{5,10,486},{135,10,1349},{18,11,91},{147,11,70},{7, +10,1635},{8,10,17},{10,0,445},{138,10,295},{136,11,404},{7,0,103},{7,0,863},{11, +0,184},{145,0,62},{138,10,558},{137,0,659},{6,11,312},{6,11,1715},{10,11,584},{ +11,11,546},{11,11,692},{12,11,259},{12,11,295},{13,11,46},{141,11,154},{134,0, +676},{132,11,588},{4,11,231},{5,11,61},{6,11,104},{7,11,729},{7,11,964},{7,11, +1658},{140,11,414},{6,11,263},{138,11,757},{11,0,337},{142,0,303},{135,11,1363}, +{132,11,320},{140,0,506},{134,10,447},{5,0,77},{7,0,1455},{10,0,843},{147,0,73}, +{7,10,577},{7,10,1432},{9,10,475},{9,10,505},{9,10,526},{9,10,609},{9,10,689},{9 +,10,726},{9,10,735},{9,10,738},{10,10,556},{10,10,674},{10,10,684},{11,10,89},{ +11,10,202},{11,10,272},{11,10,380},{11,10,415},{11,10,505},{11,10,537},{11,10, +550},{11,10,562},{11,10,640},{11,10,667},{11,10,688},{11,10,847},{11,10,927},{11 +,10,930},{11,10,940},{12,10,144},{12,10,325},{12,10,329},{12,10,389},{12,10,403} +,{12,10,451},{12,10,515},{12,10,604},{12,10,616},{12,10,626},{13,10,66},{13,10, +131},{13,10,167},{13,10,236},{13,10,368},{13,10,411},{13,10,434},{13,10,453},{13 +,10,461},{13,10,474},{14,10,59},{14,10,60},{14,10,139},{14,10,152},{14,10,276},{ +14,10,353},{14,10,402},{15,10,28},{15,10,81},{15,10,123},{15,10,152},{18,10,136} +,{148,10,88},{132,0,458},{135,0,1420},{4,10,609},{4,11,405},{6,0,109},{7,10,756} +,{7,11,817},{9,10,544},{10,0,382},{11,10,413},{14,10,307},{14,11,58},{16,10,25}, +{17,11,37},{146,11,124},{5,11,974},{6,0,330},{7,0,1084},{139,0,142},{4,10,930},{ +133,10,947},{5,10,939},{142,11,394},{16,0,91},{145,0,87},{5,10,962},{5,11,235},{ +7,11,1239},{11,11,131},{140,11,370},{5,10,651},{8,10,170},{9,10,61},{9,10,63},{ +10,10,23},{10,10,37},{10,10,834},{11,0,492},{11,10,4},{11,10,281},{11,10,503},{ +11,10,677},{12,10,96},{12,10,130},{12,10,244},{14,10,5},{14,10,40},{14,10,162},{ +14,10,202},{146,10,133},{4,10,406},{5,10,579},{12,10,492},{150,10,15},{9,11,137} +,{138,11,221},{134,0,1239},{11,0,211},{140,0,145},{7,11,390},{138,11,140},{135, +11,1418},{135,11,1144},{134,0,1049},{6,10,17},{7,0,321},{7,10,1001},{7,10,1982}, +{9,10,886},{10,10,489},{10,10,800},{11,10,782},{12,10,320},{13,10,467},{14,10, +145},{14,10,387},{143,10,119},{145,10,17},{5,11,407},{11,11,489},{19,11,37},{20, +11,73},{150,11,38},{133,10,458},{135,0,1985},{7,10,1983},{8,10,0},{8,10,171},{9, +10,120},{9,10,732},{10,10,473},{11,10,656},{11,10,998},{18,10,0},{18,10,2},{147, +10,21},{5,11,325},{7,11,1483},{8,11,5},{8,11,227},{9,11,105},{10,11,585},{140,11 +,614},{136,0,122},{132,0,234},{135,11,1196},{6,0,976},{6,0,1098},{134,0,1441},{7 +,0,253},{136,0,549},{6,11,621},{13,11,504},{144,11,19},{132,10,519},{5,0,430},{5 +,0,932},{6,0,131},{7,0,417},{9,0,522},{11,0,314},{141,0,390},{14,0,149},{14,0, +399},{143,0,57},{5,10,907},{6,10,31},{6,11,218},{7,10,491},{7,10,530},{8,10,592} +,{11,10,53},{11,10,779},{12,10,167},{12,10,411},{14,10,14},{14,10,136},{15,10,72 +},{16,10,17},{144,10,72},{140,11,330},{7,11,454},{7,11,782},{136,11,768},{132,0, +507},{10,11,676},{140,11,462},{4,10,208},{5,10,106},{6,0,630},{6,10,531},{8,10, +408},{9,0,811},{9,10,188},{138,10,572},{4,0,343},{5,0,511},{134,10,1693},{134,11 +,164},{132,0,448},{7,0,455},{138,0,591},{135,0,1381},{12,10,441},{150,11,50},{9, +10,449},{10,10,192},{138,10,740},{4,10,241},{134,0,575},{134,0,1175},{134,0,653} +,{134,0,1761},{134,0,1198},{132,10,259},{6,11,343},{7,11,195},{9,11,226},{10,11, +197},{10,11,575},{11,11,502},{139,11,899},{7,0,1127},{7,0,1572},{10,0,297},{10,0 +,422},{11,0,764},{11,0,810},{12,0,264},{13,0,102},{13,0,300},{13,0,484},{14,0, +147},{14,0,229},{17,0,71},{18,0,118},{147,0,120},{135,11,666},{132,0,678},{4,10, +173},{5,10,312},{5,10,512},{135,10,1285},{7,10,1603},{7,10,1691},{9,10,464},{11, +10,195},{12,10,279},{12,10,448},{14,10,11},{147,10,102},{16,0,99},{146,0,164},{7 +,11,1125},{9,11,143},{11,11,61},{14,11,405},{150,11,21},{137,11,260},{4,10,452}, +{5,10,583},{5,10,817},{6,10,433},{7,10,593},{7,10,720},{7,10,1378},{8,10,161},{9 +,10,284},{10,10,313},{139,10,886},{132,10,547},{136,10,722},{14,0,35},{142,0,191 +},{141,0,45},{138,0,121},{132,0,125},{134,0,1622},{133,11,959},{8,10,420},{139, +10,193},{132,0,721},{135,10,409},{136,0,145},{7,0,792},{8,0,147},{8,11,173},{10, +0,821},{11,0,970},{139,0,1021},{134,11,266},{132,0,715},{7,0,1999},{138,10,308}, +{133,0,531},{5,0,168},{5,0,930},{8,0,74},{9,0,623},{12,0,500},{140,0,579},{144,0 +,65},{138,11,246},{6,0,220},{7,0,1101},{13,0,105},{142,11,314},{5,10,1002},{136, +10,745},{134,0,960},{20,0,0},{148,11,0},{4,0,1005},{4,10,239},{6,10,477},{7,10, +1607},{11,10,68},{139,10,617},{6,0,19},{7,0,1413},{139,0,428},{149,10,13},{7,0, +96},{8,0,401},{8,0,703},{8,11,300},{137,0,896},{134,0,1595},{145,0,116},{136,0, +1021},{7,0,1961},{7,0,1965},{7,0,2030},{8,0,150},{8,0,702},{8,0,737},{8,0,750},{ +140,0,366},{11,11,75},{142,11,267},{132,10,367},{5,11,427},{5,11,734},{7,11,478} +,{8,0,800},{8,11,52},{9,0,148},{9,0,872},{9,0,890},{11,0,309},{11,0,1001},{13,0, +267},{141,0,323},{7,11,239},{11,11,217},{142,11,165},{132,11,323},{140,11,419},{ +13,0,299},{142,0,75},{6,11,87},{6,11,1734},{7,11,20},{7,11,1056},{8,11,732},{9, +11,406},{9,11,911},{138,11,694},{134,0,1383},{132,10,694},{133,11,613},{137,0, +779},{4,0,598},{140,10,687},{6,0,970},{135,0,424},{133,0,547},{7,11,32},{7,11, +984},{8,11,85},{8,11,709},{9,11,579},{9,11,847},{9,11,856},{10,11,799},{11,11, +258},{11,11,1007},{12,11,331},{12,11,615},{13,11,188},{13,11,435},{14,11,8},{15, +11,165},{16,11,27},{148,11,40},{6,0,1222},{134,0,1385},{132,0,876},{138,11,151}, +{135,10,213},{4,11,167},{135,11,82},{133,0,133},{6,11,24},{7,11,74},{7,11,678},{ +137,11,258},{5,11,62},{6,11,534},{7,11,684},{7,11,1043},{7,11,1072},{8,11,280},{ +8,11,541},{8,11,686},{10,11,519},{11,11,252},{140,11,282},{136,0,187},{8,0,8},{ +10,0,0},{10,0,818},{139,0,988},{132,11,359},{7,10,1672},{11,0,429},{143,0,51},{ +136,0,685},{5,11,211},{7,11,88},{136,11,627},{134,0,472},{136,0,132},{6,11,145}, +{141,11,336},{4,10,751},{11,10,390},{140,10,32},{4,10,409},{4,11,263},{5,10,78}, +{6,0,938},{134,0,1060},{137,0,874},{4,11,916},{6,10,473},{7,10,1602},{8,0,774},{ +10,0,670},{10,10,698},{12,0,51},{12,10,212},{13,10,307},{145,10,105},{146,0,92}, +{143,10,156},{132,0,830},{137,0,701},{4,11,599},{6,11,1634},{7,11,5},{7,11,55},{ +7,11,67},{7,11,97},{7,11,691},{7,11,979},{7,11,1697},{8,11,207},{8,11,214},{8,11 +,231},{8,11,294},{8,11,336},{8,11,428},{8,11,451},{8,11,460},{8,11,471},{8,11, +622},{8,11,626},{8,11,679},{8,11,759},{8,11,829},{9,11,11},{9,11,246},{9,11,484} +,{9,11,573},{9,11,706},{9,11,762},{9,11,798},{9,11,855},{9,11,870},{9,11,912},{ +10,11,303},{10,11,335},{10,11,424},{10,11,461},{10,11,543},{10,11,759},{10,11, +814},{11,11,59},{11,11,199},{11,11,235},{11,11,475},{11,11,590},{11,11,929},{11, +11,963},{12,11,114},{12,11,182},{12,11,226},{12,11,332},{12,11,439},{12,11,575}, +{12,11,598},{13,11,8},{13,11,125},{13,11,194},{13,11,287},{14,11,197},{14,11,383 +},{15,11,53},{17,11,63},{19,11,46},{19,11,98},{19,11,106},{148,11,85},{4,0,127}, +{5,0,350},{6,0,356},{8,0,426},{9,0,572},{10,0,247},{139,0,312},{134,0,1215},{6,0 +,59},{7,11,1853},{9,0,603},{10,11,437},{141,0,397},{134,0,1762},{147,11,126},{ +135,10,883},{13,0,293},{142,0,56},{133,10,617},{139,10,50},{5,11,187},{7,10,1518 +},{139,10,694},{135,0,441},{6,0,111},{7,0,4},{8,0,163},{8,0,776},{138,0,566},{ +132,0,806},{4,11,215},{9,11,38},{10,11,3},{11,11,23},{11,11,127},{139,11,796},{4 +,10,546},{7,10,2042},{142,0,233},{135,0,1994},{134,0,1739},{135,11,1530},{136,0, +393},{5,0,297},{7,0,1038},{14,0,359},{19,0,52},{148,0,47},{135,0,309},{4,10,313} +,{133,10,577},{8,10,184},{141,10,433},{135,10,935},{12,10,186},{12,10,292},{14, +10,100},{146,10,70},{136,0,363},{11,10,402},{12,10,109},{12,10,431},{13,10,179}, +{13,10,206},{14,0,175},{14,10,217},{16,10,3},{148,10,53},{5,10,886},{6,10,46},{6 +,10,1790},{7,10,14},{7,10,732},{7,10,1654},{8,10,95},{8,10,327},{8,10,616},{9,10 +,892},{10,10,598},{10,10,769},{11,10,134},{11,10,747},{12,10,378},{142,10,97},{ +136,0,666},{135,0,1675},{6,0,655},{134,0,1600},{135,0,808},{133,10,1021},{4,11, +28},{5,11,440},{7,11,248},{11,11,833},{140,11,344},{134,11,1654},{132,0,280},{ +140,0,54},{4,0,421},{133,0,548},{132,10,153},{6,11,339},{135,11,923},{133,11,853 +},{133,10,798},{132,10,587},{6,11,249},{7,11,1234},{139,11,573},{6,10,598},{7,10 +,42},{8,10,695},{10,10,212},{11,10,158},{14,10,196},{145,10,85},{5,10,957},{5,10 +,1008},{135,0,249},{4,10,129},{135,10,465},{6,0,254},{7,0,842},{7,0,1659},{7,10, +908},{7,10,1201},{9,0,109},{9,10,755},{10,0,103},{11,10,906},{12,10,527},{146,10 +,7},{5,0,262},{136,10,450},{144,0,1},{10,11,201},{142,11,319},{7,11,49},{7,11, +392},{8,11,20},{8,11,172},{8,11,690},{9,11,383},{9,11,845},{10,11,48},{11,11,293 +},{11,11,832},{11,11,920},{141,11,221},{5,11,858},{133,11,992},{134,0,805},{139, +10,1003},{6,0,1630},{134,11,307},{7,11,1512},{135,11,1794},{6,11,268},{137,11,62 +},{135,10,1868},{133,0,671},{4,0,989},{8,0,972},{136,0,998},{132,11,423},{132,0, +889},{135,0,1382},{135,0,1910},{7,10,965},{7,10,1460},{135,10,1604},{4,0,627},{5 +,0,775},{138,11,106},{134,11,348},{7,0,202},{11,0,362},{11,0,948},{140,0,388},{ +138,11,771},{6,11,613},{136,11,223},{6,0,560},{7,0,451},{8,0,389},{12,0,490},{13 +,0,16},{13,0,215},{13,0,351},{18,0,132},{147,0,125},{135,0,841},{136,0,566},{136 +,0,938},{132,11,670},{5,0,912},{6,0,1695},{140,11,55},{9,11,40},{139,11,136},{7, +0,1361},{7,10,982},{10,10,32},{143,10,56},{11,11,259},{140,11,270},{5,0,236},{6, +0,572},{8,0,492},{11,0,618},{144,0,56},{8,11,572},{9,11,310},{9,11,682},{137,11, +698},{134,0,1854},{5,0,190},{136,0,318},{133,10,435},{135,0,1376},{4,11,296},{6, +11,352},{7,11,401},{7,11,1410},{7,11,1594},{7,11,1674},{8,11,63},{8,11,660},{137 +,11,74},{5,10,85},{6,10,419},{7,0,349},{7,10,305},{7,10,361},{7,10,1337},{8,10, +71},{140,10,519},{4,11,139},{4,11,388},{140,11,188},{6,0,1972},{6,0,2013},{8,0, +951},{10,0,947},{10,0,974},{10,0,1018},{142,0,476},{140,10,688},{135,10,740},{5, +10,691},{7,10,345},{9,10,94},{140,10,169},{5,10,183},{6,10,582},{9,0,344},{10,10 +,679},{140,10,435},{135,10,511},{132,0,850},{8,11,441},{10,11,314},{143,11,3},{7 +,10,1993},{136,10,684},{4,11,747},{6,10,583},{6,11,290},{7,11,649},{7,11,1479},{ +135,11,1583},{133,11,232},{133,10,704},{134,0,910},{4,10,179},{5,10,198},{133,10 +,697},{7,10,347},{7,10,971},{8,10,181},{138,10,711},{136,11,525},{14,0,19},{14,0 +,28},{144,0,29},{7,0,85},{7,0,247},{8,0,585},{138,0,163},{4,0,487},{7,11,472},{7 +,11,1801},{10,11,748},{141,11,458},{4,10,243},{5,10,203},{7,10,19},{7,10,71},{7, +10,113},{10,10,405},{11,10,357},{142,10,240},{7,10,1450},{139,10,99},{132,11,425 +},{138,0,145},{147,0,83},{6,10,492},{137,11,247},{4,0,1013},{134,0,2033},{5,10, +134},{6,10,408},{6,10,495},{135,10,1593},{135,0,1922},{134,11,1768},{4,0,124},{ +10,0,457},{11,0,121},{11,0,169},{11,0,870},{11,0,874},{12,0,214},{14,0,187},{143 +,0,77},{5,0,557},{135,0,1457},{139,0,66},{5,11,943},{6,11,1779},{142,10,4},{4,10 +,248},{4,10,665},{7,10,137},{137,10,349},{5,11,245},{6,11,576},{7,0,1193},{7,11, +582},{136,11,225},{144,0,82},{7,10,1270},{139,10,612},{5,0,454},{10,0,352},{138, +11,352},{5,10,371},{7,10,563},{146,0,57},{135,0,1333},{6,0,107},{6,11,610},{7,0, +638},{7,0,1632},{137,0,396},{5,0,370},{134,0,1756},{4,10,374},{7,10,547},{7,10, +1700},{7,10,1833},{139,10,858},{133,0,204},{6,0,1305},{9,10,311},{141,10,42},{5, +0,970},{134,0,1706},{6,10,1647},{7,10,1552},{7,10,2010},{9,10,494},{137,10,509}, +{13,11,455},{15,11,99},{15,11,129},{144,11,68},{135,0,3},{4,0,35},{5,0,121},{5,0 +,483},{5,0,685},{6,0,489},{6,0,782},{6,0,1032},{7,0,1204},{136,0,394},{4,0,921}, +{133,0,1007},{8,11,360},{138,11,63},{135,0,1696},{134,0,1519},{132,11,443},{135, +11,944},{6,10,123},{7,10,214},{9,10,728},{10,10,157},{11,10,346},{11,10,662},{ +143,10,106},{137,0,981},{135,10,1435},{134,0,1072},{132,0,712},{134,0,1629},{134 +,0,728},{4,11,298},{137,11,483},{5,11,164},{6,0,1177},{6,0,1271},{7,11,121},{142 +,11,189},{4,10,707},{5,10,588},{6,10,393},{7,0,1608},{13,10,106},{18,10,49},{147 +,10,41},{23,0,16},{151,11,16},{6,10,211},{7,10,1690},{11,10,486},{140,10,369},{ +133,0,485},{19,11,15},{149,11,27},{4,11,172},{9,11,611},{10,11,436},{12,11,673}, +{141,11,255},{5,11,844},{10,11,484},{11,11,754},{12,11,457},{14,11,171},{14,11, +389},{146,11,153},{4,0,285},{5,0,27},{5,0,317},{6,0,301},{7,0,7},{8,0,153},{10,0 +,766},{11,0,468},{12,0,467},{141,0,143},{134,0,1462},{9,11,263},{10,11,147},{138 +,11,492},{133,11,537},{6,0,1945},{6,0,1986},{6,0,1991},{134,0,2038},{134,10,219} +,{137,11,842},{5,10,582},{6,10,1646},{7,10,99},{7,10,1962},{7,10,1986},{8,10,515 +},{8,10,773},{9,10,23},{9,10,491},{12,10,620},{14,0,52},{14,10,93},{145,0,50},{ +138,11,97},{5,10,851},{20,0,21},{148,0,44},{136,0,819},{139,0,917},{5,11,230},{5 +,11,392},{6,11,420},{8,10,762},{8,10,812},{9,10,910},{9,11,568},{140,11,612},{ +135,0,784},{15,0,135},{143,11,135},{10,0,454},{140,0,324},{4,11,0},{5,11,41},{7, +11,1459},{7,11,1469},{7,11,1618},{7,11,1859},{9,11,549},{139,11,905},{4,10,98},{ +7,10,1365},{9,10,422},{9,10,670},{10,10,775},{11,10,210},{13,10,26},{13,10,457}, +{141,10,476},{6,0,1719},{6,0,1735},{7,0,2016},{7,0,2020},{8,0,837},{137,0,852},{ +133,11,696},{135,0,852},{132,0,952},{134,10,1730},{132,11,771},{138,0,568},{137, +0,448},{139,0,146},{8,0,67},{138,0,419},{133,11,921},{137,10,147},{134,0,1826},{ +10,0,657},{14,0,297},{142,0,361},{6,0,666},{6,0,767},{134,0,1542},{139,0,729},{6 +,11,180},{7,11,1137},{8,11,751},{139,11,805},{4,11,183},{7,11,271},{11,11,824},{ +11,11,952},{13,11,278},{13,11,339},{13,11,482},{14,11,424},{148,11,99},{4,0,669} +,{5,11,477},{5,11,596},{6,11,505},{7,11,1221},{11,11,907},{12,11,209},{141,11, +214},{135,11,1215},{5,0,402},{6,10,30},{11,10,56},{139,10,305},{7,11,564},{142, +11,168},{139,0,152},{7,0,912},{135,10,1614},{4,10,150},{5,10,303},{134,10,327},{ +7,0,320},{8,0,51},{9,0,868},{10,0,833},{12,0,481},{12,0,570},{148,0,106},{132,0, +445},{7,11,274},{11,11,263},{11,11,479},{11,11,507},{140,11,277},{6,11,1645},{8, +10,192},{10,0,555},{10,10,78},{11,0,308},{13,10,359},{147,0,95},{135,10,786},{6, +11,92},{6,11,188},{7,11,1269},{7,11,1524},{7,11,1876},{10,11,228},{139,11,1020}, +{4,11,459},{133,11,966},{6,10,1638},{7,10,79},{7,10,496},{9,10,138},{10,10,336}, +{11,0,386},{12,10,412},{12,10,440},{142,10,305},{133,0,239},{7,0,83},{7,0,1990}, +{8,0,130},{139,0,720},{138,11,709},{4,0,143},{5,0,550},{133,0,752},{5,0,123},{6, +0,530},{7,0,348},{135,0,1419},{135,0,2024},{6,11,18},{7,11,179},{7,11,721},{7,11 +,932},{8,11,548},{8,11,757},{9,11,54},{9,11,65},{9,11,532},{9,11,844},{10,11,113 +},{10,11,117},{10,11,236},{10,11,315},{10,11,430},{10,11,798},{11,11,153},{11,11 +,351},{11,11,375},{12,11,78},{12,11,151},{12,11,392},{14,11,248},{143,11,23},{7, +10,204},{7,10,415},{8,10,42},{10,10,85},{139,10,564},{134,0,958},{133,11,965},{ +132,0,210},{135,11,1429},{138,11,480},{134,11,182},{139,11,345},{10,11,65},{10, +11,488},{138,11,497},{4,10,3},{5,10,247},{5,10,644},{7,10,744},{7,10,1207},{7,10 +,1225},{7,10,1909},{146,10,147},{132,0,430},{5,10,285},{9,10,67},{13,10,473},{ +143,10,82},{144,11,16},{7,11,1162},{9,11,588},{10,11,260},{151,10,8},{133,0,213} +,{138,0,7},{135,0,801},{134,11,1786},{135,11,308},{6,0,936},{134,0,1289},{133,0, +108},{132,0,885},{133,0,219},{139,0,587},{4,0,193},{5,0,916},{6,0,1041},{7,0,364 +},{10,0,398},{10,0,726},{11,0,317},{11,0,626},{12,0,142},{12,0,288},{12,0,678},{ +13,0,313},{15,0,113},{146,0,114},{135,0,1165},{6,0,241},{9,0,342},{10,0,729},{11 +,0,284},{11,0,445},{11,0,651},{11,0,863},{13,0,398},{146,0,99},{7,0,907},{136,0, +832},{4,10,29},{6,10,532},{7,10,1628},{7,10,1648},{9,0,303},{9,10,350},{10,10, +433},{11,10,97},{11,10,557},{11,10,745},{12,10,289},{12,10,335},{12,10,348},{12, +10,606},{13,10,116},{13,10,233},{13,10,466},{14,10,181},{14,10,209},{14,10,232}, +{14,10,236},{14,10,300},{16,10,41},{148,10,97},{7,10,1692},{7,11,423},{136,11, +588},{6,0,931},{134,0,1454},{5,10,501},{7,10,1704},{9,10,553},{11,10,520},{12,10 +,557},{141,10,249},{136,11,287},{4,0,562},{9,0,254},{139,0,879},{132,0,786},{14, +11,32},{18,11,85},{20,11,2},{152,11,16},{135,0,1294},{7,11,723},{135,11,1135},{6 +,0,216},{6,11,403},{7,0,901},{7,0,1343},{136,0,493},{7,11,719},{8,11,809},{136, +11,834},{5,11,210},{6,11,213},{7,11,60},{10,11,364},{139,11,135},{5,11,607},{7,0 +,341},{8,11,326},{8,11,490},{139,0,219},{4,11,701},{5,11,472},{5,11,639},{7,11, +1249},{9,11,758},{139,11,896},{135,11,380},{135,11,1947},{139,0,130},{135,0,1734 +},{10,0,115},{11,0,420},{12,0,154},{13,0,404},{14,0,346},{143,0,54},{134,10,129} +,{4,11,386},{7,11,41},{8,11,405},{9,11,497},{11,11,110},{11,11,360},{15,11,37},{ +144,11,84},{141,11,282},{5,11,46},{7,11,1452},{7,11,1480},{8,11,634},{140,11,472 +},{4,11,524},{136,11,810},{10,11,238},{141,11,33},{133,0,604},{5,0,1011},{136,0, +701},{8,0,856},{8,0,858},{8,0,879},{12,0,702},{142,0,447},{4,0,54},{5,0,666},{7, +0,1039},{7,0,1130},{9,0,195},{138,0,302},{4,10,25},{5,10,60},{6,10,504},{7,10, +614},{7,10,1155},{140,10,0},{7,10,1248},{11,10,621},{139,10,702},{133,11,997},{ +137,10,321},{134,0,1669},{134,0,1791},{4,10,379},{135,10,1397},{138,11,372},{5, +11,782},{5,11,829},{134,11,1738},{135,0,1228},{4,10,118},{6,10,274},{6,10,361},{ +7,10,75},{141,10,441},{132,0,623},{9,11,279},{10,11,407},{14,11,84},{150,11,18}, +{137,10,841},{135,0,798},{140,10,693},{5,10,314},{6,10,221},{7,10,419},{10,10, +650},{11,10,396},{12,10,156},{13,10,369},{14,10,333},{145,10,47},{135,11,1372},{ +7,0,122},{9,0,259},{10,0,84},{11,0,470},{12,0,541},{141,0,379},{134,0,837},{4,11 +,78},{5,11,96},{5,11,182},{7,11,1724},{7,11,1825},{8,0,1013},{10,11,394},{10,11, +471},{11,11,532},{14,11,340},{145,11,88},{134,0,577},{135,11,1964},{132,10,913}, +{134,0,460},{8,0,891},{10,0,901},{10,0,919},{10,0,932},{12,0,715},{12,0,728},{12 +,0,777},{14,0,457},{144,0,103},{5,0,82},{5,0,131},{7,0,1755},{8,0,31},{9,0,168}, +{9,0,764},{139,0,869},{136,10,475},{5,10,1016},{6,0,605},{9,11,601},{9,11,619},{ +10,11,505},{10,11,732},{11,11,355},{140,11,139},{7,10,602},{8,10,179},{10,10,781 +},{140,10,126},{134,0,1246},{6,10,329},{138,10,111},{6,11,215},{7,11,1028},{7,11 +,1473},{7,11,1721},{9,11,424},{138,11,779},{5,0,278},{137,0,68},{6,0,932},{6,0, +1084},{144,0,86},{4,0,163},{5,0,201},{5,0,307},{5,0,310},{6,0,335},{7,0,284},{7, +0,1660},{136,0,165},{136,0,781},{134,0,707},{6,0,33},{135,0,1244},{5,10,821},{6, +10,1687},{6,11,67},{7,11,258},{7,11,1630},{9,11,354},{9,11,675},{10,11,830},{14, +11,80},{145,11,80},{6,11,141},{7,11,225},{9,11,59},{9,11,607},{10,11,312},{11,11 +,687},{12,11,555},{13,11,373},{13,11,494},{148,11,58},{134,0,1113},{5,10,71},{7, +10,1407},{9,0,388},{9,10,704},{10,10,261},{10,10,619},{11,10,547},{11,10,619},{ +143,10,157},{7,0,1953},{136,0,720},{138,0,203},{7,10,2008},{9,10,337},{138,10, +517},{6,0,326},{7,0,677},{137,0,425},{139,11,81},{7,0,1316},{7,0,1412},{7,0,1839 +},{9,0,589},{11,0,241},{11,0,676},{11,0,811},{11,0,891},{12,0,140},{12,0,346},{ +12,0,479},{13,0,140},{13,0,381},{14,0,188},{18,0,30},{148,0,108},{5,0,416},{6,10 +,86},{6,10,603},{7,10,292},{7,10,561},{8,10,257},{8,10,382},{9,10,721},{9,10,778 +},{11,10,581},{140,10,466},{4,10,486},{133,10,491},{134,0,1300},{132,10,72},{6, +10,265},{7,0,847},{7,11,430},{139,11,46},{5,11,602},{6,11,106},{7,11,1786},{7,11 +,1821},{7,11,2018},{9,11,418},{137,11,763},{5,0,358},{7,0,535},{7,0,1184},{10,0, +662},{13,0,212},{13,0,304},{13,0,333},{145,0,98},{5,11,65},{6,11,416},{7,11,1720 +},{7,11,1924},{8,11,677},{10,11,109},{11,11,14},{11,11,70},{11,11,569},{11,11, +735},{15,11,153},{148,11,80},{6,0,1823},{8,0,839},{8,0,852},{8,0,903},{10,0,940} +,{12,0,707},{140,0,775},{135,11,1229},{6,0,1522},{140,0,654},{136,11,595},{139,0 +,163},{141,0,314},{132,0,978},{4,0,601},{6,0,2035},{137,10,234},{5,10,815},{6,10 +,1688},{134,10,1755},{133,0,946},{136,0,434},{6,10,197},{136,10,205},{7,0,411},{ +7,0,590},{8,0,631},{9,0,323},{10,0,355},{11,0,491},{12,0,143},{12,0,402},{13,0, +73},{14,0,408},{15,0,107},{146,0,71},{7,0,1467},{8,0,328},{10,0,544},{11,0,955}, +{12,0,13},{13,0,320},{145,0,83},{142,0,410},{11,0,511},{13,0,394},{14,0,298},{14 +,0,318},{146,0,103},{6,10,452},{7,10,312},{138,10,219},{138,10,589},{4,10,333},{ +9,10,176},{12,10,353},{141,10,187},{135,11,329},{132,11,469},{5,0,835},{134,0, +483},{134,11,1743},{5,11,929},{6,11,340},{8,11,376},{136,11,807},{134,10,1685},{ +132,0,677},{5,11,218},{7,11,1610},{138,11,83},{5,11,571},{135,11,1842},{132,11, +455},{137,0,70},{135,0,1405},{7,10,135},{8,10,7},{8,10,62},{9,10,243},{10,10,658 +},{10,10,697},{11,10,456},{139,10,756},{9,10,395},{138,10,79},{137,0,108},{6,11, +161},{7,11,372},{137,11,597},{132,11,349},{132,0,777},{132,0,331},{135,10,631},{ +133,0,747},{6,11,432},{6,11,608},{139,11,322},{138,10,835},{5,11,468},{7,11,1809 +},{10,11,325},{11,11,856},{12,11,345},{143,11,104},{133,11,223},{7,10,406},{7,10 +,459},{8,10,606},{139,10,726},{132,11,566},{142,0,68},{4,11,59},{135,11,1394},{6 +,11,436},{139,11,481},{4,11,48},{5,11,271},{135,11,953},{139,11,170},{5,11,610}, +{136,11,457},{133,11,755},{135,11,1217},{133,10,612},{132,11,197},{132,0,505},{4 +,10,372},{7,10,482},{8,10,158},{9,10,602},{9,10,615},{10,10,245},{10,10,678},{10 +,10,744},{11,10,248},{139,10,806},{133,0,326},{5,10,854},{135,10,1991},{4,0,691} +,{146,0,16},{6,0,628},{9,0,35},{10,0,680},{10,0,793},{11,0,364},{13,0,357},{143, +0,164},{138,0,654},{6,0,32},{7,0,385},{7,0,757},{7,0,1916},{8,0,37},{8,0,94},{8, +0,711},{9,0,541},{10,0,162},{10,0,795},{11,0,989},{11,0,1010},{12,0,14},{142,0, +308},{133,11,217},{6,0,152},{6,0,349},{6,0,1682},{7,0,1252},{8,0,112},{9,0,435}, +{9,0,668},{10,0,290},{10,0,319},{10,0,815},{11,0,180},{11,0,837},{12,0,240},{13, +0,152},{13,0,219},{142,0,158},{4,0,581},{134,0,726},{5,10,195},{135,10,1685},{6, +0,126},{7,0,573},{8,0,397},{142,0,44},{138,0,89},{7,10,1997},{8,10,730},{139,10, +1006},{134,0,1531},{134,0,1167},{5,0,926},{5,10,751},{140,0,203},{4,11,165},{7, +11,1398},{135,11,1829},{7,0,1232},{137,0,531},{135,10,821},{134,0,943},{133,0, +670},{4,0,880},{139,0,231},{134,0,1617},{135,0,1957},{5,11,9},{7,11,297},{7,11, +966},{140,11,306},{6,0,975},{134,0,985},{5,10,950},{5,10,994},{134,10,351},{12, +11,21},{151,11,7},{5,11,146},{6,11,411},{138,11,721},{7,0,242},{135,0,1942},{6, +11,177},{135,11,467},{5,0,421},{7,10,47},{137,10,684},{5,0,834},{7,0,1202},{8,0, +14},{9,0,481},{137,0,880},{138,0,465},{4,10,350},{6,0,688},{137,0,834},{132,0, +855},{4,0,357},{6,0,172},{7,0,143},{137,0,413},{133,11,200},{132,0,590},{7,10, +1812},{13,10,259},{13,10,356},{14,10,242},{147,10,114},{133,10,967},{4,10,473},{ +7,10,623},{8,10,808},{9,10,871},{9,10,893},{11,0,114},{11,10,431},{12,10,112},{ +12,10,217},{12,10,243},{12,10,562},{12,10,663},{12,10,683},{13,10,141},{13,10, +197},{13,10,227},{13,10,406},{13,10,487},{14,10,156},{14,10,203},{14,10,224},{14 +,10,256},{18,10,58},{150,10,0},{138,10,286},{4,10,222},{7,10,286},{136,10,629},{ +5,0,169},{7,0,333},{136,0,45},{134,11,481},{132,0,198},{4,0,24},{4,11,84},{5,0, +140},{5,0,185},{7,0,1500},{7,11,1482},{10,11,76},{10,11,142},{11,0,565},{139,0, +838},{133,0,585},{141,10,306},{133,11,1015},{4,11,315},{5,11,507},{135,11,1370}, +{136,10,146},{6,0,691},{134,0,1503},{4,0,334},{133,0,593},{4,10,465},{135,10, +1663},{142,11,173},{135,0,913},{6,11,1722},{140,0,116},{134,0,1360},{132,0,802}, +{8,11,222},{8,11,476},{9,11,238},{11,11,516},{11,11,575},{15,11,109},{146,11,100 +},{6,0,308},{7,10,138},{7,10,517},{9,0,673},{139,10,238},{132,0,709},{6,0,1876}, +{6,0,1895},{9,0,994},{9,0,1006},{12,0,829},{12,0,888},{12,0,891},{146,0,185},{ +148,10,94},{4,0,228},{133,0,897},{5,10,495},{7,0,1840},{7,10,834},{9,10,733},{ +139,10,378},{133,10,559},{6,10,21},{6,10,1737},{7,10,1444},{136,10,224},{4,0,608 +},{133,0,497},{6,11,40},{135,11,1781},{134,0,1573},{135,0,2039},{6,0,540},{136,0 +,136},{4,0,897},{5,0,786},{133,10,519},{6,0,1878},{6,0,1884},{9,0,938},{9,0,948} +,{9,0,955},{9,0,973},{9,0,1012},{12,0,895},{12,0,927},{143,0,254},{134,0,1469},{ +133,0,999},{4,0,299},{135,0,1004},{4,0,745},{133,0,578},{136,11,574},{133,0,456} +,{134,0,1457},{4,10,402},{135,0,1679},{7,0,693},{8,0,180},{8,10,323},{8,10,479}, +{140,0,163},{11,10,580},{142,10,201},{5,10,59},{135,10,672},{132,11,354},{146,10 +,34},{4,0,755},{135,11,1558},{7,0,1740},{146,0,48},{4,10,85},{135,10,549},{139,0 +,338},{133,10,94},{134,0,1091},{135,11,469},{5,11,830},{12,0,695},{12,0,704},{14 +,11,338},{20,0,113},{148,11,81},{135,0,1464},{6,10,11},{135,10,187},{135,0,975}, +{4,10,522},{141,0,335},{134,0,1979},{5,11,496},{135,11,203},{4,10,52},{135,10, +661},{7,0,1566},{8,0,269},{9,0,212},{9,0,718},{14,0,15},{14,0,132},{142,0,227},{ +4,0,890},{4,10,383},{5,0,805},{5,0,819},{5,0,961},{5,10,520},{6,0,396},{6,0,1631 +},{6,0,1678},{7,0,1967},{7,0,2041},{9,0,630},{11,0,8},{11,0,1019},{12,0,176},{13 +,0,225},{14,0,292},{149,0,24},{134,11,547},{135,11,1748},{5,11,88},{137,11,239}, +{146,11,128},{7,11,650},{135,11,1310},{4,10,281},{5,10,38},{7,10,194},{7,10,668} +,{7,10,1893},{137,10,397},{135,0,1815},{9,10,635},{139,10,559},{7,0,1505},{10,0, +190},{10,0,634},{11,0,792},{12,0,358},{140,0,447},{5,0,0},{6,0,536},{7,0,604},{ +13,0,445},{145,0,126},{7,11,1076},{9,11,80},{11,11,78},{11,11,421},{11,11,534},{ +140,11,545},{8,0,966},{10,0,1023},{14,11,369},{146,11,72},{135,11,1641},{6,0,232 +},{6,0,412},{7,0,1074},{8,0,9},{8,0,157},{8,0,786},{9,0,196},{9,0,352},{9,0,457} +,{10,0,337},{11,0,232},{11,0,877},{12,0,480},{140,0,546},{135,0,958},{4,0,382},{ +136,0,579},{4,0,212},{135,0,1206},{4,11,497},{5,11,657},{135,11,1584},{132,0,681 +},{8,0,971},{138,0,965},{5,10,448},{136,10,535},{14,0,16},{146,0,44},{11,0,584}, +{11,0,616},{11,11,584},{11,11,616},{14,0,275},{142,11,275},{136,11,13},{7,10,610 +},{135,10,1501},{7,11,642},{8,11,250},{11,11,123},{11,11,137},{13,11,48},{142,11 +,95},{133,0,655},{17,0,67},{147,0,74},{134,0,751},{134,0,1967},{6,0,231},{136,0, +423},{5,0,300},{138,0,1016},{4,10,319},{5,10,699},{138,10,673},{6,0,237},{6,10, +336},{7,0,611},{8,0,100},{8,10,552},{9,0,416},{9,10,285},{10,10,99},{11,0,335},{ +11,10,568},{12,0,173},{146,0,101},{134,0,1370},{7,10,1406},{9,10,218},{141,10, +222},{133,10,256},{135,0,1208},{14,11,213},{148,11,38},{6,0,1219},{135,11,1642}, +{13,0,417},{14,0,129},{143,0,15},{10,11,545},{140,11,301},{17,10,39},{148,10,36} +,{133,0,199},{4,11,904},{133,11,794},{12,0,427},{146,0,38},{134,0,949},{7,10,634 +},{136,0,665},{132,10,618},{135,10,259},{132,10,339},{133,11,761},{141,10,169},{ +132,10,759},{5,0,688},{7,0,539},{135,0,712},{7,11,386},{138,11,713},{134,0,1186} +,{6,11,7},{6,11,35},{7,11,147},{7,11,1069},{7,11,1568},{7,11,1575},{7,11,1917},{ +8,11,43},{8,11,208},{9,11,128},{9,11,866},{10,11,20},{11,11,981},{147,11,33},{7, +11,893},{8,10,482},{141,11,424},{6,0,312},{6,0,1715},{10,0,584},{11,0,546},{11,0 +,692},{12,0,259},{12,0,295},{13,0,46},{141,0,154},{5,10,336},{6,10,341},{6,10, +478},{6,10,1763},{136,10,386},{137,0,151},{132,0,588},{152,0,4},{6,11,322},{9,11 +,552},{11,11,274},{13,11,209},{13,11,499},{14,11,85},{15,11,126},{145,11,70},{ +135,10,73},{4,0,231},{5,0,61},{6,0,104},{7,0,729},{7,0,964},{7,0,1658},{140,0, +414},{6,0,263},{138,0,757},{135,10,1971},{4,0,612},{133,0,561},{132,0,320},{135, +10,1344},{8,11,83},{8,11,817},{9,11,28},{9,11,29},{9,11,885},{10,11,387},{11,11, +633},{11,11,740},{13,11,235},{13,11,254},{15,11,143},{143,11,146},{5,10,396},{ +134,10,501},{140,11,49},{132,0,225},{4,10,929},{5,10,799},{8,10,46},{136,10,740} +,{4,0,405},{7,0,817},{14,0,58},{17,0,37},{146,0,124},{133,0,974},{4,11,412},{133 +,11,581},{4,10,892},{133,10,770},{4,0,996},{134,0,2026},{4,0,527},{5,0,235},{7,0 +,1239},{11,0,131},{140,0,370},{7,11,421},{9,0,16},{141,0,386},{7,0,956},{7,0, +1157},{7,0,1506},{7,0,1606},{7,0,1615},{7,0,1619},{7,0,1736},{7,0,1775},{8,0,590 +},{9,0,324},{9,0,736},{9,0,774},{9,0,776},{9,0,784},{10,0,567},{10,0,708},{11,0, +518},{11,0,613},{11,0,695},{11,0,716},{11,0,739},{11,0,770},{11,0,771},{11,0,848 +},{11,0,857},{11,0,931},{11,0,947},{12,0,326},{12,0,387},{12,0,484},{12,0,528},{ +12,0,552},{12,0,613},{13,0,189},{13,0,256},{13,0,340},{13,0,432},{13,0,436},{13, +0,440},{13,0,454},{14,0,174},{14,0,220},{14,0,284},{14,0,390},{145,0,121},{135, +10,158},{9,0,137},{138,0,221},{4,11,110},{10,11,415},{10,11,597},{142,11,206},{ +141,11,496},{135,11,205},{151,10,25},{135,11,778},{7,10,2001},{7,11,1656},{9,11, +369},{10,11,338},{10,11,490},{11,11,154},{11,11,545},{11,11,775},{13,11,77},{141 +,11,274},{4,11,444},{10,11,146},{140,11,9},{7,0,390},{138,0,140},{135,0,1144},{ +134,0,464},{7,10,1461},{140,10,91},{132,10,602},{4,11,283},{135,11,1194},{5,0, +407},{11,0,204},{11,0,243},{11,0,489},{12,0,293},{19,0,37},{20,0,73},{150,0,38}, +{7,0,1218},{136,0,303},{4,10,13},{5,0,325},{5,10,567},{7,10,1498},{8,0,5},{8,0, +227},{9,0,105},{9,10,124},{10,0,585},{11,10,521},{12,0,614},{140,10,405},{135,10 +,1006},{6,11,1720},{7,0,800},{138,0,12},{135,0,1783},{132,10,735},{138,10,812},{ +4,10,170},{135,10,323},{6,0,621},{13,0,504},{144,0,89},{5,10,304},{135,10,1403}, +{137,11,216},{6,0,920},{6,0,1104},{9,11,183},{139,11,286},{4,0,376},{133,10,742} +,{134,0,218},{8,0,641},{11,0,388},{140,0,580},{7,0,454},{7,0,782},{8,0,768},{140 +,0,686},{137,11,33},{133,10,111},{144,0,0},{10,0,676},{140,0,462},{6,0,164},{136 +,11,735},{133,10,444},{150,0,50},{7,11,1862},{12,11,491},{12,11,520},{13,11,383} +,{14,11,244},{146,11,12},{5,11,132},{9,11,486},{9,11,715},{10,11,458},{11,11,373 +},{11,11,668},{11,11,795},{11,11,897},{12,11,272},{12,11,424},{12,11,539},{12,11 +,558},{14,11,245},{14,11,263},{14,11,264},{14,11,393},{142,11,403},{8,10,123},{ +15,10,6},{144,10,7},{6,0,285},{8,0,654},{11,0,749},{12,0,190},{12,0,327},{13,0, +120},{13,0,121},{13,0,327},{15,0,47},{146,0,40},{5,11,8},{6,11,89},{6,11,400},{7 +,11,1569},{7,11,1623},{7,11,1850},{8,11,218},{8,11,422},{9,11,570},{138,11,626}, +{6,11,387},{7,11,882},{141,11,111},{6,0,343},{6,11,224},{7,0,195},{7,11,877},{9, +0,226},{9,11,647},{10,0,197},{10,0,575},{11,0,502},{139,0,899},{5,10,937},{135, +10,100},{135,11,790},{150,0,29},{147,0,8},{134,0,1812},{149,0,8},{135,11,394},{7 +,0,1125},{9,0,143},{11,0,61},{14,0,405},{150,0,21},{10,11,755},{147,11,29},{9,11 +,378},{141,11,162},{135,10,922},{5,10,619},{133,10,698},{134,0,1327},{6,0,1598}, +{137,0,575},{9,11,569},{12,11,12},{12,11,81},{12,11,319},{13,11,69},{14,11,259}, +{16,11,87},{17,11,1},{17,11,21},{17,11,24},{18,11,15},{18,11,56},{18,11,59},{18, +11,127},{18,11,154},{19,11,19},{148,11,31},{6,0,895},{135,11,1231},{5,0,959},{7, +11,124},{136,11,38},{5,11,261},{7,11,78},{7,11,199},{8,11,815},{9,11,126},{138, +11,342},{5,10,917},{134,10,1659},{5,11,595},{7,0,1759},{135,11,1863},{136,0,173} +,{134,0,266},{142,0,261},{132,11,628},{5,10,251},{5,10,956},{8,10,268},{9,10,214 +},{146,10,142},{7,11,266},{136,11,804},{135,11,208},{6,11,79},{7,11,1021},{135, +11,1519},{11,11,704},{141,11,396},{5,10,346},{5,10,711},{136,10,390},{136,11,741 +},{134,11,376},{134,0,1427},{6,0,1033},{6,0,1217},{136,0,300},{133,10,624},{6,11 +,100},{7,11,244},{7,11,632},{7,11,1609},{8,11,178},{8,11,638},{141,11,58},{5,10, +783},{6,0,584},{7,10,1998},{135,10,2047},{5,0,427},{5,0,734},{7,0,478},{136,0,52 +},{7,0,239},{11,0,217},{142,0,165},{134,0,1129},{6,0,168},{6,0,1734},{7,0,20},{7 +,0,1056},{8,0,732},{9,0,406},{9,0,911},{138,0,694},{132,10,594},{133,11,791},{7, +11,686},{8,11,33},{8,11,238},{10,11,616},{11,11,467},{11,11,881},{13,11,217},{13 +,11,253},{142,11,268},{137,11,476},{134,0,418},{133,0,613},{132,0,632},{132,11, +447},{7,0,32},{7,0,984},{8,0,85},{8,0,709},{9,0,579},{9,0,847},{9,0,856},{10,0, +799},{11,0,258},{11,0,1007},{12,0,331},{12,0,615},{13,0,188},{13,0,435},{14,0,8} +,{15,0,165},{16,0,27},{16,11,35},{148,0,40},{4,11,128},{5,11,415},{6,11,462},{7, +11,294},{7,11,578},{10,11,710},{139,11,86},{5,0,694},{136,0,909},{5,10,37},{6,10 +,39},{6,10,451},{7,0,1109},{7,10,218},{7,10,1166},{7,10,1687},{8,10,662},{11,0,7 +},{144,10,2},{136,11,587},{6,11,427},{7,11,1018},{138,11,692},{4,11,195},{6,10, +508},{135,11,802},{4,0,167},{135,0,82},{5,0,62},{6,0,24},{6,0,534},{7,0,74},{7,0 +,678},{7,0,684},{7,0,1043},{7,0,1072},{8,0,280},{8,0,541},{8,0,686},{9,0,258},{ +10,0,519},{11,0,252},{140,0,282},{138,0,33},{4,0,359},{133,11,738},{7,0,980},{7, +10,635},{7,10,796},{8,10,331},{9,0,328},{9,10,330},{9,10,865},{10,10,119},{10,10 +,235},{11,10,111},{11,10,129},{11,10,240},{12,10,31},{12,10,66},{12,10,222},{12, +10,269},{12,10,599},{12,10,684},{12,10,689},{12,10,691},{13,0,186},{13,0,364},{ +142,10,345},{137,10,527},{6,0,596},{7,0,585},{135,10,702},{134,11,1683},{133,0, +211},{6,0,145},{141,0,336},{134,0,1130},{6,10,37},{7,0,873},{7,10,1666},{8,10, +195},{8,10,316},{9,10,178},{9,10,276},{9,10,339},{9,10,536},{10,10,102},{10,10, +362},{10,10,785},{11,10,55},{11,10,149},{11,10,773},{13,10,416},{13,10,419},{14, +10,38},{14,10,41},{142,10,210},{8,0,840},{136,0,841},{132,0,263},{5,11,3},{8,11, +578},{9,11,118},{10,11,705},{12,11,383},{141,11,279},{132,0,916},{133,11,229},{ +133,10,645},{8,11,102},{10,11,578},{10,11,672},{12,11,496},{13,11,408},{14,11, +121},{15,0,155},{16,0,79},{145,11,106},{4,0,599},{5,0,592},{6,0,1634},{7,0,5},{7 +,0,55},{7,0,67},{7,0,97},{7,0,691},{7,0,979},{7,0,1600},{7,0,1697},{8,0,207},{8, +0,214},{8,0,231},{8,0,294},{8,0,336},{8,0,428},{8,0,471},{8,0,622},{8,0,626},{8, +0,679},{8,0,759},{8,0,829},{9,0,11},{9,0,246},{9,0,484},{9,0,573},{9,0,706},{9,0 +,762},{9,0,798},{9,0,855},{9,0,870},{9,0,912},{10,0,303},{10,0,335},{10,0,424},{ +10,0,461},{10,0,543},{10,0,759},{10,0,814},{11,0,59},{11,0,199},{11,0,235},{11,0 +,590},{11,0,631},{11,0,929},{11,0,963},{11,0,987},{12,0,114},{12,0,182},{12,0, +226},{12,0,332},{12,0,439},{12,0,575},{12,0,598},{12,0,675},{13,0,8},{13,0,125}, +{13,0,194},{13,0,287},{14,0,197},{14,0,383},{15,0,53},{17,0,63},{19,0,46},{19,0, +98},{19,0,106},{148,0,85},{4,10,290},{135,0,1356},{6,10,70},{7,10,1292},{10,10, +762},{139,10,288},{150,11,55},{4,0,593},{8,11,115},{8,11,350},{9,11,489},{10,11, +128},{11,11,306},{12,11,373},{14,11,30},{17,11,79},{147,11,80},{135,11,1235},{ +134,0,1392},{4,11,230},{133,11,702},{147,0,126},{7,10,131},{7,10,422},{8,10,210} +,{140,10,573},{134,0,1179},{139,11,435},{139,10,797},{134,11,1728},{4,0,162},{18 +,11,26},{19,11,42},{20,11,43},{21,11,0},{23,11,27},{152,11,14},{132,10,936},{5, +10,453},{6,0,765},{134,10,441},{133,0,187},{135,0,1286},{6,0,635},{6,0,904},{6,0 +,1210},{134,0,1489},{4,0,215},{8,0,890},{9,0,38},{10,0,923},{11,0,23},{11,0,127} +,{139,0,796},{6,0,1165},{134,0,1306},{7,0,716},{13,0,97},{141,0,251},{132,10,653 +},{136,0,657},{146,10,80},{5,11,622},{7,11,1032},{11,11,26},{11,11,213},{11,11, +707},{12,11,380},{13,11,226},{141,11,355},{5,11,70},{6,0,299},{6,11,334},{9,11, +171},{11,11,637},{12,11,202},{14,11,222},{145,11,42},{142,0,134},{4,11,23},{5,11 +,313},{5,11,1014},{6,11,50},{6,11,51},{7,11,142},{7,11,384},{9,11,783},{139,11, +741},{4,11,141},{7,11,559},{8,11,640},{9,11,460},{12,11,183},{141,11,488},{136, +11,614},{7,10,1368},{8,10,232},{8,10,361},{10,10,682},{138,10,742},{137,10,534}, +{6,0,1082},{140,0,658},{137,10,27},{135,0,2002},{142,10,12},{4,0,28},{5,0,440},{ +7,0,248},{11,0,833},{140,0,344},{7,10,736},{139,10,264},{134,10,1657},{134,0, +1654},{138,0,531},{5,11,222},{9,11,140},{138,11,534},{6,0,634},{6,0,798},{134,0, +840},{138,11,503},{135,10,127},{133,0,853},{5,11,154},{7,11,1491},{10,11,379},{ +138,11,485},{6,0,249},{7,0,1234},{139,0,573},{133,11,716},{7,11,1570},{140,11, +542},{136,10,364},{138,0,527},{4,11,91},{5,11,388},{5,11,845},{6,11,206},{6,11, +252},{6,11,365},{7,11,136},{7,11,531},{8,11,264},{136,11,621},{134,0,1419},{135, +11,1441},{7,0,49},{7,0,392},{8,0,20},{8,0,172},{8,0,690},{9,0,383},{9,0,845},{10 +,0,48},{11,0,293},{11,0,832},{11,0,920},{11,0,984},{141,0,221},{5,0,858},{133,0, +992},{5,0,728},{137,10,792},{5,10,909},{9,10,849},{138,10,805},{7,0,525},{7,0, +1579},{8,0,497},{136,0,573},{6,0,268},{137,0,62},{135,11,576},{134,0,1201},{5,11 +,771},{5,11,863},{5,11,898},{6,11,1632},{6,11,1644},{134,11,1780},{133,11,331},{ +7,0,193},{7,0,1105},{7,10,397},{8,10,124},{8,10,619},{9,10,305},{10,0,495},{11, +10,40},{12,10,349},{13,10,134},{13,10,295},{14,10,155},{15,10,120},{146,10,105}, +{138,0,106},{5,11,107},{6,0,859},{7,11,201},{136,11,518},{6,11,446},{135,11,1817 +},{4,10,262},{7,10,342},{141,0,23},{133,10,641},{137,11,851},{6,0,925},{137,0, +813},{132,11,504},{6,0,613},{136,0,223},{4,10,99},{6,10,250},{6,10,346},{8,10, +127},{138,10,81},{136,0,953},{132,10,915},{139,11,892},{5,10,75},{9,10,517},{10, +10,470},{12,10,155},{141,10,224},{4,0,666},{7,0,1017},{7,11,996},{138,11,390},{5 +,11,883},{133,11,975},{14,10,83},{142,11,83},{4,0,670},{5,11,922},{134,11,1707}, +{135,0,216},{7,11,787},{9,0,40},{139,0,136},{5,10,954},{5,11,993},{7,11,515},{ +137,11,91},{139,0,259},{6,10,304},{7,0,1114},{8,10,418},{9,0,310},{9,0,682},{10, +0,440},{11,10,341},{11,10,675},{141,0,40},{9,10,410},{11,10,425},{142,0,296},{10 +,11,377},{12,11,363},{13,11,68},{13,11,94},{14,11,108},{142,11,306},{7,0,1401},{ +135,0,1476},{4,0,296},{6,0,475},{7,0,401},{7,0,1410},{7,0,1594},{7,0,1674},{8,0, +63},{8,0,660},{137,0,74},{4,0,139},{4,0,388},{140,0,188},{132,0,797},{132,11,766 +},{5,11,103},{7,11,921},{8,11,580},{8,11,593},{8,11,630},{138,11,28},{4,11,911}, +{5,11,867},{133,11,1013},{134,10,14},{134,0,1572},{134,10,1708},{5,10,113},{6,10 +,243},{7,10,1865},{11,10,161},{16,10,37},{17,10,99},{149,0,39},{7,11,1563},{141, +11,182},{5,11,135},{6,11,519},{7,11,1722},{10,11,271},{11,11,261},{145,11,54},{ +132,10,274},{134,0,1594},{4,11,300},{5,11,436},{135,11,484},{4,0,747},{6,0,290}, +{7,0,649},{7,0,1479},{135,0,1583},{133,11,535},{147,11,82},{133,0,232},{137,0, +887},{135,10,166},{136,0,521},{4,0,14},{7,0,472},{7,0,1801},{10,0,748},{141,0, +458},{134,0,741},{134,0,992},{9,10,304},{144,0,111},{4,0,425},{5,11,387},{7,11, +557},{12,11,547},{142,11,86},{135,11,1747},{5,10,654},{135,11,1489},{4,11,6},{5, +11,708},{7,0,789},{136,11,75},{6,10,273},{10,10,188},{13,10,377},{146,10,77},{4, +11,303},{6,0,1593},{7,11,619},{10,11,547},{10,11,687},{11,11,122},{140,11,601},{ +134,0,1768},{135,10,410},{138,11,772},{11,0,233},{139,10,524},{5,0,943},{134,0, +1779},{134,10,1785},{136,11,529},{132,0,955},{5,0,245},{6,0,576},{7,0,582},{136, +0,225},{132,10,780},{142,0,241},{134,0,1943},{4,11,106},{7,11,310},{7,11,1785},{ +10,11,690},{139,11,717},{134,0,1284},{5,11,890},{133,11,988},{6,11,626},{142,11, +431},{10,11,706},{145,11,32},{137,11,332},{132,11,698},{135,0,709},{5,10,948},{ +138,11,17},{136,0,554},{134,0,1564},{139,10,941},{132,0,443},{134,0,909},{134,11 +,84},{142,0,280},{4,10,532},{5,10,706},{135,10,662},{132,0,729},{5,10,837},{6,10 +,1651},{139,10,985},{135,10,1861},{4,0,348},{152,11,3},{5,11,986},{6,11,130},{7, +11,1582},{8,11,458},{10,11,101},{10,11,318},{138,11,823},{134,0,758},{4,0,298},{ +137,0,848},{4,10,330},{7,10,933},{7,10,2012},{136,10,292},{7,11,1644},{137,11, +129},{6,0,1422},{7,10,767},{137,0,829},{5,0,164},{7,0,121},{142,0,189},{7,0,812} +,{7,0,1261},{7,0,1360},{9,0,632},{140,0,352},{135,11,1788},{139,0,556},{135,11, +997},{145,10,114},{4,0,172},{9,0,611},{9,10,883},{10,0,436},{12,0,673},{141,0, +255},{10,10,274},{139,0,530},{133,0,844},{134,0,984},{4,10,703},{7,10,207},{13,0 +,232},{146,0,35},{132,10,571},{9,0,263},{10,0,147},{138,0,492},{7,11,1756},{137, +11,98},{5,10,873},{5,10,960},{8,10,823},{137,10,881},{133,0,537},{132,0,859},{7, +11,1046},{139,11,160},{137,0,842},{139,10,283},{5,10,33},{6,10,470},{139,10,424} +,{6,11,45},{7,11,433},{8,11,129},{9,11,21},{10,11,392},{11,11,79},{12,11,499},{ +13,11,199},{141,11,451},{135,0,1291},{135,10,1882},{7,11,558},{136,11,353},{134, +0,1482},{5,0,230},{5,0,392},{6,0,420},{9,0,568},{140,0,612},{6,0,262},{7,10,90}, +{7,10,664},{7,10,830},{7,10,1380},{7,10,2025},{8,10,448},{8,10,828},{8,11,81},{9 +,11,189},{9,11,201},{11,11,478},{11,11,712},{141,11,338},{142,0,31},{5,11,353},{ +151,11,26},{132,0,753},{4,0,0},{5,0,41},{7,0,1459},{7,0,1469},{7,0,1859},{9,0, +549},{139,0,905},{9,10,417},{137,10,493},{135,11,1113},{133,0,696},{141,11,448}, +{134,10,295},{132,0,834},{4,0,771},{5,10,1019},{6,11,25},{7,11,855},{7,11,1258}, +{144,11,32},{134,0,1076},{133,0,921},{133,0,674},{4,11,4},{7,11,1118},{7,11,1320 +},{7,11,1706},{8,11,277},{9,11,622},{10,11,9},{11,11,724},{12,11,350},{12,11,397 +},{13,11,28},{13,11,159},{15,11,89},{18,11,5},{19,11,9},{20,11,34},{150,11,47},{ +134,10,208},{6,0,444},{136,0,308},{6,0,180},{7,0,1137},{8,0,751},{139,0,805},{4, +0,183},{7,0,271},{11,0,824},{11,0,952},{13,0,278},{13,0,339},{13,0,482},{14,0, +424},{148,0,99},{7,11,317},{135,11,569},{4,0,19},{5,0,477},{5,0,596},{6,0,505},{ +7,0,1221},{11,0,907},{12,0,209},{141,0,214},{135,0,1215},{6,0,271},{7,0,398},{7, +10,448},{7,10,1629},{7,10,1813},{8,0,387},{8,10,442},{9,10,710},{10,0,344},{10, +10,282},{138,10,722},{11,10,844},{12,10,104},{140,10,625},{134,11,255},{133,10, +787},{134,0,1645},{11,11,956},{151,11,3},{6,0,92},{6,0,188},{7,0,209},{7,0,1269} +,{7,0,1524},{7,0,1876},{8,0,661},{10,0,42},{10,0,228},{11,0,58},{11,0,1020},{12, +0,58},{12,0,118},{141,0,32},{4,0,459},{133,0,966},{4,11,536},{7,11,1141},{10,11, +723},{139,11,371},{140,0,330},{134,0,1557},{7,11,285},{135,11,876},{136,10,491}, +{135,11,560},{6,0,18},{7,0,179},{7,0,932},{8,0,548},{8,0,757},{9,0,54},{9,0,65}, +{9,0,532},{9,0,844},{10,0,113},{10,0,117},{10,0,315},{10,0,560},{10,0,622},{10,0 +,798},{11,0,153},{11,0,351},{11,0,375},{12,0,78},{12,0,151},{12,0,392},{12,0,666 +},{14,0,248},{143,0,23},{4,11,690},{134,0,1742},{4,10,403},{5,10,441},{7,10,450} +,{10,10,840},{11,10,101},{12,10,193},{141,10,430},{133,0,965},{134,0,182},{10,0, +65},{10,0,488},{138,0,497},{135,11,1346},{6,0,973},{6,0,1158},{10,11,200},{19,11 +,2},{151,11,22},{4,11,190},{133,11,554},{133,10,679},{7,0,328},{137,10,326},{133 +,11,1001},{9,0,588},{138,0,260},{133,11,446},{135,10,1128},{135,10,1796},{147,11 +,119},{134,0,1786},{6,0,1328},{6,0,1985},{8,0,962},{138,0,1017},{135,0,308},{4, +10,574},{7,10,350},{7,10,1024},{8,10,338},{9,10,677},{10,10,808},{139,0,508},{ +138,11,752},{135,10,1081},{137,11,96},{7,10,1676},{135,10,2037},{136,0,588},{132 +,11,304},{133,0,614},{140,0,793},{136,0,287},{137,10,297},{141,10,37},{6,11,53}, +{6,11,199},{7,11,1408},{8,11,32},{8,11,93},{9,11,437},{10,11,397},{10,11,629},{ +11,11,593},{11,11,763},{13,11,326},{145,11,35},{134,11,105},{9,11,320},{10,11, +506},{138,11,794},{5,11,114},{5,11,255},{141,11,285},{140,0,290},{7,11,2035},{8, +11,19},{9,11,89},{138,11,831},{134,0,1136},{6,10,306},{7,0,719},{7,10,1140},{7, +10,1340},{8,0,796},{8,0,809},{8,0,834},{8,10,133},{138,10,449},{139,10,1011},{5, +0,210},{6,0,213},{7,0,60},{10,0,364},{139,0,135},{5,0,607},{8,0,326},{136,0,490} +,{138,11,176},{132,0,701},{5,0,472},{7,0,380},{137,0,758},{135,0,1947},{6,0,1079 +},{138,0,278},{138,11,391},{5,10,329},{8,10,260},{139,11,156},{4,0,386},{7,0,41} +,{8,0,405},{8,0,728},{9,0,497},{11,0,110},{11,0,360},{15,0,37},{144,0,84},{5,0, +46},{7,0,1452},{7,0,1480},{8,0,634},{140,0,472},{136,0,961},{4,0,524},{136,0,810 +},{10,0,238},{141,0,33},{132,10,657},{152,10,7},{133,0,532},{5,0,997},{135,10, +1665},{7,11,594},{7,11,851},{7,11,1858},{9,11,411},{9,11,574},{9,11,666},{9,11, +737},{10,11,346},{10,11,712},{11,11,246},{11,11,432},{11,11,517},{11,11,647},{11 +,11,679},{11,11,727},{12,11,304},{12,11,305},{12,11,323},{12,11,483},{12,11,572} +,{12,11,593},{12,11,602},{13,11,95},{13,11,101},{13,11,171},{13,11,315},{13,11, +378},{13,11,425},{13,11,475},{14,11,63},{14,11,380},{14,11,384},{15,11,133},{18, +11,112},{148,11,72},{5,11,955},{136,11,814},{134,0,1301},{5,10,66},{7,10,1896},{ +136,10,288},{133,11,56},{134,10,1643},{6,0,1298},{148,11,100},{5,0,782},{5,0,829 +},{6,0,671},{6,0,1156},{6,0,1738},{137,11,621},{4,0,306},{5,0,570},{5,10,91},{5, +10,648},{5,10,750},{5,10,781},{6,10,54},{6,10,112},{6,10,402},{6,10,1732},{7,0, +1347},{7,10,315},{7,10,749},{7,10,1900},{9,10,78},{9,10,508},{10,10,611},{10,10, +811},{11,10,510},{11,10,728},{13,10,36},{14,10,39},{16,10,83},{17,10,124},{148, +10,30},{8,10,570},{9,11,477},{141,11,78},{4,11,639},{10,10,322},{10,10,719},{10, +11,4},{11,10,407},{11,11,638},{12,11,177},{148,11,57},{7,0,1823},{139,0,693},{5, +11,758},{7,0,759},{8,10,125},{8,10,369},{8,10,524},{10,10,486},{11,10,13},{11,10 +,381},{11,10,736},{11,10,766},{11,10,845},{13,10,114},{13,10,292},{142,10,47},{6 +,10,1684},{6,10,1731},{7,0,1932},{7,10,356},{8,10,54},{8,10,221},{9,10,225},{9, +10,356},{10,10,77},{10,10,446},{10,10,731},{12,10,404},{141,10,491},{135,11,552} +,{135,11,1112},{4,0,78},{5,0,96},{5,0,182},{6,0,1257},{7,0,1724},{7,0,1825},{10, +0,394},{10,0,471},{11,0,532},{14,0,340},{145,0,88},{139,11,328},{135,0,1964},{ +132,10,411},{4,10,80},{5,10,44},{137,11,133},{5,11,110},{6,11,169},{6,11,1702},{ +7,11,400},{8,11,538},{9,11,184},{9,11,524},{140,11,218},{4,0,521},{5,10,299},{7, +10,1083},{140,11,554},{6,11,133},{9,11,353},{12,11,628},{146,11,79},{6,0,215},{7 +,0,584},{7,0,1028},{7,0,1473},{7,0,1721},{9,0,424},{138,0,779},{7,0,857},{7,0, +1209},{7,10,1713},{9,10,537},{10,10,165},{12,10,219},{140,10,561},{4,10,219},{6, +11,93},{7,10,1761},{7,11,1422},{7,11,1851},{8,11,673},{9,10,86},{9,11,529},{140, +11,43},{137,11,371},{136,0,671},{5,0,328},{135,0,918},{132,0,529},{9,11,25},{10, +11,467},{138,11,559},{4,11,335},{135,11,942},{134,0,716},{134,0,1509},{6,0,67},{ +7,0,258},{7,0,1630},{9,0,354},{9,0,675},{10,0,830},{12,10,428},{14,0,80},{145,0, +80},{134,0,1112},{6,0,141},{7,0,225},{9,0,59},{9,0,607},{10,0,312},{11,0,687},{ +12,0,555},{13,0,373},{13,0,494},{148,0,58},{133,10,514},{8,11,39},{10,11,773},{ +11,11,84},{12,11,205},{142,11,1},{5,11,601},{5,11,870},{136,0,783},{136,11,594}, +{4,10,55},{5,10,301},{6,10,571},{14,10,49},{146,10,102},{132,11,181},{134,11, +1652},{133,10,364},{4,11,97},{5,11,147},{6,11,286},{7,11,1362},{141,11,176},{4, +10,76},{7,10,1550},{9,10,306},{9,10,430},{9,10,663},{10,10,683},{11,10,427},{11, +10,753},{12,10,334},{12,10,442},{14,10,258},{14,10,366},{143,10,131},{137,10,52} +,{6,0,955},{134,0,1498},{6,11,375},{7,11,169},{7,11,254},{136,11,780},{7,0,430}, +{11,0,46},{14,0,343},{142,11,343},{135,0,1183},{5,0,602},{7,0,2018},{7,11,1447}, +{9,0,418},{137,0,803},{7,11,1044},{136,0,677},{139,11,285},{4,10,656},{135,10, +779},{135,10,144},{5,11,629},{135,11,1549},{135,10,1373},{138,11,209},{7,10,554} +,{7,10,605},{141,10,10},{5,10,838},{5,10,841},{134,10,1649},{133,10,1012},{6,0, +1357},{134,0,1380},{144,0,53},{6,0,590},{7,10,365},{7,10,1357},{7,10,1497},{8,10 +,154},{141,10,281},{133,10,340},{132,11,420},{135,0,329},{147,11,32},{4,0,469},{ +10,11,429},{139,10,495},{8,10,261},{9,10,144},{9,10,466},{10,10,370},{12,10,470} +,{13,10,144},{142,10,348},{142,0,460},{4,11,325},{9,10,897},{138,11,125},{6,0, +1743},{6,10,248},{9,10,546},{10,10,535},{11,10,681},{141,10,135},{4,0,990},{5,0, +929},{6,0,340},{8,0,376},{8,0,807},{8,0,963},{8,0,980},{138,0,1007},{134,0,1603} +,{140,0,250},{4,11,714},{133,11,469},{134,10,567},{136,10,445},{5,0,218},{7,0, +1610},{8,0,646},{10,0,83},{11,11,138},{140,11,40},{7,0,1512},{135,0,1794},{135, +11,1216},{4,11,718},{11,0,0},{144,0,78},{133,0,571},{132,0,455},{134,0,1012},{5, +11,124},{5,11,144},{6,11,548},{7,11,15},{7,11,153},{137,11,629},{142,11,10},{6, +11,75},{7,11,1531},{8,11,416},{9,11,240},{9,11,275},{10,11,100},{11,11,658},{11, +11,979},{12,11,86},{13,11,468},{14,11,66},{14,11,207},{15,11,20},{15,11,25},{144 +,11,58},{132,10,577},{5,11,141},{5,11,915},{6,11,1783},{7,11,211},{7,11,698},{7, +11,1353},{9,11,83},{9,11,281},{10,11,376},{10,11,431},{11,11,543},{12,11,664},{ +13,11,280},{13,11,428},{14,11,61},{14,11,128},{17,11,52},{145,11,81},{6,0,161},{ +7,0,372},{137,0,597},{132,0,349},{10,11,702},{139,11,245},{134,0,524},{134,10, +174},{6,0,432},{9,0,751},{139,0,322},{147,11,94},{4,11,338},{133,11,400},{5,0, +468},{10,0,325},{11,0,856},{12,0,345},{143,0,104},{133,0,223},{132,0,566},{4,11, +221},{5,11,659},{5,11,989},{7,11,697},{7,11,1211},{138,11,284},{135,11,1070},{4, +0,59},{135,0,1394},{5,10,878},{5,10,972},{6,0,436},{139,0,481},{4,0,48},{5,0,271 +},{135,0,953},{5,0,610},{136,0,457},{4,0,773},{5,0,618},{137,0,756},{133,0,755}, +{135,0,1217},{138,11,507},{132,10,351},{132,0,197},{143,11,78},{4,11,188},{7,11, +805},{11,11,276},{142,11,293},{5,11,884},{139,11,991},{132,10,286},{7,10,438},{7 +,10,627},{7,10,1516},{8,10,40},{9,10,56},{9,10,294},{10,0,259},{10,0,428},{11,10 +,969},{11,10,995},{146,10,148},{4,0,356},{5,0,217},{5,0,492},{5,0,656},{8,0,544} +,{136,11,544},{5,0,259},{6,0,1230},{7,0,414},{7,0,854},{142,0,107},{132,0,1007}, +{15,0,14},{144,0,5},{4,10,738},{134,0,1580},{132,11,596},{132,0,673},{133,10,866 +},{6,0,1843},{135,11,1847},{4,0,165},{7,0,1398},{135,0,1829},{135,11,1634},{147, +11,65},{6,0,885},{6,0,1009},{137,0,809},{133,10,116},{132,10,457},{136,11,770},{ +9,0,498},{10,11,361},{12,0,181},{142,11,316},{134,11,595},{5,0,9},{7,0,297},{7,0 +,966},{140,0,306},{4,11,89},{5,11,489},{6,11,315},{7,11,553},{7,11,1745},{138,11 +,243},{134,0,1487},{132,0,437},{5,0,146},{6,0,411},{138,0,721},{5,10,527},{6,10, +189},{135,10,859},{11,10,104},{11,10,554},{15,10,60},{143,10,125},{6,11,1658},{9 +,11,3},{10,11,154},{11,11,641},{13,11,85},{13,11,201},{141,11,346},{6,0,177},{ +135,0,467},{134,0,1377},{134,10,116},{136,11,645},{4,11,166},{5,11,505},{6,11, +1670},{137,11,110},{133,10,487},{4,10,86},{5,10,667},{5,10,753},{6,10,316},{6,10 +,455},{135,10,946},{133,0,200},{132,0,959},{6,0,1928},{134,0,1957},{139,11,203}, +{150,10,45},{4,10,79},{7,10,1773},{10,10,450},{11,10,589},{13,10,332},{13,10,493 +},{14,10,183},{14,10,334},{14,10,362},{14,10,368},{14,10,376},{14,10,379},{19,10 +,90},{19,10,103},{19,10,127},{148,10,90},{6,0,1435},{135,11,1275},{134,0,481},{7 +,11,445},{8,11,307},{8,11,704},{10,11,41},{10,11,439},{11,11,237},{11,11,622},{ +140,11,201},{135,11,869},{4,0,84},{7,0,1482},{10,0,76},{138,0,142},{11,11,277},{ +144,11,14},{135,11,1977},{4,11,189},{5,11,713},{136,11,57},{133,0,1015},{138,11, +371},{4,0,315},{5,0,507},{135,0,1370},{4,11,552},{142,10,381},{9,0,759},{16,0,31 +},{16,0,39},{16,0,75},{18,0,24},{20,0,42},{152,0,1},{134,0,712},{134,0,1722},{ +133,10,663},{133,10,846},{8,0,222},{8,0,476},{9,0,238},{11,0,516},{11,0,575},{15 +,0,109},{146,0,100},{5,10,378},{7,0,1402},{7,0,1414},{8,10,465},{9,10,286},{10, +10,185},{10,10,562},{10,10,635},{11,10,31},{11,10,393},{12,0,456},{13,10,312},{ +18,10,65},{18,10,96},{147,10,89},{4,0,986},{6,0,1958},{6,0,2032},{8,0,934},{138, +0,985},{7,10,1880},{9,10,680},{139,10,798},{134,10,1770},{145,11,49},{132,11,614 +},{132,10,648},{5,10,945},{6,10,1656},{6,10,1787},{7,10,167},{8,10,824},{9,10, +391},{10,10,375},{139,10,185},{138,11,661},{7,0,1273},{135,11,1945},{7,0,706},{7 +,0,1058},{138,0,538},{7,10,1645},{8,10,352},{137,10,249},{132,10,152},{11,0,92}, +{11,0,196},{11,0,409},{11,0,450},{11,0,666},{11,0,777},{12,0,262},{13,0,385},{13 +,0,393},{15,0,115},{16,0,45},{145,0,82},{133,10,1006},{6,0,40},{135,0,1781},{9, +11,614},{139,11,327},{5,10,420},{135,10,1449},{135,0,431},{7,10,832},{138,0,97}, +{6,0,423},{7,0,665},{135,0,1210},{7,0,237},{8,0,664},{9,0,42},{9,0,266},{9,0,380 +},{9,0,645},{10,0,177},{138,0,276},{5,10,351},{135,0,264},{5,10,40},{7,10,598},{ +7,10,1638},{8,0,213},{9,10,166},{9,10,640},{9,10,685},{9,10,773},{11,10,215},{13 +,10,65},{14,10,172},{14,10,317},{145,10,6},{5,11,84},{134,11,163},{8,10,60},{9, +10,343},{139,10,769},{137,0,455},{133,11,410},{8,0,906},{12,0,700},{12,0,706},{ +140,0,729},{21,11,33},{150,11,40},{7,10,1951},{8,10,765},{8,10,772},{140,10,671} +,{7,10,108},{8,10,219},{8,10,388},{9,10,639},{9,10,775},{11,10,275},{140,10,464} +,{5,11,322},{7,11,1941},{8,11,186},{9,11,262},{10,11,187},{14,11,208},{146,11, +130},{139,0,624},{5,11,227},{8,0,574},{140,11,29},{7,11,1546},{11,11,299},{142, +11,407},{5,10,15},{6,10,56},{7,10,1758},{8,10,500},{9,10,730},{11,10,331},{13,10 +,150},{142,10,282},{7,11,1395},{8,11,486},{9,11,236},{9,11,878},{10,11,218},{11, +11,95},{19,11,17},{147,11,31},{135,11,2043},{4,0,354},{146,11,4},{140,11,80},{ +135,0,1558},{134,10,1886},{5,10,205},{6,10,438},{137,10,711},{133,11,522},{133, +10,534},{7,0,235},{7,0,1475},{15,0,68},{146,0,120},{137,10,691},{4,0,942},{6,0, +1813},{8,0,917},{10,0,884},{12,0,696},{12,0,717},{12,0,723},{12,0,738},{12,0,749 +},{12,0,780},{16,0,97},{146,0,169},{6,10,443},{8,11,562},{9,10,237},{9,10,571},{ +9,10,695},{10,10,139},{11,10,715},{12,10,417},{141,10,421},{135,0,957},{133,0, +830},{134,11,1771},{146,0,23},{5,0,496},{6,0,694},{7,0,203},{7,11,1190},{137,11, +620},{137,11,132},{6,0,547},{134,0,1549},{8,11,258},{9,11,208},{137,11,359},{4,0 +,864},{5,0,88},{137,0,239},{135,11,493},{4,11,317},{135,11,1279},{132,11,477},{4 +,10,578},{5,11,63},{133,11,509},{7,0,650},{135,0,1310},{7,0,1076},{9,0,80},{11,0 +,78},{11,0,421},{11,0,534},{140,0,545},{132,11,288},{5,10,923},{12,0,553},{142,0 +,118},{7,0,274},{11,0,479},{139,0,507},{8,11,89},{8,11,620},{9,11,49},{10,11,774 +},{11,11,628},{12,11,322},{143,11,124},{4,0,497},{135,0,1584},{4,10,924},{7,0, +261},{7,0,1115},{7,0,1354},{7,0,1404},{7,0,1588},{7,0,1705},{7,0,1902},{9,0,465} +,{10,0,248},{10,0,349},{10,0,647},{11,0,527},{11,0,660},{11,0,669},{12,0,529},{ +141,0,305},{133,10,665},{136,0,13},{6,0,791},{138,11,120},{7,0,642},{8,0,250},{ +11,0,123},{11,0,137},{13,0,48},{142,0,95},{4,10,265},{7,10,807},{135,10,950},{5, +10,93},{140,10,267},{135,0,1429},{4,0,949},{10,0,885},{10,0,891},{10,0,900},{10, +0,939},{12,0,760},{142,0,449},{139,11,366},{132,0,818},{134,11,85},{135,10,994}, +{5,10,233},{5,10,320},{6,10,140},{7,0,330},{136,10,295},{4,0,1004},{8,0,982},{ +136,0,993},{133,10,978},{4,10,905},{6,10,1701},{137,10,843},{10,0,545},{140,0, +301},{6,0,947},{134,0,1062},{134,0,1188},{4,0,904},{5,0,794},{152,10,6},{134,0, +1372},{135,11,608},{5,11,279},{6,11,235},{7,11,468},{8,11,446},{9,11,637},{10,11 +,717},{11,11,738},{140,11,514},{132,10,509},{5,11,17},{6,11,371},{137,11,528},{ +132,0,693},{4,11,115},{5,11,669},{6,11,407},{8,11,311},{11,11,10},{141,11,5},{7, +10,273},{9,11,381},{139,0,377},{135,0,695},{7,0,386},{138,0,713},{135,10,1041},{ +134,0,1291},{6,0,7},{6,0,35},{7,0,147},{7,0,1069},{7,0,1568},{7,0,1575},{7,0, +1917},{8,0,43},{8,0,208},{9,0,128},{9,0,866},{10,0,20},{11,0,981},{147,0,33},{7, +0,893},{141,0,424},{139,10,234},{150,11,56},{5,11,779},{5,11,807},{6,11,1655},{ +134,11,1676},{5,10,802},{7,10,2021},{136,10,805},{4,11,196},{5,10,167},{5,10,899 +},{5,11,558},{5,11,949},{6,10,410},{137,10,777},{137,10,789},{134,10,1705},{8,0, +904},{140,0,787},{6,0,322},{9,0,552},{11,0,274},{13,0,209},{13,0,499},{14,0,85}, +{15,0,126},{145,0,70},{135,10,10},{5,10,11},{6,10,117},{6,10,485},{7,10,1133},{9 +,10,582},{9,10,594},{11,10,21},{11,10,818},{12,10,535},{141,10,86},{4,10,264},{7 +,10,1067},{8,10,204},{8,10,385},{139,10,953},{132,11,752},{138,10,56},{133,10, +470},{6,0,1808},{8,0,83},{8,0,742},{8,0,817},{9,0,28},{9,0,29},{9,0,885},{10,0, +387},{11,0,633},{11,0,740},{13,0,235},{13,0,254},{15,0,143},{143,0,146},{140,0, +49},{134,0,1832},{4,11,227},{5,11,159},{5,11,409},{7,11,80},{10,11,294},{10,11, +479},{12,11,418},{14,11,50},{14,11,249},{142,11,295},{7,11,1470},{8,11,66},{8,11 +,137},{8,11,761},{9,11,638},{11,11,80},{11,11,212},{11,11,368},{11,11,418},{12, +11,8},{13,11,15},{16,11,61},{17,11,59},{19,11,28},{148,11,84},{139,10,1015},{138 +,11,468},{135,0,421},{6,0,415},{7,0,1049},{137,0,442},{6,11,38},{7,11,1220},{8, +11,185},{8,11,256},{9,11,22},{9,11,331},{10,11,738},{11,11,205},{11,11,540},{11, +11,746},{13,11,399},{13,11,465},{14,11,88},{142,11,194},{139,0,289},{133,10,715} +,{4,0,110},{10,0,415},{10,0,597},{142,0,206},{4,11,159},{6,11,115},{7,11,252},{7 +,11,257},{7,11,1928},{8,11,69},{9,11,384},{10,11,91},{10,11,615},{12,11,375},{14 +,11,235},{18,11,117},{147,11,123},{5,11,911},{136,11,278},{7,0,205},{7,0,2000},{ +8,10,794},{9,10,400},{10,10,298},{142,10,228},{135,11,1774},{4,11,151},{7,11, +1567},{8,11,351},{137,11,322},{136,10,724},{133,11,990},{7,0,1539},{7,11,1539},{ +11,0,512},{13,0,205},{19,0,30},{22,0,36},{151,0,19},{5,11,194},{7,11,1662},{9,11 +,90},{140,11,180},{6,10,190},{7,10,768},{135,10,1170},{134,0,1340},{4,0,283},{ +135,0,1194},{133,11,425},{133,11,971},{12,0,549},{14,10,67},{147,10,60},{135,10, +1023},{134,0,1720},{138,11,587},{5,11,72},{6,11,264},{7,11,21},{7,11,46},{7,11, +2013},{8,11,215},{8,11,513},{10,11,266},{139,11,22},{5,0,319},{135,0,534},{6,10, +137},{9,10,75},{9,10,253},{10,10,194},{138,10,444},{6,11,239},{7,0,1180},{7,11, +118},{10,11,95},{11,11,603},{13,11,443},{14,11,160},{15,11,4},{148,0,112},{134, +11,431},{5,11,874},{6,11,1677},{11,10,643},{12,10,115},{143,11,0},{134,0,967},{6 +,11,65},{7,11,939},{7,11,1172},{7,11,1671},{9,11,540},{10,11,696},{11,11,265},{ +11,11,732},{11,11,928},{11,11,937},{12,11,399},{13,11,438},{149,11,19},{137,11, +200},{135,0,1940},{5,10,760},{7,10,542},{8,10,135},{136,10,496},{140,11,44},{7, +11,1655},{136,11,305},{7,10,319},{7,10,355},{7,10,763},{10,10,389},{145,10,43},{ +136,0,735},{138,10,786},{137,11,19},{132,11,696},{5,0,132},{9,0,486},{9,0,715},{ +10,0,458},{11,0,373},{11,0,668},{11,0,795},{11,0,897},{12,0,272},{12,0,424},{12, +0,539},{12,0,558},{14,0,245},{14,0,263},{14,0,264},{14,0,393},{142,0,403},{10,0, +38},{139,0,784},{132,0,838},{4,11,302},{135,11,1766},{133,0,379},{4,11,726},{5,0 +,8},{5,11,630},{6,0,89},{6,0,400},{7,0,1569},{7,0,1623},{7,0,1850},{8,0,218},{8, +0,422},{9,0,570},{138,0,626},{4,0,1017},{138,0,660},{6,0,387},{7,0,882},{141,0, +111},{6,0,224},{7,0,877},{137,0,647},{4,10,58},{5,10,286},{6,10,319},{7,10,402}, +{7,10,1254},{7,10,1903},{8,10,356},{140,10,408},{135,0,790},{4,10,389},{9,0,510} +,{9,10,181},{10,0,53},{10,10,29},{10,10,816},{11,10,311},{11,10,561},{12,10,67}, +{141,10,181},{142,0,458},{6,11,118},{7,11,215},{7,11,1521},{140,11,11},{134,0, +954},{135,0,394},{134,0,1367},{5,10,373},{133,11,225},{132,0,882},{7,0,1409},{ +135,10,1972},{135,10,1793},{4,11,370},{5,11,756},{135,11,1326},{150,11,13},{7,11 +,354},{10,11,410},{139,11,815},{6,11,1662},{7,11,48},{8,11,771},{10,11,116},{13, +11,104},{14,11,105},{14,11,184},{15,11,168},{19,11,92},{148,11,68},{7,0,124},{ +136,0,38},{5,0,261},{7,0,78},{7,0,199},{8,0,815},{9,0,126},{10,0,342},{140,0,647 +},{4,0,628},{140,0,724},{7,0,266},{7,10,1651},{8,0,804},{145,10,89},{135,0,208}, +{134,0,1178},{6,0,79},{135,0,1519},{132,10,672},{133,10,737},{136,0,741},{132,11 +,120},{4,0,710},{6,0,376},{134,0,606},{134,0,1347},{134,0,1494},{6,0,850},{6,0, +1553},{137,0,821},{5,10,145},{134,11,593},{7,0,1311},{140,0,135},{4,0,467},{5,0, +405},{134,0,544},{5,11,820},{135,11,931},{6,0,100},{7,0,244},{7,0,632},{7,0,1609 +},{8,0,178},{8,0,638},{141,0,58},{4,10,387},{135,10,1288},{6,11,151},{6,11,1675} +,{7,11,383},{151,11,10},{132,0,481},{135,10,550},{134,0,1378},{6,11,1624},{11,11 +,11},{12,11,422},{13,11,262},{142,11,360},{133,0,791},{4,11,43},{5,11,344},{133, +11,357},{7,0,1227},{140,0,978},{7,0,686},{8,0,33},{8,0,238},{10,0,616},{11,0,467 +},{11,0,881},{13,0,217},{13,0,253},{142,0,268},{137,0,857},{7,11,148},{8,0,467}, +{8,0,1006},{8,11,284},{141,11,63},{4,10,576},{135,10,1263},{133,11,888},{5,10, +919},{134,10,1673},{20,10,37},{148,11,37},{132,0,447},{132,11,711},{4,0,128},{5, +0,415},{6,0,462},{7,0,294},{7,0,578},{10,0,710},{139,0,86},{4,10,82},{5,10,333}, +{5,10,904},{6,10,207},{7,10,325},{7,10,1726},{8,10,101},{10,10,778},{139,10,220} +,{136,0,587},{137,11,440},{133,10,903},{6,0,427},{7,0,1018},{138,0,692},{4,0,195 +},{135,0,802},{140,10,147},{134,0,1546},{134,0,684},{132,10,705},{136,0,345},{11 +,11,678},{140,11,307},{133,0,365},{134,0,1683},{4,11,65},{5,11,479},{5,11,1004}, +{7,11,1913},{8,11,317},{9,11,302},{10,11,612},{141,11,22},{138,0,472},{4,11,261} +,{135,11,510},{134,10,90},{142,0,433},{151,0,28},{4,11,291},{7,11,101},{9,11,515 +},{12,11,152},{12,11,443},{13,11,392},{142,11,357},{140,0,997},{5,0,3},{8,0,578} +,{9,0,118},{10,0,705},{141,0,279},{135,11,1266},{7,10,813},{12,10,497},{141,10, +56},{133,0,229},{6,10,125},{135,10,1277},{8,0,102},{10,0,578},{10,0,672},{12,0, +496},{13,0,408},{14,0,121},{17,0,106},{151,10,12},{6,0,866},{134,0,1080},{136,0, +1022},{4,11,130},{135,11,843},{5,11,42},{5,11,879},{7,11,245},{7,11,324},{7,11, +1532},{11,11,463},{11,11,472},{13,11,363},{144,11,52},{150,0,55},{4,11,134},{5, +11,372},{8,0,115},{8,0,350},{9,0,489},{10,0,128},{11,0,306},{12,0,373},{14,0,30} +,{17,0,79},{147,0,80},{134,0,657},{134,0,933},{135,11,1147},{4,0,230},{133,0,702 +},{134,0,1728},{4,0,484},{18,0,26},{19,0,42},{20,0,43},{21,0,0},{23,0,27},{152,0 +,14},{7,0,185},{135,0,703},{6,0,417},{7,10,1106},{9,10,770},{10,0,618},{11,10, +112},{140,10,413},{134,0,803},{132,11,644},{134,0,1262},{7,11,540},{12,10,271},{ +145,10,109},{135,11,123},{132,0,633},{134,11,623},{4,11,908},{5,11,359},{5,11, +508},{6,11,1723},{7,11,343},{7,11,1996},{135,11,2026},{135,0,479},{7,10,304},{9, +10,646},{9,10,862},{10,0,262},{11,10,696},{12,10,208},{15,10,79},{147,10,108},{4 +,11,341},{135,11,480},{134,0,830},{5,0,70},{5,0,622},{6,0,334},{7,0,1032},{9,0, +171},{11,0,26},{11,0,213},{11,0,637},{11,0,707},{12,0,202},{12,0,380},{13,0,226} +,{13,0,355},{14,0,222},{145,0,42},{135,10,981},{143,0,217},{137,11,114},{4,0,23} +,{4,0,141},{5,0,313},{5,0,1014},{6,0,50},{6,0,51},{7,0,142},{7,0,384},{7,0,559}, +{8,0,640},{9,0,460},{9,0,783},{11,0,741},{12,0,183},{141,0,488},{141,0,360},{7,0 +,1586},{7,11,1995},{8,11,299},{11,11,890},{140,11,674},{132,10,434},{6,10,550},{ +135,0,652},{5,10,553},{7,0,766},{138,10,824},{7,0,737},{8,0,298},{136,10,452},{4 +,11,238},{5,11,503},{6,11,179},{7,11,2003},{8,11,381},{8,11,473},{9,11,149},{10, +11,183},{15,11,45},{143,11,86},{133,10,292},{5,0,222},{9,0,655},{138,0,534},{138 +,10,135},{4,11,121},{5,11,156},{5,11,349},{9,11,136},{10,11,605},{14,11,342},{ +147,11,107},{137,0,906},{6,0,1013},{134,0,1250},{6,0,1956},{6,0,2009},{8,0,991}, +{144,0,120},{135,11,1192},{138,0,503},{5,0,154},{7,0,1491},{10,0,379},{138,0,485 +},{6,0,1867},{6,0,1914},{6,0,1925},{9,0,917},{9,0,925},{9,0,932},{9,0,951},{9,0, +1007},{9,0,1013},{12,0,806},{12,0,810},{12,0,814},{12,0,816},{12,0,824},{12,0, +832},{12,0,837},{12,0,863},{12,0,868},{12,0,870},{12,0,889},{12,0,892},{12,0,900 +},{12,0,902},{12,0,908},{12,0,933},{12,0,942},{12,0,949},{12,0,954},{15,0,175},{ +15,0,203},{15,0,213},{15,0,218},{15,0,225},{15,0,231},{15,0,239},{15,0,248},{15, +0,252},{18,0,190},{18,0,204},{18,0,215},{18,0,216},{18,0,222},{18,0,225},{18,0, +230},{18,0,239},{18,0,241},{21,0,42},{21,0,43},{21,0,44},{21,0,45},{21,0,46},{21 +,0,53},{24,0,27},{152,0,31},{133,0,716},{135,0,844},{4,0,91},{5,0,388},{5,0,845} +,{6,0,206},{6,0,252},{6,0,365},{7,0,136},{7,0,531},{136,0,621},{7,10,393},{10,10 +,603},{139,10,206},{6,11,80},{6,11,1694},{7,11,173},{7,11,1974},{9,11,547},{10, +11,730},{14,11,18},{150,11,39},{137,0,748},{4,11,923},{134,11,1711},{4,10,912},{ +137,10,232},{7,10,98},{7,10,1973},{136,10,716},{5,10,733},{142,0,103},{132,11, +595},{5,11,240},{6,11,459},{7,11,12},{7,11,114},{7,11,502},{7,11,1751},{7,11, +1753},{7,11,1805},{8,11,658},{9,11,1},{11,11,959},{12,0,158},{13,11,446},{14,11, +211},{18,0,8},{19,0,62},{20,0,6},{22,0,4},{23,0,2},{151,0,9},{135,0,576},{5,0, +771},{5,0,863},{5,0,898},{6,0,648},{6,0,1632},{6,0,1644},{134,0,1780},{133,0,331 +},{7,11,633},{7,11,905},{7,11,909},{7,11,1538},{9,11,767},{140,11,636},{140,0, +632},{5,0,107},{7,0,201},{136,0,518},{6,0,446},{6,11,490},{135,0,1817},{9,0,851} +,{141,0,510},{7,11,250},{8,11,506},{136,11,507},{4,0,504},{137,10,72},{132,11, +158},{4,11,140},{7,11,362},{8,11,209},{9,11,10},{9,11,160},{9,11,503},{10,11,689 +},{11,11,350},{11,11,553},{11,11,725},{12,11,252},{12,11,583},{13,11,192},{13,11 +,352},{14,11,269},{14,11,356},{148,11,50},{6,11,597},{135,11,1318},{135,10,1454} +,{5,0,883},{5,0,975},{8,0,392},{148,0,7},{6,11,228},{7,11,1341},{9,11,408},{138, +11,343},{11,10,600},{11,11,348},{12,11,99},{13,10,245},{18,11,1},{18,11,11},{147 +,11,4},{134,11,296},{5,0,922},{134,0,1707},{132,11,557},{4,11,548},{7,10,164},{7 +,10,1571},{9,10,107},{140,10,225},{7,11,197},{8,11,142},{8,11,325},{9,11,150},{9 +,11,596},{10,11,350},{10,11,353},{11,11,74},{11,11,315},{14,11,423},{143,11,141} +,{5,0,993},{7,0,515},{137,0,91},{4,0,131},{5,10,484},{5,10,510},{6,10,434},{7,10 +,1000},{7,10,1098},{8,0,200},{136,10,2},{152,0,10},{4,11,62},{5,11,83},{6,11,399 +},{6,11,579},{7,11,692},{7,11,846},{7,11,1015},{7,11,1799},{8,11,403},{9,11,394} +,{10,11,133},{12,11,4},{12,11,297},{12,11,452},{16,11,81},{18,11,19},{18,11,25}, +{21,11,14},{22,11,12},{151,11,18},{140,11,459},{132,11,177},{7,0,1433},{9,0,365} +,{137,11,365},{132,10,460},{5,0,103},{5,11,411},{6,0,2004},{7,0,921},{7,11,653}, +{8,0,580},{8,0,593},{8,0,630},{138,0,28},{4,10,932},{133,10,891},{4,0,911},{5,0, +867},{5,0,1013},{7,0,2034},{8,0,798},{136,0,813},{7,11,439},{10,11,727},{11,11, +260},{139,11,684},{136,10,625},{5,11,208},{7,11,753},{135,11,1528},{5,0,461},{6, +10,76},{7,0,1925},{12,0,39},{13,0,265},{141,0,439},{6,0,853},{8,10,92},{137,10, +221},{5,0,135},{6,0,519},{7,0,1722},{10,0,271},{11,0,261},{145,0,54},{139,11,814 +},{14,0,338},{148,0,81},{4,0,300},{133,0,436},{5,0,419},{5,0,687},{7,0,864},{7, +11,864},{137,0,470},{5,11,242},{137,0,836},{134,0,1937},{4,10,763},{133,11,953}, +{132,10,622},{132,0,393},{133,10,253},{7,10,546},{8,0,357},{10,0,745},{14,0,426} +,{17,0,94},{147,0,57},{5,11,615},{146,11,37},{9,10,73},{10,10,110},{14,10,185},{ +145,10,119},{7,10,624},{7,10,916},{10,10,256},{11,0,703},{139,10,87},{133,11,290 +},{5,10,212},{12,10,35},{141,10,382},{132,11,380},{5,11,52},{7,11,277},{9,11,368 +},{139,11,791},{133,0,387},{10,11,138},{139,11,476},{4,0,6},{5,0,708},{136,0,75} +,{7,0,1351},{9,0,581},{10,0,639},{11,0,453},{140,0,584},{132,0,303},{138,0,772}, +{135,10,1175},{4,0,749},{5,10,816},{6,11,256},{7,11,307},{7,11,999},{7,11,1481}, +{7,11,1732},{7,11,1738},{8,11,265},{9,11,414},{11,11,316},{12,11,52},{13,11,420} +,{147,11,100},{135,11,1296},{5,10,869},{5,10,968},{6,0,1065},{6,10,1626},{8,10, +734},{136,10,784},{4,10,542},{6,10,1716},{6,10,1727},{7,10,1082},{7,10,1545},{8, +10,56},{8,10,118},{8,10,412},{8,10,564},{9,10,888},{9,10,908},{10,10,50},{10,10, +423},{11,10,685},{11,10,697},{11,10,933},{12,10,299},{13,10,126},{13,10,136},{13 +,10,170},{141,10,190},{134,0,226},{4,0,106},{5,11,723},{7,0,310},{139,0,717},{4, +10,232},{5,0,890},{5,0,988},{9,10,202},{10,10,474},{140,10,433},{6,0,626},{142,0 +,431},{10,0,706},{150,0,44},{6,10,108},{7,10,1003},{7,10,1181},{8,10,111},{8,10, +343},{141,0,51},{132,0,698},{5,11,109},{6,11,1784},{7,11,1895},{12,11,296},{140, +11,302},{134,0,828},{134,10,1712},{138,0,17},{4,10,133},{5,11,216},{7,0,1929},{7 +,10,711},{7,10,1298},{7,10,1585},{7,11,1879},{9,11,141},{9,11,270},{9,11,679},{ +10,11,159},{10,11,553},{11,11,197},{11,11,438},{12,11,538},{12,11,559},{13,11, +193},{13,11,423},{14,11,144},{14,11,166},{14,11,167},{15,11,67},{147,11,84},{141 +,11,127},{7,11,1872},{137,11,81},{6,10,99},{7,10,1808},{145,10,57},{134,11,391}, +{5,0,689},{6,0,84},{6,10,574},{7,0,1250},{7,10,428},{10,10,669},{11,10,485},{11, +10,840},{12,10,300},{142,10,250},{7,11,322},{136,11,249},{7,11,432},{135,11,1649 +},{135,10,1871},{137,10,252},{6,11,155},{140,11,234},{7,0,871},{19,0,27},{147,11 +,27},{140,0,498},{5,0,986},{6,0,130},{138,0,823},{6,0,1793},{6,10,323},{7,0,1582 +},{7,10,1564},{8,0,458},{10,0,101},{10,0,318},{10,0,945},{12,0,734},{16,0,104},{ +146,0,177},{5,11,632},{138,11,526},{7,10,461},{8,10,775},{138,0,435},{6,11,144}, +{7,11,948},{7,11,1042},{7,11,1857},{8,11,235},{8,11,461},{9,11,453},{9,11,530},{ +10,11,354},{17,11,77},{19,11,99},{148,11,79},{138,0,966},{7,0,1644},{137,0,129}, +{135,0,997},{136,0,502},{5,11,196},{6,11,486},{7,11,212},{8,11,309},{136,11,346} +,{7,10,727},{146,10,73},{132,0,823},{132,11,686},{135,0,1927},{4,0,762},{7,0, +1756},{137,0,98},{136,10,577},{4,11,30},{5,11,43},{24,0,8},{152,11,8},{7,0,1046} +,{139,0,160},{4,10,413},{5,10,677},{7,0,492},{7,11,492},{8,10,432},{140,10,280}, +{6,0,45},{7,0,433},{8,0,129},{9,0,21},{10,0,392},{11,0,79},{12,0,499},{13,0,199} +,{141,0,451},{7,0,558},{136,0,353},{4,11,220},{7,11,1535},{9,11,93},{139,11,474} +,{7,10,646},{7,10,1730},{11,10,446},{141,10,178},{133,0,785},{134,0,1145},{8,0, +81},{9,0,189},{9,0,201},{11,0,478},{11,0,712},{141,0,338},{5,0,353},{151,0,26},{ +4,10,395},{139,0,762},{134,0,2024},{4,0,611},{133,0,606},{9,10,174},{10,10,164}, +{11,10,440},{11,10,841},{143,10,98},{134,10,426},{10,10,608},{139,10,1002},{138, +10,250},{6,0,25},{7,0,855},{7,0,1258},{144,0,32},{7,11,1725},{138,11,393},{5,11, +263},{134,11,414},{5,10,476},{134,0,2011},{4,0,4},{6,11,178},{6,11,1750},{7,0, +1118},{7,0,1320},{7,0,1706},{8,0,277},{8,11,251},{9,0,622},{9,11,690},{10,0,9},{ +10,11,155},{10,11,196},{10,11,373},{11,0,724},{11,11,698},{12,0,350},{12,0,397}, +{13,0,28},{13,0,159},{13,11,155},{15,0,89},{18,0,5},{19,0,9},{20,0,34},{20,11,93 +},{150,0,47},{5,11,97},{137,11,393},{5,10,76},{6,10,458},{6,10,497},{7,0,764},{7 +,10,868},{9,10,658},{10,10,594},{11,0,461},{11,10,566},{12,0,172},{12,10,338},{ +141,10,200},{134,0,1449},{138,11,40},{134,11,1639},{134,0,1445},{4,10,526},{6,0, +1168},{7,10,1029},{135,10,1054},{4,11,191},{7,11,934},{8,11,647},{145,11,97},{ +132,10,636},{6,0,233},{7,10,660},{7,10,1124},{17,10,31},{19,10,22},{151,10,14},{ +6,10,1699},{136,11,110},{12,11,246},{15,11,162},{19,11,64},{20,11,8},{20,11,95}, +{22,11,24},{152,11,17},{5,11,165},{9,11,346},{138,11,655},{5,11,319},{135,11,534 +},{134,0,255},{8,11,128},{9,0,216},{139,11,179},{9,0,183},{139,0,286},{11,0,956} +,{151,0,3},{4,0,536},{7,0,1141},{10,0,723},{139,0,371},{4,10,279},{7,10,301},{ +137,10,362},{5,11,57},{6,11,101},{6,11,1663},{7,0,285},{7,11,132},{7,11,1048},{7 +,11,1154},{7,11,1415},{7,11,1507},{12,11,493},{15,11,105},{151,11,15},{5,11,459} +,{7,10,1743},{7,11,1073},{8,11,241},{136,11,334},{4,10,178},{133,10,399},{135,0, +560},{132,0,690},{135,0,1246},{18,0,157},{147,0,63},{10,0,599},{11,0,33},{12,0, +571},{149,0,1},{6,11,324},{6,11,520},{7,11,338},{7,11,1616},{7,11,1729},{8,11, +228},{9,11,69},{139,11,750},{7,0,1862},{12,0,491},{12,0,520},{13,0,383},{142,0, +244},{135,11,734},{134,10,1692},{6,10,202},{7,11,705},{10,0,448},{11,0,630},{12, +10,360},{17,0,117},{17,10,118},{18,10,27},{148,10,67},{4,11,73},{6,11,612},{7,11 +,927},{7,11,1822},{8,11,217},{9,11,472},{9,11,765},{9,11,766},{10,11,408},{11,11 +,51},{11,11,793},{12,11,266},{15,11,158},{20,11,89},{150,11,32},{4,0,190},{133,0 +,554},{133,0,1001},{5,11,389},{8,11,636},{137,11,229},{5,0,446},{7,10,872},{10, +10,516},{139,10,167},{137,10,313},{132,10,224},{134,0,1313},{5,10,546},{7,10,35} +,{8,10,11},{8,10,12},{9,10,315},{9,10,533},{10,10,802},{11,10,166},{12,10,525},{ +142,10,243},{6,0,636},{137,0,837},{5,10,241},{8,10,242},{9,10,451},{10,10,667},{ +11,10,598},{140,10,429},{22,10,46},{150,11,46},{136,11,472},{11,0,278},{142,0,73 +},{141,11,185},{132,0,868},{134,0,972},{4,10,366},{137,10,516},{138,0,1010},{5, +11,189},{6,10,1736},{7,11,442},{7,11,443},{8,11,281},{12,11,174},{13,11,83},{141 +,11,261},{139,11,384},{6,11,2},{7,11,191},{7,11,446},{7,11,758},{7,11,1262},{7, +11,1737},{8,11,22},{8,11,270},{8,11,612},{9,11,4},{9,11,167},{9,11,312},{9,11, +436},{10,11,156},{10,11,216},{10,11,311},{10,11,623},{11,11,72},{11,11,330},{11, +11,455},{12,11,101},{12,11,321},{12,11,504},{12,11,530},{12,11,543},{13,11,17},{ +13,11,156},{13,11,334},{14,11,48},{15,11,70},{17,11,60},{148,11,64},{6,10,331},{ +136,10,623},{135,0,1231},{132,0,304},{6,11,60},{7,11,670},{7,11,1327},{8,11,411} +,{8,11,435},{9,11,653},{9,11,740},{10,11,385},{11,11,222},{11,11,324},{11,11,829 +},{140,11,611},{6,11,166},{7,0,506},{7,11,374},{135,11,1174},{14,11,43},{146,11, +21},{135,11,1694},{135,10,1888},{5,11,206},{134,11,398},{135,11,50},{150,0,26},{ +6,0,53},{6,0,199},{7,0,1408},{8,0,32},{8,0,93},{10,0,397},{10,0,629},{11,0,593}, +{11,0,763},{13,0,326},{145,0,35},{134,0,105},{132,10,394},{4,0,843},{138,0,794}, +{11,0,704},{141,0,396},{5,0,114},{5,0,255},{141,0,285},{6,0,619},{7,0,898},{7,0, +1092},{7,10,1931},{8,0,485},{18,0,28},{147,0,116},{7,10,574},{7,10,1719},{137,0, +145},{7,0,2035},{8,0,19},{9,0,89},{138,0,831},{132,10,658},{6,11,517},{7,11,1159 +},{10,11,621},{139,11,192},{7,0,1933},{7,11,1933},{9,10,781},{10,10,144},{11,10, +385},{13,10,161},{13,10,228},{13,10,268},{148,10,107},{136,10,374},{10,11,223},{ +139,11,645},{135,0,1728},{7,11,64},{7,11,289},{136,11,245},{4,10,344},{6,10,498} +,{139,10,323},{136,0,746},{135,10,1063},{137,10,155},{4,0,987},{6,0,1964},{6,0, +1974},{6,0,1990},{136,0,995},{133,11,609},{133,10,906},{134,0,1550},{134,0,874}, +{5,11,129},{6,11,61},{135,11,947},{4,0,1018},{6,0,1938},{6,0,2021},{134,0,2039}, +{132,0,814},{11,0,126},{139,0,287},{134,0,1264},{5,0,955},{136,0,814},{141,11, +506},{132,11,314},{6,0,981},{139,11,1000},{5,0,56},{8,0,892},{8,0,915},{140,0, +776},{148,0,100},{10,0,4},{10,0,13},{11,0,638},{148,0,57},{148,11,74},{4,10,616} +,{133,0,738},{133,11,637},{136,10,692},{133,0,758},{132,10,305},{137,11,590},{5, +11,280},{135,11,1226},{134,11,494},{135,0,1112},{133,11,281},{5,10,214},{7,10, +603},{8,10,611},{9,10,686},{10,10,88},{11,10,459},{11,10,496},{12,10,463},{12,10 +,590},{13,0,44},{142,0,214},{139,0,328},{135,11,1064},{137,0,133},{7,0,168},{13, +0,196},{141,0,237},{134,10,1703},{134,0,1152},{135,0,1245},{5,0,110},{6,0,169},{ +6,0,1702},{7,0,400},{8,0,538},{9,0,184},{9,0,524},{140,0,218},{6,0,1816},{10,0, +871},{12,0,769},{140,0,785},{132,11,630},{7,11,33},{7,11,120},{8,11,489},{9,11, +319},{10,11,820},{11,11,1004},{12,11,379},{13,11,117},{13,11,412},{14,11,25},{15 +,11,52},{15,11,161},{16,11,47},{149,11,2},{6,0,133},{8,0,413},{9,0,353},{139,0, +993},{145,10,19},{4,11,937},{133,11,801},{134,0,978},{6,0,93},{6,0,1508},{7,0, +1422},{7,0,1851},{8,0,673},{9,0,529},{140,0,43},{4,10,737},{6,0,317},{10,0,512}, +{11,10,294},{12,10,60},{12,10,437},{13,10,64},{13,10,380},{142,10,430},{7,11, +1591},{9,0,371},{144,11,43},{6,10,1758},{8,10,520},{9,10,345},{9,10,403},{142,10 +,350},{5,0,526},{10,10,242},{138,10,579},{9,0,25},{10,0,467},{138,0,559},{5,10, +139},{7,10,1168},{138,10,539},{4,0,335},{135,0,942},{140,0,754},{132,11,365},{11 +,0,182},{142,0,195},{142,11,29},{5,11,7},{139,11,774},{4,11,746},{135,11,1090},{ +8,0,39},{10,0,773},{11,0,84},{12,0,205},{142,0,1},{5,0,601},{5,0,870},{5,11,360} +,{136,11,237},{132,0,181},{136,0,370},{134,0,1652},{4,10,107},{7,10,613},{8,0, +358},{8,10,439},{8,10,504},{9,10,501},{10,10,383},{139,10,477},{132,10,229},{137 +,11,785},{4,0,97},{5,0,147},{6,0,286},{7,0,1362},{141,0,176},{4,10,903},{6,0,537 +},{7,0,788},{135,0,1816},{140,10,71},{6,0,743},{134,0,1223},{6,0,375},{7,0,169}, +{7,0,254},{7,11,1493},{136,0,780},{4,10,47},{6,10,373},{7,0,1714},{7,10,452},{7, +10,543},{7,10,1856},{9,10,6},{11,10,257},{139,10,391},{6,0,896},{136,0,1003},{ +135,0,1447},{137,11,341},{5,10,980},{134,10,1754},{145,11,22},{4,11,277},{5,11, +608},{6,11,493},{7,11,457},{140,11,384},{7,10,536},{7,10,1331},{136,10,143},{140 +,0,744},{7,11,27},{135,11,316},{5,10,19},{6,10,533},{146,0,126},{4,0,788},{5,11, +552},{5,11,586},{5,11,676},{6,11,448},{8,11,244},{11,0,41},{11,11,1},{11,11,41}, +{13,11,3},{16,11,54},{17,11,4},{146,11,13},{4,0,985},{4,11,401},{6,0,1801},{137, +11,264},{5,10,395},{5,10,951},{134,10,1776},{5,0,629},{135,0,1549},{11,10,663},{ +12,10,210},{13,10,166},{13,10,310},{14,10,373},{147,10,43},{9,11,543},{10,11,524 +},{11,11,30},{12,11,524},{14,11,315},{16,11,18},{20,11,26},{148,11,65},{4,11,205 +},{5,11,623},{7,11,104},{136,11,519},{5,0,293},{134,0,601},{7,11,579},{9,11,41}, +{9,11,244},{9,11,669},{10,11,5},{11,11,861},{11,11,951},{139,11,980},{132,11,717 +},{132,10,695},{7,10,497},{9,10,387},{147,10,81},{132,0,420},{142,0,37},{6,0, +1134},{6,0,1900},{12,0,830},{12,0,878},{12,0,894},{15,0,221},{143,0,245},{132,11 +,489},{7,0,1570},{140,0,542},{8,0,933},{136,0,957},{5,10,284},{6,0,1371},{6,10, +49},{6,10,350},{7,0,31},{7,10,377},{7,10,1693},{8,0,373},{8,10,678},{9,10,161},{ +9,10,585},{9,10,671},{9,10,839},{11,10,912},{141,10,427},{135,11,892},{4,0,325}, +{138,0,125},{139,11,47},{132,10,597},{138,0,323},{6,0,1547},{7,11,1605},{9,11, +473},{11,11,962},{146,11,139},{139,10,908},{7,11,819},{9,11,26},{9,11,392},{10, +11,152},{10,11,226},{11,11,19},{12,11,276},{12,11,426},{12,11,589},{13,11,460},{ +15,11,97},{19,11,48},{148,11,104},{135,11,51},{4,0,718},{135,0,1216},{6,0,1896}, +{6,0,1905},{6,0,1912},{7,11,761},{7,11,1051},{9,0,947},{9,0,974},{9,11,545},{12, +0,809},{12,0,850},{12,0,858},{12,0,874},{12,0,887},{12,0,904},{12,0,929},{12,0, +948},{12,0,952},{15,0,198},{15,0,206},{15,0,220},{15,0,227},{15,0,247},{18,0,188 +},{21,0,48},{21,0,50},{24,0,25},{152,0,29},{5,0,124},{5,0,144},{6,0,548},{7,0,15 +},{7,0,153},{137,0,629},{135,11,606},{135,10,2014},{7,10,2007},{9,10,101},{9,10, +450},{9,11,46},{10,10,66},{10,10,842},{11,10,536},{140,10,587},{6,0,75},{7,0, +1531},{8,0,416},{9,0,240},{9,0,275},{10,0,100},{11,0,658},{11,0,979},{12,0,86},{ +14,0,207},{15,0,20},{143,0,25},{5,0,141},{5,0,915},{6,0,1783},{7,0,211},{7,0,698 +},{7,0,1353},{9,0,83},{9,0,281},{10,0,376},{10,0,431},{11,0,543},{12,0,664},{13, +0,280},{13,0,428},{14,0,61},{14,0,128},{17,0,52},{145,0,81},{132,11,674},{135,0, +533},{149,0,6},{132,11,770},{133,0,538},{5,11,79},{7,11,1027},{7,11,1477},{139, +11,52},{139,10,62},{4,0,338},{133,0,400},{5,11,789},{134,11,195},{4,11,251},{4, +11,688},{7,11,513},{7,11,1284},{9,11,87},{138,11,365},{134,10,1766},{6,0,0},{7,0 +,84},{11,0,895},{145,0,11},{139,0,892},{4,0,221},{5,0,659},{7,0,697},{7,0,1211}, +{138,0,284},{133,0,989},{133,11,889},{4,11,160},{5,11,330},{7,11,1434},{136,11, +174},{6,10,1665},{7,10,256},{7,10,1388},{10,10,499},{139,10,670},{4,10,22},{5,10 +,10},{7,0,848},{136,10,97},{138,0,507},{133,10,481},{4,0,188},{135,0,805},{5,0, +884},{6,0,732},{139,0,991},{135,11,968},{11,11,636},{15,11,145},{17,11,34},{19, +11,50},{151,11,20},{6,10,134},{7,0,959},{7,10,437},{9,10,37},{14,10,285},{14,10, +371},{144,0,60},{7,10,486},{8,10,155},{11,10,93},{140,10,164},{134,0,1653},{5,10 +,591},{135,0,337},{5,11,374},{6,0,1989},{8,0,922},{136,0,978},{132,0,638},{138,0 +,500},{133,11,731},{5,10,380},{5,10,650},{136,10,310},{138,11,381},{4,10,364},{7 +,10,1156},{7,10,1187},{137,10,409},{137,11,224},{140,0,166},{134,10,482},{4,11, +626},{5,11,642},{6,11,425},{10,11,202},{139,11,141},{4,10,781},{6,10,487},{7,10, +926},{8,10,263},{139,10,500},{135,0,418},{4,10,94},{135,10,1265},{136,0,760},{ +132,10,417},{136,11,835},{5,10,348},{134,10,522},{6,0,1277},{134,0,1538},{139,11 +,541},{135,11,1597},{5,11,384},{8,11,455},{140,11,48},{136,0,770},{5,11,264},{ +134,11,184},{4,0,89},{5,0,489},{6,0,315},{7,0,553},{7,0,1745},{138,0,243},{4,10, +408},{4,10,741},{135,10,500},{134,0,1396},{133,0,560},{6,0,1658},{9,0,3},{10,0, +154},{11,0,641},{13,0,85},{13,0,201},{141,0,346},{135,11,1595},{5,11,633},{6,11, +28},{7,11,219},{135,11,1323},{9,11,769},{140,11,185},{135,11,785},{7,11,359},{8, +11,243},{140,11,175},{138,0,586},{6,10,73},{135,0,1271},{132,11,105},{4,0,166},{ +5,0,505},{134,0,1670},{133,10,576},{4,11,324},{138,11,104},{142,10,231},{6,0,637 +},{7,10,1264},{7,10,1678},{11,10,945},{12,10,341},{12,10,471},{12,10,569},{23,11 +,21},{151,11,23},{8,11,559},{141,11,109},{134,0,1947},{7,0,445},{8,0,307},{8,0, +704},{10,0,41},{10,0,439},{11,0,237},{11,0,622},{140,0,201},{135,11,963},{135,0, +1977},{4,0,189},{5,0,713},{136,0,57},{138,0,371},{135,10,538},{132,0,552},{5,10, +413},{134,0,883},{4,11,758},{134,0,923},{138,11,215},{136,10,495},{7,10,54},{8, +10,312},{10,10,191},{10,10,614},{140,10,567},{7,11,351},{139,11,128},{6,10,468}, +{7,0,875},{7,10,1478},{8,10,530},{142,10,290},{135,0,1788},{5,11,918},{145,0,49} +,{12,11,398},{20,11,39},{21,11,11},{150,11,41},{6,10,484},{7,10,822},{138,0,661} +,{135,0,1945},{134,0,794},{137,10,900},{135,10,1335},{6,10,1724},{135,10,2022},{ +132,11,340},{134,0,1135},{4,0,784},{133,0,745},{5,0,84},{134,0,163},{133,0,410}, +{4,0,976},{5,11,985},{7,11,509},{7,11,529},{145,11,96},{132,10,474},{134,0,703}, +{135,11,1919},{5,0,322},{8,0,186},{9,0,262},{10,0,187},{142,0,208},{135,10,1504} +,{133,0,227},{5,10,305},{9,0,560},{141,0,208},{132,11,247},{7,0,1395},{8,0,486}, +{9,0,236},{9,0,878},{10,0,218},{11,0,95},{19,0,17},{147,0,31},{7,0,2043},{8,0, +672},{141,0,448},{4,11,184},{5,11,390},{6,11,337},{7,11,23},{7,11,494},{7,11,618 +},{7,11,1456},{8,11,27},{8,11,599},{10,11,153},{139,11,710},{135,0,466},{135,10, +1236},{4,10,480},{6,0,167},{6,10,302},{6,10,1642},{7,0,186},{7,0,656},{7,10,837} +,{7,10,1547},{7,10,1657},{8,10,429},{9,10,228},{10,0,643},{13,10,289},{13,10,343 +},{147,10,101},{134,0,1428},{134,0,1440},{5,0,412},{7,10,278},{10,10,739},{11,10 +,708},{141,10,348},{134,0,1118},{136,0,562},{148,11,46},{9,0,316},{139,0,256},{ +134,0,1771},{135,0,1190},{137,0,132},{10,11,227},{11,11,497},{11,11,709},{140,11 +,415},{143,0,66},{6,11,360},{7,11,1664},{136,11,478},{144,10,28},{4,0,317},{135, +0,1279},{5,0,63},{133,0,509},{136,11,699},{145,10,36},{134,0,1475},{11,11,343},{ +142,11,127},{132,11,739},{132,0,288},{135,11,1757},{8,0,89},{8,0,620},{9,0,608}, +{11,0,628},{12,0,322},{143,0,124},{134,0,1225},{4,11,67},{5,11,422},{6,10,363},{ +7,0,1189},{7,10,1955},{7,11,1037},{7,11,1289},{7,11,1555},{8,10,725},{9,11,741}, +{145,11,108},{134,0,1468},{6,0,689},{134,0,1451},{138,0,120},{151,0,1},{137,10, +805},{142,0,329},{5,10,813},{135,10,2046},{135,0,226},{138,11,96},{5,10,712},{7, +0,1855},{11,10,17},{13,10,321},{144,10,67},{6,10,320},{7,10,781},{7,10,1921},{9, +0,461},{9,10,55},{10,10,186},{10,10,273},{10,10,664},{10,10,801},{11,10,996},{11 +,10,997},{13,10,157},{142,10,170},{8,10,271},{8,11,203},{11,11,823},{11,11,846}, +{12,11,482},{13,11,133},{13,11,277},{13,11,302},{13,11,464},{14,11,205},{142,11, +221},{135,0,1346},{4,11,449},{133,11,718},{134,0,85},{7,10,103},{7,10,863},{11, +10,184},{14,0,299},{145,10,62},{4,11,355},{6,11,311},{9,11,256},{138,11,404},{ +137,10,659},{138,11,758},{133,11,827},{5,11,64},{140,11,581},{134,0,1171},{4,11, +442},{7,11,1047},{7,11,1352},{135,11,1643},{132,0,980},{5,11,977},{6,11,288},{7, +11,528},{135,11,1065},{5,0,279},{6,0,235},{7,0,468},{8,0,446},{9,0,637},{10,0, +717},{11,0,738},{140,0,514},{132,0,293},{11,10,337},{142,10,303},{136,11,285},{4 +,11,254},{5,0,17},{6,0,371},{9,0,528},{140,0,364},{5,10,77},{7,10,1455},{10,10, +843},{147,10,73},{150,0,5},{132,10,458},{6,11,12},{7,11,1219},{145,11,73},{135, +10,1420},{6,10,109},{138,10,382},{135,11,125},{6,10,330},{7,10,1084},{139,10,142 +},{6,11,369},{6,11,502},{7,11,1036},{8,11,348},{9,11,452},{10,11,26},{11,11,224} +,{11,11,387},{11,11,772},{12,11,95},{12,11,629},{13,11,195},{13,11,207},{13,11, +241},{14,11,260},{14,11,270},{143,11,140},{132,11,269},{5,11,480},{7,11,532},{7, +11,1197},{7,11,1358},{8,11,291},{11,11,349},{142,11,396},{150,0,48},{10,0,601},{ +13,0,353},{141,0,376},{5,0,779},{5,0,807},{6,0,1655},{134,0,1676},{142,11,223},{ +4,0,196},{5,0,558},{133,0,949},{148,11,15},{135,11,1764},{134,0,1322},{132,0,752 +},{139,0,737},{135,11,657},{136,11,533},{135,0,412},{4,0,227},{5,0,159},{5,0,409 +},{7,0,80},{8,0,556},{10,0,479},{12,0,418},{14,0,50},{14,0,123},{14,0,192},{14,0 +,249},{14,0,295},{143,0,27},{7,0,1470},{8,0,66},{8,0,137},{8,0,761},{9,0,638},{ +11,0,80},{11,0,212},{11,0,368},{11,0,418},{12,0,8},{13,0,15},{16,0,61},{17,0,59} +,{19,0,28},{148,0,84},{135,10,1985},{4,11,211},{4,11,332},{5,11,335},{6,11,238}, +{7,11,269},{7,11,811},{7,11,1797},{8,10,122},{8,11,836},{9,11,507},{141,11,242}, +{6,0,683},{134,0,1252},{4,0,873},{132,10,234},{134,0,835},{6,0,38},{7,0,1220},{8 +,0,185},{8,0,256},{9,0,22},{9,0,331},{10,0,738},{11,0,205},{11,0,540},{11,0,746} +,{13,0,465},{14,0,88},{142,0,194},{138,0,986},{5,11,1009},{12,11,582},{146,11, +131},{4,0,159},{6,0,115},{7,0,252},{7,0,257},{7,0,1928},{8,0,69},{9,0,384},{10,0 +,91},{10,0,615},{12,0,375},{14,0,235},{18,0,117},{147,0,123},{133,0,911},{136,0, +278},{5,10,430},{5,10,932},{6,10,131},{7,10,417},{9,10,522},{11,10,314},{141,10, +390},{14,10,149},{14,10,399},{143,10,57},{4,0,151},{7,0,1567},{136,0,749},{5,11, +228},{6,11,203},{7,11,156},{8,11,347},{137,11,265},{132,10,507},{10,0,989},{140, +0,956},{133,0,990},{5,0,194},{6,0,927},{7,0,1662},{9,0,90},{140,0,564},{4,10,343 +},{133,10,511},{133,0,425},{7,10,455},{138,10,591},{4,0,774},{7,11,476},{7,11, +1592},{138,11,87},{5,0,971},{135,10,1381},{5,11,318},{147,11,121},{5,11,291},{7, +11,765},{9,11,389},{140,11,548},{134,10,575},{4,0,827},{12,0,646},{12,0,705},{12 +,0,712},{140,0,714},{139,0,752},{137,0,662},{5,0,72},{6,0,264},{7,0,21},{7,0,46} +,{7,0,2013},{8,0,215},{8,0,513},{10,0,266},{139,0,22},{139,11,522},{6,0,239},{7, +0,118},{10,0,95},{11,0,603},{13,0,443},{14,0,160},{143,0,4},{6,0,431},{134,0,669 +},{7,10,1127},{7,10,1572},{10,10,297},{10,10,422},{11,10,764},{11,10,810},{12,10 +,264},{13,10,102},{13,10,300},{13,10,484},{14,10,147},{14,10,229},{17,10,71},{18 +,10,118},{147,10,120},{5,0,874},{6,0,1677},{10,11,525},{11,11,82},{143,0,0},{6,0 +,65},{7,0,939},{7,0,1172},{7,0,1671},{9,0,540},{10,0,696},{11,0,265},{11,0,732}, +{11,0,928},{11,0,937},{141,0,438},{134,0,1350},{136,11,547},{132,11,422},{5,11, +355},{145,11,0},{137,11,905},{5,0,682},{135,0,1887},{132,0,809},{4,0,696},{133, +11,865},{6,0,1074},{6,0,1472},{14,10,35},{142,10,191},{5,11,914},{134,11,1625},{ +133,11,234},{135,11,1383},{137,11,780},{132,10,125},{4,0,726},{133,0,630},{8,0, +802},{136,0,838},{132,10,721},{6,0,1337},{7,0,776},{8,10,145},{147,0,56},{132,0, +970},{7,10,792},{8,10,147},{10,10,821},{139,10,1021},{139,10,970},{8,0,940},{137 +,0,797},{135,11,1312},{7,10,1999},{7,11,816},{7,11,1241},{9,0,248},{9,11,283},{9 +,11,520},{10,0,400},{10,11,213},{10,11,307},{10,11,463},{10,11,671},{10,11,746}, +{11,11,401},{11,11,794},{12,11,517},{18,11,107},{147,11,115},{6,0,1951},{134,0, +2040},{135,11,339},{5,10,168},{5,10,930},{8,10,74},{9,10,623},{12,10,500},{12,10 +,579},{13,0,41},{143,0,93},{6,0,118},{7,0,215},{7,0,1521},{140,0,11},{6,10,220}, +{7,10,1101},{141,10,105},{6,11,421},{7,11,61},{7,11,1540},{10,11,11},{138,11,501 +},{7,0,615},{138,0,251},{140,11,631},{135,0,1044},{6,10,19},{7,10,1413},{139,10, +428},{133,0,225},{7,10,96},{8,10,401},{8,10,703},{137,10,896},{145,10,116},{6,11 +,102},{7,11,72},{15,11,142},{147,11,67},{7,10,1961},{7,10,1965},{8,10,702},{136, +10,750},{7,10,2030},{8,10,150},{8,10,737},{12,10,366},{151,11,30},{4,0,370},{5,0 +,756},{7,0,1326},{135,11,823},{8,10,800},{9,10,148},{9,10,872},{9,10,890},{11,10 +,309},{11,10,1001},{13,10,267},{141,10,323},{6,0,1662},{7,0,48},{8,0,771},{10,0, +116},{13,0,104},{14,0,105},{14,0,184},{15,0,168},{19,0,92},{148,0,68},{7,11,1870 +},{138,0,209},{7,11,68},{8,11,48},{8,11,88},{8,11,582},{8,11,681},{9,11,373},{9, +11,864},{11,11,157},{11,11,336},{11,11,843},{148,11,27},{134,0,930},{4,11,88},{5 +,11,137},{5,11,174},{5,11,777},{6,11,1664},{6,11,1725},{7,11,77},{7,11,426},{7, +11,1317},{7,11,1355},{8,11,126},{8,11,563},{9,11,523},{9,11,750},{10,11,310},{10 +,11,836},{11,11,42},{11,11,318},{11,11,731},{12,11,68},{12,11,92},{12,11,507},{ +12,11,692},{13,11,81},{13,11,238},{13,11,374},{18,11,138},{19,11,78},{19,11,111} +,{20,11,55},{20,11,77},{148,11,92},{4,11,938},{135,11,1831},{5,10,547},{7,10,424 +},{8,11,617},{138,11,351},{6,0,1286},{6,11,1668},{7,11,1499},{8,11,117},{9,11, +314},{138,11,174},{6,0,759},{6,0,894},{7,11,707},{139,11,563},{4,0,120},{135,0, +1894},{9,0,385},{149,0,17},{138,0,429},{133,11,403},{5,0,820},{135,0,931},{5,10, +133},{138,0,199},{6,0,151},{6,0,1675},{7,0,383},{151,0,10},{6,0,761},{136,10,187 +},{8,0,365},{10,10,0},{10,10,818},{139,10,988},{4,11,44},{5,11,311},{6,11,156},{ +7,11,639},{7,11,762},{7,11,1827},{9,11,8},{9,11,462},{148,11,83},{4,11,346},{7, +11,115},{9,11,180},{9,11,456},{138,11,363},{136,10,685},{7,0,1086},{145,0,46},{6 +,0,1624},{11,0,11},{12,0,422},{13,0,444},{142,0,360},{6,0,1020},{6,0,1260},{134, +0,1589},{4,0,43},{5,0,344},{5,0,357},{14,0,472},{150,0,58},{6,0,1864},{6,0,1866} +,{6,0,1868},{6,0,1869},{6,0,1874},{6,0,1877},{6,0,1903},{6,0,1911},{9,0,920},{9, +0,921},{9,0,924},{9,0,946},{9,0,959},{9,0,963},{9,0,970},{9,0,997},{9,0,1008},{9 +,0,1017},{12,0,795},{12,0,797},{12,0,798},{12,0,800},{12,0,803},{12,0,811},{12,0 +,820},{12,0,821},{12,0,839},{12,0,841},{12,0,848},{12,0,911},{12,0,921},{12,0, +922},{12,0,925},{12,0,937},{12,0,944},{12,0,945},{12,0,953},{15,0,184},{15,0,191 +},{15,0,199},{15,0,237},{15,0,240},{15,0,243},{15,0,246},{18,0,203},{21,0,40},{ +21,0,52},{21,0,57},{24,0,23},{24,0,28},{152,0,30},{134,0,725},{145,11,58},{133,0 +,888},{137,10,874},{4,0,711},{8,10,774},{10,10,670},{140,10,51},{144,11,40},{6, +11,185},{7,11,1899},{139,11,673},{137,10,701},{137,0,440},{4,11,327},{5,11,478}, +{7,11,1332},{8,11,753},{140,11,227},{4,10,127},{5,10,350},{6,10,356},{8,10,426}, +{9,10,572},{10,10,247},{139,10,312},{5,11,1020},{133,11,1022},{4,11,103},{133,11 +,401},{6,0,1913},{6,0,1926},{6,0,1959},{9,0,914},{9,0,939},{9,0,952},{9,0,979},{ +9,0,990},{9,0,998},{9,0,1003},{9,0,1023},{12,0,827},{12,0,834},{12,0,845},{12,0, +912},{12,0,935},{12,0,951},{15,0,172},{15,0,174},{18,0,198},{149,0,63},{4,11,499 +},{5,0,958},{5,0,987},{135,11,1421},{6,10,59},{6,10,1762},{7,0,885},{9,10,603},{ +141,10,397},{10,11,62},{141,11,164},{4,0,847},{135,0,326},{11,0,276},{142,0,293} +,{4,0,65},{4,11,96},{5,0,479},{5,0,1004},{7,0,1913},{8,0,317},{9,0,302},{10,0, +612},{141,0,22},{4,0,261},{135,0,510},{135,0,1514},{6,10,111},{7,10,4},{8,10,163 +},{8,10,776},{138,10,566},{4,0,291},{9,0,515},{12,0,152},{12,0,443},{13,0,392},{ +142,0,357},{7,11,399},{135,11,1492},{4,0,589},{139,0,282},{6,11,563},{135,10, +1994},{5,10,297},{135,10,1038},{4,0,130},{7,0,843},{135,0,1562},{5,0,42},{5,0, +879},{7,0,245},{7,0,324},{7,0,1532},{11,0,463},{11,0,472},{13,0,363},{144,0,52}, +{4,0,134},{133,0,372},{133,0,680},{136,10,363},{6,0,1997},{8,0,935},{136,0,977}, +{4,0,810},{135,0,1634},{135,10,1675},{4,11,910},{5,11,832},{135,0,1390},{7,10, +808},{8,11,266},{139,11,578},{132,0,644},{4,0,982},{138,0,867},{132,10,280},{135 +,0,540},{140,10,54},{135,0,123},{134,0,1978},{4,10,421},{133,10,548},{6,0,623},{ +136,0,789},{4,0,908},{5,0,359},{5,0,508},{6,0,1723},{7,0,343},{7,0,1996},{135,0, +2026},{134,0,1220},{4,0,341},{135,0,480},{6,10,254},{9,10,109},{138,10,103},{134 +,0,888},{8,11,528},{137,11,348},{4,11,20},{5,11,616},{7,0,1995},{8,0,299},{11,0, +890},{140,0,674},{135,11,1094},{134,10,1630},{4,0,238},{5,0,503},{6,0,179},{7,0, +2003},{8,0,381},{8,0,473},{9,0,149},{10,0,788},{15,0,45},{15,0,86},{20,0,110},{ +150,0,57},{133,10,671},{4,11,26},{5,11,429},{6,11,245},{7,11,704},{7,11,1379},{ +135,11,1474},{4,0,121},{5,0,156},{5,0,349},{9,0,431},{10,0,605},{142,0,342},{7, +11,943},{139,11,614},{132,10,889},{132,11,621},{7,10,1382},{7,10,1910},{135,11, +1382},{132,10,627},{133,10,775},{133,11,542},{133,11,868},{136,11,433},{6,0,1373 +},{7,0,1011},{11,10,362},{11,10,948},{140,10,388},{6,0,80},{7,0,173},{7,11,1495} +,{9,0,547},{10,0,730},{14,0,18},{150,0,39},{6,0,1694},{135,0,1974},{140,0,196},{ +4,0,923},{6,0,507},{6,0,1711},{7,10,451},{8,10,389},{12,10,490},{13,10,16},{13, +10,215},{13,10,351},{18,10,132},{147,10,125},{6,0,646},{134,0,1047},{135,10,841} +,{136,10,566},{6,0,1611},{135,0,1214},{139,0,926},{132,11,525},{132,0,595},{5,0, +240},{6,0,459},{7,0,12},{7,0,114},{7,0,949},{7,0,1753},{7,0,1805},{8,0,658},{9,0 +,1},{11,0,959},{141,0,446},{5,10,912},{134,10,1695},{132,0,446},{7,11,62},{12,11 +,45},{147,11,112},{5,10,236},{6,10,572},{8,10,492},{11,10,618},{144,10,56},{5,10 +,190},{136,10,318},{135,10,1376},{4,11,223},{6,11,359},{11,11,3},{13,11,108},{14 +,11,89},{144,11,22},{132,11,647},{134,0,490},{134,0,491},{134,0,1584},{135,11, +685},{138,11,220},{7,0,250},{136,0,507},{132,0,158},{4,0,140},{7,0,362},{8,0,209 +},{9,0,10},{9,0,160},{9,0,503},{9,0,614},{10,0,689},{11,0,327},{11,0,553},{11,0, +725},{11,0,767},{12,0,252},{12,0,583},{13,0,192},{14,0,269},{14,0,356},{148,0,50 +},{19,0,1},{19,0,26},{150,0,9},{132,11,109},{6,0,228},{7,0,1341},{9,0,408},{138, +0,343},{4,0,373},{5,0,283},{6,0,480},{7,0,609},{10,0,860},{138,0,878},{6,0,779}, +{134,0,1209},{4,0,557},{7,11,263},{7,11,628},{136,11,349},{132,0,548},{7,0,197}, +{8,0,142},{8,0,325},{9,0,150},{9,0,596},{10,0,350},{10,0,353},{11,0,74},{11,0, +315},{12,0,662},{12,0,681},{14,0,423},{143,0,141},{4,11,40},{10,11,67},{11,11, +117},{11,11,768},{139,11,935},{7,11,992},{8,11,301},{9,11,722},{12,11,63},{13,11 +,29},{14,11,161},{143,11,18},{6,0,1490},{138,11,532},{5,0,580},{7,0,378},{7,0, +674},{7,0,1424},{15,0,83},{15,11,83},{16,0,11},{144,11,11},{6,0,1057},{6,0,1335} +,{7,10,85},{7,10,247},{8,10,585},{10,0,316},{138,10,163},{4,0,169},{5,0,83},{6,0 +,399},{6,0,579},{6,0,1513},{7,0,692},{7,0,846},{7,0,1015},{7,0,1799},{8,0,403},{ +9,0,394},{10,0,133},{12,0,4},{12,0,297},{12,0,452},{16,0,81},{18,0,25},{21,0,14} +,{22,0,12},{151,0,18},{134,0,1106},{7,0,1546},{11,0,299},{142,0,407},{134,0,1192 +},{132,0,177},{5,0,411},{135,0,653},{7,0,439},{10,0,727},{11,0,260},{139,0,684}, +{138,10,145},{147,10,83},{5,0,208},{7,0,753},{135,0,1528},{137,11,617},{135,10, +1922},{135,11,825},{4,10,124},{10,10,457},{11,0,422},{11,10,121},{11,10,169},{11 +,10,870},{12,10,214},{13,0,389},{14,10,187},{143,10,77},{11,0,615},{11,11,615},{ +15,0,58},{143,11,58},{9,0,618},{138,0,482},{6,0,1952},{6,0,1970},{142,0,505},{7, +10,1193},{135,11,1838},{133,0,242},{135,10,1333},{6,10,107},{7,10,638},{7,10, +1632},{137,10,396},{133,0,953},{5,10,370},{134,10,1756},{5,11,28},{6,11,204},{10 +,11,320},{10,11,583},{13,11,502},{14,11,72},{14,11,274},{14,11,312},{14,11,344}, +{15,11,159},{16,11,62},{16,11,69},{17,11,30},{18,11,42},{18,11,53},{18,11,84},{ +18,11,140},{19,11,68},{19,11,85},{20,11,5},{20,11,45},{20,11,101},{22,11,7},{150 +,11,20},{4,11,558},{6,11,390},{7,11,162},{7,11,689},{9,11,360},{138,11,653},{11, +0,802},{141,0,67},{133,10,204},{133,0,290},{5,10,970},{134,10,1706},{132,0,380}, +{5,0,52},{7,0,277},{9,0,368},{139,0,791},{5,11,856},{6,11,1672},{6,11,1757},{6, +11,1781},{7,11,1150},{7,11,1425},{7,11,1453},{140,11,513},{5,11,92},{7,10,3},{10 +,11,736},{140,11,102},{4,0,112},{5,0,653},{5,10,483},{5,10,685},{6,10,489},{7,10 +,1204},{136,10,394},{132,10,921},{5,10,1007},{134,0,1028},{5,11,590},{9,11,213}, +{145,11,91},{135,10,1696},{10,0,138},{139,0,476},{5,0,725},{5,0,727},{135,0,1811 +},{4,0,979},{6,0,1821},{6,0,1838},{8,0,876},{8,0,883},{8,0,889},{8,0,893},{8,0, +895},{10,0,934},{12,0,720},{14,0,459},{148,0,123},{135,11,551},{4,0,38},{6,0,435 +},{7,0,307},{7,0,999},{7,0,1481},{7,0,1732},{7,0,1738},{8,0,371},{9,0,414},{11,0 +,316},{12,0,52},{13,0,420},{147,0,100},{135,0,1296},{132,10,712},{134,10,1629},{ +133,0,723},{134,0,651},{136,11,191},{9,11,791},{10,11,93},{11,11,301},{16,11,13} +,{17,11,23},{18,11,135},{19,11,12},{20,11,1},{20,11,12},{148,11,14},{136,11,503} +,{6,11,466},{135,11,671},{6,0,1200},{134,0,1330},{135,0,1255},{134,0,986},{5,0, +109},{6,0,1784},{7,0,1895},{12,0,296},{140,0,302},{135,11,983},{133,10,485},{134 +,0,660},{134,0,800},{4,10,285},{5,0,216},{5,0,294},{5,10,317},{6,0,591},{6,10, +301},{7,0,1879},{7,10,7},{8,10,153},{9,0,141},{9,0,270},{9,0,679},{10,0,159},{10 +,10,766},{11,0,197},{11,0,438},{11,10,468},{12,0,538},{12,0,559},{12,10,467},{13 +,10,143},{14,0,144},{14,0,167},{143,0,67},{136,0,945},{134,0,1090},{137,0,81},{ +12,11,468},{19,11,96},{148,11,24},{134,0,391},{138,11,241},{7,0,322},{136,0,249} +,{134,0,1412},{135,11,795},{5,0,632},{138,0,526},{136,10,819},{6,0,144},{7,0,948 +},{7,0,1042},{7,11,954},{8,0,235},{8,0,461},{9,0,453},{9,0,796},{10,0,354},{145, +0,77},{139,10,917},{6,0,940},{134,0,1228},{4,0,362},{7,0,52},{135,0,303},{6,11, +549},{8,11,34},{8,11,283},{9,11,165},{138,11,475},{7,11,370},{7,11,1007},{7,11, +1177},{135,11,1565},{5,11,652},{5,11,701},{135,11,449},{5,0,196},{6,0,486},{7,0, +212},{8,0,309},{136,0,346},{6,10,1719},{6,10,1735},{7,10,2016},{7,10,2020},{8,10 +,837},{137,10,852},{6,11,159},{6,11,364},{7,11,516},{7,11,1439},{137,11,518},{ +135,0,1912},{135,0,1290},{132,0,686},{141,11,151},{138,0,625},{136,0,706},{138, +10,568},{139,0,412},{4,0,30},{133,0,43},{8,10,67},{138,10,419},{7,0,967},{141,0, +11},{12,0,758},{14,0,441},{142,0,462},{10,10,657},{14,10,297},{142,10,361},{139, +10,729},{4,0,220},{135,0,1535},{7,11,501},{9,11,111},{10,11,141},{11,11,332},{13 +,11,43},{13,11,429},{14,11,130},{14,11,415},{145,11,102},{4,0,950},{6,0,1859},{7 +,0,11},{8,0,873},{12,0,710},{12,0,718},{12,0,748},{12,0,765},{148,0,124},{5,11, +149},{5,11,935},{136,11,233},{142,11,291},{134,0,1579},{7,0,890},{8,10,51},{9,10 +,868},{10,10,833},{12,10,481},{12,10,570},{148,10,106},{141,0,2},{132,10,445},{ +136,11,801},{135,0,1774},{7,0,1725},{138,0,393},{5,0,263},{134,0,414},{132,11, +322},{133,10,239},{7,0,456},{7,10,1990},{8,10,130},{139,10,720},{137,0,818},{5, +10,123},{6,10,530},{7,10,348},{135,10,1419},{135,10,2024},{6,0,178},{6,0,1750},{ +8,0,251},{9,0,690},{10,0,155},{10,0,196},{10,0,373},{11,0,698},{13,0,155},{148,0 +,93},{5,0,97},{137,0,393},{134,0,674},{11,0,223},{140,0,168},{132,10,210},{139, +11,464},{6,0,1639},{146,0,159},{139,11,2},{7,0,934},{8,0,647},{17,0,97},{19,0,59 +},{150,0,2},{132,0,191},{4,10,430},{5,0,165},{7,11,357},{9,0,346},{10,0,655},{ +139,0,885},{133,0,877},{5,10,213},{133,11,406},{8,0,128},{139,0,179},{6,11,69},{ +135,11,117},{135,0,1297},{11,11,43},{13,11,72},{141,11,142},{135,11,1830},{142,0 +,164},{5,0,57},{6,0,101},{6,0,586},{6,0,1663},{7,0,132},{7,0,1154},{7,0,1415},{7 +,0,1507},{12,0,493},{15,0,105},{151,0,15},{5,0,459},{7,0,1073},{8,0,241},{136,0, +334},{133,11,826},{133,10,108},{5,10,219},{10,11,132},{11,11,191},{11,11,358},{ +139,11,460},{6,0,324},{6,0,520},{7,0,338},{7,0,1729},{8,0,228},{139,0,750},{4,10 +,193},{5,10,916},{7,10,364},{10,10,398},{10,10,726},{11,10,317},{11,10,626},{12, +10,142},{12,10,288},{12,10,678},{13,10,313},{15,10,113},{18,10,114},{21,0,30},{ +150,0,53},{6,11,110},{135,11,1681},{135,0,910},{6,10,241},{7,10,907},{8,10,832}, +{9,10,342},{10,10,729},{11,10,284},{11,10,445},{11,10,651},{11,10,863},{13,10, +398},{146,10,99},{5,11,1000},{7,0,705},{7,11,733},{9,0,734},{137,11,583},{4,0,73 +},{6,0,612},{7,0,927},{7,0,1822},{8,0,217},{9,0,765},{9,0,766},{10,0,408},{11,0, +51},{11,0,793},{12,0,266},{15,0,158},{20,0,89},{150,0,32},{4,11,297},{6,11,529}, +{7,0,1330},{7,11,152},{7,11,713},{7,11,1845},{8,11,710},{8,11,717},{140,11,639}, +{5,0,389},{136,0,636},{134,0,1409},{4,10,562},{9,10,254},{139,10,879},{134,0,893 +},{132,10,786},{4,11,520},{135,11,575},{136,0,21},{140,0,721},{136,0,959},{7,11, +1428},{7,11,1640},{9,11,169},{9,11,182},{9,11,367},{9,11,478},{9,11,506},{9,11, +551},{9,11,648},{9,11,651},{9,11,697},{9,11,705},{9,11,725},{9,11,787},{9,11,794 +},{10,11,198},{10,11,214},{10,11,267},{10,11,275},{10,11,456},{10,11,551},{10,11 +,561},{10,11,613},{10,11,627},{10,11,668},{10,11,675},{10,11,691},{10,11,695},{ +10,11,707},{10,11,715},{11,11,183},{11,11,201},{11,11,244},{11,11,262},{11,11, +352},{11,11,439},{11,11,493},{11,11,572},{11,11,591},{11,11,608},{11,11,611},{11 +,11,646},{11,11,674},{11,11,711},{11,11,751},{11,11,761},{11,11,776},{11,11,785} +,{11,11,850},{11,11,853},{11,11,862},{11,11,865},{11,11,868},{11,11,898},{11,11, +902},{11,11,903},{11,11,910},{11,11,932},{11,11,942},{11,11,957},{11,11,967},{11 +,11,972},{12,11,148},{12,11,195},{12,11,220},{12,11,237},{12,11,318},{12,11,339} +,{12,11,393},{12,11,445},{12,11,450},{12,11,474},{12,11,509},{12,11,533},{12,11, +591},{12,11,594},{12,11,597},{12,11,621},{12,11,633},{12,11,642},{13,11,59},{13, +11,60},{13,11,145},{13,11,239},{13,11,250},{13,11,273},{13,11,329},{13,11,344},{ +13,11,365},{13,11,372},{13,11,387},{13,11,403},{13,11,414},{13,11,456},{13,11, +478},{13,11,483},{13,11,489},{14,11,55},{14,11,57},{14,11,81},{14,11,90},{14,11, +148},{14,11,239},{14,11,266},{14,11,321},{14,11,326},{14,11,327},{14,11,330},{14 +,11,347},{14,11,355},{14,11,401},{14,11,411},{14,11,414},{14,11,416},{14,11,420} +,{15,11,61},{15,11,74},{15,11,87},{15,11,88},{15,11,94},{15,11,96},{15,11,116},{ +15,11,149},{15,11,154},{16,11,50},{16,11,63},{16,11,73},{17,11,2},{17,11,66},{17 +,11,92},{17,11,103},{17,11,112},{18,11,50},{18,11,54},{18,11,82},{18,11,86},{18, +11,90},{18,11,111},{18,11,115},{18,11,156},{19,11,40},{19,11,79},{20,11,78},{149 +,11,22},{137,11,170},{134,0,1433},{135,11,1307},{139,11,411},{5,0,189},{7,0,442} +,{7,0,443},{8,0,281},{12,0,174},{141,0,261},{6,10,216},{7,10,901},{7,10,1343},{ +136,10,493},{5,11,397},{6,11,154},{7,10,341},{7,11,676},{8,11,443},{8,11,609},{9 +,11,24},{9,11,325},{10,11,35},{11,10,219},{11,11,535},{11,11,672},{11,11,1018},{ +12,11,637},{144,11,30},{6,0,2},{7,0,191},{7,0,446},{7,0,1262},{7,0,1737},{8,0,22 +},{8,0,270},{8,0,612},{9,0,4},{9,0,312},{9,0,436},{9,0,626},{10,0,216},{10,0,311 +},{10,0,521},{10,0,623},{11,0,72},{11,0,330},{11,0,455},{12,0,321},{12,0,504},{ +12,0,530},{12,0,543},{13,0,17},{13,0,156},{13,0,334},{14,0,131},{17,0,60},{148,0 +,64},{7,0,354},{10,0,410},{139,0,815},{139,10,130},{7,10,1734},{137,11,631},{10, +10,115},{11,10,420},{12,0,425},{13,10,404},{14,10,346},{15,0,112},{143,10,54},{6 +,0,60},{6,0,166},{7,0,374},{7,0,670},{7,0,1327},{8,0,411},{8,0,435},{9,0,653},{9 +,0,740},{10,0,385},{11,0,222},{11,0,324},{11,0,829},{140,0,611},{7,0,1611},{13,0 +,14},{15,0,44},{19,0,13},{148,0,76},{133,11,981},{4,11,56},{7,11,1791},{8,11,607 +},{8,11,651},{11,11,465},{11,11,835},{12,11,337},{141,11,480},{5,10,1011},{6,0, +1478},{136,10,701},{139,0,596},{5,0,206},{134,0,398},{4,10,54},{5,10,666},{7,10, +1039},{7,10,1130},{9,10,195},{138,10,302},{7,0,50},{9,11,158},{138,11,411},{135, +11,1120},{6,0,517},{6,10,1669},{7,0,1159},{10,0,621},{139,0,192},{4,0,592},{6,0, +600},{135,0,1653},{10,0,223},{139,0,645},{136,11,139},{7,0,64},{136,0,245},{142, +0,278},{6,11,622},{135,11,1030},{136,0,604},{134,0,1502},{138,0,265},{141,11,168 +},{7,0,1763},{140,0,310},{7,10,798},{139,11,719},{7,11,160},{10,11,624},{142,11, +279},{132,11,363},{7,10,122},{9,10,259},{10,10,84},{11,10,470},{12,10,541},{141, +10,379},{5,0,129},{6,0,61},{135,0,947},{134,0,1356},{135,11,1191},{13,0,505},{ +141,0,506},{5,10,82},{5,10,131},{7,10,1755},{8,10,31},{9,10,168},{9,10,764},{11, +0,1000},{139,10,869},{134,0,966},{134,10,605},{134,11,292},{5,11,177},{6,11,616} +,{7,11,827},{9,11,525},{138,11,656},{135,11,1486},{138,11,31},{5,10,278},{137,10 +,68},{4,10,163},{5,10,201},{5,10,307},{5,10,310},{6,10,335},{7,10,284},{136,10, +165},{6,0,839},{135,10,1660},{136,10,781},{6,10,33},{135,10,1244},{133,0,637},{4 +,11,161},{133,11,631},{137,0,590},{7,10,1953},{136,10,720},{5,0,280},{7,0,1226}, +{138,10,203},{134,0,1386},{5,0,281},{6,0,1026},{6,10,326},{7,10,677},{137,10,425 +},{7,11,1557},{135,11,1684},{135,0,1064},{9,11,469},{9,11,709},{12,11,512},{14, +11,65},{145,11,12},{134,0,917},{10,11,229},{11,11,73},{11,11,376},{139,11,433},{ +7,0,555},{7,10,1316},{7,10,1412},{7,10,1839},{9,0,192},{9,10,589},{11,10,241},{ +11,10,676},{11,10,811},{11,10,891},{12,10,140},{12,10,346},{12,10,479},{13,0,30} +,{13,0,49},{13,10,381},{14,10,188},{15,0,150},{16,0,76},{18,10,30},{148,0,52},{ +149,0,15},{6,0,1882},{6,0,1883},{6,0,1897},{9,0,945},{9,0,1014},{9,0,1020},{12,0 +,823},{12,0,842},{12,0,866},{12,0,934},{15,0,242},{146,0,208},{6,0,965},{134,0, +1499},{7,0,33},{7,0,120},{8,0,489},{9,0,319},{10,0,820},{11,0,1004},{12,0,379},{ +12,0,679},{13,0,117},{13,0,412},{14,0,25},{15,0,52},{15,0,161},{16,0,47},{149,0, +2},{6,11,558},{7,11,651},{8,11,421},{9,11,0},{138,11,34},{4,0,937},{5,0,801},{5, +10,358},{7,0,473},{7,10,1184},{10,10,662},{13,10,212},{13,10,304},{13,10,333},{ +145,10,98},{132,0,877},{6,0,693},{134,0,824},{132,0,365},{7,11,1832},{138,11,374 +},{5,0,7},{139,0,774},{4,0,734},{5,0,662},{134,0,430},{4,0,746},{135,0,1090},{5, +0,360},{8,0,237},{10,0,231},{147,0,124},{138,11,348},{6,11,6},{7,11,81},{7,11, +771},{7,11,1731},{9,11,405},{138,11,421},{6,0,740},{137,0,822},{133,10,946},{7,0 +,1485},{136,0,929},{7,10,411},{8,10,631},{9,10,323},{10,10,355},{11,10,491},{12, +10,143},{12,10,402},{13,10,73},{14,10,408},{15,10,107},{146,10,71},{135,10,590}, +{5,11,881},{133,11,885},{150,11,25},{4,0,852},{5,11,142},{134,11,546},{7,10,1467 +},{8,10,328},{10,10,544},{11,10,955},{13,10,320},{145,10,83},{9,0,17},{10,0,291} +,{11,10,511},{13,10,394},{14,10,298},{14,10,318},{146,10,103},{5,11,466},{11,11, +571},{12,11,198},{13,11,283},{14,11,186},{15,11,21},{143,11,103},{134,0,1001},{4 +,11,185},{5,11,257},{5,11,839},{5,11,936},{7,11,171},{9,11,399},{10,11,258},{10, +11,395},{10,11,734},{11,11,1014},{12,11,23},{13,11,350},{14,11,150},{147,11,6},{ +143,0,35},{132,0,831},{5,10,835},{134,10,483},{4,0,277},{5,0,608},{6,0,493},{7,0 +,457},{7,11,404},{7,11,1377},{7,11,1430},{7,11,2017},{8,11,149},{8,11,239},{8,11 +,512},{8,11,793},{8,11,818},{9,11,474},{9,11,595},{10,11,122},{10,11,565},{10,11 +,649},{10,11,783},{11,11,239},{11,11,295},{11,11,447},{11,11,528},{11,11,639},{ +11,11,800},{11,11,936},{12,0,384},{12,11,25},{12,11,73},{12,11,77},{12,11,157},{ +12,11,316},{12,11,390},{12,11,391},{12,11,394},{12,11,395},{12,11,478},{12,11, +503},{12,11,592},{12,11,680},{13,11,50},{13,11,53},{13,11,132},{13,11,198},{13, +11,275},{13,11,322},{13,11,415},{14,11,71},{14,11,257},{14,11,395},{15,11,71},{ +15,11,136},{17,11,123},{18,11,93},{147,11,58},{134,0,1351},{7,0,27},{135,0,316}, +{136,11,712},{136,0,984},{133,0,552},{137,0,264},{132,0,401},{6,0,710},{6,0,1111 +},{134,0,1343},{134,0,1211},{9,0,543},{10,0,524},{11,0,108},{11,0,653},{12,0,524 +},{13,0,123},{14,0,252},{16,0,18},{19,0,38},{20,0,26},{20,0,65},{21,0,3},{151,0, +11},{4,0,205},{5,0,623},{7,0,104},{8,0,519},{137,0,716},{132,10,677},{4,11,377}, +{152,11,13},{135,11,1673},{7,0,579},{9,0,41},{9,0,244},{9,0,669},{10,0,5},{11,0, +861},{11,0,951},{139,0,980},{132,0,717},{136,0,1011},{132,0,805},{4,11,180},{135 +,11,1906},{132,10,777},{132,10,331},{132,0,489},{4,11,491},{5,10,747},{134,0, +1024},{135,11,1182},{4,11,171},{138,11,234},{4,11,586},{7,11,1186},{138,11,631}, +{135,0,892},{135,11,336},{9,11,931},{10,11,334},{148,11,71},{137,0,473},{6,0,864 +},{11,11,926},{140,0,659},{7,0,819},{9,0,26},{9,0,392},{10,0,152},{10,0,226},{11 +,0,19},{12,0,276},{12,0,426},{12,0,589},{13,0,460},{15,0,97},{19,0,48},{148,0, +104},{135,0,51},{133,10,326},{4,10,691},{146,10,16},{9,0,130},{10,10,680},{10,10 +,793},{11,0,765},{141,10,357},{133,11,765},{6,10,32},{7,10,385},{7,10,757},{7,10 +,1916},{8,0,229},{8,10,94},{8,10,711},{9,10,541},{10,10,162},{10,10,795},{11,10, +989},{11,10,1010},{12,10,14},{142,10,308},{7,11,474},{137,11,578},{132,0,674},{ +132,0,770},{5,0,79},{7,0,1027},{7,0,1477},{139,0,52},{133,11,424},{134,0,1666},{ +6,0,409},{6,10,349},{6,10,1682},{7,10,1252},{8,10,112},{8,11,714},{9,10,435},{9, +10,668},{10,10,290},{10,10,319},{10,10,815},{11,10,180},{11,10,837},{12,10,240}, +{13,10,152},{13,10,219},{142,10,158},{5,0,789},{134,0,195},{4,0,251},{4,0,688},{ +7,0,513},{135,0,1284},{132,10,581},{9,11,420},{10,11,269},{10,11,285},{10,11,576 +},{11,11,397},{13,11,175},{145,11,90},{6,10,126},{7,10,573},{8,10,397},{142,10, +44},{132,11,429},{133,0,889},{4,0,160},{5,0,330},{7,0,1434},{136,0,174},{7,11,18 +},{7,11,699},{7,11,1966},{8,11,752},{9,11,273},{9,11,412},{9,11,703},{10,11,71}, +{10,11,427},{10,11,508},{146,11,97},{6,0,872},{134,0,899},{133,10,926},{134,0, +1126},{134,0,918},{4,11,53},{5,11,186},{135,11,752},{7,0,268},{136,0,569},{134,0 +,1224},{6,0,1361},{7,10,1232},{137,10,531},{8,11,575},{10,11,289},{139,11,319},{ +133,10,670},{132,11,675},{133,0,374},{135,10,1957},{133,0,731},{11,0,190},{11,11 +,190},{15,0,49},{143,11,49},{4,0,626},{5,0,506},{5,0,642},{6,0,425},{10,0,202},{ +139,0,141},{137,0,444},{7,10,242},{135,10,1942},{6,11,209},{8,11,468},{9,11,210} +,{11,11,36},{12,11,28},{12,11,630},{13,11,21},{13,11,349},{14,11,7},{145,11,13}, +{4,11,342},{135,11,1179},{5,10,834},{7,10,1202},{8,10,14},{9,10,481},{137,10,880 +},{4,11,928},{133,11,910},{4,11,318},{4,11,496},{7,11,856},{139,11,654},{136,0, +835},{7,0,1526},{138,10,465},{151,0,17},{135,0,477},{4,10,357},{6,10,172},{7,10, +143},{137,10,413},{6,0,1374},{138,0,994},{4,10,590},{146,0,76},{7,0,287},{8,0, +355},{9,0,293},{137,0,743},{134,0,1389},{7,11,915},{8,11,247},{147,11,0},{4,11, +202},{5,11,382},{6,11,454},{7,11,936},{7,11,1803},{8,11,758},{9,11,375},{9,11, +895},{10,11,743},{10,11,792},{11,11,978},{11,11,1012},{142,11,109},{5,0,384},{8, +0,455},{140,0,48},{132,11,390},{5,10,169},{7,10,333},{136,10,45},{5,0,264},{134, +0,184},{138,11,791},{133,11,717},{132,10,198},{6,11,445},{7,11,332},{137,11,909} +,{136,0,1001},{4,10,24},{5,10,140},{5,10,185},{7,10,1500},{11,10,565},{139,10, +838},{134,11,578},{5,0,633},{6,0,28},{135,0,1323},{132,0,851},{136,11,267},{7,0, +359},{8,0,243},{140,0,175},{4,10,334},{133,10,593},{141,11,87},{136,11,766},{10, +0,287},{10,11,287},{12,0,138},{140,11,138},{4,0,105},{132,0,740},{140,10,116},{ +134,0,857},{135,11,1841},{6,0,1402},{137,0,819},{132,11,584},{132,10,709},{133, +10,897},{5,0,224},{13,0,174},{146,0,52},{135,10,1840},{4,10,608},{133,10,497},{ +139,11,60},{4,0,758},{135,0,1649},{4,11,226},{4,11,326},{135,11,1770},{5,11,426} +,{8,11,30},{9,11,2},{11,11,549},{147,11,122},{135,10,2039},{6,10,540},{136,10, +136},{4,0,573},{4,10,897},{5,10,786},{136,0,655},{7,0,351},{139,0,128},{133,10, +999},{4,10,299},{135,10,1004},{133,0,918},{132,11,345},{4,11,385},{7,11,265},{ +135,11,587},{133,10,456},{136,10,180},{6,0,687},{134,0,1537},{4,11,347},{5,11, +423},{5,11,996},{135,11,1329},{132,10,755},{7,11,1259},{9,11,125},{11,11,65},{ +140,11,285},{5,11,136},{6,11,136},{136,11,644},{134,0,1525},{4,0,1009},{135,0, +1139},{139,10,338},{132,0,340},{135,10,1464},{8,0,847},{10,0,861},{10,0,876},{10 +,0,889},{10,0,922},{10,0,929},{10,0,933},{12,0,784},{140,0,791},{139,0,176},{9, +11,134},{10,11,2},{10,11,27},{10,11,333},{11,11,722},{143,11,1},{4,11,433},{133, +11,719},{5,0,985},{7,0,509},{7,0,529},{145,0,96},{132,0,615},{4,10,890},{5,10, +805},{5,10,819},{5,10,961},{6,10,396},{6,10,1631},{6,10,1678},{7,10,1967},{7,10, +2041},{9,10,630},{11,10,8},{11,10,1019},{12,10,176},{13,10,225},{14,10,292},{149 +,10,24},{135,0,1919},{134,0,1131},{144,11,21},{144,11,51},{135,10,1815},{4,0,247 +},{7,10,1505},{10,10,190},{10,10,634},{11,10,792},{12,10,358},{140,10,447},{5,10 +,0},{6,10,536},{7,10,604},{13,10,445},{145,10,126},{4,0,184},{5,0,390},{6,0,337} +,{7,0,23},{7,0,494},{7,0,618},{7,0,1456},{8,0,27},{8,0,599},{10,0,153},{139,0, +710},{6,10,232},{6,10,412},{7,10,1074},{8,10,9},{8,10,157},{8,10,786},{9,10,196} +,{9,10,352},{9,10,457},{10,10,337},{11,10,232},{11,10,877},{12,10,480},{140,10, +546},{7,10,958},{141,0,38},{4,10,382},{136,10,579},{4,10,212},{135,10,1206},{4, +11,555},{8,11,536},{138,11,288},{11,11,139},{139,11,171},{9,11,370},{138,11,90}, +{132,0,1015},{134,0,1088},{5,10,655},{135,11,977},{134,0,1585},{17,10,67},{147, +10,74},{10,0,227},{11,0,497},{11,0,709},{140,0,415},{6,0,360},{7,0,1664},{136,0, +478},{6,10,231},{7,0,95},{136,10,423},{140,11,65},{4,11,257},{135,11,2031},{135, +11,1768},{133,10,300},{139,11,211},{136,0,699},{6,10,237},{7,10,611},{8,10,100}, +{9,10,416},{11,10,335},{12,10,173},{146,10,101},{14,0,26},{146,0,150},{6,0,581}, +{135,0,1119},{135,10,1208},{132,0,739},{6,11,83},{6,11,1733},{135,11,1389},{137, +0,869},{4,0,67},{5,0,422},{7,0,1037},{7,0,1289},{7,0,1555},{9,0,741},{145,0,108} +,{133,10,199},{12,10,427},{146,10,38},{136,0,464},{142,0,42},{8,11,501},{9,11, +696},{138,0,96},{134,11,592},{4,0,512},{4,0,966},{5,0,342},{6,0,1855},{8,0,869}, +{8,0,875},{8,0,901},{144,0,26},{8,0,203},{11,0,823},{11,0,846},{12,0,482},{13,0, +277},{13,0,302},{13,0,464},{14,0,205},{142,0,221},{4,0,449},{133,0,718},{7,11, +1718},{9,11,95},{9,11,274},{10,11,279},{10,11,317},{10,11,420},{11,11,303},{11, +11,808},{12,11,134},{12,11,367},{13,11,149},{13,11,347},{14,11,349},{14,11,406}, +{18,11,22},{18,11,89},{18,11,122},{147,11,47},{133,11,26},{4,0,355},{6,0,311},{9 +,0,256},{138,0,404},{132,11,550},{6,10,312},{6,10,1715},{10,0,758},{10,10,584},{ +11,10,546},{11,10,692},{12,10,259},{12,10,295},{13,10,46},{141,10,154},{136,11, +822},{4,11,902},{5,0,827},{5,11,809},{6,11,122},{135,11,896},{5,0,64},{140,0,581 +},{4,0,442},{6,0,739},{7,0,1047},{7,0,1352},{7,0,1643},{7,11,1911},{9,11,449},{ +10,11,192},{138,11,740},{135,11,262},{132,10,588},{133,11,620},{4,11,34},{5,0, +977},{5,11,574},{6,0,288},{7,0,528},{7,11,279},{7,11,1624},{136,11,601},{4,10, +231},{5,10,61},{6,0,1375},{6,10,104},{7,10,729},{7,10,964},{7,10,1658},{140,10, +414},{6,10,263},{138,10,757},{132,10,320},{4,0,254},{5,11,332},{7,0,1309},{135, +11,1309},{6,11,261},{8,11,182},{139,11,943},{132,10,225},{6,0,12},{135,0,1219},{ +4,0,275},{6,11,1721},{12,0,376},{141,11,490},{4,11,933},{133,11,880},{4,10,405}, +{6,0,951},{6,0,1109},{6,0,1181},{7,0,154},{7,10,817},{14,10,58},{17,10,37},{146, +10,124},{5,10,974},{134,0,1520},{134,0,1753},{6,0,369},{6,0,502},{7,0,1036},{8,0 +,348},{9,0,452},{10,0,26},{11,0,224},{11,0,387},{11,0,772},{12,0,95},{12,0,629}, +{13,0,195},{13,0,207},{13,0,241},{14,0,260},{14,0,270},{143,0,140},{132,0,269},{ +5,0,480},{7,0,532},{7,0,1197},{7,0,1358},{8,0,291},{11,0,349},{142,0,396},{5,10, +235},{7,10,1239},{11,10,131},{140,10,370},{7,10,956},{7,10,1157},{7,10,1506},{7, +10,1606},{7,10,1615},{7,10,1619},{7,10,1736},{7,10,1775},{8,10,590},{9,10,324},{ +9,10,736},{9,10,774},{9,10,776},{9,10,784},{10,10,567},{10,10,708},{11,10,518},{ +11,10,613},{11,10,695},{11,10,716},{11,10,739},{11,10,770},{11,10,771},{11,10, +848},{11,10,857},{11,10,931},{11,10,947},{12,10,326},{12,10,387},{12,10,484},{12 +,10,528},{12,10,552},{12,10,613},{13,10,189},{13,10,256},{13,10,340},{13,10,432} +,{13,10,436},{13,10,440},{13,10,454},{14,10,174},{14,10,220},{14,10,284},{14,10, +390},{145,10,121},{8,11,598},{9,11,664},{138,11,441},{9,10,137},{138,10,221},{ +133,11,812},{148,0,15},{134,0,1341},{4,11,137},{6,0,1017},{7,11,1178},{135,11, +1520},{7,10,390},{138,10,140},{7,11,1260},{135,11,1790},{137,11,191},{135,10, +1144},{6,0,1810},{7,0,657},{8,0,886},{10,0,857},{14,0,440},{144,0,96},{6,11,1661 +},{7,11,1975},{7,11,2009},{7,11,2011},{136,0,533},{6,0,1453},{134,10,464},{132, +11,715},{5,10,407},{11,10,204},{11,10,243},{11,10,489},{12,10,293},{19,10,37},{ +20,10,73},{150,10,38},{133,11,703},{4,0,211},{5,10,325},{7,0,1483},{8,10,5},{8, +10,227},{9,10,105},{10,10,585},{140,10,614},{4,0,332},{5,0,335},{6,0,238},{7,0, +269},{7,0,811},{7,0,1797},{8,0,836},{9,0,507},{141,0,242},{5,11,89},{7,11,1915}, +{9,11,185},{9,11,235},{9,11,496},{10,11,64},{10,11,270},{10,11,403},{10,11,469}, +{10,11,529},{10,11,590},{11,11,140},{11,11,860},{13,11,1},{13,11,422},{14,11,341 +},{14,11,364},{17,11,93},{18,11,113},{19,11,97},{147,11,113},{133,11,695},{5,11, +6},{6,10,621},{6,11,183},{7,11,680},{7,11,978},{7,11,1013},{7,11,1055},{12,11, +230},{13,10,504},{13,11,172},{16,0,19},{146,11,29},{136,0,156},{133,0,1009},{6, +11,29},{139,11,63},{134,0,820},{134,10,218},{7,10,454},{7,10,782},{8,10,768},{ +140,10,686},{5,0,228},{6,0,203},{7,0,156},{8,0,347},{9,0,265},{15,11,8},{18,0,39 +},{18,11,39},{20,0,54},{20,11,54},{21,0,31},{21,11,31},{22,0,3},{22,11,3},{23,0, +0},{151,11,0},{7,0,1131},{135,0,1468},{144,10,0},{134,0,1276},{10,10,676},{140, +10,462},{132,11,311},{134,11,1740},{7,11,170},{8,11,90},{8,11,177},{8,11,415},{ +11,11,714},{142,11,281},{134,10,164},{6,0,1792},{138,0,849},{150,10,50},{5,0,291 +},{5,0,318},{7,0,765},{8,11,522},{9,0,389},{12,0,548},{142,11,328},{11,11,91},{ +13,11,129},{15,11,101},{145,11,125},{4,11,494},{6,11,74},{7,11,44},{7,11,407},{8 +,11,551},{12,11,17},{15,11,5},{148,11,11},{4,11,276},{133,11,296},{6,10,343},{7, +10,195},{7,11,1777},{9,10,226},{10,10,197},{10,10,575},{11,10,502},{139,10,899}, +{10,0,525},{139,0,82},{4,11,7},{5,11,90},{5,11,158},{6,11,542},{7,11,221},{7,11, +1574},{9,11,490},{10,11,540},{11,11,443},{11,11,757},{142,0,453},{135,0,666},{22 +,10,29},{150,11,29},{4,0,422},{147,10,8},{5,0,355},{145,0,0},{6,0,1873},{7,11, +588},{9,0,918},{9,11,175},{138,11,530},{143,11,31},{7,10,1125},{9,10,143},{11,0, +165},{14,10,405},{150,10,21},{9,0,260},{137,0,905},{5,11,872},{6,11,57},{6,11, +479},{6,11,562},{7,11,471},{7,11,1060},{9,11,447},{9,11,454},{141,11,6},{138,11, +704},{133,0,865},{5,0,914},{134,0,1625},{133,0,234},{5,11,31},{6,11,614},{7,0, +1383},{145,11,61},{7,11,1200},{138,11,460},{6,11,424},{135,11,1866},{136,0,306}, +{5,10,959},{12,11,30},{13,11,148},{14,11,87},{14,11,182},{16,11,42},{18,11,92},{ +148,11,70},{6,0,1919},{6,0,1921},{9,0,923},{9,0,930},{9,0,941},{9,0,949},{9,0, +987},{9,0,988},{9,0,992},{12,0,802},{12,0,815},{12,0,856},{12,0,885},{12,0,893}, +{12,0,898},{12,0,919},{12,0,920},{12,0,941},{12,0,947},{15,0,183},{15,0,185},{15 +,0,189},{15,0,197},{15,0,202},{15,0,233},{15,11,156},{18,0,218},{18,0,219},{146, +0,233},{135,10,1759},{136,10,173},{5,11,13},{6,11,142},{13,0,163},{13,0,180},{18 +,0,78},{148,0,35},{134,10,266},{6,11,97},{7,11,116},{8,11,322},{8,11,755},{9,11, +548},{10,11,714},{11,11,884},{141,11,324},{135,0,1312},{9,0,814},{137,11,676},{ +133,0,707},{135,0,1493},{6,0,421},{7,0,61},{7,0,1540},{10,0,11},{138,0,501},{7, +11,866},{7,11,1163},{12,0,733},{140,0,766},{137,0,341},{142,0,98},{145,11,115},{ +135,11,1111},{136,10,300},{136,0,1014},{8,11,1},{9,11,112},{138,11,326},{132,11, +730},{5,11,488},{6,11,527},{7,11,489},{7,11,1636},{8,11,121},{8,11,144},{8,11, +359},{9,11,193},{9,11,241},{9,11,336},{9,11,882},{11,11,266},{11,11,372},{11,11, +944},{12,11,401},{140,11,641},{6,0,971},{134,0,1121},{6,0,102},{7,0,72},{15,0, +142},{147,0,67},{151,0,30},{135,0,823},{134,0,1045},{5,10,427},{5,10,734},{7,10, +478},{136,10,52},{7,0,1930},{11,10,217},{142,10,165},{6,0,1512},{135,0,1870},{9, +11,31},{10,11,244},{10,11,699},{12,11,149},{141,11,497},{133,11,377},{145,11,101 +},{10,11,158},{13,11,13},{13,11,137},{13,11,258},{14,11,111},{14,11,225},{14,11, +253},{14,11,304},{14,11,339},{14,11,417},{146,11,33},{6,0,87},{6,10,1734},{7,10, +20},{7,10,1056},{8,10,732},{9,10,406},{9,10,911},{138,10,694},{134,0,1243},{137, +0,245},{7,0,68},{8,0,48},{8,0,88},{8,0,582},{8,0,681},{9,0,373},{9,0,864},{11,0, +157},{11,0,336},{11,0,843},{148,0,27},{8,11,663},{144,11,8},{133,10,613},{4,0,88 +},{5,0,137},{5,0,174},{5,0,777},{6,0,1664},{6,0,1725},{7,0,77},{7,0,426},{7,0, +1317},{7,0,1355},{8,0,126},{8,0,563},{9,0,523},{9,0,750},{10,0,310},{10,0,836},{ +11,0,42},{11,0,318},{11,0,731},{12,0,68},{12,0,92},{12,0,507},{12,0,692},{13,0, +81},{13,0,238},{13,0,374},{14,0,436},{18,0,138},{19,0,78},{19,0,111},{20,0,55},{ +20,0,77},{148,0,92},{141,0,418},{4,0,938},{137,0,625},{138,0,351},{5,11,843},{7, +10,32},{7,10,984},{8,10,85},{8,10,709},{9,10,579},{9,10,847},{9,10,856},{10,10, +799},{11,10,258},{11,10,1007},{12,10,331},{12,10,615},{13,10,188},{13,10,435},{ +14,10,8},{15,10,165},{16,10,27},{148,10,40},{6,0,1668},{7,0,1499},{8,0,117},{9,0 +,314},{138,0,174},{135,0,707},{132,11,554},{133,11,536},{5,0,403},{5,11,207},{9, +11,79},{11,11,625},{145,11,7},{132,11,424},{136,11,785},{4,10,167},{135,10,82},{ +9,0,7},{9,11,7},{23,0,6},{151,11,6},{5,10,62},{6,0,282},{6,10,534},{7,10,74},{7, +10,678},{7,10,684},{7,10,1043},{7,10,1072},{8,10,280},{8,10,541},{8,10,686},{9, +10,258},{10,10,519},{11,10,252},{140,10,282},{138,10,33},{132,10,359},{4,0,44},{ +5,0,311},{6,0,156},{7,0,639},{7,0,762},{7,0,1827},{9,0,8},{9,0,462},{148,0,83},{ +7,11,769},{9,11,18},{138,11,358},{4,0,346},{4,11,896},{6,11,1777},{7,0,115},{9,0 +,180},{9,0,456},{138,0,363},{133,10,211},{7,0,761},{7,0,1051},{137,0,545},{6,10, +145},{141,10,336},{7,11,750},{9,11,223},{11,11,27},{11,11,466},{12,11,624},{14, +11,265},{146,11,61},{6,0,752},{6,0,768},{6,0,1195},{6,0,1254},{6,0,1619},{137,0, +835},{6,0,1936},{8,0,930},{136,0,960},{132,10,263},{132,11,249},{4,10,916},{140, +0,653},{4,11,603},{133,11,661},{4,11,11},{6,11,128},{7,11,231},{7,11,1533},{8,0, +344},{138,11,725},{134,0,1483},{134,0,875},{6,0,185},{7,0,1899},{9,0,875},{139,0 +,673},{15,10,155},{144,10,79},{4,10,599},{6,10,1634},{7,0,93},{7,0,210},{7,0, +1223},{7,10,67},{7,10,691},{7,10,979},{7,10,1697},{8,0,451},{8,0,460},{8,10,207} +,{8,10,214},{8,10,231},{8,10,294},{8,10,336},{8,10,428},{8,10,471},{8,10,622},{8 +,10,626},{8,10,679},{8,10,759},{8,10,829},{9,10,11},{9,10,246},{9,10,484},{9,10, +573},{9,10,706},{9,10,762},{9,10,798},{9,10,855},{9,10,870},{9,10,912},{10,10, +303},{10,10,335},{10,10,424},{10,10,461},{10,10,543},{10,10,759},{10,10,814},{11 +,0,353},{11,0,475},{11,10,59},{11,10,235},{11,10,590},{11,10,929},{11,10,963},{ +11,10,987},{12,10,114},{12,10,182},{12,10,226},{12,10,332},{12,10,439},{12,10, +575},{12,10,598},{12,10,675},{13,10,8},{13,10,125},{13,10,194},{13,10,287},{14, +10,197},{14,10,383},{15,10,53},{17,10,63},{19,10,46},{19,10,98},{19,10,106},{148 +,10,85},{132,11,476},{4,0,327},{5,0,478},{7,0,1332},{136,0,753},{5,0,1020},{133, +0,1022},{135,11,1807},{4,0,103},{133,0,401},{4,0,499},{135,0,1421},{10,0,207},{ +13,0,164},{147,10,126},{9,11,20},{10,11,324},{139,11,488},{132,0,96},{9,11,280}, +{138,11,134},{135,0,968},{133,10,187},{135,10,1286},{5,11,112},{6,11,103},{134, +11,150},{4,10,215},{8,0,914},{9,10,38},{10,0,3},{11,10,23},{11,10,127},{139,10, +796},{135,0,399},{6,0,563},{137,0,224},{6,0,704},{134,0,1214},{4,11,708},{8,11, +15},{9,11,50},{9,11,386},{11,11,18},{11,11,529},{140,11,228},{4,11,563},{7,11, +109},{7,11,592},{7,11,637},{7,11,770},{7,11,1701},{8,11,436},{8,11,463},{9,11,60 +},{9,11,335},{9,11,904},{10,11,73},{11,11,434},{12,11,585},{13,11,331},{18,11, +110},{148,11,60},{134,0,1559},{132,11,502},{6,11,347},{138,11,161},{4,11,33},{5, +11,102},{5,11,500},{6,11,284},{7,11,1079},{7,11,1423},{7,11,1702},{8,11,470},{9, +11,554},{9,11,723},{139,11,333},{7,11,246},{135,11,840},{6,11,10},{8,11,571},{9, +11,739},{143,11,91},{5,11,626},{8,0,861},{10,0,905},{12,0,730},{140,0,789},{134, +0,946},{5,0,746},{12,0,333},{12,11,333},{14,0,332},{142,11,332},{5,11,18},{6,11, +526},{13,11,24},{13,11,110},{19,11,5},{147,11,44},{4,0,910},{5,0,832},{135,10, +2002},{10,11,768},{139,11,787},{4,11,309},{5,11,462},{7,11,970},{135,11,1097},{4 +,10,28},{5,10,440},{7,10,248},{11,10,833},{140,10,344},{134,10,1654},{6,0,632},{ +6,0,652},{6,0,1272},{6,0,1384},{134,0,1560},{134,11,1704},{5,10,853},{134,0,1393 +},{6,10,249},{7,10,1234},{139,10,573},{5,11,86},{7,11,743},{9,11,85},{10,11,281} +,{10,11,432},{11,11,490},{12,11,251},{13,11,118},{14,11,378},{146,11,143},{5,11, +524},{133,11,744},{134,0,1514},{10,0,201},{142,0,319},{7,0,717},{7,10,392},{8,10 +,20},{8,10,172},{8,10,690},{9,10,383},{9,10,845},{10,0,510},{11,10,293},{11,10, +832},{11,10,920},{11,10,984},{141,10,221},{134,0,1381},{5,10,858},{133,10,992},{ +8,0,528},{137,0,348},{10,11,107},{140,11,436},{4,0,20},{133,0,616},{134,0,1251}, +{132,11,927},{10,11,123},{12,11,670},{13,11,371},{14,11,142},{146,11,94},{134,0, +1163},{7,11,1149},{137,11,156},{134,0,307},{133,11,778},{7,0,1091},{135,0,1765}, +{5,11,502},{6,10,268},{137,10,62},{8,11,196},{10,11,283},{139,11,406},{4,0,26},{ +5,0,429},{6,0,245},{7,0,704},{7,0,1379},{135,0,1474},{133,11,855},{132,0,881},{4 +,0,621},{135,11,1596},{7,11,1400},{9,11,446},{138,11,45},{6,0,736},{138,10,106}, +{133,0,542},{134,0,348},{133,0,868},{136,0,433},{135,0,1495},{138,0,771},{6,10, +613},{136,10,223},{138,0,215},{141,0,124},{136,11,391},{135,11,172},{132,10,670} +,{140,0,55},{9,10,40},{139,10,136},{7,0,62},{147,0,112},{132,0,856},{132,11,568} +,{11,10,259},{140,0,270},{8,0,572},{137,0,698},{4,11,732},{9,10,310},{137,10,682 +},{142,10,296},{134,0,939},{136,11,733},{135,11,1435},{7,10,1401},{135,10,1476}, +{4,10,296},{6,0,352},{7,10,401},{7,10,1410},{7,10,1594},{7,10,1674},{8,10,63},{8 +,10,660},{137,10,74},{4,11,428},{133,11,668},{4,10,139},{4,10,388},{140,10,188}, +{7,11,2015},{140,11,665},{132,0,647},{146,0,10},{138,0,220},{142,0,464},{132,0, +109},{134,0,1746},{4,10,747},{6,0,515},{6,11,1623},{6,11,1681},{7,10,649},{7,10, +1479},{135,10,1583},{133,10,232},{135,0,566},{137,10,887},{4,0,40},{10,0,67},{11 +,0,117},{11,0,768},{139,0,935},{132,0,801},{7,0,992},{8,0,301},{9,0,722},{12,0, +63},{13,0,29},{14,0,161},{143,0,18},{139,0,923},{6,11,1748},{8,11,715},{9,11,802 +},{10,11,46},{10,11,819},{13,11,308},{14,11,351},{14,11,363},{146,11,67},{137,11 +,745},{4,10,14},{7,0,1145},{7,10,1801},{10,10,748},{141,10,458},{4,11,63},{5,11, +347},{134,11,474},{135,0,568},{4,10,425},{7,11,577},{7,11,1432},{9,11,475},{9,11 +,505},{9,11,526},{9,11,609},{9,11,689},{9,11,726},{9,11,735},{9,11,738},{10,11, +556},{10,11,674},{10,11,684},{11,11,89},{11,11,202},{11,11,272},{11,11,380},{11, +11,415},{11,11,505},{11,11,537},{11,11,550},{11,11,562},{11,11,640},{11,11,667}, +{11,11,688},{11,11,847},{11,11,927},{11,11,930},{11,11,940},{12,11,144},{12,11, +325},{12,11,329},{12,11,389},{12,11,403},{12,11,451},{12,11,515},{12,11,604},{12 +,11,616},{12,11,626},{13,11,66},{13,11,131},{13,11,167},{13,11,236},{13,11,368}, +{13,11,411},{13,11,434},{13,11,453},{13,11,461},{13,11,474},{14,11,59},{14,11,60 +},{14,11,139},{14,11,152},{14,11,276},{14,11,353},{14,11,402},{15,11,28},{15,11, +81},{15,11,123},{15,11,152},{18,11,136},{148,11,88},{137,0,247},{135,11,1622},{9 +,11,544},{11,11,413},{144,11,25},{4,0,645},{6,10,1768},{7,0,825},{135,11,89},{ +140,0,328},{5,10,943},{134,10,1779},{134,0,1363},{5,10,245},{6,10,576},{7,10,582 +},{136,10,225},{134,0,1280},{5,11,824},{133,11,941},{7,11,440},{8,11,230},{139, +11,106},{5,0,28},{6,0,204},{10,0,320},{10,0,583},{13,0,502},{14,0,72},{14,0,274} +,{14,0,312},{14,0,344},{15,0,159},{16,0,62},{16,0,69},{17,0,30},{18,0,42},{18,0, +53},{18,0,84},{18,0,140},{19,0,68},{19,0,85},{20,0,5},{20,0,45},{20,0,101},{22,0 +,7},{150,0,20},{4,0,558},{6,0,390},{7,0,162},{7,0,689},{9,0,360},{138,0,653},{ +134,0,764},{6,0,862},{137,0,833},{5,0,856},{6,0,1672},{6,0,1757},{134,0,1781},{5 +,0,92},{10,0,736},{140,0,102},{6,0,1927},{6,0,1944},{8,0,924},{8,0,948},{10,0, +967},{138,0,978},{134,0,1479},{5,0,590},{8,0,360},{9,0,213},{138,0,63},{134,0, +1521},{6,0,709},{134,0,891},{132,10,443},{13,0,477},{14,0,120},{148,0,61},{4,11, +914},{5,11,800},{133,11,852},{10,11,54},{141,11,115},{4,11,918},{133,11,876},{ +139,11,152},{4,11,92},{133,11,274},{135,11,1901},{9,11,800},{10,11,693},{11,11, +482},{11,11,734},{139,11,789},{4,10,298},{137,0,483},{6,0,1213},{141,11,498},{ +135,11,1451},{133,11,743},{4,0,1022},{10,0,1000},{12,0,957},{12,0,980},{12,0, +1013},{14,0,481},{144,0,116},{4,11,49},{7,11,280},{7,11,1633},{8,0,503},{145,0, +29},{135,0,1712},{134,0,466},{136,11,47},{5,10,164},{7,10,121},{142,10,189},{7, +10,812},{7,10,1261},{7,10,1360},{9,10,632},{140,10,352},{139,10,556},{132,0,731} +,{5,11,272},{5,11,908},{5,11,942},{7,11,1008},{7,11,1560},{8,11,197},{9,11,47},{ +11,11,538},{139,11,742},{4,10,172},{9,10,611},{10,10,436},{12,10,673},{141,10, +255},{133,10,844},{10,0,484},{11,0,754},{12,0,457},{14,0,171},{14,0,389},{146,0, +153},{9,10,263},{10,10,147},{138,10,492},{137,11,891},{138,0,241},{133,10,537},{ +6,0,2005},{136,0,964},{137,10,842},{151,11,8},{4,11,407},{132,11,560},{135,11, +1884},{6,0,1100},{134,0,1242},{135,0,954},{5,10,230},{5,10,392},{6,10,420},{9,10 +,568},{140,10,612},{4,11,475},{11,11,35},{11,11,90},{13,11,7},{13,11,71},{13,11, +177},{142,11,422},{136,11,332},{135,0,1958},{6,0,549},{8,0,34},{8,0,283},{9,0, +165},{138,0,475},{10,0,952},{12,0,966},{140,0,994},{5,0,652},{5,0,701},{135,0, +449},{4,0,655},{7,0,850},{17,0,75},{146,0,137},{4,0,146},{5,10,41},{7,0,1618},{7 +,10,1459},{7,10,1469},{7,10,1859},{8,0,670},{9,10,549},{139,10,905},{133,10,696} +,{6,0,159},{6,0,364},{7,0,516},{137,0,518},{135,0,1439},{6,11,222},{7,11,636},{7 +,11,1620},{8,11,409},{9,11,693},{139,11,77},{13,0,151},{141,11,45},{4,10,771},{4 +,11,336},{134,0,1027},{139,11,392},{10,11,121},{11,11,175},{149,11,16},{8,0,950} +,{138,0,983},{133,10,921},{135,0,993},{6,10,180},{7,10,1137},{8,10,751},{139,10, +805},{7,0,501},{9,0,111},{10,0,141},{11,0,332},{13,0,43},{13,0,429},{14,0,130},{ +14,0,415},{145,0,102},{4,10,183},{5,11,882},{7,10,271},{11,10,824},{11,10,952},{ +13,10,278},{13,10,339},{13,10,482},{14,10,424},{148,10,99},{4,10,19},{5,10,477}, +{5,10,596},{6,10,505},{7,10,1221},{11,10,907},{12,10,209},{141,10,214},{135,10, +1215},{133,0,452},{132,11,426},{5,0,149},{136,0,233},{133,0,935},{6,11,58},{7,11 +,654},{7,11,745},{7,11,1969},{8,11,240},{8,11,675},{9,11,479},{9,11,731},{10,11, +330},{10,11,593},{10,11,817},{11,11,32},{11,11,133},{11,11,221},{145,11,68},{7, +11,102},{9,11,538},{12,0,582},{146,0,131},{136,0,801},{134,10,1645},{132,0,70},{ +6,10,92},{6,10,188},{7,10,1269},{7,10,1524},{7,10,1876},{10,10,228},{139,10,1020 +},{4,10,459},{133,10,966},{138,0,369},{12,10,330},{144,0,36},{141,11,366},{6,10, +18},{7,0,721},{7,10,932},{8,10,757},{9,10,54},{9,10,65},{9,10,844},{10,0,236},{ +10,10,113},{10,10,315},{10,10,798},{11,10,153},{12,0,204},{12,10,151},{12,10,392 +},{12,10,666},{142,10,248},{7,0,241},{8,10,548},{9,10,532},{10,0,430},{10,10,117 +},{11,10,351},{11,10,375},{143,10,23},{134,10,1742},{133,10,965},{133,11,566},{6 +,11,48},{135,11,63},{134,10,182},{10,10,65},{10,10,488},{138,10,497},{6,11,114}, +{7,11,1224},{7,11,1556},{136,11,3},{134,0,1817},{8,11,576},{137,11,267},{6,0, +1078},{144,0,16},{9,10,588},{138,10,260},{138,0,1021},{5,0,406},{134,0,2022},{ +133,11,933},{6,0,69},{135,0,117},{7,0,1830},{136,11,427},{4,0,432},{135,0,824},{ +134,10,1786},{133,0,826},{139,11,67},{133,11,759},{135,10,308},{137,0,816},{133, +0,1000},{4,0,297},{6,0,529},{7,0,152},{7,0,713},{7,0,1845},{8,0,710},{8,0,717},{ +12,0,639},{140,0,685},{7,0,423},{136,10,588},{136,10,287},{136,0,510},{134,0, +1048},{6,0,618},{7,11,56},{7,11,1989},{8,11,337},{8,11,738},{9,11,600},{10,11, +483},{12,11,37},{13,11,447},{142,11,92},{4,0,520},{135,0,575},{8,0,990},{138,0, +977},{135,11,774},{9,11,347},{11,11,24},{140,11,170},{136,11,379},{140,10,290},{ +132,11,328},{4,0,321},{134,0,569},{4,11,101},{135,11,1171},{5,11,833},{7,0,723}, +{7,0,1135},{136,11,744},{7,10,719},{8,10,809},{136,10,834},{8,0,921},{136,10,796 +},{5,10,210},{6,10,213},{7,10,60},{10,10,364},{139,10,135},{5,0,397},{5,10,607}, +{6,0,154},{7,0,676},{8,0,443},{8,0,609},{8,10,326},{8,10,490},{9,0,24},{9,0,325} +,{10,0,35},{11,0,535},{11,0,672},{11,0,1018},{12,0,637},{144,0,30},{4,10,701},{5 +,10,472},{6,11,9},{6,11,397},{7,11,53},{7,11,1742},{9,10,758},{10,11,632},{11,11 +,828},{140,11,146},{135,10,380},{135,10,1947},{148,11,109},{10,10,278},{138,11, +278},{134,0,856},{4,10,386},{7,0,139},{8,10,405},{8,10,728},{9,10,497},{11,10, +110},{11,10,360},{15,10,37},{144,10,84},{141,0,282},{133,0,981},{5,0,288},{7,10, +1452},{7,10,1480},{8,10,634},{140,10,472},{7,0,1890},{8,11,367},{10,11,760},{14, +11,79},{20,11,17},{152,11,0},{4,10,524},{136,10,810},{4,0,56},{7,0,1791},{8,0, +607},{8,0,651},{11,0,465},{11,0,835},{12,0,337},{141,0,480},{10,10,238},{141,10, +33},{11,11,417},{12,11,223},{140,11,265},{9,0,158},{10,0,411},{140,0,261},{133, +10,532},{133,10,997},{12,11,186},{12,11,292},{14,11,100},{146,11,70},{6,0,1403}, +{136,0,617},{134,0,1205},{139,0,563},{4,0,242},{134,0,333},{4,11,186},{5,11,157} +,{8,11,168},{138,11,6},{132,0,369},{133,11,875},{5,10,782},{5,10,829},{134,10, +1738},{134,0,622},{135,11,1272},{6,0,1407},{7,11,111},{136,11,581},{7,10,1823},{ +139,10,693},{7,0,160},{10,0,624},{142,0,279},{132,0,363},{10,11,589},{12,11,111} +,{13,11,260},{14,11,82},{18,11,63},{147,11,45},{7,11,1364},{7,11,1907},{141,11, +158},{4,11,404},{4,11,659},{135,11,675},{13,11,211},{14,11,133},{14,11,204},{15, +11,64},{15,11,69},{15,11,114},{16,11,10},{19,11,23},{19,11,35},{19,11,39},{19,11 +,51},{19,11,71},{19,11,75},{152,11,15},{4,10,78},{5,10,96},{5,10,182},{7,10,1724 +},{7,10,1825},{10,10,394},{10,10,471},{11,10,532},{14,10,340},{145,10,88},{135, +10,1964},{133,11,391},{11,11,887},{14,11,365},{142,11,375},{5,11,540},{6,11,1697 +},{7,11,222},{136,11,341},{134,11,78},{9,0,601},{9,0,619},{10,0,505},{10,0,732}, +{11,0,355},{140,0,139},{134,0,292},{139,0,174},{5,0,177},{6,0,616},{7,0,827},{9, +0,525},{138,0,656},{6,10,215},{7,10,1028},{7,10,1473},{7,10,1721},{9,10,424},{10 +,0,31},{138,10,779},{135,10,584},{136,11,293},{134,0,685},{135,11,1868},{133,11, +460},{6,10,67},{7,0,647},{7,10,1630},{9,10,354},{9,10,675},{10,10,830},{14,10,80 +},{145,10,80},{4,0,161},{133,0,631},{6,10,141},{7,10,225},{9,10,59},{9,10,607},{ +10,10,312},{11,10,687},{12,10,555},{13,10,373},{13,10,494},{148,10,58},{7,11,965 +},{7,11,1460},{135,11,1604},{136,10,783},{134,11,388},{4,11,511},{6,0,722},{6,0, +1267},{9,11,333},{9,11,379},{10,11,602},{11,11,441},{11,11,723},{11,11,976},{140 +,11,357},{134,0,1797},{135,0,1684},{5,11,938},{8,11,707},{9,0,469},{9,0,709},{12 +,0,512},{14,0,65},{145,0,12},{7,0,1230},{136,0,531},{10,0,229},{11,0,73},{11,0, +376},{139,0,433},{12,0,268},{12,0,640},{142,0,119},{7,10,430},{139,10,46},{6,0, +558},{7,0,651},{8,0,421},{9,0,0},{10,0,34},{139,0,1008},{5,10,602},{6,0,106},{7, +0,1786},{7,0,1821},{7,10,2018},{9,0,102},{9,0,763},{137,10,418},{5,0,65},{6,0, +416},{7,0,1720},{7,0,1924},{8,10,677},{10,0,109},{11,0,14},{11,0,70},{11,0,569}, +{11,0,735},{15,0,153},{148,0,80},{135,11,1625},{137,11,772},{136,0,595},{6,11, +469},{7,11,1709},{138,11,515},{7,0,1832},{138,0,374},{9,0,106},{9,0,163},{9,0, +296},{10,0,167},{10,0,172},{10,0,777},{139,0,16},{6,0,6},{7,0,81},{7,0,771},{7,0 +,1731},{9,0,405},{138,0,421},{4,11,500},{135,11,938},{5,11,68},{134,11,383},{5,0 +,881},{133,0,885},{6,0,854},{6,0,1132},{6,0,1495},{6,0,1526},{6,0,1533},{134,0, +1577},{4,11,337},{6,11,353},{7,11,1934},{8,11,488},{137,11,429},{7,11,236},{7,11 +,1795},{8,11,259},{9,11,135},{9,11,177},{10,11,825},{11,11,115},{11,11,370},{11, +11,405},{11,11,604},{12,11,10},{12,11,667},{12,11,669},{13,11,76},{14,11,310},{ +15,11,76},{15,11,147},{148,11,23},{5,0,142},{134,0,546},{4,11,15},{5,11,22},{6, +11,244},{7,11,40},{7,11,200},{7,11,906},{7,11,1199},{9,11,616},{10,11,716},{11, +11,635},{11,11,801},{140,11,458},{5,0,466},{7,10,329},{11,0,571},{12,0,198},{13, +0,283},{14,0,186},{15,0,21},{143,0,103},{4,0,185},{5,0,257},{5,0,839},{5,0,936}, +{7,11,1735},{9,0,399},{10,0,258},{10,0,395},{10,0,734},{11,0,1014},{12,0,23},{13 +,0,350},{14,0,150},{147,0,6},{12,11,36},{141,11,337},{5,11,598},{7,11,791},{8,11 +,108},{137,11,123},{132,10,469},{7,0,404},{7,0,1377},{7,0,1430},{7,0,2017},{8,0, +149},{8,0,239},{8,0,512},{8,0,793},{8,0,818},{9,0,474},{9,0,595},{10,0,122},{10, +0,565},{10,0,649},{10,0,783},{11,0,239},{11,0,295},{11,0,447},{11,0,528},{11,0, +639},{11,0,800},{12,0,25},{12,0,77},{12,0,157},{12,0,256},{12,0,316},{12,0,390}, +{12,0,391},{12,0,395},{12,0,478},{12,0,503},{12,0,592},{12,0,680},{13,0,50},{13, +0,53},{13,0,132},{13,0,198},{13,0,322},{13,0,415},{13,0,511},{14,0,71},{14,0,395 +},{15,0,71},{15,0,136},{17,0,123},{18,0,93},{147,0,58},{136,0,712},{134,10,1743} +,{5,10,929},{6,10,340},{8,10,376},{136,10,807},{6,0,1848},{8,0,860},{10,0,856},{ +10,0,859},{10,0,925},{10,0,941},{140,0,762},{6,0,629},{6,0,906},{9,0,810},{140,0 +,652},{5,10,218},{7,10,1610},{138,10,83},{7,10,1512},{135,10,1794},{4,0,377},{4, +11,155},{7,11,1689},{11,10,0},{16,10,78},{152,0,13},{4,11,164},{5,11,151},{5,11, +730},{5,11,741},{7,11,498},{7,11,870},{7,11,1542},{12,11,213},{14,11,36},{14,11, +391},{17,11,111},{18,11,6},{18,11,46},{18,11,151},{19,11,36},{20,11,32},{20,11, +56},{20,11,69},{20,11,102},{21,11,4},{22,11,8},{22,11,10},{22,11,14},{150,11,31} +,{5,10,571},{135,0,1842},{4,10,455},{4,11,624},{135,11,1752},{134,0,1501},{4,11, +492},{5,11,451},{6,10,161},{7,10,372},{137,10,597},{132,10,349},{4,0,180},{135,0 +,1906},{135,11,835},{141,11,70},{132,0,491},{137,10,751},{6,10,432},{139,10,322} +,{4,0,171},{138,0,234},{6,11,113},{135,11,436},{4,0,586},{7,0,1186},{138,0,631}, +{5,10,468},{10,10,325},{11,10,856},{12,10,345},{143,10,104},{5,10,223},{10,11, +592},{10,11,753},{12,11,317},{12,11,355},{12,11,465},{12,11,469},{12,11,560},{12 +,11,578},{141,11,243},{132,10,566},{135,11,520},{4,10,59},{135,10,1394},{6,10, +436},{139,10,481},{4,10,48},{5,10,271},{7,10,953},{7,11,1878},{9,0,931},{10,0, +334},{148,0,71},{5,10,610},{8,10,457},{139,0,170},{133,10,755},{6,0,1587},{135, +10,1217},{4,10,197},{149,11,26},{133,11,585},{137,11,521},{133,0,765},{133,10, +217},{139,11,586},{133,0,424},{9,11,752},{12,11,610},{13,11,431},{16,11,59},{146 +,11,109},{136,0,714},{4,11,307},{135,0,685},{9,0,420},{10,0,269},{10,0,285},{10, +0,576},{11,0,397},{13,0,175},{145,0,90},{132,0,429},{133,11,964},{9,11,463},{138 +,11,595},{7,0,18},{7,0,699},{7,0,1966},{8,0,752},{9,0,273},{9,0,412},{9,0,703},{ +10,0,71},{10,0,427},{138,0,508},{4,10,165},{7,10,1398},{135,10,1829},{4,0,53},{5 +,0,186},{7,0,752},{7,0,828},{142,0,116},{8,0,575},{10,0,289},{139,0,319},{132,0, +675},{134,0,1424},{4,11,75},{5,11,180},{6,11,500},{7,11,58},{7,11,710},{138,11, +645},{133,11,649},{6,11,276},{7,11,282},{7,11,879},{7,11,924},{8,11,459},{9,11, +599},{9,11,754},{11,11,574},{12,11,128},{12,11,494},{13,11,52},{13,11,301},{15, +11,30},{143,11,132},{6,0,647},{134,0,1095},{5,10,9},{7,10,297},{7,10,966},{140, +10,306},{132,11,200},{134,0,1334},{5,10,146},{6,10,411},{138,10,721},{6,0,209},{ +6,0,1141},{6,0,1288},{8,0,468},{9,0,210},{11,0,36},{12,0,28},{12,0,630},{13,0,21 +},{13,0,349},{14,0,7},{145,0,13},{6,10,177},{135,10,467},{4,0,342},{135,0,1179}, +{10,11,454},{140,11,324},{4,0,928},{133,0,910},{6,11,225},{7,0,1838},{137,11,211 +},{16,0,101},{20,0,115},{20,0,118},{148,0,122},{4,0,496},{135,0,856},{4,0,318},{ +7,11,718},{11,0,654},{139,11,102},{8,11,58},{9,11,724},{11,11,809},{13,11,113},{ +145,11,72},{5,10,200},{6,11,345},{135,11,1247},{8,11,767},{8,11,803},{9,11,301}, +{137,11,903},{7,0,915},{7,11,1949},{8,0,247},{8,11,674},{147,0,0},{4,0,202},{5,0 +,382},{6,0,454},{7,0,936},{7,0,1803},{8,0,758},{9,0,375},{9,0,895},{10,0,743},{ +10,0,792},{11,0,978},{11,0,1012},{142,0,109},{7,0,1150},{7,0,1425},{7,0,1453},{ +140,0,513},{134,11,259},{138,0,791},{11,0,821},{12,0,110},{12,0,153},{18,0,41},{ +150,0,19},{134,10,481},{132,0,796},{6,0,445},{8,11,254},{137,0,909},{10,0,776},{ +13,0,345},{142,0,425},{4,10,84},{7,10,1482},{10,10,76},{138,10,142},{135,11,742} +,{5,10,1015},{134,0,578},{4,10,315},{5,10,507},{6,0,1387},{135,10,1370},{4,0,438 +},{133,0,555},{136,0,766},{133,11,248},{134,10,1722},{4,11,116},{5,11,95},{5,11, +445},{7,11,1688},{8,11,29},{9,11,272},{11,11,509},{139,11,915},{135,0,541},{133, +11,543},{8,10,222},{8,10,476},{9,10,238},{11,10,516},{11,10,575},{15,10,109},{ +146,10,100},{6,0,880},{134,0,1191},{5,11,181},{136,11,41},{134,0,1506},{132,11, +681},{7,11,25},{8,11,202},{138,11,536},{139,0,983},{137,0,768},{132,0,584},{9,11 +,423},{140,11,89},{8,11,113},{9,11,877},{10,11,554},{11,11,83},{12,11,136},{147, +11,109},{7,10,706},{7,10,1058},{138,10,538},{133,11,976},{4,11,206},{135,11,746} +,{136,11,526},{140,0,737},{11,10,92},{11,10,196},{11,10,409},{11,10,450},{11,10, +666},{11,10,777},{12,10,262},{13,10,385},{13,10,393},{15,10,115},{16,10,45},{145 +,10,82},{4,0,226},{4,0,326},{4,11,319},{5,11,699},{7,0,1770},{138,11,673},{6,10, +40},{135,10,1781},{5,0,426},{8,0,30},{9,0,2},{11,0,549},{147,0,122},{6,0,1161},{ +134,0,1329},{138,10,97},{6,10,423},{7,10,665},{135,10,1210},{7,11,13},{8,11,226} +,{10,11,537},{11,11,570},{11,11,605},{11,11,799},{11,11,804},{12,11,85},{12,11, +516},{12,11,623},{13,11,112},{13,11,361},{14,11,77},{14,11,78},{17,11,28},{147, +11,110},{132,11,769},{132,11,551},{132,11,728},{147,0,117},{9,11,57},{9,11,459}, +{10,11,425},{11,11,119},{12,11,184},{12,11,371},{13,11,358},{145,11,51},{5,11, +188},{5,11,814},{8,11,10},{9,11,421},{9,11,729},{10,11,609},{139,11,689},{134,11 +,624},{135,11,298},{135,0,462},{4,0,345},{139,10,624},{136,10,574},{4,0,385},{7, +0,265},{135,0,587},{4,11,528},{134,0,808},{133,0,398},{132,10,354},{4,0,347},{5, +0,423},{5,0,996},{135,0,1329},{135,10,1558},{7,0,1259},{9,0,125},{139,0,65},{5,0 +,136},{6,0,136},{136,0,644},{5,11,104},{6,11,173},{135,11,1631},{135,0,469},{133 +,10,830},{4,0,278},{5,0,465},{135,0,1367},{7,11,810},{8,11,138},{8,11,342},{9,11 +,84},{10,11,193},{11,11,883},{140,11,359},{5,10,496},{135,10,203},{4,0,433},{133 +,0,719},{6,10,547},{134,11,95},{5,10,88},{137,10,239},{6,11,406},{10,11,409},{10 +,11,447},{11,11,44},{140,11,100},{134,0,1423},{7,10,650},{135,10,1310},{134,0, +749},{135,11,1243},{135,0,1363},{6,0,381},{7,0,645},{7,0,694},{7,10,1076},{8,0, +546},{9,10,80},{11,10,78},{11,10,421},{11,10,534},{140,10,545},{134,11,1636},{ +135,11,1344},{7,10,274},{11,10,479},{11,10,507},{140,0,277},{4,11,282},{6,0,705} +,{6,0,783},{6,0,1275},{6,0,1481},{7,11,1034},{11,11,398},{11,11,634},{12,11,1},{ +12,11,79},{12,11,544},{14,11,237},{17,11,10},{146,11,20},{134,0,453},{4,0,555},{ +4,10,497},{7,10,1584},{8,0,536},{10,0,288},{139,0,1005},{5,11,118},{5,11,499},{6 +,11,476},{7,11,600},{7,11,888},{135,11,1096},{138,0,987},{7,0,1107},{7,10,261},{ +7,10,1115},{7,10,1354},{7,10,1588},{7,10,1705},{7,10,1902},{9,10,465},{10,10,248 +},{10,10,349},{10,10,647},{11,10,527},{11,10,660},{11,10,669},{12,10,529},{141, +10,305},{7,11,296},{7,11,596},{8,11,560},{8,11,586},{9,11,612},{11,11,100},{11, +11,304},{12,11,46},{13,11,89},{14,11,112},{145,11,122},{9,0,370},{138,0,90},{136 +,10,13},{132,0,860},{7,10,642},{8,10,250},{11,10,123},{11,10,137},{13,10,48},{ +142,10,95},{135,10,1429},{137,11,321},{132,0,257},{135,0,2031},{7,0,1768},{7,11, +1599},{7,11,1723},{8,11,79},{8,11,106},{8,11,190},{8,11,302},{8,11,383},{9,11, +119},{9,11,233},{9,11,298},{9,11,419},{9,11,471},{10,11,181},{10,11,406},{11,11, +57},{11,11,85},{11,11,120},{11,11,177},{11,11,296},{11,11,382},{11,11,454},{11, +11,758},{11,11,999},{12,11,27},{12,11,98},{12,11,131},{12,11,245},{12,11,312},{ +12,11,446},{12,11,454},{13,11,25},{13,11,98},{13,11,426},{13,11,508},{14,11,6},{ +14,11,163},{14,11,272},{14,11,277},{14,11,370},{15,11,95},{15,11,138},{15,11,167 +},{17,11,18},{17,11,38},{20,11,96},{149,11,32},{5,11,722},{134,11,1759},{145,11, +16},{6,0,1071},{134,0,1561},{10,10,545},{140,10,301},{6,0,83},{6,0,1733},{135,0, +1389},{4,0,835},{135,0,1818},{133,11,258},{4,10,904},{133,10,794},{134,0,2006},{ +5,11,30},{7,11,495},{8,11,134},{9,11,788},{140,11,438},{135,11,2004},{137,0,696} +,{5,11,50},{6,11,439},{7,11,780},{135,11,1040},{7,11,772},{7,11,1104},{7,11,1647 +},{11,11,269},{11,11,539},{11,11,607},{11,11,627},{11,11,706},{11,11,975},{12,11 +,248},{12,11,311},{12,11,434},{12,11,600},{12,11,622},{13,11,297},{13,11,367},{ +13,11,485},{14,11,69},{14,11,409},{143,11,108},{5,11,1},{6,11,81},{138,11,520},{ +7,0,1718},{9,0,95},{9,0,274},{10,0,279},{10,0,317},{10,0,420},{11,0,303},{11,0, +808},{12,0,134},{12,0,367},{13,0,149},{13,0,347},{14,0,349},{14,0,406},{18,0,22} +,{18,0,89},{18,0,122},{147,0,47},{5,11,482},{8,11,98},{9,11,172},{10,11,222},{10 +,11,700},{10,11,822},{11,11,302},{11,11,778},{12,11,50},{12,11,127},{12,11,396}, +{13,11,62},{13,11,328},{14,11,122},{147,11,72},{7,10,386},{138,10,713},{6,10,7}, +{6,10,35},{7,10,147},{7,10,1069},{7,10,1568},{7,10,1575},{7,10,1917},{8,10,43},{ +8,10,208},{9,10,128},{9,10,866},{10,10,20},{11,10,981},{147,10,33},{133,0,26},{ +132,0,550},{5,11,2},{7,11,1494},{136,11,589},{6,11,512},{7,11,797},{8,11,253},{9 +,11,77},{10,11,1},{10,11,129},{10,11,225},{11,11,118},{11,11,226},{11,11,251},{ +11,11,430},{11,11,701},{11,11,974},{11,11,982},{12,11,64},{12,11,260},{12,11,488 +},{140,11,690},{7,10,893},{141,10,424},{134,0,901},{136,0,822},{4,0,902},{5,0, +809},{134,0,122},{6,0,807},{134,0,1366},{5,11,748},{6,11,553},{135,0,262},{133,0 +,620},{4,0,34},{5,0,574},{7,0,279},{7,0,1624},{136,0,601},{6,10,322},{9,0,170},{ +9,10,552},{11,10,274},{13,10,209},{13,10,499},{14,10,85},{15,10,126},{145,10,70} +,{132,0,537},{4,11,12},{7,11,420},{7,11,522},{7,11,809},{8,11,797},{141,11,88},{ +133,0,332},{8,10,83},{8,10,742},{8,10,817},{9,10,28},{9,10,29},{9,10,885},{10,10 +,387},{11,10,633},{11,10,740},{13,10,235},{13,10,254},{15,10,143},{143,10,146},{ +6,0,1909},{9,0,964},{12,0,822},{12,0,854},{12,0,865},{12,0,910},{12,0,938},{15,0 +,169},{15,0,208},{15,0,211},{18,0,205},{18,0,206},{18,0,220},{18,0,223},{152,0, +24},{140,10,49},{5,11,528},{135,11,1580},{6,0,261},{8,0,182},{139,0,943},{134,0, +1721},{4,0,933},{133,0,880},{136,11,321},{5,11,266},{9,11,290},{9,11,364},{10,11 +,293},{11,11,606},{142,11,45},{4,11,50},{6,0,1609},{6,11,510},{6,11,594},{9,11, +121},{10,11,49},{10,11,412},{139,11,834},{7,0,895},{136,11,748},{132,11,466},{4, +10,110},{10,10,415},{10,10,597},{142,10,206},{133,0,812},{135,11,281},{6,0,1890} +,{6,0,1902},{6,0,1916},{7,10,205},{7,10,2000},{9,0,929},{9,0,942},{9,0,975},{9,0 +,984},{9,0,986},{9,0,1011},{9,0,1019},{12,0,804},{12,0,851},{12,0,867},{12,0,916 +},{12,0,923},{15,0,194},{15,0,204},{15,0,210},{15,0,222},{15,0,223},{15,0,229},{ +15,0,250},{18,0,179},{18,0,186},{146,0,192},{132,11,667},{135,0,778},{4,0,137},{ +7,0,1178},{135,0,1520},{134,0,1314},{4,11,242},{134,11,333},{6,0,1661},{7,0,1975 +},{7,0,2009},{135,0,2011},{134,0,1591},{4,10,283},{135,10,1194},{11,0,820},{150, +0,51},{4,11,39},{5,11,36},{7,11,1843},{8,11,407},{11,11,144},{140,11,523},{134, +10,1720},{4,11,510},{7,11,29},{7,11,66},{7,11,1980},{10,11,487},{10,11,809},{146 +,11,9},{5,0,89},{7,0,1915},{9,0,185},{9,0,235},{10,0,64},{10,0,270},{10,0,403},{ +10,0,469},{10,0,529},{10,0,590},{11,0,140},{11,0,860},{13,0,1},{13,0,422},{14,0, +341},{14,0,364},{17,0,93},{18,0,113},{19,0,97},{147,0,113},{133,0,695},{6,0,987} +,{134,0,1160},{5,0,6},{6,0,183},{7,0,680},{7,0,978},{7,0,1013},{7,0,1055},{12,0, +230},{13,0,172},{146,0,29},{134,11,570},{132,11,787},{134,11,518},{6,0,29},{139, +0,63},{132,11,516},{136,11,821},{132,0,311},{134,0,1740},{7,0,170},{8,0,90},{8,0 +,177},{8,0,415},{8,10,735},{11,0,714},{142,0,281},{134,0,1961},{135,11,1405},{4, +11,10},{7,11,917},{139,11,786},{5,10,132},{9,10,486},{9,10,715},{10,10,458},{11, +10,373},{11,10,668},{11,10,795},{11,10,897},{12,10,272},{12,10,424},{12,10,539}, +{12,10,558},{14,10,245},{14,10,263},{14,10,264},{14,10,393},{142,10,403},{11,0, +91},{13,0,129},{15,0,101},{145,0,125},{135,0,1132},{4,0,494},{6,0,74},{7,0,44},{ +7,0,407},{12,0,17},{15,0,5},{148,0,11},{133,10,379},{5,0,270},{5,11,684},{6,10, +89},{6,10,400},{7,10,1569},{7,10,1623},{7,10,1850},{8,10,218},{8,10,422},{9,10, +570},{138,10,626},{4,0,276},{133,0,296},{6,0,1523},{134,11,27},{6,10,387},{7,10, +882},{141,10,111},{6,10,224},{7,10,877},{137,10,647},{135,10,790},{4,0,7},{5,0, +90},{5,0,158},{6,0,542},{7,0,221},{7,0,1574},{9,0,490},{10,0,540},{11,0,443},{ +139,0,757},{7,0,588},{9,0,175},{138,0,530},{135,10,394},{142,11,23},{134,0,786}, +{135,0,580},{7,0,88},{136,0,627},{5,0,872},{6,0,57},{7,0,471},{9,0,447},{137,0, +454},{6,11,342},{6,11,496},{8,11,275},{137,11,206},{4,11,909},{133,11,940},{4,11 +,891},{134,0,735},{7,10,1409},{8,0,845},{136,0,916},{5,0,31},{134,0,614},{11,0, +458},{12,0,15},{140,0,432},{8,0,330},{140,0,477},{4,0,530},{4,11,687},{5,0,521}, +{7,0,1200},{138,0,460},{6,0,424},{135,0,1866},{9,0,569},{12,0,12},{12,0,81},{12, +0,319},{13,0,69},{14,0,259},{16,0,87},{17,0,1},{17,0,21},{17,0,24},{18,0,15},{18 +,0,56},{18,0,59},{18,0,127},{18,0,154},{19,0,19},{148,0,31},{7,0,1302},{136,10, +38},{134,11,253},{5,10,261},{7,10,78},{7,10,199},{8,10,815},{9,10,126},{138,10, +342},{5,0,595},{135,0,1863},{6,11,41},{141,11,160},{5,0,13},{134,0,142},{6,0,97} +,{7,0,116},{7,11,1304},{8,0,322},{8,0,755},{9,0,548},{10,0,714},{10,11,477},{11, +0,884},{141,0,324},{132,10,628},{134,11,1718},{7,10,266},{136,10,804},{135,10, +208},{6,10,79},{7,0,1021},{135,10,1519},{7,0,1472},{135,0,1554},{6,11,362},{146, +11,51},{7,0,1071},{7,0,1541},{7,0,1767},{7,0,1806},{11,0,162},{11,0,242},{11,0, +452},{12,0,605},{15,0,26},{144,0,44},{136,10,741},{133,11,115},{145,0,115},{134, +10,376},{6,0,1406},{134,0,1543},{5,11,193},{12,11,178},{13,11,130},{145,11,84},{ +135,0,1111},{5,11,705},{8,0,1},{9,0,650},{9,11,606},{138,0,326},{5,0,488},{6,0, +527},{7,0,489},{7,0,1636},{8,0,121},{8,0,144},{8,0,359},{9,0,193},{9,0,241},{9,0 +,336},{9,0,882},{11,0,266},{11,0,372},{11,0,944},{12,0,401},{140,0,641},{135,11, +174},{6,0,267},{7,10,244},{7,10,632},{7,10,1609},{8,10,178},{8,10,638},{141,10, +58},{134,0,1983},{134,0,1155},{134,0,1575},{134,0,1438},{9,0,31},{10,0,244},{10, +0,699},{12,0,149},{141,0,497},{133,0,377},{4,11,122},{5,11,796},{5,11,952},{6,11 +,1660},{6,11,1671},{8,11,567},{9,11,687},{9,11,742},{10,11,686},{11,11,356},{11, +11,682},{140,11,281},{145,0,101},{11,11,0},{144,11,78},{5,10,791},{5,11,179},{7, +11,1095},{135,11,1213},{8,11,372},{9,11,122},{138,11,175},{7,10,686},{8,10,33},{ +8,10,238},{10,10,616},{11,10,467},{11,10,881},{13,10,217},{13,10,253},{142,10, +268},{4,11,66},{7,11,722},{7,11,904},{137,0,476},{7,11,352},{137,11,684},{135,0, +2023},{135,0,1836},{132,10,447},{5,0,843},{144,0,35},{137,11,779},{141,11,35},{4 +,10,128},{5,10,415},{6,10,462},{7,10,294},{7,10,578},{10,10,710},{139,10,86},{ +132,0,554},{133,0,536},{136,10,587},{5,0,207},{9,0,79},{11,0,625},{145,0,7},{6, +10,427},{7,0,1371},{138,10,692},{4,0,424},{4,10,195},{135,10,802},{5,11,564},{ +136,0,785},{135,0,336},{4,0,896},{6,0,1777},{134,11,556},{137,11,103},{134,10, +1683},{7,11,544},{8,11,719},{138,11,61},{138,10,472},{4,11,5},{5,11,498},{136,11 +,637},{7,0,750},{9,0,223},{11,0,27},{11,0,466},{12,0,624},{14,0,265},{146,0,61}, +{12,0,238},{12,11,238},{18,0,155},{146,11,155},{151,10,28},{133,11,927},{5,10,3} +,{8,10,578},{9,10,118},{10,10,705},{12,0,383},{141,10,279},{4,11,893},{5,11,780} +,{133,11,893},{4,0,603},{133,0,661},{4,0,11},{5,10,229},{5,11,238},{6,0,128},{7, +0,231},{7,0,1533},{7,11,1350},{138,0,725},{8,10,102},{10,10,578},{10,10,672},{12 +,10,496},{13,10,408},{14,10,121},{145,10,106},{132,0,476},{134,0,1552},{134,11, +1729},{8,10,115},{8,10,350},{9,10,489},{10,10,128},{11,10,306},{12,10,373},{14, +10,30},{17,10,79},{19,10,80},{150,10,55},{135,0,1807},{4,0,680},{4,11,60},{7,11, +760},{7,11,1800},{8,11,314},{9,11,700},{139,11,487},{4,10,230},{5,10,702},{148, +11,94},{132,11,228},{139,0,435},{9,0,20},{10,0,324},{10,0,807},{139,0,488},{6,10 +,1728},{136,11,419},{4,10,484},{18,10,26},{19,10,42},{20,10,43},{21,10,0},{23,10 +,27},{152,10,14},{135,0,1431},{133,11,828},{5,0,112},{6,0,103},{6,0,150},{7,0, +1303},{7,11,176},{7,11,178},{7,11,1110},{9,0,292},{10,0,481},{10,11,481},{20,0, +13},{148,11,13},{138,0,356},{4,11,51},{5,11,39},{6,11,4},{7,11,591},{7,11,849},{ +7,11,951},{7,11,1129},{7,11,1613},{7,11,1760},{7,11,1988},{9,11,434},{10,11,754} +,{11,11,25},{11,11,37},{139,11,414},{6,0,1963},{134,0,2000},{132,10,633},{5,11, +902},{134,0,1244},{135,11,928},{140,0,18},{138,0,204},{135,11,1173},{134,0,867}, +{4,0,708},{8,0,15},{9,0,50},{9,0,386},{11,0,18},{11,0,529},{140,0,228},{134,11, +270},{4,0,563},{7,0,109},{7,0,592},{7,0,637},{7,0,770},{8,0,463},{9,0,60},{9,0, +335},{9,0,904},{10,0,73},{11,0,434},{12,0,585},{13,0,331},{18,0,110},{148,0,60}, +{132,0,502},{14,11,359},{19,11,52},{148,11,47},{6,11,377},{7,11,1025},{9,11,613} +,{145,11,104},{5,10,70},{5,10,622},{6,0,347},{6,10,334},{7,10,1032},{9,10,171},{ +10,0,161},{11,10,26},{11,10,213},{11,10,637},{11,10,707},{12,10,202},{12,10,380} +,{13,10,226},{13,10,355},{14,10,222},{145,10,42},{132,11,416},{4,0,33},{5,0,102} +,{6,0,284},{7,0,1079},{7,0,1423},{7,0,1702},{8,0,470},{9,0,554},{9,0,723},{11,0, +333},{142,11,372},{5,11,152},{5,11,197},{7,11,340},{7,11,867},{10,11,548},{10,11 +,581},{11,11,6},{12,11,3},{12,11,19},{14,11,110},{142,11,289},{7,0,246},{135,0, +840},{6,0,10},{8,0,571},{9,0,739},{143,0,91},{4,10,23},{4,10,141},{5,10,313},{5, +10,1014},{6,0,465},{6,10,50},{7,0,1465},{7,10,142},{7,10,559},{8,10,640},{9,10, +460},{9,10,783},{11,10,741},{12,10,183},{141,10,488},{133,0,626},{136,0,614},{ +138,0,237},{7,11,34},{7,11,190},{8,11,28},{8,11,141},{8,11,444},{8,11,811},{9,11 +,468},{11,11,334},{12,11,24},{12,11,386},{140,11,576},{133,11,757},{5,0,18},{6,0 +,526},{13,0,24},{13,0,110},{19,0,5},{147,0,44},{6,0,506},{134,11,506},{135,11, +1553},{4,0,309},{5,0,462},{7,0,970},{7,0,1097},{7,11,1385},{11,11,582},{11,11, +650},{11,11,901},{11,11,949},{12,11,232},{12,11,236},{13,11,413},{13,11,501},{18 +,11,116},{22,0,30},{150,0,33},{5,10,222},{9,0,140},{138,10,534},{6,0,1056},{137, +10,906},{134,0,1704},{138,10,503},{134,0,1036},{5,10,154},{7,10,1491},{10,10,379 +},{138,10,485},{4,11,383},{133,10,716},{134,0,1315},{5,0,86},{7,0,743},{9,0,85}, +{10,0,281},{10,0,432},{11,0,825},{12,0,251},{13,0,118},{142,0,378},{4,10,91},{5, +10,388},{5,10,845},{6,10,206},{6,10,252},{6,10,365},{7,10,136},{7,10,531},{8,0, +264},{136,10,621},{5,0,524},{133,0,744},{5,11,277},{141,11,247},{132,11,435},{10 +,0,107},{140,0,436},{132,0,927},{10,0,123},{12,0,670},{146,0,94},{7,0,1149},{9,0 +,156},{138,0,957},{5,11,265},{6,11,212},{135,11,28},{133,0,778},{133,0,502},{8,0 +,196},{10,0,283},{139,0,406},{135,10,576},{136,11,535},{134,0,1312},{5,10,771},{ +5,10,863},{5,10,898},{6,10,1632},{6,10,1644},{134,10,1780},{5,0,855},{5,10,331}, +{135,11,1487},{132,11,702},{5,11,808},{135,11,2045},{7,0,1400},{9,0,446},{138,0, +45},{140,10,632},{132,0,1003},{5,11,166},{8,11,739},{140,11,511},{5,10,107},{7, +10,201},{136,10,518},{6,10,446},{135,10,1817},{134,0,1532},{134,0,1097},{4,11, +119},{5,11,170},{5,11,447},{7,11,1708},{7,11,1889},{9,11,357},{9,11,719},{12,11, +486},{140,11,596},{9,10,851},{141,10,510},{4,10,504},{7,0,612},{8,0,545},{8,0, +568},{8,0,642},{9,0,717},{10,0,541},{10,0,763},{11,0,449},{12,0,489},{13,0,153}, +{13,0,296},{14,0,138},{14,0,392},{15,0,50},{16,0,6},{16,0,12},{148,0,9},{4,11, +450},{135,11,1158},{5,10,883},{5,10,975},{8,10,392},{11,0,54},{13,0,173},{13,0, +294},{148,10,7},{13,0,455},{15,0,99},{15,0,129},{144,0,68},{135,0,172},{132,11, +754},{5,10,922},{134,10,1707},{134,0,1029},{17,11,39},{148,11,36},{4,0,568},{5, +10,993},{7,10,515},{137,10,91},{132,0,732},{10,0,617},{138,11,617},{134,0,974},{ +7,0,989},{10,0,377},{12,0,363},{13,0,68},{13,0,94},{14,0,108},{142,0,306},{136,0 +,733},{132,0,428},{7,0,1789},{135,11,1062},{7,0,2015},{140,0,665},{135,10,1433}, +{5,0,287},{7,10,921},{8,10,580},{8,10,593},{8,10,630},{138,10,28},{138,0,806},{4 +,10,911},{5,10,867},{5,10,1013},{7,10,2034},{8,10,798},{136,10,813},{134,0,1539} +,{8,11,523},{150,11,34},{135,11,740},{7,11,238},{7,11,2033},{8,11,120},{8,11,188 +},{8,11,659},{9,11,598},{10,11,466},{12,11,342},{12,11,588},{13,11,503},{14,11, +246},{143,11,92},{7,0,1563},{141,0,182},{5,10,135},{6,10,519},{7,10,1722},{10,10 +,271},{11,10,261},{145,10,54},{14,10,338},{148,10,81},{4,10,300},{5,10,436},{135 +,0,484},{145,11,114},{6,0,1623},{134,0,1681},{133,11,640},{4,11,201},{7,11,1744} +,{8,11,602},{11,11,247},{11,11,826},{145,11,65},{8,11,164},{146,11,62},{6,0,1833 +},{6,0,1861},{136,0,878},{134,0,1569},{8,10,357},{10,10,745},{14,10,426},{17,10, +94},{147,10,57},{12,0,93},{12,0,501},{13,0,362},{14,0,151},{15,0,40},{15,0,59},{ +16,0,46},{17,0,25},{18,0,14},{18,0,134},{19,0,25},{19,0,69},{20,0,16},{20,0,19}, +{20,0,66},{21,0,23},{21,0,25},{150,0,42},{6,0,1748},{8,0,715},{9,0,802},{10,0,46 +},{10,0,819},{13,0,308},{14,0,351},{14,0,363},{146,0,67},{132,0,994},{4,0,63},{ +133,0,347},{132,0,591},{133,0,749},{7,11,1577},{10,11,304},{10,11,549},{11,11, +424},{12,11,365},{13,11,220},{13,11,240},{142,11,33},{133,0,366},{5,10,387},{7,0 +,557},{12,0,547},{142,0,86},{135,0,1747},{132,11,907},{5,11,100},{10,11,329},{12 +,11,416},{149,11,29},{4,10,6},{5,10,708},{136,10,75},{7,10,1351},{9,10,581},{10, +10,639},{11,10,453},{140,10,584},{4,10,303},{135,0,89},{138,10,772},{132,11,176} +,{5,11,636},{5,11,998},{8,11,26},{137,11,358},{7,11,9},{7,11,1508},{9,11,317},{ +10,11,210},{10,11,292},{10,11,533},{11,11,555},{12,11,526},{12,11,607},{13,11, +263},{13,11,459},{142,11,271},{134,0,1463},{6,0,772},{6,0,1137},{139,11,595},{7, +0,977},{139,11,66},{138,0,893},{20,0,48},{148,11,48},{5,0,824},{133,0,941},{134, +11,295},{4,10,106},{7,0,1543},{7,0,1785},{10,0,690},{139,10,717},{7,0,440},{8,0, +230},{139,0,106},{5,10,890},{133,10,988},{6,10,626},{142,10,431},{10,11,127},{ +141,11,27},{10,10,706},{17,0,32},{150,10,44},{132,0,216},{137,0,332},{4,10,698}, +{136,11,119},{139,11,267},{138,10,17},{11,11,526},{11,11,939},{141,11,290},{7,11 +,1167},{11,11,934},{13,11,391},{145,11,76},{139,11,39},{134,10,84},{4,0,914},{5, +0,800},{133,0,852},{10,0,416},{141,0,115},{7,0,564},{142,0,168},{4,0,918},{133,0 +,876},{134,0,1764},{152,0,3},{4,0,92},{5,0,274},{7,11,126},{136,11,84},{140,10, +498},{136,11,790},{5,10,986},{6,10,130},{7,10,1582},{8,0,501},{8,10,458},{10,10, +101},{10,10,318},{138,10,823},{6,11,64},{12,11,377},{141,11,309},{5,0,743},{138, +0,851},{4,0,49},{7,0,280},{135,0,1633},{134,0,879},{136,0,47},{7,10,1644},{137, +10,129},{132,0,865},{134,0,1202},{9,11,34},{139,11,484},{135,10,997},{5,0,272},{ +5,0,908},{5,0,942},{8,0,197},{9,0,47},{11,0,538},{139,0,742},{6,11,1700},{7,11, +26},{7,11,293},{7,11,382},{7,11,1026},{7,11,1087},{7,11,2027},{8,11,24},{8,11, +114},{8,11,252},{8,11,727},{8,11,729},{9,11,30},{9,11,199},{9,11,231},{9,11,251} +,{9,11,334},{9,11,361},{9,11,488},{9,11,712},{10,11,55},{10,11,60},{10,11,232},{ +10,11,332},{10,11,384},{10,11,396},{10,11,504},{10,11,542},{10,11,652},{11,11,20 +},{11,11,48},{11,11,207},{11,11,291},{11,11,298},{11,11,342},{11,11,365},{11,11, +394},{11,11,620},{11,11,705},{11,11,1017},{12,11,123},{12,11,340},{12,11,406},{ +12,11,643},{13,11,61},{13,11,269},{13,11,311},{13,11,319},{13,11,486},{14,11,234 +},{15,11,62},{15,11,85},{16,11,71},{18,11,119},{148,11,105},{6,0,1455},{150,11, +37},{135,10,1927},{135,0,1911},{137,0,891},{7,10,1756},{137,10,98},{7,10,1046},{ +139,10,160},{132,0,761},{6,11,379},{7,11,270},{7,11,1116},{8,11,176},{8,11,183}, +{9,11,432},{9,11,661},{12,11,247},{12,11,617},{146,11,125},{6,10,45},{7,10,433}, +{8,10,129},{9,10,21},{10,10,392},{11,10,79},{12,10,499},{13,10,199},{141,10,451} +,{4,0,407},{5,11,792},{133,11,900},{132,0,560},{135,0,183},{7,10,558},{8,10,353} +,{141,0,490},{4,0,475},{5,10,785},{6,0,731},{11,0,35},{13,0,71},{13,0,177},{142, +0,422},{8,10,81},{9,10,189},{9,10,201},{11,10,478},{11,10,712},{141,10,338},{4,0 +,418},{4,0,819},{133,10,353},{151,10,26},{4,11,901},{133,11,776},{132,0,575},{7, +0,818},{16,0,92},{17,0,14},{17,0,45},{18,0,75},{148,0,18},{6,0,222},{7,0,636},{7 +,0,1620},{8,0,409},{9,0,693},{139,0,77},{6,10,25},{7,10,855},{7,10,1258},{144,10 +,32},{6,0,1880},{6,0,1887},{6,0,1918},{6,0,1924},{9,0,967},{9,0,995},{9,0,1015}, +{12,0,826},{12,0,849},{12,0,857},{12,0,860},{12,0,886},{12,0,932},{18,0,228},{18 +,0,231},{146,0,240},{134,0,633},{134,0,1308},{4,11,37},{5,11,334},{135,11,1253}, +{4,10,4},{7,10,1118},{7,10,1320},{7,10,1706},{8,10,277},{9,10,622},{10,0,86},{11 +,10,724},{12,10,350},{12,10,397},{13,10,28},{13,10,159},{15,10,89},{18,10,5},{19 +,10,9},{20,10,34},{150,10,47},{132,11,508},{137,11,448},{12,11,107},{146,11,31}, +{132,0,817},{134,0,663},{133,0,882},{134,0,914},{132,11,540},{132,11,533},{136, +11,608},{8,0,885},{138,0,865},{132,0,426},{6,0,58},{7,0,745},{7,0,1969},{8,0,399 +},{8,0,675},{9,0,479},{9,0,731},{10,0,330},{10,0,593},{10,0,817},{11,0,32},{11,0 +,133},{11,0,221},{145,0,68},{134,10,255},{7,0,102},{137,0,538},{137,10,216},{7, +11,253},{136,11,549},{135,11,912},{9,10,183},{139,10,286},{11,10,956},{151,10,3} +,{8,11,527},{18,11,60},{147,11,24},{4,10,536},{7,10,1141},{10,10,723},{139,10, +371},{133,11,920},{7,0,876},{135,10,285},{135,10,560},{132,10,690},{142,11,126}, +{11,10,33},{12,10,571},{149,10,1},{133,0,566},{4,11,483},{9,0,139},{10,0,399},{ +11,0,469},{12,0,634},{141,0,223},{6,0,48},{135,0,63},{7,10,1862},{12,10,491},{12 +,10,520},{13,10,383},{14,10,244},{146,0,12},{135,11,1665},{132,11,448},{9,11,495 +},{146,11,104},{6,0,114},{7,0,1224},{7,0,1556},{136,0,3},{4,10,190},{133,10,554} +,{5,10,1001},{8,0,576},{137,0,267},{133,10,446},{133,0,933},{139,11,1009},{8,11, +653},{13,11,93},{147,11,14},{6,0,692},{6,0,821},{134,0,1077},{5,11,172},{135,11, +801},{138,0,752},{4,0,375},{134,0,638},{134,0,1011},{140,11,540},{5,11,260},{137 +,0,96},{139,11,587},{135,10,1231},{4,10,304},{12,0,30},{13,0,148},{14,0,87},{14, +0,182},{16,0,42},{148,0,70},{6,0,1398},{7,0,56},{7,0,1989},{8,0,337},{8,0,738},{ +9,0,600},{12,0,37},{13,0,447},{142,0,92},{138,0,666},{5,0,394},{7,0,487},{136,0, +246},{6,10,53},{6,10,199},{7,10,1408},{8,10,32},{8,10,93},{9,0,437},{10,10,397}, +{10,10,629},{11,10,593},{11,10,763},{13,10,326},{145,10,35},{134,10,105},{9,0, +320},{10,0,506},{138,10,794},{7,11,57},{8,11,167},{8,11,375},{9,11,82},{9,11,561 +},{10,11,620},{10,11,770},{11,10,704},{141,10,396},{5,10,114},{5,10,255},{6,0, +1003},{141,10,285},{7,0,866},{135,0,1163},{133,11,531},{132,0,328},{7,10,2035},{ +8,10,19},{9,10,89},{138,10,831},{8,11,194},{136,11,756},{136,0,1000},{5,11,453}, +{134,11,441},{4,0,101},{5,0,833},{7,0,1171},{136,0,744},{133,0,726},{136,10,746} +,{138,0,176},{6,0,9},{6,0,397},{7,0,53},{7,0,1742},{10,0,632},{11,0,828},{140,0, +146},{135,11,22},{145,11,64},{132,0,839},{11,0,417},{12,0,223},{140,0,265},{4,11 +,102},{7,11,815},{7,11,1699},{139,11,964},{5,10,955},{136,10,814},{6,0,1931},{6, +0,2007},{18,0,246},{146,0,247},{8,0,198},{11,0,29},{140,0,534},{135,0,1771},{6,0 +,846},{7,11,1010},{11,11,733},{11,11,759},{12,11,563},{13,11,34},{14,11,101},{18 +,11,45},{146,11,129},{4,0,186},{5,0,157},{8,0,168},{138,0,6},{132,11,899},{133, +10,56},{148,10,100},{133,0,875},{5,0,773},{5,0,991},{6,0,1635},{134,0,1788},{6,0 +,1274},{9,0,477},{141,0,78},{4,0,639},{6,11,52},{7,0,111},{8,0,581},{9,11,104},{ +9,11,559},{10,10,4},{10,10,13},{11,10,638},{12,0,177},{12,11,308},{19,11,87},{ +148,10,57},{132,11,604},{4,11,301},{133,10,738},{133,10,758},{134,0,1747},{7,11, +1440},{11,11,854},{11,11,872},{11,11,921},{12,11,551},{13,11,472},{142,11,367},{ +7,0,1364},{7,0,1907},{141,0,158},{134,0,873},{4,0,404},{4,0,659},{7,0,552},{135, +0,675},{135,10,1112},{139,10,328},{7,11,508},{137,10,133},{133,0,391},{5,10,110} +,{6,10,169},{6,10,1702},{7,10,400},{8,10,538},{9,10,184},{9,10,524},{140,10,218} +,{6,11,310},{7,11,1849},{8,11,72},{8,11,272},{8,11,431},{9,11,12},{9,11,351},{10 +,11,563},{10,11,630},{10,11,810},{11,11,367},{11,11,599},{11,11,686},{140,11,672 +},{5,0,540},{6,0,1697},{136,0,668},{132,0,883},{134,0,78},{6,10,133},{9,10,353}, +{11,10,993},{12,0,628},{146,0,79},{6,11,181},{7,11,537},{8,11,64},{9,11,127},{10 +,11,496},{12,11,510},{141,11,384},{6,10,93},{7,10,1422},{7,10,1851},{8,10,673},{ +9,10,529},{140,10,43},{137,10,371},{134,0,1460},{134,0,962},{4,11,244},{135,11, +233},{9,10,25},{10,10,467},{138,10,559},{4,10,335},{135,10,942},{133,0,460},{135 +,11,334},{134,11,1650},{4,0,199},{139,0,34},{5,10,601},{8,10,39},{10,10,773},{11 +,10,84},{12,10,205},{142,10,1},{133,10,870},{134,0,388},{14,0,474},{148,0,120},{ +133,11,369},{139,0,271},{4,0,511},{4,10,181},{9,0,333},{9,0,379},{10,0,602},{11, +0,441},{11,0,723},{11,0,976},{140,0,357},{134,0,608},{134,10,1652},{9,11,338},{ +150,0,49},{140,0,988},{134,0,617},{5,0,938},{136,0,707},{132,10,97},{5,10,147},{ +6,10,286},{7,10,1362},{141,10,176},{6,0,756},{134,0,1149},{133,11,896},{6,10,375 +},{7,10,169},{7,10,254},{136,10,780},{134,0,1583},{135,10,1447},{139,0,285},{7, +11,1117},{8,11,393},{136,11,539},{135,0,344},{6,0,469},{7,0,1709},{138,0,515},{5 +,10,629},{135,10,1549},{5,11,4},{5,11,810},{6,11,13},{6,11,538},{6,11,1690},{6, +11,1726},{7,11,499},{7,11,1819},{8,11,148},{8,11,696},{8,11,791},{12,11,125},{13 +,11,54},{143,11,9},{135,11,1268},{137,0,404},{132,0,500},{5,0,68},{134,0,383},{ +11,0,216},{139,0,340},{4,11,925},{5,11,803},{8,11,698},{138,11,828},{4,0,337},{6 +,0,353},{7,0,1934},{8,0,488},{137,0,429},{7,0,236},{7,0,1795},{8,0,259},{9,0,135 +},{9,0,177},{9,0,860},{10,0,825},{11,0,115},{11,0,370},{11,0,405},{11,0,604},{12 +,0,10},{12,0,667},{12,0,669},{13,0,76},{14,0,310},{15,0,76},{15,0,147},{148,0,23 +},{4,0,15},{4,0,490},{5,0,22},{6,0,244},{7,0,40},{7,0,200},{7,0,906},{7,0,1199}, +{9,0,616},{10,0,716},{11,0,635},{11,0,801},{140,0,458},{4,10,420},{140,0,756},{ +134,0,1504},{5,11,383},{134,0,757},{6,0,1266},{135,0,1735},{5,0,598},{7,0,791},{ +7,10,1570},{8,0,108},{9,0,123},{140,10,542},{142,11,410},{9,11,660},{138,11,347} +}; diff --git a/packages/runtime/vendor/brotli/c/enc/static_init.c b/packages/runtime/vendor/brotli/c/enc/static_init.c new file mode 100644 index 000000000..c0dff3a3a --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/static_init.c @@ -0,0 +1,59 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "static_init.h" + +#include "../common/platform.h" +#include "../common/static_init.h" + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +#include "../common/dictionary.h" +#include "dictionary_hash.h" +#include "static_dict_lut.h" +#endif + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if (BROTLI_STATIC_INIT != BROTLI_STATIC_INIT_NONE) +static BROTLI_BOOL DoBrotliEncoderStaticInit(void) { + const BrotliDictionary* dict = BrotliGetDictionary(); + BROTLI_BOOL ok = BrotliEncoderInitStaticDictionaryLut( + dict, kStaticDictionaryBuckets, kStaticDictionaryWords); + if (!ok) return BROTLI_FALSE; + ok = BrotliEncoderInitDictionaryHash(dict, kStaticDictionaryHashWords, + kStaticDictionaryHashLengths); + if (!ok) return BROTLI_FALSE; + return BROTLI_TRUE; +} +#endif /* BROTLI_STATIC_INIT_NONE */ + +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_EARLY) +static BROTLI_BOOL kEarlyInitOk; +static __attribute__((constructor)) void BrotliEncoderStaticInitEarly(void) { + kEarlyInitOk = DoBrotliEncoderStaticInit(); +} +#elif (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_LAZY) +static BROTLI_BOOL kLazyInitOk; +void BrotliEncoderLazyStaticInitInner(void) { + kLazyInitOk = DoBrotliEncoderStaticInit(); +} +#endif /* BROTLI_STATIC_INIT_EARLY */ + +BROTLI_BOOL BrotliEncoderEnsureStaticInit(void) { +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_NONE) + return BROTLI_TRUE; +#elif (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_EARLY) + return kEarlyInitOk; +#else + return kLazyInitOk; +#endif +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/static_init.h b/packages/runtime/vendor/brotli/c/enc/static_init.h new file mode 100644 index 000000000..e5cb23916 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/static_init.h @@ -0,0 +1,30 @@ +/* Copyright 2025 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Central point for static initialization. */ + +#ifndef THIRD_PARTY_BROTLI_ENC_STATIC_INIT_H_ +#define THIRD_PARTY_BROTLI_ENC_STATIC_INIT_H_ + +#include "../common/platform.h" +#include "../common/static_init.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#if (BROTLI_STATIC_INIT == BROTLI_STATIC_INIT_LAZY) +BROTLI_INTERNAL void BrotliEncoderLazyStaticInitInner(void); +BROTLI_INTERNAL void BrotliEncoderLazyStaticInit(void); +#endif /* BROTLI_STATIC_INIT */ + +BROTLI_INTERNAL BROTLI_BOOL BrotliEncoderEnsureStaticInit(void); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif // THIRD_PARTY_BROTLI_ENC_STATIC_INIT_H_ diff --git a/packages/runtime/vendor/brotli/c/enc/utf8_util.c b/packages/runtime/vendor/brotli/c/enc/utf8_util.c new file mode 100644 index 000000000..d46c3644e --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/utf8_util.c @@ -0,0 +1,85 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Heuristics for deciding about the UTF8-ness of strings. */ + +#include "utf8_util.h" + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static size_t BrotliParseAsUTF8( + int* symbol, const uint8_t* input, size_t size) { + /* ASCII */ + if ((input[0] & 0x80) == 0) { + *symbol = input[0]; + if (*symbol > 0) { + return 1; + } + } + /* 2-byte UTF8 */ + if (size > 1u && + (input[0] & 0xE0) == 0xC0 && + (input[1] & 0xC0) == 0x80) { + *symbol = (((input[0] & 0x1F) << 6) | + (input[1] & 0x3F)); + if (*symbol > 0x7F) { + return 2; + } + } + /* 3-byte UFT8 */ + if (size > 2u && + (input[0] & 0xF0) == 0xE0 && + (input[1] & 0xC0) == 0x80 && + (input[2] & 0xC0) == 0x80) { + *symbol = (((input[0] & 0x0F) << 12) | + ((input[1] & 0x3F) << 6) | + (input[2] & 0x3F)); + if (*symbol > 0x7FF) { + return 3; + } + } + /* 4-byte UFT8 */ + if (size > 3u && + (input[0] & 0xF8) == 0xF0 && + (input[1] & 0xC0) == 0x80 && + (input[2] & 0xC0) == 0x80 && + (input[3] & 0xC0) == 0x80) { + *symbol = (((input[0] & 0x07) << 18) | + ((input[1] & 0x3F) << 12) | + ((input[2] & 0x3F) << 6) | + (input[3] & 0x3F)); + if (*symbol > 0xFFFF && *symbol <= 0x10FFFF) { + return 4; + } + } + /* Not UTF8, emit a special symbol above the UTF8-code space */ + *symbol = 0x110000 | input[0]; + return 1; +} + +/* Returns 1 if at least min_fraction of the data is UTF8-encoded.*/ +BROTLI_BOOL BrotliIsMostlyUTF8( + const uint8_t* data, const size_t pos, const size_t mask, + const size_t length, const double min_fraction) { + size_t size_utf8 = 0; + size_t i = 0; + while (i < length) { + int symbol; + size_t bytes_read = + BrotliParseAsUTF8(&symbol, &data[(pos + i) & mask], length - i); + i += bytes_read; + if (symbol < 0x110000) size_utf8 += bytes_read; + } + return TO_BROTLI_BOOL((double)size_utf8 > min_fraction * (double)length); +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif diff --git a/packages/runtime/vendor/brotli/c/enc/utf8_util.h b/packages/runtime/vendor/brotli/c/enc/utf8_util.h new file mode 100644 index 000000000..cae3dba7a --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/utf8_util.h @@ -0,0 +1,31 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Heuristics for deciding about the UTF8-ness of strings. */ + +#ifndef BROTLI_ENC_UTF8_UTIL_H_ +#define BROTLI_ENC_UTF8_UTIL_H_ + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static const double kMinUTF8Ratio = 0.75; + +/* Returns 1 if at least min_fraction of the bytes between pos and + pos + length in the (data, mask) ring-buffer is UTF8-encoded, otherwise + returns 0. */ +BROTLI_INTERNAL BROTLI_BOOL BrotliIsMostlyUTF8( + const uint8_t* data, const size_t pos, const size_t mask, + const size_t length, const double min_fraction); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_UTF8_UTIL_H_ */ diff --git a/packages/runtime/vendor/brotli/c/enc/write_bits.h b/packages/runtime/vendor/brotli/c/enc/write_bits.h new file mode 100644 index 000000000..0cdc3372b --- /dev/null +++ b/packages/runtime/vendor/brotli/c/enc/write_bits.h @@ -0,0 +1,86 @@ +/* Copyright 2010 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Write bits into a byte array. */ + +#ifndef BROTLI_ENC_WRITE_BITS_H_ +#define BROTLI_ENC_WRITE_BITS_H_ + +#include "../common/platform.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* This function writes bits into bytes in increasing addresses, and within + a byte least-significant-bit first. + + The function can write up to 56 bits in one go with WriteBits + Example: let's assume that 3 bits (Rs below) have been written already: + + BYTE-0 BYTE+1 BYTE+2 + + 0000 0RRR 0000 0000 0000 0000 + + Now, we could write 5 or less bits in MSB by just shifting by 3 + and OR'ing to BYTE-0. + + For n bits, we take the last 5 bits, OR that with high bits in BYTE-0, + and locate the rest in BYTE+1, BYTE+2, etc. */ +static BROTLI_INLINE void BrotliWriteBits(size_t n_bits, + uint64_t bits, + size_t* BROTLI_RESTRICT pos, + uint8_t* BROTLI_RESTRICT array) { + BROTLI_LOG(("WriteBits %2d 0x%08x%08x %10d\n", (int)n_bits, + (uint32_t)(bits >> 32), (uint32_t)(bits & 0xFFFFFFFF), + (int)*pos)); + BROTLI_DCHECK((bits >> n_bits) == 0); + BROTLI_DCHECK(n_bits <= 56); +#if defined(BROTLI_LITTLE_ENDIAN) + /* This branch of the code can write up to 56 bits at a time, + 7 bits are lost by being perhaps already in *p and at least + 1 bit is needed to initialize the bit-stream ahead (i.e. if 7 + bits are in *p and we write 57 bits, then the next write will + access a byte that was never initialized). */ + { + uint8_t* p = &array[*pos >> 3]; + uint64_t v = (uint64_t)(*p); /* Zero-extend 8 to 64 bits. */ + v |= bits << (*pos & 7); + BROTLI_UNALIGNED_STORE64LE(p, v); /* Set some bits. */ + *pos += n_bits; + } +#else + /* implicit & 0xFF is assumed for uint8_t arithmetics */ + { + uint8_t* array_pos = &array[*pos >> 3]; + const size_t bits_reserved_in_first_byte = (*pos & 7); + size_t bits_left_to_write; + bits <<= bits_reserved_in_first_byte; + *array_pos++ |= (uint8_t)bits; + for (bits_left_to_write = n_bits + bits_reserved_in_first_byte; + bits_left_to_write >= 9; + bits_left_to_write -= 8) { + bits >>= 8; + *array_pos++ = (uint8_t)bits; + } + *array_pos = 0; + *pos += n_bits; + } +#endif +} + +static BROTLI_INLINE void BrotliWriteBitsPrepareStorage( + size_t pos, uint8_t* array) { + BROTLI_LOG(("WriteBitsPrepareStorage %10d\n", (int)pos)); + BROTLI_DCHECK((pos & 7) == 0); + array[pos >> 3] = 0; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_WRITE_BITS_H_ */ diff --git a/packages/runtime/vendor/brotli/c/include/brotli/decode.h b/packages/runtime/vendor/brotli/c/include/brotli/decode.h new file mode 100644 index 000000000..8d436db73 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/include/brotli/decode.h @@ -0,0 +1,409 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/** + * @file + * API for Brotli decompression. + */ + +#ifndef BROTLI_DEC_DECODE_H_ +#define BROTLI_DEC_DECODE_H_ + +#include +#include +#include /* IWYU pragma: export */ + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/** + * Opaque structure that holds decoder state. + * + * Allocated and initialized with ::BrotliDecoderCreateInstance. + * Cleaned up and deallocated with ::BrotliDecoderDestroyInstance. + */ +typedef struct BrotliDecoderStateStruct BrotliDecoderState; + +/** + * Result type for ::BrotliDecoderDecompress and + * ::BrotliDecoderDecompressStream functions. + */ +typedef enum { + /** Decoding error, e.g. corrupted input or memory allocation problem. */ + BROTLI_DECODER_RESULT_ERROR = 0, + /** Decoding successfully completed. */ + BROTLI_DECODER_RESULT_SUCCESS = 1, + /** Partially done; should be called again with more input. */ + BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT = 2, + /** Partially done; should be called again with more output. */ + BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3 +} BrotliDecoderResult; + +/** + * Template that evaluates items of ::BrotliDecoderErrorCode. + * + * Example: @code {.cpp} + * // Log Brotli error code. + * switch (brotliDecoderErrorCode) { + * #define CASE_(PREFIX, NAME, CODE) \ + * case BROTLI_DECODER ## PREFIX ## NAME: \ + * LOG(INFO) << "error code:" << #NAME; \ + * break; + * #define NEWLINE_ + * BROTLI_DECODER_ERROR_CODES_LIST(CASE_, NEWLINE_) + * #undef CASE_ + * #undef NEWLINE_ + * default: LOG(FATAL) << "unknown brotli error code"; + * } + * @endcode + */ +#define BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \ + BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR \ + /* Same as BrotliDecoderResult values */ \ + BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR \ + BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR \ + BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR \ + \ + /* Errors caused by invalid input */ \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_FORMAT_, DISTANCE, -16) SEPARATOR \ + \ + /* -17 code is reserved */ \ + \ + BROTLI_ERROR_CODE(_ERROR_, COMPOUND_DICTIONARY, -18) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR \ + \ + /* Memory allocation problems */ \ + BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR \ + /* Literal, insert and distance trees together */ \ + BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR \ + /* -23..-24 codes are reserved for distinct tree groups */ \ + BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR \ + BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR \ + /* -28..-29 codes are reserved for dynamic ring-buffer allocation */ \ + BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR \ + \ + /* "Impossible" states */ \ + BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31) + +/** + * Error code for detailed logging / production debugging. + * + * See ::BrotliDecoderGetErrorCode and ::BROTLI_LAST_ERROR_CODE. + */ +typedef enum { +#define BROTLI_COMMA_ , +#define BROTLI_ERROR_CODE_ENUM_ITEM_(PREFIX, NAME, CODE) \ + BROTLI_DECODER ## PREFIX ## NAME = CODE + BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE_ENUM_ITEM_, BROTLI_COMMA_) +} BrotliDecoderErrorCode; +#undef BROTLI_ERROR_CODE_ENUM_ITEM_ +#undef BROTLI_COMMA_ + +/** + * The value of the last error code, negative integer. + * + * All other error code values are in the range from ::BROTLI_LAST_ERROR_CODE + * to @c -1. There are also 4 other possible non-error codes @c 0 .. @c 3 in + * ::BrotliDecoderErrorCode enumeration. + */ +#define BROTLI_LAST_ERROR_CODE BROTLI_DECODER_ERROR_UNREACHABLE + +/** Options to be used with ::BrotliDecoderSetParameter. */ +typedef enum BrotliDecoderParameter { + /** + * Disable "canny" ring buffer allocation strategy. + * + * Ring buffer is allocated according to window size, despite the real size of + * the content. + */ + BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION = 0, + /** + * Flag that determines if "Large Window Brotli" is used. + */ + BROTLI_DECODER_PARAM_LARGE_WINDOW = 1 +} BrotliDecoderParameter; + +/** + * Sets the specified parameter to the given decoder instance. + * + * @param state decoder instance + * @param param parameter to set + * @param value new parameter value + * @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid + * @returns ::BROTLI_TRUE if value is accepted + */ +BROTLI_DEC_API BROTLI_BOOL BrotliDecoderSetParameter( + BrotliDecoderState* state, BrotliDecoderParameter param, uint32_t value); + +/** + * Adds LZ77 prefix dictionary, adds or replaces built-in static dictionary and + * transforms. + * + * Attached dictionary ownership is not transferred. + * Data provided to this method should be kept accessible until + * decoding is finished and decoder instance is destroyed. + * + * @note Dictionaries can NOT be attached after actual decoding is started. + * + * @param state decoder instance + * @param type dictionary data format + * @param data_size length of memory region pointed by @p data + * @param data dictionary data in format corresponding to @p type + * @returns ::BROTLI_FALSE if dictionary is corrupted, + * or dictionary count limit is reached + * @returns ::BROTLI_TRUE if dictionary is accepted / attached + */ +BROTLI_DEC_API BROTLI_BOOL BrotliDecoderAttachDictionary( + BrotliDecoderState* state, BrotliSharedDictionaryType type, + size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]); + +/** + * Creates an instance of ::BrotliDecoderState and initializes it. + * + * The instance can be used once for decoding and should then be destroyed with + * ::BrotliDecoderDestroyInstance, it cannot be reused for a new decoding + * session. + * + * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the + * case they are both zero, default memory allocators are used. @p opaque is + * passed to @p alloc_func and @p free_func when they are called. @p free_func + * has to return without doing anything when asked to free a NULL pointer. + * + * @param alloc_func custom memory allocation function + * @param free_func custom memory free function + * @param opaque custom memory manager handle + * @returns @c 0 if instance can not be allocated or initialized + * @returns pointer to initialized ::BrotliDecoderState otherwise + */ +BROTLI_DEC_API BrotliDecoderState* BrotliDecoderCreateInstance( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); + +/** + * Deinitializes and frees ::BrotliDecoderState instance. + * + * @param state decoder instance to be cleaned up and deallocated + */ +BROTLI_DEC_API void BrotliDecoderDestroyInstance(BrotliDecoderState* state); + +/** + * Performs one-shot memory-to-memory decompression. + * + * Decompresses the data in @p encoded_buffer into @p decoded_buffer, and sets + * @p *decoded_size to the decompressed length. + * + * @param encoded_size size of @p encoded_buffer + * @param encoded_buffer compressed data buffer with at least @p encoded_size + * addressable bytes + * @param[in, out] decoded_size @b in: size of @p decoded_buffer; \n + * @b out: length of decompressed data written to + * @p decoded_buffer + * @param decoded_buffer decompressed data destination buffer + * @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory + * allocation failed, or @p decoded_buffer is not large enough; + * @returns ::BROTLI_DECODER_RESULT_SUCCESS otherwise + */ +BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompress( + size_t encoded_size, + const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], + size_t* decoded_size, + uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]); + +/** + * Decompresses the input stream to the output stream. + * + * The values @p *available_in and @p *available_out must specify the number of + * bytes addressable at @p *next_in and @p *next_out respectively. + * When @p *available_out is @c 0, @p next_out is allowed to be @c NULL. + * + * After each call, @p *available_in will be decremented by the amount of input + * bytes consumed, and the @p *next_in pointer will be incremented by that + * amount. Similarly, @p *available_out will be decremented by the amount of + * output bytes written, and the @p *next_out pointer will be incremented by + * that amount. + * + * @p total_out, if it is not a null-pointer, will be set to the number + * of bytes decompressed since the last @p state initialization. + * + * @note Input is never overconsumed, so @p next_in and @p available_in could be + * passed to the next consumer after decoding is complete. + * + * @param state decoder instance + * @param[in, out] available_in @b in: amount of available input; \n + * @b out: amount of unused input + * @param[in, out] next_in pointer to the next compressed byte + * @param[in, out] available_out @b in: length of output buffer; \n + * @b out: remaining size of output buffer + * @param[in, out] next_out output buffer cursor; + * can be @c NULL if @p available_out is @c 0 + * @param[out] total_out number of bytes decompressed so far; can be @c NULL + * @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory + * allocation failed, arguments were invalid, etc.; + * use ::BrotliDecoderGetErrorCode to get detailed error code + * @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT decoding is blocked until + * more input data is provided + * @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT decoding is blocked until + * more output space is provided + * @returns ::BROTLI_DECODER_RESULT_SUCCESS decoding is finished, no more + * input might be consumed and no more output will be produced + */ +BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompressStream( + BrotliDecoderState* state, size_t* available_in, const uint8_t** next_in, + size_t* available_out, uint8_t** next_out, size_t* total_out); + +/** + * Checks if decoder has more output. + * + * @param state decoder instance + * @returns ::BROTLI_TRUE, if decoder has some unconsumed output + * @returns ::BROTLI_FALSE otherwise + */ +BROTLI_DEC_API BROTLI_BOOL BrotliDecoderHasMoreOutput( + const BrotliDecoderState* state); + +/** + * Acquires pointer to internal output buffer. + * + * This method is used to make language bindings easier and more efficient: + * -# push data to ::BrotliDecoderDecompressStream, + * until ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT is reported + * -# use ::BrotliDecoderTakeOutput to peek bytes and copy to language-specific + * entity + * + * Also this could be useful if there is an output stream that is able to + * consume all the provided data (e.g. when data is saved to file system). + * + * @attention After every call to ::BrotliDecoderTakeOutput @p *size bytes of + * output are considered consumed for all consecutive calls to the + * instance methods; returned pointer becomes invalidated as well. + * + * @note Decoder output is not guaranteed to be contiguous. This means that + * after the size-unrestricted call to ::BrotliDecoderTakeOutput, + * immediate next call to ::BrotliDecoderTakeOutput may return more data. + * + * @param state decoder instance + * @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if + * any amount could be handled; \n + * @b out: amount of data pointed by returned pointer and + * considered consumed; \n + * out value is never greater than in value, unless it is @c 0 + * @returns pointer to output data + */ +BROTLI_DEC_API const uint8_t* BrotliDecoderTakeOutput( + BrotliDecoderState* state, size_t* size); + +/** + * Checks if instance has already consumed input. + * + * Instance that returns ::BROTLI_FALSE is considered "fresh" and could be + * reused. + * + * @param state decoder instance + * @returns ::BROTLI_TRUE if decoder has already used some input bytes + * @returns ::BROTLI_FALSE otherwise + */ +BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* state); + +/** + * Checks if decoder instance reached the final state. + * + * @param state decoder instance + * @returns ::BROTLI_TRUE if decoder is in a state where it reached the end of + * the input and produced all of the output + * @returns ::BROTLI_FALSE otherwise + */ +BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsFinished( + const BrotliDecoderState* state); + +/** + * Acquires a detailed error code. + * + * Should be used only after ::BrotliDecoderDecompressStream returns + * ::BROTLI_DECODER_RESULT_ERROR. + * + * See also ::BrotliDecoderErrorString + * + * @param state decoder instance + * @returns last saved error code + */ +BROTLI_DEC_API BrotliDecoderErrorCode BrotliDecoderGetErrorCode( + const BrotliDecoderState* state); + +/** + * Converts error code to a c-string. + */ +BROTLI_DEC_API const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c); + +/** + * Gets a decoder library version. + * + * Look at BROTLI_MAKE_HEX_VERSION for more information. + */ +BROTLI_DEC_API uint32_t BrotliDecoderVersion(void); + +/** + * Callback to fire on metadata block start. + * + * After this callback is fired, if @p size is not @c 0, it is followed by + * ::brotli_decoder_metadata_chunk_func as more metadata block contents become + * accessible. + * + * @param opaque callback handle + * @param size size of metadata block + */ +typedef void (*brotli_decoder_metadata_start_func)(void* opaque, size_t size); + +/** + * Callback to fire on metadata block chunk becomes available. + * + * This function can be invoked multiple times per metadata block; block should + * be considered finished when sum of @p size matches the announced metadata + * block size. Chunks contents pointed by @p data are transient and shouln not + * be accessed after leaving the callback. + * + * @param opaque callback handle + * @param data pointer to metadata contents + * @param size size of metadata block chunk, at least @c 1 + */ +typedef void (*brotli_decoder_metadata_chunk_func)(void* opaque, + const uint8_t* data, + size_t size); + +/** + * Sets callback for receiving metadata blocks. + * + * @param state decoder instance + * @param start_func callback on metadata block start + * @param chunk_func callback on metadata block chunk + * @param opaque callback handle + */ +BROTLI_DEC_API void BrotliDecoderSetMetadataCallbacks( + BrotliDecoderState* state, + brotli_decoder_metadata_start_func start_func, + brotli_decoder_metadata_chunk_func chunk_func, void* opaque); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_DEC_DECODE_H_ */ diff --git a/packages/runtime/vendor/brotli/c/include/brotli/encode.h b/packages/runtime/vendor/brotli/c/include/brotli/encode.h new file mode 100644 index 000000000..ac26ea8e6 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/include/brotli/encode.h @@ -0,0 +1,505 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/** + * @file + * API for Brotli compression. + */ + +#ifndef BROTLI_ENC_ENCODE_H_ +#define BROTLI_ENC_ENCODE_H_ + +#include +#include +#include /* IWYU pragma: export */ + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/** Minimal value for ::BROTLI_PARAM_LGWIN parameter. */ +#define BROTLI_MIN_WINDOW_BITS 10 +/** + * Maximal value for ::BROTLI_PARAM_LGWIN parameter. + * + * @note equal to @c BROTLI_MAX_DISTANCE_BITS constant. + */ +#define BROTLI_MAX_WINDOW_BITS 24 +/** + * Maximal value for ::BROTLI_PARAM_LGWIN parameter + * in "Large Window Brotli" (32-bit). + */ +#define BROTLI_LARGE_MAX_WINDOW_BITS 30 +/** Minimal value for ::BROTLI_PARAM_LGBLOCK parameter. */ +#define BROTLI_MIN_INPUT_BLOCK_BITS 16 +/** Maximal value for ::BROTLI_PARAM_LGBLOCK parameter. */ +#define BROTLI_MAX_INPUT_BLOCK_BITS 24 +/** Minimal value for ::BROTLI_PARAM_QUALITY parameter. */ +#define BROTLI_MIN_QUALITY 0 +/** Maximal value for ::BROTLI_PARAM_QUALITY parameter. */ +#define BROTLI_MAX_QUALITY 11 + +/** Options for ::BROTLI_PARAM_MODE parameter. */ +typedef enum BrotliEncoderMode { + /** + * Default compression mode. + * + * In this mode compressor does not know anything in advance about the + * properties of the input. + */ + BROTLI_MODE_GENERIC = 0, + /** Compression mode for UTF-8 formatted text input. */ + BROTLI_MODE_TEXT = 1, + /** Compression mode used in WOFF 2.0. */ + BROTLI_MODE_FONT = 2 +} BrotliEncoderMode; + +/** Default value for ::BROTLI_PARAM_QUALITY parameter. */ +#define BROTLI_DEFAULT_QUALITY 11 +/** Default value for ::BROTLI_PARAM_LGWIN parameter. */ +#define BROTLI_DEFAULT_WINDOW 22 +/** Default value for ::BROTLI_PARAM_MODE parameter. */ +#define BROTLI_DEFAULT_MODE BROTLI_MODE_GENERIC + +/** Operations that can be performed by streaming encoder. */ +typedef enum BrotliEncoderOperation { + /** + * Process input. + * + * Encoder may postpone producing output, until it has processed enough input. + */ + BROTLI_OPERATION_PROCESS = 0, + /** + * Produce output for all processed input. + * + * Actual flush is performed when input stream is depleted and there is enough + * space in output stream. This means that client should repeat + * ::BROTLI_OPERATION_FLUSH operation until @p available_in becomes @c 0, and + * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired + * via ::BrotliEncoderTakeOutput, then operation should be repeated after + * output buffer is drained. + * + * @warning Until flush is complete, client @b SHOULD @b NOT swap, + * reduce or extend input stream. + * + * When flush is complete, output data will be sufficient for decoder to + * reproduce all the given input. + */ + BROTLI_OPERATION_FLUSH = 1, + /** + * Finalize the stream. + * + * Actual finalization is performed when input stream is depleted and there is + * enough space in output stream. This means that client should repeat + * ::BROTLI_OPERATION_FINISH operation until @p available_in becomes @c 0, and + * ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired + * via ::BrotliEncoderTakeOutput, then operation should be repeated after + * output buffer is drained. + * + * @warning Until finalization is complete, client @b SHOULD @b NOT swap, + * reduce or extend input stream. + * + * Helper function ::BrotliEncoderIsFinished checks if stream is finalized and + * output fully dumped. + * + * Adding more input data to finalized stream is impossible. + */ + BROTLI_OPERATION_FINISH = 2, + /** + * Emit metadata block to stream. + * + * Metadata is opaque to Brotli: neither encoder, nor decoder processes this + * data or relies on it. It may be used to pass some extra information from + * encoder client to decoder client without interfering with main data stream. + * + * @note Encoder may emit empty metadata blocks internally, to pad encoded + * stream to byte boundary. + * + * @warning Until emitting metadata is complete client @b SHOULD @b NOT swap, + * reduce or extend input stream. + * + * @warning The whole content of input buffer is considered to be the content + * of metadata block. Do @b NOT @e append metadata to input stream, + * before it is depleted with other operations. + * + * Stream is soft-flushed before metadata block is emitted. Metadata block + * @b MUST be no longer than than 16MiB. + */ + BROTLI_OPERATION_EMIT_METADATA = 3 +} BrotliEncoderOperation; + +/** Options to be used with ::BrotliEncoderSetParameter. */ +typedef enum BrotliEncoderParameter { + /** + * Tune encoder for specific input. + * + * ::BrotliEncoderMode enumerates all available values. + */ + BROTLI_PARAM_MODE = 0, + /** + * The main compression speed-density lever. + * + * The higher the quality, the slower the compression. Range is + * from ::BROTLI_MIN_QUALITY to ::BROTLI_MAX_QUALITY. + */ + BROTLI_PARAM_QUALITY = 1, + /** + * Recommended sliding LZ77 window size. + * + * Encoder may reduce this value, e.g. if input is much smaller than + * window size. + * + * Window size is `(1 << value) - 16`. + * + * Range is from ::BROTLI_MIN_WINDOW_BITS to ::BROTLI_MAX_WINDOW_BITS. + */ + BROTLI_PARAM_LGWIN = 2, + /** + * Recommended input block size. + * + * Encoder may reduce this value, e.g. if input is much smaller than input + * block size. + * + * Range is from ::BROTLI_MIN_INPUT_BLOCK_BITS to + * ::BROTLI_MAX_INPUT_BLOCK_BITS. + * + * @note Bigger input block size allows better compression, but consumes more + * memory. \n The rough formula of memory used for temporary input + * storage is `3 << lgBlock`. + */ + BROTLI_PARAM_LGBLOCK = 3, + /** + * Flag that affects usage of "literal context modeling" format feature. + * + * This flag is a "decoding-speed vs compression ratio" trade-off. + */ + BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING = 4, + /** + * Estimated total input size for all ::BrotliEncoderCompressStream calls. + * + * The default value is 0, which means that the total input size is unknown. + */ + BROTLI_PARAM_SIZE_HINT = 5, + /** + * Flag that determines if "Large Window Brotli" is used. + */ + BROTLI_PARAM_LARGE_WINDOW = 6, + /** + * Recommended number of postfix bits (NPOSTFIX). + * + * Encoder may change this value. + * + * Range is from 0 to ::BROTLI_MAX_NPOSTFIX. + */ + BROTLI_PARAM_NPOSTFIX = 7, + /** + * Recommended number of direct distance codes (NDIRECT). + * + * Encoder may change this value. + * + * Range is from 0 to (15 << NPOSTFIX) in steps of (1 << NPOSTFIX). + */ + BROTLI_PARAM_NDIRECT = 8, + /** + * Number of bytes of input stream already processed by a different instance. + * + * @note It is important to configure all the encoder instances with same + * parameters (except this one) in order to allow all the encoded parts + * obey the same restrictions implied by header. + * + * If offset is not 0, then stream header is omitted. + * In any case output start is byte aligned, so for proper streams stitching + * "predecessor" stream must be flushed. + * + * Range is not artificially limited, but all the values greater or equal to + * maximal window size have the same effect. Values greater than 2**30 are not + * allowed. + */ + BROTLI_PARAM_STREAM_OFFSET = 9 +} BrotliEncoderParameter; + +/** + * Opaque structure that holds encoder state. + * + * Allocated and initialized with ::BrotliEncoderCreateInstance. + * Cleaned up and deallocated with ::BrotliEncoderDestroyInstance. + */ +typedef struct BrotliEncoderStateStruct BrotliEncoderState; + +/** + * Sets the specified parameter to the given encoder instance. + * + * @param state encoder instance + * @param param parameter to set + * @param value new parameter value + * @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid + * @returns ::BROTLI_FALSE if value of parameter can not be changed at current + * encoder state (e.g. when encoding is started, window size might be + * already encoded and therefore it is impossible to change it) + * @returns ::BROTLI_TRUE if value is accepted + * @warning invalid values might be accepted in case they would not break + * encoding process. + */ +BROTLI_ENC_API BROTLI_BOOL BrotliEncoderSetParameter( + BrotliEncoderState* state, BrotliEncoderParameter param, uint32_t value); + +/** + * Creates an instance of ::BrotliEncoderState and initializes it. + * + * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the + * case they are both zero, default memory allocators are used. @p opaque is + * passed to @p alloc_func and @p free_func when they are called. @p free_func + * has to return without doing anything when asked to free a NULL pointer. + * + * @param alloc_func custom memory allocation function + * @param free_func custom memory free function + * @param opaque custom memory manager handle + * @returns @c 0 if instance can not be allocated or initialized + * @returns pointer to initialized ::BrotliEncoderState otherwise + */ +BROTLI_ENC_API BrotliEncoderState* BrotliEncoderCreateInstance( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); + +/** + * Deinitializes and frees ::BrotliEncoderState instance. + * + * @param state decoder instance to be cleaned up and deallocated + */ +BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state); + +/* Opaque type for pointer to different possible internal structures containing + dictionary prepared for the encoder */ +typedef struct BrotliEncoderPreparedDictionaryStruct + BrotliEncoderPreparedDictionary; + +/** + * Prepares a shared dictionary from the given file format for the encoder. + * + * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the + * case they are both zero, default memory allocators are used. @p opaque is + * passed to @p alloc_func and @p free_func when they are called. @p free_func + * has to return without doing anything when asked to free a NULL pointer. + * + * @warning Created instance is "lean"; it does not contain copy of @p data, + * rather it contains only pointer to it; therefore, + * @p data @b MUST outlive the created instance. + * + * @param type type of dictionary stored in data + * @param data_size size of @p data buffer + * @param data pointer to the dictionary data + * @param quality the maximum Brotli quality to prepare the dictionary for, + * use BROTLI_MAX_QUALITY by default + * @param alloc_func custom memory allocation function + * @param free_func custom memory free function + * @param opaque custom memory manager handle + */ +BROTLI_ENC_API BrotliEncoderPreparedDictionary* +BrotliEncoderPrepareDictionary(BrotliSharedDictionaryType type, + size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)], + int quality, + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); + +BROTLI_ENC_API void BrotliEncoderDestroyPreparedDictionary( + BrotliEncoderPreparedDictionary* dictionary); + +/** + * Attaches a prepared dictionary of any type to the encoder. Can be used + * multiple times to attach multiple dictionaries. The dictionary type was + * determined by BrotliEncoderPrepareDictionary. Multiple raw prefix + * dictionaries and/or max 1 serialized dictionary with custom words can be + * attached. + * + * @returns ::BROTLI_FALSE in case of error + * @returns ::BROTLI_TRUE otherwise + */ +BROTLI_ENC_API BROTLI_BOOL BrotliEncoderAttachPreparedDictionary( + BrotliEncoderState* state, + const BrotliEncoderPreparedDictionary* dictionary); + +/** + * Calculates the output size bound for the given @p input_size. + * + * @warning Result is only valid if quality is at least @c 2 and, in + * case ::BrotliEncoderCompressStream was used, no flushes + * (::BROTLI_OPERATION_FLUSH) were performed. + * + * @param input_size size of projected input + * @returns @c 0 if result does not fit @c size_t + */ +BROTLI_ENC_API size_t BrotliEncoderMaxCompressedSize(size_t input_size); + +/** + * Performs one-shot memory-to-memory compression. + * + * Compresses the data in @p input_buffer into @p encoded_buffer, and sets + * @p *encoded_size to the compressed length. + * + * @note If ::BrotliEncoderMaxCompressedSize(@p input_size) returns non-zero + * value, then output is guaranteed to be no longer than that. + * + * @note If @p lgwin is greater than ::BROTLI_MAX_WINDOW_BITS then resulting + * stream might be incompatible with RFC 7932; to decode such streams, + * decoder should be configured with + * ::BROTLI_DECODER_PARAM_LARGE_WINDOW = @c 1 + * + * @param quality quality parameter value, e.g. ::BROTLI_DEFAULT_QUALITY + * @param lgwin lgwin parameter value, e.g. ::BROTLI_DEFAULT_WINDOW + * @param mode mode parameter value, e.g. ::BROTLI_DEFAULT_MODE + * @param input_size size of @p input_buffer + * @param input_buffer input data buffer with at least @p input_size + * addressable bytes + * @param[in, out] encoded_size @b in: size of @p encoded_buffer; \n + * @b out: length of compressed data written to + * @p encoded_buffer, or @c 0 if compression fails + * @param encoded_buffer compressed data destination buffer + * @returns ::BROTLI_FALSE in case of compression error + * @returns ::BROTLI_FALSE if output buffer is too small + * @returns ::BROTLI_TRUE otherwise + */ +BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompress( + int quality, int lgwin, BrotliEncoderMode mode, size_t input_size, + const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)], + size_t* encoded_size, + uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]); + +/** + * Compresses input stream to output stream. + * + * The values @p *available_in and @p *available_out must specify the number of + * bytes addressable at @p *next_in and @p *next_out respectively. + * When @p *available_out is @c 0, @p next_out is allowed to be @c NULL. + * + * After each call, @p *available_in will be decremented by the amount of input + * bytes consumed, and the @p *next_in pointer will be incremented by that + * amount. Similarly, @p *available_out will be decremented by the amount of + * output bytes written, and the @p *next_out pointer will be incremented by + * that amount. + * + * @p total_out, if it is not a null-pointer, will be set to the number + * of bytes compressed since the last @p state initialization. + * + * + * + * Internally workflow consists of 3 tasks: + * -# (optionally) copy input data to internal buffer + * -# actually compress data and (optionally) store it to internal buffer + * -# (optionally) copy compressed bytes from internal buffer to output stream + * + * Whenever all 3 tasks can't move forward anymore, or error occurs, this + * method returns the control flow to caller. + * + * @p op is used to perform flush, finish the stream, or inject metadata block. + * See ::BrotliEncoderOperation for more information. + * + * Flushing the stream means forcing encoding of all input passed to encoder and + * completing the current output block, so it could be fully decoded by stream + * decoder. To perform flush set @p op to ::BROTLI_OPERATION_FLUSH. + * Under some circumstances (e.g. lack of output stream capacity) this operation + * would require several calls to ::BrotliEncoderCompressStream. The method must + * be called again until both input stream is depleted and encoder has no more + * output (see ::BrotliEncoderHasMoreOutput) after the method is called. + * + * Finishing the stream means encoding of all input passed to encoder and + * adding specific "final" marks, so stream decoder could determine that stream + * is complete. To perform finish set @p op to ::BROTLI_OPERATION_FINISH. + * Under some circumstances (e.g. lack of output stream capacity) this operation + * would require several calls to ::BrotliEncoderCompressStream. The method must + * be called again until both input stream is depleted and encoder has no more + * output (see ::BrotliEncoderHasMoreOutput) after the method is called. + * + * @warning When flushing and finishing, @p op should not change until operation + * is complete; input stream should not be swapped, reduced or + * extended as well. + * + * @param state encoder instance + * @param op requested operation + * @param[in, out] available_in @b in: amount of available input; \n + * @b out: amount of unused input + * @param[in, out] next_in pointer to the next input byte + * @param[in, out] available_out @b in: length of output buffer; \n + * @b out: remaining size of output buffer + * @param[in, out] next_out compressed output buffer cursor; + * can be @c NULL if @p available_out is @c 0 + * @param[out] total_out number of bytes produced so far; can be @c NULL + * @returns ::BROTLI_FALSE if there was an error + * @returns ::BROTLI_TRUE otherwise + */ +BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompressStream( + BrotliEncoderState* state, BrotliEncoderOperation op, size_t* available_in, + const uint8_t** next_in, size_t* available_out, uint8_t** next_out, + size_t* total_out); + +/** + * Checks if encoder instance reached the final state. + * + * @param state encoder instance + * @returns ::BROTLI_TRUE if encoder is in a state where it reached the end of + * the input and produced all of the output + * @returns ::BROTLI_FALSE otherwise + */ +BROTLI_ENC_API BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* state); + +/** + * Checks if encoder has more output. + * + * @param state encoder instance + * @returns ::BROTLI_TRUE, if encoder has some unconsumed output + * @returns ::BROTLI_FALSE otherwise + */ +BROTLI_ENC_API BROTLI_BOOL BrotliEncoderHasMoreOutput( + BrotliEncoderState* state); + +/** + * Acquires pointer to internal output buffer. + * + * This method is used to make language bindings easier and more efficient: + * -# push data to ::BrotliEncoderCompressStream, + * until ::BrotliEncoderHasMoreOutput returns BROTLI_TRUE + * -# use ::BrotliEncoderTakeOutput to peek bytes and copy to language-specific + * entity + * + * Also this could be useful if there is an output stream that is able to + * consume all the provided data (e.g. when data is saved to file system). + * + * @attention After every call to ::BrotliEncoderTakeOutput @p *size bytes of + * output are considered consumed for all consecutive calls to the + * instance methods; returned pointer becomes invalidated as well. + * + * @note Encoder output is not guaranteed to be contiguous. This means that + * after the size-unrestricted call to ::BrotliEncoderTakeOutput, + * immediate next call to ::BrotliEncoderTakeOutput may return more data. + * + * @param state encoder instance + * @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if + * any amount could be handled; \n + * @b out: amount of data pointed by returned pointer and + * considered consumed; \n + * out value is never greater than in value, unless it is @c 0 + * @returns pointer to output data + */ +BROTLI_ENC_API const uint8_t* BrotliEncoderTakeOutput( + BrotliEncoderState* state, size_t* size); + +/* Returns the estimated peak memory usage (in bytes) of the BrotliCompress() + function, not counting the memory needed for the input and output. */ +BROTLI_ENC_EXTRA_API size_t BrotliEncoderEstimatePeakMemoryUsage( + int quality, int lgwin, size_t input_size); +/* Returns 0 if dictionary is not valid; otherwise returns allocation size. */ +BROTLI_ENC_EXTRA_API size_t BrotliEncoderGetPreparedDictionarySize( + const BrotliEncoderPreparedDictionary* dictionary); + +/** + * Gets an encoder library version. + * + * Look at BROTLI_MAKE_HEX_VERSION for more information. + */ +BROTLI_ENC_API uint32_t BrotliEncoderVersion(void); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_ENC_ENCODE_H_ */ diff --git a/packages/runtime/vendor/brotli/c/include/brotli/port.h b/packages/runtime/vendor/brotli/c/include/brotli/port.h new file mode 100644 index 000000000..11cefd791 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/include/brotli/port.h @@ -0,0 +1,302 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* Macros for compiler / platform specific API declarations. */ + +#ifndef BROTLI_COMMON_PORT_H_ +#define BROTLI_COMMON_PORT_H_ + +/* The following macros were borrowed from https://github.com/nemequ/hedley + * with permission of original author - Evan Nemerson */ + +/* >>> >>> >>> hedley macros */ + +#define BROTLI_MAKE_VERSION(major, minor, revision) \ + (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) +#define BROTLI_GNUC_VERSION \ + BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) +#define BROTLI_GNUC_VERSION BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(BROTLI_GNUC_VERSION) +#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) \ + (BROTLI_GNUC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) +#define BROTLI_MSVC_VERSION \ + BROTLI_MAKE_VERSION((_MSC_FULL_VER / 10000000), \ + (_MSC_FULL_VER % 10000000) / 100000, \ + (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) +#define BROTLI_MSVC_VERSION \ + BROTLI_MAKE_VERSION((_MSC_FULL_VER / 1000000), \ + (_MSC_FULL_VER % 1000000) / 10000, \ + (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) +#define BROTLI_MSVC_VERSION \ + BROTLI_MAKE_VERSION(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if !defined(_MSC_VER) +#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else +#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) +#define BROTLI_INTEL_VERSION \ + BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, \ + __INTEL_COMPILER % 100, \ + __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) +#define BROTLI_INTEL_VERSION \ + BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(BROTLI_INTEL_VERSION) +#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) \ + (BROTLI_INTEL_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__PGI) && \ + defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) +#define BROTLI_PGI_VERSION \ + BROTLI_MAKE_VERSION(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(BROTLI_PGI_VERSION) +#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) \ + (BROTLI_PGI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) +#define BROTLI_SUNPRO_VERSION \ + BROTLI_MAKE_VERSION( \ + (((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \ + (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), \ + (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) +#define BROTLI_SUNPRO_VERSION \ + BROTLI_MAKE_VERSION((__SUNPRO_C >> 8) & 0xf, \ + (__SUNPRO_C >> 4) & 0xf, \ + (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) +#define BROTLI_SUNPRO_VERSION \ + BROTLI_MAKE_VERSION( \ + (((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \ + (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), \ + (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) +#define BROTLI_SUNPRO_VERSION \ + BROTLI_MAKE_VERSION((__SUNPRO_CC >> 8) & 0xf, \ + (__SUNPRO_CC >> 4) & 0xf, \ + (__SUNPRO_CC) & 0xf) +#endif + +#if defined(BROTLI_SUNPRO_VERSION) +#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) \ + (BROTLI_SUNPRO_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) +#define BROTLI_ARM_VERSION \ + BROTLI_MAKE_VERSION((__ARMCOMPILER_VERSION / 1000000), \ + (__ARMCOMPILER_VERSION % 1000000) / 10000, \ + (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) +#define BROTLI_ARM_VERSION \ + BROTLI_MAKE_VERSION((__ARMCC_VERSION / 1000000), \ + (__ARMCC_VERSION % 1000000) / 10000, \ + (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(BROTLI_ARM_VERSION) +#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) \ + (BROTLI_ARM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__ibmxl__) +#define BROTLI_IBM_VERSION \ + BROTLI_MAKE_VERSION(__ibmxl_version__, \ + __ibmxl_release__, \ + __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) +#define BROTLI_IBM_VERSION \ + BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) +#define BROTLI_IBM_VERSION BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(BROTLI_IBM_VERSION) +#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) \ + (BROTLI_IBM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__TI_COMPILER_VERSION__) +#define BROTLI_TI_VERSION \ + BROTLI_MAKE_VERSION((__TI_COMPILER_VERSION__ / 1000000), \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(BROTLI_TI_VERSION) +#define BROTLI_TI_VERSION_CHECK(major, minor, patch) \ + (BROTLI_TI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_TI_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__IAR_SYSTEMS_ICC__) +#if __VER__ > 1000 +#define BROTLI_IAR_VERSION \ + BROTLI_MAKE_VERSION((__VER__ / 1000000), \ + (__VER__ / 1000) % 1000, \ + (__VER__ % 1000)) +#else +#define BROTLI_IAR_VERSION BROTLI_MAKE_VERSION(VER / 100, __VER__ % 100, 0) +#endif +#endif + +#if defined(BROTLI_IAR_VERSION) +#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) \ + (BROTLI_IAR_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__TINYC__) +#define BROTLI_TINYC_VERSION \ + BROTLI_MAKE_VERSION(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(BROTLI_TINYC_VERSION) +#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) \ + (BROTLI_TINYC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch)) +#else +#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(__has_attribute) +#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ + __has_attribute(attribute) +#else +#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ + BROTLI_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(__has_builtin) +#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \ + __has_builtin(builtin) +#else +#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \ + BROTLI_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(__has_feature) +#define BROTLI_HAS_FEATURE(feature) __has_feature(feature) +#else +#define BROTLI_HAS_FEATURE(feature) (0) +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +#define BROTLI_PUBLIC +#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \ + BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ + BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ + BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \ + BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + (BROTLI_TI_VERSION_CHECK(7, 3, 0) && \ + defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__)) +#define BROTLI_PUBLIC __attribute__ ((visibility ("default"))) +#else +#define BROTLI_PUBLIC +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +#define BROTLI_INTERNAL +#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \ + BROTLI_TI_VERSION_CHECK(8, 0, 0) || \ + BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \ + BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \ + BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \ + BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + (BROTLI_TI_VERSION_CHECK(7, 3, 0) && \ + defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__)) +#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden"))) +#else +#define BROTLI_INTERNAL +#endif + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && !defined(__cplusplus) && \ + !defined(__PGI) && !defined(__PGIC__) && !defined(__TINYC__) && \ + !defined(__clang__) +#define BROTLI_ARRAY_PARAM(name) (name) +#else +#define BROTLI_ARRAY_PARAM(name) +#endif + +/* <<< <<< <<< end of hedley macros. */ + +#if defined(BROTLI_SHARED_COMPILATION) +#if defined(_WIN32) +#if defined(BROTLICOMMON_SHARED_COMPILATION) +#define BROTLI_COMMON_API __declspec(dllexport) +#else /* !BROTLICOMMON_SHARED_COMPILATION */ +#define BROTLI_COMMON_API __declspec(dllimport) +#endif /* BROTLICOMMON_SHARED_COMPILATION */ +#if defined(BROTLIDEC_SHARED_COMPILATION) +#define BROTLI_DEC_API __declspec(dllexport) +#else /* !BROTLIDEC_SHARED_COMPILATION */ +#define BROTLI_DEC_API __declspec(dllimport) +#endif /* BROTLIDEC_SHARED_COMPILATION */ +#if defined(BROTLIENC_SHARED_COMPILATION) +#define BROTLI_ENC_API __declspec(dllexport) +#else /* !BROTLIENC_SHARED_COMPILATION */ +#define BROTLI_ENC_API __declspec(dllimport) +#endif /* BROTLIENC_SHARED_COMPILATION */ +#else /* !_WIN32 */ +#define BROTLI_COMMON_API BROTLI_PUBLIC +#define BROTLI_DEC_API BROTLI_PUBLIC +#define BROTLI_ENC_API BROTLI_PUBLIC +#endif /* _WIN32 */ +#else /* BROTLI_SHARED_COMPILATION */ +#define BROTLI_COMMON_API +#define BROTLI_DEC_API +#define BROTLI_ENC_API +#endif + +#if defined(BROTLI_BUILD_ENC_EXTRA_API) +#define BROTLI_ENC_EXTRA_API BROTLI_ENC_API +#else +#define BROTLI_ENC_EXTRA_API BROTLI_INTERNAL +#endif + +#endif /* BROTLI_COMMON_PORT_H_ */ diff --git a/packages/runtime/vendor/brotli/c/include/brotli/shared_dictionary.h b/packages/runtime/vendor/brotli/c/include/brotli/shared_dictionary.h new file mode 100644 index 000000000..2970c2dcc --- /dev/null +++ b/packages/runtime/vendor/brotli/c/include/brotli/shared_dictionary.h @@ -0,0 +1,100 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/* (Opaque) Shared Dictionary definition and utilities. */ + +#ifndef BROTLI_COMMON_SHARED_DICTIONARY_H_ +#define BROTLI_COMMON_SHARED_DICTIONARY_H_ + +#include +#include + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH 4 +#define SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH 31 +#define SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS 64 +#define SHARED_BROTLI_MAX_COMPOUND_DICTS 15 + +/** + * Opaque structure that holds shared dictionary data. + * + * Allocated and initialized with ::BrotliSharedDictionaryCreateInstance. + * Cleaned up and deallocated with ::BrotliSharedDictionaryDestroyInstance. + */ +typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionary; + +/** + * Input data type for ::BrotliSharedDictionaryAttach. + */ +typedef enum BrotliSharedDictionaryType { + /** Raw LZ77 prefix dictionary. */ + BROTLI_SHARED_DICTIONARY_RAW = 0, + /** Serialized shared dictionary. + * + * DO NOT USE: methods accepting this value will fail. + */ + BROTLI_SHARED_DICTIONARY_SERIALIZED = 1 +} BrotliSharedDictionaryType; + +/** + * Creates an instance of ::BrotliSharedDictionary. + * + * Fresh instance has default word dictionary and transforms + * and no LZ77 prefix dictionary. + * + * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the + * case they are both zero, default memory allocators are used. @p opaque is + * passed to @p alloc_func and @p free_func when they are called. @p free_func + * has to return without doing anything when asked to free a NULL pointer. + * + * @param alloc_func custom memory allocation function + * @param free_func custom memory free function + * @param opaque custom memory manager handle + * @returns @c 0 if instance can not be allocated or initialized + * @returns pointer to initialized ::BrotliSharedDictionary otherwise + */ +BROTLI_COMMON_API BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); + +/** + * Deinitializes and frees ::BrotliSharedDictionary instance. + * + * @param dict shared dictionary instance to be cleaned up and deallocated + */ +BROTLI_COMMON_API void BrotliSharedDictionaryDestroyInstance( + BrotliSharedDictionary* dict); + +/** + * Attaches dictionary to a given instance of ::BrotliSharedDictionary. + * + * Dictionary to be attached is represented in a serialized format as a region + * of memory. + * + * Provided data it partially referenced by a resulting (compound) dictionary, + * and should be kept untouched, while at least one compound dictionary uses it. + * This way memory overhead is kept minimal by the cost of additional resource + * management. + * + * @param dict dictionary to extend + * @param type type of dictionary to attach + * @param data_size size of @p data + * @param data serialized dictionary of type @p type, with at least @p data_size + * addressable bytes + * @returns ::BROTLI_TRUE if provided dictionary is successfully attached + * @returns ::BROTLI_FALSE otherwise + */ +BROTLI_COMMON_API BROTLI_BOOL BrotliSharedDictionaryAttach( + BrotliSharedDictionary* dict, BrotliSharedDictionaryType type, + size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]); + +#if defined(__cplusplus) || defined(c_plusplus) +} /* extern "C" */ +#endif + +#endif /* BROTLI_COMMON_SHARED_DICTIONARY_H_ */ diff --git a/packages/runtime/vendor/brotli/c/include/brotli/types.h b/packages/runtime/vendor/brotli/c/include/brotli/types.h new file mode 100644 index 000000000..2dc61ff40 --- /dev/null +++ b/packages/runtime/vendor/brotli/c/include/brotli/types.h @@ -0,0 +1,83 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +/** + * @file + * Common types used in decoder and encoder API. + */ + +#ifndef BROTLI_COMMON_TYPES_H_ +#define BROTLI_COMMON_TYPES_H_ + +#include /* IWYU pragma: export */ + +#if defined(_MSC_VER) && (_MSC_VER < 1600) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; +#else +#include /* IWYU pragma: export */ +#endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */ + +/** + * A portable @c bool replacement. + * + * ::BROTLI_BOOL is a "documentation" type: actually it is @c int, but in API it + * denotes a type, whose only values are ::BROTLI_TRUE and ::BROTLI_FALSE. + * + * ::BROTLI_BOOL values passed to Brotli should either be ::BROTLI_TRUE or + * ::BROTLI_FALSE, or be a result of ::TO_BROTLI_BOOL macros. + * + * ::BROTLI_BOOL values returned by Brotli should not be tested for equality + * with @c true, @c false, ::BROTLI_TRUE, ::BROTLI_FALSE, but rather should be + * evaluated, for example: @code{.cpp} + * if (SomeBrotliFunction(encoder, BROTLI_TRUE) && + * !OtherBrotliFunction(decoder, BROTLI_FALSE)) { + * bool x = !!YetAnotherBrotliFunction(encoder, TO_BROLTI_BOOL(2 * 2 == 4)); + * DoSomething(x); + * } + * @endcode + */ +#define BROTLI_BOOL int +/** Portable @c true replacement. */ +#define BROTLI_TRUE 1 +/** Portable @c false replacement. */ +#define BROTLI_FALSE 0 +/** @c bool to ::BROTLI_BOOL conversion macros. */ +#define TO_BROTLI_BOOL(X) (!!(X) ? BROTLI_TRUE : BROTLI_FALSE) + +#define BROTLI_MAKE_UINT64_T(high, low) ((((uint64_t)(high)) << 32) | low) + +#define BROTLI_UINT32_MAX (~((uint32_t)0)) +#define BROTLI_SIZE_MAX (~((size_t)0)) + +/** + * Allocating function pointer type. + * + * @param opaque custom memory manager handle provided by client + * @param size requested memory region size; can not be @c 0 + * @returns @c 0 in the case of failure + * @returns a valid pointer to a memory region of at least @p size bytes + * long otherwise + */ +typedef void* (*brotli_alloc_func)(void* opaque, size_t size); + +/** + * Deallocating function pointer type. + * + * This function @b SHOULD do nothing if @p address is @c 0. + * + * @param opaque custom memory manager handle provided by client + * @param address memory region pointer returned by ::brotli_alloc_func, or @c 0 + */ +typedef void (*brotli_free_func)(void* opaque, void* address); + +#endif /* BROTLI_COMMON_TYPES_H_ */ diff --git a/packages/threads/package.json b/packages/threads/package.json index 11b8ef624..c29967cc6 100644 --- a/packages/threads/package.json +++ b/packages/threads/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/threads", - "version": "0.4.2", + "version": "0.4.4", "description": "Zero-copy publication of immutable object graphs to worker threads (scriptc), with a deep-freezing Node.js implementation", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/packages/types/package.json b/packages/types/package.json index 689c7b72f..db88b4b2f 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@scriptc/types", - "version": "0.4.2", + "version": "0.4.4", "description": "Contract types for scriptc: type-only annotations (Int32, NoThrow, Immutable) that scriptc checks and optimizes, with no runtime semantics", "license": "Apache-2.0", "homepage": "https://scriptc.dev", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 525994ba1..5fc32620a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -110,8 +110,8 @@ importers: specifier: workspace:* version: link:../runtime tsc-ts: - specifier: 0.0.8 - version: 0.0.8 + specifier: 0.0.9 + version: 0.0.9 typescript: specifier: 7.0.2 version: 7.0.2 @@ -1377,42 +1377,42 @@ packages: '@ts-morph/common@0.11.1': resolution: {integrity: sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==} - '@tsc-ts/darwin-arm64@0.0.8': - resolution: {integrity: sha512-+hB9yWk58lwZHGrVEzqEC4D7MQW7Vd2z7blV3TULIKZPTMwdf8aT7XFAmfP48PoGff0H9yuQKyRXJl9I+sQv9w==} + '@tsc-ts/darwin-arm64@0.0.9': + resolution: {integrity: sha512-aIjSulef3DIEX7F3RRWLXyHWFgjXiVffBkrfE83ay/tpbsQaniu16cFxYGYNaqqVGft/oivecpdTDBbpPlsb8Q==} cpu: [arm64] os: [darwin] - '@tsc-ts/darwin-x64@0.0.8': - resolution: {integrity: sha512-yz4UddicqMXe+T7nV7ftpf155lkiK6gIPNwSB8rH9dlwiV6a17CO/vpSk627QLMqnfGNicB6YwLMk/SJ9moPdw==} + '@tsc-ts/darwin-x64@0.0.9': + resolution: {integrity: sha512-LHE74tzIEDtrEN8ENvCH/ejnnZjT0D2Vaz2HmggcvJudgr0W300CcwGf2Kdfw8MVBBk6JN0HPF9+a5AUUtFZGg==} cpu: [x64] os: [darwin] - '@tsc-ts/linux-arm64-gnu@0.0.8': - resolution: {integrity: sha512-LHqfT9mQ4tusPdWmnanCvC8/YBOF5bHM5h5863a3t2HTgFVdps1Qwb0yzrW5DDbojIhQ0Z+GupxtXVUQcV0Q/Q==} + '@tsc-ts/linux-arm64-gnu@0.0.9': + resolution: {integrity: sha512-dNiQIKApLcKIJPzb3pk2gfGrrgmOWa+q3/qjrx5ia/SzIWz0+lENlVvHiIn9ukqDacDE30FW8xHzby8Kyd+10A==} cpu: [arm64] os: [linux] libc: [glibc] - '@tsc-ts/linux-arm64-musl@0.0.8': - resolution: {integrity: sha512-I0CC3xZMwoQM6OiVsubRHb0VI/RrJpneb33kDQhadYHq3taPos71KwP+CbCXNu88rNsDsyn7D1oCVfJb73g4Mw==} + '@tsc-ts/linux-arm64-musl@0.0.9': + resolution: {integrity: sha512-DolAqHtr1GUyG03Ch8+sJzpX1G+xzazAmj18kABzuOPre/6njrSWnmqJmtXQWvhwLBBEjajIkUJ4V+t5qigsrg==} cpu: [arm64] os: [linux] libc: [musl] - '@tsc-ts/linux-x64-gnu@0.0.8': - resolution: {integrity: sha512-PLERuwUuxT92x0BZAMQubiQP5jhlqoLmswaZWqcvfKAZyBp0TbWiVOmuNnDbJozvBaNUrfdGPb9suiQ+Me7M6A==} + '@tsc-ts/linux-x64-gnu@0.0.9': + resolution: {integrity: sha512-leANVZ4tKz3myBSVogb173ttbAurPOOGjy1WeBlRhkVJ7zOPMiyzJi3cPOAK/4O8RcxXOsCiC2AfKPKDYM8bEw==} cpu: [x64] os: [linux] libc: [glibc] - '@tsc-ts/linux-x64-musl@0.0.8': - resolution: {integrity: sha512-zDl36l1uyWc0mQniOAMZmkVD9TrlFExHlDT0fL8JGD4XLgx+bRe+To38yP0KSBehXBzjDNn8xrEoJ+3RP6mYwQ==} + '@tsc-ts/linux-x64-musl@0.0.9': + resolution: {integrity: sha512-a4FW16QXkrwNHAr4Qo/qDA5w9LxCJNrYbpqiz3TJ1sePTqvW/JTJc14tjx6avhW4mIaQp8AB52m7TqNAuDMm4Q==} cpu: [x64] os: [linux] libc: [musl] - '@tsc-ts/win32-x64-msvc@0.0.8': - resolution: {integrity: sha512-9gO+pFoLiIxITlAE2P6JL8ihZwns7z/2Raw5+38RqLzHmnSsRN47+H4p/4EqQsLW5/TTsAo9uSeSkf3EuRg+XA==} + '@tsc-ts/win32-x64-msvc@0.0.9': + resolution: {integrity: sha512-phThYnmOinlvY4dS4DEl1L9t/mFLiHR1zL5plSUfMB5NizA/jESfgMGRu+HW3uwOpCETSxgsuTNAXO7UuEIHIQ==} cpu: [x64] os: [win32] @@ -2577,8 +2577,8 @@ packages: ts-toolbelt@6.15.5: resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==} - tsc-ts@0.0.8: - resolution: {integrity: sha512-EiyDziYOryBg/YEvev/mKCn+hD4M6VFjH7VP9nv2WadDKYaPrdb+a3hP+wYr/UsDWNE7AdL7YGnjZppeXlXy6g==} + tsc-ts@0.0.9: + resolution: {integrity: sha512-7fDfC/o1YoITG6sSXj4ZznijQYg8AfoCagmYvwG1BFLR/hz4Lwnq7jqDwcZfoDuPYTkrs8jHUTAVG1turniJHA==} engines: {node: '>=20'} hasBin: true @@ -3454,25 +3454,25 @@ snapshots: mkdirp: 1.0.4 path-browserify: 1.0.1 - '@tsc-ts/darwin-arm64@0.0.8': + '@tsc-ts/darwin-arm64@0.0.9': optional: true - '@tsc-ts/darwin-x64@0.0.8': + '@tsc-ts/darwin-x64@0.0.9': optional: true - '@tsc-ts/linux-arm64-gnu@0.0.8': + '@tsc-ts/linux-arm64-gnu@0.0.9': optional: true - '@tsc-ts/linux-arm64-musl@0.0.8': + '@tsc-ts/linux-arm64-musl@0.0.9': optional: true - '@tsc-ts/linux-x64-gnu@0.0.8': + '@tsc-ts/linux-x64-gnu@0.0.9': optional: true - '@tsc-ts/linux-x64-musl@0.0.8': + '@tsc-ts/linux-x64-musl@0.0.9': optional: true - '@tsc-ts/win32-x64-msvc@0.0.8': + '@tsc-ts/win32-x64-msvc@0.0.9': optional: true '@tybys/wasm-util@0.10.3': @@ -4813,17 +4813,17 @@ snapshots: ts-toolbelt@6.15.5: {} - tsc-ts@0.0.8: + tsc-ts@0.0.9: dependencies: '@scriptc/threads': 0.4.2 optionalDependencies: - '@tsc-ts/darwin-arm64': 0.0.8 - '@tsc-ts/darwin-x64': 0.0.8 - '@tsc-ts/linux-arm64-gnu': 0.0.8 - '@tsc-ts/linux-arm64-musl': 0.0.8 - '@tsc-ts/linux-x64-gnu': 0.0.8 - '@tsc-ts/linux-x64-musl': 0.0.8 - '@tsc-ts/win32-x64-msvc': 0.0.8 + '@tsc-ts/darwin-arm64': 0.0.9 + '@tsc-ts/darwin-x64': 0.0.9 + '@tsc-ts/linux-arm64-gnu': 0.0.9 + '@tsc-ts/linux-arm64-musl': 0.0.9 + '@tsc-ts/linux-x64-gnu': 0.0.9 + '@tsc-ts/linux-x64-musl': 0.0.9 + '@tsc-ts/win32-x64-msvc': 0.0.9 tslib@2.8.1: optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e74d40442..c9f873c4e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,14 +7,14 @@ minimumReleaseAgeExclude: - "effect@4.0.0" - "@effect/platform-node@4.0.0" - "@effect/platform-node-shared@4.0.0" - - "@tsc-ts/darwin-arm64@0.0.8" - - "@tsc-ts/darwin-x64@0.0.8" - - "@tsc-ts/linux-arm64-gnu@0.0.8" - - "@tsc-ts/linux-arm64-musl@0.0.8" - - "@tsc-ts/linux-x64-gnu@0.0.8" - - "@tsc-ts/linux-x64-musl@0.0.8" - - "@tsc-ts/win32-x64-msvc@0.0.8" - - "tsc-ts@0.0.8" + - "@tsc-ts/darwin-arm64@0.0.9" + - "@tsc-ts/darwin-x64@0.0.9" + - "@tsc-ts/linux-arm64-gnu@0.0.9" + - "@tsc-ts/linux-arm64-musl@0.0.9" + - "@tsc-ts/linux-x64-gnu@0.0.9" + - "@tsc-ts/linux-x64-musl@0.0.9" + - "@tsc-ts/win32-x64-msvc@0.0.9" + - "tsc-ts@0.0.9" - "@scriptc/threads@0.4.2" allowBuilds: esbuild: true diff --git a/scripts/bench-runtime.mjs b/scripts/bench-runtime.mjs index 61134fa4c..b45e3ebdf 100644 --- a/scripts/bench-runtime.mjs +++ b/scripts/bench-runtime.mjs @@ -40,6 +40,8 @@ const { values } = parseArgs({ layouts: { type: "string", default: "1" }, "baseline-optimization": { type: "string", default: "release" }, "candidate-optimization": { type: "string", default: "release" }, + "baseline-target-cpu": { type: "string" }, + "candidate-target-cpu": { type: "string" }, json: { type: "string" }, "no-lock": { type: "boolean", default: false }, node: { type: "boolean", default: true }, @@ -51,6 +53,7 @@ if (values.help) { console.log(`Usage: node scripts/bench-runtime.mjs [--candidate=] [--baseline=] [--workloads=a,b] [--runs=15] [--warmup=2] [--timeout=120] [--layouts=1] [--json=] [--baseline-optimization=release] [--candidate-optimization=release] + [--baseline-target-cpu=] [--candidate-target-cpu=] [--no-lock] [--no-node] [--keep] Each checkout must have a built CLI (packages/cli/dist/bootstrap.js) and native @@ -69,7 +72,10 @@ Workload names are the file names in benchmarks/runtime/workloads/*.json; for ex --baseline-optimization/--candidate-optimization pick each contender's --optimization posture (default release). The same checkout may be both contenders, e.g. --baseline=. --candidate-optimization=speed compares speed with -release. cold_build_ms records each contender's first, uncached build.`); +release. --baseline-target-cpu/--candidate-target-cpu pass --target-cpu to each +contender's builds, e.g. --baseline=. --candidate-target-cpu=x86-64-v3 compares +x86-64-v3 code with the baseline. cold_build_ms records each contender's first, +uncached build.`); process.exit(0); } const runs = Number(values.runs); @@ -428,7 +434,7 @@ function revision(root) { }); return r.stdout.trim() + (dirty.stdout.trim() ? "+dirty" : ""); } -function build(label, root, workload, layout, optimization) { +function build(label, root, workload, layout, optimization, targetCpu) { const cli = cliFor(root); const out = join(work, label, layout === 0 ? workload.name : `${workload.name}.layout${layout}`); mkdirSync(dirname(out), { recursive: true }); @@ -444,7 +450,15 @@ function build(label, root, workload, layout, optimization) { const entry = join(suiteRoot, workload.entry); const result = run( process.execPath, - [cli, "build", entry, `--optimization=${optimization}`, "-o", out], + [ + cli, + "build", + entry, + `--optimization=${optimization}`, + ...(targetCpu === undefined ? [] : [`--target-cpu=${targetCpu}`]), + "-o", + out, + ], { env, timeout: 900_000, @@ -467,6 +481,7 @@ const contenders = [ label: "baseline", root: resolve(values.baseline), optimization: values["baseline-optimization"], + targetCpu: values["baseline-target-cpu"], }, ] : []), @@ -474,6 +489,7 @@ const contenders = [ label: "candidate", root: resolve(values.candidate), optimization: values["candidate-optimization"], + targetCpu: values["candidate-target-cpu"], }, ]; const report = { @@ -580,7 +596,7 @@ try { entry.results[c.label] = result; result.binaries = []; for (let layout = 0; layout < layouts && result.status === undefined; layout++) { - const built = build(c.label, c.root, w, layout, c.optimization); + const built = build(c.label, c.root, w, layout, c.optimization, c.targetCpu); result.build_ms += Math.round(built.buildMs); if (layout === 0) result.cold_build_ms = Math.round(built.buildMs); if (!built.ok) { diff --git a/scripts/ci-test-costs.json b/scripts/ci-test-costs.json index 4c4964579..99d47e972 100644 --- a/scripts/ci-test-costs.json +++ b/scripts/ci-test-costs.json @@ -409,6 +409,7 @@ "tests/harness/stdlib-surface.test.ts": 48, "tests/harness/strip-vt-linear.test.ts": 1795, "tests/harness/surface-manifest.test.ts": 5309, + "tests/harness/teardown-segments.test.ts": 2600, "tests/harness/terminal-geometry.test.ts": 2730, "tests/harness/test262-runner.test.ts": 207, "tests/harness/three-library.test.ts": 251122, @@ -828,6 +829,7 @@ "tests/harness/stdlib-surface.test.ts": 46, "tests/harness/strip-vt-linear.test.ts": 2546, "tests/harness/surface-manifest.test.ts": 5951, + "tests/harness/teardown-segments.test.ts": 5, "tests/harness/terminal-geometry.test.ts": 3270, "tests/harness/test262-runner.test.ts": 230, "tests/harness/three-library.test.ts": 361470, diff --git a/scripts/ci-test-plan.mjs b/scripts/ci-test-plan.mjs index 2b76b52d6..184f04d1c 100644 --- a/scripts/ci-test-plan.mjs +++ b/scripts/ci-test-plan.mjs @@ -42,22 +42,38 @@ export const testCosts = JSON.parse( readFileSync(new URL("./ci-test-costs.json", import.meta.url), "utf8"), ); +// Side-queue files whose process tree, run alone, peaks far above a plain +// vitest run (about 1 GB): megabytes at the peak, measured with CI's settings. +// They run beside the corpus task's three workers, so no two of them share a +// shard while there are shards without one. +export const memoryHeavyFiles = { + "tests/harness/decl-boundary.test.ts": 1838, +}; + // The shard (1-based) of each measured side-queue file and isolated suite: // longest first to the shard with the least work so far, counting about a // second per file for vitest to import it. Ties go to the highest shard, so the // longest files start from the last shard while the cost-balanced corpus and // three.js cases (tests/harness/shard.ts) place their longest from the first. -// An isolated suite without a measurement weighs as much as the median -// measured file. Files without a measurement are left to vitest's --shard, -// which splits them by count. +// An isolated suite or memory-heavy file without a measurement weighs as much +// as the median measured file. Memory-heavy files are placed after the others, +// each on the shard with the least memory-heavy work so far and, among those, +// the least work. Files without a +// measurement are left to vitest's --shard, which splits them by count. export function sideOwners(flavor, count) { const costs = testCosts[flavor]; const known = Object.values(costs).sort((a, b) => a - b); const median = known.length === 0 ? 0 : known[Math.floor(known.length / 2)]; const weight = (file) => (costs[file] ?? median) + 1000; - const files = [...new Set([...Object.keys(costs), ...isolatedFiles.map(([, file]) => file)])]; + const heavy = Object.keys(memoryHeavyFiles).sort( + (a, b) => memoryHeavyFiles[b] - memoryHeavyFiles[a] || (a < b ? -1 : a > b ? 1 : 0), + ); + const files = [ + ...new Set([...Object.keys(costs), ...isolatedFiles.map(([, file]) => file)]), + ].filter((file) => !(file in memoryHeavyFiles)); files.sort((a, b) => weight(b) - weight(a) || (a < b ? -1 : a > b ? 1 : 0)); const loads = new Array(count).fill(0); + const memory = new Array(count).fill(0); const owners = new Map(); for (const file of files) { let lightest = count - 1; @@ -66,6 +82,18 @@ export function sideOwners(flavor, count) { loads[lightest] += weight(file); owners.set(file, lightest + 1); } + for (const file of heavy) { + let lightest = count - 1; + for (let shard = count - 2; shard >= 0; shard--) + if ( + memory[shard] < memory[lightest] || + (memory[shard] === memory[lightest] && loads[shard] < loads[lightest]) + ) + lightest = shard; + memory[lightest] += memoryHeavyFiles[file]; + loads[lightest] += weight(file); + owners.set(file, lightest + 1); + } return owners; } diff --git a/tests/corpus/1006-json-fs-config.ts b/tests/corpus/1006-json-fs-config.ts index f2b1d48c0..1783202c9 100644 --- a/tests/corpus/1006-json-fs-config.ts +++ b/tests/corpus/1006-json-fs-config.ts @@ -11,7 +11,7 @@ function tail(path: string): string { } return path.slice(i + 1); } -const path = "tmp-1006-" + tail(process.argv[1]) + ".json"; +const path = "tmp-1006-" + tail(process.argv[1]) + "-" + process.pid + ".json"; type Config = { name: string; diff --git a/tests/corpus/1354-builtin-bare-specifiers.ts b/tests/corpus/1354-builtin-bare-specifiers.ts index cd0db6984..826c3e7a0 100644 --- a/tests/corpus/1354-builtin-bare-specifiers.ts +++ b/tests/corpus/1354-builtin-bare-specifiers.ts @@ -15,7 +15,7 @@ function tail(path: string): string { return path.slice(i + 1); } -const scratch = "tmp-1354-" + tail(process.argv[1]); +const scratch = "tmp-1354-" + tail(process.argv[1]) + "-" + process.pid; if (existsSync(scratch)) { if (existsSync(j(scratch, "f.txt"))) { rmSync(j(scratch, "f.txt")); diff --git a/tests/corpus/1357-fsp-roundtrip.ts b/tests/corpus/1357-fsp-roundtrip.ts index 4ccae7f3a..7368da360 100644 --- a/tests/corpus/1357-fsp-roundtrip.ts +++ b/tests/corpus/1357-fsp-roundtrip.ts @@ -15,7 +15,7 @@ function tail(path: string): string { } async function main(): Promise { - const scratch = "tmp-1357-" + tail(process.argv[1]); + const scratch = "tmp-1357-" + tail(process.argv[1]) + "-" + process.pid; if (existsSync(scratch)) { if (existsSync(scratch + "/a.txt")) { rmSync(scratch + "/a.txt"); diff --git a/tests/corpus/2570-buffer-arg-validation.cjs b/tests/corpus/2570-buffer-arg-validation.cjs index d49593a30..c0980e495 100644 --- a/tests/corpus/2570-buffer-arg-validation.cjs +++ b/tests/corpus/2570-buffer-arg-validation.cjs @@ -1,4 +1,3 @@ -// @no-deprecation // Buffer's invalid-argument ladders, Node-exact: Buffer.compare / // buf.compare / buf.equals over non-buffer values and bad offsets throw // ERR_INVALID_ARG_TYPE / ERR_OUT_OF_RANGE with Node's argument names and @@ -7,6 +6,8 @@ // real answers. Plus the deprecated `new Buffer(number, enc)` string-arm // rejection and the ERR_* codes on the write/swap/fill range ladders. 'use strict'; +// The deprecated constructor's DEP0005 report carries a process id. +process.noDeprecation = true; const show = (fn) => { try { console.log('ret', fn()); diff --git a/tests/corpus/2844-node-disposable-resources.ts b/tests/corpus/2844-node-disposable-resources.ts index 042f617ce..ebeda87a2 100644 --- a/tests/corpus/2844-node-disposable-resources.ts +++ b/tests/corpus/2844-node-disposable-resources.ts @@ -14,7 +14,7 @@ async function main(): Promise { console.log(timeout.hasRef(), immediate.hasRef()); } - const path = "/tmp/scriptc-disposable-file.txt"; + const path = `/tmp/scriptc-disposable-file-${process.pid}.txt`; await writeFile(path, "resource"); { await using handle = await open(path, "r"); diff --git a/tests/corpus/5566-fs-dirent-dyn-boundaries/main.mjs b/tests/corpus/5566-fs-dirent-dyn-boundaries/main.mjs index 95bcbecee..77bdf8905 100644 --- a/tests/corpus/5566-fs-dirent-dyn-boundaries/main.mjs +++ b/tests/corpus/5566-fs-dirent-dyn-boundaries/main.mjs @@ -8,7 +8,7 @@ function tail(path) { while (i >= 0 && path.charAt(i) !== "/" && path.charAt(i) !== "\\") i = i - 1; return path.slice(i + 1); } -const dir = `/tmp/scr-dirent-dyn-${tail(process.argv[1] === undefined ? "x" : process.argv[1])}`; +const dir = `/tmp/scr-dirent-dyn-${tail(process.argv[1] === undefined ? "x" : process.argv[1])}-${process.pid}`; if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true }); fs.mkdirSync(`${dir}/sub`, { recursive: true }); fs.writeFileSync(`${dir}/a.txt`, "x"); diff --git a/tests/corpus/5965-date-objects.ts b/tests/corpus/5965-date-objects.ts new file mode 100644 index 000000000..c4bb01fb9 --- /dev/null +++ b/tests/corpus/5965-date-objects.ts @@ -0,0 +1,115 @@ +// Dates are objects with identity: arrays of Dates, `Date | undefined` +// and `Date | null` slots, === by identity, includes/indexOf/lastIndexOf, +// at/find/pop answering undefined, Map and Set keys and values, join, +// String() and default sort through toString(), and Dates in records, +// tuples, class fields, closures, async results and generators. +export {}; +process.env.TZ = "America/Chicago"; + +const base = new Date(Date.UTC(2026, 3, 1, 12)); +const same = base; +const copy = new Date(base); +const equalTime = new Date(base.getTime()); +console.log(base === same, base === copy, copy === equalTime, base !== copy); +console.log(copy.getTime() === base.getTime(), base < new Date(base.getTime() + 1), base >= copy); + +// Arrays. +const dates: Date[] = [base, new Date(Date.UTC(2025, 0, 1)), new Date(Date.UTC(2026, 0, 1))]; +console.log(dates.length, dates.includes(base), dates.includes(copy), dates.indexOf(dates[2]!)); +console.log(dates.indexOf(equalTime), dates.lastIndexOf(base), dates.indexOf(base, 1)); +console.log(dates.join(" | ")); +console.log(String(dates)); +console.log(`${dates}`); +console.log([...dates].sort().map((d) => d.toISOString())); +console.log(dates.toSorted((a, b) => a.getTime() - b.getTime()).map((d) => d.getUTCFullYear())); +const first = dates.at(0); +const missing = dates.at(10); +console.log(first === base, missing, missing === undefined, first?.toISOString(), missing?.toISOString()); +const found = dates.find((d) => d.getUTCFullYear() === 2025); +console.log(found === dates[1], dates.find((d) => d.getTime() < 0), dates.findLast((d) => d > base)); +const stack = [...dates]; +console.log(stack.pop() === dates[2], stack.pop()?.getUTCFullYear(), stack.pop() === base, stack.pop()); +const latest = dates.reduce((m, d) => (d > m ? d : m)); +console.log(latest === base, dates.filter((d) => d < base).length, dates.map((d) => d.getUTCMonth())); +const grid: Date[][] = [[base], [copy, base]]; +console.log(grid[1]!.indexOf(base), grid.flat().length, grid.flat().includes(copy)); +const holes: (Date | undefined)[] = [base, undefined, copy]; +console.log(holes.join(","), holes.indexOf(undefined), holes.filter((d) => d !== undefined).length); + +// Nullable slots. +let maybe: Date | null = null; +console.log(maybe === null, maybe); +maybe = base; +console.log(maybe === base, maybe.getUTCHours()); +function stamp(d?: Date): string { + return d === undefined ? "none" : d.toISOString(); +} +function withDefault(d: Date = new Date(0)): number { + return d.getTime(); +} +console.log(stamp(), stamp(base), withDefault(), withDefault(base) === base.getTime()); + +// Collections. +const notes = new Map([[base, "base"]]); +notes.set(copy, "copy"); +console.log(notes.get(base), notes.get(copy), notes.get(equalTime), notes.size); +const byName = new Map([["start", base]]); +console.log(byName.get("start") === base, byName.get("end")); +const seen = new Set([base, same, copy]); +console.log(seen.size, seen.has(base), seen.has(equalTime)); + +// Records, tuples, classes and closures. +interface Event { + name: string; + at: Date; + ends?: Date; +} +const events: Event[] = [ + { name: "a", at: base }, + { name: "b", at: copy, ends: base }, +]; +console.log(events.map((e) => `${e.name}@${e.at.toISOString()} ends=${e.ends?.toISOString() ?? "-"}`)); +console.log(events[1]!.ends === events[0]!.at, events[1]!.at === events[0]!.at); +const pair: [Date, string] = [base, "x"]; +console.log(pair[0] === base, pair[1]); +class Window { + from: Date; + to: Date | null = null; + later!: Date; + constructor(from: Date) { + this.from = from; + } + span(): number { + return this.to === null ? 0 : this.to.getTime() - this.from.getTime(); + } +} +const w = new Window(base); +w.later = copy; +console.log(w.span(), w.later === copy); +w.to = new Date(base.getTime() + 60000); +console.log(w.span(), w.to === w.from); +let captured: Date | undefined; +const remember = (d: Date): void => { + captured = d; +}; +remember(base); +console.log(captured === base, captured?.getUTCDate()); + +// Async results, generators and unknown. +async function later(d: Date): Promise { + await null; + return new Date(d.getTime() + 1); +} +const next = await later(base); +console.log(next.getTime() - base.getTime(), next === base); +function* ticks(start: Date, n: number): Generator { + for (let i = 0; i < n; i++) yield new Date(start.getTime() + i * 1000); +} +console.log([...ticks(base, 3)].map((d) => d.getUTCSeconds())); +const held: unknown = base; +console.log((held as Date) === base, held instanceof Date, String(held) === String(base)); +const bag: unknown = { at: base, list: dates }; +console.log(JSON.stringify(bag)); +console.log(JSON.stringify(events), JSON.stringify([base, new Date(NaN)])); +const byKey: { [k: string]: Date } = { boot: base }; +console.log(byKey["boot"] === base, byKey["missing"], Object.keys(byKey)); diff --git a/tests/corpus/5966-date-time-values.ts b/tests/corpus/5966-date-time-values.ts new file mode 100644 index 000000000..342b4662c --- /dev/null +++ b/tests/corpus/5966-date-time-values.ts @@ -0,0 +1,80 @@ +// Date locals that are only read for their time value (method receivers, +// String() and template operands, relational and string `+` operands, +// `new Date(d)`) print and compare as Dates; locals whose identity a use +// can observe keep it. +export {}; +process.env.TZ = "America/New_York"; + +function describe(ms: number): string { + const d = new Date(ms); + const later = new Date(ms + 3_600_000); + const copy = new Date(d); + return [ + d.toISOString(), + d.toUTCString(), + String(d), + `${later}`, + "at " + d, + d < later, + later >= d, + d.getHours(), + d.getUTCDay(), + d.getTimezoneOffset(), + copy.getTime() === d.getTime(), + d.toJSON(), + ].join(" | "); +} +console.log(describe(Date.UTC(2026, 2, 8, 6, 30))); +console.log(describe(Date.UTC(2026, 10, 1, 5, 30))); +function invalid(): string { + const d = new Date(NaN); + return [String(d), d.getTime(), String(d.toJSON()), d < new Date(0), `${d}`].join(" | "); +} +console.log(invalid()); + +function sum(days: number): number { + let total = 0; + for (let i = 0; i < days; i++) { + const day = new Date(Date.UTC(2026, 0, 1) + i * 86_400_000); + total += day.getUTCDate() + day.getUTCMonth() + day.toISOString().length; + } + return total; +} +console.log(sum(400)); + +// Identity-observing uses keep the Date object. +const kept: Date[] = []; +function identities(ms: number): string { + const a = new Date(ms); + const b = a; + const stored = new Date(ms); + kept.push(stored); + const captured = new Date(ms); + const read = (): number => captured.getTime(); + const passed = new Date(ms); + return [a === b, kept.includes(stored), kept[0] === stored, read(), same(passed, passed)].join(","); +} +function same(x: Date, y: Date): boolean { + return x === y; +} +console.log(identities(5)); +const held: unknown = kept[0]; +console.log(held instanceof Date, (held as Date) === kept[0]); + +// Dates in blocks and loops at module level, read for their time value or +// kept, stored and captured. +const moduleKept: Date[] = []; +let moduleTotal = ""; +for (let i = 0; i < 3; i++) { + const read = new Date(Date.UTC(2026, 2, 8 + i, 6, 30)); + const kept = new Date(read.getTime()); + moduleKept.push(kept); + moduleTotal += `${read.toISOString()} ${read.getHours()} ${String(read)}\n`; +} +{ + const block = new Date(Date.UTC(2026, 10, 1, 5, 59)); + const captured = new Date(block); + const later = (): string => captured.toISOString(); + moduleTotal += `${block.getTimezoneOffset()} ${later()} ${captured === moduleKept[0]}`; +} +console.log(moduleTotal, moduleKept.map((d) => d.getUTCDate()).join(",")); diff --git a/tests/corpus/5967-date-alias-mutation/clock.js b/tests/corpus/5967-date-alias-mutation/clock.js new file mode 100644 index 000000000..75c89bee5 --- /dev/null +++ b/tests/corpus/5967-date-alias-mutation/clock.js @@ -0,0 +1,6 @@ +// A JavaScript module mutates the Dates it is handed (Date setters run on +// the checked-dynamic Date object). +export function advance(date, ms) { + date.setTime(date.getTime() + ms); + return date; +} diff --git a/tests/corpus/5967-date-alias-mutation/main.ts b/tests/corpus/5967-date-alias-mutation/main.ts new file mode 100644 index 000000000..f99580c45 --- /dev/null +++ b/tests/corpus/5967-date-alias-mutation/main.ts @@ -0,0 +1,22 @@ +// A Date local initialized from an existing Date is another reference to +// the same object: a mutation through the other reference (a JavaScript +// module's setTime) shows through the local. A relational comparison reads +// both operands' times after both operands evaluate, so a right operand +// that mutates the left Date is seen. +import { advance } from "./clock.js"; + +function aliases(start: Date): string { + const alias = start; + advance(start, 60_000); + const stamp = alias.toISOString(); + const other = new Date(start.getTime() + 1); + const moved = start < (advance(start, 5), other); + const after = start <= (advance(start, 10), other); + return `${stamp} ${alias.getTime() === start.getTime()} ${moved} ${after}`; +} +console.log(aliases(new Date(Date.UTC(2026, 0, 15, 12)))); + +const fresh = new Date(0); +const copy = new Date(fresh); +advance(fresh, 1000); +console.log(fresh.getTime(), copy.getTime(), fresh < copy, copy < fresh); diff --git a/tests/corpus/6343-inspect-class-values.ts b/tests/corpus/6343-inspect-class-values.ts new file mode 100644 index 000000000..5e9931c8c --- /dev/null +++ b/tests/corpus/6343-inspect-class-values.ts @@ -0,0 +1,31 @@ +// Classes reached through `unknown` print as Node prints them: +// `[class C]`, `[class C extends B]` with the runtime class's base, and +// `[class (anonymous)]`, at the top level and inside arrays and records. +import { inspect } from "node:util"; + +class A { + x = 1; +} +class B extends A { + y = 2; +} +class C extends B {} +class MyErr extends Error {} +const Anon = [class {}][0]!; +const Sub = class extends A {}; + +const a: unknown = A; +const b: unknown = B; +console.log(a, b); +console.log([a], { b }); +const values: unknown[] = [C, MyErr, Anon, Sub]; +console.log(values); +console.log(inspect({ deep: { values } }, { depth: 0 }), inspect(values, { depth: 0 })); +console.log(String(typeof values[0])); + +// A slot typed as the base holds a subclass at runtime. +const pick = (k: number): typeof A => (k === 0 ? A : k === 1 ? B : C); +for (let k = 0; k < 3; k++) { + const picked: unknown = pick(k); + console.log(k, picked); +} diff --git a/tests/corpus/6344-inspect-function-properties.cjs b/tests/corpus/6344-inspect-function-properties.cjs new file mode 100644 index 000000000..5cab04172 --- /dev/null +++ b/tests/corpus/6344-inspect-function-properties.cjs @@ -0,0 +1,62 @@ +'use strict'; +// Functions and classes reached as dynamic values print their enumerable +// own properties after the tag, as Node prints them: a class's static +// properties, properties assigned to a function, cycles through them, +// and `[Function]` past the depth limit, where a cycle still shows. +const util = require('util'); + +function f() {} +f.x = 1; +f.label = 'f'; +function g() {} +g.self = g; +function plain() {} +class A {} +class S extends A { + static get hidden() { + return 1; + } +} +S.count = 3; +function show(value) { + console.log(value); +} +show(f); +show(g); +show(S); +show(A); +show(plain); +function shallow(value) { + console.log(util.inspect({ value }, { depth: 0 })); +} +shallow(f); +shallow(S); +shallow(A); +console.log(util.inspect(g, { depth: 0 })); +shallow(g); + +// A class that reaches itself through its static properties is one cycle, +// whichever dynamic box of it the inspection meets. +class Loop { + static get hidden() { + return 1; + } +} +Loop.self = Loop; +Loop.label = 'loop'; +class Left { + static get hidden() { + return 2; + } +} +class Right { + static get hidden() { + return 3; + } +} +Left.other = Right; +Right.other = Left; +show(Loop); +show(Left); +show([Loop, Loop]); +show({ a: Left, b: Right }); diff --git a/tests/corpus/6357-format-o-hidden.ts b/tests/corpus/6357-format-o-hidden.ts new file mode 100644 index 000000000..44ed1d772 --- /dev/null +++ b/tests/corpus/6357-format-o-hidden.ts @@ -0,0 +1,35 @@ +// util.format's %o over values reached through `unknown` renders with +// showHidden, as Node does: arrays show their hidden `[length]` (with +// Node's grouping), at every depth, inside Maps and records too, ahead of +// an array's named properties. +import { format } from "node:util"; +const cases: [string, unknown][] = [ + ["ten", Array.from({ length: 10 }, (_, i) => i)], + ["wide", Array.from({ length: 120 }, (_, i) => i)], + ["hundred", Array.from({ length: 100 }, (_, i) => i)], + ["hundred-one", Array.from({ length: 101 }, (_, i) => i)], + ["hundred-strs", Array.from({ length: 100 }, (_, i) => "s" + i)], + ["seven", ["aaaaaaaaaaaaaaaaaaaa", "b", "c", "d", "e", "f", "g"]], + ["strs", Array.from({ length: 30 }, (_, i) => "s" + i)], + ["holes", [1, , 3]], + ["empty", []], + ["nested", { list: [[]], m: new Map([[1, [2]]]) }], + ["deep", [[[[[[1]]]]]]], + ["date", [new Date(0)]], +]; +for (const [k, v] of cases) console.log(k, format("%o", v)); +// The hidden `[length]` comes before an array's named properties, and the +// grid's last-entry rule counts both. +type Bag = Record; +const named = [1, 2]; +(named as unknown as Bag).foo = "x"; +const tagged = Array.from({ length: 100 }, (_, i) => i); +(tagged as unknown as Bag).tag = "t"; +const bare: number[] = []; +(bare as unknown as Bag).k = 1; +for (const [k, v] of [ + ["named", named], + ["tagged", tagged], + ["bare", bare], +] as [string, unknown][]) + console.log(k, format("%o", v)); diff --git a/tests/corpus/6358-format-o-hidden-js.cjs b/tests/corpus/6358-format-o-hidden-js.cjs new file mode 100644 index 000000000..df54b2714 --- /dev/null +++ b/tests/corpus/6358-format-o-hidden-js.cjs @@ -0,0 +1,9 @@ +'use strict'; +// %o shows an object's non-enumerable own properties as `[key]`, symbol +// keys included. +const util = require('util'); +const hidden = Object.defineProperty(JSON.parse('{"shown":1}'), 'secret', { value: [2], enumerable: false }); +Object.defineProperty(hidden, 'a-b', { value: 'x', enumerable: false }); +Object.defineProperty(hidden, Symbol('tag'), { value: 3, enumerable: false }); +console.log(util.format('%o and %O', hidden, hidden)); +console.log(util.format('%o', [hidden])); diff --git a/tests/corpus/6935-fs-url-paths-ordered.ts b/tests/corpus/6935-fs-url-paths-ordered.ts new file mode 100644 index 000000000..8d39c40ff --- /dev/null +++ b/tests/corpus/6935-fs-url-paths-ordered.ts @@ -0,0 +1,132 @@ +// URL paths that Node converts inside a try or after checking other +// arguments: existsSync answers false for a URL it cannot convert, the +// fs.promises functions return a promise rejected with the conversion's +// error, copyFileSync and renameSync check a string source before they +// convert a URL destination, and readFileSync checks a runtime encoding +// before it converts the URL. +import * as fs from "node:fs"; +import * as fsp from "node:fs/promises"; +import * as os from "node:os"; +import * as path from "node:path"; +import { pathToFileURL } from "node:url"; + +const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), "fs-url-ordered-"))); +const at = (name: string): URL => pathToFileURL(path.join(dir, name)); +const scheme = new URL("http://example.com/a.txt"); +const host = new URL("file://example.com/a.txt"); + +function report(label: string, error: unknown): void { + if (error instanceof Error) { + const code = (error as NodeJS.ErrnoException).code; + console.log(label, error.name, code, error.message.split(dir).join("")); + } +} + +function attempt(label: string, run: () => void): void { + try { + run(); + console.log(label, "returned"); + } catch (error) { + report(label, error); + } +} + +async function settle(label: string, run: () => Promise): Promise { + try { + await run(); + console.log(label, "fulfilled"); + } catch (error) { + report(label, error); + } +} + +async function main(): Promise { + fs.writeFileSync(at("a b.txt"), "hello"); + console.log(fs.existsSync(at("a b.txt")), fs.existsSync(at("missing"))); + console.log(fs.existsSync(scheme), fs.existsSync(host), fs.existsSync(new URL("file:///tmp/a%2Fb"))); + console.log(fs.existsSync(new URL("file:///tmp/a%00b"))); + + console.log(await fsp.readFile(at("a b.txt"), "utf8")); + console.log((await fsp.readFile(at("a b.txt"))).length); + await fsp.writeFile(at("w.txt"), "written"); + await fsp.appendFile(at("w.txt"), "!"); + console.log((await fsp.stat(at("w.txt"))).size, (await fsp.lstat(at("w.txt"))).isFile()); + await fsp.access(at("w.txt")); + await fsp.chmod(at("w.txt"), 0o644); + await fsp.truncate(at("w.txt"), 3); + await fsp.copyFile(at("w.txt"), at("copy.txt")); + await fsp.rename(at("copy.txt"), at("moved.txt")); + console.log(await fsp.readFile(at("moved.txt"), "utf8")); + await fsp.mkdir(at("sub/deep"), { recursive: true }); + console.log(await fsp.readdir(at("sub"))); + console.log(path.dirname(await fsp.mkdtemp(at("tmp-"))) === dir); + console.log((await fsp.realpath(at("sub/../sub"))).split(dir).join("")); + // An array literal lowers its elements more than once. + console.log([await fsp.realpath(at("sub"))].length, [fs.existsSync(scheme)][0]); + // A later argument with effects runs once per call there, and the call + // receives its value. + let dataCalls = 0; + const data = (): string => { + dataCalls++; + return `data ${dataCalls}`; + }; + console.log([await fsp.writeFile(at("w.txt"), data())].length, dataCalls); + console.log(await fsp.readFile(at("w.txt"), "utf8")); + const code = (error: unknown): string => String((error as { code?: string }).code); + const outcomes = [ + await fsp.writeFile(scheme, data()).then(() => "fulfilled", code), + await fsp.appendFile(at("w.txt"), data()).then(() => "fulfilled", code), + ]; + console.log(outcomes, dataCalls); + console.log(await fsp.readFile(at("w.txt"), "utf8")); + await fsp.rmdir(at("sub/deep")); + await fsp.unlink(at("moved.txt")); + await fsp.rm(at("w.txt")); + await fsp.rm(at("sub"), { recursive: true, force: true }); + console.log(fs.existsSync(at("sub"))); + + // The promise is returned; the error arrives as its rejection. + const pending = fsp.stat(scheme); + console.log("returned a promise"); + await pending.catch((error: unknown) => report("stat scheme", error)); + await settle("readFile host", () => fsp.readFile(host, "utf8")); + await settle("copyFile both", () => fsp.copyFile(scheme, host)); + await settle("rename dest", () => fsp.rename(at("a b.txt"), host)); + await settle("writeFile separator", () => fsp.writeFile(new URL("file:///tmp/a%2fb"), "x")); + await settle("unlink missing", () => fsp.unlink(at("missing"))); + await settle("rm scheme options", () => fsp.rm(scheme, { recursive: true, force: true })); + + // Arguments inside type-only wrappers are evaluated once. + let modeCalls = 0; + const countedMode = (): number => { + modeCalls++; + return 0o644; + }; + await settle("chmod satisfies", () => fsp.chmod(scheme, countedMode() satisfies number)); + await settle("chmod as", () => fsp.chmod(at("a b.txt"), countedMode() as number)); + await settle("chmod non-null", () => fsp.chmod(host, countedMode()!)); + console.log("mode calls", modeCalls); + await settle("copyFile as", () => fsp.copyFile(scheme, at("c.txt") as URL)); + await settle("copyFile dest as", () => fsp.copyFile(at("a b.txt"), host as URL)); + + attempt("copyFileSync source first", () => fs.copyFileSync("a\0b", scheme)); + attempt("copyFileSync dest", () => fs.copyFileSync(path.join(dir, "a b.txt"), host)); + attempt("renameSync source first", () => fs.renameSync("a\0b", host)); + fs.copyFileSync(path.join(dir, "a b.txt"), at("copied.txt")); + console.log(fs.readFileSync(at("copied.txt"), "utf8")); + + const encodings = ["utf8", "latin1", "base64", "bogus"]; + for (const encoding of encodings) { + attempt(`readFileSync ${encoding}`, () => { + console.log(fs.readFileSync(at("a b.txt"), encoding as BufferEncoding)); + }); + attempt(`readFileSync ${encoding} scheme`, () => { + console.log(fs.readFileSync(scheme, encoding as BufferEncoding)); + }); + } + + fs.rmSync(dir, { recursive: true, force: true }); + console.log("done", fs.existsSync(dir)); +} + +void main(); diff --git a/tests/corpus/7012-dictionary-views-keep-objects.ts b/tests/corpus/7012-dictionary-views-keep-objects.ts new file mode 100644 index 000000000..0df47cbb8 --- /dev/null +++ b/tests/corpus/7012-dictionary-views-keep-objects.ts @@ -0,0 +1,98 @@ +// A record or class instance converted to a string-keyed dictionary is +// the same object: writes through the dictionary reach it, and the +// dictionary is === to it. +const key = process.argv.length > 99 ? "never" : "added"; +type Point = { a: string }; + +// A type-literal record stored straight in a dictionary binding. +const direct: Point = { a: "x" }; +const directTable: Record = direct; +directTable[key] = "y"; +console.log(JSON.stringify(direct), (directTable as unknown) === direct); + +// The same record through a `{}` view. +const viewed: Point = { a: "x" }; +const view: {} = viewed; +const viewTable: Record = view; +viewTable[key] = "y"; +console.log(JSON.stringify(viewed)); + +// A declared index type, and a parameter. +interface Labels { + [name: string]: string; +} +function label(table: Labels, name: string): void { + table[name] = "set"; +} +const passed: Point = { a: "x" }; +label(passed, key); +console.log(JSON.stringify(passed)); + +// A `{}` parameter converted inside the function, called with a record +// and with a dictionary. +function viaView(value: {}): void { + const table: Record = value; + table[key] = "y"; +} +const record: Point = { a: "x" }; +viaView(record); +const dictionary: Record = { a: "x" }; +viaView(dictionary); +console.log(JSON.stringify(record), JSON.stringify(dictionary)); + +// A class instance through a `{}` view: a computed write replaces a method. +class Greeter { + name = "g"; + greet(): string { + return "hello " + this.name; + } +} +const greeter = new Greeter(); +const greeterView: {} = greeter; +const slots: Record = greeterView; +slots[process.argv.length > 99 ? "never" : "greet"] = "replaced"; +slots["name"] = "renamed"; +console.log(typeof greeter.greet, greeter.name, JSON.stringify(greeter)); +// A write under a runtime string key replaces a method too, and a direct +// call reaches the replacement, which is not a function. +class Counter { + n = 0; + tick(): number { + return ++this.n; + } +} +const counter = new Counter(); +const counterView: {} = counter; +const counterSlots: Record = counterView; +const methodKey: string = process.argv.length > 99 ? "never" : "tick"; +counterSlots[methodKey] = "replaced"; +try { + console.log(counter.tick()); +} catch (error) { + console.log((error as Error).name, (error as Error).message); +} +try { + console.log(greeter.greet()); +} catch (error) { + console.log((error as Error).name, (error as Error).message); +} + +// Deleting through the dictionary removes the key from the object. +const pruned: { a: string; b?: string } = { a: "x", b: "y" }; +const prunedTable: Record = pruned; +delete prunedTable[process.argv.length > 99 ? "never" : "b"]; +console.log(JSON.stringify(pruned), "b" in pruned); + +// A dictionary literal whose methods read `this` keeps its live object. +interface Table { + [key: string]: () => number; +} +const table: Table = { + base() { + return 2; + }, + twice() { + return this.base() * 2; + }, +}; +console.log(table.twice(), Object.keys(table).join(",")); diff --git a/tests/corpus/7013-dictionary-view-parameter.ts b/tests/corpus/7013-dictionary-view-parameter.ts new file mode 100644 index 000000000..2bfc34a76 --- /dev/null +++ b/tests/corpus/7013-dictionary-view-parameter.ts @@ -0,0 +1,30 @@ +// A record passed to a `{}` parameter and converted to a dictionary inside +// the function is the same object: the write through the dictionary +// reaches the caller's record. A dot write through a dictionary view of a +// class instance replaces its method. +const key = process.argv.length > 99 ? "never" : "added"; +function stamp(value: {}): void { + const table: Record = value; + table[key] = "y"; +} +const record: { a: string } = { a: "x" }; +stamp(record); +console.log(JSON.stringify(record), key in record); + +// A dot write through the dictionary view replaces a method too. +class Lamp { + on = false; + toggle(): boolean { + this.on = !this.on; + return this.on; + } +} +const lamp = new Lamp(); +const lampView: {} = lamp; +const lampSlots: Record = lampView; +lampSlots.toggle = "replaced"; +try { + console.log(lamp.toggle()); +} catch (error) { + console.log((error as Error).name, (error as Error).message); +} diff --git a/tests/corpus/7035-promise-all-tuple-turns.ts b/tests/corpus/7035-promise-all-tuple-turns.ts new file mode 100644 index 000000000..c0fc6a5a3 --- /dev/null +++ b/tests/corpus/7035-promise-all-tuple-turns.ts @@ -0,0 +1,80 @@ +// Promise.all over entries of different types settles, and resumes its +// awaits and then callbacks, on the same microtask turns as Node, whether +// the result is awaited at once, stored first, chained with then, or +// returned from an async function. The first rejection in settlement +// order wins, later ones count as handled, and record entries keep their +// identity. +const log: string[] = []; +function later(label: string, turns: number, value: T, fail = false): Promise { + return new Promise((resolve, reject) => { + let left = turns; + const step = (): void => { + if (left-- > 0) return queueMicrotask(step); + log.push(`settle ${label}`); + if (fail) reject(new Error(label)); + else resolve(value); + }; + step(); + }); +} +async function num(): Promise { + return 1; +} +async function text(): Promise { + return "s"; +} +async function both(): Promise<[number, string]> { + return Promise.all([num(), text()]); +} +const thenable: any = { + get then() { + log.push("then getter"); + return (resolve: (v: number) => void) => resolve(1); + }, +}; +function evaluated(): Promise { + log.push("second entry evaluated"); + return Promise.resolve("s"); +} +interface Item { + id: number; +} +const item: Item = { id: 7 }; + +async function main(): Promise { + let ticking = true; + void (async () => { + for (let i = 0; i < 40 && ticking; i++) { + log.push(`t${i}`); + await null; + } + })(); + const [a, b] = await Promise.all([num(), text()]); + log.push(`awaited ${a}${b}`); + const stored = Promise.all([text(), num(), Promise.resolve(item)]); + const [c, d, e] = await stored; + log.push(`stored ${c}${d} ${e === item}`); + Promise.all([text(), num()]).then(([f, g]) => log.push(`then ${f}${g}`)); + const [h, i] = await both(); + log.push(`returned ${h}${i}`); + const mixed = await Promise.all([later("x", 3, "x"), 5, later("y", 1, true), undefined]); + log.push(`mixed ${mixed[0]} ${mixed[1]} ${mixed[2]} ${mixed[3]}`); + // A dynamic entry's `then` getter runs after the whole array evaluated. + const [fromThenable, second] = await Promise.all([thenable, evaluated()]); + log.push(`thenable ${fromThenable} ${second}`); + const raw: any = JSON.parse('{"value":41}'); + const [dynamic, counted] = await Promise.all([raw.value, later("z", 2, 2)]); + log.push(`dynamic ${dynamic + counted}`); + // A Date entry's payload is its time value. + const [when, label] = await Promise.all([later("date", 1, new Date(86400000)), text()]); + log.push(`date ${when.toISOString()} ${label}`); + try { + await Promise.all([later("late", 4, 1, true), later("early", 2, "e", true), num()]); + } catch (err) { + log.push(`rejected ${(err as Error).message}`); + } + await later("tail", 6, 0); + ticking = false; + console.log(log.join("\n")); +} +void main(); diff --git a/tests/corpus/7110-als-completion-callbacks.ts b/tests/corpus/7110-als-completion-callbacks.ts new file mode 100644 index 000000000..4b4e97e0d --- /dev/null +++ b/tests/corpus/7110-als-completion-callbacks.ts @@ -0,0 +1,135 @@ +// The completion callback of a callback-style API runs with the +// AsyncLocalStorage stores that were active when the call started the +// operation, as in Node. +import { AsyncLocalStorage } from "node:async_hooks"; +import { execFile } from "node:child_process"; +import * as crypto from "node:crypto"; +import * as dgram from "node:dgram"; +import * as dns from "node:dns"; +import { readFile, rename, writeFileSync, unlinkSync } from "node:fs"; +import * as http from "node:http"; +import * as net from "node:net"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import * as zlib from "node:zlib"; + +const store = new AsyncLocalStorage(); + +// Starts one operation inside run(name) and waits for its callback. +function step(name: string, start: (done: () => void) => void): Promise { + return new Promise((resolve) => { + store.run(name, () => + start(() => { + console.log(name, store.getStore()); + resolve(); + }), + ); + }); +} + +const dir = tmpdir(); +const file = join(dir, `scriptc-als-${process.pid}.txt`); +writeFileSync(file, "hello"); + +await step("fs.readFile", (done) => readFile(file, "utf8", () => done())); +await step("fs.rename", (done) => rename(file, `${file}.moved`, () => done())); +unlinkSync(`${file}.moved`); + +await step("crypto.randomBytes", (done) => crypto.randomBytes(4, () => done())); +await step("crypto.randomInt", (done) => crypto.randomInt(10, () => done())); +await step("crypto.randomFill", (done) => crypto.randomFill(new Uint8Array(4), () => done())); +await step("crypto.pbkdf2", (done) => crypto.pbkdf2("p", "s", 1, 8, "sha256", () => done())); +await step("crypto.scrypt", (done) => crypto.scrypt("p", "s", 8, () => done())); +await step("crypto.hkdf", (done) => crypto.hkdf("sha256", "k", "s", "i", 8, () => done())); +await step("crypto.generateKey", (done) => + crypto.generateKey("hmac", { length: 64 }, () => done()), +); + +await step("zlib.gzip", (done) => zlib.gzip(Buffer.from("hello"), () => done())); +await step("zlib.deflate options", (done) => + zlib.deflate(Buffer.from("hello"), { level: 1 }, () => done()), +); + +await step("dns.lookup", (done) => dns.lookup("localhost", () => done())); +await step("child_process.execFile", (done) => execFile("true", [], () => done())); + +// A server's listen callback, a client connection and an HTTP response +// each run in the stores of their own call. +const server = net.createServer((socket) => socket.end()); +await step("net listen", (done) => server.listen(0, "127.0.0.1", () => done())); +const port = (server.address() as net.AddressInfo).port; +await step("net.connect", (done) => { + const socket = net.connect(port, "127.0.0.1", () => { + socket.destroy(); + done(); + }); +}); +server.close(); + +const web = http.createServer((_req, res) => res.end("ok")); +await new Promise((resolve) => web.listen(0, "127.0.0.1", () => resolve())); +const webPort = (web.address() as net.AddressInfo).port; +await step("http.get", (done) => + http.get(`http://127.0.0.1:${webPort}/`, (res) => { + res.resume(); + done(); + }), +); +await step("http.request", (done) => + http + .request({ host: "127.0.0.1", port: webPort, path: "/", method: "GET" }, (res) => { + res.resume(); + done(); + }) + .end(), +); +web.close(); + +const optionsServer = net.createServer(); +await step("net listen options", (done) => + optionsServer.listen({ port: 0, host: "127.0.0.1" }, () => done()), +); +optionsServer.close(); +const pathServer = net.createServer(); +const socketPath = join(dir, `scriptc-als-${process.pid}.sock`); +await step("net listen path", (done) => pathServer.listen(socketPath, () => done())); +pathServer.close(); + +const udp = dgram.createSocket("udp4"); +await step("dgram bind", (done) => udp.bind(0, "127.0.0.1", () => done())); +const peer = dgram.createSocket("udp4"); +await new Promise((resolve) => peer.bind(0, "127.0.0.1", () => resolve())); +await step("dgram connect", (done) => udp.connect(peer.address().port, "127.0.0.1", () => done())); +peer.close(); +await step("dgram close", (done) => udp.close(() => done())); + +await step("process.stdout.write", (done) => process.stdout.write("", () => done())); + +// Nested: a callback starts another operation in a different store. +await step("outer", (done) => + crypto.randomBytes(1, () => { + console.log("outer callback", store.getStore()); + store.run("inner", () => + crypto.randomBytes(1, () => { + console.log("inner callback", store.getStore()); + done(); + }), + ); + }), +); + +// A callback whose call ran with no store sees no store, and a store the +// callback enters does not reach the next callback. +await new Promise((resolve) => + crypto.randomBytes(1, () => { + console.log("no store", store.getStore()); + store.enterWith("entered"); + resolve(); + }), +); +await new Promise((resolve) => + crypto.randomBytes(1, () => { + console.log("after enterWith", store.getStore()); + resolve(); + }), +); diff --git a/tests/corpus/7111-als-bind-snapshot-options.ts b/tests/corpus/7111-als-bind-snapshot-options.ts new file mode 100644 index 000000000..cad93cd5c --- /dev/null +++ b/tests/corpus/7111-als-bind-snapshot-options.ts @@ -0,0 +1,66 @@ +// AsyncLocalStorage.bind, AsyncLocalStorage.snapshot and the constructor +// options, as in Node. +import { AsyncLocalStorage } from "node:async_hooks"; + +const store = new AsyncLocalStorage(); + +// bind(fn) runs fn in the context active at the bind, with its arguments. +const add = store.run("bound", () => + AsyncLocalStorage.bind((a: number, b: number) => `${store.getStore()}:${a + b}`), +); +console.log(add(1, 2)); +console.log(store.run("other", () => add(3, 4))); + +// A function bound outside any store sees no store inside run(). +const plain = AsyncLocalStorage.bind(() => store.getStore()); +console.log(store.run("ignored", () => plain())); + +// The bound function keeps its result type. +const length = store.run("len", () => AsyncLocalStorage.bind((s: string) => s.length)); +console.log(length("four") * 2); + +// snapshot() runs a function in the context of the snapshot. +const snap = store.run("snapshot", () => AsyncLocalStorage.snapshot()); +console.log(snap(() => store.getStore())); +console.log(store.run("later", () => snap(() => store.getStore()))); +console.log(snap((x: number, y: number) => x * y, 6, 7)); + +// A store entered inside the bound call ends with it. +const enter = store.run("start", () => + AsyncLocalStorage.bind(() => { + store.enterWith("entered"); + return store.getStore(); + }), +); +console.log(enter(), store.getStore()); + +// The default value and the name. +const config = new AsyncLocalStorage<{ level: number }>({ + defaultValue: { level: 1 }, + name: "config", +}); +console.log(config.name, config.getStore()?.level); +config.run({ level: 2 }, () => console.log("run", config.getStore()?.level)); +config.exit(() => console.log("exit", config.getStore())); +config.disable(); +console.log("disabled", config.getStore()?.level); +const unnamed = new AsyncLocalStorage(); +console.log(JSON.stringify(unnamed.name), unnamed.getStore()); +const empty = new AsyncLocalStorage({}); +console.log(JSON.stringify(empty.name), empty.getStore()); +// A class instance's fields are options too. +class StoreOptions { + defaultValue = 7; + name = "from-class"; +} +const fromClass = new AsyncLocalStorage(new StoreOptions()); +console.log(fromClass.name, fromClass.getStore()); + +// Bound functions and snapshots carry the context across asynchronous work. +const later = store.run("timer", () => AsyncLocalStorage.bind(() => store.getStore())); +await new Promise((resolve) => + setTimeout(() => { + console.log("in timer", later(), store.getStore()); + resolve(); + }, 1), +); diff --git a/tests/corpus/7112-als-bind-snapshot-dynamic.mjs b/tests/corpus/7112-als-bind-snapshot-dynamic.mjs new file mode 100644 index 000000000..1e20aefd3 --- /dev/null +++ b/tests/corpus/7112-als-bind-snapshot-dynamic.mjs @@ -0,0 +1,94 @@ +// AsyncLocalStorage.bind, snapshot and the constructor options over +// untyped values, as in Node. +import { AsyncLocalStorage } from "node:async_hooks"; +import { readFile } from "node:fs"; + +const store = new AsyncLocalStorage(); + +function sum(a, b) { + return [this?.tag, a + b, store.getStore()]; +} +const bound = store.run("bound", () => AsyncLocalStorage.bind(sum)); +console.log(bound.name, bound.length, bound === sum, typeof bound); +console.log(JSON.stringify(bound.call({ tag: "receiver" }, 1, 2))); +console.log(JSON.stringify(store.run("other", () => bound(3, 4)))); + +const snap = store.run("snapshot", () => AsyncLocalStorage.snapshot()); +console.log(snap.name, snap.length, typeof snap); +console.log(snap((a, b) => `${store.getStore()} ${a + b}`, 2, 3)); + +for (const attempt of [ + () => AsyncLocalStorage.bind(5), + () => AsyncLocalStorage.bind("fn"), + () => snap(5), + () => snap(), +]) { + try { + console.log("ok", attempt()); + } catch (error) { + console.log(error.name, error.code, error.message); + } +} + +for (const options of [1, "x", null, true]) { + try { + new AsyncLocalStorage(options); + console.log("ok", options); + } catch (error) { + console.log(error.name, error.code, error.message); + } +} +const named = new AsyncLocalStorage({ name: "named", defaultValue: 0 }); +console.log(named.name, named.getStore()); +named.run(5, () => console.log(named.getStore())); +console.log(new AsyncLocalStorage(undefined).name === ""); + +// The options are read as JavaScript reads properties: inherited values +// and getters count, defaultValue is read once and name twice, and the +// name becomes a string. +const inherited = new AsyncLocalStorage(Object.create({ defaultValue: "inherited", name: "proto" })); +console.log(inherited.getStore(), inherited.name); +const reads = []; +const viaGetters = new AsyncLocalStorage({ + get defaultValue() { + reads.push("defaultValue"); + return "getter"; + }, + get name() { + reads.push("name"); + return "getter-name"; + }, +}); +console.log(viaGetters.getStore(), viaGetters.name, reads.join(",")); +const coerced = [5, null, { toString: () => "object" }].map((name) => new AsyncLocalStorage({ name }).name); +console.log(JSON.stringify(coerced)); +for (const options of [ + { + get defaultValue() { + throw new Error("from a getter"); + }, + }, + { name: Symbol("s") }, +]) { + try { + new AsyncLocalStorage(options); + console.log("constructed"); + } catch (error) { + console.log(error.name, error.message); + } +} + +// A snapshot calls its callback with an undefined receiver, also when the +// snapshot itself is called as a method. +const kind = function () { + return this === undefined ? "undefined" : typeof this; +}; +const holder = { snap, tag: "holder" }; +console.log(snap(kind), holder.snap(kind), snap.call({ tag: "receiver" }, kind)); + +// An untyped callback runs in the stores of its call. +store.run("readFile", () => + readFile(import.meta.filename, "utf8", (error, text) => + console.log("readFile", store.getStore(), error === null, text.length > 0), + ), +); diff --git a/tests/corpus/7119-als-net-event-context.mjs b/tests/corpus/7119-als-net-event-context.mjs new file mode 100644 index 000000000..d17d1c31c --- /dev/null +++ b/tests/corpus/7119-als-net-event-context.mjs @@ -0,0 +1,127 @@ +// AsyncLocalStorage contexts of node:net events. A client socket's events +// run in the context of the net.connect() call, whatever context the +// listener was registered in, also after a destroy() from elsewhere. The +// server's 'listening', 'connection' and 'error' events run in the +// context of the listen() call. An accepted socket's own events run with +// no store. write() and end() callbacks, and 'finish', run in the context +// of their call. server.close(callback) runs in the close() call's +// context when no connection is open, and with no store when it waits for +// the last connection. Lines are sorted at the end: events on different +// sockets may interleave. +import { AsyncLocalStorage } from "node:async_hooks"; +import net from "node:net"; + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); + +/** @returns {Promise} */ +function echoScenario() { + return new Promise((resolve) => { + const server = store.run("create", () => + net.createServer((sock) => { + log("server connection callback"); + sock.on("data", () => { + log("accepted data"); + store.run("in-data", () => sock.write("pong")); + }); + sock.on("end", () => { + log("accepted end"); + sock.end(); + }); + sock.on("close", () => log("accepted close")); + }), + ); + server.on("connection", () => log("server connection event")); + server.on("close", () => { + log("server close event"); + resolve(); + }); + store.run("listen", () => + server.listen(0, () => { + log("listen callback"); + const client = store.run("connect", () => net.connect(server.address().port)); + store.run("register", () => { + client.on("connect", () => { + log("client connect"); + store.run("write", () => client.write("ping", () => log("write callback"))); + }); + client.on("data", () => { + log("client data"); + store.run("end-call", () => client.end(() => log("end callback"))); + }); + client.on("finish", () => log("client finish")); + client.on("end", () => log("client end")); + client.on("close", () => { + log("client close"); + store.run("close-call", () => server.close(() => log("server close callback"))); + }); + }); + }), + ); + }); +} + +/** @returns {Promise} */ +function destroyScenario() { + return new Promise((resolve) => { + const server = net.createServer((sock) => sock.on("data", () => {})); + server.listen(0, () => { + const client = store.run("connect-2", () => net.connect(server.address().port)); + client.on("error", () => {}); + client.on("connect", () => { + client.on("close", () => { + log("close after a destroy elsewhere"); + server.close(() => resolve()); + }); + setTimeout(() => store.run("destroy-call", () => client.destroy()), 10); + }); + }); + }); +} + +/** @returns {Promise} */ +function closeScenarios() { + return new Promise((resolve) => { + let pending = 3; + const done = () => { + if (--pending === 0) resolve(); + }; + const waiting = net.createServer((sock) => { + store.run("conn", () => waiting.close(() => { + log("close callback that waited"); + done(); + })); + setTimeout(() => store.run("end", () => sock.end()), 20); + }); + store.run("listen-3", () => + waiting.listen(0, () => { + const client = net.connect(waiting.address().port); + client.on("end", () => client.end()); + }), + ); + const idle = net.createServer(); + store.run("listen-4", () => + idle.listen(0, () => { + store.run("close-now", () => idle.close(() => { + log("close callback of an idle server"); + done(); + })); + }), + ); + const taken = net.createServer(); + taken.listen(0, () => { + const clash = net.createServer(); + clash.on("error", (e) => { + log(`listen error ${e.code}`); + taken.close(() => done()); + }); + store.run("clash-listen", () => clash.listen(taken.address().port)); + }); + }); +} + +await echoScenario(); +await destroyScenario(); +await closeScenarios(); +for (const line of lines.sort()) console.log(line); diff --git a/tests/corpus/7189-non-null-assertion-nullish-destination.ts b/tests/corpus/7189-non-null-assertion-nullish-destination.ts new file mode 100644 index 000000000..632655cff --- /dev/null +++ b/tests/corpus/7189-non-null-assertion-nullish-destination.ts @@ -0,0 +1,57 @@ +// `!` has no runtime effect: a value asserted non-null that still holds +// null or undefined reaches a return value, an arrow function's result or +// an argument whose declared type holds it (or is any or unknown), unchanged. +interface Shape { + size: number; +} + +const shapes = new Map(); +function shapeOf(key: string): Shape | null { + if (shapes.has(key)) return shapes.get(key)!; + const result = key.length > 3 ? { size: key.length } : null; + shapes.set(key, result); + return result; +} +console.log(shapeOf("ab"), shapeOf("abcd")); +console.log(shapeOf("ab"), shapeOf("abcd"), shapes.size); + +const counts = new Map([["a", 1]]); +function countOf(key: string): number | undefined { + return counts.get(key)!; +} +console.log(countOf("a"), countOf("b")); + +function describe(shape: Shape | null): string { + return shape === null ? "none" : `size ${shape.size}`; +} +console.log(describe(shapes.get("ab")!), describe(shapes.get("abcd")!)); + +class Slot { + readonly label: string; + constructor(shape: Shape | null) { + this.label = shape === null ? "empty" : `slot ${shape.size}`; + } +} +console.log(new Slot(shapes.get("ab")!).label, new Slot(shapes.get("abcd")!).label); + +const lookup = (key: string): Shape | null => shapes.get(key)!; +console.log(lookup("ab"), lookup("abcd")?.size); + +const names = new Map([["a", null], ["b", "bee"]]); +const amounts = new Map([["a", undefined], ["b", 2]]); +function kindOf(value: unknown): string { + return value === null ? "null" : typeof value; +} +console.log(String(names.get("a")!), String(names.get("b")!)); +console.log(Number(amounts.get("a")!), Number(amounts.get("b")!)); +console.log(kindOf(names.get("a")!), kindOf(amounts.get("a")!), kindOf(names.get("b")!)); + +// An optional parameter of a standard library method keeps the checked +// narrowing: `decode(input?)` reads the asserted Uint8Array. +const chunks = new Map([["a", new TextEncoder().encode("chunk")]]); +interface Read { + done: boolean; + value?: Uint8Array; +} +const read: Read = { done: false, value: chunks.get("a") }; +console.log(new TextDecoder().decode(chunks.get("a")!), new TextDecoder().decode(read.value!).slice(0, 3)); diff --git a/tests/corpus/7260-teardown-segments.ts b/tests/corpus/7260-teardown-segments.ts new file mode 100644 index 000000000..cbd2b144d --- /dev/null +++ b/tests/corpus/7260-teardown-segments.ts @@ -0,0 +1,179 @@ +// Teardowns that hand their blocks back in order. +// +// A typed teardown of a type with children of its own type keeps the blocks +// it frees in one segment and frees the segment when the chain's first +// teardown returns, so the next structure built of that type reuses them in +// order. This program rebuilds structures of such types for many +// generations and reads every value of each generation before dropping it: +// a block handed out twice, or one freed while still in use, shows up as a +// wrong checksum. The shapes cover the ways a segment is put together: +// record literals (allocated before their fields) and constructors whose +// arguments build the children first, lists built by prepending and by +// appending, trees far deeper than one direct teardown chain and lists +// longer than the runtime's whole depth budget (whose tails wait in its +// queue), subtrees still owned elsewhere when their parent goes, records of +// one type holding records of another, and nodes of a cycle-capable class +// that are still candidates of the cycle collector when the last reference +// goes, and rings of them that only the collector frees. + +interface Node { + left: Node | null; + right: Node | null; + value: number; +} + +function literalTree(depth: number, value: number): Node { + return depth === 0 + ? { left: null, right: null, value } + : { left: literalTree(depth - 1, value * 2), right: literalTree(depth - 1, value * 2 + 1), value }; +} + +function sum(node: Node | null): number { + return node === null ? 0 : node.value + sum(node.left) + sum(node.right); +} + +class Branch { + readonly left: Branch | null; + readonly right: Branch | null; + readonly label: string; + constructor(left: Branch | null, right: Branch | null, label: string) { + this.left = left; + this.right = right; + this.label = label; + } +} + +function classTree(depth: number, label: string): Branch { + return depth === 0 + ? new Branch(null, null, label) + : new Branch(classTree(depth - 1, label + "l"), classTree(depth - 1, label + "r"), label); +} + +function labels(branch: Branch | null): number { + return branch === null ? 0 : branch.label.length + labels(branch.left) + labels(branch.right); +} + +interface Cell { + next: Cell | null; + n: number; +} + +function prepended(count: number, base: number): Cell | null { + let head: Cell | null = null; + for (let i = 0; i < count; i++) head = { next: head, n: base + i }; + return head; +} + +function appended(count: number, base: number): Cell | null { + if (count === 0) return null; + const head: Cell = { next: null, n: base }; + let tail = head; + for (let i = 1; i < count; i++) { + const cell: Cell = { next: null, n: base + i }; + tail.next = cell; + tail = cell; + } + return head; +} + +function cells(head: Cell | null): [number, number] { + let count = 0; + let total = 0; + for (let cell = head; cell !== null; cell = cell.next) { + count++; + total += cell.n; + } + return [count, total]; +} + +// Generations of literal trees: shallow, one chain deep, and several chains +// deep. Each generation is checked against the sum the shape must have. +for (const depth of [0, 1, 5, 16, 17, 18]) { + let ok = true; + for (let generation = 0; generation < 6; generation++) { + const tree = literalTree(depth, 1); + const expected = ((2 ** (depth + 1)) * (2 ** (depth + 1) - 1)) / 2; + if (sum(tree) !== expected) ok = false; + } + console.log(`literal trees of depth ${depth}: ${ok}`); +} + +// Generations of class trees built children first. +for (const depth of [3, 12, 17]) { + let total = 0; + for (let generation = 0; generation < 4; generation++) total += labels(classTree(depth, "t")); + console.log(`class trees of depth ${depth}: ${total}`); +} + +// Lists both ways, alternating, so each reuses the other's blocks; the long +// ones outlast the runtime's depth budget. +for (const count of [1, 15, 16, 17, 1000, 3000, 40000]) { + const seen: string[] = []; + for (let generation = 0; generation < 4; generation++) { + const list = generation % 2 === 0 ? prepended(count, generation) : appended(count, generation); + const [length, total] = cells(list); + seen.push(`${length}/${total}`); + } + console.log(`lists of ${count}: ${seen.join(" ")}`); +} + +// Subtrees owned elsewhere survive their parent's teardown, and the blocks +// freed around them are not handed out while they live. +const kept: Node[] = []; +for (let generation = 0; generation < 5; generation++) { + const tree = literalTree(10, generation + 1); + if (tree.left !== null && tree.left.right !== null) kept.push(tree.left.right); + if (tree.right !== null) kept.push(tree.right); + // Building the next tree reuses the dropped blocks. + literalTree(10, 7); +} +console.log(`kept subtrees: ${kept.length} ${kept.map(sum).join(",")}`); + +// A record of one type holding trees of another: the outer teardown frees +// its own blocks as before and tears each inner tree down as a segment. +interface Forest { + first: Node; + second: Node | null; + rest: Forest | null; +} + +function forest(count: number): Forest | null { + let out: Forest | null = null; + for (let i = 0; i < count; i++) out = { first: literalTree(4, i), second: i % 3 === 0 ? null : literalTree(2, i), rest: out }; + return out; +} + +let forestTotal = 0; +for (let generation = 0; generation < 3; generation++) { + for (let f = forest(500); f !== null; f = f.rest) forestTotal += sum(f.first) + sum(f.second); +} +console.log(`forests: ${forestTotal}`); + +// A cycle-capable class: releases of shared links make nodes candidates of +// the cycle collector, which are then torn down while still buffered, and +// the peer links close some rings, which the collector frees. +class Ring { + next: Ring | null = null; + peer: Ring | null = null; + id: number; + constructor(id: number) { + this.id = id; + } +} + +let ringTotal = 0; +for (let generation = 0; generation < 3; generation++) { + let head: Ring | null = null; + const peers: Ring[] = []; + for (let i = 0; i < 2000; i++) { + const ring: Ring = new Ring(i); + ring.next = head; + if (i % 7 === 0) peers.push(ring); + head = ring; + } + for (let r = head; r !== null; r = r.next) if (r.id % 5 === 0) r.peer = peers[r.id % peers.length] ?? null; + for (const peer of peers) ringTotal += peer.id; + peers.length = 0; + for (let r = head; r !== null; r = r.next) ringTotal += r.peer === null ? 0 : r.peer.id; +} +console.log(`rings: ${ringTotal}`); diff --git a/tests/corpus/7264-array-dense-paired-loops.ts b/tests/corpus/7264-array-dense-paired-loops.ts new file mode 100644 index 000000000..59cedf60b --- /dev/null +++ b/tests/corpus/7264-array-dense-paired-loops.ts @@ -0,0 +1,120 @@ +// Two-counter loops that index number arrays at both counters +// (`for (let i = lo, j = hi; i < j; i++, j--)`), and element copies through +// `a[i]!` reads, both inside and outside the ranges a single entry test can +// cover: filled prefixes, holes, present undefined, reads past the end, +// fractional and negative starts, int32 arrays and long checked arrays. + +function reversePrefix(a: number[], end: number): void { + for (let i = 0, j = end; i < j; i++, j--) { + const t = a[i]!; + a[i] = a[j]!; + a[j] = t; + } +} + +function show(label: string, a: number[]): void { + console.log(label, a.length, JSON.stringify(a), a); +} + +// The whole prefix is present: the entry test passes. +const plain: number[] = []; +for (let k = 0; k < 8; k++) plain[k] = k * 3; +reversePrefix(plain, 5); +show("plain", plain); +reversePrefix(plain, 7); +show("whole", plain); +reversePrefix(plain, 0); +show("empty range", plain); + +// The high start is past the end: the reads there miss, the stores extend. +const short = [1, 2, 3]; +reversePrefix(short, 5); +show("past the end", short); + +// A hole inside the range. +const holey: number[] = [10, 20]; +holey[3] = 40; +holey[4] = 50; +reversePrefix(holey, 4); +show("hole", holey); + +// A present undefined inside the range, copied by a mirror loop. +const loose: number[] = [7, 8, 9, 10]; +(loose as (number | undefined)[])[2] = undefined; +for (let i = 0, j = 3; i < j; i++, j--) loose[i] = loose[j]!; +show("undefined", loose); + +// Fractional and negative starts index non-element properties. +const fractional = [1, 2, 3, 4]; +reversePrefix(fractional, 2.5); +show("fractional", fractional); +const negative = [5, 6, 7]; +for (let i = -1, j = 2; i < j; i++, j--) negative[i] = negative[j]!; +show("negative", negative); + +// A local int32 array (every stored element is a small integer). +function int32Flips(start: number): void { + const counts = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let flips = 0; + for (let first = counts[start]!; first !== 0; first = counts[0]!) { + for (let i = 0, j = first; i < j; i++, j--) { + const t = counts[i]!; + counts[i] = counts[j]!; + counts[j] = t; + } + flips++; + if (flips > 20) break; + } + console.log("int32", flips, counts.join(",")); +} +int32Flips(3); +int32Flips(9); + +// A local long array with checked int32 stores, widened by a fraction. +function longChecked(n: number, fraction: number): void { + const long = new Array(n).fill(0); + for (let k = 0; k < long.length; k++) long[k] = k; + for (let i = 0, j = n - 1; i < j; i++, j--) { + const t = long[i]!; + long[i] = long[j]!; + long[j] = t; + } + console.log("long", long[0], long[1], long[n - 2], long[n - 1]); + long[10] = fraction; + for (let i = 0, j = 20; i < j; i++, j--) { + const t = long[i]!; + long[i] = long[j]!; + long[j] = t; + } + console.log("widened", long.slice(0, 12).join(","), long[20]); +} +longChecked(5000, 0.5); + +// Element copies through `a[i]!`: the source is shorter than the range. +function copy(to: number[], from: number[], n: number): void { + for (let i = 0; i < n; i++) to[i] = from[i]!; +} +const into = [0, 0, 0, 0, 0, 0]; +copy(into, [1, 2, 3, 4, 5, 6], 6); +show("copy", into); +copy(into, [9, 8], 4); +show("short source", into); +const gappy: number[] = [1]; +gappy[2] = 3; +gappy[3] = 4; +copy(into, gappy, 4); +show("gappy source", into); + +// Two names for one array. +function aliased(n: number): void { + const same: number[] = []; + for (let k = 0; k < n; k++) same[k] = k + 1; + const alias = same; + for (let i = 0, j = n - 1; i < j; i++, j--) { + const t = alias[i]!; + same[i] = alias[j]!; + same[j] = t; + } + show("alias", same); +} +aliased(5); diff --git a/tests/corpus/7306-dunder-exports/dep.ts b/tests/corpus/7306-dunder-exports/dep.ts new file mode 100644 index 000000000..6d54eb365 --- /dev/null +++ b/tests/corpus/7306-dunder-exports/dep.ts @@ -0,0 +1,19 @@ +export const __x = 1; +export const ___y = "three"; +export let __count = 0; +export function __bump(): number { + __count++; + return __count; +} +export class __Point { + a: number; + b: number; + constructor(a: number, b: number) { + this.a = a; + this.b = b; + } + sum(): number { + return this.a + this.b; + } +} +export const plain = "plain"; diff --git a/tests/corpus/7306-dunder-exports/facade.ts b/tests/corpus/7306-dunder-exports/facade.ts new file mode 100644 index 000000000..89ab6eb32 --- /dev/null +++ b/tests/corpus/7306-dunder-exports/facade.ts @@ -0,0 +1,2 @@ +export { __x, __x as __alias, ___y as renamed } from "./dep.ts"; +export * from "./star.ts"; diff --git a/tests/corpus/7306-dunder-exports/main.ts b/tests/corpus/7306-dunder-exports/main.ts new file mode 100644 index 000000000..415bc4a77 --- /dev/null +++ b/tests/corpus/7306-dunder-exports/main.ts @@ -0,0 +1,20 @@ +// Exports whose names start with "__": named imports of a const, a function, +// a class and a live let, a name with three underscores, aliased re-exports, +// names reached through `export *`, namespace member reads and keys, and +// CommonJS exports read through require. +import { __x, ___y, __count, __bump, __Point, plain } from "./dep.ts"; +import { __alias, renamed, __starred, __twice } from "./facade.ts"; +import * as facade from "./facade.ts"; +import * as depNs from "./dep.ts"; +import { createRequire } from "node:module"; + +console.log(__x, ___y, plain); +console.log(__count, __bump(), __bump(), __count); +console.log(new __Point(2, 3).sum()); +console.log(__alias, renamed, __starred, __twice(21)); +console.log(facade.__x, facade.__starred, facade.__twice(2)); +console.log(Object.keys(depNs).join(",")); + +const require = createRequire(import.meta.url); +const table = require("./table.cjs"); +console.log(table.__a, table.__hello("dunder"), table.b); diff --git a/tests/corpus/7306-dunder-exports/star.ts b/tests/corpus/7306-dunder-exports/star.ts new file mode 100644 index 000000000..6ca21d73d --- /dev/null +++ b/tests/corpus/7306-dunder-exports/star.ts @@ -0,0 +1,4 @@ +export const __starred = "from export *"; +export function __twice(n: number): number { + return n * 2; +} diff --git a/tests/corpus/7306-dunder-exports/table.cjs b/tests/corpus/7306-dunder-exports/table.cjs new file mode 100644 index 000000000..e775925ff --- /dev/null +++ b/tests/corpus/7306-dunder-exports/table.cjs @@ -0,0 +1,5 @@ +exports.__a = 10; +exports.__hello = function (name) { + return "hello " + name; +}; +exports.b = 20; diff --git a/tests/corpus/7309-globalthis-buffer-aliases.ts b/tests/corpus/7309-globalthis-buffer-aliases.ts new file mode 100644 index 000000000..bf0b9ba5c --- /dev/null +++ b/tests/corpus/7309-globalthis-buffer-aliases.ts @@ -0,0 +1,43 @@ +// Aliases of the global Buffer constructor read through a cast of +// globalThis, the capability-probe spelling of code that also runs where +// Buffer is absent: typed and `as any` casts, `if`, ternary, typeof and `!` +// guards, member calls through the alias, and typeof of the alias. +type RuntimeBuffer = { + byteLength(value: string, encoding: "utf8"): number; + from(value: string, encoding: "utf8"): Uint8Array; + isBuffer(value: unknown): boolean; +}; + +const runtimeBuffer = (globalThis as { Buffer?: RuntimeBuffer }).Buffer; +const anyBuffer = (globalThis as any).Buffer; +const text = "héllo wörld"; + +if (runtimeBuffer) { + console.log("typed", runtimeBuffer.byteLength(text, "utf8")); +} else { + console.log("typed: no Buffer"); +} +if (typeof anyBuffer === "function") { + console.log("any", anyBuffer.byteLength(text, "utf8")); +} +const bytes = runtimeBuffer ? runtimeBuffer.from(text, "utf8") : undefined; +console.log("from", bytes ? bytes.length : -1); +console.log("isBuffer", runtimeBuffer ? runtimeBuffer.isBuffer(bytes) : false); +console.log("typeof", typeof runtimeBuffer, typeof anyBuffer, !runtimeBuffer); + +function utf8Length(value: string): number { + return runtimeBuffer ? runtimeBuffer.byteLength(value, "utf8") : value.length; +} +console.log("function", utf8Length("✓✓"), utf8Length("abc")); + +// A copy of an alias, and an alias whose cast read is guarded with `||`, +// alias the same constructor. +const copied = runtimeBuffer; +const fallback: RuntimeBuffer = { + byteLength: (value: string) => value.length, + from: (value: string) => new Uint8Array(value.length), + isBuffer: () => false, +}; +const orFallback = (globalThis as { Buffer?: RuntimeBuffer }).Buffer || fallback; +console.log("copy", copied ? copied.byteLength(text, "utf8") : -1, typeof copied, !copied); +console.log("or", orFallback.byteLength(text, "utf8"), orFallback.isBuffer(bytes), typeof orFallback); diff --git a/tests/corpus/7310-any-destructure-string-keys.ts b/tests/corpus/7310-any-destructure-string-keys.ts new file mode 100644 index 000000000..4ab24ec77 --- /dev/null +++ b/tests/corpus/7310-any-destructure-string-keys.ts @@ -0,0 +1,60 @@ +// Destructuring an `any` source read through a string-literal key +// (`const { value } = events["a.b"]`): reads, renames, defaults, rest, a +// nested pattern, the assignment form, a template-literal key, chained +// string keys, and V8's TypeError for undefined and null sources, whose +// message spells the key as a property name with its text decoded. +const events: any = { + "a.b": { value: 1, label: "first", extra: true, nested: { deep: "d" } }, + "x-y": { value: 2 }, + outer: { "in.ner": { value: 3 } }, + "nul": null, +}; + +const { value } = events["a.b"]; +const { value: renamed, missing = "fallback", ...rest } = events["a.b"]; +const { + nested: { deep }, +} = events["a.b"]; +console.log(value, renamed, missing, JSON.stringify(rest), deep); + +let assigned: unknown; +({ value: assigned } = events[`x-y`]); +const { value: chained } = events["outer"]["in.ner"]; +console.log(assigned, chained); + +function attempt(label: string, f: () => unknown): void { + try { + console.log(label, f()); + } catch (e) { + console.log(label, e instanceof TypeError, (e as Error).message); + } +} +attempt("missing", () => { + const { value } = events["no.such"]; + return value; +}); +attempt("null", () => { + const { value } = events["nul"]; + return value; +}); +attempt("quotes", () => { + const { value } = events["it's \"quoted\""]; + return value; +}); +attempt("escapes", () => { + const { value } = events["tab\there\nnext"]; + return value; +}); +attempt("accents", () => { + const { value } = events["été ✓"]; + return value; +}); +attempt("template", () => { + const { value } = events[`plain`]; + return value; +}); +attempt("assignment", () => { + let target: unknown; + ({ value: target } = events["gone"]); + return target; +}); diff --git a/tests/corpus/7380-als-http-event-context.mjs b/tests/corpus/7380-als-http-event-context.mjs new file mode 100644 index 000000000..fd6ef6225 --- /dev/null +++ b/tests/corpus/7380-als-http-event-context.mjs @@ -0,0 +1,155 @@ +// AsyncLocalStorage contexts of node:http, node:https and node:tls events. +// A server's 'request' event and its requests' and responses' events run +// in the context of the server's listen() call. A client request's +// events, its response callback and its response's events run in the +// context of the http.request() call, also on a keep-alive socket that +// an earlier request used. TLS client sockets run in the tls.connect() +// call's context, and a TLS server's connections in its listen() +// context. Lines are sorted at the end: events on different sockets may +// interleave. +import { AsyncLocalStorage } from "node:async_hooks"; +import * as fs from "node:fs"; +import http from "node:http"; +import https from "node:https"; +import tls from "node:tls"; + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); + +/** @returns {Promise} */ +function httpScenario() { + return new Promise((resolve) => { + const server = store.run("create", () => + http.createServer((req, res) => { + log("request callback"); + req.on("data", () => log("request data")); + req.on("end", () => { + log("request end"); + res.end("ok"); + }); + res.on("finish", () => log("response finish")); + res.on("close", () => log("response close")); + }), + ); + server.on("request", () => log("request event")); + store.run("listen", () => + server.listen(0, () => { + const port = server.address().port; + const req = store.run("request-call", () => + http.request({ port, method: "POST" }, (res) => { + log("response callback"); + res.on("data", () => log("client response data")); + res.on("end", () => log("client response end")); + }), + ); + store.run("register", () => { + req.on("socket", () => log("client socket")); + req.on("finish", () => log("client finish")); + req.on("close", () => { + log("client close"); + server.close(() => resolve()); + }); + }); + store.run("end-call", () => req.end("body")); + }), + ); + }); +} + +/** @returns {Promise} */ +function keepAliveScenario() { + return new Promise((resolve) => { + const agent = new http.Agent({ keepAlive: true, maxSockets: 1 }); + const server = http.createServer((req, res) => res.end("ok")); + server.listen(0, () => { + const port = server.address().port; + let done = 0; + for (const name of ["first", "second"]) { + store.run(name, () => { + const req = http.get({ port, agent }, (res) => { + log(`${name} response`); + res.on("data", () => {}); + res.on("end", () => { + log(`${name} response end`); + if (++done === 2) { + agent.destroy(); + server.close(() => resolve()); + } + }); + }); + req.on("finish", () => log(`${name} finish`)); + req.on("close", () => log(`${name} close`)); + }); + } + }); + }); +} + +const key = fs.readFileSync("tests/fixtures/server/certs/localhost-key.pem"); +const cert = fs.readFileSync("tests/fixtures/server/certs/localhost.pem"); +const ca = fs.readFileSync("tests/fixtures/server/certs/ca.pem", "utf8"); + +/** @returns {Promise} */ +function httpsScenario() { + return new Promise((resolve) => { + const server = https.createServer({ key, cert }, (req, res) => { + log("https request"); + req.resume(); + res.on("finish", () => log("https response finish")); + res.end("ok"); + }); + store.run("https-listen", () => + server.listen(0, () => { + const port = server.address().port; + store.run("https-get", () => + https + .get({ port, host: "localhost", ca }, (res) => { + log("https response"); + res.on("data", () => {}); + res.on("end", () => log("https response end")); + }) + .on("close", () => { + log("https client close"); + server.close(() => resolve()); + }), + ); + }), + ); + }); +} + +/** @returns {Promise} */ +function tlsScenario() { + return new Promise((resolve) => { + const server = tls.createServer({ key, cert }, (sock) => { + log("tls secureConnection"); + sock.on("data", () => { + log("tls server data"); + sock.end("bye"); + }); + }); + store.run("tls-listen", () => + server.listen(0, () => { + const client = store.run("tls-connect", () => + tls.connect({ port: server.address().port, host: "localhost", ca }, () => { + log("tls secureConnect"); + client.write("hi"); + }), + ); + client.on("data", () => log("tls client data")); + client.on("end", () => log("tls client end")); + client.on("close", () => { + log("tls client close"); + server.close(() => resolve()); + }); + }), + ); + }); +} + +await httpScenario(); +await keepAliveScenario(); +await httpsScenario(); +await tlsScenario(); +for (const line of lines.sort()) console.log(line); diff --git a/tests/corpus/7380-cycle-deferred-folded-key/core.ts b/tests/corpus/7380-cycle-deferred-folded-key/core.ts new file mode 100644 index 000000000..7f4f4ab14 --- /dev/null +++ b/tests/corpus/7380-cycle-deferred-folded-key/core.ts @@ -0,0 +1,5 @@ +import { describe, kind, mark } from "./util.ts"; + +export const version = 3; +export const marked = mark({}); +console.log("core body", kind(), describe()); diff --git a/tests/corpus/7380-cycle-deferred-folded-key/main.ts b/tests/corpus/7380-cycle-deferred-folded-key/main.ts new file mode 100644 index 000000000..92617eba3 --- /dev/null +++ b/tests/corpus/7380-cycle-deferred-folded-key/main.ts @@ -0,0 +1,10 @@ +// A cycle member runs user code before it declares a constant that only +// functions read, one of them as a computed key and one under `typeof` +// (both resolved from the constant's literal type). Nothing calls those +// functions before the declaration ran, so they read the constant +// normally once the other member's top level calls them. +import { marked } from "./core.ts"; +import { describe, kind, mark } from "./util.ts"; + +console.log(JSON.stringify(marked), kind()); +console.log(JSON.stringify(mark({ seen: false })), describe()); diff --git a/tests/corpus/7380-cycle-deferred-folded-key/util.ts b/tests/corpus/7380-cycle-deferred-folded-key/util.ts new file mode 100644 index 000000000..a038d9e63 --- /dev/null +++ b/tests/corpus/7380-cycle-deferred-folded-key/util.ts @@ -0,0 +1,23 @@ +import { version } from "./core.ts"; + +export function makeCounter(): () => number { + let n = 0; + return () => ++n; +} +const next = makeCounter(); +console.log("util body", next(), next()); + +export const TAG = "~tagged"; + +export function mark(box: Record): Record { + box[TAG] = true; + return box; +} + +export function kind(): string { + return typeof TAG; +} + +export function describe(): string { + return `core v${version}`; +} diff --git a/tests/corpus/7381-als-async-generator-steps.ts b/tests/corpus/7381-als-async-generator-steps.ts new file mode 100644 index 000000000..4f6ace58d --- /dev/null +++ b/tests/corpus/7381-als-async-generator-steps.ts @@ -0,0 +1,133 @@ +// AsyncLocalStorage contexts of async generator steps. Each step starts in +// the context of the next(), return() or throw() call that resumes it, +// whatever context created the generator; a store the step enters before +// its first await is the caller's after the call, and after an await the +// step keeps its own copy. A request queued behind a running step starts +// in that step's context, and a return() at a yield unwinds in the +// return() call's context. Covers yield*, for await with break, and two +// generators resumed in turn. +import { AsyncLocalStorage } from "node:async_hooks"; + +const store = new AsyncLocalStorage(); +const lines: string[] = []; +const log = (what: string): void => { + lines.push(`${what}: ${store.getStore() ?? "none"}`); +}; + +async function* steps(): AsyncGenerator { + log("body start"); + yield 1; + log("after the first yield"); + await null; + log("after an await"); + store.enterWith("inside"); + yield 2; + log("after the second yield"); + yield 3; + log("body end"); +} +const it = store.run("create", () => steps()); +await store.run("next-1", () => it.next()); +log("caller after next-1"); +await store.run("next-2", () => it.next()); +log("caller after next-2"); +await store.run("next-3", () => it.next()); +await store.run("next-4", () => it.next()); + +async function* prefixes(): AsyncGenerator { + store.enterWith("prefix"); + yield 1; + store.enterWith("step-2"); + await null; + log("after an await in step 2"); + yield 2; +} +await store.run("outer", async () => { + const gen = prefixes(); + const first = gen.next(); + log("synchronously after next"); + await first; + log("after awaiting next"); + const second = gen.next(); + log("synchronously after the second next"); + await second; + log("after awaiting the second next"); +}); + +async function* queued(): AsyncGenerator { + log("queued start"); + yield 1; + log("queued resumed"); + yield 2; +} +const q = queued(); +await Promise.all([store.run("q-1", () => q.next()), store.run("q-2", () => q.next())]); + +async function* inner(): AsyncGenerator { + log("inner start"); + yield "a"; + log("inner resumed"); + yield "b"; +} +async function* outerGen(): AsyncGenerator { + log("outer start"); + yield* inner(); + log("outer after yield*"); +} +const delegating = outerGen(); +await store.run("o-1", () => delegating.next()); +await store.run("o-2", () => delegating.next()); +await store.run("o-3", () => delegating.next()); + +async function* catching(): AsyncGenerator { + try { + yield 1; + } catch (e) { + log(`caught ${(e as Error).message}`); + yield 2; + } +} +const c = catching(); +await store.run("t-1", () => c.next()); +await store.run("t-2", () => c.throw(new Error("boom"))); + +async function* finallyGen(): AsyncGenerator { + try { + yield 1; + } finally { + log("finally"); + } +} +const f = finallyGen(); +await store.run("n", () => f.next()); +await store.run("return-call", () => f.return()); + +async function* endless(): AsyncGenerator { + let i = 0; + try { + while (true) { + log(`endless ${i}`); + yield i++; + } + } finally { + log("endless finally"); + } +} +await store.run("loop", async () => { + for await (const v of endless()) { + if (v === 1) break; + } +}); + +async function* named(name: string): AsyncGenerator { + log(`${name} 1`); + yield 1; + log(`${name} 2`); + yield 2; +} +const a = named("A"); +const b = named("B"); +await Promise.all([store.run("a-1", () => a.next()), store.run("b-1", () => b.next())]); +await Promise.all([store.run("b-2", () => b.next()), store.run("a-2", () => a.next())]); + +for (const line of lines) console.log(line); diff --git a/tests/corpus/7381-cycle-deferred-reached-tdz/alpha.ts b/tests/corpus/7381-cycle-deferred-reached-tdz/alpha.ts new file mode 100644 index 000000000..8dc002c27 --- /dev/null +++ b/tests/corpus/7381-cycle-deferred-reached-tdz/alpha.ts @@ -0,0 +1,13 @@ +import { probe } from "./beta.ts"; + +export function report(): string { + return `alpha ${NAME}`; +} + +export function late(): string { + return `${OTHER} after ${probe}`; +} + +export const NAME = "ready"; +export const OTHER = "other"; +console.log("alpha body", report()); diff --git a/tests/corpus/7381-cycle-deferred-reached-tdz/beta.ts b/tests/corpus/7381-cycle-deferred-reached-tdz/beta.ts new file mode 100644 index 000000000..c3d1551ff --- /dev/null +++ b/tests/corpus/7381-cycle-deferred-reached-tdz/beta.ts @@ -0,0 +1,12 @@ +import { report } from "./alpha.ts"; + +function attempt(): string { + try { + return report(); + } catch (error) { + return error instanceof ReferenceError ? `ReferenceError: ${error.message}` : "other error"; + } +} + +export const probe = attempt(); +console.log("beta body", probe); diff --git a/tests/corpus/7381-cycle-deferred-reached-tdz/main.ts b/tests/corpus/7381-cycle-deferred-reached-tdz/main.ts new file mode 100644 index 000000000..6a0b7e3ca --- /dev/null +++ b/tests/corpus/7381-cycle-deferred-reached-tdz/main.ts @@ -0,0 +1,7 @@ +// The first cycle member to run calls a function of the other member +// before that member declared the constant the function reads: Node's +// temporal-dead-zone ReferenceError. A second function of the same member +// reads another constant but runs only after the cycle finished. +import { late, report } from "./alpha.ts"; + +console.log(report(), late()); diff --git a/tests/corpus/7382-als-async-generator-steps.mjs b/tests/corpus/7382-als-async-generator-steps.mjs new file mode 100644 index 000000000..bbabdb592 --- /dev/null +++ b/tests/corpus/7382-als-async-generator-steps.mjs @@ -0,0 +1,43 @@ +// AsyncLocalStorage contexts of async generator steps from JavaScript: +// steps start in the context of the call that resumes them, a store a step +// enters before its first await is the caller's, queued requests start in +// the running step's context, and return() at a yield unwinds in the +// return() call's context. +import { AsyncLocalStorage } from "node:async_hooks"; +const s = new AsyncLocalStorage(); +const lines = []; +const g = (t) => lines.push(`${t}: ${s.getStore() ?? "none"}`); +async function* gen() { + g("body start"); + yield 1; + g("after first yield"); + await null; + g("after await"); + s.enterWith("inside"); + yield 2; + g("after second yield"); + yield 3; + g("body end"); +} +const it = s.run("create", () => gen()); +await s.run("next1", () => it.next()); +g("caller after next1"); +await s.run("next2", () => it.next()); +g("caller after next2"); +await s.run("next3", () => it.next()); +await s.run("next4", () => it.next()); +// concurrent next calls queued +async function* gen2() { g("g2 start"); yield 1; g("g2 resumed"); yield 2; } +const it2 = gen2(); +const p1 = s.run("q1", () => it2.next()); +const p2 = s.run("q2", () => it2.next()); +await Promise.all([p1, p2]); +// for await +async function* gen3() { for (let i = 0; i < 2; i++) { g("g3 step " + i); yield i; } } +await s.run("for-await", async () => { for await (const v of gen3()) g("loop body " + v); }); +// return/throw +async function* gen4() { try { yield 1; } finally { g("g4 finally"); } } +const it4 = gen4(); +await s.run("n", () => it4.next()); +await s.run("ret", () => it4.return()); +for (const line of lines) console.log(line); diff --git a/tests/corpus/7382-cycle-static-this-tdz/alpha.ts b/tests/corpus/7382-cycle-static-this-tdz/alpha.ts new file mode 100644 index 000000000..ff8eb4a87 --- /dev/null +++ b/tests/corpus/7382-cycle-static-this-tdz/alpha.ts @@ -0,0 +1,11 @@ +import { Probe, Lazy } from "./beta.ts"; + +export { Probe }; +export const label = "ready"; +export const other = "other"; + +export function later(): string { + return `${Lazy.describe()} ${other}`; +} + +console.log("alpha body", Probe.result, Lazy.value); diff --git a/tests/corpus/7382-cycle-static-this-tdz/beta.ts b/tests/corpus/7382-cycle-static-this-tdz/beta.ts new file mode 100644 index 000000000..428907967 --- /dev/null +++ b/tests/corpus/7382-cycle-static-this-tdz/beta.ts @@ -0,0 +1,26 @@ +import { label, other } from "./alpha.ts"; + +function guard(read: () => string): string { + try { + return read(); + } catch (error) { + return error instanceof ReferenceError ? `ReferenceError: ${error.message}` : "other error"; + } +} + +export class Probe { + static helper(): string { + return guard(() => `label ${label}`); + } + static result = this.helper(); +} + +export class Lazy { + static read(): string { + return guard(() => `other ${other}`); + } + static describe(): string { + return `lazy ${other}`; + } + static value = [0].map(() => this.read()).join(""); +} diff --git a/tests/corpus/7382-cycle-static-this-tdz/main.ts b/tests/corpus/7382-cycle-static-this-tdz/main.ts new file mode 100644 index 000000000..e3bbf1581 --- /dev/null +++ b/tests/corpus/7382-cycle-static-this-tdz/main.ts @@ -0,0 +1,8 @@ +// The first cycle member to run builds classes whose static initializers +// call a method through `this`, directly and from an arrow function; both +// methods read constants of the other member before it declared them: +// Node's temporal-dead-zone ReferenceError. A third method reads a constant +// only after the cycle finished. +import { label, Probe, later } from "./alpha.ts"; + +console.log(label, Probe.result, later()); diff --git a/tests/corpus/7383-als-stream-events.ts b/tests/corpus/7383-als-stream-events.ts new file mode 100644 index 000000000..077e15346 --- /dev/null +++ b/tests/corpus/7383-als-stream-events.ts @@ -0,0 +1,204 @@ +// AsyncLocalStorage contexts of stream events and callbacks. A stream +// step that Node defers with process.nextTick runs in the context of the +// code that scheduled it; other events run in the context of the call that +// emits them: push(), write(), end(), destroy() or pipe(). stream.finished +// and stream.pipeline callbacks run in the context of their call. fs read +// and write streams and zlib streams run their events in the context that +// created them, as Node's handles do. +import { AsyncLocalStorage } from "node:async_hooks"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { finished, pipeline, Readable, Transform, Writable } from "node:stream"; +import { pipeline as pipelinePromise } from "node:stream/promises"; +import * as zlib from "node:zlib"; + +const store = new AsyncLocalStorage(); +const lines: string[] = []; +const log = (what: string): void => { + lines.push(`${what}: ${store.getStore() ?? "none"}`); +}; +const step = (fn: (done: () => void) => void): Promise => + new Promise((resolve) => fn(resolve)); + +// pipe(): the destination's writes and 'finish' follow the pipe() call. +await step((done) => { + const source = new Readable({ read(): void {} }); + const sink = new Writable({ + write(chunk: Buffer, encoding: BufferEncoding, cb: (error?: Error | null) => void): void { + log("pipe write"); + cb(); + }, + }); + sink.on("finish", () => { + log("pipe finish"); + done(); + }); + store.run("pipe", () => source.pipe(sink)); + store.run("push", () => { + source.push("a"); + source.push(null); + }); +}); + +// A Transform's steps follow write() and end(); 'end' follows end(). +await step((done) => { + const upper = new Transform({ + transform(chunk: Buffer, encoding: BufferEncoding, cb: (error?: Error | null, data?: string) => void): void { + log("transform"); + cb(null, chunk.toString().toUpperCase()); + }, + flush(cb: (error?: Error | null, data?: string) => void): void { + log("flush"); + cb(null, "!"); + }, + }); + upper.on("data", () => log("transform data")); + upper.on("end", () => { + log("transform end"); + done(); + }); + store.run("write", () => upper.write("a")); + store.run("end", () => upper.end()); +}); + +// A write that completes later: its callback, _final, 'prefinish', the +// end() callback, 'finish' and 'close' follow the write() call. +await step((done) => { + const slow = new Writable({ + write(chunk: Buffer, encoding: BufferEncoding, cb: (error?: Error | null) => void): void { + log("slow write"); + setImmediate(cb); + }, + final(cb: (error?: Error | null) => void): void { + log("final"); + cb(); + }, + }); + slow.on("prefinish", () => log("prefinish")); + slow.on("finish", () => log("finish")); + slow.on("close", () => { + log("close"); + done(); + }); + store.run("slow-write", () => slow.write("a", () => log("write callback"))); + store.run("slow-end", () => slow.end(() => log("end callback"))); +}); + +// 'readable' and 'end' follow the push() that triggers them. +await step((done) => { + const source = new Readable({ read(): void {} }); + source.on("readable", () => { + log("readable"); + while (source.read() !== null) {} + }); + source.on("end", () => { + log("readable end"); + done(); + }); + store.run("push-x", () => source.push("x")); + setTimeout(() => store.run("push-eof", () => source.push(null)), 5); +}); + +// destroy(): 'error' and 'close' follow the destroy() call. +await step((done) => { + const source = new Readable({ read(): void {} }); + source.on("error", () => log("destroy error")); + source.on("close", () => { + log("destroy close"); + done(); + }); + store.run("destroy", () => source.destroy(new Error("stopped"))); +}); + +// finished() and pipeline() callbacks follow their call, also when another +// context ends or destroys the stream. +await step((done) => { + const source = new Readable({ read(): void {} }); + store.run("finished", () => + finished(source, () => { + log("finished callback"); + done(); + }), + ); + source.resume(); + store.run("finished-eof", () => source.push(null)); +}); +await step((done) => { + const source = new Readable({ read(): void {} }); + const sink = new Writable({ + write(chunk: Buffer, encoding: BufferEncoding, cb: (error?: Error | null) => void): void { + cb(); + }, + }); + store.run("pipeline", () => + pipeline(source, sink, (err: NodeJS.ErrnoException | null) => { + log(`pipeline callback ${err ? err.message : "ok"}`); + done(); + }), + ); + setTimeout(() => store.run("pipeline-destroy", () => source.destroy(new Error("boom"))), 5); +}); + +// stream/promises pipeline: the stages follow the call; after the await +// the caller's context is back. +await store.run("promise-pipeline", () => + pipelinePromise( + Readable.from(["a"]), + new Writable({ + write(chunk: Buffer, encoding: BufferEncoding, cb: (error?: Error | null) => void): void { + log("promise pipeline write"); + cb(); + }, + }), + ), +); +log("after the promise pipeline"); + +// fs streams: the events follow the context that created the stream. +const file = path.join(os.tmpdir(), `scriptc-7383-${process.pid}.txt`); +await step((done) => { + const out = store.run("fs-write-create", () => fs.createWriteStream(file)); + out.on("finish", () => log("fs finish")); + out.on("close", () => { + log("fs write close"); + done(); + }); + store.run("fs-write", () => out.write("hello", () => log("fs write callback"))); + store.run("fs-end", () => out.end()); +}); +await step((done) => { + const input = store.run("fs-read-create", () => fs.createReadStream(file)); + store.run("fs-listen", () => { + input.on("open", () => log("fs open")); + input.on("data", () => log("fs data")); + input.on("end", () => log("fs end")); + input.on("close", () => { + log("fs read close"); + done(); + }); + }); +}); +fs.unlinkSync(file); + +// zlib streams: the events follow the context that created the stream. +await step((done) => { + const gzip = store.run("gzip-create", () => zlib.createGzip()); + const gunzip = store.run("gunzip-create", () => zlib.createGunzip()); + const parts: Buffer[] = []; + const sink = new Writable({ + write(chunk: Buffer, encoding: BufferEncoding, cb: (error?: Error | null) => void): void { + log("gunzip output"); + parts.push(chunk); + cb(); + }, + }); + store.run("zlib-pipeline", () => + pipeline(Readable.from(["hello ", "world"]), gzip, gunzip, sink, () => { + log(`zlib pipeline callback ${Buffer.concat(parts).toString()}`); + done(); + }), + ); +}); + +for (const line of lines) console.log(line); diff --git a/tests/corpus/7383-cycle-class-expression-name-tdz/alpha.ts b/tests/corpus/7383-cycle-class-expression-name-tdz/alpha.ts new file mode 100644 index 000000000..ff8eb4a87 --- /dev/null +++ b/tests/corpus/7383-cycle-class-expression-name-tdz/alpha.ts @@ -0,0 +1,11 @@ +import { Probe, Lazy } from "./beta.ts"; + +export { Probe }; +export const label = "ready"; +export const other = "other"; + +export function later(): string { + return `${Lazy.describe()} ${other}`; +} + +console.log("alpha body", Probe.result, Lazy.value); diff --git a/tests/corpus/7383-cycle-class-expression-name-tdz/beta.ts b/tests/corpus/7383-cycle-class-expression-name-tdz/beta.ts new file mode 100644 index 000000000..d1d89ad1a --- /dev/null +++ b/tests/corpus/7383-cycle-class-expression-name-tdz/beta.ts @@ -0,0 +1,26 @@ +import { label, other } from "./alpha.ts"; + +function guard(read: () => string): string { + try { + return read(); + } catch (error) { + return error instanceof ReferenceError ? `ReferenceError: ${error.message}` : "other error"; + } +} + +export const Probe = class ProbeImpl { + static helper(): string { + return guard(() => `label ${label}`); + } + static result = ProbeImpl.helper(); +}; + +export const Lazy = class LazyImpl { + static read(): string { + return guard(() => `other ${other}`); + } + static describe(): string { + return `lazy ${other}`; + } + static value = [0].map(() => LazyImpl.read()).join(""); +}; diff --git a/tests/corpus/7383-cycle-class-expression-name-tdz/main.ts b/tests/corpus/7383-cycle-class-expression-name-tdz/main.ts new file mode 100644 index 000000000..93b7a47ee --- /dev/null +++ b/tests/corpus/7383-cycle-class-expression-name-tdz/main.ts @@ -0,0 +1,9 @@ +// The first cycle member to run builds classes from named class +// expressions whose static initializers call a method through the inner +// name, directly and from an arrow function; both methods read constants +// of the other member before it declared them: Node's temporal-dead-zone +// ReferenceError. A third method reads a constant only after the cycle +// finished. +import { label, Probe, later } from "./alpha.ts"; + +console.log(label, Probe.result, later()); diff --git a/tests/corpus/7384-als-stream-events.mjs b/tests/corpus/7384-als-stream-events.mjs new file mode 100644 index 000000000..6d67bf26f --- /dev/null +++ b/tests/corpus/7384-als-stream-events.mjs @@ -0,0 +1,214 @@ +// AsyncLocalStorage contexts of stream events and callbacks, from +// JavaScript. Stream steps that Node defers with process.nextTick run in +// the context that scheduled them: resume() after a 'data' listener, +// uncork() from a tick, a write after end(), a push() from a user tick. +// Other events follow the call that emits them. stream.finished callbacks +// run in the context of their call, with stream/promises' finished() +// resolving in the context that awaits it. readline lines follow the +// createInterface() call that resumes the input, and zlib streams run +// their events and flush() callbacks in the context that created them. +import { AsyncLocalStorage } from "node:async_hooks"; +import readline from "node:readline"; +import { Duplex, finished, PassThrough, Readable, Writable } from "node:stream"; +import { finished as finishedPromise } from "node:stream/promises"; +import zlib from "node:zlib"; + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); +const step = (fn) => new Promise((resolve) => fn(resolve)); + +// pipe() and unpipe() events, and writes through a PassThrough. +await step((done) => { + const through = new PassThrough(); + const sink = new Writable({ write(chunk, encoding, cb) { log("pipe write"); cb(); } }); + sink.on("pipe", () => log("pipe event")); + sink.on("unpipe", () => log("unpipe event")); + store.run("pipe", () => through.pipe(sink)); + store.run("through-write", () => through.write("q")); + setTimeout(() => { + store.run("unpipe", () => through.unpipe(sink)); + done(); + }, 5); +}); + +// cork() and uncork() from a tick: the writes follow the tick's context. +await step((done) => { + const sink = new Writable({ write(chunk, encoding, cb) { log("corked write"); cb(); } }); + sink.on("finish", () => { + log("corked finish"); + done(); + }); + store.run("cork", () => { + sink.cork(); + sink.write("a"); + sink.write("b"); + }); + store.run("uncork", () => process.nextTick(() => sink.uncork())); + setTimeout(() => store.run("corked-end", () => sink.end()), 5); +}); + +// A write after end(): its callback and 'error' follow the write() call. +await step((done) => { + const sink = new Writable({ write(chunk, encoding, cb) { cb(); } }); + sink.on("error", () => log("write after end error")); + sink.end(); + store.run("late-write", () => + sink.write("x", () => { + log("late write callback"); + done(); + }), + ); +}); + +// A failing _write: the callback, 'error' and 'close' follow write(). +await step((done) => { + const sink = new Writable({ write(chunk, encoding, cb) { cb(new Error("bad")); } }); + sink.on("error", () => log("failed write error")); + sink.on("close", () => { + log("failed write close"); + done(); + }); + store.run("failed-write", () => sink.write("x", () => log("failed write callback"))); +}); + +// A Duplex that pushes from _write: 'data' and 'end' follow the resume() +// tick, which the 'data' listener scheduled with no store. +await step((done) => { + const duplex = new Duplex({ + read() {}, + write(chunk, encoding, cb) { + log("duplex write"); + this.push(chunk); + cb(); + }, + }); + duplex.on("data", () => { + log("duplex data"); + duplex.push(null); + }); + duplex.on("end", () => { + log("duplex end"); + done(); + }); + store.run("duplex-write", () => duplex.write("z")); +}); + +// A push() from a user tick, a 'data' listener registered in a store with +// setEncoding(), and PassThrough's 'end' and 'close' after end(). +await step((done) => { + const source = new Readable({ read() {} }); + source.on("data", () => log("tick data")); + source.on("end", () => { + log("tick end"); + done(); + }); + store.run("user-tick", () => + process.nextTick(() => { + log("user tick"); + source.push("x"); + }), + ); + setTimeout(() => store.run("tick-eof", () => source.push(null)), 5); +}); +await step((done) => { + const source = new Readable({ read() {} }); + source.setEncoding("utf8"); + store.run("data-listener", () => source.on("data", () => log("text data"))); + source.on("end", () => { + log("text end"); + done(); + }); + source.push("hi"); + source.push(null); +}); +await step((done) => { + const through = new PassThrough(); + through.on("end", () => log("through end")); + through.on("close", () => { + log("through close"); + done(); + }); + through.resume(); + store.run("through-end", () => through.end("last")); +}); + +// finished() on a Writable, and stream/promises' finished(). +await step((done) => { + const sink = new Writable({ write(chunk, encoding, cb) { setTimeout(cb, 1); } }); + store.run("finished-writable", () => + finished(sink, () => { + log("finished callback"); + done(); + }), + ); + sink.on("finish", () => log("writable finish")); + store.run("writable-end", () => { + sink.write("a"); + sink.end(); + }); +}); +await step((done) => { + const source = new Readable({ read() {} }); + source.resume(); + store.run("finished-promise", () => finishedPromise(source)).then(() => { + log("finished promise"); + done(); + }); + setTimeout(() => store.run("promise-eof", () => source.push(null)), 5); +}); + +// Readable.from() over an async generator: the reads follow the resume() +// tick, scheduled with no store. +await step((done) => { + async function* letters() { + log("generator start"); + yield "a"; + log("generator end"); + } + const source = store.run("from", () => Readable.from(letters())); + source.on("data", () => log("from data")); + source.on("end", () => { + log("from end"); + done(); + }); +}); + +// readline over a Readable: createInterface() resumes the input. +await step((done) => { + const input = Readable.from(["a\nb\n"]); + const rl = store.run("readline", () => readline.createInterface({ input })); + rl.on("line", (line) => log(`line ${line}`)); + rl.on("close", () => { + log("readline close"); + done(); + }); +}); + +// zlib streams: events and flush() callbacks follow the creation context. +await step((done) => { + const gzip = store.run("gzip-create", () => zlib.createGzip()); + store.run("gzip-listen", () => { + gzip.on("data", () => log("gzip data")); + gzip.on("end", () => { + log("gzip end"); + done(); + }); + }); + store.run("gzip-write", () => { + gzip.write("hello"); + gzip.end(); + }); +}); +await step((done) => { + const deflate = store.run("deflate-create", () => zlib.createDeflate()); + store.run("deflate-flush", () => deflate.flush(() => log("deflate flush callback"))); + deflate.on("data", () => {}); + deflate.on("end", () => { + log("deflate end"); + done(); + }); + store.run("deflate-end", () => deflate.end("x")); +}); + +for (const line of lines) console.log(line); diff --git a/tests/corpus/7385-als-child-process-events.ts b/tests/corpus/7385-als-child-process-events.ts new file mode 100644 index 000000000..54fd80c3a --- /dev/null +++ b/tests/corpus/7385-als-child-process-events.ts @@ -0,0 +1,104 @@ +// AsyncLocalStorage contexts of node:child_process events. A child's +// 'spawn', 'error', 'exit' and 'close' events and its stdout and stderr +// 'data' and 'end' events run in the context of the spawn() call, +// whatever context registered the listener or killed the child. stdin's +// 'finish' runs in the context of the end() call, and its 'drain' and +// 'error' in the context of the write() call. Lines are sorted at the end: +// the stdout and stderr events of one child may interleave. +import { AsyncLocalStorage } from "node:async_hooks"; +import { spawn } from "node:child_process"; + +const store = new AsyncLocalStorage(); +const lines: string[] = []; +const log = (what: string): void => { + lines.push(`${what}: ${store.getStore() ?? "none"}`); +}; +const step = (fn: (done: () => void) => void): Promise => + new Promise((resolve) => fn(resolve)); + +await step((done) => { + const child = store.run("spawn", () => spawn("/bin/sh", ["-c", "printf out; printf err >&2"])); + store.run("listen", () => { + child.on("spawn", () => log("spawn event")); + child.stdout!.on("data", () => log("stdout data")); + child.stdout!.on("end", () => log("stdout end")); + child.stderr!.on("data", () => log("stderr data")); + child.on("exit", () => log("exit")); + child.on("close", () => { + log("close"); + done(); + }); + }); +}); + +await step((done) => { + const child = store.run("spawn-cat", () => spawn("/bin/cat")); + child.stdout!.on("data", () => log("cat data")); + store.run("stdin-write", () => { + child.stdin!.write("x"); + }); + child.stdin!.on("finish", () => log("stdin finish")); + store.run("stdin-end", () => { + child.stdin!.end(); + }); + child.on("close", () => { + log("cat close"); + done(); + }); +}); + +await step((done) => { + const child = store.run("spawn-missing", () => spawn("/nonexistent/command")); + child.on("error", () => log("spawn error")); + child.on("close", () => { + log("missing close"); + done(); + }); +}); + +await step((done) => { + const child = store.run("spawn-sleep", () => spawn("/bin/sleep", ["5"])); + child.on("exit", () => log("killed exit")); + child.on("close", () => { + log("killed close"); + done(); + }); + setTimeout(() => store.run("kill", () => child.kill()), 20); +}); + +// A write that fills the pipe: 'drain' runs in the write() call's context. +await step((done) => { + const child = store.run("spawn-slow", () => + spawn("/bin/sh", ["-c", "sleep 0.2; cat > /dev/null"]), + ); + child.stdin!.on("drain", () => { + log("stdin drain"); + child.stdin!.end(); + }); + child.on("close", () => { + log("slow close"); + done(); + }); + store.run("large-write", () => { + child.stdin!.write(Buffer.alloc(1 << 22, 97)); + }); +}); + +// A write to a child that closed its stdin: 'error' runs in the write() +// call's context. +await step((done) => { + const child = store.run("spawn-closed", () => spawn("/bin/sh", ["-c", "exec 0<&-; sleep 0.1"])); + child.stdin!.on("error", () => log("stdin error")); + child.on("close", () => { + log("closed close"); + done(); + }); + setTimeout(() => { + store.run("late-write", () => { + child.stdin!.write(Buffer.alloc(1 << 20, 97)); + }); + }, 50); +}); + +lines.sort(); +for (const line of lines) console.log(line); diff --git a/tests/corpus/7386-als-child-fork-events/main.mjs b/tests/corpus/7386-als-child-fork-events/main.mjs new file mode 100644 index 000000000..18b410643 --- /dev/null +++ b/tests/corpus/7386-als-child-fork-events/main.mjs @@ -0,0 +1,25 @@ +// AsyncLocalStorage contexts of a forked child's IPC events. The child's +// 'message', 'disconnect', 'exit' and 'close' events run in the context of +// the fork() call, whatever context registered the listener; a send() +// callback runs in the context of the send() call. +import { AsyncLocalStorage } from "node:async_hooks"; +import childProcess from "node:child_process"; + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); + +const child = store.run("fork", () => + childProcess.fork(new URL("./worker.mjs", import.meta.url), []), +); +store.run("listen", () => { + child.on("message", (message) => log(`message ${JSON.stringify(message)}`)); +}); +child.on("disconnect", () => log("disconnect")); +store.run("send", () => child.send("hi", () => log("send callback"))); +setTimeout(() => store.run("send-bye", () => child.send("bye")), 50); +child.on("exit", () => log("exit")); +child.on("close", () => { + log("close"); + for (const line of lines) console.log(line); +}); diff --git a/tests/corpus/7386-als-child-fork-events/worker.mjs b/tests/corpus/7386-als-child-fork-events/worker.mjs new file mode 100644 index 000000000..79f4bfdc6 --- /dev/null +++ b/tests/corpus/7386-als-child-fork-events/worker.mjs @@ -0,0 +1,4 @@ +process.on("message", (m) => { + process.send({ echo: m }); + if (m === "bye") process.disconnect(); +}); diff --git a/tests/corpus/7387-als-http2-events.mjs b/tests/corpus/7387-als-http2-events.mjs new file mode 100644 index 000000000..4dd17916e --- /dev/null +++ b/tests/corpus/7387-als-http2-events.mjs @@ -0,0 +1,91 @@ +// AsyncLocalStorage contexts of node:http2 events. A client session's +// 'connect' and 'close' run in the context of the http2.connect() call, +// and a client stream's 'response', 'data', 'end' and 'close' in the +// context of the request() call. A server's 'session' and 'stream' events +// run in the context of its listen() call; a server stream's own 'data', +// 'end' and 'close' events run with no store, as does a server.close() +// callback that waits for the sessions. Lines are sorted at the end: +// events on the two sides may interleave. +import { AsyncLocalStorage } from "node:async_hooks"; +import http2 from "node:http2"; + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); + +/** @returns {Promise} */ +function requestScenario() { + return new Promise((resolve) => { + const server = store.run("create", () => http2.createServer()); + server.on("session", () => log("server session")); + server.on("stream", (stream) => { + log("server stream"); + stream.on("data", () => log("server stream data")); + stream.on("end", () => { + log("server stream end"); + store.run("respond", () => { + stream.respond({ ":status": 200 }); + stream.end("ok"); + }); + }); + }); + store.run("listen", () => + server.listen(0, () => { + log("listening"); + const client = store.run("connect", () => + http2.connect(`http://localhost:${server.address().port}`), + ); + client.on("connect", () => log("client connect")); + client.on("close", () => log("client session close")); + const req = store.run("request", () => client.request({ ":method": "POST", ":path": "/" })); + req.on("response", () => log("client response")); + req.on("data", () => log("client data")); + req.on("end", () => { + log("client end"); + store.run("close", () => { + client.close(); + server.close(() => { + log("server close callback"); + resolve(); + }); + }); + }); + store.run("write", () => { + req.write("x"); + req.end(); + }); + }), + ); + }); +} + +/** @returns {Promise} */ +function resetScenario() { + return new Promise((resolve) => { + const server = store.run("reset-create", () => http2.createServer()); + server.on("stream", (stream) => { + log("reset server stream"); + stream.on("close", () => log("reset server stream close")); + store.run("reset", () => stream.close(http2.constants.NGHTTP2_CANCEL)); + }); + store.run("reset-listen", () => + server.listen(0, () => { + const client = store.run("reset-connect", () => + http2.connect(`http://localhost:${server.address().port}`), + ); + const req = store.run("reset-request", () => client.request({ ":path": "/" })); + req.on("close", () => { + log("reset client close"); + client.close(); + server.close(() => resolve()); + }); + req.end(); + }), + ); + }); +} + +await requestScenario(); +await resetScenario(); +lines.sort(); +for (const line of lines) console.log(line); diff --git a/tests/corpus/7388-als-http2-compat-events.mjs b/tests/corpus/7388-als-http2-compat-events.mjs new file mode 100644 index 000000000..da44dc378 --- /dev/null +++ b/tests/corpus/7388-als-http2-compat-events.mjs @@ -0,0 +1,49 @@ +// AsyncLocalStorage contexts of the node:http2 compatibility API. The +// createServer() request handler runs in the context of the server's +// listen() call. The request's 'end' and the response's 'finish' and +// 'close' follow the server stream, which runs with no store, whatever +// context calls res.end(). A client stream's 'response', 'data' and +// 'close' run in the context of the request() call, and the client +// session's 'close' in the context of the http2.connect() call. Lines are +// sorted at the end: events on the two sides may interleave. +import { AsyncLocalStorage } from "node:async_hooks"; +import http2 from "node:http2"; + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); + +const server = store.run("create", () => + http2.createServer((req, res) => { + log("request handler"); + req.resume(); + req.on("end", () => { + log("request end"); + res.on("finish", () => log("response finish")); + res.on("close", () => log("response close")); + store.run("respond", () => res.end("done")); + }); + }), +); +store.run("listen", () => + server.listen(0, () => { + const client = store.run("connect", () => + http2.connect(`http://localhost:${server.address().port}`), + ); + client.on("close", () => { + log("client session close"); + server.close(() => { + lines.sort(); + for (const line of lines) console.log(line); + }); + }); + const req = store.run("request", () => client.request({ ":method": "POST", ":path": "/x" })); + req.on("response", () => log("client response")); + req.on("data", () => log("client data")); + req.on("close", () => { + log("client stream close"); + store.run("close", () => client.close()); + }); + store.run("write", () => req.end("body")); + }), +); diff --git a/tests/corpus/7389-als-http2-secure-events.mjs b/tests/corpus/7389-als-http2-secure-events.mjs new file mode 100644 index 000000000..0772b5267 --- /dev/null +++ b/tests/corpus/7389-als-http2-secure-events.mjs @@ -0,0 +1,94 @@ +// AsyncLocalStorage contexts of node:http2 events over TLS +// (http2.createSecureServer and an https: authority). They follow corpus +// 7387's rules, with one difference that comes from the transport: a TLS +// server's connections run in the context of its listen() call, so a +// server.close() callback that waits for the sessions runs there too. +// Lines are sorted at the end: events on the two sides may interleave. +import { AsyncLocalStorage } from "node:async_hooks"; +import fs from "node:fs"; +import http2 from "node:http2"; + +const key = fs.readFileSync("tests/fixtures/server/certs/localhost-key.pem"); +const cert = fs.readFileSync("tests/fixtures/server/certs/localhost.pem"); +const ca = fs.readFileSync("tests/fixtures/server/certs/ca.pem", "utf8"); + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); + +/** @returns {Promise} */ +function requestScenario() { + return new Promise((resolve) => { + const server = store.run("create", () => http2.createSecureServer({ key, cert })); + server.on("session", () => log("server session")); + server.on("stream", (stream) => { + log("server stream"); + stream.on("data", () => log("server stream data")); + stream.on("end", () => { + log("server stream end"); + store.run("respond", () => { + stream.respond({ ":status": 200 }); + stream.end("ok"); + }); + }); + }); + store.run("listen", () => + server.listen(0, () => { + log("listening"); + const client = store.run("connect", () => + http2.connect(`https://localhost:${server.address().port}`, { ca }), + ); + client.on("connect", () => log("client connect")); + client.on("close", () => log("client session close")); + const req = store.run("request", () => client.request({ ":method": "POST", ":path": "/" })); + req.on("response", () => log("client response")); + req.on("data", () => log("client data")); + req.on("end", () => { + log("client end"); + store.run("close", () => { + client.close(); + server.close(() => { + log("server close callback"); + resolve(); + }); + }); + }); + store.run("write", () => { + req.write("x"); + req.end(); + }); + }), + ); + }); +} + +/** @returns {Promise} */ +function resetScenario() { + return new Promise((resolve) => { + const server = store.run("reset-create", () => http2.createSecureServer({ key, cert })); + server.on("stream", (stream) => { + log("reset server stream"); + stream.on("close", () => log("reset server stream close")); + store.run("reset", () => stream.close(http2.constants.NGHTTP2_CANCEL)); + }); + store.run("reset-listen", () => + server.listen(0, () => { + const client = store.run("reset-connect", () => + http2.connect(`https://localhost:${server.address().port}`, { ca }), + ); + const req = store.run("reset-request", () => client.request({ ":path": "/" })); + req.on("close", () => { + log("reset client close"); + client.close(); + server.close(() => resolve()); + }); + req.end(); + }), + ); + }); +} + +await requestScenario(); +await resetScenario(); +lines.sort(); +for (const line of lines) console.log(line); diff --git a/tests/corpus/7390-record-key-read-presence.ts b/tests/corpus/7390-record-key-read-presence.ts new file mode 100644 index 000000000..76bb355ca --- /dev/null +++ b/tests/corpus/7390-record-key-read-presence.ts @@ -0,0 +1,49 @@ +// Keyed reads of string-indexed records whose values are numbers or +// booleans, consumed by `??`, `!`, comparisons with undefined and typeof: +// declared fields, an optional declared field, index-signature entries and +// missing keys. A stored NaN is a value, so `??` keeps it; -0 survives the +// read; a missing key is undefined. +interface Scores { + base: number; + bonus?: number; + [name: string]: number | undefined; +} + +const scores: Scores = { base: 10 }; +scores["alice"] = 3; +scores["nan"] = NaN; +scores["negzero"] = -0; + +const keys = ["base", "bonus", "alice", "nan", "negzero", "missing"]; +for (const key of keys) { + const read = scores[key]; + console.log( + key, + scores[key] ?? "default", + scores[key] === undefined, + typeof scores[key], + Object.is(scores[key] ?? 1, -0), + (scores[key] ?? 0) + 1, + read, + ); +} +scores.bonus = 5; +console.log("bonus", scores["bonus"] ?? "default", scores["bonus"]! * 2); + +const counts: Record = {}; +for (const word of "a b a c b a d".split(" ")) counts[word] = (counts[word] ?? 0) + 1; +let total = 0; +for (const word in counts) total += counts[word]!; +console.log(JSON.stringify(counts), total, counts["zzz"] ?? -1); + +const flags: Record = { on: true, off: false }; +for (const key of ["on", "off", "unset"]) { + console.log(key, flags[key] ?? "unset", flags[key] === undefined, typeof flags[key]); +} +let seen = 0; +for (let i = 0; i < 1000; i++) { + const key = "k" + (i % 7); + flags[key] = !(flags[key] ?? false); + if (flags[key]!) seen++; +} +console.log(seen, Object.keys(flags).length); diff --git a/tests/corpus/7391-als-response-end-and-finished.mjs b/tests/corpus/7391-als-response-end-and-finished.mjs new file mode 100644 index 000000000..91730201e --- /dev/null +++ b/tests/corpus/7391-als-response-end-and-finished.mjs @@ -0,0 +1,54 @@ +// AsyncLocalStorage contexts that follow the call rather than the resource. +// An HTTP server response ended inside another store runs its end() +// callback, 'finish' and 'close' in the end() call's context, not the +// server's listen() context. A stream.finished() callback registered with +// no store runs with no store, also when another context destroys the +// stream; one registered inside a store runs in that store. +import { AsyncLocalStorage } from "node:async_hooks"; +import http from "node:http"; +import { Readable, finished } from "node:stream"; + +const store = new AsyncLocalStorage(); +const lines = []; +const log = (what) => lines.push(`${what}: ${store.getStore() ?? "none"}`); + +/** @returns {Promise} */ +function responseScenario() { + return new Promise((resolve) => { + const server = http.createServer((req, res) => { + log("request"); + res.on("finish", () => log("response finish")); + res.on("close", () => log("response close")); + store.run("handler-end", () => res.end("ok", () => log("response end callback"))); + }); + store.run("listen", () => + server.listen(0, () => { + http.get(`http://localhost:${server.address().port}/`, (response) => { + response.resume(); + response.on("end", () => server.close(() => resolve())); + }); + }), + ); + }); +} +await responseScenario(); + +/** @returns {Promise} */ +function finishedScenario(registration) { + return new Promise((resolve) => { + const stream = new Readable({ read() {} }); + const register = () => + finished(stream, () => { + log(`finished registered ${registration ?? "outside"}`); + resolve(); + }); + if (registration === undefined) register(); + else store.run(registration, register); + store.run("destroyer", () => stream.destroy()); + }); +} +await finishedScenario(undefined); +await finishedScenario("inside"); + +lines.sort(); +for (const line of lines) console.log(line); diff --git a/tests/corpus/7392-non-null-operands.ts b/tests/corpus/7392-non-null-operands.ts new file mode 100644 index 000000000..60928ddb6 --- /dev/null +++ b/tests/corpus/7392-non-null-operands.ts @@ -0,0 +1,137 @@ +// Non-null assertions on values that still hold undefined or null, used as +// operands of operators that convert them: arithmetic, comparisons, string +// conversion, Math functions, and loose equality with null. `!` +// performs no runtime check, so the operator sees undefined or null and +// converts it as JavaScript does. + +interface Settings { + limit?: number; + label?: string; + enabled?: boolean; + ratio: number | null; + note?: string | null; +} + +const empty: Settings = { ratio: null }; +const full: Settings = { limit: 4, label: "abc", enabled: true, ratio: 0.5, note: "n" }; +const counts: Record = { a: 1, b: 2 }; +const sizes = new Map([["small", 1]]); +const values = [3, 1, 4]; + +function show(v: unknown): string { + return Object.is(v, -0) ? "-0" : String(v); +} + +function line(label: string, parts: unknown[]): void { + console.log(label.padEnd(12), parts.map(show).join(" ")); +} + +for (const s of [empty, full]) { + const tag = s === empty ? "empty" : "full"; + line(`${tag} arith`, [ + s.limit! + 1, + s.limit! - 1, + s.limit! * 2, + s.limit! / 2, + s.limit! % 3, + s.limit! ** 2, + -s.limit!, + +s.limit!, + s.ratio! + 1, + s.ratio! * 4, + -s.ratio!, + s.limit! - s.ratio!, + ]); + line(`${tag} bits`, [s.limit! | 0, s.limit! & 6, s.limit! << 1, s.limit! >>> 0, ~s.limit!, s.ratio! ^ 1]); + line(`${tag} compare`, [ + s.limit! < 5, + s.limit! >= 0, + s.ratio! < 1, + s.ratio! > -1, + s.limit! === 4, + s.limit! !== 4, + s.ratio! == 0, + s.label! < "b", + s.label! >= "a", + s.label! === "abc", + s.enabled! === true, + s.limit! > s.ratio!, + ]); + line(`${tag} strings`, [ + `${s.limit!}`, + `${s.ratio!}`, + `${s.enabled!}`, + `[${s.label!}]`, + `${s.note!}`, + "n=" + s.limit!, + s.limit! + "!", + s.label! + "?", + "<" + s.note!, + ]); + line(`${tag} convert`, [ + Math.round(s.limit!), + Math.max(s.limit!, 1), + Math.min(s.ratio!, 1), + !s.enabled!, + !s.ratio!, + ]); +} + +line("record", [counts["z"]! + 1, counts["a"]! + 1, counts["z"]! > 0, `${counts["z"]!}`]); +line("map", [sizes.get("large")! * 2, sizes.get("small")! * 2, `${sizes.get("large")!}`]); +line("find", [values.find((v) => v > 9)! + 1, values.find((v) => v > 3)! + 1]); + +let total = 0; +let text = ""; +let mask = 7; +for (const key of ["a", "b", "c", "a"]) { + total += counts[key]!; + text += counts[key]!; + text += ","; + mask &= counts[key]!; +} +line("compound", [total, text, mask]); + +const tally: Record = {}; +for (const word of ["x", "y", "x"]) tally[word] = tally[word]! + 1; +line("tally", [tally["x"], tally["y"]]); + +type Cell = number | string | null | undefined; +for (const cell of [3, "4", null, undefined] as Cell[]) { + const m = new Map([["k", cell]]); + line("cell", [`${m.get("k")!}`, m.get("k")! === 3, String(m.get("k")!)]); +} + +// Loose equality with null tests both unit arms of a value read once. +const optional = new Map([["one", 1], ["none", null]]); +let reads = 0; +function lookup(key: string): string | null | undefined { + reads++; + return key === "a" ? "x" : key === "b" ? null : undefined; +} +for (const key of ["one", "none", "missing"]) { + line(`loose ${key}`, [optional.get(key)! == null, optional.get(key)! != undefined, optional.get(key)! == 1]); +} +line("loose call", [lookup("a") == null, lookup("b") == null, lookup("c") != null, reads]); + +// `+` with an untyped partner concatenates or adds depending on what the +// partner holds when it runs. +const untypedText: any = "x"; +const untypedNumber: any = 5; +const untypedObject: any = { toString: () => "obj" }; +for (const s of [empty, full]) { + let joined: any = "t"; + let summed: any = 1; + joined += s.limit!; + summed += s.limit!; + line(`untyped ${s === empty ? "empty" : "full"}`, [ + s.limit! + untypedText, + untypedText + s.limit!, + s.limit! + untypedNumber, + untypedNumber + s.limit!, + s.label! + untypedText, + s.limit! + untypedObject, + joined, + summed, + ]); +} diff --git a/tests/corpus/7394-non-null-typed-array-reads.ts b/tests/corpus/7394-non-null-typed-array-reads.ts new file mode 100644 index 000000000..bab1bcf9c --- /dev/null +++ b/tests/corpus/7394-non-null-typed-array-reads.ts @@ -0,0 +1,80 @@ +// Non-null assertions on typed-array element reads. `!` performs no +// runtime check: an index outside the array reads undefined, as in Node, +// wherever the use tells undefined from NaN (equality, string conversion, +// typeof, Object.is, an array element), and NaN where it converts the value +// to a number. + +const int32 = new Int32Array([1, -2, 3]); +const float64 = new Float64Array([1.5, NaN, -0]); +const uint8 = new Uint8Array([7, 255]); + +function show(v: unknown): string { + return Object.is(v, -0) ? "-0" : String(v); +} + +function line(label: string, parts: unknown[]): void { + console.log(label.padEnd(12), parts.map(show).join(" ")); +} + +for (const k of [0, 1, 2, 3, -1, 1.5]) { + line(`int32 ${k}`, [ + int32[k]! === int32[k]!, + int32[k]! !== int32[0]!, + int32[k]! === undefined, + int32[k]! == null, + Object.is(int32[k]!, undefined), + typeof int32[k]!, + `${int32[k]!}`, + "v=" + int32[k]!, + String(int32[k]!), + int32[k]! + 1, + int32[k]! < 2, + [int32[k]!].length, + ]); + line(`float64 ${k}`, [ + float64[k]! === float64[k]!, + float64[k]! === undefined, + Object.is(float64[k]!, NaN), + typeof float64[k]!, + `${float64[k]!}`, + float64[k]! * 2, + -float64[k]!, + ]); + line(`uint8 ${k}`, [ + uint8[k]! === uint8[k]!, + uint8[k]! == undefined, + typeof uint8[k]!, + `${uint8[k]!}`, + uint8[k]! | 0, + uint8[k]! > 100, + ]); +} + +const optional: number | undefined = uint8[5]!; +const listed = [int32[9]!, float64[0]!]; +line("slots", [optional, listed[0], listed[1], listed.length]); + +let total = 0; +for (let i = 0; i <= int32.length; i++) total += int32[i]!; +line("sum", [total]); + +// Arrays that store an asserted read past the end hold undefined, which +// their callbacks see. +const pushed: number[] = []; +pushed.push(int32[7]!); +pushed.push(uint8[1]!); +const literal = [float64[5]!, float64[0]!]; +const assigned: number[] = [1, 2]; +assigned[0] = uint8[9]!; +let visited = 0; +pushed.forEach((v) => { + visited += v; +}); +line("callbacks", [ + pushed.map((v) => v * 2).join(","), + visited, + literal.map((v) => typeof v).join(","), + literal.filter((v) => v === undefined).length, + assigned.map((v) => v + 1).join(","), + assigned.some((v) => v === undefined), +]); diff --git a/tests/corpus/7404-frozen-records.ts b/tests/corpus/7404-frozen-records.ts new file mode 100644 index 000000000..acf9b393b --- /dev/null +++ b/tests/corpus/7404-frozen-records.ts @@ -0,0 +1,124 @@ +// Object.freeze of records that other references still hold: writes +// through any reference then throw Node's TypeError (this module is strict +// mode code; 7405 covers sloppy mode). Covers static freezes, freezes +// through untyped values, Object.isFrozen, keyed writes and deletes, +// optional-field deletes, Object.assign, tuples, and frozen records that are +// released or collected as cycles. +export {}; + +type Counter = { count: number; label: string }; +type Link = { name: string; next?: Link }; + +function report(e: unknown): void { + console.log((e as Error).name + ": " + (e as Error).message); +} + +function freezeUnknown(value: unknown): unknown { + return Object.freeze(value); +} + +function isFrozenUnknown(value: unknown): boolean { + return Object.isFrozen(value); +} + +function deepFreeze(value: unknown): unknown { + if (typeof value === "object" && value !== null) { + Object.freeze(value); + for (const key of Object.keys(value)) deepFreeze((value as Record)[key]); + } + return value; +} + +type Config = { name: string; limits: { max: number; min: number } }; + +function bump(c: Counter): void { + c.count += 1; +} + +function writes(): void { + const a: Counter = { count: 1, label: "a" }; + const alias = a; + console.log(Object.isFrozen(a), isFrozenUnknown(a)); + const frozen = Object.freeze(alias); + console.log(frozen === a, Object.isFrozen(a), isFrozenUnknown(alias)); + try { bump(a); } catch (e) { report(e); } + try { a.label = "b"; } catch (e) { report(e); } + console.log(a.count, a.label, JSON.stringify(a)); + + const viaUnknown: Counter = { count: 10, label: "u" }; + const back = freezeUnknown(viaUnknown); + console.log(back === viaUnknown, Object.isFrozen(viaUnknown)); + try { viaUnknown.count = 11; } catch (e) { report(e); } + console.log(viaUnknown.count); + + const dict: Record = { one: 1 }; + const dictAlias = dict; + Object.freeze(dictAlias); + try { dict["one"] = 2; } catch (e) { report(e); } + try { dict["two"] = 2; } catch (e) { report(e); } + try { delete dict["one"]; } catch (e) { report(e); } + delete dict["three"]; + console.log(JSON.stringify(dict), Object.isFrozen(dict)); + + const optional: { id: number; note?: string } = { id: 1, note: "n" }; + const optionalAlias = optional; + Object.freeze(optionalAlias); + try { delete optional.note; } catch (e) { report(e); } + console.log(JSON.stringify(optional)); + const bare: { id: number; note?: string } = { id: 2 }; + Object.freeze(bare); + delete bare.note; + console.log(JSON.stringify(bare)); + + const target: Counter = { count: 0, label: "t" }; + Object.freeze(target); + try { Object.assign(target, { count: 5, label: "x" }); } catch (e) { report(e); } + console.log(JSON.stringify(target)); + + const pair: [string, number] = ["p", 1]; + Object.freeze(pair); + try { pair[1] = 2; } catch (e) { report(e); } + console.log(pair[0], pair[1]); +} + +function deepFreezes(): void { + const config: Config = JSON.parse('{"name":"c","limits":{"max":2,"min":1}}'); + const limits = config.limits; + console.log(Object.isFrozen(config), Object.isFrozen(limits)); + deepFreeze(config); + console.log(Object.isFrozen(config), Object.isFrozen(limits), isFrozenUnknown(config.limits)); + try { config.limits.max = 3; } catch (e) { report(e); } + try { limits.min = 0; } catch (e) { report(e); } + try { config.name = "d"; } catch (e) { report(e); } + console.log(JSON.stringify(config)); +} + +function lifetimes(): void { + const list: Counter[] = []; + for (let i = 0; i < 4; i++) { + const item: Counter = { count: i, label: "x" + i }; + list.push(item); + if (i % 2 === 1) Object.freeze(item); + } + for (const item of list) { + try { item.count = item.count * 10; } catch (e) { console.log(item.label, (e as Error).message); } + } + console.log(JSON.stringify(list)); + const kept = list[1]!; + list.length = 0; + console.log(kept.count, Object.isFrozen(kept)); + + for (let round = 0; round < 3; round++) { + const first: Link = { name: "first" + round }; + const second: Link = { name: "second" + round, next: first }; + first.next = second; + Object.freeze(first); + Object.freeze(second); + try { first.name = "changed"; } catch (e) { report(e); } + console.log(first.next?.name, second.next?.name); + } +} + +writes(); +deepFreezes(); +lifetimes(); diff --git a/tests/corpus/7405-frozen-records-sloppy.cjs b/tests/corpus/7405-frozen-records-sloppy.cjs new file mode 100644 index 000000000..2b4e29213 --- /dev/null +++ b/tests/corpus/7405-frozen-records-sloppy.cjs @@ -0,0 +1,53 @@ +// Writes to frozen records in sloppy-mode code leave them unchanged, where +// strict mode code throws (7404). The records are typed through JSDoc, so +// the writes are native record writes. + +/** @typedef {{ count: number, label: string }} Counter */ +/** @typedef {{ id: number, note?: string }} Note */ +/** @typedef {{ kind: "a", name: string }} Small */ +/** @typedef {{ kind: "b", name: string, size: number }} Large */ + +/** @param {Counter} c */ +function bump(c) { + c.count += 1; + c.label = c.label + "!"; +} + +/** @param {Note} n */ +function dropNote(n) { + delete n.note; +} + +/** @param {number} count @returns {Counter} */ +function counter(count) { + return { count: count, label: "c" + count }; +} + +const open = counter(1); +const frozen = Object.freeze(counter(2)); +bump(open); +bump(frozen); +console.log(JSON.stringify(open), JSON.stringify(frozen), Object.isFrozen(frozen)); + +/** @type {Note} */ +const note = { id: 1, note: "n" }; +Object.freeze(note); +dropNote(note); +console.log(JSON.stringify(note)); + +/** @param {Small | Large} shape */ +function rename(shape) { + shape.name = "renamed"; +} + +/** @param {boolean} large @returns {Small | Large} */ +function shapeOf(large) { + return large ? { kind: "b", name: "b", size: 1 } : { kind: "a", name: "a" }; +} + +const openShape = shapeOf(true); +const frozenShape = shapeOf(false); +Object.freeze(frozenShape); +rename(openShape); +rename(frozenShape); +console.log(openShape.name, frozenShape.name); diff --git a/tests/corpus/7406-frozen-containers.ts b/tests/corpus/7406-frozen-containers.ts new file mode 100644 index 000000000..a8ba8e35c --- /dev/null +++ b/tests/corpus/7406-frozen-containers.ts @@ -0,0 +1,142 @@ +// Object.freeze of arrays, Maps and Sets that other references still hold. +// A frozen array refuses every write through any reference with Node's +// TypeError for the operation's first write (this module is strict mode +// code). A frozen Map or Set only stops being extensible: its entries stay +// writable, and Object.isFrozen, isSealed and isExtensible report it. +export {}; + +function report(e: unknown): void { + console.log((e as Error).name + ": " + (e as Error).message); +} + +function freezeUnknown(value: unknown): unknown { + return Object.freeze(value); +} + +function writeAny(value: any): void { + value[0] = 7; +} + +function pushAny(value: any): void { + value.push(8); +} + +function integrity(value: unknown): string { + return [Object.isFrozen(value), Object.isSealed(value), Object.isExtensible(value)].join(" "); +} + +function arrays(): void { + const a: number[] = [1, 2, 3]; + const alias = a; + console.log(Object.isFrozen(a), integrity(a)); + Object.freeze(alias); + console.log(Object.isFrozen(a), integrity(a)); + try { a[0] = 9; } catch (e) { report(e); } + try { a[5] = 9; } catch (e) { report(e); } + try { a.length = 0; } catch (e) { report(e); } + try { a.push(4); } catch (e) { report(e); } + try { a.pop(); } catch (e) { report(e); } + try { a.shift(); } catch (e) { report(e); } + try { a.unshift(0); } catch (e) { report(e); } + try { a.splice(0, 1); } catch (e) { report(e); } + try { a.splice(1, 0, 7); } catch (e) { report(e); } + try { a.reverse(); } catch (e) { report(e); } + try { a.sort(); } catch (e) { report(e); } + try { a.fill(0, 1); } catch (e) { report(e); } + try { a.copyWithin(1, 0); } catch (e) { report(e); } + console.log(a.join(), a.length); + + const one: string[] = ["x"]; + Object.freeze(one); + console.log(one.reverse().join(), one.sort().join(), one.copyWithin(0, 1).join()); + try { one.splice(0, 1); } catch (e) { report(e); } + try { one.shift(); } catch (e) { report(e); } + const none: string[] = []; + Object.freeze(none); + console.log(none.reverse().length, none.fill("y").length); + try { none.pop(); } catch (e) { report(e); } + try { none.splice(0, 0, "z"); } catch (e) { report(e); } + + let calls = 0; + const byValue = (x: number, y: number): number => { + calls++; + return x - y; + }; + const unsorted: number[] = [3, 1, 2]; + Object.freeze(unsorted); + try { unsorted.sort(byValue); } catch (e) { report(e); } + const single: number[] = [5]; + Object.freeze(single); + console.log(unsorted.join(), calls > 0, single.sort(byValue).join()); + + // A comparator that freezes the array stops the sort's write-back. + const freezing: number[] = [3, 1, 2]; + try { + freezing.sort((x, y) => { + Object.freeze(freezing); + return x - y; + }); + } catch (e) { report(e); } + console.log(freezing.join(), Object.isFrozen(freezing)); + + const open: number[] = [4, 5]; + open.push(6); + open[0] = 1; + console.log(open.join(), Object.isFrozen(open)); +} + +function throughUnknown(): void { + const a: number[] = [1, 2]; + console.log(integrity(a)); + const back = freezeUnknown(a); + console.log(back === a, Object.isFrozen(a), integrity(a)); + try { a.push(3); } catch (e) { report(e); } + try { a[1] = 5; } catch (e) { report(e); } + try { writeAny(a); } catch (e) { report(e); } + try { pushAny(a); } catch (e) { report(e); } + console.log(a.join()); + + const rows: number[][] = [[1], [2]]; + freezeUnknown(rows[1]); + rows[0]!.push(10); + try { rows[1]!.push(20); } catch (e) { report(e); } + console.log(JSON.stringify(rows), Object.isFrozen(rows), Object.isFrozen(rows[1])); +} + +function collections(): void { + const m = new Map([["x", 1]]); + console.log(integrity(m)); + const frozen = Object.freeze(m); + m.set("y", 2); + m.delete("x"); + console.log(frozen === m, Object.isFrozen(m), Object.isSealed(m), Object.isExtensible(m), m.size); + const s = new Set([1]); + freezeUnknown(s); + s.add(2); + console.log(integrity(s), s.size); + const sealed = new Set(); + Object.seal(sealed); + const fresh = new Map(); + Object.preventExtensions(fresh); + console.log(Object.isFrozen(sealed), Object.isExtensible(fresh), integrity(new Map())); +} + +function lifetimes(): void { + const kept: string[][] = []; + for (let round = 0; round < 3; round++) { + const row = ["r" + round, "s" + round]; + if (round !== 1) Object.freeze(row); + kept.push(row); + } + for (const row of kept) { + try { row[0] = "changed"; } catch (e) { console.log(row[1], (e as Error).message); } + } + console.log(JSON.stringify(kept)); + kept.length = 0; + console.log(kept.length); +} + +arrays(); +throughUnknown(); +collections(); +lifetimes(); diff --git a/tests/corpus/7449-crypto-raw-keys.mjs b/tests/corpus/7449-crypto-raw-keys.mjs new file mode 100644 index 000000000..0795dbb0b --- /dev/null +++ b/tests/corpus/7449-crypto-raw-keys.mjs @@ -0,0 +1,120 @@ +// The raw-public, raw-private and raw-seed key formats: generateKeyPair +// encodings, KeyObject.export and createPublicKey/createPrivateKey with +// asymmetricKeyType and namedCurve, for EC keys (uncompressed and +// compressed points, scalars at the curve's size) and Ed25519 keys, and +// Node's errors for the other key types and formats. +import * as crypto from "node:crypto"; + +function describe(value) { + if (Buffer.isBuffer(value)) return `Buffer(${value.length})`; + if (value instanceof crypto.KeyObject) return `${value.type} ${value.asymmetricKeyType} ${JSON.stringify(value.asymmetricKeyDetails)}`; + if (value && typeof value === "object") return Object.entries(value).map(([key, field]) => `${key}=${describe(field)}`).join(" "); + return String(value); +} + +function attempt(label, run) { + try { + console.log(label, describe(run())); + } catch (e) { + console.log(label, "throws", e.name, e.code, e.message); + } +} + +// generateKeyPair encodings +for (const namedCurve of ["P-256", "P-384", "P-521", "secp256k1"]) { + attempt(`ec ${namedCurve} raw-public`, () => crypto.generateKeyPairSync("ec", { namedCurve, publicKeyEncoding: { format: "raw-public" } })); + attempt(`ec ${namedCurve} compressed`, () => crypto.generateKeyPairSync("ec", { namedCurve, publicKeyEncoding: { format: "raw-public", type: "compressed" } })); + attempt(`ec ${namedCurve} raw-private`, () => crypto.generateKeyPairSync("ec", { namedCurve, privateKeyEncoding: { format: "raw-private" } })); +} +attempt("ec raw-seed", () => crypto.generateKeyPairSync("ec", { namedCurve: "P-256", privateKeyEncoding: { format: "raw-seed" } })); +attempt("ed25519 raw", () => + crypto.generateKeyPairSync("ed25519", { publicKeyEncoding: { format: "raw-public" }, privateKeyEncoding: { format: "raw-private" } }), +); +attempt("ed25519 raw-seed", () => crypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "raw-seed" } })); +attempt("rsa raw-public", () => crypto.generateKeyPairSync("rsa", { modulusLength: 1024, publicKeyEncoding: { format: "raw-public" } })); + +// export +const ec = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" }); +const ed = crypto.generateKeyPairSync("ed25519"); +const rsa = crypto.generateKeyPairSync("rsa", { modulusLength: 1024 }); +const point = Buffer.from(ec.publicKey.export({ format: "raw-public" })); +const compressed = Buffer.from(ec.publicKey.export({ format: "raw-public", type: "compressed" })); +const scalar = Buffer.from(ec.privateKey.export({ format: "raw-private" })); +const jwk = ec.privateKey.export({ format: "jwk" }); +console.log("ec point", point.length, point[0], point.subarray(1, 33).toString("base64url") === jwk.x, point.subarray(33).toString("base64url") === jwk.y); +console.log("ec compressed", compressed.length, compressed[0] === 2 + (point[64] & 1), compressed.subarray(1).equals(point.subarray(1, 33))); +console.log("ec scalar", scalar.length, scalar.toString("base64url") === jwk.d); +console.log("ec uncompressed", Buffer.from(ec.publicKey.export({ format: "raw-public", type: "uncompressed" })).equals(point)); +const edPublic = Buffer.from(ed.publicKey.export({ format: "raw-public" })); +const edSeed = Buffer.from(ed.privateKey.export({ format: "raw-private" })); +const edJwk = ed.privateKey.export({ format: "jwk" }); +console.log("ed25519", edPublic.toString("base64url") === edJwk.x, edSeed.toString("base64url") === edJwk.d); +attempt("ed25519 raw-public with a type", () => ed.publicKey.export({ format: "raw-public", type: "x" })); +attempt("ec bad point type", () => ec.publicKey.export({ format: "raw-public", type: "x" })); +attempt("ec null point type", () => ec.publicKey.export({ format: "raw-public", type: null })); +attempt("private raw-public", () => ec.privateKey.export({ format: "raw-public" })); +attempt("public raw-private", () => ec.publicKey.export({ format: "raw-private" })); +attempt("ec raw-seed", () => ec.privateKey.export({ format: "raw-seed" })); +attempt("ed25519 raw-seed", () => ed.privateKey.export({ format: "raw-seed" })); +attempt("rsa raw-public", () => rsa.publicKey.export({ format: "raw-public" })); +attempt("rsa raw-private", () => rsa.privateKey.export({ format: "raw-private" })); +for (const format of [undefined, 5, "jwk", "raw-private", "raw-seed"]) + attempt(`passphrase with ${format}`, () => ec.privateKey.export({ format, passphrase: "x" })); + +// import +const pub = (key) => () => crypto.createPublicKey(key); +const priv = (key) => () => crypto.createPrivateKey(key); +attempt("ec point", pub({ key: point, format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("ec compressed point", pub({ key: compressed, format: "raw-public", asymmetricKeyType: "ec", namedCurve: "prime256v1" })); +attempt("ec scalar", priv({ key: scalar, format: "raw-private", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("ec scalar as public", pub({ key: scalar, format: "raw-private", asymmetricKeyType: "ec", namedCurve: "P-256" })); +const reread = crypto.createPublicKey({ key: compressed, format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-256" }); +console.log("ec reread", reread.equals(ec.publicKey), Buffer.from(reread.export({ format: "raw-public" })).equals(point)); +const rereadPrivate = crypto.createPrivateKey({ key: scalar, format: "raw-private", asymmetricKeyType: "ec", namedCurve: "P-256" }); +console.log("ec private reread", crypto.createPublicKey(rereadPrivate).equals(ec.publicKey)); +attempt("ed25519 public", pub({ key: edPublic, format: "raw-public", asymmetricKeyType: "ed25519" })); +attempt("ed25519 seed", priv({ key: edSeed, format: "raw-private", asymmetricKeyType: "ed25519" })); +console.log("ed25519 reread", crypto.createPublicKey({ key: edSeed, format: "raw-private", asymmetricKeyType: "ed25519" }).equals(ed.publicKey)); + +// fixed keys: the EC scalar 1 is the generator, and an Ed25519 seed of +// zeros has a known public key +const one = Buffer.concat([Buffer.alloc(31), Buffer.from([1])]); +const generator = crypto.createPrivateKey({ key: one, format: "raw-private", asymmetricKeyType: "ec", namedCurve: "P-256" }); +console.log("generator", Buffer.from(crypto.createPublicKey(generator).export({ format: "raw-public", type: "compressed" })).toString("hex")); +const zeros = crypto.createPrivateKey({ key: Buffer.alloc(32), format: "raw-private", asymmetricKeyType: "ed25519" }); +console.log("zero seed", Buffer.from(crypto.createPublicKey(zeros).export({ format: "raw-public" })).toString("hex")); +const message = Buffer.from("raw keys"); +console.log("zero seed signature", crypto.sign(null, message, zeros).toString("hex").slice(0, 32)); + +// import errors +attempt("raw-public to createPrivateKey", priv({ key: point, format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("string data", pub({ key: "abc", format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("no asymmetricKeyType", pub({ key: point, format: "raw-public" })); +attempt("number asymmetricKeyType", pub({ key: point, format: "raw-public", asymmetricKeyType: 5 })); +attempt("no namedCurve", pub({ key: point, format: "raw-public", asymmetricKeyType: "ec" })); +attempt("unknown asymmetricKeyType", pub({ key: point, format: "raw-public", asymmetricKeyType: "nope" })); +attempt("Ed25519 spelling", pub({ key: edPublic, format: "raw-public", asymmetricKeyType: "Ed25519" })); +attempt("unknown curve", pub({ key: point, format: "raw-public", asymmetricKeyType: "ec", namedCurve: "nope" })); +attempt("lower-case NIST curve", pub({ key: point, format: "raw-public", asymmetricKeyType: "ec", namedCurve: "p-256" })); +attempt("other curve", pub({ key: point, format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-384" })); +attempt("short point", pub({ key: point.subarray(0, 64), format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("off-curve point", pub({ key: Buffer.concat([Buffer.from([4]), Buffer.alloc(64, 1)]), format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("bad compressed x", pub({ key: Buffer.concat([Buffer.from([2]), Buffer.alloc(32, 0xff)]), format: "raw-public", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("short scalar", priv({ key: scalar.subarray(0, 31), format: "raw-private", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("long scalar", priv({ key: Buffer.concat([Buffer.alloc(1), scalar]), format: "raw-private", asymmetricKeyType: "ec", namedCurve: "P-256" })); +attempt("ec raw-seed", priv({ key: scalar, format: "raw-seed", asymmetricKeyType: "ec", namedCurve: "nope" })); +attempt("ed25519 raw-seed", priv({ key: edSeed, format: "raw-seed", asymmetricKeyType: "ed25519" })); +attempt("short ed25519 key", pub({ key: edPublic.subarray(0, 31), format: "raw-public", asymmetricKeyType: "ed25519" })); +attempt("short ed25519 seed", priv({ key: edSeed.subarray(0, 31), format: "raw-private", asymmetricKeyType: "ed25519" })); +for (const asymmetricKeyType of ["rsa", "rsa-pss", "dsa", "dh"]) + attempt(`${asymmetricKeyType} raw`, pub({ key: edPublic, format: "raw-public", asymmetricKeyType })); + +// generateKeyPair passes an encoding the key type has no form for to its +// callback +crypto.generateKeyPair("ed25519", { privateKeyEncoding: { format: "raw-seed" } }, (...args) => { + console.log("async raw-seed", args.length, args[0].code); + crypto.generateKeyPair("ec", { namedCurve: "P-384", publicKeyEncoding: { format: "raw-public", type: "compressed" } }, (err, publicKey, privateKey) => { + console.log("async raw-public", err, describe(publicKey), describe(privateKey)); + }); +}); +console.log("returned"); diff --git a/tests/corpus/7450-crypto-x25519.mjs b/tests/corpus/7450-crypto-x25519.mjs new file mode 100644 index 000000000..180221390 --- /dev/null +++ b/tests/corpus/7450-crypto-x25519.mjs @@ -0,0 +1,90 @@ +// X25519 KeyObjects: generateKeyPair, PKCS#8, SubjectPublicKeyInfo, JWK +// and raw keys, RFC 7748's test vectors through crypto.diffieHellman, and +// Node's errors for signatures and incompatible encodings. +import * as crypto from "node:crypto"; + +function describe(value) { + if (Buffer.isBuffer(value)) return `Buffer(${value.length})`; + if (value instanceof crypto.KeyObject) return `${value.type} ${value.asymmetricKeyType} ${JSON.stringify(value.asymmetricKeyDetails)}`; + if (typeof value === "string") return value.split("\n")[0]; + if (value && typeof value === "object") return Object.entries(value).map(([key, field]) => `${key}=${describe(field)}`).join(" "); + return String(value); +} + +function attempt(label, run) { + try { + console.log(label, describe(run())); + } catch (e) { + console.log(label, "throws", e.name, e.code, e.message); + } +} + +// RFC 7748, section 6.1 +const alicePrivate = Buffer.from("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", "hex"); +const alicePublic = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"; +const bobPrivate = Buffer.from("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", "hex"); +const bobPublic = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"; +const alice = crypto.createPrivateKey({ key: alicePrivate, format: "raw-private", asymmetricKeyType: "x25519" }); +const bob = crypto.createPrivateKey({ key: bobPrivate, format: "raw-private", asymmetricKeyType: "x25519" }); +console.log("alice public", Buffer.from(crypto.createPublicKey(alice).export({ format: "raw-public" })).toString("hex") === alicePublic); +console.log("bob public", Buffer.from(crypto.createPublicKey(bob).export({ format: "raw-public" })).toString("hex") === bobPublic); +const shared = crypto.diffieHellman({ privateKey: alice, publicKey: crypto.createPublicKey(bob) }); +console.log("shared", shared.toString("hex")); +console.log("both ways", crypto.diffieHellman({ privateKey: bob, publicKey: crypto.createPublicKey(alice) }).equals(shared)); +console.log("raw-private kept as given", Buffer.from(alice.export({ format: "raw-private" })).equals(alicePrivate)); + +// encodings +console.log("details", describe(alice), describe(crypto.createPublicKey(alice))); +const spki = alice.export({ format: "der", type: "pkcs8" }); +console.log("pkcs8", spki.length, spki.subarray(0, 16).toString("hex")); +const pub = crypto.createPublicKey(alice); +const spkiDer = pub.export({ format: "der", type: "spki" }); +console.log("spki", spkiDer.length, spkiDer.subarray(0, 12).toString("hex")); +console.log("pem", describe(alice.export({ format: "pem", type: "pkcs8" })), describe(pub.export({ format: "pem", type: "spki" }))); +const jwk = alice.export({ format: "jwk" }); +console.log("jwk", Object.keys(jwk).join(), jwk.kty, jwk.crv, jwk.x === Buffer.from(alicePublic, "hex").toString("base64url"), jwk.d === alicePrivate.toString("base64url")); +console.log("public jwk", JSON.stringify(pub.export({ format: "jwk" }))); +const fromPem = crypto.createPrivateKey(alice.export({ format: "pem", type: "pkcs8" })); +console.log("pem round trip", fromPem.equals(alice), crypto.createPublicKey(pub.export({ format: "pem", type: "spki" })).equals(pub)); +console.log("jwk round trip", crypto.createPrivateKey({ key: jwk, format: "jwk" }).equals(alice), crypto.createPublicKey({ key: pub.export({ format: "jwk" }), format: "jwk" }).equals(pub)); +console.log("public of pkcs8", crypto.createPublicKey({ key: spki, format: "der", type: "pkcs8" }).equals(pub)); +console.log("not ed25519", alice.equals(crypto.generateKeyPairSync("ed25519").privateKey)); + +// generateKeyPair +const pair = crypto.generateKeyPairSync("x25519"); +console.log("generated", describe(pair)); +const clamped = Buffer.from(pair.privateKey.export({ format: "raw-private" })); +console.log("generated clamped", (clamped[0] & 7) === 0, (clamped[31] & 0xc0) === 0x40); +attempt("pem pair", () => + crypto.generateKeyPairSync("x25519", { + publicKeyEncoding: { type: "spki", format: "pem" }, + privateKeyEncoding: { type: "pkcs8", format: "der" }, + }), +); +attempt("raw pair", () => + crypto.generateKeyPairSync("x25519", { publicKeyEncoding: { format: "raw-public" }, privateKeyEncoding: { format: "raw-private" } }), +); +attempt("pkcs1", () => crypto.generateKeyPairSync("x25519", { publicKeyEncoding: { type: "pkcs1", format: "pem" } })); +attempt("sec1", () => crypto.generateKeyPairSync("x25519", { privateKeyEncoding: { type: "sec1", format: "pem" } })); +attempt("raw-seed", () => crypto.generateKeyPairSync("x25519", { privateKeyEncoding: { format: "raw-seed" } })); +attempt("X25519 spelling", () => crypto.generateKeyPairSync("X25519")); + +// what X25519 keys cannot do +const data = Buffer.from("x25519"); +attempt("sign", () => crypto.sign(null, data, alice)); +attempt("sign sha256", () => crypto.sign("sha256", data, alice)); +attempt("verify", () => crypto.verify(null, data, pub, Buffer.alloc(64))); +attempt("createSign", () => crypto.createSign("sha256").update(data).sign(alice)); +attempt("createVerify", () => crypto.createVerify("sha256").update(data).verify(pub, Buffer.alloc(64))); +attempt("hmac", () => crypto.createHmac("sha256", alice)); +attempt("export pkcs1", () => pub.export({ format: "pem", type: "pkcs1" })); +attempt("export raw-seed", () => alice.export({ format: "raw-seed" })); +attempt("short raw key", () => crypto.createPublicKey({ key: Buffer.alloc(31), format: "raw-public", asymmetricKeyType: "x25519" })); + +crypto.generateKeyPair("x25519", (err, publicKey, privateKey) => { + console.log("async", err, describe(publicKey), describe(privateKey)); + crypto.diffieHellman({ privateKey, publicKey: crypto.createPublicKey(bob) }, (err2, secret) => { + console.log("async secret", err2, describe(secret)); + }); +}); +console.log("returned"); diff --git a/tests/corpus/7451-crypto-diffie-hellman.ts b/tests/corpus/7451-crypto-diffie-hellman.ts new file mode 100644 index 000000000..9648cd32b --- /dev/null +++ b/tests/corpus/7451-crypto-diffie-hellman.ts @@ -0,0 +1,55 @@ +// crypto.diffieHellman with typed KeyObjects: ECDH on every supported +// curve, checked against createECDH, X25519, the callback form, and +// Node's errors for incompatible key types, mismatched curves and a +// small-order X25519 public key. +import * as crypto from "node:crypto"; + +function attempt(label: string, run: () => Buffer): void { + try { + const secret = run(); + console.log(label, secret.length); + } catch (e) { + const error = e as NodeJS.ErrnoException; + console.log(label, "throws", error.name, error.code, error.message); + } +} + +for (const namedCurve of ["prime256v1", "secp384r1", "secp521r1", "secp256k1"]) { + const a = crypto.generateKeyPairSync("ec", { namedCurve }); + const ecdh = crypto.createECDH(namedCurve); + const ecdhPublic = ecdh.generateKeys(); + const peer = crypto.createPublicKey({ key: ecdhPublic, format: "raw-public", asymmetricKeyType: "ec", namedCurve } as unknown as crypto.PublicKeyInput); + const secret = crypto.diffieHellman({ privateKey: a.privateKey, publicKey: peer }); + // The SubjectPublicKeyInfo ends with the uncompressed point. + const spki = a.publicKey.export({ format: "der", type: "spki" }); + const own = spki.subarray(spki.length - ecdhPublic.length); + console.log(namedCurve, secret.length, secret.equals(ecdh.computeSecret(own))); +} + +const x = crypto.generateKeyPairSync("x25519"); +const y = crypto.generateKeyPairSync("x25519"); +const xy = crypto.diffieHellman({ privateKey: x.privateKey, publicKey: y.publicKey }); +console.log("x25519", xy.length, xy.equals(crypto.diffieHellman({ privateKey: y.privateKey, publicKey: x.publicKey }))); +attempt("public key given as the private key", () => crypto.diffieHellman({ privateKey: x.privateKey, publicKey: y.privateKey })); + +const p256 = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" }); +const p384 = crypto.generateKeyPairSync("ec", { namedCurve: "P-384" }); +const rsa = crypto.generateKeyPairSync("rsa", { modulusLength: 1024 }); +const ed = crypto.generateKeyPairSync("ed25519"); +attempt("x25519 and ec", () => crypto.diffieHellman({ privateKey: x.privateKey, publicKey: p256.publicKey })); +attempt("ec and x25519", () => crypto.diffieHellman({ privateKey: p256.privateKey, publicKey: x.publicKey })); +attempt("rsa", () => crypto.diffieHellman({ privateKey: rsa.privateKey, publicKey: rsa.publicKey })); +attempt("ed25519", () => crypto.diffieHellman({ privateKey: ed.privateKey, publicKey: ed.publicKey })); +attempt("P-256 and P-384", () => crypto.diffieHellman({ privateKey: p256.privateKey, publicKey: p384.publicKey })); +attempt("public as private", () => crypto.diffieHellman({ privateKey: x.publicKey, publicKey: y.publicKey })); +const zero = crypto.createPublicKey({ key: Buffer.alloc(32), format: "raw-public", asymmetricKeyType: "x25519" } as unknown as crypto.PublicKeyInput); +attempt("small-order point", () => crypto.diffieHellman({ privateKey: x.privateKey, publicKey: zero })); + +crypto.diffieHellman({ privateKey: x.privateKey, publicKey: y.publicKey }, (err, secret) => { + console.log("callback", err, secret.equals(xy)); + crypto.diffieHellman({ privateKey: x.privateKey, publicKey: zero }, (...args: unknown[]) => { + const error = args[0] as NodeJS.ErrnoException; + console.log("callback error", args.length, error.code, error.message); + }); +}); +console.log("returned"); diff --git a/tests/corpus/7452-crypto-diffie-hellman-errors.mjs b/tests/corpus/7452-crypto-diffie-hellman-errors.mjs new file mode 100644 index 000000000..d8adc6442 --- /dev/null +++ b/tests/corpus/7452-crypto-diffie-hellman-errors.mjs @@ -0,0 +1,61 @@ +// crypto.diffieHellman's argument checks in Node's order, key inputs other +// than KeyObjects, and the own properties of the OpenSSL errors it and +// X25519 signatures throw. +import * as crypto from "node:crypto"; + +function attempt(label, run) { + try { + const result = run(); + console.log(label, Buffer.isBuffer(result) ? `Buffer(${result.length})` : String(result)); + } catch (e) { + console.log(label, "throws", e.name, e.code, e.message, JSON.stringify(Object.keys(e))); + } +} + +const x = crypto.generateKeyPairSync("x25519"); +const y = crypto.generateKeyPairSync("x25519"); +const ec = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" }); +const other = crypto.generateKeyPairSync("ec", { namedCurve: "P-384" }); +const dh = (options, callback) => () => + callback === undefined ? crypto.diffieHellman(options) : crypto.diffieHellman(options, callback); + +attempt("no options", dh()); +attempt("number options", dh(5)); +attempt("array options", dh([])); +attempt("number callback", dh({ privateKey: x.privateKey, publicKey: y.publicKey }, 5)); +attempt("callback before keys", dh({}, 5)); +attempt("no private key", dh({ publicKey: y.publicKey })); +attempt("no public key", dh({ privateKey: x.privateKey })); +attempt("number public key", dh({ privateKey: x.privateKey, publicKey: 2 })); +attempt("number private key", dh({ privateKey: 1, publicKey: y.publicKey })); +attempt("null private key", dh({ privateKey: null, publicKey: y.publicKey })); +attempt("public key object as private", dh({ privateKey: x.publicKey, publicKey: y.publicKey })); +attempt("types checked first", dh({ privateKey: x.privateKey, publicKey: ec.publicKey })); + +// key inputs other than KeyObjects +const pkcs8 = x.privateKey.export({ format: "pem", type: "pkcs8" }); +const spki = y.publicKey.export({ format: "pem", type: "spki" }); +attempt("pem keys", dh({ privateKey: pkcs8, publicKey: spki })); +attempt("der key objects", dh({ + privateKey: { key: x.privateKey.export({ format: "der", type: "pkcs8" }), format: "der", type: "pkcs8" }, + publicKey: { key: y.publicKey.export({ format: "jwk" }), format: "jwk" }, +})); +attempt("public key from a private pem", dh({ privateKey: x.privateKey, publicKey: pkcs8 })); +attempt("same result", () => + crypto.diffieHellman({ privateKey: pkcs8, publicKey: spki }).equals(crypto.diffieHellman({ privateKey: x.privateKey, publicKey: y.publicKey })), +); +attempt("mixed types from pem", dh({ privateKey: x.privateKey, publicKey: ec.publicKey.export({ format: "pem", type: "spki" }) })); +attempt("mismatched curves", dh({ privateKey: ec.privateKey, publicKey: other.publicKey })); +const zero = crypto.createPublicKey({ key: Buffer.alloc(32), format: "raw-public", asymmetricKeyType: "x25519" }); +attempt("small-order point", dh({ privateKey: x.privateKey, publicKey: zero })); +attempt("sign", () => crypto.sign(null, Buffer.from("x"), x.privateKey)); + +// the callback form: argument errors throw, job errors arrive without +// the OpenSSL properties +attempt("callback returns", dh({ privateKey: x.privateKey, publicKey: y.publicKey }, (err, secret) => { + console.log("callback", err, secret.length); + crypto.diffieHellman({ privateKey: ec.privateKey, publicKey: other.publicKey }, (error) => { + console.log("callback mismatch", error.code, error.message, JSON.stringify(Object.keys(error))); + }); +})); +attempt("callback with a bad key throws", dh({ privateKey: 1, publicKey: y.publicKey }, () => console.log("never"))); diff --git a/tests/corpus/7453-untyped-overload-results.cjs b/tests/corpus/7453-untyped-overload-results.cjs new file mode 100644 index 000000000..21923827d --- /dev/null +++ b/tests/corpus/7453-untyped-overload-results.cjs @@ -0,0 +1,85 @@ +// An untyped argument can match any overload of a call at run time. The +// result follows the overload the value selects, through bindings, +// destructuring and function returns. +const crypto = require("crypto"); +const fs = require("fs"); +const os = require("os"); +const path = require("path"); + +const ecPair = (options) => crypto.generateKeyPairSync("ec", options); +const objects = ecPair({ namedCurve: "P-256" }); +console.log(typeof objects.privateKey, objects.privateKey.type, objects.publicKey.asymmetricKeyType); +const pem = ecPair({ + namedCurve: "P-256", + publicKeyEncoding: { type: "spki", format: "pem" }, + privateKeyEncoding: { type: "pkcs8", format: "pem" }, +}); +console.log(typeof pem.privateKey, pem.publicKey.split("\n")[0]); + +function privateKeyKind(options) { + const pair = crypto.generateKeyPairSync("ec", options); + return typeof pair.privateKey; +} +console.log(privateKeyKind({ namedCurve: "P-384" })); + +function spread(options) { + const { privateKey, publicKey } = crypto.generateKeyPairSync("ec", { + namedCurve: "P-256", + ...options, + }); + return [typeof privateKey, Buffer.isBuffer(publicKey)]; +} +console.log(spread({}), spread({ publicKeyEncoding: { type: "spki", format: "der" } })); + +const digest = (encoding) => crypto.createHash("sha256").update("x").digest(encoding); +console.log(typeof digest("hex"), Buffer.isBuffer(digest(undefined)), digest(undefined).length); +const hmac = (encoding) => crypto.createHmac("sha256", "k").update("v").digest(encoding); +console.log(hmac("base64"), Buffer.isBuffer(hmac())); +function digestLength(encoding) { + let value = crypto.createHash("sha1").update("y").digest(encoding); + return Buffer.isBuffer(value) ? `buffer ${value.length}` : `string ${value.length}`; +} +console.log(digestLength(undefined), digestLength("hex")); +async function digestLater(encoding) { + return crypto.createHash("md5").update("z").digest(encoding); +} + +const key = crypto.generateKeyPairSync("ed25519").publicKey; +const exported = (options) => key.export(options); +console.log( + Buffer.isBuffer(exported({ format: "der", type: "spki" })), + typeof exported({ format: "pem", type: "spki" }), + exported({ format: "jwk" }).kty, +); + +const file = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "overloads-")), "text.txt"); +fs.writeFileSync(file, "hello"); +const read = (options) => fs.readFileSync(file, options); +const text = read("utf8"); +const bytes = read(undefined); +const latin1 = read({ encoding: "latin1" }); +console.log(text, Buffer.isBuffer(bytes), bytes.length, latin1); +console.log(read("utf8"), read({ encoding: "hex" })); + +/** + * @overload + * @param {string} value + * @returns {string} + */ +/** + * @overload + * @param {number} value + * @returns {number} + */ +/** @param {string | number} value */ +function same(value) { + return value; +} +const viaSame = (value) => same(value); +console.log(typeof viaSame("s"), typeof viaSame(3), viaSame(3) + 1); + +(async () => { + const later = await digestLater(undefined); + console.log(Buffer.isBuffer(later), typeof (await digestLater("hex"))); + fs.rmSync(path.dirname(file), { recursive: true }); +})(); diff --git a/tests/corpus/7527-buffer-from-array-buffer.ts b/tests/corpus/7527-buffer-from-array-buffer.ts new file mode 100644 index 000000000..fb650e9f2 --- /dev/null +++ b/tests/corpus/7527-buffer-from-array-buffer.ts @@ -0,0 +1,33 @@ +// Buffer.from(arrayBuffer, byteOffset, length): Node's fromArrayBuffer over +// typed and untyped ArrayBuffers and SharedArrayBuffers: the offset and +// length conversions, the shared memory, zero and negative lengths, and +// ERR_BUFFER_OUT_OF_BOUNDS for an offset or length past the end. +const ab = new ArrayBuffer(6); +new Uint8Array(ab).set([1, 2, 3, 4, 5, 6]); +const offsets: unknown[] = [undefined, NaN, "1", 1.5, -1, 0, 6, 7, null, true]; +const lengths: unknown[] = [undefined, 0, -1, 2.5, "2", 6, 9, NaN, null]; +for (const o of offsets) { + for (const l of lengths) { + try { + const b = Buffer.from(ab, o as number, l as number); + console.log(String(o), String(l), b, b.byteOffset, b.length); + } catch (e) { + const err = e as NodeJS.ErrnoException; + console.log(String(o), String(l), err.name, err.code, err.message); + } + } +} +const typed = Buffer.from(ab, 2, 2); +typed[0] = 99; +console.log(new Uint8Array(ab)[2], Buffer.from(ab, 4).toString("hex"), Buffer.isBuffer(Buffer.from(ab, 1, 1))); +const shared = new SharedArrayBuffer(4); +console.log(Buffer.from(shared, 1, 2).length); +const loose: any = new ArrayBuffer(4); +const pairs: [unknown, unknown][] = [[1, 2], [4, undefined], [5, 1], [0, 5], ["2", "1"]]; +for (const [o, l] of pairs) { + try { + console.log(Buffer.from(loose, o as number, l as number).toString("hex")); + } catch (e) { + console.log((e as NodeJS.ErrnoException).code, (e as Error).message); + } +} diff --git a/tests/corpus/7528-buffer-from-array-buffer.cjs b/tests/corpus/7528-buffer-from-array-buffer.cjs new file mode 100644 index 000000000..f3d185b1a --- /dev/null +++ b/tests/corpus/7528-buffer-from-array-buffer.cjs @@ -0,0 +1,46 @@ +// Buffer.from(arrayBuffer, byteOffset, length) in JavaScript, with offsets +// and lengths of every kind and an ArrayBuffer held in an untyped slot. +const ab = new ArrayBuffer(6); +new Uint8Array(ab).set([1, 2, 3, 4, 5, 6]); +const offsets = [undefined, NaN, "1", 1.5, -1, 0, 6, 7, null, true]; +const lengths = [undefined, 0, -1, 2.5, "2", 6, 9, NaN, null]; +for (const o of offsets) { + for (const l of lengths) { + try { + const b = Buffer.from(ab, o, l); + console.log(String(o), String(l), b, b.byteOffset, b.length); + } catch (e) { + const err = e; + console.log(String(o), String(l), err.name, err.code, err.message); + } + } +} +const typed = Buffer.from(ab, 2, 2); +typed[0] = 99; +console.log(new Uint8Array(ab)[2], Buffer.from(ab, 4).toString("hex"), Buffer.isBuffer(Buffer.from(ab, 1, 1))); +const shared = new SharedArrayBuffer(4); +console.log(Buffer.from(shared, 1, 2).length); +const slots = { buffer: new ArrayBuffer(3) }; +new Uint8Array(slots.buffer).set([9, 8, 7]); +for (const args of [[1], [1, 1], [0, 0], [3, 0], [2, 5], [-0.5, 2]]) { + try { + console.log(args.join(","), Buffer.from(slots.buffer, args[0], args[1])); + } catch (e) { + console.log(args.join(","), e.code, e.message); + } +} +// A string's second argument is its encoding, with or without a third. +const encoded = JSON.parse('"aGk="'); +console.log( + Buffer.from(encoded, "base64", 1).toString(), + Buffer.from(encoded, "latin1", 2).length, + Buffer.from(encoded, undefined, 3).toString(), + Buffer.from(encoded, "", 3).toString(), +); +for (const text of [encoded, JSON.parse('""')]) { + try { + console.log(Buffer.from(text, "bogus", 1)); + } catch (e) { + console.log(e.code, e.message); + } +} diff --git a/tests/corpus/7529-buffer-constructor.cjs b/tests/corpus/7529-buffer-constructor.cjs new file mode 100644 index 000000000..3181cebec --- /dev/null +++ b/tests/corpus/7529-buffer-constructor.cjs @@ -0,0 +1,80 @@ +// The deprecated Buffer constructor, called and constructed, as Node's +// Buffer function runs it: the arguments evaluate in order, the first call +// reports DEP0005 on the next tick (once), a number allocates zero-filled +// bytes, and other values convert as Buffer.from does. +// The program re-runs itself and prints the child's output with process +// ids normalized. +"use strict"; +const { spawnSync } = require("node:child_process"); +const { Buffer: Imported } = require("buffer"); + +function child() { + process.on("warning", (w) => console.log("listener", w.name, w.code, w.message.length)); + const B = Buffer; + + const show = (label, fn) => { + try { + const b = fn(); + console.log(label, b, b.length, Buffer.isBuffer(b)); + } catch (e) { + console.log(label, e.name, e.code, e.message); + } + }; + + // The throwing form still reports the warning, after the catch. + show("number+encoding", () => new Buffer(3, "utf8")); + show("size", () => new Buffer(4)); + show("fraction", () => new Buffer(2.7)); + show("zero", () => Buffer(0)); + show("negative", () => new Buffer(-1)); + show("nan", () => new Buffer(NaN)); + show("string", () => new Buffer("héllo")); + show("hex", () => new Buffer("6869", "hex")); + show("base64", () => Buffer("aGk=", "base64")); + show("empty encoding", () => new Buffer("x", "")); + show("bad encoding", () => new Buffer("x", "nope")); + show("array", () => new Buffer([1, 2, 300, -1])); + show("array-like", () => new Buffer({ length: 2, 0: 7, 1: 8 })); + show("buffer", () => new B(Buffer.from("copy"))); + show("typed", () => Buffer(new Uint16Array([1, 258]))); + const storage = new Uint8Array([10, 20, 30, 40, 50]).buffer; + show("arraybuffer", () => new Buffer(storage)); + show("offset", () => new Buffer(storage, 1)); + show("range", () => Buffer(storage, 1, 3)); + show("range past end", () => new Buffer(storage, 6)); + show("string third", () => new Buffer("abc", "latin1", 1)); + show("undefined", () => new Buffer(undefined)); + show("null", () => Buffer(null)); + show("none", () => new Buffer()); + show("imported", () => new Imported("imp")); + + console.log(Buffer(2), Buffer("ab").length, Buffer("hi").toString("hex"), new Buffer([5])); + console.log(Buffer.isBuffer(Buffer(1)), Buffer(1) instanceof Uint8Array, new Buffer(1) instanceof Uint8Array); + + // The view shares the ArrayBuffer's memory. + const view = new Buffer(storage, 2, 2); + new Uint8Array(storage)[2] = 99; + console.log(view); + + const order = []; + const arg = (v) => (order.push(v), v); + new Buffer(arg("a"), arg("utf8")); + Buffer(arg(2)); + console.log(order); + + console.log("sync end"); + process.nextTick(() => console.log("tick")); + setTimeout(() => { + new Buffer(1); + console.log("timer"); + }, 0); +} + +if (process.argv[2] === "child") { + child(); +} else { + const run = spawnSync(process.execPath, [process.argv[1], "child"], { encoding: "utf8" }); + console.log(`status: ${run.status}`); + console.log(run.stdout); + console.log(run.stderr.replace(/\(node:\d+\)/g, "(node:)")); +} diff --git a/tests/corpus/7530-buffer-constructor.ts b/tests/corpus/7530-buffer-constructor.ts new file mode 100644 index 000000000..14eac61d5 --- /dev/null +++ b/tests/corpus/7530-buffer-constructor.ts @@ -0,0 +1,42 @@ +// The deprecated `new Buffer(...)` in TypeScript, over the forms +// @types/node declares: strings with an encoding, sizes, arrays and +// ArrayBuffers. The first call reports DEP0005 on the next tick. +// The program re-runs itself and prints the child's output with process +// ids normalized. +import { spawnSync } from "node:child_process"; + +function child(): void { + process.on("warning", (w) => console.log("listener", w.name, (w as { code?: string }).code)); + + function sized(n: number): Buffer { + return new Buffer(n); + } + + const encodings: BufferEncoding[] = ["utf8", "hex", "base64", "latin1"]; + for (const encoding of encodings) { + console.log(encoding, new Buffer("6869", encoding)); + } + console.log(sized(3), sized(0).length); + console.log(new Buffer([104, 105]).toString(), new Buffer(new Uint8Array([1, 2]))); + const storage = new ArrayBuffer(4); + const view = new Buffer(storage); + view[1] = 5; + console.log(new Uint8Array(storage), view.buffer === storage); + const text: string = "typed"; + console.log(new Buffer(text).toString("hex")); + try { + sized(-2); + } catch (e) { + console.log((e as Error).name, (e as { code?: string }).code); + } + console.log("sync end"); +} + +if (process.argv[2] === "child") { + child(); +} else { + const run = spawnSync(process.execPath, [process.argv[1]!, "child"], { encoding: "utf8" }); + console.log(`status: ${run.status}`); + console.log(run.stdout); + console.log(run.stderr.replace(/\(node:\d+\)/g, "(node:)")); +} diff --git a/tests/corpus/7531-buffer-constructor-dependency/main.cjs b/tests/corpus/7531-buffer-constructor-dependency/main.cjs new file mode 100644 index 000000000..75b130bc6 --- /dev/null +++ b/tests/corpus/7531-buffer-constructor-dependency/main.cjs @@ -0,0 +1,36 @@ +// The deprecated Buffer constructor called from a dependency under +// node_modules: Node reports no DEP0005 for those calls, and the program's +// own first call reports it once, on the next tick. +// The program re-runs itself and prints the child's output with process +// ids normalized. +"use strict"; +const { spawnSync } = require("node:child_process"); +const legacy = require("legacy-buf"); + +function child() { + process.on("warning", (w) => console.log("listener", w.name, w.code)); + + console.log(legacy.construct("hi", "utf8"), legacy.construct(3), legacy.call([1, 2])); + const storage = new Uint8Array([1, 2, 3, 4]).buffer; + console.log(legacy.construct(storage, 1, 2), legacy.many(5)); + try { + legacy.construct(4, "hex"); + } catch (e) { + console.log(e.code); + } + process.nextTick(() => { + console.log("tick"); + console.log(new Buffer("own")); + console.log(Buffer(2), new Buffer([9])); + setTimeout(() => console.log("timer"), 0); + }); +} + +if (process.argv[2] === "child") { + child(); +} else { + const run = spawnSync(process.execPath, [process.argv[1], "child"], { encoding: "utf8" }); + console.log(`status: ${run.status}`); + console.log(run.stdout); + console.log(run.stderr.replace(/\(node:\d+\)/g, "(node:)")); +} diff --git a/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/index.d.ts b/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/index.d.ts new file mode 100644 index 000000000..ef314d625 --- /dev/null +++ b/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/index.d.ts @@ -0,0 +1,3 @@ +export declare function construct(value: unknown, encodingOrOffset?: unknown, length?: unknown): Buffer; +export declare function call(value: unknown): Buffer; +export declare function many(count: number): number; diff --git a/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/index.js b/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/index.js new file mode 100644 index 000000000..062e4e41d --- /dev/null +++ b/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/index.js @@ -0,0 +1,15 @@ +"use strict"; +// A dependency that keeps the deprecated Buffer constructor. +exports.construct = function (value, encodingOrOffset, length) { + if (length !== undefined) return new Buffer(value, encodingOrOffset, length); + if (encodingOrOffset !== undefined) return new Buffer(value, encodingOrOffset); + return new Buffer(value); +}; +exports.call = function (value) { + return Buffer(value); +}; +exports.many = function (count) { + let total = 0; + for (let i = 0; i < count; i++) total += new Buffer(1).length; + return total; +}; diff --git a/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/package.json b/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/package.json new file mode 100644 index 000000000..db2277b60 --- /dev/null +++ b/tests/corpus/7531-buffer-constructor-dependency/node_modules/legacy-buf/package.json @@ -0,0 +1,5 @@ +{ + "name": "legacy-buf", + "version": "1.0.0", + "main": "index.js" +} diff --git a/tests/corpus/7532-buffer-constructor-check-limit/main.cjs b/tests/corpus/7532-buffer-constructor-check-limit/main.cjs new file mode 100644 index 000000000..c08690a83 --- /dev/null +++ b/tests/corpus/7532-buffer-constructor-check-limit/main.cjs @@ -0,0 +1,10 @@ +// Node checks the caller of the deprecated Buffer constructor for its first +// 10000 calls only: once a dependency under node_modules has made them, the +// program's own calls report no DEP0005. +"use strict"; +process.on("warning", (w) => console.log("listener", w.code)); +const legacy = require("legacy-buf"); + +console.log(legacy.many(10000)); +console.log(new Buffer("after"), Buffer(1)); +setTimeout(() => console.log("timer"), 0); diff --git a/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/index.d.ts b/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/index.d.ts new file mode 100644 index 000000000..ef314d625 --- /dev/null +++ b/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/index.d.ts @@ -0,0 +1,3 @@ +export declare function construct(value: unknown, encodingOrOffset?: unknown, length?: unknown): Buffer; +export declare function call(value: unknown): Buffer; +export declare function many(count: number): number; diff --git a/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/index.js b/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/index.js new file mode 100644 index 000000000..062e4e41d --- /dev/null +++ b/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/index.js @@ -0,0 +1,15 @@ +"use strict"; +// A dependency that keeps the deprecated Buffer constructor. +exports.construct = function (value, encodingOrOffset, length) { + if (length !== undefined) return new Buffer(value, encodingOrOffset, length); + if (encodingOrOffset !== undefined) return new Buffer(value, encodingOrOffset); + return new Buffer(value); +}; +exports.call = function (value) { + return Buffer(value); +}; +exports.many = function (count) { + let total = 0; + for (let i = 0; i < count; i++) total += new Buffer(1).length; + return total; +}; diff --git a/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/package.json b/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/package.json new file mode 100644 index 000000000..db2277b60 --- /dev/null +++ b/tests/corpus/7532-buffer-constructor-check-limit/node_modules/legacy-buf/package.json @@ -0,0 +1,5 @@ +{ + "name": "legacy-buf", + "version": "1.0.0", + "main": "index.js" +} diff --git a/tests/corpus/7533-buffer-value.cjs b/tests/corpus/7533-buffer-value.cjs new file mode 100644 index 000000000..3fd500ec2 --- /dev/null +++ b/tests/corpus/7533-buffer-value.cjs @@ -0,0 +1,101 @@ +// The Buffer function read as a value: one identity across the global, the +// buffer module and its aliases, Node's own enumerable members in Node's +// order, statics that apply Node's argument handling, instanceof, and a +// poolSize property the pool reads. +"use strict"; +const buffer = require("buffer"); +const nodeBuffer = require("node:buffer"); +const util = require("util"); + +// bn.js keeps the module's Buffer in a variable assigned under try. +var BnBuffer; +try { + BnBuffer = require("buffer").Buffer; +} catch (e) {} +const pass = (value) => value; +const Stored = pass(require("buffer").Buffer); +const Alias = Buffer; +const table = { Buffer: buffer.Buffer }; + +console.log(typeof Stored, Stored.name, Stored.length, Alias.name, Alias.length); +console.log(Stored === Buffer, Stored === globalThis.Buffer, Stored === buffer.Buffer, Stored === nodeBuffer.Buffer); +console.log(Stored === Alias, table.Buffer === Stored, Stored.constructor === Function); +console.log(Stored); +console.log(Object.keys(Stored)); +console.log(util.inspect({ nested: { Buffer: Stored } }, { depth: 1 })); + +// Statics through the value. +const show = (label, run) => { + try { + console.log(label, run()); + } catch (e) { + console.log(label, e.name, e.code, e.message); + } +}; +show("from string", () => Stored.from("héllo")); +show("from hex", () => Stored.from("6869", "hex")); +show("from empty encoding", () => Stored.from("x", "")); +show("from bad encoding", () => Stored.from("x", "nope")); +show("from array", () => Stored.from([1, 2, 300])); +const storage = new Uint8Array([1, 2, 3, 4, 5]).buffer; +show("from range", () => Stored.from(storage, 1, 3)); +show("from number", () => Stored.from(5)); +show("alloc", () => Stored.alloc(3, "ab")); +show("alloc fill", () => Stored.alloc(2, 7)); +show("alloc string size", () => Stored.alloc("3")); +show("allocUnsafe", () => Stored.allocUnsafe(4).fill(9)); +show("allocUnsafe negative", () => Stored.allocUnsafe(-1)); +show("allocUnsafe string", () => Stored.allocUnsafe("4")); +show("allocUnsafeSlow", () => Stored.allocUnsafeSlow(2).fill(1)); +show("isBuffer", () => [Stored.isBuffer(Buffer.alloc(1)), Stored.isBuffer(new Uint8Array(1)), Stored.isBuffer("x")]); +show("isEncoding", () => [Stored.isEncoding("UTF-8"), Stored.isEncoding(""), Stored.isEncoding("nope"), Stored.isEncoding(1)]); +show("compare", () => Stored.compare(Buffer.from("a"), Buffer.from("b"))); +show("compare bad", () => Stored.compare("a", Buffer.from("b"))); +show("concat", () => Stored.concat([Buffer.from("ab"), Buffer.from("cd")])); +show("concat length", () => Stored.concat([Buffer.from("ab"), Buffer.from("cd")], 3)); +show("concat empty", () => Stored.concat([], "x")); +show("concat bad length", () => Stored.concat([Buffer.from("a")], "x")); +show("concat bad list", () => Stored.concat("x")); +show("byteLength", () => [Stored.byteLength("héllo"), Stored.byteLength("aGk=", "base64"), Stored.byteLength(new Uint16Array(3))]); + +// Member values keep one identity, with Node's names and lengths. +const members = [ + [Buffer.from, Stored.from], + [Buffer.alloc, Stored.alloc], + [Buffer.allocUnsafe, Stored.allocUnsafe], + [Buffer.isBuffer, Stored.isBuffer], + [Buffer.compare, Stored.compare], + [Buffer.concat, Stored.concat], + [Buffer.byteLength, Stored.byteLength], + [Buffer.isEncoding, Stored.isEncoding], +]; +console.log(members.map(([a, b]) => `${a.name}/${a.length}/${a === b}`).join(" ")); +const isBuffer = require("buffer").Buffer.isBuffer; +console.log(isBuffer(Buffer.from("x")), isBuffer(null), typeof Buffer.allocUnsafe); + +// A bn.js-style allocator over Buffer or Array. +function allocate(ArrayType, size) { + if (ArrayType.allocUnsafe) return ArrayType.allocUnsafe(size); + return new ArrayType(size); +} +function toArrayLike(ArrayType, size) { + const made = allocate(ArrayType, size); + made.fill(4); + return made; +} +function toBuffer(size) { + return BnBuffer ? toArrayLike(BnBuffer, size) : null; +} +const made = toBuffer(3); +console.log(made, toArrayLike(Array, 2), made instanceof Stored, made instanceof Buffer); +console.log(new Uint8Array(2) instanceof Stored, Buffer.from("x") instanceof Alias, pass("x") instanceof Stored); + +// poolSize: one setting through the value and the static spelling. +console.log(Stored.poolSize, Buffer.poolSize); +Buffer.poolSize = 1024; +console.log(Stored.poolSize); +const small = Buffer.allocUnsafe(600); +console.log(small.buffer.byteLength, Buffer.allocUnsafe(400).buffer.byteLength); +Stored.poolSize = 2048; +console.log(Buffer.poolSize, Buffer.allocUnsafe(600).buffer.byteLength); +Stored.poolSize = 65536; diff --git a/tests/corpus/7534-buffer-prototype-members.cjs b/tests/corpus/7534-buffer-prototype-members.cjs new file mode 100644 index 000000000..2fd347373 --- /dev/null +++ b/tests/corpus/7534-buffer-prototype-members.cjs @@ -0,0 +1,29 @@ +// Buffer.prototype members read as values, as smart-buffer probes and +// calls them: own methods are functions with Node's names and lengths that +// run on a Buffer receiver, the parent and offset getters read on the +// prototype answer undefined, constructor is Buffer, and absent names are +// undefined. +"use strict"; +const buffer_1 = require("buffer"); + +function check(method) { + if (typeof buffer_1.Buffer.prototype[method] === "undefined") return `${method}: missing`; + return `${method}: ${typeof buffer_1.Buffer.prototype[method]}`; +} +console.log(["writeBigInt64BE", "readBigUInt64LE", "readUInt8", "nope", "parent", "offset"].map(check)); +console.log(Buffer.prototype.constructor === Buffer, Buffer.prototype["constructor"] === buffer_1.Buffer); +console.log(Buffer.prototype[0], Buffer.prototype[undefined], Buffer.prototype.toJSON === Buffer.prototype.toJSON); + +const names = ["readUInt8", "readUint8", "readFloatLE", "writeUInt16LE", "readUIntLE", "toString", "toLocaleString", "slice", "hexSlice"]; +console.log(names.map((n) => `${n}=${JSON.stringify(Buffer.prototype[n].name)}/${Buffer.prototype[n].length}`).join(" ")); + +const buf = Buffer.alloc(8); +function write(func, value, offset) { + return func.call(buf, value, offset); +} +console.log(write(Buffer.prototype.writeUInt16LE, 513, 0), write(Buffer.prototype.writeInt8, -2, 2), buf); +console.log(Buffer.prototype.readUInt16LE.call(buf, 0), Buffer.prototype.readInt8.call(buf, 2)); +console.log(Buffer.prototype.toString.call(buf, "hex", 0, 3), Buffer.prototype.toLocaleString.call(Buffer.from("hi"))); +console.log(Buffer.prototype.slice.call(buf, 0, 2), Buffer.prototype.equals.call(buf, Buffer.alloc(8))); +console.log(Buffer.prototype.fill.call(Buffer.alloc(3), 1), Buffer.prototype.indexOf.call(Buffer.from("abc"), "c")); +console.log(Buffer.prototype.write.call(buf, "zz", 4), buf, Buffer.prototype.toJSON.call(Buffer.from([1]))); diff --git a/tests/corpus/7535-inspect-function-properties.cjs b/tests/corpus/7535-inspect-function-properties.cjs new file mode 100644 index 000000000..5e3f8c6a6 --- /dev/null +++ b/tests/corpus/7535-inspect-function-properties.cjs @@ -0,0 +1,29 @@ +// util.inspect of functions with own enumerable properties, as Node's +// formatRaw prints them: the function's base then its keys (symbols +// included), `[Function]` past the depth limit, and the plain base when +// every own property is hidden. +"use strict"; +const util = require("util"); + +function named() {} +named.x = 1; +const arrow = () => {}; +arrow.a = "s"; +arrow[Symbol("k")] = 2; +Object.defineProperty(arrow, "hidden", { value: 3, enumerable: false }); +function hiddenOnly() {} +Object.defineProperty(hiddenOnly, "secret", { value: 1, enumerable: false }); +function big() {} +for (let i = 0; i < 8; i++) big["key" + i] = i; +function self() {} +self.me = self; + +const pass = (value) => value; + +console.log(named, arrow, hiddenOnly); +console.log(util.inspect(named, { depth: 0 })); +console.log(util.inspect(pass({ wrap: named }), { depth: 0 })); +console.log(util.inspect(pass([named, arrow]))); +console.log(util.inspect(big)); +console.log(util.inspect(self)); +console.log(util.inspect(pass({ a: { b: { f: big } } }))); diff --git a/tests/corpus/7580-int32-arrays-across-calls.ts b/tests/corpus/7580-int32-arrays-across-calls.ts new file mode 100644 index 000000000..df180b264 --- /dev/null +++ b/tests/corpus/7580-int32-arrays-across-calls.ts @@ -0,0 +1,166 @@ +// Number arrays of proven int32s that reach other functions: passed to +// helpers called directly, along chains of helpers and recursion, and held +// in module globals that several functions read. A helper that also +// receives an array of other numbers or one that is not a fresh array, +// stores a fraction through its parameter, or escapes as a value keeps +// every array it can reach in doubles, as does a global an async function +// reads. Each case uses its own arrays and helpers. + +const data: number[] = []; +let seed = 7; +for (let i = 0; i < 2000; i++) { + seed = (Math.imul(seed, 48271) + 11) & 0x7fffffff; + data.push(seed % 1000); +} + +function fnv(values: number[]): number { + let h = 2166136261; + for (let i = 0; i < values.length; i++) { + h ^= values[i]! & 0xff; + h = Math.imul(h, 16777619) >>> 0; + } + return h; +} + +function histogram(values: number[], buckets: number): number[] { + const counts: number[] = new Array(buckets).fill(0); + for (let i = 0; i < values.length; i++) { + const b = values[i]! % buckets; + counts[b] = counts[b]! + 1; + } + return counts; +} + +// A helper that writes through its parameter and calls another helper. +function scale(values: number[], factor: number): number { + for (let i = 0; i < values.length; i++) values[i] = (values[i]! * factor) & 0xffff; + return sum(values); +} + +function sum(values: number[]): number { + let s = 0; + for (let i = 0; i < values.length; i++) s = (s + values[i]!) | 0; + return s; +} + +// Recursion passes the array on, and the elements index another array. +function chase(next: number[], at: number, steps: number): number { + if (steps === 0) return at; + return chase(next, next[at]!, steps - 1); +} + +function pushAll(target: number[], count: number): void { + for (let i = 0; i < count; i++) target.push((i * 3) & 0xffff); +} + +// `n` is a power of two: the masked elements are indexes of `perm`. +function locals(n: number): string { + const perm: number[] = []; + for (let i = 0; i < n; i++) perm.push((i * 37 + 11) & (n - 1)); + const copy: number[] = []; + pushAll(copy, n); + const s1 = scale(copy, 5); + return `locals ${chase(perm, 0, 1000)} ${s1} ${copy[n - 1]} ${copy.length} ${sum(perm)}`; +} + +// Read only through the element reader (`a[i] ?? 0`) and states. +function readSparse(values: number[], at: number): string { + return `${values[at] ?? -1} ${at in values} ${values.length}`; +} + +// The same helper receives an array of other numbers too: the arrays it +// can reach keep doubles. +function mean(values: number[]): number { + let s = 0; + for (let i = 0; i < values.length; i++) s += values[i]!; + return s / values.length; +} + +// A store through the parameter that is not an int32. +function halve(values: number[]): void { + for (let i = 0; i < values.length; i++) values[i] = values[i]! / 2; +} + +// A function used as a value keeps its parameters. +function first(values: number[]): number { + return values[0]!; +} + +let reassigned: number[] = [1, 2, 3]; +function regrow(n: number): number { + reassigned = []; + for (let i = 0; i < n; i++) reassigned.push(i & 15); + let s = 0; + for (let i = 0; i < reassigned.length; i++) s += reassigned[i]!; + return s; +} + +// Not a fresh array: the result of a call. +function total(values: number[]): number { + let s = 0; + for (let i = 0; i < values.length; i++) s += values[i]!; + return s; +} + +const awaited: number[] = [4, 5, 6]; +async function later(): Promise { + return awaited[0]! + awaited.length; +} + +// A helper whose record result its callers only project: the backend may +// call its result-slot entry instead, so its parameter keeps doubles. +interface Span { + lo: number; + hi: number; +} +function spanAt(values: number[], i: number): Span { + return { lo: values[i]!, hi: values[i + 1]! }; +} +const spans: number[] = [5, 9, 2, 7, 11]; +function spanTotal(): number { + let total = 0; + for (let i = 0; i < 4; i++) { + const span = spanAt(spans, i); + total += span.hi * 10 - span.lo; + } + return total; +} + +const ints: number[] = [3, 4, 5]; +const fractions: number[] = [0.5, 1.5, 2.5]; +const halved: number[] = [8, 6, 4]; +halve(halved); +const pick: (values: number[]) => number = first; +const viaValue: number[] = [9, 8, 7]; + +// An index that calls a function reassigning the global: the read uses the +// array the global held before the index ran, as in Node. +let swapped: number[] = [1, 2, 3]; +function swapIn(): number { + swapped = []; + for (let i = 0; i < 4; i++) swapped.push(i + 10); + return 1; +} +function fillSwapped(n: number): number { + swapped = []; + for (let i = 0; i < n; i++) swapped.push(i & 15); + let s = 0; + for (let i = 0; i < swapped.length; i++) s += swapped[i]!; + return s; +} +function readSwapped(): number | undefined { + return swapped[swapIn()]; +} + +console.log(`fnv ${fnv(data)} ${fnv(data)}`); +const h = histogram(data, 10); +console.log(`hist ${h.length} ${h[0]} ${h[9]} ${total(h)}`); +console.log(locals(128)); +console.log(readSparse(data, 5), readSparse(data, 5000)); +console.log(`mean ${mean(ints)} ${mean(fractions)}`); +console.log(`halved ${halved[0]} ${halved[2]} ${halved.length}`); +console.log(`value ${pick(viaValue)} ${viaValue.length}`); +console.log(`regrow ${regrow(40)} ${reassigned.length} ${reassigned[39]} ${regrow(5)}`); +console.log(`span ${spanTotal()}`); +console.log(`swap ${fillSwapped(6)} ${readSwapped()} ${swapped[1]}`); +void later().then((v) => console.log(`later ${v}`)); diff --git a/tests/corpus/7623-key-order-javascript-built-records/lib.js b/tests/corpus/7623-key-order-javascript-built-records/lib.js new file mode 100644 index 000000000..a981505fc --- /dev/null +++ b/tests/corpus/7623-key-order-javascript-built-records/lib.js @@ -0,0 +1,18 @@ +// Records built in JavaScript in another key order than the TypeScript +// literals of the same type: a replacement method installed through a +// computed key (beside another computed write that installs a function +// returning a primitive), and a function stored on the global object, read +// through a typed view, that calls a callback inside its options record. +export function install(target, name) { + target[name] = () => ({ a: 1, b: 2 }); +} + +// A later computed write of a function that returns a primitive does not +// hide the record-returning one above. +export function installLabel(target, name) { + target[name] = () => "label"; +} + +globalThis.__eachRow = function (options) { + options.visit({ x: 5, y: 6 }); +}; diff --git a/tests/corpus/7623-key-order-javascript-built-records/main.ts b/tests/corpus/7623-key-order-javascript-built-records/main.ts new file mode 100644 index 000000000..1615351d7 --- /dev/null +++ b/tests/corpus/7623-key-order-javascript-built-records/main.ts @@ -0,0 +1,53 @@ +// Copies and enumerations of records JavaScript builds follow the order +// JavaScript wrote, also when the TypeScript literals of the type write +// another order and spread their own type. Each case uses its own type. +import { install, installLabel } from "./lib.js"; + +interface Pair { + a: number; + b: number; +} + +interface Row { + x: number; + y: number; +} + +interface Options { + visit: (row: Row) => void; +} + +class Store { + get(): Pair { + return { b: 2, a: 1 }; + } +} + +function showPair(label: string, value: Pair): void { + const copy = { ...value }; + console.log(label, Object.keys(value).join(), JSON.stringify(copy), Object.keys(copy).join()); +} + +function showRow(label: string, value: Row): void { + const copy = { ...value }; + console.log(label, Object.keys(value).join(), JSON.stringify(copy), Object.keys(copy).join()); +} + +// A replaceable method whose replacement returns a record JavaScript builds. +const store = new Store(); +showPair("method", store.get()); +showPair("self copy", { ...store.get(), b: 3 }); +install(store, "get"); +showPair("replaced", store.get()); +const labels: { label(): string } = { label: () => "plain" }; +installLabel(labels, "label"); +console.log(labels.label()); + +// A JavaScript function, checked into a typed slot, calls a native callback +// it receives inside its options record with a record it builds. +const literal: Row = { y: 20, x: 10 }; +showRow("literal", literal); +showRow("self copy", { ...literal, y: 21 }); +const eachRow = (globalThis as typeof globalThis & { __eachRow: (options: Options) => void }) + .__eachRow; +eachRow({ visit: (row: Row) => showRow("checked function", row) }); diff --git a/tests/corpus/7624-key-order-boxed-callback-records/lib.js b/tests/corpus/7624-key-order-boxed-callback-records/lib.js new file mode 100644 index 000000000..47ba5b99c --- /dev/null +++ b/tests/corpus/7624-key-order-boxed-callback-records/lib.js @@ -0,0 +1,7 @@ +// A JavaScript function that calls a callback inside its options object +// with records it builds, in another key order than the TypeScript literals +// of the callback's parameter type. +export function each(options) { + options.visit({ a: 1, b: 2 }); + options.visit({ a: 3, b: 4 }); +} diff --git a/tests/corpus/7624-key-order-boxed-callback-records/main.ts b/tests/corpus/7624-key-order-boxed-callback-records/main.ts new file mode 100644 index 000000000..4cdfeff37 --- /dev/null +++ b/tests/corpus/7624-key-order-boxed-callback-records/main.ts @@ -0,0 +1,27 @@ +// A native callback handed to JavaScript inside an options record receives +// records JavaScript builds: copies and enumerations of them follow the +// order JavaScript wrote, also when the TypeScript literals of the type +// write another order and spread their own type. +import { each } from "./lib.js"; + +interface Row { + a: number; + b: number; +} + +function show(label: string, row: Row): void { + const copy = { ...row }; + console.log(label, Object.keys(row).join(), JSON.stringify(copy), Object.keys(copy).join()); +} + +const literal: Row = { b: 20, a: 10 }; +show("literal", literal); +show("self copy", { ...literal, b: 21 }); +const seen: Row[] = []; +each({ + visit: (row: Row) => { + seen.push(row); + show("callback", row); + }, +}); +console.log(JSON.stringify(seen), JSON.stringify(seen.map((row) => ({ ...row, a: row.a * 2 })))); diff --git a/tests/corpus/7642-element-assignment-order.ts b/tests/corpus/7642-element-assignment-order.ts new file mode 100644 index 000000000..e020d05fc --- /dev/null +++ b/tests/corpus/7642-element-assignment-order.ts @@ -0,0 +1,45 @@ +// An element assignment evaluates its receiver and index before its value, +// also when the value is an optional element read or a call that assigns +// the bindings the receiver and index read. +const a = [10, 20, 30, 40]; +const b = [1, 2, 3, 4]; +let n = 0; +a[n++] = b[n]!; +console.log(a.join(), n); +a[n] = b[n++]; +console.log(a.join(), n); + +const optional: (number | undefined)[] = [undefined, 5, undefined]; +let k = 0; +optional[k++] = optional[k]; +console.log(optional, k); + +// A call in the value assigns the receiver and the index through closures. +let target: (number | undefined)[] = [0, 0, 0]; +const first = target; +let at = 0; +function swap(): number | undefined { + target = [9, 9, 9]; + at = 2; + return b[3]; +} +target[at] = swap(); +console.log(first, target, at); + +// The index and receiver are not evaluated twice. +let reads = 0; +function index(): number { + reads++; + return 1; +} +const c: (number | undefined)[] = [0, 0]; +c[index()] = optional[1]; +console.log(c, reads); + +// The index assigns the receiver's binding: the store uses the receiver +// evaluated before it. +let receiver: (number | undefined)[] = [0, 0]; +const original = receiver; +const replacement: (number | undefined)[] = [7, 7]; +receiver[((receiver = replacement), 1)] = optional[1]; +console.log(original, replacement, receiver === replacement); diff --git a/tests/corpus/7660-pointer-union-forwarded-arguments.ts b/tests/corpus/7660-pointer-union-forwarded-arguments.ts new file mode 100644 index 000000000..664f79fcb --- /dev/null +++ b/tests/corpus/7660-pointer-union-forwarded-arguments.ts @@ -0,0 +1,127 @@ +// Records and class instances passed to `T | undefined` and `T | null` +// parameters. The callee receives the object pointer itself, so a binding +// that the callee only reads keeps its frame storage; callees that keep, +// return or compare the argument still see one shared object. +interface Vec { + x: number; + y: number; +} +interface Tagged { + name: string; + at: Vec; +} +function vec(x: number, y: number): Vec { + return { x, y }; +} +function len(v: Vec | undefined): number { + return v === undefined ? -1 : Math.sqrt(v.x * v.x + v.y * v.y); +} +function sum(a: Vec | undefined, b: Vec): Vec { + return a === undefined ? b : { x: a.x + b.x, y: a.y + b.y }; +} +function label(t: Tagged | undefined): string { + return t === undefined ? "none" : t.name + "@" + t.at.x + "," + t.at.y; +} +function depth(v: Vec | undefined, n: number): number { + if (v === undefined || n === 0) return 0; + return v.x + depth(v, n - 1); +} + +const kept: (Vec | undefined)[] = []; +function keep(v: Vec | undefined): number { + kept.push(v); + return kept.length; +} +function same(a: Vec | undefined, b: Vec): boolean { + return a === b; +} +function orNull(v: Vec | undefined): Vec | undefined { + return v !== undefined && v.x < 0 ? undefined : v; +} + +class Cell { + value: number; + next: Cell | null; + constructor(value: number, next: Cell | null) { + this.value = value; + this.next = next; + } +} +function cellTotal(c: Cell | null): number { + let total = 0; + for (let at = c; at !== null; at = at.next) total += at.value; + return total; +} +function fail(v: Vec | undefined): number { + if (v !== undefined && v.x > 100) throw new Error("too far: " + v.x); + return v === undefined ? 0 : v.x; +} + +function projections(): void { + const a = vec(3, 4); + const b = sum(a, vec(1, 1)); + console.log("len", len(a), len(b), len(undefined)); + const c = sum(sum(a, b), a); + console.log("sum", c.x, c.y, sum(undefined, a).x); + console.log("depth", depth(a, 5), depth(undefined, 3)); + let total = 0; + for (let i = 0; i < 1000; i++) { + const p = vec(i, i + 1); + total += len(p) + len(sum(p, a)); + } + console.log("loop", total.toFixed(6)); +} + +function mutated(): void { + const p = vec(1, 2); + console.log("before", len(p)); + p.x = 6; + p.y = 8; + console.log("after", len(p)); +} + +function strings(): void { + const prefix = ["a", "b", "c"].join("-"); + const t: Tagged = { name: prefix + "!", at: vec(2, 5) }; + console.log("label", label(t), label(undefined)); + const u: Tagged = { name: label(t), at: t.at }; + console.log("nested", label(u)); +} + +function escapes(): void { + const a = vec(7, 8); + const count = keep(a); + const b = vec(9, 10); + console.log("kept", count, kept[0] === a, kept[0]!.x, keep(b), kept[1]!.y); + a.x = 70; + console.log("shared", kept[0]!.x); + const c = vec(1, 1); + console.log("same", same(c, c), same(c, vec(1, 1)), same(undefined, c)); + const d = vec(-1, 2); + const e = vec(4, 5); + console.log("returned", orNull(d), orNull(e) === e, orNull(e)!.y); +} + +function classes(): void { + const tail = new Cell(3, null); + const list = new Cell(1, new Cell(2, tail)); + console.log("cells", cellTotal(list), cellTotal(tail), cellTotal(null)); +} + +function throwing(): void { + const near = vec(5, 0); + const far = vec(500, 0); + try { + console.log("near", fail(near)); + console.log("far", fail(far)); + } catch (e) { + console.log("caught", (e as Error).message, near.x, far.x); + } +} + +projections(); +mutated(); +strings(); +escapes(); +classes(); +throwing(); diff --git a/tests/corpus/7663-pointer-union-stored-payload.ts b/tests/corpus/7663-pointer-union-stored-payload.ts new file mode 100644 index 000000000..92ac68bfb --- /dev/null +++ b/tests/corpus/7663-pointer-union-stored-payload.ts @@ -0,0 +1,83 @@ +// Records passed to `T | undefined` parameters whose callee keeps the +// narrowed record: pushed into a global array (also through a non-null +// assertion), kept through `??`, stored in a nullable class field or a record +// field, or passed on to a callee that keeps it. Each record must outlive its +// caller's frame. A callee that only reads the narrowed record lets it stay +// where it is. +interface Tagged { + id: number; + tags: string[]; +} +interface Slot { + held: Tagged | undefined; +} +const kept: Tagged[] = []; +const slot: Slot = { held: undefined }; +function pushNarrowed(t: Tagged | undefined): void { + if (t !== undefined) kept.push(t); +} +function pushAsserted(t: Tagged | undefined): void { + kept.push(t!); +} +function keepDefaulted(t: Tagged | undefined): void { + kept.push(t ?? { id: -1, tags: [] }); +} +function storeInSlot(t: Tagged | undefined): void { + if (t !== undefined) slot.held = t; +} +function keepRecord(t: Tagged): void { + kept.push(t); +} +function passOn(t: Tagged | undefined): void { + if (t !== undefined) keepRecord(t); +} +function readOnly(t: Tagged | undefined): number { + return t === undefined ? 0 : t.id + t.tags.length; +} +class Holder { + last: Tagged | undefined = undefined; + take(t: Tagged | undefined): void { + this.last = t; + } +} +const holder = new Holder(); +function fill(round: number): number { + let read = 0; + for (let i = 0; i < 3; i++) { + const a: Tagged = { id: round * 10 + i, tags: ["a", String(i)] }; + pushNarrowed(a); + const b: Tagged = { id: round * 10 + i + 100, tags: ["b"] }; + keepDefaulted(b); + const c: Tagged = { id: round * 10 + i + 200, tags: ["c"] }; + storeInSlot(c); + const d: Tagged = { id: round * 10 + i + 300, tags: ["d", "d"] }; + holder.take(d); + const e: Tagged = { id: round * 10 + i + 400, tags: [] }; + passOn(e); + const g: Tagged = { id: round * 10 + i + 600, tags: ["g", "g", "g"] }; + pushAsserted(g); + const f: Tagged = { id: round * 10 + i + 500, tags: ["f"] }; + read += readOnly(f); + } + return read; +} +// Reuses the stack the callers above ran on, and allocates collectable +// records, before the kept ones are read. +function churn(n: number): number { + let total = 0; + for (let i = 0; i < n; i++) { + const t: Tagged = { id: i, tags: [String(i), String(i + 1)] }; + total += t.tags.length + readOnly(t); + } + return total; +} +let reads = 0; +for (let round = 0; round < 4; round++) { + reads += fill(round); + reads += churn(200); +} +console.log("reads", reads); +console.log("kept", kept.length); +for (const t of kept) console.log(t.id, t.tags.join("+")); +console.log("slot", slot.held === undefined ? "none" : slot.held.id, slot.held?.tags.join("+")); +console.log("holder", holder.last === undefined ? "none" : holder.last.id, holder.last?.tags.join("+")); diff --git a/tests/corpus/7680-typed-array-search.ts b/tests/corpus/7680-typed-array-search.ts new file mode 100644 index 000000000..7672c3712 --- /dev/null +++ b/tests/corpus/7680-typed-array-search.ts @@ -0,0 +1,70 @@ +// TypedArray.prototype.indexOf, lastIndexOf and includes on every numeric +// element kind: strict equality (NaN never found) for indexOf/lastIndexOf, +// SameValueZero (NaN found, -0 equals 0) for includes, and fromIndex as +// ToIntegerOrInfinity, counted from the end when negative. +const bytes = new Uint8Array([1, 2, 10, 3, 10, 255]); +console.log("u8", bytes.indexOf(10), bytes.indexOf(10, 3), bytes.indexOf(10, 5), bytes.lastIndexOf(10)); +console.log("u8 from", bytes.indexOf(10, -2), bytes.indexOf(10, -100), bytes.indexOf(1, Infinity), bytes.indexOf(10, 2.9)); +console.log("u8 last", bytes.lastIndexOf(10, 3), bytes.lastIndexOf(10, -3), bytes.lastIndexOf(1, -100), bytes.lastIndexOf(255, 99)); +console.log("u8 values", bytes.indexOf(256 + 10), bytes.indexOf(-246), bytes.indexOf(10.5), bytes.includes(255), bytes.includes(0)); +console.log("u8 includes from", bytes.includes(1, 1), bytes.includes(255, -1), bytes.includes(2, -5)); + +const clamped = new Uint8ClampedArray([0, 300, -5, 127.5]); +console.log("u8c", clamped.indexOf(255), clamped.indexOf(0, 1), clamped.lastIndexOf(0), clamped.indexOf(128)); + +const signed = new Int8Array([-1, 127, -128, 0]); +console.log("i8", signed.indexOf(-1), signed.indexOf(255), signed.indexOf(-128), signed.lastIndexOf(0)); + +const shorts = new Int16Array([5, -300, 5, 7]); +const ushorts = new Uint16Array([65535, 1, 65535]); +console.log("16", shorts.indexOf(-300), shorts.lastIndexOf(5), ushorts.indexOf(-1), ushorts.lastIndexOf(65535, 1)); + +const ints = new Int32Array([-2147483648, 0, 2147483647, 0]); +const uints = new Uint32Array([4294967295, 3]); +console.log("32", ints.indexOf(2147483647), ints.lastIndexOf(0), uints.indexOf(4294967295), uints.includes(-1)); + +const floats = new Float64Array([1.5, NaN, -0, 0, Infinity]); +console.log("f64", floats.indexOf(NaN), floats.lastIndexOf(NaN), floats.includes(NaN), floats.indexOf(0), floats.indexOf(-0)); +console.log("f64 more", floats.lastIndexOf(0), floats.indexOf(Infinity), floats.includes(-Infinity), floats.includes(NaN, 2)); + +const singles = new Float32Array([1.1, 0.5, NaN]); +console.log("f32", singles.indexOf(1.1), singles.indexOf(Math.fround(1.1)), singles.indexOf(0.5), singles.includes(NaN), singles.indexOf(NaN)); + +const empty = new Float64Array(0); +console.log("empty", empty.indexOf(0), empty.lastIndexOf(0), empty.includes(0), empty.includes(0, -1)); + +// Views search their own window of the buffer. +const buffer = new ArrayBuffer(16); +const whole = new Uint8Array(buffer); +whole.set([9, 9, 1, 2, 9, 3, 9, 9]); +const view = new Uint8Array(buffer, 2, 4); +const sub = whole.subarray(3, 6); +console.log("views", view.indexOf(9), view.lastIndexOf(9), view.indexOf(1), sub.indexOf(9), sub.includes(1), sub.lastIndexOf(3)); +const words = new Uint16Array(buffer, 2, 3); +console.log("word view", words.indexOf(0x0201), words.length); + +// Shared memory searches the same way. +const shared = new Int32Array(new SharedArrayBuffer(16)); +shared[1] = 7; +shared[3] = 7; +console.log("shared", shared.indexOf(7), shared.lastIndexOf(7), shared.includes(0), shared.indexOf(7, 2)); + +// A loop that finds every occurrence, as a line splitter does. +const text = new TextEncoder().encode("one\ntwo\n\nthree\n"); +const lines: number[] = []; +for (let at = text.indexOf(10); at !== -1; at = text.indexOf(10, at + 1)) lines.push(at); +console.log("lines", lines.join(",")); + +// A Buffer passed as a Uint8Array keeps Buffer's search: a number needle +// is taken as a byte, and a NaN byteOffset searches the whole buffer. +function searchView(view: Uint8Array): string { + return [view.indexOf(97 + 256), view.lastIndexOf(98, NaN), view.includes(353), view.indexOf(98, -1)].join(","); +} +console.log(searchView(Buffer.from("abcab")), searchView(new Uint8Array([97, 98, 99, 97, 98]))); + +// An explicit undefined fromIndex converts to NaN, which searches from 0: +// lastIndexOf then looks at index 0 only. +const values = new Int16Array([3, 1, 3, 2, 3]); +const absent: number | undefined = process.argv.length > 99 ? 1 : undefined; +console.log("undefined from", values.indexOf(3, undefined), values.lastIndexOf(3, undefined), values.lastIndexOf(1, undefined), values.includes(2, undefined)); +console.log("maybe undefined from", values.indexOf(3, absent), values.lastIndexOf(3, absent), values.includes(1, absent)); diff --git a/tests/corpus/7681-typed-array-search-dynamic.mjs b/tests/corpus/7681-typed-array-search-dynamic.mjs new file mode 100644 index 000000000..643ba0cfa --- /dev/null +++ b/tests/corpus/7681-typed-array-search-dynamic.mjs @@ -0,0 +1,28 @@ +// TypedArray search on untyped values (parameters of untyped functions and +// object members): needles that are not numbers are never found (strict +// equality), fromIndex may be undefined, a string or a fraction, and +// Buffers keep their own indexOf, which wraps number needles and takes +// strings. +function views(bytes, floats, shorts) { + return { bytes, floats, shorts }; +} +const { bytes, floats, shorts } = views( + new Uint8Array([1, 2, 10, 3, 10]), + new Float64Array([0.5, NaN, -0]), + new Int16Array([-1, 300, -1]), +); +console.log(bytes.indexOf(10), bytes.indexOf(10, 3), bytes.lastIndexOf(10), bytes.includes(3), bytes.includes(4)); +console.log(bytes.lastIndexOf(1, undefined), bytes.lastIndexOf(10), bytes.indexOf(10, undefined), bytes.includes(1, undefined)); +console.log(bytes.indexOf(10, '3'), bytes.indexOf(10, 2.9), bytes.indexOf(10, -1), bytes.lastIndexOf(10, -2), bytes.indexOf(10, null)); +console.log(bytes.indexOf('10'), bytes.includes('1'), bytes.indexOf(10n), bytes.indexOf(), bytes.includes(), bytes.indexOf(266)); +console.log(floats.indexOf(NaN), floats.includes(NaN), floats.indexOf(0), floats.lastIndexOf(-0), floats.includes(0.5, 1)); +console.log(shorts.indexOf(-1), shorts.lastIndexOf(-1), shorts.indexOf(300, -2), shorts.includes(65535)); +const holder = { view: bytes.subarray(2) }; +console.log(holder.view.indexOf(10), holder.view.lastIndexOf(10), holder.view.includes(1)); +const buffer = Buffer.from([1, 2, 10, 3, 10]); +const boxed = [buffer]; +console.log(boxed[0].indexOf(10), boxed[0].indexOf(266), boxed[0].indexOf('\n'), boxed[0].includes(Buffer.from([3, 10]))); +console.log(boxed[0].subarray(3).indexOf(10), Buffer.isBuffer(boxed[0].subarray(3)), boxed[0].lastIndexOf(10, -2)); +let count = 0; +for (let at = bytes.indexOf(10); at !== -1; at = bytes.indexOf(10, at + 1)) count++; +console.log('count', count); diff --git a/tests/corpus/7682-constructor-function-globals.js b/tests/corpus/7682-constructor-function-globals.js new file mode 100644 index 000000000..58292e4ac --- /dev/null +++ b/tests/corpus/7682-constructor-function-globals.js @@ -0,0 +1,55 @@ +// Module bindings that hold `new F()` of an ordinary constructor function +// (const, let and var), read and reassigned by functions declared elsewhere +// in the module, such as the head and tail of a linked list. +'use strict'; +function Segment(prev) { + this.prev = prev; + this.next = null; + this.data = new Array(4); + this.pos = 0; +} +const head = new Segment(); +let current = head; +var spare = new Segment(head); + +function append(value) { + if (current.pos === current.data.length) { + current = current.next || (current.next = new Segment(current)); + } + current.data[current.pos++] = value; +} + +function collect() { + const out = []; + for (let node = head; node; node = node.next) out.push(node.data.slice(0, node.pos).join('')); + return out.join('|'); +} + +function reset() { + current = head; + for (let node = head; node; node = node.next) node.pos = 0; +} + +for (const ch of 'abcdefghij') append(ch); +console.log(collect(), current === head, current.prev === head.next ? 'two' : 'more'); +reset(); +append('z'); +console.log(collect(), current === head, head.next !== null); +console.log(spare.prev === head, spare instanceof Segment, typeof spare.data); +spare = new Segment(spare); +console.log(spare.prev.prev === head); + +function Counter(start) { + this.value = start; +} +Counter.prototype.bump = function () { + return ++this.value; +}; +let counter = new Counter(10); +function bumpTwice() { + counter.bump(); + return counter.bump(); +} +console.log(bumpTwice(), counter.value); +counter = new Counter(0); +console.log(bumpTwice()); diff --git a/tests/corpus/7713-buffer-numeric-untyped-arguments.cjs b/tests/corpus/7713-buffer-numeric-untyped-arguments.cjs new file mode 100644 index 000000000..81c9d767a --- /dev/null +++ b/tests/corpus/7713-buffer-numeric-untyped-arguments.cjs @@ -0,0 +1,22 @@ +// Buffer's numeric read/write methods with arguments that are not numbers +// (untyped JavaScript): Node validates them at runtime. +const buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); +const attempts = [ + () => buf.readUInt8("0"), + () => buf.readUInt16LE("1"), + () => buf.readInt32BE(true), + () => buf.readDoubleLE(null), + () => buf.writeUInt8(1, "0"), + () => buf.writeUInt16BE("258", 0), + () => buf.writeInt8("x", 0), + () => buf.readUInt8(undefined), + () => buf.readUInt8(1), +]; +for (const attempt of attempts) { + try { + console.log("ok", attempt()); + } catch (error) { + console.log(error.name, error.code, error.message); + } +} +console.log(Array.from(buf).join(",")); diff --git a/tests/corpus/7714-zlib-brotli-compress.cjs b/tests/corpus/7714-zlib-brotli-compress.cjs new file mode 100644 index 000000000..9f12f35f8 --- /dev/null +++ b/tests/corpus/7714-zlib-brotli-compress.cjs @@ -0,0 +1,91 @@ +// zlib's Brotli compression over the vendored brotli 1.2.0, the version +// Node bundles: compressed bytes match Node's for the same parameters, and +// the options, errors, callback, promisified and stream forms follow Node. +const zlib = require("node:zlib"); +const { promisify } = require("node:util"); + +const text = Buffer.from("hello hello hello brotli ".repeat(20)); +const hex = (b) => b.toString("hex"); +const tryIt = (label, run) => { + try { + const r = run(); + console.log(label, "ok", Buffer.isBuffer(r) ? hex(r) : r); + } catch (e) { + console.log(label, e.name, e.code, e.errno, e.message); + } +}; + +console.log(hex(zlib.brotliCompressSync("")), hex(zlib.brotliCompressSync("a"))); +for (const quality of [0, 1, 4, 5, 9, 11]) { + const out = zlib.brotliCompressSync(text, { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: quality } }); + console.log("quality", quality, out.length, hex(out)); +} +console.log("text mode", hex(zlib.brotliCompressSync(text, { params: { [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT } }))); +console.log("font mode", hex(zlib.brotliCompressSync(text, { params: { [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_FONT } }))); +console.log( + "window", + hex(zlib.brotliCompressSync(text, { + params: { [zlib.constants.BROTLI_PARAM_LGWIN]: 10, [zlib.constants.BROTLI_PARAM_SIZE_HINT]: text.length }, + })), +); +console.log("dictionary", hex(zlib.brotliCompressSync("hello world", { dictionary: Buffer.from("hello world") }))); +console.log("arraybuffer dictionary", hex(zlib.brotliCompressSync("hello world", { dictionary: new Uint8Array([104, 101, 108, 108, 111]).buffer }))); +console.log("finishFlush", hex(zlib.brotliCompressSync("hello", { finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH }))); +tryIt("boolean param", () => zlib.brotliCompressSync("x", { params: { [zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: true } })); +tryIt("fractional quality", () => zlib.brotliCompressSync("hello hello hello", { params: { 1: 4.7 } })); +tryIt("negative quality", () => zlib.brotliCompressSync("hello hello hello", { params: { 1: -1 } })); +tryIt("quality above 11", () => zlib.brotliCompressSync("hello hello hello", { params: { 1: 12 } })); +tryIt("unknown param", () => zlib.brotliCompressSync("x", { params: { 9999: 1 } })); +tryIt("param 9", () => zlib.brotliCompressSync("x", { params: { 9: 0 } })); +tryIt("repeated param", () => zlib.brotliCompressSync("x", { params: { 1: 5, "01": 4 } })); +tryIt("fractional param key", () => zlib.brotliCompressSync("x", { params: { "1.5": 5 } })); +tryIt("negative param key", () => zlib.brotliCompressSync("x", { params: { "-1": 5 } })); +tryIt("string param value", () => zlib.brotliCompressSync("x", { params: { 1: "5" } })); +tryIt("string dictionary", () => zlib.brotliCompressSync("x", { dictionary: "abc" })); +tryIt("chunkSize", () => zlib.brotliCompressSync(text, { chunkSize: 10 })); +tryIt("flush", () => zlib.brotliCompressSync(text, { flush: 4 })); +tryIt("finishFlush range", () => zlib.brotliCompressSync(text, { finishFlush: 99 })); +tryIt("rejectGarbageAfterEnd", () => zlib.brotliCompressSync(text, { rejectGarbageAfterEnd: 1 })); +tryIt("maxOutputLength", () => zlib.brotliCompressSync(text, { maxOutputLength: 8 })); +tryIt("number input", () => zlib.brotliCompressSync(JSON.parse("5"))); + +zlib.brotliCompress(text, (err, out) => { + console.log("callback", err, hex(out)); + zlib.brotliCompress("abc", { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 2 } }, (err2, out2) => { + console.log("callback options", err2, hex(out2)); + promised(); + }); +}); + +function promised() { + const compress = promisify(zlib.brotliCompress); + compress("promised").then((out) => { + console.log("promisified", hex(out)); + streams(); + }); +} + +function streams() { + const chunks = []; + const stream = zlib.createBrotliCompress({ params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 5 } }); + console.log(stream instanceof zlib.BrotliCompress, stream instanceof zlib.Gzip); + stream.on("data", (chunk) => chunks.push(chunk)); + stream.on("end", () => { + console.log("stream", hex(Buffer.concat(chunks)), stream.bytesWritten); + const second = new zlib.BrotliCompress(); + const parts = []; + second.on("data", (chunk) => parts.push(chunk)); + second.on("end", () => { + console.log("class", hex(Buffer.concat(parts))); + try { zlib.createBrotliCompress({ params: { 99: 1 } }); } catch (e) { console.log(e.code, e.message); } + try { zlib.createBrotliCompress({ flush: 9 }); } catch (e) { console.log(e.code, e.message); } + }); + second.reset(); + second.end("reset first"); + }); + stream.write(text.subarray(0, 100)); + stream.flush(() => { + console.log("flushed", chunks.length > 0); + stream.end(text.subarray(100)); + }); +} diff --git a/tests/corpus/7715-zlib-brotli-decompress.cjs b/tests/corpus/7715-zlib-brotli-decompress.cjs new file mode 100644 index 000000000..c2f826f75 --- /dev/null +++ b/tests/corpus/7715-zlib-brotli-decompress.cjs @@ -0,0 +1,96 @@ +// zlib's Brotli decompression: fixed blobs Node produced, Brotli's own +// error codes (ERR_ + the decoder's error name) and zlib's for truncated +// input, trailing data, maxOutputLength, dictionaries, the decoder +// parameters, and the callback, promisified and stream forms. +const zlib = require("node:zlib"); +const { promisify } = require("node:util"); +const { pipeline, Readable, Writable } = require("node:stream"); + +const hello = Buffer.from("1b1600f88d946ede44558696206c6f350b32b54006543b", "hex"); +const repeated = Buffer.from("1b8713f825f0e2b14020680100", "hex"); +const withDictionary = Buffer.from("1b0a00f8250002980820", "hex"); +const tryIt = (label, run) => { + try { + const r = run(); + console.log(label, "ok", Buffer.isBuffer(r) ? r.length + " " + r.toString().slice(0, 24) : r); + } catch (e) { + console.log(label, e.name, e.code, e.errno, e.message); + } +}; + +tryIt("hello", () => zlib.brotliDecompressSync(hello)); +tryIt("repeated", () => zlib.brotliDecompressSync(repeated)); +tryIt("string input", () => zlib.brotliDecompressSync("abc")); +tryIt("arraybuffer input", () => zlib.brotliDecompressSync(hello.buffer.slice(hello.byteOffset, hello.byteOffset + hello.length))); +for (const hex of ["ff", "ffffffff", "ce", "0b008061", "3b00", "06", "2f", "1bffff00ffffffffffff"]) { + tryIt(hex, () => zlib.brotliDecompressSync(Buffer.from(hex, "hex"))); +} +tryIt("truncated", () => zlib.brotliDecompressSync(hello.subarray(0, 10))); +tryIt("trailing data", () => zlib.brotliDecompressSync(Buffer.concat([hello, Buffer.from("junk")]))); +tryIt("trailing data rejected", () => zlib.brotliDecompressSync(Buffer.concat([hello, Buffer.from("junk")]), { rejectGarbageAfterEnd: true })); +tryIt("maxOutputLength", () => zlib.brotliDecompressSync(repeated, { maxOutputLength: 100 })); +tryIt("small chunks", () => zlib.brotliDecompressSync(repeated, { chunkSize: 64 })); +tryIt("partial with flush", () => zlib.brotliDecompressSync(hello.subarray(0, 8), { finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH })); +tryIt("dictionary", () => zlib.brotliDecompressSync(withDictionary, { dictionary: Buffer.from("hello world") })); +tryIt("missing dictionary", () => zlib.brotliDecompressSync(withDictionary)); +tryIt("large window", () => zlib.brotliDecompressSync(hello, { params: { [zlib.constants.BROTLI_DECODER_PARAM_LARGE_WINDOW]: 1 } })); +tryIt("encoder param", () => zlib.brotliDecompressSync(hello, { params: { [zlib.constants.BROTLI_PARAM_LGWIN]: 10 } })); +tryIt("round trip", () => zlib.brotliDecompressSync(zlib.brotliCompressSync("round trip ".repeat(100))).toString() === "round trip ".repeat(100)); + +zlib.brotliDecompress(hello, (err, out) => { + console.log("callback", err, out.toString()); + zlib.brotliDecompress(Buffer.from([1, 2, 3]), (err2) => { + console.log("callback error", err2.code, err2.errno, err2.message); + promised(); + }); +}); + +function promised() { + const decompress = promisify(zlib.brotliDecompress); + decompress(repeated, { maxOutputLength: 10 }).catch((err) => { + console.log("promisified", err.code, err.message); + streams(); + }); +} + +function streams() { + const out = []; + const stream = zlib.createBrotliDecompress(); + console.log(stream instanceof zlib.BrotliDecompress, stream instanceof zlib.BrotliCompress); + stream.on("data", (chunk) => out.push(chunk)); + stream.on("end", () => { + console.log("stream", Buffer.concat(out).toString(), stream.bytesWritten); + const bad = new zlib.BrotliDecompress(); + bad.on("error", (err) => { + console.log("stream error", err.code, err.errno, err.message); + piped(); + }); + bad.write(Buffer.from("ffff", "hex")); + bad.end(); + }); + stream.write(hello.subarray(0, 5)); + stream.end(hello.subarray(5)); +} + +function piped() { + const sink = []; + pipeline( + Readable.from([Buffer.from("one "), Buffer.from("two "), Buffer.from("three")]), + zlib.createBrotliCompress(), + zlib.createBrotliDecompress(), + new Writable({ + write(chunk, _encoding, callback) { + sink.push(chunk); + callback(); + }, + }), + (err) => { + console.log("pipeline", err, Buffer.concat(sink).toString()); + try { + new zlib.BrotliDecompress({ params: { 3: 1 } }); + } catch (e) { + console.log(e.code, e.message); + } + }, + ); +} diff --git a/tests/corpus/7716-zlib-brotli.ts b/tests/corpus/7716-zlib-brotli.ts new file mode 100644 index 000000000..812cf6fae --- /dev/null +++ b/tests/corpus/7716-zlib-brotli.ts @@ -0,0 +1,47 @@ +// zlib's Brotli forms through their TypeScript types: typed options, +// dictionaries, maxOutputLength, the callback and promisified forms, and the +// BrotliCompress and BrotliDecompress classes. +import * as zlib from "node:zlib"; +import { promisify } from "node:util"; + +const input: string = "typed brotli ".repeat(64); +const compressed: Buffer = zlib.brotliCompressSync(input, { + params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 4, [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT }, +}); +console.log(compressed.toString("base64")); +console.log(zlib.brotliDecompressSync(compressed).toString() === input); +const dict: Buffer = Buffer.from("typed brotli"); +const withDict = zlib.brotliCompressSync(input, { dictionary: dict }); +console.log(withDict.length, zlib.brotliDecompressSync(withDict, { dictionary: dict }).length); +try { + zlib.brotliDecompressSync(compressed, { maxOutputLength: 16 }); +} catch (e) { + const err = e as NodeJS.ErrnoException; + console.log(err.code, err.message); +} +zlib.brotliCompress(Buffer.from("abc"), (err, out) => { + console.log(err, out.toString("hex")); + zlib.brotliDecompress(out, { finishFlush: zlib.constants.BROTLI_OPERATION_FINISH }, (err2, back) => { + console.log(err2, back.toString()); + main(); + }); +}); +const compressAsync = promisify(zlib.brotliCompress); +const decompressAsync = promisify(zlib.brotliDecompress); +async function main(): Promise { + const packed = await compressAsync("promised"); + console.log((await decompressAsync(packed)).toString()); + try { + await decompressAsync(Buffer.from([0xff])); + } catch (e) { + console.log((e as NodeJS.ErrnoException).code); + } + const stream: zlib.BrotliCompress = new zlib.BrotliCompress(); + const parts: Buffer[] = []; + stream.on("data", (c: Buffer) => parts.push(c)); + stream.on("end", () => console.log("stream", Buffer.concat(parts).equals(zlib.brotliCompressSync("streamed")), stream.bytesWritten)); + stream.end("streamed"); + const dec = zlib.createBrotliDecompress(); + dec.reset(); + console.log(dec instanceof zlib.BrotliDecompress); +} diff --git a/tests/corpus/7736-key-order-replaceable-method-result/main.ts b/tests/corpus/7736-key-order-replaceable-method-result/main.ts new file mode 100644 index 000000000..5ebbfa761 --- /dev/null +++ b/tests/corpus/7736-key-order-replaceable-method-result/main.ts @@ -0,0 +1,106 @@ +import { Registry, describe, titleIndex } from "./registry.js"; + +// Records keep the key order their object literals write when values of +// the same type also pass through checked conversions that build none of +// them: +// - a typed method the program may replace on its instance (the +// computed-key write in registry.js makes every method name replaceable) +// returns the records it stores, checked back from the guarded call; +// - an index spec from JavaScript, checked into a type whose function +// field takes a Book, receives Books but never builds one. +// JSON, Object.keys and spreads of those records follow insertion order, +// not the interface's declared order. +interface Book { + id: string; + title: string; + author: string; + createdAt: string; + updatedAt: string; +} + +type Draft = { title: string; author: string }; + +interface IndexSpec { + name: string; + key: (row: T) => string; +} + +class Repository { + readonly #rows = new Map(); + + insert(row: T): T { + this.#rows.set(row.id, row); + return row; + } + + require(id: string): T { + const row = this.#rows.get(id); + if (row === undefined) throw new Error(`missing ${id}`); + return row; + } + + find(id: string): T | undefined { + return this.#rows.get(id); + } + + all(): T[] { + return [...this.#rows.values()]; + } +} + +class Service { + readonly #books = new Repository(); + readonly #index: IndexSpec = titleIndex; + #clock = 0; + + #now(): string { + this.#clock++; + return `t${this.#clock}`; + } + + create(draft: Draft, id: string): Book { + const at = this.#now(); + return this.#books.insert({ ...draft, id, createdAt: at, updatedAt: at }); + } + + update(id: string, patch: Partial): Book { + const current = this.#books.require(id); + return this.#books.insert({ ...current, ...patch, updatedAt: this.#now() }); + } + + get(id: string): Book { + return this.#books.require(id); + } + + maybe(id: string): Book | undefined { + return this.#books.find(id); + } + + list(): Book[] { + return this.#books.all(); + } + + indexed(): string[] { + return this.list().map((book) => `${this.#index.name}=${this.#index.key(book)}`); + } +} + +const registry = new Registry(); +console.log(describe(registry)); + +const service = new Service(); +console.log(JSON.stringify(service.create({ title: "Dune", author: "Herbert" }, "b1"))); +console.log(JSON.stringify(service.create({ title: "Earthsea", author: "Le Guin" }, "b2"))); +console.log(JSON.stringify(service.update("b1", { title: "Dune Messiah" }))); +console.log(Object.keys(service.get("b2")).join(",")); +const found = service.maybe("b1"); +if (found !== undefined) console.log(JSON.stringify(found)); +console.log(JSON.stringify(service.list())); +const copy = { ...service.get("b1") }; +console.log(Object.keys(copy).join(",")); +console.log(service.indexed().join(" ")); +try { + service.get("missing"); +} catch (error) { + console.log((error as Error).message); +} diff --git a/tests/corpus/7736-key-order-replaceable-method-result/registry.js b/tests/corpus/7736-key-order-replaceable-method-result/registry.js new file mode 100644 index 000000000..d6da1edc5 --- /dev/null +++ b/tests/corpus/7736-key-order-replaceable-method-result/registry.js @@ -0,0 +1,21 @@ +// Installs one handler per method name with a computed-key write, the way +// router classes attach `get`, `post`, ... in their constructor. The +// compiler cannot tell which names such a write replaces. +const METHODS = ["get", "post"]; + +export class Registry { + constructor() { + METHODS.forEach((method) => { + this[method] = (...args) => `${method}:${args.length}`; + }); + } +} + +export function describe(registry) { + return `${registry.get(1, 2)} ${registry.post()}`; +} + +export const titleIndex = { + name: "title", + key: (row) => row.title.toLowerCase(), +}; diff --git a/tests/corpus/7737-key-order-self-spread.ts b/tests/corpus/7737-key-order-self-spread.ts new file mode 100644 index 000000000..5577f7531 --- /dev/null +++ b/tests/corpus/7737-key-order-self-spread.ts @@ -0,0 +1,34 @@ +// A literal that starts by spreading a value of its own type copies that +// value's keys in their order, then overwrites some of them. It must not +// stop the type from taking the order its other literals write: the +// records below enumerate as created (title before id), not in the +// interface's declared order. +interface Book { + id: string; + title: string; + tags: string[]; + updatedAt: string; + version: number; +} + +type Draft = { title: string; tags: string[] }; + +function create(draft: Draft, id: string): Book { + return { ...draft, id, updatedAt: "t1", version: 1 }; +} + +function update(current: Book, patch: Partial): Book { + return { ...current, ...patch, updatedAt: "t2", version: current.version + 1 }; +} + +function touch(current: Book): Book { + return { ...current, version: current.version + 1 }; +} + +const book = create({ title: "Dune", tags: ["sf"] }, "b1"); +console.log(JSON.stringify(book)); +const updated = update(book, { title: "Dune Messiah" }); +console.log(JSON.stringify(updated)); +console.log(Object.keys(touch(updated)).join(",")); +console.log(Object.keys(update(updated, { tags: [] })).join(" ")); +for (const key in touch(book)) console.log(key); diff --git a/tests/corpus/7742-response-clone-tee.ts b/tests/corpus/7742-response-clone-tee.ts new file mode 100644 index 000000000..56c5e74e6 --- /dev/null +++ b/tests/corpus/7742-response-clone-tee.ts @@ -0,0 +1,212 @@ +// Response.clone and Request.clone on checked-dynamic handles: the body +// tees, so the original and the clone each read every chunk; a clone of a +// used or locked body throws; canceling both branches cancels the source +// with both reasons; the clone's headers are a copy. +const enc = new TextEncoder(); +const dec = new TextDecoder(); + +function describe(e: unknown): string { + const err = e as { name?: string; message?: string }; + return `${String(err.name)}: ${String(err.message)}`; +} + +async function readAll(stream: any): Promise { + const reader = stream.getReader(); + let out = ""; + for (;;) { + const { done, value } = await reader.read(); + if (done) return out; + out += dec.decode(value); + } +} + +async function main(): Promise { + const r: any = new Response("hello world", { status: 201, headers: { "x-a": "1" } }); + const c: any = r.clone(); + console.log(c.status, c.headers.get("x-a"), c.bodyUsed, r.bodyUsed, c.body === r.body); + console.log(await c.text(), "|", await r.text()); + console.log("used", c.bodyUsed, r.bodyUsed); + try { + r.clone(); + } catch (e) { + console.log("clone used:", describe(e), e instanceof TypeError); + } + + const locked: any = new Response("abc"); + locked.body.getReader(); + try { + locked.clone(); + } catch (e) { + console.log("clone locked:", describe(e)); + } + + const empty: any = new Response(null, { status: 204, statusText: "Nothing" }); + const emptyClone: any = empty.clone(); + console.log("null body", emptyClone.body, emptyClone.status, emptyClone.statusText, JSON.stringify(await emptyClone.text())); + + // Headers are copied: changing the clone's leaves the original's alone. + const h: any = new Response("x", { headers: { "x-b": "2" } }); + const hc: any = h.clone(); + hc.headers.set("x-b", "3"); + console.log("headers", h.headers.get("x-b"), hc.headers.get("x-b"), h.headers === hc.headers); + + // A pulled source: both branches see each chunk (the same object). + let n = 0; + const source: any = new ReadableStream({ + start(): void { + console.log("start"); + }, + pull(controller: any): void { + n++; + console.log("pull", n); + if (n <= 3) controller.enqueue(enc.encode(`chunk${n};`)); + else controller.close(); + }, + }); + const streamed: any = new Response(source); + const streamedClone: any = streamed.clone(); + console.log("cloned"); + const r1 = streamed.body.getReader(); + const r2 = streamedClone.body.getReader(); + const a = await r1.read(); + const b = await r2.read(); + console.log("same chunk", a.value === b.value, dec.decode(a.value)); + console.log("rest 1", await readAllFrom(r1)); + console.log("rest 2", await readAllFrom(r2)); + + // One branch read to the end first; the other buffers. + const big: any = new Response(new ReadableStream({ + start(controller: any): void { + controller.enqueue(enc.encode("a")); + controller.enqueue(enc.encode("b")); + controller.close(); + }, + })); + const bigClone: any = big.clone(); + console.log("clone first", await bigClone.text()); + console.log("then original", await readAll(big.body)); + + // Canceling both branches cancels the source with both reasons. + const cancelSource: any = new ReadableStream({ + pull(controller: any): void { + controller.enqueue(enc.encode("z")); + }, + cancel(reason: unknown): void { + console.log("source cancel", JSON.stringify(reason)); + }, + }); + const cr: any = new Response(cancelSource); + const crc: any = cr.clone(); + const p1 = cr.body.cancel("first"); + console.log("one canceled"); + const p2 = crc.body.cancel("second"); + await p1; + await p2; + console.log("both canceled", cr.bodyUsed, crc.bodyUsed); + + // An erroring source errors both branches. + const failing: any = new ReadableStream({ + pull(controller: any): void { + controller.error(new Error("source failed")); + }, + }); + const fr: any = new Response(failing); + const frc: any = fr.clone(); + for (const branch of [fr, frc]) { + try { + await branch.text(); + } catch (e) { + console.log("branch error:", describe(e)); + } + } + + // Request.clone: the body tees and the signal follows the original's. + const q: any = new Request("http://example.test/path", { method: "POST", body: "data" }); + const qc: any = q.clone(); + console.log("request", qc.method, qc.url, await qc.text(), await q.text(), qc.signal === q.signal); + try { + q.clone(); + } catch (e) { + console.log("request clone used:", describe(e)); + } + const ac = new AbortController(); + const q2: any = new Request("http://example.test/", { signal: ac.signal }); + const q2c: any = q2.clone(); + console.log("before abort", q2c.signal.aborted); + ac.abort("why"); + console.log("after abort", q2c.signal.aborted, q2c.signal.reason); + const q3: any = new Request("http://example.test/", { signal: AbortSignal.abort("early") }); + console.log("aborted clone", q3.clone().signal.aborted, q3.clone().signal.reason); + + // clone is a method value like the body readers. + console.log(typeof r.clone, "clone" in r, r.clone === c.clone); +} + +async function readAllFrom(reader: any): Promise { + let out = ""; + for (;;) { + const { done, value } = await reader.read(); + if (done) return out; + out += dec.decode(value); + } +} + + +// Tee branches error when their source errors: after both buffered a +// chunk and with no source read pending, reads reject once the error's +// reaction ran (a read issued at once still takes the buffered chunk, as in +// Node); a clone of an errored body errors both branches. +async function readFirst(label: string, r: any): Promise { + try { + const chunk = await r.body.getReader().read(); + console.log(label, "read", JSON.stringify(chunk.value ? [...chunk.value] : null), chunk.done); + } catch (error) { + console.log(label, "rejects", (error as Error).message); + } +} + +function erroring(): { res: any; error: (reason: Error) => void } { + let ctrl: ReadableStreamDefaultController | undefined; + const source = new ReadableStream({ + start(c) { + ctrl = c; + c.enqueue(new Uint8Array([1, 2])); + }, + }); + return { res: new Response(source), error: (reason) => ctrl!.error(reason) }; +} + +const tick = (): Promise => new Promise((resolve) => setTimeout(resolve, 10)); + +async function sourceErrors(): Promise { + // Error, then read after the reaction: both branches reject. + { + const { res, error } = erroring(); + const clone = res.clone(); + await tick(); + error(new Error("late")); + await tick(); + await readFirst("later original", res); + await readFirst("later clone", clone); + } + // Error, then read at once: the first read takes the buffered chunk. + { + const { res, error } = erroring(); + const clone = res.clone(); + await tick(); + error(new Error("soon")); + await readFirst("at once original", res); + await readFirst("at once clone", clone); + } + // A body that errored before the clone. + { + const { res, error } = erroring(); + error(new Error("before")); + const clone = res.clone(); + await readFirst("errored original", res); + await readFirst("errored clone", clone); + } +} + +await main(); +await sourceErrors(); diff --git a/tests/corpus/7743-transform-stream-identity.ts b/tests/corpus/7743-transform-stream-identity.ts new file mode 100644 index 000000000..3664c8ff1 --- /dev/null +++ b/tests/corpus/7743-transform-stream-identity.ts @@ -0,0 +1,239 @@ +// The identity TransformStream: writes wait for reads (the readable side's +// high-water mark is 0), the writer's desiredSize/ready track the writable +// side's queue, close/abort/cancel propagate across the pair (in either +// order when they race), and the readable side feeds a Response body (the +// StreamingApi pattern of Hono's stream helpers). +const enc = new TextEncoder(); +const dec = new TextDecoder(); + +function describe(e: unknown): string { + if (e instanceof Error) { + const code = (e as NodeJS.ErrnoException).code; + return `${e.name}: ${e.message}${code === undefined ? "" : ` [${code}]`}`; + } + return `value ${String(e)}`; +} + +async function basics(): Promise { + const ts = new TransformStream(); + const writer = ts.writable.getWriter(); + const reader = ts.readable.getReader(); + console.log("desired", writer.desiredSize, ts.writable.locked, ts.readable.locked); + const written = writer.write("a").then(() => console.log("write a done")); + console.log("desired after write", writer.desiredSize); + void writer.ready.then(() => console.log("ready")); + const first = await reader.read(); + console.log("read", first.done, first.value); + await written; + console.log("desired", writer.desiredSize); + const closing = writer.close().then(() => console.log("closed")); + const last = await reader.read(); + console.log("read", last.done, last.value); + await closing; + await writer.closed; + console.log("writer.closed resolved"); + try { + await writer.write("b"); + } catch (e) { + console.log("write after close:", describe(e)); + } + try { + ts.writable.getWriter(); + } catch (e) { + console.log("getWriter locked:", describe(e)); + } + writer.releaseLock(); + try { + await writer.write("c"); + } catch (e) { + console.log("released write:", describe(e)); + } + try { + await writer.closed; + } catch (e) { + console.log("released closed:", describe(e)); + } +} + +async function abortWriter(): Promise { + const ts = new TransformStream(); + const writer = ts.writable.getWriter(); + const reader = ts.readable.getReader(); + const pending = writer.write("z").catch((e: unknown) => console.log("pending write:", describe(e))); + await writer.abort("boom").then(() => console.log("abort resolved")); + await pending; + try { + await reader.read(); + } catch (e) { + console.log("read after abort:", describe(e)); + } + try { + await writer.closed; + } catch (e) { + console.log("closed after abort:", describe(e)); + } + try { + await writer.ready; + } catch (e) { + console.log("ready after abort:", describe(e)); + } + console.log("desired after abort", writer.desiredSize); +} + +async function cancelReadable(): Promise { + const ts = new TransformStream(); + const writer = ts.writable.getWriter(); + const pending = writer.write("q").catch((e: unknown) => console.log("write rejected by cancel:", describe(e))); + await ts.readable.cancel("nope").then(() => console.log("cancel resolved")); + await pending; + try { + await writer.closed; + } catch (e) { + console.log("writer.closed after cancel:", describe(e)); + } +} + +async function intoResponse(): Promise { + const ts = new TransformStream(); + const writer = ts.writable.getWriter(); + void (async (): Promise => { + for (const s of ["x", "y", "z"]) { + await writer.write(enc.encode(s)); + console.log("wrote", s); + } + await writer.close(); + console.log("writer closed"); + })(); + console.log("text", await new Response(ts.readable).text()); +} + +/** Hono's StreamingApi: a writer on the writable side and a pull-based + * ReadableStream over a reader of the readable side. */ +interface StreamingApi { + readonly responseReadable: ReadableStream; + write(input: string): Promise; + close(): Promise; + state(): string; +} + +function streamingApi(writable: WritableStream, readable: ReadableStream): StreamingApi { + const writer = writable.getWriter(); + const reader = readable.getReader(); + let closed = false; + let aborted = false; + const responseReadable = new ReadableStream({ + async pull(controller): Promise { + const { done, value } = await reader.read(); + if (done) controller.close(); + else controller.enqueue(value); + }, + cancel: (): void => { + if (!closed) aborted = true; + }, + }); + return { + responseReadable, + async write(input: string): Promise { + try { + await writer.write(enc.encode(input)); + } catch { + // a closed or aborted stream drops the write + } + }, + async close(): Promise { + closed = true; + try { + await writer.close(); + } catch { + // already closed + } + }, + state: (): string => `closed=${String(closed)} aborted=${String(aborted)}`, + }; +} + +async function streamedResponse(): Promise { + const { readable, writable } = new TransformStream(); + const api = streamingApi(writable, readable); + void (async (): Promise => { + try { + for (let i = 0; i < 3; i++) { + await api.write(`data: ${i}\n\n`); + } + } finally { + await api.close(); + } + })(); + const response = new Response(api.responseReadable, { headers: { "content-type": "text/event-stream" } }); + const reader = response.body!.getReader(); + let chunks = 0; + let text = ""; + for (;;) { + const { done, value } = await reader.read(); + if (done) break; + chunks++; + text += dec.decode(value); + } + console.log("streamed", chunks, JSON.stringify(text), api.state()); +} + +async function writableMethods(): Promise { + const a = new TransformStream(); + await a.writable.abort("r").then(() => console.log("writable.abort ok")); + const b = new TransformStream(); + const writer = b.writable.getWriter(); + try { + await b.writable.close(); + } catch (e) { + console.log("close locked:", describe(e)); + } + try { + await b.writable.abort(); + } catch (e) { + console.log("abort locked:", describe(e)); + } + writer.releaseLock(); + const reader = b.readable.getReader(); + const closing = b.writable.close().then(() => console.log("writable.close ok")); + const end = await reader.read(); + console.log("readable done", end.done); + await closing; +} + +// writable.abort() and readable.cancel() share the transform's finish +// promise: whichever runs second answers the first one's result. +function settled(label: string, p: Promise): Promise { + return p.then( + () => console.log(label, "fulfilled"), + (e: unknown) => console.log(label, "rejected", describe(e)), + ); +} + +async function abortCancelRaces(): Promise { + const a = new TransformStream(); + const aWriter = a.writable.getWriter(); + await new Promise((r) => setTimeout(r, 0)); + const aborted = settled("abort then cancel: abort", aWriter.abort("abort first")); + const cancelled = settled("abort then cancel: cancel", a.readable.cancel("cancel second")); + await aborted; + await cancelled; + const b = new TransformStream(); + const bWriter = b.writable.getWriter(); + await new Promise((r) => setTimeout(r, 0)); + const cancelledFirst = settled("cancel then abort: cancel", b.readable.cancel("cancel first")); + const abortedSecond = settled("cancel then abort: abort", bWriter.abort("abort second")); + await cancelledFirst; + await abortedSecond; +} + +async function main(): Promise { + await basics(); + await abortWriter(); + await cancelReadable(); + await intoResponse(); + await streamedResponse(); + await writableMethods(); + await abortCancelRaces(); +} + +await main(); diff --git a/tests/corpus/7745-typed-array-unions.ts b/tests/corpus/7745-typed-array-unions.ts new file mode 100644 index 000000000..a7fc6f2cb --- /dev/null +++ b/tests/corpus/7745-typed-array-unions.ts @@ -0,0 +1,93 @@ +// Unions of typed-array classes: every element operation runs on each arm. + +type UintArray = Uint8Array | Uint16Array | Uint32Array; +type Samples = Int16Array | Float32Array | Float64Array; + +function uintArray(max: number, size: number): UintArray { + return max <= 2 ** 8 + ? new Uint8Array(size) + : max <= 2 ** 16 + ? new Uint16Array(size) + : new Uint32Array(size); +} + +function samples(kind: number, size: number): Samples { + return kind === 0 + ? new Int16Array(size) + : kind === 1 + ? new Float32Array(size) + : new Float64Array(size); +} + +function sum(xs: UintArray): number { + let total = 0; + for (let i = 0; i < xs.length; i++) total += xs[i]!; + return total; +} + +function describe(xs: UintArray): string { + if (xs instanceof Uint8Array) return `u8 ${xs.length}`; + if (xs instanceof Uint16Array) return `u16 ${xs[0]}`; + return `u32 ${xs.BYTES_PER_ELEMENT}`; +} + +for (const max of [200, 60000, 5000000]) { + const xs = uintArray(max, 6); + // Stores convert to each arm's element kind (wrapping past its range). + for (let i = 0; i < xs.length; i++) xs[i] = i * 70000 + 255; + console.log(xs.length, xs.byteLength, xs.byteOffset, xs.BYTES_PER_ELEMENT); + console.log(xs[0], xs[5], xs[6], xs[-1], sum(xs)); + xs[1] += 3; + xs[2]++; + --xs[3]; + xs[4] = -1; + xs[5] = 2.75; + console.log(xs[1], xs[2], xs[3], xs[4], xs[5]); + // Writes past the end are ignored, and reads there are undefined. + xs[10] = 9; + console.log(xs[10] === undefined, xs[10] ?? "missing"); + let iterated = 0; + for (const v of xs) iterated += v; + console.log(iterated, [...xs].length, Array.from(xs)[1]); + const part = xs.subarray(1, 4); + part[0] = 42; + console.log(part.length, xs[1], xs.slice(2, 4).length, xs.slice(2, 4)[0] === xs[2]); + xs.fill(7, 4); + xs.copyWithin(0, 4, 5); + console.log(xs.join("-"), xs.toReversed()[0]); + xs.set([1, 2], 2); + console.log(xs[2], xs[3], describe(xs)); +} + +for (const kind of [0, 1, 2]) { + const s = samples(kind, 4); + s[0] = 1.5; + s[1] = -40000; + s[2] = 0.1; + s[3] = s[0]! * 3; + console.log(s.BYTES_PER_ELEMENT, s[0], s[1], s[2], s[3]); + console.log(s); + console.log(s instanceof Float32Array, s instanceof Int16Array); +} + +// A stored value that reassigns the receiver or index stores into the +// receiver and at the index evaluated before it. +type Wide = Uint8Array | Uint16Array; +function rebound(wide: boolean): void { + const before: Wide = wide ? new Uint16Array(4) : new Uint8Array(4); + let u: Wide = before; + let i = 0; + const swap = (): number => { + u = new Uint16Array(4); + i = 2; + return 300; + }; + u[i] = swap(); + console.log(before, u, i); + const target = u; + target[1] = 5; + u[i] += swap(); + console.log(target, u, i); +} +rebound(false); +rebound(true); diff --git a/tests/corpus/7746-key-order-iterable-iterator.ts b/tests/corpus/7746-key-order-iterable-iterator.ts new file mode 100644 index 000000000..d9d4ea15a --- /dev/null +++ b/tests/corpus/7746-key-order-iterable-iterator.ts @@ -0,0 +1,109 @@ +// Records that leave a typed collection through an `IterableIterator` +// method or a `Generator` keep the order their literals create (title +// before id), including copies that spread a value of their own type. The +// iterators stay live and hand out the stored objects themselves. +interface Book { + id: string; + title: string; + version: number; +} +type BookDraft = Omit; +type BookPatch = Partial; + +class Repo { + private readonly rows = new Map(); + put(row: T): void { + this.rows.set(row.id, row); + } + get(id: string): T | undefined { + return this.rows.get(id); + } + all(): IterableIterator { + return this.rows.values(); + } + ids(): IterableIterator { + return this.rows.keys(); + } +} + +function* newest(books: readonly Book[]): Generator { + for (let i = books.length - 1; i >= 0; i--) yield books[i]!; +} + +const books = new Repo(); +let n = 0; +function create(draft: BookDraft): Book { + const book: Book = { ...draft, id: "b" + ++n, version: 1 }; + books.put(book); + return book; +} +function update(id: string, patch: BookPatch): Book { + const current = books.get(id)!; + const next: Book = { ...current, ...patch, version: current.version + 1 }; + books.put(next); + return next; +} + +create({ title: "Dune" }); +const live = books.all(); +console.log(JSON.stringify(create({ title: "Emma" }))); +console.log(JSON.stringify(update("b1", { title: "Dune II" }))); +const listed = [...live]; +console.log(JSON.stringify(listed)); +console.log(listed.length, listed[0] === books.get("b1"), [...books.ids()].join()); +for (const book of books.all()) console.log(Object.keys(book).join()); +const ordered = [...newest(listed)]; +console.log(JSON.stringify(ordered), ordered[0] === listed[1]); +const step = newest(listed).next(); +console.log(step.done, JSON.stringify(step.value)); + +// Deleting the current entry before leaving a loop over an iterator-typed +// method: the iterator parks after that entry and continues with the next. +class Store { + readonly rows = new Map(); + readonly tags = new Set(); + values(): IterableIterator { + return this.rows.values(); + } + keys(): IterableIterator { + return this.rows.keys(); + } + tagValues(): IterableIterator { + return this.tags.values(); + } + anyPairs(): IterableIterator { + return this.rows.entries(); + } + unknownPairs(): IterableIterator { + return this.rows.entries(); + } + tagAny(): IterableIterator { + return this.tags.entries(); + } +} +const store = new Store(); +for (const [k, v] of [["a", 1], ["b", 2], ["c", 3]] as const) store.rows.set(k, v); +for (const t of ["x", "y", "z"]) store.tags.add(t); +console.log(JSON.stringify([...store.anyPairs()]), JSON.stringify([...store.unknownPairs()])); +console.log(JSON.stringify([...store.tagAny()])); +const values = store.values(); +for (const v of values) { + if (v === 2) { + store.rows.delete("b"); + break; + } +} +console.log([...values].join(), [...store.rows.keys()].join()); +for (const k of store.keys()) { + store.rows.delete(k); + break; +} +console.log([...store.rows.keys()].join()); +const tags = store.tagValues(); +for (const t of tags) { + if (t === "y") { + store.tags.delete("y"); + break; + } +} +console.log([...tags].join(), [...store.tags].join()); diff --git a/tests/corpus/7747-text-codec-untyped-slot.js b/tests/corpus/7747-text-codec-untyped-slot.js new file mode 100644 index 000000000..cdf8345da --- /dev/null +++ b/tests/corpus/7747-text-codec-untyped-slot.js @@ -0,0 +1,69 @@ +// TextEncoder and TextDecoder instances held in untyped storage (hono's +// StreamingApi keeps `this.encoder = new TextEncoder()` in a JavaScript +// class field) and called from dynamic code, with their identity, streaming +// state, properties, method values and argument errors. +class StreamingApi { + /** @type {any} */ encoder; + /** @type {any} */ decoder; + /** @type {any} */ latin; + /** @type {any} */ strict; + constructor() { + this.encoder = new TextEncoder(); + this.decoder = new TextDecoder(); + this.latin = new TextDecoder("latin1"); + this.strict = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }); + } + write(input) { + if (typeof input === "string") input = this.encoder.encode(input); + return input; + } +} + +const show = (error) => console.log(error.name, error.code); +const api = new StreamingApi(); +console.log(api.write("héllo 😀")); +console.log(api.encoder.encoding, typeof api.encoder, api.encoder === api.encoder); +console.log(String(api.encoder), JSON.stringify(api.encoder), JSON.stringify({ d: api.decoder })); +console.log(api.encoder.encode(), api.encoder.encode(42), api.encoder.encode(undefined)); +const dest = new Uint8Array(4); +console.log(api.encoder.encodeInto("héllo", dest), dest); +try { + api.encoder.encodeInto(1, dest); +} catch (error) { + show(error); +} + +console.log(api.decoder.encoding, api.decoder.fatal, api.decoder.ignoreBOM); +console.log(api.latin.encoding, api.strict.fatal, api.strict.ignoreBOM); +const euro = api.encoder.encode("€uro"); +const head = api.decoder.decode(euro.subarray(0, 2), { stream: true }); +console.log(JSON.stringify(head), api.decoder.decode(euro.subarray(2))); +console.log(api.latin.decode(new Uint8Array([0xe9, 0x41]))); +console.log(JSON.stringify(api.strict.decode(new Uint8Array([0xef, 0xbb, 0xbf, 0x41])))); +try { + api.strict.decode(new Uint8Array([0xff])); +} catch (error) { + show(error); +} +try { + api.decoder.decode("text"); +} catch (error) { + show(error); +} +console.log(JSON.stringify(api.decoder.decode()), JSON.stringify(api.decoder.decode(undefined, null))); + +const encode = api.encoder.encode; +try { + encode("x"); +} catch (error) { + console.log(error.name, error.code, error.message); +} +console.log(typeof api.encoder.encode, api.encoder.encode.length, api.encoder.encodeInto.length); +console.log(api.decoder.decode.length, encode === api.encoder.encode); +console.log(api.encoder.encode.call(api.encoder, "ab")); +console.log("encode" in api.encoder, "decode" in api.decoder, api.encoder.missing); + +console.log(api.encoder instanceof TextEncoder, api.decoder instanceof TextDecoder); +console.log(api.encoder instanceof TextDecoder, JSON.parse("{}") instanceof TextEncoder); +const codecs = [api.encoder, api.decoder]; +console.log(codecs.indexOf(api.decoder), codecs.includes(api.encoder)); diff --git a/tests/corpus/7747-typed-array-class-values.ts b/tests/corpus/7747-typed-array-class-values.ts new file mode 100644 index 000000000..523896c96 --- /dev/null +++ b/tests/corpus/7747-typed-array-class-values.ts @@ -0,0 +1,42 @@ +// Typed-array unions chosen through class values, and held in fields, +// parameters, returns and records. + +type UintArray = Uint8Array | Uint16Array | Uint32Array; + +const pick = (max: number): { new (n: number): UintArray } => + max <= 2 ** 8 ? Uint8Array : max <= 2 ** 16 ? Uint16Array : Uint32Array; + +class Stack { + heap: UintArray; + length = 0; + constructor(max: number) { + const Heap = pick(max); + this.heap = new Heap(max); + } + push(n: number): void { + this.heap[this.length++] = n; + } + pop(): number | undefined { + return this.heap[--this.length]; + } +} + +for (const max of [10, 1000, 100000]) { + const stack = new Stack(max); + for (let i = 1; i <= 4; i++) stack.push(i * 250); + console.log(stack.heap.BYTES_PER_ELEMENT, stack.pop(), stack.pop(), stack.length); +} + +function grow(xs: UintArray, by: number): UintArray { + const Next = pick(xs.length * by); + const out = new Next(xs.length * by); + out.set(xs); + return out; +} + +const record: { data: UintArray; label: string } = { data: new Uint8Array([1, 2, 3]), label: "r" }; +record.data = grow(record.data, 100); +record.data[299] = 65535; +console.log(record.label, record.data.length, record.data[0], record.data[2], record.data[299]); +record.data = grow(record.data, 1000); +console.log(record.data.BYTES_PER_ELEMENT, record.data.length, record.data[1]); diff --git a/tests/corpus/7748-npm-any-bytes-receiver/main.ts b/tests/corpus/7748-npm-any-bytes-receiver/main.ts new file mode 100644 index 000000000..58301765a --- /dev/null +++ b/tests/corpus/7748-npm-any-bytes-receiver/main.ts @@ -0,0 +1,33 @@ +// Typed arrays the checker types `any` in package JavaScript: hono's etag +// digest (hono/dist/middleware/etag/digest.js) merges multi-chunk bodies +// through `nextChunk.set(...)`, where `nextChunk`'s initializer depends on +// `chunk`, which is assigned `nextChunk`. Method calls and property reads on +// such values run on the native bytes, as in Node. +import { generateDigest } from "etag-digest"; +import { grow } from "chunk-buffers"; + +const enc = new TextEncoder(); +const sha1 = (body: Uint8Array): Promise => crypto.subtle.digest({ name: "SHA-1" }, body); + +function chunked(parts: string[]): ReadableStream { + let i = 0; + return new ReadableStream({ + pull(controller): void { + if (i < parts.length) controller.enqueue(enc.encode(parts[i++]!)); + else controller.close(); + }, + }); +} + +async function main(): Promise { + console.log("single", await generateDigest(chunked([JSON.stringify({ books: 400 })]), sha1)); + console.log("chunked", await generateDigest(chunked(["alpha\n", "beta\n", "gamma\n", "delta\n"]), sha1)); + const large = ["x".repeat(200000), "y".repeat(100000), "z".repeat(5)]; + console.log("large", await generateDigest(chunked(large), sha1)); + console.log("exact", await generateDigest(chunked(["a".repeat(262144), "b"]), sha1)); + console.log("empty", await generateDigest(chunked([]), sha1)); + console.log("null", await generateDigest(null, sha1)); + console.log(grow([new Uint8Array([1, 2, 3]), new Uint8Array([4]), new Uint8Array([5])])); +} + +await main(); diff --git a/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/index.d.ts b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/index.d.ts new file mode 100644 index 000000000..26fe364a0 --- /dev/null +++ b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/index.d.ts @@ -0,0 +1 @@ +export declare function grow(parts: Uint8Array[]): Uint8Array | undefined; diff --git a/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/index.js b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/index.js new file mode 100644 index 000000000..a0a1fea79 --- /dev/null +++ b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/index.js @@ -0,0 +1,18 @@ +// `next` is referenced by its own initializer's inference (through +// `chunk = next`), so the checker types it `any` while it is a Uint8Array. +export function grow(parts) { + let chunk; + for (const value of parts) { + if (!chunk) chunk = value.slice(0); + else { + const next = new Uint8Array(new ArrayBuffer(Math.max(8, chunk.byteLength * 2))); + next.set(chunk.subarray(0, 2)); + next.set([9], 2); + next[3] = next.byteLength; + console.log(next.length, next[0], next.subarray(1, 3), next.at(-1), next.byteOffset); + console.log(next.slice(2, 4), next.buffer.byteLength, next.BYTES_PER_ELEMENT); + chunk = next; + } + } + return chunk; +} diff --git a/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/package.json b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/package.json new file mode 100644 index 000000000..74e354d34 --- /dev/null +++ b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/chunk-buffers/package.json @@ -0,0 +1 @@ +{ "name": "chunk-buffers", "version": "1.0.0", "type": "module", "main": "index.js", "types": "index.d.ts" } diff --git a/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/index.d.ts b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/index.d.ts new file mode 100644 index 000000000..79d9dc396 --- /dev/null +++ b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/index.d.ts @@ -0,0 +1 @@ +export declare const generateDigest: (stream: ReadableStream | null, generator: (body: Uint8Array) => ArrayBuffer | Promise) => Promise; diff --git a/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/index.js b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/index.js new file mode 100644 index 000000000..bf438a5ea --- /dev/null +++ b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/index.js @@ -0,0 +1,60 @@ +// hono/dist/middleware/etag/digest.js (hono 4.13.13) +const mergeBuffers = (buffer1, buffer2) => { + if (!buffer1) return buffer2; + const merged = new Uint8Array(new ArrayBuffer(buffer1.byteLength + buffer2.byteLength)); + merged.set(new Uint8Array(buffer1), 0); + merged.set(buffer2, buffer1.byteLength); + return merged; +}; +const CHUNK_SIZE = 262144; +const generateDigest = async (stream, generator) => { + if (!stream) return null; + let result = void 0; + let chunk; + let chunkLength = 0; + const digest = async (body) => { + result = await generator(mergeBuffers(result, body)); + }; + const reader = stream.getReader(); + for (;;) { + const { value, done } = await reader.read(); + if (done) break; + let offset = 0; + while (offset < value.byteLength) { + const remaining = value.byteLength - offset; + if (chunkLength === 0 && remaining >= CHUNK_SIZE) { + await digest(value.subarray(offset, offset + CHUNK_SIZE)); + offset += CHUNK_SIZE; + continue; + } + const requiredLength = chunkLength + remaining; + if (requiredLength < CHUNK_SIZE) { + if (!chunk) chunk = value.slice(offset); + else { + if (chunk.byteLength < requiredLength) { + const nextChunk = new Uint8Array(new ArrayBuffer(Math.min(CHUNK_SIZE, Math.max(requiredLength, chunk.byteLength * 2)))); + nextChunk.set(chunk.subarray(0, chunkLength)); + chunk = nextChunk; + } + chunk.set(value.subarray(offset), chunkLength); + } + chunkLength = requiredLength; + break; + } + const length = CHUNK_SIZE - chunkLength; + if (chunk?.byteLength !== CHUNK_SIZE) { + const nextChunk = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(CHUNK_SIZE)); + if (chunk) nextChunk.set(chunk.subarray(0, chunkLength)); + chunk = nextChunk; + } + chunk.set(value.subarray(offset, offset + length), chunkLength); + await digest(chunk); + chunkLength = 0; + offset += length; + } + } + if (chunk && chunkLength > 0) await digest(chunk.subarray(0, chunkLength)); + if (!result) return null; + return Array.prototype.map.call(new Uint8Array(result), (x) => x.toString(16).padStart(2, "0")).join(""); +}; +export { generateDigest }; diff --git a/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/package.json b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/package.json new file mode 100644 index 000000000..f15495eed --- /dev/null +++ b/tests/corpus/7748-npm-any-bytes-receiver/node_modules/etag-digest/package.json @@ -0,0 +1 @@ +{ "name": "etag-digest", "version": "1.0.0", "type": "module", "main": "index.js", "types": "index.d.ts" } diff --git a/tests/corpus/7748-typed-array-number-array-unions.ts b/tests/corpus/7748-typed-array-number-array-unions.ts new file mode 100644 index 000000000..706ed37b5 --- /dev/null +++ b/tests/corpus/7748-typed-array-number-array-unions.ts @@ -0,0 +1,78 @@ +// Unions of typed arrays and number arrays, as a cache picks its index +// arrays by capacity: a linked list over next/prev index arrays. + +type NumberArray = Uint8Array | Uint16Array | Uint32Array | number[]; + +function indexArray(max: number): NumberArray { + if (max <= 2 ** 8) return new Uint8Array(max); + if (max <= 2 ** 16) return new Uint16Array(max); + if (max <= 2 ** 32) return new Uint32Array(max); + return new Array(4).fill(0); +} + +class Order { + next: NumberArray; + prev: NumberArray; + head = 0; + tail = 0; + size = 0; + + constructor(max: number, force = false) { + this.next = force ? new Array(max).fill(0) : indexArray(max); + this.prev = force ? new Array(max).fill(0) : indexArray(max); + } + + push(index: number): void { + if (this.size === 0) { + this.head = this.tail = index; + } else { + this.next[this.tail] = index; + this.prev[index] = this.tail; + this.tail = index; + } + this.size++; + } + + moveToTail(index: number): void { + if (index === this.tail) return; + if (index === this.head) this.head = this.next[index]!; + else this.next[this.prev[index]!] = this.next[index]!; + this.prev[this.next[index]!] = this.prev[index]!; + this.next[this.tail] = index; + this.prev[index] = this.tail; + this.tail = index; + } + + walk(): number[] { + const out: number[] = []; + for (let i = this.head, n = 0; n < this.size; n++, i = this.next[i]!) out.push(i); + return out; + } +} + +for (const [max, force] of [ + [8, false], + [300, false], + [70000, false], + [8, true], +] as const) { + const order = new Order(max, force); + for (let i = 0; i < 6; i++) order.push(i); + order.moveToTail(0); + order.moveToTail(3); + order.moveToTail(5); + console.log(max, force, order.walk().join(","), order.next.length, order.prev[3]); +} + +// The number-array arm reads holes and missing indexes as undefined, +// and writes past its length grow it. +const arms: NumberArray[] = [new Uint8Array(3), [1, 2, 3]]; +for (const a of arms) { + a[1] = 300; + a[2] += 0.5; + a[5] = 8; + console.log(a.length, a[1], a[2], a[4], a[5]); + let total = 0; + for (const v of a) total += v ?? -1; + console.log(total, [...a].length, Array.from(a).length); +} diff --git a/tests/corpus/7749-text-codec-unknown-roundtrip.ts b/tests/corpus/7749-text-codec-unknown-roundtrip.ts new file mode 100644 index 000000000..b7c18b3ef --- /dev/null +++ b/tests/corpus/7749-text-codec-unknown-roundtrip.ts @@ -0,0 +1,36 @@ +// TypeScript codecs crossing 'unknown' slots: boxed by reference and +// checked back, keeping identity and a decoder's streaming state. +const holder: { codec: unknown; decoder: unknown } = { + codec: new TextEncoder(), + decoder: new TextDecoder("utf-16le"), +}; + +const encoder = holder.codec as TextEncoder; +console.log(encoder.encode("typed"), encoder === (holder.codec as TextEncoder)); +const decoder = holder.decoder as TextDecoder; +const bytes = new Uint8Array([0x68, 0x00, 0x69]); +console.log(JSON.stringify(decoder.decode(bytes, { stream: true }))); +console.log(JSON.stringify(decoder.decode(new Uint8Array([0x00]))), decoder.encoding); + +function store(value: unknown): unknown[] { + return [value, value]; +} +const pair = store(encoder); +console.log(pair[0] === pair[1], pair[0] === holder.codec); + +const maybe: unknown = Math.random() > 2 ? 1 : new TextDecoder(); +if (typeof maybe === "object" && maybe !== null) console.log((maybe as TextDecoder).encoding); + +// Codecs inside arrays and records cross as their handles too. +const codecs: TextEncoder[] = [encoder, new TextEncoder()]; +const asUnknown: unknown = codecs; +const back = asUnknown as TextEncoder[]; +console.log(back.length, back[0] === encoder, back[1]!.encode("x")); +const streaming = new TextDecoder(); +const nested: { name: string; decoder: TextDecoder } = { name: "nested", decoder: streaming }; +const boxed: unknown = nested; +const unboxed = boxed as { name: string; decoder: TextDecoder }; +console.log(unboxed.name, unboxed.decoder === streaming); +console.log(JSON.stringify(unboxed.decoder.decode(new Uint8Array([0xe2, 0x82]), { stream: true }))); +console.log(JSON.stringify(streaming.decode(new Uint8Array([0xac])))); +console.log(JSON.stringify(boxed)); diff --git a/tests/corpus/7749-typed-array-element-searches.ts b/tests/corpus/7749-typed-array-element-searches.ts new file mode 100644 index 000000000..7cae267ec --- /dev/null +++ b/tests/corpus/7749-typed-array-element-searches.ts @@ -0,0 +1,22 @@ +// TypedArray indexOf, lastIndexOf and includes on every element kind, on +// Buffers (their own search), and on unions of typed arrays: strict +// equality for indexOf/lastIndexOf, SameValueZero for includes. +const f = new Float64Array([1, NaN, -0, 0, 2.5, NaN, 1]); +console.log(f.indexOf(NaN), f.includes(NaN), f.lastIndexOf(NaN)); +console.log(f.indexOf(0), f.indexOf(-0), f.lastIndexOf(0), f.lastIndexOf(-0), f.includes(-0)); +console.log(f.indexOf(1, 1), f.indexOf(1, -1), f.indexOf(1, -100), f.indexOf(1, 100), f.indexOf(1, 1.9)); +console.log(f.lastIndexOf(1, 5), f.lastIndexOf(1, -2), f.lastIndexOf(1, -100), f.lastIndexOf(1, 100), f.lastIndexOf(1, undefined)); +console.log(f.includes(2.5, 4), f.includes(2.5, 5), f.includes(2.5, -3), f.includes(NaN, 6)); +const u = new Uint8Array([0, 255, 3, 255]); +console.log(u.indexOf(255), u.indexOf(-1), u.indexOf(511), u.indexOf(3.5), u.includes(NaN), u.lastIndexOf(255)); +const i = new Int16Array([-5, 7, -5]); +console.log(i.indexOf(-5), i.lastIndexOf(-5), i.includes(7), i.indexOf(65531)); +const h = new Float32Array([0.1, 0.5, 16777217]); +console.log(h.indexOf(0.1), h.indexOf(0.5), h.includes(16777216), h.indexOf(16777217)); +const c = new Uint8ClampedArray([300, -4]); +console.log(c.indexOf(255), c.indexOf(0), new Uint32Array(0).indexOf(0), new Uint32Array(0).includes(0)); +const b = Buffer.from([1, 2, 3, 2]); +console.log(b.indexOf(2), b.indexOf(258), b.lastIndexOf(2), b.includes("\u0003")); +type UintArray = Uint8Array | Uint16Array | Uint32Array; +const mk = (n: number): UintArray => (n > 1 ? new Uint16Array([1, 700, 1]) : new Uint8Array([1, 188, 1])); +for (const x of [mk(1), mk(2)]) console.log(x.indexOf(700), x.lastIndexOf(1), x.includes(188), x.indexOf(1, 1)); diff --git a/tests/corpus/7750-key-order-javascript-spread-sources/lib.js b/tests/corpus/7750-key-order-javascript-spread-sources/lib.js new file mode 100644 index 000000000..c8fc0d066 --- /dev/null +++ b/tests/corpus/7750-key-order-javascript-spread-sources/lib.js @@ -0,0 +1,14 @@ +// Records JavaScript builds in its own key order, handed to TypeScript +// through typed slots: a draft from a "validator", a stored row, and a +// replacement method installed through a computed key. +export function validate(input) { + return { year: input.year, title: input.title, tags: input.tags }; +} + +export function storeRow(rows, id) { + rows.set(id, { title: "Stored", id, tags: [], year: 1999, createdAt: "t0", version: 1 }); +} + +export function replace(target, name) { + target[name] = (id) => ({ version: 7, id, year: 2001, title: "Replaced", tags: ["r"], createdAt: "t1" }); +} diff --git a/tests/corpus/7750-key-order-javascript-spread-sources/main.ts b/tests/corpus/7750-key-order-javascript-spread-sources/main.ts new file mode 100644 index 000000000..073ff6c5e --- /dev/null +++ b/tests/corpus/7750-key-order-javascript-spread-sources/main.ts @@ -0,0 +1,122 @@ +// Literals that start by spreading a record JavaScript built keep that +// record's key order, as Node does: a draft from JavaScript spread into a +// new record, a self copy of a record JavaScript stored or returned, and +// a wider view of either. Native drafts and copies keep the literal order. +import { replace, storeRow, validate } from "./lib.js"; + +interface Book { + id: string; + title: string; + year: number; + tags: readonly string[]; + createdAt: string; + version: number; +} + +type Draft = Omit; +type Patch = Partial; + +interface Availability { + copies: number; + onLoan: number; +} + +type BookView = Book & { availability: Availability }; + +class Repository { + private readonly rows = new Map(); + private sequence = 0; + + create(draft: Draft): Book { + const book: Book = { + ...draft, + tags: [...draft.tags].sort(), + id: `bk_${++this.sequence}`, + createdAt: "t" + this.sequence, + version: 1, + }; + this.rows.set(book.id, book); + return book; + } + + require(id: string): Book { + const row = this.rows.get(id); + if (row === undefined) throw new Error("missing " + id); + return row; + } + + update(id: string, patch: Patch): Book { + const current = this.require(id); + const next: Book = { ...current, ...patch, version: current.version + 1 }; + this.rows.set(id, next); + return next; + } + + view(book: Book): BookView { + return { ...book, availability: { copies: 2, onLoan: book.version } }; + } + + table(): Map { + return this.rows; + } +} + +function show(label: string, value: Book): void { + console.log(label, JSON.stringify(value)); + console.log(" keys", Object.keys(value).join()); +} + +function showView(label: string, value: BookView): void { + console.log(label, JSON.stringify(value)); + console.log(" keys", Object.keys(value).join()); +} + +const repo = new Repository(); + +// A native draft: the literal's order. +const native = repo.create({ title: "Native", year: 1990, tags: ["b", "a"] }); +show("native", native); +showView("native view", repo.view(native)); +show("native update", repo.update(native.id, { year: 1991 })); + +// A draft JavaScript built in another order. +const draft: Draft = validate({ title: "Checked", year: 2020, tags: ["z", "y"] }); +const checked = repo.create(draft); +show("checked", checked); +showView("checked view", repo.view(checked)); +show("checked update", repo.update(checked.id, { title: "Checked 2" })); +showView("checked update view", repo.view(repo.require(checked.id))); + +// A row JavaScript stored into the typed map. +storeRow(repo.table(), "js_1"); +show("stored", repo.require("js_1")); +show("stored update", repo.update("js_1", { tags: ["s"] })); +showView("stored view", repo.view(repo.require("js_1"))); + +// A replacement method returning rows JavaScript builds. +replace(repo, "require"); +show("replaced", repo.require("x")); +show("replaced update", repo.update("x", { year: 2002 })); +showView("replaced view", repo.view(repo.require("y"))); + +// The copies are independent records. +const again = repo.view(checked); +showView("again", again); +console.log(native.version, checked.version, again.availability.onLoan); + +// An integer-named key the spread source lacks comes first, as Node +// creates integer keys first whatever the literal's order. +interface Ranked { + year: number; + title: string; + tags: readonly string[]; + id: string; + "1": string; +} +function rank(source: Draft, id: string): Ranked { + return { ...source, id, "1": "gold" }; +} +for (const ranked of [rank(draft, "r1"), rank({ title: "Native", year: 1990, tags: [] }, "r2")]) { + console.log("ranked", JSON.stringify(ranked)); + console.log(" keys", Object.keys(ranked).join()); +} diff --git a/tests/corpus/7750-typed-array-union-inspect.ts b/tests/corpus/7750-typed-array-union-inspect.ts new file mode 100644 index 000000000..aebb0dbdf --- /dev/null +++ b/tests/corpus/7750-typed-array-union-inspect.ts @@ -0,0 +1,14 @@ +// Printing u8 byte views whose static type does not say what they are: +// union arms, elements and fields print as Node does by the value's kind. +type UintArray = Uint8Array | Uint16Array | Uint32Array; +const mk = (n: number): UintArray => (n > 1 ? new Uint16Array([1, 700]) : new Uint8Array([1, 188])); +const a = mk(1), b = mk(2); +console.log(a, b); +console.log([a, b]); +const bufArm: Uint8Array | Uint16Array = Buffer.from([1, 2, 3]); +console.log(bufArm); +const u8s: Uint8Array[] = [new Uint8Array([9]), Buffer.from([10, 11])]; +console.log(u8s); +const rec: { bytes: Uint8Array; view: DataView } = { bytes: new Uint8Array(2), view: new DataView(new ArrayBuffer(2)) }; +console.log(rec); +console.log(new Uint8Array([5, 6]), Buffer.from([7])); diff --git a/tests/corpus/7751-class-well-known-symbol-fields.ts b/tests/corpus/7751-class-well-known-symbol-fields.ts new file mode 100644 index 000000000..d57fce3f8 --- /dev/null +++ b/tests/corpus/7751-class-well-known-symbol-fields.ts @@ -0,0 +1,115 @@ +// Class fields keyed by well-known symbols, Symbol.toStringTag first (the +// lru-cache shape), beside toStringTag getters and module symbol keys. +// String conversion, Object.prototype.toString and util.inspect read the +// tag as Node does, through typed values and through `unknown`. + +const limit = Symbol("limit"); + +class Cache { + size = 0; + [limit] = 10; + [Symbol.toStringTag] = "LRUCache"; +} + +class Sub extends Cache { + extra = 1; +} + +class Retag extends Cache { + constructor() { + super(); + this[Symbol.toStringTag] = "Retagged"; + } +} + +class Tagged { + x = 1; + get [Symbol.toStringTag](): string { + return "Custom"; + } +} + +class TaggedSub extends Tagged { + y = 2; +} + +class Named { + get [Symbol.toStringTag](): string { + return "Named"; + } +} + +class Blank { + get [Symbol.toStringTag](): string { + return ""; + } +} + +class Plain { + n = 3; +} + +const c = new Cache(); +console.log(String(c), `${c}`, Object.prototype.toString.call(c), c[Symbol.toStringTag]); +console.log(c); +console.log(Object.keys(c), JSON.stringify(c)); +console.log(new Sub(), String(new Sub())); +const r: Cache = new Retag(); +console.log(String(r), r); +c[Symbol.toStringTag] = "Other"; +console.log(`${c}`, c); + +const t = new Tagged(); +console.log(String(t), `${t}`, t, [t], { deep: { deeper: { t } } }); +const ts: Tagged = new TaggedSub(); +console.log(String(ts), ts); +console.log(new Named(), new Blank(), String(new Blank())); +console.log(String(new Plain()), new Plain()); + +const values: unknown[] = [new Cache(), new Tagged(), new TaggedSub(), new Plain()]; +for (const v of values) { + console.log(Object.prototype.toString.call(v), String(v), (v as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag]); +} +console.log(values); + +const literal: unknown = { [Symbol.toStringTag]: "Literal", a: 1 }; +console.log(Object.prototype.toString.call(literal), String(literal), `${literal}`); + +// util.inspect ignores a Symbol.toStringTag getter that throws, typed or +// held as an untyped value. +class Throwing { + get [Symbol.toStringTag](): string { + throw new Error("tag"); + } +} +const throwing: unknown = new Throwing(); +console.log(new Throwing(), [new Throwing()], throwing, [throwing]); + +// A conversion to "[object Object]" still evaluates its receiver. +let made = 0; +function make(): Plain { + made++; + return new Plain(); +} +function fail(): Plain { + throw new Error("receiver"); +} +console.log(Object.prototype.toString.call(make()), String(make()), made); +try { + console.log(Object.prototype.toString.call(fail())); +} catch (error) { + console.log((error as Error).message); +} + +// ToString of a class with valueOf and no toString reads its tag; the +// default hint calls valueOf. +class Valued { + valueOf(): number { + return 1; + } + get [Symbol.toStringTag](): string { + return "Valued"; + } +} +const valued: unknown = new Valued(); +console.log(String(valued), `${valued}`, [valued].join(), valued + "", Object.prototype.toString.call(valued)); diff --git a/tests/corpus/7752-typed-array-dynamic-methods.js b/tests/corpus/7752-typed-array-dynamic-methods.js new file mode 100644 index 000000000..a728e3ce9 --- /dev/null +++ b/tests/corpus/7752-typed-array-dynamic-methods.js @@ -0,0 +1,125 @@ +// %TypedArray%.prototype methods called on typed arrays held as untyped +// values, as JavaScript sources and Test262's testTypedArray.js use them. +const kinds = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + +function show(label, value) { + console.log(label, value); +} + +// TA arrives untyped, as Test262's testWithTypedArrayConstructors passes it. +function check(TA) { + const a = new TA([3, 1, 2, 1, 0]); + show(TA.name + " search", [a.indexOf(1), a.lastIndexOf(1), a.includes(2), a.indexOf(1, 2), a.lastIndexOf(1, -3), a.includes(3, 1), a.indexOf(9), a.indexOf("1")]); + show(" map", a.map((v, i) => v * 2 + i)); + show(" filter", a.filter((v) => v > 0)); + show(" some/every", [a.some((v) => v > 2), a.every((v) => v >= 0), a.every((v) => v > 0)]); + show(" find", [a.find((v) => v < 2), a.findIndex((v) => v < 2), a.findLast((v) => v < 2), a.findLastIndex((v) => v < 2), a.find((v) => v > 5), a.findLastIndex((v) => v > 5)]); + show(" reduce", [a.reduce((s, v) => s + v), a.reduceRight((s, v, i) => s + "" + v + i, ""), a.reduce((s, v) => s * 10 + v, 0)]); + const seen = []; + a.forEach(function (v, i, arr) { seen.push([v, i, arr === a, this.tag]); }, { tag: "t" }); + show(" forEach", seen.map((x) => x.join(":")).join(" ")); + show(" sorted", [a.toSorted().join(), a.toSorted((x, y) => y - x).join(), a.toReversed().join(), a.with(1, 7).join(), a.with(-1, 9).join(), a.join()]); + show(" iterators", [[...a.keys()].join(), [...a.values()].join(), [...a.entries()].map((e) => e.join("=")).join(" ")]); + const b = a.slice(); + b.sort(); + show(" sort", [b.join(), b.reverse().join(), b.sort((x, y) => x - y) === b]); +} +kinds.forEach(check); + +// Edge cases over untyped constructors. +function edges(F, C, I, U, S) { + const f = new F([NaN, -0, 0, 1, -Infinity, NaN]); + show("float search", [f.indexOf(NaN), f.includes(NaN), f.indexOf(-0), f.lastIndexOf(0), f.includes(-0)]); + show("float sort", Array.from(f.toSorted(), (v) => Object.is(v, -0) ? "-0" : String(v)).join()); + show("clamped map", new C([1, 2]).map((v) => v * 200)); + show("wrapping map", new I([1, 2]).map((v) => v * 100)); + const live = new U([1, 2, 3]); + show("live", live.map((v, i, arr) => { if (i === 0) arr[2] = 9; return v; })); + for (const bad of [() => new I(2).forEach(1), () => new I(0).reduce((a, b) => a), () => new I(2).with(5, 1), () => new I(2).sort("x")]) { + try { + bad(); + show("no throw", ""); + } catch (e) { + show(e.constructor.name, e.message.length > 0); + } + } + let order = []; + new S([5, 1, 4]).sort((x, y) => { order.push(x + "" + y); return x - y; }); + show("comparator calls", order.length > 0); +} +edges(Float64Array, Uint8ClampedArray, Int8Array, Uint8Array, Int16Array); + +// Method values, and the constructors' from and of. +function run(TA) { + const s = new TA([1, 2, 3]); + console.log(typeof s.findLast, typeof s.with, typeof s.toSorted, typeof s.map, typeof s.at); + const m = s.findLast; + console.log(m.call(s, (v) => v < 3)); + console.log(typeof TA.from, typeof TA.of); +} +run(Int16Array); +function statics(TA) { + console.log(TA.from([1, 2, 300]), TA.from({ length: 2, 0: "7", 1: 8 }), TA.from(new Set([4, 5]), (v, k) => v * 10 + k)); + console.log(TA.of(1, 2, 3), TA.of(), TA.from({ [Symbol.iterator]: function* () { yield 9; yield 8; } })); + try { TA.from([1], 5); } catch (e) { console.log(e.constructor.name); } + try { TA.from(null); } catch (e) { console.log(e.constructor.name); } +} +statics(Uint8Array); +statics(Float32Array); + +// TypedArray.from and TypedArray.of with other constructors as this. +function customs(TA) { + let calls = []; + function Big(n) { calls.push(n); return new TA(n + 2); } + const fromBig = TA.from.call(Big, [1, 2, 3], (v) => v * 2); + console.log(fromBig, calls); + console.log(TA.of.call(Big, 7, 8)); + function attempt(label, C) { + try { TA.from.call(C, [1, 2]); console.log(label, "no throw"); } catch (e) { console.log(label, e.constructor.name); } + try { TA.of.call(C, 1, 2); console.log(label, "no throw"); } catch (e) { console.log(label, e.constructor.name); } + } + attempt("small", function () { return new TA(1); }); + attempt("plain", function () { return {}; }); + attempt("arrow", () => new TA(5)); + attempt("number", 5); + const order = []; + const arrayLike = { length: 2, get 0() { order.push("get0"); return 1; }, get 1() { order.push("get1"); return 2; } }; + TA.from.call(function (n) { order.push("create" + n); return new TA(n); }, arrayLike, (v) => { order.push("map" + v); return v; }); + console.log(order.join(" ")); +} +customs(Int32Array); + +// Reducers and comparators run with an undefined this, also through method +// values; a Symbol.iterator getter is read once. +function review(TA) { + "use strict"; + const ta = new TA([3, 1, 2]); + const seen = []; + const reduce = ta.reduce, reduceRight = ta.reduceRight, sort = ta.sort, toSorted = ta.toSorted; + reduce.call(ta, function (a, b) { seen.push(this === undefined); return a + b; }); + reduceRight.call(ta, function (a, b) { seen.push(this === undefined); return a + b; }, 0); + sort.call(ta.slice(), function (x, y) { seen.push(this === undefined); return x - y; }); + toSorted.call(ta, function (x, y) { seen.push(this === undefined); return y - x; }); + console.log(seen.every((v) => v), seen.length > 0); + let gets = 0; + const source = {}; + Object.defineProperty(source, Symbol.iterator, { get() { gets++; return function* () { yield 4; yield 5; }; } }); + console.log(new TA(source), gets); + console.log(TA.from(source), gets); +} +review(Uint8Array); +function plainIterator(TA) { + let gets = 0, nexts = 0; + const source = {}; + Object.defineProperty(source, Symbol.iterator, { + get() { + gets++; + return function () { + let i = 0; + return { next() { nexts++; return i < 2 ? { value: 10 + i++, done: false } : { value: undefined, done: true }; } }; + }; + }, + }); + console.log(new TA(source), TA.from(source, (v) => v * 2), gets, nexts); +} +plainIterator(Int16Array); diff --git a/tests/corpus/7790-js-class-integrity-layouts.js b/tests/corpus/7790-js-class-integrity-layouts.js new file mode 100644 index 000000000..f889c02f8 --- /dev/null +++ b/tests/corpus/7790-js-class-integrity-layouts.js @@ -0,0 +1,139 @@ +"use strict"; +// Object.freeze, Object.seal and Object.preventExtensions next to JavaScript +// classes whose fields all exist from construction. The integrity state of +// a class instance lives beside its fields, so classes keep fixed layouts +// whether or not a program freezes values; frozen instances still refuse +// writes and additions as in Node. + +class Point { + constructor(x, y) { + this.x = x; + this.y = y; + } + norm() { + return Math.abs(this.x) + Math.abs(this.y); + } +} + +class Point3 extends Point { + constructor(x, y, z) { + super(x, y); + this.z = z; + } + norm() { + return super.norm() + Math.abs(this.z); + } +} + +class Status { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") this.value = "dirty"; + } +} + +/** @param {any} target @param {string} key @param {unknown} value */ +function put(target, key, value) { + target[key] = value; +} + +/** @param {any} value */ +function describe(value) { + const keys = Object.keys(value); + const parts = []; + for (const key in value) parts.push(`${key}=${String(value[key])}`); + return `${keys.join(",")} | ${parts.join(" ")} | ${JSON.stringify(value)}`; +} + +/** @param {() => void} fn */ +function attempt(label, fn) { + try { + fn(); + console.log(label, "ok"); + } catch (e) { + console.log(label, e instanceof TypeError ? "TypeError" : "other", e.message); + } +} + +// The zod shape: freeze whatever value a result carries. Here it is always +// plain data, never a class instance. +/** @param {{ status: string, value: any }} data */ +function freezeResult(data) { + if (data.status === "valid") data.value = Object.freeze(data.value); + return data; +} + +const results = [ + freezeResult({ status: "valid", value: { a: 1, list: [1, 2] } }), + freezeResult({ status: "valid", value: [3, 4] }), + freezeResult({ status: "aborted", value: { b: 2 } }), + freezeResult({ status: "valid", value: "text" }), +]; +for (const result of results) console.log(result.status, Object.isFrozen(result.value), JSON.stringify(result.value)); +/** @param {() => void} fn */ +function throws(fn) { + try { + fn(); + return false; + } catch (e) { + return e instanceof TypeError; + } +} +console.log(throws(() => put(results[0].value, "a", 9)), throws(() => put(results[2].value, "b", 3))); +console.log(JSON.stringify(results[2].value)); + +// Class instances that only ever travel through untyped code. +const status = new Status(); +status.dirty(); +const p = new Point(1, -2); +const q = new Point3(3, 4, -5); +put(p, "tag", "p"); +put(q, "w", 6); +console.log(describe(status), status.value); +console.log(describe(p), p.norm()); +console.log(describe(q), q.norm()); +console.log(Object.isFrozen(p), Object.isExtensible(q), Object.isSealed(status)); + +// Statically typed freeze, seal and preventExtensions of instances. +const frozen = new Point(5, 6); +Object.freeze(frozen); +attempt("frozen field write", () => { + /** @type {Point} */ (frozen).x = 50; +}); +attempt("frozen field write untyped", () => put(frozen, "y", 60)); +attempt("frozen addition", () => put(frozen, "extra", 1)); +console.log(describe(frozen), Object.isFrozen(frozen), Object.isSealed(frozen), Object.isExtensible(frozen)); +console.log(JSON.stringify(Object.getOwnPropertyDescriptor(frozen, "x"))); + +const sealed = new Point3(1, 2, 3); +put(sealed, "before", true); +Object.seal(sealed); +attempt("sealed field write", () => { + sealed.z = 30; +}); +attempt("sealed added write", () => put(sealed, "before", false)); +attempt("sealed addition", () => put(sealed, "after", 1)); +console.log(describe(sealed), Object.isFrozen(sealed), Object.isSealed(sealed), Object.isExtensible(sealed)); +console.log(JSON.stringify(Object.getOwnPropertyDescriptor(sealed, "z"))); + +const closed = new Status(); +Object.preventExtensions(closed); +closed.dirty(); +attempt("closed addition", () => put(closed, "more", 1)); +console.log(describe(closed), Object.isFrozen(closed), Object.isExtensible(closed)); + +// A subclass instance frozen through its base type. +/** @type {Point} */ +const asBase = new Point3(7, 8, 9); +Object.freeze(asBase); +attempt("frozen subclass field", () => put(asBase, "z", 90)); +console.log(describe(asBase), asBase.norm(), Object.isFrozen(asBase)); + +// Fresh instances of the same classes stay writable. +const fresh = new Point3(0, 0, 0); +fresh.x = 1; +put(fresh, "y", 2); +put(fresh, "added", 3); +console.log(describe(fresh), fresh.norm(), Object.isFrozen(fresh)); diff --git a/tests/corpus/7791-js-class-accessor-layouts.js b/tests/corpus/7791-js-class-accessor-layouts.js new file mode 100644 index 000000000..6c87989cb --- /dev/null +++ b/tests/corpus/7791-js-class-accessor-layouts.js @@ -0,0 +1,85 @@ +"use strict"; +// Getters and setters on JavaScript classes. An accessor whose name no +// field uses lives on the prototype beside the fields; an accessor that +// shares a field's name decides what an assignment does. Both behave as in +// Node, for direct use and for instances passed to untyped code. + +class LazyPath { + constructor(parent, value, path, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (Array.isArray(this._key)) this._cachedPath.push(...this._path, ...this._key); + else this._cachedPath.push(...this._path, this._key); + } + return this._cachedPath; + } +} + +class Temperature { + constructor(celsius) { + this.celsius = celsius; + } + get fahrenheit() { + return this.celsius * 1.8 + 32; + } + set fahrenheit(value) { + this.celsius = (value - 32) / 1.8; + } +} + +class Labeled extends Temperature { + constructor(celsius, label) { + super(celsius); + this.label = label; + } + get description() { + return `${this.label}: ${this.celsius.toFixed(1)}C`; + } +} + +/** @param {any} target @param {string} key @param {unknown} value */ +function put(target, key, value) { + target[key] = value; +} + +/** @param {any} value */ +function describe(value) { + const parts = []; + for (const key in value) parts.push(key); + return `${Object.keys(value).join(",")} | ${parts.join(",")} | ${JSON.stringify(value)}`; +} + +/** @param {any} value @param {string} key */ +function probe(value, key) { + return `${key}: in=${key in value} own=${Object.prototype.hasOwnProperty.call(value, key)} value=${JSON.stringify(value[key])}`; +} + +const root = { common: true }; +const paths = []; +for (let i = 0; i < 3; i++) paths.push(new LazyPath(root, { n: i }, ["items"], i)); +paths.push(new LazyPath(root, null, ["a"], ["b", "c"])); +for (const item of paths) console.log(item.path.join("."), item.data && item.data.n, item.parent === root); +console.log(describe(paths[0])); +console.log(probe(paths[1], "path"), probe(paths[1], "_key")); +put(paths[2], "extra", 1); +console.log(describe(paths[2]), paths[2].path.length); + +const t = new Temperature(100); +console.log(t.fahrenheit, describe(t)); +t.fahrenheit = 32; +console.log(t.celsius, t.fahrenheit); +put(t, "fahrenheit", 212); +console.log(t.celsius, probe(t, "fahrenheit"), probe(t, "celsius")); +put(t, "note", "added"); +console.log(describe(t)); + +const l = new Labeled(20, "room"); +console.log(l.description, l.fahrenheit, describe(l)); +l.fahrenheit = 50; +console.log(l.description, probe(l, "description")); diff --git a/tests/corpus/7792-untyped-property-reads-mixed-receivers.js b/tests/corpus/7792-untyped-property-reads-mixed-receivers.js new file mode 100644 index 000000000..6a2785541 --- /dev/null +++ b/tests/corpus/7792-untyped-property-reads-mixed-receivers.js @@ -0,0 +1,111 @@ +"use strict"; +// One untyped property read site meets class instances (fixed and tracked +// layouts, getters, own properties added later), plain objects, arrays, +// strings, functions, class constructors with statics, null prototypes and +// optional chains. Each receiver kind answers as in Node. + +class Status { + constructor() { + this.value = "valid"; + } +} + +class Lazy { + constructor(key) { + this.key = key; + } + get value() { + return `lazy:${this.key}`; + } + get length() { + return 7; + } +} + +class Tracked { + constructor(list) { + list.push(this); + this.value = list.length; + } + static value = "static"; +} + +function plainFunction() {} +plainFunction.value = "fn"; + +/** @param {any} target */ +function readValue(target) { + return target.value; +} +/** @param {any} target */ +function readLength(target) { + return target.length; +} +/** @param {any} target */ +function readOptional(target) { + return target?.value; +} +/** @param {any} target @param {unknown} value */ +function writeValue(target, value) { + target.value = value; +} + +const list = []; +const added = new Status(); +writeValue(added, "dirty"); +/** @type {any} */ +const extra = new Lazy("x"); +extra.other = 1; +const nullProto = Object.create(null); +nullProto.value = "bare"; +const inherited = Object.create({ value: "from prototype" }); +const receivers = [ + new Status(), + added, + new Lazy("k"), + extra, + new Tracked(list), + { value: 1, length: 2 }, + { other: true }, + [1, 2, 3], + "text", + 42, + true, + plainFunction, + Tracked, + nullProto, + inherited, +]; +for (const receiver of receivers) { + console.log(typeof receiver, JSON.stringify(readValue(receiver)), JSON.stringify(readLength(receiver)), JSON.stringify(readOptional(receiver))); +} +console.log(readOptional(null), readOptional(undefined)); +for (const bad of [null, undefined]) { + try { + readValue(bad); + } catch (e) { + console.log(e instanceof TypeError, e.message); + } +} + +const plain = { value: 0 }; +const arr = []; +writeValue(plain, 5); +writeValue(arr, "on array"); +writeValue(inherited, "own now"); +writeValue(plainFunction, "fn2"); +const status = new Status(); +writeValue(status, "aborted"); +console.log(plain.value, arr.value, Object.keys(inherited), plainFunction.value, status.value, JSON.stringify(status)); +try { + writeValue("text", 1); +} catch (e) { + console.log(e instanceof TypeError, e.message); +} +const frozen = Object.freeze({ value: "frozen" }); +try { + writeValue(frozen, 2); +} catch (e) { + console.log(e instanceof TypeError); +} +console.log(frozen.value); diff --git a/tests/corpus/7793-untyped-key-writes.js b/tests/corpus/7793-untyped-key-writes.js new file mode 100644 index 000000000..93158145e --- /dev/null +++ b/tests/corpus/7793-untyped-key-writes.js @@ -0,0 +1,83 @@ +"use strict"; +// Untyped property writes through one site: new keys append in order, +// existing keys keep their position, own and inherited setters run, +// getter-only and read-only properties refuse the write, and +// non-extensible objects refuse new keys, as in Node. + +/** @param {any} target @param {string} key @param {unknown} value */ +function put(target, key, value) { + target[key] = value; +} + +/** @param {() => void} fn */ +function attempt(label, fn) { + try { + fn(); + console.log(label, "ok"); + } catch (e) { + console.log(label, e instanceof TypeError ? "TypeError" : String(e)); + } +} + +const order = {}; +for (const key of ["b", "a", "c", "a", "b", "d"]) put(order, key, key.charCodeAt(0)); +console.log(JSON.stringify(order), Object.keys(order).join("")); + +const log = []; +const withSetter = { + _v: 0, + get v() { + return this._v; + }, + set v(value) { + log.push(`own ${value}`); + this._v = value * 2; + }, + get onlyGetter() { + return "g"; + }, +}; +put(withSetter, "v", 4); +console.log(withSetter.v, JSON.stringify(log)); +attempt("getter-only", () => put(withSetter, "onlyGetter", 1)); + +const proto = { + /** @param {number} value */ + set inherited(value) { + log.push(`inherited ${value} on ${this === child}`); + }, +}; +Object.defineProperty(proto, "fixed", { value: 1, writable: false }); +const child = Object.create(proto); +put(child, "inherited", 7); +attempt("inherited read-only", () => put(child, "fixed", 2)); +put(child, "own", 3); +console.log(JSON.stringify(log), Object.keys(child).join(","), child.fixed); + +/** @type {any} */ +const sealed = { x: 1 }; +Object.seal(sealed); +put(sealed, "x", 2); +attempt("sealed add", () => put(sealed, "y", 3)); +/** @type {any} */ +const closed = { x: 1 }; +Object.preventExtensions(closed); +put(closed, "x", 5); +attempt("closed add", () => put(closed, "z", 1)); +const frozen = Object.freeze({ x: 1 }); +attempt("frozen write", () => put(frozen, "x", 9)); +console.log(JSON.stringify(sealed), JSON.stringify(closed), JSON.stringify(frozen)); + +const readOnly = {}; +Object.defineProperty(readOnly, "k", { value: "kept", writable: false, enumerable: true }); +attempt("own read-only", () => put(readOnly, "k", "changed")); +console.log(readOnly.k); + +const error = new Error("boom"); +put(error, "cause", "why"); +put(error, "code", "E_X"); +console.log(error.cause, error.code); + +const wide = {}; +for (let i = 0; i < 40; i++) put(wide, `k${i % 25}`, i); +console.log(Object.keys(wide).length, wide.k0, wide.k24, Object.keys(wide).slice(0, 3).join(",")); diff --git a/tests/corpus/7794-untyped-array-iteration.js b/tests/corpus/7794-untyped-array-iteration.js new file mode 100644 index 000000000..ba7fe0810 --- /dev/null +++ b/tests/corpus/7794-untyped-array-iteration.js @@ -0,0 +1,86 @@ +"use strict"; +// for...of, spread and destructuring over untyped arrays: elements are read +// live, so pushes and truncation during the loop show, holes read as +// undefined, an own Symbol.iterator replaces the builtin one, and keys() +// and entries() keep their own shapes, as in Node. + +/** @param {any} list */ +function collect(list) { + const out = []; + for (const item of list) out.push(item); + return out; +} + +/** @param {any} list */ +function growWhileIterating(list) { + const seen = []; + for (const item of list) { + seen.push(item); + if (item === 2) list.push(99); + if (seen.length > 10) break; + } + return seen; +} + +/** @param {any} list */ +function shrinkWhileIterating(list) { + const seen = []; + for (const item of list) { + seen.push(item); + list.length = 2; + } + return seen; +} + +const parsed = JSON.parse('[1, "two", {"three": 3}, [4], null, true]'); +console.log(JSON.stringify(collect(parsed))); +console.log(JSON.stringify(collect([]))); +console.log(JSON.stringify(growWhileIterating(JSON.parse("[1, 2, 3]")))); +console.log(JSON.stringify(shrinkWhileIterating(JSON.parse("[1, 2, 3, 4, 5]")))); + +/** @type {any} */ +const holes = [1, , 3]; +holes.length = 5; +console.log(collect(holes).map((x) => String(x)).join(",")); + +/** @type {any} */ +const custom = JSON.parse("[1, 2, 3]"); +custom[Symbol.iterator] = function* () { + yield "custom"; + yield this.length; +}; +console.log(JSON.stringify(collect(custom))); + +/** @type {any} */ +const data = JSON.parse('[10, 20, 30]'); +console.log(JSON.stringify([...data]), JSON.stringify([...data.keys()]), JSON.stringify([...data.entries()])); +const [first, , third = "default", fourth = "missing"] = data; +console.log(first, third, fourth); + +let total = 0; +for (const value of data) { + if (value === 20) continue; + total += value; +} +console.log(total); + +/** @param {any} list */ +function nested(list) { + const pairs = []; + for (const a of list) for (const b of list) pairs.push(`${a}${b}`); + return pairs.join(" "); +} +console.log(nested(JSON.parse('["x", "y"]'))); + +/** @param {any} list */ +function earlyReturn(list) { + for (const item of list) if (item > 1) return item; + return -1; +} +console.log(earlyReturn(JSON.parse("[0, 1, 2, 3]")), earlyReturn(JSON.parse("[0]"))); + +try { + console.log(collect(null)); +} catch (e) { + console.log(e instanceof TypeError, e.message); +} diff --git a/tests/corpus/7795-untyped-global-constructors.js b/tests/corpus/7795-untyped-global-constructors.js new file mode 100644 index 000000000..0b464b7b2 --- /dev/null +++ b/tests/corpus/7795-untyped-global-constructors.js @@ -0,0 +1,29 @@ +"use strict"; +// Global constructors read as values in untyped code: instanceof tests, +// identity comparisons and constructor names, as zod's type sniffing uses +// them, answer as in Node for every global the lookup knows. + +/** @param {unknown} data */ +function kind(data) { + if (typeof Map !== "undefined" && data instanceof Map) return "map"; + if (typeof Set !== "undefined" && data instanceof Set) return "set"; + if (typeof Date !== "undefined" && data instanceof Date) return "date"; + if (data instanceof TypeError) return "type-error"; + if (data instanceof RangeError) return "range-error"; + if (data instanceof Error) return "error"; + if (data instanceof Promise) return "promise"; + if (Array.isArray(data)) return "array"; + return typeof data; +} + +const values = [new Map(), new Set([1]), new Date(0), new TypeError("t"), new RangeError("r"), new SyntaxError("s"), new Error("e"), Promise.resolve(1), [1], { a: 1 }, "s", 1, null]; +for (const value of values) console.log(kind(value)); + +const ctors = [Map, Set, Date, Error, TypeError, RangeError, SyntaxError, ReferenceError, EvalError, URIError, Object, Promise, WeakMap, WeakSet, RegExp]; +console.log(ctors.map((ctor) => ctor.name).join(",")); +console.log(ctors.every((ctor, i) => ctors.indexOf(ctor) === i)); +const again = [Map, Set, Date, Error, TypeError]; +console.log(again.every((ctor, i) => ctor === ctors[i])); +/** @type {any} */ +const err = new TypeError("x"); +console.log(err.constructor === TypeError, err.constructor === Error, err instanceof Error); diff --git a/tests/corpus/7796-boxed-subclass-identity.js b/tests/corpus/7796-boxed-subclass-identity.js new file mode 100644 index 000000000..12ed8b3c5 --- /dev/null +++ b/tests/corpus/7796-boxed-subclass-identity.js @@ -0,0 +1,106 @@ +"use strict"; +// Class instances of a hierarchy cross into untyped code through their +// base type many times. Each crossing names the same object: identity, +// Map, Set and WeakMap keys, properties added through untyped code, and +// field writes from either side stay one object, and every subclass keeps +// its own members, as in Node. + +class Shape { + constructor(name) { + this.name = name; + this.hits = 0; + } + area() { + return 0; + } + describe() { + return `${this.name}:${this.area()}`; + } +} + +class Square extends Shape { + constructor(side) { + super("square"); + this.side = side; + } + area() { + return this.side * this.side; + } +} + +class Cube extends Square { + constructor(side) { + super(side); + this.name = "cube"; + } + area() { + return 6 * super.area(); + } + get volume() { + return this.side ** 3; + } +} + +class Circle extends Shape { + constructor(r) { + super("circle"); + this.r = r; + } + area() { + return Math.round(Math.PI * this.r * this.r); + } +} + +/** @param {any} value */ +function untyped(value) { + return value; +} +/** @param {any} value @param {string} key @param {unknown} extra */ +function tag(value, key, extra) { + value[key] = extra; + value.hits++; + return value; +} + +/** @param {Shape} shape */ +function crossAsBase(shape) { + return untyped(shape); +} + +const shapes = [new Shape("plain"), new Square(2), new Cube(3), new Circle(1)]; +const seen = new Map(); +const weak = new WeakMap(); +const set = new Set(); +for (let round = 0; round < 50; round++) { + for (const shape of shapes) { + const boxed = crossAsBase(shape); + if (round === 0) { + seen.set(boxed, shape.name); + weak.set(boxed, round); + } + set.add(boxed); + if (boxed !== shape || untyped(boxed) !== crossAsBase(shape)) console.log("identity lost", shape.name); + } +} +console.log(seen.size, set.size, shapes.map((s) => seen.get(crossAsBase(s))).join(","), shapes.every((s) => weak.has(untyped(s)))); + +for (const shape of shapes) { + const boxed = crossAsBase(shape); + tag(boxed, "color", shape.name.length); + shape.hits += 10; + console.log(boxed.describe(), boxed.area(), boxed.hits, shape.hits, boxed.color, Object.keys(boxed).join(",")); + console.log(boxed instanceof Shape, boxed instanceof Square, boxed instanceof Cube, boxed instanceof Circle, boxed.constructor.name); +} + +/** @type {Shape} */ +const asBase = shapes[2]; +const cube = crossAsBase(asBase); +console.log(cube.volume, JSON.stringify(cube), "side" in cube, "volume" in cube, Object.prototype.hasOwnProperty.call(cube, "volume")); +/** @type {any} */ (shapes[1]).side = 5; +console.log(crossAsBase(shapes[1]).area(), shapes[1].area(), untyped(shapes[1]).side); + +const many = []; +for (let i = 0; i < 1000; i++) many.push(crossAsBase(i % 2 ? new Square(i) : new Circle(i))); +let squares = 0; +for (const item of many) if (item instanceof Square) squares += item.side; +console.log(many.length, squares, many[999].describe(), many[998].describe()); diff --git a/tests/corpus/7940-typed-array-prototype-set.mjs b/tests/corpus/7940-typed-array-prototype-set.mjs new file mode 100644 index 000000000..4f1b2301d --- /dev/null +++ b/tests/corpus/7940-typed-array-prototype-set.mjs @@ -0,0 +1,40 @@ +// Uint8Array.prototype.set borrowed through .call on untyped receivers. +let cached = null; +function memory() { + if (cached === null) cached = new Uint8Array(4); + return cached; +} +function copy(target, source, offset) { + return Uint8Array.prototype.set.call(target, source, offset); +} +console.log(copy(memory(), new Uint8Array([3, 4]), 1)); +console.log(String(memory())); +copy(memory(), [9], undefined); +console.log(String(memory())); + +// Every typed array shares %TypedArray%.prototype.set. +const wide = new Int16Array(3); +copy(wide, [1, -2], 1); +console.log(String(wide)); +Float64Array.prototype.set.call(memory(), new Uint8Array([7]), 3); +console.log(String(memory())); + +const order = []; +const arg = (label, value) => { + order.push(label); + return value; +}; +for (const target of [{}, [0, 0], "ab", null, new DataView(new ArrayBuffer(2))]) { + order.length = 0; + try { + Uint8Array.prototype.set.call(arg("target", target), arg("source", [1]), arg("offset", 0)); + console.log("set"); + } catch (e) { + console.log(e instanceof TypeError, e.message, order.join(",")); + } +} +try { + copy(memory(), [1, 2], 3); +} catch (e) { + console.log(e instanceof RangeError, e.message); +} diff --git a/tests/corpus/7941-typed-array-prototype-set.ts b/tests/corpus/7941-typed-array-prototype-set.ts new file mode 100644 index 000000000..db880e38a --- /dev/null +++ b/tests/corpus/7941-typed-array-prototype-set.ts @@ -0,0 +1,21 @@ +// Uint8Array.prototype.set borrowed through .call on typed receivers. +const target = new Uint8Array(4); +Uint8Array.prototype.set.call(target, new Uint8Array([3, 4]), 1); +console.log(String(target)); +Uint8Array.prototype.set.call(target, [5]); +console.log(String(target)); + +const wide = new Int32Array(3); +Uint8Array.prototype.set.call(wide, new Uint8Array([200, 100]), 1); +console.log(String(wide)); + +function fill(view: Uint8Array, bytes: number[], at: number): void { + Uint8Array.prototype.set.call(view, bytes, at); +} +fill(target, [8, 9], 2); +console.log(String(target)); +try { + fill(target, [1, 2, 3], 2); +} catch (e) { + console.log(e instanceof RangeError, (e as Error).message); +} diff --git a/tests/corpus/7942-dictionary-store-method-calls.ts b/tests/corpus/7942-dictionary-store-method-calls.ts new file mode 100644 index 000000000..0056c26f6 --- /dev/null +++ b/tests/corpus/7942-dictionary-store-method-calls.ts @@ -0,0 +1,45 @@ +// Stores into dictionaries whose values cannot be functions leave class +// methods in place. +interface Data { + items: string[]; +} +class Store { + data: Data = { items: [] }; + get(): Data { + return this.data; + } +} +class Counter { + n = 0; + add(): number { + return ++this.n; + } +} +interface Settings { + groups: Record>; +} +interface Tree { + [key: string]: Tree; +} +function addGroup(settings: Settings, name: string): void { + settings.groups[name] = {}; +} +function grow(tree: Tree, name: string): Tree { + tree[name] = {}; + return tree[name]!; +} +const settings: Settings = { groups: {} }; +addGroup(settings, "colours"); +addGroup(settings, "fonts"); +settings.groups["colours"]!["fg"] = "red"; +console.log(Object.keys(settings.groups).join(","), JSON.stringify(settings.groups)); +const tree: Tree = {}; +grow(grow(tree, "a"), "b"); +console.log(JSON.stringify(tree)); +const store = new Store(); +store.get().items.push("a"); +store.get().items.push("b"); +const counter = new Counter(); +let total = 0; +for (let i = 0; i < 1000; i++) total += counter.add(); +console.log(store.data.items.length, total, typeof counter.add); diff --git a/tests/corpus/7981-typed-array-paired-loops.ts b/tests/corpus/7981-typed-array-paired-loops.ts new file mode 100644 index 000000000..d13eecbd6 --- /dev/null +++ b/tests/corpus/7981-typed-array-paired-loops.ts @@ -0,0 +1,57 @@ +// Two-counter loops over typed arrays (`for (let i = lo, j = hi; i < j; +// i++, j--)`), with starts inside the arrays and outside them: past the +// end, negative, fractional and NaN. Views over one buffer, other element +// types, inclusive conditions and steps other than one. + +function reverse(a: Int32Array, lo: number, hi: number): string { + for (let i = lo, j = hi; i < j; i++, j--) { + const t = a[i]!; + a[i] = a[j]!; + a[j] = t; + } + return Array.from(a).join(); +} + +function mirror(a: Float64Array, b: Float64Array, hi: number): number { + let sum = 0; + for (let i = 0, j = hi; i <= j; i++, j--) { + b[i] = a[j]! * 2; + sum += a[i]! - b[j]!; + } + return sum; +} + +function skipping(a: Uint8ClampedArray, hi: number): string { + for (let i = 0, j = hi; i < j; i += 2, j -= 3) { + a[i] = a[j]! + 200; + a[j] = i - 10; + } + return Array.from(a).join(); +} + +function palindrome(text: Uint16Array, n: number): boolean { + for (let i = 0, j = n - 1; i < j; i++, j--) if (text[i] !== text[j]) return false; + return true; +} + +console.log(reverse(new Int32Array([1, 2, 3, 4, 5, 6]), 0, 5)); +console.log(reverse(new Int32Array([1, 2, 3, 4, 5, 6]), 1, 3)); +console.log(reverse(new Int32Array([1, 2, 3, 4]), 0, 6)); +console.log(reverse(new Int32Array([1, 2, 3, 4]), -2, 2)); +console.log(reverse(new Int32Array([1, 2, 3, 4]), 0.5, 3.5)); +console.log(reverse(new Int32Array([1, 2, 3, 4]), 0, NaN)); +console.log(reverse(new Int32Array([1, 2, 3, 4]), 3, 0)); + +const buffer = new ArrayBuffer(48); +const whole = new Float64Array(buffer); +for (let k = 0; k < whole.length; k++) whole[k] = k + 0.5; +const tail = new Float64Array(buffer, 16, 3); +console.log(mirror(whole, tail, 4), Array.from(whole).join()); +console.log(mirror(tail, tail, 2), Array.from(whole).join()); +console.log(mirror(tail, whole, 7), Array.from(whole).join()); + +console.log(skipping(new Uint8ClampedArray([10, 20, 30, 40, 50, 60, 70, 80]), 7)); +console.log(skipping(new Uint8ClampedArray([10, 20, 30]), 9)); + +const word = new Uint16Array([114, 97, 99, 101, 99, 97, 114]); +console.log(palindrome(word, 7), palindrome(word, 6), palindrome(word, 9), palindrome(word, 0)); diff --git a/tests/corpus/7982-integer-view-compares.ts b/tests/corpus/7982-integer-view-compares.ts new file mode 100644 index 000000000..5978eaf86 --- /dev/null +++ b/tests/corpus/7982-integer-view-compares.ts @@ -0,0 +1,85 @@ +// Loops whose integer locals take part in compares and typed-array indexes as +// `i + 1`, `i - 1` and `2 * i + 1`: heap sift-downs, neighbor reads at both +// ends of an array, a binary search, and counters near 2^53, where the sum +// of a counter and a constant is still an exact double. + +function siftDown(a: Int32Array, start: number, end: number): void { + let root = start; + while (2 * root + 1 < end) { + let child = 2 * root + 1; + if (child + 1 < end && a[child]! < a[child + 1]!) child++; + if (a[root]! >= a[child]!) return; + const t = a[root]!; + a[root] = a[child]!; + a[child] = t; + root = child; + } +} + +function heapSort(a: Int32Array): string { + for (let start = (a.length >> 1) - 1; start >= 0; start--) siftDown(a, start, a.length); + for (let end = a.length - 1; end > 0; end--) { + const t = a[0]!; + a[0] = a[end]!; + a[end] = t; + siftDown(a, 0, end); + } + return Array.from(a).join(); +} + +function neighbors(a: Float64Array): string { + const out: number[] = []; + let i = 0; + while (i < a.length) { + out.push(a[i - 1]! + a[i + 1]!); + i = i + 1; + } + return out.join(); +} + +function lowerBound(a: Int32Array, value: number): number { + let lo = 0; + let hi = a.length; + while (lo < hi) { + const mid = lo + ((hi - lo) >> 1); + if (a[mid]! < value) lo = mid + 1; + else hi = mid; + } + return lo; +} + +function nearLimit(start: number): string { + const seen: string[] = []; + let i = start; + let n = 0; + while (n < 4) { + seen.push(`${i + 1 > 9007199254740991}:${i + 1 === i + 2}:${i - 1 < i}`); + i = i + 1; + n = n + 1; + } + return seen.join(" "); +} + +function atLimit(start: number, end: number): string { + // A counter of integer locals only, up to 2^53 - 1: `i + 2` may be + // 2^53 + 1, whose double is 2^53, so `i + 1 === i + 2` holds at the end. + let same = 0; + let above = 0; + let mixed = 0; + for (let i = start; i <= end; i++) { + if (i + 1 === i + 2) same++; + if (i + 2 > 9007199254740992) above++; + mixed += (i * 2) % 3; + } + return `${same} ${above} ${mixed}`; +} + +console.log(heapSort(new Int32Array([5, -3, 9, 0, 9, 2147483647, -2147483648, 7, 1]))); +console.log(heapSort(new Int32Array([])), heapSort(new Int32Array([4]))); +console.log(neighbors(new Float64Array([1, 2, 3, 4]))); +const sorted = new Int32Array([-5, -1, 0, 0, 3, 8, 8, 13]); +console.log([-6, -1, 0, 1, 8, 13, 14].map((v) => lowerBound(sorted, v)).join()); +console.log(nearLimit(9007199254740988)); +console.log(nearLimit(-9007199254740991)); +console.log(atLimit(9007199254740988, 9007199254740991)); +console.log(atLimit(-9007199254740991, -9007199254740988)); diff --git a/tests/corpus/7991-unknown-class-union-casts.ts b/tests/corpus/7991-unknown-class-union-casts.ts new file mode 100644 index 000000000..6560abbc2 --- /dev/null +++ b/tests/corpus/7991-unknown-class-union-casts.ts @@ -0,0 +1,70 @@ +// Checked casts from `unknown` to unions of classes: each instance selects +// its own arm, whatever the order of the arms, including subclasses, arrays +// of instances, instances inside records, and values of no listed class. +class Circle { + readonly r: number; + constructor(r: number) { + this.r = r; + } + area(): number { + return Math.round(Math.PI * this.r * this.r); + } +} +class Square { + readonly side: number; + constructor(side: number) { + this.side = side; + } + area(): number { + return this.side * this.side; + } +} +class Rect { + readonly w: number; + readonly h: number; + constructor(w: number, h: number) { + this.w = w; + this.h = h; + } + area(): number { + return this.w * this.h; + } +} +class Tile extends Square { + label(): string { + return `tile ${this.side}`; + } +} + +type Shape = Circle | Square | Rect; + +function describe(value: unknown): string { + try { + const shape = value as Shape; + const kind = shape instanceof Circle ? "circle" : shape instanceof Rect ? "rect" : "square"; + return `${kind} ${shape.area()}`; + } catch (error) { + return `rejected: ${(error as Error).name}`; + } +} + +const values: unknown[] = [new Circle(2), new Square(3), new Rect(2, 5), new Tile(4), { side: 3 }, 7, null]; +for (const value of values) console.log(describe(value)); + +const reversed = values.slice(0, 4).reverse() as Shape[]; +console.log(reversed.map((s) => s.area()).join(",")); + +const boxed: unknown = { first: new Rect(1, 2), second: new Circle(1) }; +const pair = boxed as { first: Shape; second: Shape }; +console.log(pair.first.area(), pair.second.area(), pair.first instanceof Rect); + +const tiles: unknown = [new Tile(1), new Tile(2)]; +const asSquares = tiles as Square[]; +console.log(asSquares.map((t) => t.area()).join(" "), asSquares[0] instanceof Tile); + +let total = 0; +for (let i = 0; i < 1000; i++) { + const v: unknown = i % 3 === 0 ? new Circle(1) : i % 3 === 1 ? new Square(2) : new Rect(1, 3); + total += (v as Shape).area(); +} +console.log(total); diff --git a/tests/corpus/8100-typed-array-prototype-set-data-view.ts b/tests/corpus/8100-typed-array-prototype-set-data-view.ts new file mode 100644 index 000000000..23f3824cd --- /dev/null +++ b/tests/corpus/8100-typed-array-prototype-set-data-view.ts @@ -0,0 +1,30 @@ +// A borrowed TypedArray.prototype.set over a DataView target throws Node's +// TypeError after evaluating its arguments, and leaves the view unchanged. +const view = new DataView(new ArrayBuffer(4)); +const order: string[] = []; +function arg(label: string, value: T): T { + order.push(label); + return value; +} +try { + Uint8Array.prototype.set.call(arg("target", view), arg("source", [1, 2]), arg("offset", 1)); + console.log("set"); +} catch (e) { + console.log(e instanceof TypeError, (e as Error).message, order.join(",")); +} +console.log(view.getUint8(1), view.getUint8(2)); + +function fill(target: DataView | Uint8Array, bytes: number[]): string { + try { + Uint8Array.prototype.set.call(target, bytes); + return "set"; + } catch (e) { + return (e as Error).message; + } +} +console.log(fill(view, [7]), view.getUint8(0)); +const bytes = new Uint8Array(2); +console.log(fill(bytes, [7]), bytes[0]); +const buffer = Buffer.alloc(3); +Uint8Array.prototype.set.call(buffer, [4, 5], 1); +console.log(buffer.toString("hex")); diff --git a/tests/corpus/8101-url-host-and-path-parsing.js b/tests/corpus/8101-url-host-and-path-parsing.js new file mode 100644 index 000000000..6b04dd9ef --- /dev/null +++ b/tests/corpus/8101-url-host-and-path-parsing.js @@ -0,0 +1,103 @@ +// WHATWG URL host and path parsing: special hosts are percent-decoded and +// checked for forbidden domain code points (and read as IPv4 when they end +// in a number), opaque hosts and paths percent-encode the C0 control set, +// file: URLs take no port or credentials, empty hosts refuse credentials and +// ports, and host-less paths that start with "//" round-trip. Cases from the +// WPT url test data. +const parses = [ + ["file://example:1/", null], + ["non-special:opaque ?hi", null], + ["non-special:opaque #hi", null], + ["non-special:opaque \t\t \t#hi", null], + ["non-special:opaque \t\t #hi", null], + ["non-special:opaque\t\t \r #hi", null], + ["sc://faß.ExAmPlE/", null], + ["http://%30%78%63%30%2e%30%32%35%30.01", "http://other.com/"], + ["http://%30%78%63%30%2e%30%32%35%30.01%2e", "http://other.com/"], + ["sc://ñ.test/", null], + ["sc://%/", null], + ["sc://@/", null], + ["sc://te@s:t@/", null], + ["sc://:/", null], + ["sc://:12/", null], + ["x", "sc://ñ"], + ["wow:\uFFFF", null], + ["http://a\u007Fb/", null], + ["sc://\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\u000b\f\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\u007F!\"$%&'()*+,-.;=_`{}~/", null], + ["asdf://%43%7C/", null], + ["C|", "file://host/dir/file"], + ["C|", "file://host/D:/dir1/dir2/file"], + ["C|#", "file://host/dir/file"], + ["C|?", "file://host/dir/file"], + ["C|/", "file://host/dir/file"], + ["C|\n/", "file://host/dir/file"], + ["C|\\", "file://host/dir/file"], + ["C|/", "file://host/"], + ["file:C:/", "file://host/"], + ["sc://ñ", null], + ["sc://ñ?x", null], + ["sc://ñ#x", null], + ["#x", "sc://ñ"], + ["?x", "sc://ñ"], + ["", "non-spec:/..//p"], + ["path", "non-spec:/..//p"], + ["../path", "non-spec:/.//p"], + ["non-special://%E2%80%A0/", null], + ["non-special://H%4fSt/path", null], + ["non-special:cannot-be-a-base-url-\u0000\u0001\u001f\u001e~\u007F\u0080", null], + ["foo://!\"$%&'()*+,-.;=_`{}~/", null], + ["foo://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", null], + ["wss://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", null], + ["non-special:\u0000y", null], + ["non-special:x/\u0000y", null], + ["non-special:\uFFFFy", null], + ["non-special:x/\uFFFFy", null], + ["data://:443", null], + ["javascript://:443", null], + ["mailto://:443", null], + ["intent://:443", null], + ["urn://:443", null], + ["turn://:443", null], + ["stun://:443", null], +]; +function describe(url) { + return [url.href, url.protocol, url.username, url.password, url.host, url.hostname, url.port, url.pathname, url.search, url.hash].join(" | "); +} +for (const [input, base] of parses) { + let line; + try { + line = describe(base === null ? new URL(input) : new URL(input, base)); + } catch (e) { + line = `throws ${e.code}`; + } + console.log(JSON.stringify(input), JSON.stringify(base), "=>", line); +} + +const writes = [ + ["host", "sc://x/", "ß"], + ["host", "file://y/", "loc%41lhost"], + ["hostname", "file://y/", "loc%41lhost"], + ["pathname", "a:/", "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007F\u0080\u0081Éé"], + ["search", "data:space ?query", ""], + ["search", "sc:space ?query", ""], + ["search", "data:space ?query#fragment", ""], + ["search", "sc:space ?query#fragment", ""], + ["hash", "data:space #fragment", ""], + ["hash", "sc:space #fragment", ""], + ["hash", "data:space ?query#fragment", ""], + ["hash", "sc:space ?query#fragment", ""], +]; +for (const [name, href, value] of writes) { + const url = new URL(href); + if (name === "host") url.host = value; + else if (name === "hostname") url.hostname = value; + else if (name === "port") url.port = value; + else if (name === "pathname") url.pathname = value; + else if (name === "protocol") url.protocol = value; + else if (name === "username") url.username = value; + else if (name === "password") url.password = value; + else if (name === "search") url.search = value; + else if (name === "hash") url.hash = value; + else if (name === "href") url.href = value; + console.log(name, JSON.stringify(href), JSON.stringify(value), "=>", describe(url)); +} diff --git a/tests/corpus/8102-url-path-to-file-url-unc-hosts.js b/tests/corpus/8102-url-path-to-file-url-unc-hosts.js new file mode 100644 index 000000000..a96d1252e --- /dev/null +++ b/tests/corpus/8102-url-path-to-file-url-unc-hosts.js @@ -0,0 +1,36 @@ +// pathToFileURL(path, { windows: true }) parses a UNC servername with the URL +// host parser after dropping tabs and newlines and stopping at '#' and '?': +// forbidden host code points throw ERR_INVALID_URL, and the +// name is lowercased, percent-decoded and read as IPv4 when it ends in a +// number. "~" in a path is percent-encoded. +import url from "node:url"; +const paths = [ + "\\\\exa mple\\share\\file.txt", + "\\\\host name\\share\\file.txt", + "\\\\host@name\\share\\file.txt", + "\\\\host:name\\share\\file.txt", + "\\\\host[name\\share\\file.txt", + "\\\\host]name\\share\\file.txt", + "\\\\h Object.prototype.hasOwnProperty.call(object, key); + +const objectKeys = ["constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toString", "toLocaleString", "valueOf", "__proto__", "__defineGetter__", "__lookupSetter__", "missing", "length"]; +for (const key of objectKeys) console.log("Object.prototype", key, has(Object.prototype, key), Object.hasOwn(Object.prototype, key)); +console.log(Object.getOwnPropertyNames(Object.prototype).join(",")); +console.log(Object.prototype.hasOwnProperty("isPrototypeOf"), Object.prototype.hasOwnProperty("nope")); + +const arrayKeys = ["constructor", "push", "map", "length", "at", "hasOwnProperty", "isPrototypeOf", "missing"]; +for (const key of arrayKeys) console.log("Array.prototype", key, has(Array.prototype, key)); +const arrayNames = Object.getOwnPropertyNames(Array.prototype); +console.log(arrayNames.length, arrayNames.includes("constructor"), arrayNames.includes("length"), arrayNames.includes("hasOwnProperty")); + +/** @param {any} value */ +function links(label, value) { + const ctor = value.constructor; + const prototype = ctor.prototype; + console.log(label, typeof ctor, ctor.name, typeof prototype, prototype === Object.getPrototypeOf(value), has(prototype, "constructor"), has(prototype, "isPrototypeOf")); +} +links("object", {}); +links("object-with-keys", { a: 1, b: [2] }); +links("parsed", JSON.parse('{"x":{"y":1}}')); +links("nested", JSON.parse('{"x":{"y":1}}').x); +links("array", []); +links("array-of", [1, 2, 3]); +const error = new Error("boom"); +console.log("error", typeof error.constructor, error.constructor.name, error.constructor.prototype === Error.prototype, has(error.constructor.prototype, "isPrototypeOf")); + +/** @param {any} value */ +const ctorOf = (value) => value.constructor; +console.log(ctorOf({}) === Object, ctorOf({}).prototype === Object.prototype, ctorOf([]) === Array, ctorOf([]).prototype === Array.prototype); +console.log(Object.prototype.constructor === Object, Object.prototype.constructor.prototype === Object.prototype); +console.log(Object.getPrototypeOf({}) === Object.prototype, has(Object.getPrototypeOf({ k: 1 }), "isPrototypeOf")); + +const O = ctorOf({}); +const descriptor = Object.getOwnPropertyDescriptor(O, "prototype"); +console.log(typeof O.prototype, O.prototype === Object.prototype, descriptor.writable, descriptor.enumerable, descriptor.configurable); +const A = ctorOf([]); +console.log(A.prototype === Array.prototype, Object.keys(A).length, A.name, A.length); +const E = ctorOf(error); +console.log(E.prototype === Error.prototype, E.prototype.name, E.name); + +// A check shaped like zod's isPlainObject: the constructor's prototype must +// be an object that has its own isPrototypeOf. +/** @param {unknown} o */ +function isPlainObject(o) { + if (typeof o !== "object" || o === null || Array.isArray(o)) return false; + const ctor = /** @type {any} */ (o).constructor; + if (ctor === undefined) return true; + if (typeof ctor !== "function") return true; + const prot = ctor.prototype; + if (typeof prot !== "object" || prot === null || Array.isArray(prot)) return false; + return Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf"); +} +class Point { + constructor() { + this.x = 1; + } +} +const samples = [{}, { a: 1 }, JSON.parse("{}"), Object.create(null), [], new Point(), new Error("e"), { constructor: 5 }, { constructor: undefined }, "s", 1, null]; +console.log(samples.map((sample) => isPlainObject(sample)).join(",")); + +// A constructor stored on Array.prototype stays one own name, after length. +function Replacement() {} +Array.prototype.constructor = Replacement; +const replacedNames = Object.getOwnPropertyNames(Array.prototype); +console.log(replacedNames.slice(0, 2).join(","), replacedNames.filter((name) => name === "constructor").length, has(Array.prototype, "constructor")); diff --git a/tests/corpus/8111-plain-object-check-unknown.ts b/tests/corpus/8111-plain-object-check-unknown.ts new file mode 100644 index 000000000..267f3d65e --- /dev/null +++ b/tests/corpus/8111-plain-object-check-unknown.ts @@ -0,0 +1,54 @@ +// A plain-object check over unknown values, the way schema libraries +// accept records: the value's constructor must be absent, not a function, +// or one whose prototype has its own isPrototypeOf. Values that crossed +// JSON.parse and object literals pass; arrays, class instances and errors +// fail. + +function isObject(data: unknown): data is Record { + return typeof data === "object" && data !== null && !Array.isArray(data); +} + +function isPlainObject(o: unknown): o is Record { + if (!isObject(o)) return false; + const ctor: unknown = o.constructor; + if (ctor === undefined) return true; + if (typeof ctor !== "function") return true; + const prot: unknown = (ctor as { prototype?: unknown }).prototype; + if (!isObject(prot)) return false; + if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) return false; + return true; +} + +class Point { + x = 1; +} + +const inputs: [string, unknown][] = [ + ["literal", { a: 1 }], + ["empty", {}], + ["parsed", JSON.parse('{"k":[1,2],"m":{"n":true}}')], + ["parsed-nested", (JSON.parse('{"m":{"n":true}}') as { m: unknown }).m], + ["null-proto", Object.create(null)], + ["array", [1, 2]], + ["instance", new Point()], + ["error", new Error("x")], + ["ctor-number", { constructor: 3 }], + ["string", "s"], + ["null", null], +]; + +for (const [label, value] of inputs) { + console.log(label, isPlainObject(value)); +} + +const records: Record = {}; +for (const [label, value] of inputs) { + if (!isPlainObject(value)) continue; + records[label] = Object.keys(value).length; +} +console.log(JSON.stringify(records)); + +const proto: unknown = Object.getPrototypeOf({}); +console.log(proto === Object.prototype, Object.prototype.hasOwnProperty.call(proto, "isPrototypeOf")); +console.log(Object.hasOwn(Object.prototype, "hasOwnProperty"), Object.hasOwn(Object.prototype, "nope")); +console.log(Object.getOwnPropertyNames(Object.prototype).length); diff --git a/tests/corpus/8112-error-factory-own-key-order.js b/tests/corpus/8112-error-factory-own-key-order.js new file mode 100644 index 000000000..3e7cf277a --- /dev/null +++ b/tests/corpus/8112-error-factory-own-key-order.js @@ -0,0 +1,73 @@ +"use strict"; +// Errors built by a constructor factory, the way zod 4 builds ZodError: a +// class extending the parent passed in, then an initializer that assigns +// `name`, defines non-enumerable fields and an enumerable `message` +// accessor, and a second initializer that reassigns `name`. JSON and +// Object.keys list `name` before `message`, as they were created, and +// `name` holds the last assignment. + +const messageDescriptor = { + get() { + return this.state.message ?? JSON.stringify(this.state.issues); + }, + set(value) { + this.state.message = value; + }, + enumerable: true, + configurable: true, +}; + +function factory(name, initializer, parent) { + const Parent = parent ?? Object; + class Definition extends Parent {} + Object.defineProperty(Definition, "name", { value: name }); + function construct(def) { + const instance = parent ? new Definition() : this; + Object.defineProperty(instance, "state", { value: { issues: def }, enumerable: false }); + initializer(instance, def); + return instance; + } + Object.defineProperty(construct, "init", { value: initializer }); + Object.defineProperty(construct, "name", { value: name }); + return construct; +} + +const baseInit = (instance, def) => { + instance.name = "$BaseError"; + Object.defineProperty(instance, "issues", { value: def, enumerable: false }); + Object.defineProperty(instance, "message", messageDescriptor); +}; +const BaseError = factory("$BaseError", baseInit, Error); +const ReportError = factory( + "ReportError", + (instance, def) => { + BaseError.init(instance, def); + instance.name = "ReportError"; + }, + Error, +); + +const report = new ReportError([{ code: "too_small", path: ["title"] }]); +console.log(report.name, report instanceof Error, Object.keys(report).join(",")); +console.log(JSON.stringify(report)); +console.log(JSON.stringify({ success: false, error: report })); +report.message = "replaced"; +console.log(JSON.stringify(report), report.message); +report.name = "Renamed"; +console.log(JSON.stringify(report), report.name); + +const base = new BaseError([1, 2]); +console.log(base.name, Object.keys(base).join(","), JSON.stringify(base)); + +// The same steps on a class extending Error directly. +function build(flag) { + class Direct extends Error {} + const instance = flag ? new Direct() : {}; + instance.name = "First"; + Object.defineProperty(instance, "message", messageDescriptor); + Object.defineProperty(instance, "state", { value: { issues: ["i"] }, enumerable: false }); + instance.name = "Second"; + return instance; +} +const direct = build(true); +console.log(Object.keys(direct).join(","), JSON.stringify(direct), direct.name); diff --git a/tests/corpus/8113-error-class-field-before-name.js b/tests/corpus/8113-error-class-field-before-name.js new file mode 100644 index 000000000..e10a9456b --- /dev/null +++ b/tests/corpus/8113-error-class-field-before-name.js @@ -0,0 +1,78 @@ +"use strict"; +// Error subclasses with a property bag that a later dynamic definition or +// write commits. Object.keys and JSON keep the order in which an instance +// created its properties: a class field before an own `name` that is +// assigned after construction, a `name` a constructor assigns before a +// later property, and a `message` that Error's constructor created before +// all of them. + +const messageDescriptor = { + get() { + return "described"; + }, + enumerable: true, + configurable: true, +}; + +// A factory-built class: the field, then an initializer that assigns `name` +// and defines `message`. +function factory(initializer) { + class Definition extends Error { + code = "E_FIELD"; + } + return function construct() { + const instance = new Definition(); + initializer(instance); + return instance; + }; +} +const Fielded = factory((instance) => { + instance.name = "Fielded"; + Object.defineProperty(instance, "message", messageDescriptor); +}); +const fielded = new Fielded(); +console.log(Object.keys(fielded).join(","), JSON.stringify(fielded)); +fielded.extra = 1; +console.log(Object.keys(fielded).join(","), JSON.stringify(fielded)); +fielded.name = "Renamed"; +console.log(Object.keys(fielded).join(","), JSON.stringify(fielded), fielded.name); + +// A class whose constructor passes a message to Error and then assigns +// `name`; the message is redefined after construction. +class Direct extends Error { + code = "E_DIRECT"; + constructor() { + super("constructed"); + this.name = "Direct"; + } +} +function redefine(instance) { + Object.defineProperty(instance, "message", messageDescriptor); + instance.extra = 2; + return instance; +} +const direct = redefine(new Direct()); +console.log(Object.keys(direct).join(","), JSON.stringify(direct), direct.name); + +// Constructors that assign `name` and then another property, which their +// instances create in that order, with and without a message passed to +// Error, before the message is redefined. +class Named extends Error { + constructor() { + super(); + this.name = "Named"; + this.detail = "named"; + } +} +const named = redefine(new Named()); +console.log(Object.keys(named).join(","), JSON.stringify(named), named.name); + +class NamedMessage extends Error { + constructor() { + super("constructed"); + this.name = "NamedMessage"; + this.detail = "named"; + } +} +const namedMessage = redefine(new NamedMessage()); +console.log(Object.keys(namedMessage).join(","), JSON.stringify(namedMessage), namedMessage.name); diff --git a/tests/corpus/8115-error-prototype-identity.js b/tests/corpus/8115-error-prototype-identity.js new file mode 100644 index 000000000..5c98e1209 --- /dev/null +++ b/tests/corpus/8115-error-prototype-identity.js @@ -0,0 +1,55 @@ +"use strict"; +// Object.getPrototypeOf of an Error is Error.prototype, whose own prototype +// is Object.prototype: with or without a message, after its name is +// reassigned, and when it was thrown and caught. + +const describe = (label, value) => { + const proto = Object.getPrototypeOf(value); + console.log(label, proto === Error.prototype, proto === Object.prototype, Object.getPrototypeOf(proto) === Object.prototype); +}; + +describe("message", new Error("boom")); +describe("empty", new Error()); +const renamed = new Error("x"); +renamed.name = "Custom"; +describe("renamed", renamed); +const coded = new Error("y"); +coded.code = "E_CODE"; +describe("coded", coded); +try { + throw new Error("thrown"); +} catch (error) { + describe("caught", error); +} + +// Walks up the chain, as error serializers and type sniffers do. +/** @param {unknown} value */ +function chainLength(value) { + let length = 0; + for (let current = Object.getPrototypeOf(value); current !== null; current = Object.getPrototypeOf(current)) length++; + return length; +} +console.log(chainLength(new Error("a")), chainLength({}), chainLength([])); + +/** @param {unknown} value */ +function kind(value) { + if (typeof value !== "object" || value === null) return typeof value; + const proto = Object.getPrototypeOf(value); + if (proto === Object.prototype || proto === null) return "plain"; + if (proto === Error.prototype) return "error"; + if (proto === Array.prototype) return "array"; + return "other"; +} +const values = [new Error("e"), {}, Object.create(null), [], JSON.parse('{"a":1}'), "s"]; +console.log(values.map(kind).join(",")); +const proto = Object.getPrototypeOf(new Error("m")); +console.log(proto.name, JSON.stringify(proto.message), Object.prototype.hasOwnProperty.call(proto, "name")); + +// After Object.setPrototypeOf, the prototype is the object that was set. +const custom = {}; +const moved = Object.setPrototypeOf(new Error("moved"), custom); +console.log(Object.getPrototypeOf(moved) === custom, Object.getPrototypeOf(moved) === Error.prototype); +const named = { kind: "named" }; +const movedNamed = new Error(); +Object.setPrototypeOf(movedNamed, named); +console.log(Object.getPrototypeOf(movedNamed) === named, Object.getPrototypeOf(movedNamed).kind); diff --git a/tests/corpus/8129-brotli-long-param-name.cjs b/tests/corpus/8129-brotli-long-param-name.cjs new file mode 100644 index 000000000..c3d1e1712 --- /dev/null +++ b/tests/corpus/8129-brotli-long-param-name.cjs @@ -0,0 +1,23 @@ +// A Brotli params key that names no parameter: ERR_BROTLI_INVALID_PARAM +// carries the whole key, however long, in Node's message. +const zlib = require("node:zlib"); +for (const key of ["k".repeat(5000), "é".repeat(300) + "x", "9", "-1", "1.5", "x"]) { + try { + zlib.brotliCompressSync("data", { params: { [key]: 1 } }); + console.log("accepted", key.length); + } catch (err) { + const suffix = " is not a valid Brotli parameter"; + console.log( + err.name, + err.code, + err.message.length, + err.message === key + suffix, + err.message.slice(-suffix.length - 3), + ); + } +} +try { + zlib.createBrotliDecompress({ params: { ["p".repeat(100000)]: 0 } }); +} catch (err) { + console.log(err.name, err.code, err.message.length, err.message.startsWith("pppp")); +} diff --git a/tests/corpus/8160-assert-expected-from-fields.cjs b/tests/corpus/8160-assert-expected-from-fields.cjs new file mode 100644 index 000000000..4c16bd78f --- /dev/null +++ b/tests/corpus/8160-assert-expected-from-fields.cjs @@ -0,0 +1,58 @@ +// Expected errors stored in an object literal's fields: a JavaScript +// object keeps its fields checked-dynamic, and assert.throws, assert.match +// and the expected-object form convert them to the regex or string the +// fields hold. +"use strict"; +const assert = require("assert"); + +const errMessages = { + auth: / auth/, + state: / state/, + length: /Invalid initialization vector/, + code: "ERR_CRYPTO_INVALID_IV", +}; + +const error = (message, code) => { + const e = new Error(message); + if (code) e.code = code; + return e; +}; + +assert.throws(() => { + throw error("Unsupported state or unable to authenticate data"); +}, errMessages.auth); +assert.throws(() => { + throw error("Invalid state for operation getAuthTag"); +}, errMessages.state); +assert.throws( + () => { + throw error("Invalid initialization vector", "ERR_CRYPTO_INVALID_IV"); + }, + { code: errMessages.code, message: errMessages.length }, +); +assert.match("unable to auth", errMessages.auth); +assert.doesNotMatch("no match here", errMessages.state); + +const report = (label, run) => { + try { + run(); + console.log(label, "passed"); + } catch (error) { + console.log(label, error.name, error.code, error.message.split("\n")[0]); + } +}; +report("wrong message", () => + assert.throws(() => { + throw error("something else"); + }, errMessages.length), +); +report("wrong code", () => + assert.throws( + () => { + throw error("Invalid initialization vector", "ERR_OTHER"); + }, + { code: errMessages.code }, + ), +); +report("match", () => assert.match("plain text", errMessages.auth)); +console.log("done"); diff --git a/tests/corpus/8180-pow2-quotients.ts b/tests/corpus/8180-pow2-quotients.ts new file mode 100644 index 000000000..f98d4e46e --- /dev/null +++ b/tests/corpus/8180-pow2-quotients.ts @@ -0,0 +1,57 @@ +// x / Math.pow(2, n) over every integer exponent from -1080 to 1030, against +// the same divisor read back from an array (an ordinary division): zeros, +// subnormals, values next to the limits, infinities and NaN, and a divisor of +// 2 ** n that is subnormal, zero or Infinity. Non-integer, signed-zero, +// infinite and NaN exponents give divisors that are not powers of two. +function bits(x: number): string { + const view = new DataView(new ArrayBuffer(8)); + view.setFloat64(0, x); + return view.getBigUint64(0).toString(16).padStart(16, "0"); +} + +const values = [ + 1, -1, 3, 0.1, -7.25, 0, -0, 5e-324, -5e-324, 2.2250738585072014e-308, 1.7976931348623157e308, + -1.7976931348623157e308, 4503599627370497, 123456789.123, Infinity, -Infinity, NaN, +]; +const divisors: number[] = []; +for (let n = -1080; n <= 1030; n++) divisors.push(Math.pow(2, n)); + +const scratch = new DataView(new ArrayBuffer(8)); +let digest = 0; +let mismatches = 0; +for (let n = -1080; n <= 1030; n++) { + const divisor = divisors[n + 1080]!; + for (const x of values) { + const quotient = x / Math.pow(2, n); + const plain = x / divisor; + scratch.setFloat64(0, quotient); + digest = (Math.imul(digest, 31) + scratch.getUint32(0)) | 0; + digest = (Math.imul(digest, 31) + scratch.getUint32(4)) | 0; + if (!Object.is(quotient, plain)) { + mismatches++; + if (mismatches <= 5) console.log("mismatch", x, n, bits(quotient), bits(plain)); + } + } +} +console.log("digest", (digest >>> 0).toString(16), "mismatches", mismatches); + +for (const n of [-1075, -1074, -1023, -1022, -1, 0, 1, 52, 1022, 1023, 1024]) { + console.log(n, bits(3 / Math.pow(2, n)), bits(-5e-324 / Math.pow(2, n)), bits(1.7976931348623157e308 / Math.pow(2, n))); +} +for (const e of [0.5, -0.5, -0, 1e-300, 1023.0000001, -1074.5, Infinity, -Infinity, NaN, 2 ** 31, -(2 ** 31)]) { + console.log("exponent", e, bits(10 / Math.pow(2, e)), bits(-0 / Math.pow(2, e))); +} + +// A divisor that changes every pass, and one computed once. +let total = 0; +for (let i = 0; i < 100000; i++) { + const shift = (i % 40) - 20; + total += (i * 7 + 3) / Math.pow(2, shift); +} +console.log("total", total); +let key = 12345; +const width = 34; +for (let i = 0; i < 100000; i++) { + key = (key - Math.floor(key / Math.pow(2, width)) * Math.pow(2, width)) * 4 + (i % 4); +} +console.log("key", key); diff --git a/tests/corpus/8213-cycle-star-reexport/main.ts b/tests/corpus/8213-cycle-star-reexport/main.ts new file mode 100644 index 000000000..04cd7cea1 --- /dev/null +++ b/tests/corpus/8213-cycle-star-reexport/main.ts @@ -0,0 +1,11 @@ +// A module of an import cycle re-exports another member with `export *` +// (pi-coding-agent's sdk.js → agent-session-runtime.js → +// agent-session-services.js → sdk.js). The re-exported names are live +// bindings of the member that declares them, so they read the same values, +// and the same temporal dead zone errors, as in Node. +import { createSession, describeRuntime, Runtime, SDK_VERSION } from "./sdk.ts"; +import { servicesReport } from "./services.ts"; + +console.log(servicesReport()); +console.log(createSession("main").describe()); +console.log(describeRuntime(), new Runtime("direct").name, SDK_VERSION); diff --git a/tests/corpus/8213-cycle-star-reexport/runtime.ts b/tests/corpus/8213-cycle-star-reexport/runtime.ts new file mode 100644 index 000000000..f3653d255 --- /dev/null +++ b/tests/corpus/8213-cycle-star-reexport/runtime.ts @@ -0,0 +1,17 @@ +import { createServices, servicesLabel } from "./services.ts"; + +export class Runtime { + readonly name: string; + constructor(name: string) { + this.name = name; + } + describe(): string { + return `${this.name} runtime with ${servicesLabel}`; + } +} + +export const runtimeLabel = "runtime"; + +export function describeRuntime(): string { + return `${runtimeLabel} (${typeof createServices})`; +} diff --git a/tests/corpus/8213-cycle-star-reexport/sdk.ts b/tests/corpus/8213-cycle-star-reexport/sdk.ts new file mode 100644 index 000000000..7d67384c4 --- /dev/null +++ b/tests/corpus/8213-cycle-star-reexport/sdk.ts @@ -0,0 +1,9 @@ +import { createServices } from "./services.ts"; + +export * from "./runtime.ts"; + +export const SDK_VERSION = "1.4.0"; + +export function createSession(name: string) { + return createServices(name); +} diff --git a/tests/corpus/8213-cycle-star-reexport/services.ts b/tests/corpus/8213-cycle-star-reexport/services.ts new file mode 100644 index 000000000..62e6b684e --- /dev/null +++ b/tests/corpus/8213-cycle-star-reexport/services.ts @@ -0,0 +1,27 @@ +import { Runtime, SDK_VERSION, describeRuntime, runtimeLabel } from "./sdk.ts"; + +export const servicesLabel = "services"; + +function probe(read: () => unknown): string { + try { + return String(read()); + } catch (error) { + return `${(error as Error).name}: ${(error as Error).message}`; + } +} + +// This module runs first: sdk.ts and runtime.ts have not run yet. +const early = [ + probe(() => SDK_VERSION), + probe(() => runtimeLabel), + probe(() => typeof describeRuntime), + probe(() => new Runtime("early").name), +]; + +export function servicesReport(): string { + return `early ${early.join(" | ")}; late ${SDK_VERSION} ${runtimeLabel}`; +} + +export function createServices(name: string): Runtime { + return new Runtime(name); +} diff --git a/tests/corpus/957-builtins-namespace.ts b/tests/corpus/957-builtins-namespace.ts index 4b27bfe79..f6980cda6 100644 --- a/tests/corpus/957-builtins-namespace.ts +++ b/tests/corpus/957-builtins-namespace.ts @@ -17,7 +17,7 @@ function tail(p: string): string { return p.slice(i + 1); } -const scratch = "tmp-957-" + tail(process.argv[1]); +const scratch = "tmp-957-" + tail(process.argv[1]) + "-" + process.pid; if (fs.existsSync(scratch)) { fs.rmSync(scratch + "/a.txt"); fs.rmdirSync(scratch); diff --git a/tests/corpus/992-fs-roundtrip.ts b/tests/corpus/992-fs-roundtrip.ts index a039c936f..92b527fb1 100644 --- a/tests/corpus/992-fs-roundtrip.ts +++ b/tests/corpus/992-fs-roundtrip.ts @@ -32,7 +32,7 @@ function freshDir(dir: string): void { } mkdirSync(dir); } -const scratch = "tmp-992-" + tail(process.argv[1]); +const scratch = "tmp-992-" + tail(process.argv[1]) + "-" + process.pid; freshDir(scratch); const file = scratch + "/notes.txt"; diff --git a/tests/corpus/993-fs-readdir.ts b/tests/corpus/993-fs-readdir.ts index e948d3878..b0409f2ef 100644 --- a/tests/corpus/993-fs-readdir.ts +++ b/tests/corpus/993-fs-readdir.ts @@ -26,7 +26,7 @@ function freshDir(dir: string): void { } mkdirSync(dir); } -const scratch = "tmp-993-" + tail(process.argv[1]); +const scratch = "tmp-993-" + tail(process.argv[1]) + "-" + process.pid; freshDir(scratch); console.log(readdirSync(scratch).length); // fresh dir is empty diff --git a/tests/corpus/994-fs-errors.ts b/tests/corpus/994-fs-errors.ts index 74a1ed74e..d317c1aba 100644 --- a/tests/corpus/994-fs-errors.ts +++ b/tests/corpus/994-fs-errors.ts @@ -30,7 +30,7 @@ function freshDir(dir: string): void { } mkdirSync(dir); } -const scratch = "tmp-994-" + tail(process.argv[1]); +const scratch = "tmp-994-" + tail(process.argv[1]) + "-" + process.pid; freshDir(scratch); let caught = 0; diff --git a/tests/corpus/996-fs-rc-stress.ts b/tests/corpus/996-fs-rc-stress.ts index d6a8f0ba3..063845309 100644 --- a/tests/corpus/996-fs-rc-stress.ts +++ b/tests/corpus/996-fs-rc-stress.ts @@ -28,7 +28,7 @@ function freshDir(dir: string): void { } mkdirSync(dir); } -const scratch = "tmp-996-" + tail(process.argv[1]); +const scratch = "tmp-996-" + tail(process.argv[1]) + "-" + process.pid; freshDir(scratch); // Contents into an array, back through map/filter/join. diff --git a/tests/corpus/997-fs-modules/store.ts b/tests/corpus/997-fs-modules/store.ts index 65e59df1b..1b2a0f1fe 100644 --- a/tests/corpus/997-fs-modules/store.ts +++ b/tests/corpus/997-fs-modules/store.ts @@ -17,7 +17,7 @@ export function scratchDir(tag: string): string { while (i >= 0 && script.charAt(i) !== "/" && script.charAt(i) !== "\\") { i = i - 1; } - const dir = tag + script.slice(i + 1); + const dir = tag + script.slice(i + 1) + "-" + process.pid; if (existsSync(dir)) { for (const name of readdirSync(dir)) { rmSync(dir + "/" + name); diff --git a/tests/corpus/class-brand-probe-keys.ts b/tests/corpus/class-brand-probe-keys.ts new file mode 100644 index 000000000..77b5ff138 --- /dev/null +++ b/tests/corpus/class-brand-probe-keys.ts @@ -0,0 +1,68 @@ +// Untyped reads of class instances dispatch on the capsule's class key. +// These classes have keys of equal length, keys that share their last +// eight bytes, and short keys, so every stage of the brand probe decides. +class FooHandlerA { + kind = "foo-a"; + label(): string { + return `FooHandlerA:${this.kind}`; + } +} +class BarHandlerA { + kind = "bar-a"; + label(): string { + return `BarHandlerA:${this.kind}`; + } +} +class FooHandlerB extends FooHandlerA { + override kind = "foo-b"; + extra = 2; +} +class Q { + kind = "q"; + label(): string { + return "Q"; + } +} +class R { + kind = "r"; + label(): string { + return "R"; + } +} +class LongerClassNameWithSharedTail { + kind = "long"; + label(): string { + return "long"; + } +} +class OtherClassNameWithSharedTail { + kind = "other"; + label(): string { + return "other"; + } +} + +const values: any[] = [ + new FooHandlerA(), + new BarHandlerA(), + new FooHandlerB(), + new Q(), + new R(), + new LongerClassNameWithSharedTail(), + new OtherClassNameWithSharedTail(), + { kind: "plain", label: () => "plain" }, +]; + +for (const value of values) { + const extra = value.extra; + console.log(value.kind, value.label(), extra === undefined ? "-" : extra, value.constructor.name); +} + +// Writes through the untyped view reach the right instance. +for (const value of values) value.kind = `${value.kind}!`; +console.log(values.map((value) => value.kind).join(",")); + +// Computed reads go through the same tables. +const key: string = ["kind", "label"][values.length % 2]; +console.log(values.map((value) => typeof value[key]).join(",")); +console.log(values.filter((value) => value instanceof FooHandlerA).length); diff --git a/tests/corpus/fetch-headers-copy-cache.ts b/tests/corpus/fetch-headers-copy-cache.ts new file mode 100644 index 000000000..cbbc7fc6d --- /dev/null +++ b/tests/corpus/fetch-headers-copy-cache.ts @@ -0,0 +1,125 @@ +// Headers copies and the cached sort-and-combine list: copies are +// independent of their source, reads see every mutation, and live +// iterators re-read the current list after a change. +function dump(label: string, h: Headers): void { + const parts: string[] = []; + h.forEach((value, name) => parts.push(`${name}=${value}`)); + console.log(`${label}: ${parts.join(" | ")}`); +} + +// The corpus lib has no DOM iterable typings for Headers, so iterators +// are reached through an untyped view. +const iter = (h: Headers): any => h; + +const source = new Headers({ "X-B": " two ", "x-a": "one", Accept: "text/html" }); +source.append("accept", "application/json"); +source.append("Set-Cookie", "a=1"); +source.append("set-cookie", "b=2"); +dump("source", source); + +// A Headers copy keeps the combined values and stays separate. +const copy = new Headers(source); +source.set("x-a", "changed"); +copy.append("x-c", "three"); +dump("source after set", source); +dump("copy after append", copy); +console.log(JSON.stringify(copy.get("accept")), JSON.stringify(copy.get("set-cookie"))); +console.log(JSON.stringify(copy.getSetCookie())); + +// Copying into an empty Response and a Request, then appending a repeat. +const response = new Response("body", { headers: copy, status: 201 }); +response.headers.append("accept", "*/*"); +response.headers.append("cookie", "c=3"); +response.headers.append("Cookie", "d=4"); +dump("response", response.headers); +dump("copy unchanged", copy); +const clone = new Response(response.body, response); +dump("response clone", clone.headers); +const request = new Request("http://example.test/", { headers: copy }); +console.log("request x-b", JSON.stringify(request.headers.get("X-B"))); + +// Every read after a mutation sees it: get, has, iteration, spread. +const h = new Headers([["x-z", "z"], ["x-m", "m"]]); +console.log([...iter(h).entries()].map(([n, v]) => `${n}:${v}`).join(",")); +h.delete("x-z"); +console.log([...iter(h).keys()].join(","), h.has("x-z")); +h.delete("x-missing"); +console.log([...iter(h).values()].join(",")); +h.append("x-a", "first"); +console.log([...iter(h).entries()].map(([n, v]) => `${n}:${v}`).join(",")); +h.set("X-M", "again"); +console.log(JSON.stringify(h.get("x-m")), [...iter(h).keys()].join(",")); + +// A live iterator: mutations between steps change what comes next. +const it = iter(h).entries(); +console.log(JSON.stringify(it.next().value)); +h.append("x-0", "before"); +console.log(JSON.stringify(it.next().value)); +h.delete("x-m"); +h.append("x-zz", "after"); +console.log(JSON.stringify(it.next()), JSON.stringify(it.next())); + +// forEach that mutates as it walks. +const walk = new Headers([["b", "2"], ["d", "4"]]); +const seen: string[] = []; +walk.forEach((value, name) => { + seen.push(`${name}=${value}`); + if (name === "b") { + walk.append("c", "3"); + walk.append("a", "1"); + } + if (name === "c") walk.set("d", "four"); +}); +console.log(seen.join(" | ")); + +// Invalid input in a copy source is still rejected (plain object source). +try { + new Response("x", { headers: { "bad name": "v" } }); +} catch (error) { + console.log((error as Error).name, (error as Error).message); +} +try { + new Headers(source).append("x", "a\nb"); +} catch (error) { + console.log((error as Error).name, (error as Error).message); +} + +// Appending an empty value leaves the separator's space at the end of the +// combined value; a copy trims it again, as Node's does. A leading +// separator has no whitespace to trim. +const spaced = new Headers(); +spaced.append("a", "x"); +spaced.append("a", ""); +spaced.append("b", ""); +spaced.append("b", "y"); +spaced.append("c", ""); +spaced.append("c", ""); +spaced.append("cookie", "k=v"); +spaced.append("cookie", ""); +const spacedList = (headers: Headers): string => { + const out: string[] = []; + headers.forEach((value, name) => out.push(`${name}=${JSON.stringify(value)}`)); + return out.join(" "); +}; +console.log(spacedList(spaced)); +console.log(spacedList(new Headers(spaced))); +console.log(spacedList(new Headers(new Headers(spaced)))); +console.log(spacedList(new Response("", { headers: spaced }).headers)); +console.log(spacedList(new Request("http://example.test/", { headers: spaced }).headers)); + +// The same through duplicate constructor entries, in a Headers object and +// in a Response's own headers. +const entries: [string, string][] = [ + ["a", "x"], + ["a", ""], + ["c", ""], + ["c", ""], + ["d", "plain"], +]; +const built = new Headers(entries); +console.log(spacedList(built)); +console.log(spacedList(new Headers(built))); +const builtResponse = new Response("", { headers: entries }); +console.log(spacedList(builtResponse.headers)); +console.log(spacedList(new Headers(builtResponse.headers))); +console.log(spacedList(new Request("http://example.test/", { headers: built }).headers)); diff --git a/tests/corpus/split-cjs-require-binding/chain-wrap.cjs b/tests/corpus/split-cjs-require-binding/chain-wrap.cjs new file mode 100644 index 000000000..435dcaeab --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/chain-wrap.cjs @@ -0,0 +1,3 @@ +var chained; +chained = require("./chain.cjs"); +module.exports = chained; diff --git a/tests/corpus/split-cjs-require-binding/chain.cjs b/tests/corpus/split-cjs-require-binding/chain.cjs new file mode 100644 index 000000000..a279f456e --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/chain.cjs @@ -0,0 +1 @@ +module.exports = require("./whole.cjs"); diff --git a/tests/corpus/split-cjs-require-binding/lib.cjs b/tests/corpus/split-cjs-require-binding/lib.cjs new file mode 100644 index 000000000..b318eb85c --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/lib.cjs @@ -0,0 +1,5 @@ +console.log("lib: init"); +exports.n = 7; +exports.f = function (x) { + return x * 2; +}; diff --git a/tests/corpus/split-cjs-require-binding/main.cjs b/tests/corpus/split-cjs-require-binding/main.cjs new file mode 100644 index 000000000..974421bd3 --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/main.cjs @@ -0,0 +1,55 @@ +console.log("main: start"); +var lib, whole; +lib = require("./lib.cjs"); +whole = require("./whole.cjs"); +console.log("main: after", lib.n, lib.f(3), whole(4)); +exports.fromLib = lib.f; +exports.whole = whole; +console.log("main: exported", exports.fromLib(5), whole(6)); +console.log("main: identity", exports.fromLib === lib.f, exports.whole === whole); +const re = require("./reexport.cjs"); +console.log("main: reexport", re(7)); +let later; +later = require("./lib.cjs"); +console.log("main: let", later.n); +const rx = require("./reexport-extra.cjs"); +console.log("main: reexport extra", rx(8), rx.extra); +const splitObject = { whole }; +console.log("main: shorthand value", splitObject.whole(10)); +var libAgain; +libAgain = require("./lib.cjs"); +const { n: splitN, f: splitF } = libAgain; +console.log("main: destructured split", splitN, splitF(4)); +var chained; +chained = require("./chain-wrap.cjs"); +console.log("main: chained", chained(11)); + +// Any later write, including a destructuring target, keeps ordinary storage. +var shorthand; +shorthand = require("./whole.cjs"); +({ shorthand } = { shorthand: "shorthand" }); +console.log("main: shorthand write", shorthand); +var renamed; +renamed = require("./whole.cjs"); +({ value: renamed } = { value: "renamed" }); +console.log("main: renamed write", renamed); +var nested; +nested = require("./whole.cjs"); +({ a: { nested } } = { a: { nested: "nested" } }); +console.log("main: nested write", nested); +var rest; +rest = require("./whole.cjs"); +({ ...rest } = { a: 1, b: 2 }); +console.log("main: rest write", rest.a, rest.b); +var array; +array = require("./whole.cjs"); +[array] = ["array"]; +console.log("main: array write", array); +var arrayRest; +arrayRest = require("./whole.cjs"); +[...arrayRest] = [1, 2]; +console.log("main: array-rest write", arrayRest[0], arrayRest[1]); +var each; +each = require("./whole.cjs"); +for ({ each } of [{ each: "each" }]) {} +console.log("main: for-of write", each); diff --git a/tests/corpus/split-cjs-require-binding/package.json b/tests/corpus/split-cjs-require-binding/package.json new file mode 100644 index 000000000..a3c15a7a6 --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/split-cjs-require-binding/reexport-extra.cjs b/tests/corpus/split-cjs-require-binding/reexport-extra.cjs new file mode 100644 index 000000000..584de98fe --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/reexport-extra.cjs @@ -0,0 +1,6 @@ +var whole; +whole = require("./whole.cjs"); +console.log("extra: before", whole.extra, whole(9)); +module.exports = whole; +module.exports.extra = 1; +console.log("extra: after", whole.extra, module.exports.extra); diff --git a/tests/corpus/split-cjs-require-binding/reexport.cjs b/tests/corpus/split-cjs-require-binding/reexport.cjs new file mode 100644 index 000000000..9a931bdef --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/reexport.cjs @@ -0,0 +1,4 @@ +console.log("reexport: init"); +var whole; +whole = require("./whole.cjs"); +module.exports = whole; diff --git a/tests/corpus/split-cjs-require-binding/whole.cjs b/tests/corpus/split-cjs-require-binding/whole.cjs new file mode 100644 index 000000000..31b476c4e --- /dev/null +++ b/tests/corpus/split-cjs-require-binding/whole.cjs @@ -0,0 +1,4 @@ +console.log("whole: init"); +module.exports = function whole(x) { + return x + 1; +}; diff --git a/tests/corpus/split-cjs-require-cycle/even.cjs b/tests/corpus/split-cjs-require-cycle/even.cjs new file mode 100644 index 000000000..94d74ea87 --- /dev/null +++ b/tests/corpus/split-cjs-require-cycle/even.cjs @@ -0,0 +1,10 @@ +var odd; +exports.name = "even"; +exports.isEven = function (n) { + return n === 0 ? true : odd.isOdd(n - 1); +}; +exports.describe = function (n) { + return n + " is " + (exports.isEven(n) ? "even" : "odd") + " (asked " + odd.name + ")"; +}; +odd = require("./odd.cjs"); +console.log("even: loaded"); diff --git a/tests/corpus/split-cjs-require-cycle/main.cjs b/tests/corpus/split-cjs-require-cycle/main.cjs new file mode 100644 index 000000000..9ec7997db --- /dev/null +++ b/tests/corpus/split-cjs-require-cycle/main.cjs @@ -0,0 +1,12 @@ +// Split require bindings (`var m; m = require("./x.cjs")`) that close a +// CommonJS cycle: each module publishes its exports before its require, and +// reads the other module's binding only in functions that run after both +// modules finish, the cycle shape the const form compiles too. One +// assignment parenthesizes its require. +console.log("main: start"); +var even, odd; +even = require("./even.cjs"); +odd = require("./odd.cjs"); +console.log(even.isEven(10), odd.isOdd(7), even.isEven(3)); +console.log(even.describe(4), "|", even.describe(9)); +console.log("odd calls", odd.calls(), even.name, odd.name); diff --git a/tests/corpus/split-cjs-require-cycle/odd.cjs b/tests/corpus/split-cjs-require-cycle/odd.cjs new file mode 100644 index 000000000..d449c28e8 --- /dev/null +++ b/tests/corpus/split-cjs-require-cycle/odd.cjs @@ -0,0 +1,12 @@ +var even; +let calls = 0; +exports.name = "odd"; +exports.isOdd = function (n) { + calls++; + return n === 0 ? false : even.isEven(n - 1); +}; +exports.calls = function () { + return calls; +}; +even = (require("./even.cjs")); +console.log("odd: loaded"); diff --git a/tests/corpus/split-cjs-require-cycle/package.json b/tests/corpus/split-cjs-require-cycle/package.json new file mode 100644 index 000000000..a3c15a7a6 --- /dev/null +++ b/tests/corpus/split-cjs-require-cycle/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/string-last-index-of-ascii.ts b/tests/corpus/string-last-index-of-ascii.ts new file mode 100644 index 000000000..8dcf405f5 --- /dev/null +++ b/tests/corpus/string-last-index-of-ascii.ts @@ -0,0 +1,21 @@ +// lastIndexOf on ASCII receivers: every position form, empty and long +// needles, needles with non-ASCII text, and receivers built at run time. +const ids = ["bk_00042", "mb_00410", "plain", "_lead", "trail_", "a_b_c_d", ""]; +for (const id of ids) console.log(JSON.stringify(id), id.lastIndexOf("_"), Number(id.slice(id.lastIndexOf("_") + 1))); + +const text = "abcabcabc"; +const positions = [Infinity, -Infinity, NaN, -0, 0, 1, 2.9, 3, 5, 8, 9, 100, -5]; +for (const needle of ["abc", "c", "a", "", "abcabcabcabc", "cab", "x", "é", "\ud83d", "😀"]) { + const row = positions.map((position) => text.lastIndexOf(needle, position)); + console.log(JSON.stringify(needle), text.lastIndexOf(needle), row.join(",")); +} + +// Receivers built at run time (concatenation, repeat, join, slices). +const built = ["x", "y"].join("-").repeat(3) + String(42); +console.log(built, built.lastIndexOf("-"), built.lastIndexOf("x-", 3), built.lastIndexOf("42", 6)); +const slice = built.slice(2, 9); +console.log(slice, slice.lastIndexOf("y"), slice.lastIndexOf("y", 0), slice.lastIndexOf("-y-")); + +// A non-ASCII receiver keeps UTF-16 positions. +const mixed = "é_ü_😀_z"; +console.log(mixed.lastIndexOf("_"), mixed.lastIndexOf("_", 3), mixed.lastIndexOf("😀"), mixed.lastIndexOf("\ude00")); diff --git a/tests/corpus/typed-array-masked-index-stores.ts b/tests/corpus/typed-array-masked-index-stores.ts new file mode 100644 index 000000000..88a25b4c5 --- /dev/null +++ b/tests/corpus/typed-array-masked-index-stores.ts @@ -0,0 +1,40 @@ +// Several typed array accesses in one counted loop share an index that is +// not an affine function of the counter (a masked or remaindered counter). +// The loop versioning analysis records that such an index has no affine +// shape and reads the recorded result again at each later access. +const length = 64; +const bytes = new Uint8Array(length); +const words = new Int32Array(16); +const totals = new Float64Array(16); + +for (let i = 0; i < bytes.length; i++) { + const slot = i & 15; + words[slot] = words[slot]! + i; + totals[slot] = totals[slot]! + i * 0.5; + bytes[i] = slot; +} +console.log(Array.from(words).join(",")); +console.log(Array.from(totals).join(",")); +console.log(bytes.reduce((sum, value) => sum + value, 0)); + +const ring = new Uint16Array(8); +const seen = new Uint8Array(8); +for (let i = 0; i < length; i++) { + const at = i % 8; + ring[at] = ring[at]! * 3 + i; + seen[at] = seen[at]! + 1; +} +console.log(Array.from(ring).join(","), Array.from(seen).join(",")); + +function scatter(source: Uint8Array, mask: number): Uint32Array { + const out = new Uint32Array(mask + 1); + const counts = new Uint32Array(mask + 1); + for (let i = 0; i < source.length; i++) { + const bucket = (source[i]! + i) & mask; + out[bucket] = out[bucket]! + source[i]!; + counts[bucket] = counts[bucket]! + 1; + } + for (let i = 0; i < out.length; i++) out[i] = out[i]! * 10 + counts[i]!; + return out; +} +console.log(Array.from(scatter(bytes, 7)).join(",")); diff --git a/tests/coverage-fixtures/no-divergences.ts b/tests/coverage-fixtures/no-divergences.ts index 3739336e3..70409d319 100644 --- a/tests/coverage-fixtures/no-divergences.ts +++ b/tests/coverage-fixtures/no-divergences.ts @@ -1,6 +1,6 @@ // Ordinary code: identity-preserving calls, declaration-ordered literals, -// Latin-script comparisons, related-class casts, ISO dates and views of the -// global object report no divergence. +// Latin-script comparisons, related-class casts, ISO dates, views of the +// global object and a dictionary held as `{}` report no divergence. interface Item { name: string; qty: number; @@ -38,3 +38,17 @@ const COUNT = Symbol.for("no-divergences.count"); const globalState = globalThis as { [COUNT]?: { n: number } }; globalState[COUNT] = { n: 1 }; console.log(globalState[COUNT]?.n); + +// A dictionary held as `{}` comes back out as itself. +const counts: Record = { a: "x" }; +const opaque: {} = counts; +const again: Record = opaque; +again[counts["a"] ?? "a"] = "y"; +console.log(JSON.stringify(counts)); + +// A record stored in a dictionary binding and written through it stays +// the same object. +const named: { first: string } = { first: "a" }; +const byName: Record = named; +byName[named.first] = "b"; +console.log(JSON.stringify(named)); diff --git a/tests/diagnostics/any-destructure-unspelled-keys.ts b/tests/diagnostics/any-destructure-unspelled-keys.ts new file mode 100644 index 000000000..6680de854 --- /dev/null +++ b/tests/diagnostics/any-destructure-unspelled-keys.ts @@ -0,0 +1,12 @@ +// Destructuring an `any` source keeps SC1031 where the source has no +// compiled spelling for V8's TypeError: a key computed by an expression, an +// optional element read, a template key with a substitution, and a string key +// holding NUL (the message is a C string). String-literal keys compile +// (tests/corpus/7310-any-destructure-string-keys.ts). +const events: any = { ab: { value: 1 } }; +const k = "a"; +const { value: computed } = events["a" + "b"]; +const { value: optional } = events?.["ab"]; +const { value: template } = events[`${k}b`]; +const { value: nul } = events["a\0b"]; +console.log(computed, optional, template, nul); diff --git a/tests/diagnostics/component-fences.ts b/tests/diagnostics/component-fences.ts index 6d88f9af5..0d382bf28 100644 --- a/tests/diagnostics/component-fences.ts +++ b/tests/diagnostics/component-fences.ts @@ -5,9 +5,9 @@ const byFlag = new Map(); console.log(byFlag); -// Map values have no slot for Dates. -const stamps = new Map(); -console.log(stamps); +// Map values have no slot for URLSearchParams. +const queries = new Map(); +console.log(queries); // Boolean Set elements remain outside the supported element domain. const flags = new Set(); diff --git a/tests/diagnostics/date-union-fences.ts b/tests/diagnostics/date-union-fences.ts deleted file mode 100644 index 478df74d6..000000000 --- a/tests/diagnostics/date-union-fences.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Compiler-synthesized `T | undefined` slots must observe the same Date -// union fence as source-written unions. Each construct below used to reach -// IR validation as an SC9001 internal compiler error. - -function defaultDate(value: Date = new Date(0)): number { - return value.getTime(); -} -defaultDate(); - -function* dates(): Generator { - yield new Date(0); -} -for (const value of dates()) console.log(value.getTime()); - -class DeferredDate { - value!: Date; - initialize(): void { - this.value = new Date(0); - } -} -const deferred = new DeferredDate(); -deferred.initialize(); diff --git a/tests/diagnostics/fs-url-path-fences.ts b/tests/diagnostics/fs-url-path-fences.ts index 080c561e1..6b65bfac0 100644 --- a/tests/diagnostics/fs-url-path-fences.ts +++ b/tests/diagnostics/fs-url-path-fences.ts @@ -1,15 +1,16 @@ // URL paths whose conversion would run in a different order than Node's. import * as fs from "node:fs"; +import * as fsp from "node:fs/promises"; const url = new URL("file:///tmp/fs-url-path-fences"); -const encoding: BufferEncoding = process.argv.length > 9 ? "latin1" : "utf8"; const recursive = process.argv.length > 9; -// Node validates the string source before converting the URL destination. -fs.copyFileSync("/tmp/source", url); // Node validates the options before the path. -console.log(fs.readFileSync(url, encoding)); fs.mkdirSync(url, { recursive }); +void fsp.mkdir(url, { recursive }); // A literal's members are lowered one by one, so they cannot be evaluated // ahead of the conversion as one value. console.log(fs.statSync(url, { throwIfNoEntry: process.argv.length > 9 })); +// fs.promises checks the string source inside the call, after the +// conversion would have run. +void fsp.copyFile("/tmp/source", url); diff --git a/tests/diagnostics/globalthis-buffer-alias-values.ts b/tests/diagnostics/globalthis-buffer-alias-values.ts new file mode 100644 index 000000000..29214f5c3 --- /dev/null +++ b/tests/diagnostics/globalthis-buffer-alias-values.ts @@ -0,0 +1,18 @@ +// An alias of the Buffer global read through a cast of globalThis compiles +// in conditions, typeof and member calls +// (tests/corpus/7309-globalthis-buffer-aliases.ts). Its other value uses +// keep a named SC1090 refusal: the alias has the cast's static type. +type RuntimeBuffer = { from(value: string, encoding: "utf8"): Uint8Array }; +const runtimeBuffer = (globalThis as { Buffer?: RuntimeBuffer }).Buffer; +function keep(value: unknown): unknown { + return value; +} +const bytes = runtimeBuffer && runtimeBuffer.from("abc", "utf8"); +keep(runtimeBuffer); +keep(bytes); +// A copy of the alias, and an alias whose cast read is guarded with `||`, +// carry the cast's type too. +const copied = runtimeBuffer; +const orFallback = (globalThis as { Buffer?: RuntimeBuffer }).Buffer || { from: () => new Uint8Array(0) }; +keep(copied); +keep(orFallback); diff --git a/tests/diagnostics/index-signature-values.ts b/tests/diagnostics/index-signature-values.ts index 6c53b141c..1f3a01ba5 100644 --- a/tests/diagnostics/index-signature-values.ts +++ b/tests/diagnostics/index-signature-values.ts @@ -8,10 +8,6 @@ const bySymbol: { [s: symbol]: number } = {}; console.log(bySymbol); -// Storage is supported, but composite typed-array inspection is not. -const buffers: Record = {}; -console.log(buffers); - // Dual signatures whose value types intern DIFFERENTLY cannot share the // one store (tsc requires only assignability, but the store gives one // answer per key). diff --git a/tests/diagnostics/node-fallback-fence.ts b/tests/diagnostics/node-fallback-fence.ts index 86034f1ac..b044e22d3 100644 --- a/tests/diagnostics/node-fallback-fence.ts +++ b/tests/diagnostics/node-fallback-fence.ts @@ -30,9 +30,9 @@ const raw = readFileSync("/etc/hosts").toString(tty ? "hex" : "latin1"); // runt const intervalWithSignal = promiseInterval(1, "tick", {}); // AbortSignal options stay fenced const sleepWithSignal = promiseSleep(1, "tick", { signal: AbortSignal.timeout(5) }); // resolve values and AbortSignal options lower // The computed encoding is now part of the static Buffer surface. The -// one-shot zlib/raw/gzip codecs take Node's options, validated at run -// time; Brotli remains an explicit member fence. +// one-shot zlib/raw/gzip and Brotli codecs take Node's options, validated +// at run time. import { brotliCompressSync, deflateSync } from "node:zlib"; const packed = deflateSync("data", { level: 9, strategy: 1 }); // options lower -const brotli = brotliCompressSync(Buffer.from("data")); // outside the lowered family: fenced +const brotli = brotliCompressSync(Buffer.from("data")); // Brotli lowers too // The remaining imports continue the declared-but-not-lowered surface. diff --git a/tests/diagnostics/split-cjs-require-cycle/b.js b/tests/diagnostics/split-cjs-require-cycle/b.js new file mode 100644 index 000000000..ad5a7441a --- /dev/null +++ b/tests/diagnostics/split-cjs-require-cycle/b.js @@ -0,0 +1,7 @@ +var a; +a = require('./main.js'); +exports.fromB = 'b-value'; +exports.readA = function () { + return a.early + '/' + a.late; +}; +console.log('b sees a.early:', a.early, 'a.late:', a.late); diff --git a/tests/diagnostics/split-cjs-require-cycle/main.js b/tests/diagnostics/split-cjs-require-cycle/main.js new file mode 100644 index 000000000..286de459a --- /dev/null +++ b/tests/diagnostics/split-cjs-require-cycle/main.js @@ -0,0 +1,11 @@ +// A split require binding (`var b; b = require("./b.js")`) whose require +// closes a CommonJS cycle that top-level code can observe: b.js reads a.js's +// partial exports while a.js is still evaluating, which the compiled export +// table does not model. Preflight refuses it with SC1016, as it refuses the +// `const b = require("./b.js")` form; tests/corpus/split-cjs-require-cycle +// compiles the cycle shape both forms admit. +exports.early = 'a-early'; +var b; +b = require('./b.js'); +exports.late = 'a-late'; +console.log('a sees', b.fromB, b.readA()); diff --git a/tests/diagnostics/split-cjs-require-cycle/package.json b/tests/diagnostics/split-cjs-require-cycle/package.json new file mode 100644 index 000000000..a3c15a7a6 --- /dev/null +++ b/tests/diagnostics/split-cjs-require-cycle/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/diagnostics/split-cjs-require-early/lib.js b/tests/diagnostics/split-cjs-require-early/lib.js new file mode 100644 index 000000000..2a9669e12 --- /dev/null +++ b/tests/diagnostics/split-cjs-require-early/lib.js @@ -0,0 +1,8 @@ +'use strict'; + +/** @param {string} who */ +function greet(who) { + return `hello ${who}`; +} + +module.exports = { greet }; diff --git a/tests/diagnostics/split-cjs-require-early/main.js b/tests/diagnostics/split-cjs-require-early/main.js new file mode 100644 index 000000000..ae1d8d770 --- /dev/null +++ b/tests/diagnostics/split-cjs-require-early/main.js @@ -0,0 +1,21 @@ +// A split require binding (`let lib;` then `lib = require(...)`) that code +// above the assignment can reach: Node reads undefined there, a value the +// compiled module alias cannot hold, so the assignment keeps a named SC1013 +// fence. Assigned before any such code, the binding compiles +// (tests/corpus/split-cjs-require-binding). +'use strict'; + +let lib; +console.log('boot', helper()); +lib = require('./lib.js'); + +// The same with the require parenthesized. +let wrapped; +console.log('peek', wrapped === undefined); +wrapped = (require('./lib.js')); + +function helper() { + return lib === undefined ? 'not loaded' : lib.greet('late'); +} + +console.log(helper()); diff --git a/tests/diagnostics/split-cjs-require-early/package.json b/tests/diagnostics/split-cjs-require-early/package.json new file mode 100644 index 000000000..a3c15a7a6 --- /dev/null +++ b/tests/diagnostics/split-cjs-require-early/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/diagnostics/stdlib-fence.ts b/tests/diagnostics/stdlib-fence.ts index 1fdc01675..d28333178 100644 --- a/tests/diagnostics/stdlib-fence.ts +++ b/tests/diagnostics/stdlib-fence.ts @@ -23,7 +23,6 @@ const fmt = new Intl.NumberFormat(); const d = new Date(...([2024, 0] as const)); // spread field arguments stay fenced const mutableDate = new Date(0); mutableDate.setUTCFullYear(2024); // mutation is outside the read-only Date slice -const sameDate = mutableDate === mutableDate; // scalar storage must not fake object identity const wr = new WeakRef({}); const px = new Proxy({ a: 1 }, {}); const buf = new ArrayBuffer(8); @@ -74,13 +73,6 @@ function pick(a: string | number, b: number | boolean): boolean { } const picked = pick(1, 2); -// Date values deliberately have no tagged-union payload yet. Nullable -// Date positions must fence in the frontend instead of reaching a backend -// scalar/ref mismatch. -function optionalDate(value: Date | undefined): number { - return value === undefined ? -1 : value.getTime(); -} -optionalDate(undefined); function throwDate(): void { throw new Date(0); // the exception cell cannot preserve Date's object kind } diff --git a/tests/diagnostics/unions.ts b/tests/diagnostics/unions.ts index ccf62f40e..ddacb8015 100644 --- a/tests/diagnostics/unions.ts +++ b/tests/diagnostics/unions.ts @@ -60,14 +60,6 @@ const bareN = null; // honest text (JS prints "[object Object]") and stays fenced. const templated = `${mkAB()}`; -// `x == null` lowers as the null-or-undefined tag test — but a union with -// BOTH unit arms needs the operand twice, so an effectful operand (a call) -// gets its own fence pointing at a const binding. -function mkNullish(): string | null | undefined { - return null; -} -const looseCall = mkNullish() == null; - // A DECLARED `T | string` param infers nothing for T (no union // unification); the body's use of T then fails with the instantiation. function tagOr(x: T | string): T { diff --git a/tests/diagnostics/utility-types.ts b/tests/diagnostics/utility-types.ts index c6d10648b..cad1fcca4 100644 --- a/tests/diagnostics/utility-types.ts +++ b/tests/diagnostics/utility-types.ts @@ -5,9 +5,7 @@ // fields + an overflow map; number keys canonicalize to their JS string // spelling; function/Map/Set/nested-record values ride the same store — // differential corpus); what stays rejected: -// - symbol-keyed index signatures (no lowering for symbol keys); -// - index-signature value types outside the overflow-map slice (Dates and -// other lib API objects, even when the value itself has a representation). +// - symbol-keyed index signatures (no lowering for symbol keys). // (Readonly bindings compile, holding the Date as a checked value. // Dot access/writes to index-signature keys compile now — the overflow // path in dot spelling — and self-referential mapped types intern as @@ -15,9 +13,5 @@ const bySymbol: { [s: symbol]: string } = {}; console.log(bySymbol); -const dateBag: { [k: string]: Date } = {}; -const boot = dateBag["boot"]; -console.log(boot); - const stamped: Readonly = new Date(0); console.log(stamped.getTime()); diff --git a/tests/fixtures/event-loop/stdin-for-await-buffers.mjs b/tests/fixtures/event-loop/stdin-for-await-buffers.mjs new file mode 100644 index 000000000..d64ae460a --- /dev/null +++ b/tests/fixtures/event-loop/stdin-for-await-buffers.mjs @@ -0,0 +1,10 @@ +// for await over process.stdin yields Buffers: Buffer.isBuffer holds and +// indexOf takes a byte and a start offset (only chunking-independent facts +// are printed). +const chunks = []; +for await (const chunk of process.stdin) chunks.push(chunk); +console.log('buffers', chunks.every((chunk) => Buffer.isBuffer(chunk))); +const all = Buffer.concat(chunks); +const newlines = []; +for (let at = all.indexOf(10); at !== -1; at = all.indexOf(10, at + 1)) newlines.push(at); +console.log('newlines', newlines.join(','), all.subarray(newlines[0] + 1).indexOf('#')); diff --git a/tests/fixtures/event-loop/stdio-readable-backpressure.mjs b/tests/fixtures/event-loop/stdio-readable-backpressure.mjs new file mode 100644 index 000000000..df2afb18f --- /dev/null +++ b/tests/fixtures/event-loop/stdio-readable-backpressure.mjs @@ -0,0 +1,27 @@ +// 'readable', 'data' and resume() together, with no read() for a while: +// 'readable' keeps the stream paused, so it stops reading at its read-ahead +// mark until a read drains it, and 'data' sees what read() returns. +const input = process.stdin; +let draining = false; +let peak = 0; +let dataBytes = 0; +let readBytes = 0; +const drain = () => { + let chunk; + while ((chunk = input.read()) !== null) readBytes += chunk.length; +}; +input.on('data', (chunk) => { + dataBytes += chunk.length; +}); +input.on('readable', () => { + peak = Math.max(peak, input.readableLength); + if (draining) drain(); +}); +input.resume(); +setTimeout(() => { + peak = Math.max(peak, input.readableLength); + console.log('held below 128 KiB while not draining', peak <= 131072); + draining = true; + drain(); +}, 400); +input.on('end', () => console.log('read', readBytes, 'data', dataBytes)); diff --git a/tests/fixtures/event-loop/stdio-readable-large-size.mjs b/tests/fixtures/event-loop/stdio-readable-large-size.mjs new file mode 100644 index 000000000..f1f44edce --- /dev/null +++ b/tests/fixtures/event-loop/stdio-readable-large-size.mjs @@ -0,0 +1,9 @@ +// read(size) above the 64 KiB read-ahead mark: 'readable' keeps firing as +// input arrives until the requested size is buffered. +const input = process.stdin; +const sizes = []; +input.on('readable', () => { + let chunk; + while ((chunk = input.read(150000)) !== null) sizes.push(chunk.length); +}); +input.on('end', () => console.log(sizes.join(','))); diff --git a/tests/fixtures/event-loop/stdio-readable-once-rest.mjs b/tests/fixtures/event-loop/stdio-readable-once-rest.mjs new file mode 100644 index 000000000..81eae144b --- /dev/null +++ b/tests/fixtures/event-loop/stdio-readable-once-rest.mjs @@ -0,0 +1,12 @@ +// A once('readable') listener that reads part of the input: the rest stays +// buffered for a 'data' listener added afterwards. +const input = process.stdin; +input.once('readable', () => { + const head = input.read(3); + console.log('head', head === null ? null : head.toString('ascii')); + setTimeout(() => { + const rest = []; + input.on('data', (chunk) => rest.push(chunk.toString('ascii'))); + input.on('end', () => console.log('rest', rest.join(''))); + }, 50); +}); diff --git a/tests/fixtures/event-loop/stdio-readable-size.mjs b/tests/fixtures/event-loop/stdio-readable-size.mjs new file mode 100644 index 000000000..9ff76dafe --- /dev/null +++ b/tests/fixtures/event-loop/stdio-readable-size.mjs @@ -0,0 +1,18 @@ +// Fixed-size reads of stdin: read(n) answers null until n bytes are +// buffered, and the rest once the input ended. +const input = process.stdin; +const chunks = []; +const counts = []; +input.on('readable', () => { + let reads = 0; + let chunk; + while ((chunk = input.read(4)) !== null) { + chunks.push(chunk.toString('ascii')); + reads++; + } + counts.push(reads); +}); +input.on('end', () => { + console.log(chunks.join('|')); + console.log(counts.join(',')); +}); diff --git a/tests/fixtures/event-loop/stdio-readable.mjs b/tests/fixtures/event-loop/stdio-readable.mjs new file mode 100644 index 000000000..324a4b57b --- /dev/null +++ b/tests/fixtures/event-loop/stdio-readable.mjs @@ -0,0 +1,19 @@ +// process.stdin in paused mode through 'readable': each event drains the +// buffer with read(), one more event follows EOF, and 'end' comes after the +// read that finds the buffer empty. +const input = process.stdin; +const events = []; +let text = ''; +input.on('readable', () => { + let reads = 0; + let chunk; + while ((chunk = input.read()) !== null) { + text += String(chunk); + reads++; + } + events.push(`readable ${reads}`); +}); +input.on('end', () => { + console.log(events.join(', ')); + console.log(JSON.stringify(text), input.readableLength); +}); diff --git a/tests/fixtures/fetch/static-stream/main.mts b/tests/fixtures/fetch/static-stream/main.mts index 9ff8c050b..0ae400bc3 100644 --- a/tests/fixtures/fetch/static-stream/main.mts +++ b/tests/fixtures/fetch/static-stream/main.mts @@ -259,6 +259,22 @@ try { console.log("consumed request reader:", (error as Error).name); } +// A TransformStream's readable side as the request body: fetch's upload +// reads it, so writes to the writable side do not wait on backpressure. +const uploadTransform = new TransformStream(); +const uploadWriter = uploadTransform.writable.getWriter(); +const transformPosted = fetch(`${process.argv[2]}/post-echo`, { + method: "POST", + body: uploadTransform.readable, + duplex: "half", +}); +for (const part of ["trans", "form ", "body"]) { + await uploadWriter.write(new TextEncoder().encode(part)); +} +await uploadWriter.close(); +const transformResult = (await (await transformPosted).json()) as { body: string }; +console.log("transform request body:", transformResult.body); + const prelockedRequestBody = ReadableStream.from([ Buffer.from("prelocked request"), ]); diff --git a/tests/fixtures/node-types/diffie-hellman.ts b/tests/fixtures/node-types/diffie-hellman.ts new file mode 100644 index 000000000..d14fd5ffc --- /dev/null +++ b/tests/fixtures/node-types/diffie-hellman.ts @@ -0,0 +1,19 @@ +import { type KeyObject, createPublicKey, diffieHellman, generateKeyPair, generateKeyPairSync } from "node:crypto"; + +const alice = generateKeyPairSync("x25519"); +const bob = generateKeyPairSync("x25519"); +const secret: Buffer = diffieHellman({ privateKey: alice.privateKey, publicKey: bob.publicKey }); +console.log(alice.publicKey.asymmetricKeyType, secret.length, secret.equals(diffieHellman({ privateKey: bob.privateKey, publicKey: alice.publicKey }))); +const exported = alice.publicKey.export({ type: "spki", format: "pem" }); +const pem = typeof exported === "string" ? exported : exported.toString(); +const reread: KeyObject = createPublicKey(pem); +console.log(reread.equals(alice.publicKey), pem.split("\n")[0]); +const ec = generateKeyPairSync("ec", { namedCurve: "P-384" }); +const peer = generateKeyPairSync("ec", { namedCurve: "P-384" }); +console.log("ecdh", diffieHellman({ privateKey: ec.privateKey, publicKey: peer.publicKey }).length); +generateKeyPair("x25519", undefined, (err, publicKey, privateKey) => { + console.log("generated", err, publicKey.type, privateKey.asymmetricKeyType); + diffieHellman({ privateKey, publicKey: bob.publicKey }, (error, shared) => { + console.log("callback", error, shared.length); + }); +}); diff --git a/tests/fixtures/node-types/fenced.ts b/tests/fixtures/node-types/fenced.ts index 7c468de01..0d90e8cac 100644 --- a/tests/fixtures/node-types/fenced.ts +++ b/tests/fixtures/node-types/fenced.ts @@ -45,9 +45,8 @@ console.log(win32.sep); const u = new URL("https://example.com/x?a=1"); console.log(URL.createObjectURL(new Blob(["data"]))); u.searchParams.get("a"); -/* The one-shot zlib/raw/gzip codecs take Node's options (the deflateSync - * call lowers), while Brotli remains a member-qualified fence with the - * lowered family named. */ +/* The one-shot zlib/raw/gzip and Brotli codecs take Node's options (the + * deflateSync and brotliCompressSync calls lower). */ import { brotliCompressSync, deflateSync } from "zlib"; deflateSync("data", { level: 9, strategy: 1 }); brotliCompressSync(Buffer.from("data")); @@ -99,12 +98,9 @@ void callbackified; import { createECDH } from "node:crypto"; createECDH("sect163k1"); createECDH("prime256v1").computeSecret(Buffer.alloc(65), "hex"); -/* DiffieHellman's Object.prototype members, and crypto.diffieHellman over - * asymmetric KeyObjects. */ -import { createDiffieHellman, diffieHellman, type KeyObject } from "node:crypto"; +/* DiffieHellman's Object.prototype members. */ +import { createDiffieHellman } from "node:crypto"; createDiffieHellman(Buffer.from([23])).toString(); -declare const dhKey: KeyObject; -diffieHellman({ privateKey: dhKey, publicKey: dhKey }); /* WebCrypto beyond the secret-key slice: @types/node declares the whole * SubtleCrypto surface, and the methods outside the slice, such as * getPublicKey, fence by name. */ diff --git a/tests/fixtures/node-types/fs-url-fallible.ts b/tests/fixtures/node-types/fs-url-fallible.ts new file mode 100644 index 000000000..da86879d5 --- /dev/null +++ b/tests/fixtures/node-types/fs-url-fallible.ts @@ -0,0 +1,19 @@ +import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { pathToFileURL } from "node:url"; + +const dir = mkdtempSync(join(tmpdir(), "node-types-url-")); +const file = pathToFileURL(join(dir, "a.txt")); +writeFileSync(file, "hi"); +// existsSync answers false for a URL it cannot convert. +console.log(existsSync(file), existsSync(new URL("http://example.com/"))); +// fs.promises functions reject for a URL they cannot convert. +readFile(file, "utf8") + .then((text) => console.log(text)) + .then(() => readFile(new URL("file://example.com/a.txt"), "utf8")) + .catch((error: unknown) => { + if (error instanceof Error) console.log((error as NodeJS.ErrnoException).code); + rmSync(dir, { recursive: true }); + }); diff --git a/tests/fixtures/node-types/fs-url-path-fences.ts b/tests/fixtures/node-types/fs-url-path-fences.ts deleted file mode 100644 index 4ae142189..000000000 --- a/tests/fixtures/node-types/fs-url-path-fences.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { existsSync } from "node:fs"; -import { readFile, stat } from "node:fs/promises"; - -const url = new URL("file:///tmp/node-types-url-fences"); -// existsSync answers false for a URL it cannot convert. -console.log(existsSync(url)); -// The fs.promises functions reject for a URL they cannot convert. -void readFile(url, "utf8"); -void stat(url); diff --git a/tests/fixtures/npm/cases/hono-zod-default-hook/main.ts b/tests/fixtures/npm/cases/hono-zod-default-hook/main.ts new file mode 100644 index 000000000..9981e7154 --- /dev/null +++ b/tests/fixtures/npm/cases/hono-zod-default-hook/main.ts @@ -0,0 +1,38 @@ +// @hono/zod-validator without a hook: a failed check answers 400 with the +// whole safeParse result serialized, so the ZodError's own enumerable +// properties (name, then message) and their order reach the client. +import { zValidator } from "@hono/zod-validator"; +import { Hono } from "hono"; +import { z } from "zod"; + +const Item = z.object({ title: z.string().min(2), qty: z.number().int().positive() }); + +const app = new Hono(); +app.post("/items", zValidator("json", Item), (c) => { + const item = c.req.valid("json"); + return c.json({ created: item.title, qty: item.qty }, 201); +}); +app.get("/items", zValidator("query", z.object({ limit: z.coerce.number().int().max(5) })), (c) => { + return c.json({ limit: c.req.valid("query").limit }); +}); + +const post = (body: unknown) => + app.request("/items", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }); + +const show = async (label: string, res: Response | Promise) => { + const r = await res; + console.log(label, r.status, await r.text()); +}; + +await show("ok", post({ title: "pen", qty: 2 })); +await show("bad-title", post({ title: "p", qty: 2 })); +await show("bad-both", post({ title: 3, qty: -1 })); +await show("query-ok", app.request("/items?limit=4")); +await show("query-bad", app.request("/items?limit=9")); + +const result = Item.safeParse({ title: "x", qty: 0 }); +if (!result.success) { + const e = result.error; + console.log(e.name, e instanceof Error, Object.keys(e).join(",")); + console.log(JSON.stringify(e)); +} diff --git a/tests/fixtures/npm/cases/hono-zod-default-hook/package.json b/tests/fixtures/npm/cases/hono-zod-default-hook/package.json new file mode 100644 index 000000000..a734aaddb --- /dev/null +++ b/tests/fixtures/npm/cases/hono-zod-default-hook/package.json @@ -0,0 +1 @@ +{ "name": "hono-zod-default-hook-case", "private": true, "type": "module" } diff --git a/tests/fixtures/npm/cases/hono-zod-validator/lying.ts b/tests/fixtures/npm/cases/hono-zod-validator/lying.ts new file mode 100644 index 000000000..fa44c2e12 --- /dev/null +++ b/tests/fixtures/npm/cases/hono-zod-validator/lying.ts @@ -0,0 +1,42 @@ +// Native-only (decl-boundary.test.ts): `c.req.valid()` is a checked +// boundary. z.custom checks nothing, so a body its declared type +// misdescribes fails the cast with a path TypeError, which hono's error +// handler reports; Node does not check. A union discriminated by boolean +// literals keeps both arms. +import { zValidator } from "@hono/zod-validator"; +import { Hono } from "hono"; +import { z } from "zod"; + +const Liar = z.custom<{ n: number }>(); +const Outcome = z.discriminatedUnion("ok", [ + z.object({ ok: z.literal(true), value: z.number() }), + z.object({ ok: z.literal(false), reason: z.string() }), +]); + +const app = new Hono(); +app.onError((err, c) => c.text(`${err instanceof TypeError} ${err.message}`, 500)); +app.post("/liar", zValidator("json", Liar), (c) => c.text(String(c.req.valid("json").n + 1))); +app.post("/outcome", zValidator("json", Outcome), (c) => { + const outcome = c.req.valid("json"); + return c.text(outcome.ok ? `value ${outcome.value + 1}` : `reason ${outcome.reason}`); +}); + +const requests: [string, unknown][] = [ + ["/liar", { n: 1 }], + ["/liar", { n: "x" }], + ["/outcome", { ok: true, value: 41 }], + ["/outcome", { ok: false, reason: "late" }], +]; + +async function main(): Promise { + for (const [path, body] of requests) { + const res = await app.request(path, { + method: "POST", + body: JSON.stringify(body), + headers: { "content-type": "application/json" }, + }); + console.log(path, res.status, await res.text()); + } +} + +void main(); diff --git a/tests/fixtures/npm/cases/hono-zod-validator/main.ts b/tests/fixtures/npm/cases/hono-zod-validator/main.ts new file mode 100644 index 000000000..9de69c482 --- /dev/null +++ b/tests/fixtures/npm/cases/hono-zod-validator/main.ts @@ -0,0 +1,96 @@ +// A Hono app validated by @hono/zod-validator and zod 4.6, all compiled +// from their JavaScript: `c.req.valid(target)` hands the handler the +// parsed value, typed by the schema's z.infer through hono's declarations. +// Requests go through app.request, so no socket is involved. +import { zValidator } from "@hono/zod-validator"; +import { Hono, type Context } from "hono"; +import { z } from "zod"; + +const Book = z.strictObject({ + title: z.string().trim().min(1), + year: z.number().int().min(1450), + tags: z.array(z.string()).max(4).default([]), + rating: z.number().min(0).max(5).optional(), +}); +type Book = z.infer; +const ListQuery = z.object({ + minYear: z.coerce.number().int().optional(), + sort: z.enum(["title", "year"]).default("title"), + limit: z.coerce.number().int().min(1).max(10).default(3), +}); +const IdParam = z.object({ id: z.string().regex(/^b\d+$/) }); + +// Failed checks answer with the issues' paths and codes. +const reject = ( + result: { success: true } | { success: false; error: z.core.$ZodError }, + c: Context, +): Response | undefined => { + if (result.success) return undefined; + const paths = result.error.issues.map((i) => `${i.path.join(".")}:${i.code}`); + return c.json({ error: "invalid", paths }, 400); +}; + +const books = new Map(); +let nextId = 1; + +const app = new Hono(); +app.post( + "/books", + zValidator("json", Book, (result, c) => { + if (!result.success) { + const paths = result.error.issues.map((i) => `${i.path.join(".")}:${i.code}`); + return c.json({ error: "invalid", paths }, 400); + } + }), + (c) => { + const book = c.req.valid("json"); + book.tags.push("new"); + const id = `b${nextId++}`; + books.set(id, book); + const score = (book.rating ?? 0) * 2 + book.tags.length; + return c.json({ id, title: book.title.toUpperCase(), year: book.year, score }, 201); + }, +); +app.get("/books", zValidator("query", ListQuery, reject), (c) => { + const { minYear, sort, limit } = c.req.valid("query"); + const rows = [...books.values()] + .filter((b) => minYear === undefined || b.year >= minYear) + .sort((a, b) => (sort === "year" ? a.year - b.year : a.title < b.title ? -1 : 1)) + .slice(0, limit) + .map((b) => `${b.title}/${b.year}`); + return c.json({ sort, limit, rows }); +}); +app.get("/books/:id", zValidator("param", IdParam, reject), (c) => { + const id = c.req.valid("param").id; + const book = books.get(id); + if (book === undefined) return c.json({ error: "missing", id }, 404); + return c.json({ id, ...book }); +}); + +async function call(method: string, path: string, body?: unknown): Promise { + const init: RequestInit = { method }; + if (body !== undefined) { + init.body = JSON.stringify(body); + init.headers = { "content-type": "application/json" }; + } + const res = await app.request(path, init); + console.log(method, path, res.status, await res.text()); +} + +async function main(): Promise { + await call("POST", "/books", { title: " Dune ", year: 1965, tags: ["sf"], rating: 4.5 }); + await call("POST", "/books", { title: "Emma", year: 1815 }); + await call("POST", "/books", { title: "Ulysses", year: 1922, tags: ["modern"] }); + await call("POST", "/books", { title: "", year: 1200, extra: true }); + await call("POST", "/books", { title: "Bad", year: "x", tags: [1, 2, 3, 4, 5] }); + await call("GET", "/books"); + await call("GET", "/books?minYear=1900&sort=year&limit=5"); + await call("GET", "/books?limit=99"); + await call("GET", "/books/b2"); + await call("GET", "/books/b9"); + await call("GET", "/books/nope"); + const kept = books.get("b1")!; + console.log("stored", kept.tags.join(","), kept.title); +} + +void main(); diff --git a/tests/fixtures/npm/cases/hono-zod-validator/package.json b/tests/fixtures/npm/cases/hono-zod-validator/package.json new file mode 100644 index 000000000..27e5f1b4b --- /dev/null +++ b/tests/fixtures/npm/cases/hono-zod-validator/package.json @@ -0,0 +1 @@ +{ "name": "hono-zod-validator-case", "private": true, "type": "module" } diff --git a/tests/fixtures/npm/cases/zod4-boundary/main.ts b/tests/fixtures/npm/cases/zod4-boundary/main.ts new file mode 100644 index 000000000..b4079d1be --- /dev/null +++ b/tests/fixtures/npm/cases/zod4-boundary/main.ts @@ -0,0 +1,146 @@ +// Values that cross from zod 4.6 (compiled from its JavaScript) into code +// typed by zod's declarations: z.infer, z.input and z.output, identity, +// aliasing and mutation after parse, values zod hands back unchanged, +// callbacks zod calls, typed array work, and caught failures. +import { z } from "zod"; + +const show = (label: string, value: unknown): void => { + console.log(label + " " + JSON.stringify(value)); +}; + +// Typed records: field reads, writes and array work on parsed data. +const Item = z.object({ + sku: z.string(), + price: z.number().nonnegative(), + qty: z.number().int(), + tags: z.array(z.string()).default([]), + note: z.string().optional(), + owner: z.string().nullable(), +}); +type Item = z.infer; +const items: Item[] = z.array(Item).parse([ + { sku: "a", price: 2.5, qty: 4, tags: ["x"], owner: null }, + { sku: "b", price: 10, qty: 1, owner: "ann", note: "fragile" }, + { sku: "c", price: 0.5, qty: 10, tags: ["x", "y"], owner: null }, +]); +const total = items.reduce((sum, it) => sum + it.price * it.qty, 0); +console.log("total", total); +const byTag = new Map(); +for (const it of items) for (const t of it.tags) byTag.set(t, (byTag.get(t) ?? 0) + it.qty); +console.log("byTag", [...byTag].map(([k, v]) => `${k}=${v}`).join(",")); +const sorted = [...items].sort((a, b) => b.price - a.price).map((it) => it.sku); +console.log("sorted", sorted.join(",")); +console.log("optional", items.map((it) => `${it.note ?? "-"}/${it.owner ?? "nobody"}`).join(",")); +console.log("keys", items.map((it) => Object.keys(it).join("+")).join(" ")); + +// Mutation after parse is visible through every reference. +const first = items[0]!; +first.qty += 1; +first.tags.push("z"); +console.log("mutated", items[0]!.qty, items[0]!.tags.join("|"), first === items[0]); +show("item", items[0]); + +// Strict objects reject unknown keys; the error is zod's, caught. +const Strict = z.strictObject({ name: z.string().trim().min(1), email: z.email() }); +type Strict = z.infer; +const person: Strict = Strict.parse({ name: " Ada ", email: "ada@example.com" }); +console.log("strict", person.name.length, person.email.toUpperCase()); +const rejected = Strict.safeParse({ name: "x", email: "x@example.com", extra: 1 }); +console.log("strict bad", rejected.success, rejected.error?.issues.map((i) => i.code).join(",")); + +// z.input and z.output differ across a transform and a default. +const Config = z.object({ + port: z.string().transform((s) => Number(s)), + host: z.string().default("localhost"), + debug: z.enum(["true", "false"]).transform((v) => v === "true"), +}); +type ConfigIn = z.input; +type ConfigOut = z.output; +const raw: ConfigIn = { port: "8080", debug: "true" }; +const config: ConfigOut = Config.parse(raw); +console.log("config", config.port + 1, config.host, config.debug, typeof config.port); + +// A value zod hands back unchanged keeps its identity and its mutations. +const Point = z.custom<{ x: number; y: number }>(); +const original = { x: 1, y: 2 }; +const same = Point.parse(original); +same.x = 10; +console.log("custom", original.x, same === original); +original.y = 20; +console.log("custom back", same.y); + +// A shared object returned by a transform: every parse sees the same one. +const shared = { hits: 0 }; +const Counter = z.string().transform(() => shared); +const c1 = Counter.parse("a"); +const c2 = Counter.parse("b"); +c1.hits += 1; +console.log("shared", c2.hits, shared.hits, c1 === c2); + +// A refine callback reads typed fields of the value zod checks. +const Range = z + .object({ lo: z.number(), hi: z.number() }) + .refine((r) => r.lo <= r.hi, { message: "lo must not exceed hi" }); +console.log("range ok", JSON.stringify(Range.parse({ lo: 1, hi: 3 }))); +const bad = Range.safeParse({ lo: 5, hi: 3 }); +console.log("range bad", bad.success ? "?" : bad.error.issues[0]?.message); + +// safeParse data, a discriminated union, and loose objects' extra keys. +const Event = z.discriminatedUnion("type", [ + z.object({ type: z.literal("click"), x: z.number(), y: z.number() }), + z.object({ type: z.literal("key"), key: z.string() }), +]); +type Event = z.infer; +const describe = (e: Event): string => (e.type === "click" ? `click@${e.x},${e.y}` : `key:${e.key}`); +const r = Event.safeParse({ type: "key", key: "Enter" }); +if (r.success) console.log("event", describe(r.data)); +const events = z.array(Event).parse([ + { type: "click", x: 1, y: 2 }, + { type: "key", key: "a" }, +]); +console.log("events", events.map(describe).join(" ")); +const Loose = z.looseObject({ id: z.number() }); +const loose = Loose.parse({ extra: true, id: 3, more: "m" }); +console.log("loose", loose.id, Object.keys(loose).join(","), JSON.stringify(loose)); + +// A union discriminated by boolean literals, read through a safeParse +// result narrowed to its success arm. +const Outcome = z.discriminatedUnion("ok", [ + z.object({ ok: z.literal(true), value: z.number() }), + z.object({ ok: z.literal(false), reason: z.string() }), +]); +type Outcome = z.infer; +const outcomes: Outcome[] = []; +for (const input of [{ ok: true, value: 41 }, { ok: false, reason: "late" }, { ok: "maybe" }]) { + const parsed = Outcome.safeParse(input); + if (parsed.success) outcomes.push(parsed.data); + else console.log("outcome bad", parsed.error.issues.length); +} +console.log("outcomes", outcomes.map((o) => (o.ok ? `value ${o.value + 1}` : `reason ${o.reason}`)).join(", ")); +const Flags = z.object({ beta: z.boolean().optional(), mode: z.enum(["a", "b"]) }); +const flagged = Flags.safeParse({ beta: true, mode: "b" }); +if (flagged.success) { + const flags = flagged.data; + console.log("flags", flags.beta === true ? "beta on" : "beta off", flags.mode); +} + +// Tuples, brands and coerced query-style input. +const Pair = z.tuple([z.string(), z.number()]); +const [label, count] = Pair.parse(["n", 4]); +console.log("tuple", label, count + 1); +const UserId = z.string().brand<"UserId">(); +const uid = UserId.parse("u_1"); +console.log("brand", uid.length, uid); +const Query = z.object({ page: z.coerce.number().int().min(1).default(1), q: z.string().optional() }); +const query = Query.parse({ page: "3" }); +console.log("query", query.page * 10, query.q === undefined, "q" in query); + +// async parse +async function main(): Promise { + const it = await Item.parseAsync({ sku: "d", price: 1, qty: 2, owner: null }); + console.log("async", it.sku, it.price * it.qty, it.tags.length); + const failed = await Item.safeParseAsync({ sku: 1, price: -1, qty: 0.5, owner: null }); + console.log("async bad", failed.success, failed.error?.issues.map((i) => i.path.join(".")).join(",")); +} + +void main(); diff --git a/tests/fixtures/npm/cases/zod4-boundary/package.json b/tests/fixtures/npm/cases/zod4-boundary/package.json new file mode 100644 index 000000000..39bd81280 --- /dev/null +++ b/tests/fixtures/npm/cases/zod4-boundary/package.json @@ -0,0 +1 @@ +{ "name": "zod4-boundary-case", "private": true, "type": "module" } diff --git a/tests/fixtures/npm/cases/zod4-record/main.ts b/tests/fixtures/npm/cases/zod4-record/main.ts new file mode 100644 index 000000000..b43eaebab --- /dev/null +++ b/tests/fixtures/npm/cases/zod4-record/main.ts @@ -0,0 +1,37 @@ +// zod 4's z.record and z.partialRecord accept only plain objects. Its check +// reads the value's constructor, that constructor's prototype, and whether +// the prototype has its own isPrototypeOf, so it needs Object.prototype's +// reflection to match Node. +import { z } from "zod"; + +const Scores = z.record(z.string(), z.number().int()); +const Flags = z.record(z.enum(["a", "b"]), z.boolean()); +const Partial = z.partialRecord(z.enum(["x", "y", "z"]), z.string()); +const Nested = z.object({ name: z.string(), meta: z.record(z.string(), z.record(z.string(), z.number())) }); + +const show = (label: string, schema: z.ZodType, value: unknown): void => { + const r = schema.safeParse(value); + if (r.success) { + console.log(label, "ok", JSON.stringify(r.data)); + } else { + console.log(label, "fail", r.error.issues.map((i) => `${i.path.join(".")}:${i.code}:${i.message}`).join(" | ")); + } +}; + +show("scores", Scores, { alice: 3, bob: 7 }); +show("scores-empty", Scores, {}); +show("scores-bad-value", Scores, { alice: 1.5 }); +show("scores-array", Scores, [1, 2]); +show("scores-null-proto", Scores, Object.assign(Object.create(null), { k: 1 })); +show("scores-parsed", Scores, JSON.parse('{"x":1,"y":2}')); +show("flags", Flags, { a: true, b: false }); +show("flags-missing", Flags, { a: true }); +show("flags-extra", Flags, { a: true, b: true, c: false }); +show("partial", Partial, { y: "why" }); +show("nested", Nested, { name: "n", meta: { one: { two: 2 } } }); +show("nested-bad", Nested, { name: "n", meta: { one: [] } }); + +class Point { + x = 1; +} +show("class-instance", Scores, new Point()); diff --git a/tests/fixtures/npm/cases/zod4-record/package.json b/tests/fixtures/npm/cases/zod4-record/package.json new file mode 100644 index 000000000..82c365084 --- /dev/null +++ b/tests/fixtures/npm/cases/zod4-record/package.json @@ -0,0 +1 @@ +{ "name": "zod4-record-case", "private": true, "type": "module" } diff --git a/tests/fixtures/npm/node_modules/@hono/zod-validator/dist/index.d.mts b/tests/fixtures/npm/node_modules/@hono/zod-validator/dist/index.d.mts new file mode 100644 index 000000000..6028477a3 --- /dev/null +++ b/tests/fixtures/npm/node_modules/@hono/zod-validator/dist/index.d.mts @@ -0,0 +1,43 @@ +import { InferInput } from "hono/validator"; +import { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from "hono"; +import * as v3 from "zod/v3"; +import { ZodSafeParseResult } from "zod/v4"; +import * as v4 from "zod/v4/core"; +//#region src/index.d.ts +type ZodSchema = v3.ZodType | v4.$ZodType; +type ZodError = T extends v4.$ZodType ? v4.$ZodError> : v3.ZodError; +type ZodSafeParseResult$1 = T3 extends v4.$ZodType ? ZodSafeParseResult : v3.SafeParseReturnType; +type zInput = T extends v3.ZodType ? v3.input : T extends v4.$ZodType ? v4.input : never; +type zOutput = T extends v3.ZodType ? v3.output : T extends v4.$ZodType ? v4.output : never; +type zInfer = T extends v3.ZodType ? v3.infer : T extends v4.$ZodType ? v4.infer : never; +type ZodValidatorFailureBody = T extends v4.$ZodType ? Extract>, { + success: false; +}> : T extends v3.ZodType ? Extract, v3.output>, { + success: false; +}> : never; +type ZodValidatorFailureResponse = Response & TypedResponse, 400, "json">; +type Hook = (result: ({ + success: true; + data: T; +} | { + success: false; + error: ZodError; + data: T; +}) & { + target: Target; +}, c: Context) => Response | void | TypedResponse | Promise>; +type HasUndefined = undefined extends T ? true : false; +type ExtractValidationResponse = VF extends ((value: any, c: any) => infer R) ? R extends Promise ? PR extends TypedResponse ? Response & TypedResponse : PR extends Response ? PR : PR extends undefined ? never : never : R extends TypedResponse ? Response & TypedResponse : R extends Response ? R : R extends undefined ? never : never : never; +type ValidationFunctionOption = { + validationFunction: (schema: T, value: ValidationTargets[Target]) => ZodSafeParseResult$1 | Promise>; +}; +type DefaultInput = { + in: HasUndefined extends true ? { [K in Target]?: [In] extends [ValidationTargets[K]] ? In : InferInput; } : { [K in Target]: [In] extends [ValidationTargets[K]] ? In : InferInput; }; + out: { [K in Target]: Out; }; +}; +declare function zValidatorFunction, Out = zOutput, I extends Input = DefaultInput, V extends I = I>(target: Target, schema: T, hook?: undefined, options?: ValidationFunctionOption): MiddlewareHandler>; +declare function zValidatorFunction, In = zInput, Out = zOutput, I extends Input = DefaultInput, V extends I = I, InferredValue = zInfer>(target: Target, schema: T, hook?: HookFn, options?: ValidationFunctionOption): MiddlewareHandler>; +declare const zValidator: typeof zValidatorFunction; +//#endregion +export { Hook, zValidator }; +//# sourceMappingURL=index.d.mts.map \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/@hono/zod-validator/dist/index.mjs b/tests/fixtures/npm/node_modules/@hono/zod-validator/dist/index.mjs new file mode 100644 index 000000000..946d23d2a --- /dev/null +++ b/tests/fixtures/npm/node_modules/@hono/zod-validator/dist/index.mjs @@ -0,0 +1,28 @@ +import { validator } from "hono/validator"; +//#region src/index.ts +function zValidatorFunction(target, schema, hook, options) { + const caseInsensitiveKeymap = target === "header" && ("_def" in schema || "_zod" in schema) ? Object.fromEntries(Object.keys("in" in schema ? schema.in.shape : schema.shape).map((key) => [key.toLowerCase(), key])) : void 0; + return validator(target, async (value, c) => { + let validatorValue = value; + if (caseInsensitiveKeymap) validatorValue = Object.fromEntries(Object.entries(value).map(([key, value]) => [caseInsensitiveKeymap[key] || key, value])); + const result = options && options.validationFunction ? await options.validationFunction(schema, validatorValue) : await schema.safeParseAsync(validatorValue); + if (hook) { + const hookResult = await hook({ + data: validatorValue, + ...result, + target + }, c); + if (hookResult) { + if (hookResult instanceof Response) return hookResult; + if ("response" in hookResult) return hookResult.response; + } + } + if (!result.success) return c.json(result, 400); + return result.data; + }); +} +const zValidator = zValidatorFunction; +//#endregion +export { zValidator }; + +//# sourceMappingURL=index.mjs.map \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/@hono/zod-validator/package.json b/tests/fixtures/npm/node_modules/@hono/zod-validator/package.json new file mode 100644 index 000000000..31355f555 --- /dev/null +++ b/tests/fixtures/npm/node_modules/@hono/zod-validator/package.json @@ -0,0 +1,14 @@ +{ + "name": "@hono/zod-validator", + "version": "0.9.1", + "description": "Test fixture: the ESM build and its declarations.", + "type": "module", + "license": "MIT", + "types": "./dist/index.d.mts", + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs" + } + } +} diff --git a/tests/fixtures/npm/node_modules/hono/dist/compose.js b/tests/fixtures/npm/node_modules/hono/dist/compose.js new file mode 100644 index 000000000..ec576df0f --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/compose.js @@ -0,0 +1,50 @@ +//#region src/compose.ts +/** +* Compose middleware functions into a single function based on `koa-compose` package. +* +* @template E - The environment type. +* +* @param {[[Function, unknown], unknown][] | [[Function]][]} middleware - An array of middleware functions and their corresponding parameters. +* @param {ErrorHandler} [onError] - An optional error handler function. +* @param {NotFoundHandler} [onNotFound] - An optional not-found handler function. +* +* @returns {(context: Context, next?: Next) => Promise} - A composed middleware function. +*/ +const compose = (middleware, onError, onNotFound) => { + return (context, next) => { + let index = -1; + return dispatch(0); + /** + * Dispatch the middleware functions. + * + * @param {number} i - The current index in the middleware array. + * + * @returns {Promise} - A promise that resolves to the context. + */ + async function dispatch(i) { + if (i <= index) throw new Error("next() called multiple times"); + index = i; + let res; + let isError = false; + let handler; + if (middleware[i]) { + handler = middleware[i][0][0]; + context.req.routeIndex = i; + } else handler = i === middleware.length && next || void 0; + if (handler) try { + res = await handler(context, () => dispatch(i + 1)); + } catch (err) { + if (err instanceof Error && onError) { + context.error = err; + res = await onError(err, context); + isError = true; + } else throw err; + } + else if (context.finalized === false && onNotFound) res = await onNotFound(context); + if (res && (context.finalized === false || isError)) context.res = res; + return context; + } + }; +}; +//#endregion +export { compose }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/context.js b/tests/fixtures/npm/node_modules/hono/dist/context.js new file mode 100644 index 000000000..7f8184af5 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/context.js @@ -0,0 +1,381 @@ +import { HonoRequest } from "./request.js"; +import { HtmlEscapedCallbackPhase, resolveCallback } from "./utils/html.js"; +//#region src/context.ts +const TEXT_PLAIN = "text/plain; charset=UTF-8"; +const setDefaultContentType = (contentType, headers) => { + return { + "Content-Type": contentType, + ...headers + }; +}; +const createResponseInstance = (body, init) => new Response(body, init); +var Context = class { + #rawRequest; + #req; + /** + * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers. + * + * @see {@link https://hono.dev/docs/api/context#env} + * + * @example + * ```ts + * // Environment object for Cloudflare Workers + * app.get('*', async c => { + * const counter = c.env.COUNTER + * }) + * ``` + */ + env = {}; + #var; + finalized = false; + /** + * `.error` can get the error object from the middleware if the Handler throws an error. + * + * @see {@link https://hono.dev/docs/api/context#error} + * + * @example + * ```ts + * app.use('*', async (c, next) => { + * await next() + * if (c.error) { + * // do something... + * } + * }) + * ``` + */ + error; + #status; + #executionCtx; + #res; + #layout; + #renderer; + #notFoundHandler; + #preparedHeaders; + #matchResult; + #path; + /** + * Creates an instance of the Context class. + * + * @param req - The Request object. + * @param options - Optional configuration options for the context. + */ + constructor(req, options) { + this.#rawRequest = req; + if (options) { + this.#executionCtx = options.executionCtx; + this.env = options.env; + this.#notFoundHandler = options.notFoundHandler; + this.#path = options.path; + this.#matchResult = options.matchResult; + } + } + /** + * `.req` is the instance of {@link HonoRequest}. + */ + get req() { + this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult); + return this.#req; + } + /** + * @see {@link https://hono.dev/docs/api/context#event} + * The FetchEvent associated with the current request. + * + * @throws Will throw an error if the context does not have a FetchEvent. + */ + get event() { + if (this.#executionCtx && "respondWith" in this.#executionCtx) return this.#executionCtx; + else throw Error("This context has no FetchEvent"); + } + /** + * @see {@link https://hono.dev/docs/api/context#executionctx} + * The ExecutionContext associated with the current request. + * + * @throws Will throw an error if the context does not have an ExecutionContext. + */ + get executionCtx() { + if (this.#executionCtx) return this.#executionCtx; + else throw Error("This context has no ExecutionContext"); + } + /** + * @see {@link https://hono.dev/docs/api/context#res} + * The Response object for the current request. + */ + get res() { + return this.#res ||= createResponseInstance(null, { headers: this.#preparedHeaders ??= new Headers() }); + } + /** + * Sets the Response object for the current request. + * + * @param _res - The Response object to set. + */ + set res(_res) { + if (this.#res && _res) { + _res = createResponseInstance(_res.body, _res); + for (const [k, v] of this.#res.headers.entries()) { + if (k === "content-type") continue; + if (k === "set-cookie") { + const cookies = this.#res.headers.getSetCookie(); + _res.headers.delete("set-cookie"); + for (const cookie of cookies) _res.headers.append("set-cookie", cookie); + } else _res.headers.set(k, v); + } + } + this.#res = _res; + this.finalized = true; + } + /** + * `.render()` can create a response within a layout. + * + * @see {@link https://hono.dev/docs/api/context#render-setrenderer} + * + * @example + * ```ts + * app.get('/', (c) => { + * return c.render('Hello!') + * }) + * ``` + */ + render = (...args) => { + this.#renderer ??= (content) => this.html(content); + return this.#renderer(...args); + }; + /** + * Sets the layout for the response. + * + * @param layout - The layout to set. + * @returns The layout function. + */ + setLayout = (layout) => this.#layout = layout; + /** + * Gets the current layout for the response. + * + * @returns The current layout function. + */ + getLayout = () => this.#layout; + /** + * `.setRenderer()` can set the layout in the custom middleware. + * + * @see {@link https://hono.dev/docs/api/context#render-setrenderer} + * + * @example + * ```tsx + * app.use('*', async (c, next) => { + * c.setRenderer((content) => { + * return c.html( + * + * + *

{content}

+ * + * + * ) + * }) + * await next() + * }) + * ``` + */ + setRenderer = (renderer) => { + this.#renderer = renderer; + }; + /** + * `.header()` can set headers. + * + * @see {@link https://hono.dev/docs/api/context#header} + * + * @example + * ```ts + * app.get('/welcome', (c) => { + * // Set headers + * c.header('X-Message', 'Hello!') + * c.header('Content-Type', 'text/plain') + * + * // Append multiple headers using the append option (e.g. Vary) + * c.header('Vary', 'Accept-Encoding', { append: true }) + * c.header('Vary', 'User-Agent', { append: true }) + * + * return c.body('Thank you for coming') + * }) + * ``` + */ + header = (name, value, options) => { + if (this.finalized) this.#res = createResponseInstance(this.#res.body, this.#res); + const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers(); + if (value === void 0) headers.delete(name); + else if (options?.append) headers.append(name, value); + else headers.set(name, value); + }; + status = (status) => { + this.#status = status; + }; + /** + * `.set()` can set the value specified by the key. + * + * @see {@link https://hono.dev/docs/api/context#set-get} + * + * @example + * ```ts + * app.use('*', async (c, next) => { + * c.set('message', 'Hono is hot!!') + * await next() + * }) + * ``` + */ + set = (key, value) => { + this.#var ??= /* @__PURE__ */ new Map(); + this.#var.set(key, value); + }; + /** + * `.get()` can use the value specified by the key. + * + * @see {@link https://hono.dev/docs/api/context#set-get} + * + * @example + * ```ts + * app.get('/', (c) => { + * const message = c.get('message') + * return c.text(`The message is "${message}"`) + * }) + * ``` + */ + get = (key) => { + return this.#var ? this.#var.get(key) : void 0; + }; + /** + * `.var` can access the value of a variable. + * + * @see {@link https://hono.dev/docs/api/context#var} + * + * @example + * ```ts + * const result = c.var.client.oneMethod() + * ``` + */ + get var() { + if (!this.#var) return {}; + return Object.fromEntries(this.#var); + } + #newResponse(data, arg, headers) { + let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders; + if (typeof arg === "object" && arg.headers) { + responseHeaders ??= new Headers(); + for (const [key, value] of new Headers(arg.headers)) if (key === "set-cookie") responseHeaders.append(key, value); + else responseHeaders.set(key, value); + } + if (headers) { + if (!responseHeaders) { + let count = 0; + for (const k in headers) if (++count > 1 || typeof headers[k] !== "string") { + responseHeaders = new Headers(); + break; + } + } + if (responseHeaders) for (const k in headers) { + const v = headers[k]; + if (typeof v === "string") responseHeaders.set(k, v); + else { + responseHeaders.delete(k); + for (const v2 of v) responseHeaders.append(k, v2); + } + } + } + const status = typeof arg === "number" ? arg : arg?.status ?? this.#status; + return createResponseInstance(data, { + status, + headers: responseHeaders ?? headers + }); + } + newResponse = (...args) => this.#newResponse(...args); + /** + * `.body()` can return the HTTP response. + * You can set headers with `.header()` and set HTTP status code with `.status`. + * This can also be set in `.text()`, `.json()` and so on. + * + * @see {@link https://hono.dev/docs/api/context#body} + * + * @example + * ```ts + * app.get('/welcome', (c) => { + * // Set headers + * c.header('X-Message', 'Hello!') + * c.header('Content-Type', 'text/plain') + * // Set HTTP status code + * c.status(201) + * + * // Return the response body + * return c.body('Thank you for coming') + * }) + * ``` + */ + body = (data, arg, headers) => this.#newResponse(data, arg, headers); + /** + * `.text()` can render text as `Content-Type:text/plain`. + * + * @see {@link https://hono.dev/docs/api/context#text} + * + * @example + * ```ts + * app.get('/say', (c) => { + * return c.text('Hello!') + * }) + * ``` + */ + text = (text, arg, headers) => { + return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(text, arg, setDefaultContentType(TEXT_PLAIN, headers)); + }; + /** + * `.json()` can render JSON as `Content-Type:application/json`. + * + * @see {@link https://hono.dev/docs/api/context#json} + * + * @example + * ```ts + * app.get('/api', (c) => { + * return c.json({ message: 'Hello!' }) + * }) + * ``` + */ + json = (object, arg, headers) => { + return this.#newResponse(JSON.stringify(object), arg, setDefaultContentType("application/json", headers)); + }; + html = (html, arg, headers) => { + const res = (html) => this.#newResponse(html, arg, setDefaultContentType("text/html; charset=UTF-8", headers)); + return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html); + }; + /** + * `.redirect()` can Redirect, default status code is 302. + * + * @see {@link https://hono.dev/docs/api/context#redirect} + * + * @example + * ```ts + * app.get('/redirect', (c) => { + * return c.redirect('/') + * }) + * app.get('/redirect-permanently', (c) => { + * return c.redirect('/', 301) + * }) + * ``` + */ + redirect = (location, status) => { + const locationString = String(location); + this.header("Location", !/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString)); + return this.newResponse(null, status ?? 302); + }; + /** + * `.notFound()` can return the Not Found Response. + * + * @see {@link https://hono.dev/docs/api/context#notfound} + * + * @example + * ```ts + * app.get('/notfound', (c) => { + * return c.notFound() + * }) + * ``` + */ + notFound = () => { + this.#notFoundHandler ??= () => createResponseInstance(); + return this.#notFoundHandler(this); + }; +}; +//#endregion +export { Context, TEXT_PLAIN }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/helper/cookie/index.js b/tests/fixtures/npm/node_modules/hono/dist/helper/cookie/index.js new file mode 100644 index 000000000..48f9cbfb7 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/helper/cookie/index.js @@ -0,0 +1,82 @@ +import { parse, parseSigned, serialize, serializeSigned } from "../../utils/cookie.js"; +//#region src/helper/cookie/index.ts +const getCookie = (c, key, prefix) => { + const cookie = c.req.raw.headers.get("Cookie"); + if (typeof key === "string") { + if (!cookie) return; + let finalKey = key; + if (prefix === "secure") finalKey = "__Secure-" + key; + else if (prefix === "host") finalKey = "__Host-" + key; + return parse(cookie, finalKey)[finalKey]; + } + if (!cookie) return {}; + return parse(cookie); +}; +const getSignedCookie = async (c, secret, key, prefix) => { + const cookie = c.req.raw.headers.get("Cookie"); + if (typeof key === "string") { + if (!cookie) return; + let finalKey = key; + if (prefix === "secure") finalKey = "__Secure-" + key; + else if (prefix === "host") finalKey = "__Host-" + key; + return (await parseSigned(cookie, secret, finalKey))[finalKey]; + } + if (!cookie) return {}; + return await parseSigned(cookie, secret); +}; +const generateCookie = (name, value, opt) => { + let cookie; + if (opt?.prefix === "secure") cookie = serialize("__Secure-" + name, value, { + path: "/", + ...opt, + secure: true + }); + else if (opt?.prefix === "host") cookie = serialize("__Host-" + name, value, { + ...opt, + path: "/", + secure: true, + domain: void 0 + }); + else cookie = serialize(name, value, { + path: "/", + ...opt + }); + return cookie; +}; +const setCookie = (c, name, value, opt) => { + const cookie = generateCookie(name, value, opt); + c.header("Set-Cookie", cookie, { append: true }); +}; +const generateSignedCookie = async (name, value, secret, opt) => { + let cookie; + if (opt?.prefix === "secure") cookie = await serializeSigned("__Secure-" + name, value, secret, { + path: "/", + ...opt, + secure: true + }); + else if (opt?.prefix === "host") cookie = await serializeSigned("__Host-" + name, value, secret, { + ...opt, + path: "/", + secure: true, + domain: void 0 + }); + else cookie = await serializeSigned(name, value, secret, { + path: "/", + ...opt + }); + return cookie; +}; +const setSignedCookie = async (c, name, value, secret, opt) => { + const cookie = await generateSignedCookie(name, value, secret, opt); + c.header("set-cookie", cookie, { append: true }); +}; +const deleteCookie = (c, name, opt) => { + const deletedCookie = getCookie(c, name, opt?.prefix); + setCookie(c, name, "", { + ...opt, + maxAge: 0 + }); + return deletedCookie; +}; +//#endregion +export { deleteCookie, generateCookie, generateSignedCookie, getCookie, getSignedCookie, setCookie, setSignedCookie }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/hono-base.js b/tests/fixtures/npm/node_modules/hono/dist/hono-base.js new file mode 100644 index 000000000..7bc399a89 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/hono-base.js @@ -0,0 +1,350 @@ +import { compose } from "./compose.js"; +import { getPath, getPathNoStrict, mergePath } from "./utils/url.js"; +import { Context } from "./context.js"; +import { METHODS } from "./router.js"; +import { COMPOSED_HANDLER } from "./utils/constants.js"; +//#region src/hono-base.ts +/** +* @module +* This module is the base module for the Hono object. +*/ +const notFoundHandler = (c) => { + return c.text("404 Not Found", 404); +}; +const errorHandler = (err, c) => { + if ("getResponse" in err) { + const res = err.getResponse(); + return c.newResponse(res.body, res); + } + console.error(err); + return c.text("Internal Server Error", 500); +}; +var Hono = class Hono { + get; + post; + put; + delete; + options; + patch; + query; + all; + on; + use; + router; + getPath; + _basePath = "/"; + #path = "/"; + routes = []; + constructor(options = {}) { + [...METHODS, "all"].forEach((method) => { + this[method] = (args1, ...args) => { + const methodName = method.toUpperCase(); + if (typeof args1 === "string") this.#path = args1; + else this.#addRoute(methodName, this.#path, args1); + args.forEach((handler) => { + this.#addRoute(methodName, this.#path, handler); + }); + return this; + }; + }); + this.on = (method, path, ...handlers) => { + for (const p of [path].flat()) { + this.#path = p; + for (const m of [method].flat()) { + const methodName = m.toUpperCase(); + for (const handler of handlers) this.#addRoute(methodName, this.#path, handler); + } + } + return this; + }; + this.use = (arg1, ...handlers) => { + if (typeof arg1 === "string") this.#path = arg1; + else { + this.#path = "*"; + handlers.unshift(arg1); + } + handlers.forEach((handler) => { + this.#addRoute("ALL", this.#path, handler); + }); + return this; + }; + const { strict, ...optionsWithoutStrict } = options; + Object.assign(this, optionsWithoutStrict); + this.getPath = strict ?? true ? options.getPath ?? getPath : getPathNoStrict; + } + #clone() { + const clone = new Hono({ + router: this.router, + getPath: this.getPath + }); + clone.errorHandler = this.errorHandler; + clone.#notFoundHandler = this.#notFoundHandler; + clone.routes = this.routes; + return clone; + } + #notFoundHandler = notFoundHandler; + errorHandler = errorHandler; + /** + * `.route()` allows grouping other Hono instance in routes. + * + * @see {@link https://hono.dev/docs/api/routing#grouping} + * + * @param {string} path - base Path + * @param {Hono} app - other Hono instance + * @returns {Hono} routed Hono instance + * + * @example + * ```ts + * const app = new Hono() + * const app2 = new Hono() + * + * app2.get("/user", (c) => c.text("user")) + * app.route("/api", app2) // GET /api/user + * ``` + */ + route(path, app) { + const subApp = this.basePath(path); + app.routes.map((r) => { + let handler; + if (app.errorHandler === errorHandler) handler = r.handler; + else { + handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res; + handler[COMPOSED_HANDLER] = r.handler; + } + subApp.#addRoute(r.method, r.path, handler, r.basePath); + }); + return this; + } + /** + * `.basePath()` allows base paths to be specified. + * + * @see {@link https://hono.dev/docs/api/routing#base-path} + * + * @param {string} path - base Path + * @returns {Hono} changed Hono instance + * + * @example + * ```ts + * const api = new Hono().basePath('/api') + * ``` + */ + basePath(path) { + const subApp = this.#clone(); + subApp._basePath = mergePath(this._basePath, path); + return subApp; + } + /** + * `.onError()` handles an error and returns a customized Response. + * + * @see {@link https://hono.dev/docs/api/hono#error-handling} + * + * @param {ErrorHandler} handler - request Handler for error + * @returns {Hono} changed Hono instance + * + * @example + * ```ts + * app.onError((err, c) => { + * console.error(`${err}`) + * return c.text('Custom Error Message', 500) + * }) + * ``` + */ + onError = (handler) => { + this.errorHandler = handler; + return this; + }; + /** + * `.notFound()` allows you to customize a Not Found Response. + * + * @see {@link https://hono.dev/docs/api/hono#not-found} + * + * @param {NotFoundHandler} handler - request handler for not-found + * @returns {Hono} changed Hono instance + * + * @example + * ```ts + * app.notFound((c) => { + * return c.text('Custom 404 Message', 404) + * }) + * ``` + */ + notFound = (handler) => { + this.#notFoundHandler = handler; + return this; + }; + /** + * `.mount()` allows you to mount applications built with other frameworks into your Hono application. + * + * @deprecated Use `mount()` from `hono/mount` instead. `.mount()` will be removed in v5. + * + * @see {@link https://hono.dev/docs/api/hono#mount} + * + * @param {string} path - base Path + * @param {Function} applicationHandler - other Request Handler + * @param {MountOptions} [options] - options of `.mount()` + * @returns {Hono} mounted Hono instance + * + * @example + * ```ts + * import { Router as IttyRouter } from 'itty-router' + * import { Hono } from 'hono' + * // Create itty-router application + * const ittyRouter = IttyRouter() + * // GET /itty-router/hello + * ittyRouter.get('/hello', () => new Response('Hello from itty-router')) + * + * const app = new Hono() + * app.mount('/itty-router', ittyRouter.handle) + * ``` + * + * @example + * ```ts + * const app = new Hono() + * // Send the request to another application without modification. + * app.mount('/app', anotherApp, { + * replaceRequest: (req) => req, + * }) + * ``` + */ + mount(path, applicationHandler, options) { + let replaceRequest; + let optionHandler; + if (options) { + if (typeof options === "function") optionHandler = options; + else { + optionHandler = options.optionHandler; + if (options.replaceRequest === false) replaceRequest = (request) => request; + else replaceRequest = options.replaceRequest; + } + } + const getOptions = optionHandler ? (c) => { + const options = optionHandler(c); + return Array.isArray(options) ? options : [options]; + } : (c) => { + let executionContext = void 0; + try { + executionContext = c.executionCtx; + } catch {} + return [c.env, executionContext]; + }; + replaceRequest ||= (() => { + const mergedPath = mergePath(this._basePath, path); + const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length; + return (request) => { + const url = new URL(request.url); + url.pathname = this.getPath(request).slice(pathPrefixLength) || "/"; + return new Request(url, request); + }; + })(); + const handler = async (c, next) => { + const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c)); + if (res) return res; + await next(); + }; + this.#addRoute("ALL", mergePath(path, "*"), handler); + return this; + } + #addRoute(method, path, handler, baseRoutePath) { + path = mergePath(this._basePath, path); + const r = { + basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath, + path, + method, + handler + }; + this.router.add(method, path, [handler, r]); + this.routes.push(r); + } + #handleError(err, c) { + if (err instanceof Error) return this.errorHandler(err, c); + throw err; + } + #dispatch(request, executionCtx, env, method) { + if (method === "HEAD") return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))(); + const path = this.getPath(request, { env }); + const matchResult = this.router.match(method, path); + const c = new Context(request, { + path, + matchResult, + env, + executionCtx, + notFoundHandler: this.#notFoundHandler + }); + if (matchResult[0].length === 1) { + let res; + try { + res = matchResult[0][0][0][0](c, async () => { + c.res = await this.#notFoundHandler(c); + }); + } catch (err) { + return this.#handleError(err, c); + } + return res instanceof Promise ? res.then((resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c); + } + const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler); + return (async () => { + try { + const context = await composed(c); + if (!context.finalized) throw new Error("Context is not finalized. Did you forget to return a Response object or `await next()`?"); + return context.res; + } catch (err) { + return this.#handleError(err, c); + } + })(); + } + /** + * `.fetch()` will be entry point of your app. + * + * @see {@link https://hono.dev/docs/api/hono#fetch} + * + * @param {Request} request - request Object of request + * @param {Env} env - env Object + * @param {ExecutionContext} executionCtx - context of execution + * @returns {Response | Promise} response of request + * + */ + fetch = (request, ...rest) => { + return this.#dispatch(request, rest[1], rest[0], request.method); + }; + /** + * `.request()` is a useful method for testing. + * You can pass a URL or pathname to send a GET request. + * app will return a Response object. + * ```ts + * test('GET /hello is ok', async () => { + * const res = await app.request('/hello') + * expect(res.status).toBe(200) + * }) + * ``` + * @see https://hono.dev/docs/api/hono#request + */ + request = (input, requestInit, Env, executionCtx) => { + if (input instanceof Request) return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx); + input = input.toString(); + return this.fetch(new Request(/^https?:\/\//.test(input) ? input : `http://localhost${mergePath("/", input)}`, requestInit), Env, executionCtx); + }; + /** + * `.fire()` automatically adds a global fetch event listener. + * This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers. + * @deprecated + * Use `fire` from `hono/service-worker` instead. + * ```ts + * import { Hono } from 'hono' + * import { fire } from 'hono/service-worker' + * + * const app = new Hono() + * // ... + * fire(app) + * ``` + * @see https://hono.dev/docs/api/hono#fire + * @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API + * @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/ + */ + fire = () => { + addEventListener("fetch", (event) => { + event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method)); + }); + }; +}; +//#endregion +export { Hono as HonoBase }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/hono.js b/tests/fixtures/npm/node_modules/hono/dist/hono.js new file mode 100644 index 000000000..9602aafc3 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/hono.js @@ -0,0 +1,28 @@ +import { HonoBase as Hono$1 } from "./hono-base.js"; +import { RegExpRouter } from "./router/reg-exp-router/router.js"; +import "./router/reg-exp-router/index.js"; +import { SmartRouter } from "./router/smart-router/router.js"; +import { TrieRouter } from "./router/trie-router/router.js"; +import "./router/trie-router/index.js"; +//#region src/hono.ts +/** +* The Hono class extends the functionality of the HonoBase class. +* It sets up routing and allows for custom options to be passed. +* +* @template E - The environment type. +* @template S - The schema type. +* @template BasePath - The base path type. +*/ +var Hono = class extends Hono$1 { + /** + * Creates an instance of the Hono class. + * + * @param options - Optional configuration options for the Hono instance. + */ + constructor(options = {}) { + super(options); + this.router = options.router ?? new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }); + } +}; +//#endregion +export { Hono }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/http-exception.js b/tests/fixtures/npm/node_modules/hono/dist/http-exception.js new file mode 100644 index 000000000..800b530cd --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/http-exception.js @@ -0,0 +1,55 @@ +//#region src/http-exception.ts +/** +* `HTTPException` must be used when a fatal error such as authentication failure occurs. +* +* @see {@link https://hono.dev/docs/api/exception} +* +* @param {StatusCode} status - status code of HTTPException +* @param {HTTPExceptionOptions} options - options of HTTPException +* @param {HTTPExceptionOptions["res"]} options.res - response of options of HTTPException +* @param {HTTPExceptionOptions["message"]} options.message - message of options of HTTPException +* @param {HTTPExceptionOptions["cause"]} options.cause - cause of options of HTTPException +* +* @example +* ```ts +* import { HTTPException } from 'hono/http-exception' +* +* // ... +* +* app.post('/auth', async (c, next) => { +* // authentication +* if (authorized === false) { +* throw new HTTPException(401, { message: 'Custom error message' }) +* } +* await next() +* }) +* ``` +*/ +var HTTPException = class extends Error { + res; + status; + /** + * Creates an instance of `HTTPException`. + * @param status - HTTP status code for the exception. Defaults to 500. + * @param options - Additional options for the exception. + */ + constructor(status = 500, options) { + super(options?.message, { cause: options?.cause }); + this.res = options?.res; + this.status = status; + } + /** + * Returns the response object associated with the exception. + * If a response object is not provided, a new response is created with the error message and status code. + * @returns The response object. + */ + getResponse() { + if (this.res) return new Response(this.res.body, { + status: this.status, + headers: this.res.headers + }); + return new Response(this.message, { status: this.status }); + } +}; +//#endregion +export { HTTPException }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/index.js b/tests/fixtures/npm/node_modules/hono/dist/index.js new file mode 100644 index 000000000..20c96572c --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/index.js @@ -0,0 +1,20 @@ +import { Context } from "./context.js"; +import { Hono } from "./hono.js"; +//#region src/index.ts +/** +* @module +* +* Hono - Web Framework built on Web Standards +* +* @example +* ```ts +* import { Hono } from 'hono' +* const app = new Hono() +* +* app.get('/', (c) => c.text('Hono!')) +* +* export default app +* ``` +*/ +//#endregion +export { Context, Hono }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/request.js b/tests/fixtures/npm/node_modules/hono/dist/request.js new file mode 100644 index 000000000..bf130a518 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/request.js @@ -0,0 +1,335 @@ +import { HTTPException } from "./http-exception.js"; +import { GET_MATCH_RESULT } from "./request/constants.js"; +import { parseBody } from "./utils/body.js"; +import { getQueryParam, getQueryParams, tryDecodeURIComponent } from "./utils/url.js"; +//#region src/request.ts +var HonoRequest = class { + /** + * `.raw` can get the raw Request object. + * + * @see {@link https://hono.dev/docs/api/request#raw} + * + * @example + * ```ts + * // For Cloudflare Workers + * app.post('/', async (c) => { + * const metadata = c.req.raw.cf?.hostMetadata? + * ... + * }) + * ``` + */ + raw; + #validatedData; + #matchResult; + routeIndex = 0; + /** + * `.path` can get the pathname of the request. + * + * @see {@link https://hono.dev/docs/api/request#path} + * + * @example + * ```ts + * app.get('/about/me', (c) => { + * const pathname = c.req.path // `/about/me` + * }) + * ``` + */ + path; + bodyCache = {}; + constructor(request, path = "/", matchResult = [[]]) { + this.raw = request; + this.path = path; + this.#matchResult = matchResult; + } + param(key) { + return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams(); + } + #getDecodedParam(key) { + const paramKey = this.#matchResult[0][this.routeIndex]?.[1][key]; + const param = this.#getParamValue(paramKey); + return param && tryDecodeURIComponent(param); + } + #getAllDecodedParams() { + const decoded = {}; + const keys = Object.keys(this.#matchResult[0][this.routeIndex]?.[1] ?? {}); + for (const key of keys) { + const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]); + if (value !== void 0) decoded[key] = tryDecodeURIComponent(value); + } + return decoded; + } + #getParamValue(paramKey) { + return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey; + } + query(key) { + return getQueryParam(this.url, key); + } + queries(key) { + return getQueryParams(this.url, key); + } + header(name) { + if (name) return this.raw.headers.get(name) ?? void 0; + const headerData = Object.create(null); + this.raw.headers.forEach((value, key) => { + headerData[key] = value; + }); + return headerData; + } + async parseBody(options) { + return parseBody(this, options); + } + #cachedBody = (key) => { + const { bodyCache, raw } = this; + const cachedBody = bodyCache[key]; + if (cachedBody) return cachedBody; + for (const anyCachedKey in bodyCache) return bodyCache[anyCachedKey].then((body) => { + if (anyCachedKey === "json") body = JSON.stringify(body); + const contentType = anyCachedKey === "formData" ? void 0 : raw.headers.get("content-type"); + return new Response(body, { headers: contentType ? { "Content-Type": contentType } : void 0 })[key](); + }); + return bodyCache[key] = raw[key](); + }; + /** + * `.json()` can parse Request body of type `application/json` + * + * @see {@link https://hono.dev/docs/api/request#json} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.json() + * }) + * ``` + */ + json() { + return this.#cachedBody("text").then((text) => JSON.parse(text)); + } + /** + * `.text()` can parse Request body of type `text/plain` + * + * @see {@link https://hono.dev/docs/api/request#text} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.text() + * }) + * ``` + */ + text() { + return this.#cachedBody("text"); + } + /** + * `.arrayBuffer()` parse Request body as an `ArrayBuffer` + * + * @see {@link https://hono.dev/docs/api/request#arraybuffer} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.arrayBuffer() + * }) + * ``` + */ + arrayBuffer() { + return this.#cachedBody("arrayBuffer"); + } + /** + * `.bytes()` parses the request body as a `Uint8Array`. + * + * @see {@link https://hono.dev/docs/api/request#bytes} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.bytes() + * }) + * ``` + */ + bytes() { + return this.#cachedBody("arrayBuffer").then((buffer) => new Uint8Array(buffer)); + } + /** + * Parses the request body as a `Blob`. + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.blob(); + * }); + * ``` + * @see https://hono.dev/docs/api/request#blob + */ + blob() { + return this.#cachedBody("blob"); + } + /** + * Parses the request body as `FormData`. + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.formData(); + * }); + * ``` + * @see https://hono.dev/docs/api/request#formdata + */ + formData() { + return this.#cachedBody("formData"); + } + /** + * Adds validated data to the request. + * + * @param target - The target of the validation. + * @param data - The validated data to add. + */ + addValidatedData(target, data) { + (this.#validatedData ??= {})[target] = data; + } + valid(target) { + return this.#validatedData?.[target]; + } + /** + * `.url` can get the request url strings. + * + * @see {@link https://hono.dev/docs/api/request#url} + * + * @example + * ```ts + * app.get('/about/me', (c) => { + * const url = c.req.url // `http://localhost:8787/about/me` + * ... + * }) + * ``` + */ + get url() { + return this.raw.url; + } + /** + * `.method` can get the method name of the request. + * + * @see {@link https://hono.dev/docs/api/request#method} + * + * @example + * ```ts + * app.get('/about/me', (c) => { + * const method = c.req.method // `GET` + * }) + * ``` + */ + get method() { + return this.raw.method; + } + get [GET_MATCH_RESULT]() { + return this.#matchResult; + } + /** + * `.matchedRoutes` can return a matched route in the handler + * + * @deprecated + * + * Use matchedRoutes helper defined in "hono/route" instead. + * + * @see {@link https://hono.dev/docs/api/request#matchedroutes} + * + * @example + * ```ts + * app.use('*', async function logger(c, next) { + * await next() + * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => { + * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]') + * console.log( + * method, + * ' ', + * path, + * ' '.repeat(Math.max(10 - path.length, 0)), + * name, + * i === c.req.routeIndex ? '<- respond from here' : '' + * ) + * }) + * }) + * ``` + */ + get matchedRoutes() { + return this.#matchResult[0].map(([[, route]]) => route); + } + /** + * `.routePath` can retrieve the path registered within the handler + * + * @deprecated + * + * Use routePath helper defined in "hono/route" instead. + * + * @see {@link https://hono.dev/docs/api/request#routepath} + * + * @example + * ```ts + * app.get('/posts/:id', (c) => { + * return c.json({ path: c.req.routePath }) + * }) + * ``` + */ + get routePath() { + return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path; + } +}; +/** +* Clones a HonoRequest's underlying raw Request object. +* +* This utility handles both consumed and unconsumed request bodies: +* - If the request body hasn't been consumed, it uses the native `clone()` method +* - If the request body has been consumed, it reconstructs a new Request using cached body data +* +* This is particularly useful when you need to: +* - Process the same request body multiple times +* - Pass requests to external services after validation +* +* @param req - The HonoRequest object to clone +* @returns A Promise that resolves to a new Request object with the same properties +* @throws {HTTPException} If the request body was consumed directly via `req.raw` +* without using HonoRequest methods (e.g., `req.json()`, `req.text()`), making it +* impossible to reconstruct the body from cache +* +* @example +* ```ts +* // Clone after consuming the body (e.g., after validation) +* app.post('/forward', +* validator('json', (data) => data), +* async (c) => { +* const validated = c.req.valid('json') +* // Body has been consumed, but cloneRawRequest still works +* const clonedReq = await cloneRawRequest(c.req) +* return fetch('http://backend-service.com', clonedReq) +* } +* ) +* ``` +*/ +const cloneRawRequest = async (req) => { + if (!req.raw.bodyUsed) return req.raw.clone(); + const cacheKey = Object.keys(req.bodyCache)[0]; + if (!cacheKey) throw new HTTPException(500, { message: "Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly." }); + let body = await req[cacheKey](); + const headers = req.header(); + if (cacheKey === "json") { + body = JSON.stringify(body); + delete headers["content-length"]; + } else if (body instanceof FormData) { + delete headers["content-type"]; + delete headers["content-length"]; + } + const requestInit = { + body, + cache: req.raw.cache, + credentials: req.raw.credentials, + headers, + integrity: req.raw.integrity, + keepalive: req.raw.keepalive, + method: req.method, + mode: req.raw.mode, + redirect: req.raw.redirect, + referrer: req.raw.referrer, + referrerPolicy: req.raw.referrerPolicy, + signal: req.raw.signal + }; + return new Request(req.url, requestInit); +}; +//#endregion +export { HonoRequest, cloneRawRequest }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/request/constants.js b/tests/fixtures/npm/node_modules/hono/dist/request/constants.js new file mode 100644 index 000000000..0f2734437 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/request/constants.js @@ -0,0 +1,4 @@ +//#region src/request/constants.ts +const GET_MATCH_RESULT = Symbol(); +//#endregion +export { GET_MATCH_RESULT }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router.js b/tests/fixtures/npm/node_modules/hono/dist/router.js new file mode 100644 index 000000000..52ea7f5c2 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router.js @@ -0,0 +1,35 @@ +//#region src/router.ts +/** +* @module +* This module provides types definitions and variables for the routers. +*/ +/** +* Constant representing all HTTP methods in uppercase. +*/ +const METHOD_NAME_ALL = "ALL"; +/** +* Constant representing all HTTP methods in lowercase. +*/ +const METHOD_NAME_ALL_LOWERCASE = "all"; +/** +* Array of supported HTTP methods. +*/ +const METHODS = [ + "get", + "post", + "put", + "delete", + "options", + "patch", + "query" +]; +/** +* Error message indicating that a route cannot be added because the matcher is already built. +*/ +const MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built."; +/** +* Error class representing an unsupported path error. +*/ +var UnsupportedPathError = class extends Error {}; +//#endregion +export { MESSAGE_MATCHER_IS_ALREADY_BUILT, METHODS, METHOD_NAME_ALL, METHOD_NAME_ALL_LOWERCASE, UnsupportedPathError }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/index.js b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/index.js new file mode 100644 index 000000000..38dfdd48a --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/index.js @@ -0,0 +1,3 @@ +import { RegExpRouter } from "./router.js"; +import { PreparedRegExpRouter, buildInitParams, serializeInitParams } from "./prepared-router.js"; +export { PreparedRegExpRouter, RegExpRouter, buildInitParams, serializeInitParams }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/matcher.js b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/matcher.js new file mode 100644 index 000000000..97ac8435c --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/matcher.js @@ -0,0 +1,19 @@ +import "../../router.js"; +//#region src/router/reg-exp-router/matcher.ts +const emptyParam = []; +function match(method, path) { + const matchers = this.buildAllMatchers(); + const match = ((method, path) => { + const matcher = matchers[method] || matchers["ALL"]; + const staticMatch = matcher[2][path]; + if (staticMatch) return staticMatch; + const match = path.match(matcher[0]); + if (!match) return [[], emptyParam]; + const index = match.indexOf("", 1); + return [matcher[1][index], match]; + }); + this.match = match; + return match(method, path); +} +//#endregion +export { emptyParam, match }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/node.js b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/node.js new file mode 100644 index 000000000..6738b9e98 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/node.js @@ -0,0 +1,91 @@ +import { createNullObject } from "../utils.js"; +//#region src/router/reg-exp-router/node.ts +const LABEL_REG_EXP_STR = "[^/]+"; +const ONLY_WILDCARD_REG_EXP_STR = ".*"; +const TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)"; +const PATH_ERROR = Symbol(); +const regExpMetaChars = /* @__PURE__ */ new Set(".\\+*[^]$()"); +/** +* Sort order: +* 1. literal +* 2. special pattern (e.g. :label{[0-9]+}) +* 3. common label pattern (e.g. :label) +* 4. wildcard +*/ +function compareKey(a, b) { + if (a.length === 1) return b.length === 1 ? a < b ? -1 : 1 : -1; + if (b.length === 1) return 1; + if (a === ".*" || a === "(?:|/.*)") return b === "(?:|/.*)" ? -1 : 1; + else if (b === ".*" || b === "(?:|/.*)") return -1; + if (a === "[^/]+") return 1; + else if (b === "[^/]+") return -1; + return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length; +} +var Node = class Node { + #index; + #varIndex; + #children = createNullObject(); + insert(tokens, index, paramMap, context, isStatic) { + let node = this; + for (let i = 0, len = tokens.length; i < len; i++) { + const token = tokens[i]; + const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? [ + "", + "", + ".*" + ] : [ + "", + "", + LABEL_REG_EXP_STR + ] : null : token === "/*" ? [ + "", + "", + TAIL_WILDCARD_REG_EXP_STR + ] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); + let nextNode; + if (pattern) { + const name = pattern[1]; + let regexpStr = pattern[2] || "[^/]+"; + if (name && pattern[2]) { + if (regexpStr === ".*") throw PATH_ERROR; + regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:"); + if (/\((?!\?:)/.test(regexpStr)) throw PATH_ERROR; + if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) throw PATH_ERROR; + } + nextNode = node.#children[regexpStr]; + if (!nextNode) { + if (regexpStr !== ".*" && regexpStr !== "(?:|/.*)") { + for (const k in node.#children) if ((regexpStr.length > 1 || k.length > 1) && k !== ".*" && k !== "(?:|/.*)") throw PATH_ERROR; + } + nextNode = node.#children[regexpStr] = new Node(); + } + if (name !== "") { + nextNode.#varIndex ??= context.varIndex++; + paramMap.push([name, nextNode.#varIndex]); + } + } else { + nextNode = node.#children[token]; + if (!nextNode) { + for (const k in node.#children) if (k.length > 1 && k !== ".*" && k !== "(?:|/.*)") throw PATH_ERROR; + nextNode = node.#children[token] = new Node(); + } + } + node = nextNode; + } + if (node.#index !== void 0) throw PATH_ERROR; + node.#index = isStatic ? -1 : index; + } + buildRegExpStr() { + const strList = Object.keys(this.#children).sort(compareKey).map((k) => { + const c = this.#children[k]; + const childStr = c.buildRegExpStr(); + return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr; + }).filter(Boolean); + if (typeof this.#index === "number" && this.#index !== -1) strList.unshift(`#${this.#index}`); + if (strList.length === 0) return ""; + if (strList.length === 1) return strList[0]; + return "(?:" + strList.join("|") + ")"; + } +}; +//#endregion +export { LABEL_REG_EXP_STR, Node, ONLY_WILDCARD_REG_EXP_STR, PATH_ERROR, TAIL_WILDCARD_REG_EXP_STR }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/prepared-router.js b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/prepared-router.js new file mode 100644 index 000000000..c9a8c1314 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/prepared-router.js @@ -0,0 +1,94 @@ +import "../../router.js"; +import { emptyParam, match } from "./matcher.js"; +import { RegExpRouter } from "./router.js"; +//#region src/router/reg-exp-router/prepared-router.ts +var PreparedRegExpRouter = class { + name = "PreparedRegExpRouter"; + #matchers; + #relocateMap; + constructor(matchers, relocateMap) { + this.#matchers = matchers; + this.#relocateMap = relocateMap; + } + #addWildcard(method, handlerData) { + const matcher = this.#matchers[method]; + matcher[1].forEach((list) => list && list.push(handlerData)); + Object.values(matcher[2]).forEach((list) => list[0].push(handlerData)); + } + #addPath(method, path, handler, indexes, map) { + const matcher = this.#matchers[method]; + if (!map) matcher[2][path][0].push([handler, {}]); + else indexes.forEach((index) => { + if (typeof index === "number") matcher[1][index].push([handler, map]); + else matcher[2][index || path][0].push([handler, map]); + }); + } + add(method, path, handler) { + if (!this.#matchers[method]) { + const all = this.#matchers["ALL"]; + const staticMap = {}; + for (const key in all[2]) staticMap[key] = [all[2][key][0].slice(), emptyParam]; + this.#matchers[method] = [ + all[0], + all[1].map((list) => Array.isArray(list) ? list.slice() : 0), + staticMap + ]; + } + if (path === "/*" || path === "*") { + const handlerData = [handler, {}]; + if (method === "ALL") for (const m in this.#matchers) this.#addWildcard(m, handlerData); + else this.#addWildcard(method, handlerData); + return; + } + const data = this.#relocateMap[path]; + if (!data) throw new Error(`Path ${path} is not registered`); + for (const [indexes, map] of data) if (method === "ALL") for (const m in this.#matchers) this.#addPath(m, path, handler, indexes, map); + else this.#addPath(method, path, handler, indexes, map); + } + buildAllMatchers() { + return this.#matchers; + } + match = match; +}; +const buildInitParams = ({ paths }) => { + const RegExpRouterWithMatcherExport = class extends RegExpRouter { + buildAndExportAllMatchers() { + return this.buildAllMatchers(); + } + }; + const router = new RegExpRouterWithMatcherExport(); + for (const path of paths) router.add("ALL", path, path); + const matchers = router.buildAndExportAllMatchers(); + const all = matchers["ALL"]; + const relocateMap = {}; + for (const path of paths) { + if (path === "/*" || path === "*") continue; + all[1].forEach((list, i) => { + list.forEach(([p, map]) => { + if (p === path) { + if (relocateMap[path]) relocateMap[path][0][1] = { + ...relocateMap[path][0][1], + ...map + }; + else relocateMap[path] = [[[], map]]; + if (relocateMap[path][0][0].findIndex((j) => j === i) === -1) relocateMap[path][0][0].push(i); + } + }); + }); + for (const path2 in all[2]) all[2][path2][0].forEach(([p]) => { + if (p === path) { + relocateMap[path] ||= [[[]]]; + const value = path2 === path ? "" : path2; + if (relocateMap[path][0][0].findIndex((v) => v === value) === -1) relocateMap[path][0][0].push(value); + } + }); + } + for (let i = 0, len = all[1].length; i < len; i++) all[1][i] = all[1][i] ? [] : 0; + for (const path in all[2]) all[2][path][0] = []; + return [matchers, relocateMap]; +}; +const serializeInitParams = ([matchers, relocateMap]) => { + return `[${JSON.stringify(matchers, (_, value) => value instanceof RegExp ? `##${value.toString()}##` : value).replace(/"##(.+?)##"/g, (_, str) => str.replace(/\\\\/g, "\\"))},${JSON.stringify(relocateMap)}]`; +}; +//#endregion +export { PreparedRegExpRouter, buildInitParams, serializeInitParams }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/router.js b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/router.js new file mode 100644 index 000000000..21ef235d1 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/router.js @@ -0,0 +1,101 @@ +import { checkOptionalParameter } from "../../utils/url.js"; +import { MESSAGE_MATCHER_IS_ALREADY_BUILT, UnsupportedPathError } from "../../router.js"; +import { createNullObject } from "../utils.js"; +import { emptyParam, match } from "./matcher.js"; +import { LABEL_REG_EXP_STR, PATH_ERROR, TAIL_WILDCARD_REG_EXP_STR } from "./node.js"; +import { Trie } from "./trie.js"; +//#region src/router/reg-exp-router/router.ts +let wildcardRegExpCache = createNullObject(); +function buildWildcardRegExp(path) { + return wildcardRegExpCache[path] ??= new RegExp(`^${path.replace(/\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g, (match, metaChar) => metaChar ? `\\${metaChar}` : match === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match === "*" ? ".*" : `/:${LABEL_REG_EXP_STR}`)}$`); +} +function findMiddleware(middleware, path) { + for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) if (buildWildcardRegExp(k).test(path)) return [...middleware[k]]; +} +var RegExpRouter = class { + name = "RegExpRouter"; + #middleware; + #routes; + #tries; + constructor() { + this.#middleware = { ["ALL"]: createNullObject() }; + this.#routes = { ["ALL"]: createNullObject() }; + this.#tries = { ["ALL"]: new Trie() }; + } + #insertPath(method, path) { + try { + this.#tries[method].insert(path, !/\*|\/:/.test(path)); + } catch (e) { + throw e === PATH_ERROR ? new UnsupportedPathError(path) : e; + } + } + add(method, path, handler) { + const middleware = this.#middleware; + const routes = this.#routes; + if (!middleware) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT); + if (!middleware[method]) { + this.#tries[method] = new Trie(); + for (const handlerMap of [middleware, routes]) { + handlerMap[method] = createNullObject(); + for (const p in handlerMap["ALL"]) { + handlerMap[method][p] = [...handlerMap["ALL"][p]]; + this.#insertPath(method, p); + } + } + } + if (path === "/*") path = "*"; + const methods = method === "ALL" ? Object.keys(middleware) : [method]; + if (/\*$/.test(path)) { + const re = buildWildcardRegExp(path); + for (const m of methods) if (!middleware[m][path]) { + this.#insertPath(m, path); + middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware["ALL"], path) || []; + } + for (const handlerMap of [middleware, routes]) for (const m of methods) for (const p in handlerMap[m]) re.test(p) && handlerMap[m][p].push([handler, path]); + return; + } + const paths = checkOptionalParameter(path) || [path]; + for (const path of paths) for (const m of methods) { + if (!routes[m][path]) { + this.#insertPath(m, path); + routes[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware["ALL"], path) || []; + } + routes[m][path].push([handler, path]); + } + } + match = match; + buildAllMatchers() { + const matchers = createNullObject(); + for (const method of Object.keys(this.#routes)) matchers[method] = this.#buildMatcher(method); + this.#middleware = this.#routes = this.#tries = void 0; + wildcardRegExpCache = createNullObject(); + return matchers; + } + #buildMatcher(method) { + const middleware = this.#middleware[method]; + const routes = this.#routes[method]; + const trie = this.#tries[method]; + const staticMap = createNullObject(); + const handlerData = []; + const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp(); + for (const r of [middleware, routes]) for (const path in r) { + const handlers = r[path]; + const pathData = trie.paths[path]; + if (!pathData) { + staticMap[path] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam]; + continue; + } + handlerData[pathData[0]] = handlers.map(([h, handlerPath]) => [h, trie.paths[handlerPath][1].reduceRight((map, [key], i) => { + map[key] = paramReplacementMap[pathData[1][i][1]]; + return map; + }, createNullObject())]); + } + return [ + regexp, + indexReplacementMap.map((i) => handlerData[i]), + staticMap + ]; + } +}; +//#endregion +export { RegExpRouter }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/trie.js b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/trie.js new file mode 100644 index 000000000..a995678c0 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/reg-exp-router/trie.js @@ -0,0 +1,73 @@ +import { createNullObject } from "../utils.js"; +import { Node } from "./node.js"; +//#region src/router/reg-exp-router/trie.ts +var Trie = class { + #context = { varIndex: 0 }; + #root = new Node(); + #index = 0; + paths = createNullObject(); + insert(path, isStatic) { + if (isStatic) { + this.#root.insert(path.split(""), 0, [], this.#context, true); + return; + } + const paramAssoc = []; + const groups = []; + let markedPath = path; + for (let i = 0;;) { + let replaced = false; + markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => { + const mark = `@\\${i}`; + groups[i] = [mark, m]; + i++; + replaced = true; + return mark; + }); + if (!replaced) break; + } + /** + * - pattern (:label, :label{0-9]+}, ...) + * - /* wildcard + * - character + */ + const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []; + for (let i = groups.length - 1; i >= 0; i--) { + const [mark] = groups[i]; + for (let j = tokens.length - 1; j >= 0; j--) if (tokens[j].indexOf(mark) !== -1) { + tokens[j] = tokens[j].replace(mark, groups[i][1]); + break; + } + } + this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false); + this.paths[path] = [this.#index++, paramAssoc]; + } + buildRegExp() { + let regexp = this.#root.buildRegExpStr(); + if (regexp === "") return [ + /^$/, + [], + [] + ]; + let captureIndex = 0; + const indexReplacementMap = []; + const paramReplacementMap = []; + regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => { + if (handlerIndex !== void 0) { + indexReplacementMap[++captureIndex] = Number(handlerIndex); + return "$()"; + } + if (paramIndex !== void 0) { + paramReplacementMap[Number(paramIndex)] = ++captureIndex; + return ""; + } + return ""; + }); + return [ + new RegExp(`^${regexp}`), + indexReplacementMap, + paramReplacementMap + ]; + } +}; +//#endregion +export { Trie }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/smart-router/router.js b/tests/fixtures/npm/node_modules/hono/dist/router/smart-router/router.js new file mode 100644 index 000000000..a115ab938 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/smart-router/router.js @@ -0,0 +1,49 @@ +import { MESSAGE_MATCHER_IS_ALREADY_BUILT, UnsupportedPathError } from "../../router.js"; +//#region src/router/smart-router/router.ts +var SmartRouter = class { + name = "SmartRouter"; + #routers = []; + #routes = []; + constructor(init) { + this.#routers = init.routers; + } + add(method, path, handler) { + if (!this.#routes) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT); + this.#routes.push([ + method, + path, + handler + ]); + } + match(method, path) { + if (!this.#routes) throw new Error("Fatal error"); + const routers = this.#routers; + const routes = this.#routes; + const len = routers.length; + let i = 0; + let res; + for (; i < len; i++) { + const router = routers[i]; + try { + for (let i = 0, len = routes.length; i < len; i++) router.add(...routes[i]); + res = router.match(method, path); + } catch (e) { + if (e instanceof UnsupportedPathError) continue; + throw e; + } + this.match = router.match.bind(router); + this.#routers = [router]; + this.#routes = void 0; + break; + } + if (i === len) throw new Error("Fatal error"); + this.name = `SmartRouter + ${this.activeRouter.name}`; + return res; + } + get activeRouter() { + if (this.#routes || this.#routers.length !== 1) throw new Error("No active router has been determined yet."); + return this.#routers[0]; + } +}; +//#endregion +export { SmartRouter }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/index.js b/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/index.js new file mode 100644 index 000000000..8ea5b3150 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/index.js @@ -0,0 +1,2 @@ +import { TrieRouter } from "./router.js"; +export { TrieRouter }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/node.js b/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/node.js new file mode 100644 index 000000000..c33c7af1e --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/node.js @@ -0,0 +1,134 @@ +import { getPattern, splitPath, splitRoutingPath } from "../../utils/url.js"; +import "../../router.js"; +import { createNullObject } from "../utils.js"; +//#region src/router/trie-router/node.ts +const emptyParams = createNullObject(); +let order = 0; +var Node = class Node { + #methods = []; + #children = createNullObject(); + #patterns = []; + #pattern; + #params = emptyParams; + insert(method, path, handler) { + let curNode = this; + const parts = splitRoutingPath(path); + const possibleKeys = /* @__PURE__ */ new Set(); + let i = 0; + for (const p of parts) { + const nextP = parts[++i]; + const pattern = getPattern(p, nextP) || (nextP === void 0 && p && p.indexOf("*") === p.length - 1 ? p : null); + const isParam = Array.isArray(pattern); + const key = isParam ? pattern[0] : pattern || p; + const child = curNode.#children[key] ||= new Node(); + if (pattern && !child.#pattern) { + child.#pattern = pattern; + curNode.#patterns.push(child); + } + curNode = child; + if (isParam) possibleKeys.add(pattern[1]); + } + curNode.#methods.push({ [method]: { + handler, + possibleKeys: [...possibleKeys], + score: ++order + } }); + } + #pushHandlerSets(handlerSets, node, method, nodeParams, params) { + for (let i = 0, len = node.#methods.length; i < len; i++) { + const m = node.#methods[i]; + const handlerSet = m[method] || m["ALL"]; + if (handlerSet) { + handlerSet.params = createNullObject(); + handlerSets.push(handlerSet); + for (let i = 0, len = handlerSet.possibleKeys.length; i < len; i++) { + const key = handlerSet.possibleKeys[i]; + handlerSet.params[key] = params?.[key] && !i ? params[key] : nodeParams[key] ?? params?.[key]; + } + } + } + } + search(method, path) { + const handlerSets = []; + this.#params = emptyParams; + let curNodes = [this]; + const parts = splitPath(path); + const curNodesQueue = []; + const len = parts.length; + let partOffsets = null; + for (let i = 0; i < len; i++) { + const part = parts[i]; + const isLast = i === len - 1; + const tempNodes = []; + for (let j = 0, len2 = curNodes.length; j < len2; j++) { + const node = curNodes[j]; + const nextNode = node.#children[part]; + if (nextNode) { + nextNode.#params = node.#params; + if (isLast) { + if (nextNode.#children["*"]) this.#pushHandlerSets(handlerSets, nextNode.#children["*"], method, node.#params); + this.#pushHandlerSets(handlerSets, nextNode, method, node.#params); + } else tempNodes.push(nextNode); + } + for (const child of node.#patterns) { + const pattern = child.#pattern; + const params = node.#params === emptyParams ? {} : { ...node.#params }; + if (typeof pattern === "string") { + if (pattern === "*" || part.startsWith(pattern.slice(0, -1))) { + this.#pushHandlerSets(handlerSets, child, method, node.#params); + if (pattern === "*") { + child.#params = params; + tempNodes.push(child); + } + } + continue; + } + const [, name, matcher] = pattern; + if (!part && matcher === true) continue; + if (matcher !== true) { + if (!partOffsets) { + partOffsets = []; + let offset = path[0] === "/" ? 1 : 0; + for (let p = 0; p < len; p++) { + partOffsets[p] = offset; + offset += parts[p].length + 1; + } + } + const restPathString = path.slice(partOffsets[i]); + const m = matcher.exec(restPathString); + if (m) { + params[name] = m[0]; + this.#pushHandlerSets(handlerSets, child, method, node.#params, params); + if (m[0].length === restPathString.length && child.#children["*"]) this.#pushHandlerSets(handlerSets, child.#children["*"], method, node.#params, params); + for (const _ in child.#children) { + child.#params = params; + const componentCount = m[0].match(/\//g)?.length ?? 0; + (curNodesQueue[componentCount] ||= []).push(child); + break; + } + continue; + } + } + if (matcher === true || matcher.test(part)) { + params[name] = part; + if (isLast) { + this.#pushHandlerSets(handlerSets, child, method, params, node.#params); + if (child.#children["*"]) this.#pushHandlerSets(handlerSets, child.#children["*"], method, params, node.#params); + } else { + child.#params = params; + tempNodes.push(child); + } + } + } + } + const shifted = curNodesQueue.shift(); + curNodes = shifted ? tempNodes.concat(shifted) : tempNodes; + } + if (handlerSets[1]) handlerSets.sort((a, b) => { + return a.score - b.score; + }); + return [handlerSets.map(({ handler, params }) => [handler, params])]; + } +}; +//#endregion +export { Node }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/router.js b/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/router.js new file mode 100644 index 000000000..96f4c9351 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/trie-router/router.js @@ -0,0 +1,15 @@ +import { checkOptionalParameter } from "../../utils/url.js"; +import { Node } from "./node.js"; +//#region src/router/trie-router/router.ts +var TrieRouter = class { + name = "TrieRouter"; + #node = new Node(); + add(method, path, handler) { + for (const result of checkOptionalParameter(path) || [path]) this.#node.insert(method, result, handler); + } + match(method, path) { + return this.#node.search(method, path); + } +}; +//#endregion +export { TrieRouter }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/router/utils.js b/tests/fixtures/npm/node_modules/hono/dist/router/utils.js new file mode 100644 index 000000000..3f1e9d328 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/router/utils.js @@ -0,0 +1,4 @@ +//#region src/router/utils.ts +const createNullObject = () => Object.create(null); +//#endregion +export { createNullObject }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts new file mode 100644 index 000000000..81dc47313 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/client/types.d.ts @@ -0,0 +1,204 @@ +import { METHODS, METHOD_NAME_ALL_LOWERCASE } from "../router.js"; +import { HonoBase as Hono } from "../hono-base.js"; +import { StatusCode, SuccessStatusCode } from "../utils/http-status.js"; +import { HasRequiredKeys } from "../utils/types.js"; +import { Endpoint, ExtractSchema, KnownResponseFormat, ResponseFormat, Schema } from "../types.js"; +import { Hono as Hono$1 } from "../hono.js"; +//#region src/client/types.d.ts +/** + * Type representing the '$all' method name + */ +type MethodNameAll = `$${typeof METHOD_NAME_ALL_LOWERCASE}`; +/** + * Type representing all standard HTTP methods prefixed with '$' + * e.g., '$get' | '$post' | '$put' | '$delete' | '$options' | '$patch' | '$query' + */ +type StandardMethods = `$${(typeof METHODS)[number]}`; +/** + * Expands '$all' into all standard HTTP methods. + * If the schema contains '$all', it creates a type where all standard HTTP methods + * point to the same endpoint definition as '$all', while removing '$all' itself. + */ +type ExpandAllMethod = MethodNameAll extends keyof S ? { [M in StandardMethods]: S[MethodNameAll]; } & Omit : S; +type HonoRequest = (typeof Hono$1.prototype)['request']; +export type BuildSearchParamsFn = (query: Record) => URLSearchParams; +export type ClientRequestOptions = { + fetch?: typeof fetch | HonoRequest; + webSocket?: (...args: ConstructorParameters) => WebSocket; + /** + * Standard `RequestInit`, caution that this take highest priority + * and could be used to overwrite things that Hono sets for you, like `body | method | headers`. + * + * If you want to add some headers, use in `headers` instead of `init` + */ + init?: RequestInit; + /** + * Custom function to serialize query parameters into URLSearchParams. + * By default, arrays are serialized as multiple parameters with the same key (e.g., `key=a&key=b`). + * You can provide a custom function to change this behavior, for example to use bracket notation (e.g., `key[]=a&key[]=b`). + * + * @example + * ```ts + * const client = hc('http://localhost', { + * buildSearchParams: (query) => { + * return new URLSearchParams(qs.stringify(query)) + * } + * }) + * ``` + */ + buildSearchParams?: BuildSearchParamsFn; +} & (keyof T extends never ? { + headers?: Record | (() => Record | Promise>); +} : { + headers: T | (() => T | Promise); +}); +export type ClientRequest = { [M in keyof ExpandAllMethod]: ExpandAllMethod[M] extends Endpoint & { + input: infer R; +} ? R extends object ? HasRequiredKeys extends true ? (args: R, options?: ClientRequestOptions) => Promise[M]>> : (args?: R, options?: ClientRequestOptions) => Promise[M]>> : never : never; } & { + $url: (arg?: Arg) => HonoURL; + $path: (arg?: Arg) => BuildPath; +} & (S['$get'] extends { + outputFormat: 'ws'; +} ? S['$get'] extends { + input: infer I; +} ? { + $ws: (args?: I) => WebSocket; +} : {} : {}); +type ClientResponseOfEndpoint = T extends { + output: infer O; + outputFormat: infer F; + status: infer S; +} ? ClientResponse : never; +export interface ClientResponse { + readonly body: ReadableStream | null; + readonly bodyUsed: boolean; + ok: U extends SuccessStatusCode ? true : U extends Exclude ? false : boolean; + redirected: boolean; + status: U; + statusText: string; + type: 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect'; + headers: Headers; + url: string; + redirect(url: string, status: number): Response; + clone(): Response; + bytes(): Promise>; + json(): F extends 'text' ? Promise : F extends 'json' ? Promise : Promise; + text(): F extends 'text' ? (T extends string ? Promise : Promise) : Promise; + blob(): Promise; + formData(): Promise; + arrayBuffer(): Promise; +} +type BuildSearch = Arg extends { [K in Key]: infer Query; } ? IsEmptyObject extends true ? '' : `?${string}` : ''; +type BuildPathname

= Arg extends { + param: infer Param; +} ? `${ApplyParam, Param>}` : `/${TrimStartSlash

}`; +type BuildPath

= `${BuildPathname}${BuildSearch}`; +type BuildTypedURL = TypedURL<`${Protocol}:`, Host, Port, BuildPathname, BuildSearch>; +type HonoURL = IsLiteral extends true ? TrimEndSlash extends `${infer Protocol}://${infer Rest}` ? Rest extends `${infer Hostname}/${infer P}` ? ParseHostName extends [infer Host extends string, infer Port extends string] ? BuildTypedURL : never : ParseHostName extends [infer Host extends string, infer Port extends string] ? BuildTypedURL : never : URL : URL; +type ParseHostName = T extends `${infer Host}:${infer Port}` ? [Host, Port] : [T, '']; +type TrimStartSlash = T extends `/${infer R}` ? TrimStartSlash : T; +type TrimEndSlash = T extends `${infer R}/` ? TrimEndSlash : T; +type IsLiteral = [T] extends [never] ? false : string extends T ? false : true; +type ApplyParam = Path extends `${infer Head}/${infer Rest}` ? Head extends `:${infer Param}` ? P extends Record ? IsLiteral extends true ? ApplyParam : ApplyParam : ApplyParam : ApplyParam : Path extends `:${infer Param}` ? P extends Record ? IsLiteral extends true ? `${Result}/${Value & string}` : `${Result}/${Path}` : `${Result}/${Path}` : `${Result}/${Path}`; +type IsEmptyObject = keyof T extends never ? true : false; +export interface TypedURL extends URL { + protocol: Protocol; + hostname: Hostname; + port: Port; + host: Port extends '' ? Hostname : `${Hostname}:${Port}`; + origin: `${Protocol}//${Hostname}${Port extends '' ? '' : `:${Port}`}`; + pathname: Pathname; + search: Search; + href: `${Protocol}//${Hostname}${Port extends '' ? '' : `:${Port}`}${Pathname}${Search}`; +} +export interface Response extends ClientResponse {} +export type Fetch = (args?: InferRequestType, opt?: ClientRequestOptions) => Promise>>; +type InferEndpointType = T extends ((args: infer R, options: any | undefined) => Promise) ? U extends ClientResponse ? { + input: NonNullable; + output: O; + outputFormat: F; + status: S; +} extends Endpoint ? { + input: NonNullable; + output: O; + outputFormat: F; + status: S; +} : never : never : never; +export type InferResponseType = InferResponseTypeFromEndpoint, U>; +type InferResponseTypeFromEndpoint = T extends { + output: infer O; + status: infer S; +} ? S extends U ? O : never : never; +export type InferRequestType = T extends ((args: infer R, options: any | undefined) => Promise>) ? NonNullable : never; +export type InferRequestOptionsType = T extends ((args: any, options: infer R) => Promise>) ? NonNullable : never; +/** + * Filter a ClientResponse type so it only includes responses of specific status codes. + */ +export type FilterClientResponseByStatusCode, U extends number = StatusCode> = T extends ClientResponse ? RC extends U ? ClientResponse : never : never; +type PathToChain = Path extends `/${infer P}` ? PathToChain : Path extends `${infer P}/${infer R}` ? { [K in P]: PathToChain; } : { [K in Path extends '' ? 'index' : Path]: ClientRequest ? E[Original] : never>; }; +export type Client = T extends Hono ? S extends Record ? K extends string ? PathToChain : never : never : never; +export type Callback = (opts: CallbackOptions) => unknown; +interface CallbackOptions { + path: string[]; + args: any[]; +} +export type ObjectType = { + [key: string]: T; +}; +type GlobalResponseDefinition = { [S in StatusCode]?: { [F in KnownResponseFormat]?: unknown; }; }; +type ToEndpoints = { [S in keyof Def & StatusCode]: { [F in keyof Def[S] & KnownResponseFormat]: Omit & { + output: Def[S][F]; + status: S; + outputFormat: F; +}; }[keyof Def[S] & KnownResponseFormat]; }[keyof Def & StatusCode]; +type ModRoute = R extends Endpoint ? R | ToEndpoints : R; +type ModSchema = { [K in keyof D]: { [M in keyof D[K]]: ModRoute; }; }; +export type ApplyGlobalResponse = App extends Hono ? ModSchema, Def> extends (infer S extends Schema) ? Hono$1 : never : never; +type PickRoute = R extends Endpoint ? R extends { + status: U; +} ? R : never : R; +type PickSchema = { [K in keyof D]: { [M in keyof D[K]]: PickRoute; }; }; +/** + * Keep only specific status code responses from all routes of an app. + * Useful when error responses are handled centrally (e.g., via custom fetch) + * and you want the client to only expose success response types. + * + * @example + * ```ts + * type AppSuccessOnly = PickResponseByStatusCode + * const client = hc('http://localhost') + * ``` + */ +export type PickResponseByStatusCode = App extends Hono ? PickSchema, U> extends (infer S extends Schema) ? Hono$1 : never : never; +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/context.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/context.d.ts new file mode 100644 index 000000000..163c86b8c --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/context.d.ts @@ -0,0 +1,459 @@ +import { Result } from "./router.js"; +import { ResponseHeader } from "./utils/headers.js"; +import { ContentfulStatusCode, RedirectStatusCode, StatusCode } from "./utils/http-status.js"; +import { InvalidJSONValue, IsAny, JSONParsed, JSONValue } from "./utils/types.js"; +import { Env, FetchEventLike, H, Input, NotFoundHandler, RouterRoute, TypedResponse } from "./types.js"; +import { HonoRequest } from "./request.js"; +import { BaseMime } from "./utils/mime.js"; +//#region src/context.d.ts +type HeaderRecord = Record<'Content-Type', BaseMime> | Record | Record; +/** + * Data type can be a string, ArrayBuffer, Blob, Uint8Array (buffer), or ReadableStream. + */ +export type Data = string | ArrayBuffer | Blob | ReadableStream | Uint8Array; +/** + * Interface for the execution context in a web worker or similar environment. + */ +export interface ExecutionContext { + /** + * Extends the lifetime of the event callback until the promise is settled. + * + * @param promise - A promise to wait for. + */ + waitUntil(promise: Promise): void; + /** + * Allows the event to be passed through to subsequent event listeners. + */ + passThroughOnException(): void; + /** + * For compatibility with Wrangler 4.x. + */ + props: any; + /** + * For compatibility with Wrangler 4.x. + */ + exports?: any; +} +/** + * Interface for context variable mapping. + */ +export interface ContextVariableMap {} +/** + * Interface for context renderer. + */ +export interface ContextRenderer {} +/** + * Interface representing a renderer for content. + * + * @interface DefaultRenderer + * @param {string | Promise} content - The content to be rendered, which can be either a string or a Promise resolving to a string. + * @returns {Response | Promise} - The response after rendering the content, which can be either a Response or a Promise resolving to a Response. + */ +interface DefaultRenderer { + (content: string | Promise): Response | Promise; +} +/** + * Renderer type which can either be a ContextRenderer or DefaultRenderer. + */ +export type Renderer = ContextRenderer extends Function ? ContextRenderer : DefaultRenderer; +/** + * Extracts the props for the renderer. + */ +export type PropsForRenderer = [...Required>] extends [unknown, infer Props] ? Props : unknown; +export type Layout> = (props: T) => any; +/** + * Interface for getting context variables. + * + * @template E - Environment type. + */ +interface Get { + (key: Key): E['Variables'][Key]; + (key: Key): ContextVariableMap[Key]; +} +/** + * Interface for setting context variables. + * + * @template E - Environment type. + */ +interface Set { + (key: Key, value: E['Variables'][Key]): void; + (key: Key, value: ContextVariableMap[Key]): void; +} +/** + * Interface for creating a new response. + */ +interface NewResponse { + (data: Data | null, status?: StatusCode, headers?: HeaderRecord): Response; + (data: Data | null, init?: ResponseOrInit): Response; +} +/** + * Interface for responding with a body. + */ +interface BodyRespond { + (data: T, status?: U, headers?: HeaderRecord): Response & TypedResponse; + (data: T, init?: ResponseOrInit): Response & TypedResponse; + (data: T, status?: U, headers?: HeaderRecord): Response & TypedResponse; + (data: T, init?: ResponseOrInit): Response & TypedResponse; +} +/** + * Interface for responding with text. + * + * @interface TextRespond + * @template T - The type of the text content. + * @template U - The type of the status code. + * + * @param {T} text - The text content to be included in the response. + * @param {U} [status] - An optional status code for the response. + * @param {HeaderRecord} [headers] - An optional record of headers to include in the response. + * + * @returns {Response & TypedResponse} - The response after rendering the text content, typed with the provided text and status code types. + */ +interface TextRespond { + (text: T, status?: U, headers?: HeaderRecord): Response & TypedResponse; + (text: T, init?: ResponseOrInit): Response & TypedResponse; +} +/** + * Interface for responding with JSON. + * + * @interface JSONRespond + * @template T - The type of the JSON value or simplified unknown type. + * @template U - The type of the status code. + * + * @param {T} object - The JSON object to be included in the response. + * @param {U} [status] - An optional status code for the response. + * @param {HeaderRecord} [headers] - An optional record of headers to include in the response. + * + * @returns {JSONRespondReturn} - The response after rendering the JSON object, typed with the provided object and status code types. + */ +interface JSONRespond { + (object: T, status?: U, headers?: HeaderRecord): JSONRespondReturn; + (object: T, init?: ResponseOrInit): JSONRespondReturn; +} +/** + * @template T - The type of the JSON value or simplified unknown type. + * @template U - The type of the status code. + * + * @returns {Response & TypedResponse, U, 'json'>} - The response after rendering the JSON object, typed with the provided object and status code types. + */ +type JSONRespondReturn = Response & TypedResponse, U, 'json'>; +/** + * Interface representing a function that responds with HTML content. + * + * @param html - The HTML content to respond with, which can be a string or a Promise that resolves to a string. + * @param status - (Optional) The HTTP status code for the response. + * @param headers - (Optional) A record of headers to include in the response. + * @param init - (Optional) The response initialization object. + * + * @returns A Response object or a Promise that resolves to a Response object. + */ +interface HTMLRespond { + >(html: T, status?: ContentfulStatusCode, headers?: HeaderRecord): T extends string ? Response : Promise; + >(html: T, init?: ResponseOrInit): T extends string ? Response : Promise; +} +/** + * Options for configuring the context. + * + * @template E - Environment type. + */ +type ContextOptions = { + /** + * Bindings for the environment. + */ + env: E['Bindings']; + /** + * Execution context for the request. + */ + executionCtx?: FetchEventLike | ExecutionContext | undefined; + /** + * Handler for not found responses. + */ + notFoundHandler?: NotFoundHandler; + matchResult?: Result<[H, RouterRoute]>; + path?: string; +}; +interface SetHeadersOptions { + append?: boolean; +} +interface SetHeaders { + (name: 'Content-Type', value?: BaseMime, options?: SetHeadersOptions): void; + (name: ResponseHeader, value?: string, options?: SetHeadersOptions): void; + (name: string, value?: string, options?: SetHeadersOptions): void; +} +type ResponseHeadersInit = [string, string][] | Record<'Content-Type', BaseMime> | Record | Record | Headers; +interface ResponseInit { + headers?: ResponseHeadersInit; + status?: T; + statusText?: string; +} +type ResponseOrInit = ResponseInit | Response; +export declare const TEXT_PLAIN = "text/plain; charset=UTF-8"; +export declare class Context { + + /** + * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers. + * + * @see {@link https://hono.dev/docs/api/context#env} + * + * @example + * ```ts + * // Environment object for Cloudflare Workers + * app.get('*', async c => { + * const counter = c.env.COUNTER + * }) + * ``` + */ + env: E['Bindings']; + finalized: boolean; + /** + * `.error` can get the error object from the middleware if the Handler throws an error. + * + * @see {@link https://hono.dev/docs/api/context#error} + * + * @example + * ```ts + * app.use('*', async (c, next) => { + * await next() + * if (c.error) { + * // do something... + * } + * }) + * ``` + */ + error: Error | undefined; + /** + * Creates an instance of the Context class. + * + * @param req - The Request object. + * @param options - Optional configuration options for the context. + */ + constructor(req: Request, options?: ContextOptions); + /** + * `.req` is the instance of {@link HonoRequest}. + */ + get req(): HonoRequest; + /** + * @see {@link https://hono.dev/docs/api/context#event} + * The FetchEvent associated with the current request. + * + * @throws Will throw an error if the context does not have a FetchEvent. + */ + get event(): FetchEventLike; + /** + * @see {@link https://hono.dev/docs/api/context#executionctx} + * The ExecutionContext associated with the current request. + * + * @throws Will throw an error if the context does not have an ExecutionContext. + */ + get executionCtx(): ExecutionContext; + /** + * @see {@link https://hono.dev/docs/api/context#res} + * The Response object for the current request. + */ + get res(): Response; + /** + * Sets the Response object for the current request. + * + * @param _res - The Response object to set. + */ + set res(_res: Response | undefined); + /** + * `.render()` can create a response within a layout. + * + * @see {@link https://hono.dev/docs/api/context#render-setrenderer} + * + * @example + * ```ts + * app.get('/', (c) => { + * return c.render('Hello!') + * }) + * ``` + */ + render: Renderer; + /** + * Sets the layout for the response. + * + * @param layout - The layout to set. + * @returns The layout function. + */ + setLayout: (layout: Layout) => Layout; + /** + * Gets the current layout for the response. + * + * @returns The current layout function. + */ + getLayout: () => Layout | undefined; + /** + * `.setRenderer()` can set the layout in the custom middleware. + * + * @see {@link https://hono.dev/docs/api/context#render-setrenderer} + * + * @example + * ```tsx + * app.use('*', async (c, next) => { + * c.setRenderer((content) => { + * return c.html( + * + * + *

{content}

+ * + * + * ) + * }) + * await next() + * }) + * ``` + */ + setRenderer: (renderer: Renderer) => void; + /** + * `.header()` can set headers. + * + * @see {@link https://hono.dev/docs/api/context#header} + * + * @example + * ```ts + * app.get('/welcome', (c) => { + * // Set headers + * c.header('X-Message', 'Hello!') + * c.header('Content-Type', 'text/plain') + * + * // Append multiple headers using the append option (e.g. Vary) + * c.header('Vary', 'Accept-Encoding', { append: true }) + * c.header('Vary', 'User-Agent', { append: true }) + * + * return c.body('Thank you for coming') + * }) + * ``` + */ + header: SetHeaders; + status: (status: StatusCode) => void; + /** + * `.set()` can set the value specified by the key. + * + * @see {@link https://hono.dev/docs/api/context#set-get} + * + * @example + * ```ts + * app.use('*', async (c, next) => { + * c.set('message', 'Hono is hot!!') + * await next() + * }) + * ``` + */ + set: Set extends true ? { + Variables: ContextVariableMap & Record; + } : E>; + /** + * `.get()` can use the value specified by the key. + * + * @see {@link https://hono.dev/docs/api/context#set-get} + * + * @example + * ```ts + * app.get('/', (c) => { + * const message = c.get('message') + * return c.text(`The message is "${message}"`) + * }) + * ``` + */ + get: Get extends true ? { + Variables: ContextVariableMap & Record; + } : E>; + /** + * `.var` can access the value of a variable. + * + * @see {@link https://hono.dev/docs/api/context#var} + * + * @example + * ```ts + * const result = c.var.client.oneMethod() + * ``` + */ + get var(): Readonly extends true ? Record : E['Variables'])>; + newResponse: NewResponse; + /** + * `.body()` can return the HTTP response. + * You can set headers with `.header()` and set HTTP status code with `.status`. + * This can also be set in `.text()`, `.json()` and so on. + * + * @see {@link https://hono.dev/docs/api/context#body} + * + * @example + * ```ts + * app.get('/welcome', (c) => { + * // Set headers + * c.header('X-Message', 'Hello!') + * c.header('Content-Type', 'text/plain') + * // Set HTTP status code + * c.status(201) + * + * // Return the response body + * return c.body('Thank you for coming') + * }) + * ``` + */ + body: BodyRespond; + /** + * `.text()` can render text as `Content-Type:text/plain`. + * + * @see {@link https://hono.dev/docs/api/context#text} + * + * @example + * ```ts + * app.get('/say', (c) => { + * return c.text('Hello!') + * }) + * ``` + */ + text: TextRespond; + /** + * `.json()` can render JSON as `Content-Type:application/json`. + * + * @see {@link https://hono.dev/docs/api/context#json} + * + * @example + * ```ts + * app.get('/api', (c) => { + * return c.json({ message: 'Hello!' }) + * }) + * ``` + */ + json: JSONRespond; + html: HTMLRespond; + /** + * `.redirect()` can Redirect, default status code is 302. + * + * @see {@link https://hono.dev/docs/api/context#redirect} + * + * @example + * ```ts + * app.get('/redirect', (c) => { + * return c.redirect('/') + * }) + * app.get('/redirect-permanently', (c) => { + * return c.redirect('/', 301) + * }) + * ``` + */ + redirect: (location: string | URL, status?: T) => Response & TypedResponse; + /** + * `.notFound()` can return the Not Found Response. + * + * @see {@link https://hono.dev/docs/api/context#notfound} + * + * @example + * ```ts + * app.get('/notfound', (c) => { + * return c.notFound() + * }) + * ``` + */ + notFound: () => ReturnType; +} +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/hono-base.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/hono-base.d.ts new file mode 100644 index 000000000..85f57dfc6 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/hono-base.d.ts @@ -0,0 +1,221 @@ +import { Router } from "./router.js"; +import { Env, ErrorHandler, H, HandlerInterface, MergePath, MergeSchemaPath, MiddlewareHandlerInterface, NotFoundHandler, OnHandlerInterface, RouterRoute, Schema } from "./types.js"; +import { Context, ExecutionContext } from "./context.js"; +//#region src/hono-base.d.ts +type GetPath = (request: Request, options?: { + env?: E['Bindings']; +}) => string; +export type HonoOptions = { + /** + * `strict` option specifies whether to distinguish whether the last path is a directory or not. + * + * @see {@link https://hono.dev/docs/api/hono#strict-mode} + * + * @default true + */ + strict?: boolean; + /** + * `router` option specifies which router to use. + * + * @see {@link https://hono.dev/docs/api/hono#router-option} + * + * @example + * ```ts + * const app = new Hono({ router: new RegExpRouter() }) + * ``` + */ + router?: Router<[H, RouterRoute]>; + /** + * `getPath` can handle the host header value. + * + * @see {@link https://hono.dev/docs/api/routing#routing-with-host-header-value} + * + * @example + * ```ts + * const app = new Hono({ + * getPath: (req) => + * '/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'), + * }) + * + * app.get('/www1.example.com/hello', () => c.text('hello www1')) + * + * // A following request will match the route: + * // new Request('http://www1.example.com/hello', { + * // headers: { host: 'www1.example.com' }, + * // }) + * ``` + */ + getPath?: GetPath; +}; +type MountOptionHandler = (c: Context) => unknown; +type MountReplaceRequest = (originalRequest: Request) => Request; +type MountOptions = MountOptionHandler | { + optionHandler?: MountOptionHandler; + replaceRequest?: MountReplaceRequest | false; +}; +declare class Hono { + + get: HandlerInterface; + post: HandlerInterface; + put: HandlerInterface; + delete: HandlerInterface; + options: HandlerInterface; + patch: HandlerInterface; + query: HandlerInterface; + all: HandlerInterface; + on: OnHandlerInterface; + use: MiddlewareHandlerInterface; + router: Router<[H, RouterRoute]>; + readonly getPath: GetPath; + private _basePath; + routes: RouterRoute[]; + constructor(options?: HonoOptions); + private errorHandler; + /** + * `.route()` allows grouping other Hono instance in routes. + * + * @see {@link https://hono.dev/docs/api/routing#grouping} + * + * @param {string} path - base Path + * @param {Hono} app - other Hono instance + * @returns {Hono} routed Hono instance + * + * @example + * ```ts + * const app = new Hono() + * const app2 = new Hono() + * + * app2.get("/user", (c) => c.text("user")) + * app.route("/api", app2) // GET /api/user + * ``` + */ + route(path: SubPath, app: Hono): Hono> | S, BasePath, CurrentPath>; + /** + * `.basePath()` allows base paths to be specified. + * + * @see {@link https://hono.dev/docs/api/routing#base-path} + * + * @param {string} path - base Path + * @returns {Hono} changed Hono instance + * + * @example + * ```ts + * const api = new Hono().basePath('/api') + * ``` + */ + basePath(path: SubPath): Hono, MergePath>; + /** + * `.onError()` handles an error and returns a customized Response. + * + * @see {@link https://hono.dev/docs/api/hono#error-handling} + * + * @param {ErrorHandler} handler - request Handler for error + * @returns {Hono} changed Hono instance + * + * @example + * ```ts + * app.onError((err, c) => { + * console.error(`${err}`) + * return c.text('Custom Error Message', 500) + * }) + * ``` + */ + onError: (handler: ErrorHandler) => Hono; + /** + * `.notFound()` allows you to customize a Not Found Response. + * + * @see {@link https://hono.dev/docs/api/hono#not-found} + * + * @param {NotFoundHandler} handler - request handler for not-found + * @returns {Hono} changed Hono instance + * + * @example + * ```ts + * app.notFound((c) => { + * return c.text('Custom 404 Message', 404) + * }) + * ``` + */ + notFound: (handler: NotFoundHandler) => Hono; + /** + * `.mount()` allows you to mount applications built with other frameworks into your Hono application. + * + * @deprecated Use `mount()` from `hono/mount` instead. `.mount()` will be removed in v5. + * + * @see {@link https://hono.dev/docs/api/hono#mount} + * + * @param {string} path - base Path + * @param {Function} applicationHandler - other Request Handler + * @param {MountOptions} [options] - options of `.mount()` + * @returns {Hono} mounted Hono instance + * + * @example + * ```ts + * import { Router as IttyRouter } from 'itty-router' + * import { Hono } from 'hono' + * // Create itty-router application + * const ittyRouter = IttyRouter() + * // GET /itty-router/hello + * ittyRouter.get('/hello', () => new Response('Hello from itty-router')) + * + * const app = new Hono() + * app.mount('/itty-router', ittyRouter.handle) + * ``` + * + * @example + * ```ts + * const app = new Hono() + * // Send the request to another application without modification. + * app.mount('/app', anotherApp, { + * replaceRequest: (req) => req, + * }) + * ``` + */ + mount(path: string, applicationHandler: (request: Request, ...args: any) => Response | Promise, options?: MountOptions): Hono; + /** + * `.fetch()` will be entry point of your app. + * + * @see {@link https://hono.dev/docs/api/hono#fetch} + * + * @param {Request} request - request Object of request + * @param {Env} env - env Object + * @param {ExecutionContext} executionCtx - context of execution + * @returns {Response | Promise} response of request + * + */ + fetch: (request: Request, env?: E['Bindings'] | {}, executionCtx?: ExecutionContext) => Response | Promise; + /** + * `.request()` is a useful method for testing. + * You can pass a URL or pathname to send a GET request. + * app will return a Response object. + * ```ts + * test('GET /hello is ok', async () => { + * const res = await app.request('/hello') + * expect(res.status).toBe(200) + * }) + * ``` + * @see https://hono.dev/docs/api/hono#request + */ + request: (input: Request | string | URL, requestInit?: RequestInit, Env?: E["Bindings"] | {}, executionCtx?: ExecutionContext) => Response | Promise; + /** + * `.fire()` automatically adds a global fetch event listener. + * This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers. + * @deprecated + * Use `fire` from `hono/service-worker` instead. + * ```ts + * import { Hono } from 'hono' + * import { fire } from 'hono/service-worker' + * + * const app = new Hono() + * // ... + * fire(app) + * ``` + * @see https://hono.dev/docs/api/hono#fire + * @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API + * @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/ + */ + fire: () => void; +} +//#endregion +export { Hono as HonoBase }; +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/hono.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/hono.d.ts new file mode 100644 index 000000000..52ce78cec --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/hono.d.ts @@ -0,0 +1,20 @@ +import { HonoBase as Hono$1, HonoOptions } from "./hono-base.js"; +import { BlankEnv, BlankSchema, Env, Schema } from "./types.js"; +//#region src/hono.d.ts +/** + * The Hono class extends the functionality of the HonoBase class. + * It sets up routing and allows for custom options to be passed. + * + * @template E - The environment type. + * @template S - The schema type. + * @template BasePath - The base path type. + */ +export declare class Hono extends Hono$1 { + /** + * Creates an instance of the Hono class. + * + * @param options - Optional configuration options for the Hono instance. + */ + constructor(options?: HonoOptions); +} +//#endregion \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/index.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/index.d.ts new file mode 100644 index 000000000..12bbd7aaf --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/index.d.ts @@ -0,0 +1,6 @@ +import { Env, ErrorHandler, Handler, Input, MiddlewareHandler, Next, NotFoundHandler, NotFoundResponse, Schema, ToSchema, TypedResponse, ValidationTargets } from "./types.js"; +import { HonoRequest } from "./request.js"; +import { Context, ContextRenderer, ContextVariableMap, ExecutionContext } from "./context.js"; +import { Hono } from "./hono.js"; +import { ClientRequestOptions, InferRequestType, InferResponseType } from "./client/types.js"; +export { type ClientRequestOptions, Context, type ContextRenderer, type ContextVariableMap, type Env, type ErrorHandler, type ExecutionContext, type Handler, Hono, type HonoRequest, type InferRequestType, type InferResponseType, type Input, type MiddlewareHandler, type Next, type NotFoundHandler, type NotFoundResponse, type Schema, type ToSchema, type TypedResponse, type ValidationTargets }; \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/request.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/request.d.ts new file mode 100644 index 000000000..03d14a385 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/request.d.ts @@ -0,0 +1,326 @@ +import { GET_MATCH_RESULT } from "./request/constants.js"; +import { Result } from "./router.js"; +import { CustomHeader, RequestHeader } from "./utils/headers.js"; +import { Simplify, UnionToIntersection } from "./utils/types.js"; +import { Input, InputToDataByTarget, ParamKeyToRecord, ParamKeys, RemoveQuestion, RouterRoute, ValidationTargets } from "./types.js"; +import { BodyData, ParseBodyOptions } from "./utils/body.js"; +//#region src/request.d.ts +type Body = { + json: any; + text: string; + arrayBuffer: ArrayBuffer; + blob: Blob; + formData: FormData; +}; +type BodyCache = Partial; +export declare class HonoRequest

{ + + /** + * `.raw` can get the raw Request object. + * + * @see {@link https://hono.dev/docs/api/request#raw} + * + * @example + * ```ts + * // For Cloudflare Workers + * app.post('/', async (c) => { + * const metadata = c.req.raw.cf?.hostMetadata? + * ... + * }) + * ``` + */ + raw: Request; + routeIndex: number; + /** + * `.path` can get the pathname of the request. + * + * @see {@link https://hono.dev/docs/api/request#path} + * + * @example + * ```ts + * app.get('/about/me', (c) => { + * const pathname = c.req.path // `/about/me` + * }) + * ``` + */ + path: string; + bodyCache: BodyCache; + constructor(request: Request, path?: string, matchResult?: Result<[unknown, RouterRoute]>); + /** + * `.req.param()` gets the path parameters. + * + * @see {@link https://hono.dev/docs/api/routing#path-parameter} + * + * @example + * ```ts + * const name = c.req.param('name') + * // or all parameters at once + * const { id, comment_id } = c.req.param() + * ``` + */ + param = ParamKeys

>(key: string extends P ? never : P2 extends `${infer _}?` ? never : P2): string; + param> = RemoveQuestion>>(key: P2): string | undefined; + param(key: string): string | undefined; + param(): Simplify>>>; + /** + * `.query()` can get querystring parameters. + * + * @see {@link https://hono.dev/docs/api/request#query} + * + * @example + * ```ts + * // Query params + * app.get('/search', (c) => { + * const query = c.req.query('q') + * }) + * + * // Get all params at once + * app.get('/search', (c) => { + * const { q, limit, offset } = c.req.query() + * }) + * ``` + */ + query(key: string): string | undefined; + query(): Record; + /** + * `.queries()` can get multiple querystring parameter values, e.g. /search?tags=A&tags=B + * + * @see {@link https://hono.dev/docs/api/request#queries} + * + * @example + * ```ts + * app.get('/search', (c) => { + * // tags will be string[] + * const tags = c.req.queries('tags') + * }) + * ``` + */ + queries(key: string): string[] | undefined; + queries(): Record; + /** + * `.header()` can get the request header value. + * + * @see {@link https://hono.dev/docs/api/request#header} + * + * @example + * ```ts + * app.get('/', (c) => { + * const userAgent = c.req.header('User-Agent') + * }) + * ``` + */ + header(name: RequestHeader): string | undefined; + header(name: string): string | undefined; + header(): Record; + /** + * `.parseBody()` can parse Request body of type `multipart/form-data` or `application/x-www-form-urlencoded` + * + * @see {@link https://hono.dev/docs/api/request#parsebody} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.parseBody() + * }) + * ``` + */ + parseBody, T extends BodyData>(options?: Options): Promise; + parseBody(options?: Partial): Promise; + /** + * `.json()` can parse Request body of type `application/json` + * + * @see {@link https://hono.dev/docs/api/request#json} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.json() + * }) + * ``` + */ + json(): Promise; + /** + * `.text()` can parse Request body of type `text/plain` + * + * @see {@link https://hono.dev/docs/api/request#text} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.text() + * }) + * ``` + */ + text(): Promise; + /** + * `.arrayBuffer()` parse Request body as an `ArrayBuffer` + * + * @see {@link https://hono.dev/docs/api/request#arraybuffer} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.arrayBuffer() + * }) + * ``` + */ + arrayBuffer(): Promise; + /** + * `.bytes()` parses the request body as a `Uint8Array`. + * + * @see {@link https://hono.dev/docs/api/request#bytes} + * + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.bytes() + * }) + * ``` + */ + bytes(): Promise; + /** + * Parses the request body as a `Blob`. + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.blob(); + * }); + * ``` + * @see https://hono.dev/docs/api/request#blob + */ + blob(): Promise; + /** + * Parses the request body as `FormData`. + * @example + * ```ts + * app.post('/entry', async (c) => { + * const body = await c.req.formData(); + * }); + * ``` + * @see https://hono.dev/docs/api/request#formdata + */ + formData(): Promise; + /** + * Adds validated data to the request. + * + * @param target - The target of the validation. + * @param data - The validated data to add. + */ + addValidatedData(target: keyof ValidationTargets, data: {}): void; + /** + * Gets validated data from the request. + * + * @param target - The target of the validation. + * @returns The validated data. + * + * @see https://hono.dev/docs/api/request#valid + */ + valid(target: T): InputToDataByTarget; + /** + * `.url` can get the request url strings. + * + * @see {@link https://hono.dev/docs/api/request#url} + * + * @example + * ```ts + * app.get('/about/me', (c) => { + * const url = c.req.url // `http://localhost:8787/about/me` + * ... + * }) + * ``` + */ + get url(): string; + /** + * `.method` can get the method name of the request. + * + * @see {@link https://hono.dev/docs/api/request#method} + * + * @example + * ```ts + * app.get('/about/me', (c) => { + * const method = c.req.method // `GET` + * }) + * ``` + */ + get method(): string; + get [GET_MATCH_RESULT](): Result<[unknown, RouterRoute]>; + /** + * `.matchedRoutes` can return a matched route in the handler + * + * @deprecated + * + * Use matchedRoutes helper defined in "hono/route" instead. + * + * @see {@link https://hono.dev/docs/api/request#matchedroutes} + * + * @example + * ```ts + * app.use('*', async function logger(c, next) { + * await next() + * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => { + * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]') + * console.log( + * method, + * ' ', + * path, + * ' '.repeat(Math.max(10 - path.length, 0)), + * name, + * i === c.req.routeIndex ? '<- respond from here' : '' + * ) + * }) + * }) + * ``` + */ + get matchedRoutes(): RouterRoute[]; + /** + * `.routePath` can retrieve the path registered within the handler + * + * @deprecated + * + * Use routePath helper defined in "hono/route" instead. + * + * @see {@link https://hono.dev/docs/api/request#routepath} + * + * @example + * ```ts + * app.get('/posts/:id', (c) => { + * return c.json({ path: c.req.routePath }) + * }) + * ``` + */ + get routePath(): string; +} +/** + * Clones a HonoRequest's underlying raw Request object. + * + * This utility handles both consumed and unconsumed request bodies: + * - If the request body hasn't been consumed, it uses the native `clone()` method + * - If the request body has been consumed, it reconstructs a new Request using cached body data + * + * This is particularly useful when you need to: + * - Process the same request body multiple times + * - Pass requests to external services after validation + * + * @param req - The HonoRequest object to clone + * @returns A Promise that resolves to a new Request object with the same properties + * @throws {HTTPException} If the request body was consumed directly via `req.raw` + * without using HonoRequest methods (e.g., `req.json()`, `req.text()`), making it + * impossible to reconstruct the body from cache + * + * @example + * ```ts + * // Clone after consuming the body (e.g., after validation) + * app.post('/forward', + * validator('json', (data) => data), + * async (c) => { + * const validated = c.req.valid('json') + * // Body has been consumed, but cloneRawRequest still works + * const clonedReq = await cloneRawRequest(c.req) + * return fetch('http://backend-service.com', clonedReq) + * } + * ) + * ``` + */ +export declare const cloneRawRequest: (req: HonoRequest) => Promise; +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/request/constants.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/request/constants.d.ts new file mode 100644 index 000000000..0899a5d2b --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/request/constants.d.ts @@ -0,0 +1,3 @@ +//#region src/request/constants.d.ts +export declare const GET_MATCH_RESULT: unique symbol; +//#endregion \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/router.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/router.d.ts new file mode 100644 index 000000000..f00b40662 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/router.d.ts @@ -0,0 +1,98 @@ +//#region src/router.d.ts +/** + * @module + * This module provides types definitions and variables for the routers. + */ +/** + * Constant representing all HTTP methods in uppercase. + */ +export declare const METHOD_NAME_ALL: "ALL"; +/** + * Constant representing all HTTP methods in lowercase. + */ +export declare const METHOD_NAME_ALL_LOWERCASE: "all"; +/** + * Array of supported HTTP methods. + */ +export declare const METHODS: readonly ["get", "post", "put", "delete", "options", "patch", "query"]; +/** + * Error message indicating that a route cannot be added because the matcher is already built. + */ +export declare const MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built."; +/** + * Interface representing a router. + * + * @template T - The type of the handler. + */ +export interface Router { + /** + * The name of the router. + */ + name: string; + /** + * Adds a route to the router. + * + * @param method - The HTTP method (e.g., 'get', 'post'). + * @param path - The path for the route. + * @param handler - The handler for the route. + */ + add(method: string, path: string, handler: T): void; + /** + * Matches a route based on the given method and path. + * + * @param method - The HTTP method (e.g., 'get', 'post'). + * @param path - The path to match. + * @returns The result of the match. + */ + match(method: string, path: string): Result; +} +/** + * Type representing a map of parameter indices. + */ +export type ParamIndexMap = Record; +/** + * Type representing a stash of parameters. + */ +export type ParamStash = string[]; +/** + * Type representing a map of parameters. + */ +export type Params = Record; +/** + * Type representing the result of a route match. + * + * The result can be in one of two formats: + * 1. An array of handlers with their corresponding parameter index maps, followed by a parameter stash. + * 2. An array of handlers with their corresponding parameter maps. + * + * Example: + * + * [[handler, paramIndexMap][], paramArray] + * ```typescript + * [ + * [ + * [middlewareA, {}], // '*' + * [funcA, {'id': 0}], // '/user/:id/*' + * [funcB, {'id': 0, 'action': 1}], // '/user/:id/:action' + * ], + * ['123', 'abc'] + * ] + * ``` + * + * [[handler, params][]] + * ```typescript + * [ + * [ + * [middlewareA, {}], // '*' + * [funcA, {'id': '123'}], // '/user/:id/*' + * [funcB, {'id': '123', 'action': 'abc'}], // '/user/:id/:action' + * ] + * ] + * ``` + */ +export type Result = [[T, ParamIndexMap][], ParamStash] | [[T, Params][]]; +/** + * Error class representing an unsupported path error. + */ +export declare class UnsupportedPathError extends Error {} +//#endregion \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/types.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/types.d.ts new file mode 100644 index 000000000..4a0be1dea --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/types.d.ts @@ -0,0 +1,214 @@ +import { HonoBase as Hono } from "./hono-base.js"; +import { CustomHeader, RequestHeader } from "./utils/headers.js"; +import { StatusCode } from "./utils/http-status.js"; +import { IfAnyThenEmptyObject, IsAny, JSONValue, RemoveBlankRecord, Simplify, UnionToIntersection } from "./utils/types.js"; +import { Context } from "./context.js"; +//#region src/types.d.ts +export type Bindings = object; +export type Variables = object; +export type BlankEnv = {}; +export type Env = { + Bindings?: Bindings; + Variables?: Variables; +}; +export type Next = () => Promise; +export type ExtractInput = I extends Input ? unknown extends I['in'] ? {} : I['in'] : I; +export type Input = { + in?: {}; + out?: {}; + outputFormat?: ResponseFormat; +}; +export type BlankSchema = {}; +export type BlankInput = {}; +export interface RouterRoute { + basePath: string; + path: string; + method: string; + handler: H; +} +export type HandlerResponse = Response | TypedResponse | Promise> | Promise; +export type Handler = any> = (c: Context, next: Next) => R; +export type MiddlewareHandler = Response> = (c: Context, next: Next) => Promise; +export type H = any> = Handler | MiddlewareHandler; +/** + * You can extend this interface to define a custom `c.notFound()` Response type. + * + * @example + * declare module 'hono' { + * interface NotFoundResponse extends Response, TypedResponse {} + * } + */ +export interface NotFoundResponse {} +export type NotFoundHandler = (c: Context) => NotFoundResponse extends Response ? NotFoundResponse | Promise : Response | Promise; +export interface HTTPResponseError extends Error { + getResponse: () => Response; +} +export type ErrorHandler = (err: Error | HTTPResponseError, c: Context) => Response | Promise; +export interface HandlerInterface { +

= any, E2 extends Env = E>(handler: H): Hono, S & ToSchema>, BasePath, CurrentPath>; +

= any, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, M1 extends H = H>(...handlers: [H & M1, H]): Hono, S & ToSchema | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, E2 extends Env = E>(path: P, handler: H): Hono, S, M, P, I, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, M1 extends H = H, M2 extends H = H>(...handlers: [H & M1, H & M2, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, M1 extends H = H>(path: P, ...handlers: [H & M1, H]): Hono | MergeMiddlewareResponse, S, M, P, I2, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, M1 extends H = H, M2 extends H = H, M3 extends H = H>(...handlers: [H & M1, H & M2, H & M3, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, M1 extends H = H, M2 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I3, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H>(...handlers: [H & M1, H & M2, H & M3, H & M4, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, M1 extends H = H, M2 extends H = H, M3 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H & M3, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I4, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H>(...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I5, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H>(...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I6, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H>(...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I7, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H>(...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I8, BasePath>, BasePath, MergePath>; +

= any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, I10 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8 & I9, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, E11 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H, M9 extends H = H>(...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H & M9, H]): Hono, S & ToSchema | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, CurrentPath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I9, BasePath>, BasePath, MergePath>; +

, R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, I10 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8 & I9, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, E11 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H, M9 extends H = H>(path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H & M9, H]): Hono | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse, S, M, P, I10, BasePath>, BasePath, MergePath>; +

= any>(...handlers: H[]): Hono>, BasePath, CurrentPath>; +

= any>(path: P, ...handlers: [H, I, R>, ...H, I, R>[]]): Hono, I, MergeTypedResponse>, BasePath, MergePath>; +

= any, I extends Input = BlankInput>(path: P): Hono, I, MergeTypedResponse>, BasePath, MergePath>; +} +export interface MiddlewareHandlerInterface { + (...handlers: MiddlewareHandler>[]): Hono, S, BasePath, MergePath>; + (handler: MiddlewareHandler>): Hono, S, BasePath, MergePath>; + , P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E>(path: P, handler: MiddlewareHandler): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; + , E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, E11 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>, P extends string = MergePath>(...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, P>; +

, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>>(path: P, ...handlers: [MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler, MiddlewareHandler]): Hono, S, BasePath, MergedPath>; +

(path: P, ...handlers: MiddlewareHandler>[]): Hono>; +} +export interface OnHandlerInterface { + , R extends HandlerResponse = any, I extends Input = BlankInput, E2 extends Env = E>(method: M, path: P, handler: H): Hono, S & ToSchema, I, MergeTypedResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, M1 extends H = H>(method: M, path: P, ...handlers: [H & M1, H]): Hono, S & ToSchema, I2, MergeTypedResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, M1 extends H = H, M2 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H]): Hono, S & ToSchema, I3, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, M1 extends H = H, M2 extends H = H, M3 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H & M3, H]): Hono, S & ToSchema, I4, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H]): Hono, S & ToSchema, I5, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H]): Hono, S & ToSchema, I6, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H]): Hono, S & ToSchema, I7, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H]): Hono, S & ToSchema, I8, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H]): Hono, S & ToSchema, I9, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, I10 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8 & I9, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, E11 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H, M9 extends H = H>(method: M, path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H & M9, H]): Hono, S & ToSchema, I10, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + = any, I extends Input = BlankInput>(method: M, path: P, ...handlers: [H, I, R>, ...H, I, R>[]]): Hono, I, MergeTypedResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, E2 extends Env = E>(methods: M[], path: P, handler: H): Hono, S & ToSchema, I, MergeTypedResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, M1 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H]): Hono, S & ToSchema, I2, MergeTypedResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, M1 extends H = H, M2 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H]): Hono, S & ToSchema, I3, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, M1 extends H = H, M2 extends H = H, M3 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H & M3, H]): Hono, S & ToSchema, I4, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H]): Hono, S & ToSchema, I5, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H]): Hono, S & ToSchema, I6, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H]): Hono, S & ToSchema, I7, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H]): Hono, S & ToSchema, I8, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H]): Hono, S & ToSchema, I9, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + , R extends HandlerResponse = any, I extends Input = BlankInput, I2 extends Input = I, I3 extends Input = I & I2, I4 extends Input = I & I2 & I3, I5 extends Input = I & I2 & I3 & I4, I6 extends Input = I & I2 & I3 & I4 & I5, I7 extends Input = I & I2 & I3 & I4 & I5 & I6, I8 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7, I9 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8, I10 extends Input = I & I2 & I3 & I4 & I5 & I6 & I7 & I8 & I9, E2 extends Env = E, E3 extends Env = IntersectNonAnyTypes<[E, E2]>, E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>, E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>, E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, E11 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>, M1 extends H = H, M2 extends H = H, M3 extends H = H, M4 extends H = H, M5 extends H = H, M6 extends H = H, M7 extends H = H, M8 extends H = H, M9 extends H = H>(methods: M[], path: P, ...handlers: [H & M1, H & M2, H & M3, H & M4, H & M5, H & M6, H & M7, H & M8, H & M9, H]): Hono, S & ToSchema, I10, MergeTypedResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse | MergeMiddlewareResponse>, BasePath, MergePath>; + = any, I extends Input = BlankInput>(methods: M[], path: P, ...handlers: [H, I, R>, ...H, I, R>[]]): Hono, I, MergeTypedResponse>, BasePath, MergePath>; + = any, E2 extends Env = E>(methods: M | M[], paths: Ps, ...handlers: H, I, R>[]): Hono, I, MergeTypedResponse>, BasePath, Ps extends [...string[], infer LastPath extends string] ? MergePath : never>; +} +type ToSchemaOutput = RorO extends TypedResponse ? { + output: unknown extends T ? {} : T; + outputFormat: I extends { + outputFormat: string; + } ? I['outputFormat'] : F; + status: U; +} : { + output: unknown extends RorO ? {} : RorO; + outputFormat: unknown extends RorO ? 'json' : I extends { + outputFormat: string; + } ? I['outputFormat'] : 'json'; + status: StatusCode; +}; +export type ToSchema = IsAny extends true ? { [K in P]: { [K2 in M as AddDollar]: { + input: AddParam, P>; + output: {}; + outputFormat: ResponseFormat; + status: StatusCode; +}; }; } : [RorO] extends [never] ? {} : [RorO] extends [Promise] ? {} : { [K in P]: { [K2 in M as AddDollar]: Simplify<{ + input: AddParam, P>; +} & ToSchemaOutput>; }; }; +export type Schema = { + [Path: string]: { + [Method: `$${Lowercase}`]: Endpoint; + }; +}; +type AddSchemaIfHasResponse = [Merged] extends [Promise] ? S : S & ToSchema, I, Merged>; +export type Endpoint = { + input: any; + output: any; + outputFormat: ResponseFormat; + status: StatusCode; +}; +type ExtractParams = string extends Path ? Record : Path extends `${infer _Start}:${infer Param}/${infer Rest}` ? { [K in Param | keyof ExtractParams<`/${Rest}`>]: string; } : Path extends `${infer _Start}:${infer Param}` ? { [K in Param]: string; } : never; +type FlattenIfIntersect = T extends (infer O) ? { [K in keyof O]: O[K]; } : never; +export type MergeSchemaPath = { [P in keyof OrigSchema as MergePath]: [OrigSchema[P]] extends [Record] ? { [M in keyof OrigSchema[P]]: MergeEndpointParamsWithPath; } : never; }; +type MergeEndpointParamsWithPath = T extends unknown ? { + input: T['input'] extends { + param: infer _; + } ? ExtractParams extends never ? T['input'] : FlattenIfIntersect as K extends `${infer Prefix}{${infer _}}` ? Prefix : K]: string; }; + }> : RemoveBlankRecord> extends never ? T['input'] : T['input'] & { + param: { [K in keyof ExtractParams as K extends `${infer Prefix}{${infer _}}` ? Prefix : K]: string; }; + }; + output: T['output']; + outputFormat: T['outputFormat']; + status: T['status']; +} : never; +export type AddParam = ParamKeys

extends never ? I : I extends { + param: infer _; +} ? I : I & { + param: UnionToIntersection>>; +}; +type AddDollar = `$${Lowercase}`; +export type MergePath = B extends '' ? MergePath : A extends '' ? B : A extends '/' ? B : A extends `${infer P}/` ? B extends `/${infer Q}` ? `${P}/${Q}` : `${P}/${B}` : B extends `/${infer Q}` ? Q extends '' ? A : `${A}/${Q}` : `${A}/${B}`; +export type KnownResponseFormat = 'json' | 'text' | 'redirect'; +export type ResponseFormat = KnownResponseFormat | string; +export type TypedResponse = { + _data: T; + _status: U; + _format: F; +}; +type MergeTypedResponse = T extends Promise ? T : T extends Promise ? T2 extends TypedResponse ? T2 : TypedResponse : T extends TypedResponse ? T : TypedResponse; +type ExtractTypedResponseOnly = T extends TypedResponse ? T : never; +type MergeMiddlewareResponse = T extends ((c: any, next: any) => Promise) ? Exclude extends never ? never : Exclude extends Response | TypedResponse ? ExtractTypedResponseOnly> : never : T extends ((c: any, next: any) => infer R) ? R extends Response | TypedResponse ? ExtractTypedResponseOnly : never : never; +export type FormValue = string | Blob; +export type ParsedFormValue = string | File; +export type ValidationTargets = { + json: any; + form: Record; + query: Record; + param: Record; + header: Record; + cookie: Record; +}; +type ParamKey = Component extends `:${infer NameWithPattern}` ? NameWithPattern extends `${infer Name}{${infer Rest}` ? Rest extends `${infer _Pattern}?` ? `${Name}?` : Name : NameWithPattern : never; +export type ParamKeys = Path extends `${infer Component}/${infer Rest}` ? ParamKey | ParamKeys : ParamKey; +export type ParamKeyToRecord = T extends `${infer R}?` ? Record : { [K in T]: string; }; +export type InputToDataByTarget = T extends { [K in Target]: infer R; } ? R : never; +export type RemoveQuestion = T extends `${infer R}?` ? R : T; +export type ExtractSchema = UnionToIntersection ? S : never>; +export type ExtractSchemaForStatusCode = { [Path in keyof ExtractSchema]: { [Method in keyof ExtractSchema[Path]]: Extract[Path][Method], { + status: Status; +}>; }; }; +export type ExtractHandlerResponse = T extends ((c: any, next: any) => Promise) ? Exclude extends never ? never : Exclude extends Response | TypedResponse ? Exclude : never : T extends ((c: any, next: any) => infer R) ? R extends Response | TypedResponse ? R : never : never; +type ProcessHead = IfAnyThenEmptyObject; +export type IntersectNonAnyTypes = T extends [infer Head, ...infer Rest] ? ProcessHead & IntersectNonAnyTypes : {}; +export declare abstract class FetchEventLike { + abstract readonly request: Request; + abstract respondWith(promise: Response | Promise): void; + abstract passThroughOnException(): void; + abstract waitUntil(promise: Promise): void; +} +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/utils/body.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/utils/body.d.ts new file mode 100644 index 000000000..9bbf8cafd --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/utils/body.d.ts @@ -0,0 +1,75 @@ +import { HonoRequest } from "../request.js"; +//#region src/utils/body.d.ts +type BodyDataValueDot = { + [x: string]: string | File | BodyDataValueDot; +}; +type BodyDataValueDotAll = { + [x: string]: string | File | (string | File)[] | BodyDataValueDotAll; +}; +type SimplifyBodyData = { [K in keyof T]: string | File | (string | File)[] | BodyDataValueDotAll extends T[K] ? string | File | (string | File)[] | BodyDataValueDotAll : string | File | BodyDataValueDot extends T[K] ? string | File | BodyDataValueDot : string | File | (string | File)[] extends T[K] ? string | File | (string | File)[] : string | File; } & {}; +type BodyDataValueComponent = string | File | (T extends { + all: false; +} ? never : T extends { + all: true; +} | { + all: boolean; +} ? (string | File)[] : never); +type BodyDataValueObject = { + [key: string]: BodyDataValueComponent | BodyDataValueObject; +}; +type BodyDataValue = BodyDataValueComponent | (T extends { + dot: false; +} ? never : T extends { + dot: true; +} | { + dot: boolean; +} ? BodyDataValueObject : never); +export type BodyData = {}> = SimplifyBodyData>>; +export type ParseBodyOptions = { + /** + * Determines whether all fields with multiple values should be parsed as arrays. + * @default false + * @example + * const data = new FormData() + * data.append('file', 'aaa') + * data.append('file', 'bbb') + * data.append('message', 'hello') + * + * If all is false: + * parseBody should return { file: 'bbb', message: 'hello' } + * + * If all is true: + * parseBody should return { file: ['aaa', 'bbb'], message: 'hello' } + */ + all: boolean; + /** + * Determines whether all fields with dot notation should be parsed as nested objects. + * @default false + * @example + * const data = new FormData() + * data.append('obj.key1', 'value1') + * data.append('obj.key2', 'value2') + * + * If dot is false: + * parseBody should return { 'obj.key1': 'value1', 'obj.key2': 'value2' } + * + * If dot is true: + * parseBody should return { obj: { key1: 'value1', key2: 'value2' } } + */ + dot: boolean; +}; +/** + * Parses the body of a request based on the provided options. + * + * @template T - The type of the parsed body data. + * @param {HonoRequest | Request} request - The request object to parse. + * @param {Partial} [options] - Options for parsing the body. + * @returns {Promise} The parsed body data. + */ +interface ParseBody { + , T extends BodyData>(request: HonoRequest | Request, options?: Options): Promise; + (request: HonoRequest | Request, options?: Partial): Promise; +} +export declare const parseBody: ParseBody; +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/utils/headers.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/utils/headers.d.ts new file mode 100644 index 000000000..d3edf8c77 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/utils/headers.d.ts @@ -0,0 +1,10 @@ +//#region src/utils/headers.d.ts +/** + * @module + * HTTP Headers utility. + */ +export type RequestHeader = 'A-IM' | 'Accept' | 'Accept-Additions' | 'Accept-CH' | 'Accept-Charset' | 'Accept-Datetime' | 'Accept-Encoding' | 'Accept-Features' | 'Accept-Language' | 'Accept-Patch' | 'Accept-Post' | 'Accept-Query' | 'Accept-Ranges' | 'Accept-Signature' | 'Access-Control' | 'Access-Control-Allow-Credentials' | 'Access-Control-Allow-Headers' | 'Access-Control-Allow-Methods' | 'Access-Control-Allow-Origin' | 'Access-Control-Expose-Headers' | 'Access-Control-Max-Age' | 'Access-Control-Request-Headers' | 'Access-Control-Request-Method' | 'Activate-Storage-Access' | 'Age' | 'Allow' | 'ALPN' | 'Alt-Svc' | 'Alt-Used' | 'Alternates' | 'AMP-Cache-Transform' | 'Apply-To-Redirect-Ref' | 'Authentication-Control' | 'Authentication-Info' | 'Authorization' | 'Available-Dictionary' | 'C-Ext' | 'C-Man' | 'C-Opt' | 'C-PEP' | 'C-PEP-Info' | 'Cache-Control' | 'Cache-Group-Invalidation' | 'Cache-Groups' | 'Cache-Status' | 'Cal-Managed-ID' | 'CalDAV-Timezones' | 'Capsule-Protocol' | 'CDN-Cache-Control' | 'CDN-Loop' | 'Cert-Not-After' | 'Cert-Not-Before' | 'Clear-Site-Data' | 'Client-Cert' | 'Client-Cert-Chain' | 'Close' | 'CMCD-Object' | 'CMCD-Request' | 'CMCD-Session' | 'CMCD-Status' | 'CMSD-Dynamic' | 'CMSD-Static' | 'Concealed-Auth-Export' | 'Configuration-Context' | 'Connection' | 'Content-Base' | 'Content-Digest' | 'Content-Disposition' | 'Content-Encoding' | 'Content-ID' | 'Content-Language' | 'Content-Length' | 'Content-Location' | 'Content-MD5' | 'Content-Range' | 'Content-Script-Type' | 'Content-Security-Policy' | 'Content-Security-Policy-Report-Only' | 'Content-Style-Type' | 'Content-Type' | 'Content-Version' | 'Cookie' | 'Cookie2' | 'Cross-Origin-Embedder-Policy' | 'Cross-Origin-Embedder-Policy-Report-Only' | 'Cross-Origin-Opener-Policy' | 'Cross-Origin-Opener-Policy-Report-Only' | 'Cross-Origin-Resource-Policy' | 'CTA-Common-Access-Token' | 'DASL' | 'Date' | 'DAV' | 'Default-Style' | 'Delta-Base' | 'Deprecation' | 'Depth' | 'Derived-From' | 'Destination' | 'Detached-JWS' | 'Differential-ID' | 'Dictionary-ID' | 'Digest' | 'DPoP' | 'DPoP-Nonce' | 'Early-Data' | 'EDIINT-Features' | 'ETag' | 'Expect' | 'Expect-CT' | 'Expires' | 'Ext' | 'Forwarded' | 'From' | 'GetProfile' | 'Hobareg' | 'Host' | 'HTTP2-Settings' | 'If' | 'If-Match' | 'If-Modified-Since' | 'If-None-Match' | 'If-Range' | 'If-Schedule-Tag-Match' | 'If-Unmodified-Since' | 'IM' | 'Include-Referred-Token-Binding-ID' | 'Incremental' | 'Isolation' | 'Keep-Alive' | 'Label' | 'Last-Event-ID' | 'Last-Modified' | 'Link' | 'Link-Template' | 'Location' | 'Lock-Token' | 'Man' | 'Max-Forwards' | 'Memento-Datetime' | 'Meter' | 'Method-Check' | 'Method-Check-Expires' | 'MIME-Version' | 'Negotiate' | 'NEL' | 'OData-EntityId' | 'OData-Isolation' | 'OData-MaxVersion' | 'OData-Version' | 'Opt' | 'Optional-WWW-Authenticate' | 'Ordering-Type' | 'Origin' | 'Origin-Agent-Cluster' | 'OSCORE' | 'OSLC-Core-Version' | 'Overwrite' | 'P3P' | 'PEP' | 'PEP-Info' | 'Permissions-Policy' | 'PICS-Label' | 'Ping-From' | 'Ping-To' | 'Position' | 'Pragma' | 'Prefer' | 'Preference-Applied' | 'Priority' | 'ProfileObject' | 'Protocol' | 'Protocol-Info' | 'Protocol-Query' | 'Protocol-Request' | 'Proxy-Authenticate' | 'Proxy-Authentication-Info' | 'Proxy-Authorization' | 'Proxy-Features' | 'Proxy-Instruction' | 'Proxy-Status' | 'Public' | 'Public-Key-Pins' | 'Public-Key-Pins-Report-Only' | 'Range' | 'Redirect-Ref' | 'Referer' | 'Referer-Root' | 'Referrer-Policy' | 'Refresh' | 'Repeatability-Client-ID' | 'Repeatability-First-Sent' | 'Repeatability-Request-ID' | 'Repeatability-Result' | 'Replay-Nonce' | 'Reporting-Endpoints' | 'Repr-Digest' | 'Retry-After' | 'Safe' | 'Schedule-Reply' | 'Schedule-Tag' | 'Sec-Fetch-Dest' | 'Sec-Fetch-Mode' | 'Sec-Fetch-Site' | 'Sec-Fetch-Storage-Access' | 'Sec-Fetch-User' | 'Sec-GPC' | 'Sec-Purpose' | 'Sec-Token-Binding' | 'Sec-WebSocket-Accept' | 'Sec-WebSocket-Extensions' | 'Sec-WebSocket-Key' | 'Sec-WebSocket-Protocol' | 'Sec-WebSocket-Version' | 'Security-Scheme' | 'Server' | 'Server-Timing' | 'Set-Cookie' | 'Set-Cookie2' | 'Set-Txn' | 'SetProfile' | 'Signature' | 'Signature-Input' | 'SLUG' | 'SoapAction' | 'Status-URI' | 'Strict-Transport-Security' | 'Sunset' | 'Surrogate-Capability' | 'Surrogate-Control' | 'TCN' | 'TE' | 'Timeout' | 'Timing-Allow-Origin' | 'Topic' | 'Traceparent' | 'Tracestate' | 'Trailer' | 'Transfer-Encoding' | 'TTL' | 'Unencoded-Digest' | 'Upgrade' | 'Urgency' | 'URI' | 'Use-As-Dictionary' | 'User-Agent' | 'Variant-Vary' | 'Vary' | 'Via' | 'Want-Content-Digest' | 'Want-Digest' | 'Want-Repr-Digest' | 'Want-Unencoded-Digest' | 'Warning' | 'WWW-Authenticate' | 'X-Content-Type-Options' | 'X-Frame-Options'; +export type ResponseHeader = 'Accept-Query' | 'Access-Control-Allow-Credentials' | 'Access-Control-Allow-Headers' | 'Access-Control-Allow-Methods' | 'Access-Control-Allow-Origin' | 'Access-Control-Expose-Headers' | 'Access-Control-Max-Age' | 'Activate-Storage-Access' | 'Age' | 'Allow' | 'Cache-Control' | 'Cache-Group-Invalidation' | 'Cache-Groups' | 'Clear-Site-Data' | 'Content-Disposition' | 'Content-Encoding' | 'Content-Language' | 'Content-Length' | 'Content-Location' | 'Content-Range' | 'Content-Security-Policy' | 'Content-Security-Policy-Report-Only' | 'Content-Type' | 'Cookie' | 'Cross-Origin-Embedder-Policy' | 'Cross-Origin-Opener-Policy' | 'Cross-Origin-Resource-Policy' | 'Date' | 'ETag' | 'Expires' | 'Incremental' | 'Last-Modified' | 'Location' | 'Permissions-Policy' | 'Pragma' | 'Retry-After' | 'Save-Data' | 'Sec-CH-Prefers-Color-Scheme' | 'Sec-CH-Prefers-Reduced-Motion' | 'Sec-CH-UA' | 'Sec-CH-UA-Arch' | 'Sec-CH-UA-Bitness' | 'Sec-CH-UA-Form-Factor' | 'Sec-CH-UA-Full-Version' | 'Sec-CH-UA-Full-Version-List' | 'Sec-CH-UA-Mobile' | 'Sec-CH-UA-Model' | 'Sec-CH-UA-Platform' | 'Sec-CH-UA-Platform-Version' | 'Sec-CH-UA-WoW64' | 'Sec-Fetch-Dest' | 'Sec-Fetch-Mode' | 'Sec-Fetch-Site' | 'Sec-Fetch-User' | 'Sec-GPC' | 'Server' | 'Server-Timing' | 'Service-Worker-Navigation-Preload' | 'Set-Cookie' | 'Set-Txn' | 'Strict-Transport-Security' | 'Timing-Allow-Origin' | 'Trailer' | 'Transfer-Encoding' | 'Unencoded-Digest' | 'Upgrade' | 'Vary' | 'Want-Unencoded-Digest' | 'Warning' | 'WWW-Authenticate' | 'X-Content-Type-Options' | 'X-DNS-Prefetch-Control' | 'X-Frame-Options' | 'X-Permitted-Cross-Domain-Policies' | 'X-Powered-By' | 'X-Robots-Tag' | 'X-XSS-Protection'; +export type AcceptHeader = 'Accept' | 'Accept-Charset' | 'Accept-Encoding' | 'Accept-Language' | 'Accept-Patch' | 'Accept-Post' | 'Accept-Ranges'; +export type CustomHeader = string & {}; +//#endregion \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/utils/http-status.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/utils/http-status.d.ts new file mode 100644 index 000000000..4c48304e2 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/utils/http-status.d.ts @@ -0,0 +1,34 @@ +//#region src/utils/http-status.d.ts +/** + * @module + * HTTP Status utility. + */ +export type InfoStatusCode = 100 | 101 | 102 | 103; +export type SuccessStatusCode = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226; +export type DeprecatedStatusCode = 305 | 306; +export type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | DeprecatedStatusCode | 307 | 308; +export type ClientErrorStatusCode = 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451; +export type ServerErrorStatusCode = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511; +/** + * `UnofficialStatusCode` can be used to specify an unofficial status code. + * @example + * + * ```ts + * app.get('/unknown', (c) => { + * return c.text("Unknown Error", 520 as UnofficialStatusCode) + * }) + * ``` + */ +export type UnofficialStatusCode = -1; +/** + * @deprecated + * Use `UnofficialStatusCode` instead. + */ +export type UnOfficalStatusCode = UnofficialStatusCode; +/** + * If you want to use an unofficial status, use `UnofficialStatusCode`. + */ +export type StatusCode = InfoStatusCode | SuccessStatusCode | RedirectStatusCode | ClientErrorStatusCode | ServerErrorStatusCode | UnofficialStatusCode; +export type ContentlessStatusCode = 101 | 204 | 205 | 304; +export type ContentfulStatusCode = Exclude; +//#endregion \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/utils/mime.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/utils/mime.d.ts new file mode 100644 index 000000000..b4ea3597e --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/utils/mime.d.ts @@ -0,0 +1,73 @@ +//#region src/utils/mime.d.ts +/** + * @module + * MIME utility. + */ +export declare const getMimeType: (filename: string, mimes?: Record) => string | undefined; +export declare const getExtension: (mimeType: string) => string | undefined; +/** + * Union types for BaseMime + */ +export type BaseMime = (typeof _baseMimes)[keyof typeof _baseMimes]; +declare const _baseMimes: { + readonly aac: "audio/aac"; + readonly avi: "video/x-msvideo"; + readonly avif: "image/avif"; + readonly av1: "video/av1"; + readonly bin: "application/octet-stream"; + readonly bmp: "image/bmp"; + readonly css: "text/css; charset=utf-8"; + readonly csv: "text/csv; charset=utf-8"; + readonly eot: "application/vnd.ms-fontobject"; + readonly epub: "application/epub+zip"; + readonly gif: "image/gif"; + readonly gz: "application/gzip"; + readonly htm: "text/html; charset=utf-8"; + readonly html: "text/html; charset=utf-8"; + readonly ico: "image/x-icon"; + readonly ics: "text/calendar; charset=utf-8"; + readonly jpeg: "image/jpeg"; + readonly jpg: "image/jpeg"; + readonly js: "text/javascript; charset=utf-8"; + readonly json: "application/json"; + readonly jsonld: "application/ld+json"; + readonly map: "application/json"; + readonly mid: "audio/x-midi"; + readonly midi: "audio/x-midi"; + readonly mjs: "text/javascript; charset=utf-8"; + readonly mp3: "audio/mpeg"; + readonly mp4: "video/mp4"; + readonly mpeg: "video/mpeg"; + readonly oga: "audio/ogg"; + readonly ogv: "video/ogg"; + readonly ogx: "application/ogg"; + readonly opus: "audio/opus"; + readonly otf: "font/otf"; + readonly pdf: "application/pdf"; + readonly png: "image/png"; + readonly rtf: "application/rtf"; + readonly svg: "image/svg+xml; charset=utf-8"; + readonly tif: "image/tiff"; + readonly tiff: "image/tiff"; + readonly ts: "video/mp2t"; + readonly ttf: "font/ttf"; + readonly txt: "text/plain; charset=utf-8"; + readonly wasm: "application/wasm"; + readonly webm: "video/webm"; + readonly weba: "audio/webm"; + readonly webmanifest: "application/manifest+json"; + readonly webp: "image/webp"; + readonly woff: "font/woff"; + readonly woff2: "font/woff2"; + readonly xhtml: "application/xhtml+xml; charset=utf-8"; + readonly xml: "application/xml; charset=utf-8"; + readonly zip: "application/zip"; + readonly '3gp': "video/3gpp"; + readonly '3g2': "video/3gpp2"; + readonly gltf: "model/gltf+json"; + readonly glb: "model/gltf-binary"; +}; +declare const baseMimes: Record; +//#endregion +export { baseMimes as mimes }; +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/utils/types.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/utils/types.d.ts new file mode 100644 index 000000000..2a4ad3781 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/utils/types.d.ts @@ -0,0 +1,62 @@ +//#region src/utils/types.d.ts +/** + * @module + * Types utility. + */ +export type Expect = T; +export type Equal = (() => T extends X ? 1 : 2) extends (() => T extends Y ? 1 : 2) ? true : false; +export type NotEqual = true extends Equal ? false : true; +export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; +export type RemoveBlankRecord = T extends Record ? (K extends string ? T : never) : never; +export type IfAnyThenEmptyObject = 0 extends 1 & T ? {} : T; +export type JSONPrimitive = string | boolean | number | null; +export type JSONArray = (JSONPrimitive | JSONObject | JSONArray)[]; +export type JSONObject = { + [key: string]: JSONPrimitive | JSONArray | JSONObject | object | InvalidJSONValue; +}; +export type InvalidJSONValue = undefined | symbol | ((...args: unknown[]) => unknown); +type InvalidToNull = T extends InvalidJSONValue ? null : T; +type IsInvalid = T extends InvalidJSONValue ? true : false; +/** + * symbol keys are omitted through `JSON.stringify` + */ +type OmitSymbolKeys = { [K in keyof T as K extends symbol ? never : K]: T[K]; }; +export type JSONValue = JSONObject | JSONArray | JSONPrimitive; +/** + * Convert a type to a JSON-compatible type. + * + * Non-JSON values such as `Date` implement `.toJSON()`, + * so they can be transformed to a value assignable to `JSONObject` + * + * `JSON.stringify()` throws a `TypeError` when it encounters a `bigint` value, + * unless a custom `replacer` function or `.toJSON()` method is provided. + * + * This behavior can be controlled by the `TError` generic type parameter, + * which defaults to `bigint | ReadonlyArray`. + * You can set it to `never` to disable this check. + */ +export type JSONParsed> = T extends { + toJSON(): infer J; +} ? (() => J) extends (() => JSONPrimitive) ? J : (() => J) extends (() => { + toJSON(): unknown; +}) ? {} : JSONParsed : T extends JSONPrimitive ? T : T extends InvalidJSONValue ? never : T extends ReadonlyArray ? { [K in keyof T]: JSONParsed, TError>; } extends (infer A) ? A extends ReadonlyArray ? A : JSONParsed, TError>[] : never : T extends Set | Map | Record ? {} : T extends object ? T[keyof T] extends TError ? never : { [K in keyof OmitSymbolKeys as IsInvalid extends true ? never : K]: boolean extends IsInvalid ? JSONParsed | undefined : JSONParsed; } : T extends unknown ? T extends TError ? never : JSONValue : never; +/** + * Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability. + * @copyright from sindresorhus/type-fest + */ +export type Simplify = { [KeyType in keyof T]: T[KeyType]; } & {}; +/** + * A simple extension of Simplify that will deeply traverse array elements. + */ +export type SimplifyDeepArray = T extends any[] ? { [E in keyof T]: SimplifyDeepArray; } : Simplify; +export type InterfaceToType = T extends Function ? T : { [K in keyof T]: InterfaceToType; }; +export type RequiredKeysOf = Exclude<{ [Key in keyof BaseType]: BaseType extends Record ? Key : never; }[keyof BaseType], undefined>; +export type HasRequiredKeys = RequiredKeysOf extends never ? false : true; +export type IsAny = boolean extends (T extends never ? true : false) ? true : false; +/** + * String literal types with auto-completion + * @see https://github.com/Microsoft/TypeScript/issues/29729 + */ +export type StringLiteralUnion = T | (string & Record); +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/validator/index.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/validator/index.d.ts new file mode 100644 index 000000000..3a494895e --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/validator/index.d.ts @@ -0,0 +1,3 @@ +import { InferInput } from "./utils.js"; +import { ValidationFunction, validator } from "./validator.js"; +export { type InferInput, type ValidationFunction, validator }; \ No newline at end of file diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/validator/utils.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/validator/utils.d.ts new file mode 100644 index 000000000..13159be3c --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/validator/utils.d.ts @@ -0,0 +1,27 @@ +import { UnionToIntersection } from "../utils/types.js"; +import { FormValue, ParsedFormValue, ValidationTargets } from "../types.js"; +//#region src/validator/utils.d.ts +/** + * Checks if T is a literal union type (e.g., 'asc' | 'desc') + * that should be preserved in input types. + * Returns true for union literals, false for single literals or wide types. + */ +export type IsLiteralUnion = [Exclude] extends [Base] ? [Exclude] extends [UnionToIntersection>] ? false : true : false; +type IsOptionalUnion = [unknown] extends [T] ? false : undefined extends T ? true : false; +type SimplifyDeep = { [K in keyof T]: T[K]; } & {}; +type InferInputInner = SimplifyDeep<{ [K in keyof Output]: IsLiteralUnion extends true ? Output[K] : IsOptionalUnion extends true ? Output[K] : Target extends 'form' ? T | T[] : Target extends 'query' ? string | string[] : Target extends 'param' ? string : Target extends 'header' ? string : Target extends 'cookie' ? string : unknown; }>; +/** + * Utility type to infer input types for validation targets. + * Preserves literal union types (e.g., 'asc' | 'desc') while using + * the default ValidationTargets type for other values. + * + * @example + * ```ts + * // In @hono/zod-validator or similar: + * type Input = InferInput, 'query'> + * // { orderBy: 'asc' | 'desc', page: string | string[] } + * ``` + */ +export type InferInput = [Exclude] extends [never] ? {} : [Exclude] extends [object] ? undefined extends Output ? SimplifyDeep, Target, T>> | undefined : SimplifyDeep> : {}; +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/types/validator/validator.d.ts b/tests/fixtures/npm/node_modules/hono/dist/types/validator/validator.d.ts new file mode 100644 index 000000000..85e09a1a3 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/types/validator/validator.d.ts @@ -0,0 +1,18 @@ +import { Env, FormValue, MiddlewareHandler, TypedResponse, ValidationTargets } from "../types.js"; +import { Context } from "../context.js"; +import { InferInput } from "./utils.js"; +//#region src/validator/validator.d.ts +type ValidationTargetKeysWithBody = 'form' | 'json'; +type ValidationTargetByMethod = M extends 'get' | 'head' ? Exclude : keyof ValidationTargets; +export type ValidationFunction = (value: InputType, c: Context) => OutputType | TypedResponse | Promise | Promise; +export type ExtractValidationResponse = VF extends ((value: any, c: any) => infer R) ? R extends Promise ? PR extends TypedResponse ? TypedResponse : PR extends Response ? PR : PR extends undefined ? never : never : R extends TypedResponse ? TypedResponse : R extends Response ? R : R extends undefined ? never : never : never; +export declare const validator: , P2 extends string = P, VF extends (value: unknown extends InputType ? ValidationTargets[U] : InputType, c: Context) => any = (value: unknown extends InputType ? ValidationTargets[U] : InputType, c: Context) => any, V extends { + in: { [K in U]: K extends "json" ? unknown extends InputType ? ExtractValidatorOutput : InputType : InferInput, K, FormValue>; }; + out: { [K in U]: ExtractValidatorOutput; }; +} = { + in: { [K in U]: K extends "json" ? unknown extends InputType ? ExtractValidatorOutput : InputType : InferInput, K, FormValue>; }; + out: { [K in U]: ExtractValidatorOutput; }; +}, E extends Env = any>(target: U, validationFunc: VF) => MiddlewareHandler>; +export type ExtractValidatorOutput = VF extends ((value: any, c: any) => infer R) ? R extends Promise ? PR extends Response | TypedResponse ? never : PR : R extends Response | TypedResponse ? never : R : never; +//#endregion +export {}; diff --git a/tests/fixtures/npm/node_modules/hono/dist/utils/body.js b/tests/fixtures/npm/node_modules/hono/dist/utils/body.js new file mode 100644 index 000000000..6fe8e3031 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/utils/body.js @@ -0,0 +1,96 @@ +import { bufferToFormData } from "./buffer.js"; +//#region src/utils/body.ts +const MAX_NESTED_OBJECTS = 1e4; +const isRawRequest = (request) => "headers" in request; +const parseBody = async (request, options = Object.create(null)) => { + const { all = false, dot = false } = options; + const mediaType = (isRawRequest(request) ? request.headers : request.raw.headers).get("Content-Type")?.split(";")[0].trim().toLowerCase(); + if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") return parseFormData(request, { + all, + dot + }); + return {}; +}; +/** +* Parses form data from a request. +* +* @template T - The type of the parsed body data. +* @param {HonoRequest | Request} request - The request object containing form data. +* @param {ParseBodyOptions} options - Options for parsing the form data. +* @returns {Promise} The parsed body data. +*/ +async function parseFormData(request, options) { + if (!isRawRequest(request) && request.bodyCache.formData) return convertFormDataToBodyData(await request.bodyCache.formData, options); + const headers = isRawRequest(request) ? request.headers : request.raw.headers; + const arrayBuffer = await request.arrayBuffer(); + const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || ""); + if (!isRawRequest(request)) request.bodyCache.formData = formDataPromise; + const formData = await formDataPromise; + if (formData) return convertFormDataToBodyData(formData, options); + return {}; +} +/** +* Converts form data to body data based on the provided options. +* +* @template T - The type of the parsed body data. +* @param {FormData} formData - The form data to convert. +* @param {ParseBodyOptions} options - Options for parsing the form data. +* @returns {T} The converted body data. +*/ +function convertFormDataToBodyData(formData, options) { + const form = Object.create(null); + const nestingState = { count: 0 }; + formData.forEach((value, key) => { + if (!(options.all || key.endsWith("[]"))) form[key] = value; + else handleParsingAllValues(form, key, value); + }); + if (options.dot) Object.entries(form).forEach(([key, value]) => { + if (key.includes(".")) { + handleParsingNestedValues(form, key, value, nestingState); + delete form[key]; + } + }); + return form; +} +/** +* Handles parsing all values for a given key, supporting multiple values as arrays. +* +* @param {BodyData} form - The form data object. +* @param {string} key - The key to parse. +* @param {FormDataEntryValue} value - The value to assign. +*/ +const handleParsingAllValues = (form, key, value) => { + if (form[key] !== void 0) { + if (Array.isArray(form[key])) form[key].push(value); + else form[key] = [form[key], value]; + } else if (!key.endsWith("[]")) form[key] = value; + else form[key] = [value]; +}; +/** +* Handles parsing nested values using dot notation keys. +* +* @param {BodyData} form - The form data object. +* @param {string} key - The dot notation key. +* @param {BodyDataValue} value - The value to assign. +*/ +const handleParsingNestedValues = (form, key, value, state) => { + if (/(?:^|\.)__proto__\./.test(key)) return; + let nestedForm = form; + const keys = key.split(".", 34); + if (keys.length > 33) throwNestingLimitExceeded(); + keys.forEach((key, index) => { + if (index === keys.length - 1) nestedForm[key] = value; + else { + if (!nestedForm[key] || typeof nestedForm[key] !== "object" || Array.isArray(nestedForm[key]) || nestedForm[key] instanceof File) { + if (state.count++ >= MAX_NESTED_OBJECTS) throwNestingLimitExceeded(); + nestedForm[key] = Object.create(null); + } + nestedForm = nestedForm[key]; + } + }); +}; +const throwNestingLimitExceeded = () => { + throw new Error("Nesting limit exceeded"); +}; +//#endregion +export { parseBody }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/utils/buffer.js b/tests/fixtures/npm/node_modules/hono/dist/utils/buffer.js new file mode 100644 index 000000000..8c6506452 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/utils/buffer.js @@ -0,0 +1,51 @@ +import { sha256 } from "./crypto.js"; +//#region src/utils/buffer.ts +/** +* @module +* Buffer utility. +*/ +const equal = (a, b) => { + if (a === b) return true; + if (a.byteLength !== b.byteLength) return false; + const va = new DataView(a); + const vb = new DataView(b); + let i = va.byteLength; + while (i--) if (va.getUint8(i) !== vb.getUint8(i)) return false; + return true; +}; +const constantTimeEqualString = (a, b) => { + const aLen = a.length; + const bLen = b.length; + const maxLen = Math.max(aLen, bLen); + let out = aLen ^ bLen; + for (let i = 0; i < maxLen; i++) { + const aChar = i < aLen ? a.charCodeAt(i) : 0; + const bChar = i < bLen ? b.charCodeAt(i) : 0; + out |= aChar ^ bChar; + } + return out === 0; +}; +const timingSafeEqualString = async (a, b, hashFunction) => { + if (!hashFunction) hashFunction = sha256; + const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)]); + if (sa == null || sb == null || typeof sa !== "string" || typeof sb !== "string") return false; + const hashEqual = constantTimeEqualString(sa, sb); + const originalEqual = constantTimeEqualString(a, b); + return hashEqual && originalEqual; +}; +const timingSafeEqual = async (a, b, hashFunction) => { + if (typeof a === "string" && typeof b === "string") return timingSafeEqualString(a, b, hashFunction); + if (!hashFunction) hashFunction = sha256; + const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)]); + if (!sa || !sb || typeof sa !== "string" || typeof sb !== "string") return false; + return timingSafeEqualString(sa, sb); +}; +const bufferToString = (buffer) => { + if (buffer instanceof ArrayBuffer) return new TextDecoder("utf-8").decode(buffer); + return buffer; +}; +const bufferToFormData = (arrayBuffer, contentType) => { + return new Response(arrayBuffer, { headers: { "Content-Type": contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase()) } }).formData(); +}; +//#endregion +export { bufferToFormData, bufferToString, equal, timingSafeEqual }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/utils/constants.js b/tests/fixtures/npm/node_modules/hono/dist/utils/constants.js new file mode 100644 index 000000000..8b0615b69 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/utils/constants.js @@ -0,0 +1,7 @@ +//#region src/utils/constants.ts +/** +* Constant used to mark a composed handler. +*/ +const COMPOSED_HANDLER = "__COMPOSED_HANDLER"; +//#endregion +export { COMPOSED_HANDLER }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/utils/cookie.js b/tests/fixtures/npm/node_modules/hono/dist/utils/cookie.js new file mode 100644 index 000000000..fd541131c --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/utils/cookie.js @@ -0,0 +1,125 @@ +import { tryDecodeURIComponent } from "./url.js"; +//#region src/utils/cookie.ts +/** +* @module +* Cookie utility. +*/ +const algorithm = { + name: "HMAC", + hash: "SHA-256" +}; +const getCryptoKey = async (secret) => { + const secretBuf = typeof secret === "string" ? new TextEncoder().encode(secret) : secret; + return await crypto.subtle.importKey("raw", secretBuf, algorithm, false, ["sign", "verify"]); +}; +const makeSignature = async (value, secret) => { + const key = await getCryptoKey(secret); + const signature = await crypto.subtle.sign(algorithm.name, key, new TextEncoder().encode(value)); + return btoa(String.fromCharCode(...new Uint8Array(signature))); +}; +const verifySignature = async (base64Signature, value, secret) => { + try { + const signatureBinStr = atob(base64Signature); + const signature = new Uint8Array(signatureBinStr.length); + for (let i = 0, len = signatureBinStr.length; i < len; i++) signature[i] = signatureBinStr.charCodeAt(i); + return await crypto.subtle.verify(algorithm, secret, signature, new TextEncoder().encode(value)); + } catch { + return false; + } +}; +const validCookieNameRegEx = /^[\w!#$%&'*.^`|~+-]+$/; +const relaxedCookieNameRegEx = /^[!#-:<>-[\]-~]+$/; +const validCookieValueRegEx = /^[ !#-:<-[\]-~]*$/; +const trimCookieWhitespace = (value) => { + let start = 0; + let end = value.length; + while (start < end) { + const charCode = value.charCodeAt(start); + if (charCode !== 32 && charCode !== 9) break; + start++; + } + while (end > start) { + const charCode = value.charCodeAt(end - 1); + if (charCode !== 32 && charCode !== 9) break; + end--; + } + return start === 0 && end === value.length ? value : value.slice(start, end); +}; +const parse = (cookie, name) => { + if (name && cookie.indexOf(name) === -1) return {}; + const pairs = cookie.split(";"); + const parsedCookie = Object.create(null); + for (const pairStr of pairs) { + const valueStartPos = pairStr.indexOf("="); + if (valueStartPos === -1) continue; + const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos)); + if (name && name !== cookieName || !relaxedCookieNameRegEx.test(cookieName) || cookieName in parsedCookie) continue; + let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1)); + if (cookieValue.startsWith("\"") && cookieValue.endsWith("\"")) cookieValue = cookieValue.slice(1, -1); + if (validCookieValueRegEx.test(cookieValue)) { + parsedCookie[cookieName] = tryDecodeURIComponent(cookieValue); + if (name) break; + } + } + return parsedCookie; +}; +const parseSigned = async (cookie, secret, name) => { + const parsedCookie = Object.create(null); + const secretKey = await getCryptoKey(secret); + for (const [key, value] of Object.entries(parse(cookie, name))) { + const signatureStartPos = value.lastIndexOf("."); + if (signatureStartPos < 0) continue; + const signedValue = value.substring(0, signatureStartPos); + const signature = value.substring(signatureStartPos + 1); + if (signature.length !== 44 || !signature.endsWith("=")) continue; + parsedCookie[key] = await verifySignature(signature, signedValue, secretKey) ? signedValue : false; + } + return parsedCookie; +}; +const _serialize = (name, value, opt = {}) => { + if (!validCookieNameRegEx.test(name)) throw new Error("Invalid cookie name"); + let cookie = `${name}=${value}`; + if (name.startsWith("__Secure-") && !opt.secure) throw new Error("__Secure- Cookie must have Secure attributes"); + if (name.startsWith("__Host-")) { + if (!opt.secure) throw new Error("__Host- Cookie must have Secure attributes"); + if (opt.path !== "/") throw new Error("__Host- Cookie must have Path attributes with \"/\""); + if (opt.domain) throw new Error("__Host- Cookie must not have Domain attributes"); + } + for (const key of [ + "domain", + "path", + "sameSite", + "priority" + ]) if (opt[key] && /[;\r\n]/.test(opt[key])) throw new Error(`${key} must not contain ";", "\\r", or "\\n"`); + if (opt && typeof opt.maxAge === "number" && opt.maxAge >= 0) { + if (opt.maxAge > 3456e4) throw new Error("Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration."); + cookie += `; Max-Age=${opt.maxAge | 0}`; + } + if (opt.domain && opt.prefix !== "host") cookie += `; Domain=${opt.domain}`; + if (opt.path) cookie += `; Path=${opt.path}`; + if (opt.expires) { + if (opt.expires.getTime() - Date.now() > 3456e7) throw new Error("Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future."); + cookie += `; Expires=${opt.expires.toUTCString()}`; + } + if (opt.httpOnly) cookie += "; HttpOnly"; + if (opt.secure) cookie += "; Secure"; + if (opt.sameSite) cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`; + if (opt.priority) cookie += `; Priority=${opt.priority.charAt(0).toUpperCase() + opt.priority.slice(1)}`; + if (opt.partitioned) { + if (!opt.secure) throw new Error("Partitioned Cookie must have Secure attributes"); + cookie += "; Partitioned"; + } + return cookie; +}; +const serialize = (name, value, opt) => { + value = encodeURIComponent(value); + return _serialize(name, value, opt); +}; +const serializeSigned = async (name, value, secret, opt = {}) => { + const signature = await makeSignature(value, secret); + value = `${value}.${signature}`; + value = encodeURIComponent(value); + return _serialize(name, value, opt); +}; +//#endregion +export { parse, parseSigned, serialize, serializeSigned }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/utils/crypto.js b/tests/fixtures/npm/node_modules/hono/dist/utils/crypto.js new file mode 100644 index 000000000..548867c49 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/utils/crypto.js @@ -0,0 +1,34 @@ +//#region src/utils/crypto.ts +const sha256 = async (data) => { + return await createHash(data, { + name: "SHA-256", + alias: "sha256" + }); +}; +const sha1 = async (data) => { + return await createHash(data, { + name: "SHA-1", + alias: "sha1" + }); +}; +const md5 = async (data) => { + return await createHash(data, { + name: "MD5", + alias: "md5" + }); +}; +const createHash = async (data, algorithm) => { + let sourceBuffer; + if (ArrayBuffer.isView(data) || data instanceof ArrayBuffer) sourceBuffer = data; + else { + if (typeof data === "object") data = JSON.stringify(data); + sourceBuffer = new TextEncoder().encode(String(data)); + } + if (crypto && crypto.subtle) { + const buffer = await crypto.subtle.digest({ name: algorithm.name }, sourceBuffer); + return Array.prototype.map.call(new Uint8Array(buffer), (x) => ("00" + x.toString(16)).slice(-2)).join(""); + } + return null; +}; +//#endregion +export { createHash, md5, sha1, sha256 }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/utils/html.js b/tests/fixtures/npm/node_modules/hono/dist/utils/html.js new file mode 100644 index 000000000..7615ebdbd --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/utils/html.js @@ -0,0 +1,103 @@ +//#region src/utils/html.ts +/** +* @module +* HTML utility. +*/ +const HtmlEscapedCallbackPhase = { + Stringify: 1, + BeforeStream: 2, + Stream: 3 +}; +const raw = (value, callbacks) => { + const escapedString = new String(value); + escapedString.isEscaped = true; + escapedString.callbacks = callbacks; + return escapedString; +}; +const escapeRe = /[&<>'"]/; +const stringBufferToString = async (buffer, callbacks) => { + let str = ""; + callbacks ||= []; + const resolvedBuffer = await Promise.all(buffer); + for (let i = resolvedBuffer.length - 1;; i--) { + str += resolvedBuffer[i]; + i--; + if (i < 0) break; + let r = resolvedBuffer[i]; + if (typeof r === "object") callbacks.push(...r.callbacks || []); + const isEscaped = r.isEscaped; + r = await (typeof r === "object" ? r.toString() : r); + if (typeof r === "object") callbacks.push(...r.callbacks || []); + if (r.isEscaped ?? isEscaped) str += r; + else { + const buf = [str]; + escapeToBuffer(r, buf); + str = buf[0]; + } + } + return raw(str, callbacks); +}; +const escapeToBuffer = (str, buffer) => { + const match = str.search(escapeRe); + if (match === -1) { + buffer[0] += str; + return; + } + let escape; + let index; + let lastIndex = 0; + for (index = match; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: + escape = """; + break; + case 39: + escape = "'"; + break; + case 38: + escape = "&"; + break; + case 60: + escape = "<"; + break; + case 62: + escape = ">"; + break; + default: continue; + } + buffer[0] += str.substring(lastIndex, index) + escape; + lastIndex = index + 1; + } + buffer[0] += str.substring(lastIndex, index); +}; +const resolveCallbackSync = (str) => { + const callbacks = str.callbacks; + if (!callbacks?.length) return str; + const buffer = [str]; + const context = {}; + callbacks.forEach((c) => c({ + phase: HtmlEscapedCallbackPhase.Stringify, + buffer, + context + })); + return buffer[0]; +}; +const resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => { + if (typeof str === "object" && !(str instanceof String)) { + if (!(str instanceof Promise)) str = str.toString(); + if (str instanceof Promise) str = await str; + } + const callbacks = str.callbacks; + if (!callbacks?.length) return Promise.resolve(str); + if (buffer) buffer[0] += str; + else buffer = [str]; + const resStr = Promise.all(callbacks.map((c) => c({ + phase, + buffer, + context + }))).then((res) => Promise.all(res.filter(Boolean).map((str) => resolveCallback(str, phase, false, context, buffer))).then(() => buffer[0])); + if (preserveCallbacks) return raw(await resStr, callbacks); + else return resStr; +}; +//#endregion +export { HtmlEscapedCallbackPhase, escapeToBuffer, raw, resolveCallback, resolveCallbackSync, stringBufferToString }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/utils/url.js b/tests/fixtures/npm/node_modules/hono/dist/utils/url.js new file mode 100644 index 000000000..8433d4cb9 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/utils/url.js @@ -0,0 +1,201 @@ +//#region src/utils/url.ts +const splitPath = (path) => { + const paths = path.split("/"); + if (paths[0] === "") paths.shift(); + return paths; +}; +const splitRoutingPath = (routePath) => { + const { groups, path } = extractGroupsFromPath(routePath); + const paths = splitPath(path); + return replaceGroupMarks(paths, groups); +}; +const extractGroupsFromPath = (path) => { + const groups = []; + path = path.replace(/\{[^}]+\}/g, (match, index) => { + const mark = `@${index}`; + groups.push([mark, match]); + return mark; + }); + return { + groups, + path + }; +}; +const replaceGroupMarks = (paths, groups) => { + for (let i = groups.length - 1; i >= 0; i--) { + const [mark] = groups[i]; + for (let j = paths.length - 1; j >= 0; j--) if (paths[j].includes(mark)) { + paths[j] = paths[j].replace(mark, groups[i][1]); + break; + } + } + return paths; +}; +const patternCache = {}; +const getPattern = (label, next) => { + if (label === "*") return "*"; + const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); + if (match) { + const cacheKey = `${label}#${next}`; + if (!patternCache[cacheKey]) { + if (match[2]) patternCache[cacheKey] = next && next[0] !== ":" && next[0] !== "*" ? [ + cacheKey, + match[1], + new RegExp(`^${match[2]}(?=/${next})`) + ] : [ + label, + match[1], + new RegExp(`^${match[2]}$`) + ]; + else patternCache[cacheKey] = [ + label, + match[1], + true + ]; + } + return patternCache[cacheKey]; + } + return null; +}; +const tryDecode = (str, decoder) => { + try { + return decoder(str); + } catch { + return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => { + try { + return decoder(match); + } catch { + return match; + } + }); + } +}; +/** +* Try to apply decodeURI() to given string. +* If it fails, skip invalid percent encoding or invalid UTF-8 sequences, and apply decodeURI() to the rest as much as possible. +* @param str The string to decode. +* @returns The decoded string that sometimes contains undecodable percent encoding. +* @example +* tryDecodeURI('Hello%20World') // 'Hello World' +* tryDecodeURI('Hello%20World/%A4%A2') // 'Hello World/%A4%A2' +*/ +const tryDecodeURI = (str) => tryDecode(str, decodeURI); +const getPath = (request) => { + const url = request.url; + const start = url.indexOf("/", url.indexOf(":") + 4); + let i = start; + for (; i < url.length; i++) { + const charCode = url.charCodeAt(i); + if (charCode === 37) { + const queryIndex = url.indexOf("?", i); + const hashIndex = url.indexOf("#", i); + const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex); + const path = url.slice(start, end); + return tryDecodeURI(path.includes("%25") ? path.replace(/%25/g, "%2525") : path); + } else if (charCode === 63 || charCode === 35) break; + } + return url.slice(start, i); +}; +/** +* @deprecated +* Use the `URL` API instead. +*/ +const getQueryStrings = (url) => { + const queryIndex = url.indexOf("?", 8); + if (queryIndex === -1) return ""; + const hashIndex = url.indexOf("#", 8); + return hashIndex === -1 ? url.slice(queryIndex) : queryIndex < hashIndex ? url.slice(queryIndex, hashIndex) : ""; +}; +const getPathNoStrict = (request) => { + const result = getPath(request); + return result.length > 1 && result.at(-1) === "/" ? result.slice(0, -1) : result; +}; +/** +* Merge paths. +* @param {string[]} ...paths - The paths to merge. +* @returns {string} The merged path. +* @example +* mergePath('/api', '/users') // '/api/users' +* mergePath('/api/', '/users') // '/api/users' +* mergePath('/api', '/') // '/api' +* mergePath('/api/', '/') // '/api/' +*/ +const mergePath = (base, sub, ...rest) => { + if (rest.length) sub = mergePath(sub, ...rest); + return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`; +}; +const checkOptionalParameter = (path) => { + if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(":")) return null; + const segments = path.split("/"); + const results = []; + let basePath = ""; + segments.forEach((segment) => { + if (segment !== "" && !/\:/.test(segment)) basePath += "/" + segment; + else if (/\:/.test(segment)) { + if (segment.charCodeAt(segment.length - 1) === 63) { + if (results.length === 0 && basePath === "") results.push("/"); + else results.push(basePath); + const optionalSegment = segment.slice(0, -1); + basePath += "/" + optionalSegment; + results.push(basePath); + } else basePath += "/" + segment; + } + }); + return results.filter((v, i, a) => a.indexOf(v) === i); +}; +const tryDecodeURIComponent = (str) => str.indexOf("%") !== -1 ? tryDecode(str, decodeURIComponent_) : str; +const _decodeURI = (value) => { + if (value.indexOf("+") !== -1) value = value.replace(/\+/g, " "); + return tryDecodeURIComponent(value); +}; +const _getQueryParam = (url, key, multiple) => { + const hashIndex = url.indexOf("#", 8); + if (hashIndex !== -1) url = url.slice(0, hashIndex); + let encoded; + if (!multiple && key && key.indexOf("%") === -1 && key.indexOf("+") === -1) { + let keyIndex = url.indexOf("?", 8); + if (keyIndex === -1) return; + if (!url.startsWith(key, keyIndex + 1)) keyIndex = url.indexOf(`&${key}`, keyIndex + 1); + while (keyIndex !== -1) { + const trailingKeyCode = url.charCodeAt(keyIndex + key.length + 1); + if (trailingKeyCode === 61) { + const valueIndex = keyIndex + key.length + 2; + const endIndex = url.indexOf("&", valueIndex); + return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex)); + } else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) return ""; + keyIndex = url.indexOf(`&${key}`, keyIndex + 1); + } + encoded = /[%+]/.test(url); + if (!encoded) return; + } + const results = Object.create(null); + encoded ??= /[%+]/.test(url); + let keyIndex = url.indexOf("?", 8); + while (keyIndex !== -1) { + const nextKeyIndex = url.indexOf("&", keyIndex + 1); + let valueIndex = url.indexOf("=", keyIndex); + if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) valueIndex = -1; + let name = url.slice(keyIndex + 1, valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex); + if (encoded) name = _decodeURI(name); + keyIndex = nextKeyIndex; + if (name === "") continue; + let value; + if (valueIndex === -1) value = ""; + else { + value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex); + if (encoded) value = _decodeURI(value); + } + if (multiple) { + if (!(results[name] && Array.isArray(results[name]))) results[name] = []; + results[name].push(value); + } else results[name] ??= value; + } + return key ? results[key] : results; +}; +const getQueryParam = _getQueryParam; +const getQueryParams = (url, key) => { + return _getQueryParam(url, key, true); +}; +const decodeURIComponent_ = decodeURIComponent; +//#endregion +export { checkOptionalParameter, decodeURIComponent_, getPath, getPathNoStrict, getPattern, getQueryParam, getQueryParams, getQueryStrings, mergePath, splitPath, splitRoutingPath, tryDecode, tryDecodeURI, tryDecodeURIComponent }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/validator/index.js b/tests/fixtures/npm/node_modules/hono/dist/validator/index.js new file mode 100644 index 000000000..6e744aca6 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/validator/index.js @@ -0,0 +1,2 @@ +import { validator } from "./validator.js"; +export { validator }; diff --git a/tests/fixtures/npm/node_modules/hono/dist/validator/validator.js b/tests/fixtures/npm/node_modules/hono/dist/validator/validator.js new file mode 100644 index 000000000..1cc282071 --- /dev/null +++ b/tests/fixtures/npm/node_modules/hono/dist/validator/validator.js @@ -0,0 +1,64 @@ +import { HTTPException } from "../http-exception.js"; +import { bufferToFormData } from "../utils/buffer.js"; +import { getCookie } from "../helper/cookie/index.js"; +//#region src/validator/validator.ts +const jsonRegex = /^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i; +const multipartRegex = /^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/i; +const urlencodedRegex = /^application\/x-www-form-urlencoded(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i; +const validator = (target, validationFunc) => { + return async (c, next) => { + let value = {}; + const contentType = c.req.header("Content-Type"); + switch (target) { + case "json": + if (!contentType || !jsonRegex.test(contentType)) break; + try { + value = await c.req.json(); + } catch { + throw new HTTPException(400, { message: "Malformed JSON in request body" }); + } + break; + case "form": { + if (!contentType || !(multipartRegex.test(contentType) || urlencodedRegex.test(contentType))) break; + let formData; + if (c.req.bodyCache.formData) formData = await c.req.bodyCache.formData; + else try { + const arrayBuffer = await c.req.arrayBuffer(); + formData = await bufferToFormData(arrayBuffer, contentType); + c.req.bodyCache.formData = formData; + } catch (e) { + let message = "Malformed FormData request."; + message += e instanceof Error ? ` ${e.message}` : ` ${String(e)}`; + throw new HTTPException(400, { message }); + } + const form = Object.create(null); + formData.forEach((value, key) => { + if (key.endsWith("[]")) (form[key] ??= []).push(value); + else if (Array.isArray(form[key])) form[key].push(value); + else if (Object.hasOwn(form, key)) form[key] = [form[key], value]; + else form[key] = value; + }); + value = form; + break; + } + case "query": + value = Object.fromEntries(Object.entries(c.req.queries()).map(([k, v]) => { + return v.length === 1 ? [k, v[0]] : [k, v]; + })); + break; + case "param": + value = c.req.param(); + break; + case "header": + value = c.req.header(); + break; + case "cookie": value = getCookie(c); + } + const res = await validationFunc(value, c); + if (res instanceof Response) return res; + c.req.addValidatedData(target, res); + return await next(); + }; +}; +//#endregion +export { validator }; diff --git a/tests/fixtures/npm/node_modules/hono/package.json b/tests/fixtures/npm/node_modules/hono/package.json index 6264c824a..6c7dec6e9 100644 --- a/tests/fixtures/npm/node_modules/hono/package.json +++ b/tests/fixtures/npm/node_modules/hono/package.json @@ -1,10 +1,18 @@ { "name": "hono", "version": "4.13.13", - "description": "Test fixture: only the hono/ws helper that @hono/node-server's index imports.", + "description": "Test fixture: the hono core and validator (with their declarations) and the hono/ws helper that @hono/node-server's index imports.", "type": "module", "license": "MIT", "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/index.js" + }, + "./validator": { + "types": "./dist/types/validator/index.d.ts", + "import": "./dist/validator/index.js" + }, "./ws": { "import": "./dist/helper/websocket/index.js" } diff --git a/tests/fixtures/npm/node_modules/zod/LICENSE b/tests/fixtures/npm/node_modules/zod/LICENSE new file mode 100644 index 000000000..c06579627 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Colin McDonnell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tests/fixtures/npm/node_modules/zod/index.d.cts b/tests/fixtures/npm/node_modules/zod/index.d.cts new file mode 100644 index 000000000..b8f3021a8 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/index.d.cts @@ -0,0 +1,3 @@ +import * as z from "./v4/classic/external.cjs"; +export * from "./v4/classic/external.cjs"; +export { z, z as default }; diff --git a/tests/fixtures/npm/node_modules/zod/index.js b/tests/fixtures/npm/node_modules/zod/index.js new file mode 100644 index 000000000..4102a4715 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/index.js @@ -0,0 +1,7 @@ +import * as z from "./v4/classic/external.js"; +export * from "./v4/classic/external.js"; +// Aliasing the binding, instead of `export default z`, keeps the default export a +// re-export of `z` rather than a fresh namespace value. Rollup and Webpack can then +// shake `import z from "zod"` as they already shake `import { z } from "zod"` — without +// this they pull in every locale. See #6050. +export { z, z as default }; diff --git a/tests/fixtures/npm/node_modules/zod/package.json b/tests/fixtures/npm/node_modules/zod/package.json new file mode 100644 index 000000000..58b9916bd --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/package.json @@ -0,0 +1,23 @@ +{ + "name": "zod", + "version": "4.6.7", + "description": "Test fixture: zod 4's classic API, its core and locales, and the declarations @hono/zod-validator reads (the files the npm cases load).", + "type": "module", + "license": "MIT", + "types": "./index.d.cts", + "exports": { + ".": { + "types": "./index.d.cts", + "import": "./index.js" + }, + "./v3": { + "types": "./v3/index.d.cts" + }, + "./v4": { + "types": "./v4/index.d.cts" + }, + "./v4/core": { + "types": "./v4/core/index.d.cts" + } + } +} diff --git a/tests/fixtures/npm/node_modules/zod/v3/ZodError.d.cts b/tests/fixtures/npm/node_modules/zod/v3/ZodError.d.cts new file mode 100644 index 000000000..384b36487 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/ZodError.d.cts @@ -0,0 +1,164 @@ +import type { Primitive } from "./helpers/typeAliases.cjs"; +import { util, type ZodParsedType } from "./helpers/util.cjs"; +import type { TypeOf, ZodType } from "./index.cjs"; +type allKeys = T extends any ? keyof T : never; +export type inferFlattenedErrors, U = string> = typeToFlattenedError, U>; +export type typeToFlattenedError = { + formErrors: U[]; + fieldErrors: { + [P in allKeys]?: U[]; + }; +}; +export declare const ZodIssueCode: { + invalid_type: "invalid_type"; + too_big: "too_big"; + too_small: "too_small"; + not_multiple_of: "not_multiple_of"; + unrecognized_keys: "unrecognized_keys"; + invalid_union: "invalid_union"; + custom: "custom"; + invalid_literal: "invalid_literal"; + invalid_union_discriminator: "invalid_union_discriminator"; + invalid_enum_value: "invalid_enum_value"; + invalid_arguments: "invalid_arguments"; + invalid_return_type: "invalid_return_type"; + invalid_date: "invalid_date"; + invalid_string: "invalid_string"; + invalid_intersection_types: "invalid_intersection_types"; + not_finite: "not_finite"; +}; +export type ZodIssueCode = keyof typeof ZodIssueCode; +export type ZodIssueBase = { + path: (string | number)[]; + message?: string | undefined; +}; +export interface ZodInvalidTypeIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_type; + expected: ZodParsedType; + received: ZodParsedType; +} +export interface ZodInvalidLiteralIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_literal; + expected: unknown; + received: unknown; +} +export interface ZodUnrecognizedKeysIssue extends ZodIssueBase { + code: typeof ZodIssueCode.unrecognized_keys; + keys: string[]; +} +export interface ZodInvalidUnionIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_union; + unionErrors: ZodError[]; +} +export interface ZodInvalidUnionDiscriminatorIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_union_discriminator; + options: Primitive[]; +} +export interface ZodInvalidEnumValueIssue extends ZodIssueBase { + received: string | number; + code: typeof ZodIssueCode.invalid_enum_value; + options: (string | number)[]; +} +export interface ZodInvalidArgumentsIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_arguments; + argumentsError: ZodError; +} +export interface ZodInvalidReturnTypeIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_return_type; + returnTypeError: ZodError; +} +export interface ZodInvalidDateIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_date; +} +export type StringValidation = "email" | "url" | "emoji" | "uuid" | "nanoid" | "regex" | "cuid" | "cuid2" | "ulid" | "datetime" | "date" | "time" | "duration" | "ip" | "cidr" | "base64" | "jwt" | "base64url" | { + includes: string; + position?: number | undefined; +} | { + startsWith: string; +} | { + endsWith: string; +}; +export interface ZodInvalidStringIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_string; + validation: StringValidation; +} +export interface ZodTooSmallIssue extends ZodIssueBase { + code: typeof ZodIssueCode.too_small; + minimum: number | bigint; + inclusive: boolean; + exact?: boolean; + type: "array" | "string" | "number" | "set" | "date" | "bigint"; +} +export interface ZodTooBigIssue extends ZodIssueBase { + code: typeof ZodIssueCode.too_big; + maximum: number | bigint; + inclusive: boolean; + exact?: boolean; + type: "array" | "string" | "number" | "set" | "date" | "bigint"; +} +export interface ZodInvalidIntersectionTypesIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_intersection_types; +} +export interface ZodNotMultipleOfIssue extends ZodIssueBase { + code: typeof ZodIssueCode.not_multiple_of; + multipleOf: number | bigint; +} +export interface ZodNotFiniteIssue extends ZodIssueBase { + code: typeof ZodIssueCode.not_finite; +} +export interface ZodCustomIssue extends ZodIssueBase { + code: typeof ZodIssueCode.custom; + params?: { + [k: string]: any; + }; +} +export type DenormalizedError = { + [k: string]: DenormalizedError | string[]; +}; +export type ZodIssueOptionalMessage = ZodInvalidTypeIssue | ZodInvalidLiteralIssue | ZodUnrecognizedKeysIssue | ZodInvalidUnionIssue | ZodInvalidUnionDiscriminatorIssue | ZodInvalidEnumValueIssue | ZodInvalidArgumentsIssue | ZodInvalidReturnTypeIssue | ZodInvalidDateIssue | ZodInvalidStringIssue | ZodTooSmallIssue | ZodTooBigIssue | ZodInvalidIntersectionTypesIssue | ZodNotMultipleOfIssue | ZodNotFiniteIssue | ZodCustomIssue; +export type ZodIssue = ZodIssueOptionalMessage & { + fatal?: boolean | undefined; + message: string; +}; +export declare const quotelessJson: (obj: any) => string; +type recursiveZodFormattedError = T extends [any, ...any[]] ? { + [K in keyof T]?: ZodFormattedError; +} : T extends any[] ? { + [k: number]: ZodFormattedError; +} : T extends object ? { + [K in keyof T]?: ZodFormattedError; +} : unknown; +export type ZodFormattedError = { + _errors: U[]; +} & recursiveZodFormattedError>; +export type inferFormattedError, U = string> = ZodFormattedError, U>; +export declare class ZodError extends Error { + issues: ZodIssue[]; + get errors(): ZodIssue[]; + constructor(issues: ZodIssue[]); + format(): ZodFormattedError; + format(mapper: (issue: ZodIssue) => U): ZodFormattedError; + static create: (issues: ZodIssue[]) => ZodError; + static assert(value: unknown): asserts value is ZodError; + toString(): string; + get message(): string; + get isEmpty(): boolean; + addIssue: (sub: ZodIssue) => void; + addIssues: (subs?: ZodIssue[]) => void; + flatten(): typeToFlattenedError; + flatten(mapper?: (issue: ZodIssue) => U): typeToFlattenedError; + get formErrors(): typeToFlattenedError; +} +type stripPath = T extends any ? util.OmitKeys : never; +export type IssueData = stripPath & { + path?: (string | number)[]; + fatal?: boolean | undefined; +}; +export type ErrorMapCtx = { + defaultError: string; + data: any; +}; +export type ZodErrorMap = (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => { + message: string; +}; +export {}; diff --git a/tests/fixtures/npm/node_modules/zod/v3/errors.d.cts b/tests/fixtures/npm/node_modules/zod/v3/errors.d.cts new file mode 100644 index 000000000..205f3a096 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/errors.d.cts @@ -0,0 +1,5 @@ +import type { ZodErrorMap } from "./ZodError.cjs"; +import defaultErrorMap from "./locales/en.cjs"; +export { defaultErrorMap }; +export declare function setErrorMap(map: ZodErrorMap): void; +export declare function getErrorMap(): ZodErrorMap; diff --git a/tests/fixtures/npm/node_modules/zod/v3/external.d.cts b/tests/fixtures/npm/node_modules/zod/v3/external.d.cts new file mode 100644 index 000000000..ced9c1a85 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/external.d.cts @@ -0,0 +1,6 @@ +export * from "./errors.cjs"; +export * from "./helpers/parseUtil.cjs"; +export * from "./helpers/typeAliases.cjs"; +export * from "./helpers/util.cjs"; +export * from "./types.cjs"; +export * from "./ZodError.cjs"; diff --git a/tests/fixtures/npm/node_modules/zod/v3/helpers/enumUtil.d.cts b/tests/fixtures/npm/node_modules/zod/v3/helpers/enumUtil.d.cts new file mode 100644 index 000000000..fad28a711 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/helpers/enumUtil.d.cts @@ -0,0 +1,8 @@ +export declare namespace enumUtil { + type UnionToIntersectionFn = (T extends unknown ? (k: () => T) => void : never) extends (k: infer Intersection) => void ? Intersection : never; + type GetUnionLast = UnionToIntersectionFn extends () => infer Last ? Last : never; + type UnionToTuple = [T] extends [never] ? Tuple : UnionToTuple>, [GetUnionLast, ...Tuple]>; + type CastToStringTuple = T extends [string, ...string[]] ? T : never; + export type UnionToTupleString = CastToStringTuple>; + export {}; +} diff --git a/tests/fixtures/npm/node_modules/zod/v3/helpers/errorUtil.d.cts b/tests/fixtures/npm/node_modules/zod/v3/helpers/errorUtil.d.cts new file mode 100644 index 000000000..bc9b033ff --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/helpers/errorUtil.d.cts @@ -0,0 +1,9 @@ +export declare namespace errorUtil { + type ErrMessage = string | { + message?: string | undefined; + }; + const errToObj: (message?: ErrMessage) => { + message?: string | undefined; + }; + const toString: (message?: ErrMessage) => string | undefined; +} diff --git a/tests/fixtures/npm/node_modules/zod/v3/helpers/parseUtil.d.cts b/tests/fixtures/npm/node_modules/zod/v3/helpers/parseUtil.d.cts new file mode 100644 index 000000000..f34a63d7f --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/helpers/parseUtil.d.cts @@ -0,0 +1,78 @@ +import type { IssueData, ZodErrorMap, ZodIssue } from "../ZodError.cjs"; +import type { ZodParsedType } from "./util.cjs"; +export declare const makeIssue: (params: { + data: any; + path: (string | number)[]; + errorMaps: ZodErrorMap[]; + issueData: IssueData; +}) => ZodIssue; +export type ParseParams = { + path: (string | number)[]; + errorMap: ZodErrorMap; + async: boolean; +}; +export type ParsePathComponent = string | number; +export type ParsePath = ParsePathComponent[]; +export declare const EMPTY_PATH: ParsePath; +export interface ParseContext { + readonly common: { + readonly issues: ZodIssue[]; + readonly contextualErrorMap?: ZodErrorMap | undefined; + readonly async: boolean; + }; + readonly path: ParsePath; + readonly schemaErrorMap?: ZodErrorMap | undefined; + readonly parent: ParseContext | null; + readonly data: any; + readonly parsedType: ZodParsedType; +} +export type ParseInput = { + data: any; + path: (string | number)[]; + parent: ParseContext; +}; +export declare function addIssueToContext(ctx: ParseContext, issueData: IssueData): void; +export type ObjectPair = { + key: SyncParseReturnType; + value: SyncParseReturnType; +}; +export declare class ParseStatus { + value: "aborted" | "dirty" | "valid"; + dirty(): void; + abort(): void; + static mergeArray(status: ParseStatus, results: SyncParseReturnType[]): SyncParseReturnType; + static mergeObjectAsync(status: ParseStatus, pairs: { + key: ParseReturnType; + value: ParseReturnType; + }[]): Promise>; + static mergeObjectSync(status: ParseStatus, pairs: { + key: SyncParseReturnType; + value: SyncParseReturnType; + alwaysSet?: boolean; + }[]): SyncParseReturnType; +} +export interface ParseResult { + status: "aborted" | "dirty" | "valid"; + data: any; +} +export type INVALID = { + status: "aborted"; +}; +export declare const INVALID: INVALID; +export type DIRTY = { + status: "dirty"; + value: T; +}; +export declare const DIRTY: (value: T) => DIRTY; +export type OK = { + status: "valid"; + value: T; +}; +export declare const OK: (value: T) => OK; +export type SyncParseReturnType = OK | DIRTY | INVALID; +export type AsyncParseReturnType = Promise>; +export type ParseReturnType = SyncParseReturnType | AsyncParseReturnType; +export declare const isAborted: (x: ParseReturnType) => x is INVALID; +export declare const isDirty: (x: ParseReturnType) => x is OK | DIRTY; +export declare const isValid: (x: ParseReturnType) => x is OK; +export declare const isAsync: (x: ParseReturnType) => x is AsyncParseReturnType; diff --git a/tests/fixtures/npm/node_modules/zod/v3/helpers/partialUtil.d.cts b/tests/fixtures/npm/node_modules/zod/v3/helpers/partialUtil.d.cts new file mode 100644 index 000000000..3f9e46a2a --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/helpers/partialUtil.d.cts @@ -0,0 +1,8 @@ +import type { ZodArray, ZodNullable, ZodObject, ZodOptional, ZodRawShape, ZodTuple, ZodTupleItems, ZodTypeAny } from "../types.cjs"; +export declare namespace partialUtil { + type DeepPartial = T extends ZodObject ? ZodObject<{ + [k in keyof T["shape"]]: ZodOptional>; + }, T["_def"]["unknownKeys"], T["_def"]["catchall"]> : T extends ZodArray ? ZodArray, Card> : T extends ZodOptional ? ZodOptional> : T extends ZodNullable ? ZodNullable> : T extends ZodTuple ? { + [k in keyof Items]: Items[k] extends ZodTypeAny ? DeepPartial : never; + } extends infer PI ? PI extends ZodTupleItems ? ZodTuple : never : never : T; +} diff --git a/tests/fixtures/npm/node_modules/zod/v3/helpers/typeAliases.d.cts b/tests/fixtures/npm/node_modules/zod/v3/helpers/typeAliases.d.cts new file mode 100644 index 000000000..32df022ca --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/helpers/typeAliases.d.cts @@ -0,0 +1,2 @@ +export type Primitive = string | number | symbol | bigint | boolean | null | undefined; +export type Scalars = Primitive | Primitive[]; diff --git a/tests/fixtures/npm/node_modules/zod/v3/helpers/util.d.cts b/tests/fixtures/npm/node_modules/zod/v3/helpers/util.d.cts new file mode 100644 index 000000000..eeaaeee1c --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/helpers/util.d.cts @@ -0,0 +1,85 @@ +export declare namespace util { + type AssertEqual = (() => V extends T ? 1 : 2) extends () => V extends U ? 1 : 2 ? true : false; + export type isAny = 0 extends 1 & T ? true : false; + export const assertEqual: (_: AssertEqual) => void; + export function assertIs(_arg: T): void; + export function assertNever(_x: never): never; + export type Omit = Pick>; + export type OmitKeys = Pick>; + export type MakePartial = Omit & Partial>; + export type Exactly = T & Record, never>; + export type InexactPartial = { + [k in keyof T]?: T[k] | undefined; + }; + export const arrayToEnum: (items: U) => { [k in U[number]]: k; }; + export const getValidEnumValues: (obj: any) => any[]; + export const objectValues: (obj: any) => any[]; + export const objectKeys: ObjectConstructor["keys"]; + export const find: (arr: T[], checker: (arg: T) => any) => T | undefined; + export type identity = objectUtil.identity; + export type flatten = objectUtil.flatten; + export type noUndefined = T extends undefined ? never : T; + export const isInteger: NumberConstructor["isInteger"]; + export function joinValues(array: T, separator?: string): string; + export const jsonStringifyReplacer: (_: string, value: any) => any; + export {}; +} +export declare namespace objectUtil { + export type MergeShapes = keyof U & keyof V extends never ? U & V : { + [k in Exclude]: U[k]; + } & V; + type optionalKeys = { + [k in keyof T]: undefined extends T[k] ? k : never; + }[keyof T]; + type requiredKeys = { + [k in keyof T]: undefined extends T[k] ? never : k; + }[keyof T]; + export type addQuestionMarks = { + [K in requiredKeys]: T[K]; + } & { + [K in optionalKeys]?: T[K]; + } & { + [k in keyof T]?: unknown; + }; + export type identity = T; + export type flatten = identity<{ + [k in keyof T]: T[k]; + }>; + export type noNeverKeys = { + [k in keyof T]: [T[k]] extends [never] ? never : k; + }[keyof T]; + export type noNever = identity<{ + [k in noNeverKeys]: k extends keyof T ? T[k] : never; + }>; + export const mergeShapes: (first: U, second: T) => T & U; + export type extendShape = keyof A & keyof B extends never ? A & B : { + [K in keyof A as K extends keyof B ? never : K]: A[K]; + } & { + [K in keyof B]: B[K]; + }; + export {}; +} +export declare const ZodParsedType: { + string: "string"; + nan: "nan"; + number: "number"; + integer: "integer"; + float: "float"; + boolean: "boolean"; + date: "date"; + bigint: "bigint"; + symbol: "symbol"; + function: "function"; + undefined: "undefined"; + null: "null"; + array: "array"; + object: "object"; + unknown: "unknown"; + promise: "promise"; + void: "void"; + never: "never"; + map: "map"; + set: "set"; +}; +export type ZodParsedType = keyof typeof ZodParsedType; +export declare const getParsedType: (data: any) => ZodParsedType; diff --git a/tests/fixtures/npm/node_modules/zod/v3/index.d.cts b/tests/fixtures/npm/node_modules/zod/v3/index.d.cts new file mode 100644 index 000000000..b0662babe --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/index.d.cts @@ -0,0 +1,4 @@ +import * as z from "./external.cjs"; +export * from "./external.cjs"; +export { z }; +export default z; diff --git a/tests/fixtures/npm/node_modules/zod/v3/locales/en.d.cts b/tests/fixtures/npm/node_modules/zod/v3/locales/en.d.cts new file mode 100644 index 000000000..e146a11c5 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/locales/en.d.cts @@ -0,0 +1,3 @@ +import { type ZodErrorMap } from "../ZodError.cjs"; +declare const errorMap: ZodErrorMap; +export = errorMap; diff --git a/tests/fixtures/npm/node_modules/zod/v3/standard-schema.d.cts b/tests/fixtures/npm/node_modules/zod/v3/standard-schema.d.cts new file mode 100644 index 000000000..7ddff3b7f --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/standard-schema.d.cts @@ -0,0 +1,102 @@ +/** + * The Standard Schema interface. + */ +export type StandardSchemaV1 = { + /** + * The Standard Schema properties. + */ + readonly "~standard": StandardSchemaV1.Props; +}; +export declare namespace StandardSchemaV1 { + /** + * The Standard Schema properties interface. + */ + export interface Props { + /** + * The version number of the standard. + */ + readonly version: 1; + /** + * The vendor name of the schema library. + */ + readonly vendor: string; + /** + * Validates unknown input values. + */ + readonly validate: (value: unknown) => Result | Promise>; + /** + * Inferred types associated with the schema. + */ + readonly types?: Types | undefined; + } + /** + * The result interface of the validate function. + */ + export type Result = SuccessResult | FailureResult; + /** + * The result interface if validation succeeds. + */ + export interface SuccessResult { + /** + * The typed output value. + */ + readonly value: Output; + /** + * The non-existent issues. + */ + readonly issues?: undefined; + } + /** + * The result interface if validation fails. + */ + export interface FailureResult { + /** + * The issues of failed validation. + */ + readonly issues: ReadonlyArray; + } + /** + * The issue interface of the failure output. + */ + export interface Issue { + /** + * The error message of the issue. + */ + readonly message: string; + /** + * The path of the issue, if any. + */ + readonly path?: ReadonlyArray | undefined; + } + /** + * The path segment interface of the issue. + */ + export interface PathSegment { + /** + * The key representing a path segment. + */ + readonly key: PropertyKey; + } + /** + * The Standard Schema types interface. + */ + export interface Types { + /** + * The input type of the schema. + */ + readonly input: Input; + /** + * The output type of the schema. + */ + readonly output: Output; + } + /** + * Infers the input type of a Standard Schema. + */ + export type InferInput = NonNullable["input"]; + /** + * Infers the output type of a Standard Schema. + */ + export type InferOutput = NonNullable["output"]; + export {}; +} diff --git a/tests/fixtures/npm/node_modules/zod/v3/types.d.cts b/tests/fixtures/npm/node_modules/zod/v3/types.d.cts new file mode 100644 index 000000000..f1013a74f --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v3/types.d.cts @@ -0,0 +1,1034 @@ +import { type IssueData, type StringValidation, type ZodCustomIssue, ZodError, type ZodErrorMap } from "./ZodError.cjs"; +import type { enumUtil } from "./helpers/enumUtil.cjs"; +import { errorUtil } from "./helpers/errorUtil.cjs"; +import { type AsyncParseReturnType, INVALID, type ParseContext, type ParseInput, type ParseParams, type ParseReturnType, ParseStatus, type SyncParseReturnType } from "./helpers/parseUtil.cjs"; +import type { partialUtil } from "./helpers/partialUtil.cjs"; +import type { Primitive } from "./helpers/typeAliases.cjs"; +import { util, type objectUtil } from "./helpers/util.cjs"; +import type { StandardSchemaV1 } from "./standard-schema.cjs"; +export interface RefinementCtx { + addIssue: (arg: IssueData) => void; + path: (string | number)[]; +} +export type ZodRawShape = { + [k: string]: ZodTypeAny; +}; +export type ZodTypeAny = ZodType; +export type TypeOf> = T["_output"]; +export type input> = T["_input"]; +export type output> = T["_output"]; +export type { TypeOf as infer }; +export type CustomErrorParams = Partial>; +export interface ZodTypeDef { + errorMap?: ZodErrorMap | undefined; + description?: string | undefined; +} +export type RawCreateParams = { + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + message?: string | undefined; + description?: string | undefined; +} | undefined; +export type ProcessedCreateParams = { + errorMap?: ZodErrorMap | undefined; + description?: string | undefined; +}; +export type SafeParseSuccess = { + success: true; + data: Output; + error?: never; +}; +export type SafeParseError = { + success: false; + error: ZodError; + data?: never; +}; +export type SafeParseReturnType = SafeParseSuccess | SafeParseError; +export declare abstract class ZodType { + readonly _type: Output; + readonly _output: Output; + readonly _input: Input; + readonly _def: Def; + get description(): string | undefined; + "~standard": StandardSchemaV1.Props; + abstract _parse(input: ParseInput): ParseReturnType; + _getType(input: ParseInput): string; + _getOrReturnCtx(input: ParseInput, ctx?: ParseContext | undefined): ParseContext; + _processInputParams(input: ParseInput): { + status: ParseStatus; + ctx: ParseContext; + }; + _parseSync(input: ParseInput): SyncParseReturnType; + _parseAsync(input: ParseInput): AsyncParseReturnType; + parse(data: unknown, params?: util.InexactPartial): Output; + safeParse(data: unknown, params?: util.InexactPartial): SafeParseReturnType; + "~validate"(data: unknown): StandardSchemaV1.Result | Promise>; + parseAsync(data: unknown, params?: util.InexactPartial): Promise; + safeParseAsync(data: unknown, params?: util.InexactPartial): Promise>; + /** Alias of safeParseAsync */ + spa: (data: unknown, params?: util.InexactPartial) => Promise>; + refine(check: (arg: Output) => arg is RefinedOutput, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): ZodEffects; + refine(check: (arg: Output) => unknown | Promise, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): ZodEffects; + refinement(check: (arg: Output) => arg is RefinedOutput, refinementData: IssueData | ((arg: Output, ctx: RefinementCtx) => IssueData)): ZodEffects; + refinement(check: (arg: Output) => boolean, refinementData: IssueData | ((arg: Output, ctx: RefinementCtx) => IssueData)): ZodEffects; + _refinement(refinement: RefinementEffect["refinement"]): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => arg is RefinedOutput): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => void): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => Promise): ZodEffects; + constructor(def: Def); + optional(): ZodOptional; + nullable(): ZodNullable; + nullish(): ZodOptional>; + array(): ZodArray; + promise(): ZodPromise; + or(option: T): ZodUnion<[this, T]>; + and(incoming: T): ZodIntersection; + transform(transform: (arg: Output, ctx: RefinementCtx) => NewOut | Promise): ZodEffects; + default(def: util.noUndefined): ZodDefault; + default(def: () => util.noUndefined): ZodDefault; + brand(brand?: B): ZodBranded; + catch(def: Output): ZodCatch; + catch(def: (ctx: { + error: ZodError; + input: Input; + }) => Output): ZodCatch; + describe(description: string): this; + pipe(target: T): ZodPipeline; + readonly(): ZodReadonly; + isOptional(): boolean; + isNullable(): boolean; +} +export type IpVersion = "v4" | "v6"; +export type ZodStringCheck = { + kind: "min"; + value: number; + message?: string | undefined; +} | { + kind: "max"; + value: number; + message?: string | undefined; +} | { + kind: "length"; + value: number; + message?: string | undefined; +} | { + kind: "email"; + message?: string | undefined; +} | { + kind: "url"; + message?: string | undefined; +} | { + kind: "emoji"; + message?: string | undefined; +} | { + kind: "uuid"; + message?: string | undefined; +} | { + kind: "nanoid"; + message?: string | undefined; +} | { + kind: "cuid"; + message?: string | undefined; +} | { + kind: "includes"; + value: string; + position?: number | undefined; + message?: string | undefined; +} | { + kind: "cuid2"; + message?: string | undefined; +} | { + kind: "ulid"; + message?: string | undefined; +} | { + kind: "startsWith"; + value: string; + message?: string | undefined; +} | { + kind: "endsWith"; + value: string; + message?: string | undefined; +} | { + kind: "regex"; + regex: RegExp; + message?: string | undefined; +} | { + kind: "trim"; + message?: string | undefined; +} | { + kind: "toLowerCase"; + message?: string | undefined; +} | { + kind: "toUpperCase"; + message?: string | undefined; +} | { + kind: "jwt"; + alg?: string; + message?: string | undefined; +} | { + kind: "datetime"; + offset: boolean; + local: boolean; + precision: number | null; + message?: string | undefined; +} | { + kind: "date"; + message?: string | undefined; +} | { + kind: "time"; + precision: number | null; + message?: string | undefined; +} | { + kind: "duration"; + message?: string | undefined; +} | { + kind: "ip"; + version?: IpVersion | undefined; + message?: string | undefined; +} | { + kind: "cidr"; + version?: IpVersion | undefined; + message?: string | undefined; +} | { + kind: "base64"; + message?: string | undefined; +} | { + kind: "base64url"; + message?: string | undefined; +}; +export interface ZodStringDef extends ZodTypeDef { + checks: ZodStringCheck[]; + typeName: ZodFirstPartyTypeKind.ZodString; + coerce: boolean; +} +export declare function datetimeRegex(args: { + precision?: number | null; + offset?: boolean; + local?: boolean; +}): RegExp; +export declare class ZodString extends ZodType { + _parse(input: ParseInput): ParseReturnType; + protected _regex(regex: RegExp, validation: StringValidation, message?: errorUtil.ErrMessage): ZodEffects; + _addCheck(check: ZodStringCheck): ZodString; + email(message?: errorUtil.ErrMessage): ZodString; + url(message?: errorUtil.ErrMessage): ZodString; + emoji(message?: errorUtil.ErrMessage): ZodString; + uuid(message?: errorUtil.ErrMessage): ZodString; + nanoid(message?: errorUtil.ErrMessage): ZodString; + cuid(message?: errorUtil.ErrMessage): ZodString; + cuid2(message?: errorUtil.ErrMessage): ZodString; + ulid(message?: errorUtil.ErrMessage): ZodString; + base64(message?: errorUtil.ErrMessage): ZodString; + base64url(message?: errorUtil.ErrMessage): ZodString; + jwt(options?: { + alg?: string; + message?: string | undefined; + }): ZodString; + ip(options?: string | { + version?: IpVersion; + message?: string | undefined; + }): ZodString; + cidr(options?: string | { + version?: IpVersion; + message?: string | undefined; + }): ZodString; + datetime(options?: string | { + message?: string | undefined; + precision?: number | null; + offset?: boolean; + local?: boolean; + }): ZodString; + date(message?: string): ZodString; + time(options?: string | { + message?: string | undefined; + precision?: number | null; + }): ZodString; + duration(message?: errorUtil.ErrMessage): ZodString; + regex(regex: RegExp, message?: errorUtil.ErrMessage): ZodString; + includes(value: string, options?: { + message?: string; + position?: number; + }): ZodString; + startsWith(value: string, message?: errorUtil.ErrMessage): ZodString; + endsWith(value: string, message?: errorUtil.ErrMessage): ZodString; + min(minLength: number, message?: errorUtil.ErrMessage): ZodString; + max(maxLength: number, message?: errorUtil.ErrMessage): ZodString; + length(len: number, message?: errorUtil.ErrMessage): ZodString; + /** + * Equivalent to `.min(1)` + */ + nonempty(message?: errorUtil.ErrMessage): ZodString; + trim(): ZodString; + toLowerCase(): ZodString; + toUpperCase(): ZodString; + get isDatetime(): boolean; + get isDate(): boolean; + get isTime(): boolean; + get isDuration(): boolean; + get isEmail(): boolean; + get isURL(): boolean; + get isEmoji(): boolean; + get isUUID(): boolean; + get isNANOID(): boolean; + get isCUID(): boolean; + get isCUID2(): boolean; + get isULID(): boolean; + get isIP(): boolean; + get isCIDR(): boolean; + get isBase64(): boolean; + get isBase64url(): boolean; + get minLength(): number | null; + get maxLength(): number | null; + static create: (params?: RawCreateParams & { + coerce?: true; + }) => ZodString; +} +export type ZodNumberCheck = { + kind: "min"; + value: number; + inclusive: boolean; + message?: string | undefined; +} | { + kind: "max"; + value: number; + inclusive: boolean; + message?: string | undefined; +} | { + kind: "int"; + message?: string | undefined; +} | { + kind: "multipleOf"; + value: number; + message?: string | undefined; +} | { + kind: "finite"; + message?: string | undefined; +}; +export interface ZodNumberDef extends ZodTypeDef { + checks: ZodNumberCheck[]; + typeName: ZodFirstPartyTypeKind.ZodNumber; + coerce: boolean; +} +export declare class ZodNumber extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodNumber; + gte(value: number, message?: errorUtil.ErrMessage): ZodNumber; + min: (value: number, message?: errorUtil.ErrMessage) => ZodNumber; + gt(value: number, message?: errorUtil.ErrMessage): ZodNumber; + lte(value: number, message?: errorUtil.ErrMessage): ZodNumber; + max: (value: number, message?: errorUtil.ErrMessage) => ZodNumber; + lt(value: number, message?: errorUtil.ErrMessage): ZodNumber; + protected setLimit(kind: "min" | "max", value: number, inclusive: boolean, message?: string): ZodNumber; + _addCheck(check: ZodNumberCheck): ZodNumber; + int(message?: errorUtil.ErrMessage): ZodNumber; + positive(message?: errorUtil.ErrMessage): ZodNumber; + negative(message?: errorUtil.ErrMessage): ZodNumber; + nonpositive(message?: errorUtil.ErrMessage): ZodNumber; + nonnegative(message?: errorUtil.ErrMessage): ZodNumber; + multipleOf(value: number, message?: errorUtil.ErrMessage): ZodNumber; + step: (value: number, message?: errorUtil.ErrMessage) => ZodNumber; + finite(message?: errorUtil.ErrMessage): ZodNumber; + safe(message?: errorUtil.ErrMessage): ZodNumber; + get minValue(): number | null; + get maxValue(): number | null; + get isInt(): boolean; + get isFinite(): boolean; +} +export type ZodBigIntCheck = { + kind: "min"; + value: bigint; + inclusive: boolean; + message?: string | undefined; +} | { + kind: "max"; + value: bigint; + inclusive: boolean; + message?: string | undefined; +} | { + kind: "multipleOf"; + value: bigint; + message?: string | undefined; +}; +export interface ZodBigIntDef extends ZodTypeDef { + checks: ZodBigIntCheck[]; + typeName: ZodFirstPartyTypeKind.ZodBigInt; + coerce: boolean; +} +export declare class ZodBigInt extends ZodType { + _parse(input: ParseInput): ParseReturnType; + _getInvalidInput(input: ParseInput): INVALID; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodBigInt; + gte(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + min: (value: bigint, message?: errorUtil.ErrMessage) => ZodBigInt; + gt(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + lte(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + max: (value: bigint, message?: errorUtil.ErrMessage) => ZodBigInt; + lt(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + protected setLimit(kind: "min" | "max", value: bigint, inclusive: boolean, message?: string): ZodBigInt; + _addCheck(check: ZodBigIntCheck): ZodBigInt; + positive(message?: errorUtil.ErrMessage): ZodBigInt; + negative(message?: errorUtil.ErrMessage): ZodBigInt; + nonpositive(message?: errorUtil.ErrMessage): ZodBigInt; + nonnegative(message?: errorUtil.ErrMessage): ZodBigInt; + multipleOf(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + get minValue(): bigint | null; + get maxValue(): bigint | null; +} +export interface ZodBooleanDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodBoolean; + coerce: boolean; +} +export declare class ZodBoolean extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodBoolean; +} +export type ZodDateCheck = { + kind: "min"; + value: number; + message?: string | undefined; +} | { + kind: "max"; + value: number; + message?: string | undefined; +}; +export interface ZodDateDef extends ZodTypeDef { + checks: ZodDateCheck[]; + coerce: boolean; + typeName: ZodFirstPartyTypeKind.ZodDate; +} +export declare class ZodDate extends ZodType { + _parse(input: ParseInput): ParseReturnType; + _addCheck(check: ZodDateCheck): ZodDate; + min(minDate: Date, message?: errorUtil.ErrMessage): ZodDate; + max(maxDate: Date, message?: errorUtil.ErrMessage): ZodDate; + get minDate(): Date | null; + get maxDate(): Date | null; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodDate; +} +export interface ZodSymbolDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodSymbol; +} +export declare class ZodSymbol extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodSymbol; +} +export interface ZodUndefinedDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodUndefined; +} +export declare class ZodUndefined extends ZodType { + _parse(input: ParseInput): ParseReturnType; + params?: RawCreateParams; + static create: (params?: RawCreateParams) => ZodUndefined; +} +export interface ZodNullDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNull; +} +export declare class ZodNull extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNull; +} +export interface ZodAnyDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodAny; +} +export declare class ZodAny extends ZodType { + _any: true; + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodAny; +} +export interface ZodUnknownDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodUnknown; +} +export declare class ZodUnknown extends ZodType { + _unknown: true; + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodUnknown; +} +export interface ZodNeverDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNever; +} +export declare class ZodNever extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNever; +} +export interface ZodVoidDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodVoid; +} +export declare class ZodVoid extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodVoid; +} +export interface ZodArrayDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodArray; + exactLength: { + value: number; + message?: string | undefined; + } | null; + minLength: { + value: number; + message?: string | undefined; + } | null; + maxLength: { + value: number; + message?: string | undefined; + } | null; +} +export type ArrayCardinality = "many" | "atleastone"; +export type arrayOutputType = Cardinality extends "atleastone" ? [T["_output"], ...T["_output"][]] : T["_output"][]; +export declare class ZodArray extends ZodType, ZodArrayDef, Cardinality extends "atleastone" ? [T["_input"], ...T["_input"][]] : T["_input"][]> { + _parse(input: ParseInput): ParseReturnType; + get element(): T; + min(minLength: number, message?: errorUtil.ErrMessage): this; + max(maxLength: number, message?: errorUtil.ErrMessage): this; + length(len: number, message?: errorUtil.ErrMessage): this; + nonempty(message?: errorUtil.ErrMessage): ZodArray; + static create: (schema: El, params?: RawCreateParams) => ZodArray; +} +export type ZodNonEmptyArray = ZodArray; +export type UnknownKeysParam = "passthrough" | "strict" | "strip"; +export interface ZodObjectDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodObject; + shape: () => T; + catchall: Catchall; + unknownKeys: UnknownKeys; +} +export type mergeTypes = { + [k in keyof A | keyof B]: k extends keyof B ? B[k] : k extends keyof A ? A[k] : never; +}; +export type objectOutputType = objectUtil.flatten>> & CatchallOutput & PassthroughType; +export type baseObjectOutputType = { + [k in keyof Shape]: Shape[k]["_output"]; +}; +export type objectInputType = objectUtil.flatten> & CatchallInput & PassthroughType; +export type baseObjectInputType = objectUtil.addQuestionMarks<{ + [k in keyof Shape]: Shape[k]["_input"]; +}>; +export type CatchallOutput = ZodType extends T ? unknown : { + [k: string]: T["_output"]; +}; +export type CatchallInput = ZodType extends T ? unknown : { + [k: string]: T["_input"]; +}; +export type PassthroughType = T extends "passthrough" ? { + [k: string]: unknown; +} : unknown; +export type deoptional = T extends ZodOptional ? deoptional : T extends ZodNullable ? ZodNullable> : T; +export type SomeZodObject = ZodObject; +export type noUnrecognized = { + [k in keyof Obj]: k extends keyof Shape ? Obj[k] : never; +}; +export declare class ZodObject, Input = objectInputType> extends ZodType, Input> { + _cached: { + shape: T; + keys: string[]; + } | null; + _getCached(): { + shape: T; + keys: string[]; + }; + _parse(input: ParseInput): ParseReturnType; + get shape(): T; + strict(message?: errorUtil.ErrMessage): ZodObject; + strip(): ZodObject; + passthrough(): ZodObject; + /** + * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped. + * If you want to pass through unknown properties, use `.passthrough()` instead. + */ + nonstrict: () => ZodObject; + extend(augmentation: Augmentation): ZodObject, UnknownKeys, Catchall>; + /** + * @deprecated Use `.extend` instead + * */ + augment: (augmentation: Augmentation) => ZodObject, UnknownKeys, Catchall>; + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging: Incoming): ZodObject, Incoming["_def"]["unknownKeys"], Incoming["_def"]["catchall"]>; + setKey(key: Key, schema: Schema): ZodObject; + catchall(index: Index): ZodObject; + pick>(mask: Mask): ZodObject>, UnknownKeys, Catchall>; + omit>(mask: Mask): ZodObject, UnknownKeys, Catchall>; + /** + * @deprecated + */ + deepPartial(): partialUtil.DeepPartial; + partial(): ZodObject<{ + [k in keyof T]: ZodOptional; + }, UnknownKeys, Catchall>; + partial>(mask: Mask): ZodObject : T[k]; + }>, UnknownKeys, Catchall>; + required(): ZodObject<{ + [k in keyof T]: deoptional; + }, UnknownKeys, Catchall>; + required>(mask: Mask): ZodObject : T[k]; + }>, UnknownKeys, Catchall>; + keyof(): ZodEnum>; + static create: (shape: Shape, params?: RawCreateParams) => ZodObject, objectInputType>; + static strictCreate: (shape: Shape, params?: RawCreateParams) => ZodObject; + static lazycreate: (shape: () => Shape, params?: RawCreateParams) => ZodObject; +} +export type AnyZodObject = ZodObject; +export type ZodUnionOptions = Readonly<[ZodTypeAny, ...ZodTypeAny[]]>; +export interface ZodUnionDef> extends ZodTypeDef { + options: T; + typeName: ZodFirstPartyTypeKind.ZodUnion; +} +export declare class ZodUnion extends ZodType, T[number]["_input"]> { + _parse(input: ParseInput): ParseReturnType; + get options(): T; + static create: >(types: Options, params?: RawCreateParams) => ZodUnion; +} +export type ZodDiscriminatedUnionOption = ZodObject<{ + [key in Discriminator]: ZodTypeAny; +} & ZodRawShape, UnknownKeysParam, ZodTypeAny>; +export interface ZodDiscriminatedUnionDef[] = ZodDiscriminatedUnionOption[]> extends ZodTypeDef { + discriminator: Discriminator; + options: Options; + optionsMap: Map>; + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion; +} +export declare class ZodDiscriminatedUnion[]> extends ZodType, ZodDiscriminatedUnionDef, input> { + _parse(input: ParseInput): ParseReturnType; + get discriminator(): Discriminator; + get options(): Options; + get optionsMap(): Map>; + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create, + ...ZodDiscriminatedUnionOption[] + ]>(discriminator: Discriminator, options: Types, params?: RawCreateParams): ZodDiscriminatedUnion; +} +export interface ZodIntersectionDef extends ZodTypeDef { + left: T; + right: U; + typeName: ZodFirstPartyTypeKind.ZodIntersection; +} +export declare class ZodIntersection extends ZodType, T["_input"] & U["_input"]> { + _parse(input: ParseInput): ParseReturnType; + static create: (left: TSchema, right: USchema, params?: RawCreateParams) => ZodIntersection; +} +export type ZodTupleItems = [ZodTypeAny, ...ZodTypeAny[]]; +export type AssertArray = T extends any[] ? T : never; +export type OutputTypeOfTuple = AssertArray<{ + [k in keyof T]: T[k] extends ZodType ? T[k]["_output"] : never; +}>; +export type OutputTypeOfTupleWithRest = Rest extends ZodTypeAny ? [...OutputTypeOfTuple, ...Rest["_output"][]] : OutputTypeOfTuple; +export type InputTypeOfTuple = AssertArray<{ + [k in keyof T]: T[k] extends ZodType ? T[k]["_input"] : never; +}>; +export type InputTypeOfTupleWithRest = Rest extends ZodTypeAny ? [...InputTypeOfTuple, ...Rest["_input"][]] : InputTypeOfTuple; +export interface ZodTupleDef extends ZodTypeDef { + items: T; + rest: Rest; + typeName: ZodFirstPartyTypeKind.ZodTuple; +} +export type AnyZodTuple = ZodTuple<[ZodTypeAny, ...ZodTypeAny[]] | [], ZodTypeAny | null>; +export declare class ZodTuple extends ZodType, ZodTupleDef, InputTypeOfTupleWithRest> { + _parse(input: ParseInput): ParseReturnType; + get items(): T; + rest(rest: RestSchema): ZodTuple; + static create: (schemas: Items, params?: RawCreateParams) => ZodTuple; +} +export interface ZodRecordDef extends ZodTypeDef { + valueType: Value; + keyType: Key; + typeName: ZodFirstPartyTypeKind.ZodRecord; +} +export type KeySchema = ZodType; +export type RecordType = [string] extends [K] ? Record : [number] extends [K] ? Record : [symbol] extends [K] ? Record : [BRAND] extends [K] ? Record : Partial>; +export declare class ZodRecord extends ZodType, ZodRecordDef, RecordType> { + get keySchema(): Key; + get valueSchema(): Value; + _parse(input: ParseInput): ParseReturnType; + get element(): Value; + static create(valueType: Value, params?: RawCreateParams): ZodRecord; + static create(keySchema: Keys, valueType: Value, params?: RawCreateParams): ZodRecord; +} +export interface ZodMapDef extends ZodTypeDef { + valueType: Value; + keyType: Key; + typeName: ZodFirstPartyTypeKind.ZodMap; +} +export declare class ZodMap extends ZodType, ZodMapDef, Map> { + get keySchema(): Key; + get valueSchema(): Value; + _parse(input: ParseInput): ParseReturnType; + static create: (keyType: KeySchema, valueType: ValueSchema, params?: RawCreateParams) => ZodMap; +} +export interface ZodSetDef extends ZodTypeDef { + valueType: Value; + typeName: ZodFirstPartyTypeKind.ZodSet; + minSize: { + value: number; + message?: string | undefined; + } | null; + maxSize: { + value: number; + message?: string | undefined; + } | null; +} +export declare class ZodSet extends ZodType, ZodSetDef, Set> { + _parse(input: ParseInput): ParseReturnType; + min(minSize: number, message?: errorUtil.ErrMessage): this; + max(maxSize: number, message?: errorUtil.ErrMessage): this; + size(size: number, message?: errorUtil.ErrMessage): this; + nonempty(message?: errorUtil.ErrMessage): ZodSet; + static create: (valueType: ValueSchema, params?: RawCreateParams) => ZodSet; +} +export interface ZodFunctionDef = ZodTuple, Returns extends ZodTypeAny = ZodTypeAny> extends ZodTypeDef { + args: Args; + returns: Returns; + typeName: ZodFirstPartyTypeKind.ZodFunction; +} +export type OuterTypeOfFunction, Returns extends ZodTypeAny> = Args["_input"] extends Array ? (...args: Args["_input"]) => Returns["_output"] : never; +export type InnerTypeOfFunction, Returns extends ZodTypeAny> = Args["_output"] extends Array ? (...args: Args["_output"]) => Returns["_input"] : never; +export declare class ZodFunction, Returns extends ZodTypeAny> extends ZodType, ZodFunctionDef, InnerTypeOfFunction> { + _parse(input: ParseInput): ParseReturnType; + parameters(): Args; + returnType(): Returns; + args[0]>(...items: Items): ZodFunction, Returns>; + returns>(returnType: NewReturnType): ZodFunction; + implement>(func: F): ReturnType extends Returns["_output"] ? (...args: Args["_input"]) => ReturnType : OuterTypeOfFunction; + strictImplement(func: InnerTypeOfFunction): InnerTypeOfFunction; + validate: >(func: F) => ReturnType extends Returns["_output"] ? (...args: Args["_input"]) => ReturnType : OuterTypeOfFunction; + static create(): ZodFunction, ZodUnknown>; + static create>(args: T): ZodFunction; + static create(args: T, returns: U): ZodFunction; + static create, U extends ZodTypeAny = ZodUnknown>(args: T, returns: U, params?: RawCreateParams): ZodFunction; +} +export interface ZodLazyDef extends ZodTypeDef { + getter: () => T; + typeName: ZodFirstPartyTypeKind.ZodLazy; +} +export declare class ZodLazy extends ZodType, ZodLazyDef, input> { + get schema(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (getter: () => Inner, params?: RawCreateParams) => ZodLazy; +} +export interface ZodLiteralDef extends ZodTypeDef { + value: T; + typeName: ZodFirstPartyTypeKind.ZodLiteral; +} +export declare class ZodLiteral extends ZodType, T> { + _parse(input: ParseInput): ParseReturnType; + get value(): T; + static create: (value: Value, params?: RawCreateParams) => ZodLiteral; +} +export type ArrayKeys = keyof any[]; +export type Indices = Exclude; +export type EnumValues = readonly [T, ...T[]]; +export type Values = { + [k in T[number]]: k; +}; +export interface ZodEnumDef extends ZodTypeDef { + values: T; + typeName: ZodFirstPartyTypeKind.ZodEnum; +} +export type Writeable = { + -readonly [P in keyof T]: T[P]; +}; +export type FilterEnum = Values extends [] ? [] : Values extends [infer Head, ...infer Rest] ? Head extends ToExclude ? FilterEnum : [Head, ...FilterEnum] : never; +export type typecast = A extends T ? A : never; +declare function createZodEnum>(values: T, params?: RawCreateParams): ZodEnum>; +declare function createZodEnum(values: T, params?: RawCreateParams): ZodEnum; +export declare class ZodEnum extends ZodType, T[number]> { + _cache: Set | undefined; + _parse(input: ParseInput): ParseReturnType; + get options(): T; + get enum(): Values; + get Values(): Values; + get Enum(): Values; + extract(values: ToExtract, newDef?: RawCreateParams): ZodEnum>; + exclude(values: ToExclude, newDef?: RawCreateParams): ZodEnum>, [string, ...string[]]>>; + static create: typeof createZodEnum; +} +export interface ZodNativeEnumDef extends ZodTypeDef { + values: T; + typeName: ZodFirstPartyTypeKind.ZodNativeEnum; +} +export type EnumLike = { + [k: string]: string | number; + [nu: number]: string; +}; +export declare class ZodNativeEnum extends ZodType, T[keyof T]> { + _cache: Set | undefined; + _parse(input: ParseInput): ParseReturnType; + get enum(): T; + static create: (values: Elements, params?: RawCreateParams) => ZodNativeEnum; +} +export interface ZodPromiseDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodPromise; +} +export declare class ZodPromise extends ZodType, ZodPromiseDef, Promise> { + unwrap(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (schema: Inner, params?: RawCreateParams) => ZodPromise; +} +export type Refinement = (arg: T, ctx: RefinementCtx) => any; +export type SuperRefinement = (arg: T, ctx: RefinementCtx) => void | Promise; +export type RefinementEffect = { + type: "refinement"; + refinement: (arg: T, ctx: RefinementCtx) => any; +}; +export type TransformEffect = { + type: "transform"; + transform: (arg: T, ctx: RefinementCtx) => any; +}; +export type PreprocessEffect = { + type: "preprocess"; + transform: (arg: T, ctx: RefinementCtx) => any; +}; +export type Effect = RefinementEffect | TransformEffect | PreprocessEffect; +export interface ZodEffectsDef extends ZodTypeDef { + schema: T; + typeName: ZodFirstPartyTypeKind.ZodEffects; + effect: Effect; +} +export declare class ZodEffects, Input = input> extends ZodType, Input> { + innerType(): T; + sourceType(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (schema: I, effect: Effect, params?: RawCreateParams) => ZodEffects; + static createWithPreprocess: (preprocess: (arg: unknown, ctx: RefinementCtx) => unknown, schema: I, params?: RawCreateParams) => ZodEffects; +} +export { ZodEffects as ZodTransformer }; +export interface ZodOptionalDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodOptional; +} +export type ZodOptionalType = ZodOptional; +export declare class ZodOptional extends ZodType, T["_input"] | undefined> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; + static create: (type: Inner, params?: RawCreateParams) => ZodOptional; +} +export interface ZodNullableDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodNullable; +} +export type ZodNullableType = ZodNullable; +export declare class ZodNullable extends ZodType, T["_input"] | null> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; + static create: (type: Inner, params?: RawCreateParams) => ZodNullable; +} +export interface ZodDefaultDef extends ZodTypeDef { + innerType: T; + defaultValue: () => util.noUndefined; + typeName: ZodFirstPartyTypeKind.ZodDefault; +} +export declare class ZodDefault extends ZodType, ZodDefaultDef, T["_input"] | undefined> { + _parse(input: ParseInput): ParseReturnType; + removeDefault(): T; + static create: (type: Inner, params: RawCreateParams & { + default: Inner["_input"] | (() => util.noUndefined); + }) => ZodDefault; +} +export interface ZodCatchDef extends ZodTypeDef { + innerType: T; + catchValue: (ctx: { + error: ZodError; + input: unknown; + }) => T["_input"]; + typeName: ZodFirstPartyTypeKind.ZodCatch; +} +export declare class ZodCatch extends ZodType, unknown> { + _parse(input: ParseInput): ParseReturnType; + removeCatch(): T; + static create: (type: Inner, params: RawCreateParams & { + catch: Inner["_output"] | (() => Inner["_output"]); + }) => ZodCatch; +} +export interface ZodNaNDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNaN; +} +export declare class ZodNaN extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNaN; +} +export interface ZodBrandedDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodBranded; +} +export declare const BRAND: unique symbol; +export type BRAND = { + [BRAND]: { + [k in T]: true; + }; +}; +export declare class ZodBranded extends ZodType, ZodBrandedDef, T["_input"]> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; +} +export interface ZodPipelineDef extends ZodTypeDef { + in: A; + out: B; + typeName: ZodFirstPartyTypeKind.ZodPipeline; +} +export declare class ZodPipeline extends ZodType, A["_input"]> { + _parse(input: ParseInput): ParseReturnType; + static create(a: ASchema, b: BSchema): ZodPipeline; +} +type BuiltIn = (((...args: any[]) => any) | (new (...args: any[]) => any)) | { + readonly [Symbol.toStringTag]: string; +} | Date | Error | Generator | Promise | RegExp; +type MakeReadonly = T extends Map ? ReadonlyMap : T extends Set ? ReadonlySet : T extends [infer Head, ...infer Tail] ? readonly [Head, ...Tail] : T extends Array ? ReadonlyArray : T extends BuiltIn ? T : Readonly; +export interface ZodReadonlyDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodReadonly; +} +export declare class ZodReadonly extends ZodType, ZodReadonlyDef, MakeReadonly> { + _parse(input: ParseInput): ParseReturnType; + static create: (type: Inner, params?: RawCreateParams) => ZodReadonly; + unwrap(): T; +} +type CustomParams = CustomErrorParams & { + fatal?: boolean; +}; +export declare function custom(check?: (data: any) => any, _params?: string | CustomParams | ((input: any) => CustomParams), +/** + * @deprecated + * + * Pass `fatal` into the params object instead: + * + * ```ts + * z.string().custom((val) => val.length > 5, { fatal: false }) + * ``` + * + */ +fatal?: boolean): ZodType; +export { ZodType as Schema, ZodType as ZodSchema }; +export declare const late: { + object: (shape: () => Shape, params?: RawCreateParams) => ZodObject; +}; +export declare enum ZodFirstPartyTypeKind { + ZodString = "ZodString", + ZodNumber = "ZodNumber", + ZodNaN = "ZodNaN", + ZodBigInt = "ZodBigInt", + ZodBoolean = "ZodBoolean", + ZodDate = "ZodDate", + ZodSymbol = "ZodSymbol", + ZodUndefined = "ZodUndefined", + ZodNull = "ZodNull", + ZodAny = "ZodAny", + ZodUnknown = "ZodUnknown", + ZodNever = "ZodNever", + ZodVoid = "ZodVoid", + ZodArray = "ZodArray", + ZodObject = "ZodObject", + ZodUnion = "ZodUnion", + ZodDiscriminatedUnion = "ZodDiscriminatedUnion", + ZodIntersection = "ZodIntersection", + ZodTuple = "ZodTuple", + ZodRecord = "ZodRecord", + ZodMap = "ZodMap", + ZodSet = "ZodSet", + ZodFunction = "ZodFunction", + ZodLazy = "ZodLazy", + ZodLiteral = "ZodLiteral", + ZodEnum = "ZodEnum", + ZodEffects = "ZodEffects", + ZodNativeEnum = "ZodNativeEnum", + ZodOptional = "ZodOptional", + ZodNullable = "ZodNullable", + ZodDefault = "ZodDefault", + ZodCatch = "ZodCatch", + ZodPromise = "ZodPromise", + ZodBranded = "ZodBranded", + ZodPipeline = "ZodPipeline", + ZodReadonly = "ZodReadonly" +} +export type ZodFirstPartySchemaTypes = ZodString | ZodNumber | ZodNaN | ZodBigInt | ZodBoolean | ZodDate | ZodUndefined | ZodNull | ZodAny | ZodUnknown | ZodNever | ZodVoid | ZodArray | ZodObject | ZodUnion | ZodDiscriminatedUnion | ZodIntersection | ZodTuple | ZodRecord | ZodMap | ZodSet | ZodFunction | ZodLazy | ZodLiteral | ZodEnum | ZodEffects | ZodNativeEnum | ZodOptional | ZodNullable | ZodDefault | ZodCatch | ZodPromise | ZodBranded | ZodPipeline | ZodReadonly | ZodSymbol; +declare abstract class Class { + constructor(..._: any[]); +} +declare const instanceOfType: (cls: T, params?: CustomParams) => ZodType, ZodTypeDef, InstanceType>; +declare const stringType: (params?: RawCreateParams & { + coerce?: true; +}) => ZodString; +declare const numberType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodNumber; +declare const nanType: (params?: RawCreateParams) => ZodNaN; +declare const bigIntType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodBigInt; +declare const booleanType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodBoolean; +declare const dateType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodDate; +declare const symbolType: (params?: RawCreateParams) => ZodSymbol; +declare const undefinedType: (params?: RawCreateParams) => ZodUndefined; +declare const nullType: (params?: RawCreateParams) => ZodNull; +declare const anyType: (params?: RawCreateParams) => ZodAny; +declare const unknownType: (params?: RawCreateParams) => ZodUnknown; +declare const neverType: (params?: RawCreateParams) => ZodNever; +declare const voidType: (params?: RawCreateParams) => ZodVoid; +declare const arrayType: (schema: El, params?: RawCreateParams) => ZodArray; +declare const objectType: (shape: Shape, params?: RawCreateParams) => ZodObject, objectInputType>; +declare const strictObjectType: (shape: Shape, params?: RawCreateParams) => ZodObject; +declare const unionType: >(types: Options, params?: RawCreateParams) => ZodUnion; +declare const discriminatedUnionType: typeof ZodDiscriminatedUnion.create; +declare const intersectionType: (left: TSchema, right: USchema, params?: RawCreateParams) => ZodIntersection; +declare const tupleType: (schemas: Items, params?: RawCreateParams) => ZodTuple; +declare const recordType: typeof ZodRecord.create; +declare const mapType: (keyType: KeySchema, valueType: ValueSchema, params?: RawCreateParams) => ZodMap; +declare const setType: (valueType: ValueSchema, params?: RawCreateParams) => ZodSet; +declare const functionType: typeof ZodFunction.create; +declare const lazyType: (getter: () => Inner, params?: RawCreateParams) => ZodLazy; +declare const literalType: (value: Value, params?: RawCreateParams) => ZodLiteral; +declare const enumType: typeof createZodEnum; +declare const nativeEnumType: (values: Elements, params?: RawCreateParams) => ZodNativeEnum; +declare const promiseType: (schema: Inner, params?: RawCreateParams) => ZodPromise; +declare const effectsType: (schema: I, effect: Effect, params?: RawCreateParams) => ZodEffects; +declare const optionalType: (type: Inner, params?: RawCreateParams) => ZodOptional; +declare const nullableType: (type: Inner, params?: RawCreateParams) => ZodNullable; +declare const preprocessType: (preprocess: (arg: unknown, ctx: RefinementCtx) => unknown, schema: I, params?: RawCreateParams) => ZodEffects; +declare const pipelineType: typeof ZodPipeline.create; +declare const ostring: () => ZodOptional; +declare const onumber: () => ZodOptional; +declare const oboolean: () => ZodOptional; +export declare const coerce: { + string: (typeof ZodString)["create"]; + number: (typeof ZodNumber)["create"]; + boolean: (typeof ZodBoolean)["create"]; + bigint: (typeof ZodBigInt)["create"]; + date: (typeof ZodDate)["create"]; +}; +export { anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, dateType as date, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, instanceOfType as instanceof, intersectionType as intersection, lazyType as lazy, literalType as literal, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, oboolean, onumber, optionalType as optional, ostring, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, recordType as record, setType as set, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, voidType as void, }; +export declare const NEVER: never; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/checks.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/checks.d.cts new file mode 100644 index 000000000..757d60e6e --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/checks.d.cts @@ -0,0 +1 @@ +export { _lt as lt, _lte as lte, _gt as gt, _gte as gte, _positive as positive, _negative as negative, _nonpositive as nonpositive, _nonnegative as nonnegative, _multipleOf as multipleOf, _maxSize as maxSize, _minSize as minSize, _size as size, _maxLength as maxLength, _minLength as minLength, _length as length, _regex as regex, _lowercase as lowercase, _uppercase as uppercase, _includes as includes, _startsWith as startsWith, _endsWith as endsWith, _property as property, _properties as properties, _mime as mime, _overwrite as overwrite, _normalize as normalize, _trim as trim, _toLowerCase as toLowerCase, _toUpperCase as toUpperCase, _slugify as slugify, type $RefinementCtx as RefinementCtx, } from "../core/index.cjs"; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/checks.js b/tests/fixtures/npm/node_modules/zod/v4/classic/checks.js new file mode 100644 index 000000000..50a08b884 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/checks.js @@ -0,0 +1 @@ +export { _lt as lt, _lte as lte, _gt as gt, _gte as gte, _positive as positive, _negative as negative, _nonpositive as nonpositive, _nonnegative as nonnegative, _multipleOf as multipleOf, _maxSize as maxSize, _minSize as minSize, _size as size, _maxLength as maxLength, _minLength as minLength, _length as length, _regex as regex, _lowercase as lowercase, _uppercase as uppercase, _includes as includes, _startsWith as startsWith, _endsWith as endsWith, _property as property, _properties as properties, _mime as mime, _overwrite as overwrite, _normalize as normalize, _trim as trim, _toLowerCase as toLowerCase, _toUpperCase as toUpperCase, _slugify as slugify, } from "../core/index.js"; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/coerce.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/coerce.d.cts new file mode 100644 index 000000000..231b91745 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/coerce.d.cts @@ -0,0 +1,17 @@ +import * as core from "../core/index.cjs"; +import * as schemas from "./schemas.cjs"; +export interface ZodCoercedString extends schemas._ZodString> { +} +export declare function string(params?: string | core.$ZodStringParams): ZodCoercedString; +export interface ZodCoercedNumber extends schemas._ZodNumber> { +} +export declare function number(params?: string | core.$ZodNumberParams): ZodCoercedNumber; +export interface ZodCoercedBoolean extends schemas._ZodBoolean> { +} +export declare function boolean(params?: string | core.$ZodBooleanParams): ZodCoercedBoolean; +export interface ZodCoercedBigInt extends schemas._ZodBigInt> { +} +export declare function bigint(params?: string | core.$ZodBigIntParams): ZodCoercedBigInt; +export interface ZodCoercedDate extends schemas._ZodDate> { +} +export declare function date(params?: string | core.$ZodDateParams): ZodCoercedDate; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/coerce.js b/tests/fixtures/npm/node_modules/zod/v4/classic/coerce.js new file mode 100644 index 000000000..141cf8936 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/coerce.js @@ -0,0 +1,17 @@ +import * as core from "../core/index.js"; +import * as schemas from "./schemas.js"; +export function string(params) { + return core._coercedString(schemas.ZodString, params); +} +export function number(params) { + return core._coercedNumber(schemas.ZodNumber, params); +} +export function boolean(params) { + return core._coercedBoolean(schemas.ZodBoolean, params); +} +export function bigint(params) { + return core._coercedBigint(schemas.ZodBigInt, params); +} +export function date(params) { + return core._coercedDate(schemas.ZodDate, params); +} diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/compat.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/compat.d.cts new file mode 100644 index 000000000..8f1b3d8c8 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/compat.d.cts @@ -0,0 +1,48 @@ +import * as core from "../core/index.cjs"; +import type { ZodType } from "./schemas.cjs"; +/** @deprecated Use `z.output` instead. */ +export type TypeOf = core.output; +/** @deprecated Use `z.output` instead. */ +export type Infer = core.output; +/** @deprecated Use `z.core.$ZodTypes` instead */ +export type ZodFirstPartySchemaTypes = core.$ZodTypes; +/** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */ +export declare const ZodIssueCode: { + readonly invalid_type: "invalid_type"; + readonly too_big: "too_big"; + readonly too_small: "too_small"; + readonly invalid_format: "invalid_format"; + readonly not_multiple_of: "not_multiple_of"; + readonly unrecognized_keys: "unrecognized_keys"; + readonly invalid_union: "invalid_union"; + readonly invalid_key: "invalid_key"; + readonly invalid_element: "invalid_element"; + readonly invalid_value: "invalid_value"; + readonly custom: "custom"; +}; +/** @deprecated Use `z.core.$ZodFlattenedError>` instead; it takes an inferred type, not a schema. */ +export type inferFlattenedErrors = core.$ZodFlattenedError, U>; +/** @deprecated Use `z.core.$ZodFormattedError>` instead; it takes an inferred type, not a schema. */ +export type inferFormattedError, U = string> = core.$ZodFormattedError, U>; +/** Use `z.$brand` instead */ +export type BRAND = { + [core.$brand]: { + [k in T]: true; + }; +}; +export { $brand, config } from "../core/index.cjs"; +/** @deprecated Use `z.config(params)` instead. */ +export declare function setErrorMap(map: core.$ZodErrorMap): void; +/** @deprecated Use `z.config()` instead. */ +export declare function getErrorMap(): core.$ZodErrorMap | undefined; +/** @deprecated Use z.ZodType (without generics) instead. */ +export type ZodTypeAny = core.$ZodTypeInternals> = ZodType; +/** @deprecated Use `z.ZodType` */ +export type ZodSchema = core.$ZodTypeInternals> = ZodType; +/** @deprecated Use `z.ZodType` */ +export type Schema = core.$ZodTypeInternals> = ZodType; +/** Included for Zod 3 compatibility */ +export type ZodRawShape = core.$ZodShape; +/** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */ +export declare enum ZodFirstPartyTypeKind { +} diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/compat.js b/tests/fixtures/npm/node_modules/zod/v4/classic/compat.js new file mode 100644 index 000000000..2179ea904 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/compat.js @@ -0,0 +1,31 @@ +// Zod 3 compat layer +import * as core from "../core/index.js"; +/** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */ +export const ZodIssueCode = { + invalid_type: "invalid_type", + too_big: "too_big", + too_small: "too_small", + invalid_format: "invalid_format", + not_multiple_of: "not_multiple_of", + unrecognized_keys: "unrecognized_keys", + invalid_union: "invalid_union", + invalid_key: "invalid_key", + invalid_element: "invalid_element", + invalid_value: "invalid_value", + custom: "custom", +}; +export { $brand, config } from "../core/index.js"; +/** @deprecated Use `z.config(params)` instead. */ +export function setErrorMap(map) { + core.config({ + customError: map, + }); +} +/** @deprecated Use `z.config()` instead. */ +export function getErrorMap() { + return core.config().customError; +} +/** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */ +export var ZodFirstPartyTypeKind; +(function (ZodFirstPartyTypeKind) { +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/deep-partial.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/deep-partial.d.cts new file mode 100644 index 000000000..674ecf18e --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/deep-partial.d.cts @@ -0,0 +1,14 @@ +import type * as core from "../core/index.cjs"; +import * as schemas from "./schemas.cjs"; +/** Distributes `.partial()` through every object in a schema type, preserving the wrappers around them. */ +export type DeepPartial = T extends schemas.ZodObject ? schemas.ZodObject<{ + -readonly [K in keyof Shape]: schemas.ZodOptional>; +}, Config> : T extends schemas.ZodDiscriminatedUnion ? schemas.ZodUnion<{ + -readonly [I in keyof Options]: Options[I] extends core.SomeType ? DeepPartial : Options[I]; +}> : T extends schemas.ZodUnion ? schemas.ZodUnion<{ + -readonly [I in keyof Options]: Options[I] extends core.SomeType ? DeepPartial : Options[I]; +}> : T extends schemas.ZodArray ? schemas.ZodArray> : T extends schemas.ZodTuple ? schemas.ZodTuple<{ + -readonly [K in keyof Items]: Items[K] extends core.SomeType ? DeepPartial : Items[K]; +}, Rest extends core.SomeType ? DeepPartial : Rest> : T extends schemas.ZodIntersection ? schemas.ZodIntersection, DeepPartial> : T extends schemas.ZodRecord ? schemas.ZodRecord> : T extends schemas.ZodMap ? schemas.ZodMap> : T extends schemas.ZodSet ? schemas.ZodSet> : T extends schemas.ZodOptional ? schemas.ZodOptional> : T extends schemas.ZodNullable ? schemas.ZodNullable> : T extends schemas.ZodDefault ? schemas.ZodDefault> : T extends schemas.ZodPrefault ? schemas.ZodPrefault> : T extends schemas.ZodNonOptional ? schemas.ZodNonOptional> : T extends schemas.ZodCatch ? schemas.ZodCatch> : T extends schemas.ZodReadonly ? schemas.ZodReadonly> : T extends schemas.ZodSuccess ? schemas.ZodSuccess> : T extends schemas.ZodPromise ? schemas.ZodPromise> : T extends schemas.ZodCodec ? schemas.ZodCodec, DeepPartial> : T extends schemas.ZodPreprocess ? schemas.ZodPreprocess> : T extends schemas.ZodPipe ? schemas.ZodPipe, DeepPartial> : T extends schemas.ZodLazy ? schemas.ZodLazy> : T; +/** Returns a copy of the schema with every nested object's properties made optional. */ +export declare function deepPartial(schema: T): DeepPartial; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/deep-partial.js b/tests/fixtures/npm/node_modules/zod/v4/classic/deep-partial.js new file mode 100644 index 000000000..a87bb625c --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/deep-partial.js @@ -0,0 +1,13 @@ +import { visit } from "../core/visit.js"; +import * as schemas from "./schemas.js"; +/** Returns a copy of the schema with every nested object's properties made optional. */ +export function deepPartial(schema) { + return visit(schema, { + object: (s) => s.partial(), + // Every partialed option now admits `undefined`, which the constructor rejects as a duplicate. + union: (s) => { + const def = s._zod.def; + return def.discriminator === undefined ? s : schemas.union(def.options); + }, + }); +} diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/errors.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/errors.d.cts new file mode 100644 index 000000000..472102f6a --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/errors.d.cts @@ -0,0 +1,29 @@ +import * as core from "../core/index.cjs"; +import { $ZodError } from "../core/index.cjs"; +/** @deprecated Use `z.core.$ZodIssue` from `@zod/core` instead, especially if you are building a library on top of Zod. */ +export type ZodIssue = core.$ZodIssue; +/** An Error-like class used to store Zod validation issues. */ +export interface ZodError extends $ZodError { + /** @deprecated Use the `z.treeifyError(err)` function instead. */ + format(): core.$ZodFormattedError; + format(mapper: (issue: core.$ZodIssue) => U): core.$ZodFormattedError; + /** @deprecated Use the `z.treeifyError(err)` function instead. */ + flatten(): core.$ZodFlattenedError; + flatten(mapper: (issue: core.$ZodIssue) => U): core.$ZodFlattenedError; + /** @deprecated Push directly to `.issues` instead. */ + addIssue(issue: core.$ZodIssue): void; + /** @deprecated Push directly to `.issues` instead. */ + addIssues(issues: core.$ZodIssue[]): void; + /** @deprecated Check `err.issues.length === 0` instead. */ + isEmpty: boolean; +} +export declare const ZodError: core.$constructor; +export declare const ZodRealError: core.$constructor; +/** @deprecated Use `z.core.$ZodFlattenedError` instead. */ +export type ZodFlattenedError = core.$ZodFlattenedError; +/** @deprecated Use `z.core.$ZodFormattedError` instead. */ +export type ZodFormattedError = core.$ZodFormattedError; +/** @deprecated Use `z.core.$ZodErrorMap` instead. */ +export type ZodErrorMap = core.$ZodErrorMap; +/** @deprecated Use `z.core.$ZodRawIssue` instead. */ +export type IssueData = core.$ZodRawIssue; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/errors.js b/tests/fixtures/npm/node_modules/zod/v4/classic/errors.js new file mode 100644 index 000000000..e3c02587a --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/errors.js @@ -0,0 +1,57 @@ +import * as core from "../core/index.js"; +import { $ZodError } from "../core/index.js"; +import * as util from "../core/util.js"; +/* Prototypes that already carry the lazy helper methods. Seeded with the + * intrinsics so that `init` on a foreign object — it accepts any object — + * can never install an accessor onto a prototype we do not own. */ +const _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]); +/* Helper methods live as non-enumerable lazy getters on the shared + * prototype instead of own properties on every instance. On first + * access the getter allocates the per-instance closure and caches it + * as a non-enumerable own property, so detached usage still works and + * the allocation only happens for methods actually touched. */ +function _lazyMethod(proto, key, make) { + Object.defineProperty(proto, key, { + configurable: true, + enumerable: false, + get() { + const value = make(this); + Object.defineProperty(this, key, { value, configurable: true, writable: true }); + return value; + }, + set(value) { + Object.defineProperty(this, key, { value, configurable: true, writable: true }); + }, + }); +} +const initializer = (inst, issues) => { + $ZodError.init(inst, issues); + inst.name = "ZodError"; + const proto = Object.getPrototypeOf(inst); + if (_installedErrorProtos.has(proto)) + return; + _installedErrorProtos.add(proto); + _lazyMethod(proto, "format", (self) => (mapper) => core.formatError(self, mapper)); + _lazyMethod(proto, "flatten", (self) => (mapper) => core.flattenError(self, mapper)); + _lazyMethod(proto, "addIssue", (self) => (issue) => { + self.issues.push(issue); + self.message = JSON.stringify(self.issues, util.jsonStringifyReplacer, 2); + }); + _lazyMethod(proto, "addIssues", (self) => (issues) => { + self.issues.push(...issues); + self.message = JSON.stringify(self.issues, util.jsonStringifyReplacer, 2); + }); + Object.defineProperty(proto, "isEmpty", { + configurable: true, + enumerable: false, + get() { + return this.issues.length === 0; + }, + }); +}; +export const ZodError = /*@__PURE__*/ core.$constructor("ZodError", initializer); +export const ZodRealError = /*@__PURE__*/ core.$constructor("ZodError", initializer, undefined, { + Parent: Error, +}); +// /** @deprecated Use `z.core.$ZodErrorMapCtx` instead. */ +// export type ErrorMapCtx = core.$ZodErrorMapCtx; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/external.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/external.d.cts new file mode 100644 index 000000000..7e9ce5e74 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/external.d.cts @@ -0,0 +1,19 @@ +export * as core from "../core/index.cjs"; +export * from "./schemas.cjs"; +export * from "./checks.cjs"; +export * from "./errors.cjs"; +export * from "./parse.cjs"; +export * from "./compat.cjs"; +export type { infer } from "../core/index.cjs"; +export type { JSONType } from "../core/util.cjs"; +export type { CompileOptions } from "../core/index.cjs"; +export { globalRegistry, type GlobalMeta, registry, config, memoizer, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, TimePrecision, util, NEVER, INVALID, toZod, compile, withParser, ZodCompileAsyncError, ZodCompileUnsupportedError, getDiscriminatedOption, } from "../core/index.cjs"; +export { toJSONSchema } from "../core/json-schema-processors.cjs"; +export { fromJSONSchema } from "./from-json-schema.cjs"; +export { deepPartial } from "./deep-partial.cjs"; +export { input, output } from "./in-out.cjs"; +export * as locales from "../locales/index.cjs"; +export { ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration } from "./iso.cjs"; +export * as iso from "./iso.cjs"; +export type { ZodCoercedString, ZodCoercedNumber, ZodCoercedBigInt, ZodCoercedBoolean, ZodCoercedDate, } from "./coerce.cjs"; +export * as coerce from "./coerce.cjs"; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/external.js b/tests/fixtures/npm/node_modules/zod/v4/classic/external.js new file mode 100644 index 000000000..435be167f --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/external.js @@ -0,0 +1,17 @@ +export * as core from "../core/index.js"; +export * from "./schemas.js"; +export * from "./checks.js"; +export * from "./errors.js"; +export * from "./parse.js"; +export * from "./compat.js"; +export { globalRegistry, registry, config, memoizer, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, TimePrecision, util, NEVER, INVALID, toZod, compile, withParser, ZodCompileAsyncError, ZodCompileUnsupportedError, getDiscriminatedOption, } from "../core/index.js"; +export { toJSONSchema } from "../core/json-schema-processors.js"; +export { fromJSONSchema } from "./from-json-schema.js"; +export { deepPartial } from "./deep-partial.js"; +// Types and values both, from one module — re-exporting the types from core would collide. +export { input, output } from "./in-out.js"; +export * as locales from "../locales/index.js"; +// iso must be exported from top-level https://github.com/colinhacks/zod/issues/4491 +export { ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration } from "./iso.js"; +export * as iso from "./iso.js"; +export * as coerce from "./coerce.js"; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/from-json-schema.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/from-json-schema.d.cts new file mode 100644 index 000000000..dc028bc01 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/from-json-schema.d.cts @@ -0,0 +1,12 @@ +import type * as JSONSchema from "../core/json-schema.cjs"; +import { type $ZodRegistry } from "../core/registries.cjs"; +import type { ZodType } from "./schemas.cjs"; +type JSONSchemaVersion = "draft-2020-12" | "draft-7" | "draft-4" | "openapi-3.0"; +interface FromJSONSchemaParams { + defaultTarget?: JSONSchemaVersion; + registry?: $ZodRegistry; +} +/** + * Converts a JSON Schema to a Zod schema. This function should be considered semi-experimental. It's behavior is liable to change. */ +export declare function fromJSONSchema(schema: JSONSchema.JSONSchema | boolean, params?: FromJSONSchemaParams): ZodType; +export {}; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/from-json-schema.js b/tests/fixtures/npm/node_modules/zod/v4/classic/from-json-schema.js new file mode 100644 index 000000000..1f79b5c85 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/from-json-schema.js @@ -0,0 +1,881 @@ +import { globalRegistry } from "../core/registries.js"; +import { assignProp, isPlainObject } from "../core/util.js"; +import * as _checks from "./checks.js"; +import * as _iso from "./iso.js"; +import * as _schemas from "./schemas.js"; +// Local z object to avoid circular dependency with ../index.js +const z = { + ..._schemas, + ..._checks, + iso: _iso, +}; +// Keys that are recognized and handled by the conversion logic +const RECOGNIZED_KEYS = /*@__PURE__*/ new Set([ + // Schema identification + "$schema", + "$ref", + "$defs", + "definitions", + // Core schema keywords + "$id", + "id", + "$comment", + "$anchor", + "$vocabulary", + "$dynamicRef", + "$dynamicAnchor", + // Type + "type", + "enum", + "const", + // Composition + "anyOf", + "oneOf", + "allOf", + "not", + // Object + "properties", + "required", + "additionalProperties", + "patternProperties", + "propertyNames", + "minProperties", + "maxProperties", + // Array + "items", + "prefixItems", + "additionalItems", + "minItems", + "maxItems", + "uniqueItems", + "contains", + "minContains", + "maxContains", + // String + "minLength", + "maxLength", + "pattern", + "format", + // Number + "minimum", + "maximum", + "exclusiveMinimum", + "exclusiveMaximum", + "multipleOf", + // Already handled metadata + "description", + "default", + // Content + "contentEncoding", + "contentMediaType", + "contentSchema", + // Unsupported (error-throwing) + "unevaluatedItems", + "unevaluatedProperties", + "if", + "then", + "else", + "dependentSchemas", + "dependentRequired", + // OpenAPI + "nullable", + "readOnly", +]); +function detectVersion(schema, defaultTarget) { + const $schema = schema.$schema; + if ($schema === "https://json-schema.org/draft/2020-12/schema") { + return "draft-2020-12"; + } + if ($schema === "http://json-schema.org/draft-07/schema#") { + return "draft-7"; + } + if ($schema === "http://json-schema.org/draft-04/schema#") { + return "draft-4"; + } + // Use defaultTarget if provided, otherwise default to draft-2020-12 + return defaultTarget ?? "draft-2020-12"; +} +// Positional schemas constrain the elements that are present; only minItems makes them required. +function applyMinItems(items, minItems) { + return items.map((item, index) => (index < minItems ? item : item.optional())); +} +// Inverse of the encoding applied to $ref pointer segments in to-json-schema.ts. Per RFC 6901 the `~1` replacement must run before `~0`. +function decodeJSONPointerSegment(segment) { + return segment.replace(/~1/g, "/").replace(/~0/g, "~"); +} +function resolveRef(ref, ctx) { + if (!ref.startsWith("#")) { + throw new Error("External $ref is not supported, only local refs (#/...) are allowed"); + } + const path = ref.slice(1).split("/").filter(Boolean); + // Handle root reference "#" + if (path.length === 0) { + return ctx.rootSchema; + } + const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions"; + if (path[0] === defsKey) { + const key = path[1] === undefined ? undefined : decodeJSONPointerSegment(path[1]); + if (!key || !ctx.defs[key]) { + throw new Error(`Reference not found: ${ref}`); + } + return ctx.defs[key]; + } + throw new Error(`Reference not found: ${ref}`); +} +/** + * Enforces the keywords that constrain an object's own keys — `propertyNames`, + * `minProperties`, `maxProperties` — before `objectSchema` runs. The guard has + * to see the raw input: an object parse drops `__proto__` and can add keys from + * a property `default`, so its output is not the set of names the instance + * actually carried. + */ +function checkObjectGuards(objectSchema, guards) { + // An identity transform, not z.any(), so `toJSONSchema` reports the object on both the input and the output side of the pipe. + const guard = z + .transform((value) => value) + .check((payload) => { + const value = payload.value; + if (typeof value !== "object" || value === null || Array.isArray(value)) + return; + const keys = Object.getOwnPropertyNames(value); + if (guards.minProperties !== undefined && keys.length < guards.minProperties) { + payload.issues.push({ + origin: "object", + code: "too_small", + minimum: guards.minProperties, + inclusive: true, + message: `Too small: expected object to have >=${guards.minProperties} properties`, + input: value, + inst: objectSchema, + continue: true, + }); + } + if (guards.maxProperties !== undefined && keys.length > guards.maxProperties) { + payload.issues.push({ + origin: "object", + code: "too_big", + maximum: guards.maxProperties, + inclusive: true, + message: `Too big: expected object to have <=${guards.maxProperties} properties`, + input: value, + inst: objectSchema, + continue: true, + }); + } + if (guards.keySchema) { + for (const key of keys) { + const result = guards.keySchema.safeParse(key); + if (result.success) + continue; + payload.issues.push({ + code: "invalid_key", + origin: "record", + issues: result.error.issues, + input: key, + path: [key], + continue: true, + }); + } + } + }); + return guard.pipe(objectSchema); +} +/** + * An injective string for JSON data: two values share a key exactly when `uniqueItems` calls them + * equal, so duplicate detection is a Map lookup rather than a pairwise walk. Strings and keys are + * length-prefixed so no value can spell another one. `null` means "never equal to anything" — a + * cycle or a NaN, neither of which JSON can express. + */ +function canonicalKey(value, seen) { + if (value === null) + return "z"; + const type = typeof value; + if (type !== "object") { + if (type === "number" && Number.isNaN(value)) + return null; + const raw = String(value); + return `${type[0]}${raw.length}:${raw}`; + } + if (seen.has(value)) + return null; + seen.add(value); + try { + if (Array.isArray(value)) { + const parts = []; + for (const item of value) { + const key = canonicalKey(item, seen); + if (key === null) + return null; + parts.push(key); + } + return `a${parts.length}:[${parts.join(",")}]`; + } + const keys = Object.keys(value).sort(); + const parts = []; + for (const k of keys) { + const key = canonicalKey(value[k], seen); + if (key === null) + return null; + parts.push(`${k.length}:${k}=${key}`); + } + return `o${parts.length}:{${parts.join(",")}}`; + } + finally { + seen.delete(value); + } +} +// keywords whose value is a schema or an array of them +const SCHEMA_KEYWORDS = /*@__PURE__*/ new Set([ + "items", + "prefixItems", + "additionalItems", + "additionalProperties", + "contains", + "propertyNames", + "not", + "if", + "then", + "else", + "allOf", + "anyOf", + "oneOf", + "unevaluatedItems", + "unevaluatedProperties", + "contentSchema", +]); +// keywords whose value maps names to schemas. draft-7 `dependencies` also allows an array of property names, which holds no schema to walk +const SCHEMA_MAP_KEYWORDS = /*@__PURE__*/ new Set([ + "properties", + "patternProperties", + "dependentSchemas", + "dependencies", + "$defs", + "definitions", +]); +/** + * True when a subschema holds a `$ref` anywhere a schema can appear. Only those positions are + * walked: `$ref` under `default` or an `x-` annotation is instance data with an ordinary key, and + * reading it as a reference would drop a constraint that resolves perfectly well. + */ +function containsRef(value) { + if (typeof value !== "object" || value === null) + return false; + if (Array.isArray(value)) + return value.some(containsRef); + if (typeof value.$ref === "string") + return true; + return Object.entries(value).some(([key, sub]) => { + if (SCHEMA_KEYWORDS.has(key)) + return containsRef(sub); + if (!SCHEMA_MAP_KEYWORDS.has(key) || typeof sub !== "object" || sub === null) + return false; + return Object.values(sub).some(containsRef); + }); +} +function plural(n) { + return n === 1 ? "element" : "elements"; +} +/** + * Enforces `uniqueItems` and the `contains` family before `arraySchema` runs, for the same reason + * `checkObjectGuards` runs pre-pipe: an item parse can apply a nested `default`, so the parsed + * array is not the instance the keywords are defined over. A guard issue aborts the pipe, so + * `minItems`/`maxItems` are not also reported on the same parse — instance correctness is worth + * more than co-reporting two issues. + */ +function checkArrayGuards(arraySchema, guards) { + // An identity transform, not z.any(), so `toJSONSchema` reports the array on both the input and the output side of the pipe. + const guard = z + .transform((value) => value) + .check((payload) => { + const items = payload.value; + if (!Array.isArray(items)) + return; + if (guards.uniqueItems === true) { + const firstSeen = new Map(); + for (let i = 0; i < items.length; i++) { + const key = canonicalKey(items[i], new Set()); + if (key === null) + continue; + const first = firstSeen.get(key); + if (first === undefined) { + firstSeen.set(key, i); + continue; + } + payload.issues.push({ + code: "custom", + message: `Array items must be unique: element at index ${i} duplicates the one at index ${first}`, + input: items, + path: [i], + continue: true, + }); + } + } + if (guards.containsSchema) { + const minContains = guards.minContains ?? 1; + // one past the ceiling already proves the failure, and nothing above it changes the verdict + const ceiling = guards.maxContains !== undefined ? guards.maxContains + 1 : Number.POSITIVE_INFINITY; + let matches = 0; + for (const item of items) { + if (guards.containsSchema.safeParse(item).success && ++matches >= ceiling) + break; + } + if (matches < minContains) { + // the scan ran to the end, so this count is exact + payload.issues.push({ + code: "custom", + message: `Array must contain at least ${minContains} matching ${plural(minContains)}; found ${matches}`, + input: items, + continue: true, + }); + } + if (guards.maxContains !== undefined && matches > guards.maxContains) { + payload.issues.push({ + code: "custom", + message: `Array must contain at most ${guards.maxContains} matching ${plural(guards.maxContains)}`, + input: items, + continue: true, + }); + } + } + }); + return guard.pipe(arraySchema); +} +function getTupleRest(restSchema, ctx) { + if (restSchema === false) { + return undefined; + } + if (restSchema === undefined || restSchema === true) { + return z.any(); + } + return convertSchema(restSchema, ctx); +} +// the RFC 3339 full-time keyword, narrower than `z.iso.time()`; local because sharing it via `timeSource` pins that builder into every bundle (+139 B gzipped on a mini `z.boolean()`) +const fullTime = /^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/; +function convertBaseSchema(schema, ctx) { + // Handle unsupported features + if (schema.not !== undefined) { + // Special case: { not: {} } represents never + if (typeof schema.not === "object" && Object.keys(schema.not).length === 0) { + return z.never(); + } + throw new Error("not is not supported in Zod (except { not: {} } for never)"); + } + if (schema.unevaluatedItems !== undefined) { + throw new Error("unevaluatedItems is not supported"); + } + if (schema.unevaluatedProperties !== undefined) { + throw new Error("unevaluatedProperties is not supported"); + } + if (schema.if !== undefined || schema.then !== undefined || schema.else !== undefined) { + throw new Error("Conditional schemas (if/then/else) are not supported"); + } + if (schema.dependentSchemas !== undefined || schema.dependentRequired !== undefined) { + throw new Error("dependentSchemas and dependentRequired are not supported"); + } + // Handle $ref + if (schema.$ref) { + const refPath = schema.$ref; + if (ctx.refs.has(refPath)) { + return ctx.refs.get(refPath); + } + if (ctx.processing.has(refPath)) { + // Circular reference - use lazy + return z.lazy(() => { + if (!ctx.refs.has(refPath)) { + throw new Error(`Circular reference not resolved: ${refPath}`); + } + return ctx.refs.get(refPath); + }); + } + ctx.processing.add(refPath); + const resolved = resolveRef(refPath, ctx); + const zodSchema = convertSchema(resolved, ctx); + ctx.refs.set(refPath, zodSchema); + ctx.processing.delete(refPath); + return zodSchema; + } + // Handle enum + if (schema.enum !== undefined) { + const enumValues = schema.enum; + // Special case: OpenAPI 3.0 null representation { type: "string", nullable: true, enum: [null] } + if (ctx.version === "openapi-3.0" && + schema.nullable === true && + enumValues.length === 1 && + enumValues[0] === null) { + return z.null(); + } + if (enumValues.length === 0) { + return z.never(); + } + if (enumValues.length === 1) { + return z.literal(enumValues[0]); + } + // Check if all values are strings + if (enumValues.every((v) => typeof v === "string")) { + return z.enum(enumValues); + } + // Mixed types - use union of literals + const literalSchemas = enumValues.map((v) => z.literal(v)); + if (literalSchemas.length < 2) { + return literalSchemas[0]; + } + return z.union([literalSchemas[0], literalSchemas[1], ...literalSchemas.slice(2)]); + } + // Handle const + if (schema.const !== undefined) { + return z.literal(schema.const); + } + // Handle type + const type = schema.type; + if (Array.isArray(type)) { + // Expand type array into anyOf union + const typeSchemas = type.map((t) => { + const typeSchema = { ...schema, type: t }; + return convertBaseSchema(typeSchema, ctx); + }); + if (typeSchemas.length === 0) { + return z.never(); + } + if (typeSchemas.length === 1) { + return typeSchemas[0]; + } + return z.union(typeSchemas); + } + if (!type) { + // No type specified - empty schema (any) + return z.any(); + } + let zodSchema; + switch (type) { + case "string": { + let stringSchema = z.string(); + // Apply format using .check() with Zod format functions + if (schema.format) { + const format = schema.format; + // Map common formats to Zod check functions + if (format === "email") { + stringSchema = stringSchema.check(z.email()); + } + else if (format === "uri" || format === "uri-reference") { + stringSchema = stringSchema.check(z.url()); + } + else if (format === "uuid" || format === "guid") { + stringSchema = stringSchema.check(z.uuid()); + } + else if (format === "date-time") { + stringSchema = stringSchema.check(z.iso.datetime({ offset: true })); + } + else if (format === "date") { + stringSchema = stringSchema.check(z.iso.date()); + } + else if (format === "time") { + stringSchema = stringSchema.check(z.regex(fullTime)); + } + else if (format === "duration") { + stringSchema = stringSchema.check(z.iso.duration()); + } + else if (format === "hostname") { + stringSchema = stringSchema.check(z.hostname()); + } + else if (format === "ipv4") { + stringSchema = stringSchema.check(z.ipv4()); + } + else if (format === "ipv6") { + stringSchema = stringSchema.check(z.ipv6()); + } + else if (format === "mac") { + stringSchema = stringSchema.check(z.mac()); + } + else if (format === "cidr") { + stringSchema = stringSchema.check(z.cidrv4()); + } + else if (format === "cidr-v6") { + stringSchema = stringSchema.check(z.cidrv6()); + } + else if (format === "base64") { + stringSchema = stringSchema.check(z.base64()); + } + else if (format === "base64url") { + stringSchema = stringSchema.check(z.base64url()); + } + else if (format === "e164") { + stringSchema = stringSchema.check(z.e164()); + } + else if (format === "credit_card") { + stringSchema = stringSchema.check(z.creditCard()); + } + else if (format === "iban") { + stringSchema = stringSchema.check(z.iban()); + } + else if (format === "jwt") { + stringSchema = stringSchema.check(z.jwt()); + } + else if (format === "emoji") { + stringSchema = stringSchema.check(z.emoji()); + } + else if (format === "nanoid") { + stringSchema = stringSchema.check(z.nanoid()); + } + else if (format === "cuid") { + stringSchema = stringSchema.check(z.cuid()); + } + else if (format === "cuid2") { + stringSchema = stringSchema.check(z.cuid2()); + } + else if (format === "ulid") { + stringSchema = stringSchema.check(z.ulid()); + } + else if (format === "xid") { + stringSchema = stringSchema.check(z.xid()); + } + else if (format === "ksuid") { + stringSchema = stringSchema.check(z.ksuid()); + } + // Note: json-string format is not currently supported by Zod + // Custom formats are ignored - keep as plain string + } + // Apply constraints + if (typeof schema.minLength === "number") { + stringSchema = stringSchema.min(schema.minLength); + } + if (typeof schema.maxLength === "number") { + stringSchema = stringSchema.max(schema.maxLength); + } + if (schema.pattern) { + // JSON Schema patterns are not implicitly anchored (match anywhere in string) + stringSchema = stringSchema.regex(new RegExp(schema.pattern)); + } + zodSchema = stringSchema; + break; + } + case "number": + case "integer": { + let numberSchema = type === "integer" ? z.number().int() : z.number(); + // Apply constraints + // In draft-04, `exclusiveMinimum: true` makes the sibling `minimum` exclusive rather than an independent bound, so the inclusive `.min()` is skipped; emitting it too would be dominated by the exclusive check and report a second, weaker issue. + if (typeof schema.minimum === "number" && schema.exclusiveMinimum !== true) { + numberSchema = numberSchema.min(schema.minimum); + } + if (typeof schema.maximum === "number" && schema.exclusiveMaximum !== true) { + numberSchema = numberSchema.max(schema.maximum); + } + if (typeof schema.exclusiveMinimum === "number") { + numberSchema = numberSchema.gt(schema.exclusiveMinimum); + } + else if (schema.exclusiveMinimum === true && typeof schema.minimum === "number") { + numberSchema = numberSchema.gt(schema.minimum); + } + if (typeof schema.exclusiveMaximum === "number") { + numberSchema = numberSchema.lt(schema.exclusiveMaximum); + } + else if (schema.exclusiveMaximum === true && typeof schema.maximum === "number") { + numberSchema = numberSchema.lt(schema.maximum); + } + if (typeof schema.multipleOf === "number") { + numberSchema = numberSchema.multipleOf(schema.multipleOf); + } + zodSchema = numberSchema; + break; + } + case "boolean": { + zodSchema = z.boolean(); + break; + } + case "null": { + zodSchema = z.null(); + break; + } + case "object": { + const shape = {}; + const properties = schema.properties || {}; + const requiredSet = new Set(schema.required || []); + const additionalSchema = typeof schema.additionalProperties === "object" + ? convertSchema(schema.additionalProperties, ctx) + : undefined; + // Convert properties - mark optional ones + for (const [key, propSchema] of Object.entries(properties)) { + const propZodSchema = convertSchema(propSchema, ctx); + // If not in required array, make it optional. assignProp so a __proto__ key becomes an own property instead of hitting the inherited setter + assignProp(shape, key, requiredSet.has(key) ? propZodSchema : propZodSchema.optional()); + } + // Handle patternProperties + if (schema.patternProperties) { + // patternProperties: keys matching pattern must satisfy corresponding schema. Use loose records so non-matching keys pass through + const patternProps = schema.patternProperties; + const patternKeys = Object.keys(patternProps); + const looseRecords = []; + for (const pattern of patternKeys) { + const patternValue = convertSchema(patternProps[pattern], ctx); + const keySchema = z.string().regex(new RegExp(pattern)); + looseRecords.push(z.looseRecord(keySchema, patternValue)); + } + // Build intersection: object schema + all pattern property records + const schemasToIntersect = []; + if (Object.keys(shape).length > 0) { + // Use passthrough so patternProperties can validate additional keys + schemasToIntersect.push(z.object(shape).passthrough()); + } + schemasToIntersect.push(...looseRecords); + if (schemasToIntersect.length === 0) { + zodSchema = z.object({}).passthrough(); + } + else if (schemasToIntersect.length === 1) { + zodSchema = schemasToIntersect[0]; + } + else { + // Chain intersections: (A & B) & C & D ... + let result = z.intersection(schemasToIntersect[0], schemasToIntersect[1]); + for (let i = 2; i < schemasToIntersect.length; i++) { + result = z.intersection(result, schemasToIntersect[i]); + } + zodSchema = result; + } + // When additionalProperties is false, reject keys that are neither defined in properties nor matched by any patternProperty. + if (schema.additionalProperties === false) { + const propertyKeys = Object.keys(shape); + const patterns = patternKeys.map((p) => new RegExp(p)); + const basePatternSchema = zodSchema; + zodSchema = zodSchema.check((payload) => { + if (!isPlainObject(payload.value)) + return; + const unrecognized = []; + for (const key of Object.keys(payload.value)) { + if (propertyKeys.includes(key)) + continue; + if (patterns.some((regex) => regex.test(key))) + continue; + unrecognized.push(key); + } + if (unrecognized.length) { + payload.issues.push({ + code: "unrecognized_keys", + keys: unrecognized, + input: payload.value, + inst: basePatternSchema, + }); + } + }); + } + } + else { + // Handle additionalProperties. In JSON Schema, additionalProperties defaults to true (allow any extra properties). In Zod, objects strip unknown keys by default, so we need to handle this explicitly + const objectSchema = z.object(shape); + if (schema.additionalProperties === false) { + // Strict mode - no extra properties allowed + zodSchema = objectSchema.strict(); + } + else if (additionalSchema) { + // Extra properties must match the specified schema + zodSchema = objectSchema.catchall(additionalSchema); + } + else { + // additionalProperties is true or undefined - allow any extra properties (passthrough) + zodSchema = objectSchema.passthrough(); + } + } + // propertyNames constrains key *names* only, and says nothing about which keys are required or how their values validate. Layering it on top of the result keeps properties/patternProperties/additionalProperties composing underneath. `true` allows every name, so it needs no guard. + const hasKeyGuard = schema.propertyNames !== undefined && schema.propertyNames !== true; + const minProperties = typeof schema.minProperties === "number" ? schema.minProperties : undefined; + const maxProperties = typeof schema.maxProperties === "number" ? schema.maxProperties : undefined; + if (hasKeyGuard || minProperties !== undefined || maxProperties !== undefined) { + let keySchema; + if (hasKeyGuard) { + // Keys are always strings, so a propertyNames subschema that omits `type` still constrains them — without this it would convert to z.any(). + const keyJSONSchema = typeof schema.propertyNames === "object" && schema.propertyNames.type === undefined + ? { type: "string", ...schema.propertyNames } + : schema.propertyNames; + keySchema = convertSchema(keyJSONSchema, ctx); + } + zodSchema = checkObjectGuards(zodSchema, { keySchema, minProperties, maxProperties }); + } + break; + } + case "array": { + // Check if this is a tuple (prefixItems or items as array) + const prefixItems = schema.prefixItems; + const items = schema.items; + if (prefixItems && Array.isArray(prefixItems)) { + // Tuple with prefixItems (draft-2020-12) + const minItems = typeof schema.minItems === "number" ? schema.minItems : 0; + const tupleItems = prefixItems.map((item) => convertSchema(item, ctx)); + const positionalItems = applyMinItems(tupleItems, minItems); + const rest = !Array.isArray(items) ? getTupleRest(items, ctx) : undefined; + const tupleSchema = z.tuple(positionalItems); + zodSchema = rest ? tupleSchema.rest(rest) : tupleSchema; + // Apply minItems/maxItems constraints to tuples + if (typeof schema.minItems === "number") { + zodSchema = zodSchema.check(z.minLength(schema.minItems)); + } + if (typeof schema.maxItems === "number") { + zodSchema = zodSchema.check(z.maxLength(schema.maxItems)); + } + } + else if (Array.isArray(items)) { + // Tuple with items array (draft-7) + const minItems = typeof schema.minItems === "number" ? schema.minItems : 0; + const tupleItems = items.map((item) => convertSchema(item, ctx)); + const positionalItems = applyMinItems(tupleItems, minItems); + const rest = getTupleRest(schema.additionalItems, ctx); + const tupleSchema = z.tuple(positionalItems); + zodSchema = rest ? tupleSchema.rest(rest) : tupleSchema; + // Apply minItems/maxItems constraints to tuples + if (typeof schema.minItems === "number") { + zodSchema = zodSchema.check(z.minLength(schema.minItems)); + } + if (typeof schema.maxItems === "number") { + zodSchema = zodSchema.check(z.maxLength(schema.maxItems)); + } + } + else if (items !== undefined) { + // Regular array + const element = convertSchema(items, ctx); + let arraySchema = z.array(element); + // Apply constraints + if (typeof schema.minItems === "number") { + arraySchema = arraySchema.min(schema.minItems); + } + if (typeof schema.maxItems === "number") { + arraySchema = arraySchema.max(schema.maxItems); + } + zodSchema = arraySchema; + } + else { + // No items specified - array of any + zodSchema = z.array(z.any()); + } + // minContains/maxContains only constrain anything when `contains` itself is present + if (schema.uniqueItems === true || schema.contains !== undefined) { + zodSchema = checkArrayGuards(zodSchema, { + uniqueItems: schema.uniqueItems === true, + containsSchema: schema.contains !== undefined ? convertSchema(schema.contains, ctx) : undefined, + minContains: typeof schema.minContains === "number" ? schema.minContains : undefined, + maxContains: typeof schema.maxContains === "number" ? schema.maxContains : undefined, + }); + } + break; + } + default: + throw new Error(`Unsupported type: ${type}`); + } + return zodSchema; +} +function convertSchema(schema, ctx) { + if (typeof schema === "boolean") { + return schema ? z.any() : z.never(); + } + // Convert base schema first (ignoring composition keywords) + let baseSchema = convertBaseSchema(schema, ctx); + const hasExplicitType = schema.type || schema.enum !== undefined || schema.const !== undefined; + // Process composition keywords LAST (they can appear together) + // Handle anyOf - wrap base schema with union + if (schema.anyOf && Array.isArray(schema.anyOf)) { + const options = schema.anyOf.map((s) => convertSchema(s, ctx)); + const anyOfUnion = z.union(options); + baseSchema = hasExplicitType ? z.intersection(baseSchema, anyOfUnion) : anyOfUnion; + } + // Handle oneOf - exclusive union (exactly one must match) + if (schema.oneOf && Array.isArray(schema.oneOf)) { + const options = schema.oneOf.map((s) => convertSchema(s, ctx)); + const oneOfUnion = z.xor(options); + baseSchema = hasExplicitType ? z.intersection(baseSchema, oneOfUnion) : oneOfUnion; + } + // Handle allOf - wrap base schema with intersection + if (schema.allOf && Array.isArray(schema.allOf)) { + if (schema.allOf.length === 0) { + baseSchema = hasExplicitType ? baseSchema : z.any(); + } + else { + let result = hasExplicitType ? baseSchema : convertSchema(schema.allOf[0], ctx); + const startIdx = hasExplicitType ? 0 : 1; + for (let i = startIdx; i < schema.allOf.length; i++) { + result = z.intersection(result, convertSchema(schema.allOf[i], ctx)); + } + baseSchema = result; + } + } + // Handle nullable (OpenAPI 3.0) + if (schema.nullable === true && ctx.version === "openapi-3.0") { + baseSchema = z.nullable(baseSchema); + } + // Handle readOnly + if (schema.readOnly === true) { + baseSchema = z.readonly(baseSchema); + } + // Apply `default` so it wraps the fully-composed schema. This ensures `parse(undefined) -> default` works regardless of which branch of `convertBaseSchema` produced the inner schema (enum/const/not/typed/etc.). + if (schema.default !== undefined) { + baseSchema = baseSchema.default(schema.default); + } + // Collect non-description annotation metadata into the user-supplied registry. Description is handled separately below via `.describe()` to preserve the contract that `schema.description` reads from globalRegistry. + const extraMeta = {}; + const coreMetadataKeys = ["$id", "id", "$comment", "$anchor", "$vocabulary", "$dynamicRef", "$dynamicAnchor"]; + for (const key of coreMetadataKeys) { + if (key in schema) { + extraMeta[key] = schema[key]; + } + } + const contentMetadataKeys = ["contentEncoding", "contentMediaType", "contentSchema"]; + for (const key of contentMetadataKeys) { + if (key in schema) { + extraMeta[key] = schema[key]; + } + } + // `propertyNames` and `contains` are enforced by a guard, which `toJSONSchema` cannot infer, so the original keyword is carried as metadata to keep the round-trip lossless. Only where it was actually applied: on any other type it is inert, and on a `$ref` the metadata would land on the target every reference shares. A carried subschema is copied verbatim while the root `$defs` stay behind, so one holding a `$ref` is dropped rather than emitted as a dangling reference. + if (schema.type === "object" && schema.$ref === undefined) { + if (schema.propertyNames !== undefined && !containsRef(schema.propertyNames)) { + extraMeta.propertyNames = schema.propertyNames; + } + for (const key of ["minProperties", "maxProperties"]) { + if (schema[key] !== undefined) + extraMeta[key] = schema[key]; + } + } + if (schema.type === "array" && schema.$ref === undefined) { + if (schema.contains !== undefined && !containsRef(schema.contains)) { + extraMeta.contains = schema.contains; + } + for (const key of ["uniqueItems", "minContains", "maxContains"]) { + if (schema[key] !== undefined) + extraMeta[key] = schema[key]; + } + } + for (const key of Object.keys(schema)) { + if (!RECOGNIZED_KEYS.has(key)) { + assignProp(extraMeta, key, schema[key]); + } + } + if (Object.keys(extraMeta).length > 0) { + ctx.registry.add(baseSchema, extraMeta); + } + // Apply description last. `.describe()` clones the schema and sets `_zod.parent` on the clone, so registry lookups on the returned reference still resolve `extraMeta` via parent inheritance. + if (schema.description) { + baseSchema = baseSchema.describe(schema.description); + } + return baseSchema; +} +/** + * Converts a JSON Schema to a Zod schema. This function should be considered semi-experimental. It's behavior is liable to change. */ +export function fromJSONSchema(schema, params) { + // Handle boolean schemas + if (typeof schema === "boolean") { + return schema ? z.any() : z.never(); + } + // Normalize input via a JSON round-trip. This guarantees the converter walks a plain, finite, JSON-valid object graph: cyclic inputs fail here, getter/Proxy-based properties are materialized into static values, and class instances collapse to plain objects. + let normalized; + try { + normalized = JSON.parse(JSON.stringify(schema)); + } + catch { + throw new Error("fromJSONSchema input is not valid JSON (possibly cyclic); use $defs/$ref for recursive schemas"); + } + const version = detectVersion(normalized, params?.defaultTarget); + const defs = (normalized.$defs || normalized.definitions || {}); + const ctx = { + version, + defs, + refs: new Map(), + processing: new Set(), + rootSchema: normalized, + registry: params?.registry ?? globalRegistry, + }; + return convertSchema(normalized, ctx); +} diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/in-out.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/in-out.d.cts new file mode 100644 index 000000000..09b530fe2 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/in-out.d.cts @@ -0,0 +1,8 @@ +import type * as core from "../core/index.cjs"; +import * as schemas from "./schemas.cjs"; +export type input = core.input; +export type output = core.output; +/** Returns a copy of the schema with every pipe replaced by its input side. A codec's checks are dropped: they constrain the decoded value the input side never produces. */ +export declare function input(schema: T): schemas.ZodType, core.input>; +/** Returns a copy of the schema with every pipe replaced by its output side, carrying over the pipe's own checks. */ +export declare function output(schema: T): schemas.ZodType, core.output>; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/in-out.js b/tests/fixtures/npm/node_modules/zod/v4/classic/in-out.js new file mode 100644 index 000000000..4a0002688 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/in-out.js @@ -0,0 +1,36 @@ +import { clone, mergeDefs } from "../core/util.js"; +import { visit } from "../core/visit.js"; +import * as schemas from "./schemas.js"; +/** Appends a pipe's own checks to the side that replaces it, mirroring how `.check()` clones. */ +function withChecks(side, checks) { + if (!checks?.length) + return side; + const def = side._zod.def; + return clone(side, mergeDefs(def, { checks: [...(def.checks ?? []), ...checks] }), { parent: true }); +} +/** The out side, carrying the pipe's own checks: those run against the decoded value, which is what `out` produces. */ +function outSide(def) { + return withChecks(def.out, def.checks); +} +/** The in side. `z.preprocess` pipes a transform into a schema, and a bare transform validates nothing, so the schema it feeds is the real input side — the resolution `toJSONSchema` already makes for this case. */ +function inSide(def) { + return def.in._zod.traits.has("$ZodTransform") ? outSide(def) : def.in; +} +/** Returns a copy of the schema with every pipe replaced by its input side. A codec's checks are dropped: they constrain the decoded value the input side never produces. */ +export function input(schema) { + return visit(schema, { + pipe: (s) => inSide(s._zod.def), + // A default value belongs to the output side, so a rewritten inner type leaves it stranded. `.default()` widens the declared input type with `undefined`, and `optional` is what carries that across. + default: (s, rewritten) => (rewritten ? schemas.optional(s._zod.def.innerType) : s), + // A catch value is output-side too, but `.catch()` leaves the declared input type alone, so the inner schema stands on its own. + catch: (s, rewritten) => (rewritten ? s._zod.def.innerType : s), + }); +} +/** Returns a copy of the schema with every pipe replaced by its output side, carrying over the pipe's own checks. */ +export function output(schema) { + return visit(schema, { + pipe: (s) => outSide(s._zod.def), + // A prefault value is fed through the schema, which makes it input-side, so a rewritten inner type leaves it stranded. + prefault: (s, rewritten) => (rewritten ? s._zod.def.innerType : s), + }); +} diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/index.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/index.d.cts new file mode 100644 index 000000000..b5fc2a17b --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/index.d.cts @@ -0,0 +1,3 @@ +import * as z from "./external.cjs"; +export * from "./external.cjs"; +export { z, z as default }; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/iso.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/iso.d.cts new file mode 100644 index 000000000..db764d9f8 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/iso.d.cts @@ -0,0 +1,7 @@ +import * as core from "../core/index.cjs"; +import { ZodISODate, ZodISODateTime, ZodISODuration, ZodISOTime } from "./schemas.cjs"; +export { ZodISODate, ZodISODateTime, ZodISODuration, ZodISOTime } from "./schemas.cjs"; +export declare function datetime(params?: string | core.$ZodISODateTimeParams): ZodISODateTime; +export declare function date(params?: string | core.$ZodISODateParams): ZodISODate; +export declare function time(params?: string | core.$ZodISOTimeParams): ZodISOTime; +export declare function duration(params?: string | core.$ZodISODurationParams): ZodISODuration; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/iso.js b/tests/fixtures/npm/node_modules/zod/v4/classic/iso.js new file mode 100644 index 000000000..d38f24ddd --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/iso.js @@ -0,0 +1,15 @@ +import * as core from "../core/index.js"; +import { ZodISODate, ZodISODateTime, ZodISODuration, ZodISOTime } from "./schemas.js"; +export { ZodISODate, ZodISODateTime, ZodISODuration, ZodISOTime } from "./schemas.js"; +export function datetime(params) { + return core._isoDateTime(ZodISODateTime, params); +} +export function date(params) { + return core._isoDate(ZodISODate, params); +} +export function time(params) { + return core._isoTime(ZodISOTime, params); +} +export function duration(params) { + return core._isoDuration(ZodISODuration, params); +} diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/parse.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/parse.d.cts new file mode 100644 index 000000000..408566b52 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/parse.d.cts @@ -0,0 +1,44 @@ +import * as core from "../core/index.cjs"; +import { type ZodError } from "./errors.cjs"; +export type ZodSafeParseResult = ZodSafeParseSuccess | ZodSafeParseError; +export type ZodSafeParseSuccess = { + success: true; + data: T; + error?: never; +}; +export type ZodSafeParseError = { + success: false; + data?: never; + error: ZodError; +}; +export declare const parse: (schema: T, value: unknown, _ctx?: core.ParseContext, _params?: { + callee?: core.util.AnyFunc; + Err?: core.$ZodErrorClass; +}) => core.output; +export declare const parseAsync: (schema: T, value: unknown, _ctx?: core.ParseContext, _params?: { + callee?: core.util.AnyFunc; + Err?: core.$ZodErrorClass; +}) => Promise>; +export declare const safeParse: (schema: T, value: unknown, _ctx?: core.ParseContext) => ZodSafeParseResult>; +export declare const safeParseAsync: (schema: T, value: unknown, _ctx?: core.ParseContext) => Promise>>; +export { validate, validateAsync } from "../core/index.cjs"; +export declare const encode: (schema: T, value: core.output, _ctx?: core.ParseContext, _params?: { + callee?: core.util.AnyFunc; + Err?: core.$ZodErrorClass; +}) => core.input; +export declare const decode: (schema: T, value: core.input, _ctx?: core.ParseContext, _params?: { + callee?: core.util.AnyFunc; + Err?: core.$ZodErrorClass; +}) => core.output; +export declare const encodeAsync: (schema: T, value: core.output, _ctx?: core.ParseContext, _params?: { + callee?: core.util.AnyFunc; + Err?: core.$ZodErrorClass; +}) => Promise>; +export declare const decodeAsync: (schema: T, value: core.input, _ctx?: core.ParseContext, _params?: { + callee?: core.util.AnyFunc; + Err?: core.$ZodErrorClass; +}) => Promise>; +export declare const safeEncode: (schema: T, value: core.output, _ctx?: core.ParseContext) => ZodSafeParseResult>; +export declare const safeDecode: (schema: T, value: core.input, _ctx?: core.ParseContext) => ZodSafeParseResult>; +export declare const safeEncodeAsync: (schema: T, value: core.output, _ctx?: core.ParseContext) => Promise>>; +export declare const safeDecodeAsync: (schema: T, value: core.input, _ctx?: core.ParseContext) => Promise>>; diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/parse.js b/tests/fixtures/npm/node_modules/zod/v4/classic/parse.js new file mode 100644 index 000000000..514aacc64 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/parse.js @@ -0,0 +1,16 @@ +import * as core from "../core/index.js"; +import { ZodRealError } from "./errors.js"; +export const parse = /* @__PURE__ */ core._parse(ZodRealError); +export const parseAsync = /* @__PURE__ */ core._parseAsync(ZodRealError); +export const safeParse = /* @__PURE__ */ core._safeParse(ZodRealError); +export const safeParseAsync = /* @__PURE__ */ core._safeParseAsync(ZodRealError); +export { validate, validateAsync } from "../core/index.js"; +// Codec functions +export const encode = /* @__PURE__ */ core._encode(ZodRealError); +export const decode = /* @__PURE__ */ core._decode(ZodRealError); +export const encodeAsync = /* @__PURE__ */ core._encodeAsync(ZodRealError); +export const decodeAsync = /* @__PURE__ */ core._decodeAsync(ZodRealError); +export const safeEncode = /* @__PURE__ */ core._safeEncode(ZodRealError); +export const safeDecode = /* @__PURE__ */ core._safeDecode(ZodRealError); +export const safeEncodeAsync = /* @__PURE__ */ core._safeEncodeAsync(ZodRealError); +export const safeDecodeAsync = /* @__PURE__ */ core._safeDecodeAsync(ZodRealError); diff --git a/tests/fixtures/npm/node_modules/zod/v4/classic/schemas.d.cts b/tests/fixtures/npm/node_modules/zod/v4/classic/schemas.d.cts new file mode 100644 index 000000000..226a3ae25 --- /dev/null +++ b/tests/fixtures/npm/node_modules/zod/v4/classic/schemas.d.cts @@ -0,0 +1,816 @@ +import type { $ZodBigIntFormats } from "../core/checks.cjs"; +import * as core from "../core/index.cjs"; +import { util, type $ZodNumberFormats } from "../core/index.cjs"; +import type { StandardSchemaWithJSONProps } from "../core/standard-schema.cjs"; +import * as parse from "./parse.cjs"; +export type ZodStandardSchemaWithJSON = StandardSchemaWithJSONProps, core.output>; +export interface ZodType = core.$ZodTypeInternals> extends core.$ZodType { + def: Internals["def"]; + type: Internals["def"]["type"]; + /** @deprecated Use `.def` instead. */ + _def: Internals["def"]; + /** @deprecated Use `z.output` instead. */ + _output: Internals["output"]; + /** @deprecated Use `z.input` instead. */ + _input: Internals["input"]; + "~standard": ZodStandardSchemaWithJSON; + /** Converts this schema to a JSON Schema representation. */ + toJSONSchema(params?: core.ToJSONSchemaParams): core.ZodStandardJSONSchemaPayload; + check(...checks: (core.CheckFn> | core.$ZodCheck>)[]): this; + with(...checks: (core.CheckFn> | core.$ZodCheck>)[]): this; + clone(def?: Internals["def"], params?: { + parent: boolean; + }): this; + register(registry: R, ...meta: this extends R["_schema"] ? undefined extends R["_meta"] ? [core.$replace?] : [core.$replace] : ["Incompatible schema"]): this; + brand(value?: T): PropertyKey extends T ? this : core.$ZodBranded; + parse(data: unknown, params?: core.ParseContext): core.output; + safeParse(data: unknown, params?: core.ParseContext): parse.ZodSafeParseResult>; + parseAsync(data: unknown, params?: core.ParseContext): Promise>; + safeParseAsync(data: unknown, params?: core.ParseContext): Promise>>; + spa: (data: unknown, params?: core.ParseContext) => Promise>>; + validate(data: unknown, params?: core.ParseContext): data is core.input; + validateAsync(data: unknown, params?: core.ParseContext): Promise; + encode(data: core.output, params?: core.ParseContext): core.input; + decode(data: core.input, params?: core.ParseContext): core.output; + encodeAsync(data: core.output, params?: core.ParseContext): Promise>; + decodeAsync(data: core.input, params?: core.ParseContext): Promise>; + safeEncode(data: core.output, params?: core.ParseContext): parse.ZodSafeParseResult>; + safeDecode(data: core.input, params?: core.ParseContext): parse.ZodSafeParseResult>; + safeEncodeAsync(data: core.output, params?: core.ParseContext): Promise>>; + safeDecodeAsync(data: core.input, params?: core.ParseContext): Promise>>; + refine) => unknown | Promise>(check: Ch, params?: string | core.$ZodCustomParams): Ch extends (arg: any) => arg is infer R ? this & ZodType> : this; + superRefine(refinement: (arg: core.output, ctx: core.$RefinementCtx>) => void | Promise, params?: core.$ZodSuperRefineParams): this; + overwrite(fn: (x: core.output) => core.output): this; + optional(): ZodOptional; + exactOptional(): ZodExactOptional; + nonoptional(params?: string | core.$ZodNonOptionalParams): ZodNonOptional; + nullable(): ZodNullable; + nullish(): ZodOptional>; + default(def: util.NoUndefined>): ZodDefault; + default(def: () => util.NoUndefined>): ZodDefault; + prefault(def: () => core.input): ZodPrefault; + prefault(def: core.input): ZodPrefault; + array(): ZodArray; + or(option: T): ZodUnion<[this, T]>; + and(incoming: T): ZodIntersection; + transform(transform: (arg: core.output, ctx: core.$RefinementCtx>) => NewOut | Promise): ZodPipe, core.output>>; + catch(def: core.output): ZodCatch; + catch(def: (ctx: core.$ZodCatchCtx) => core.output): ZodCatch; + pipe>>(target: T | core.$ZodType>): ZodPipe; + readonly(): ZodReadonly; + /** Returns a new instance that has been registered in `z.globalRegistry` with the specified description */ + describe(description: string): this; + description?: string; + /** Returns the metadata associated with this instance in `z.globalRegistry` */ + meta(): core.$replace | undefined; + /** Returns a new instance that has been registered in `z.globalRegistry` with the specified metadata */ + meta(data: core.$replace): this; + /** @deprecated Try safe-parsing `undefined` (this is what `isOptional` does internally): + * + * ```ts + * const schema = z.string().optional(); + * const isOptional = schema.safeParse(undefined).success; // true + * ``` + */ + isOptional(): boolean; + /** + * @deprecated Try safe-parsing `null` (this is what `isNullable` does internally): + * + * ```ts + * const schema = z.string().nullable(); + * const isNullable = schema.safeParse(null).success; // true + * ``` + */ + isNullable(): boolean; + apply(fn: (schema: this, ...args: TArgs) => T, ...args: TArgs): T; +} +export interface _ZodType extends ZodType { +} +export declare const ZodType: core.$constructor; +export interface _ZodString = core.$ZodStringInternals> extends _ZodType { + format: string | null; + minLength: number | null; + maxLength: number | null; + regex(regex: RegExp, params?: string | core.$ZodCheckRegexParams): this; + includes(value: string, params?: string | core.$ZodCheckIncludesParams): this; + startsWith(value: string, params?: string | core.$ZodCheckStartsWithParams): this; + endsWith(value: string, params?: string | core.$ZodCheckEndsWithParams): this; + min(minLength: number, params?: string | core.$ZodCheckMinLengthParams): this; + max(maxLength: number, params?: string | core.$ZodCheckMaxLengthParams): this; + length(len: number, params?: string | core.$ZodCheckLengthEqualsParams): this; + nonempty(params?: string | core.$ZodCheckMinLengthParams): this; + lowercase(params?: string | core.$ZodCheckLowerCaseParams): this; + uppercase(params?: string | core.$ZodCheckUpperCaseParams): this; + trim(): this; + normalize(form?: "NFC" | "NFD" | "NFKC" | "NFKD" | (string & {})): this; + toLowerCase(): this; + toUpperCase(): this; + slugify(): this; +} +/** @internal */ +export declare const _ZodString: core.$constructor<_ZodString>; +export interface ZodString extends _ZodString> { + /** @deprecated Use `z.email()` instead. */ + email(params?: string | core.$ZodCheckEmailParams): this; + /** @deprecated Use `z.url()` instead. */ + url(params?: string | core.$ZodCheckURLParams): this; + /** @deprecated Use `z.jwt()` instead. */ + jwt(params?: string | core.$ZodCheckJWTParams): this; + /** @deprecated Use `z.emoji()` instead. */ + emoji(params?: string | core.$ZodCheckEmojiParams): this; + /** @deprecated Use `z.guid()` instead. */ + guid(params?: string | core.$ZodCheckGUIDParams): this; + /** @deprecated Use `z.uuid()` instead. */ + uuid(params?: string | core.$ZodCheckUUIDParams): this; + /** @deprecated Use `z.uuid()` instead. */ + uuidv4(params?: string | core.$ZodCheckUUIDParams): this; + /** @deprecated Use `z.uuid()` instead. */ + uuidv6(params?: string | core.$ZodCheckUUIDParams): this; + /** @deprecated Use `z.uuid()` instead. */ + uuidv7(params?: string | core.$ZodCheckUUIDParams): this; + /** @deprecated Use `z.nanoid()` instead. */ + nanoid(params?: string | core.$ZodCheckNanoIDParams): this; + /** @deprecated Use `z.guid()` instead. */ + guid(params?: string | core.$ZodCheckGUIDParams): this; + /** + * @deprecated CUID v1 is deprecated by its authors due to information leakage + * (timestamps embedded in the id). Use `z.cuid2()` instead. + * See https://github.com/paralleldrive/cuid. + */ + cuid(params?: string | core.$ZodCheckCUIDParams): this; + /** @deprecated Use `z.cuid2()` instead. */ + cuid2(params?: string | core.$ZodCheckCUID2Params): this; + /** @deprecated Use `z.ulid()` instead. */ + ulid(params?: string | core.$ZodCheckULIDParams): this; + /** @deprecated Use `z.base64()` instead. */ + base64(params?: string | core.$ZodCheckBase64Params): this; + /** @deprecated Use `z.base64url()` instead. */ + base64url(params?: string | core.$ZodCheckBase64URLParams): this; + /** @deprecated Use `z.xid()` instead. */ + xid(params?: string | core.$ZodCheckXIDParams): this; + /** @deprecated Use `z.ksuid()` instead. */ + ksuid(params?: string | core.$ZodCheckKSUIDParams): this; + /** @deprecated Use `z.ipv4()` instead. */ + ipv4(params?: string | core.$ZodCheckIPv4Params): this; + /** @deprecated Use `z.ipv6()` instead. */ + ipv6(params?: string | core.$ZodCheckIPv6Params): this; + /** @deprecated Use `z.cidrv4()` instead. */ + cidrv4(params?: string | core.$ZodCheckCIDRv4Params): this; + /** @deprecated Use `z.cidrv6()` instead. */ + cidrv6(params?: string | core.$ZodCheckCIDRv6Params): this; + /** @deprecated Use `z.e164()` instead. */ + e164(params?: string | core.$ZodCheckE164Params): this; + /** @deprecated Use `z.iso.datetime()` instead. */ + datetime(params?: string | core.$ZodCheckISODateTimeParams): this; + /** @deprecated Use `z.iso.date()` instead. */ + date(params?: string | core.$ZodCheckISODateParams): this; + /** @deprecated Use `z.iso.time()` instead. */ + time(params?: string | core.$ZodCheckISOTimeParams): this; + /** @deprecated Use `z.iso.duration()` instead. */ + duration(params?: string | core.$ZodCheckISODurationParams): this; +} +export declare const ZodString: core.$constructor; +export declare function string(params?: string | core.$ZodStringParams): ZodString; +export declare function string(params?: string | core.$ZodStringParams): core.$ZodType; +export interface ZodStringFormat extends _ZodString> { +} +export declare const ZodStringFormat: core.$constructor; +export interface ZodISODateTime extends ZodStringFormat { + _zod: core.$ZodISODateTimeInternals; +} +export declare const ZodISODateTime: core.$constructor; +export interface ZodISODate extends ZodStringFormat { + _zod: core.$ZodISODateInternals; +} +export declare const ZodISODate: core.$constructor; +export interface ZodISOTime extends ZodStringFormat { + _zod: core.$ZodISOTimeInternals; +} +export declare const ZodISOTime: core.$constructor; +export interface ZodISODuration extends ZodStringFormat { + _zod: core.$ZodISODurationInternals; +} +export declare const ZodISODuration: core.$constructor; +export interface ZodEmail extends ZodStringFormat<"email"> { + _zod: core.$ZodEmailInternals; +} +export declare const ZodEmail: core.$constructor; +export declare function email(params?: string | core.$ZodEmailParams): ZodEmail; +export interface ZodGUID extends ZodStringFormat<"guid"> { + _zod: core.$ZodGUIDInternals; +} +export declare const ZodGUID: core.$constructor; +export declare function guid(params?: string | core.$ZodGUIDParams): ZodGUID; +export interface ZodUUID extends ZodStringFormat<"uuid"> { + _zod: core.$ZodUUIDInternals; +} +export declare const ZodUUID: core.$constructor; +export declare function uuid(params?: string | core.$ZodUUIDParams): ZodUUID; +export declare function uuidv4(params?: string | core.$ZodUUIDv4Params): ZodUUID; +export declare function uuidv6(params?: string | core.$ZodUUIDv6Params): ZodUUID; +export declare function uuidv7(params?: string | core.$ZodUUIDv7Params): ZodUUID; +export interface ZodURL extends ZodStringFormat<"url"> { + _zod: core.$ZodURLInternals; +} +export declare const ZodURL: core.$constructor; +export declare function url(params?: string | core.$ZodURLParams): ZodURL; +export declare function httpUrl(params?: string | Omit): ZodURL; +export interface ZodEmoji extends ZodStringFormat<"emoji"> { + _zod: core.$ZodEmojiInternals; +} +export declare const ZodEmoji: core.$constructor; +export declare function emoji(params?: string | core.$ZodEmojiParams): ZodEmoji; +export interface ZodNanoID extends ZodStringFormat<"nanoid"> { + _zod: core.$ZodNanoIDInternals; +} +export declare const ZodNanoID: core.$constructor; +export declare function nanoid(params?: string | core.$ZodNanoIDParams): ZodNanoID; +/** + * @deprecated CUID v1 is deprecated by its authors due to information leakage + * (timestamps embedded in the id). Use {@link ZodCUID2} instead. + * See https://github.com/paralleldrive/cuid. + */ +export interface ZodCUID extends ZodStringFormat<"cuid"> { + _zod: core.$ZodCUIDInternals; +} +/** + * @deprecated CUID v1 is deprecated by its authors due to information leakage + * (timestamps embedded in the id). Use {@link ZodCUID2} instead. + * See https://github.com/paralleldrive/cuid. + */ +export declare const ZodCUID: core.$constructor; +/** + * Validates a CUID v1 string. + * + * @deprecated CUID v1 is deprecated by its authors due to information leakage + * (timestamps embedded in the id). Use {@link cuid2 | `z.cuid2()`} instead. + * See https://github.com/paralleldrive/cuid. + */ +export declare function cuid(params?: string | core.$ZodCUIDParams): ZodCUID; +export interface ZodCUID2 extends ZodStringFormat<"cuid2"> { + _zod: core.$ZodCUID2Internals; +} +export declare const ZodCUID2: core.$constructor; +export declare function cuid2(params?: string | core.$ZodCUID2Params): ZodCUID2; +export interface ZodULID extends ZodStringFormat<"ulid"> { + _zod: core.$ZodULIDInternals; +} +export declare const ZodULID: core.$constructor; +export declare function ulid(params?: string | core.$ZodULIDParams): ZodULID; +export interface ZodXID extends ZodStringFormat<"xid"> { + _zod: core.$ZodXIDInternals; +} +export declare const ZodXID: core.$constructor; +export declare function xid(params?: string | core.$ZodXIDParams): ZodXID; +export interface ZodKSUID extends ZodStringFormat<"ksuid"> { + _zod: core.$ZodKSUIDInternals; +} +export declare const ZodKSUID: core.$constructor; +export declare function ksuid(params?: string | core.$ZodKSUIDParams): ZodKSUID; +export interface ZodIPv4 extends ZodStringFormat<"ipv4"> { + _zod: core.$ZodIPv4Internals; +} +export declare const ZodIPv4: core.$constructor; +export declare function ipv4(params?: string | core.$ZodIPv4Params): ZodIPv4; +export interface ZodMAC extends ZodStringFormat<"mac"> { + _zod: core.$ZodMACInternals; +} +export declare const ZodMAC: core.$constructor; +export declare function mac(params?: string | core.$ZodMACParams): ZodMAC; +export interface ZodIPv6 extends ZodStringFormat<"ipv6"> { + _zod: core.$ZodIPv6Internals; +} +export declare const ZodIPv6: core.$constructor; +export declare function ipv6(params?: string | core.$ZodIPv6Params): ZodIPv6; +export interface ZodCIDRv4 extends ZodStringFormat<"cidrv4"> { + _zod: core.$ZodCIDRv4Internals; +} +export declare const ZodCIDRv4: core.$constructor; +export declare function cidrv4(params?: string | core.$ZodCIDRv4Params): ZodCIDRv4; +export interface ZodCIDRv6 extends ZodStringFormat<"cidrv6"> { + _zod: core.$ZodCIDRv6Internals; +} +export declare const ZodCIDRv6: core.$constructor; +export declare function cidrv6(params?: string | core.$ZodCIDRv6Params): ZodCIDRv6; +export interface ZodBase64 extends ZodStringFormat<"base64"> { + _zod: core.$ZodBase64Internals; +} +export declare const ZodBase64: core.$constructor; +export declare function base64(params?: string | core.$ZodBase64Params): ZodBase64; +export interface ZodBase64URL extends ZodStringFormat<"base64url"> { + _zod: core.$ZodBase64URLInternals; +} +export declare const ZodBase64URL: core.$constructor; +export declare function base64url(params?: string | core.$ZodBase64URLParams): ZodBase64URL; +export interface ZodE164 extends ZodStringFormat<"e164"> { + _zod: core.$ZodE164Internals; +} +export declare const ZodE164: core.$constructor; +export declare function e164(params?: string | core.$ZodE164Params): ZodE164; +export interface ZodCreditCard extends ZodStringFormat<"credit_card"> { + _zod: core.$ZodCreditCardInternals; +} +export declare const ZodCreditCard: core.$constructor; +export declare function creditCard(params?: string | core.$ZodCreditCardParams): ZodCreditCard; +export interface ZodIBAN extends ZodStringFormat<"iban"> { + _zod: core.$ZodIBANInternals; +} +export declare const ZodIBAN: core.$constructor; +export declare function iban(params?: string | core.$ZodIBANParams): ZodIBAN; +export interface ZodJWT extends ZodStringFormat<"jwt"> { + _zod: core.$ZodJWTInternals; +} +export declare const ZodJWT: core.$constructor; +export declare function jwt(params?: string | core.$ZodJWTParams): ZodJWT; +export interface ZodCustomStringFormat extends ZodStringFormat, core.$ZodCustomStringFormat { + _zod: core.$ZodCustomStringFormatInternals; + "~standard": ZodStandardSchemaWithJSON; +} +export declare const ZodCustomStringFormat: core.$constructor; +export declare function stringFormat(format: Format, fnOrRegex: ((arg: string) => util.MaybeAsync) | RegExp, _params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat; +export declare function hostname(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"hostname">; +export declare function hex(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"hex">; +export declare function currencyCode(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"currency_code">; +export declare function hash(alg: Alg, params?: { + enc?: Enc; +} & core.$ZodStringFormatParams): ZodCustomStringFormat<`${Alg}_${Enc}`>; +export interface _ZodNumber extends _ZodType { + gt(value: number, params?: string | core.$ZodCheckGreaterThanParams): this; + /** Identical to .min() */ + gte(value: number, params?: string | core.$ZodCheckGreaterThanParams): this; + min(value: number, params?: string | core.$ZodCheckGreaterThanParams): this; + lt(value: number, params?: string | core.$ZodCheckLessThanParams): this; + /** Identical to .max() */ + lte(value: number, params?: string | core.$ZodCheckLessThanParams): this; + max(value: number, params?: string | core.$ZodCheckLessThanParams): this; + /** Consider `z.int()` instead. This API is considered *legacy*; it will never be removed but a better alternative exists. */ + int(params?: string | core.$ZodCheckNumberFormatParams): this; + /** @deprecated This is now identical to `.int()`. Only numbers in the safe integer range are accepted. */ + safe(params?: string | core.$ZodCheckNumberFormatParams): this; + positive(params?: string | core.$ZodCheckGreaterThanParams): this; + nonnegative(params?: string | core.$ZodCheckGreaterThanParams): this; + negative(params?: string | core.$ZodCheckLessThanParams): this; + nonpositive(params?: string | core.$ZodCheckLessThanParams): this; + multipleOf(value: number, params?: string | core.$ZodCheckMultipleOfParams): this; + /** @deprecated Use `.multipleOf()` instead. */ + step(value: number, params?: string | core.$ZodCheckMultipleOfParams): this; + /** @deprecated In v4 and later, z.number() does not allow infinite values by default. This is a no-op. */ + finite(params?: unknown): this; + minValue: number | null; + maxValue: number | null; + /** @deprecated Check the `format` property instead. */ + isInt: boolean; + /** @deprecated Number schemas no longer accept infinite values, so this always returns `true`. */ + isFinite: boolean; + format: string | null; +} +export interface ZodNumber extends _ZodNumber> { +} +export declare const ZodNumber: core.$constructor; +export declare function number(params?: string | core.$ZodNumberParams): ZodNumber; +export interface ZodNumberFormat extends ZodNumber { + _zod: core.$ZodNumberFormatInternals; +} +export declare const ZodNumberFormat: core.$constructor; +export interface ZodInt extends ZodNumberFormat<"safeint"> { +} +export declare function int(params?: string | core.$ZodCheckNumberFormatParams): ZodInt; +export interface ZodFloat32 extends ZodNumberFormat<"float32"> { +} +export declare function float32(params?: string | core.$ZodCheckNumberFormatParams): ZodFloat32; +export interface ZodFloat64 extends ZodNumberFormat<"float64"> { +} +export declare function float64(params?: string | core.$ZodCheckNumberFormatParams): ZodFloat64; +export interface ZodInt32 extends ZodNumberFormat<"int32"> { +} +export declare function int32(params?: string | core.$ZodCheckNumberFormatParams): ZodInt32; +export interface ZodUInt32 extends ZodNumberFormat<"uint32"> { +} +export declare function uint32(params?: string | core.$ZodCheckNumberFormatParams): ZodUInt32; +export interface _ZodBoolean extends _ZodType { +} +export interface ZodBoolean extends _ZodBoolean> { +} +export declare const ZodBoolean: core.$constructor; +export declare function boolean(params?: string | core.$ZodBooleanParams): ZodBoolean; +export interface _ZodBigInt extends _ZodType { + gte(value: bigint, params?: string | core.$ZodCheckGreaterThanParams): this; + /** Alias of `.gte()` */ + min(value: bigint, params?: string | core.$ZodCheckGreaterThanParams): this; + gt(value: bigint, params?: string | core.$ZodCheckGreaterThanParams): this; + /** Alias of `.lte()` */ + lte(value: bigint, params?: string | core.$ZodCheckLessThanParams): this; + max(value: bigint, params?: string | core.$ZodCheckLessThanParams): this; + lt(value: bigint, params?: string | core.$ZodCheckLessThanParams): this; + positive(params?: string | core.$ZodCheckGreaterThanParams): this; + negative(params?: string | core.$ZodCheckLessThanParams): this; + nonpositive(params?: string | core.$ZodCheckLessThanParams): this; + nonnegative(params?: string | core.$ZodCheckGreaterThanParams): this; + multipleOf(value: bigint, params?: string | core.$ZodCheckMultipleOfParams): this; + minValue: bigint | null; + maxValue: bigint | null; + format: string | null; +} +export interface ZodBigInt extends _ZodBigInt> { +} +export declare const ZodBigInt: core.$constructor; +export declare function bigint(params?: string | core.$ZodBigIntParams): ZodBigInt; +export interface ZodBigIntFormat extends ZodBigInt { + _zod: core.$ZodBigIntFormatInternals; +} +export declare const ZodBigIntFormat: core.$constructor; +export interface ZodInt64 extends ZodBigIntFormat<"int64"> { +} +export declare function int64(params?: string | core.$ZodBigIntFormatParams): ZodInt64; +export interface ZodUInt64 extends ZodBigIntFormat<"uint64"> { +} +export declare function uint64(params?: string | core.$ZodBigIntFormatParams): ZodUInt64; +export interface ZodSymbol extends _ZodType { +} +export declare const ZodSymbol: core.$constructor; +export declare function symbol(params?: string | core.$ZodSymbolParams): ZodSymbol; +export interface ZodUndefined extends _ZodType { +} +export declare const ZodUndefined: core.$constructor; +declare function _undefined(params?: string | core.$ZodUndefinedParams): ZodUndefined; +export { _undefined as undefined }; +export interface ZodNull extends _ZodType { +} +export declare const ZodNull: core.$constructor; +declare function _null(params?: string | core.$ZodNullParams): ZodNull; +export { _null as null }; +export interface ZodAny extends _ZodType { +} +export declare const ZodAny: core.$constructor; +export declare function any(): ZodAny; +export interface ZodUnknown extends _ZodType { +} +export declare const ZodUnknown: core.$constructor; +export declare function unknown(): ZodUnknown; +export interface ZodNever extends _ZodType { +} +export declare const ZodNever: core.$constructor; +export declare function never(params?: string | core.$ZodNeverParams): ZodNever; +export interface ZodVoid extends _ZodType { +} +export declare const ZodVoid: core.$constructor; +declare function _void(params?: string | core.$ZodVoidParams): ZodVoid; +export { _void as void }; +export interface _ZodDate extends _ZodType { + min(value: number | Date, params?: string | core.$ZodCheckGreaterThanParams): this; + max(value: number | Date, params?: string | core.$ZodCheckLessThanParams): this; + /** @deprecated Not recommended. */ + minDate: Date | null; + /** @deprecated Not recommended. */ + maxDate: Date | null; +} +export interface ZodDate extends _ZodDate> { +} +export declare const ZodDate: core.$constructor; +export declare function date(params?: string | core.$ZodDateParams): ZodDate; +export interface ZodArray extends _ZodType>, core.$ZodArray { + element: T; + min(minLength: number, params?: string | core.$ZodCheckMinLengthParams): this; + nonempty(params?: string | core.$ZodCheckMinLengthParams): this; + max(maxLength: number, params?: string | core.$ZodCheckMaxLengthParams): this; + length(len: number, params?: string | core.$ZodCheckLengthEqualsParams): this; + unwrap(): T; + "~standard": ZodStandardSchemaWithJSON; +} +export declare const ZodArray: core.$constructor; +export declare function array(element: T, params?: string | core.$ZodArrayParams): ZodArray; +export declare function keyof(schema: T): ZodEnum>; +export type SafeExtendShape = { + [K in keyof Ext]: K extends keyof Base ? core.output extends core.output ? core.input extends core.input ? Ext[K] : never : never : Ext[K]; +}; +export interface ZodObject< +/** @ts-ignore Cast variance */ +out Shape extends core.$ZodShape = core.$ZodLooseShape, out Config extends core.$ZodObjectConfig = core.$strip> extends _ZodType>, core.$ZodObject { + "~standard": ZodStandardSchemaWithJSON; + shape: Shape; + keyof(): ZodEnum>; + /** Define a schema to validate all unrecognized keys. This overrides the existing strict/loose behavior. */ + catchall(schema: T): ZodObject>; + /** @deprecated Use `z.looseObject()` or `.loose()` instead. */ + passthrough(): ZodObject; + /** Consider `z.looseObject(A.shape)` instead */ + loose(): ZodObject; + /** Consider `z.strictObject(A.shape)` instead */ + strict(): ZodObject; + /** This is the default behavior. This method call is likely unnecessary. */ + strip(): ZodObject; + extend(shape: U): ZodObject>, Config>; + safeExtend(shape: SafeExtendShape & Partial>): ZodObject>, Config>; + /** + * @deprecated Use [`A.extend(B.shape)`](https://zod.dev/api?id=extend) instead. + */ + merge(other: U): ZodObject, U["_zod"]["config"]>; + pick>(mask: M & Record, never>): ZodObject>>, Config>; + omit>(mask: M & Record, never>): ZodObject>>, Config>; + partial(): ZodObject<{ + -readonly [k in keyof Shape]: ZodOptional; + }, Config>; + partial>(mask: M & Record, never>): ZodObject<{ + -readonly [k in keyof Shape]: k extends keyof M ? ZodOptional : Shape[k]; + }, Config>; + exactPartial(): ZodObject<{ + -readonly [k in keyof Shape]: ZodExactOptional; + }, Config>; + exactPartial>(mask: M & Record, never>): ZodObject<{ + -readonly [k in keyof Shape]: k extends keyof M ? ZodExactOptional : Shape[k]; + }, Config>; + required(): ZodObject<{ + -readonly [k in keyof Shape]: ZodNonOptional; + }, Config>; + required>(mask: M & Record, never>): ZodObject<{ + -readonly [k in keyof Shape]: k extends keyof M ? ZodNonOptional : Shape[k]; + }, Config>; +} +export declare const ZodObject: core.$constructor; +export declare function object>>(shape?: T, params?: string | core.$ZodObjectParams): ZodObject, core.$strip>; +export declare function strictObject(shape: T, params?: string | core.$ZodObjectParams): ZodObject, core.$strict>; +export declare function looseObject(shape: T, params?: string | core.$ZodObjectParams): ZodObject, core.$loose>; +export interface ZodUnion extends _ZodType>, core.$ZodUnion { + "~standard": ZodStandardSchemaWithJSON; + options: T; +} +export declare const ZodUnion: core.$constructor; +export declare function union(options: T, params?: string | core.$ZodUnionParams): ZodUnion; +export interface ZodXor extends _ZodType>, core.$ZodXor { + "~standard": ZodStandardSchemaWithJSON; + options: T; +} +export declare const ZodXor: core.$constructor; +/** Creates an exclusive union (XOR) where exactly one option must match. + * Unlike regular unions that succeed when any option matches, xor fails if + * zero or more than one option matches the input. */ +export declare function xor(options: T, params?: string | core.$ZodXorParams): ZodXor; +export interface ZodDiscriminatedUnion extends ZodUnion, core.$ZodDiscriminatedUnion { + "~standard": ZodStandardSchemaWithJSON; + _zod: core.$ZodDiscriminatedUnionInternals; + def: core.$ZodDiscriminatedUnionDef; +} +export declare const ZodDiscriminatedUnion: core.$constructor; +export declare function discriminatedUnion(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodDiscriminatedUnion; +export interface ZodIntersection extends _ZodType>, core.$ZodIntersection { + "~standard": ZodStandardSchemaWithJSON; +} +export declare const ZodIntersection: core.$constructor; +export declare function intersection(left: T, right: U): ZodIntersection; +export interface ZodTuple extends _ZodType>, core.$ZodTuple { + "~standard": ZodStandardSchemaWithJSON; + rest(rest: Rest): ZodTuple; + partial(): ZodTuple<{ + -readonly [k in keyof T]: ZodOptional; + }, Rest>; +} +export declare const ZodTuple: core.$constructor; +export declare function tuple(items: T, params?: string | core.$ZodTupleParams): ZodTuple; +export declare function tuple(items: T, rest: Rest, params?: string | core.$ZodTupleParams): ZodTuple; +export declare function tuple(items: [], params?: string | core.$ZodTupleParams): ZodTuple<[], null>; +export interface ZodRecord extends _ZodType>, core.$ZodRecord { + "~standard": ZodStandardSchemaWithJSON; + keyType: Key; + valueType: Value; +} +export declare const ZodRecord: core.$constructor; +export declare function record(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodRecord; +export declare function partialRecord(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodRecord; +export declare function looseRecord(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodRecord; +export interface ZodMap extends _ZodType>, core.$ZodMap { + "~standard": ZodStandardSchemaWithJSON; + keyType: Key; + valueType: Value; + min(minSize: number, params?: string | core.$ZodCheckMinSizeParams): this; + nonempty(params?: string | core.$ZodCheckMinSizeParams): this; + max(maxSize: number, params?: string | core.$ZodCheckMaxSizeParams): this; + size(size: number, params?: string | core.$ZodCheckSizeEqualsParams): this; +} +export declare const ZodMap: core.$constructor; +export declare function map(keyType: Key, valueType: Value, params?: string | core.$ZodMapParams): ZodMap; +export interface ZodSet extends _ZodType>, core.$ZodSet { + "~standard": ZodStandardSchemaWithJSON; + min(minSize: number, params?: string | core.$ZodCheckMinSizeParams): this; + nonempty(params?: string | core.$ZodCheckMinSizeParams): this; + max(maxSize: number, params?: string | core.$ZodCheckMaxSizeParams): this; + size(size: number, params?: string | core.$ZodCheckSizeEqualsParams): this; +} +export declare const ZodSet: core.$constructor; +export declare function set(valueType: Value, params?: string | core.$ZodSetParams): ZodSet; +export interface ZodEnum< +/** @ts-ignore Cast variance */ +out T extends util.EnumLike = util.EnumLike> extends _ZodType>, core.$ZodEnum { + "~standard": ZodStandardSchemaWithJSON; + enum: T; + options: Array; + extract(values: U, params?: string | core.$ZodEnumParams): ZodEnum>>; + exclude(values: U, params?: string | core.$ZodEnumParams): ZodEnum>>; +} +export declare const ZodEnum: core.$constructor; +declare function _enum(values: T, params?: string | core.$ZodEnumParams): ZodEnum>; +declare function _enum(entries: T, params?: string | core.$ZodEnumParams): ZodEnum; +export { _enum as enum }; +/** @deprecated This API has been merged into `z.enum()`. Use `z.enum()` instead. + * + * ```ts + * enum Colors { red, green, blue } + * z.enum(Colors); + * ``` + */ +export declare function nativeEnum(entries: T, params?: string | core.$ZodEnumParams): ZodEnum; +export interface ZodLiteral extends _ZodType>, core.$ZodLiteral { + "~standard": ZodStandardSchemaWithJSON; + values: Set; + /** @legacy Use `.values` instead. Accessing this property will throw an error if the literal accepts multiple values. */ + value: T; +} +export declare const ZodLiteral: core.$constructor; +export declare function literal>(value: T, params?: string | core.$ZodLiteralParams): ZodLiteral; +export declare function literal(value: T, params?: string | core.$ZodLiteralParams): ZodLiteral; +export interface ZodFile extends _ZodType, core.$ZodFile { + "~standard": ZodStandardSchemaWithJSON; + min(size: number, params?: string | core.$ZodCheckMinSizeParams): this; + max(size: number, params?: string | core.$ZodCheckMaxSizeParams): this; + mime(types: util.MimeTypes | Array, params?: string | core.$ZodCheckMimeTypeParams): this; +} +export declare const ZodFile: core.$constructor; +export declare function file(params?: string | core.$ZodFileParams): ZodFile; +export interface ZodTransform extends _ZodType>, core.$ZodTransform { + "~standard": ZodStandardSchemaWithJSON; +} +export declare const ZodTransform: core.$constructor; +export declare function transform(fn: (input: I, ctx: core.$RefinementCtx) => O): ZodTransform, I>; +export interface ZodOptional extends _ZodType>, core.$ZodOptional { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; +} +export declare const ZodOptional: core.$constructor; +export declare function optional(innerType: T): ZodOptional; +export interface ZodExactOptional extends _ZodType>, core.$ZodExactOptional { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; +} +export declare const ZodExactOptional: core.$constructor; +export declare function exactOptional(innerType: T): ZodExactOptional; +export interface ZodNullable extends _ZodType>, core.$ZodNullable { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; +} +export declare const ZodNullable: core.$constructor; +export declare function nullable(innerType: T): ZodNullable; +export declare function nullish(innerType: T): ZodOptional>; +export interface ZodDefault extends _ZodType>, core.$ZodDefault { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; + /** @deprecated Use `.unwrap()` instead. */ + removeDefault(): T; +} +export declare const ZodDefault: core.$constructor; +export declare function _default(innerType: T, defaultValue: util.NoUndefined> | (() => util.NoUndefined>)): ZodDefault; +export interface ZodPrefault extends _ZodType>, core.$ZodPrefault { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; +} +export declare const ZodPrefault: core.$constructor; +export declare function prefault(innerType: T, defaultValue: core.input | (() => core.input)): ZodPrefault; +export interface ZodNonOptional extends _ZodType>, core.$ZodNonOptional { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; +} +export declare const ZodNonOptional: core.$constructor; +export declare function nonoptional(innerType: T, params?: string | core.$ZodNonOptionalParams): ZodNonOptional; +export interface ZodSuccess extends _ZodType>, core.$ZodSuccess { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; +} +export declare const ZodSuccess: core.$constructor; +export declare function success(innerType: T): ZodSuccess; +export interface ZodCatch extends _ZodType>, core.$ZodCatch { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; + /** @deprecated Use `.unwrap()` instead. */ + removeCatch(): T; +} +export declare const ZodCatch: core.$constructor; +declare function _catch(innerType: T, catchValue: core.output | ((ctx: core.$ZodCatchCtx) => core.output)): ZodCatch; +export { _catch as catch }; +export interface ZodNaN extends _ZodType, core.$ZodNaN { + "~standard": ZodStandardSchemaWithJSON; +} +export declare const ZodNaN: core.$constructor; +export declare function nan(params?: string | core.$ZodNaNParams): ZodNaN; +export interface ZodPipe extends _ZodType>, core.$ZodPipe { + "~standard": ZodStandardSchemaWithJSON; + in: A; + out: B; +} +export declare const ZodPipe: core.$constructor; +export declare function pipe> = core.$ZodType>>(in_: A, out: B | core.$ZodType>): ZodPipe; +export interface ZodCodec extends ZodPipe, core.$ZodCodec { + "~standard": ZodStandardSchemaWithJSON; + _zod: core.$ZodCodecInternals; + def: core.$ZodCodecDef; +} +export declare const ZodCodec: core.$constructor; +export declare function codec(in_: A, out: B, params: { + decode: (value: core.output, payload: core.ParsePayload>) => core.util.MaybeAsync>; + encode: (value: core.input, payload: core.ParsePayload>) => core.util.MaybeAsync>; +}): ZodCodec; +export declare function invertCodec(codec: ZodCodec): ZodCodec; +export interface ZodPreprocess extends ZodPipe, B>, core.$ZodPreprocess { + "~standard": ZodStandardSchemaWithJSON; + _zod: core.$ZodPreprocessInternals; + def: core.$ZodPreprocessDef; +} +export declare const ZodPreprocess: core.$constructor; +export interface ZodReadonly extends _ZodType>, core.$ZodReadonly { + "~standard": ZodStandardSchemaWithJSON; + unwrap(): T; +} +export declare const ZodReadonly: core.$constructor; +export declare function readonly(innerType: T): ZodReadonly; +export interface ZodTemplateLiteral

LLVM textPartitions
Up to 4 MiB1
Over 4 MiB, up to 8 MiB2
Over 8 MiB, up to 16 MiB4
Over 16 MiB, up to 32 MiB8
Up to 1 MiB1
Over 1 MiB, up to 2 MiB4
Over 2 MiB, up to 32 MiB8
Over 32 MiB, up to 64 MiB16
Over 64 MiB, up to 128 MiB32
Over 128 MiB64
+
+ {hasChildren ? ( + + ) : } +
+ + {displayName} + +
+ {row.kind}{row.apiSymbol && row.apiSymbol !== row.signature && !isRoot ? ` · ${row.apiSymbol}` : ""} +
+
+
+
-
- {hasChildren ? ( - - ) : } -
- - {displayName} - -
- {row.kind}{row.apiSymbol && row.apiSymbol !== row.signature && !isRoot ? ` · ${row.apiSymbol}` : ""} -
-
-
-