Skip to content

Commit 935343d

Browse files
committed
feat: add stubs for concat functions and mutable array
1 parent d1c4260 commit 935343d

File tree

12 files changed

+549
-15
lines changed

12 files changed

+549
-15
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@
7171
"tslib": "^2.6.2"
7272
},
7373
"dependencies": {
74-
"@algorandfoundation/algorand-typescript": "^1.0.0-beta.17",
75-
"@algorandfoundation/puya-ts": "^1.0.0-beta.24",
74+
"@algorandfoundation/algorand-typescript": "^1.0.0-beta.18",
75+
"@algorandfoundation/puya-ts": "^1.0.0-beta.25",
7676
"elliptic": "^6.5.7",
7777
"js-sha256": "^0.11.0",
7878
"js-sha3": "^0.9.3",

src/impl/encoded-types.ts

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,17 @@ import { lazyContext } from '../context-helpers/internal-context'
3535
import type { fromBytes, TypeInfo } from '../encoders'
3636
import { AvmError, avmInvariant, CodeError } from '../errors'
3737
import type { DeliberateAny } from '../typescript-helpers'
38-
import { asBigInt, asBigUint, asBigUintCls, asBytesCls, asUint64, asUint8Array, conactUint8Arrays, uint8ArrayToNumber } from '../util'
38+
import {
39+
asBigInt,
40+
asBigUint,
41+
asBigUintCls,
42+
asBytes,
43+
asBytesCls,
44+
asUint64,
45+
asUint8Array,
46+
conactUint8Arrays,
47+
uint8ArrayToNumber,
48+
} from '../util'
3949
import type { StubBytesCompat } from './primitives'
4050
import { AlgoTsPrimitiveCls, arrayUtil, BigUintCls, Bytes, BytesCls, getUint8Array, isBytes, Uint64Cls } from './primitives'
4151
import { Account, AccountCls, ApplicationCls, AssetCls } from './reference'
@@ -46,7 +56,8 @@ const maxBigIntValue = (bitSize: number) => 2n ** BigInt(bitSize) - 1n
4656
const maxBytesLength = (bitSize: number) => Math.floor(bitSize / BITS_IN_BYTE)
4757
const encodeLength = (length: number) => new BytesCls(encodingUtil.bigIntToUint8Array(BigInt(length), ABI_LENGTH_SIZE))
4858

49-
type CompatForArc4Int<N extends BitSize> = N extends 8 | 16 | 32 | 64 ? Uint64Compat : BigUintCompat
59+
type CompatForArc4Int<N extends BitSize> = N extends 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 ? Uint64Compat : BigUintCompat
60+
const validBitSizes = [...Array(64).keys()].map((x) => (x + 1) * 8)
5061
export class UintNImpl<N extends BitSize> extends UintN<N> {
5162
private value: Uint8Array
5263
private bitSize: N
@@ -57,7 +68,7 @@ export class UintNImpl<N extends BitSize> extends UintN<N> {
5768
this.typeInfo = typeof typeInfo === 'string' ? JSON.parse(typeInfo) : typeInfo
5869
this.bitSize = UintNImpl.getMaxBitsLength(this.typeInfo) as N
5970

60-
assert([8, 16, 32, 64, 128, 256, 512].includes(this.bitSize), `Invalid bit size ${this.bitSize}`)
71+
assert(validBitSizes.includes(this.bitSize), `Invalid bit size ${this.bitSize}`)
6172

6273
const bigIntValue = asBigUintCls(v ?? 0n).valueOf()
6374
const maxValue = maxBigIntValue(this.bitSize)
@@ -299,7 +310,7 @@ const arrayProxyHandler = <TItem>() => ({
299310
get(target: { items: TItem[] }, prop: PropertyKey) {
300311
const idx = prop ? parseInt(prop.toString(), 10) : NaN
301312
if (!isNaN(idx)) {
302-
if (idx < target.items.length) return target.items[idx]
313+
if (idx >= 0 && idx < target.items.length) return target.items[idx]
303314
throw new AvmError('Index out of bounds')
304315
} else if (prop === Symbol.iterator) {
305316
return target.items[Symbol.iterator].bind(target.items)
@@ -315,7 +326,7 @@ const arrayProxyHandler = <TItem>() => ({
315326
set(target: { items: TItem[]; setItem: (index: number, value: TItem) => void }, prop: PropertyKey, value: TItem) {
316327
const idx = prop ? parseInt(prop.toString(), 10) : NaN
317328
if (!isNaN(idx)) {
318-
if (idx < target.items.length) {
329+
if (idx >= 0 && idx < target.items.length) {
319330
target.setItem(idx, value)
320331
return true
321332
}
@@ -391,6 +402,20 @@ export class StaticArrayImpl<TItem extends ARC4Encoded, TLength extends number>
391402
return StaticArrayImpl.fromBytesImpl(this.bytes, JSON.stringify(this.typeInfo)) as StaticArrayImpl<TItem, TLength>
392403
}
393404

405+
concat(other: Parameters<InstanceType<typeof StaticArray>['concat']>[0]): DynamicArrayImpl<TItem> {
406+
const items = this.items
407+
const otherEntries = other.entries()
408+
let next = otherEntries.next()
409+
while (!next.done) {
410+
items.push(next.value[1] as TItem)
411+
next = otherEntries.next()
412+
}
413+
return new DynamicArrayImpl<TItem>(
414+
{ name: `DynamicArray<${this.genericArgs.elementType.name}>`, genericArgs: { elementType: this.genericArgs.elementType } },
415+
...items,
416+
)
417+
}
418+
394419
get native(): TItem[] {
395420
return this.items
396421
}
@@ -578,6 +603,17 @@ export class DynamicArrayImpl<TItem extends ARC4Encoded> extends DynamicArray<TI
578603
return popped
579604
}
580605

606+
concat(other: Parameters<InstanceType<typeof DynamicArray>['concat']>[0]): DynamicArrayImpl<TItem> {
607+
const items = this.items
608+
const otherEntries = other.entries()
609+
let next = otherEntries.next()
610+
while (!next.done) {
611+
items.push(next.value[1] as TItem)
612+
next = otherEntries.next()
613+
}
614+
return new DynamicArrayImpl<TItem>(this.typeInfo, ...items)
615+
}
616+
581617
static fromBytesImpl(
582618
value: StubBytesCompat | Uint8Array,
583619
typeInfo: string | TypeInfo,
@@ -822,6 +858,20 @@ export class DynamicBytesImpl extends DynamicBytes {
822858
throw new CodeError('DynamicBytes is immutable')
823859
}
824860

861+
concat(other: Parameters<InstanceType<typeof DynamicBytes>['concat']>[0]): DynamicBytesImpl {
862+
const items = this.items
863+
const otherEntries = other.entries()
864+
let next = otherEntries.next()
865+
while (!next.done) {
866+
items.push(next.value[1] as ByteImpl)
867+
next = otherEntries.next()
868+
}
869+
const concatenatedBytes = items
870+
.map((item) => item.bytes)
871+
.reduce((acc, curr) => conactUint8Arrays(acc, asUint8Array(curr)), new Uint8Array())
872+
return new DynamicBytesImpl(this.typeInfo, asBytes(concatenatedBytes))
873+
}
874+
825875
static fromBytesImpl(
826876
value: StubBytesCompat | Uint8Array,
827877
typeInfo: string | TypeInfo,
@@ -877,6 +927,20 @@ export class StaticBytesImpl extends StaticBytes {
877927
throw new CodeError('StaticBytes is immutable')
878928
}
879929

930+
concat(other: Parameters<InstanceType<typeof StaticBytes>['concat']>[0]): DynamicBytesImpl {
931+
const items = this.items
932+
const otherEntries = other.entries()
933+
let next = otherEntries.next()
934+
while (!next.done) {
935+
items.push(next.value[1] as ByteImpl)
936+
next = otherEntries.next()
937+
}
938+
const concatenatedBytes = items
939+
.map((item) => item.bytes)
940+
.reduce((acc, curr) => conactUint8Arrays(acc, asUint8Array(curr)), new Uint8Array())
941+
return new DynamicBytesImpl(this.typeInfo, asBytes(concatenatedBytes))
942+
}
943+
880944
static fromBytesImpl(value: StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix: 'none' | 'log' = 'none'): StaticBytesImpl {
881945
const staticArrayValue = StaticArrayImpl.fromBytesImpl(value, typeInfo, prefix) as StaticArrayImpl<ByteImpl, number>
882946
const result = new StaticBytesImpl(typeInfo)

src/impl/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export * from './pure'
1616
export { gloadBytes, gloadUint64, Scratch } from './scratch'
1717
export { gaid, Txn } from './txn'
1818
export { VoterParams } from './voter-params'
19+
export { MutableArray } from './mutable-array'

src/impl/mutable-array.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import type { uint64, Uint64Compat } from '@algorandfoundation/algorand-typescript'
2+
import { AvmError } from '../errors'
3+
import { asNumber } from '../util'
4+
5+
export class MutableArray<TItem> {
6+
private _values: TItem[]
7+
8+
constructor(...items: TItem[]) {
9+
this._values = items
10+
11+
return new Proxy(this, {
12+
get(target, prop: PropertyKey) {
13+
const idx = prop ? parseInt(prop.toString(), 10) : NaN
14+
if (!isNaN(idx)) {
15+
if (idx >= 0 && idx < target._values.length) return target._values[idx]
16+
throw new AvmError('Index out of bounds')
17+
}
18+
return Reflect.get(target, prop)
19+
},
20+
set(target, prop: PropertyKey, value: TItem) {
21+
const idx = prop ? parseInt(prop.toString(), 10) : NaN
22+
if (!isNaN(idx)) {
23+
if (idx >= 0 && idx < target._values.length) {
24+
target._values[idx] = value
25+
return true
26+
}
27+
throw new AvmError('Index out of bounds')
28+
}
29+
30+
return Reflect.set(target, prop, value)
31+
},
32+
})
33+
}
34+
35+
/**
36+
* Returns the current length of this array
37+
*/
38+
get length(): uint64 {
39+
return this._values.length
40+
}
41+
42+
/**
43+
* Returns the item at the given index.
44+
* Negative indexes are taken from the end.
45+
* @param index The index of the item to retrieve
46+
*/
47+
at(index: Uint64Compat): TItem {
48+
return this._values[asNumber(index)]
49+
}
50+
51+
/**
52+
* Create a new Dynamic array with all items from this array
53+
* @internal Not supported yet
54+
*/
55+
slice(): MutableArray<TItem>
56+
/**
57+
* Create a new MutableArray with all items up till `end`.
58+
* Negative indexes are taken from the end.
59+
* @param end An index in which to stop copying items.
60+
* @internal Not supported yet
61+
*/
62+
slice(end: Uint64Compat): MutableArray<TItem>
63+
/**
64+
* Create a new MutableArray with items from `start`, up until `end`
65+
* Negative indexes are taken from the end.
66+
* @param start An index in which to start copying items.
67+
* @param end An index in which to stop copying items
68+
* @internal Not supported yet
69+
*/
70+
slice(start: Uint64Compat, end: Uint64Compat): MutableArray<TItem>
71+
slice(start?: Uint64Compat, end?: Uint64Compat): MutableArray<TItem> {
72+
const startIndex = end === undefined ? 0 : asNumber(start ?? 0)
73+
const endIndex = end === undefined ? asNumber(start ?? this._values.length) : asNumber(end)
74+
return new MutableArray<TItem>(...this._values.slice(startIndex, endIndex))
75+
}
76+
77+
/**
78+
* Returns an iterator for the items in this array
79+
*/
80+
[Symbol.iterator](): IterableIterator<TItem> {
81+
return this._values[Symbol.iterator]()
82+
}
83+
84+
/**
85+
* Returns an iterator for a tuple of the indexes and items in this array
86+
*/
87+
entries(): IterableIterator<readonly [uint64, TItem]> {
88+
return this._values.entries()
89+
}
90+
91+
/**
92+
* Returns an iterator for the indexes in this array
93+
*/
94+
keys(): IterableIterator<uint64> {
95+
return this._values.keys()
96+
}
97+
98+
/**
99+
* Get or set the item at the specified index.
100+
* Negative indexes are not supported
101+
*/
102+
[index: uint64]: TItem
103+
104+
/**
105+
* Push a number of items into this array
106+
* @param items The items to be added to this array
107+
*/
108+
push(...items: TItem[]): void {
109+
this._values.push(...items)
110+
}
111+
112+
/**
113+
* Pop a single item from this array
114+
*/
115+
pop(): TItem {
116+
return this._values.pop()!
117+
}
118+
119+
copy(): MutableArray<TItem> {
120+
return new MutableArray(...this._values)
121+
}
122+
}

src/impl/primitives.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ export class Uint64Cls extends AlgoTsPrimitiveCls {
317317
}
318318
return Number(this.#value)
319319
}
320+
toString(): string {
321+
return this.#value.toString()
322+
}
320323
}
321324

322325
export function BigUintImpl(v?: BigUintCompat | string): biguint {

src/internal/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { ensureBudgetImpl as ensureBudget } from '../impl/ensure-budget'
66
export { Global } from '../impl/global'
77
export { log } from '../impl/log'
88
export { assertMatchImpl as assertMatch, matchImpl as match } from '../impl/match'
9+
export { MutableArray } from '../impl/mutable-array'
910
export { BigUint, Bytes, Uint64 } from '../impl/primitives'
1011
export { Account, Application, Asset } from '../impl/reference'
1112
export { Box, BoxMap, BoxRef, GlobalState, LocalState } from '../impl/state'

0 commit comments

Comments
 (0)