Skip to content

Commit a1dc974

Browse files
committed
Initial fixed array size implementation
#118
1 parent 909f409 commit a1dc974

File tree

4 files changed

+140
-6
lines changed

4 files changed

+140
-6
lines changed

src/chain/abi.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ export namespace ABI {
373373
fields?: {name: string; type: ResolvedType}[]
374374
variant?: ResolvedType[]
375375
ref?: ResolvedType
376+
size?: number // Fixed Size Array
376377

377378
constructor(fullName: string, id = 0) {
378379
let name = fullName
@@ -388,12 +389,20 @@ export namespace ABI {
388389
} else {
389390
this.isOptional = false
390391
}
392+
this.isArray = false
391393
if (name.endsWith('[]')) {
392394
name = name.slice(0, -2)
393395
this.isArray = true
394-
} else {
395-
this.isArray = false
396396
}
397+
398+
const fixedMatch = name.match(/(.*)\[(\d+)\]/)
399+
if (fixedMatch) {
400+
const [, fixedName, fixedSize] = fixedMatch
401+
name = fixedName
402+
this.isArray = true
403+
this.size = Number(fixedSize)
404+
}
405+
397406
this.id = id
398407
this.name = name
399408
}
@@ -404,7 +413,11 @@ export namespace ABI {
404413
get typeName(): string {
405414
let rv = this.name
406415
if (this.isArray) {
407-
rv += '[]'
416+
if (this.size) {
417+
rv += `[${this.size}]`
418+
} else {
419+
rv += '[]'
420+
}
408421
}
409422
if (this.isOptional) {
410423
rv += '?'

src/serializer/encoder.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ export function encodeAny(value: any, type: ABI.ResolvedType, ctx: EncodingConte
159159
throw new Error(`Expected array for: ${type.typeName}`)
160160
}
161161
const len = value.length
162-
ctx.encoder.writeVaruint32(len)
162+
if (!type.size) {
163+
ctx.encoder.writeVaruint32(len)
164+
}
163165
for (let i = 0; i < len; i++) {
164166
ctx.codingPath.push({field: i, type})
165167
encodeInner(value[i])

src/serializer/serializable.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export interface ABITypeModifiers {
2828
optional?: boolean
2929
/** Type is an array, defaults to false. */
3030
array?: boolean
31+
/** Type has a fixed array size, defaults to undefined */
32+
size?: number
3133
/** Type is a binary extension, defaults to false. */
3234
extension?: boolean
3335
}
@@ -93,7 +95,11 @@ export function synthesizeABI(type: ABISerializableConstructor) {
9395
typeName = t.type
9496
}
9597
if (t.array === true) {
96-
typeName += '[]'
98+
if (t.size) {
99+
typeName += `[${t.size}]`
100+
} else {
101+
typeName += '[]'
102+
}
97103
}
98104
if (t.optional === true) {
99105
typeName += '?'
@@ -147,7 +153,11 @@ export function synthesizeABI(type: ABISerializableConstructor) {
147153
export function abiTypeString(type: ABITypeDescriptor) {
148154
let typeName = typeof type.type === 'string' ? type.type : type.type.abiName
149155
if (type.array === true) {
150-
typeName += '[]'
156+
if (type.size) {
157+
typeName += `[${type.size}]`
158+
} else {
159+
typeName += '[]'
160+
}
151161
}
152162
if (type.optional === true) {
153163
typeName += '?'

test/chain.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,4 +756,113 @@ suite('chain', function () {
756756
})
757757
assert.instanceOf(compressedSuccess.getTransaction(), Transaction)
758758
})
759+
760+
test('fixed size array', function () {
761+
const data = {
762+
version: 'eosio::abi/1.2',
763+
types: [],
764+
structs: [
765+
{
766+
name: 'basic',
767+
base: '',
768+
fields: [
769+
{
770+
name: 'input',
771+
type: 'int32',
772+
},
773+
],
774+
},
775+
{
776+
name: 'array',
777+
base: '',
778+
fields: [
779+
{
780+
name: 'input',
781+
type: 'int32[]',
782+
},
783+
],
784+
},
785+
{
786+
name: 'fixed',
787+
base: '',
788+
fields: [
789+
{
790+
name: 'input',
791+
type: 'int32[4]',
792+
},
793+
],
794+
},
795+
],
796+
actions: [
797+
{
798+
name: 'basic',
799+
type: 'basic',
800+
ricardian_contract: '',
801+
},
802+
{
803+
name: 'array',
804+
type: 'array',
805+
ricardian_contract: '',
806+
},
807+
{
808+
name: 'fixed',
809+
type: 'fixed',
810+
ricardian_contract: '',
811+
},
812+
],
813+
tables: [],
814+
ricardian_clauses: [],
815+
error_messages: [],
816+
abi_extensions: [],
817+
variants: [],
818+
action_results: [
819+
{
820+
name: 'basic',
821+
result_type: 'int32',
822+
},
823+
{
824+
name: 'array',
825+
result_type: 'int32[]',
826+
},
827+
{
828+
name: 'fixed',
829+
result_type: 'int32[4]',
830+
},
831+
],
832+
}
833+
834+
const abi = ABI.from(data)
835+
assert.isTrue(abi.equals(data))
836+
assert.equal(abi.structs[0].fields[0].type, 'int32')
837+
assert.equal(abi.structs[1].fields[0].type, 'int32[]')
838+
assert.equal(abi.structs[2].fields[0].type, 'int32[4]')
839+
assert.equal(abi.action_results[0].result_type, 'int32')
840+
assert.equal(abi.action_results[1].result_type, 'int32[]')
841+
assert.equal(abi.action_results[2].result_type, 'int32[4]')
842+
843+
const encoded = Serializer.encode({object: abi})
844+
const decoded = Serializer.decode({data: encoded, type: ABI})
845+
assert.isTrue(decoded.equals(data))
846+
assert.equal(decoded.structs[0].fields[0].type, 'int32')
847+
assert.equal(decoded.structs[1].fields[0].type, 'int32[]')
848+
assert.equal(decoded.structs[2].fields[0].type, 'int32[4]')
849+
assert.equal(decoded.action_results[0].result_type, 'int32')
850+
assert.equal(decoded.action_results[1].result_type, 'int32[]')
851+
assert.equal(decoded.action_results[2].result_type, 'int32[4]')
852+
853+
assert.equal(
854+
'01000000',
855+
Serializer.encode({object: {input: 1}, abi, type: 'basic'}).hexString
856+
)
857+
858+
assert.equal(
859+
'0401000000020000000300000004000000',
860+
Serializer.encode({object: {input: [1, 2, 3, 4]}, abi, type: 'array'}).hexString
861+
)
862+
863+
assert.equal(
864+
'01000000020000000300000004000000',
865+
Serializer.encode({object: {input: [1, 2, 3, 4]}, abi, type: 'fixed'}).hexString
866+
)
867+
})
759868
})

0 commit comments

Comments
 (0)