@statewalker/webrun-files-sqlite 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +212 -0
- package/dist/blocks/byte-reader.d.ts +19 -0
- package/dist/blocks/byte-reader.d.ts.map +1 -0
- package/dist/blocks/sha256.d.ts +11 -0
- package/dist/blocks/sha256.d.ts.map +1 -0
- package/dist/blocks/sqlite-files-api.d.ts +61 -0
- package/dist/blocks/sqlite-files-api.d.ts.map +1 -0
- package/dist/blocks/stream-codec.d.ts +40 -0
- package/dist/blocks/stream-codec.d.ts.map +1 -0
- package/dist/bytes.d.ts +3 -0
- package/dist/bytes.d.ts.map +1 -0
- package/dist/cjs/index.cjs +1264 -0
- package/dist/codec.d.ts +47 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/drivers.d.ts +73 -0
- package/dist/drivers.d.ts.map +1 -0
- package/dist/esm/index.js +1253 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/sql.types.d.ts +25 -0
- package/dist/sql.types.d.ts.map +1 -0
- package/dist/sqlar-files-api.d.ts +52 -0
- package/dist/sqlar-files-api.d.ts.map +1 -0
- package/package.json +52 -0
- package/src/blocks/byte-reader.ts +64 -0
- package/src/blocks/sha256.ts +116 -0
- package/src/blocks/sqlite-files-api.ts +587 -0
- package/src/blocks/stream-codec.ts +132 -0
- package/src/bytes.ts +8 -0
- package/src/codec.ts +88 -0
- package/src/drivers.ts +133 -0
- package/src/index.ts +25 -0
- package/src/sql.types.ts +25 -0
- package/src/sqlar-files-api.ts +413 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { SqliteFilesApi, type SqliteFilesApiOptions } from "./blocks/sqlite-files-api.js";
|
|
2
|
+
export { type PakoStreamModule, pakoDeflateCodec, type StreamCodec, webDeflateCodec, } from "./blocks/stream-codec.js";
|
|
3
|
+
export { type Codec, defaultCodec, type PakoModule, pakoCodec, rawCodec, webCodec, } from "./codec.js";
|
|
4
|
+
export { type D1Database, D1SqlDriver, DoSqlDriver, type DoStorage, NodeSqlDriver, type NodeSqliteDatabase, } from "./drivers.js";
|
|
5
|
+
export type { SqlDriver, SqlStatement } from "./sql.types.js";
|
|
6
|
+
export { SqlarFilesApi, type SqlarFilesApiOptions } from "./sqlar-files-api.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EACL,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,KAAK,WAAW,EAChB,eAAe,GAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,KAAK,EACV,YAAY,EACZ,KAAK,UAAU,EACf,SAAS,EACT,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,WAAW,EACX,KAAK,SAAS,EACd,aAAa,EACb,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** One statement of a transaction. */
|
|
2
|
+
export interface SqlStatement {
|
|
3
|
+
sql: string;
|
|
4
|
+
params?: unknown[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The SQL port. Each method may be sync or async, so node:sqlite, a Durable
|
|
8
|
+
* Object's storage and D1 all satisfy it through a thin driver.
|
|
9
|
+
*/
|
|
10
|
+
export interface SqlDriver {
|
|
11
|
+
/** Rows as objects keyed by column name. */
|
|
12
|
+
all<T = Record<string, unknown>>(sql: string, ...params: unknown[]): T[] | Promise<T[]>;
|
|
13
|
+
/** Execute one statement for its effect. */
|
|
14
|
+
run(sql: string, ...params: unknown[]): void | Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Run the statements in order as one transaction: all apply or none do, and
|
|
17
|
+
* a failure rethrows after rolling back. Returns, per statement, a count that
|
|
18
|
+
* is zero exactly when that statement changed nothing.
|
|
19
|
+
*
|
|
20
|
+
* No statement may depend on reading another's result — D1's `batch()` cannot
|
|
21
|
+
* pass results along — so conditions belong in SQL (`WHERE EXISTS …`).
|
|
22
|
+
*/
|
|
23
|
+
transaction(statements: SqlStatement[]): number[] | Promise<number[]>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=sql.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.types.d.ts","sourceRoot":"","sources":["../src/sql.types.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,4CAA4C;IAC5C,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACxF,4CAA4C;IAC5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D;;;;;;;OAOG;IACH,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACvE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { FileInfo, FileStats, FilesApi, ListOptions, ReadOptions } from "@statewalker/webrun-files";
|
|
2
|
+
import { type Codec } from "./codec.js";
|
|
3
|
+
import type { SqlDriver } from "./sql.types.js";
|
|
4
|
+
export interface SqlarFilesApiOptions {
|
|
5
|
+
/** Defaults to {@link defaultCodec}. */
|
|
6
|
+
codec?: Codec;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A FilesApi whose whole state is one SQLite Archive table.
|
|
10
|
+
*
|
|
11
|
+
* SQLAR can also hold symlinks, which `FileKind` cannot express. The FilesApi
|
|
12
|
+
* view follows them the way POSIX `stat()` does — an unresolvable link is a
|
|
13
|
+
* missing path — and {@link symlink} / {@link readlink} expose the link itself.
|
|
14
|
+
*/
|
|
15
|
+
export declare class SqlarFilesApi implements FilesApi {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(sql: SqlDriver, opts?: SqlarFilesApiOptions);
|
|
18
|
+
/** Creates the table if it is missing. Await it before any other call. */
|
|
19
|
+
init(): Promise<void>;
|
|
20
|
+
/** A missing path or a directory yields nothing; a file yields one chunk. */
|
|
21
|
+
read(path: string, options?: ReadOptions): AsyncIterable<Uint8Array>;
|
|
22
|
+
write(path: string, content: Iterable<Uint8Array> | AsyncIterable<Uint8Array>): Promise<void>;
|
|
23
|
+
/** `sz = 0` with `data IS NULL` is the format's only marker for a directory. */
|
|
24
|
+
mkdir(path: string): Promise<void>;
|
|
25
|
+
/** Writes a symlink row: `sz = -1`, the target verbatim as TEXT. */
|
|
26
|
+
symlink(path: string, target: string): Promise<void>;
|
|
27
|
+
/** The stored target of a symlink, or `undefined` for anything else. */
|
|
28
|
+
readlink(path: string): Promise<string | undefined>;
|
|
29
|
+
stats(path: string): Promise<FileStats | undefined>;
|
|
30
|
+
exists(path: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* SQLAR is a flat keyspace, so the tree is derived from names: a path that
|
|
33
|
+
* only exists as a prefix of other names is yielded as a directory, once,
|
|
34
|
+
* before anything under it. Link entries report their target's stats; a
|
|
35
|
+
* recursive listing never descends through one, which rules out cycles.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Rows come back in name order, but implicit directories and resolved links
|
|
39
|
+
* do not sit where `comparePaths` puts them, so the entries are collected,
|
|
40
|
+
* sorted and filtered by `after`. This backend already loads every row of
|
|
41
|
+
* the directory; sorting them costs no extra reads.
|
|
42
|
+
*/
|
|
43
|
+
list(path: string, options?: ListOptions): AsyncIterable<FileInfo>;
|
|
44
|
+
remove(path: string): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Rows are copied verbatim — mode, sz and the stored data — so a compressed
|
|
47
|
+
* row is never inflated and deflated again. Only mtime is fresh.
|
|
48
|
+
*/
|
|
49
|
+
copy(source: string, target: string): Promise<boolean>;
|
|
50
|
+
move(source: string, target: string): Promise<boolean>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=sqlar-files-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlar-files-api.d.ts","sourceRoot":"","sources":["../src/sqlar-files-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,KAAK,KAAK,EAAgB,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAuBhD,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,QAAQ;;IAI5C,YAAY,GAAG,EAAE,SAAS,EAAE,IAAI,GAAE,oBAAyB,EAG1D;IAED,0EAA0E;IACpE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;IAED,6EAA6E;IACtE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CAAC,UAAU,CAAC,CAe9E;IAEK,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC,CAYf;IAED,gFAAgF;IAC1E,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvC;IAED,oEAAoE;IAC9D,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzD;IAED,wEAAwE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGxD;IAEK,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAExD;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE3C;IAED;;;;;OAKG;IACH;;;;;OAKG;IACI,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC,CAO5E;IAwCK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU3C;IAED;;;OAGG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAsB3D;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAI3D;CAwEF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@statewalker/webrun-files-sqlite",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "SQLite-backed FilesApi implementations",
|
|
7
|
+
"homepage": "https://github.com/statewalker/webrun-files",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Mikhail Kotelnikov",
|
|
10
|
+
"email": "mikhail.kotelnikov@gmail.com"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git@github.com:statewalker/webrun-files.git"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/cjs/index.cjs",
|
|
18
|
+
"module": "./dist/esm/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/esm/index.js",
|
|
24
|
+
"require": "./dist/cjs/index.cjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"src"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@statewalker/webrun-files": "0.10.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^26.2.0",
|
|
36
|
+
"pako": "^3.0.2",
|
|
37
|
+
"rimraf": "^6.1.3",
|
|
38
|
+
"rolldown": "^1.2.4",
|
|
39
|
+
"typescript": "^7.0.2",
|
|
40
|
+
"vitest": "^4.1.10",
|
|
41
|
+
"@statewalker/webrun-files-tests": "3.0.0"
|
|
42
|
+
},
|
|
43
|
+
"sideEffects": false,
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "rimraf dist && rolldown -c && tsc --emitDeclarationOnly --declaration",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"lint": "biome lint src tests"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull bytes from a chunk source in bounded pieces.
|
|
3
|
+
*
|
|
4
|
+
* `read(max)` hands out at most `max` bytes as a zero-copy view of the current
|
|
5
|
+
* source chunk, and pulls the next chunk only when the current one is spent.
|
|
6
|
+
* The source is therefore never read ahead of its consumer: this is the
|
|
7
|
+
* backpressure boundary between a caller's stream and block storage.
|
|
8
|
+
*/
|
|
9
|
+
export class ByteReader {
|
|
10
|
+
readonly #iterator: AsyncIterator<Uint8Array> | Iterator<Uint8Array>;
|
|
11
|
+
#chunk: Uint8Array | undefined;
|
|
12
|
+
#done = false;
|
|
13
|
+
|
|
14
|
+
constructor(source: Iterable<Uint8Array> | AsyncIterable<Uint8Array>) {
|
|
15
|
+
this.#iterator =
|
|
16
|
+
Symbol.asyncIterator in source
|
|
17
|
+
? source[Symbol.asyncIterator]()
|
|
18
|
+
: (source as Iterable<Uint8Array>)[Symbol.iterator]();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** At most `max` bytes, or `undefined` once the source is exhausted. */
|
|
22
|
+
async read(max: number): Promise<Uint8Array | undefined> {
|
|
23
|
+
while (!this.#chunk || this.#chunk.length === 0) {
|
|
24
|
+
if (this.#done) return undefined;
|
|
25
|
+
const next = await this.#iterator.next();
|
|
26
|
+
if (next.done) {
|
|
27
|
+
this.#done = true;
|
|
28
|
+
this.#chunk = undefined;
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
this.#chunk = next.value;
|
|
32
|
+
}
|
|
33
|
+
const piece = this.#chunk.subarray(0, max);
|
|
34
|
+
this.#chunk = this.#chunk.subarray(piece.length);
|
|
35
|
+
return piece;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Release the source early, as `break` in a `for await` would. */
|
|
39
|
+
async close(): Promise<void> {
|
|
40
|
+
if (this.#done) return;
|
|
41
|
+
this.#done = true;
|
|
42
|
+
this.#chunk = undefined;
|
|
43
|
+
await this.#iterator.return?.();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One Uint8Array from a stream's chunks — only ever used on a single block's worth. */
|
|
48
|
+
export async function collectBlock(chunks: AsyncIterable<Uint8Array>): Promise<Uint8Array> {
|
|
49
|
+
const parts: Uint8Array[] = [];
|
|
50
|
+
let total = 0;
|
|
51
|
+
for await (const chunk of chunks) {
|
|
52
|
+
parts.push(chunk);
|
|
53
|
+
total += chunk.length;
|
|
54
|
+
}
|
|
55
|
+
// Always a copy: a piece may be a view into a caller's buffer, which a binding
|
|
56
|
+
// could read whole and a caller could reuse once we pull again.
|
|
57
|
+
const out = new Uint8Array(total);
|
|
58
|
+
let at = 0;
|
|
59
|
+
for (const part of parts) {
|
|
60
|
+
out.set(part, at);
|
|
61
|
+
at += part.length;
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental SHA-256 (FIPS 180-4). WebCrypto only hashes a whole buffer, and
|
|
3
|
+
* content here arrives as a stream it must never be collected from.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const K = new Uint32Array([
|
|
7
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
8
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
9
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
10
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
11
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
12
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
13
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
14
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export class Sha256 {
|
|
18
|
+
readonly #state = new Uint32Array([
|
|
19
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
20
|
+
]);
|
|
21
|
+
readonly #buffer = new Uint8Array(64);
|
|
22
|
+
readonly #words = new Uint32Array(80);
|
|
23
|
+
#buffered = 0;
|
|
24
|
+
#length = 0;
|
|
25
|
+
#finished = false;
|
|
26
|
+
|
|
27
|
+
update(bytes: Uint8Array): this {
|
|
28
|
+
if (this.#finished) throw new Error("Sha256: already digested");
|
|
29
|
+
this.#length += bytes.length;
|
|
30
|
+
let at = 0;
|
|
31
|
+
if (this.#buffered > 0) {
|
|
32
|
+
const take = Math.min(64 - this.#buffered, bytes.length);
|
|
33
|
+
this.#buffer.set(bytes.subarray(0, take), this.#buffered);
|
|
34
|
+
this.#buffered += take;
|
|
35
|
+
at = take;
|
|
36
|
+
if (this.#buffered < 64) return this;
|
|
37
|
+
this.#compress(this.#buffer, 0);
|
|
38
|
+
this.#buffered = 0;
|
|
39
|
+
}
|
|
40
|
+
for (; at + 64 <= bytes.length; at += 64) this.#compress(bytes, at);
|
|
41
|
+
if (at < bytes.length) {
|
|
42
|
+
this.#buffer.set(bytes.subarray(at));
|
|
43
|
+
this.#buffered = bytes.length - at;
|
|
44
|
+
}
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Finalises the hash; the instance cannot be used afterwards. */
|
|
49
|
+
digestHex(): string {
|
|
50
|
+
if (this.#finished) throw new Error("Sha256: already digested");
|
|
51
|
+
const bitLength = this.#length * 8;
|
|
52
|
+
const padding = new Uint8Array((this.#buffered < 56 ? 56 : 120) - this.#buffered + 8);
|
|
53
|
+
padding[0] = 0x80;
|
|
54
|
+
const view = new DataView(padding.buffer);
|
|
55
|
+
view.setUint32(padding.length - 8, Math.floor(bitLength / 2 ** 32));
|
|
56
|
+
view.setUint32(padding.length - 4, bitLength >>> 0);
|
|
57
|
+
this.update(padding);
|
|
58
|
+
this.#finished = true;
|
|
59
|
+
let hex = "";
|
|
60
|
+
for (const word of this.#state) hex += word.toString(16).padStart(8, "0");
|
|
61
|
+
return hex;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#compress(bytes: Uint8Array, at: number): void {
|
|
65
|
+
const w = this.#words;
|
|
66
|
+
for (let i = 0; i < 16; i++) {
|
|
67
|
+
const j = at + i * 4;
|
|
68
|
+
w[i] = (bytes[j] << 24) | (bytes[j + 1] << 16) | (bytes[j + 2] << 8) | bytes[j + 3];
|
|
69
|
+
}
|
|
70
|
+
for (let i = 16; i < 64; i++) {
|
|
71
|
+
const a = w[i - 15];
|
|
72
|
+
const b = w[i - 2];
|
|
73
|
+
const s0 = ((a >>> 7) | (a << 25)) ^ ((a >>> 18) | (a << 14)) ^ (a >>> 3);
|
|
74
|
+
const s1 = ((b >>> 17) | (b << 15)) ^ ((b >>> 19) | (b << 13)) ^ (b >>> 10);
|
|
75
|
+
w[i] = (s1 + w[i - 7] + s0 + w[i - 16]) | 0;
|
|
76
|
+
}
|
|
77
|
+
const h = this.#state;
|
|
78
|
+
let a = h[0],
|
|
79
|
+
b = h[1],
|
|
80
|
+
c = h[2],
|
|
81
|
+
d = h[3],
|
|
82
|
+
e = h[4],
|
|
83
|
+
f = h[5],
|
|
84
|
+
g = h[6],
|
|
85
|
+
k = h[7];
|
|
86
|
+
for (let i = 0; i < 64; i++) {
|
|
87
|
+
const t1 =
|
|
88
|
+
(k +
|
|
89
|
+
(((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7))) +
|
|
90
|
+
((e & f) ^ (~e & g)) +
|
|
91
|
+
K[i] +
|
|
92
|
+
w[i]) |
|
|
93
|
+
0;
|
|
94
|
+
const t2 =
|
|
95
|
+
((((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10))) +
|
|
96
|
+
((a & b) ^ (a & c) ^ (b & c))) |
|
|
97
|
+
0;
|
|
98
|
+
k = g;
|
|
99
|
+
g = f;
|
|
100
|
+
f = e;
|
|
101
|
+
e = (d + t1) | 0;
|
|
102
|
+
d = c;
|
|
103
|
+
c = b;
|
|
104
|
+
b = a;
|
|
105
|
+
a = (t1 + t2) | 0;
|
|
106
|
+
}
|
|
107
|
+
h[0] += a;
|
|
108
|
+
h[1] += b;
|
|
109
|
+
h[2] += c;
|
|
110
|
+
h[3] += d;
|
|
111
|
+
h[4] += e;
|
|
112
|
+
h[5] += f;
|
|
113
|
+
h[6] += g;
|
|
114
|
+
h[7] += k;
|
|
115
|
+
}
|
|
116
|
+
}
|