From c5f467dce19c3b35de76c9ac5e3f81eeb33904c6 Mon Sep 17 00:00:00 2001 From: Jaco Date: Fri, 27 May 2022 09:01:11 +0200 Subject: [PATCH] Remove some internal Array allocations (#4858) * Handle Vec assignment inline * mappings * Adjust * Adjust * Cleanup build * Fix BTreeSet build * Adjust * hasNoDupes adjustment * Adjust PortableRegistry extractName --- .../types-codec/src/abstract/AbstractArray.ts | 12 +-- packages/types-codec/src/base/Enum.ts | 21 ++--- packages/types-codec/src/base/Tuple.ts | 86 +++++++++---------- packages/types-codec/src/base/Vec.ts | 52 ++++++----- packages/types-codec/src/base/VecFixed.ts | 30 +++---- packages/types-codec/src/extended/BTreeSet.ts | 8 +- packages/types-codec/src/extended/Map.ts | 2 +- packages/types-codec/src/native/Struct.ts | 51 ++++++----- packages/types-codec/src/types/codec.ts | 2 - packages/types-codec/src/utils/decodeU8a.ts | 24 +++--- .../types-codec/src/utils/mapToTypeMap.ts | 8 +- packages/types-codec/src/utils/typesToMap.ts | 9 +- .../PortableRegistry/PortableRegistry.ts | 46 ++++++---- 13 files changed, 166 insertions(+), 185 deletions(-) diff --git a/packages/types-codec/src/abstract/AbstractArray.ts b/packages/types-codec/src/abstract/AbstractArray.ts index f595a738ec..db92a7cc79 100644 --- a/packages/types-codec/src/abstract/AbstractArray.ts +++ b/packages/types-codec/src/abstract/AbstractArray.ts @@ -20,18 +20,10 @@ export abstract class AbstractArray extends Array implements public createdAtHash?: IU8a; - readonly initialU8aLength?: number; - - protected constructor (registry: Registry, values: T[], initialU8aLength?: number) { - super(values.length); - - // explicitly set the values here - this removes the need for any extra allocations - for (let i = 0; i < values.length; i++) { - this[i] = values[i]; - } + protected constructor (registry: Registry, length: number) { + super(length); this.registry = registry; - this.initialU8aLength = initialU8aLength; } /** diff --git a/packages/types-codec/src/base/Enum.ts b/packages/types-codec/src/base/Enum.ts index 06af6ffc63..d9aac53afc 100644 --- a/packages/types-codec/src/base/Enum.ts +++ b/packages/types-codec/src/base/Enum.ts @@ -66,12 +66,10 @@ function extractDef (registry: Registry, _def: Record Type !== Null); @@ -403,15 +401,14 @@ export class Enum implements IEnum { : this.defKeys; } - const typeMap = Object - .entries(this.#def) - .reduce((out: Record, [key, { Type }]) => { - out[key] = Type; + const entries = Object.entries(this.#def); - return out; - }, {}); + return typesToMap(this.registry, entries.reduce<[CodecClass[], string[]]>((out, [key, { Type }], i) => { + out[0][i] = Type; + out[1][i] = key; - return typesToMap(this.registry, typeMap); + return out; + }, [new Array(entries.length), new Array(entries.length)])); } /** diff --git a/packages/types-codec/src/base/Tuple.ts b/packages/types-codec/src/base/Tuple.ts index e807668eb4..1ac971c213 100644 --- a/packages/types-codec/src/base/Tuple.ts +++ b/packages/types-codec/src/base/Tuple.ts @@ -8,51 +8,44 @@ import { isFunction, isHex, isString, isU8a, stringify, u8aConcat, u8aToU8a } fr import { AbstractArray } from '../abstract/AbstractArray'; import { decodeU8a, mapToTypeMap, typeToConstructor } from '../utils'; -type TupleCodecClass = CodecClass[] | { - [index: string]: CodecClass; -}; - type TupleType = (CodecClass | string); type TupleTypes = TupleType[] | { [index: string]: CodecClass | string; }; +type Definition = [CodecClass[], string[]]; + interface Options { - definition?: TupleCodecClass; - setDefinition?: (d: TupleCodecClass) => TupleCodecClass; + definition?: Definition; + setDefinition?: (d: Definition) => Definition; } -function noopSetDefinition (d: TupleCodecClass): TupleCodecClass { +function noopSetDefinition (d: Definition): Definition { return d; } /** @internal */ -function decodeTuple (registry: Registry, Classes: TupleCodecClass, value?: Exclude): [Codec[], number] { +function decodeTuple (registry: Registry, result: Codec[], Classes: Definition, value?: Exclude): [Codec[], number] { if (isU8a(value) || isHex(value)) { - return decodeU8a(registry, u8aToU8a(value), Classes); + return decodeU8a(registry, result, u8aToU8a(value), Classes); } - const Types: CodecClass[] = Array.isArray(Classes) - ? Classes - : Object.values(Classes); + const Types = Classes[0]; - return [ - Types.map((Type, index): Codec => { - try { - const entry = value?.[index]; + for (let i = 0; i < Types.length; i++) { + try { + const entry = value?.[i]; - if (entry instanceof Type) { - return entry; - } + result[i] = entry instanceof Types[i] + ? entry + : new Types[i](registry, entry); + } catch (error) { + throw new Error(`Tuple: failed on ${i}:: ${(error as Error).message}`); + } + } - return new Type(registry, entry); - } catch (error) { - throw new Error(`Tuple: failed on ${index}:: ${(error as Error).message}`); - } - }), - 0 - ]; + return [result, 0]; } /** @@ -62,29 +55,34 @@ function decodeTuple (registry: Registry, Classes: TupleCodecClass, value?: Excl * own type. It extends the base JS `Array` object. */ export class Tuple extends AbstractArray implements ITuple { - #Types: TupleCodecClass; + readonly initialU8aLength?: number; + + #Types: Definition; constructor (registry: Registry, Types: TupleTypes | TupleType, value?: AnyTupleValue, { definition, setDefinition = noopSetDefinition }: Options = {}) { - const Classes = definition || setDefinition(Array.isArray(Types) - ? Types.map((t) => typeToConstructor(registry, t)) - : isFunction(Types) || isString(Types) - ? [typeToConstructor(registry, Types)] - : mapToTypeMap(registry, Types) + const Classes = definition || setDefinition( + Array.isArray(Types) + ? [Types.map((t) => typeToConstructor(registry, t)), []] + : isFunction(Types) || isString(Types) + ? [[typeToConstructor(registry, Types)], []] + : mapToTypeMap(registry, Types) ); - const [values, decodedLength] = isU8a(value) - ? decodeU8a(registry, value, Classes) - : decodeTuple(registry, Classes, value); - super(registry, values, decodedLength); + super(registry, Classes[0].length); + const [, decodedLength] = isU8a(value) + ? decodeU8a(registry, this, value, Classes, false) + : decodeTuple(registry, this, Classes, value); + + this.initialU8aLength = decodedLength; this.#Types = Classes; } public static with (Types: TupleTypes | TupleType): CodecClass { - let definition: TupleCodecClass | undefined; + let definition: Definition | undefined; // eslint-disable-next-line no-return-assign - const setDefinition = (d: TupleCodecClass) => + const setDefinition = (d: Definition) => definition = d; return class extends Tuple { @@ -111,9 +109,9 @@ export class Tuple extends AbstractArray implements ITuple { * @description The types definition of the tuple */ public get Types (): string[] { - return Array.isArray(this.#Types) - ? this.#Types.map((T) => new T(this.registry).toRawType()) - : Object.keys(this.#Types); + return this.#Types[1].length + ? this.#Types[1] + : this.#Types[0].map((T) => new T(this.registry).toRawType()); } /** @@ -129,11 +127,7 @@ export class Tuple extends AbstractArray implements ITuple { * @description Returns the base runtime type name for this instance */ public toRawType (): string { - const types = ( - Array.isArray(this.#Types) - ? this.#Types - : Object.values(this.#Types) - ).map((T) => + const types = this.#Types[0].map((T) => this.registry.getClassName(T) || new T(this.registry).toRawType() ); diff --git a/packages/types-codec/src/base/Vec.ts b/packages/types-codec/src/base/Vec.ts index d5df613def..1395eb0413 100644 --- a/packages/types-codec/src/base/Vec.ts +++ b/packages/types-codec/src/base/Vec.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { Codec, CodecClass, Registry } from '../types'; -import { assert, compactFromU8a, logger, u8aToU8a } from '@polkadot/util'; +import { assert, compactFromU8a, isU8a, logger, u8aToU8a } from '@polkadot/util'; import { AbstractArray } from '../abstract/AbstractArray'; import { decodeU8aVec, typeToConstructor } from '../utils'; @@ -22,11 +22,24 @@ function noopSetDefinition (d: CodecClass): CodecClass { return d; } -export function decodeVec (registry: Registry, Type: CodecClass, value: Uint8Array | HexString | unknown[], length = -1): [T[], number, number] { +function decodeVecLength (value: Uint8Array | HexString | unknown[]): [Uint8Array | unknown[], number, number] { if (Array.isArray(value)) { - const result = new Array(value.length); + return [value, value.length, 0]; + } - for (let i = 0; i < value.length; i++) { + const u8a = u8aToU8a(value); + const [startAt, length] = compactFromU8a(u8a); + + assert(length.lten(MAX_LENGTH), () => `Vec length ${length.toString()} exceeds ${MAX_LENGTH}`); + + return [u8a, length.toNumber(), startAt]; +} + +export function decodeVec (registry: Registry, result: T[], value: Uint8Array | HexString | unknown[], startAt: number, Type: CodecClass): [number, number] { + if (Array.isArray(value)) { + const count = result.length; + + for (let i = 0; i < count; i++) { const entry = value[i]; try { @@ -40,22 +53,10 @@ export function decodeVec (registry: Registry, Type: CodecClass } } - return [result, 0, 0]; + return [0, 0]; } - const u8a = u8aToU8a(value); - let offset = 0; - - if (length === -1) { - const [_offset, _length] = compactFromU8a(u8a); - - assert(_length.lten(MAX_LENGTH), () => `Vec length ${_length.toString()} exceeds ${MAX_LENGTH}`); - - length = _length.toNumber(); - offset = _offset; - } - - return decodeU8aVec(registry, u8a, offset, Type, length); + return decodeU8aVec(registry, result, u8aToU8a(value), startAt, Type); } /** @@ -66,15 +67,22 @@ export function decodeVec (registry: Registry, Type: CodecClass * specific encoding/decoding on top of the base type. */ export class Vec extends AbstractArray { + readonly initialU8aLength?: number; + #Type: CodecClass; constructor (registry: Registry, Type: CodecClass | string, value: Uint8Array | HexString | unknown[] = [], { definition, setDefinition = noopSetDefinition }: Options = {}) { - const Clazz = definition || setDefinition(typeToConstructor(registry, Type)); - const [values, decodedLength] = decodeVec(registry, Clazz, value); + const [decodeFrom, length, startAt] = decodeVecLength(value); - super(registry, values, decodedLength); + super(registry, length); - this.#Type = Clazz; + this.#Type = definition || setDefinition(typeToConstructor(registry, Type)); + + const [decodedLength] = isU8a(decodeFrom) + ? decodeU8aVec(registry, this, decodeFrom, startAt, this.#Type) + : decodeVec(registry, this, decodeFrom, startAt, this.#Type); + + this.initialU8aLength = decodedLength; } public static with (Type: CodecClass | string): CodecClass> { diff --git a/packages/types-codec/src/base/VecFixed.ts b/packages/types-codec/src/base/VecFixed.ts index 8c01b0a343..0bbb60c157 100644 --- a/packages/types-codec/src/base/VecFixed.ts +++ b/packages/types-codec/src/base/VecFixed.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { Codec, CodecClass, Inspect, Registry } from '../types'; -import { assert, isU8a, u8aConcat } from '@polkadot/util'; +import { isU8a, u8aConcat } from '@polkadot/util'; import { AbstractArray } from '../abstract/AbstractArray'; import { decodeU8aVec, typeToConstructor } from '../utils'; @@ -19,36 +19,26 @@ function noopSetDefinition (d: CodecClass): CodecClass { return d; } -/** @internal */ -function decodeVecFixed (registry: Registry, value: HexString | unknown[], Type: CodecClass, length: number): [T[], number, number] { - const [values, decodedLength, decodedLengthNoOffset] = decodeVec(registry, Type, value, length); - - while (values.length < length) { - values.push(new Type(registry)); - } - - assert(values.length === length, () => `Expected a length of exactly ${length} entries`); - - return [values, decodedLength, decodedLengthNoOffset]; -} - /** * @name VecFixed * @description * This manages codec arrays of a fixed length */ export class VecFixed extends AbstractArray { + readonly initialU8aLength?: number; + #Type: CodecClass; constructor (registry: Registry, Type: CodecClass | string, length: number, value: Uint8Array | HexString | unknown[] = [] as unknown[], { definition, setDefinition = noopSetDefinition }: Options = {}) { - const Clazz = definition || setDefinition(typeToConstructor(registry, Type)); - const [values,, decodedLengthNoOffset] = isU8a(value) - ? decodeU8aVec(registry, value, 0, Clazz, length) - : decodeVecFixed(registry, value, Clazz, length); + super(registry, length); - super(registry, values, decodedLengthNoOffset); + this.#Type = definition || setDefinition(typeToConstructor(registry, Type)); - this.#Type = Clazz; + const [, decodedLengthNoOffset] = isU8a(value) + ? decodeU8aVec(registry, this, value, 0, this.#Type) + : decodeVec(registry, this, value, 0, this.#Type); + + this.initialU8aLength = decodedLengthNoOffset; } public static with (Type: CodecClass | string, length: number): CodecClass> { diff --git a/packages/types-codec/src/extended/BTreeSet.ts b/packages/types-codec/src/extended/BTreeSet.ts index fb4184e80b..9dce1d4734 100644 --- a/packages/types-codec/src/extended/BTreeSet.ts +++ b/packages/types-codec/src/extended/BTreeSet.ts @@ -14,10 +14,12 @@ const l = logger('BTreeSet'); function decodeSetFromU8a (registry: Registry, ValClass: CodecClass, u8a: Uint8Array): [CodecClass, Set, number] { const output = new Set(); const [offset, length] = compactFromU8a(u8a); - const [values, decodedLength] = decodeU8aVec(registry, u8a, offset, ValClass, length.toNumber()); + const count = length.toNumber(); + const result = new Array(count); + const [decodedLength] = decodeU8aVec(registry, result, u8a, offset, ValClass); - for (let i = 0; i < values.length; i++) { - output.add(values[i]); + for (let i = 0; i < count; i++) { + output.add(result[i] as unknown as V); } return [ValClass, output, decodedLength]; diff --git a/packages/types-codec/src/extended/Map.ts b/packages/types-codec/src/extended/Map.ts index 1f38a2f205..126ca5741f 100644 --- a/packages/types-codec/src/extended/Map.ts +++ b/packages/types-codec/src/extended/Map.ts @@ -24,7 +24,7 @@ function decodeMapFromU8a (registry: Registry, types.push(KeyClass, ValClass); } - const [values, decodedLength] = decodeU8a(registry, u8a.subarray(offset), types); + const [values, decodedLength] = decodeU8a(registry, new Array(types.length), u8a.subarray(offset), [types, []]); for (let i = 0; i < values.length; i += 2) { output.set(values[i] as K, values[i + 1] as V); diff --git a/packages/types-codec/src/native/Struct.ts b/packages/types-codec/src/native/Struct.ts index 8204bd1405..58d5a8479d 100644 --- a/packages/types-codec/src/native/Struct.ts +++ b/packages/types-codec/src/native/Struct.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { HexString } from '@polkadot/util/types'; -import type { AnyJson, BareOpts, Codec, CodecClass, CodecClassDef, Inspect, IStruct, IU8a, Registry } from '../types'; +import type { AnyJson, BareOpts, Codec, CodecClass, Inspect, IStruct, IU8a, Registry } from '../types'; import { assert, isBoolean, isFunction, isHex, isObject, isU8a, isUndefined, objectProperties, stringCamelCase, stringify, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util'; @@ -10,31 +10,32 @@ import { compareMap, decodeU8a, mapToTypeMap, typesToMap } from '../utils'; type TypesDef = Record>; +type Definition = [CodecClass[], string[]]; + interface Options { - definition?: CodecClassDef; - setDefinition?: (d: CodecClassDef) => CodecClassDef; + definition?: Definition; + setDefinition?: (d: Definition) => Definition; } -function noopSetDefinition (d: CodecClassDef): CodecClassDef { +function noopSetDefinition (d: Definition): Definition { return d; } /** @internal */ -function decodeStructFromObject (registry: Registry, Types: CodecClassDef, value: any, jsonMap: Map): [Iterable<[string, Codec]>, number] { +function decodeStructFromObject (registry: Registry, [Types, keys]: Definition, value: any, jsonMap: Map): [Iterable<[string, Codec]>, number] { let jsonObj: Record | undefined; - const inputKeys = Object.keys(Types); const typeofArray = Array.isArray(value); const typeofMap = value instanceof Map; assert(typeofArray || typeofMap || isObject(value), () => `Struct: Cannot decode value ${stringify(value)} (typeof ${typeof value}), expected an input object, map or array`); - assert(!typeofArray || value.length === inputKeys.length, () => `Struct: Unable to map ${stringify(value)} array to object with known keys ${inputKeys.join(', ')}`); + assert(!typeofArray || value.length === keys.length, () => `Struct: Unable to map ${stringify(value)} array to object with known keys ${keys.join(', ')}`); - const raw = new Array<[string, Codec]>(inputKeys.length); + const raw = new Array<[string, Codec]>(keys.length); - for (let i = 0; i < inputKeys.length; i++) { - const key = inputKeys[i]; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; const jsonKey = jsonMap.get(key) || key; - const Type = Types[key]; + const Type = Types[i]; let assign: unknown; try { @@ -107,17 +108,15 @@ export class Struct< readonly #jsonMap: Map; - readonly #Types: CodecClassDef; + readonly #Types: Definition; constructor (registry: Registry, Types: S, value?: V | Map | unknown[] | HexString | null, jsonMap = new Map(), { definition, setDefinition = noopSetDefinition }: Options = {}) { const typeMap = definition || setDefinition(mapToTypeMap(registry, Types)); - const [decoded, decodedLength] = isU8a(value) - ? decodeU8a(registry, value, typeMap, true) - : isHex(value) - ? decodeU8a(registry, u8aToU8a(value), typeMap, true) - : value instanceof Struct - ? [value as Iterable<[string, Codec]>, 0] - : decodeStructFromObject(registry, typeMap, value || {}, jsonMap); + const [decoded, decodedLength] = isU8a(value) || isHex(value) + ? decodeU8a(registry, new Array(typeMap[0].length), u8aToU8a(value), typeMap, true) + : value instanceof Struct + ? [value as Iterable<[string, Codec]>, 0] + : decodeStructFromObject(registry, typeMap, value || {}, jsonMap); super(decoded); @@ -130,10 +129,10 @@ export class Struct< public static with (Types: S, jsonMap?: Map): CodecClass> { const keys = Object.keys(Types); - let definition: CodecClassDef | undefined; + let definition: Definition | undefined; // eslint-disable-next-line no-return-assign - const setDefinition = (d: CodecClassDef) => + const setDefinition = (d: Definition) => definition = d; return class extends Struct { @@ -149,7 +148,7 @@ export class Struct< * @description The available keys for this struct */ public get defKeys (): string[] { - return Object.keys(this.#Types); + return this.#Types[1]; } public getT (key: string): T { @@ -174,12 +173,10 @@ export class Struct< */ public get Type (): E { const result: Record = {}; - const defs = Object.entries(this.#Types); + const [Types, keys] = this.#Types; - for (let i = 0; i < defs.length; i++) { - const [key, Type] = defs[i]; - - result[key] = new Type(this.registry).toRawType(); + for (let i = 0; i < keys.length; i++) { + result[keys[i]] = new Types[i](this.registry).toRawType(); } return result as E; diff --git a/packages/types-codec/src/types/codec.ts b/packages/types-codec/src/types/codec.ts index b0b30d0828..213519cb31 100644 --- a/packages/types-codec/src/types/codec.ts +++ b/packages/types-codec/src/types/codec.ts @@ -105,8 +105,6 @@ export interface CodecClass { new(registry: Registry, ...args: any[]): T; } -export type CodecClassDef = Record>; - export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a'; export type ArgsDef = Record; diff --git a/packages/types-codec/src/utils/decodeU8a.ts b/packages/types-codec/src/utils/decodeU8a.ts index 09c4380a32..14aa6a8057 100644 --- a/packages/types-codec/src/utils/decodeU8a.ts +++ b/packages/types-codec/src/utils/decodeU8a.ts @@ -25,22 +25,18 @@ function getRawType (registry: Registry, Type: CodecClass): string | null { * @param result - The result array (will be returned with values pushed) * @param types - The array of CodecClass to decode the U8a against. */ -export function decodeU8a (registry: Registry, u8a: Uint8Array, types: CodecClass[] | { [index: string]: CodecClass }, withZip?: boolean): [E[], number] { - const [Types, keys]: [CodecClass[], string[]] = Array.isArray(types) - ? [types, []] - : [Object.values(types), Object.keys(types)]; - - const result = new Array(Types.length); +export function decodeU8a (registry: Registry, result: unknown[], u8a: Uint8Array, [Types, keys]: [CodecClass[], string[]], withZip?: boolean): [E[], number] { + const count = result.length; let offset = 0; - for (let i = 0; i < Types.length; i++) { + for (let i = 0; i < count; i++) { try { const value = new Types[i](registry, u8a.subarray(offset)); offset += value.initialU8aLength || value.encodedLength; result[i] = withZip - ? [keys[i], value] as unknown as E - : value as unknown as E; + ? [keys[i], value] + : value; } catch (error) { throw new Error(formatFailure( error as Error, @@ -51,14 +47,14 @@ export function decodeU8a (registry: Registry, } } - return [result, offset]; + return [result as E[], offset]; } // Split from decodeU8a since this is specialized to 1 instance ... yes duplication, but // since we have to do less checks (and these are intensive anyway), much faster -export function decodeU8aVec (registry: Registry, u8a: Uint8Array, start: number, Type: CodecClass, count: number): [T[], number, number] { - const result = new Array(count); - let offset = start; +export function decodeU8aVec (registry: Registry, result: unknown[], u8a: Uint8Array, startAt: number, Type: CodecClass): [number, number] { + const count = result.length; + let offset = startAt; for (let i = 0; i < count; i++) { try { @@ -75,5 +71,5 @@ export function decodeU8aVec (registry: Registry, u8a: } } - return [result, offset, offset - start]; + return [offset, offset - startAt]; } diff --git a/packages/types-codec/src/utils/mapToTypeMap.ts b/packages/types-codec/src/utils/mapToTypeMap.ts index 6468ffcc13..1063dca9e7 100644 --- a/packages/types-codec/src/utils/mapToTypeMap.ts +++ b/packages/types-codec/src/utils/mapToTypeMap.ts @@ -8,12 +8,14 @@ import { typeToConstructor } from './typeToConstructor'; /** * @description takes an input map of the form `{ [string]: string | CodecClass }` and returns a map of `{ [string]: CodecClass }` */ -export function mapToTypeMap (registry: Registry, input: Record): Record { +export function mapToTypeMap (registry: Registry, input: Record): [CodecClass[], string[]] { const entries = Object.entries(input); - const output: Record = {}; + const count = entries.length; + const output: [CodecClass[], string[]] = [new Array(count), new Array(count)]; for (let i = 0; i < entries.length; i++) { - output[entries[i][0]] = typeToConstructor(registry, entries[i][1]); + output[1][i] = entries[i][0]; + output[0][i] = typeToConstructor(registry, entries[i][1]); } return output; diff --git a/packages/types-codec/src/utils/typesToMap.ts b/packages/types-codec/src/utils/typesToMap.ts index 2e8150f46d..be3ac1e3ff 100644 --- a/packages/types-codec/src/utils/typesToMap.ts +++ b/packages/types-codec/src/utils/typesToMap.ts @@ -3,14 +3,11 @@ import type { CodecClass, Registry } from '../types'; -export function typesToMap (registry: Registry, Types: Record): Record { - const entries = Object.entries(Types); +export function typesToMap (registry: Registry, [Types, keys]: [CodecClass[], string[]]): Record { const result: Record = {}; - for (let i = 0; i < entries.length; i++) { - const [key, Type] = entries[i]; - - result[key] = registry.getClassName(Type) || new Type(registry).toRawType(); + for (let i = 0; i < keys.length; i++) { + result[keys[i]] = registry.getClassName(Types[i]) || new Types[i](registry).toRawType(); } return result; diff --git a/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts b/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts index 0ea630e4ed..2b81fdb073 100644 --- a/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts +++ b/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts @@ -117,11 +117,13 @@ function getAliasPath (path: SiPath): string | null { : null; } -function hasNoDupes (input: [number, string, SiTypeParameter[]][]): boolean { - for (let i = 0; i < input.length; i++) { +function hasNoDupes (input: [number, string][]): boolean { + const count = input.length; + + for (let i = 0; i < count; i++) { const [ai, an] = input[i]; - for (let j = 0; j < input.length; j++) { + for (let j = i + 1; j < count; j++) { const [bi, bn] = input[j]; // if the indexes are not the same and the names match, we have a dupe @@ -177,7 +179,7 @@ function removeDuplicateNames (lookup: PortableRegistry, names: [number, string } // see if using the param type helps - const adjusted = new Array<[number, string, SiTypeParameter[]]>(allSame.length); + const adjusted = new Array<[number, string]>(allSame.length); for (let i = 0; i < allSame.length; i++) { const [oIndex, oName, oParams] = allSame[i]; @@ -191,8 +193,7 @@ function removeDuplicateNames (lookup: PortableRegistry, names: [number, string oIndex, def.isPrimitive ? `${oName}${def.asPrimitive.toString()}` - : `${oName}${path[path.length - 1].toString()}`, - params + : `${oName}${path[path.length - 1].toString()}` ]; } @@ -217,29 +218,36 @@ function removeDuplicateNames (lookup: PortableRegistry, names: [number, string } function extractName (types: PortableType[], { id, type: { params, path } }: PortableType): [number, string, SiTypeParameter[]] | null { + const last = path.length - 1; + // if we have no path or determined as a wrapper, we just skip it - if (!path.length || WRAPPERS.includes(path[path.length - 1].toString())) { + if (last === -1 || WRAPPERS.includes(path[last].toString())) { return null; } - const parts = path - .map((p) => stringPascalCase(p)) - .filter((p, index) => { - const lower = p.toLowerCase(); + const parts: string[] = []; + let typeName = ''; - return ( + for (let i = 0; i <= last; i++) { + const p = stringPascalCase(path[i]); + const l = p.toLowerCase(); + + if ( + ( // Remove ::{generic, misc, pallet, traits, types}:: - index !== 1 || - !PATH_RM_INDEX_1.includes(lower) + i !== 1 || + !PATH_RM_INDEX_1.includes(l) ) && ( // sp_runtime::generic::digest::Digest -> sp_runtime::generic::Digest // sp_runtime::multiaddress::MultiAddress -> sp_runtime::MultiAddress - index === path.length - 1 || - lower !== path[index + 1].toLowerCase() - ); - }); - let typeName = parts.join(''); + i === last || + l !== path[i + 1].toLowerCase() + )) { + parts.push(p); + typeName += p; + } + } // do magic for RawOrigin lookup, e.g. pallet_collective::RawOrigin if (parts.length === 2 && parts[1] === 'RawOrigin' && params.length === 2 && params[1].type.isSome) {