UNPKG

cbor-edn

Version:

Parse CBOR Extended Diagnostic Notation as defined by [draft-ietf-cbor-edn-literals-16](https://www.ietf.org/archive/id/draft-ietf-cbor-edn-literals-16.html) and some CBOR working group discussions.

174 lines (173 loc) 4.82 kB
import { getRanges, hasRanges, setRanges, u8toHex, } from 'cbor2/utils'; function len(items) { let tot = 0; for (const i of items) { if (Array.isArray(i)) { tot += len(i); } else { tot += i.length; } } return tot; } function allBytes(into, offset, items) { for (const t of items) { if (Array.isArray(t)) { offset = allBytes(into, offset, t); } else if (t instanceof Uint8Array) { into.set(t, offset); offset += t.length; // eslint-disable-next-line @typescript-eslint/no-use-before-define } else if (t instanceof ByteTree) { t.bytes(into, offset); offset += t.length; } // Impossible to have invalid entry since all pathways are checked in // hasRegions. } return offset; } function hasRegions(item) { // eslint-disable-next-line @typescript-eslint/no-use-before-define if (item instanceof ByteTree) { return item.hasRegions; } if (Array.isArray(item)) { return item.some(hasRegions); } if (item instanceof Uint8Array) { return hasRanges(item); } throw new Error(`Invalid ByteTree item: ${item}`); } function getRegions(item, offset, depth = 0) { if (Array.isArray(item)) { const ret = []; for (const i of item) { const r = getRegions(i, offset, depth + 1); const last = r[r.length - 1]; offset = last[0] + last[1]; ret.push(...r); } if (ret.length === 0) { ret.push([offset, 0]); } return ret; } if (item instanceof Uint8Array) { const r = getRanges(item); if (!r) { return [[offset, item.length]]; } const ret = []; for (const s of r) { const t = [...s]; t[0] += offset; ret.push(t); } return ret; } // eslint-disable-next-line @typescript-eslint/no-use-before-define if (item instanceof ByteTree) { if (!item.hasRegions) { item.setRegion(); } const ret = []; for (const r of item.regions) { const s = [...r]; s[0] += offset; ret.push(s); } return ret; } throw new Error(`Invalid ByteTree item: ${item}`); } export class ByteTree { mt = 0; #length = 0; #items = []; #hasRegions; #regions = []; constructor(...item) { this.#items = item; this.#length = len(item); this.#hasRegions = hasRegions(item); if (this.#hasRegions) { this.#regions = getRegions(item, 0); } } get length() { return this.#length; } get hasRegions() { return this.#hasRegions; } get regions() { return this.#regions; } setRegion(type) { this.#hasRegions = true; this.#regions = getRegions(this.#items, 0); if (type) { if ((this.#regions[0][0] === 0) && (this.#regions[0][1] === this.#length)) { this.#regions[0][2] = type; } else { this.#regions.unshift([0, this.#length, type]); } } } bytes(into, offset = 0) { if (!into) { if (this.#hasRegions) { // Always copy if there are regions into = new Uint8Array(this.#length); setRanges(into, this.#regions); } else if ((this.#items.length === 1) && (this.#items[0] instanceof Uint8Array)) { // Shortcut, don't copy return this.#items[0]; } else { into = new Uint8Array(this.#length); } } allBytes(into, offset, this.#items); return into; } push(...item) { this.#items.push(...item); if (this.#hasRegions) { this.#regions.push(...getRegions(item, this.#length)); } else if (hasRegions(item)) { this.#hasRegions = true; this.#regions = getRegions(this.#items, 0); } this.#length += len(item); } [Symbol.for('nodejs.util.inspect.custom')]() { return this.toString(); } toString() { let ret = 'ByteTree('; ret += this.#length; ret += ')['; ret += this.#items.map(i => { if (i instanceof Uint8Array) { if (i.length) { return `0x${u8toHex(i)}`; } return '""'; } return String(i); }).join(', '); ret += ']'; return ret; } }