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.
@@ -1,13 +1,90 @@
1
1
  // src/collection/writer.ts
2
- import { randomBytes as randomBytes2 } from "node:crypto";
3
- import { mkdirSync as mkdirSync3 } from "node:fs";
2
+ import { mkdirSync as mkdirSync2 } from "node:fs";
4
3
  import { writeFile } from "node:fs/promises";
5
- import { dirname, resolve as resolve3 } from "node:path";
4
+ import { dirname, resolve as resolve2 } from "node:path";
6
5
 
7
6
  // src/schema/limits.ts
8
7
  var MAX_PIXEL_BYTES = 512 * 1024 * 1024;
9
8
  var MAX_STREAM_BYTES = 50 * 1024 * 1024 * 1024;
10
9
 
10
+ // src/syntheticFixtures/streamWrite.ts
11
+ import { closeSync, mkdirSync, openSync, writeSync } from "node:fs";
12
+ import { resolve } from "node:path";
13
+
14
+ // src/loadDcmjs.ts
15
+ import dcmjsDefaultImport, * as dcmjsNamespace from "dcmjs";
16
+ function loadDcmjs() {
17
+ if (dcmjsNamespace.data?.DicomDict) {
18
+ return dcmjsNamespace;
19
+ }
20
+ const d = dcmjsDefaultImport;
21
+ if (d?.data?.DicomDict) {
22
+ return d;
23
+ }
24
+ if (d?.default?.data?.DicomDict) {
25
+ return d.default;
26
+ }
27
+ throw new Error(
28
+ "dcmjs failed to load (missing data.DicomDict). Install dcmjs >= 0.29."
29
+ );
30
+ }
31
+ var dcmjs = loadDcmjs();
32
+
33
+ // src/platform/bytes.ts
34
+ function bytesToHex(bytes) {
35
+ let hex = "";
36
+ for (const byte of bytes) {
37
+ hex += byte.toString(16).padStart(2, "0");
38
+ }
39
+ return hex;
40
+ }
41
+ function utf8Bytes(text) {
42
+ return new TextEncoder().encode(text);
43
+ }
44
+ function concatBytes(...parts) {
45
+ const out = new Uint8Array(parts.reduce((sum, p) => sum + p.length, 0));
46
+ let offset = 0;
47
+ for (const part of parts) {
48
+ out.set(part, offset);
49
+ offset += part.length;
50
+ }
51
+ return out;
52
+ }
53
+ function patternBytes(length, pattern) {
54
+ const out = new Uint8Array(length);
55
+ const seed = utf8Bytes(pattern);
56
+ if (length === 0 || seed.length === 0) return out;
57
+ out.set(seed.subarray(0, Math.min(seed.length, length)));
58
+ let filled = Math.min(seed.length, length);
59
+ while (filled < length) {
60
+ const chunk = Math.min(filled, length - filled);
61
+ out.copyWithin(filled, 0, chunk);
62
+ filled += chunk;
63
+ }
64
+ return out;
65
+ }
66
+
67
+ // src/nonStandardDicom/dicomdir.ts
68
+ var DICOMDIR_SOP_CLASS_UID = "1.2.840.10008.1.3.10";
69
+ function buildDicomdirBuffer() {
70
+ const uid = DICOMDIR_SOP_CLASS_UID;
71
+ const buf = new Uint8Array(256);
72
+ const view = new DataView(buf.buffer);
73
+ buf.set(utf8Bytes("DICM"), 128);
74
+ let off = 132;
75
+ view.setUint16(off, 2, true);
76
+ off += 2;
77
+ view.setUint16(off, 2, true);
78
+ off += 2;
79
+ buf.set(utf8Bytes("UI"), off);
80
+ off += 2;
81
+ view.setUint16(off, uid.length, true);
82
+ off += 2;
83
+ buf.set(utf8Bytes(uid), off);
84
+ off += uid.length;
85
+ return buf.subarray(0, off);
86
+ }
87
+
11
88
  // src/syntheticFixtures/tagTemplate.ts
