Performance tweaks (#4143)
* Avoid unnecessary work in `filterEvents` * Memoize computed extrinsic hash * Optimize sanitize by reusing precompiled regular expressions * Memoize generation of property names in `Enum` and `Set` * Memoize sanitizing
This commit is contained in:
@@ -155,6 +155,42 @@ function decodeEnum (registry: Registry, def: TypesDef, value?: any, index?: num
|
||||
return decodeFromValue(registry, def, value);
|
||||
}
|
||||
|
||||
const camelCaseMap = new Map<string, string>();
|
||||
|
||||
function memoizedStringCamelCase (value: string): string {
|
||||
{
|
||||
const camelCaseValue = camelCaseMap.get(value);
|
||||
|
||||
if (camelCaseValue) {
|
||||
return camelCaseValue;
|
||||
}
|
||||
}
|
||||
|
||||
const camelCaseValue = stringCamelCase(value);
|
||||
|
||||
camelCaseMap.set(value, camelCaseValue);
|
||||
|
||||
return camelCaseValue;
|
||||
}
|
||||
|
||||
const propertySuffixMap = new Map<string, string>();
|
||||
|
||||
function memoizedPropertySuffix (value: string): string {
|
||||
{
|
||||
const suffix = propertySuffixMap.get(value);
|
||||
|
||||
if (suffix) {
|
||||
return suffix;
|
||||
}
|
||||
}
|
||||
|
||||
const suffix = stringUpperFirst(stringCamelCase(value.replace(' ', '_')));
|
||||
|
||||
propertySuffixMap.set(value, suffix);
|
||||
|
||||
return suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Enum
|
||||
* @description
|
||||
@@ -206,7 +242,7 @@ export class Enum implements IEnum {
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
const name = stringUpperFirst(stringCamelCase(key.replace(' ', '_')));
|
||||
const name = memoizedPropertySuffix(key);
|
||||
const askey = `as${name}`;
|
||||
const iskey = `is${name}`;
|
||||
|
||||
@@ -344,7 +380,7 @@ export class Enum implements IEnum {
|
||||
public toJSON (): AnyJson {
|
||||
return this.#isBasic
|
||||
? this.type
|
||||
: { [stringCamelCase(this.type)]: this.#raw.toJSON() };
|
||||
: { [memoizedStringCamelCase(this.type)]: this.#raw.toJSON() };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,6 +80,24 @@ function decodeSet (setValues: SetValues, value: string[] | Set<string> | Uint8A
|
||||
return decodeSetNumber(setValues, value);
|
||||
}
|
||||
|
||||
const iskeyMap = new Map<string, string>();
|
||||
|
||||
function memoizedIskey (value: string): string {
|
||||
{
|
||||
const iskey = iskeyMap.get(value);
|
||||
|
||||
if (iskey) {
|
||||
return iskey;
|
||||
}
|
||||
}
|
||||
|
||||
const iskey = `is${stringUpperFirst(stringCamelCase(value))}`;
|
||||
|
||||
iskeyMap.set(value, iskey);
|
||||
|
||||
return iskey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Set
|
||||
* @description
|
||||
@@ -113,7 +131,7 @@ export class CodecSet extends Set<string> implements ISet<string> {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
|
||||
defineProperty(this, `is${stringUpperFirst(stringCamelCase(key))}`, () => this.strings.includes(key));
|
||||
defineProperty(this, memoizedIskey(key), () => this.strings.includes(key));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,14 +68,18 @@ export function findClosing (value: string, start: number): number {
|
||||
}
|
||||
|
||||
export function alias (src: string, dest: string, withChecks = true): Mapper {
|
||||
const from = new RegExp(`(^${src}|${BOX_PRECEDING.map((box) => `\\${box}${src}`).join('|')})`, 'g');
|
||||
|
||||
const to = (src: string): string => {
|
||||
from.lastIndex = 0;
|
||||
|
||||
return withChecks && BOX_PRECEDING.includes(src[0])
|
||||
? `${src[0]}${dest}`
|
||||
: dest;
|
||||
};
|
||||
|
||||
return (value: string): string =>
|
||||
value.replace(
|
||||
new RegExp(`(^${src}|${BOX_PRECEDING.map((box) => `\\${box}${src}`).join('|')})`, 'g'),
|
||||
(src): string =>
|
||||
withChecks && BOX_PRECEDING.includes(src[0])
|
||||
? `${src[0]}${dest}`
|
||||
: dest
|
||||
);
|
||||
value.replace(from, to);
|
||||
}
|
||||
|
||||
export function cleanupCompact (): Mapper {
|
||||
@@ -95,12 +99,18 @@ export function cleanupCompact (): Mapper {
|
||||
}
|
||||
|
||||
export function flattenSingleTuple (): Mapper {
|
||||
return (value: string) =>
|
||||
value
|
||||
const from1 = /,\)/g;
|
||||
const from2 = /\(([^,]+)\)/;
|
||||
|
||||
return (value: string) => {
|
||||
from1.lastIndex = 0;
|
||||
|
||||
return value
|
||||
// tuples may have trailing commas, e.g. (u32, BlockNumber, )
|
||||
.replace(/,\)/g, ')')
|
||||
.replace(from1, ')')
|
||||
// change (u32) -> u32
|
||||
.replace(/\(([^,]+)\)/, '$1');
|
||||
.replace(from2, '$1');
|
||||
};
|
||||
}
|
||||
|
||||
function replaceTagWith (value: string, matcher: string, replacer: (inner: string) => string): string {
|
||||
@@ -204,20 +214,35 @@ export function removePairOf (): Mapper {
|
||||
|
||||
// remove the type traits
|
||||
export function removeTraits (): Mapper {
|
||||
return (value: string): string =>
|
||||
value
|
||||
const from1 = /\s/g;
|
||||
const from2 = /(T|Self)::/g;
|
||||
const from3 = /<(T|Self)asTrait>::/g;
|
||||
const from4 = /<Tas[a-z]+::Trait>::/g;
|
||||
const from5 = /<LookupasStaticLookup>/g;
|
||||
const from6 = /::Type/g;
|
||||
|
||||
return (value: string): string => {
|
||||
from1.lastIndex = 0;
|
||||
from2.lastIndex = 0;
|
||||
from3.lastIndex = 0;
|
||||
from4.lastIndex = 0;
|
||||
from5.lastIndex = 0;
|
||||
from6.lastIndex = 0;
|
||||
|
||||
return value
|
||||
// remove all whitespaces
|
||||
.replace(/\s/g, '')
|
||||
.replace(from1, '')
|
||||
// anything `T::<type>` to end up as `<type>`
|
||||
.replace(/(T|Self)::/g, '')
|
||||
.replace(from2, '')
|
||||
// replace `<T as Trait>::` (whitespaces were removed above)
|
||||
.replace(/<(T|Self)asTrait>::/g, '')
|
||||
.replace(from3, '')
|
||||
// replace `<T as something::Trait>::` (whitespaces were removed above)
|
||||
.replace(/<Tas[a-z]+::Trait>::/g, '')
|
||||
.replace(from4, '')
|
||||
// replace <Lookup as StaticLookup>
|
||||
.replace(/<LookupasStaticLookup>/g, 'Lookup')
|
||||
.replace(from5, 'Lookup')
|
||||
// replace `<...>::Type`
|
||||
.replace(/::Type/g, '');
|
||||
.replace(from6, '');
|
||||
};
|
||||
}
|
||||
|
||||
// remove wrapping values, i.e. Box<Proposal> -> Proposal
|
||||
@@ -228,13 +253,29 @@ export function removeWrap (check: string): Mapper {
|
||||
replaceTagWith(value, check, replacer);
|
||||
}
|
||||
|
||||
const sanitizeMap = new Map<string, string>();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function sanitize (value: String | string, options?: SanitizeOptions): string {
|
||||
let result = value.toString();
|
||||
|
||||
if (!options) {
|
||||
const memoized = sanitizeMap.get(result);
|
||||
|
||||
if (memoized) {
|
||||
return memoized;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < mappings.length; i++) {
|
||||
result = mappings[i](result, options);
|
||||
}
|
||||
|
||||
return result.trim();
|
||||
result = result.trim();
|
||||
|
||||
if (!options) {
|
||||
sanitizeMap.set(value.toString(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { assert, compactAddLength, compactFromU8a, isHex, isU8a, u8aConcat, u8aT
|
||||
|
||||
import { Base } from '../codec/Base';
|
||||
import { Compact } from '../codec/Compact';
|
||||
import { CodecHash } from '../interfaces/runtime';
|
||||
import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_VERSION, UNMASK_VERSION } from './constants';
|
||||
|
||||
interface CreateOptions {
|
||||
@@ -36,6 +37,8 @@ const VERSIONS = [
|
||||
export { EXTRINSIC_VERSION as LATEST_EXTRINSIC_VERSION } from './v4/Extrinsic';
|
||||
|
||||
abstract class ExtrinsicBase<A extends AnyTuple> extends Base<ExtrinsicVx | ExtrinsicUnknown> {
|
||||
protected hashCache?: CodecHash;
|
||||
|
||||
/**
|
||||
* @description The arguments passed to for the call, exposes args so it is compatible with [[Call]]
|
||||
*/
|
||||
@@ -78,6 +81,17 @@ abstract class ExtrinsicBase<A extends AnyTuple> extends Base<ExtrinsicVx | Extr
|
||||
return this.toU8a().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns a hash of the contents
|
||||
*/
|
||||
public override get hash (): CodecHash {
|
||||
if (!this.hashCache) {
|
||||
this.hashCache = super.hash;
|
||||
}
|
||||
|
||||
return this.hashCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description `true` id the extrinsic is signed
|
||||
*/
|
||||
@@ -219,6 +233,7 @@ export class GenericExtrinsic<A extends AnyTuple = AnyTuple> extends ExtrinsicBa
|
||||
*/
|
||||
public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsic<A> {
|
||||
(this._raw as ExtrinsicVx).addSignature(signer, signature, payload);
|
||||
this.hashCache = undefined;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -228,6 +243,7 @@ export class GenericExtrinsic<A extends AnyTuple = AnyTuple> extends ExtrinsicBa
|
||||
*/
|
||||
public sign (account: IKeyringPair, options: SignatureOptions): GenericExtrinsic<A> {
|
||||
(this._raw as ExtrinsicVx).sign(account, options);
|
||||
this.hashCache = undefined;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -237,6 +253,7 @@ export class GenericExtrinsic<A extends AnyTuple = AnyTuple> extends ExtrinsicBa
|
||||
*/
|
||||
public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsic<A> {
|
||||
(this._raw as ExtrinsicVx).signFake(signer, options);
|
||||
this.hashCache = undefined;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { ExtrinsicOptions } from '../types';
|
||||
import { isU8a } from '@polkadot/util';
|
||||
|
||||
import { Struct } from '../../codec/Struct';
|
||||
import { CodecHash } from '../../interfaces/runtime';
|
||||
|
||||
export const EXTRINSIC_VERSION = 4;
|
||||
|
||||
@@ -24,6 +25,8 @@ export interface ExtrinsicValueV4 {
|
||||
* The third generation of compact extrinsics
|
||||
*/
|
||||
export class GenericExtrinsicV4 extends Struct implements IExtrinsicImpl {
|
||||
#hashCache?: CodecHash;
|
||||
|
||||
constructor (registry: Registry, value?: Uint8Array | ExtrinsicValueV4 | Call, { isSigned }: Partial<ExtrinsicOptions> = {}) {
|
||||
super(registry, {
|
||||
signature: 'ExtrinsicSignatureV4',
|
||||
@@ -59,6 +62,17 @@ export class GenericExtrinsicV4 extends Struct implements IExtrinsicImpl {
|
||||
return this.toU8a().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns a hash of the contents
|
||||
*/
|
||||
public override get hash (): CodecHash {
|
||||
if (!this.#hashCache) {
|
||||
this.#hashCache = super.hash;
|
||||
}
|
||||
|
||||
return this.#hashCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The [[Call]] this extrinsic wraps
|
||||
*/
|
||||
@@ -85,6 +99,7 @@ export class GenericExtrinsicV4 extends Struct implements IExtrinsicImpl {
|
||||
*/
|
||||
public addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | HexString, payload: ExtrinsicPayloadValue | Uint8Array | HexString): GenericExtrinsicV4 {
|
||||
this.signature.addSignature(signer, signature, payload);
|
||||
this.#hashCache = undefined;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -94,6 +109,7 @@ export class GenericExtrinsicV4 extends Struct implements IExtrinsicImpl {
|
||||
*/
|
||||
public sign (account: IKeyringPair, options: SignatureOptions): GenericExtrinsicV4 {
|
||||
this.signature.sign(this.method, account, options);
|
||||
this.#hashCache = undefined;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -103,6 +119,7 @@ export class GenericExtrinsicV4 extends Struct implements IExtrinsicImpl {
|
||||
*/
|
||||
public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): GenericExtrinsicV4 {
|
||||
this.signature.signFake(this.method, signer, options);
|
||||
this.#hashCache = undefined;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user