@cloudbitmaps/roaring 0.22.0 → 0.24.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.
@@ -1,118 +1,49 @@
1
1
  /**
2
- * SafeBitmap — the thin wrapper around the `roaring` (CRoaring) engine that owns the
3
- * untrusted-bytes boundary.
2
+ * The codec selection point and the load-input and decode helpers the `CloudRoaring` facade builds on.
4
3
  *
5
- * All (de)serialization uses the **portable** format — the stable, bounds-checked one. The
6
- * `unsafe_frozen_*` formats are never used (they are explicitly documented as crash/attack
7
- * vectors). Every deserialize is preceded by a hard size cap and a structural check, because the native
8
- * portable deserializer bounds its reads and checks nothing else (see `portable/layout.ts`).
4
+ * The store's codec is the TypeScript Roaring codec (`ts-roaring/bitmap.ts`): the portable format, read and written in
5
+ * JavaScript with no native addon. Every deserialize is preceded by a hard size cap and a structural check
6
+ * (`portable/layout.ts`). The `unsafe_frozen_*` formats are never used.
9
7
  */
10
- import roaring from 'roaring';
11
- import type { CodecBitmap, CodecInterface, EncodedChunk, LoadInput } from '@cloudbitmaps/core';
12
- declare const RoaringBitmap32: typeof roaring.RoaringBitmap32;
13
- type RoaringBitmap32 = InstanceType<typeof RoaringBitmap32>;
14
- export declare class SafeBitmap implements CodecBitmap {
15
- private readonly bitmap;
16
- private constructor();
17
- static empty(): SafeBitmap;
18
- static fromValues(values: Iterable<number>): SafeBitmap;
19
- /**
20
- * Cap the size, check the structure, then deserialize with the **portable** format.
21
- * Throws `IntegrityError` if the input exceeds `maxBytes`, is not a well-formed portable bitmap, or fails to
22
- * decode — the native addon is never handed unbounded, malformed or unsafe-format input. With `whole`, bytes
23
- * after the bitmap's last container are refused too; without it they are ignored, as the native decoder ignores
24
- * them.
25
- *
26
- * The structural check is what makes the decoded bitmap's answers mean anything. The native deserializer
27
- * bounds its reads and checks nothing else, so bytes of the right length and the wrong shape (containers or
28
- * values out of order, overlapping runs, a cardinality that disagrees with the bits) decode into a bitmap
29
- * whose `maximum()`, `has()` and `size` are wrong, and some of those shapes crash the process when used.
30
- */
31
- static safeDeserialize(bytes: Uint8Array, maxBytes: number, options?: {
32
- readonly whole?: boolean;
33
- }): SafeBitmap;
34
- /** The `roaring` bitmap this wraps, for a caller that was handed it once and keeps no wrapper. */
35
- unwrap(): RoaringBitmap32;
36
- serialize(): Uint8Array;
37
- /**
38
- * Convert containers to run-encoding where that is smaller. Representation only — membership is untouched, and
39
- * the portable format still round-trips through {@link RoaringCodec.safeDeserialize}.
40
- *
41
- * Roaring's third container type is a RUN, and no implementation selects it on its own: it takes an explicit
42
- * `runOptimize()` pass. Nothing here made that call, so run-shaped ids paid array or bitset prices — measured
43
- * at **570×** for a contiguous 1M-id range (128.1 KiB → 0.2 KiB) and **63×** for a 2,000-run shape
44
- * (536.5 KiB → 8.5 KiB). Sparse ids come out byte-identical, so this is never a losing trade; roaring keeps
45
- * whichever encoding is smaller per container.
46
- *
47
- * Those two factors are THIS CALL's own before/after on the same ids. They are not the ones the site quotes
48
- * (543× and 63×) — those compare the optimized encoding against the best *fixed* representation at its floor,
49
- * a different baseline that happens to land near 63× on the run shape too. `pnpm bench:encoding` prints both
50
- * columns; don't carry a figure between them without checking which denominator it had.
51
- *
52
- * Called only when writing an immutable objects generation — see {@link CodecBitmap.optimize} for why the read
53
- * path deliberately does not.
54
- *
55
- * CANONICAL, which `runOptimize()` alone is not. Where a container's run and array encodings are the same size
56
- * (cardinality = 2 × runs + 1: three values in one run, five in two), CRoaring keeps whichever kind the
57
- * container already has. A chunk built from ids starts as an array and stays one; the same set reached through
58
- * `addRange`, an earlier `runOptimize()` or an operation over run containers is a run, and serializes to other
59
- * bytes. Undoing run compression first starts every container where one built from ids starts, so the bytes
60
- * depend on membership alone. On a bitmap built from ids there is nothing to undo.
61
- */
62
- optimize(): void;
63
- /**
64
- * This set as storage chunks, cut from its own containers: {@link optimize} and one `serialize` when called, then
65
- * a lazy header walk that copies one container out per step (`containerPayloads`). Per container and per byte,
66
- * never per id, and the payloads are the ones {@link SafeBitmap.fromValues} of each chunk's low 16 bits would
67
- * serialize to. The walk trusts the bytes it cuts because the native serializer wrote them, from a bitmap that
68
- * passed the structural check when it was decoded.
69
- */
70
- encodeChunks(): Iterable<EncodedChunk>;
71
- /**
72
- * Largest value, or `undefined` when empty. O(1) — roaring reads it off the last container, so the engine's
73
- * per-chunk range assertion costs one call per chunk rather than a walk per id. The last container holds the
74
- * largest value only because {@link SafeBitmap.safeDeserialize} refuses bytes whose containers or values are
75
- * out of order.
76
- */
77
- maximum(): number | undefined;
78
- add(value: number): void;
79
- addMany(values: Iterable<number>): void;
80
- remove(value: number): void;
81
- removeMany(values: Iterable<number>): void;
82
- has(value: number): boolean;
83
- get size(): number;
84
- get isEmpty(): boolean;
85
- clone(): SafeBitmap;
86
- /** In-place union: `this = this ∪ other`. */
87
- orInPlace(other: CodecBitmap): void;
88
- /** In-place difference: `this = this \ other`. */
89
- andNotInPlace(other: CodecBitmap): void;
90
- /** In-place intersection: `this = this ∩ other`. */
91
- andInPlace(other: CodecBitmap): void;
92
- /** Ascending iterator over the set values. */
93
- [Symbol.iterator](): IterableIterator<number>;
94
- toArray(): number[];
95
- /** The values as one fresh ascending `Uint32Array`, exported natively. */
96
- toUint32Array(): Uint32Array;
97
- }
8
+ import type { CodecInterface, LoadInput, PortableBitmap } from '@cloudbitmaps/core';
98
9
  /**
99
- * A bare `RoaringBitmap32` of this package's own `roaring`, passed where ids go, loads as `{ bitmap }`: from its
100
- * containers, through the same check as any bytes, never id by id. Any other input passes through unchanged, and a
101
- * bitmap from another copy of `roaring` is loaded as the ids it iterates, which is correct and slower.
10
+ * An iterable that serializes itself to portable Roaring (an object with `Symbol.iterator` and a `serialize` method: a
11
+ * caller's `RoaringBitmap32` from the `roaring` package, another copy's bitmap, or a bitmap {@link deserializePortable}
12
+ * returned), passed where ids go, loads as `{ bitmap }`: from its containers, through the same check as any bytes,
13
+ * never id by id. Its `serialize` decides, even where it disagrees with what it iterates. Any other input passes
14
+ * through unchanged, so the core's own rules apply to it: a byte array (`Uint8Array`, `Buffer`) is refused as ids even
15
+ * with a `serialize` of its own, and an object that is not iterable is read as `{ serialized }` or `{ bitmap }` or
16
+ * refused, whatever else it has.
102
17
  */
