UNPKG

@multiformats/multiaddr

Version:

multiaddr implementation (binary + string representation of network addresses)

163 lines 6.12 kB
import * as varint from 'uint8-varint'; import { concat as uint8ArrayConcat } from 'uint8arrays/concat'; import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'; import { toString as uint8ArrayToString } from 'uint8arrays/to-string'; import { InvalidMultiaddrError } from "./errors.js"; import { registry, V } from "./registry.js"; export function bytesToComponents(bytes) { const components = []; let i = 0; while (i < bytes.length) { const code = varint.decode(bytes, i); const codec = registry.getProtocol(code); const codeLength = varint.encodingLength(code); const size = sizeForAddr(codec, bytes, i + codeLength); let sizeLength = 0; if (size > 0 && codec.size === V) { sizeLength = varint.encodingLength(size); } const componentLength = codeLength + sizeLength + size; const component = { code, name: codec.name, bytes: bytes.subarray(i, i + componentLength) }; if (size > 0) { const valueOffset = i + codeLength + sizeLength; const valueBytes = bytes.subarray(valueOffset, valueOffset + size); component.value = codec.bytesToValue?.(valueBytes) ?? uint8ArrayToString(valueBytes); } components.push(component); i += componentLength; } return components; } export function componentsToBytes(components) { let length = 0; const bytes = []; for (const component of components) { if (component.bytes == null) { const codec = registry.getProtocol(component.code); const codecLength = varint.encodingLength(component.code); let valueBytes; let valueLength = 0; let valueLengthLength = 0; if (component.value != null) { valueBytes = codec.valueToBytes?.(component.value) ?? uint8ArrayFromString(component.value); valueLength = valueBytes.byteLength; if (codec.size === V) { valueLengthLength = varint.encodingLength(valueLength); } } const bytes = new Uint8Array(codecLength + valueLengthLength + valueLength); // encode the protocol code let offset = 0; varint.encodeUint8Array(component.code, bytes, offset); offset += codecLength; // if there is a value if (valueBytes != null) { // if the value has variable length, encode the length if (codec.size === V) { varint.encodeUint8Array(valueLength, bytes, offset); offset += valueLengthLength; } // finally encode the value bytes.set(valueBytes, offset); } component.bytes = bytes; } bytes.push(component.bytes); length += component.bytes.byteLength; } return uint8ArrayConcat(bytes, length); } export function stringToComponents(string) { if (string.charAt(0) !== '/') { throw new InvalidMultiaddrError('String multiaddr must start with "/"'); } const components = []; let collecting = 'protocol'; let value = ''; let protocol = ''; for (let i = 1; i < string.length; i++) { const char = string.charAt(i); if (char !== '/') { if (collecting === 'protocol') { protocol += string.charAt(i); } else { value += string.charAt(i); } } const ended = i === string.length - 1; if (char === '/' || ended) { const codec = registry.getProtocol(protocol); if (collecting === 'protocol') { if (codec.size == null || codec.size === 0) { // a protocol without an address, eg. `/tls` components.push({ code: codec.code, name: codec.name }); value = ''; protocol = ''; collecting = 'protocol'; continue; } else if (ended) { throw new InvalidMultiaddrError(`Component ${protocol} was missing value`); } // continue collecting value collecting = 'value'; } else if (collecting === 'value') { const component = { code: codec.code, name: codec.name }; if (codec.size != null && codec.size !== 0) { if (value === '') { throw new InvalidMultiaddrError(`Component ${protocol} was missing value`); } component.value = codec.stringToValue?.(value) ?? value; } components.push(component); value = ''; protocol = ''; collecting = 'protocol'; } } } if (protocol !== '' && value !== '') { throw new InvalidMultiaddrError('Incomplete multiaddr'); } return components; } export function componentsToString(components) { return `/${components.flatMap(component => { if (component.value == null) { return component.name; } const codec = registry.getProtocol(component.code); if (codec == null) { throw new InvalidMultiaddrError(`Unknown protocol code ${component.code}`); } return [ component.name, codec.valueToString?.(component.value) ?? component.value ]; }).join('/')}`; } /** * For the passed address, return the serialized size */ function sizeForAddr(codec, bytes, offset) { if (codec.size == null || codec.size === 0) { return 0; } if (codec.size > 0) { return codec.size / 8; } return varint.decode(bytes, offset); } //# sourceMappingURL=components.js.map