@gmod/hic 1.0.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.
Files changed (88) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +91 -0
  3. package/dist/binWindow.d.ts +24 -0
  4. package/dist/binWindow.js +32 -0
  5. package/dist/binWindow.js.map +1 -0
  6. package/dist/binary.d.ts +15 -0
  7. package/dist/binary.js +67 -0
  8. package/dist/binary.js.map +1 -0
  9. package/dist/bufferedFile.d.ts +13 -0
  10. package/dist/bufferedFile.js +78 -0
  11. package/dist/bufferedFile.js.map +1 -0
  12. package/dist/contactRecords.d.ts +25 -0
  13. package/dist/contactRecords.js +3 -0
  14. package/dist/contactRecords.js.map +1 -0
  15. package/dist/hicFile.d.ts +94 -0
  16. package/dist/hicFile.js +769 -0
  17. package/dist/hicFile.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.js +9 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/lru.d.ts +29 -0
  22. package/dist/lru.js +55 -0
  23. package/dist/lru.js.map +1 -0
  24. package/dist/matrix.d.ts +9 -0
  25. package/dist/matrix.js +41 -0
  26. package/dist/matrix.js.map +1 -0
  27. package/dist/matrixZoomData.d.ts +14 -0
  28. package/dist/matrixZoomData.js +96 -0
  29. package/dist/matrixZoomData.js.map +1 -0
  30. package/dist/normalizationVector.d.ts +10 -0
  31. package/dist/normalizationVector.js +51 -0
  32. package/dist/normalizationVector.js.map +1 -0
  33. package/dist/package.json +1 -0
  34. package/dist/reader.d.ts +3 -0
  35. package/dist/reader.js +12 -0
  36. package/dist/reader.js.map +1 -0
  37. package/dist/types.d.ts +35 -0
  38. package/dist/types.js +3 -0
  39. package/dist/types.js.map +1 -0
  40. package/esm/binWindow.d.ts +24 -0
  41. package/esm/binWindow.js +29 -0
  42. package/esm/binWindow.js.map +1 -0
  43. package/esm/binary.d.ts +15 -0
  44. package/esm/binary.js +64 -0
  45. package/esm/binary.js.map +1 -0
  46. package/esm/bufferedFile.d.ts +13 -0
  47. package/esm/bufferedFile.js +75 -0
  48. package/esm/bufferedFile.js.map +1 -0
  49. package/esm/contactRecords.d.ts +25 -0
  50. package/esm/contactRecords.js +2 -0
  51. package/esm/contactRecords.js.map +1 -0
  52. package/esm/hicFile.d.ts +94 -0
  53. package/esm/hicFile.js +762 -0
  54. package/esm/hicFile.js.map +1 -0
  55. package/esm/index.d.ts +5 -0
  56. package/esm/index.js +3 -0
  57. package/esm/index.js.map +1 -0
  58. package/esm/lru.d.ts +29 -0
  59. package/esm/lru.js +52 -0
  60. package/esm/lru.js.map +1 -0
  61. package/esm/matrix.d.ts +9 -0
  62. package/esm/matrix.js +35 -0
  63. package/esm/matrix.js.map +1 -0
  64. package/esm/matrixZoomData.d.ts +14 -0
  65. package/esm/matrixZoomData.js +93 -0
  66. package/esm/matrixZoomData.js.map +1 -0
  67. package/esm/normalizationVector.d.ts +10 -0
  68. package/esm/normalizationVector.js +45 -0
  69. package/esm/normalizationVector.js.map +1 -0
  70. package/esm/reader.d.ts +3 -0
  71. package/esm/reader.js +9 -0
  72. package/esm/reader.js.map +1 -0
  73. package/esm/types.d.ts +35 -0
  74. package/esm/types.js +2 -0
  75. package/esm/types.js.map +1 -0
  76. package/package.json +73 -0
  77. package/src/binWindow.ts +30 -0
  78. package/src/binary.ts +72 -0
  79. package/src/bufferedFile.ts +74 -0
  80. package/src/contactRecords.ts +25 -0
  81. package/src/hicFile.ts +943 -0
  82. package/src/index.ts +12 -0
  83. package/src/lru.ts +77 -0
  84. package/src/matrix.ts +43 -0
  85. package/src/matrixZoomData.ts +133 -0
  86. package/src/normalizationVector.ts +58 -0
  87. package/src/reader.ts +11 -0
  88. package/src/types.ts +40 -0