103
18
  export declare function bitmapAsLoadInput(input: LoadInput): LoadInput;
104
19
  /**
105
- * The roaring {@link CodecInterface} — the flagship codec, delegating to {@link SafeBitmap}'s statics. This is
106
- * the default the `CloudRoaring` facade injects into the engine; `core/` itself never hard-references it once a
107
- * caller supplies a codec.
20
+ * The codec the store hands to the engine at every site that injects one (`CloudRoaring`, a load, a pin, an export
21
+ * and {@link deserializePortable}): the TypeScript codec unless a harness has flipped it with {@link setFlagshipCodec}.
22
+ * Internal: no public entry exports it, and a store cannot be configured with a codec.
23
+ */
24
+ export declare function flagshipCodec(): CodecInterface;
25
+ /** Flip the codec the store uses, for the whole process. Test and bench harnesses only; returns the previous one. */
26
+ export declare function setFlagshipCodec(codec: CodecInterface): CodecInterface;
27
+ /**
28
+ * A decoded bitmap that can only be read: what {@link deserializePortable} returns. Its ids come out ascending when
29
+ * it is iterated, which is a promise. It is a `PortableBitmap`, so a load takes it as `{ bitmap }`, and the library
30
+ * keeps no reference to it. What {@link deserializePortable} returns has these members only: a call to any other
31
+ * throws `TypeError`, it has no `getSerializationSizeInBytes`, and `serialize` takes exactly `'portable'`. Its class
32
+ * is not part of the contract: do not cast it and do not test it with `instanceof`. Passed to a method of the `roaring`
33
+ * package from npm in place of a `RoaringBitmap32`, it answers wrongly: decode its bytes first. `JSON.stringify` of one gives `"{}"`; write
34
+ * `JSON.stringify([...bitmap])`. For the rest of a Roaring library's API, decode its portable bytes with that library.
108
35
  */