12
89
  var TEMPLATE_VOCAB = [
13
90
  "index",
@@ -63,47 +140,6 @@ function positionalName(prefix, ordinal) {
63
140
  return `${prefix}-${String(ordinal).padStart(3, "0")}`;
64
141
  }
65
142
 
66
- // src/loadDcmjs.ts
67
- import dcmjsDefaultImport, * as dcmjsNamespace from "dcmjs";
68
- function loadDcmjs() {
69
- if (dcmjsNamespace.data?.DicomDict) {
70
- return dcmjsNamespace;
71
- }
72
- const d = dcmjsDefaultImport;
73
- if (d?.data?.DicomDict) {
74
- return d;
75
- }
76
- if (d?.default?.data?.DicomDict) {
77
- return d.default;
78
- }
79
- throw new Error(
80
- "dcmjs failed to load (missing data.DicomDict). Install dcmjs >= 0.29."
81
- );
82
- }
83
- var dcmjs = loadDcmjs();
84
-
85
- // src/nonStandardDicom/dicomdir.ts
86
- import { mkdirSync, writeFileSync } from "node:fs";
87
- import { resolve } from "node:path";
88
- var DICOMDIR_SOP_CLASS_UID = "1.2.840.10008.1.3.10";
89
- function buildDicomdirBuffer() {
90
- const uid = DICOMDIR_SOP_CLASS_UID;
91
- const buf = Buffer.alloc(256, 0);
92
- buf.write("DICM", 128, 4, "ascii");
93
- let off = 132;
94
- buf.writeUInt16LE(2, off);
95
- off += 2;
96
- buf.writeUInt16LE(2, off);
97
- off += 2;
98
- buf.write("UI", off, 2, "ascii");
99
- off += 2;
100
- buf.writeUInt16LE(uid.length, off);
101
- off += 2;
102
- buf.write(uid, off, uid.length, "ascii");
103
- off += uid.length;
104
- return buf.subarray(0, off);
105
- }
106
-
107
143
  // src/syntheticFixtures/generator.ts
108
144
  var MODALITY_PRESETS = {
109
145
  CT: {
@@ -246,7 +282,7 @@ function serializeDict(meta, dataset, rawElements) {
246
282
  if (rawElements) {
247
283
  Object.assign(dicomDict.dict, rawElements);
248
284
  }
249
- return Buffer.from(dicomDict.write({ allowInvalidVRLength: true }));
285
+ return new Uint8Array(dicomDict.write({ allowInvalidVRLength: true }));
250
286
  }
251
287
  var MAX_DIMENSION = 65535;
252
288
  function applySizing(dataset, meta, spec, rawElements) {
@@ -291,8 +327,8 @@ function buildImageBuffer(spec, uid) {
291
327
  return { buffer: serializeDict(meta, dataset, rawElements), rawElements };
292
328
  }
293
329
  function buildFakeSignatureBuffer() {
294
- const buf = Buffer.alloc(200, 0);
295
- buf.write("XXXX", 128, 4, "ascii");
330
+ const buf = new Uint8Array(200);
331
+ buf.set(utf8Bytes("XXXX"), 128);
296
332
  return buf;
297
333
  }
298
334
  function sizedNonDicomBytes(spec) {
@@ -300,8 +336,8 @@ function sizedNonDicomBytes(spec) {
300
336
  }
301
337
  function buildNonDicomBuffer(spec) {
302
338
  const bytes = sizedNonDicomBytes(spec);
303
- if (bytes !== void 0) return Buffer.alloc(bytes, "not dicom ");
304
- return Buffer.from(spec.content ?? "not dicom", "utf8");
339
+ if (bytes !== void 0) return patternBytes(bytes, "not dicom ");
340
+ return utf8Bytes(spec.content ?? "not dicom");
305
341
  }
306
342
  function buildBufferForSpec(spec, uid) {
307
343
  switch (spec.type) {
@@ -322,19 +358,150 @@ function buildBufferForSpec(spec, uid) {
322
358
  }
323
359
  }
324
360
 
325
- // src/syntheticFixtures/streamWrite.ts
326
- import { closeSync, mkdirSync as mkdirSync2, openSync, writeSync } from "node:fs";
327
- import { resolve as resolve2 } from "node:path";
361
+ // src/platform/random.ts
362
+ function randomHex(byteLength) {
363
+ return bytesToHex(
364
+ globalThis.crypto.getRandomValues(new Uint8Array(byteLength))
365
+ );
366
+ }
367
+
368
+ // src/platform/sha256.ts
369
+ var K = new Uint32Array([
370
+ 1116352408,
371
+ 1899447441,
372
+ 3049323471,
373
+ 3921009573,
374
+ 961987163,
375
+ 1508970993,
376
+ 2453635748,
377
+ 2870763221,
378
+ 3624381080,
379
+ 310598401,
380
+ 607225278,
381
+ 1426881987,
382
+ 1925078388,
383
+ 2162078206,
384
+ 2614888103,
385
+ 3248222580,
386
+ 3835390401,
387
+ 4022224774,
388
+ 264347078,
389
+ 604807628,
390
+ 770255983,
391
+ 1249150122,
392
+ 1555081692,
393
+ 1996064986,
394
+ 2554220882,
395
+ 2821834349,
396
+ 2952996808,
397
+ 3210313671,
398
+ 3336571891,
399
+ 3584528711,
400
+ 113926993,
401
+ 338241895,
402
+ 666307205,
403
+ 773529912,
404
+ 1294757372,
405
+ 1396182291,
406
+ 1695183700,
407
+ 1986661051,
408
+ 2177026350,
409
+ 2456956037,
410
+ 2730485921,
411
+ 2820302411,
412
+ 3259730800,
413
+ 3345764771,
414
+ 3516065817,
415
+ 3600352804,
416
+ 4094571909,
417
+ 275423344,
418
+ 430227734,
419
+ 506948616,
420
+ 659060556,
421
+ 883997877,
422
+ 958139571,
423
+ 1322822218,
424
+ 1537002063,
425
+ 1747873779,
426
+ 1955562222,
427
+ 2024104815,
428
+ 2227730452,
429
+ 2361852424,
430
+ 2428436474,
431
+ 2756734187,
432
+ 3204031479,
433
+ 3329325298
434
+ ]);
435
+ var rotr = (x, n) => x >>> n | x << 32 - n;
436
+ function sha256(input) {
437
+ const data2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
438
+ const padded = new Uint8Array((data2.length + 8 >> 6) + 1 << 6);
439
+ padded.set(data2);
440
+ padded[data2.length] = 128;
441
+ new DataView(padded.buffer).setBigUint64(
442
+ padded.length - 8,
443
+ BigInt(data2.length * 8),
444
+ false
445
+ );
446
+ const h = new Uint32Array([
447
+ 1779033703,
448
+ 3144134277,
449
+ 1013904242,
450
+ 2773480762,
451
+ 1359893119,
452
+ 2600822924,
453
+ 528734635,
454
+ 1541459225
455
+ ]);
456
+ const w = new Uint32Array(64);
457
+ const view = new DataView(padded.buffer);
458
+ for (let offset = 0; offset < padded.length; offset += 64) {
459
+ for (let t = 0; t < 16; t++) w[t] = view.getUint32(offset + t * 4, false);
460
+ for (let t = 16; t < 64; t++) {
461
+ const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ w[t - 15] >>> 3;
462
+ const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ w[t - 2] >>> 10;
463
+ w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
464
+ }
465
+ let [a, b, c, d, e, f, g, hh] = h;
466
+ for (let t = 0; t < 64; t++) {
467
+ const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
468
+ const ch = e & f ^ ~e & g;
469
+ const temp1 = hh + S1 + ch + K[t] + w[t] >>> 0;
470
+ const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
471
+ const maj = a & b ^ a & c ^ b & c;
472
+ const temp2 = S0 + maj >>> 0;
473
+ hh = g;
474
+ g = f;
475
+ f = e;
476
+ e = d + temp1 >>> 0;
477
+ d = c;
478
+ c = b;
479
+ b = a;
480
+ a = temp1 + temp2 >>> 0;
481
+ }
482
+ h[0] = h[0] + a >>> 0;
483
+ h[1] = h[1] + b >>> 0;
484
+ h[2] = h[2] + c >>> 0;
485
+ h[3] = h[3] + d >>> 0;
486
+ h[4] = h[4] + e >>> 0;
487
+ h[5] = h[5] + f >>> 0;
488
+ h[6] = h[6] + g >>> 0;
489
+ h[7] = h[7] + hh >>> 0;
490
+ }
491
+ const out = new Uint8Array(32);
492
+ const outView = new DataView(out.buffer);
493
+ for (let i = 0; i < 8; i++) outView.setUint32(i * 4, h[i], false);
494
+ return out;
495
+ }
328
496
 
329
497
  // src/syntheticFixtures/uid.ts
330
- import { createHash, randomBytes } from "node:crypto";
331
498
  function hashUid(salt, key) {
332
- const digest = createHash("sha256").update(salt).update(" ").update(key).digest();
333
- const n = BigInt(`0x${digest.subarray(0, 16).toString("hex")}`);
499
+ const digest = sha256(`${salt} ${key}`);
500
+ const n = BigInt(`0x${bytesToHex(digest.subarray(0, 16))}`);
334
501
  return `2.25.${n.toString()}`;
335
502
  }
336
503
  function randomUid() {
337
- const n = BigInt(`0x${randomBytes(16).toString("hex")}`);
504
+ const n = BigInt(`0x${randomHex(16)}`);
338
505
  return `2.25.${n.toString()}`;
339
506
  }
340
507
  function seedToSalt(seed) {
@@ -370,6 +537,9 @@ var PIXEL_DATA_OB_HEADER = Buffer.from([224, 127, 16, 0, 79, 66]);
370
537
  var ITEM_TAG_GROUP = 65534;
371
538
  var ITEM_TAG_ELEMENT = 57344;
372
539
  var SEQUENCE_DELIMITER_ELEMENT = 57565;
540
+ function asBuffer(bytes) {
541
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
542
+ }
373
543
  function itemTag(element, byteLength = 0) {
374
544
  const b = Buffer.alloc(8);
375
545
  b.writeUInt16LE(ITEM_TAG_GROUP, 0);
@@ -400,7 +570,7 @@ function writeNative(outPath, params, dims, zeroChunk) {
400
570
  dataset.Rows = dims.rows;
401
571
  dataset.Columns = dims.columns;
402
572
  const minimalPixelBytes = dataset.PixelData.byteLength;
403
- const probe = serializeDict(meta, dataset, rawElements);
573
+ const probe = asBuffer(serializeDict(meta, dataset, rawElements));
404
574
  const pixelElement = Buffer.alloc(12);
405
575
  PIXEL_DATA_OW_HEADER.copy(pixelElement);
406
576
  pixelElement.writeUInt32LE(minimalPixelBytes, 8);
@@ -433,7 +603,7 @@ function writeEncapsulated(outPath, params, pixelBytes, fragmentBytes, zeroChunk
433
603
  syncMetaWithDataset(meta, dataset, rawElements);
434
604
  dataset.PixelData = [new Uint8Array([]).buffer, new Uint8Array([0, 0]).buffer];
435
605
  dataset._vrMap = { PixelData: "OB" };
436
- const probe = serializeDict(meta, dataset, rawElements);
606
+ const probe = asBuffer(serializeDict(meta, dataset, rawElements));
437
607
  const idx = probe.indexOf(PIXEL_DATA_OB_HEADER);
438
608
  if (idx < 0 || probe.readUInt32LE(idx + 8) !== 4294967295) {
439
609
  throw new Error("failed to locate encapsulated PixelData element header");
@@ -474,7 +644,7 @@ function streamLargeImage(outPath, options) {
474
644
  const modality = options.modality ?? "CT";
475
645
  const uid = options.uid ?? makeUidGenerator(options.uidSalt ?? seedToSalt(options.seed))(0);
476
646
  const identity = { modality, uid, tags: options.tags };
477
- mkdirSync2(resolve2(outPath, ".."), { recursive: true });
647
+ mkdirSync(resolve(outPath, ".."), { recursive: true });
478
648
  const zeroChunk = Buffer.alloc(Math.max(1, chunkBytes));
479
649
  const probe = serializeDict(
480
650
  buildMeta(uid, modality),
@@ -519,7 +689,7 @@ function parseBuffer(buffer) {
519
689
  return dcmjsAny.data.DicomMessage.readFile(ab);
520
690
  }
521
691
  function reserialize(parsed) {
522
- return Buffer.from(parsed.write({ allowInvalidVRLength: true }));
692
+ return new Uint8Array(parsed.write({ allowInvalidVRLength: true }));
523
693
  }
524
694
  var OVERLONG_UID = `2.25.${"0".repeat(60)}`;
525
695
  var VIOLATION_LEVEL = {
@@ -584,11 +754,12 @@ function applyByteLevel(buffer, violations) {
584
754
  result = result.subarray(132);
585
755
  break;
586
756
  case "malformed-sq-delimiter": {
587
- const delimiter = Buffer.alloc(8);
588
- delimiter.writeUInt16LE(65534, 0);
589
- delimiter.writeUInt16LE(57565, 2);
590
- delimiter.writeUInt32LE(4, 4);
591
- result = Buffer.concat([result, delimiter]);
757
+ const delimiter = new Uint8Array(8);
758
+ const view = new DataView(delimiter.buffer);
759
+ view.setUint16(0, 65534, true);
760
+ view.setUint16(2, 57565, true);
761
+ view.setUint32(4, 4, true);
762
+ result = concatBytes(result, delimiter);
592
763
  break;
593
764
  }
594
765
  default:
@@ -609,7 +780,7 @@ function applyViolations(buffer, violations, rawElements) {
609
780
  );
610
781
  }
611
782
 
612
- // src/collection/writer.ts
783
+ // src/collection/generate.ts
613
784
  var NO_EXTENSION_TYPES = /* @__PURE__ */ new Set([
614
785
  "fake-signature",
615
786
  "non-dicom"
@@ -748,12 +919,6 @@ function hierarchicalName(studyOrdinal, seriesIndex, instanceNumber) {
748
919
  relativePath: `study-${pad3(studyOrdinal + 1)}/series-${pad3(seriesIndex + 1)}/${name}`
749
920
  };
750
921
  }
751
- function withResolvedSeed(spec) {
752
- return spec.seed === void 0 ? { ...spec, seed: randomBytes2(4).readUInt32BE(0) } : spec;
753
- }
754
- function withResolvedSalt(spec) {
755
- return spec.uidSalt === void 0 ? { ...spec, uidSalt: randomBytes2(16).toString("hex") } : spec;
756
- }
757
922
  function computeNames(type, index, padWidth, grouped, quirks) {
758
923
  let names;
759
924
  if (grouped) {
@@ -923,49 +1088,23 @@ async function materialisePlan(plan, salt) {
923
1088
  });
924
1089
  return { ...file, filename: plan.filename, relativePath: plan.relativePath };
925
1090
  }
926
- async function* generateCollectionFromSpec(spec) {
927
- const salt = effectiveSalt(spec);
928
- for (const plan of planCollection(spec)) {
929
- yield await materialisePlan(plan, salt);
930
- }
931
- }
932
- async function* previewCollection(spec) {
933
- const salt = effectiveSalt(spec);
934
- for (const plan of planCollection(spec)) {
935
- const knownBytes = plan.fileSpec.type === "large-image" ? plan.fileSpec.targetBytes : plan.fileSpec.type === "non-dicom" ? sizedNonDicomBytes(plan.fileSpec) : void 0;
936
- if (knownBytes !== void 0) {
937
- yield {
938
- relativePath: plan.relativePath,
939
- type: plan.fileSpec.type,
940
- index: plan.index,
941
- approxBytes: knownBytes
942
- };
943
- } else {
944
- const file = await materialisePlan(plan, salt);
945
- yield {
946
- relativePath: plan.relativePath,
947
- type: plan.fileSpec.type,
948
- index: plan.index,
949
- approxBytes: file.buffer.length
950
- };
951
- }
952
- }
953
- }
1091
+
1092
+ // src/collection/writer.ts
954
1093
  async function writeCollectionFromSpec(spec, outDir) {
955
- const root = resolve3(outDir);
956
- mkdirSync3(root, { recursive: true });
1094
+ const root = resolve2(outDir);
1095
+ mkdirSync2(root, { recursive: true });
957
1096
  const manifest = [];
958
1097
  const salt = effectiveSalt(spec);
959
1098
  const createdDirs = /* @__PURE__ */ new Set([root]);
960
1099
  const ensureDir = (filePath) => {
961
1100
  const dir = dirname(filePath);
962
1101
  if (!createdDirs.has(dir)) {
963
- mkdirSync3(dir, { recursive: true });
1102
+ mkdirSync2(dir, { recursive: true });
964
1103
  createdDirs.add(dir);
965
1104
  }
966
1105
  };
967
1106
  for (const plan of planCollection(spec)) {
968
- const filePath = resolve3(root, plan.relativePath);
1107
+ const filePath = resolve2(root, plan.relativePath);
969
1108
  ensureDir(filePath);
970
1109
  if (plan.fileSpec.type === "large-image") {
971
1110
  const large = plan.fileSpec;
@@ -994,10 +1133,5 @@ async function writeCollectionFromSpec(spec, outDir) {
994
1133
  return manifest;
995
1134
  }
996
1135
  export {
997
- generateCollectionFromSpec,
998
- generateFile,
999
- previewCollection,
1000
- withResolvedSalt,
1001
- withResolvedSeed,
1002
1136
  writeCollectionFromSpec
1003
1137
  };
@@ -1,5 +1,5 @@
1
1
  // src/describe/describe.ts
2
- import { createHash as createHash2 } from "node:crypto";
2
+ import { createHash } from "node:crypto";
3
3
  import {
4
4
  closeSync,
5
5
  openSync,
@@ -687,11 +687,148 @@ function validateDatasetSpec(raw) {
687
687
  };
688
688
  }
689
689
 
690
+ // src/platform/bytes.ts
691
+ function bytesToHex(bytes) {
692
+ let hex = "";
693
+ for (const byte of bytes) {
694
+ hex += byte.toString(16).padStart(2, "0");
695
+ }
696
+ return hex;
697
+ }
698
+
699
+ // src/platform/sha256.ts
700
+ var K = new Uint32Array([
701
+ 1116352408,
702
+ 1899447441,
703
+ 3049323471,
704
+ 3921009573,
705
+ 961987163,
706
+ 1508970993,
707
+ 2453635748,
708
+ 2870763221,
709
+ 3624381080,
710
+ 310598401,
711
+ 607225278,
712
+ 1426881987,
713
+ 1925078388,
714
+ 2162078206,
715
+ 2614888103,
716
+ 3248222580,
717
+ 3835390401,
718
+ 4022224774,
719
+ 264347078,
720
+ 604807628,
721
+ 770255983,
722
+ 1249150122,
723
+ 1555081692,
724
+ 1996064986,
725
+ 2554220882,
726
+ 2821834349,
727
+ 2952996808,
728
+ 3210313671,
729
+ 3336571891,
730
+ 3584528711,
731
+ 113926993,
732
+ 338241895,
733
+ 666307205,
734
+ 773529912,
735
+ 1294757372,
736
+ 1396182291,
737
+ 1695183700,
738
+ 1986661051,
739
+ 2177026350,
740
+ 2456956037,
741
+ 2730485921,
742
+ 2820302411,
743
+ 3259730800,
744
+ 3345764771,
745
+ 3516065817,
746
+ 3600352804,
747
+ 4094571909,
748
+ 275423344,
749
+ 430227734,
750
+ 506948616,
751
+ 659060556,
752
+ 883997877,
753
+ 958139571,
754
+ 1322822218,
755
+ 1537002063,
756
+ 1747873779,
757
+ 1955562222,
758
+ 2024104815,
759
+ 2227730452,
760
+ 2361852424,
761
+ 2428436474,
762
+ 2756734187,
763
+ 3204031479,
764
+ 3329325298
765
+ ]);
766
+ var rotr = (x, n) => x >>> n | x << 32 - n;
767
+ function sha256(input) {
768
+ const data2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
769
+ const padded = new Uint8Array((data2.length + 8 >> 6) + 1 << 6);
770
+ padded.set(data2);
771
+ padded[data2.length] = 128;
772
+ new DataView(padded.buffer).setBigUint64(
773
+ padded.length - 8,
774
+ BigInt(data2.length * 8),
775
+ false
776
+ );
777
+ const h = new Uint32Array([
778
+ 1779033703,
779
+ 3144134277,
780
+ 1013904242,
781
+ 2773480762,
782
+ 1359893119,
783
+ 2600822924,
784
+ 528734635,
785
+ 1541459225
786
+ ]);
787
+ const w = new Uint32Array(64);
788
+ const view = new DataView(padded.buffer);
789
+ for (let offset = 0; offset < padded.length; offset += 64) {
790
+ for (let t = 0; t < 16; t++) w[t] = view.getUint32(offset + t * 4, false);
791
+ for (let t = 16; t < 64; t++) {
792
+ const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ w[t - 15] >>> 3;
793
+ const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ w[t - 2] >>> 10;
794
+ w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
795
+ }
796
+ let [a, b, c, d, e, f, g, hh] = h;
797
+ for (let t = 0; t < 64; t++) {
798
+ const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
799
+ const ch = e & f ^ ~e & g;
800
+ const temp1 = hh + S1 + ch + K[t] + w[t] >>> 0;
801
+ const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
802
+ const maj = a & b ^ a & c ^ b & c;
803
+ const temp2 = S0 + maj >>> 0;
804
+ hh = g;
805
+ g = f;
806
+ f = e;
807
+ e = d + temp1 >>> 0;
808
+ d = c;
809
+ c = b;
810
+ b = a;
811
+ a = temp1 + temp2 >>> 0;
812
+ }
813
+ h[0] = h[0] + a >>> 0;
814
+ h[1] = h[1] + b >>> 0;
815
+ h[2] = h[2] + c >>> 0;
816
+ h[3] = h[3] + d >>> 0;
817
+ h[4] = h[4] + e >>> 0;
818
+ h[5] = h[5] + f >>> 0;
819
+ h[6] = h[6] + g >>> 0;
820
+ h[7] = h[7] + hh >>> 0;
821
+ }
822
+ const out = new Uint8Array(32);
823
+ const outView = new DataView(out.buffer);
824
+ for (let i = 0; i < 8; i++) outView.setUint32(i * 4, h[i], false);
825
+ return out;
826
+ }
827
+
690
828
  // src/syntheticFixtures/uid.ts
691
- import { createHash, randomBytes } from "node:crypto";
692
829
  function hashUid(salt, key) {
693
- const digest = createHash("sha256").update(salt).update(" ").update(key).digest();
694
- const n = BigInt(`0x${digest.subarray(0, 16).toString("hex")}`);
830
+ const digest = sha256(`${salt} ${key}`);
831
+ const n = BigInt(`0x${bytesToHex(digest.subarray(0, 16))}`);
695
832
  return `2.25.${n.toString()}`;
696
833
  }
697
834
 
@@ -1032,7 +1169,7 @@ function hashUidTags(uids, salt, map) {
1032
1169
  return tags;
1033
1170
  }
1034
1171
  function deriveSalt(studyUids) {
1035
- const h = createHash2("sha256");
1172
+ const h = createHash("sha256");
1036
1173
  for (const uid of [...studyUids].sort()) h.update(uid).update("\n");
1037
1174
  return h.digest("hex");
1038
1175
  }