@multiformats/multiaddr
Version:
multiaddr implementation (binary + string representation of network addresses)
163 lines • 6.12 kB
JavaScript
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