@btc-vision/btc-runtime 1.6.0 → 1.6.2
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/env/Atomic.ts +51 -0
- package/runtime/env/BlockchainEnvironment.ts +16 -17
- package/runtime/env/global.ts +2 -0
- package/runtime/index.ts +5 -0
- package/runtime/math/bytes.ts +7 -7
- package/runtime/storage/AdvancedStoredString.ts +199 -0
- package/runtime/storage/StoredU32.ts +178 -0
- package/runtime/storage/arrays/StoredAddressArray.ts +20 -279
- package/runtime/storage/arrays/StoredBooleanArray.ts +122 -52
- package/runtime/storage/arrays/StoredPackedArray.ts +125 -49
- package/runtime/storage/arrays/StoredU128Array.ts +6 -6
- package/runtime/storage/arrays/StoredU16Array.ts +6 -6
- package/runtime/storage/arrays/StoredU256Array.ts +5 -5
- package/runtime/storage/arrays/StoredU32Array.ts +5 -5
- package/runtime/storage/arrays/StoredU64Array.ts +14 -7
- package/runtime/storage/arrays/StoredU8Array.ts +5 -5
- package/runtime/storage/maps/StoredMapU256.ts +62 -0
- package/runtime/types/SafeMath.ts +50 -0
|
@@ -1,298 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
addUint8ArraysBE,
|
|
5
|
-
bigEndianAdd,
|
|
6
|
-
encodeBasePointer,
|
|
7
|
-
readLengthAndStartIndex,
|
|
8
|
-
u64ToBE32Bytes,
|
|
9
|
-
} from '../../math/bytes';
|
|
1
|
+
import { DEFAULT_MAX_LENGTH, StoredPackedArray } from './StoredPackedArray';
|
|
2
|
+
import { bigEndianAdd } from '../../math/bytes';
|
|
10
3
|
import { Address } from '../../types/Address';
|
|
11
|
-
import { Revert } from '../../types/Revert';
|
|
12
4
|
|
|
13
5
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
6
|
+
* StoredAddressArray
|
|
7
|
+
*
|
|
8
|
+
* Array of addresses.
|
|
17
9
|
*/
|
|
18
10
|
@final
|
|
19
|
-
export class StoredAddressArray {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
private _values: Map<u64, Address> = new Map(); // slotIndex -> Address
|
|
24
|
-
private _isChanged: Set<u64> = new Set(); // track changed slotIndexes
|
|
25
|
-
|
|
26
|
-
private _length: u64 = 0;
|
|
27
|
-
private _startIndex: u64 = 0;
|
|
28
|
-
private _isChangedLength: bool = false;
|
|
29
|
-
private _isChangedStartIndex: bool = false;
|
|
30
|
-
|
|
31
|
-
private readonly MAX_LENGTH: u64 = u64(u32.MAX_VALUE - 1);
|
|
32
|
-
|
|
33
|
-
private readonly defaultValue: Address = Address.zero();
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @constructor
|
|
37
|
-
* @param {u16} pointer - The primary pointer identifier.
|
|
38
|
-
* @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing.
|
|
39
|
-
*/
|
|
40
|
-
constructor(public pointer: u16, public subPointer: Uint8Array) {
|
|
41
|
-
assert(
|
|
42
|
-
subPointer.length <= 30,
|
|
43
|
-
`You must pass a 30 bytes sub-pointer. (AddressArray, got ${subPointer.length})`,
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const basePointer = encodeBasePointer(pointer, subPointer);
|
|
47
|
-
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
48
|
-
this.baseU256Pointer = bigEndianAdd(basePointer, 1);
|
|
49
|
-
|
|
50
|
-
const storedLenStart = Blockchain.getStorageAt(basePointer);
|
|
51
|
-
const data = readLengthAndStartIndex(storedLenStart);
|
|
52
|
-
|
|
53
|
-
this._length = data[0];
|
|
54
|
-
this._startIndex = data[1];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@inline
|
|
58
|
-
public has(index: u64): bool {
|
|
59
|
-
return index < this._length;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Get an element by its global index. */
|
|
63
|
-
@inline
|
|
64
|
-
@operator('[]')
|
|
65
|
-
public get(index: u64): Address {
|
|
66
|
-
if (index >= this._length) {
|
|
67
|
-
throw new Revert('get: index out of range (address array)');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
71
|
-
const slotIndex: u32 = <u32>physicalIndex;
|
|
72
|
-
this.ensureValues(slotIndex);
|
|
73
|
-
|
|
74
|
-
return this._values.get(slotIndex);
|
|
11
|
+
export class StoredAddressArray extends StoredPackedArray<Address> {
|
|
12
|
+
public constructor(pointer: u16, subPointer: Uint8Array, maxLength: u32 = DEFAULT_MAX_LENGTH) {
|
|
13
|
+
super(pointer, subPointer, Address.zero(), maxLength);
|
|
75
14
|
}
|
|
76
15
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@operator('[]=')
|
|
80
|
-
public set(index: u64, value: Address): void {
|
|
81
|
-
if (index >= this._length) {
|
|
82
|
-
throw new Revert('set: index out of range (address array)');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
86
|
-
const slotIndex: u32 = <u32>physicalIndex;
|
|
87
|
-
this.ensureValues(slotIndex);
|
|
88
|
-
|
|
89
|
-
const currentValue = this._values.get(slotIndex);
|
|
90
|
-
if (currentValue != value) {
|
|
91
|
-
this._values.set(slotIndex, value);
|
|
92
|
-
this._isChanged.add(slotIndex);
|
|
93
|
-
}
|
|
16
|
+
protected getSlotCapacity(): u32 {
|
|
17
|
+
return 1; // 1 x u256 => 32 bytes
|
|
94
18
|
}
|
|
95
19
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
public push(value: Address): void {
|
|
99
|
-
if (this._length >= this.MAX_LENGTH) {
|
|
100
|
-
throw new Revert('push: array reached maximum length (address array)');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const newLogicalIndex: u64 = this._length;
|
|
104
|
-
const physicalIndex: u64 = (this._startIndex + newLogicalIndex) % this.MAX_LENGTH;
|
|
105
|
-
const slotIndex: u32 = <u32>physicalIndex;
|
|
106
|
-
|
|
107
|
-
this.ensureValues(slotIndex);
|
|
108
|
-
this._values.set(slotIndex, value);
|
|
109
|
-
this._isChanged.add(slotIndex);
|
|
110
|
-
|
|
111
|
-
this._length += 1;
|
|
112
|
-
this._isChangedLength = true;
|
|
20
|
+
protected zeroValue(): Address {
|
|
21
|
+
return Address.zero();
|
|
113
22
|
}
|
|
114
23
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (this._length === 0) {
|
|
118
|
-
throw new Revert('deleteLast: array is empty (address array)');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const lastLogicalIndex: u64 = this._length - 1;
|
|
122
|
-
const physicalIndex: u64 = (this._startIndex + lastLogicalIndex) % this.MAX_LENGTH;
|
|
123
|
-
const slotIndex: u32 = <u32>physicalIndex;
|
|
124
|
-
this.ensureValues(slotIndex);
|
|
125
|
-
|
|
126
|
-
const currentValue = this._values.get(slotIndex);
|
|
127
|
-
if (currentValue != this.defaultValue) {
|
|
128
|
-
this._values.set(slotIndex, this.defaultValue);
|
|
129
|
-
this._isChanged.add(slotIndex);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
this._length -= 1;
|
|
133
|
-
this._isChangedLength = true;
|
|
24
|
+
protected eq(a: Address, b: Address): bool {
|
|
25
|
+
return a == b;
|
|
134
26
|
}
|
|
135
27
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this._startIndex = index;
|
|
139
|
-
this._isChangedStartIndex = true;
|
|
28
|
+
protected packSlot(values: Address[]): Uint8Array {
|
|
29
|
+
return values[0];
|
|
140
30
|
}
|
|
141
31
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
public delete(index: u64): void {
|
|
145
|
-
if (index > this.MAX_LENGTH) {
|
|
146
|
-
throw new Revert('delete: index out of range (address array)');
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const physicalIndex: u64 = (this._startIndex + index) % this.MAX_LENGTH;
|
|
150
|
-
const slotIndex: u32 = <u32>physicalIndex;
|
|
151
|
-
this.ensureValues(slotIndex);
|
|
152
|
-
|
|
153
|
-
const currentValue = this._values.get(slotIndex);
|
|
154
|
-
if (currentValue != this.defaultValue) {
|
|
155
|
-
this._values.set(slotIndex, this.defaultValue);
|
|
156
|
-
this._isChanged.add(slotIndex);
|
|
157
|
-
}
|
|
32
|
+
protected unpackSlot(slotData: Uint8Array): Address[] {
|
|
33
|
+
return [Address.fromUint8Array(slotData)];
|
|
158
34
|
}
|
|
159
35
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
* - Store any changed slotIndex -> Address
|
|
163
|
-
* - Store updated length and startIndex if changed
|
|
164
|
-
*/
|
|
165
|
-
@inline
|
|
166
|
-
public save(): void {
|
|
167
|
-
// 1) Save changed slots
|
|
168
|
-
const changed = this._isChanged.values();
|
|
169
|
-
for (let i = 0; i < changed.length; i++) {
|
|
170
|
-
const slotIndex = changed[i];
|
|
171
|
-
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
172
|
-
|
|
173
|
-
const value = this._values.get(slotIndex);
|
|
174
|
-
Blockchain.setStorageAt(storagePointer, value);
|
|
175
|
-
}
|
|
176
|
-
this._isChanged.clear();
|
|
177
|
-
|
|
178
|
-
// 2) Save length and startIndex if changed
|
|
179
|
-
if (this._isChangedLength || this._isChangedStartIndex) {
|
|
180
|
-
const writer = new BytesWriter(16);
|
|
181
|
-
writer.writeU64(this._length);
|
|
182
|
-
writer.writeU64(this._startIndex);
|
|
183
|
-
|
|
184
|
-
Blockchain.setStorageAt(this.lengthPointer, writer.getBuffer());
|
|
185
|
-
this._isChangedLength = false;
|
|
186
|
-
this._isChangedStartIndex = false;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/** Clear entire array content from storage, reset length and startIndex. */
|
|
191
|
-
public deleteAll(): void {
|
|
192
|
-
const keys = this._values.keys();
|
|
193
|
-
for (let i = 0; i < keys.length; i++) {
|
|
194
|
-
const slotIndex = keys[i];
|
|
195
|
-
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
196
|
-
Blockchain.setStorageAt(storagePointer, this.defaultValue);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
Blockchain.setStorageAt(this.lengthPointer, new Uint8Array(32));
|
|
200
|
-
|
|
201
|
-
this._length = 0;
|
|
202
|
-
this._startIndex = 0;
|
|
203
|
-
this._isChangedLength = false;
|
|
204
|
-
this._isChangedStartIndex = false;
|
|
205
|
-
|
|
206
|
-
this._values.clear();
|
|
207
|
-
this._isChanged.clear();
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** Bulk-set multiple addresses starting at `startIndex`. */
|
|
211
|
-
@inline
|
|
212
|
-
public setMultiple(startIndex: u32, values: Address[]): void {
|
|
213
|
-
for (let i: u32 = 0; i < values.length; i++) {
|
|
214
|
-
this.set(<u64>(startIndex + i), values[i]);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** Retrieve a batch of addresses (range). */
|
|
219
|
-
@inline
|
|
220
|
-
public getAll(startIndex: u32, count: u32): Address[] {
|
|
221
|
-
if (startIndex + count > this._length) {
|
|
222
|
-
throw new Revert('getAll: index out of range (address array)');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const result = new Array<Address>(count);
|
|
226
|
-
for (let i: u32 = 0; i < count; i++) {
|
|
227
|
-
result[i] = this.get(<u64>(startIndex + i));
|
|
228
|
-
}
|
|
229
|
-
return result;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** Returns a string of the form "[addr0, addr1, ...]". */
|
|
233
|
-
@inline
|
|
234
|
-
public toString(): string {
|
|
235
|
-
let str = '[';
|
|
236
|
-
for (let i: u32 = 0; i < this._length; i++) {
|
|
237
|
-
const value = this.get(<u64>i);
|
|
238
|
-
str += value.toString();
|
|
239
|
-
if (i !== this._length - 1) {
|
|
240
|
-
str += ', ';
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
str += ']';
|
|
244
|
-
return str;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/** Reset in-memory and persist. */
|
|
248
|
-
@inline
|
|
249
|
-
public reset(): void {
|
|
250
|
-
this._length = 0;
|
|
251
|
-
this._startIndex = 0;
|
|
252
|
-
this._isChangedLength = true;
|
|
253
|
-
this._isChangedStartIndex = true;
|
|
254
|
-
|
|
255
|
-
this._values.clear();
|
|
256
|
-
this._isChanged.clear();
|
|
257
|
-
|
|
258
|
-
this.save();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/** Current array length. */
|
|
262
|
-
@inline
|
|
263
|
-
public getLength(): u64 {
|
|
264
|
-
return this._length;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** Current starting index. */
|
|
268
|
-
public startingIndex(): u64 {
|
|
269
|
-
return this._startIndex;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Ensure the given slot index is loaded into `_values`.
|
|
274
|
-
*/
|
|
275
|
-
private ensureValues(slotIndex: u32): void {
|
|
276
|
-
if (!this._values.has(slotIndex)) {
|
|
277
|
-
const storagePointer = this.calculateStoragePointer(slotIndex);
|
|
278
|
-
|
|
279
|
-
// Load raw bytes from storage
|
|
280
|
-
const stored: Uint8Array = Blockchain.getStorageAt(storagePointer);
|
|
281
|
-
|
|
282
|
-
const storedAddress: Address =
|
|
283
|
-
stored.length == 0 ? this.defaultValue : Address.fromUint8Array(stored);
|
|
284
|
-
|
|
285
|
-
this._values.set(slotIndex, storedAddress);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Compute a 32-byte storage pointer = basePointer + (slotIndex + 1) big-endian.
|
|
291
|
-
*/
|
|
292
|
-
private calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
293
|
-
// Convert (slotIndex) to a 32-byte big-endian offset
|
|
294
|
-
const offset = u64ToBE32Bytes(slotIndex);
|
|
295
|
-
|
|
296
|
-
return addUint8ArraysBE(this.baseU256Pointer, offset);
|
|
36
|
+
protected calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
37
|
+
return bigEndianAdd(this.basePointer, slotIndex);
|
|
297
38
|
}
|
|
298
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {BytesWriter} from '../../buffer/BytesWriter';
|
|
2
|
+
import {Blockchain} from '../../env';
|
|
3
|
+
import {Revert} from '../../types/Revert';
|
|
4
4
|
import {
|
|
5
5
|
addUint8ArraysBE,
|
|
6
6
|
bigEndianAdd,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
setBit,
|
|
12
12
|
u64ToBE32Bytes,
|
|
13
13
|
} from '../../math/bytes';
|
|
14
|
+
import {DEFAULT_MAX_LENGTH} from './StoredPackedArray';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* @class StoredBooleanArray
|
|
@@ -21,34 +22,40 @@ import {
|
|
|
21
22
|
* - slot 0 stores indexes [0..255]
|
|
22
23
|
* - slot 1 stores indexes [256..511]
|
|
23
24
|
* - ...
|
|
25
|
+
* Note: This is designed to wrap around.
|
|
24
26
|
*/
|
|
25
27
|
@final
|
|
26
28
|
export class StoredBooleanArray {
|
|
27
29
|
private readonly basePointer: Uint8Array;
|
|
28
30
|
private readonly lengthPointer: Uint8Array;
|
|
29
31
|
|
|
30
|
-
private _values: Map<
|
|
31
|
-
private _isChanged: Set<
|
|
32
|
+
private _values: Map<u32, Uint8Array> = new Map();
|
|
33
|
+
private _isChanged: Set<u32> = new Set();
|
|
32
34
|
|
|
33
|
-
private _length:
|
|
34
|
-
private _startIndex:
|
|
35
|
+
private _length: u32 = 0;
|
|
36
|
+
private _startIndex: u32 = 0;
|
|
35
37
|
private _isChangedLength: bool = false;
|
|
36
38
|
private _isChangedStartIndex: bool = false;
|
|
37
39
|
|
|
38
|
-
private
|
|
40
|
+
private nextItemOffset: u32 = 0;
|
|
39
41
|
|
|
40
42
|
/**
|
|
41
43
|
* @constructor
|
|
42
44
|
* @param {u16} pointer - The primary pointer identifier.
|
|
43
45
|
* @param {Uint8Array} subPtr - The sub-pointer for memory slot addressing.
|
|
46
|
+
* @param {u32} [MAX_LENGTH=DEFAULT_MAX_LENGTH] - The maximum length of the array.
|
|
44
47
|
*
|
|
45
48
|
* The code below treats the first 16 bytes of `lengthPointer` as storing [length, startIndex].
|
|
46
49
|
*/
|
|
47
50
|
constructor(
|
|
48
51
|
public pointer: u16,
|
|
49
52
|
public subPtr: Uint8Array,
|
|
53
|
+
protected MAX_LENGTH: u32 = DEFAULT_MAX_LENGTH,
|
|
50
54
|
) {
|
|
51
|
-
assert(
|
|
55
|
+
assert(
|
|
56
|
+
subPtr.length <= 30,
|
|
57
|
+
`You must pass a 30 bytes sub-pointer. (StoredBooleanArray, got ${subPtr.length})`,
|
|
58
|
+
);
|
|
52
59
|
|
|
53
60
|
const basePointer = encodeBasePointer(pointer, subPtr);
|
|
54
61
|
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
@@ -61,28 +68,81 @@ export class StoredBooleanArray {
|
|
|
61
68
|
this._startIndex = data[1];
|
|
62
69
|
}
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
@inline
|
|
72
|
+
public get previousOffset(): u32 {
|
|
73
|
+
return <u32>(
|
|
74
|
+
((this._startIndex +
|
|
75
|
+
<u64>(this.nextItemOffset === 0 ? this.nextItemOffset : this.nextItemOffset - 1)) %
|
|
76
|
+
this.MAX_LENGTH)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
65
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Set the maximum length of the array.
|
|
82
|
+
* This is a safety check to prevent unbounded usage.
|
|
83
|
+
*/
|
|
66
84
|
@inline
|
|
67
|
-
public
|
|
85
|
+
public setMaxLength(maxLength: u32): void {
|
|
86
|
+
if (maxLength > this.MAX_LENGTH) {
|
|
87
|
+
throw new Revert('setMaxLength: maxLength exceeds MAX_LENGTH');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.MAX_LENGTH = maxLength;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@inline
|
|
94
|
+
public has(index: u32): bool {
|
|
68
95
|
return index < this._length;
|
|
69
96
|
}
|
|
70
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Get the next item in the array, starting from the current offset.
|
|
100
|
+
* This is useful for iterating through the array.
|
|
101
|
+
*/
|
|
102
|
+
@inline
|
|
103
|
+
public next(): bool {
|
|
104
|
+
const value = this.get(this.nextItemOffset);
|
|
105
|
+
this.nextItemOffset += 1;
|
|
106
|
+
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Apply the starting index with n offset.
|
|
112
|
+
*/
|
|
113
|
+
@inline
|
|
114
|
+
public applyNextOffsetToStartingIndex(): void {
|
|
115
|
+
if (!this.nextItemOffset) return;
|
|
116
|
+
|
|
117
|
+
this._startIndex += this.nextItemOffset - 1;
|
|
118
|
+
this._isChangedStartIndex = true;
|
|
119
|
+
this.nextItemOffset = 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@inline
|
|
123
|
+
public incrementStartingIndex(): void {
|
|
124
|
+
if (this._startIndex >= this.MAX_LENGTH) {
|
|
125
|
+
this._startIndex = 0;
|
|
126
|
+
} else {
|
|
127
|
+
this._startIndex += 1;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this._isChangedStartIndex = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
71
133
|
/**
|
|
72
134
|
* Retrieve boolean at `index`.
|
|
73
135
|
*/
|
|
74
136
|
@operator('[]')
|
|
75
137
|
@inline
|
|
76
|
-
public get(index:
|
|
138
|
+
public get(index: u32): bool {
|
|
77
139
|
if (index >= this._length) {
|
|
78
|
-
throw new Revert(
|
|
140
|
+
throw new Revert(
|
|
141
|
+
`get: index out of range (${index} >= ${this._length}, boolean array)`,
|
|
142
|
+
);
|
|
79
143
|
}
|
|
80
144
|
|
|
81
|
-
const
|
|
82
|
-
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
83
|
-
? effectiveIndex
|
|
84
|
-
: effectiveIndex % this.MAX_LENGTH;
|
|
85
|
-
|
|
145
|
+
const wrappedIndex = this.getRealIndex(index);
|
|
86
146
|
const slotIndex = wrappedIndex / 256;
|
|
87
147
|
const bitIndex = <u16>(wrappedIndex % 256);
|
|
88
148
|
|
|
@@ -97,16 +157,14 @@ export class StoredBooleanArray {
|
|
|
97
157
|
*/
|
|
98
158
|
@operator('[]=')
|
|
99
159
|
@inline
|
|
100
|
-
public set(index:
|
|
160
|
+
public set(index: u32, value: bool): void {
|
|
101
161
|
if (index >= this._length) {
|
|
102
|
-
throw new Revert(
|
|
162
|
+
throw new Revert(
|
|
163
|
+
`set: index out of range (${index} >= ${this._length}, boolean array)`,
|
|
164
|
+
);
|
|
103
165
|
}
|
|
104
166
|
|
|
105
|
-
const
|
|
106
|
-
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
107
|
-
? effectiveIndex
|
|
108
|
-
: effectiveIndex % this.MAX_LENGTH;
|
|
109
|
-
|
|
167
|
+
const wrappedIndex = this.getRealIndex(index);
|
|
110
168
|
const slotIndex = wrappedIndex / 256;
|
|
111
169
|
const bitIndex = <u16>(wrappedIndex % 256);
|
|
112
170
|
|
|
@@ -126,17 +184,12 @@ export class StoredBooleanArray {
|
|
|
126
184
|
* Push a new boolean at the "end" of the array.
|
|
127
185
|
*/
|
|
128
186
|
@inline
|
|
129
|
-
public push(value: bool):
|
|
187
|
+
public push(value: bool): u32 {
|
|
130
188
|
if (this._length >= this.MAX_LENGTH) {
|
|
131
189
|
throw new Revert('push: reached max allowed length (boolean array)');
|
|
132
190
|
}
|
|
133
191
|
|
|
134
|
-
const
|
|
135
|
-
const effectiveIndex = this._startIndex + newIndex;
|
|
136
|
-
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
137
|
-
? effectiveIndex
|
|
138
|
-
: effectiveIndex % this.MAX_LENGTH;
|
|
139
|
-
|
|
192
|
+
const wrappedIndex = this.getRealIndex(this._length);
|
|
140
193
|
const slotIndex = wrappedIndex / 256;
|
|
141
194
|
const bitIndex = <u16>(wrappedIndex % 256);
|
|
142
195
|
|
|
@@ -150,22 +203,20 @@ export class StoredBooleanArray {
|
|
|
150
203
|
|
|
151
204
|
this._length += 1;
|
|
152
205
|
this._isChangedLength = true;
|
|
206
|
+
|
|
207
|
+
return wrappedIndex;
|
|
153
208
|
}
|
|
154
209
|
|
|
155
210
|
/**
|
|
156
211
|
* Delete the boolean at `index` by setting it to false.
|
|
157
212
|
*/
|
|
158
213
|
@inline
|
|
159
|
-
public delete(index:
|
|
214
|
+
public delete(index: u32): void {
|
|
160
215
|
if (index >= this._length) {
|
|
161
216
|
throw new Revert('delete: index out of range (boolean array)');
|
|
162
217
|
}
|
|
163
218
|
|
|
164
|
-
const
|
|
165
|
-
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
166
|
-
? effectiveIndex
|
|
167
|
-
: effectiveIndex % this.MAX_LENGTH;
|
|
168
|
-
|
|
219
|
+
const wrappedIndex = this.getRealIndex(index);
|
|
169
220
|
const slotIndex = wrappedIndex / 256;
|
|
170
221
|
const bitIndex = <u16>(wrappedIndex % 256);
|
|
171
222
|
|
|
@@ -181,6 +232,16 @@ export class StoredBooleanArray {
|
|
|
181
232
|
}
|
|
182
233
|
}
|
|
183
234
|
|
|
235
|
+
@inline
|
|
236
|
+
public removeItemFromLength(): void {
|
|
237
|
+
if (this._length == 0) {
|
|
238
|
+
throw new Revert('delete: array is empty');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this._length -= 1;
|
|
242
|
+
this._isChangedLength = true;
|
|
243
|
+
}
|
|
244
|
+
|
|
184
245
|
/**
|
|
185
246
|
* Remove the last element by setting it false and decrementing length.
|
|
186
247
|
*/
|
|
@@ -212,11 +273,10 @@ export class StoredBooleanArray {
|
|
|
212
273
|
|
|
213
274
|
this._isChanged.clear();
|
|
214
275
|
|
|
215
|
-
// 2) If length or startIndex changed, store them
|
|
216
276
|
if (this._isChangedLength || this._isChangedStartIndex) {
|
|
217
277
|
const w = new BytesWriter(32);
|
|
218
|
-
w.
|
|
219
|
-
w.
|
|
278
|
+
w.writeU32(this._length);
|
|
279
|
+
w.writeU32(this._startIndex);
|
|
220
280
|
|
|
221
281
|
const data = w.getBuffer();
|
|
222
282
|
Blockchain.setStorageAt(this.lengthPointer, data);
|
|
@@ -255,8 +315,8 @@ export class StoredBooleanArray {
|
|
|
255
315
|
* Set multiple bools starting at `startIndex`.
|
|
256
316
|
*/
|
|
257
317
|
@inline
|
|
258
|
-
public setMultiple(startIndex:
|
|
259
|
-
for (let i:
|
|
318
|
+
public setMultiple(startIndex: u32, values: bool[]): void {
|
|
319
|
+
for (let i: u32 = 0; i < values.length; i++) {
|
|
260
320
|
this.set(startIndex + i, values[i]);
|
|
261
321
|
}
|
|
262
322
|
}
|
|
@@ -265,17 +325,17 @@ export class StoredBooleanArray {
|
|
|
265
325
|
* Retrieve a batch of bools.
|
|
266
326
|
*/
|
|
267
327
|
@inline
|
|
268
|
-
public getAll(start:
|
|
328
|
+
public getAll(start: u32, count: u32): bool[] {
|
|
269
329
|
if (start + count > this._length) {
|
|
270
330
|
throw new Revert('getAll: range exceeds array length (boolean array)');
|
|
271
331
|
}
|
|
272
332
|
|
|
273
|
-
if (count >
|
|
333
|
+
if (count > u32(u32.MAX_VALUE)) {
|
|
274
334
|
throw new Revert('getAll: range exceeds max allowed (boolean array)');
|
|
275
335
|
}
|
|
276
336
|
|
|
277
337
|
const result = new Array<bool>(<i32>count);
|
|
278
|
-
for (let i:
|
|
338
|
+
for (let i: u32 = 0; i < count; i++) {
|
|
279
339
|
result[<i32>i] = this.get(start + i);
|
|
280
340
|
}
|
|
281
341
|
|
|
@@ -288,7 +348,7 @@ export class StoredBooleanArray {
|
|
|
288
348
|
@inline
|
|
289
349
|
public toString(): string {
|
|
290
350
|
let s = '[';
|
|
291
|
-
for (let i:
|
|
351
|
+
for (let i: u32 = 0; i < this._length; i++) {
|
|
292
352
|
s += this.get(i).toString();
|
|
293
353
|
if (i < this._length - 1) {
|
|
294
354
|
s += ', ';
|
|
@@ -318,12 +378,12 @@ export class StoredBooleanArray {
|
|
|
318
378
|
* Current array length (number of booleans stored).
|
|
319
379
|
*/
|
|
320
380
|
@inline
|
|
321
|
-
public getLength():
|
|
381
|
+
public getLength(): u32 {
|
|
322
382
|
return this._length;
|
|
323
383
|
}
|
|
324
384
|
|
|
325
385
|
@inline
|
|
326
|
-
public setStartingIndex(index:
|
|
386
|
+
public setStartingIndex(index: u32): void {
|
|
327
387
|
this._startIndex = index;
|
|
328
388
|
this._isChangedStartIndex = true;
|
|
329
389
|
}
|
|
@@ -332,14 +392,24 @@ export class StoredBooleanArray {
|
|
|
332
392
|
* Current starting index for the array.
|
|
333
393
|
*/
|
|
334
394
|
@inline
|
|
335
|
-
public startingIndex():
|
|
395
|
+
public startingIndex(): u32 {
|
|
336
396
|
return this._startIndex;
|
|
337
397
|
}
|
|
338
398
|
|
|
399
|
+
private getRealIndex(index: u32): u32 {
|
|
400
|
+
const maxLength: u64 = <u64>this.MAX_LENGTH;
|
|
401
|
+
let realIndex: u64 = <u64>this._startIndex + <u64>index;
|
|
402
|
+
if (!(realIndex < maxLength)) {
|
|
403
|
+
realIndex %= maxLength;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return <u32>realIndex;
|
|
407
|
+
}
|
|
408
|
+
|
|
339
409
|
/**
|
|
340
410
|
* Ensure the 32-byte slot for `slotIndex` is loaded into _values.
|
|
341
411
|
*/
|
|
342
|
-
private ensureSlotLoaded(slotIndex:
|
|
412
|
+
private ensureSlotLoaded(slotIndex: u32): void {
|
|
343
413
|
if (!this._values.has(slotIndex)) {
|
|
344
414
|
const pointer = this.calculateStoragePointer(slotIndex);
|
|
345
415
|
const stored = Blockchain.getStorageAt(pointer);
|
|
@@ -350,7 +420,7 @@ export class StoredBooleanArray {
|
|
|
350
420
|
/**
|
|
351
421
|
* Convert `slotIndex` -> pointer = basePointer + (slotIndex + 1), as big-endian addition.
|
|
352
422
|
*/
|
|
353
|
-
private calculateStoragePointer(slotIndex:
|
|
423
|
+
private calculateStoragePointer(slotIndex: u32): Uint8Array {
|
|
354
424
|
const offset = u64ToBE32Bytes(slotIndex);
|
|
355
425
|
return addUint8ArraysBE(this.basePointer, offset);
|
|
356
426
|
}
|