From 9b18ff4ce03d975ba87d878d963dddc5690f102a Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Thu, 29 Sep 2022 13:25:04 +0100 Subject: [PATCH] fix: cache string and tuple representations (#275) Converting multiaddrs to strings and tuples is expensive and done in many places so cache these representations when they are first requested. --- src/codec.ts | 11 ++++++----- src/index.ts | 30 ++++++++++++++++++++++++------ test/index.spec.ts | 6 +++--- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/codec.ts b/src/codec.ts index 58afd41d..5b013126 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -4,6 +4,7 @@ import varint from 'varint' import { concat as uint8ArrayConcat } from 'uint8arrays/concat' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import type { Protocol } from './protocols-table.js' +import type { StringTuple, Tuple } from './index.js' /** * string -> [[str name, str addr]... ] @@ -50,7 +51,7 @@ export function stringToStringTuples (str: string) { /** * [[str name, str addr]... ] -> string */ -export function stringTuplesToString (tuples: Array<[number, string?]>) { +export function stringTuplesToString (tuples: StringTuple[]) { const parts: string[] = [] tuples.map((tup) => { const proto = protoFromTuple(tup) @@ -67,7 +68,7 @@ export function stringTuplesToString (tuples: Array<[number, string?]>) { /** * [[str name, str addr]... ] -> [[int code, Uint8Array]... ] */ -export function stringTuplesToTuples (tuples: Array): Array<[number, Uint8Array?]> { +export function stringTuplesToTuples (tuples: Array): Tuple[] { return tuples.map((tup) => { if (!Array.isArray(tup)) { tup = [tup] @@ -85,7 +86,7 @@ export function stringTuplesToTuples (tuples: Array): Array<[ * * [[int code, Uint8Array]... ] -> [[int code, str addr]... ] */ -export function tuplesToStringTuples (tuples: Array<[number, Uint8Array?]>): Array<[number, string?]> { +export function tuplesToStringTuples (tuples: Tuple[]): StringTuple[] { return tuples.map(tup => { const proto = protoFromTuple(tup) if (tup[1] != null) { @@ -98,7 +99,7 @@ export function tuplesToStringTuples (tuples: Array<[number, Uint8Array?]>): Arr /** * [[int code, Uint8Array ]... ] -> Uint8Array */ -export function tuplesToBytes (tuples: Array<[number, Uint8Array?]>) { +export function tuplesToBytes (tuples: Tuple[]) { return fromBytes(uint8ArrayConcat(tuples.map((tup) => { const proto = protoFromTuple(tup) let buf = Uint8Array.from(varint.encode(proto.code)) @@ -122,7 +123,7 @@ export function sizeForAddr (p: Protocol, addr: Uint8Array | number[]) { } } -export function bytesToTuples (buf: Uint8Array): Array<[number, Uint8Array?]> { +export function bytesToTuples (buf: Uint8Array): Tuple[] { const tuples: Array<[number, Uint8Array?]> = [] let i = 0 while (i < buf.length) { diff --git a/src/index.ts b/src/index.ts index a5b3ad8d..677399ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,6 +46,10 @@ export type MultiaddrInput = string | Multiaddr | Uint8Array | null export interface Resolver { (addr: Multiaddr, options?: AbortOptions): Promise } +export type Tuple = [number, Uint8Array?] + +export type StringTuple = [number, string?] + export interface AbortOptions { signal?: AbortSignal } @@ -137,7 +141,7 @@ export interface Multiaddr { * // [ [ 4, ], [ 6, ] ] * ``` */ - tuples: () => Array<[number, Uint8Array?]> + tuples: () => Tuple[] /** * Returns a tuple of string/number parts @@ -150,7 +154,7 @@ export interface Multiaddr { * // [ [ 4, '127.0.0.1' ], [ 6, '4001' ] ] * ``` */ - stringTuples: () => Array<[number, string?]> + stringTuples: () => StringTuple[] /** * Encapsulates a Multiaddr in another Multiaddr @@ -396,6 +400,9 @@ export function isMultiaddr (value: any): value is Multiaddr { */ class DefaultMultiaddr implements Multiaddr { public bytes: Uint8Array + private _string?: string + private _tuples?: Tuple[] + private _stringTuples?: StringTuple[] [symbol]: boolean = true @@ -429,7 +436,11 @@ class DefaultMultiaddr implements Multiaddr { } toString () { - return codec.bytesToString(this.bytes) + if (this._string == null) { + this._string = codec.bytesToString(this.bytes) + } + + return this._string } toJSON () { @@ -495,12 +506,19 @@ class DefaultMultiaddr implements Multiaddr { } tuples (): Array<[number, Uint8Array?]> { - return codec.bytesToTuples(this.bytes) + if (this._tuples == null) { + this._tuples = codec.bytesToTuples(this.bytes) + } + + return this._tuples } stringTuples (): Array<[number, string?]> { - const t = codec.bytesToTuples(this.bytes) - return codec.tuplesToStringTuples(t) + if (this._stringTuples == null) { + this._stringTuples = codec.tuplesToStringTuples(this.tuples()) + } + + return this._stringTuples } encapsulate (addr: MultiaddrInput): Multiaddr { diff --git a/test/index.spec.ts b/test/index.spec.ts index e59a3b89..247025c5 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -702,13 +702,13 @@ describe('helpers', () => { const relay = relayTCP.encapsulate('/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6/p2p-circuit') const target = multiaddr('/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC') const original = relay.encapsulate(target) - expect(original.decapsulateCode(421)).to.eql(relay) - expect(relay.decapsulateCode(421)).to.eql(relayTCP) + expect(original.decapsulateCode(421).toJSON()).to.eql(relay.toJSON()) + expect(relay.decapsulateCode(421).toJSON()).to.eql(relayTCP.toJSON()) }) it('ignores missing codes', () => { const tcp = multiaddr('/ip4/0.0.0.0/tcp/8080') - expect(tcp.decapsulateCode(421)).to.eql(tcp) + expect(tcp.decapsulateCode(421).toJSON()).to.eql(tcp.toJSON()) }) })