split-hash 0.3.1 → 0.3.3
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/lib/nodejs/split-hash-validator.d.ts +3 -3
- package/lib/nodejs/split-hash-validator.js +13 -12
- package/lib/nodejs/split-hash-validator.js.map +1 -1
- package/lib/nodejs/split-hash.js +11 -10
- package/lib/nodejs/split-hash.js.map +1 -1
- package/lib/whatwg/split-hash.js +11 -9
- package/lib/whatwg/split-hash.js.map +1 -1
- package/package.json +3 -2
- package/src/nodejs/index.ts +3 -0
- package/src/nodejs/split-hash-validator.ts +80 -0
- package/src/nodejs/split-hash.ts +49 -0
- package/src/nodejs/types.ts +6 -0
- package/src/whatwg/index.ts +2 -0
- package/src/whatwg/split-hash.ts +43 -0
- package/src/whatwg/types.ts +6 -0
|
@@ -5,13 +5,13 @@ import { CustomError } from '@blackglory/errors';
|
|
|
5
5
|
import { ProgressiveHashFactory } from './types.js';
|
|
6
6
|
export declare class SplitHashValidator<T> extends Transform {
|
|
7
7
|
private digests;
|
|
8
|
-
private
|
|
8
|
+
private blockSizeBytes;
|
|
9
9
|
private createHash;
|
|
10
10
|
private equals;
|
|
11
11
|
private hash;
|
|
12
|
-
private
|
|
12
|
+
private currentBlockBytes;
|
|
13
13
|
private digestIndex;
|
|
14
|
-
constructor(digests: T[],
|
|
14
|
+
constructor(digests: T[], blockSizeBytes: number, createHash: ProgressiveHashFactory<T>, equals?: (a: T, b: T) => boolean);
|
|
15
15
|
_transform(chunk: Buffer, encoding: BufferEncoding, callback: TransformCallback): void;
|
|
16
16
|
_flush(callback: TransformCallback): void;
|
|
17
17
|
}
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import { Transform } from 'stream';
|
|
2
|
-
import { CustomError } from '@blackglory/errors';
|
|
2
|
+
import { assert, CustomError } from '@blackglory/errors';
|
|
3
3
|
export class SplitHashValidator extends Transform {
|
|
4
|
-
constructor(digests,
|
|
4
|
+
constructor(digests, blockSizeBytes, createHash, equals = Object.is) {
|
|
5
|
+
assert(blockSizeBytes > 0, 'The parameter blockSizeBytes must be greater than zero');
|
|
5
6
|
super();
|
|
6
7
|
this.digests = digests;
|
|
7
|
-
this.
|
|
8
|
+
this.blockSizeBytes = blockSizeBytes;
|
|
8
9
|
this.createHash = createHash;
|
|
9
10
|
this.equals = equals;
|
|
10
11
|
this.hash = this.createHash();
|
|
11
|
-
this.
|
|
12
|
+
this.currentBlockBytes = 0;
|
|
12
13
|
this.digestIndex = 0;
|
|
13
14
|
}
|
|
14
15
|
_transform(chunk, encoding, callback) {
|
|
15
|
-
if (this.
|
|
16
|
+
if (this.currentBlockBytes + chunk.length < this.blockSizeBytes) {
|
|
16
17
|
this.hash.update(chunk);
|
|
17
|
-
this.
|
|
18
|
+
this.currentBlockBytes += chunk.length;
|
|
18
19
|
}
|
|
19
20
|
else {
|
|
20
21
|
let offset = 0;
|
|
21
22
|
while (true) {
|
|
22
|
-
const
|
|
23
|
-
const slice = chunk.slice(offset, offset +
|
|
24
|
-
if (slice.length ===
|
|
23
|
+
const remainingBlockBytes = this.blockSizeBytes - this.currentBlockBytes;
|
|
24
|
+
const slice = chunk.slice(offset, offset + remainingBlockBytes);
|
|
25
|
+
if (slice.length === remainingBlockBytes) {
|
|
25
26
|
this.hash.update(slice);
|
|
26
27
|
const digest = this.hash.digest();
|
|
27
28
|
if (!this.equals(this.digests[this.digestIndex], digest)) {
|
|
@@ -29,12 +30,12 @@ export class SplitHashValidator extends Transform {
|
|
|
29
30
|
}
|
|
30
31
|
this.digestIndex++;
|
|
31
32
|
this.hash = this.createHash();
|
|
32
|
-
this.
|
|
33
|
+
this.currentBlockBytes = 0;
|
|
33
34
|
offset += slice.length;
|
|
34
35
|
}
|
|
35
36
|
else {
|
|
36
37
|
this.hash.update(slice);
|
|
37
|
-
this.
|
|
38
|
+
this.currentBlockBytes += slice.length;
|
|
38
39
|
break;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -42,7 +43,7 @@ export class SplitHashValidator extends Transform {
|
|
|
42
43
|
callback(null, chunk);
|
|
43
44
|
}
|
|
44
45
|
_flush(callback) {
|
|
45
|
-
if (this.
|
|
46
|
+
if (this.currentBlockBytes > 0) {
|
|
46
47
|
const digest = this.hash.digest();
|
|
47
48
|
if (!this.equals(this.digests[this.digestIndex], digest)) {
|
|
48
49
|
return callback(new NotMatchedError());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split-hash-validator.js","sourceRoot":"","sources":["../../src/nodejs/split-hash-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,MAAM,QAAQ,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"split-hash-validator.js","sourceRoot":"","sources":["../../src/nodejs/split-hash-validator.ts"],"names":[],"mappings":"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"}
|
package/lib/nodejs/split-hash.js
CHANGED
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
import { CustomError } from '@blackglory/errors';
|
|
1
|
+
import { assert, CustomError } from '@blackglory/errors';
|
|
2
2
|
export async function* splitHash(stream, blockSizeBytes, createHash) {
|
|
3
|
+
assert(blockSizeBytes > 0, 'The parameter blockSizeBytes must be greater than zero');
|
|
3
4
|
let hash = createHash();
|
|
4
|
-
let
|
|
5
|
+
let currentBlockBytes = 0;
|
|
5
6
|
for await (const chunk of stream) {
|
|
6
7
|
if (!Buffer.isBuffer(chunk))
|
|
7
8
|
throw new StreamEncodingError();
|
|
8
|
-
if (
|
|
9
|
+
if (currentBlockBytes + chunk.length < blockSizeBytes) {
|
|
9
10
|
hash.update(chunk);
|
|
10
|
-
|
|
11
|
+
currentBlockBytes += chunk.length;
|
|
11
12
|
}
|
|
12
13
|
else {
|
|
13
14
|
let offset = 0;
|
|
14
15
|
while (true) {
|
|
15
|
-
const
|
|
16
|
-
const slice = chunk.slice(offset, offset +
|
|
17
|
-
if (slice.length ===
|
|
16
|
+
const remainingBlockBytes = blockSizeBytes - currentBlockBytes;
|
|
17
|
+
const slice = chunk.slice(offset, offset + remainingBlockBytes);
|
|
18
|
+
if (slice.length === remainingBlockBytes) {
|
|
18
19
|
hash.update(slice);
|
|
19
20
|
const digest = hash.digest();
|
|
20
21
|
yield digest;
|
|
21
22
|
hash = createHash();
|
|
22
|
-
|
|
23
|
+
currentBlockBytes = 0;
|
|
23
24
|
offset += slice.length;
|
|
24
25
|
}
|
|
25
26
|
else {
|
|
26
27
|
hash.update(slice);
|
|
27
|
-
|
|
28
|
+
currentBlockBytes += slice.length;
|
|
28
29
|
break;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
if (
|
|
34
|
+
if (currentBlockBytes > 0)
|
|
34
35
|
yield hash.digest();
|
|
35
36
|
}
|
|
36
37
|
export class StreamEncodingError extends CustomError {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split-hash.js","sourceRoot":"","sources":["../../src/nodejs/split-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"split-hash.js","sourceRoot":"","sources":["../../src/nodejs/split-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGxD,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,SAAS,CAC9B,MAA6B,EAC7B,cAAsB,EACtB,UAAqC;IAErC,MAAM,CAAC,cAAc,GAAG,CAAC,EAAE,wDAAwD,CAAC,CAAA;IAEpF,IAAI,IAAI,GAAG,UAAU,EAAE,CAAA;IACvB,IAAI,iBAAiB,GAAG,CAAC,CAAA;IACzB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE;QAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,mBAAmB,EAAE,CAAA;QAE5D,IAAI,iBAAiB,GAAG,KAAK,CAAC,MAAM,GAAG,cAAc,EAAE;YACrD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClB,iBAAiB,IAAI,KAAK,CAAC,MAAM,CAAA;SAClC;aAAM;YACL,IAAI,MAAM,GAAG,CAAC,CAAA;YACd,OAAO,IAAI,EAAE;gBACX,MAAM,mBAAmB,GAAG,cAAc,GAAG,iBAAiB,CAAA;gBAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAAC,CAAA;gBAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,mBAAmB,EAAE;oBACxC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;oBAC5B,MAAM,MAAM,CAAA;oBAEZ,IAAI,GAAG,UAAU,EAAE,CAAA;oBACnB,iBAAiB,GAAG,CAAC,CAAA;oBACrB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAA;iBACvB;qBAAM;oBAEL,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClB,iBAAiB,IAAI,KAAK,CAAC,MAAM,CAAA;oBACjC,MAAK;iBACN;aACF;SACF;KACF;IAED,IAAI,iBAAiB,GAAG,CAAC;QAAE,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;AAChD,CAAC;AAED,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IAClD;QACE,KAAK,CAAC,iCAAiC,CAAC,CAAA;IAC1C,CAAC;CACF"}
|
package/lib/whatwg/split-hash.js
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
import { toAsyncIterableIterator } from 'extra-stream';
|
|
2
|
+
import { assert } from '@blackglory/errors';
|
|
2
3
|
export async function* splitHash(stream, blockSizeBytes, createHash) {
|
|
4
|
+
assert(blockSizeBytes > 0, 'The parameter blockSizeBytes must be greater than zero');
|
|
3
5
|
let hash = createHash();
|
|
4
|
-
let
|
|
6
|
+
let currentBlockBytes = 0;
|
|
5
7
|
for await (const chunk of toAsyncIterableIterator(stream)) {
|
|
6
|
-
if (
|
|
8
|
+
if (currentBlockBytes + chunk.length < blockSizeBytes) {
|
|
7
9
|
hash.update(chunk);
|
|
8
|
-
|
|
10
|
+
currentBlockBytes += chunk.length;
|
|
9
11
|
}
|
|
10
12
|
else {
|
|
11
13
|
let offset = 0;
|
|
12
14
|
while (true) {
|
|
13
|
-
const
|
|
14
|
-
const slice = chunk.slice(offset, offset +
|
|
15
|
-
if (slice.length ===
|
|
15
|
+
const remainingBlockBytes = blockSizeBytes - currentBlockBytes;
|
|
16
|
+
const slice = chunk.slice(offset, offset + remainingBlockBytes);
|
|
17
|
+
if (slice.length === remainingBlockBytes) {
|
|
16
18
|
hash.update(slice);
|
|
17
19
|
const digest = await hash.digest();
|
|
18
20
|
yield digest;
|
|
19
21
|
hash = createHash();
|
|
20
|
-
|
|
22
|
+
currentBlockBytes = 0;
|
|
21
23
|
offset += slice.length;
|
|
22
24
|
}
|
|
23
25
|
else {
|
|
24
26
|
hash.update(slice);
|
|
25
|
-
|
|
27
|
+
currentBlockBytes += slice.length;
|
|
26
28
|
break;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
|
-
if (
|
|
33
|
+
if (currentBlockBytes > 0)
|
|
32
34
|
yield await hash.digest();
|
|
33
35
|
}
|
|
34
36
|
//# sourceMappingURL=split-hash.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split-hash.js","sourceRoot":"","sources":["../../src/whatwg/split-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"split-hash.js","sourceRoot":"","sources":["../../src/whatwg/split-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAG3C,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,SAAS,CAC9B,MAAsB,EACtB,cAAsB,EACtB,UAAqC;IAErC,MAAM,CAAC,cAAc,GAAG,CAAC,EAAE,wDAAwD,CAAC,CAAA;IAEpF,IAAI,IAAI,GAAG,UAAU,EAAE,CAAA;IACvB,IAAI,iBAAiB,GAAG,CAAC,CAAA;IACzB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE;QACzD,IAAI,iBAAiB,GAAG,KAAK,CAAC,MAAM,GAAG,cAAc,EAAE;YACrD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClB,iBAAiB,IAAI,KAAK,CAAC,MAAM,CAAA;SAClC;aAAM;YACL,IAAI,MAAM,GAAG,CAAC,CAAA;YACd,OAAO,IAAI,EAAE;gBACX,MAAM,mBAAmB,GAAG,cAAc,GAAG,iBAAiB,CAAA;gBAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAAC,CAAA;gBAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,mBAAmB,EAAE;oBACxC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;oBAClC,MAAM,MAAM,CAAA;oBAEZ,IAAI,GAAG,UAAU,EAAE,CAAA;oBACnB,iBAAiB,GAAG,CAAC,CAAA;oBACrB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAA;iBACvB;qBAAM;oBAGL,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClB,iBAAiB,IAAI,KAAK,CAAC,MAAM,CAAA;oBACjC,MAAK;iBACN;aACF;SACF;KACF;IAED,IAAI,iBAAiB,GAAG,CAAC;QAAE,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;AACtD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "split-hash",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Split the stream based on bytes and get digests from each part.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"split",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"chunk"
|
|
11
11
|
],
|
|
12
12
|
"files": [
|
|
13
|
-
"lib"
|
|
13
|
+
"lib",
|
|
14
|
+
"src"
|
|
14
15
|
],
|
|
15
16
|
"type": "module",
|
|
16
17
|
"exports": {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Transform, TransformCallback } from 'stream'
|
|
2
|
+
import { assert, CustomError } from '@blackglory/errors'
|
|
3
|
+
import { ProgressiveHashFactory, IProgressiveHash } from './types.js'
|
|
4
|
+
|
|
5
|
+
export class SplitHashValidator<T> extends Transform {
|
|
6
|
+
private hash: IProgressiveHash<T> = this.createHash()
|
|
7
|
+
private currentBlockBytes = 0
|
|
8
|
+
private digestIndex = 0
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
private digests: T[]
|
|
12
|
+
, private blockSizeBytes: number
|
|
13
|
+
, private createHash: ProgressiveHashFactory<T>
|
|
14
|
+
, private equals: (a: T, b: T) => boolean = Object.is
|
|
15
|
+
) {
|
|
16
|
+
assert(blockSizeBytes > 0, 'The parameter blockSizeBytes must be greater than zero')
|
|
17
|
+
|
|
18
|
+
super()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_transform(
|
|
22
|
+
chunk: Buffer
|
|
23
|
+
, encoding: BufferEncoding
|
|
24
|
+
, callback: TransformCallback
|
|
25
|
+
): void {
|
|
26
|
+
// chunk is always Buffer, encoding is always 'buffer', so there is no need to check
|
|
27
|
+
|
|
28
|
+
if (this.currentBlockBytes + chunk.length < this.blockSizeBytes) {
|
|
29
|
+
this.hash.update(chunk)
|
|
30
|
+
this.currentBlockBytes += chunk.length
|
|
31
|
+
} else {
|
|
32
|
+
let offset = 0
|
|
33
|
+
while (true) {
|
|
34
|
+
const remainingBlockBytes = this.blockSizeBytes - this.currentBlockBytes
|
|
35
|
+
const slice = chunk.slice(offset, offset + remainingBlockBytes)
|
|
36
|
+
if (slice.length === remainingBlockBytes) {
|
|
37
|
+
this.hash.update(slice)
|
|
38
|
+
const digest = this.hash.digest()
|
|
39
|
+
if (!this.equals(this.digests[this.digestIndex], digest)) {
|
|
40
|
+
return callback(new NotMatchedError())
|
|
41
|
+
}
|
|
42
|
+
this.digestIndex++
|
|
43
|
+
// prepare for the next round
|
|
44
|
+
this.hash = this.createHash()
|
|
45
|
+
this.currentBlockBytes = 0
|
|
46
|
+
offset += slice.length
|
|
47
|
+
} else {
|
|
48
|
+
// if the length does not match, the remaining data is not long enough, update the remaining data and exit the loop.
|
|
49
|
+
this.hash.update(slice)
|
|
50
|
+
this.currentBlockBytes += slice.length
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
callback(null, chunk)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_flush(callback: TransformCallback): void {
|
|
60
|
+
if (this.currentBlockBytes > 0) {
|
|
61
|
+
const digest = this.hash.digest()
|
|
62
|
+
if (!this.equals(this.digests[this.digestIndex], digest)) {
|
|
63
|
+
return callback(new NotMatchedError())
|
|
64
|
+
}
|
|
65
|
+
this.digestIndex++
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this.digestIndex !== this.digests.length) {
|
|
69
|
+
return callback(new NotMatchedError())
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
callback()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class NotMatchedError extends CustomError {
|
|
77
|
+
constructor() {
|
|
78
|
+
super('hashes do not match')
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { assert, CustomError } from '@blackglory/errors'
|
|
2
|
+
import { ProgressiveHashFactory } from './types.js'
|
|
3
|
+
|
|
4
|
+
export async function* splitHash<T>(
|
|
5
|
+
stream: NodeJS.ReadableStream
|
|
6
|
+
, blockSizeBytes: number
|
|
7
|
+
, createHash: ProgressiveHashFactory<T>
|
|
8
|
+
): AsyncIterableIterator<T> {
|
|
9
|
+
assert(blockSizeBytes > 0, 'The parameter blockSizeBytes must be greater than zero')
|
|
10
|
+
|
|
11
|
+
let hash = createHash()
|
|
12
|
+
let currentBlockBytes = 0
|
|
13
|
+
for await (const chunk of stream) {
|
|
14
|
+
if (!Buffer.isBuffer(chunk)) throw new StreamEncodingError()
|
|
15
|
+
|
|
16
|
+
if (currentBlockBytes + chunk.length < blockSizeBytes) {
|
|
17
|
+
hash.update(chunk)
|
|
18
|
+
currentBlockBytes += chunk.length
|
|
19
|
+
} else {
|
|
20
|
+
let offset = 0
|
|
21
|
+
while (true) {
|
|
22
|
+
const remainingBlockBytes = blockSizeBytes - currentBlockBytes
|
|
23
|
+
const slice = chunk.slice(offset, offset + remainingBlockBytes)
|
|
24
|
+
if (slice.length === remainingBlockBytes) {
|
|
25
|
+
hash.update(slice)
|
|
26
|
+
const digest = hash.digest()
|
|
27
|
+
yield digest
|
|
28
|
+
// prepare for the next round
|
|
29
|
+
hash = createHash()
|
|
30
|
+
currentBlockBytes = 0
|
|
31
|
+
offset += slice.length
|
|
32
|
+
} else {
|
|
33
|
+
// if the length does not match, the remaining data is not long enough, update the remaining data and exit the loop.
|
|
34
|
+
hash.update(slice)
|
|
35
|
+
currentBlockBytes += slice.length
|
|
36
|
+
break
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// digest remaining data if it exists
|
|
42
|
+
if (currentBlockBytes > 0) yield hash.digest()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class StreamEncodingError extends CustomError {
|
|
46
|
+
constructor() {
|
|
47
|
+
super('stream encoding must not be set')
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { toAsyncIterableIterator } from 'extra-stream'
|
|
2
|
+
import { assert } from '@blackglory/errors'
|
|
3
|
+
import { ProgressiveHashFactory } from './types.js'
|
|
4
|
+
|
|
5
|
+
export async function* splitHash<T>(
|
|
6
|
+
stream: ReadableStream
|
|
7
|
+
, blockSizeBytes: number
|
|
8
|
+
, createHash: ProgressiveHashFactory<T>
|
|
9
|
+
): AsyncIterableIterator<T> {
|
|
10
|
+
assert(blockSizeBytes > 0, 'The parameter blockSizeBytes must be greater than zero')
|
|
11
|
+
|
|
12
|
+
let hash = createHash()
|
|
13
|
+
let currentBlockBytes = 0
|
|
14
|
+
for await (const chunk of toAsyncIterableIterator(stream)) {
|
|
15
|
+
if (currentBlockBytes + chunk.length < blockSizeBytes) {
|
|
16
|
+
hash.update(chunk)
|
|
17
|
+
currentBlockBytes += chunk.length
|
|
18
|
+
} else {
|
|
19
|
+
let offset = 0
|
|
20
|
+
while (true) {
|
|
21
|
+
const remainingBlockBytes = blockSizeBytes - currentBlockBytes
|
|
22
|
+
const slice = chunk.slice(offset, offset + remainingBlockBytes)
|
|
23
|
+
if (slice.length === remainingBlockBytes) {
|
|
24
|
+
hash.update(slice)
|
|
25
|
+
const digest = await hash.digest()
|
|
26
|
+
yield digest
|
|
27
|
+
// prepare for the next round
|
|
28
|
+
hash = createHash()
|
|
29
|
+
currentBlockBytes = 0
|
|
30
|
+
offset += slice.length
|
|
31
|
+
} else {
|
|
32
|
+
// if the length does not match,
|
|
33
|
+
// the remaining data is not long enough, update the remaining data and exit the loop.
|
|
34
|
+
hash.update(slice)
|
|
35
|
+
currentBlockBytes += slice.length
|
|
36
|
+
break
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// digest remaining data if it exists
|
|
42
|
+
if (currentBlockBytes > 0) yield await hash.digest()
|
|
43
|
+
}
|