package/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 hic-straw contributors (https://github.com/aidenlab/hic-straw)
4
+ Copyright (c) 2026 Colin Diesh
5
+
6
+ This package is a fork of hic-straw, whose MIT license notice is preserved
7
+ above.
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @gmod/hic
2
+
3
+ [![NPM version](https://img.shields.io/npm/v/@gmod/hic.svg?style=flat-square)](https://npmjs.org/package/@gmod/hic)
4
+ ![Build Status](https://img.shields.io/github/actions/workflow/status/GMOD/hic/publish.yml?branch=main)
5
+
6
+ Read `.hic` contact matrix files, in Node.js or the browser.
7
+
8
+ A fork of [hic-straw](https://github.com/aidenlab/hic-straw), rewritten in
9
+ TypeScript and retuned for a genome browser's access pattern — many region
10
+ pairs per fetch, arbitrary viewport windows, and contacts handed to a GPU
11
+ renderer. See [docs/optimizations.md](docs/optimizations.md) for what changed
12
+ and why.
13
+
14
+ ## Install
15
+
16
+ $ npm install @gmod/hic
17
+
18
+ ## Usage
19
+
20
+ ```js
21
+ import { HicFile } from '@gmod/hic'
22
+
23
+ const hic = new HicFile({ path: 'path/to/file.hic' })
24
+
25
+ const meta = await hic.getMetaData()
26
+ // { version, genome, chromosomes: [{ index, name, size }], resolutions }
27
+
28
+ await hic.getNormalizationOptions() // ['NONE', 'VC', 'VC_SQRT', 'KR', 'SCALE']
29
+
30
+ const region = { chr: '1', start: 0, end: 20_000_000 }
31
+ const { records, appliedNormalization, transposed } =
32
+ await hic.getContactRecords('KR', region, region, 'BP', 2_500_000)
33
+
34
+ // records is struct-of-arrays: three parallel typed arrays
35
+ records.bin1 // Int32Array of bin indices along region1
36
+ records.bin2 // Int32Array of bin indices along region2
37
+ records.counts // Float32Array of contact counts
38
+ ```
39
+
40
+ In the browser, or for a file over HTTP, pass a `filehandle` from
41
+ [`generic-filehandle2`](https://www.npmjs.com/package/generic-filehandle2)
42
+ instead of a `path`:
43
+
44
+ ```js
45
+ import { RemoteFile } from 'generic-filehandle2'
46
+
47
+ const hic = new HicFile({
48
+ filehandle: new RemoteFile('https://example.com/file.hic'),
49
+ })
50
+ ```
51
+
52
+ Anything that can read `length` bytes at `position` works too, which is the
53
+ hook for a caller with its own IO layer or cache:
54
+
55
+ ```js
56
+ const hic = new HicFile({
57
+ reader: { read: (position, length) => myCache.read(position, length) },
58
+ })
59
+ ```
60
+
61
+ See [docs/api.md](docs/api.md) for the full API reference.
62
+
63
+ ## Notes
64
+
65
+ - Bin indices are **absolute for their chromosome**, not relative to the
66
+ requested region.
67
+ - A `.hic` stores only the `bin1 <= bin2` half of the matrix, so a query whose
68
+ x window sits left of its y window is swapped before it is issued;
69
+ `transposed` says so, and `bin1` then runs along `region2`.
70
+ - `appliedNormalization` is what was actually applied, which is not always what
71
+ was asked for — normalization vectors are stored per (type, chromosome, unit,
72
+ binsize), so a file can offer KR at 5 kb and nothing at 2.5 Mb.
73
+ - `BP` is the only unit supported; the FRAG code paths were dropped.
74
+
75
+ ## Academic use
76
+
77
+ This package was written with funding from the [NHGRI](http://genome.gov) as
78
+ part of the [JBrowse](http://jbrowse.org) project. If you use it in an academic
79
+ project that you publish, please cite the most recent JBrowse paper, which will
80
+ be linked from [jbrowse.org](http://jbrowse.org).
81
+
82
+ If you use `.hic` files, please also cite the Juicer/Juicebox papers from the
83
+ [Aiden Lab](https://github.com/aidenlab), whose hic-straw this is derived from.
84
+
85
+ ## Contributing
86
+
87
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development and release workflow.
88
+
89
+ ## License
90
+
91
+ MIT, as is upstream hic-straw. See [LICENSE](LICENSE).
@@ -0,0 +1,24 @@
1
+ import type { HicRegion } from './types.ts';
2
+ /**
3
+ * Half-open `[first, last+1)` range of bin indices **overlapping** a region.
4
+ *
5
+ * Upstream hic-straw compares bin indices against the raw fractional quotients
6
+ * `start/binsize` and `end/binsize`, which is exact only for the
7
+ * chromosome-aligned queries juicebox issues. A genome browser queries
8
+ * arbitrary viewport windows, where `start % binsize !== 0` essentially always
9
+ * holds, and the raw quotient then (a) drops the bin straddling the region's
10
+ * start — a missing column at the left edge of every block, jittering as the
11
+ * user pans — while keeping the one straddling the end, and (b) selects
12
+ * *nothing at all* for a region narrower than one bin, which is reachable by
13
+ * locking a coarse binsize while zoomed in.
14
+ *
15
+ * Flooring/ceiling also matches `getNormVectors`, which already builds its
16
+ * vector slice over `[floor(start/binsize), ceil(end/binsize))` — so
17
+ * `rec.bin1 - norm.offset1` indexes exactly the values that were fetched.
18
+ *
19
+ * Lives in its own module because both the record filter (`hicFile`) and the
20
+ * block-number selection (`matrixZoomData`) must agree on the window: widening
21
+ * only one of them still drops records, since a bin can sit in a block the
22
+ * other never asked for.
23
+ */
24
+ export declare function binWindow(region: HicRegion, binsize: number): readonly [number, number];
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.binWindow = binWindow;
4
+ /**
5
+ * Half-open `[first, last+1)` range of bin indices **overlapping** a region.
6
+ *
7
+ * Upstream hic-straw compares bin indices against the raw fractional quotients
8
+ * `start/binsize` and `end/binsize`, which is exact only for the
9
+ * chromosome-aligned queries juicebox issues. A genome browser queries
10
+ * arbitrary viewport windows, where `start % binsize !== 0` essentially always
11
+ * holds, and the raw quotient then (a) drops the bin straddling the region's
12
+ * start — a missing column at the left edge of every block, jittering as the
13
+ * user pans — while keeping the one straddling the end, and (b) selects
14
+ * *nothing at all* for a region narrower than one bin, which is reachable by
15
+ * locking a coarse binsize while zoomed in.
16
+ *
17
+ * Flooring/ceiling also matches `getNormVectors`, which already builds its
18
+ * vector slice over `[floor(start/binsize), ceil(end/binsize))` — so
19
+ * `rec.bin1 - norm.offset1` indexes exactly the values that were fetched.
20
+ *
21
+ * Lives in its own module because both the record filter (`hicFile`) and the
22
+ * block-number selection (`matrixZoomData`) must agree on the window: widening
23
+ * only one of them still drops records, since a bin can sit in a block the
24
+ * other never asked for.
25
+ */
26
+ function binWindow(region, binsize) {
27
+ return [
28
+ Math.floor(region.start / binsize),
29
+ Math.ceil(region.end / binsize),
30
+ ];
31
+ }
32
+ //# sourceMappingURL=binWindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binWindow.js","sourceRoot":"","sources":["../src/binWindow.ts"],"names":[],"mappings":";;AAwBA,8BAKC;AA3BD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,SAAS,CAAC,MAAiB,EAAE,OAAe;IAC1D,OAAO;QACL,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC;KACvB,CAAA;AACZ,CAAC"}
@@ -0,0 +1,15 @@
1
+ export default class BinaryParser {
2
+ position: number;
3
+ private view;
4
+ private length;
5
+ private littleEndian;
6
+ constructor(dataView: DataView, littleEndian?: boolean);
7
+ available(): number;
8
+ getByte(): number;
9
+ getShort(): number;
10
+ getInt(): number;
11
+ getLong(): number;
12
+ getString(): string;
13
+ getFloat(): number;
14
+ getDouble(): number;
15
+ }
package/dist/binary.js ADDED
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class BinaryParser {
4
+ position = 0;
5
+ view;
6
+ length;
7
+ littleEndian;
8
+ constructor(dataView, littleEndian = true) {
9
+ this.view = dataView;
10
+ this.length = dataView.byteLength;
11
+ this.littleEndian = littleEndian;
12
+ }
13
+ available() {
14
+ return this.length - this.position;
15
+ }
16
+ getByte() {
17
+ const ret = this.view.getUint8(this.position);
18
+ this.position += 1;
19
+ return ret;
20
+ }
21
+ getShort() {
22
+ const ret = this.view.getInt16(this.position, this.littleEndian);
23
+ this.position += 2;
24
+ return ret;
25
+ }
26
+ getInt() {
27
+ const ret = this.view.getInt32(this.position, this.littleEndian);
28
+ this.position += 4;
29
+ return ret;
30
+ }
31
+ // No native 64-bit read that lands in a `number`, so compose two 32-bit words
32
+ // (deliberately not getBigUint64 — every caller is a file offset or a count
33
+ // that stays well inside Number.MAX_SAFE_INTEGER, and a BigInt would have to
34
+ // be converted back at each use site). hic-straw accumulated byte-by-byte
35
+ // through a throwaway 8-element array, which is a per-entry allocation in the
36
+ // block-index parse loop.
37
+ getLong() {
38
+ const p = this.position;
39
+ const le = this.littleEndian;
40
+ const first = this.view.getUint32(p, le);
41
+ const second = this.view.getUint32(p + 4, le);
42
+ this.position += 8;
43
+ // little-endian puts the low word first, big-endian the high word
44
+ return le ? second * 0x1_0000_0000 + first : first * 0x1_0000_0000 + second;
45
+ }
46
+ getString() {
47
+ let s = '';
48
+ let c = this.view.getUint8(this.position++);
49
+ while (c !== 0) {
50
+ s += String.fromCharCode(c);
51
+ c = this.view.getUint8(this.position++);
52
+ }
53
+ return s;
54
+ }
55
+ getFloat() {
56
+ const ret = this.view.getFloat32(this.position, this.littleEndian);
57
+ this.position += 4;
58
+ return ret;
59
+ }
60
+ getDouble() {
61
+ const ret = this.view.getFloat64(this.position, this.littleEndian);
62
+ this.position += 8;
63
+ return ret;
64
+ }
65
+ }
66
+ exports.default = BinaryParser;
67
+ //# sourceMappingURL=binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary.js","sourceRoot":"","sources":["../src/binary.ts"],"names":[],"mappings":";;AAAA,MAAqB,YAAY;IAC/B,QAAQ,GAAG,CAAC,CAAA;IACJ,IAAI,CAAU;IACd,MAAM,CAAQ;IACd,YAAY,CAAS;IAE7B,YAAY,QAAkB,EAAE,YAAY,GAAG,IAAI;QACjD,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAA;QACpB,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAA;QACjC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAA;IAClC,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAA;IACpC,CAAC;IAED,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC7C,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;QAClB,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,QAAQ;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QAChE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;QAClB,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,MAAM;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QAChE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;QAClB,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,0EAA0E;IAC1E,8EAA8E;IAC9E,0BAA0B;IAC1B,OAAO;QACL,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAA;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAA;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAA;QAC7C,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;QAClB,kEAAkE;QAClE,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,aAAa,GAAG,MAAM,CAAA;IAC7E,CAAC;IAED,SAAS;QACP,IAAI,CAAC,GAAG,EAAE,CAAA;QACV,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC3C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACf,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;YAC3B,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QACzC,CAAC;QACD,OAAO,CAAC,CAAA;IACV,CAAC;IAED,QAAQ;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QAClE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;QAClB,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,SAAS;QACP,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QAClE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;QAClB,OAAO,GAAG,CAAA;IACZ,CAAC;CACF;AAvED,+BAuEC"}
@@ -0,0 +1,13 @@
1
+ import type { Reader } from './types.ts';
2
+ export default class BufferedFile {
3
+ private file;
4
+ private size;
5
+ private bufferStart;
6
+ private bufferLength;
7
+ private buffer;
8
+ constructor(args: {
9
+ file: Reader;
10
+ size?: number;
11
+ });
12
+ read(position: number, length: number): Promise<ArrayBuffer>;
13
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function concatBuffers(buffer1, buffer2) {
4
+ const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
5
+ tmp.set(new Uint8Array(buffer1), 0);
6
+ tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
7
+ return tmp.buffer;
8
+ }
9
+ class BufferedFile {
10
+ file;
11
+ size;
12
+ bufferStart = 0;
13
+ bufferLength = 0;
14
+ buffer;
15
+ constructor(args) {
16
+ this.file = args.file;
17
+ this.size = args.size ?? 64000;
18
+ }
19
+ async read(position, length) {
20
+ const start = position;
21
+ const end = position + length;
22
+ const bufferStart = this.bufferStart;
23
+ const bufferEnd = this.bufferStart + this.bufferLength;
24
+ const buf = this.buffer;
25
+ let result;
26
+ if (length > this.size) {
27
+ // Request larger than max buffer size, pass through to underlying file
28
+ this.buffer = undefined;
29
+ this.bufferStart = 0;
30
+ this.bufferLength = 0;
31
+ result = await this.file.read(position, length);
32
+ }
33
+ else if (buf && start >= bufferStart && end <= bufferEnd) {
34
+ // Request within buffer bounds
35
+ const sliceStart = start - bufferStart;
36
+ result = buf.slice(sliceStart, sliceStart + length);
37
+ }
38
+ else if (buf && start < bufferStart && end > bufferStart) {
39
+ // Overlap left (unexpected in straw); we don't adjust the buffer
40
+ const l1 = bufferStart - start;
41
+ const a1 = await this.file.read(position, l1);
42
+ const l2 = length - l1;
43
+ if (l2 > 0) {
44
+ result = concatBuffers(a1, buf.slice(0, l2));
45
+ }
46
+ else {
47
+ result = a1;
48
+ }
49
+ }
50
+ else if (buf && start < bufferEnd && end > bufferEnd) {
51
+ // Overlap right
52
+ const l1 = bufferEnd - start;
53
+ const a1 = buf.slice(this.bufferLength - l1, this.bufferLength);
54
+ const l2 = length - l1;
55
+ if (l2 > 0) {
56
+ const next = await this.file.read(bufferEnd, this.size);
57
+ this.buffer = next;
58
+ this.bufferStart = bufferEnd;
59
+ this.bufferLength = next.byteLength;
60
+ result = concatBuffers(a1, next.slice(0, l2));
61
+ }
62
+ else {
63
+ result = a1;
64
+ }
65
+ }
66
+ else {
67
+ // No overlap with buffer
68
+ const next = await this.file.read(position, this.size);
69
+ this.buffer = next;
70
+ this.bufferStart = position;
71
+ this.bufferLength = next.byteLength;
72
+ result = next.slice(0, length);
73
+ }
74
+ return result;
75
+ }
76
+ }
77
+ exports.default = BufferedFile;
78
+ //# sourceMappingURL=bufferedFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bufferedFile.js","sourceRoot":"","sources":["../src/bufferedFile.ts"],"names":[],"mappings":";;AAEA,SAAS,aAAa,CAAC,OAAoB,EAAE,OAAoB;IAC/D,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACnE,GAAG,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;IACnC,GAAG,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACpD,OAAO,GAAG,CAAC,MAAM,CAAA;AACnB,CAAC;AAED,MAAqB,YAAY;IACvB,IAAI,CAAQ;IACZ,IAAI,CAAQ;IACZ,WAAW,GAAG,CAAC,CAAA;IACf,YAAY,GAAG,CAAC,CAAA;IAChB,MAAM,CAAyB;IAEvC,YAAY,IAAqC;QAC/C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,KAAK,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAgB,EAAE,MAAc;QACzC,MAAM,KAAK,GAAG,QAAQ,CAAA;QACtB,MAAM,GAAG,GAAG,QAAQ,GAAG,MAAM,CAAA;QAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAA;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,YAAY,CAAA;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAA;QAEvB,IAAI,MAAmB,CAAA;QACvB,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACvB,uEAAuE;YACvE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;YACvB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAA;YACpB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAA;YACrB,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACjD,CAAC;aAAM,IAAI,GAAG,IAAI,KAAK,IAAI,WAAW,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;YAC3D,+BAA+B;YAC/B,MAAM,UAAU,GAAG,KAAK,GAAG,WAAW,CAAA;YACtC,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC,CAAA;QACrD,CAAC;aAAM,IAAI,GAAG,IAAI,KAAK,GAAG,WAAW,IAAI,GAAG,GAAG,WAAW,EAAE,CAAC;YAC3D,iEAAiE;YACjE,MAAM,EAAE,GAAG,WAAW,GAAG,KAAK,CAAA;YAC9B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;YAC7C,MAAM,EAAE,GAAG,MAAM,GAAG,EAAE,CAAA;YACtB,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACX,MAAM,GAAG,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;YAC9C,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,EAAE,CAAA;YACb,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,IAAI,KAAK,GAAG,SAAS,IAAI,GAAG,GAAG,SAAS,EAAE,CAAC;YACvD,gBAAgB;YAChB,MAAM,EAAE,GAAG,SAAS,GAAG,KAAK,CAAA;YAC5B,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;YAC/D,MAAM,EAAE,GAAG,MAAM,GAAG,EAAE,CAAA;YACtB,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;gBACvD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;gBAClB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAA;gBAC5B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAA;gBACnC,MAAM,GAAG,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;YAC/C,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,EAAE,CAAA;YACb,CAAC;QACH,CAAC;aAAM,CAAC;YACN,yBAAyB;YACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;YACtD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;YAClB,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAA;YAC3B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAA;YACnC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;QAChC,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAhED,+BAgEC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A run of contact records, held struct-of-arrays.
3
+ *
4
+ * hic-straw models a contact as an object, and the whole read path inherits
5
+ * that shape: a block holds `ContactRecord[]` and the window filter rebuilds
6
+ * the array. A renderer then has to unpack that into typed arrays anyway, so a
7
+ * contact was allocated several times over on its way to a vertex buffer — and
8
+ * a matrix is routinely millions of contacts.
9
+ *
10
+ * The per-contact allocation is the obvious cost, but the block cache is the
11
+ * expensive one: cached blocks are long-lived, so as objects they left millions
12
+ * of live pointers for every GC to trace for the rest of the session. Twelve
13
+ * bytes a contact here against ~50 for an object, and nothing for the collector
14
+ * to walk.
15
+ *
16
+ * The three arrays are always exactly the same length — `bin1.length` is the
17
+ * record count. Counts are float32 because that is what the file stores
18
+ * (`getFloat`/`getShort`) and what a GPU renderer takes, so carrying them as
19
+ * doubles only ever widened a value on its way back down to float32.
20
+ */
21
+ export interface ContactRecords {
22
+ bin1: Int32Array;
23
+ bin2: Int32Array;
24
+ counts: Float32Array;
25
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=contactRecords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contactRecords.js","sourceRoot":"","sources":["../src/contactRecords.ts"],"names":[],"mappings":""}
@@ -0,0 +1,94 @@
1
+ import BinaryParser from './binary.ts';
2
+ import Matrix from './matrix.ts';
3
+ import NormalizationVector from './normalizationVector.ts';
4
+ import type { ContactRecords } from './contactRecords.ts';
5
+ import type MatrixZoomData from './matrixZoomData.ts';
6
+ import type { BlockIndexEntry, HicMetadata, HicRegion, Reader } from './types.ts';
7
+ import type { GenericFilehandle } from 'generic-filehandle2';
8
+ /**
9
+ * Prefix of the error thrown when a region pair has no matrix at the requested
10
+ * binsize. A caller fetching many region pairs at once will want to drop that
11
+ * pair rather than fail the whole fetch, and it can only tell this apart from a
12
+ * real failure by the message — so it is exported instead of hand-copied.
13
+ */
14
+ export declare const NO_DATA_FOR_RESOLUTION = "No data available for resolution";
15
+ export interface HicConfig {
16
+ /** a `generic-filehandle2` handle, e.g. `RemoteFile` for a URL */
17
+ filehandle?: GenericFilehandle;
18
+ /** a local path, node only */
19
+ path?: string;
20
+ /** any object that can read `length` bytes at `position` */
21
+ reader?: Reader;
22
+ /**
23
+ * `"position,size"` of the normalization vector index, if it is known. Skips
24
+ * the walk through the expected-value vectors that locating it otherwise
25
+ * costs on a pre-v9 file.
26
+ */
27
+ nvi?: string;
28
+ }
29
+ interface Block {
30
+ blockNumber: number;
31
+ records: ContactRecords;
32
+ }
33
+ export declare class HicFile {
34
+ private config;
35
+ private file;
36
+ private normVectorCache;
37
+ private normalizationTypes;
38
+ private matrixCache;
39
+ private blockCache;
40
+ private normVectorIndexPosition;
41
+ private normVectorIndexSize;
42
+ private initPromise;
43
+ private normVectorIndexP;
44
+ private version;
45
+ private genomeId;
46
+ private footerPosition;
47
+ normExpectedValueVectorsPosition: number | undefined;
48
+ private normVectorIndex;
49
+ private chromosomes;
50
+ private chromosomeIndexMap;
51
+ private chrAliasTable;
52
+ private bpResolutions;
53
+ private masterIndex;
54
+ private meta;
55
+ constructor(config: HicConfig);
56
+ init(): Promise<void>;
57
+ getMetaData(): Promise<HicMetadata>;
58
+ readHeaderAndFooter(): Promise<void>;
59
+ readFooter(): Promise<void>;
60
+ getMatrix(chrIdx1: number, chrIdx2: number): Promise<Matrix | undefined>;
61
+ readMatrix(chrIdx1: number, chrIdx2: number): Promise<Matrix | undefined>;
62
+ getContactRecords(normalization: string, region1: HicRegion, region2: HicRegion, units: string, binsize: number): Promise<{
63
+ records: ContactRecords;
64
+ appliedNormalization: string;
65
+ transposed: boolean;
66
+ }>;
67
+ private getNormVectors;
68
+ getBlocks(region1: HicRegion, region2: HicRegion, binSize: number): Promise<(Block | undefined)[]>;
69
+ readBlock(blockNumber: number, zd: MatrixZoomData): Promise<Block | undefined>;
70
+ /**
71
+ * Decode one block's records straight into typed arrays.
72
+ *
73
+ * Every encoding here knows its length up front, so each array is allocated
74
+ * once and filled by a write cursor. Where a filter can drop records (the
75
+ * dense encoding's empty cells) the arrays are sized to the upper bound and
76
+ * copied down to the true length at the end — blocks are cached for the life
77
+ * of the session, so it is worth one memcpy not to pin an oversized buffer.
78
+ */
79
+ private parseBlockRecords;
80
+ getNormalizationVector(type: string, chr: string, unit: string, binSize: number): Promise<NormalizationVector | undefined>;
81
+ private readNormalizationVector;
82
+ getNormVectorIndex(): Promise<Record<string, BlockIndexEntry> | undefined>;
83
+ private loadNormVectorIndex;
84
+ getNormalizationOptions(): Promise<string[]>;
85
+ readNormVectorIndex(range: {
86
+ start: number;
87
+ size: number;
88
+ }): Promise<Record<string, BlockIndexEntry>>;
89
+ readNormExpectedValuesAndNormVectorIndex(): Promise<void>;
90
+ skipExpectedValues(start: number): Promise<number>;
91
+ parseNormVectorEntry(binaryParser: BinaryParser): void;
92
+ getFileChrName(chrAlias: string): string;
93
+ }
94
+ export {};