UNPKG

@libp2p/mplex

Version:

JavaScript implementation of https://github.com/libp2p/mplex

100 lines 3.54 kB
import { InvalidMessageError } from '@libp2p/interface'; import { Uint8ArrayList } from 'uint8arraylist'; import { MessageTypeNames, MessageTypes } from './message-types.js'; export const MAX_MSG_SIZE = 1 << 20; // 1MB export const MAX_MSG_QUEUE_SIZE = 4 << 20; // 4MB export class Decoder { _buffer; _headerInfo; _maxMessageSize; _maxUnprocessedMessageQueueSize; constructor(maxMessageSize = MAX_MSG_SIZE, maxUnprocessedMessageQueueSize = MAX_MSG_QUEUE_SIZE) { this._buffer = new Uint8ArrayList(); this._headerInfo = null; this._maxMessageSize = maxMessageSize; this._maxUnprocessedMessageQueueSize = maxUnprocessedMessageQueueSize; } write(chunk) { if (chunk == null || chunk.length === 0) { return []; } this._buffer.append(chunk); if (this._buffer.byteLength > this._maxUnprocessedMessageQueueSize) { throw new InvalidMessageError('Unprocessed message queue size too large!'); } const msgs = []; while (this._buffer.length !== 0) { if (this._headerInfo == null) { try { this._headerInfo = this._decodeHeader(this._buffer); } catch (err) { if (err.name === 'InvalidMessageError') { throw err; } break; // We haven't received enough data yet } } const { id, type, length, offset } = this._headerInfo; const bufferedDataLength = this._buffer.length - offset; if (bufferedDataLength < length) { break; // not enough data yet } const msg = { id, type }; if (type === MessageTypes.NEW_STREAM || type === MessageTypes.MESSAGE_INITIATOR || type === MessageTypes.MESSAGE_RECEIVER) { msg.data = this._buffer.sublist(offset, offset + length); } msgs.push(msg); this._buffer.consume(offset + length); this._headerInfo = null; } return msgs; } /** * Attempts to decode the message header from the buffer */ _decodeHeader(data) { const { value: h, offset } = readVarInt(data); const { value: length, offset: end } = readVarInt(data, offset); const type = h & 7; // @ts-expect-error h is a number not a CODE if (MessageTypeNames[type] == null) { throw new Error(`Invalid type received: ${type}`); } // test message type varint + data length if (length > this._maxMessageSize) { throw new InvalidMessageError('Message size too large'); } // @ts-expect-error h is a number not a CODE return { id: h >> 3, type, offset: offset + end, length }; } } const MSB = 0x80; const REST = 0x7F; function readVarInt(buf, offset = 0) { let res = 0; let shift = 0; let counter = offset; let b; const l = buf.length; do { if (counter >= l || shift > 49) { offset = 0; throw new RangeError('Could not decode varint'); } b = buf.get(counter++); res += shift < 28 ? (b & REST) << shift : (b & REST) * Math.pow(2, shift); shift += 7; } while (b >= MSB); offset = counter - offset; return { value: res, offset }; } //# sourceMappingURL=decode.js.map