@btc-vision/btc-runtime 1.3.12 → 1.3.13
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/package.json +1 -1
- package/runtime/contracts/DeployableOP_20.ts +1 -1
- package/runtime/index.ts +1 -0
- package/runtime/storage/StoredBooleanArray.ts +497 -0
- package/runtime/storage/StoredU128Array.ts +14 -1
- package/runtime/storage/StoredU16Array.ts +21 -3
- package/runtime/storage/StoredU256Array.ts +26 -1
package/package.json
CHANGED
|
@@ -331,7 +331,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
protected _transferFrom(from: Address, to: Address, value: u256): boolean {
|
|
334
|
-
if (
|
|
334
|
+
if (from === Blockchain.DEAD_ADDRESS) {
|
|
335
335
|
throw new Revert('Cannot transfer to or from dead address');
|
|
336
336
|
}
|
|
337
337
|
|
package/runtime/index.ts
CHANGED
|
@@ -63,6 +63,7 @@ export * from './memory/Uint8ArrayMerger';
|
|
|
63
63
|
export * from './storage/StoredU256';
|
|
64
64
|
export * from './storage/StoredU64';
|
|
65
65
|
export * from './storage/StoredU16Array';
|
|
66
|
+
export * from './storage/StoredBooleanArray';
|
|
66
67
|
export * from './storage/StoredU128Array';
|
|
67
68
|
export * from './storage/StoredU256Array';
|
|
68
69
|
export * from './storage/StoredString';
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { u256 } from 'as-bignum/assembly';
|
|
2
|
+
import { Blockchain } from '../env';
|
|
3
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
4
|
+
import { SafeMath } from '../types/SafeMath';
|
|
5
|
+
import { Revert } from '../types/Revert';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @class StoredBooleanArray
|
|
9
|
+
* @description Manages an array of boolean values across multiple storage slots. Each slot holds 256 booleans packed into a u256.
|
|
10
|
+
*/
|
|
11
|
+
@final
|
|
12
|
+
export class StoredBooleanArray {
|
|
13
|
+
private readonly baseU256Pointer: u256;
|
|
14
|
+
private readonly lengthPointer: u256;
|
|
15
|
+
|
|
16
|
+
// Internal cache for storage slots
|
|
17
|
+
private _values: Map<u64, u256> = new Map(); // Map from slotIndex to u256
|
|
18
|
+
private _isLoaded: Set<u64> = new Set(); // Set of slotIndexes that are loaded
|
|
19
|
+
private _isChanged: Set<u64> = new Set(); // Set of slotIndexes that are modified
|
|
20
|
+
|
|
21
|
+
// Internal variables for length and startIndex management
|
|
22
|
+
private _length: u64 = 0; // Current length of the array
|
|
23
|
+
private _startIndex: u64 = 0; // Starting index of the array
|
|
24
|
+
private _isChangedLength: bool = false; // Indicates if the length has been modified
|
|
25
|
+
private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
|
|
26
|
+
|
|
27
|
+
// Define a maximum allowed length to prevent excessive storage usage
|
|
28
|
+
private readonly MAX_LENGTH: u64 = u32.MAX_VALUE;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @constructor
|
|
32
|
+
* @param {u16} pointer - The primary pointer identifier.
|
|
33
|
+
* @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
|
|
34
|
+
* @param {u256} defaultValue - The default u256 value if storage is uninitialized.
|
|
35
|
+
*/
|
|
36
|
+
constructor(
|
|
37
|
+
public pointer: u16,
|
|
38
|
+
public subPointer: Uint8Array,
|
|
39
|
+
private defaultValue: u256,
|
|
40
|
+
) {
|
|
41
|
+
// Initialize the base u256 pointer using the primary pointer and subPointer
|
|
42
|
+
const writer = new BytesWriter(32);
|
|
43
|
+
writer.writeU16(pointer);
|
|
44
|
+
writer.writeBytes(subPointer);
|
|
45
|
+
|
|
46
|
+
// Initialize the base and length pointers
|
|
47
|
+
const baseU256Pointer = u256.fromBytes(writer.getBuffer(), true);
|
|
48
|
+
const lengthPointer = baseU256Pointer.clone();
|
|
49
|
+
|
|
50
|
+
// Load the current length and startIndex from storage
|
|
51
|
+
const storedLengthAndStartIndex: u256 = Blockchain.getStorageAt(lengthPointer, u256.Zero);
|
|
52
|
+
this.lengthPointer = lengthPointer;
|
|
53
|
+
this.baseU256Pointer = baseU256Pointer;
|
|
54
|
+
|
|
55
|
+
this._length = storedLengthAndStartIndex.lo1; // Bytes 0-7: length
|
|
56
|
+
this._startIndex = storedLengthAndStartIndex.lo2; // Bytes 8-15: startIndex
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @method get
|
|
61
|
+
* @description Retrieves the boolean value at the specified global index.
|
|
62
|
+
* @param {u64} index - The global index (0 to ∞) of the boolean value to retrieve.
|
|
63
|
+
* @returns {bool} - The boolean value at the specified index.
|
|
64
|
+
*/
|
|
65
|
+
@inline
|
|
66
|
+
public get(index: u64): bool {
|
|
67
|
+
assert(index < this._length, 'Index out of bounds');
|
|
68
|
+
|
|
69
|
+
const slotIndex: u64 = index / 256; // Each slot holds 256 bits
|
|
70
|
+
const bitIndex: u16 = <u16>(index % 256); // 0 to 255
|
|
71
|
+
this.ensureValues(slotIndex);
|
|
72
|
+
const slotValue = this._values.get(slotIndex);
|
|
73
|
+
if (slotValue) {
|
|
74
|
+
return this.getBit(slotValue, bitIndex);
|
|
75
|
+
} else {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @method set
|
|
82
|
+
* @description Sets the boolean value at the specified global index.
|
|
83
|
+
* @param {u64} index - The global index (0 to ∞) of the boolean value to set.
|
|
84
|
+
* @param {bool} value - The boolean value to assign.
|
|
85
|
+
*/
|
|
86
|
+
@inline
|
|
87
|
+
public set(index: u64, value: bool): void {
|
|
88
|
+
assert(index < this._length, 'Index exceeds current array length');
|
|
89
|
+
const slotIndex: u64 = index / 256;
|
|
90
|
+
const bitIndex: u16 = <u16>(index % 256);
|
|
91
|
+
this.ensureValues(slotIndex);
|
|
92
|
+
|
|
93
|
+
const slotValue = this._values.get(slotIndex);
|
|
94
|
+
if (slotValue) {
|
|
95
|
+
const oldValue = this.getBit(slotValue, bitIndex);
|
|
96
|
+
if (oldValue != value) {
|
|
97
|
+
this.setBit(slotValue, bitIndex, value);
|
|
98
|
+
this._isChanged.add(slotIndex);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @method push
|
|
105
|
+
* @description Appends a new boolean value to the end of the array.
|
|
106
|
+
* @param {bool} value - The boolean value to append.
|
|
107
|
+
*/
|
|
108
|
+
public push(value: bool): void {
|
|
109
|
+
if (this._length >= this.MAX_LENGTH) {
|
|
110
|
+
throw new Revert(
|
|
111
|
+
'Push operation failed: Array has reached its maximum allowed length.',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const newIndex: u64 = this._length;
|
|
116
|
+
const effectiveIndex: u64 = this._startIndex + newIndex;
|
|
117
|
+
const wrappedIndex: u64 =
|
|
118
|
+
effectiveIndex < this.MAX_LENGTH ? effectiveIndex : effectiveIndex % this.MAX_LENGTH;
|
|
119
|
+
const slotIndex: u64 = wrappedIndex / 256;
|
|
120
|
+
const bitIndex: u8 = <u8>(wrappedIndex % 256);
|
|
121
|
+
|
|
122
|
+
// Ensure the slot is loaded
|
|
123
|
+
this.ensureValues(slotIndex);
|
|
124
|
+
|
|
125
|
+
// Set the new value
|
|
126
|
+
const slotValue = this._values.get(slotIndex);
|
|
127
|
+
if (slotValue) {
|
|
128
|
+
this.setBit(slotValue, bitIndex, value);
|
|
129
|
+
this._isChanged.add(slotIndex);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Increment the length
|
|
133
|
+
this._length += 1;
|
|
134
|
+
this._isChangedLength = true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @method delete
|
|
139
|
+
* @description Deletes the boolean value at the specified index by setting it to false. Does not reorder the array.
|
|
140
|
+
* @param {u64} index - The global index of the boolean value to delete.
|
|
141
|
+
*/
|
|
142
|
+
public delete(index: u64): void {
|
|
143
|
+
if (index >= this._length) {
|
|
144
|
+
throw new Revert('Delete operation failed: Index out of bounds.');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const slotIndex: u64 = index / 256;
|
|
148
|
+
const bitIndex: u16 = <u16>(index % 256);
|
|
149
|
+
this.ensureValues(slotIndex);
|
|
150
|
+
|
|
151
|
+
const slotValue = this._values.get(slotIndex);
|
|
152
|
+
if (slotValue) {
|
|
153
|
+
const oldValue = this.getBit(slotValue, bitIndex);
|
|
154
|
+
if (oldValue != false) {
|
|
155
|
+
this.setBit(slotValue, bitIndex, false);
|
|
156
|
+
this._isChanged.add(slotIndex);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @method shift
|
|
163
|
+
* @description Removes the first element of the array by setting it to false, decrementing the length, and incrementing the startIndex.
|
|
164
|
+
* If the startIndex reaches the maximum value of u64, it wraps around to 0.
|
|
165
|
+
*/
|
|
166
|
+
public shift(): void {
|
|
167
|
+
if (this._length === 0) {
|
|
168
|
+
throw new Revert('Shift operation failed: Array is empty.');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const currentStartIndex: u64 = this._startIndex;
|
|
172
|
+
const slotIndex: u64 = currentStartIndex / 256;
|
|
173
|
+
const bitIndex: u16 = <u16>(currentStartIndex % 256);
|
|
174
|
+
this.ensureValues(slotIndex);
|
|
175
|
+
|
|
176
|
+
const slotValue = this._values.get(slotIndex);
|
|
177
|
+
if (slotValue) {
|
|
178
|
+
const oldValue = this.getBit(slotValue, bitIndex);
|
|
179
|
+
if (oldValue != false) {
|
|
180
|
+
this.setBit(slotValue, bitIndex, false);
|
|
181
|
+
this._isChanged.add(slotIndex);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Decrement the length
|
|
186
|
+
this._length -= 1;
|
|
187
|
+
this._isChangedLength = true;
|
|
188
|
+
|
|
189
|
+
// Increment the startIndex with wrap-around
|
|
190
|
+
if (this._startIndex < this.MAX_LENGTH - 1) {
|
|
191
|
+
this._startIndex += 1;
|
|
192
|
+
} else {
|
|
193
|
+
this._startIndex = 0;
|
|
194
|
+
}
|
|
195
|
+
this._isChangedStartIndex = true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @method save
|
|
200
|
+
* @description Persists all cached boolean values, the length, and the startIndex to their respective storage slots if any have been modified.
|
|
201
|
+
*/
|
|
202
|
+
public save(): void {
|
|
203
|
+
// Save all changed slots
|
|
204
|
+
const values = this._isChanged.values();
|
|
205
|
+
for (let i = 0; i < values.length; i++) {
|
|
206
|
+
const slotIndex = values[i];
|
|
207
|
+
const slotValue = this._values.get(slotIndex);
|
|
208
|
+
if (slotValue) {
|
|
209
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
210
|
+
Blockchain.setStorageAt(storagePointer, slotValue);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
this._isChanged.clear();
|
|
214
|
+
|
|
215
|
+
// Save length and startIndex if changed
|
|
216
|
+
if (this._isChangedLength || this._isChangedStartIndex) {
|
|
217
|
+
const packedLengthAndStartIndex = new u256();
|
|
218
|
+
packedLengthAndStartIndex.lo1 = this._length;
|
|
219
|
+
packedLengthAndStartIndex.lo2 = this._startIndex;
|
|
220
|
+
Blockchain.setStorageAt(this.lengthPointer, packedLengthAndStartIndex);
|
|
221
|
+
this._isChangedLength = false;
|
|
222
|
+
this._isChangedStartIndex = false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @method deleteAll
|
|
228
|
+
* @description Deletes all storage slots by setting them to zero, including the length and startIndex slots.
|
|
229
|
+
*/
|
|
230
|
+
public deleteAll(): void {
|
|
231
|
+
// Iterate over all loaded slots and clear them
|
|
232
|
+
const keys = this._values.keys();
|
|
233
|
+
for (let i = 0; i < keys.length; i++) {
|
|
234
|
+
const slotIndex = keys[i];
|
|
235
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
236
|
+
Blockchain.setStorageAt(storagePointer, u256.Zero);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Reset the length and startIndex to zero
|
|
240
|
+
const zeroLengthAndStartIndex = u256.Zero;
|
|
241
|
+
Blockchain.setStorageAt(this.lengthPointer, zeroLengthAndStartIndex);
|
|
242
|
+
this._length = 0;
|
|
243
|
+
this._startIndex = 0;
|
|
244
|
+
this._isChangedLength = false;
|
|
245
|
+
this._isChangedStartIndex = false;
|
|
246
|
+
|
|
247
|
+
// Clear internal caches
|
|
248
|
+
this._values.clear();
|
|
249
|
+
this._isLoaded.clear();
|
|
250
|
+
this._isChanged.clear();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @method setMultiple
|
|
255
|
+
* @description Sets multiple boolean values starting from a specific global index.
|
|
256
|
+
* @param {u64} startIndex - The starting global index.
|
|
257
|
+
* @param {bool[]} values - An array of boolean values to set.
|
|
258
|
+
*/
|
|
259
|
+
@inline
|
|
260
|
+
public setMultiple(startIndex: u64, values: bool[]): void {
|
|
261
|
+
for (let i: u64 = 0; i < values.length; i++) {
|
|
262
|
+
this.set(startIndex + i, values[i]);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @method getAll
|
|
268
|
+
* @description Retrieves a range of boolean values starting from a specific global index.
|
|
269
|
+
* @param {u64} startIndex - The starting global index.
|
|
270
|
+
* @param {u64} count - The number of boolean values to retrieve.
|
|
271
|
+
* @returns {bool[]} - An array containing the retrieved boolean values.
|
|
272
|
+
*/
|
|
273
|
+
@inline
|
|
274
|
+
public getAll(startIndex: u64, count: u64): bool[] {
|
|
275
|
+
assert(startIndex + count <= this._length, 'Requested range exceeds array length');
|
|
276
|
+
|
|
277
|
+
if (u32.MAX_VALUE < count) {
|
|
278
|
+
throw new Revert('Requested range exceeds maximum allowed value.');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const result: bool[] = new Array<bool>(count as u32);
|
|
282
|
+
for (let i: u64 = 0; i < count; i++) {
|
|
283
|
+
result[i as u32] = this.get(startIndex + i);
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @method toString
|
|
290
|
+
* @description Returns a string representation of all cached boolean values.
|
|
291
|
+
* @returns {string} - A string in the format "[true, false, ..., true]".
|
|
292
|
+
*/
|
|
293
|
+
@inline
|
|
294
|
+
public toString(): string {
|
|
295
|
+
let str = '[';
|
|
296
|
+
for (let i: u64 = 0; i < this._length; i++) {
|
|
297
|
+
const value = this.get(i);
|
|
298
|
+
str += value.toString();
|
|
299
|
+
if (i !== this._length - 1) {
|
|
300
|
+
str += ', ';
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
str += ']';
|
|
304
|
+
return str;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @method toBytes
|
|
309
|
+
* @description Returns the packed u256 values as a byte array.
|
|
310
|
+
* @returns {u8[]} - The packed u256 values in byte form.
|
|
311
|
+
*/
|
|
312
|
+
@inline
|
|
313
|
+
public toBytes(): u8[] {
|
|
314
|
+
const bytes: u8[] = new Array<u8>();
|
|
315
|
+
const slotCount: u64 = (this._length + 255) / 256;
|
|
316
|
+
|
|
317
|
+
for (let slotIndex: u64 = 0; slotIndex < slotCount; slotIndex++) {
|
|
318
|
+
this.ensureValues(slotIndex);
|
|
319
|
+
const slotValue = this._values.get(slotIndex);
|
|
320
|
+
if (slotValue) {
|
|
321
|
+
const slotBytes = slotValue.toBytes();
|
|
322
|
+
for (let i: u32 = 0; i < slotBytes.length; i++) {
|
|
323
|
+
bytes.push(slotBytes[i]);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return bytes;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @method reset
|
|
332
|
+
* @description Resets all cached boolean values to false and marks them as changed, including resetting the length and startIndex.
|
|
333
|
+
*/
|
|
334
|
+
@inline
|
|
335
|
+
public reset(): void {
|
|
336
|
+
// Reset the length and startIndex to zero
|
|
337
|
+
this._length = 0;
|
|
338
|
+
this._startIndex = 0;
|
|
339
|
+
this._isChangedLength = true;
|
|
340
|
+
this._isChangedStartIndex = true;
|
|
341
|
+
|
|
342
|
+
// Clear internal caches
|
|
343
|
+
this._values.clear();
|
|
344
|
+
this._isLoaded.clear();
|
|
345
|
+
this._isChanged.clear();
|
|
346
|
+
|
|
347
|
+
this.save();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @method getLength
|
|
352
|
+
* @description Retrieves the current length of the array.
|
|
353
|
+
* @returns {u64} - The current length.
|
|
354
|
+
*/
|
|
355
|
+
@inline
|
|
356
|
+
public getLength(): u64 {
|
|
357
|
+
return this._length;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @method startingIndex
|
|
362
|
+
* @description Retrieves the current starting index of the array.
|
|
363
|
+
* @returns {u64} - The starting index.
|
|
364
|
+
*/
|
|
365
|
+
public startingIndex(): u64 {
|
|
366
|
+
return this._startIndex;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @method setLength
|
|
371
|
+
* @description Sets the length of the array.
|
|
372
|
+
* @param {u64} newLength - The new length to set.
|
|
373
|
+
*/
|
|
374
|
+
public setLength(newLength: u64): void {
|
|
375
|
+
if (newLength > this.MAX_LENGTH) {
|
|
376
|
+
throw new Revert('SetLength operation failed: Length exceeds maximum allowed value.');
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (newLength < this._length) {
|
|
380
|
+
// Truncate the array if newLength is smaller
|
|
381
|
+
for (let i: u64 = newLength; i < this._length; i++) {
|
|
382
|
+
this.delete(i);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
this._length = newLength;
|
|
387
|
+
this._isChangedLength = true;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @method deleteLast
|
|
392
|
+
* @description Deletes the last element of the array by setting it to false and decrementing the length.
|
|
393
|
+
*/
|
|
394
|
+
public deleteLast(): void {
|
|
395
|
+
if (this._length === 0) {
|
|
396
|
+
throw new Revert('DeleteLast operation failed: Array is empty.');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const index = this._length - 1;
|
|
400
|
+
this.delete(index);
|
|
401
|
+
|
|
402
|
+
// Decrement the length
|
|
403
|
+
this._length -= 1;
|
|
404
|
+
this._isChangedLength = true;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* @private
|
|
409
|
+
* @method ensureValues
|
|
410
|
+
* @description Loads and caches the u256 value from the specified storage slot.
|
|
411
|
+
* @param {u64} slotIndex - The index of the storage slot.
|
|
412
|
+
*/
|
|
413
|
+
private ensureValues(slotIndex: u64): void {
|
|
414
|
+
if (!this._isLoaded.has(slotIndex)) {
|
|
415
|
+
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
416
|
+
const storedU256: u256 = Blockchain.getStorageAt(storagePointer, this.defaultValue);
|
|
417
|
+
this._values.set(slotIndex, storedU256);
|
|
418
|
+
this._isLoaded.add(slotIndex);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @private
|
|
424
|
+
* @method calculateStoragePointer
|
|
425
|
+
* @description Calculates the storage pointer for a given slot index by incrementing the base pointer.
|
|
426
|
+
* @param {u64} slotIndex - The index of the storage slot.
|
|
427
|
+
* @returns {u256} - The calculated storage pointer.
|
|
428
|
+
*/
|
|
429
|
+
private calculateStoragePointer(slotIndex: u64): u256 {
|
|
430
|
+
// Each slot is identified by baseU256Pointer + slotIndex + 1
|
|
431
|
+
return SafeMath.add(this.baseU256Pointer, u256.fromU64(slotIndex + 1));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* @private
|
|
436
|
+
* @method getBit
|
|
437
|
+
* @description Retrieves the bit value at the specified bit index from the u256 value.
|
|
438
|
+
* @param {u256} value - The u256 value containing the bits.
|
|
439
|
+
* @param {u16} bitIndex - The index of the bit to retrieve (0-255).
|
|
440
|
+
* @returns {bool} - The value of the bit at the specified index.
|
|
441
|
+
*/
|
|
442
|
+
private getBit(value: u256, bitIndex: u16): bool {
|
|
443
|
+
assert(bitIndex < 256, 'Bit index out of range');
|
|
444
|
+
|
|
445
|
+
if (bitIndex < 64) {
|
|
446
|
+
return ((value.lo1 >> bitIndex) & 0b1) == 1;
|
|
447
|
+
} else if (bitIndex < 128) {
|
|
448
|
+
return ((value.lo2 >> (bitIndex - 64)) & 0b1) == 1;
|
|
449
|
+
} else if (bitIndex < 192) {
|
|
450
|
+
return ((value.hi1 >> (bitIndex - 128)) & 0b1) == 1;
|
|
451
|
+
} else {
|
|
452
|
+
return ((value.hi2 >> (bitIndex - 192)) & 0b1) == 1;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @private
|
|
458
|
+
* @method setBit
|
|
459
|
+
* @description Sets the bit value at the specified bit index in the u256 value.
|
|
460
|
+
* @param {u256} value - The u256 value containing the bits.
|
|
461
|
+
* @param {u16} bitIndex - The index of the bit to set (0-255).
|
|
462
|
+
* @param {bool} bitValue - The value to set (true or false).
|
|
463
|
+
*/
|
|
464
|
+
private setBit(value: u256, bitIndex: u16, bitValue: bool): void {
|
|
465
|
+
assert(bitIndex < 256, 'Bit index out of range');
|
|
466
|
+
|
|
467
|
+
if (bitIndex < 64) {
|
|
468
|
+
const mask = u64(1) << bitIndex;
|
|
469
|
+
if (bitValue) {
|
|
470
|
+
value.lo1 |= mask;
|
|
471
|
+
} else {
|
|
472
|
+
value.lo1 &= ~mask;
|
|
473
|
+
}
|
|
474
|
+
} else if (bitIndex < 128) {
|
|
475
|
+
const mask = u64(1) << (bitIndex - 64);
|
|
476
|
+
if (bitValue) {
|
|
477
|
+
value.lo2 |= mask;
|
|
478
|
+
} else {
|
|
479
|
+
value.lo2 &= ~mask;
|
|
480
|
+
}
|
|
481
|
+
} else if (bitIndex < 192) {
|
|
482
|
+
const mask = u64(1) << (bitIndex - 128);
|
|
483
|
+
if (bitValue) {
|
|
484
|
+
value.hi1 |= mask;
|
|
485
|
+
} else {
|
|
486
|
+
value.hi1 &= ~mask;
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
const mask = u64(1) << (bitIndex - 192);
|
|
490
|
+
if (bitValue) {
|
|
491
|
+
value.hi2 |= mask;
|
|
492
|
+
} else {
|
|
493
|
+
value.hi2 &= ~mask;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
@@ -25,7 +25,7 @@ export class StoredU128Array {
|
|
|
25
25
|
private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
|
|
26
26
|
|
|
27
27
|
// Define a maximum allowed length to prevent excessive storage usage
|
|
28
|
-
private readonly MAX_LENGTH: u64 = u64.MAX_VALUE - 1;
|
|
28
|
+
private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1); // we need to check what happen in overflow situation to be able to set it to u64.MAX_VALUE
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* @constructor
|
|
@@ -246,6 +246,19 @@ export class StoredU128Array {
|
|
|
246
246
|
this._isChanged.clear();
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
public deleteLast(): void {
|
|
250
|
+
if (this._length === 0) {
|
|
251
|
+
throw new Revert('DeleteLast operation failed: Array is empty.');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const lastIndex = this._length - 1;
|
|
255
|
+
this.delete(lastIndex);
|
|
256
|
+
|
|
257
|
+
// Decrement the length
|
|
258
|
+
this._length -= 1;
|
|
259
|
+
this._isChangedLength = true;
|
|
260
|
+
}
|
|
261
|
+
|
|
249
262
|
/**
|
|
250
263
|
* @method setMultiple
|
|
251
264
|
* @description Sets multiple u128 values starting from a specific global index.
|
|
@@ -25,7 +25,7 @@ export class StoredU16Array {
|
|
|
25
25
|
private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
|
|
26
26
|
|
|
27
27
|
// Define a maximum allowed length to prevent excessive storage usage
|
|
28
|
-
private readonly MAX_LENGTH: u64 = u64.MAX_VALUE - 1;
|
|
28
|
+
private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1); // we need to check what happen in overflow situation to be able to set it to u64.MAX_VALUE
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* @constructor
|
|
@@ -258,9 +258,14 @@ export class StoredU16Array {
|
|
|
258
258
|
@inline
|
|
259
259
|
public getAll(startIndex: u64, count: u64): u16[] {
|
|
260
260
|
assert(startIndex + count <= this._length, 'Requested range exceeds array length');
|
|
261
|
-
|
|
261
|
+
|
|
262
|
+
if (u32.MAX_VALUE < count) {
|
|
263
|
+
throw new Revert('Requested range exceeds maximum allowed value.');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const result: u16[] = new Array<u16>(count as u32);
|
|
262
267
|
for (let i: u64 = 0; i < count; i++) {
|
|
263
|
-
result[i] = this.get(startIndex + i);
|
|
268
|
+
result[i as u32] = this.get(startIndex + i);
|
|
264
269
|
}
|
|
265
270
|
return result;
|
|
266
271
|
}
|
|
@@ -360,6 +365,19 @@ export class StoredU16Array {
|
|
|
360
365
|
this._isChangedLength = true;
|
|
361
366
|
}
|
|
362
367
|
|
|
368
|
+
public deleteLast(): void {
|
|
369
|
+
if (this._length === 0) {
|
|
370
|
+
throw new Revert('DeleteLast operation failed: Array is empty.');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const index = this._length - 1;
|
|
374
|
+
this.delete(index);
|
|
375
|
+
|
|
376
|
+
// Decrement the length
|
|
377
|
+
this._length -= 1;
|
|
378
|
+
this._isChangedLength = true;
|
|
379
|
+
}
|
|
380
|
+
|
|
363
381
|
/**
|
|
364
382
|
* @private
|
|
365
383
|
* @method ensureValues
|
|
@@ -25,7 +25,7 @@ export class StoredU256Array {
|
|
|
25
25
|
private _isChangedStartIndex: bool = false; // Indicates if the startIndex has been modified
|
|
26
26
|
|
|
27
27
|
// Define a maximum allowed length to prevent excessive storage usage
|
|
28
|
-
private readonly MAX_LENGTH: u64 = u64.MAX_VALUE - 1;
|
|
28
|
+
private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1); // we need to check what happen in overflow situation to be able to set it to u64.MAX_VALUE
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* @constructor
|
|
@@ -120,6 +120,31 @@ export class StoredU256Array {
|
|
|
120
120
|
this._isChangedLength = true;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
public deleteLast(): void {
|
|
124
|
+
if (this._length === 0) {
|
|
125
|
+
throw new Revert('Delete operation failed: Array is empty.');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const lastIndex: u64 = this._length - 1;
|
|
129
|
+
const slotIndex: u32 = <u32>(this._startIndex + lastIndex);
|
|
130
|
+
this.ensureValues(slotIndex);
|
|
131
|
+
|
|
132
|
+
const currentValue = this._values.get(slotIndex);
|
|
133
|
+
if (!u256.eq(currentValue, u256.Zero)) {
|
|
134
|
+
this._values.set(slotIndex, u256.Zero);
|
|
135
|
+
this._isChanged.add(slotIndex);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Decrement the length
|
|
139
|
+
this._length -= 1;
|
|
140
|
+
this._isChangedLength = true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public setStartingIndex(index: u64): void {
|
|
144
|
+
this._startIndex = index;
|
|
145
|
+
this._isChangedStartIndex = true;
|
|
146
|
+
}
|
|
147
|
+
|
|
123
148
|
/**
|
|
124
149
|
* @method delete
|
|
125
150
|
* @description Deletes the u256 value at the specified index by setting it to zero. Does not reorder the array.
|