initial commit
This commit is contained in:
118
app/workers/dump.worker.ts
Normal file
118
app/workers/dump.worker.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/// <reference lib="webworker" />
|
||||
import { parseDump } from '../lib/dump-parse'
|
||||
import { DumpStore, type Query } from '../lib/dump-store'
|
||||
import { fingerprint, saveDump, loadDump } from '../lib/persist'
|
||||
|
||||
/**
|
||||
* The store never crosses to the main thread. The UI asks for a window of
|
||||
* rows and gets back ~60 plain objects; the 30–80 MB of columns stay here.
|
||||
*/
|
||||
let store: DumpStore | null = null
|
||||
let current: Uint32Array = new Uint32Array(0)
|
||||
|
||||
type Req =
|
||||
| { id: number; op: 'parse'; buffer: ArrayBuffer; name: string }
|
||||
| { id: number; op: 'restore'; key: string }
|
||||
| { id: number; op: 'query'; query: Query }
|
||||
| { id: number; op: 'window'; start: number; end: number }
|
||||
| { id: number; op: 'children'; prefix: string }
|
||||
| { id: number; op: 'resolve'; address: number }
|
||||
| { id: number; op: 'close' }
|
||||
|
||||
function summary() {
|
||||
const s = store!
|
||||
const { addr, count, types } = s.cols
|
||||
let min = Infinity
|
||||
let max = 0
|
||||
for (let i = 0; i < count; i++) {
|
||||
const el = addr[i];
|
||||
if(!el) continue
|
||||
if (el < min) min = el
|
||||
if (el > max) max = el
|
||||
}
|
||||
// Every address in a dump shares a leading run of hex digits. Finding it
|
||||
// lets the table ghost the noise and highlight the digits that differ.
|
||||
const a = min.toString(16).padStart(12, '0').toUpperCase()
|
||||
const b = max.toString(16).padStart(12, '0').toUpperCase()
|
||||
let shared = 0
|
||||
while (shared < a.length && a[shared] === b[shared]) shared++
|
||||
return { count, types, sharedAddressDigits: shared, skipped: s.cols.skipped }
|
||||
}
|
||||
|
||||
async function handle(msg: Req) {
|
||||
switch (msg.op) {
|
||||
case 'parse': {
|
||||
const src = new Uint8Array(msg.buffer)
|
||||
const key = fingerprint(msg.name, src)
|
||||
const cached = await loadDump(key).catch(() => undefined)
|
||||
if (cached) {
|
||||
store = new DumpStore(cached.cols)
|
||||
return { ...summary(), key, label: cached.label, cached: true }
|
||||
}
|
||||
const t0 = performance.now()
|
||||
const cols = parseDump(src, (f) =>
|
||||
self.postMessage({ id: -1, event: 'progress', fraction: f }),
|
||||
)
|
||||
const ms = performance.now() - t0
|
||||
store = new DumpStore(cols)
|
||||
await saveDump({
|
||||
key,
|
||||
label: msg.name,
|
||||
bytes: src.length,
|
||||
parsedAt: Date.now(),
|
||||
cols,
|
||||
}).catch(() => {})
|
||||
return { ...summary(), key, label: msg.name, cached: false, parseMs: ms }
|
||||
}
|
||||
|
||||
case 'restore': {
|
||||
const cached = await loadDump(msg.key)
|
||||
if (!cached) throw new Error('That dump is no longer cached. Load the file again.')
|
||||
store = new DumpStore(cached.cols)
|
||||
return { ...summary(), key: cached.key, label: cached.label, cached: true }
|
||||
}
|
||||
|
||||
case 'query': {
|
||||
const t0 = performance.now()
|
||||
const { rows, typeCounts } = store!.query(msg.query)
|
||||
current = rows
|
||||
return { total: rows.length, typeCounts, ms: performance.now() - t0 }
|
||||
}
|
||||
|
||||
case 'window': {
|
||||
const end = Math.min(msg.end, current.length)
|
||||
const out = []
|
||||
for (let i = msg.start; i < end; i++) {
|
||||
const el = current[i];
|
||||
if(!el) continue
|
||||
const row = store!.row(el)
|
||||
if (row) out.push(row)
|
||||
}
|
||||
return { start: msg.start, rows: out }
|
||||
}
|
||||
|
||||
case 'children':
|
||||
return { prefix: msg.prefix, children: store!.children(msg.prefix) }
|
||||
|
||||
case 'resolve': {
|
||||
const row = store!.rowByAddress(msg.address)
|
||||
return { row: row >= 0 ? store!.row(row) : null }
|
||||
}
|
||||
|
||||
// Drops the only references to the columns; the dump stays in IndexedDB,
|
||||
// so 'restore' can bring it back without re-parsing.
|
||||
case 'close': {
|
||||
store = null
|
||||
current = new Uint32Array(0)
|
||||
return { closed: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.onmessage = async (e: MessageEvent<Req>) => {
|
||||
try {
|
||||
self.postMessage({ id: e.data.id, result: await handle(e.data) })
|
||||
} catch (err) {
|
||||
self.postMessage({ id: e.data.id, error: (err as Error).message })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user