@cosyte/dicom 0.1.5 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 39ef9b3: `deidentify()` and `Dataset.directory` now strip a value's padding in linear time, so a long run of NULL or SPACE bytes in a file no longer stalls them for seconds.
8
+
9
+ Seven trims of file-controlled text used patterns that backtracked quadratically on a run of pad bytes ending in any other byte: the NULL trim of every UID `deidentify()` remaps, its reads of a Private Creator, a Block Identifying Information Status and the Burned In Annotation flag, and the DICOMDIR's reads of its `CS` values. A SOP Instance UID of 50,000 NULL bytes and one letter took `deidentify()` close to four seconds, and the time grew with the square of the run. Each trim now scans the value once from its end, or from each end, and returns the same result as before.
10
+
3
11
  ## 0.1.5
4
12
 
5
13
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -11,7 +11,7 @@ var __export = (target, all) => {
11
11
  };
12
12
 
13
13
  // src/version.ts
14
- var VERSION = "0.1.5";
14
+ var VERSION = "0.1.6";
15
15
 
16
16
  // src/dictionary/index.ts
17
17
  var dictionary_exports = {};
@@ -11141,8 +11141,24 @@ function directoryRecords(source) {
11141
11141
  }
11142
11142
  function csValues(el) {
11143
11143
  if (el === void 0) return void 0;
11144
- const text = el.rawBytes.toString("latin1").replace(/[\0 ]+$/u, "");
11145
- return Object.freeze(text === "" ? [] : text.split("\\").map((v) => v.replace(/^ +| +$/gu, "")));
11144
+ const text = stripTrailingPad(el.rawBytes.toString("latin1"));
11145
+ return Object.freeze(text === "" ? [] : text.split("\\").map(trimSpaces));
11146
+ }
11147
+ function stripTrailingPad(text) {
11148
+ let end = text.length;
11149
+ while (end > 0) {
11150
+ const code = text.charCodeAt(end - 1);
11151
+ if (code !== 0 && code !== 32) break;
11152
+ end--;
11153
+ }
11154
+ return text.slice(0, end);
11155
+ }
11156
+ function trimSpaces(value) {
11157
+ let start = 0;
11158
+ let end = value.length;
11159
+ while (start < end && value.charCodeAt(start) === 32) start++;
11160
+ while (end > start && value.charCodeAt(end - 1) === 32) end--;
11161
+ return value.slice(start, end);
11146
11162
  }
11147
11163
  function analyzeDirectory(source) {
11148
11164
  if (!isDicomdir(source.fileMeta)) return void 0;
@@ -13801,7 +13817,7 @@ function stripPad(bytes, tag, vr, position, warnings) {
13801
13817
  }
13802
13818
  return bytes.subarray(0, end);
13803
13819
  }
13804
- function trimSpaces(value) {
13820
+ function trimSpaces2(value) {
13805
13821
  let start = 0;
13806
13822
  let end = value.length;
13807
13823
  while (start < end && value.charCodeAt(start) === SPACE2) start++;
@@ -13860,7 +13876,7 @@ function decodeElementValue(element) {
13860
13876
  }
13861
13877
  if (vr === "LO" || vr === "SH" || vr === "UC") {
13862
13878
  const text = decodeCharsetString(trimmed, tag, vr, charset, position, warnings);
13863
- const values = text.split("\\").map(trimSpaces);
13879
+ const values = text.split("\\").map(trimSpaces2);
13864
13880
  return attachWarnings({ kind: "strings", values }, warnings);
13865
13881
  }
13866
13882
  if (vr === "LT" || vr === "ST" || vr === "UT") {
@@ -13873,7 +13889,7 @@ function decodeElementValue(element) {
13873
13889
  }
13874
13890
  if (vr === "AE" || vr === "CS" || vr === "AS" || vr === "UI") {
13875
13891
  const text = decodeAsciiString(trimmed, tag, vr, position, warnings);
13876
- const values = text.split("\\").map(trimSpaces);
13892
+ const values = text.split("\\").map(trimSpaces2);
13877
13893
  return attachWarnings({ kind: "strings", values }, warnings);
13878
13894
  }
13879
13895
  if (vr === "DS") {
@@ -16766,14 +16782,19 @@ function dummyBytes(vr) {
16766
16782
  const buf = buffer.Buffer.from(text, "latin1");
16767
16783
  return buf.length % 2 === 0 ? buf : buffer.Buffer.concat([buf, buffer.Buffer.from([32])]);
16768
16784
  }
16785
+ function stripTrailingNulls(text) {
16786
+ let end = text.length;
16787
+ while (end > 0 && text.charCodeAt(end - 1) === 0) end--;
16788
+ return text.slice(0, end);
16789
+ }
16769
16790
  function remapUidBytes(rawBytes, remap) {
16770
- const raw = rawBytes.toString("latin1").replace(/\0+$/, "");
16791
+ const raw = stripTrailingNulls(rawBytes.toString("latin1"));
16771
16792
  const remapped = raw.split("\\").map((uid2) => remap(uid2.trim())).join("\\");
16772
16793
  const buf = buffer.Buffer.from(remapped, "latin1");
16773
16794
  return buf.length % 2 === 0 ? buf : buffer.Buffer.concat([buf, buffer.Buffer.from([0])]);
16774
16795
  }
16775
16796
  function uidValueMultiplicity(rawBytes) {
16776
- const raw = rawBytes.toString("latin1").replace(/\0+$/, "");
16797
+ const raw = stripTrailingNulls(rawBytes.toString("latin1"));
16777
16798
  if (raw.length === 0) return 0;
16778
16799
  return raw.split("\\").length;
16779
16800
  }
@@ -17175,7 +17196,7 @@ function insertedSequence(tag, items, encoding) {
17175
17196
  });
17176
17197
  }
17177
17198
  function decodeCreator(el) {
17178
- return el.rawBytes.toString("latin1").replace(/[\0 ]+$/, "").trim();
17199
+ return trimTrailingPad(el.rawBytes).toString("latin1").trim();
17179
17200
  }
17180
17201
  function isPrivateCreatorElement(tag) {
17181
17202
  const { element } = splitTag(tag);
@@ -17303,7 +17324,7 @@ function declarationKey(group, creator) {
17303
17324
  return `${String(group)}:${creator}`;
17304
17325
  }
17305
17326
  function decodeEnumerated(el) {
17306
- return el.rawBytes.toString("latin1").replace(/[\0 ]+$/, "").trim();
17327
+ return trimTrailingPad(el.rawBytes).toString("latin1").trim();
17307
17328
  }
17308
17329
  function readUnsignedShorts(el) {
17309
17330
  const values = [];
@@ -17740,7 +17761,7 @@ function hasUncleanedBurnedIn(ds) {
17740
17761
  if (!ds.has(TAG_PIXEL_DATA)) return false;
17741
17762
  const flag = ds.get(TAG_BURNED_IN_ANNOTATION);
17742
17763
  if (flag === void 0) return true;
17743
- return flag.rawBytes.toString("latin1").replace(/[\0 ]+$/, "").trim().toUpperCase() !== "NO";
17764
+ return trimTrailingPad(flag.rawBytes).toString("latin1").trim().toUpperCase() !== "NO";
17744
17765
  }
17745
17766
  function deidentify(ds, options = {}) {
17746
17767
  if (isRefusedTransferSyntax(ds.fileMeta?.transferSyntaxUID)) {