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,
|
|
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);
|
|
@@ -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
|
};
|