functionalscript 0.35.1 → 0.35.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,10 @@
1
1
  import { mask } from "../../types/bigint/module.f.js";
2
- import { vec, length, empty, msb } from "../../types/bit_vec/module.f.js";
2
+ import { vec, length, empty, msb, chunkList, uint } from "../../types/bit_vec/module.f.js";
3
3
  import { fold } from "../../types/list/module.f.js";
4
- const { concat, popFront, front } = msb;
4
+ const { concat, front } = msb;
5
+ // `chunkList(msb)` depends on neither `chunkLength` nor `v`/`state` — shared
6
+ // across every `base(...)` config (32-bit and 64-bit SHA-2 variants).
7
+ const chunkListMsb = chunkList(msb);
5
8
  const lastOne = vec(1n)(1n);
6
9
  const base = ({ logBitLen, k, bs0, bs1, ss0, ss1 }) => {
7
10
  const bitLength = 1n << logBitLen;
@@ -109,6 +112,20 @@ const base = ({ logBitLen, k, bs0, bs1, ss0, ss1 }) => {
109
112
  ]);
110
113
  };
111
114
  const chunkLength = bitLength << 4n; // * 16
115
+ // `chunkListMsb(chunkLength)` depends on `chunkLength` but not `v`/`state`
116
+ // — computed once per `base(...)` config, not once per `append` call.
117
+ const chunkListChunkLength = chunkListMsb(chunkLength);
118
+ // Folds one block (or the final, shorter-than-`chunkLength` leftover) into
119
+ // `State`. `chunkList` yields chunks of exactly `chunkLength` bits except
120
+ // possibly the last one, which is why `remainder` only ever holds that
121
+ // last chunk (`empty` otherwise) — same shape as `State` itself, so no
122
+ // separate accumulator type is needed.
123
+ const appendChunk = (chunk) => (state) => length(chunk) === chunkLength
124
+ ? { hash: compress(state.hash)(uint(chunk)), len: state.len + chunkLength, remainder: empty }
125
+ : { ...state, remainder: chunk };
126
+ // `fold(appendChunk)` depends on neither `v` nor `state` — only the
127
+ // starting accumulator passed to it per `append` call does.
128
+ const foldChunks = fold(appendChunk);
112
129
  const fromV8 = (a) => a.reduce((p, v) => (p << bitLength) | v);
113
130
  // See https://www.rfc-editor.org/rfc/rfc6234#section-4
114
131
  const lastChunkLength = chunkLength - 1n - (bitLength << 1n);
@@ -117,23 +134,7 @@ const base = ({ logBitLen, k, bs0, bs1, ss0, ss1 }) => {
117
134
  chunkLength,
118
135
  compress,
119
136
  fromV8,
120
- append: (v) => (state) => {
121
- let { remainder, hash, len } = state;
122
- remainder = concat(remainder)(v);
123
- let remainderLen = length(remainder);
124
- while (remainderLen >= chunkLength) {
125
- const [u, nr] = popFront(chunkLength)(remainder);
126
- hash = compress(hash)(u);
127
- remainder = nr;
128
- remainderLen -= chunkLength;
129
- len += chunkLength;
130
- }
131
- return {
132
- hash,
133
- len,
134
- remainder
135
- };
136
- },
137
+ append: (v) => (state) => foldChunks({ ...state, remainder: empty })(chunkListChunkLength(concat(state.remainder)(v))),
137
138
  end: (hashLength) => {
138
139
  const offset = (bitLength << 3n) - hashLength;
139
140
  const result = vec(hashLength);
@@ -27,4 +27,5 @@ export declare const proof: {
27
27
  padding: {
28
28
  overflow: () => void;
29
29
  };
30
+ appendLargeVecIsFast: () => void;
30
31
  };
@@ -149,4 +149,22 @@ export const proof = {
149
149
  }
150
150
  },
151
151
  },
152
+ // Regression guard (sha2-append-quadratic): `append` used to be
153
+ // quadratic in the input size, not linear — a single call on a
154
+ // 100,000-byte `Vec` used to cost real seconds and scale worse than
155
+ // O(n²); now near-linear (~40-60ms on Node, well under `bun test`'s 5s
156
+ // per-test limit on Bun). No timing assertion (duration varies by
157
+ // engine/machine); relies on the test runner's own per-test timing to
158
+ // catch a regression, same convention as `fs/base64/proof.f.ts`
159
+ // `encodeLargeVecIsSlow`.
160
+ appendLargeVecIsFast: () => {
161
+ const big = repeat(100000n)(vec(8n)(0xffn));
162
+ let state = sha256.init;
163
+ state = sha256.append(big)(state);
164
+ const h = sha256.end(state);
165
+ const x = 0xbe87f6dbe42cdf682276fbecab3636fbfcaa008cf454d635dd77872b50d940aan;
166
+ if (uint(h) !== x) {
167
+ throw h;
168
+ }
169
+ },
152
170
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.35.1",
3
+ "version": "0.35.2",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",