Remove some internal Array allocations (#4858)

* Handle Vec assignment inline

* mappings

* Adjust

* Adjust

* Cleanup build

* Fix BTreeSet build

* Adjust

* hasNoDupes adjustment

* Adjust PortableRegistry extractName
This commit is contained in:
Jaco
2022-05-27 10:01:11 +03:00
committed by GitHub
parent c8ce65f079
commit c5f467dce1
13 changed files with 166 additions and 185 deletions
@@ -20,18 +20,10 @@ export abstract class AbstractArray<T extends Codec> extends Array<T> 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;
}
/**
+9 -12
View File
@@ -66,12 +66,10 @@ function extractDef (registry: Registry, _def: Record<string, string | CodecClas
isBasic = true;
isIndexed = false;
} else if (isRustEnum(_def)) {
const entries = Object.entries(mapToTypeMap(registry, _def));
const [Types, keys] = mapToTypeMap(registry, _def);
for (let i = 0; i < entries.length; i++) {
const [key, Type] = entries[i];
def[key] = { Type, index: i };
for (let i = 0; i < keys.length; i++) {
def[keys[i]] = { Type: Types[i], index: i };
}
isBasic = !Object.values(def).some(({ Type }) => Type !== Null);
@@ -403,15 +401,14 @@ export class Enum implements IEnum {
: this.defKeys;
}
const typeMap = Object
.entries(this.#def)
.reduce((out: Record<string, CodecClass>, [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<CodecClass>(entries.length), new Array<string>(entries.length)]));
}
/**
+40 -46
View File
@@ -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<AnyTupleValue, Uint8Array>): [Codec[], number] {
function decodeTuple (registry: Registry, result: Codec[], Classes: Definition, value?: Exclude<AnyTupleValue, Uint8Array>): [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<Codec> implements ITuple<Codec[]> {
#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<Tuple> {
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<Codec> implements ITuple<Codec[]> {
* @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<Codec> implements ITuple<Codec[]> {
* @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()
);
+30 -22
View File
@@ -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 <T extends Codec> (d: CodecClass<T>): CodecClass<T> {
return d;
}
export function decodeVec<T extends Codec> (registry: Registry, Type: CodecClass<T>, 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<T>(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<T extends Codec> (registry: Registry, result: T[], value: Uint8Array | HexString | unknown[], startAt: number, Type: CodecClass<T>): [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<T extends Codec> (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<T extends Codec> (registry: Registry, Type: CodecClass
* specific encoding/decoding on top of the base type.
*/
export class Vec<T extends Codec> extends AbstractArray<T> {
readonly initialU8aLength?: number;
#Type: CodecClass<T>;
constructor (registry: Registry, Type: CodecClass<T> | string, value: Uint8Array | HexString | unknown[] = [], { definition, setDefinition = noopSetDefinition }: Options<T> = {}) {
const Clazz = definition || setDefinition(typeToConstructor<T>(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<T>(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<O extends Codec> (Type: CodecClass<O> | string): CodecClass<Vec<O>> {
+10 -20
View File
@@ -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 <T extends Codec> (d: CodecClass<T>): CodecClass<T> {
return d;
}
/** @internal */
function decodeVecFixed<T extends Codec> (registry: Registry, value: HexString | unknown[], Type: CodecClass<T>, 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<T extends Codec> extends AbstractArray<T> {
readonly initialU8aLength?: number;
#Type: CodecClass<T>;
constructor (registry: Registry, Type: CodecClass<T> | string, length: number, value: Uint8Array | HexString | unknown[] = [] as unknown[], { definition, setDefinition = noopSetDefinition }: Options<T> = {}) {
const Clazz = definition || setDefinition(typeToConstructor<T>(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<T>(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<O extends Codec> (Type: CodecClass<O> | string, length: number): CodecClass<VecFixed<O>> {
@@ -14,10 +14,12 @@ const l = logger('BTreeSet');
function decodeSetFromU8a<V extends Codec> (registry: Registry, ValClass: CodecClass<V>, u8a: Uint8Array): [CodecClass<V>, Set<V>, number] {
const output = new Set<V>();
const [offset, length] = compactFromU8a(u8a);
const [values, decodedLength] = decodeU8aVec(registry, u8a, offset, ValClass, length.toNumber());
const count = length.toNumber();
const result = new Array<V>(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];
+1 -1
View File
@@ -24,7 +24,7 @@ function decodeMapFromU8a<K extends Codec, V extends Codec> (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);
+24 -27
View File
@@ -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<T = Codec> = Record<string, string | CodecClass<T>>;
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<string, string>): [Iterable<[string, Codec]>, number] {
function decodeStructFromObject (registry: Registry, [Types, keys]: Definition, value: any, jsonMap: Map<string, string>): [Iterable<[string, Codec]>, number] {
let jsonObj: Record<string, unknown> | 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<keyof S, string>;
readonly #Types: CodecClassDef;
readonly #Types: Definition;
constructor (registry: Registry, Types: S, value?: V | Map<unknown, unknown> | unknown[] | HexString | null, jsonMap = new Map<string, string>(), { definition, setDefinition = noopSetDefinition }: Options = {}) {
const typeMap = definition || setDefinition(mapToTypeMap(registry, Types));
const [decoded, decodedLength] = isU8a(value)
? decodeU8a<Codec, [string, Codec]>(registry, value, typeMap, true)
: isHex(value)
? decodeU8a<Codec, [string, Codec]>(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<Codec, [string, Codec]>(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<S extends TypesDef> (Types: S, jsonMap?: Map<string, string>): CodecClass<Struct<S>> {
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<S> {
@@ -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 <T> (key: string): T {
@@ -174,12 +173,10 @@ export class Struct<
*/
public get Type (): E {
const result: Record<string, string> = {};
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;
-2
View File
@@ -105,8 +105,6 @@ export interface CodecClass<T = Codec> {
new(registry: Registry, ...args: any[]): T;
}
export type CodecClassDef<T = Codec> = Record<string, CodecClass<T>>;
export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a';
export type ArgsDef = Record<string, CodecClass | string>;
+10 -14
View File
@@ -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 <T extends Codec = Codec, E = T> (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<E>(Types.length);
export function decodeU8a <T extends Codec = Codec, E = T> (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 <T extends Codec = Codec, E = T> (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 <T extends Codec = Codec> (registry: Registry, u8a: Uint8Array, start: number, Type: CodecClass<T>, count: number): [T[], number, number] {
const result = new Array<T>(count);
let offset = start;
export function decodeU8aVec <T extends Codec = Codec> (registry: Registry, result: unknown[], u8a: Uint8Array, startAt: number, Type: CodecClass<T>): [number, number] {
const count = result.length;
let offset = startAt;
for (let i = 0; i < count; i++) {
try {
@@ -75,5 +71,5 @@ export function decodeU8aVec <T extends Codec = Codec> (registry: Registry, u8a:
}
}
return [result, offset, offset - start];
return [offset, offset - startAt];
}
@@ -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<string, string | CodecClass>): Record<string, CodecClass> {
export function mapToTypeMap (registry: Registry, input: Record<string, string | CodecClass>): [CodecClass[], string[]] {
const entries = Object.entries(input);
const output: Record<string, CodecClass> = {};
const count = entries.length;
const output: [CodecClass[], string[]] = [new Array<CodecClass>(count), new Array<string>(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;
+3 -6
View File
@@ -3,14 +3,11 @@
import type { CodecClass, Registry } from '../types';
export function typesToMap (registry: Registry, Types: Record<string, CodecClass>): Record<string, string> {
const entries = Object.entries(Types);
export function typesToMap (registry: Registry, [Types, keys]: [CodecClass[], string[]]): Record<string, string> {
const result: Record<string, string> = {};
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;
@@ -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) {