@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.
- package/LICENSE +25 -0
- package/README.md +91 -0
- package/dist/binWindow.d.ts +24 -0
- package/dist/binWindow.js +32 -0
- package/dist/binWindow.js.map +1 -0
- package/dist/binary.d.ts +15 -0
- package/dist/binary.js +67 -0
- package/dist/binary.js.map +1 -0
- package/dist/bufferedFile.d.ts +13 -0
- package/dist/bufferedFile.js +78 -0
- package/dist/bufferedFile.js.map +1 -0
- package/dist/contactRecords.d.ts +25 -0
- package/dist/contactRecords.js +3 -0
- package/dist/contactRecords.js.map +1 -0
- package/dist/hicFile.d.ts +94 -0
- package/dist/hicFile.js +769 -0
- package/dist/hicFile.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/lru.d.ts +29 -0
- package/dist/lru.js +55 -0
- package/dist/lru.js.map +1 -0
- package/dist/matrix.d.ts +9 -0
- package/dist/matrix.js +41 -0
- package/dist/matrix.js.map +1 -0
- package/dist/matrixZoomData.d.ts +14 -0
- package/dist/matrixZoomData.js +96 -0
- package/dist/matrixZoomData.js.map +1 -0
- package/dist/normalizationVector.d.ts +10 -0
- package/dist/normalizationVector.js +51 -0
- package/dist/normalizationVector.js.map +1 -0
- package/dist/package.json +1 -0
- package/dist/reader.d.ts +3 -0
- package/dist/reader.js +12 -0
- package/dist/reader.js.map +1 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/esm/binWindow.d.ts +24 -0
- package/esm/binWindow.js +29 -0
- package/esm/binWindow.js.map +1 -0
- package/esm/binary.d.ts +15 -0
- package/esm/binary.js +64 -0
- package/esm/binary.js.map +1 -0
- package/esm/bufferedFile.d.ts +13 -0
- package/esm/bufferedFile.js +75 -0
- package/esm/bufferedFile.js.map +1 -0
- package/esm/contactRecords.d.ts +25 -0
- package/esm/contactRecords.js +2 -0
- package/esm/contactRecords.js.map +1 -0
- package/esm/hicFile.d.ts +94 -0
- package/esm/hicFile.js +762 -0
- package/esm/hicFile.js.map +1 -0
- package/esm/index.d.ts +5 -0
- package/esm/index.js +3 -0
- package/esm/index.js.map +1 -0
- package/esm/lru.d.ts +29 -0
- package/esm/lru.js +52 -0
- package/esm/lru.js.map +1 -0
- package/esm/matrix.d.ts +9 -0
- package/esm/matrix.js +35 -0
- package/esm/matrix.js.map +1 -0
- package/esm/matrixZoomData.d.ts +14 -0
- package/esm/matrixZoomData.js +93 -0
- package/esm/matrixZoomData.js.map +1 -0
- package/esm/normalizationVector.d.ts +10 -0
- package/esm/normalizationVector.js +45 -0
- package/esm/normalizationVector.js.map +1 -0
- package/esm/reader.d.ts +3 -0
- package/esm/reader.js +9 -0
- package/esm/reader.js.map +1 -0
- package/esm/types.d.ts +35 -0
- package/esm/types.js +2 -0
- package/esm/types.js.map +1 -0
- package/package.json +73 -0
- package/src/binWindow.ts +30 -0
- package/src/binary.ts +72 -0
- package/src/bufferedFile.ts +74 -0
- package/src/contactRecords.ts +25 -0
- package/src/hicFile.ts +943 -0
- package/src/index.ts +12 -0
- package/src/lru.ts +77 -0
- package/src/matrix.ts +43 -0
- package/src/matrixZoomData.ts +133 -0
- package/src/normalizationVector.ts +58 -0
- package/src/reader.ts +11 -0
- 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
|
+
[](https://npmjs.org/package/@gmod/hic)
|
|
4
|
+

|
|
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"}
|
package/dist/binary.d.ts
ADDED
|
@@ -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":"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"}
|
|
@@ -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 @@
|
|
|
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 {};
|