cspell-trie-lib 8.3.2 → 8.4.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,61 +1,56 @@
1
+ import { NumberSequenceByteDecoderAccumulator, NumberSequenceByteEncoderDecoder, } from './NumberSequenceByteDecoderAccumulator.js';
2
+ const SpecialCharIndexMask = NumberSequenceByteEncoderDecoder.SpecialCharIndexMask;
1
3
  const EmptyKeys = Object.freeze([]);
2
4
  const EmptyNodes = Object.freeze([]);
5
+ const EmptyEntries = Object.freeze([]);
3
6
  class FastTrieBlobINode {
4
7
  trie;
5
8
  nodeIdx;
6
9
  id;
7
- size;
8
10
  node;
9
11
  eow;
10
- charToIdx;
11
12
  _keys;
13
+ _count;
14
+ _size;
15
+ _chained;
16
+ _nodesEntries;
17
+ _entries;
18
+ _values;
19
+ charToIdx;
12
20
  constructor(trie, nodeIdx) {
13
21
  this.trie = trie;
14
22
  this.nodeIdx = nodeIdx;
15
23
  const node = trie.nodes[nodeIdx];
16
24
  this.node = node;
17
- const value = node[0];
18
- this.eow = !!(value & trie.NodeMaskEOW);
19
- this.size = node.length - 1;
25
+ this.eow = !!(node[0] & trie.NodeMaskEOW);
26
+ this._count = node.length - 1;
20
27
  this.id = nodeIdx;
21
28
  }
22
- keys() {
23
- return (this._keys ??= this.calcKeys());
24
- }
25
29
  /** get keys to children */
26
- calcKeys() {
27
- if (!this.size)
30
+ keys() {
31
+ if (this._keys)
32
+ return this._keys;
33
+ if (!this._count)
28
34
  return EmptyKeys;
29
- const NodeMaskChildCharIndex = this.trie.NodeMaskChildCharIndex;
30
- const charIndex = this.trie.charIndex;
31
- const keys = Array(this.size);
32
- const len = this.size;
33
- const node = this.trie.nodes[this.nodeIdx];
34
- for (let i = 0; i < len; ++i) {
35
- const entry = node[i + 1];
36
- const charIdx = entry & NodeMaskChildCharIndex;
37
- keys[i] = charIndex[charIdx];
38
- }
39
- return Object.freeze(keys);
35
+ this._keys = this.getNodesEntries().map(([key]) => key);
36
+ return this._keys;
40
37
  }
41
38
  values() {
42
- if (!this.size)
39
+ if (!this._count)
43
40
  return EmptyNodes;
44
- const nodes = Array(this.size);
45
- for (let i = 0; i < this.size; ++i) {
46
- nodes[i] = this.child(i);
47
- }
48
- return nodes;
41
+ if (this._values)
42
+ return this._values;
43
+ this._values = this.entries().map(([, value]) => value);
44
+ return this._values;
49
45
  }
50
46
  entries() {
51
- const keys = this.keys();
52
- const values = this.values();
53
- const len = keys.length;
54
- const entries = Array(len);
55
- for (let i = 0; i < len; ++i) {
56
- entries[i] = [keys[i], values[i]];
57
- }
58
- return entries;
47
+ if (this._entries)
48
+ return this._entries;
49
+ if (!this._count)
50
+ return EmptyEntries;
51
+ const entries = this.getNodesEntries();
52
+ this._entries = entries.map(([key, value]) => [key, new FastTrieBlobINode(this.trie, value)]);
53
+ return this._entries;
59
54
  }
60
55
  /** get child ITrieNode */
61
56
  get(char) {
@@ -69,12 +64,15 @@ class FastTrieBlobINode {
69
64
  return idx !== undefined;
70
65
  }
71
66
  hasChildren() {
72
- return this.size > 0;
67
+ return this._count > 0;
73
68
  }
74
69
  child(keyIdx) {
75
- const node = this.trie.nodes[this.nodeIdx];
76
- const nodeIdx = node[keyIdx + 1] >>> this.trie.NodeChildRefShift;
77
- return new FastTrieBlobINode(this.trie, nodeIdx);
70
+ if (!this._values && !this.containsChainedIndexes()) {
71
+ const n = this.node[keyIdx + 1];
72
+ const nodeIdx = n >>> this.trie.NodeChildRefShift;
73
+ return new FastTrieBlobINode(this.trie, nodeIdx);
74
+ }
75
+ return this.values()[keyIdx];
78
76
  }
79
77
  getCharToIdxMap() {
80
78
  const m = this.charToIdx;
@@ -88,6 +86,98 @@ class FastTrieBlobINode {
88
86
  this.charToIdx = map;
89
87
  return map;
90
88
  }
89
+ containsChainedIndexes() {
90
+ if (this._chained !== undefined)
91
+ return this._chained;
92
+ if (!this._count || !this.trie.isIndexDecoderNeeded) {
93
+ this._chained = false;
94
+ return false;
95
+ }
96
+ // scan the node to see if there are encoded entries.
97
+ let found = false;
98
+ const NodeMaskChildCharIndex = this.trie.NodeMaskChildCharIndex;
99
+ const len = this._count;
100
+ const node = this.node;
101
+ for (let i = 1; i <= len && !found; ++i) {
102
+ const entry = node[i];
103
+ const charIdx = entry & NodeMaskChildCharIndex;
104
+ found = (charIdx & SpecialCharIndexMask) === SpecialCharIndexMask;
105
+ }
106
+ this._chained = !!found;
107
+ return this._chained;
108
+ }
109
+ getNodesEntries() {
110
+ if (this._nodesEntries)
111
+ return this._nodesEntries;
112
+ if (!this.containsChainedIndexes()) {
113
+ const entries = Array(this._count);
114
+ const nodes = this.node;
115
+ const charIndex = this.trie.charIndex;
116
+ const NodeMaskChildCharIndex = this.trie.NodeMaskChildCharIndex;
117
+ const RefShift = this.trie.NodeChildRefShift;
118
+ for (let i = 0; i < this._count; ++i) {
119
+ const entry = nodes[i + 1];
120
+ const charIdx = entry & NodeMaskChildCharIndex;
121
+ entries[i] = [charIndex[charIdx], entry >>> RefShift];
122
+ }
123
+ this._nodesEntries = entries;
124
+ return entries;
125
+ }
126
+ this._nodesEntries = this.walkChainedIndexes();
127
+ return this._nodesEntries;
128
+ }
129
+ walkChainedIndexes() {
130
+ const NodeMaskChildCharIndex = this.trie.NodeMaskChildCharIndex;
131
+ const NodeChildRefShift = this.trie.NodeChildRefShift;
132
+ const nodes = this.trie.nodes;
133
+ const acc = NumberSequenceByteDecoderAccumulator.create();
134
+ const stack = [{ n: this.node, c: 1, acc }];
135
+ let depth = 0;
136
+ /** there is at least this._count number of entries, more if there are nested indexes. */
137
+ const entries = Array(this._count);
138
+ let eIdx = 0;
139
+ const charIndex = this.trie.charIndex;
140
+ while (depth >= 0) {
141
+ const s = stack[depth];
142
+ const { n: node, c: off } = s;
143
+ if (off >= node.length) {
144
+ --depth;
145
+ continue;
146
+ }
147
+ ++s.c;
148
+ const entry = node[off];
149
+ const charIdx = entry & NodeMaskChildCharIndex;
150
+ const acc = s.acc.clone();
151
+ const letterIdx = acc.decode(charIdx);
152
+ if (letterIdx !== undefined) {
153
+ const char = charIndex[letterIdx];
154
+ const nodeIdx = entry >>> NodeChildRefShift;
155
+ entries[eIdx++] = [char, nodeIdx];
156
+ continue;
157
+ }
158
+ const idx = entry >>> NodeChildRefShift;
159
+ const ss = stack[++depth];
160
+ if (ss) {
161
+ ss.n = nodes[idx];
162
+ ss.c = 1;
163
+ ss.acc = acc;
164
+ }
165
+ else {
166
+ stack[depth] = { n: nodes[idx], c: 1, acc };
167
+ }
168
+ }
169
+ return entries;
170
+ }
171
+ get size() {
172
+ if (this._size === undefined) {
173
+ if (!this.containsChainedIndexes()) {
174
+ this._size = this._count;
175
+ return this._size;
176
+ }
177
+ this._size = this.getNodesEntries().length;
178
+ }
179
+ return this._size;
180
+ }
91
181
  }
92
182
  export class FastTrieBlobIRoot extends FastTrieBlobINode {
93
183
  info;
@@ -1,11 +1,12 @@
1
1
  import type { FastTrieBlobBitMaskInfo } from './FastTrieBlobBitMaskInfo.js';
2
2
  export declare class FastTrieBlobInternals implements FastTrieBlobBitMaskInfo {
3
3
  readonly nodes: number[][];
4
- readonly charIndex: string[];
4
+ readonly charIndex: readonly string[];
5
5
  readonly charToIndexMap: Readonly<Record<string, number>>;
6
6
  readonly NodeMaskEOW: number;
7
7
  readonly NodeMaskChildCharIndex: number;
8
8
  readonly NodeChildRefShift: number;
9
- constructor(nodes: number[][], charIndex: string[], charToIndexMap: Readonly<Record<string, number>>, maskInfo: FastTrieBlobBitMaskInfo);
9
+ readonly isIndexDecoderNeeded: boolean;
10
+ constructor(nodes: number[][], charIndex: readonly string[], charToIndexMap: Readonly<Record<string, number>>, maskInfo: FastTrieBlobBitMaskInfo);
10
11
  }
11
12
  //# sourceMappingURL=FastTrieBlobInternals.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { NumberSequenceByteEncoderDecoder } from './NumberSequenceByteDecoderAccumulator.js';
1
2
  export class FastTrieBlobInternals {
2
3
  nodes;
3
4
  charIndex;
@@ -5,6 +6,7 @@ export class FastTrieBlobInternals {
5
6
  NodeMaskEOW;
6
7
  NodeMaskChildCharIndex;
7
8
  NodeChildRefShift;
9
+ isIndexDecoderNeeded;
8
10
  constructor(nodes, charIndex, charToIndexMap, maskInfo) {
9
11
  this.nodes = nodes;
10
12
  this.charIndex = charIndex;
@@ -13,6 +15,7 @@ export class FastTrieBlobInternals {
13
15
  this.NodeMaskEOW = NodeMaskEOW;
14
16
  this.NodeMaskChildCharIndex = NodeMaskChildCharIndex;
15
17
  this.NodeChildRefShift = NodeChildRefShift;
18
+ this.isIndexDecoderNeeded = charIndex.length > NumberSequenceByteEncoderDecoder.MaxCharIndex;
16
19
  }
17
20
  }
18
21
  //# sourceMappingURL=FastTrieBlobInternals.js.map
@@ -0,0 +1,34 @@
1
+ export declare enum SpecialCharIndex {
2
+ Mask = 248,
3
+ MaxCharIndex = 247,
4
+ Index8bit = 249,
5
+ Index14bit = 250,
6
+ Index21bit = 251
7
+ }
8
+ export type EncodedSequence = [number] | [SpecialCharIndex.Index8bit, number] | [SpecialCharIndex.Index14bit, number, number] | [SpecialCharIndex.Index21bit, number, number, number];
9
+ export declare class NumberSequenceByteEncoderDecoder {
10
+ static encode(n: number): EncodedSequence;
11
+ static decode(encodedSequence: EncodedSequence): number;
12
+ static encodeSequence(sequence: Iterable<number>): Iterable<number>;
13
+ static decodeSequence(sequence: Iterable<number>): number[];
14
+ static SpecialCharIndexMask: SpecialCharIndex.Mask;
15
+ static MaxCharIndex: SpecialCharIndex.MaxCharIndex;
16
+ /**
17
+ * SpecialCharIndex8bit is used to indicate a node chain. Where the final character index is 248 + the index found in the next node.
18
+ */
19
+ static SpecialCharIndex8bit: SpecialCharIndex.Index8bit;
20
+ static SpecialCharIndex16bit: SpecialCharIndex.Index14bit;
21
+ static SpecialCharIndex24bit: SpecialCharIndex.Index21bit;
22
+ }
23
+ export declare class NumberSequenceByteDecoderAccumulator {
24
+ private byteMode;
25
+ private accumulation;
26
+ protected constructor(byteMode?: number, accumulation?: number);
27
+ decodeSequence(sequence: Iterable<number>): Iterable<number>;
28
+ decode(idx: number): number | undefined;
29
+ reset(): void;
30
+ clone(): NumberSequenceByteDecoderAccumulator;
31
+ isPending(): boolean;
32
+ static create(): NumberSequenceByteDecoderAccumulator;
33
+ }
34
+ //# sourceMappingURL=NumberSequenceByteDecoderAccumulator.d.ts.map
@@ -0,0 +1,120 @@
1
+ import { assert } from '../utils/assert.js';
2
+ export var SpecialCharIndex;
3
+ (function (SpecialCharIndex) {
4
+ SpecialCharIndex[SpecialCharIndex["Mask"] = 248] = "Mask";
5
+ SpecialCharIndex[SpecialCharIndex["MaxCharIndex"] = 247] = "MaxCharIndex";
6
+ SpecialCharIndex[SpecialCharIndex["Index8bit"] = 249] = "Index8bit";
7
+ SpecialCharIndex[SpecialCharIndex["Index14bit"] = 250] = "Index14bit";
8
+ SpecialCharIndex[SpecialCharIndex["Index21bit"] = 251] = "Index21bit";
9
+ })(SpecialCharIndex || (SpecialCharIndex = {}));
10
+ export class NumberSequenceByteEncoderDecoder {
11
+ static encode(n) {
12
+ if (n < this.SpecialCharIndexMask)
13
+ return [n];
14
+ if (n < this.SpecialCharIndexMask * 2) {
15
+ return [SpecialCharIndex.Index8bit, n - this.SpecialCharIndexMask];
16
+ }
17
+ if (n < 1 << 14)
18
+ return [SpecialCharIndex.Index14bit, n >>> 7, n & 0x7f];
19
+ assert(n < 1 << 21);
20
+ return [SpecialCharIndex.Index21bit, (n >>> 14) & 0x7f, (n >>> 7) & 0x7f, n & 0x7f];
21
+ }
22
+ static decode(encodedSequence) {
23
+ const [a, b, c, d] = encodedSequence;
24
+ switch (a) {
25
+ case SpecialCharIndex.Index8bit:
26
+ // assert(encodedSequence.length === 2);
27
+ return (b || 0) + this.SpecialCharIndexMask;
28
+ case SpecialCharIndex.Index14bit:
29
+ // assert(encodedSequence.length === 3);
30
+ return ((b || 0) << 7) + (c || 0);
31
+ case SpecialCharIndex.Index21bit:
32
+ // assert(encodedSequence.length === 4);
33
+ return ((b || 0) << 14) + ((c || 0) << 7) + (d || 0);
34
+ default:
35
+ // assert(a <= SpecialCharIndex.MaxCharIndex);
36
+ return a;
37
+ }
38
+ }
39
+ static *encodeSequence(sequence) {
40
+ for (const n of sequence) {
41
+ const encoded = this.encode(n);
42
+ yield* encoded;
43
+ }
44
+ }
45
+ static decodeSequence(sequence) {
46
+ const acc = NumberSequenceByteDecoderAccumulator.create();
47
+ return [...acc.decodeSequence(sequence)];
48
+ }
49
+ static SpecialCharIndexMask = SpecialCharIndex.Mask;
50
+ static MaxCharIndex = SpecialCharIndex.MaxCharIndex;
51
+ /**
52
+ * SpecialCharIndex8bit is used to indicate a node chain. Where the final character index is 248 + the index found in the next node.
53
+ */
54
+ static SpecialCharIndex8bit = SpecialCharIndex.Index8bit;
55
+ static SpecialCharIndex16bit = SpecialCharIndex.Index14bit;
56
+ static SpecialCharIndex24bit = SpecialCharIndex.Index21bit;
57
+ }
58
+ export class NumberSequenceByteDecoderAccumulator {
59
+ byteMode;
60
+ accumulation;
61
+ constructor(byteMode = 0, accumulation = 0) {
62
+ this.byteMode = byteMode;
63
+ this.accumulation = accumulation;
64
+ }
65
+ *decodeSequence(sequence) {
66
+ const accumulator = this.clone();
67
+ for (const idx of sequence) {
68
+ const r = accumulator.decode(idx);
69
+ if (r !== undefined) {
70
+ yield r;
71
+ }
72
+ }
73
+ }
74
+ decode(idx) {
75
+ if (!this.byteMode) {
76
+ if (idx < NumberSequenceByteEncoderDecoder.SpecialCharIndexMask) {
77
+ const v = idx + this.accumulation;
78
+ this.accumulation = 0;
79
+ return v;
80
+ }
81
+ switch (idx) {
82
+ case NumberSequenceByteEncoderDecoder.SpecialCharIndex8bit:
83
+ this.accumulation += NumberSequenceByteEncoderDecoder.SpecialCharIndexMask;
84
+ break;
85
+ case NumberSequenceByteEncoderDecoder.SpecialCharIndex16bit:
86
+ this.byteMode = 2;
87
+ break;
88
+ case NumberSequenceByteEncoderDecoder.SpecialCharIndex24bit:
89
+ this.byteMode = 3;
90
+ break;
91
+ default:
92
+ throw new Error('Invalid SpecialCharIndex');
93
+ }
94
+ return undefined;
95
+ }
96
+ this.accumulation = (this.accumulation << 7) + idx;
97
+ --this.byteMode;
98
+ if (!this.byteMode) {
99
+ const r = this.accumulation;
100
+ this.accumulation = 0;
101
+ return r;
102
+ }
103
+ return undefined;
104
+ }
105
+ reset() {
106
+ this.byteMode = 0;
107
+ this.accumulation = 0;
108
+ }
109
+ clone() {
110
+ const n = new NumberSequenceByteDecoderAccumulator(this.byteMode, this.accumulation);
111
+ return n;
112
+ }
113
+ isPending() {
114
+ return !!(this.byteMode || this.accumulation);
115
+ }
116
+ static create() {
117
+ return new NumberSequenceByteDecoderAccumulator();
118
+ }
119
+ }
120
+ //# sourceMappingURL=NumberSequenceByteDecoderAccumulator.js.map
@@ -3,13 +3,17 @@ import type { PartialTrieInfo, TrieInfo } from '../ITrieNode/TrieInfo.js';
3
3
  import type { TrieData } from '../TrieData.js';
4
4
  export declare class TrieBlob implements TrieData {
5
5
  protected nodes: Uint32Array;
6
- protected charIndex: string[];
6
+ readonly charIndex: readonly string[];
7
7
  protected charToIndexMap: Record<string, number>;
8
8
  readonly info: Readonly<TrieInfo>;
9
9
  private _forbidIdx;
10
10
  private _size;
11
11
  private _iTrieRoot;
12
- constructor(nodes: Uint32Array, charIndex: string[], info: PartialTrieInfo);
12
+ wordToCharacters: (word: string) => string[];
13
+ constructor(nodes: Uint32Array, charIndex: readonly string[], info: PartialTrieInfo);
14
+ private _lookUpCharIndex;
15
+ private wordToNodeCharIndexSequence;
16
+ private letterToNodeCharIndexSequence;
13
17
  has(word: string): boolean;
14
18
  isForbiddenWord(word: string): boolean;
15
19
  hasForbiddenWords(): boolean;
@@ -17,13 +21,26 @@ export declare class TrieBlob implements TrieData {
17
21
  private _getRoot;
18
22
  getNode(prefix: string): ITrieNode | undefined;
19
23
  private _has;
24
+ /**
25
+ * Find the node index for the given character.
26
+ * @param nodeIdx - node index to start the search
27
+ * @param char - character to look for
28
+ * @returns
29
+ */
20
30
  private _lookupNode;
31
+ /**
32
+ * Find the node index for the given character.
33
+ * @param nodeIdx - node index to start the search
34
+ * @param char - character to look for
35
+ * @returns
36
+ */
37
+ private _lookupNodeByCharIndexSeq;
21
38
  words(): Iterable<string>;
22
39
  get size(): number;
23
40
  toJSON(): {
24
- charIndex: string[];
25
41
  options: Readonly<TrieInfo>;
26
- nodes: string[];
42
+ nodes: NodeElement[];
43
+ charIndex: readonly string[];
27
44
  };
28
45
  encodeBin(): Uint8Array;
29
46
  static decodeBin(blob: Uint8Array): TrieBlob;
@@ -31,6 +48,42 @@ export declare class TrieBlob implements TrieData {
31
48
  static NodeMaskNumChildren: number;
32
49
  static NodeMaskNumChildrenShift: number;
33
50
  static NodeChildRefShift: number;
51
+ /**
52
+ * Only 8 bits are reserved for the character index.
53
+ * The max index is {@link TrieBlob.SpecialCharIndexMask} - 1.
54
+ * Node chaining is used to reference higher character indexes.
55
+ * - @see {@link TrieBlob.SpecialCharIndexMask}
56
+ * - @see {@link TrieBlob.MaxCharIndex}
57
+ */
34
58
  static NodeMaskChildCharIndex: number;
59
+ /** SpecialCharIndexMask is used to indicate a node chain */
60
+ static SpecialCharIndexMask: number;
61
+ static MaxCharIndex: number;
62
+ /**
63
+ * SpecialCharIndex8bit is used to indicate a node chain. Where the final character index is 248 + the index found in the next node.
64
+ */
65
+ static SpecialCharIndex8bit: number;
66
+ static SpecialCharIndex16bit: number;
67
+ static SpecialCharIndex24bit: number;
68
+ /**
69
+ * Since it is only possible to store single byte indexes, a multi-byte index is stored as a sequence of indexes chained between nodes.
70
+ * @param charIndex - character index to convert to a sequence of indexes
71
+ * @returns encoded index values.
72
+ */
73
+ static toCharIndexSequence(charIndex: number): number[];
74
+ static fromCharIndexSequence(charIndexes: Iterable<number>): Iterable<number>;
75
+ static charactersToCharIndexSequence(chars: readonly string[], charToIndexMap: Readonly<Record<string, number>> | ((char: string) => number)): number[];
76
+ static charIndexSequenceToCharacters(charIndexSequence: number[], charIndex: readonly string[] | Readonly<Record<number, string>>): readonly string[];
77
+ static nodesView(trie: TrieBlob): Readonly<Uint32Array>;
35
78
  }
79
+ interface NodeElement {
80
+ id: number;
81
+ eow: boolean;
82
+ c: {
83
+ c: number | string;
84
+ o: number;
85
+ }[];
86
+ n: number;
87
+ }
88
+ export {};
36
89
  //# sourceMappingURL=TrieBlob.d.ts.map