{"version":3,"sources":["../src/bloomfilter.ts"],"names":["fileURLToPath","fs"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\n\n// type FastBloomFilter = {\n// \taddString(text: string): void; // Insert a UTF-8 string into the filter\n// \thasString(text: string): boolean; // Check a UTF-8 string\n// \tadd(buffer: Uint8Array): void; // Insert a raw byte buffer\n// \thas(buffer: Uint8Array): boolean; // Check a raw byte buffer\n// \tbitCount: number; // Total number of bits in the bit array\n// \thashCount: number; // Number of hash functions (k)\n// \tdispose(): void; // Free all WASM-allocated memory\n// };\n\n/**\n * WebAssembly-backed Bloom filter.\n *\n * Use `FastBloomFilter.create(bitCount, hashCount)` to asynchronously load the\n * WASM module, allocate the underlying bitset, and receive a runtime wrapper\n * with convenience methods (`addString`, `has`, etc.).\n *\n * Memory layout:\n * - A persistent bitset is allocated once via `malloc`, sized according to\n *   `bitCount` (rounded up to the next 32-bit boundary).\n * - A temporary input buffer (`bufferPtr`) is allocated and dynamically resized\n *   when strings or byte arrays are added / queried.\n *\n * HOW IT WORKS:\n * - Strings are UTF-8 encoded on the JS side and copied into WASM memory.\n *   The WASM function `bloom_add(...)` then reads the raw bytes, hashes them\n *   using `XXH3`, and sets multiple bits in the bitset.\n * - `has(...)` performs the same hashing logic and checks whether all probed\n *   bit positions are set.\n * - This is a **classic Bloom filter**: it offers *no false negatives* but may\n *   return *false positives* depending on `k` (hashCount) and bit density.\n *\n * Performance notes:\n * - Reuses a single scratch buffer to avoid allocating / freeing every call.\n *\n * Example usage:\n * ```ts\n * const filter = await FastBloomFilter.create(1_000_000, 7);\n * filter.addString(\"hello\");\n * console.log(filter.hasString(\"hello\")); // true\n * console.log(filter.hasString(\"world\")); // maybe false\n * filter.dispose();\n * ```\n */\nexport type BloomFilterExport = Uint8Array;\n\nexport default class FastBloomFilter {\n  private static readonly MAGIC :Uint8Array = new Uint8Array([0x46, 0x42, 0x46, 0x32]); // \"FBF2\"\n\n  private readonly bitCount: number;\n  private readonly byteCount: number;\n\n  private readonly hashCount: number;\n\n  private readonly bitsetPtr: number;\n  \n  private readonly ex: WasmExports;\n\n  private readonly encoder: TextEncoder;\n\n  private bufferPtr: number;\n  private bufferSize: number;\n  private bufferU8View: Uint8Array;\n\n  private constructor(bitCount: number, hashCount: number, ex: WasmExports, data?: Uint8Array) {\n    // round up to the next multiple of 32\n    this.bitCount = (bitCount + 31) & ~31;\n    this.byteCount = this.bitCount >>> 3;\n    this.bitsetPtr = ex.malloc(this.byteCount);\n    if (data) {\n      const bitsetU8View = new Uint8Array(ex.memory.buffer, this.bitsetPtr, this.byteCount);\n      bitsetU8View.set(data);\n    }\n\n    this.hashCount = hashCount | 0;\n    this.encoder = new TextEncoder();\n    this.bufferPtr = 0;\n    this.bufferSize = 0;\n    this.bufferU8View = new Uint8Array(ex.memory.buffer, 0, 0);\n\n    this.ex = ex;\n  }\n\n  /**\n   * Load the WASM module and return a ready-to-use Bloom filter.\n   *\n   * @param bitCount Desired number of bits. Rounded up to the nearest multiple of 32.\n   * @param hashCount Number of hash rounds (k). More rounds reduce false positives.\n   */\n  static async create(bitCount: number, hashCount: number): Promise<FastBloomFilter> {\n    const ex = await this.instantiateWasm();\n    return new FastBloomFilter(bitCount, hashCount, ex);\n  }\n\n  private static async instantiateWasm(): Promise<WasmExports> {\n    const wasmUrl = new URL(\"./wasm/bloomfilter.wasm\", import.meta.url);\n    const wasmPath = fileURLToPath(wasmUrl);\n    const wasmBuffer = fs.readFileSync(wasmPath);\n    const { instance } = await WebAssembly.instantiate(wasmBuffer);\n    return instance.exports as unknown as WasmExports;\n  }\n\n  // Ensure the temp input buffer is large enough; if not, free and reallocate\n  ensureCapacity(utf8Len: number) {\n    if (utf8Len <= this.bufferSize) return;\n    let newSize = this.bufferSize ? this.bufferSize : 256;\n    while (newSize < utf8Len) newSize <<= 1;\n    if (this.bufferPtr) this.ex.free(this.bufferPtr);\n    this.bufferPtr = this.ex.malloc(newSize);\n    this.bufferSize = newSize;\n    this.bufferU8View = new Uint8Array(\n      this.ex.memory.buffer,\n      this.bufferPtr,\n      this.bufferSize\n    );\n  }\n\n  // ---- String-based API ----\n\n  addString(text: string) {\n    this.ensureCapacity(text.length << 2); // Overprovision worst-case UTF-8 expansion\n    const { written } = this.encoder.encodeInto(text, this.bufferU8View);\n    this.ex.bloom_add(\n      this.bufferPtr,\n      written,\n      this.hashCount,\n      this.bitCount,\n      this.bitsetPtr\n    );\n  }\n\n  hasString(text: string): boolean {\n    this.ensureCapacity(text.length << 2);\n    const { written } = this.encoder.encodeInto(text, this.bufferU8View);\n    return (\n      this.ex.bloom_has(\n        this.bufferPtr,\n        written,\n        this.hashCount,\n        this.bitCount,\n        this.bitsetPtr\n      ) === 1\n    );\n  }\n\n  // ---- Raw buffer API ----\n\n  add(buffer: Uint8Array) {\n    this.ensureCapacity(buffer.length);\n    this.bufferU8View.set(buffer);\n    this.ex.bloom_add(\n      this.bufferPtr,\n      buffer.length,\n      this.hashCount,\n      this.bitCount,\n      this.bitsetPtr\n    );\n  }\n\n  has(buffer: Uint8Array): boolean {\n    this.ensureCapacity(buffer.length);\n    this.bufferU8View.set(buffer);\n    return (\n      this.ex.bloom_has(\n        this.bufferPtr,\n        buffer.length,\n        this.hashCount,\n        this.bitCount,\n        this.bitsetPtr\n      ) === 1\n    );\n  }\n\n  export(): Uint8Array {\n    const bitsetU8View = new Uint8Array(this.ex.memory.buffer, this.bitsetPtr, this.byteCount);\n    const headerLength = FastBloomFilter.MAGIC.length + 8;\n    const exportBuffer = new ArrayBuffer(headerLength + bitsetU8View.length);\n    const exportView8 = new Uint8Array(exportBuffer);\n    exportView8.set(FastBloomFilter.MAGIC, 0);\n    const headerView = new DataView(exportBuffer, FastBloomFilter.MAGIC.length, 8);\n    headerView.setUint32(0, this.bitCount, true);\n    headerView.setUint32(4, this.hashCount, true);\n    exportView8.set(bitsetU8View, headerLength);\n    return exportView8;\n  }\n\n  static async import(exportedBloomFilter: Uint8Array): Promise<FastBloomFilter> {\n    const headerLength = FastBloomFilter.MAGIC.length + 8;\n\n    const { buffer, byteOffset, byteLength } = exportedBloomFilter;\n    const magicView = new Uint8Array(buffer, byteOffset, FastBloomFilter.MAGIC.length);\n    for (let i = 0; i < FastBloomFilter.MAGIC.length; i += 1) {\n      if (magicView[i] !== FastBloomFilter.MAGIC[i]) {\n        throw new Error(\"Invalid magic number\");\n      }\n    }\n\n    const headerView = new DataView(buffer, byteOffset + FastBloomFilter.MAGIC.length, 8);\n    const bitCount = headerView.getUint32(0, true);\n    const hashCount = headerView.getUint32(4, true);\n\n    const expectedByteCount = ((bitCount + 31) & ~31) >>> 3;\n    const payloadLength = byteLength - headerLength;\n    if (payloadLength !== expectedByteCount) {\n      throw new Error(\"Bloom filter payload has unexpected length\");\n    }\n\n    const data = new Uint8Array(buffer, byteOffset + headerLength, payloadLength);\n    const ex = await FastBloomFilter.instantiateWasm();\n\n    return new FastBloomFilter(bitCount, hashCount, ex, data);\n  }\n\n  // ---- Cleanup ----\n\n  dispose() {\n    if (this.bufferPtr) {\n      this.ex.free(this.bufferPtr);\n      this.bufferPtr = 0;\n      this.bufferSize = 0;\n    }\n    if (this.bitsetPtr) this.ex.free(this.bitsetPtr);\n  }\n}\n\ntype WasmExports = {\n  memory: WebAssembly.Memory;\n  malloc: (size: number) => number;\n  free: (ptr: number) => void;\n  bloom_add: (\n    dataPtr: number,\n    len: number,\n    k: number,\n    bitCount: number,\n    bitset32Ptr: number\n  ) => void;\n  bloom_has: (\n    dataPtr: number,\n    len: number,\n    k: number,\n    bitCount: number,\n    bitset32Ptr: number\n  ) => number;\n};\n"]}