diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index 3800399c82ad62..76fd8bc7fe2581 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -30,6 +30,7 @@ const { ObjectKeys, ObjectSetPrototypeOf, Promise, + Proxy, SafeSet, Symbol, SymbolAsyncDispose, @@ -102,6 +103,8 @@ const kErroredValue = Symbol('kErroredValue'); const kDefaultEncodingValue = Symbol('kDefaultEncodingValue'); const kDecoderValue = Symbol('kDecoderValue'); const kEncodingValue = Symbol('kEncodingValue'); +const kBuffer = Symbol('kBuffer'); +const kBufferIndex = Symbol('kBufferIndex'); const kEnded = 1 << 9; const kEndEmitted = 1 << 10; @@ -276,8 +279,8 @@ function ReadableState(options, stream, isDuplex) { getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex) : getDefaultHighWaterMark(false); - this.buffer = []; - this.bufferIndex = 0; + this[kBuffer] = []; + this[kBufferIndex] = 0; this.length = 0; this.pipes = []; @@ -561,13 +564,13 @@ function addChunk(stream, state, chunk, addToFront) { // Update the buffer info. state.length += (state[kState] & kObjectMode) !== 0 ? 1 : chunk.length; if (addToFront) { - if (state.bufferIndex > 0) { - state.buffer[--state.bufferIndex] = chunk; + if (state[kBufferIndex] > 0) { + state[kBuffer][--state[kBufferIndex]] = chunk; } else { - state.buffer.unshift(chunk); // Slow path + state[kBuffer].unshift(chunk); // Slow path } } else { - state.buffer.push(chunk); + state[kBuffer].push(chunk); } if ((state[kState] & kNeedReadable) !== 0) @@ -592,14 +595,14 @@ Readable.prototype.setEncoding = function(enc) { // Iterate over current buffer to convert already stored Buffers: let content = ''; - for (const data of state.buffer.slice(state.bufferIndex)) { + for (const data of state[kBuffer].slice(state[kBufferIndex])) { content += decoder.write(data); } - state.buffer.length = 0; - state.bufferIndex = 0; + state[kBuffer].length = 0; + state[kBufferIndex] = 0; if (content !== '') - state.buffer.push(content); + state[kBuffer].push(content); state.length = content.length; return this; }; @@ -633,7 +636,7 @@ function howMuchToRead(n, state) { if (NumberIsNaN(n)) { // Only flow one buffer at a time. if ((state[kState] & kFlowing) !== 0 && state.length) - return state.buffer[state.bufferIndex].length; + return state[kBuffer][state[kBufferIndex]].length; return state.length; } if (n <= state.length) @@ -790,7 +793,7 @@ function onEofChunk(stream, state) { if (decoder) { const chunk = decoder.end(); if (chunk && chunk.length) { - state.buffer.push(chunk); + state[kBuffer].push(chunk); state.length += (state[kState] & kObjectMode) !== 0 ? 1 : chunk.length; } } @@ -1459,7 +1462,7 @@ ObjectDefineProperties(Readable.prototype, { __proto__: null, enumerable: false, get: function() { - return this._readableState && this._readableState.buffer; + return this._readableState && this._readableState[kBuffer]; }, }, @@ -1541,9 +1544,16 @@ ObjectDefineProperties(Readable.prototype, { return this._readableState ? this._readableState.endEmitted : false; }, }, - }); +function normalizeBuffer(state) { + if (state[kBufferIndex] > 0) { + state.splice(0, state[kBufferIndex]); + state[kBufferIndex] = 0; + } +} +const proxyCache = new WeakMap(); + ObjectDefineProperties(ReadableState.prototype, { // Legacy getter for `pipesCount`. pipesCount: { @@ -1568,6 +1578,43 @@ ObjectDefineProperties(ReadableState.prototype, { } }, }, + + // Legacy compat + buffer: { + __proto__: null, + get() { + const r = this._readableState; + let proxy = proxyCache.get(r); + if (!proxy) { + proxy = new Proxy(r[kBuffer], { + __proto__: null, + get(target, name) { + if (name === 'length') { + return target[kBuffer].length - target[kBufferIndex]; + } + if (name === '0') { + return target[kBuffer][target[kBufferIndex]]; + } + normalizeBuffer(target); + return target[name]; + }, + set(target, name, value, receiver) { + normalizeBuffer(target); + target[name] = value; + return true; + } + }); + proxyCache.set(r, proxy); + } + return proxy + }, + }, + bufferIndex: { + __proto__: null, + get() { + return 0; + } + } }); // Exposed for testing purposes only. @@ -1582,10 +1629,10 @@ function fromList(n, state) { if (state.length === 0) return null; - let idx = state.bufferIndex; + let idx = state[kBufferIndex]; let ret; - const buf = state.buffer; + const buf = state[kBuffer]; const len = buf.length; if ((state[kState] & kObjectMode) !== 0) { @@ -1665,13 +1712,13 @@ function fromList(n, state) { } if (idx === len) { - state.buffer.length = 0; - state.bufferIndex = 0; + state[kBuffer].length = 0; + state[kBufferIndex] = 0; } else if (idx > 1024) { - state.buffer.splice(0, idx); - state.bufferIndex = 0; + state[kBuffer].splice(0, idx); + state[kBufferIndex] = 0; } else { - state.bufferIndex = idx; + state[kBufferIndex] = idx; } return ret;