dicom-synth 1.18.0 → 1.19.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/README.md +1 -1
- package/dist/esm/collection/generate.js +977 -0
- package/dist/esm/collection/writer.js +243 -109
- package/dist/esm/describe/describe.js +142 -5
- package/dist/esm/index.js +396 -223
- package/dist/esm/nonStandardDicom/dicomdir.js +14 -15
- package/dist/esm/platform/bytes.js +39 -0
- package/dist/esm/platform/random.js +22 -0
- package/dist/esm/platform/sha256.js +131 -0
- package/dist/esm/schema/parametric.js +73 -4
- package/dist/esm/syntheticFixtures/generator.js +31 -14
- package/dist/esm/syntheticFixtures/streamWrite.js +156 -13
- package/dist/esm/syntheticFixtures/uid.js +148 -4
- package/dist/esm/syntheticFixtures/violations.js +18 -6
- package/dist/types/collection/generate.d.ts +43 -0
- package/dist/types/collection/writer.d.ts +0 -27
- package/dist/types/index.d.ts +2 -1
- package/dist/types/nonStandardDicom/dicomdir.d.ts +1 -6
- package/dist/types/platform/bytes.d.ts +4 -0
- package/dist/types/platform/random.d.ts +2 -0
- package/dist/types/platform/sha256.d.ts +1 -0
- package/dist/types/syntheticFixtures/generator.d.ts +2 -2
- package/dist/types/syntheticFixtures/violations.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,30 +1,29 @@
|
|
|
1
|
+
// src/platform/bytes.ts
|
|
2
|
+
function utf8Bytes(text) {
|
|
3
|
+
return new TextEncoder().encode(text);
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
// src/nonStandardDicom/dicomdir.ts
|
|
2
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
7
|
var DICOMDIR_SOP_CLASS_UID = "1.2.840.10008.1.3.10";
|
|
5
8
|
function buildDicomdirBuffer() {
|
|
6
9
|
const uid = DICOMDIR_SOP_CLASS_UID;
|
|
7
|
-
const buf =
|
|
8
|
-
|
|
10
|
+
const buf = new Uint8Array(256);
|
|
11
|
+
const view = new DataView(buf.buffer);
|
|
12
|
+
buf.set(utf8Bytes("DICM"), 128);
|
|
9
13
|
let off = 132;
|
|
10
|
-
|
|
14
|
+
view.setUint16(off, 2, true);
|
|
11
15
|
off += 2;
|
|
12
|
-
|
|
16
|
+
view.setUint16(off, 2, true);
|
|
13
17
|
off += 2;
|
|
14
|
-
buf.
|
|
18
|
+
buf.set(utf8Bytes("UI"), off);
|
|
15
19
|
off += 2;
|
|
16
|
-
|
|
20
|
+
view.setUint16(off, uid.length, true);
|
|
17
21
|
off += 2;
|
|
18
|
-
buf.
|
|
22
|
+
buf.set(utf8Bytes(uid), off);
|
|
19
23
|
off += uid.length;
|
|
20
24
|
return buf.subarray(0, off);
|
|
21
25
|
}
|
|
22
|
-
function writeDicomdirFile(filePath) {
|
|
23
|
-
mkdirSync(resolve(filePath, ".."), { recursive: true });
|
|
24
|
-
writeFileSync(filePath, buildDicomdirBuffer());
|
|
25
|
-
}
|
|
26
26
|
export {
|
|
27
27
|
DICOMDIR_SOP_CLASS_UID,
|
|
28
|
-
buildDicomdirBuffer
|
|
29
|
-
writeDicomdirFile
|
|
28
|
+
buildDicomdirBuffer
|
|
30
29
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/platform/bytes.ts
|
|
2
|
+
function bytesToHex(bytes) {
|
|
3
|
+
let hex = "";
|
|
4
|
+
for (const byte of bytes) {
|
|
5
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
6
|
+
}
|
|
7
|
+
return hex;
|
|
8
|
+
}
|
|
9
|
+
function utf8Bytes(text) {
|
|
10
|
+
return new TextEncoder().encode(text);
|
|
11
|
+
}
|
|
12
|
+
function concatBytes(...parts) {
|
|
13
|
+
const out = new Uint8Array(parts.reduce((sum, p) => sum + p.length, 0));
|
|
14
|
+
let offset = 0;
|
|
15
|
+
for (const part of parts) {
|
|
16
|
+
out.set(part, offset);
|
|
17
|
+
offset += part.length;
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
function patternBytes(length, pattern) {
|
|
22
|
+
const out = new Uint8Array(length);
|
|
23
|
+
const seed = utf8Bytes(pattern);
|
|
24
|
+
if (length === 0 || seed.length === 0) return out;
|
|
25
|
+
out.set(seed.subarray(0, Math.min(seed.length, length)));
|
|
26
|
+
let filled = Math.min(seed.length, length);
|
|
27
|
+
while (filled < length) {
|
|
28
|
+
const chunk = Math.min(filled, length - filled);
|
|
29
|
+
out.copyWithin(filled, 0, chunk);
|
|
30
|
+
filled += chunk;
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
bytesToHex,
|
|
36
|
+
concatBytes,
|
|
37
|
+
patternBytes,
|
|
38
|
+
utf8Bytes
|
|
39
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/platform/bytes.ts
|
|
2
|
+
function bytesToHex(bytes) {
|
|
3
|
+
let hex = "";
|
|
4
|
+
for (const byte of bytes) {
|
|
5
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
6
|
+
}
|
|
7
|
+
return hex;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/platform/random.ts
|
|
11
|
+
function randomUint32() {
|
|
12
|
+
return globalThis.crypto.getRandomValues(new Uint32Array(1))[0];
|
|
13
|
+
}
|
|
14
|
+
function randomHex(byteLength) {
|
|
15
|
+
return bytesToHex(
|
|
16
|
+
globalThis.crypto.getRandomValues(new Uint8Array(byteLength))
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
randomHex,
|
|
21
|
+
randomUint32
|
|
22
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/platform/sha256.ts
|
|
2
|
+
var K = new Uint32Array([
|
|
3
|
+
1116352408,
|
|
4
|
+
1899447441,
|
|
5
|
+
3049323471,
|
|
6
|
+
3921009573,
|
|
7
|
+
961987163,
|
|
8
|
+
1508970993,
|
|
9
|
+
2453635748,
|
|
10
|
+
2870763221,
|
|
11
|
+
3624381080,
|
|
12
|
+
310598401,
|
|
13
|
+
607225278,
|
|
14
|
+
1426881987,
|
|
15
|
+
1925078388,
|
|
16
|
+
2162078206,
|
|
17
|
+
2614888103,
|
|
18
|
+
3248222580,
|
|
19
|
+
3835390401,
|
|
20
|
+
4022224774,
|
|
21
|
+
264347078,
|
|
22
|
+
604807628,
|
|
23
|
+
770255983,
|
|
24
|
+
1249150122,
|
|
25
|
+
1555081692,
|
|
26
|
+
1996064986,
|
|
27
|
+
2554220882,
|
|
28
|
+
2821834349,
|
|
29
|
+
2952996808,
|
|
30
|
+
3210313671,
|
|
31
|
+
3336571891,
|
|
32
|
+
3584528711,
|
|
33
|
+
113926993,
|
|
34
|
+
338241895,
|
|
35
|
+
666307205,
|
|
36
|
+
773529912,
|
|
37
|
+
1294757372,
|
|
38
|
+
1396182291,
|
|
39
|
+
1695183700,
|
|
40
|
+
1986661051,
|
|
41
|
+
2177026350,
|
|
42
|
+
2456956037,
|
|
43
|
+
2730485921,
|
|
44
|
+
2820302411,
|
|
45
|
+
3259730800,
|
|
46
|
+
3345764771,
|
|
47
|
+
3516065817,
|
|
48
|
+
3600352804,
|
|
49
|
+
4094571909,
|
|
50
|
+
275423344,
|
|
51
|
+
430227734,
|
|
52
|
+
506948616,
|
|
53
|
+
659060556,
|
|
54
|
+
883997877,
|
|
55
|
+
958139571,
|
|
56
|
+
1322822218,
|
|
57
|
+
1537002063,
|
|
58
|
+
1747873779,
|
|
59
|
+
1955562222,
|
|
60
|
+
2024104815,
|
|
61
|
+
2227730452,
|
|
62
|
+
2361852424,
|
|
63
|
+
2428436474,
|
|
64
|
+
2756734187,
|
|
65
|
+
3204031479,
|
|
66
|
+
3329325298
|
|
67
|
+
]);
|
|
68
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
69
|
+
function sha256(input) {
|
|
70
|
+
const data = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
71
|
+
const padded = new Uint8Array((data.length + 8 >> 6) + 1 << 6);
|
|
72
|
+
padded.set(data);
|
|
73
|
+
padded[data.length] = 128;
|
|
74
|
+
new DataView(padded.buffer).setBigUint64(
|
|
75
|
+
padded.length - 8,
|
|
76
|
+
BigInt(data.length * 8),
|
|
77
|
+
false
|
|
78
|
+
);
|
|
79
|
+
const h = new Uint32Array([
|
|
80
|
+
1779033703,
|
|
81
|
+
3144134277,
|
|
82
|
+
1013904242,
|
|
83
|
+
2773480762,
|
|
84
|
+
1359893119,
|
|
85
|
+
2600822924,
|
|
86
|
+
528734635,
|
|
87
|
+
1541459225
|
|
88
|
+
]);
|
|
89
|
+
const w = new Uint32Array(64);
|
|
90
|
+
const view = new DataView(padded.buffer);
|
|
91
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
92
|
+
for (let t = 0; t < 16; t++) w[t] = view.getUint32(offset + t * 4, false);
|
|
93
|
+
for (let t = 16; t < 64; t++) {
|
|
94
|
+
const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ w[t - 15] >>> 3;
|
|
95
|
+
const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ w[t - 2] >>> 10;
|
|
96
|
+
w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
|
|
97
|
+
}
|
|
98
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
99
|
+
for (let t = 0; t < 64; t++) {
|
|
100
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
101
|
+
const ch = e & f ^ ~e & g;
|
|
102
|
+
const temp1 = hh + S1 + ch + K[t] + w[t] >>> 0;
|
|
103
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
104
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
105
|
+
const temp2 = S0 + maj >>> 0;
|
|
106
|
+
hh = g;
|
|
107
|
+
g = f;
|
|
108
|
+
f = e;
|
|
109
|
+
e = d + temp1 >>> 0;
|
|
110
|
+
d = c;
|
|
111
|
+
c = b;
|
|
112
|
+
b = a;
|
|
113
|
+
a = temp1 + temp2 >>> 0;
|
|
114
|
+
}
|
|
115
|
+
h[0] = h[0] + a >>> 0;
|
|
116
|
+
h[1] = h[1] + b >>> 0;
|
|
117
|
+
h[2] = h[2] + c >>> 0;
|
|
118
|
+
h[3] = h[3] + d >>> 0;
|
|
119
|
+
h[4] = h[4] + e >>> 0;
|
|
120
|
+
h[5] = h[5] + f >>> 0;
|
|
121
|
+
h[6] = h[6] + g >>> 0;
|
|
122
|
+
h[7] = h[7] + hh >>> 0;
|
|
123
|
+
}
|
|
124
|
+
const out = new Uint8Array(32);
|
|
125
|
+
const outView = new DataView(out.buffer);
|
|
126
|
+
for (let i = 0; i < 8; i++) outView.setUint32(i * 4, h[i], false);
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
sha256
|
|
131
|
+
};
|
|
@@ -1,8 +1,77 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
1
|
+
// src/platform/random.ts
|
|
2
|
+
function randomUint32() {
|
|
3
|
+
return globalThis.crypto.getRandomValues(new Uint32Array(1))[0];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/platform/sha256.ts
|
|
7
|
+
var K = new Uint32Array([
|
|
8
|
+
1116352408,
|
|
9
|
+
1899447441,
|
|
10
|
+
3049323471,
|
|
11
|
+
3921009573,
|
|
12
|
+
961987163,
|
|
13
|
+
1508970993,
|
|
14
|
+
2453635748,
|
|
15
|
+
2870763221,
|
|
16
|
+
3624381080,
|
|
17
|
+
310598401,
|
|
18
|
+
607225278,
|
|
19
|
+
1426881987,
|
|
20
|
+
1925078388,
|
|
21
|
+
2162078206,
|
|
22
|
+
2614888103,
|
|
23
|
+
3248222580,
|
|
24
|
+
3835390401,
|
|
25
|
+
4022224774,
|
|
26
|
+
264347078,
|
|
27
|
+
604807628,
|
|
28
|
+
770255983,
|
|
29
|
+
1249150122,
|
|
30
|
+
1555081692,
|
|
31
|
+
1996064986,
|
|
32
|
+
2554220882,
|
|
33
|
+
2821834349,
|
|
34
|
+
2952996808,
|
|
35
|
+
3210313671,
|
|
36
|
+
3336571891,
|
|
37
|
+
3584528711,
|
|
38
|
+
113926993,
|
|
39
|
+
338241895,
|
|
40
|
+
666307205,
|
|
41
|
+
773529912,
|
|
42
|
+
1294757372,
|
|
43
|
+
1396182291,
|
|
44
|
+
1695183700,
|
|
45
|
+
1986661051,
|
|
46
|
+
2177026350,
|
|
47
|
+
2456956037,
|
|
48
|
+
2730485921,
|
|
49
|
+
2820302411,
|
|
50
|
+
3259730800,
|
|
51
|
+
3345764771,
|
|
52
|
+
3516065817,
|
|
53
|
+
3600352804,
|
|
54
|
+
4094571909,
|
|
55
|
+
275423344,
|
|
56
|
+
430227734,
|
|
57
|
+
506948616,
|
|
58
|
+
659060556,
|
|
59
|
+
883997877,
|
|
60
|
+
958139571,
|
|
61
|
+
1322822218,
|
|
62
|
+
1537002063,
|
|
63
|
+
1747873779,
|
|
64
|
+
1955562222,
|
|
65
|
+
2024104815,
|
|
66
|
+
2227730452,
|
|
67
|
+
2361852424,
|
|
68
|
+
2428436474,
|
|
69
|
+
2756734187,
|
|
70
|
+
3204031479,
|
|
71
|
+
3329325298
|
|
72
|
+
]);
|
|
3
73
|
|
|
4
74
|
// src/syntheticFixtures/uid.ts
|
|
5
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
6
75
|
function lcg(seed) {
|
|
7
76
|
let s = seed >>> 0;
|
|
8
77
|
return () => {
|
|
@@ -548,7 +617,7 @@ function fraction(next) {
|
|
|
548
617
|
}
|
|
549
618
|
function resolveParametricSpec(spec) {
|
|
550
619
|
const validated = validateParametricSpec(spec);
|
|
551
|
-
const seed = validated.seed ??
|
|
620
|
+
const seed = validated.seed ?? randomUint32();
|
|
552
621
|
const next = seededStream(seed, PARAMETRIC_STREAM_OFFSET, 0, 0);
|
|
553
622
|
const params = validated.studies;
|
|
554
623
|
const studies = [];
|
|
@@ -17,24 +17,41 @@ function loadDcmjs() {
|
|
|
17
17
|
}
|
|
18
18
|
var dcmjs = loadDcmjs();
|
|
19
19
|
|
|
20
|
+
// src/platform/bytes.ts
|
|
21
|
+
function utf8Bytes(text) {
|
|
22
|
+
return new TextEncoder().encode(text);
|
|
23
|
+
}
|
|
24
|
+
function patternBytes(length, pattern) {
|
|
25
|
+
const out = new Uint8Array(length);
|
|
26
|
+
const seed = utf8Bytes(pattern);
|
|
27
|
+
if (length === 0 || seed.length === 0) return out;
|
|
28
|
+
out.set(seed.subarray(0, Math.min(seed.length, length)));
|
|
29
|
+
let filled = Math.min(seed.length, length);
|
|
30
|
+
while (filled < length) {
|
|
31
|
+
const chunk = Math.min(filled, length - filled);
|
|
32
|
+
out.copyWithin(filled, 0, chunk);
|
|
33
|
+
filled += chunk;
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
20
38
|
// src/nonStandardDicom/dicomdir.ts
|
|
21
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
22
|
-
import { resolve } from "node:path";
|
|
23
39
|
var DICOMDIR_SOP_CLASS_UID = "1.2.840.10008.1.3.10";
|
|
24
40
|
function buildDicomdirBuffer() {
|
|
25
41
|
const uid = DICOMDIR_SOP_CLASS_UID;
|
|
26
|
-
const buf =
|
|
27
|
-
|
|
42
|
+
const buf = new Uint8Array(256);
|
|
43
|
+
const view = new DataView(buf.buffer);
|
|
44
|
+
buf.set(utf8Bytes("DICM"), 128);
|
|
28
45
|
let off = 132;
|
|
29
|
-
|
|
46
|
+
view.setUint16(off, 2, true);
|
|
30
47
|
off += 2;
|
|
31
|
-
|
|
48
|
+
view.setUint16(off, 2, true);
|
|
32
49
|
off += 2;
|
|
33
|
-
buf.
|
|
50
|
+
buf.set(utf8Bytes("UI"), off);
|
|
34
51
|
off += 2;
|
|
35
|
-
|
|
52
|
+
view.setUint16(off, uid.length, true);
|
|
36
53
|
off += 2;
|
|
37
|
-
buf.
|
|
54
|
+
buf.set(utf8Bytes(uid), off);
|
|
38
55
|
off += uid.length;
|
|
39
56
|
return buf.subarray(0, off);
|
|
40
57
|
}
|
|
@@ -191,7 +208,7 @@ function serializeDict(meta, dataset, rawElements) {
|
|
|
191
208
|
if (rawElements) {
|
|
192
209
|
Object.assign(dicomDict.dict, rawElements);
|
|
193
210
|
}
|
|
194
|
-
return
|
|
211
|
+
return new Uint8Array(dicomDict.write({ allowInvalidVRLength: true }));
|
|
195
212
|
}
|
|
196
213
|
var MAX_DIMENSION = 65535;
|
|
197
214
|
function applySizing(dataset, meta, spec, rawElements) {
|
|
@@ -236,8 +253,8 @@ function buildImageBuffer(spec, uid) {
|
|
|
236
253
|
return { buffer: serializeDict(meta, dataset, rawElements), rawElements };
|
|
237
254
|
}
|
|
238
255
|
function buildFakeSignatureBuffer() {
|
|
239
|
-
const buf =
|
|
240
|
-
buf.
|
|
256
|
+
const buf = new Uint8Array(200);
|
|
257
|
+
buf.set(utf8Bytes("XXXX"), 128);
|
|
241
258
|
return buf;
|
|
242
259
|
}
|
|
243
260
|
function sizedNonDicomBytes(spec) {
|
|
@@ -245,8 +262,8 @@ function sizedNonDicomBytes(spec) {
|
|
|
245
262
|
}
|
|
246
263
|
function buildNonDicomBuffer(spec) {
|
|
247
264
|
const bytes = sizedNonDicomBytes(spec);
|
|
248
|
-
if (bytes !== void 0) return
|
|
249
|
-
return
|
|
265
|
+
if (bytes !== void 0) return patternBytes(bytes, "not dicom ");
|
|
266
|
+
return utf8Bytes(spec.content ?? "not dicom");
|
|
250
267
|
}
|
|
251
268
|
function buildBufferForSpec(spec, uid) {
|
|
252
269
|
switch (spec.type) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/syntheticFixtures/streamWrite.ts
|
|
2
|
-
import { closeSync, mkdirSync
|
|
3
|
-
import { resolve
|
|
2
|
+
import { closeSync, mkdirSync, openSync, writeSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
4
|
|
|
5
5
|
// src/schema/limits.ts
|
|
6
6
|
var MAX_PIXEL_BYTES = 512 * 1024 * 1024;
|
|
@@ -25,9 +25,14 @@ function loadDcmjs() {
|
|
|
25
25
|
}
|
|
26
26
|
var dcmjs = loadDcmjs();
|
|
27
27
|
|
|
28
|
-
// src/
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
// src/platform/bytes.ts
|
|
29
|
+
function bytesToHex(bytes) {
|
|
30
|
+
let hex = "";
|
|
31
|
+
for (const byte of bytes) {
|
|
32
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
33
|
+
}
|
|
34
|
+
return hex;
|
|
35
|
+
}
|
|
31
36
|
|
|
32
37
|
// src/schema/validate.ts
|
|
33
38
|
var HEX_TAG_RE = /^[0-9a-fA-F]{8}$/;
|
|
@@ -177,18 +182,153 @@ function serializeDict(meta, dataset, rawElements) {
|
|
|
177
182
|
if (rawElements) {
|
|
178
183
|
Object.assign(dicomDict.dict, rawElements);
|
|
179
184
|
}
|
|
180
|
-
return
|
|
185
|
+
return new Uint8Array(dicomDict.write({ allowInvalidVRLength: true }));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/platform/random.ts
|
|
189
|
+
function randomHex(byteLength) {
|
|
190
|
+
return bytesToHex(
|
|
191
|
+
globalThis.crypto.getRandomValues(new Uint8Array(byteLength))
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/platform/sha256.ts
|
|
196
|
+
var K = new Uint32Array([
|
|
197
|
+
1116352408,
|
|
198
|
+
1899447441,
|
|
199
|
+
3049323471,
|
|
200
|
+
3921009573,
|
|
201
|
+
961987163,
|
|
202
|
+
1508970993,
|
|
203
|
+
2453635748,
|
|
204
|
+
2870763221,
|
|
205
|
+
3624381080,
|
|
206
|
+
310598401,
|
|
207
|
+
607225278,
|
|
208
|
+
1426881987,
|
|
209
|
+
1925078388,
|
|
210
|
+
2162078206,
|
|
211
|
+
2614888103,
|
|
212
|
+
3248222580,
|
|
213
|
+
3835390401,
|
|
214
|
+
4022224774,
|
|
215
|
+
264347078,
|
|
216
|
+
604807628,
|
|
217
|
+
770255983,
|
|
218
|
+
1249150122,
|
|
219
|
+
1555081692,
|
|
220
|
+
1996064986,
|
|
221
|
+
2554220882,
|
|
222
|
+
2821834349,
|
|
223
|
+
2952996808,
|
|
224
|
+
3210313671,
|
|
225
|
+
3336571891,
|
|
226
|
+
3584528711,
|
|
227
|
+
113926993,
|
|
228
|
+
338241895,
|
|
229
|
+
666307205,
|
|
230
|
+
773529912,
|
|
231
|
+
1294757372,
|
|
232
|
+
1396182291,
|
|
233
|
+
1695183700,
|
|
234
|
+
1986661051,
|
|
235
|
+
2177026350,
|
|
236
|
+
2456956037,
|
|
237
|
+
2730485921,
|
|
238
|
+
2820302411,
|
|
239
|
+
3259730800,
|
|
240
|
+
3345764771,
|
|
241
|
+
3516065817,
|
|
242
|
+
3600352804,
|
|
243
|
+
4094571909,
|
|
244
|
+
275423344,
|
|
245
|
+
430227734,
|
|
246
|
+
506948616,
|
|
247
|
+
659060556,
|
|
248
|
+
883997877,
|
|
249
|
+
958139571,
|
|
250
|
+
1322822218,
|
|
251
|
+
1537002063,
|
|
252
|
+
1747873779,
|
|
253
|
+
1955562222,
|
|
254
|
+
2024104815,
|
|
255
|
+
2227730452,
|
|
256
|
+
2361852424,
|
|
257
|
+
2428436474,
|
|
258
|
+
2756734187,
|
|
259
|
+
3204031479,
|
|
260
|
+
3329325298
|
|
261
|
+
]);
|
|
262
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
263
|
+
function sha256(input) {
|
|
264
|
+
const data2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
265
|
+
const padded = new Uint8Array((data2.length + 8 >> 6) + 1 << 6);
|
|
266
|
+
padded.set(data2);
|
|
267
|
+
padded[data2.length] = 128;
|
|
268
|
+
new DataView(padded.buffer).setBigUint64(
|
|
269
|
+
padded.length - 8,
|
|
270
|
+
BigInt(data2.length * 8),
|
|
271
|
+
false
|
|
272
|
+
);
|
|
273
|
+
const h = new Uint32Array([
|
|
274
|
+
1779033703,
|
|
275
|
+
3144134277,
|
|
276
|
+
1013904242,
|
|
277
|
+
2773480762,
|
|
278
|
+
1359893119,
|
|
279
|
+
2600822924,
|
|
280
|
+
528734635,
|
|
281
|
+
1541459225
|
|
282
|
+
]);
|
|
283
|
+
const w = new Uint32Array(64);
|
|
284
|
+
const view = new DataView(padded.buffer);
|
|
285
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
286
|
+
for (let t = 0; t < 16; t++) w[t] = view.getUint32(offset + t * 4, false);
|
|
287
|
+
for (let t = 16; t < 64; t++) {
|
|
288
|
+
const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ w[t - 15] >>> 3;
|
|
289
|
+
const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ w[t - 2] >>> 10;
|
|
290
|
+
w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
|
|
291
|
+
}
|
|
292
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
293
|
+
for (let t = 0; t < 64; t++) {
|
|
294
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
295
|
+
const ch = e & f ^ ~e & g;
|
|
296
|
+
const temp1 = hh + S1 + ch + K[t] + w[t] >>> 0;
|
|
297
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
298
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
299
|
+
const temp2 = S0 + maj >>> 0;
|
|
300
|
+
hh = g;
|
|
301
|
+
g = f;
|
|
302
|
+
f = e;
|
|
303
|
+
e = d + temp1 >>> 0;
|
|
304
|
+
d = c;
|
|
305
|
+
c = b;
|
|
306
|
+
b = a;
|
|
307
|
+
a = temp1 + temp2 >>> 0;
|
|
308
|
+
}
|
|
309
|
+
h[0] = h[0] + a >>> 0;
|
|
310
|
+
h[1] = h[1] + b >>> 0;
|
|
311
|
+
h[2] = h[2] + c >>> 0;
|
|
312
|
+
h[3] = h[3] + d >>> 0;
|
|
313
|
+
h[4] = h[4] + e >>> 0;
|
|
314
|
+
h[5] = h[5] + f >>> 0;
|
|
315
|
+
h[6] = h[6] + g >>> 0;
|
|
316
|
+
h[7] = h[7] + hh >>> 0;
|
|
317
|
+
}
|
|
318
|
+
const out = new Uint8Array(32);
|
|
319
|
+
const outView = new DataView(out.buffer);
|
|
320
|
+
for (let i = 0; i < 8; i++) outView.setUint32(i * 4, h[i], false);
|
|
321
|
+
return out;
|
|
181
322
|
}
|
|
182
323
|
|
|
183
324
|
// src/syntheticFixtures/uid.ts
|
|
184
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
185
325
|
function hashUid(salt, key) {
|
|
186
|
-
const digest =
|
|
187
|
-
const n = BigInt(`0x${digest.subarray(0, 16)
|
|
326
|
+
const digest = sha256(`${salt} ${key}`);
|
|
327
|
+
const n = BigInt(`0x${bytesToHex(digest.subarray(0, 16))}`);
|
|
188
328
|
return `2.25.${n.toString()}`;
|
|
189
329
|
}
|
|
190
330
|
function randomUid() {
|
|
191
|
-
const n = BigInt(`0x${
|
|
331
|
+
const n = BigInt(`0x${randomHex(16)}`);
|
|
192
332
|
return `2.25.${n.toString()}`;
|
|
193
333
|
}
|
|
194
334
|
function seedToSalt(seed) {
|
|
@@ -215,6 +355,9 @@ var PIXEL_DATA_OB_HEADER = Buffer.from([224, 127, 16, 0, 79, 66]);
|
|
|
215
355
|
var ITEM_TAG_GROUP = 65534;
|
|
216
356
|
var ITEM_TAG_ELEMENT = 57344;
|
|
217
357
|
var SEQUENCE_DELIMITER_ELEMENT = 57565;
|
|
358
|
+
function asBuffer(bytes) {
|
|
359
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
360
|
+
}
|
|
218
361
|
function itemTag(element, byteLength = 0) {
|
|
219
362
|
const b = Buffer.alloc(8);
|
|
220
363
|
b.writeUInt16LE(ITEM_TAG_GROUP, 0);
|
|
@@ -245,7 +388,7 @@ function writeNative(outPath, params, dims, zeroChunk) {
|
|
|
245
388
|
dataset.Rows = dims.rows;
|
|
246
389
|
dataset.Columns = dims.columns;
|
|
247
390
|
const minimalPixelBytes = dataset.PixelData.byteLength;
|
|
248
|
-
const probe = serializeDict(meta, dataset, rawElements);
|
|
391
|
+
const probe = asBuffer(serializeDict(meta, dataset, rawElements));
|
|
249
392
|
const pixelElement = Buffer.alloc(12);
|
|
250
393
|
PIXEL_DATA_OW_HEADER.copy(pixelElement);
|
|
251
394
|
pixelElement.writeUInt32LE(minimalPixelBytes, 8);
|
|
@@ -278,7 +421,7 @@ function writeEncapsulated(outPath, params, pixelBytes, fragmentBytes, zeroChunk
|
|
|
278
421
|
syncMetaWithDataset(meta, dataset, rawElements);
|
|
279
422
|
dataset.PixelData = [new Uint8Array([]).buffer, new Uint8Array([0, 0]).buffer];
|
|
280
423
|
dataset._vrMap = { PixelData: "OB" };
|
|
281
|
-
const probe = serializeDict(meta, dataset, rawElements);
|
|
424
|
+
const probe = asBuffer(serializeDict(meta, dataset, rawElements));
|
|
282
425
|
const idx = probe.indexOf(PIXEL_DATA_OB_HEADER);
|
|
283
426
|
if (idx < 0 || probe.readUInt32LE(idx + 8) !== 4294967295) {
|
|
284
427
|
throw new Error("failed to locate encapsulated PixelData element header");
|
|
@@ -319,7 +462,7 @@ function streamLargeImage(outPath, options) {
|
|
|
319
462
|
const modality = options.modality ?? "CT";
|
|
320
463
|
const uid = options.uid ?? makeUidGenerator(options.uidSalt ?? seedToSalt(options.seed))(0);
|
|
321
464
|
const identity = { modality, uid, tags: options.tags };
|
|
322
|
-
|
|
465
|
+
mkdirSync(resolve(outPath, ".."), { recursive: true });
|
|
323
466
|
const zeroChunk = Buffer.alloc(Math.max(1, chunkBytes));
|
|
324
467
|
const probe = serializeDict(
|
|
325
468
|
buildMeta(uid, modality),
|