109
- export declare const roaringCodec: CodecInterface;
36
+ export interface ReadonlyBitmap extends PortableBitmap, Iterable<number> {
37
+ /** How many ids it holds. */
38
+ readonly size: number;
39
+ /** Whether it holds `id`. */
40
+ has(id: number): boolean;
41
+ }
110
42
  /**
111
- * Decode portable Roaring bytes you hold into a `RoaringBitmap32`, through the check a `{ serialized }` load makes
112
- * first: a size cap, a structural check, one whole bitmap, and only then the native decoder.
43
+ * Decode portable Roaring bytes you hold into a {@link ReadonlyBitmap}, through the check a `{ serialized }` load
44
+ * makes first: a size cap, a structural check, one whole bitmap, and only then the decoder.
113
45
  *
114
46
  * @param bytes a `Uint8Array`, which a Node `Buffer` is.
115
47
  * @throws {ValidationError} for bytes that are not exactly one well-formed portable bitmap, as a load refuses them.
116
48
  */
117
- export declare function deserializePortable(bytes: Uint8Array): RoaringBitmap32;
118
- export {};
49
+ export declare function deserializePortable(bytes: Uint8Array): ReadonlyBitmap;
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@cloudbitmaps/roaring",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "Distributed, cloud-native Roaring Bitmaps — query and intersect billion-scale integer sets straight out of object storage, from a bounded RAM cache over immutable objects in your own bucket, at a fraction of an always-on cache.",
5
5
  "keywords": [
6
6
  "roaring",
7
7
  "bitmap",
8
8
  "roaring-bitmap",
9
9
  "roaring-bitmaps",
10
- "croaring",
11
10
  "bitset",
12
11
  "compressed-bitmap",
13
12
  "lambda",
@@ -48,7 +47,8 @@
48
47
  ".": {
49
48
  "types": "./dist/index.d.ts",
50
49
  "default": "./dist/index.js"
51
- }
50
+ },
51
+ "./package.json": "./package.json"
52
52
  },
53
53
  "files": [
54
54
  "dist",
@@ -64,8 +64,7 @@
64
64
  "export-segments": "./dist/bin/export-segments.js"
65
65
  },
66
66
  "dependencies": {
67
- "roaring": "2.7.0",
68
- "@cloudbitmaps/core": "^0.22.0"
67
+ "@cloudbitmaps/core": "0.24.0"
69
68
  },
70
69
  "devDependencies": {
71
70
  "@types/node": "^22.0.0",