@gmod/gbz-base 1.0.0 → 2.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/README.md +166 -26
- package/bin/query.js +0 -0
- package/dist/cli.js +76 -28
- package/dist/cli.js.map +1 -1
- package/dist/db.d.ts +26 -9
- package/dist/db.js +158 -24
- package/dist/db.js.map +1 -1
- package/dist/gbwt/record.d.ts +4 -0
- package/dist/gbwt/record.js +22 -0
- package/dist/gbwt/record.js.map +1 -1
- package/dist/graphName.d.ts +9 -0
- package/dist/graphName.js +83 -0
- package/dist/graphName.js.map +1 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/pathName.d.ts +17 -0
- package/dist/pathName.js +33 -0
- package/dist/pathName.js.map +1 -0
- package/dist/query.d.ts +9 -9
- package/dist/query.js +23 -17
- package/dist/query.js.map +1 -1
- package/dist/sqlite/btree.d.ts +2 -0
- package/dist/sqlite/btree.js +55 -20
- package/dist/sqlite/btree.js.map +1 -1
- package/dist/sqlite/pager.d.ts +5 -0
- package/dist/sqlite/pager.js +65 -8
- package/dist/sqlite/pager.js.map +1 -1
- package/dist/subgraph.d.ts +34 -11
- package/dist/subgraph.js +346 -87
- package/dist/subgraph.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +85 -24
- package/src/db.ts +225 -33
- package/src/gbwt/record.ts +23 -0
- package/src/graphName.ts +94 -0
- package/src/index.ts +15 -10
- package/src/pathName.ts +54 -0
- package/src/query.ts +41 -29
- package/src/sqlite/btree.ts +68 -19
- package/src/sqlite/pager.ts +78 -8
- package/src/subgraph.ts +430 -114
- package/tools/haplotype-index/src/main.rs +205 -163
package/src/sqlite/btree.ts
CHANGED
|
@@ -15,8 +15,20 @@ interface PageHeader {
|
|
|
15
15
|
cellPointers: number
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function readUint16(page: Uint8Array, offset: number) {
|
|
19
|
+
return page[offset]! * 256 + page[offset + 1]!
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readUint32(page: Uint8Array, offset: number) {
|
|
23
|
+
return (
|
|
24
|
+
page[offset]! * 16777216 +
|
|
25
|
+
page[offset + 1]! * 65536 +
|
|
26
|
+
page[offset + 2]! * 256 +
|
|
27
|
+
page[offset + 3]!
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
function readHeader(page: Uint8Array, start: number): PageHeader {
|
|
19
|
-
const view = new DataView(page.buffer, page.byteOffset, page.byteLength)
|
|
20
32
|
const type = page[start]
|
|
21
33
|
if (
|
|
22
34
|
type !== INTERIOR_INDEX &&
|
|
@@ -29,25 +41,27 @@ function readHeader(page: Uint8Array, start: number): PageHeader {
|
|
|
29
41
|
const interior = type === INTERIOR_INDEX || type === INTERIOR_TABLE
|
|
30
42
|
return {
|
|
31
43
|
type,
|
|
32
|
-
cellCount:
|
|
33
|
-
rightChild: interior ?
|
|
44
|
+
cellCount: readUint16(page, start + 3),
|
|
45
|
+
rightChild: interior ? readUint32(page, start + 8) : 0,
|
|
34
46
|
cellPointers: start + (interior ? 12 : 8),
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
49
|
|
|
38
50
|
function cellOffset(page: Uint8Array, header: PageHeader, index: number) {
|
|
39
|
-
|
|
40
|
-
return view.getUint16(header.cellPointers + 2 * index)
|
|
51
|
+
return readUint16(page, header.cellPointers + 2 * index)
|
|
41
52
|
}
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
interface IndexCell {
|
|
55
|
+
values: SqlValue[]
|
|
56
|
+
leftChild: number
|
|
46
57
|
}
|
|
47
58
|
|
|
59
|
+
const MAX_DECODED_INDEX_PAGES = 4096
|
|
60
|
+
|
|
48
61
|
export class BTree {
|
|
49
62
|
private readonly usable: number
|
|
50
63
|
private pager: Pager
|
|
64
|
+
private decodedIndexPages = new Map<number, (IndexCell | undefined)[]>()
|
|
51
65
|
|
|
52
66
|
constructor(pager: Pager, reservedBytes: number) {
|
|
53
67
|
this.pager = pager
|
|
@@ -167,19 +181,54 @@ export class BTree {
|
|
|
167
181
|
}
|
|
168
182
|
}
|
|
169
183
|
} else {
|
|
184
|
+
const children: number[] = []
|
|
170
185
|
for (let i = 0; i < header.cellCount; i++) {
|
|
171
|
-
|
|
186
|
+
children.push(readUint32(page, cellOffset(page, header, i)))
|
|
187
|
+
}
|
|
188
|
+
children.push(header.rightChild)
|
|
189
|
+
this.pager.prefetch(children)
|
|
190
|
+
for (const child of children) {
|
|
191
|
+
yield* this.tableScan(child)
|
|
172
192
|
}
|
|
173
|
-
yield* this.tableScan(header.rightChild)
|
|
174
193
|
}
|
|
175
194
|
}
|
|
176
195
|
|
|
177
|
-
private
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
196
|
+
private decodedCells(pageNumber: number, header: PageHeader) {
|
|
197
|
+
let cells = this.decodedIndexPages.get(pageNumber)
|
|
198
|
+
if (cells) {
|
|
199
|
+
this.decodedIndexPages.delete(pageNumber)
|
|
200
|
+
} else {
|
|
201
|
+
cells = new Array<IndexCell | undefined>(header.cellCount)
|
|
202
|
+
if (this.decodedIndexPages.size >= MAX_DECODED_INDEX_PAGES) {
|
|
203
|
+
const oldest = this.decodedIndexPages.keys().next().value
|
|
204
|
+
if (oldest !== undefined) {
|
|
205
|
+
this.decodedIndexPages.delete(oldest)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.decodedIndexPages.set(pageNumber, cells)
|
|
210
|
+
return cells
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private async indexCell(
|
|
214
|
+
pageNumber: number,
|
|
215
|
+
page: Uint8Array,
|
|
216
|
+
header: PageHeader,
|
|
217
|
+
index: number,
|
|
218
|
+
): Promise<IndexCell> {
|
|
219
|
+
const cells = this.decodedCells(pageNumber, header)
|
|
220
|
+
let cell = cells[index]
|
|
221
|
+
if (!cell) {
|
|
222
|
+
const offset = cellOffset(page, header, index)
|
|
223
|
+
const interior = header.type === INTERIOR_INDEX
|
|
224
|
+
const [size, afterSize] = readVarint(page, interior ? offset + 4 : offset)
|
|
225
|
+
cell = {
|
|
226
|
+
values: decodeRecord(await this.payload(page, afterSize, size, true)),
|
|
227
|
+
leftChild: interior ? readUint32(page, offset) : 0,
|
|
228
|
+
}
|
|
229
|
+
cells[index] = cell
|
|
230
|
+
}
|
|
231
|
+
return cell
|
|
183
232
|
}
|
|
184
233
|
|
|
185
234
|
async *indexScanFrom(
|
|
@@ -191,7 +240,7 @@ export class BTree {
|
|
|
191
240
|
let high = header.cellCount
|
|
192
241
|
while (first < high) {
|
|
193
242
|
const mid = (first + high) >> 1
|
|
194
|
-
const cell = await this.indexCell(page, header, mid)
|
|
243
|
+
const cell = await this.indexCell(root, page, header, mid)
|
|
195
244
|
if (compareKey(cell.values, low) < 0) {
|
|
196
245
|
first = mid + 1
|
|
197
246
|
} else {
|
|
@@ -199,7 +248,7 @@ export class BTree {
|
|
|
199
248
|
}
|
|
200
249
|
}
|
|
201
250
|
for (let i = first; i < header.cellCount; i++) {
|
|
202
|
-
const cell = await this.indexCell(page, header, i)
|
|
251
|
+
const cell = await this.indexCell(root, page, header, i)
|
|
203
252
|
if (header.type === INTERIOR_INDEX) {
|
|
204
253
|
yield* this.indexScanFrom(cell.leftChild, low)
|
|
205
254
|
}
|
|
@@ -223,7 +272,7 @@ export class BTree {
|
|
|
223
272
|
let child = 0
|
|
224
273
|
while (low < high) {
|
|
225
274
|
const mid = (low + high) >> 1
|
|
226
|
-
const cell = await this.indexCell(page, header, mid)
|
|
275
|
+
const cell = await this.indexCell(pageNumber, page, header, mid)
|
|
227
276
|
if (compareKey(cell.values, key) <= 0) {
|
|
228
277
|
best = cell.values
|
|
229
278
|
low = mid + 1
|
package/src/sqlite/pager.ts
CHANGED
|
@@ -33,6 +33,23 @@ export class Pager {
|
|
|
33
33
|
this.maxBlocks = opts.maxBlocks ?? 256
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
private async read(length: number, start: number) {
|
|
37
|
+
let attempt = 0
|
|
38
|
+
for (;;) {
|
|
39
|
+
try {
|
|
40
|
+
const bytes = await this.source.read(length, start)
|
|
41
|
+
this.bytesFetched += bytes.length
|
|
42
|
+
return bytes
|
|
43
|
+
} catch (error) {
|
|
44
|
+
attempt += 1
|
|
45
|
+
if (attempt >= 3) {
|
|
46
|
+
throw error
|
|
47
|
+
}
|
|
48
|
+
await new Promise(resolve => setTimeout(resolve, 200 * attempt))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
36
53
|
seed(index: number, bytes: Uint8Array) {
|
|
37
54
|
if (
|
|
38
55
|
bytes.length ===
|
|
@@ -51,19 +68,72 @@ export class Pager {
|
|
|
51
68
|
}
|
|
52
69
|
const start = index * this.blockSize
|
|
53
70
|
const length = Math.min(this.blockSize, this.fileSize - start)
|
|
54
|
-
const pending = this.
|
|
55
|
-
this.bytesFetched += bytes.length
|
|
56
|
-
return bytes
|
|
57
|
-
})
|
|
71
|
+
const pending = this.read(length, start)
|
|
58
72
|
this.fetches += 1
|
|
59
73
|
this.blocks.set(index, pending)
|
|
60
|
-
|
|
74
|
+
this.forgetOnFailure(pending, [index])
|
|
75
|
+
this.evict()
|
|
76
|
+
return pending
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
prefetch(pageNumbers: number[]) {
|
|
80
|
+
const wanted = [
|
|
81
|
+
...new Set(
|
|
82
|
+
pageNumbers.map(pageNumber =>
|
|
83
|
+
Math.floor(((pageNumber - 1) * this.pageSize) / this.blockSize),
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
]
|
|
87
|
+
.filter(index => !this.blocks.has(index))
|
|
88
|
+
.sort((a, b) => a - b)
|
|
89
|
+
let runStart = 0
|
|
90
|
+
while (runStart < wanted.length) {
|
|
91
|
+
let runEnd = runStart + 1
|
|
92
|
+
while (
|
|
93
|
+
runEnd < wanted.length &&
|
|
94
|
+
wanted[runEnd] === wanted[runEnd - 1]! + 1
|
|
95
|
+
) {
|
|
96
|
+
runEnd += 1
|
|
97
|
+
}
|
|
98
|
+
this.fetchRun(wanted[runStart]!, runEnd - runStart)
|
|
99
|
+
runStart = runEnd
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private fetchRun(firstIndex: number, count: number) {
|
|
104
|
+
const start = firstIndex * this.blockSize
|
|
105
|
+
const length = Math.min(count * this.blockSize, this.fileSize - start)
|
|
106
|
+
const pending = this.read(length, start)
|
|
107
|
+
this.fetches += 1
|
|
108
|
+
const indexes: number[] = []
|
|
109
|
+
for (let i = 0; i < count; i++) {
|
|
110
|
+
const within = i * this.blockSize
|
|
111
|
+
indexes.push(firstIndex + i)
|
|
112
|
+
this.blocks.set(
|
|
113
|
+
firstIndex + i,
|
|
114
|
+
pending.then(bytes => bytes.subarray(within, within + this.blockSize)),
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
this.forgetOnFailure(pending, indexes)
|
|
118
|
+
this.evict()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private forgetOnFailure(pending: Promise<Uint8Array>, indexes: number[]) {
|
|
122
|
+
pending.catch(() => {
|
|
123
|
+
for (const index of indexes) {
|
|
124
|
+
this.blocks.delete(index)
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private evict() {
|
|
130
|
+
while (this.blocks.size > this.maxBlocks) {
|
|
61
131
|
const oldest = this.blocks.keys().next().value
|
|
62
|
-
if (oldest
|
|
63
|
-
|
|
132
|
+
if (oldest === undefined) {
|
|
133
|
+
break
|
|
64
134
|
}
|
|
135
|
+
this.blocks.delete(oldest)
|
|
65
136
|
}
|
|
66
|
-
return pending
|
|
67
137
|
}
|
|
68
138
|
|
|
69
139
|
async page(pageNumber: number) {
|