@cloudbitmaps/roaring 0.25.0 → 0.26.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/PRIVACY.md +1 -1
- package/README.md +2 -2
- package/dist/bin/export-segments.js +132 -1
- package/dist/bin/export-segments.js.map +3 -3
- package/dist/index.d.ts +53 -0
- package/dist/index.js +132 -1
- package/dist/index.js.map +3 -3
- package/package.json +2 -2
package/PRIVACY.md
CHANGED
|
@@ -173,7 +173,7 @@ A call with a feed reads operands the library never stores, which no erasure can
|
|
|
173
173
|
Erasure and crypto-shred are **immediate in storage and immediate in the store whose verb performed them**, for
|
|
174
174
|
every read that starts after the verb returns. A read already in progress in that store can still yield an erased id
|
|
175
175
|
from a chunk it had requested before the verb ran: up to 32 chunks for `iterate` and `count`, and up to `concurrency` + 1
|
|
176
|
-
keys (33 by default) for a combine. That is the bound: the chunk or key a read is handing out and what it had already requested, which a `count` that reads chunks (on a source with no index of its own) reaches. A running `iterate` or combine on the library's own sources stops sooner: the verb invalidates the segment, so before the next key it takes the read lists its chunk keys again from the generation current then and drops what it had requested, and so yields the id only from the chunk or key it is handing out. A combine or `iterate` reads ranges of the object, and resolves the segment again before it serves each chunk, one held in its cache included, as a read of that chunk alone does, so the ranges it had already requested are dropped, not served. A pinned read in progress does not move to the rewritten generation: before its next chunk it opens its object again, and fails with `NotFoundError` once the erasure has deleted it, so it yields no more than that bound either. They are not immediate in any *other* store, in the same process or another, and this library ships nothing that could
|
|
176
|
+
keys (33 by default) for a combine or a count of a combine. That is the bound: the chunk or key a read is handing out and what it had already requested, which a `count` that reads chunks (on a source with no index of its own) reaches. A running `iterate` or combine on the library's own sources stops sooner: the verb invalidates the segment, so before the next key it takes the read lists its chunk keys again from the generation current then and drops what it had requested, and so yields the id only from the chunk or key it is handing out. A combine or `iterate` reads ranges of the object, and resolves the segment again before it serves each chunk, one held in its cache included, as a read of that chunk alone does, so the ranges it had already requested are dropped, not served. A pinned read in progress does not move to the rewritten generation: before its next chunk it opens its object again, and fails with `NotFoundError` once the erasure has deleted it, so it yields no more than that bound either. They are not immediate in any *other* store, in the same process or another, and this library ships nothing that could
|
|
177
177
|
make them so — there is no daemon, no bus, and no connection between two stores that happen to point at the same
|
|
178
178
|
bucket.
|
|
179
179
|
|
package/README.md
CHANGED
|
@@ -105,8 +105,8 @@ name.
|
|
|
105
105
|
|
|
106
106
|
## What it costs
|
|
107
107
|
|
|
108
|
-
A cold `intersect` of two 500,000-id segments sharing 100 of their 1,999 chunks made 6 GETs and took
|
|
109
|
-
million** at list prices: measured on S3 in-region, in the run of 2026-10-
|
|
108
|
+
A cold `intersect` of two 500,000-id segments sharing 100 of their 1,999 chunks made 6 GETs and took 107.87 ms at the median, **$2.40 per
|
|
109
|
+
million** at list prices: measured on S3 in-region, in the run of 2026-10-30, whose client had 128 sockets. On a spread layout it reads more
|
|
110
110
|
bytes than it needs, which cross-region can cost more than the requests it saves. A segment's first single-part `store.load()` made
|
|
111
111
|
2 PUT + 3 GET, **$11.20 per million** at the default prices, pointer included: the requests are measured on S3 in-region, in the same run. A steady single-part `store.load()` at `keep: 12` that deletes a generation by name costs **$12.00 per million**, for 2 PUT + 5 GET and a free delete, the requests measured on S3 in-region, in the same run. A cold `count()` is one
|
|
112
112
|
pointer read: it reads no payload and no object.
|
|
@@ -3572,6 +3572,29 @@ var CALL_KEYS = {
|
|
|
3572
3572
|
"allowAbsentOperands",
|
|
3573
3573
|
"exclude"
|
|
3574
3574
|
]),
|
|
3575
|
+
intersectCount: keysOf()([
|
|
3576
|
+
"after",
|
|
3577
|
+
"through",
|
|
3578
|
+
"concurrency",
|
|
3579
|
+
"budget",
|
|
3580
|
+
"allowAbsentOperands",
|
|
3581
|
+
"exclude"
|
|
3582
|
+
]),
|
|
3583
|
+
unionCount: keysOf()([
|
|
3584
|
+
"after",
|
|
3585
|
+
"through",
|
|
3586
|
+
"concurrency",
|
|
3587
|
+
"budget",
|
|
3588
|
+
"allowAbsentOperands",
|
|
3589
|
+
"exclude"
|
|
3590
|
+
]),
|
|
3591
|
+
andNotCount: keysOf()([
|
|
3592
|
+
"after",
|
|
3593
|
+
"through",
|
|
3594
|
+
"concurrency",
|
|
3595
|
+
"budget",
|
|
3596
|
+
"allowAbsentOperands"
|
|
3597
|
+
]),
|
|
3575
3598
|
into: keysOf()([
|
|
3576
3599
|
"after",
|
|
3577
3600
|
"through",
|
|
@@ -3649,6 +3672,9 @@ var MISPLACED = {
|
|
|
3649
3672
|
range: outside(CALL_KEYS.into, CALL_KEYS.range),
|
|
3650
3673
|
andNot: outside(CALL_KEYS.into, CALL_KEYS.andNot),
|
|
3651
3674
|
combine: outside(CALL_KEYS.into, CALL_KEYS.combine),
|
|
3675
|
+
intersectCount: outside(CALL_KEYS.into, CALL_KEYS.intersectCount),
|
|
3676
|
+
unionCount: outside(CALL_KEYS.into, CALL_KEYS.unionCount),
|
|
3677
|
+
andNotCount: outside(CALL_KEYS.into, CALL_KEYS.andNotCount),
|
|
3652
3678
|
andNotInto: outside(CALL_KEYS.into, CALL_KEYS.andNotInto),
|
|
3653
3679
|
exportSegments: ["codec"]
|
|
3654
3680
|
};
|
|
@@ -5415,7 +5441,7 @@ function operandsLabel(verb) {
|
|
|
5415
5441
|
}
|
|
5416
5442
|
function refuseNoOperands(verb) {
|
|
5417
5443
|
throw new ValidationError5(
|
|
5418
|
-
verb === "intersect" ? "intersect requires at least one segment to intersect with; to read every id of one segment, use iterate()" : "intersectInto requires at least one segment to intersect with; to copy this segment into dest, call materializeMany({ operands: { src: thisSegment }, outputs: [{ dest, expr: { or: ['src'] } }], keep })"
|
|
5444
|
+
verb === "intersect" ? "intersect requires at least one segment to intersect with; to read every id of one segment, use iterate()" : verb === "intersectCount" ? "intersectCount requires at least one segment to intersect with; to count one segment, use count()" : "intersectInto requires at least one segment to intersect with; to copy this segment into dest, call materializeMany({ operands: { src: thisSegment }, outputs: [{ dest, expr: { or: ['src'] } }], keep })"
|
|
5419
5445
|
);
|
|
5420
5446
|
}
|
|
5421
5447
|
var AS_IDS = {
|
|
@@ -6125,6 +6151,111 @@ var Segment = class _Segment {
|
|
|
6125
6151
|
);
|
|
6126
6152
|
return this.combineInto("andNotInto", dest, excludes, options);
|
|
6127
6153
|
}
|
|
6154
|
+
/**
|
|
6155
|
+
* How many ids `this ∩ others…` holds (minus `exclude`): the length of what {@link intersect} streams, without
|
|
6156
|
+
* streaming it. Every key all the operands hold is decoded and counted as the stream reads it, and a key only some
|
|
6157
|
+
* operands hold is not read, so the call makes the requests {@link intersect} makes and returns a number instead of
|
|
6158
|
+
* the ids. It is exact: it never answers a key from an index, so it trusts nothing {@link count} trusts.
|
|
6159
|
+
*
|
|
6160
|
+
* The range, `concurrency`, `budget`, `allowAbsentOperands` and `exclude` are {@link intersect}'s; a write option fails
|
|
6161
|
+
* the call with {@link ValidationError} before any request, and so does an empty `others` (to count one segment, use
|
|
6162
|
+
* {@link count}).
|
|
6163
|
+
*
|
|
6164
|
+
* Like a stream, a count that runs while a segment is erased or reloaded can describe two instants, within the bound
|
|
6165
|
+
* the streams state. A union or a difference, and an intersection with an `exclude`, looks at its operands'
|
|
6166
|
+
* generations every `concurrency` keys; an intersection with no `exclude` makes no such look, since it decodes every
|
|
6167
|
+
* key and so shows a move in the chunks it decodes, as its stream does. Either way it plans again above the last key
|
|
6168
|
+
* it took when one moved. A pinned handle counts the generation it pinned, and each pinned operand's object is
|
|
6169
|
+
* looked up once before the first key (one footer read each, which `budget` does not charge, and which the stream
|
|
6170
|
+
* does not make), and again at each look where there is one, so an erasure of what it pinned fails the count with
|
|
6171
|
+
* {@link NotFoundError}; a
|
|
6172
|
+
* fault the look meets other than a transient one, which is retried, is raised as it is. A call that involves a
|
|
6173
|
+
* handle whose lease has ended, or has ended by the next look, throws {@link LeaseExpiredError}.
|
|
6174
|
+
*/
|
|
6175
|
+
async intersectCount(others, options) {
|
|
6176
|
+
checkOptions(
|
|
6177
|
+
options,
|
|
6178
|
+
"intersectCount",
|
|
6179
|
+
CALL_KEYS.intersectCount,
|
|
6180
|
+
["allowAbsentOperands"],
|
|
6181
|
+
MISPLACED.intersectCount
|
|
6182
|
+
);
|
|
6183
|
+
return this.countCombine("intersect", others, options ?? void 0);
|
|
6184
|
+
}
|
|
6185
|
+
/**
|
|
6186
|
+
* How many ids `this ∪ others…` holds (minus `exclude`): the length of what {@link union} streams, without streaming
|
|
6187
|
+
* it. A chunk key only one operand holds, that no `exclude` holds and the range does not cut, is answered from that
|
|
6188
|
+
* operand's index, so a union of segments that mostly do not overlap reads little of them; every other key is
|
|
6189
|
+
* decoded and counted. On handles that are not pinned it makes no more requests than {@link union} does, and reads
|
|
6190
|
+
* fewer bytes when the shared keys are few. An empty `others`, with no `exclude` and no range, is {@link count} of
|
|
6191
|
+
* `this`, answered from the registry row when it can be; when that figure is 0 it adds one registry read, to refuse
|
|
6192
|
+
* a segment that does not exist as the stream does.
|
|
6193
|
+
*
|
|
6194
|
+
* What it trusts: the index, for each key it answers without decoding, as {@link count} does, so a party who can write
|
|
6195
|
+
* an object's index can make that key's figure wrong; a decoded key is exact. A source with no per-chunk counts
|
|
6196
|
+
* decodes every key. It takes the options {@link intersectCount} takes; the erasure bound, the lease checks and the
|
|
6197
|
+
* pinned handles are as for {@link intersectCount}, a pinned operand adding a footer read before the first key and at
|
|
6198
|
+
* each look. An empty `others` on a pinned handle is {@link count} of the pin, with no look.
|
|
6199
|
+
*/
|
|
6200
|
+
async unionCount(others, options) {
|
|
6201
|
+
checkOptions(
|
|
6202
|
+
options,
|
|
6203
|
+
"unionCount",
|
|
6204
|
+
CALL_KEYS.unionCount,
|
|
6205
|
+
["allowAbsentOperands"],
|
|
6206
|
+
MISPLACED.unionCount
|
|
6207
|
+
);
|
|
6208
|
+
return this.countCombine("union", others, options ?? void 0);
|
|
6209
|
+
}
|
|
6210
|
+
/**
|
|
6211
|
+
* How many ids `this \ (excludes…)` holds: the length of what {@link andNot} streams, without streaming it. A chunk
|
|
6212
|
+
* key of `this` that no exclude holds and the range does not cut is answered from its index and not read; a key an
|
|
6213
|
+
* exclude shares is decoded and counted. On handles that are not pinned it makes no more requests than {@link andNot}
|
|
6214
|
+
* does, and reads fewer bytes when the excludes overlap little of `this`. An empty `excludes`, with no range, is
|
|
6215
|
+
* {@link count} of `this` (the stream of it is the whole of `this`), answered from the registry row when it can be,
|
|
6216
|
+
* with one registry read added when that figure is 0.
|
|
6217
|
+
*
|
|
6218
|
+
* It takes the options {@link andNot} takes (every one of {@link intersectCount}'s but `exclude`, which a count of a
|
|
6219
|
+
* difference has no use for, since `excludes` is its subtraction) and trusts what {@link unionCount} trusts. The
|
|
6220
|
+
* erasure bound, the lease checks and the pinned handles are as for {@link intersectCount}.
|
|
6221
|
+
*/
|
|
6222
|
+
async andNotCount(excludes, options) {
|
|
6223
|
+
checkOptions(
|
|
6224
|
+
options,
|
|
6225
|
+
"andNotCount",
|
|
6226
|
+
CALL_KEYS.andNotCount,
|
|
6227
|
+
["allowAbsentOperands"],
|
|
6228
|
+
MISPLACED.andNotCount
|
|
6229
|
+
);
|
|
6230
|
+
return this.countCombine("andNot", excludes, options ?? void 0);
|
|
6231
|
+
}
|
|
6232
|
+
/** The one body of the three count methods, after each has checked its own options. */
|
|
6233
|
+
async countCombine(verb, operands, options) {
|
|
6234
|
+
const name = `${verb}Count`;
|
|
6235
|
+
const exclude = verb === "andNot" ? NO_SEGMENTS : this.excludesOf(options);
|
|
6236
|
+
const others = segmentList(
|
|
6237
|
+
operands,
|
|
6238
|
+
verb === "andNot" ? `${name}: \`excludes\`` : `${name}: \`others\``
|
|
6239
|
+
);
|
|
6240
|
+
if (verb === "intersect" && others.length === 0) refuseNoOperands("intersectCount");
|
|
6241
|
+
const handles = [this, ...others, ...exclude];
|
|
6242
|
+
this.assertSegments(name, verb === "andNot" ? "an exclude" : "an operand", others);
|
|
6243
|
+
this.assertSegments(name, "an exclude", exclude);
|
|
6244
|
+
this.assertLeases(handles);
|
|
6245
|
+
const engine = this.combineEngine(handles) ?? this.engine;
|
|
6246
|
+
const read = options == null ? void 0 : readOptions(options);
|
|
6247
|
+
const guard2 = handles.some((h) => h.leaseHold !== void 0) ? () => this.assertLeases(handles) : void 0;
|
|
6248
|
+
const opts = guard2 === void 0 ? read : { ...read, guard: guard2 };
|
|
6249
|
+
const refs = others.map((o) => o.ref);
|
|
6250
|
+
if (verb === "andNot") return engine.combineCount([this.ref], refs, "all", verb, opts);
|
|
6251
|
+
return engine.combineCount(
|
|
6252
|
+
[this.ref, ...refs],
|
|
6253
|
+
exclude.map((o) => o.ref),
|
|
6254
|
+
verb === "union" ? "any" : "all",
|
|
6255
|
+
verb,
|
|
6256
|
+
opts
|
|
6257
|
+
);
|
|
6258
|
+
}
|
|
6128
6259
|
/**
|
|
6129
6260
|
* The one body of the three combines, read as ids or as chunks. `operands` is `others` for `intersect` and
|
|
6130
6261
|
* `union`, and `excludes` for `andNot`, which takes no `exclude` option.
|