@btc-vision/transaction 1.7.3 → 1.7.5
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/browser/index.js +1 -1
- package/browser/src/deterministic/AddressMap.d.ts +1 -2
- package/browser/src/deterministic/CustomMap.d.ts +29 -0
- package/browser/src/deterministic/DeterministicSet.d.ts +4 -2
- package/browser/src/deterministic/Map.d.ts +4 -4
- package/browser/src/opnet.d.ts +1 -0
- package/build/deterministic/AddressMap.d.ts +1 -2
- package/build/deterministic/AddressMap.js +16 -29
- package/build/deterministic/CustomMap.d.ts +29 -0
- package/build/deterministic/CustomMap.js +253 -0
- package/build/deterministic/DeterministicMap.js +26 -4
- package/build/deterministic/DeterministicSet.d.ts +4 -2
- package/build/deterministic/DeterministicSet.js +38 -20
- package/build/deterministic/Map.d.ts +4 -4
- package/build/deterministic/Map.js +30 -22
- package/build/opnet.d.ts +1 -0
- package/build/opnet.js +1 -0
- package/package.json +1 -1
- package/src/deterministic/AddressMap.ts +20 -31
- package/src/deterministic/CustomMap.ts +316 -0
- package/src/deterministic/DeterministicMap.ts +30 -4
- package/src/deterministic/DeterministicSet.ts +43 -20
- package/src/deterministic/Map.ts +37 -28
- package/src/opnet.ts +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export class Map {
|
|
2
2
|
constructor() {
|
|
3
3
|
this._keys = [];
|
|
4
|
-
this._values =
|
|
4
|
+
this._values = {};
|
|
5
5
|
}
|
|
6
6
|
get size() {
|
|
7
7
|
return this._keys.length;
|
|
@@ -10,57 +10,65 @@ export class Map {
|
|
|
10
10
|
yield* this._keys;
|
|
11
11
|
}
|
|
12
12
|
*values() {
|
|
13
|
-
|
|
13
|
+
for (const key of this._keys) {
|
|
14
|
+
yield this._values[this.keyToString(key)];
|
|
15
|
+
}
|
|
14
16
|
}
|
|
15
17
|
*entries() {
|
|
16
|
-
for (
|
|
17
|
-
yield [
|
|
18
|
+
for (const key of this._keys) {
|
|
19
|
+
yield [key, this._values[this.keyToString(key)]];
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
set(key, value) {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
+
const keyStr = this.keyToString(key);
|
|
24
|
+
if (!this.has(key)) {
|
|
23
25
|
this._keys.push(key);
|
|
24
|
-
this._values.push(value);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
this._values[index] = value;
|
|
28
26
|
}
|
|
27
|
+
this._values[keyStr] = value;
|
|
29
28
|
}
|
|
30
29
|
indexOf(key) {
|
|
31
30
|
for (let i = 0; i < this._keys.length; i++) {
|
|
32
|
-
if (this._keys[i]
|
|
31
|
+
if (this._keys[i] === key) {
|
|
33
32
|
return i;
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
return -1;
|
|
37
36
|
}
|
|
38
37
|
get(key) {
|
|
39
|
-
|
|
40
|
-
if (index == -1) {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
return this._values[index];
|
|
38
|
+
return this._values[this.keyToString(key)];
|
|
44
39
|
}
|
|
45
40
|
has(key) {
|
|
46
|
-
return this.
|
|
41
|
+
return Object.prototype.hasOwnProperty.call(this._values, this.keyToString(key));
|
|
47
42
|
}
|
|
48
43
|
delete(key) {
|
|
49
44
|
const index = this.indexOf(key);
|
|
50
|
-
if (index
|
|
45
|
+
if (index === -1) {
|
|
51
46
|
return false;
|
|
52
47
|
}
|
|
48
|
+
const keyStr = this.keyToString(key);
|
|
53
49
|
this._keys.splice(index, 1);
|
|
54
|
-
this._values
|
|
50
|
+
delete this._values[keyStr];
|
|
55
51
|
return true;
|
|
56
52
|
}
|
|
57
53
|
clear() {
|
|
58
54
|
this._keys = [];
|
|
59
|
-
this._values =
|
|
55
|
+
this._values = {};
|
|
60
56
|
}
|
|
61
57
|
*[Symbol.iterator]() {
|
|
62
|
-
for (
|
|
63
|
-
yield [
|
|
58
|
+
for (const key of this._keys) {
|
|
59
|
+
yield [key, this._values[this.keyToString(key)]];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
keyToString(key) {
|
|
63
|
+
if (typeof key === 'string') {
|
|
64
|
+
return key;
|
|
65
|
+
}
|
|
66
|
+
if (typeof key === 'number' || typeof key === 'boolean') {
|
|
67
|
+
return String(key);
|
|
68
|
+
}
|
|
69
|
+
if (typeof key === 'object' && key !== null) {
|
|
70
|
+
return JSON.stringify(key);
|
|
64
71
|
}
|
|
72
|
+
return String(key);
|
|
65
73
|
}
|
|
66
74
|
}
|
package/build/opnet.d.ts
CHANGED
|
@@ -76,6 +76,7 @@ export * from './transaction/browser/Web3Provider.js';
|
|
|
76
76
|
export * from './keypair/Secp256k1PointDeriver.js';
|
|
77
77
|
export * from './transaction/ContractAddress.js';
|
|
78
78
|
export * from './deterministic/Map.js';
|
|
79
|
+
export * from './deterministic/CustomMap.js';
|
|
79
80
|
declare global {
|
|
80
81
|
interface Window {
|
|
81
82
|
unisat?: Unisat;
|
package/build/opnet.js
CHANGED
package/package.json
CHANGED
|
@@ -3,11 +3,9 @@ import { Map } from './Map.js';
|
|
|
3
3
|
|
|
4
4
|
export class AddressMap<V> {
|
|
5
5
|
private items: Map<bigint, V>;
|
|
6
|
-
private keyOrder: Address[];
|
|
7
6
|
|
|
8
7
|
constructor(iterable?: ReadonlyArray<readonly [Address, V]> | null) {
|
|
9
8
|
this.items = new Map();
|
|
10
|
-
this.keyOrder = [];
|
|
11
9
|
|
|
12
10
|
if (iterable) {
|
|
13
11
|
for (const [key, value] of iterable) {
|
|
@@ -17,15 +15,14 @@ export class AddressMap<V> {
|
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
public get size(): number {
|
|
20
|
-
return this.
|
|
18
|
+
return this.items.size;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
public set(key: Address, value: V):
|
|
21
|
+
public set(key: Address, value: V): this {
|
|
24
22
|
const keyBigInt = key.toBigInt();
|
|
25
|
-
if (!this.items.has(keyBigInt)) {
|
|
26
|
-
this.keyOrder.push(key);
|
|
27
|
-
}
|
|
28
23
|
this.items.set(keyBigInt, value);
|
|
24
|
+
|
|
25
|
+
return this;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
public get(key: Address): V | undefined {
|
|
@@ -38,41 +35,35 @@ export class AddressMap<V> {
|
|
|
38
35
|
|
|
39
36
|
public delete(key: Address): boolean {
|
|
40
37
|
const keyBigInt = key.toBigInt();
|
|
41
|
-
|
|
42
|
-
this.keyOrder = this.keyOrder.filter((k) => k.toBigInt() !== keyBigInt);
|
|
43
|
-
return true;
|
|
44
|
-
}
|
|
45
|
-
return false;
|
|
38
|
+
return this.items.delete(keyBigInt);
|
|
46
39
|
}
|
|
47
40
|
|
|
48
41
|
public clear(): void {
|
|
49
42
|
this.items.clear();
|
|
50
|
-
this.keyOrder = [];
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
public indexOf(address: Address): number {
|
|
54
|
-
|
|
55
|
-
for (let i: number = 0; i < this.keyOrder.length; i++) {
|
|
56
|
-
if (this.keyOrder[i].toBigInt() === addressBigInt) {
|
|
57
|
-
return i;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return -1;
|
|
46
|
+
return this.items.indexOf(address.toBigInt());
|
|
61
47
|
}
|
|
62
48
|
|
|
49
|
+
/**
|
|
50
|
+
* WARNING, THIS RETURN NEW COPY OF THE KEYS
|
|
51
|
+
*/
|
|
63
52
|
*entries(): IterableIterator<[Address, V]> {
|
|
64
|
-
for (const
|
|
65
|
-
yield [
|
|
53
|
+
for (const [keyBigInt, value] of this.items.entries()) {
|
|
54
|
+
yield [Address.fromBigInt(keyBigInt), value];
|
|
66
55
|
}
|
|
67
56
|
}
|
|
68
57
|
|
|
69
58
|
*keys(): IterableIterator<Address> {
|
|
70
|
-
|
|
59
|
+
for (const keyBigInt of this.items.keys()) {
|
|
60
|
+
yield Address.fromBigInt(keyBigInt);
|
|
61
|
+
}
|
|
71
62
|
}
|
|
72
63
|
|
|
73
64
|
*values(): IterableIterator<V> {
|
|
74
|
-
for (const
|
|
75
|
-
yield
|
|
65
|
+
for (const value of this.items.values()) {
|
|
66
|
+
yield value;
|
|
76
67
|
}
|
|
77
68
|
}
|
|
78
69
|
|
|
@@ -80,15 +71,13 @@ export class AddressMap<V> {
|
|
|
80
71
|
callback: (value: V, key: Address, map: AddressMap<V>) => void,
|
|
81
72
|
thisArg?: unknown,
|
|
82
73
|
): void {
|
|
83
|
-
for (const
|
|
84
|
-
|
|
74
|
+
for (const [keyBigInt, value] of this.items.entries()) {
|
|
75
|
+
const key = Address.fromBigInt(keyBigInt);
|
|
76
|
+
callback.call(thisArg, value, key, this);
|
|
85
77
|
}
|
|
86
78
|
}
|
|
87
79
|
|
|
88
80
|
*[Symbol.iterator](): IterableIterator<[Address, V]> {
|
|
89
|
-
|
|
90
|
-
const key = this.keyOrder[i];
|
|
91
|
-
yield [key, this.items.get(key.toBigInt()) as V];
|
|
92
|
-
}
|
|
81
|
+
yield* this.entries();
|
|
93
82
|
}
|
|
94
83
|
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
export class CustomMap<K, V> {
|
|
2
|
+
private static readonly INITIAL_CAPACITY = 16;
|
|
3
|
+
private static readonly LOAD_FACTOR = 0.75;
|
|
4
|
+
|
|
5
|
+
#keys: (K | undefined)[];
|
|
6
|
+
#values: (V | undefined)[];
|
|
7
|
+
|
|
8
|
+
private deleted: boolean[];
|
|
9
|
+
private capacity: number;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.capacity = CustomMap.INITIAL_CAPACITY;
|
|
13
|
+
this.#keys = new Array<K>(this.capacity);
|
|
14
|
+
this.#values = new Array<V>(this.capacity);
|
|
15
|
+
this.deleted = new Array<boolean>(this.capacity).fill(false);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private _size: number = 0;
|
|
19
|
+
|
|
20
|
+
public get size(): number {
|
|
21
|
+
return this._size;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public set(key: K, value: V): boolean {
|
|
25
|
+
let exist: boolean = true;
|
|
26
|
+
|
|
27
|
+
const index = this.findInsertIndex(key);
|
|
28
|
+
if (this.#keys[index] === undefined || this.deleted[index]) {
|
|
29
|
+
this._size++;
|
|
30
|
+
exist = false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.#keys[index] = key;
|
|
34
|
+
this.#values[index] = value;
|
|
35
|
+
this.deleted[index] = false;
|
|
36
|
+
|
|
37
|
+
if (this._size > this.capacity * CustomMap.LOAD_FACTOR) {
|
|
38
|
+
this.resize();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return exist;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public get(key: K): V | undefined {
|
|
45
|
+
const index = this.findIndex(key);
|
|
46
|
+
return index === -1 ? undefined : this.#values[index];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public has(key: K): boolean {
|
|
50
|
+
return this.findIndex(key) !== -1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public indexOf(key: K): number {
|
|
54
|
+
return this.findIndex(key);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public delete(key: K): boolean {
|
|
58
|
+
const index = this.findIndex(key);
|
|
59
|
+
|
|
60
|
+
if (index === -1) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.#keys[index] = undefined;
|
|
65
|
+
this.#values[index] = undefined;
|
|
66
|
+
this.deleted[index] = true;
|
|
67
|
+
this._size--;
|
|
68
|
+
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public clear(): void {
|
|
73
|
+
this.#keys = new Array<K>(this.capacity);
|
|
74
|
+
this.#values = new Array<V>(this.capacity);
|
|
75
|
+
this.deleted = new Array<boolean>(this.capacity).fill(false);
|
|
76
|
+
this._size = 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public *entries(): MapIterator<[K, V]> {
|
|
80
|
+
for (let i = 0; i < this.capacity; i++) {
|
|
81
|
+
if (this.#keys[i] !== undefined && !this.deleted[i]) {
|
|
82
|
+
yield [this.#keys[i] as K, this.#values[i] as V];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public *keys(): MapIterator<K> {
|
|
88
|
+
for (let i = 0; i < this.capacity; i++) {
|
|
89
|
+
if (this.#keys[i] !== undefined && !this.deleted[i]) {
|
|
90
|
+
yield this.#keys[i] as K;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public *values(): MapIterator<V> {
|
|
96
|
+
for (let i = 0; i < this.capacity; i++) {
|
|
97
|
+
if (this.#keys[i] !== undefined && !this.deleted[i]) {
|
|
98
|
+
yield this.#values[i] as V;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
*[Symbol.iterator](): MapIterator<[K, V]> {
|
|
104
|
+
yield* this.entries();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private hashBigInt(key: bigint): number {
|
|
108
|
+
// For small bigints that fit in 32 bits, use direct conversion
|
|
109
|
+
if (key >= -2147483648n && key <= 2147483647n) {
|
|
110
|
+
return Number(key) | 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// For larger bigints, use bit manipulation
|
|
114
|
+
// Mix high and low 32-bit parts
|
|
115
|
+
let hash = 2166136261; // FNV-1a initial value
|
|
116
|
+
|
|
117
|
+
// Process the bigint in 32-bit chunks
|
|
118
|
+
let n = key < 0n ? -key : key;
|
|
119
|
+
|
|
120
|
+
while (n > 0n) {
|
|
121
|
+
// Extract 32-bit chunk
|
|
122
|
+
const chunk = Number(n & 0xffffffffn);
|
|
123
|
+
hash ^= chunk;
|
|
124
|
+
hash = Math.imul(hash, 16777619);
|
|
125
|
+
n = n >> 32n;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Mix in the sign
|
|
129
|
+
if (key < 0n) {
|
|
130
|
+
hash ^= 0x80000000;
|
|
131
|
+
hash = Math.imul(hash, 16777619);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return Math.abs(hash);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private hash(key: K): number {
|
|
138
|
+
let hash = 0;
|
|
139
|
+
|
|
140
|
+
switch (typeof key) {
|
|
141
|
+
case 'number':
|
|
142
|
+
// Handle NaN and infinity specially
|
|
143
|
+
if (key !== key) return 0x7ff8000000000000; // NaN
|
|
144
|
+
if (!isFinite(key)) return key > 0 ? 0x7ff0000000000000 : 0xfff0000000000000;
|
|
145
|
+
// Use the number itself as hash
|
|
146
|
+
hash = key | 0; // Convert to 32-bit integer
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'string':
|
|
150
|
+
// FNV-1a hash for strings
|
|
151
|
+
hash = 2166136261;
|
|
152
|
+
for (let i = 0; i < (key as string).length; i++) {
|
|
153
|
+
hash ^= (key as string).charCodeAt(i);
|
|
154
|
+
hash = Math.imul(hash, 16777619);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
case 'boolean':
|
|
159
|
+
hash = key ? 1231 : 1237;
|
|
160
|
+
break;
|
|
161
|
+
|
|
162
|
+
case 'symbol': {
|
|
163
|
+
// Symbols need special handling - use description
|
|
164
|
+
const desc = (key as symbol).description || '';
|
|
165
|
+
hash = this.hash(desc as K); // Recursive call with string
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
case 'bigint':
|
|
170
|
+
// Convert bigint to string for hashing
|
|
171
|
+
hash = this.hashBigInt(key);
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case 'undefined':
|
|
175
|
+
hash = 0;
|
|
176
|
+
break;
|
|
177
|
+
|
|
178
|
+
case 'object':
|
|
179
|
+
if (key === null) {
|
|
180
|
+
hash = 0;
|
|
181
|
+
} else if (key instanceof Date) {
|
|
182
|
+
hash = key.getTime() | 0;
|
|
183
|
+
} else if (ArrayBuffer.isView(key) || key instanceof ArrayBuffer) {
|
|
184
|
+
// Handle Buffer, TypedArrays, ArrayBuffer
|
|
185
|
+
hash = this.hashBuffer(key);
|
|
186
|
+
} else if (Array.isArray(key)) {
|
|
187
|
+
// Hash arrays by combining element hashes
|
|
188
|
+
hash = 1;
|
|
189
|
+
for (const item of key) {
|
|
190
|
+
hash = Math.imul(hash, 31) + this.hash(item as K);
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
throw new Error('Raw object not supported.');
|
|
194
|
+
// For objects, we need reference equality
|
|
195
|
+
// So we'll use linear probing with === comparison
|
|
196
|
+
// Start with a random-ish position
|
|
197
|
+
//hash = 0x42424242;
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case 'function':
|
|
202
|
+
// Hash function by its string representation
|
|
203
|
+
hash = this.hash(key.toString() as K);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Ensure positive index
|
|
208
|
+
return Math.abs(hash) % this.capacity;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private hashBuffer(buffer: ArrayBuffer | object): number {
|
|
212
|
+
let bytes: Uint8Array;
|
|
213
|
+
|
|
214
|
+
if (buffer instanceof ArrayBuffer) {
|
|
215
|
+
bytes = new Uint8Array(buffer);
|
|
216
|
+
} else if (ArrayBuffer.isView(buffer)) {
|
|
217
|
+
bytes = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
218
|
+
} else {
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// FNV-1a hash for bytes
|
|
223
|
+
let hash = 2166136261;
|
|
224
|
+
for (let i = 0; i < Math.min(bytes.length, 100); i++) {
|
|
225
|
+
// Cap at 100 bytes for performance
|
|
226
|
+
hash ^= bytes[i];
|
|
227
|
+
hash = Math.imul(hash, 16777619);
|
|
228
|
+
}
|
|
229
|
+
return hash;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private equals(a: K, b: K): boolean {
|
|
233
|
+
// Handle special cases
|
|
234
|
+
if (a === b) return true;
|
|
235
|
+
|
|
236
|
+
// NaN === NaN should be true for map #keys
|
|
237
|
+
if (typeof a === 'number' && typeof b === 'number' && a !== a && b !== b) {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// For buffers, do deep comparison
|
|
242
|
+
if (
|
|
243
|
+
(ArrayBuffer.isView(a) || a instanceof ArrayBuffer) &&
|
|
244
|
+
(ArrayBuffer.isView(b) || b instanceof ArrayBuffer)
|
|
245
|
+
) {
|
|
246
|
+
return this.buffersEqual(a, b);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private buffersEqual(a: ArrayBuffer | object, b: ArrayBuffer | object): boolean {
|
|
253
|
+
const bytesA = this.getBytes(a);
|
|
254
|
+
const bytesB = this.getBytes(b);
|
|
255
|
+
|
|
256
|
+
if (bytesA.length !== bytesB.length) return false;
|
|
257
|
+
|
|
258
|
+
for (let i = 0; i < bytesA.length; i++) {
|
|
259
|
+
if (bytesA[i] !== bytesB[i]) return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private getBytes(buffer: ArrayBuffer | object): Uint8Array {
|
|
266
|
+
if (buffer instanceof ArrayBuffer) {
|
|
267
|
+
return new Uint8Array(buffer);
|
|
268
|
+
} else if (ArrayBuffer.isView(buffer)) {
|
|
269
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
270
|
+
}
|
|
271
|
+
return new Uint8Array(0);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private findIndex(key: K): number {
|
|
275
|
+
let index = this.hash(key);
|
|
276
|
+
|
|
277
|
+
while (this.#keys[index] !== undefined || this.deleted[index]) {
|
|
278
|
+
if (this.#keys[index] !== undefined && this.equals(this.#keys[index] as K, key)) {
|
|
279
|
+
return index;
|
|
280
|
+
}
|
|
281
|
+
index = (index + 1) % this.capacity;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return -1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private findInsertIndex(key: K): number {
|
|
288
|
+
let index = this.hash(key);
|
|
289
|
+
|
|
290
|
+
while (this.#keys[index] !== undefined && !this.deleted[index]) {
|
|
291
|
+
if (this.equals(this.#keys[index] as K, key)) {
|
|
292
|
+
return index; // Key already exists
|
|
293
|
+
}
|
|
294
|
+
index = (index + 1) % this.capacity;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return index;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private resize(): void {
|
|
301
|
+
const oldKeys = this.#keys;
|
|
302
|
+
const oldValues = this.#values;
|
|
303
|
+
|
|
304
|
+
this.capacity *= 2;
|
|
305
|
+
this.#keys = new Array<K>(this.capacity);
|
|
306
|
+
this.#values = new Array<V>(this.capacity);
|
|
307
|
+
this.deleted = new Array<boolean>(this.capacity).fill(false);
|
|
308
|
+
this._size = 0;
|
|
309
|
+
|
|
310
|
+
for (let i = 0; i < oldKeys.length; i++) {
|
|
311
|
+
if (oldKeys[i] !== undefined && !this.deleted[i]) {
|
|
312
|
+
this.set(oldKeys[i] as K, oldValues[i] as V);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -26,8 +26,18 @@ export class DeterministicMap<K, V> {
|
|
|
26
26
|
|
|
27
27
|
public set(key: K, value: V): void {
|
|
28
28
|
if (!this.map.has(key)) {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
// Binary search for insertion position
|
|
30
|
+
let left = 0,
|
|
31
|
+
right = this.#keys.length;
|
|
32
|
+
while (left < right) {
|
|
33
|
+
const mid = Math.floor((left + right) / 2);
|
|
34
|
+
if (this.compareFn(this.#keys[mid], key) < 0) {
|
|
35
|
+
left = mid + 1;
|
|
36
|
+
} else {
|
|
37
|
+
right = mid;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
this.#keys.splice(left, 0, key);
|
|
31
41
|
}
|
|
32
42
|
this.map.set(key, value);
|
|
33
43
|
}
|
|
@@ -64,8 +74,24 @@ export class DeterministicMap<K, V> {
|
|
|
64
74
|
public delete(key: K): boolean {
|
|
65
75
|
if (this.map.has(key)) {
|
|
66
76
|
this.map.delete(key);
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
|
|
78
|
+
// Binary search to find the key's index
|
|
79
|
+
let left = 0,
|
|
80
|
+
right = this.#keys.length - 1;
|
|
81
|
+
while (left <= right) {
|
|
82
|
+
const mid = Math.floor((left + right) / 2);
|
|
83
|
+
const cmp = this.compareFn(this.#keys[mid], key);
|
|
84
|
+
|
|
85
|
+
if (cmp === 0) {
|
|
86
|
+
// Found it, remove at this index
|
|
87
|
+
this.#keys.splice(mid, 1);
|
|
88
|
+
return true;
|
|
89
|
+
} else if (cmp < 0) {
|
|
90
|
+
left = mid + 1;
|
|
91
|
+
} else {
|
|
92
|
+
right = mid - 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
69
95
|
}
|
|
70
96
|
return false;
|
|
71
97
|
}
|
|
@@ -5,25 +5,39 @@ export class DeterministicSet<T> {
|
|
|
5
5
|
this.elements = [];
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
public get size(): number {
|
|
9
|
+
return this.elements.length;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public static fromSet<T>(set: Set<T>, compareFn: (a: T, b: T) => number): DeterministicSet<T> {
|
|
13
|
+
const deterministicSet = new DeterministicSet<T>(compareFn);
|
|
14
|
+
for (const value of set) {
|
|
15
|
+
deterministicSet.add(value);
|
|
16
|
+
}
|
|
17
|
+
return deterministicSet;
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
public add(value: T): void {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
const { found, index } = this.binarySearch(value);
|
|
22
|
+
|
|
23
|
+
if (!found) {
|
|
24
|
+
this.elements.splice(index, 0, value);
|
|
12
25
|
}
|
|
13
26
|
}
|
|
14
27
|
|
|
15
28
|
public delete(value: T): boolean {
|
|
16
|
-
const index = this.
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
const { found, index } = this.binarySearch(value);
|
|
30
|
+
|
|
31
|
+
if (found) {
|
|
32
|
+
this.elements.splice(index, 1);
|
|
33
|
+
return true;
|
|
19
34
|
}
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
return true;
|
|
36
|
+
return false;
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
public has(value: T): boolean {
|
|
26
|
-
return this.
|
|
40
|
+
return this.binarySearch(value).found;
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
public clear(): void {
|
|
@@ -36,21 +50,30 @@ export class DeterministicSet<T> {
|
|
|
36
50
|
}
|
|
37
51
|
}
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
for (const value of set) {
|
|
42
|
-
deterministicSet.add(value);
|
|
43
|
-
}
|
|
44
|
-
return deterministicSet;
|
|
53
|
+
*values(): IterableIterator<T> {
|
|
54
|
+
yield* this.elements;
|
|
45
55
|
}
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
*[Symbol.iterator](): IterableIterator<T> {
|
|
58
|
+
yield* this.elements;
|
|
49
59
|
}
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
private binarySearch(value: T): { found: boolean; index: number } {
|
|
62
|
+
let left = 0, right = this.elements.length;
|
|
63
|
+
|
|
64
|
+
while (left < right) {
|
|
65
|
+
const mid = Math.floor((left + right) / 2);
|
|
66
|
+
const cmp = this.compareFn(this.elements[mid], value);
|
|
67
|
+
|
|
68
|
+
if (cmp === 0) {
|
|
69
|
+
return { found: true, index: mid };
|
|
70
|
+
} else if (cmp < 0) {
|
|
71
|
+
left = mid + 1;
|
|
72
|
+
} else {
|
|
73
|
+
right = mid;
|
|
74
|
+
}
|
|
54
75
|
}
|
|
76
|
+
|
|
77
|
+
return { found: false, index: left };
|
|
55
78
|
}
|
|
56
79
|
}
|