UNPKG

@gent-js/gent

Version:

template-based data generator.

118 lines (117 loc) 4.53 kB
import { Buffer } from "node:buffer"; import * as stream from "node:stream"; import { GeneratingDocument } from "./document/index.js"; const Per1000Window = 1000; const Per100Window = 100; export class DocumentTransformStream extends stream.Transform { documentTransformer; timeWindow; numOfEventPerTimeWindow; timeWindowIntervalTimeout; numOfEventInCurrentTimeWindow; pendingTransformTasks = []; nThTimeWindow = 0; totalNumOfEvent = 0; constructor(options, debug = false) { super({ highWaterMark: 0, objectMode: true, }); const transformMode = options.transformMode; if (transformMode === "object") { this.documentTransformer = undefined; } else if (transformMode === "buffer") { const framing = options.framing; if (framing === "octet-counting") { this.documentTransformer = transformDocumentIntoOctetCountingBuffer; } else if (framing === "non-transparent") { this.documentTransformer = createNonTransparentDocumentTransformer(options.trailer, options.trailerReplacer); } else { throw new Error(`Unexpected framingMethod: ${framing}`); } } else { throw new Error(`Unexpected transferMode: ${transformMode}`); } if (options.eps < Per100Window) { this.timeWindow = Per1000Window; } else { this.timeWindow = Per100Window; } this.numOfEventPerTimeWindow = Math.ceil(options.eps * (this.timeWindow / 1000)); this.numOfEventInCurrentTimeWindow = 0; this.timeWindowIntervalTimeout = setInterval(() => { this.__flushPendingTransformTasks(); if (debug) { this.debugReport(); } this.numOfEventInCurrentTimeWindow = 0; }, this.timeWindow); } debugReport() { this.totalNumOfEvent = this.totalNumOfEvent + this.numOfEventInCurrentTimeWindow; this.nThTimeWindow = this.nThTimeWindow + 1; console.log(`[${this.nThTimeWindow}] => ${this.numOfEventInCurrentTimeWindow}/${this.totalNumOfEvent}`); } _transform(chunk, encoding, callback) { if (!(chunk instanceof GeneratingDocument)) { callback(new Error(`Unexpected chunk type(${typeof chunk}). Cannot process any chunk type except Document.`)); return; } if (this.numOfEventInCurrentTimeWindow < this.numOfEventPerTimeWindow) { this.numOfEventInCurrentTimeWindow = this.numOfEventInCurrentTimeWindow + 1; this.__transformDocument(chunk, encoding, callback); return; } this.pendingTransformTasks.push([chunk, encoding, callback]); } _flush(callback) { this.__flushPendingTransformTasks(); this.__clearWindowInternal(); callback(); } __transformDocument(document, encoding, callback) { const documentTransformer = this.documentTransformer; if (documentTransformer === undefined) { callback(null, document); return; } const bufferArray = documentTransformer(document, encoding); bufferArray.forEach((buffer) => this.push(buffer, encoding)); callback(); } __flushPendingTransformTasks() { const tasks = this.pendingTransformTasks; this.pendingTransformTasks = []; tasks.forEach(([document, encoding, callback]) => this.__transformDocument(document, encoding, callback)); } __clearWindowInternal() { clearInterval(this.timeWindowIntervalTimeout); } } const SP = " "; function transformDocumentIntoOctetCountingBuffer(document, encoding) { const outputString = document.stamp(); const messageBuf8 = Buffer.from(outputString, encoding); const lengthCount = messageBuf8.length.toString(); const lengthBuf8 = Buffer.from(lengthCount + SP, encoding); return [lengthBuf8, messageBuf8]; } function createNonTransparentDocumentTransformer(trailer, trailerReplacer) { return (document, encoding) => { let outputString = document.stamp(); if (trailerReplacer !== undefined) { outputString = outputString.replaceAll(trailer, trailerReplacer); } return [ Buffer.from(outputString, encoding), Buffer.from(trailer, encoding), ]; }; }