@gmod/hic 1.3.2 → 2.1.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 +17 -32
- package/dist/bufferedFile.js +8 -25
- package/dist/bufferedFile.js.map +1 -1
- package/dist/hicFile.d.ts +22 -9
- package/dist/hicFile.js +218 -175
- package/dist/hicFile.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/matrixZoomData.d.ts +3 -1
- package/dist/matrixZoomData.js +6 -3
- package/dist/matrixZoomData.js.map +1 -1
- package/dist/normalizationVector.d.ts +3 -1
- package/dist/normalizationVector.js +60 -39
- package/dist/normalizationVector.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/esm/bufferedFile.js +8 -25
- package/esm/bufferedFile.js.map +1 -1
- package/esm/hicFile.d.ts +22 -9
- package/esm/hicFile.js +218 -175
- package/esm/hicFile.js.map +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/matrixZoomData.d.ts +3 -1
- package/esm/matrixZoomData.js +6 -3
- package/esm/matrixZoomData.js.map +1 -1
- package/esm/normalizationVector.d.ts +3 -1
- package/esm/normalizationVector.js +60 -39
- package/esm/normalizationVector.js.map +1 -1
- package/esm/types.d.ts +16 -0
- package/package.json +1 -1
- package/src/bufferedFile.ts +8 -22
- package/src/hicFile.ts +303 -218
- package/src/index.ts +1 -0
- package/src/matrixZoomData.ts +6 -1
- package/src/normalizationVector.ts +77 -43
- package/src/types.ts +12 -0
- package/dist/lru.d.ts +0 -29
- package/dist/lru.js +0 -53
- package/dist/lru.js.map +0 -1
- package/esm/lru.d.ts +0 -29
- package/esm/lru.js +0 -50
- package/esm/lru.js.map +0 -1
- package/src/lru.ts +0 -75
package/src/index.ts
CHANGED
package/src/matrixZoomData.ts
CHANGED
|
@@ -7,6 +7,8 @@ export default class MatrixZoomData {
|
|
|
7
7
|
chr1: Chromosome
|
|
8
8
|
chr2: Chromosome
|
|
9
9
|
zoom: Zoom
|
|
10
|
+
/** the raw contacts summed over the whole map at this binsize */
|
|
11
|
+
sumCounts: number
|
|
10
12
|
blockBinCount: number
|
|
11
13
|
blockColumnCount: number
|
|
12
14
|
blockIndex: Record<number, BlockIndexEntry>
|
|
@@ -15,6 +17,7 @@ export default class MatrixZoomData {
|
|
|
15
17
|
chr1: Chromosome,
|
|
16
18
|
chr2: Chromosome,
|
|
17
19
|
zoom: Zoom,
|
|
20
|
+
sumCounts: number,
|
|
18
21
|
blockBinCount: number,
|
|
19
22
|
blockColumnCount: number,
|
|
20
23
|
blockIndex: Record<number, BlockIndexEntry>,
|
|
@@ -22,6 +25,7 @@ export default class MatrixZoomData {
|
|
|
22
25
|
this.chr1 = chr1
|
|
23
26
|
this.chr2 = chr2
|
|
24
27
|
this.zoom = zoom
|
|
28
|
+
this.sumCounts = sumCounts
|
|
25
29
|
this.blockBinCount = blockBinCount
|
|
26
30
|
this.blockColumnCount = blockColumnCount
|
|
27
31
|
this.blockIndex = blockIndex
|
|
@@ -104,7 +108,7 @@ export default class MatrixZoomData {
|
|
|
104
108
|
) {
|
|
105
109
|
const unit = dis.getString()
|
|
106
110
|
const zoomIndex = dis.getInt()
|
|
107
|
-
dis.getFloat()
|
|
111
|
+
const sumCounts = dis.getFloat()
|
|
108
112
|
dis.getFloat() // occupiedCellCount
|
|
109
113
|
dis.getFloat() // stdDev
|
|
110
114
|
dis.getFloat() // percent95
|
|
@@ -125,6 +129,7 @@ export default class MatrixZoomData {
|
|
|
125
129
|
chr1,
|
|
126
130
|
chr2,
|
|
127
131
|
{ index: zoomIndex, unit, binSize },
|
|
132
|
+
sumCounts,
|
|
128
133
|
blockBinCount,
|
|
129
134
|
blockColumnCount,
|
|
130
135
|
blockIndex,
|
|
@@ -4,9 +4,16 @@ import type { BaseOpts, Reader } from './types.ts'
|
|
|
4
4
|
|
|
5
5
|
const DOUBLE = 8
|
|
6
6
|
|
|
7
|
+
interface CachedValues {
|
|
8
|
+
start: number
|
|
9
|
+
end: number
|
|
10
|
+
values: Promise<Float64Array>
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
export default class NormalizationVector {
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
// Holds the in-flight read, so the two concurrent `getValues` calls of a
|
|
15
|
+
// same-chromosome pair share one read instead of both missing.
|
|
16
|
+
private cache: CachedValues | undefined
|
|
10
17
|
|
|
11
18
|
private file: Reader
|
|
12
19
|
private filePosition: number
|
|
@@ -25,50 +32,77 @@ export default class NormalizationVector {
|
|
|
25
32
|
this.dataType = dataType
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
async getValues(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//
|
|
35
|
+
async getValues(
|
|
36
|
+
start: number,
|
|
37
|
+
end: number,
|
|
38
|
+
opts?: BaseOpts,
|
|
39
|
+
): Promise<Float64Array> {
|
|
40
|
+
// The read clamps to `nValues`, so the cache can never cover past it.
|
|
41
|
+
// Comparing against the clamped bound keeps a request running off the end
|
|
42
|
+
// of the vector (an assembly refseq longer than the `.hic` recorded) from
|
|
43
|
+
// missing forever.
|
|
34
44
|
const wanted = Math.min(end, this.nValues)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
? await this.file.read(startPosition, n * this.dataType, opts)
|
|
56
|
-
: new ArrayBuffer(0)
|
|
57
|
-
const parser = new BinaryParser(new DataView(data))
|
|
58
|
-
const values = new Float64Array(n)
|
|
59
|
-
for (let i = 0; i < n; i++) {
|
|
60
|
-
values[i] =
|
|
61
|
-
this.dataType === DOUBLE ? parser.getDouble() : parser.getFloat()
|
|
45
|
+
let cache = this.cache
|
|
46
|
+
const joined = !!cache && start >= cache.start && wanted <= cache.end
|
|
47
|
+
if (!cache || !joined) {
|
|
48
|
+
cache = this.readRange(start, end, opts)
|
|
49
|
+
this.cache = cache
|
|
50
|
+
const pending = cache
|
|
51
|
+
pending.values.catch(() => {
|
|
52
|
+
if (this.cache === pending) {
|
|
53
|
+
this.cache = undefined
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
let values: Float64Array
|
|
58
|
+
try {
|
|
59
|
+
values = await cache.values
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// The pair that started a joined read passed its own signal, so that
|
|
62
|
+
// pair aborting says nothing about this caller.
|
|
63
|
+
if (!joined || opts?.signal?.aborted) {
|
|
64
|
+
throw e
|
|
62
65
|
}
|
|
63
|
-
this.
|
|
66
|
+
return this.getValues(start, end, opts)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// A view, not a copy. Both this and `subarray` clamp a request running past
|
|
70
|
+
// the end; an out-of-range index reads undefined, the product goes NaN, and
|
|
71
|
+
// the record filter drops it.
|
|
72
|
+
const sliceStart = start - cache.start
|
|
73
|
+
return values.subarray(sliceStart, sliceStart + (end - start))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private readRange(start: number, end: number, opts?: BaseOpts): CachedValues {
|
|
77
|
+
const adjustedEnd = Math.min(this.nValues, end + 1000)
|
|
78
|
+
// Clamped against `adjustedEnd` too, or a window starting past the vector
|
|
79
|
+
// gives a negative read length, which the reader turns into a RangeError
|
|
80
|
+
// instead of the empty answer the caller handles.
|
|
81
|
+
const adjustedStart = Math.min(Math.max(0, start - 1000), adjustedEnd)
|
|
82
|
+
const n = adjustedEnd - adjustedStart
|
|
83
|
+
return {
|
|
84
|
+
start: adjustedStart,
|
|
85
|
+
end: adjustedEnd,
|
|
86
|
+
values: this.readValues(adjustedStart, n, opts),
|
|
64
87
|
}
|
|
88
|
+
}
|
|
65
89
|
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
90
|
+
private async readValues(start: number, n: number, opts?: BaseOpts) {
|
|
91
|
+
// A zero-length range request on a remote file is still a round trip.
|
|
92
|
+
if (n <= 0) {
|
|
93
|
+
return new Float64Array(0)
|
|
94
|
+
}
|
|
95
|
+
const data = await this.file.read(
|
|
96
|
+
this.filePosition + start * this.dataType,
|
|
97
|
+
n * this.dataType,
|
|
98
|
+
opts,
|
|
99
|
+
)
|
|
100
|
+
const parser = new BinaryParser(new DataView(data))
|
|
101
|
+
const values = new Float64Array(n)
|
|
102
|
+
for (let i = 0; i < n; i++) {
|
|
103
|
+
values[i] =
|
|
104
|
+
this.dataType === DOUBLE ? parser.getDouble() : parser.getFloat()
|
|
105
|
+
}
|
|
106
|
+
return values
|
|
73
107
|
}
|
|
74
108
|
}
|
package/src/types.ts
CHANGED
|
@@ -68,6 +68,18 @@ export interface BlockIndexEntry {
|
|
|
68
68
|
size: number
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* The expected contact count for one region pair, the denominator of
|
|
73
|
+
* observed/expected. Within a chromosome it depends only on the distance in
|
|
74
|
+
* bins, so it is the expected-value vector's entries over the distances the
|
|
75
|
+
* pair spans: `values[|bin2 - bin1| - firstDistance]`, already divided by the
|
|
76
|
+
* chromosome's scale factor and holding the vector's last value past its end.
|
|
77
|
+
* Between chromosomes it is one number, the map's average contact.
|
|
78
|
+
*/
|
|
79
|
+
export type ExpectedValues =
|
|
80
|
+
| { kind: 'intra'; firstDistance: number; values: Float64Array }
|
|
81
|
+
| { kind: 'inter'; average: number }
|
|
82
|
+
|
|
71
83
|
export interface HicMetadata {
|
|
72
84
|
version: number
|
|
73
85
|
genome: string
|
package/dist/lru.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Optional second bound, for a cache whose entries differ wildly in size.
|
|
3
|
-
*
|
|
4
|
-
* An entry cap is a memory bound only while entries are interchangeable, and the
|
|
5
|
-
* block cache's are not: a block holds every contact in its bin square, so one
|
|
6
|
-
* can be a few thousand contacts or a few hundred thousand depending on binsize
|
|
7
|
-
* and distance from the diagonal. Capping entries therefore forced one number to
|
|
8
|
-
* answer two questions — how many blocks a fetch needs at once, and how much
|
|
9
|
-
* memory the biggest of them may hold — and the memory question won, which is
|
|
10
|
-
* what left the cache too small to serve a multi-region fetch (see hicFile.ts).
|
|
11
|
-
*
|
|
12
|
-
* Splitting them lets the entry cap track the working set while `maxBytes`
|
|
13
|
-
* stays the backstop it was standing in for.
|
|
14
|
-
*/
|
|
15
|
-
interface WeightOpts<V> {
|
|
16
|
-
maxBytes: number;
|
|
17
|
-
weigh: (value: V) => number;
|
|
18
|
-
}
|
|
19
|
-
export default class LRU<K, V> {
|
|
20
|
-
private max;
|
|
21
|
-
private map;
|
|
22
|
-
private weight;
|
|
23
|
-
private weightOpts;
|
|
24
|
-
constructor(max?: number, weightOpts?: WeightOpts<V>);
|
|
25
|
-
get(key: K): V | undefined;
|
|
26
|
-
set(key: K, val: V): void;
|
|
27
|
-
delete(key: K): void;
|
|
28
|
-
}
|
|
29
|
-
export {};
|
package/dist/lru.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
class LRU {
|
|
4
|
-
max;
|
|
5
|
-
map = new Map();
|
|
6
|
-
weight = 0;
|
|
7
|
-
weightOpts;
|
|
8
|
-
constructor(max = 10, weightOpts) {
|
|
9
|
-
this.max = max;
|
|
10
|
-
this.weightOpts = weightOpts;
|
|
11
|
-
}
|
|
12
|
-
get(key) {
|
|
13
|
-
const item = this.map.get(key);
|
|
14
|
-
if (item !== undefined) {
|
|
15
|
-
// refresh key
|
|
16
|
-
this.map.delete(key);
|
|
17
|
-
this.map.set(key, item);
|
|
18
|
-
}
|
|
19
|
-
return item;
|
|
20
|
-
}
|
|
21
|
-
set(key, val) {
|
|
22
|
-
if (this.map.has(key)) {
|
|
23
|
-
// refresh key, and drop its old weight — `set` on an existing key
|
|
24
|
-
// replaces the value, so the weight it contributed goes with it
|
|
25
|
-
this.delete(key);
|
|
26
|
-
}
|
|
27
|
-
this.map.set(key, val);
|
|
28
|
-
this.weight += this.weightOpts?.weigh(val) ?? 0;
|
|
29
|
-
// Entry cap first, then the byte budget. `size > 1` keeps a single entry
|
|
30
|
-
// larger than the whole budget rather than evicting it and looping forever
|
|
31
|
-
// on an empty map — a cache that can't hold the thing just asked for should
|
|
32
|
-
// still answer the caller that asked.
|
|
33
|
-
while (this.map.size > this.max ||
|
|
34
|
-
(this.weightOpts !== undefined &&
|
|
35
|
-
this.weight > this.weightOpts.maxBytes &&
|
|
36
|
-
this.map.size > 1)) {
|
|
37
|
-
const oldest = this.map.keys().next().value;
|
|
38
|
-
if (oldest === undefined) {
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
this.delete(oldest);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
delete(key) {
|
|
45
|
-
const existing = this.map.get(key);
|
|
46
|
-
if (existing !== undefined) {
|
|
47
|
-
this.weight -= this.weightOpts?.weigh(existing) ?? 0;
|
|
48
|
-
}
|
|
49
|
-
this.map.delete(key);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
exports.default = LRU;
|
|
53
|
-
//# sourceMappingURL=lru.js.map
|
package/dist/lru.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lru.js","sourceRoot":"","sources":["../src/lru.ts"],"names":[],"mappings":";;AAmBA,MAAqB,GAAG;IACd,GAAG,CAAQ;IACX,GAAG,GAAG,IAAI,GAAG,EAAQ,CAAA;IACrB,MAAM,GAAG,CAAC,CAAA;IACV,UAAU,CAA2B;IAE7C,YAAY,GAAG,GAAG,EAAE,EAAE,UAA0B;QAC9C,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;IAED,GAAG,CAAC,GAAM;QACR,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,cAAc;YACd,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,GAAG,CAAC,GAAM,EAAE,GAAM;QAChB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,kEAAkE;YAClE,gEAAgE;YAChE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAClB,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAE/C,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,sCAAsC;QACtC,OACE,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG;YACxB,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS;gBAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ;gBACtC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,EACpB,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;YAC3C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAK;YACP,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAED,MAAM,CAAC,GAAM;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACtD,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;CACF;AAvDD,sBAuDC"}
|
package/esm/lru.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Optional second bound, for a cache whose entries differ wildly in size.
|
|
3
|
-
*
|
|
4
|
-
* An entry cap is a memory bound only while entries are interchangeable, and the
|
|
5
|
-
* block cache's are not: a block holds every contact in its bin square, so one
|
|
6
|
-
* can be a few thousand contacts or a few hundred thousand depending on binsize
|
|
7
|
-
* and distance from the diagonal. Capping entries therefore forced one number to
|
|
8
|
-
* answer two questions — how many blocks a fetch needs at once, and how much
|
|
9
|
-
* memory the biggest of them may hold — and the memory question won, which is
|
|
10
|
-
* what left the cache too small to serve a multi-region fetch (see hicFile.ts).
|
|
11
|
-
*
|
|
12
|
-
* Splitting them lets the entry cap track the working set while `maxBytes`
|
|
13
|
-
* stays the backstop it was standing in for.
|
|
14
|
-
*/
|
|
15
|
-
interface WeightOpts<V> {
|
|
16
|
-
maxBytes: number;
|
|
17
|
-
weigh: (value: V) => number;
|
|
18
|
-
}
|
|
19
|
-
export default class LRU<K, V> {
|
|
20
|
-
private max;
|
|
21
|
-
private map;
|
|
22
|
-
private weight;
|
|
23
|
-
private weightOpts;
|
|
24
|
-
constructor(max?: number, weightOpts?: WeightOpts<V>);
|
|
25
|
-
get(key: K): V | undefined;
|
|
26
|
-
set(key: K, val: V): void;
|
|
27
|
-
delete(key: K): void;
|
|
28
|
-
}
|
|
29
|
-
export {};
|
package/esm/lru.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export default class LRU {
|
|
2
|
-
max;
|
|
3
|
-
map = new Map();
|
|
4
|
-
weight = 0;
|
|
5
|
-
weightOpts;
|
|
6
|
-
constructor(max = 10, weightOpts) {
|
|
7
|
-
this.max = max;
|
|
8
|
-
this.weightOpts = weightOpts;
|
|
9
|
-
}
|
|
10
|
-
get(key) {
|
|
11
|
-
const item = this.map.get(key);
|
|
12
|
-
if (item !== undefined) {
|
|
13
|
-
// refresh key
|
|
14
|
-
this.map.delete(key);
|
|
15
|
-
this.map.set(key, item);
|
|
16
|
-
}
|
|
17
|
-
return item;
|
|
18
|
-
}
|
|
19
|
-
set(key, val) {
|
|
20
|
-
if (this.map.has(key)) {
|
|
21
|
-
// refresh key, and drop its old weight — `set` on an existing key
|
|
22
|
-
// replaces the value, so the weight it contributed goes with it
|
|
23
|
-
this.delete(key);
|
|
24
|
-
}
|
|
25
|
-
this.map.set(key, val);
|
|
26
|
-
this.weight += this.weightOpts?.weigh(val) ?? 0;
|
|
27
|
-
// Entry cap first, then the byte budget. `size > 1` keeps a single entry
|
|
28
|
-
// larger than the whole budget rather than evicting it and looping forever
|
|
29
|
-
// on an empty map — a cache that can't hold the thing just asked for should
|
|
30
|
-
// still answer the caller that asked.
|
|
31
|
-
while (this.map.size > this.max ||
|
|
32
|
-
(this.weightOpts !== undefined &&
|
|
33
|
-
this.weight > this.weightOpts.maxBytes &&
|
|
34
|
-
this.map.size > 1)) {
|
|
35
|
-
const oldest = this.map.keys().next().value;
|
|
36
|
-
if (oldest === undefined) {
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
this.delete(oldest);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
delete(key) {
|
|
43
|
-
const existing = this.map.get(key);
|
|
44
|
-
if (existing !== undefined) {
|
|
45
|
-
this.weight -= this.weightOpts?.weigh(existing) ?? 0;
|
|
46
|
-
}
|
|
47
|
-
this.map.delete(key);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=lru.js.map
|
package/esm/lru.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lru.js","sourceRoot":"","sources":["../src/lru.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,OAAO,OAAO,GAAG;IACd,GAAG,CAAQ;IACX,GAAG,GAAG,IAAI,GAAG,EAAQ,CAAA;IACrB,MAAM,GAAG,CAAC,CAAA;IACV,UAAU,CAA2B;IAE7C,YAAY,GAAG,GAAG,EAAE,EAAE,UAA0B;QAC9C,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;IAED,GAAG,CAAC,GAAM;QACR,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,cAAc;YACd,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,GAAG,CAAC,GAAM,EAAE,GAAM;QAChB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,kEAAkE;YAClE,gEAAgE;YAChE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAClB,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAE/C,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,sCAAsC;QACtC,OACE,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG;YACxB,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS;gBAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ;gBACtC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,EACpB,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;YAC3C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAK;YACP,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAED,MAAM,CAAC,GAAM;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACtD,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;CACF"}
|
package/src/lru.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Optional second bound, for a cache whose entries differ wildly in size.
|
|
3
|
-
*
|
|
4
|
-
* An entry cap is a memory bound only while entries are interchangeable, and the
|
|
5
|
-
* block cache's are not: a block holds every contact in its bin square, so one
|
|
6
|
-
* can be a few thousand contacts or a few hundred thousand depending on binsize
|
|
7
|
-
* and distance from the diagonal. Capping entries therefore forced one number to
|
|
8
|
-
* answer two questions — how many blocks a fetch needs at once, and how much
|
|
9
|
-
* memory the biggest of them may hold — and the memory question won, which is
|
|
10
|
-
* what left the cache too small to serve a multi-region fetch (see hicFile.ts).
|
|
11
|
-
*
|
|
12
|
-
* Splitting them lets the entry cap track the working set while `maxBytes`
|
|
13
|
-
* stays the backstop it was standing in for.
|
|
14
|
-
*/
|
|
15
|
-
interface WeightOpts<V> {
|
|
16
|
-
maxBytes: number
|
|
17
|
-
weigh: (value: V) => number
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default class LRU<K, V> {
|
|
21
|
-
private max: number
|
|
22
|
-
private map = new Map<K, V>()
|
|
23
|
-
private weight = 0
|
|
24
|
-
private weightOpts: WeightOpts<V> | undefined
|
|
25
|
-
|
|
26
|
-
constructor(max = 10, weightOpts?: WeightOpts<V>) {
|
|
27
|
-
this.max = max
|
|
28
|
-
this.weightOpts = weightOpts
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get(key: K) {
|
|
32
|
-
const item = this.map.get(key)
|
|
33
|
-
if (item !== undefined) {
|
|
34
|
-
// refresh key
|
|
35
|
-
this.map.delete(key)
|
|
36
|
-
this.map.set(key, item)
|
|
37
|
-
}
|
|
38
|
-
return item
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
set(key: K, val: V) {
|
|
42
|
-
if (this.map.has(key)) {
|
|
43
|
-
// refresh key, and drop its old weight — `set` on an existing key
|
|
44
|
-
// replaces the value, so the weight it contributed goes with it
|
|
45
|
-
this.delete(key)
|
|
46
|
-
}
|
|
47
|
-
this.map.set(key, val)
|
|
48
|
-
this.weight += this.weightOpts?.weigh(val) ?? 0
|
|
49
|
-
|
|
50
|
-
// Entry cap first, then the byte budget. `size > 1` keeps a single entry
|
|
51
|
-
// larger than the whole budget rather than evicting it and looping forever
|
|
52
|
-
// on an empty map — a cache that can't hold the thing just asked for should
|
|
53
|
-
// still answer the caller that asked.
|
|
54
|
-
while (
|
|
55
|
-
this.map.size > this.max ||
|
|
56
|
-
(this.weightOpts !== undefined &&
|
|
57
|
-
this.weight > this.weightOpts.maxBytes &&
|
|
58
|
-
this.map.size > 1)
|
|
59
|
-
) {
|
|
60
|
-
const oldest = this.map.keys().next().value
|
|
61
|
-
if (oldest === undefined) {
|
|
62
|
-
break
|
|
63
|
-
}
|
|
64
|
-
this.delete(oldest)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
delete(key: K) {
|
|
69
|
-
const existing = this.map.get(key)
|
|
70
|
-
if (existing !== undefined) {
|
|
71
|
-
this.weight -= this.weightOpts?.weigh(existing) ?? 0
|
|
72
|
-
}
|
|
73
|
-
this.map.delete(key)
|
|
74
|
-
}
|
|
75
|
-
}
|