UNPKG

@ndn/tlv

Version:
148 lines (147 loc) 4.12 kB
import { asDataView, fromUtf8 } from "@ndn/util"; import { NNI } from "./nni_browser.js"; class DecodedTlv { type; buf; offsetT; offsetV; offsetE; get length() { return this.offsetE - this.offsetV; } get value() { return this.buf.subarray(this.offsetV, this.offsetE); } get tlv() { return this.buf.subarray(this.offsetT, this.offsetE); } get size() { return this.offsetE - this.offsetT; } get decoder() { return new Decoder(this.tlv); } get vd() { return new Decoder(this.value); } get nni() { return NNI.decode(this.value); } get nniBig() { return NNI.decode(this.value, { big: true }); } get text() { return fromUtf8(this.value); } get before() { return this.buf.subarray(0, this.offsetT); } get after() { return this.buf.subarray(this.offsetE); } constructor(type, buf, offsetT, offsetV, offsetE) { this.type = type; this.buf = buf; this.offsetT = offsetT; this.offsetV = offsetV; this.offsetE = offsetE; } } /** TLV decoder. */ export class Decoder { input; constructor(input) { this.input = input; this.dv = asDataView(input); } dv; offset = 0; /** Determine whether end of input has been reached. */ get eof() { return this.offset >= this.input.length; } /** * Ensure EOF has been reached. * * @throws Error * Thrown if EOF has not been reached. */ throwUnlessEof() { if (!this.eof) { throw new Error("junk after end of TLV"); } } /** * Read the next TLV structure from input. * @returns TLV structure. * * @throws Error * Thrown if there isn't a complete TLV structure in the input. */ read() { const offsetT = this.offset; const type = this.readVarNum(); const length = this.readVarNum(); const offsetV = this.offset; if (length === undefined || (this.offset += length) > this.input.length) { throw new Error(`TLV at offset ${offsetT} is incomplete`); } // length!==undefined implies type!==undefined return new DecodedTlv(type, this.input, offsetT, offsetV, this.offset); } /** Read a Decodable object. */ decode(d) { return d.decodeFrom(this); } /** * Read a variable-size number. * @returns The number up to uint32 or `undefined` if there isn't a complete number. */ readVarNum() { if (this.eof) { return undefined; } switch (this.input[this.offset]) { case 0xFD: { this.offset += 3; if (this.offset > this.input.length) { return undefined; } return this.dv.getUint16(this.offset - 2); } case 0xFE: { this.offset += 5; if (this.offset > this.input.length) { return undefined; } return this.dv.getUint32(this.offset - 4); } case 0xFF: { // JavaScript cannot reliably represent 64-bit integers return undefined; } default: { this.offset += 1; return this.input[this.offset - 1]; } } } } (function (Decoder) { /** * Decode a single object from Uint8Array. * @param input - Input buffer, which should contain the encoded object and nothing else. * @param d - Decodable object type. * @returns Decoded object. * * @throws Error * Thrown if the input cannot be decoded as the specified object type, or there's junk leftover. */ function decode(input, d) { const decoder = new Decoder(input); const res = d.decodeFrom(decoder); decoder.throwUnlessEof(); return res; } Decoder.decode = decode; })(Decoder || (Decoder = {}));