X-Git-Url: https://git.josue.xyz/?p=VSoRC%2F.git;a=blobdiff_plain;f=node_modules%2Fxterm%2Fsrc%2Fcommon%2Finput%2FWriteBuffer.ts;fp=node_modules%2Fxterm%2Fsrc%2Fcommon%2Finput%2FWriteBuffer.ts;h=c7d824585da4c095fcd59b33927e4f23863def6b;hp=0000000000000000000000000000000000000000;hb=4339da12467b75fb8b6ca831f4bf0081c485ed2c;hpb=af450fde25a9ccf4767b29254c463ffb8ef25237 diff --git a/node_modules/xterm/src/common/input/WriteBuffer.ts b/node_modules/xterm/src/common/input/WriteBuffer.ts new file mode 100644 index 0000000..c7d8245 --- /dev/null +++ b/node_modules/xterm/src/common/input/WriteBuffer.ts @@ -0,0 +1,110 @@ + +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +declare const setTimeout: (handler: () => void, timeout?: number) => void; + +/** + * Safety watermark to avoid memory exhaustion and browser engine crash on fast data input. + * Enable flow control to avoid this limit and make sure that your backend correctly + * propagates this to the underlying pty. (see docs for further instructions) + * Since this limit is meant as a safety parachute to prevent browser crashs, + * it is set to a very high number. Typically xterm.js gets unresponsive with + * a 100 times lower number (>500 kB). + */ +const DISCARD_WATERMARK = 50000000; // ~50 MB + +/** + * The max number of ms to spend on writes before allowing the renderer to + * catch up with a 0ms setTimeout. A value of < 33 to keep us close to + * 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS + * depends on the time it takes for the renderer to draw the frame. + */ +const WRITE_TIMEOUT_MS = 12; + +/** + * Threshold of max held chunks in the write buffer, that were already processed. + * This is a tradeoff between extensive write buffer shifts (bad runtime) and high + * memory consumption by data thats not used anymore. + */ +const WRITE_BUFFER_LENGTH_THRESHOLD = 50; + +export class WriteBuffer { + private _writeBuffer: (string | Uint8Array)[] = []; + private _callbacks: ((() => void) | undefined)[] = []; + private _pendingData = 0; + private _bufferOffset = 0; + + constructor(private _action: (data: string | Uint8Array) => void) { } + + public writeSync(data: string | Uint8Array): void { + // force sync processing on pending data chunks to avoid in-band data scrambling + // does the same as innerWrite but without event loop + if (this._writeBuffer.length) { + for (let i = this._bufferOffset; i < this._writeBuffer.length; ++i) { + const data = this._writeBuffer[i]; + const cb = this._callbacks[i]; + this._action(data); + if (cb) cb(); + } + // reset all to avoid reprocessing of chunks with scheduled innerWrite call + this._writeBuffer = []; + this._callbacks = []; + this._pendingData = 0; + // stop scheduled innerWrite by offset > length condition + this._bufferOffset = 0x7FFFFFFF; + } + // handle current data chunk + this._action(data); + } + + public write(data: string | Uint8Array, callback?: () => void): void { + if (this._pendingData > DISCARD_WATERMARK) { + throw new Error('write data discarded, use flow control to avoid losing data'); + } + + // schedule chunk processing for next event loop run + if (!this._writeBuffer.length) { + this._bufferOffset = 0; + setTimeout(() => this._innerWrite()); + } + + this._pendingData += data.length; + this._writeBuffer.push(data); + this._callbacks.push(callback); + } + + protected _innerWrite(): void { + const startTime = Date.now(); + while (this._writeBuffer.length > this._bufferOffset) { + const data = this._writeBuffer[this._bufferOffset]; + const cb = this._callbacks[this._bufferOffset]; + this._bufferOffset++; + + this._action(data); + this._pendingData -= data.length; + if (cb) cb(); + + if (Date.now() - startTime >= WRITE_TIMEOUT_MS) { + break; + } + } + if (this._writeBuffer.length > this._bufferOffset) { + // Allow renderer to catch up before processing the next batch + // trim already processed chunks if we are above threshold + if (this._bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) { + this._writeBuffer = this._writeBuffer.slice(this._bufferOffset); + this._callbacks = this._callbacks.slice(this._bufferOffset); + this._bufferOffset = 0; + } + setTimeout(() => this._innerWrite(), 0); + } else { + this._writeBuffer = []; + this._callbacks = []; + this._pendingData = 0; + this._bufferOffset = 0; + } + } +}