@btc-vision/btc-runtime 1.5.4 → 1.5.6
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/BlockchainEnvironment.ts +2 -0
- package/runtime/env/classes/Transaction.ts +1 -0
- package/runtime/exports/index.ts +1 -1
- package/runtime/math/bytes.ts +2 -18
- package/runtime/storage/arrays/StoredAddressArray.ts +48 -42
- package/runtime/storage/arrays/StoredBooleanArray.ts +58 -26
- package/runtime/storage/arrays/StoredPackedArray.ts +27 -7
- package/runtime/storage/arrays/StoredU128Array.ts +1 -1
- package/runtime/storage/arrays/StoredU16Array.ts +1 -1
- package/runtime/storage/arrays/StoredU256Array.ts +1 -1
- package/runtime/storage/arrays/StoredU32Array.ts +1 -1
- package/runtime/storage/arrays/StoredU64Array.ts +1 -1
- package/runtime/storage/arrays/StoredU8Array.ts +1 -1
package/package.json
CHANGED
|
@@ -148,6 +148,7 @@ export class BlockchainEnvironment {
|
|
|
148
148
|
const blockHash = reader.readBytes(32);
|
|
149
149
|
const blockNumber = reader.readU64();
|
|
150
150
|
const blockMedianTime = reader.readU64();
|
|
151
|
+
const txId = reader.readBytes(32);
|
|
151
152
|
const txHash = reader.readBytes(32);
|
|
152
153
|
const contractAddress = reader.readAddress();
|
|
153
154
|
const contractDeployer = reader.readAddress();
|
|
@@ -157,6 +158,7 @@ export class BlockchainEnvironment {
|
|
|
157
158
|
this._tx = new Transaction(
|
|
158
159
|
caller,
|
|
159
160
|
origin,
|
|
161
|
+
txId,
|
|
160
162
|
txHash,
|
|
161
163
|
);
|
|
162
164
|
|
|
@@ -9,6 +9,7 @@ export class Transaction {
|
|
|
9
9
|
public constructor(
|
|
10
10
|
public readonly sender: Address, // "immediate caller"
|
|
11
11
|
public readonly origin: Address, // "leftmost thing in the call chain"
|
|
12
|
+
public readonly txId: Uint8Array,
|
|
12
13
|
public readonly hash: Uint8Array,
|
|
13
14
|
) {
|
|
14
15
|
}
|
package/runtime/exports/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Selector } from '../math/abi';
|
|
|
5
5
|
import { Calldata } from '../types';
|
|
6
6
|
import { env_exit, getCalldata, getEnvironmentVariables } from '../env/global';
|
|
7
7
|
|
|
8
|
-
const ENVIRONMENT_VARIABLES_BYTE_LENGTH: u32 =
|
|
8
|
+
const ENVIRONMENT_VARIABLES_BYTE_LENGTH: u32 = 240;
|
|
9
9
|
|
|
10
10
|
export function execute(calldataLength: u32): u32 {
|
|
11
11
|
const environmentVariablesBuffer = new ArrayBuffer(ENVIRONMENT_VARIABLES_BYTE_LENGTH);
|
package/runtime/math/bytes.ts
CHANGED
|
@@ -171,23 +171,7 @@ export function encodeBasePointer(pointer: u16, subPointer: Uint8Array): Uint8Ar
|
|
|
171
171
|
|
|
172
172
|
@inline
|
|
173
173
|
export function bigEndianAdd(base: Uint8Array, increment: u64): Uint8Array {
|
|
174
|
-
const
|
|
174
|
+
const add = u64ToBE32Bytes(increment);
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
for (let i = 0; i < 32; i++) {
|
|
178
|
-
out[i] = base[i];
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// If this is truly big-endian, out[0] is the MSB, out[31] is the LSB.
|
|
182
|
-
// So to add `increment`, we start from out[31] backward.
|
|
183
|
-
let carry: u64 = increment;
|
|
184
|
-
for (let i = 31; i >= 0; i--) {
|
|
185
|
-
const sum = <u64>out[i] + (carry & 0xFF);
|
|
186
|
-
out[i] = <u8>(sum & 0xFF);
|
|
187
|
-
carry = sum >> 8;
|
|
188
|
-
if (carry == 0 || i == 0) {
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return out;
|
|
176
|
+
return addUint8ArraysBE(base, add);
|
|
193
177
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
2
2
|
import { Blockchain } from '../../env';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
addUint8ArraysBE,
|
|
5
|
+
bigEndianAdd,
|
|
6
|
+
encodeBasePointer,
|
|
7
|
+
readLengthAndStartIndex,
|
|
8
|
+
u64ToBE32Bytes,
|
|
9
|
+
} from '../../math/bytes';
|
|
4
10
|
import { Address } from '../../types/Address';
|
|
5
11
|
import { Revert } from '../../types/Revert';
|
|
6
12
|
|
|
@@ -39,7 +45,7 @@ export class StoredAddressArray {
|
|
|
39
45
|
|
|
40
46
|
const basePointer = encodeBasePointer(pointer, subPointer);
|
|
41
47
|
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
42
|
-
this.baseU256Pointer = basePointer;
|
|
48
|
+
this.baseU256Pointer = bigEndianAdd(basePointer, 1);
|
|
43
49
|
|
|
44
50
|
const storedLenStart = Blockchain.getStorageAt(basePointer);
|
|
45
51
|
const data = readLengthAndStartIndex(storedLenStart);
|
|
@@ -48,13 +54,21 @@ export class StoredAddressArray {
|
|
|
48
54
|
this._startIndex = data[1];
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
@inline
|
|
58
|
+
public has(index: u64): bool {
|
|
59
|
+
return index < this._length;
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
/** Get an element by its global index. */
|
|
52
63
|
@inline
|
|
64
|
+
@operator('[]')
|
|
53
65
|
public get(index: u64): Address {
|
|
54
|
-
if (index
|
|
55
|
-
throw new Revert('
|
|
66
|
+
if (index >= this._length) {
|
|
67
|
+
throw new Revert('get: index out of range (address array)');
|
|
56
68
|
}
|
|
57
|
-
|
|
69
|
+
|
|
70
|
+
const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
71
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
58
72
|
this.ensureValues(slotIndex);
|
|
59
73
|
|
|
60
74
|
return this._values.get(slotIndex);
|
|
@@ -62,11 +76,14 @@ export class StoredAddressArray {
|
|
|
62
76
|
|
|
63
77
|
/** Set an element by its global index. */
|
|
64
78
|
@inline
|
|
79
|
+
@operator('[]=')
|
|
65
80
|
public set(index: u64, value: Address): void {
|
|
66
|
-
if (index
|
|
67
|
-
throw new Revert('
|
|
81
|
+
if (index >= this._length) {
|
|
82
|
+
throw new Revert('set: index out of range (address array)');
|
|
68
83
|
}
|
|
69
|
-
|
|
84
|
+
|
|
85
|
+
const physicalIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
86
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
70
87
|
this.ensureValues(slotIndex);
|
|
71
88
|
|
|
72
89
|
const currentValue = this._values.get(slotIndex);
|
|
@@ -76,33 +93,16 @@ export class StoredAddressArray {
|
|
|
76
93
|
}
|
|
77
94
|
}
|
|
78
95
|
|
|
79
|
-
/** Find the first index containing `value`. Returns -1 if not found. */
|
|
80
|
-
@inline
|
|
81
|
-
public indexOf(value: Address): i64 {
|
|
82
|
-
for (let i: u64 = 0; i < this._length; i++) {
|
|
83
|
-
if (this.get(i) == value) {
|
|
84
|
-
return i64(i);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return -1;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Check if the array contains `value`. */
|
|
91
|
-
@inline
|
|
92
|
-
public contains(value: Address): boolean {
|
|
93
|
-
return this.indexOf(value) !== -1;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
96
|
/** Append an address at the end of the array. */
|
|
97
|
+
@inline
|
|
97
98
|
public push(value: Address): void {
|
|
98
99
|
if (this._length >= this.MAX_LENGTH) {
|
|
99
|
-
throw new Revert('
|
|
100
|
+
throw new Revert('push: array reached maximum length (address array)');
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
const slotIndex: u32 = <u32>wrappedIndex;
|
|
103
|
+
const newLogicalIndex: u64 = this._length;
|
|
104
|
+
const physicalIndex: u64 = (this._startIndex + newLogicalIndex) % this.MAX_LENGTH;
|
|
105
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
106
106
|
|
|
107
107
|
this.ensureValues(slotIndex);
|
|
108
108
|
this._values.set(slotIndex, value);
|
|
@@ -115,11 +115,12 @@ export class StoredAddressArray {
|
|
|
115
115
|
/** Delete the last element. */
|
|
116
116
|
public deleteLast(): void {
|
|
117
117
|
if (this._length === 0) {
|
|
118
|
-
throw new Revert('
|
|
118
|
+
throw new Revert('deleteLast: array is empty (address array)');
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
const
|
|
122
|
-
const
|
|
121
|
+
const lastLogicalIndex: u64 = this._length - 1;
|
|
122
|
+
const physicalIndex: u64 = (this._startIndex + lastLogicalIndex) % this.MAX_LENGTH;
|
|
123
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
123
124
|
this.ensureValues(slotIndex);
|
|
124
125
|
|
|
125
126
|
const currentValue = this._values.get(slotIndex);
|
|
@@ -139,12 +140,14 @@ export class StoredAddressArray {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
/** Delete a specific element by setting it to `defaultValue`. */
|
|
143
|
+
@inline
|
|
142
144
|
public delete(index: u64): void {
|
|
143
145
|
if (index > this.MAX_LENGTH) {
|
|
144
|
-
throw new Revert('
|
|
146
|
+
throw new Revert('delete: index out of range (address array)');
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
const
|
|
149
|
+
const physicalIndex: u64 = (this._startIndex + index) % this.MAX_LENGTH;
|
|
150
|
+
const slotIndex: u32 = <u32>physicalIndex;
|
|
148
151
|
this.ensureValues(slotIndex);
|
|
149
152
|
|
|
150
153
|
const currentValue = this._values.get(slotIndex);
|
|
@@ -159,6 +162,7 @@ export class StoredAddressArray {
|
|
|
159
162
|
* - Store any changed slotIndex -> Address
|
|
160
163
|
* - Store updated length and startIndex if changed
|
|
161
164
|
*/
|
|
165
|
+
@inline
|
|
162
166
|
public save(): void {
|
|
163
167
|
// 1) Save changed slots
|
|
164
168
|
const changed = this._isChanged.values();
|
|
@@ -185,7 +189,6 @@ export class StoredAddressArray {
|
|
|
185
189
|
|
|
186
190
|
/** Clear entire array content from storage, reset length and startIndex. */
|
|
187
191
|
public deleteAll(): void {
|
|
188
|
-
// Clear all loaded slots
|
|
189
192
|
const keys = this._values.keys();
|
|
190
193
|
for (let i = 0; i < keys.length; i++) {
|
|
191
194
|
const slotIndex = keys[i];
|
|
@@ -193,14 +196,13 @@ export class StoredAddressArray {
|
|
|
193
196
|
Blockchain.setStorageAt(storagePointer, this.defaultValue);
|
|
194
197
|
}
|
|
195
198
|
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
Blockchain.setStorageAt(this.lengthPointer, new Uint8Array(32));
|
|
200
|
+
|
|
198
201
|
this._length = 0;
|
|
199
202
|
this._startIndex = 0;
|
|
200
203
|
this._isChangedLength = false;
|
|
201
204
|
this._isChangedStartIndex = false;
|
|
202
205
|
|
|
203
|
-
// Clear internal caches
|
|
204
206
|
this._values.clear();
|
|
205
207
|
this._isChanged.clear();
|
|
206
208
|
}
|
|
@@ -217,7 +219,7 @@ export class StoredAddressArray {
|
|
|
217
219
|
@inline
|
|
218
220
|
public getAll(startIndex: u32, count: u32): Address[] {
|
|
219
221
|
if (startIndex + count > this._length) {
|
|
220
|
-
throw new Revert('
|
|
222
|
+
throw new Revert('getAll: index out of range (address array)');
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
const result = new Array<Address>(count);
|
|
@@ -249,6 +251,10 @@ export class StoredAddressArray {
|
|
|
249
251
|
this._startIndex = 0;
|
|
250
252
|
this._isChangedLength = true;
|
|
251
253
|
this._isChangedStartIndex = true;
|
|
254
|
+
|
|
255
|
+
this._values.clear();
|
|
256
|
+
this._isChanged.clear();
|
|
257
|
+
|
|
252
258
|
this.save();
|
|
253
259
|
}
|
|
254
260
|
|
|
@@ -284,8 +290,8 @@ export class StoredAddressArray {
|
|
|
284
290
|
* Compute a 32-byte storage pointer = basePointer + (slotIndex + 1) big-endian.
|
|
285
291
|
*/
|
|
286
292
|
private calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
287
|
-
// Convert (slotIndex
|
|
288
|
-
const offset = u64ToBE32Bytes(slotIndex
|
|
293
|
+
// Convert (slotIndex) to a 32-byte big-endian offset
|
|
294
|
+
const offset = u64ToBE32Bytes(slotIndex);
|
|
289
295
|
|
|
290
296
|
return addUint8ArraysBE(this.baseU256Pointer, offset);
|
|
291
297
|
}
|
|
@@ -3,6 +3,7 @@ import { Blockchain } from '../../env';
|
|
|
3
3
|
import { Revert } from '../../types/Revert';
|
|
4
4
|
import {
|
|
5
5
|
addUint8ArraysBE,
|
|
6
|
+
bigEndianAdd,
|
|
6
7
|
encodeBasePointer,
|
|
7
8
|
GET_EMPTY_BUFFER,
|
|
8
9
|
getBit,
|
|
@@ -51,7 +52,7 @@ export class StoredBooleanArray {
|
|
|
51
52
|
|
|
52
53
|
const basePointer = encodeBasePointer(pointer, subPtr);
|
|
53
54
|
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
54
|
-
this.basePointer = basePointer;
|
|
55
|
+
this.basePointer = bigEndianAdd(basePointer, 1);
|
|
55
56
|
|
|
56
57
|
const storedLenStart = Blockchain.getStorageAt(basePointer);
|
|
57
58
|
const data = readLengthAndStartIndex(storedLenStart);
|
|
@@ -62,16 +63,28 @@ export class StoredBooleanArray {
|
|
|
62
63
|
|
|
63
64
|
// -------------- Public Accessors -------------- //
|
|
64
65
|
|
|
66
|
+
@inline
|
|
67
|
+
public has(index: u64): bool {
|
|
68
|
+
return index < this._length;
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* Retrieve boolean at `index`.
|
|
67
73
|
*/
|
|
74
|
+
@operator('[]')
|
|
68
75
|
@inline
|
|
69
76
|
public get(index: u64): bool {
|
|
70
|
-
if (index
|
|
71
|
-
throw new Revert(
|
|
77
|
+
if (index >= this._length) {
|
|
78
|
+
throw new Revert(`get: index out of range (${index} >= ${this._length}, boolean array)`);
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
const
|
|
80
|
+
|
|
81
|
+
const effectiveIndex = this._startIndex + index;
|
|
82
|
+
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
83
|
+
? effectiveIndex
|
|
84
|
+
: effectiveIndex % this.MAX_LENGTH;
|
|
85
|
+
|
|
86
|
+
const slotIndex = wrappedIndex / 256;
|
|
87
|
+
const bitIndex = <u16>(wrappedIndex % 256);
|
|
75
88
|
|
|
76
89
|
this.ensureSlotLoaded(slotIndex);
|
|
77
90
|
|
|
@@ -82,13 +95,20 @@ export class StoredBooleanArray {
|
|
|
82
95
|
/**
|
|
83
96
|
* Set boolean at `index`.
|
|
84
97
|
*/
|
|
98
|
+
@operator('[]=')
|
|
85
99
|
@inline
|
|
86
100
|
public set(index: u64, value: bool): void {
|
|
87
|
-
if (index
|
|
88
|
-
throw new Revert(
|
|
101
|
+
if (index >= this._length) {
|
|
102
|
+
throw new Revert(`set: index out of range (${index} >= ${this._length}, boolean array)`);
|
|
89
103
|
}
|
|
90
|
-
|
|
91
|
-
const
|
|
104
|
+
|
|
105
|
+
const effectiveIndex = this._startIndex + index;
|
|
106
|
+
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
107
|
+
? effectiveIndex
|
|
108
|
+
: effectiveIndex % this.MAX_LENGTH;
|
|
109
|
+
|
|
110
|
+
const slotIndex = wrappedIndex / 256;
|
|
111
|
+
const bitIndex = <u16>(wrappedIndex % 256);
|
|
92
112
|
|
|
93
113
|
this.ensureSlotLoaded(slotIndex);
|
|
94
114
|
|
|
@@ -108,7 +128,7 @@ export class StoredBooleanArray {
|
|
|
108
128
|
@inline
|
|
109
129
|
public push(value: bool): void {
|
|
110
130
|
if (this._length >= this.MAX_LENGTH) {
|
|
111
|
-
throw new Revert('push:
|
|
131
|
+
throw new Revert('push: reached max allowed length (boolean array)');
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
const newIndex = this._length;
|
|
@@ -137,11 +157,17 @@ export class StoredBooleanArray {
|
|
|
137
157
|
*/
|
|
138
158
|
@inline
|
|
139
159
|
public delete(index: u64): void {
|
|
140
|
-
if (index
|
|
141
|
-
throw new Revert('delete:
|
|
160
|
+
if (index >= this._length) {
|
|
161
|
+
throw new Revert('delete: index out of range (boolean array)');
|
|
142
162
|
}
|
|
143
|
-
|
|
144
|
-
const
|
|
163
|
+
|
|
164
|
+
const effectiveIndex = this._startIndex + index;
|
|
165
|
+
const wrappedIndex = effectiveIndex < this.MAX_LENGTH
|
|
166
|
+
? effectiveIndex
|
|
167
|
+
: effectiveIndex % this.MAX_LENGTH;
|
|
168
|
+
|
|
169
|
+
const slotIndex = wrappedIndex / 256;
|
|
170
|
+
const bitIndex = <u16>(wrappedIndex % 256);
|
|
145
171
|
|
|
146
172
|
this.ensureSlotLoaded(slotIndex);
|
|
147
173
|
|
|
@@ -161,8 +187,9 @@ export class StoredBooleanArray {
|
|
|
161
187
|
@inline
|
|
162
188
|
public deleteLast(): void {
|
|
163
189
|
if (this._length === 0) {
|
|
164
|
-
throw new Revert('deleteLast:
|
|
190
|
+
throw new Revert('deleteLast: array is empty');
|
|
165
191
|
}
|
|
192
|
+
|
|
166
193
|
const lastIndex = this._length - 1;
|
|
167
194
|
this.delete(lastIndex);
|
|
168
195
|
|
|
@@ -200,11 +227,10 @@ export class StoredBooleanArray {
|
|
|
200
227
|
}
|
|
201
228
|
|
|
202
229
|
/**
|
|
203
|
-
* Delete all slots in storage (that are loaded) + reset length +
|
|
230
|
+
* Delete all slots in storage (that are loaded) + reset length + _startIndex.
|
|
204
231
|
*/
|
|
205
232
|
@inline
|
|
206
233
|
public deleteAll(): void {
|
|
207
|
-
// clear all loaded slots
|
|
208
234
|
const keys = this._values.keys();
|
|
209
235
|
const zeroArr = GET_EMPTY_BUFFER();
|
|
210
236
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -213,15 +239,14 @@ export class StoredBooleanArray {
|
|
|
213
239
|
Blockchain.setStorageAt(storagePointer, zeroArr);
|
|
214
240
|
}
|
|
215
241
|
|
|
216
|
-
// also reset length + startIndex in storage
|
|
217
242
|
const writer = new BytesWriter(32);
|
|
218
243
|
Blockchain.setStorageAt(this.lengthPointer, writer.getBuffer());
|
|
219
244
|
|
|
220
|
-
// reset in memory
|
|
221
245
|
this._length = 0;
|
|
222
246
|
this._startIndex = 0;
|
|
223
247
|
this._isChangedLength = false;
|
|
224
248
|
this._isChangedStartIndex = false;
|
|
249
|
+
|
|
225
250
|
this._values.clear();
|
|
226
251
|
this._isChanged.clear();
|
|
227
252
|
}
|
|
@@ -240,18 +265,20 @@ export class StoredBooleanArray {
|
|
|
240
265
|
* Retrieve a batch of bools.
|
|
241
266
|
*/
|
|
242
267
|
@inline
|
|
243
|
-
public getAll(
|
|
244
|
-
if (
|
|
245
|
-
throw new Revert('getAll: range exceeds array length');
|
|
268
|
+
public getAll(start: u64, count: u64): bool[] {
|
|
269
|
+
if (start + count > this._length) {
|
|
270
|
+
throw new Revert('getAll: range exceeds array length (boolean array)');
|
|
246
271
|
}
|
|
272
|
+
|
|
247
273
|
if (count > u64(u32.MAX_VALUE)) {
|
|
248
|
-
throw new Revert('getAll: range exceeds max allowed');
|
|
274
|
+
throw new Revert('getAll: range exceeds max allowed (boolean array)');
|
|
249
275
|
}
|
|
250
276
|
|
|
251
277
|
const result = new Array<bool>(<i32>count);
|
|
252
278
|
for (let i: u64 = 0; i < count; i++) {
|
|
253
|
-
result[<i32>i] = this.get(
|
|
279
|
+
result[<i32>i] = this.get(start + i);
|
|
254
280
|
}
|
|
281
|
+
|
|
255
282
|
return result;
|
|
256
283
|
}
|
|
257
284
|
|
|
@@ -295,6 +322,12 @@ export class StoredBooleanArray {
|
|
|
295
322
|
return this._length;
|
|
296
323
|
}
|
|
297
324
|
|
|
325
|
+
@inline
|
|
326
|
+
public setStartingIndex(index: u64): void {
|
|
327
|
+
this._startIndex = index;
|
|
328
|
+
this._isChangedStartIndex = true;
|
|
329
|
+
}
|
|
330
|
+
|
|
298
331
|
/**
|
|
299
332
|
* Current starting index for the array.
|
|
300
333
|
*/
|
|
@@ -303,7 +336,6 @@ export class StoredBooleanArray {
|
|
|
303
336
|
return this._startIndex;
|
|
304
337
|
}
|
|
305
338
|
|
|
306
|
-
|
|
307
339
|
/**
|
|
308
340
|
* Ensure the 32-byte slot for `slotIndex` is loaded into _values.
|
|
309
341
|
*/
|
|
@@ -319,7 +351,7 @@ export class StoredBooleanArray {
|
|
|
319
351
|
* Convert `slotIndex` -> pointer = basePointer + (slotIndex + 1), as big-endian addition.
|
|
320
352
|
*/
|
|
321
353
|
private calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
322
|
-
const offset = u64ToBE32Bytes(slotIndex
|
|
354
|
+
const offset = u64ToBE32Bytes(slotIndex);
|
|
323
355
|
return addUint8ArraysBE(this.basePointer, offset);
|
|
324
356
|
}
|
|
325
357
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
bigEndianAdd,
|
|
2
3
|
encodeBasePointer,
|
|
3
4
|
GET_EMPTY_BUFFER,
|
|
4
5
|
readLengthAndStartIndex,
|
|
@@ -53,7 +54,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
53
54
|
|
|
54
55
|
const basePointer = encodeBasePointer(pointer, subPointer);
|
|
55
56
|
this.lengthPointer = Uint8Array.wrap(basePointer.buffer);
|
|
56
|
-
this.basePointer = basePointer;
|
|
57
|
+
this.basePointer = bigEndianAdd(basePointer, 1);
|
|
57
58
|
|
|
58
59
|
const storedLenStart = Blockchain.getStorageAt(basePointer);
|
|
59
60
|
const data = readLengthAndStartIndex(storedLenStart);
|
|
@@ -62,9 +63,11 @@ export abstract class StoredPackedArray<T> {
|
|
|
62
63
|
this._startIndex = data[1];
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
@inline
|
|
67
|
+
@operator('[]')
|
|
65
68
|
public get(index: u64): T {
|
|
66
69
|
if (index > this.MAX_LENGTH) {
|
|
67
|
-
throw new Revert('get: index exceeds MAX_LENGTH');
|
|
70
|
+
throw new Revert('get: index exceeds MAX_LENGTH (packed array)');
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
const cap = this.getSlotCapacity();
|
|
@@ -79,7 +82,13 @@ export abstract class StoredPackedArray<T> {
|
|
|
79
82
|
return arr[subIndex];
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
@inline
|
|
86
|
+
@operator('[]=')
|
|
82
87
|
public set(index: u64, value: T): void {
|
|
88
|
+
if (index > this.MAX_LENGTH) {
|
|
89
|
+
throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
|
|
90
|
+
}
|
|
91
|
+
|
|
83
92
|
const cap = this.getSlotCapacity();
|
|
84
93
|
const slotIndex = index / cap;
|
|
85
94
|
const subIndex = <u32>(index % cap);
|
|
@@ -95,9 +104,10 @@ export abstract class StoredPackedArray<T> {
|
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
|
|
107
|
+
@inline
|
|
98
108
|
public push(value: T): void {
|
|
99
109
|
if (this._length >= this.MAX_LENGTH) {
|
|
100
|
-
throw new Revert('push: array has reached MAX_LENGTH');
|
|
110
|
+
throw new Revert('push: array has reached MAX_LENGTH (packed array)');
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
const newIndex = this._length;
|
|
@@ -123,6 +133,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
123
133
|
* "Delete" by zeroing out the element at `index`,
|
|
124
134
|
* but does not reduce the length.
|
|
125
135
|
*/
|
|
136
|
+
@inline
|
|
126
137
|
public delete(index: u64): void {
|
|
127
138
|
const cap = this.getSlotCapacity();
|
|
128
139
|
const slotIndex = index / cap;
|
|
@@ -143,9 +154,10 @@ export abstract class StoredPackedArray<T> {
|
|
|
143
154
|
/**
|
|
144
155
|
* Remove the last element by zeroing it and decrementing length by 1.
|
|
145
156
|
*/
|
|
157
|
+
@inline
|
|
146
158
|
public deleteLast(): void {
|
|
147
159
|
if (this._length == 0) {
|
|
148
|
-
throw new Revert('deleteLast: array is empty');
|
|
160
|
+
throw new Revert('deleteLast: array is empty (packed array)');
|
|
149
161
|
}
|
|
150
162
|
|
|
151
163
|
const lastIndex = this._length - 1;
|
|
@@ -155,11 +167,13 @@ export abstract class StoredPackedArray<T> {
|
|
|
155
167
|
this._isChangedLength = true;
|
|
156
168
|
}
|
|
157
169
|
|
|
170
|
+
@inline
|
|
158
171
|
public setMultiple(startIndex: u64, values: T[]): void {
|
|
159
172
|
const end = startIndex + <u64>values.length;
|
|
160
173
|
if (end > this._length) {
|
|
161
|
-
throw new Revert('setMultiple: out of range');
|
|
174
|
+
throw new Revert('setMultiple: out of range (packed array)');
|
|
162
175
|
}
|
|
176
|
+
|
|
163
177
|
for (let i = 0; i < values.length; i++) {
|
|
164
178
|
this.set(startIndex + <u64>i, values[i]);
|
|
165
179
|
}
|
|
@@ -168,10 +182,10 @@ export abstract class StoredPackedArray<T> {
|
|
|
168
182
|
// -----------------------------------------------------------
|
|
169
183
|
// Public Array-Like Methods
|
|
170
184
|
// -----------------------------------------------------------
|
|
171
|
-
|
|
185
|
+
@inline
|
|
172
186
|
public getAll(startIndex: u64, count: u64): T[] {
|
|
173
187
|
if (count > <u64>u32.MAX_VALUE) {
|
|
174
|
-
throw new Revert('getAll: count too large');
|
|
188
|
+
throw new Revert('getAll: count too large (packed array)');
|
|
175
189
|
}
|
|
176
190
|
|
|
177
191
|
const out = new Array<T>(<i32>count);
|
|
@@ -182,14 +196,17 @@ export abstract class StoredPackedArray<T> {
|
|
|
182
196
|
return out;
|
|
183
197
|
}
|
|
184
198
|
|
|
199
|
+
@inline
|
|
185
200
|
public getLength(): u64 {
|
|
186
201
|
return this._length;
|
|
187
202
|
}
|
|
188
203
|
|
|
204
|
+
@inline
|
|
189
205
|
public startingIndex(): u64 {
|
|
190
206
|
return this._startIndex;
|
|
191
207
|
}
|
|
192
208
|
|
|
209
|
+
@inline
|
|
193
210
|
public setStartingIndex(index: u64): void {
|
|
194
211
|
this._startIndex = index;
|
|
195
212
|
this._isChangedStartIndex = true;
|
|
@@ -250,6 +267,9 @@ export abstract class StoredPackedArray<T> {
|
|
|
250
267
|
this._isChanged.clear();
|
|
251
268
|
}
|
|
252
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Reset the array to its initial state.
|
|
272
|
+
*/
|
|
253
273
|
public reset(): void {
|
|
254
274
|
this._length = 0;
|
|
255
275
|
this._startIndex = 0;
|
|
@@ -43,6 +43,6 @@ export class StoredU8Array extends StoredPackedArray<u8> {
|
|
|
43
43
|
|
|
44
44
|
protected calculateStoragePointer(slotIndex: u64): Uint8Array {
|
|
45
45
|
// basePointer + (slotIndex+1) in big-endian
|
|
46
|
-
return bigEndianAdd(this.basePointer, slotIndex
|
|
46
|
+
return bigEndianAdd(this.basePointer, slotIndex);
|
|
47
47
|
}
|
|
48
48
|
}
|