@thi.ng/column-store 0.5.1 → 0.6.1

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/README.md CHANGED
@@ -448,7 +448,7 @@ For Node.js REPL:
448
448
  const cs = await import("@thi.ng/column-store");
449
449
  ```
450
450
 
451
- Package sizes (brotli'd, pre-treeshake): ESM: 5.37 KB
451
+ Package sizes (brotli'd, pre-treeshake): ESM: 5.50 KB
452
452
 
453
453
  ## Dependencies
454
454
 
package/api.d.ts CHANGED
@@ -92,15 +92,44 @@ export interface IColumn {
92
92
  validate(value: any): boolean;
93
93
  /**
94
94
  * Searches for `value` in the column data, optionally constrained to given
95
- * `start`/`end` range. If found, returns row ID of first occurrence,
96
- * otherwise -1.
95
+ * `start`/`end` range (end index is exclusive and defaults to current table
96
+ * length). If found, returns row ID of first occurrence, otherwise -1.
97
97
  *
98
98
  * @param value
99
99
  * @param start
100
100
  * @param end
101
101
  */
102
102
  indexOf(value: any, start?: number, end?: number): number;
103
+ /**
104
+ * Searches for `value` in the column data in reverse order (from the last
105
+ * row), optionally constrained to given `start`/`end` range (end index is
106
+ * exclusive and defaults to current table length). If found, returns row ID
107
+ * of first occurrence, otherwise -1.
108
+ *
109
+ * @param value
110
+ * @param start
111
+ * @param end
112
+ */
113
+ lastIndexOf(value: any, start?: number, end?: number): number;
114
+ /**
115
+ * Similar to {@link IColumn.indexOf}, but applies given predicate function
116
+ * to each row value. Returns index of first row for which the predicate is
117
+ * truthy, otherwise returns -1.
118
+ *
119
+ * @param pred
120
+ * @param start
121
+ * @param end
122
+ */
103
123
  findIndex(pred: Predicate<any>, start?: number, end?: number): number;
124
+ /**
125
+ * Similar to {@link IColumn.lastIndexOf}, but applies given predicate
126
+ * function to each row value. Returns index of first row for which the
127
+ * predicate is truthy, otherwise returns -1.
128
+ *
129
+ * @param pred
130
+ * @param start
131
+ * @param end
132
+ */
104
133
  findLastIndex(pred: Predicate<any>, start?: number, end?: number): number;
105
134
  /**
106
135
  * Returns value at given row.
package/bitmap.d.ts CHANGED
@@ -28,6 +28,7 @@ export declare class Bitfield {
28
28
  constructor(buffer?: Uint32Array | undefined);
29
29
  ones(max?: number): Generator<number, void, unknown>;
30
30
  first(start?: number, end?: number): number;
31
+ last(start?: number, end?: number): number;
31
32
  setBit(id: number): void;
32
33
  clearBit(id: number): void;
33
34
  fill(x: 0 | 1, start: number, end: number): this;
package/bitmap.js CHANGED
@@ -1,4 +1,4 @@
1
- import { __clamp } from "./internal/indexof.js";
1
+ import { __clampRange } from "./internal/indexof.js";
2
2
  class BitmapIndex {
3
3
  index = /* @__PURE__ */ new Map();
4
4
  /**
@@ -66,9 +66,7 @@ class Bitfield {
66
66
  first(start = 0, end) {
67
67
  const { buffer } = this;
68
68
  if (!buffer) return -1;
69
- const max = buffer.length << 5;
70
- start = __clamp(start, 0, max);
71
- end = __clamp(end ?? max, 0, max);
69
+ [start, end] = __clampRange(buffer.length << 5, start, end);
72
70
  if (start >= end) return -1;
73
71
  for (let i = start >>> 5, n = Math.min(Math.ceil(end / 32), buffer.length); i < n; i++) {
74
72
  let bits = buffer[i];
@@ -81,6 +79,22 @@ class Bitfield {
81
79
  }
82
80
  return -1;
83
81
  }
82
+ last(start = 0, end) {
83
+ const { buffer } = this;
84
+ if (!buffer) return -1;
85
+ [start, end] = __clampRange(buffer.length << 5, start, end);
86
+ if (start >= end) return -1;
87
+ for (let i = end >>> 5, n = start >>> 5; i >= n; i--) {
88
+ let bits = buffer[i];
89
+ while (bits) {
90
+ const msb = Math.clz32(bits) ^ 31;
91
+ const x = (i << 5) + msb;
92
+ if (x >= start && x < end) return x;
93
+ bits ^= 1 << msb;
94
+ }
95
+ }
96
+ return -1;
97
+ }
84
98
  setBit(id) {
85
99
  const w = id >>> 5;
86
100
  this.ensure(w)[w] |= 1 << (id & 31);
@@ -21,6 +21,7 @@ export declare abstract class AColumn<T extends Row = Row> implements IColumn {
21
21
  abstract getRow(i: number): any;
22
22
  abstract getRowKey(i: number): any;
23
23
  abstract indexOf(value: any, start?: number, end?: number): number;
24
+ abstract lastIndexOf(value: any, start?: number, end?: number): number;
24
25
  findIndex(pred: Predicate<any>, start?: number, end?: number): number;
25
26
  findLastIndex(pred: Predicate<any>, start?: number, end?: number): number;
26
27
  encode(value: any): any;
@@ -3,7 +3,7 @@ import {
3
3
  } from "../api.js";
4
4
  import { BitmapIndex } from "../bitmap.js";
5
5
  import { __columnError } from "../internal/checks.js";
6
- import { __clamp } from "../internal/indexof.js";
6
+ import { __clampRange } from "../internal/indexof.js";
7
7
  class AColumn {
8
8
  constructor(id, table) {
9
9
  this.id = id;
@@ -18,19 +18,15 @@ class AColumn {
18
18
  this.updateBitmap();
19
19
  }
20
20
  findIndex(pred, start = 0, end) {
21
- const max = this.table.length - 1;
22
- start = __clamp(start, 0, max);
23
- end = __clamp(end ?? max, 0, max);
24
- for (let i = start; i <= end; i++) {
21
+ [start, end] = __clampRange(this.table.length, start, end);
22
+ for (let i = start; i < end; i++) {
25
23
  if (pred(this.getRow(i))) return i;
26
24
  }
27
25
  return -1;
28
26
  }
29
27
  findLastIndex(pred, start = 0, end) {
30
- const max = this.table.length - 1;
31
- start = __clamp(start, 0, max);
32
- end = __clamp(end ?? max, 0, max);
33
- for (let i = end; i >= start; i--) {
28
+ [start, end] = __clampRange(this.table.length, start, end);
29
+ for (let i = end; i-- > start; ) {
34
30
  if (pred(this.getRow(i))) return i;
35
31
  }
36
32
  return -1;
@@ -60,11 +56,11 @@ class AColumn {
60
56
  if (!bitmap) return;
61
57
  bitmap.clear();
62
58
  for (let i = 0, n = this.table.length; i < n; i++) {
63
- const value = this.getRow(i);
59
+ const value = this.getRowKey(i);
64
60
  if (value == null) continue;
65
61
  if (isArray) {
66
62
  for (let x of value) bitmap.setBit(x, i);
67
- } else bitmap.setBit(this.getRowKey(i), i);
63
+ } else bitmap.setBit(value, i);
68
64
  }
69
65
  }
70
66
  ensureValue(val) {
@@ -16,6 +16,7 @@ export declare class DictTupleColumn<T extends Row = Row> extends AColumn<T> {
16
16
  valueKey(value: any): (number | null)[];
17
17
  removeRow(i: number): void;
18
18
  indexOf(value: any, start?: number, end?: number): number;
19
+ lastIndexOf(value: any, start?: number, end?: number): number;
19
20
  replaceValue(currValue: any, newValue: any): boolean;
20
21
  toJSON(): {
21
22
  dict: {
@@ -2,7 +2,7 @@ import { BidirIndex } from "@thi.ng/bidir-index";
2
2
  import { isArray } from "@thi.ng/checks/is-array";
3
3
  import { FLAG_UNIQUE } from "../api.js";
4
4
  import { __validateArrayValue } from "../internal/checks.js";
5
- import { __indexOfTuple } from "../internal/indexof.js";
5
+ import { __indexOfTuple, __lastIndexOfTuple } from "../internal/indexof.js";
6
6
  import { __serializeDict } from "../internal/serialize.js";
7
7
  import { AColumn } from "./acolumn.js";
8
8
  class DictTupleColumn extends AColumn {
@@ -65,6 +65,15 @@ class DictTupleColumn extends AColumn {
65
65
  end
66
66
  );
67
67
  }
68
+ lastIndexOf(value, start = 0, end) {
69
+ return __lastIndexOfTuple(
70
+ value != null ? this.encode(value) : null,
71
+ this.values,
72
+ this.table.length,
73
+ start,
74
+ end
75
+ );
76
+ }
68
77
  replaceValue(currValue, newValue) {
69
78
  const { dict, values, bitmap } = this;
70
79
  const res = dict.renameKey(currValue, newValue);
package/columns/dict.d.ts CHANGED
@@ -16,6 +16,7 @@ export declare class DictColumn<T extends Row = Row> extends AColumn<T> {
16
16
  valueKey(value: any): number | number[] | undefined;
17
17
  removeRow(i: number): void;
18
18
  indexOf(value: any, start?: number, end?: number): number;
19
+ lastIndexOf(value: any, start?: number, end?: number): number;
19
20
  replaceValue(currValue: any, newValue: any): boolean;
20
21
  toJSON(): {
21
22
  dict: {
package/columns/dict.js CHANGED
@@ -4,7 +4,7 @@ import { decodeBinary, encodeBinary } from "@thi.ng/rle-pack/binary";
4
4
  import { decodeSimple, encodeSimple } from "@thi.ng/rle-pack/simple";
5
5
  import { FLAG_RLE } from "../api.js";
6
6
  import { __validateValue } from "../internal/checks.js";
7
- import { __indexOfSingle } from "../internal/indexof.js";
7
+ import { __indexOfSingle, __lastIndexOfSingle } from "../internal/indexof.js";
8
8
  import { __serializeDict } from "../internal/serialize.js";
9
9
  import { AColumn } from "./acolumn.js";
10
10
  class DictColumn extends AColumn {
@@ -68,6 +68,16 @@ class DictColumn extends AColumn {
68
68
  end
69
69
  );
70
70
  }
71
+ lastIndexOf(value, start = 0, end) {
72
+ return __lastIndexOfSingle(
73
+ this.encode(value),
74
+ this.values,
75
+ this.bitmap,
76
+ this.table.length,
77
+ start,
78
+ end
79
+ );
80
+ }
71
81
  replaceValue(currValue, newValue) {
72
82
  const { dict, values, bitmap } = this;
73
83
  const res = dict.renameKey(currValue, newValue);
@@ -11,6 +11,7 @@ export declare class PlainColumn<T extends Row = Row> extends AColumn<T> {
11
11
  valueKey(x: any): any;
12
12
  removeRow(i: number): void;
13
13
  indexOf(value: any, start?: number, end?: number): number;
14
+ lastIndexOf(value: any, start?: number, end?: number): number;
14
15
  replaceValue(currValue: any, newValue: any): boolean;
15
16
  toJSON(): {
16
17
  values: any[];
package/columns/plain.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { decodeSimple, encodeSimple } from "@thi.ng/rle-pack/simple";
2
2
  import { FLAG_RLE } from "../api.js";
3
3
  import { __validateValue } from "../internal/checks.js";
4
- import { __indexOfSingle } from "../internal/indexof.js";
4
+ import { __indexOfSingle, __lastIndexOfSingle } from "../internal/indexof.js";
5
5
  import { __replaceValue } from "../internal/replace.js";
6
6
  import { AColumn } from "./acolumn.js";
7
7
  class PlainColumn extends AColumn {
@@ -46,6 +46,16 @@ class PlainColumn extends AColumn {
46
46
  end
47
47
  );
48
48
  }
49
+ lastIndexOf(value, start = 0, end) {
50
+ return __lastIndexOfSingle(
51
+ value,
52
+ this.values,
53
+ this.bitmap,
54
+ this.table.length,
55
+ start,
56
+ end
57
+ );
58
+ }
49
59
  replaceValue(currValue, newValue) {
50
60
  return __replaceValue(this.bitmap, this.values, currValue, newValue);
51
61
  }
@@ -13,6 +13,7 @@ export declare class TupleColumn<T extends Row = Row> extends AColumn<T> {
13
13
  valueKey(value: any): any[];
14
14
  removeRow(i: number): void;
15
15
  indexOf(value: any, start?: number, end?: number): number;
16
+ lastIndexOf(value: any, start?: number, end?: number): number;
16
17
  replaceValue(currValue: any, newValue: any): boolean;
17
18
  toJSON(): {
18
19
  values: Nullable<number[]>[];
package/columns/tuple.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isArray } from "@thi.ng/checks/is-array";
2
2
  import { FLAG_UNIQUE } from "../api.js";
3
3
  import { __validateArrayValue } from "../internal/checks.js";
4
- import { __indexOfTuple } from "../internal/indexof.js";
4
+ import { __indexOfTuple, __lastIndexOfTuple } from "../internal/indexof.js";
5
5
  import { AColumn } from "./acolumn.js";
6
6
  class TupleColumn extends AColumn {
7
7
  values = [];
@@ -48,6 +48,15 @@ class TupleColumn extends AColumn {
48
48
  end
49
49
  );
50
50
  }
51
+ lastIndexOf(value, start = 0, end) {
52
+ return __lastIndexOfTuple(
53
+ value,
54
+ this.values,
55
+ this.table.length,
56
+ start,
57
+ end
58
+ );
59
+ }
51
60
  replaceValue(currValue, newValue) {
52
61
  const { values, bitmap } = this;
53
62
  const isUnique = this.spec.flags & FLAG_UNIQUE;
@@ -17,6 +17,7 @@ export declare class TypedArrayColumn<T extends Row = Row> extends AColumn<T> {
17
17
  valueKey(value: any): number | number[];
18
18
  removeRow(i: number): void;
19
19
  indexOf(value: any, start?: number, end?: number): number;
20
+ lastIndexOf(value: any, start?: number, end?: number): number;
20
21
  replaceValue(currValue: any, newValue: any): boolean;
21
22
  toJSON(): {
22
23
  values: any[];
@@ -4,7 +4,7 @@ import { isNumber } from "@thi.ng/checks/is-number";
4
4
  import {
5
5
  LIMITS
6
6
  } from "../api.js";
7
- import { __indexOfSingle } from "../internal/indexof.js";
7
+ import { __indexOfSingle, __lastIndexOfSingle } from "../internal/indexof.js";
8
8
  import { __replaceValue } from "../internal/replace.js";
9
9
  import { __deserializeTyped, __serializeTyped } from "../internal/serialize.js";
10
10
  import { AColumn } from "./acolumn.js";
@@ -78,6 +78,16 @@ class TypedArrayColumn extends AColumn {
78
78
  end
79
79
  );
80
80
  }
81
+ lastIndexOf(value, start = 0, end) {
82
+ return __lastIndexOfSingle(
83
+ value,
84
+ this.values,
85
+ this.bitmap,
86
+ this.table.length,
87
+ start,
88
+ end
89
+ );
90
+ }
81
91
  replaceValue(currValue, newValue) {
82
92
  return __replaceValue(this.bitmap, this.values, currValue, newValue);
83
93
  }
@@ -13,11 +13,12 @@ export declare class VectorColumn<T extends Row = Row> extends AColumn<T> {
13
13
  load({ values }: SerializedColumn): void;
14
14
  validate(value: any): boolean;
15
15
  setRow(i: number, value: any): void;
16
- getRow(i: number): Uint32Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike>;
16
+ getRow(i: number): Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
17
17
  getRowKey(i: number): string;
18
18
  valueKey(value: any): string | string[];
19
19
  removeRow(i: number): void;
20
20
  indexOf(value: any, start?: number, end?: number): number;
21
+ lastIndexOf(value: any, start?: number, end?: number): number;
21
22
  replaceValue(): boolean;
22
23
  toJSON(): {
23
24
  values: any[];
package/columns/vector.js CHANGED
@@ -6,6 +6,7 @@ import { unsupportedOp } from "@thi.ng/errors/unsupported";
6
6
  import {
7
7
  LIMITS
8
8
  } from "../api.js";
9
+ import { __clampRange } from "../internal/indexof.js";
9
10
  import { __deserializeTyped, __serializeTyped } from "../internal/serialize.js";
10
11
  import { AColumn } from "./acolumn.js";
11
12
  class VectorColumn extends AColumn {
@@ -76,11 +77,10 @@ class VectorColumn extends AColumn {
76
77
  this.values.fill(0, (length - 1) * size);
77
78
  this.bitmap?.removeBit(i);
78
79
  }
79
- indexOf(value, start = 0, end = this.table.length) {
80
+ indexOf(value, start = 0, end) {
80
81
  if (value == null) return -1;
81
82
  const { values, bitmap, size } = this;
82
- start = Math.max(start, 0);
83
- end = Math.min(end, this.table.length);
83
+ [start, end] = __clampRange(this.table.length, start, end);
84
84
  if (bitmap) {
85
85
  return bitmap.index.get(this.valueKey(value))?.first(start, end) ?? -1;
86
86
  }
@@ -94,6 +94,23 @@ class VectorColumn extends AColumn {
94
94
  }
95
95
  return -1;
96
96
  }
97
+ lastIndexOf(value, start = 0, end) {
98
+ if (value == null || value.length !== this.size) return -1;
99
+ const { values, bitmap, size } = this;
100
+ [start, end] = __clampRange(this.table.length, start, end);
101
+ if (bitmap) {
102
+ return bitmap.index.get(this.valueKey(value))?.last(start, end) ?? -1;
103
+ }
104
+ start *= size;
105
+ let i, j;
106
+ outer: for (i = end * size; (i -= size) >= start; ) {
107
+ for (j = 0; j < size; j++) {
108
+ if (values[i + j] !== value[j]) continue outer;
109
+ }
110
+ return i / size;
111
+ }
112
+ return -1;
113
+ }
97
114
  replaceValue() {
98
115
  unsupportedOp("TODO");
99
116
  }
@@ -3,7 +3,13 @@ import type { BitmapIndex } from "../bitmap.js";
3
3
  /** @internal */
4
4
  export declare const __indexOfSingle: (needle: any, values: ArrayLike<any>, bitmap: Maybe<BitmapIndex>, max: number, start: number, end?: number) => number;
5
5
  /** @internal */
6
+ export declare const __lastIndexOfSingle: (needle: any, values: ArrayLike<any>, bitmap: Maybe<BitmapIndex>, max: number, start: number, end?: number) => number;
7
+ /** @internal */
6
8
  export declare const __indexOfTuple: (needle: ArrayLike<any> | null, values: ArrayLike<any>, max: number, start: number, end?: number) => number;
7
9
  /** @internal */
10
+ export declare const __lastIndexOfTuple: (needle: ArrayLike<any> | null, values: ArrayLike<any>, max: number, start: number, end?: number) => number;
11
+ /** @internal */
8
12
  export declare const __clamp: (x: number, a: number, b: number) => number;
13
+ /** @internal */
14
+ export declare const __clampRange: (max: number, start: number, end?: number) => number[];
9
15
  //# sourceMappingURL=indexof.d.ts.map
@@ -1,15 +1,21 @@
1
1
  const __indexOfSingle = (needle, values, bitmap, max, start, end = max) => {
2
- start = __clamp(start, 0, max);
3
- end = __clamp(end, 0, max);
2
+ [start, end] = __clampRange(max, start, end);
4
3
  if (bitmap) return bitmap.index.get(needle)?.first(start, end) ?? -1;
5
4
  for (let i = start; i < end; i++) {
6
5
  if (values[i] === needle) return i;
7
6
  }
8
7
  return -1;
9
8
  };
9
+ const __lastIndexOfSingle = (needle, values, bitmap, max, start, end = max) => {
10
+ [start, end] = __clampRange(max, start, end);
11
+ if (bitmap) return bitmap.index.get(needle)?.last(start, end) ?? -1;
12
+ for (let i = end; i-- > start; ) {
13
+ if (values[i] === needle) return i;
14
+ }
15
+ return -1;
16
+ };
10
17
  const __indexOfTuple = (needle, values, max, start, end = max) => {
11
- start = __clamp(start, 0, max);
12
- end = __clamp(end, 0, max);
18
+ [start, end] = __clampRange(max, start, end);
13
19
  if (needle == null) {
14
20
  for (let i2 = start; i2 < end; i2++) {
15
21
  if (!values[i2]) return i2;
@@ -29,9 +35,38 @@ const __indexOfTuple = (needle, values, max, start, end = max) => {
29
35
  }
30
36
  return -1;
31
37
  };
38
+ const __lastIndexOfTuple = (needle, values, max, start, end = max) => {
39
+ [start, end] = __clampRange(max, start, end);
40
+ if (needle == null) {
41
+ for (let i2 = end; i2-- > start; ) {
42
+ if (!values[i2]) return i2;
43
+ }
44
+ return -1;
45
+ }
46
+ const n = needle.length;
47
+ let i, j, row;
48
+ outer: for (i = end; i-- > start; ) {
49
+ row = values[i];
50
+ if (row?.length === n) {
51
+ for (j = 0; j < n; j++) {
52
+ if (row[j] !== needle[j]) continue outer;
53
+ }
54
+ return i;
55
+ }
56
+ }
57
+ return -1;
58
+ };
32
59
  const __clamp = (x, a, b) => x < a ? a : x > b ? b : x;
60
+ const __clampRange = (max, start, end) => {
61
+ start = __clamp(start, 0, max);
62
+ end = __clamp(end ?? max, start, max);
63
+ return [start, end];
64
+ };
33
65
  export {
34
66
  __clamp,
67
+ __clampRange,
35
68
  __indexOfSingle,
36
- __indexOfTuple
69
+ __indexOfTuple,
70
+ __lastIndexOfSingle,
71
+ __lastIndexOfTuple
37
72
  };
@@ -11,5 +11,5 @@ export declare const __serializeTyped: ($values: NumericArray, spec: ColumnSpec,
11
11
  values: any[];
12
12
  };
13
13
  /** @internal */
14
- export declare const __deserializeTyped: (type: Type, flags: number, values: number[]) => Uint32Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike>;
14
+ export declare const __deserializeTyped: (type: Type, flags: number, values: number[]) => Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
15
15
  //# sourceMappingURL=serialize.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/column-store",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "In-memory column store database with customizable column types, extensible query engine, bitfield indexing for query acceleration, JSON serialization with optional RLE compression",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -126,5 +126,5 @@
126
126
  "status": "alpha",
127
127
  "year": 2025
128
128
  },
129
- "gitHead": "34b762e0eeaf54adf4a879843a96a53e5e59d6d1\n"
129
+ "gitHead": "c048d3c6149769309feff3fa943dcd3767b5c6b2\n"
130
130
  }
package/query.js CHANGED
@@ -3,7 +3,7 @@ import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
3
3
  import { unsupportedOp } from "@thi.ng/errors/unsupported";
4
4
  import { Bitfield } from "./bitmap.js";
5
5
  import { __columnError } from "./internal/checks.js";
6
- import { __clamp } from "./internal/indexof.js";
6
+ import { __clampRange } from "./internal/indexof.js";
7
7
  class Query {
8
8
  constructor(table, terms = []) {
9
9
  this.table = table;
@@ -302,16 +302,7 @@ const QUERY_OPS = {
302
302
  if (start != null) $start = column.findIndex((x) => x >= start);
303
303
  if (end != null)
304
304
  $end = column.findLastIndex((x) => x <= end, $start);
305
- if ($start >= 0 && $end >= 0) {
306
- const mask = ctx.makeMask();
307
- const bitmap = new Bitfield(mask);
308
- bitmap.fill(
309
- 1,
310
- $start >= 0 ? $start : 0,
311
- $end >= 0 ? $end + 1 : max
312
- );
313
- ctx.mergeMask(mask);
314
- } else ctx.clear();
305
+ __fillMask(ctx, $start, $end >= 0 ? $end + 1 : $end);
315
306
  }
316
307
  },
317
308
  rowRange: {
@@ -319,17 +310,19 @@ const QUERY_OPS = {
319
310
  fn: (ctx, term) => {
320
311
  const max = ctx.table.length;
321
312
  let { start = 0, end = max } = term.value;
322
- start = __clamp(start, 0, max);
323
- end = __clamp(end, 0, max);
324
- if (start >= 0 && end >= 0) {
325
- const mask = ctx.makeMask();
326
- const bitmap = new Bitfield(mask);
327
- bitmap.fill(1, start, end);
328
- ctx.mergeMask(mask);
329
- } else ctx.clear();
313
+ [start, end] = __clampRange(max, start, end);
314
+ __fillMask(ctx, start, end);
330
315
  }
331
316
  }
332
317
  };
318
+ const __fillMask = (ctx, start, end, fill = 1) => {
319
+ if (start >= 0 && end >= 0) {
320
+ const mask = ctx.makeMask();
321
+ const bitmap = new Bitfield(mask);
322
+ bitmap.fill(fill, start, end);
323
+ ctx.mergeMask(mask);
324
+ } else ctx.clear();
325
+ };
333
326
  const registerQueryOp = (type, spec) => {
334
327
  if (QUERY_OPS[type]) illegalArgs(`query op ${type} already registered`);
335
328
  QUERY_OPS[type] = spec;
package/table.d.ts CHANGED
@@ -33,6 +33,7 @@ export declare class Table<T extends Row> {
33
33
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean, includeID?: false): Maybe<Pick<T, K>>;
34
34
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean, includeID?: true): Maybe<RowWithMeta<Pick<T, K>>>;
35
35
  indexOf(id: ColumnID<T>, value: any, start?: number, end?: number): number;
36
+ lastIndexOf(id: ColumnID<T>, value: any, start?: number, end?: number): number;
36
37
  validateRow(row: Partial<T>): void;
37
38
  validateColumnSpec(id: ColumnID<T>, spec: ColumnSpec): void;
38
39
  toJSON(): {
package/table.js CHANGED
@@ -105,6 +105,9 @@ class Table {
105
105
  indexOf(id, value, start, end) {
106
106
  return this.columns[id]?.indexOf(value, start, end) ?? -1;
107
107
  }
108
+ lastIndexOf(id, value, start, end) {
109
+ return this.columns[id]?.lastIndexOf(value, start, end) ?? -1;
110
+ }
108
111
  validateRow(row) {
109
112
  const { columns } = this;
110
113
  for (let id in columns) {