UNPKG

node-fxplc

Version:

Node.js library for low-level Mitsubishi FX (MELSEC) PLC framed protocol communication

95 lines (94 loc) 3.04 kB
"use strict"; // transport-buffer.js // Accumulates incoming chunks and fulfills read(size) promises without busy-wait. Object.defineProperty(exports, "__esModule", { value: true }); exports.BufferAccumulator = void 0; class BufferAccumulator { constructor() { this._chunks = []; this._length = 0; this._waiters = new Set(); // elements: {size, resolve, reject, timeoutHandle} this._closed = false; } /** Push new incoming data */ push(chunk) { if (!chunk || chunk.length === 0) return; this._chunks.push(chunk); this._length += chunk.length; this._notify(); } /** Close buffer (reject all pending read promises) */ close(err) { this._closed = true; for (const w of this._waiters) { if (w.timeoutHandle) clearTimeout(w.timeoutHandle); w.reject(err || new Error('transport closed')); } this._waiters.clear(); } _notify() { if (this._waiters.size === 0) return; for (const w of Array.from(this._waiters)) { if (this._length >= w.size) { this._waiters.delete(w); if (w.timeoutHandle) clearTimeout(w.timeoutHandle); try { const data = this._consume(w.size); w.resolve(data); } catch (e) { w.reject(e); } } } } _consume(size) { if (size > this._length) throw new Error('consume size > buffer length'); const out = Buffer.alloc(size); let off = 0; while (off < size) { const c = this._chunks[0]; const need = size - off; if (c.length <= need) { c.copy(out, off); off += c.length; this._chunks.shift(); } else { c.copy(out, off, 0, need); this._chunks[0] = c.slice(need); off += need; } } this._length -= size; return out; } /** * Read exactly size bytes or reject on timeout / close. * @param {number} size * @param {number} timeoutMs optional * @returns {Promise<Buffer>} */ read(size, timeoutMs) { if (this._closed) return Promise.reject(new Error('transport closed')); if (this._length >= size) return Promise.resolve(this._consume(size)); return new Promise((resolve, reject) => { const waiter = { size, resolve, reject, timeoutHandle: null }; if (timeoutMs) { waiter.timeoutHandle = setTimeout(() => { this._waiters.delete(waiter); reject(new Error('timeout')); }, timeoutMs); } this._waiters.add(waiter); }); } } exports.BufferAccumulator = BufferAccumulator;