UNPKG

cesr

Version:

[![NPM Version](https://img.shields.io/npm/v/cesr.svg?style=flat)](https://www.npmjs.com/package/cesr) [![NPM License](https://img.shields.io/npm/l/cesr.svg?style=flat)](https://github.com/lenkan/cesr-js/blob/main/LICENSE) [![CI](https://github.com/lenkan

274 lines (273 loc) 11.1 kB
import { Attachments, } from "./attachments.js"; import { CountCode_10, CountCode_20 } from "./codes.js"; import { Counter } from "./counter.js"; import { Indexer } from "./indexer.js"; import { Matter } from "./matter.js"; export class AttachmentsReader { #version; #buffer; #numberOfBytesRead = 0; constructor(input, options = {}) { this.#version = options.version ?? 1; this.#buffer = input; } get bytesRead() { return this.#numberOfBytesRead; } #peekBytes(count) { if (this.#buffer.length < count) { throw new Error("Not enough data to peek"); } return this.#buffer.slice(0, count); } #readBytes(count) { const bytes = this.#peekBytes(count); this.#buffer = this.#buffer.slice(count); this.#numberOfBytesRead += count; return bytes; } #readMatter() { const result = Matter.peek(this.#buffer); if (!result.frame) { throw new Error("Failed to read matter, not enough data"); } this.#readBytes(result.n); return new Matter({ code: result.frame.code, raw: result.frame.raw, soft: result.frame.soft, }); } #readIndexer() { const result = Indexer.peek(this.#buffer); if (!result.frame) { throw new Error("Failed to read indexer, not enough data"); } this.#readBytes(result.n); return result.frame; } #readCounter() { const result = Counter.peek(this.#buffer); if (!result.frame) { throw new Error("Failed to read counter, not enough data"); } this.#readBytes(result.n); return result.frame; } /** * Read count couples (pairs) of matter primitives */ *#readCouples(counter) { for (let i = 0; i < counter.count; i++) { try { const couple0 = this.#readMatter(); const couple1 = this.#readMatter(); yield [couple0, couple1]; } catch (error) { throw new Error(`Failed to read counter ${counter.code} at index ${i} of ${counter.count}: ${error}`); } } } /** * Read count triples of matter primitives */ *#readTriples(count) { for (let i = 0; i < count; i++) { const triple0 = this.#readMatter(); const triple1 = this.#readMatter(); const triple2 = this.#readMatter(); yield [triple0, triple1, triple2]; } } /** * Read count indexed signatures * Behavior differs based on version (v1 counts signatures, v2 counts quadlets) */ *#readIndexedSignatures(count) { if (this.#version === 1) { for (let n = 0; n < count; n++) { yield this.#readIndexer(); } return; } let counted = 0; while (counted < count) { const frame = this.#readIndexer(); yield frame; counted += frame.text().length / 4; } } *#readTransLastIdxSigGroups(counter) { for (let i = 0; i < counter.count; i++) { const pre = this.#readMatter(); const group = this.#readCounter(); if (group.type !== CountCode_10.ControllerIdxSigs) { throw new Error(`Expected ControllerIdxSigs count code, got ${group.code}`); } const sigs = Array.from(this.#readIndexedSignatures(group.count)); yield { prefix: pre.text(), ControllerIdxSigs: sigs.map((sig) => sig.text()), }; } } *#readTransIdxSigGroups(counter) { for (let i = 0; i < counter.count; i++) { const pre = this.#readMatter(); const snu = this.#readMatter(); const dig = this.#readMatter(); const group = this.#readCounter(); if (group.type !== CountCode_10.ControllerIdxSigs) { throw new Error(`Expected ControllerIdxSigs count code, got ${group.code}`); } const sigs = Array.from(this.#readIndexedSignatures(group.count)); yield { prefix: pre.text(), snu: snu.as.hex(), digest: dig.text(), ControllerIdxSigs: sigs.map((sig) => sig.text()), }; } } #readPathedAttachments(size) { const chunk = this.#readBytes(size * 4); const reader = new AttachmentsReader(chunk, { version: this.#version }); const path = reader.#readMatter().as.string(); const result = Counter.peek(reader.#buffer); const grouped = (result.frame && result.frame.type) === (this.#version === 1 ? CountCode_10.AttachmentGroup : CountCode_20.AttachmentGroup); const pathAttachments = reader.readAttachments(); if (!pathAttachments) { throw new Error(`Not enougth data to read pathed attachments for path ${path}`); } return { path, grouped, attachments: pathAttachments ?? new Attachments({}), }; } readAttachments() { if (this.#buffer.length === 0) { return null; } const result = Counter.peek(this.#buffer); if (!result.frame) { return null; } let end = 0; if (this.#version === 1 && result.frame.type === CountCode_10.AttachmentGroup) { const requiredLength = result.frame.count * 4 + result.frame.quadlets * 4; if (this.#buffer.length < requiredLength) { return null; } this.#readBytes(result.n); end = this.#buffer.length - result.frame.count * 4; } else if (this.#version === 2 && result.frame.type === CountCode_20.AttachmentGroup) { const requiredLength = result.frame.count * 4 + result.frame.quadlets * 4; if (this.#buffer.length < requiredLength) { return null; } this.#readBytes(result.n); end = this.#buffer.length - result.frame.count * 4; } const attachments = new Attachments(); while (this.#buffer.length > end) { const counter = this.#readCounter(); switch (this.#version) { case 1: switch (counter.type) { case CountCode_10.ControllerIdxSigs: { for (const sig of this.#readIndexedSignatures(counter.count)) { attachments.ControllerIdxSigs.push(sig.text()); } break; } case CountCode_10.WitnessIdxSigs: { for (const sig of this.#readIndexedSignatures(counter.count)) { attachments.WitnessIdxSigs.push(sig.text()); } break; } case CountCode_10.FirstSeenReplayCouples: { for (const [fnu, dt] of this.#readCouples(counter)) { attachments.FirstSeenReplayCouples.push({ fnu: fnu.as.hex(), dt: dt.as.date(), }); } break; } case CountCode_10.SealSourceCouples: for (const [snu, dig] of this.#readCouples(counter)) { attachments.SealSourceCouples.push({ snu: snu.as.hex(), digest: dig.text(), }); } break; case CountCode_10.SealSourceTriples: for (const [pre, snu, dig] of this.#readTriples(counter.count)) { attachments.SealSourceTriples.push({ prefix: pre.text(), snu: snu.as.hex(), digest: dig.text(), }); } break; case CountCode_10.NonTransReceiptCouples: for (const [pre, sig] of this.#readCouples(counter)) { attachments.NonTransReceiptCouples.push({ prefix: pre.text(), sig: sig.text(), }); } break; case CountCode_10.PathedMaterialCouples: { const pathedAttachments = this.#readPathedAttachments(counter.count); attachments.PathedMaterialCouples.push(pathedAttachments); break; } case CountCode_10.TransIdxSigGroups: { for (const group of this.#readTransIdxSigGroups(counter)) { attachments.TransIdxSigGroups.push(group); } break; } case CountCode_10.TransLastIdxSigGroups: { for (const group of this.#readTransLastIdxSigGroups(counter)) { attachments.TransLastIdxSigGroups.push(group); } break; } default: throw new Error(`Unsupported group code ${counter.code}`); } break; case 2: switch (counter.type) { case CountCode_20.ControllerIdxSigs: { for (const sig of this.#readIndexedSignatures(counter.count)) { attachments.ControllerIdxSigs.push(sig.text()); } break; } case CountCode_20.WitnessIdxSigs: { for (const sig of this.#readIndexedSignatures(counter.count)) { attachments.WitnessIdxSigs.push(sig.text()); } break; } default: this.#readBytes(counter.count * 4); break; } break; default: throw new Error(`Unsupported parser version ${this.#version}`); } } return attachments; } }