@livestore/sqlite-wasm
Version:
69 lines (56 loc) • 2.49 kB
text/typescript
import * as VFS from '@livestore/wa-sqlite/src/VFS.js'
const SECTOR_SIZE = 4096
const HEADER_MAX_PATH_SIZE = 512
const HEADER_FLAGS_SIZE = 4
const HEADER_DIGEST_SIZE = 8
const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE
const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE
const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE
export const HEADER_OFFSET_DATA = SECTOR_SIZE
const PERSISTENT_FILE_TYPES =
VFS.SQLITE_OPEN_MAIN_DB | VFS.SQLITE_OPEN_MAIN_JOURNAL | VFS.SQLITE_OPEN_SUPER_JOURNAL | VFS.SQLITE_OPEN_WAL
const textDecoder = new TextDecoder()
export const decodeSAHPoolFilename = async (file: File): Promise<string> => {
// Read the path and digest of the path from the file.
const corpus = new Uint8Array(await file.slice(0, HEADER_CORPUS_SIZE).arrayBuffer())
// Delete files not expected to be present.
const dataView = new DataView(corpus.buffer, corpus.byteOffset)
const flags = dataView.getUint32(HEADER_OFFSET_FLAGS)
if (corpus[0] && (flags & VFS.SQLITE_OPEN_DELETEONCLOSE || (flags & PERSISTENT_FILE_TYPES) === 0)) {
console.warn(`Remove file with unexpected flags ${flags.toString(16)}`)
return ''
}
const fileDigest = new Uint32Array(
await file.slice(HEADER_OFFSET_DIGEST, HEADER_OFFSET_DIGEST + HEADER_DIGEST_SIZE).arrayBuffer(),
)
// Verify the digest.
const computedDigest = computeDigest(corpus)
if (fileDigest.every((value, i) => value === computedDigest[i])) {
// Good digest. Decode the null-terminated path string.
const pathBytes = corpus.indexOf(0)
if (pathBytes === 0) {
// Note: We can't truncate the file here as File objects are read-only
// console.warn('Unassociated file detected')
}
return textDecoder.decode(corpus.subarray(0, pathBytes))
} else {
// Bad digest. Repair this header.
console.warn('Disassociating file with bad digest.')
return ''
}
}
const computeDigest = (corpus: Uint8Array): Uint32Array => {
if (!corpus[0]) {
// Optimization for deleted file.
return new Uint32Array([0xfe_cc_5f_80, 0xac_ce_c0_37])
}
let h1 = 0xde_ad_be_ef
let h2 = 0x41_c6_ce_57
for (const value of corpus) {
h1 = Math.imul(h1 ^ value, 2_654_435_761)
h2 = Math.imul(h2 ^ value, 1_597_334_677)
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2_246_822_507) ^ Math.imul(h2 ^ (h2 >>> 13), 3_266_489_909)
h2 = Math.imul(h2 ^ (h2 >>> 16), 2_246_822_507) ^ Math.imul(h1 ^ (h1 >>> 13), 3_266_489_909)
return new Uint32Array([h1 >>> 0, h2 >>> 0])
}