Skip to content

Commit 7764b84

Browse files
committed
stream: Break up the onread function
A primary motivation of this is to make the onread function more inline-friendly, but also to make it more easy to explore not having onread at all, in favor of always using push() to signal the end of reading.
1 parent c116120 commit 7764b84

File tree

1 file changed

+83
-81
lines changed

1 file changed

+83
-81
lines changed

lib/_stream_readable.js

+83-81
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,51 @@ function Readable(options) {
105105
// similar to how Writable.write() returns true if you should
106106
// write() some more.
107107
Readable.prototype.push = function(chunk) {
108-
var rs = this._readableState;
109-
rs.onread(null, chunk);
110-
111-
// if it's past the high water mark, we can push in some more.
112-
// Also, if we have no data yet, we can stand some
113-
// more bytes. This is to work around cases where hwm=0,
114-
// such as the repl. Also, if the push() triggered a
115-
// readable event, and the user called read(largeNumber) such that
116-
// needReadable was set, then we ought to push more, so that another
117-
// 'readable' event will be triggered.
118-
return rs.needReadable ||
119-
rs.length < rs.highWaterMark ||
120-
rs.length === 0;
108+
var state = this._readableState;
109+
return readableAddChunk(this, state, chunk);
121110
};
122111

112+
function readableAddChunk(stream, state, chunk) {
113+
state.reading = false;
114+
115+
var er = chunkInvalid(state, chunk);
116+
if (er) {
117+
stream.emit('error', er);
118+
} else if (chunk === null || chunk === undefined) {
119+
onreadEof(stream, state);
120+
} else if (state.objectMode || chunk && chunk.length > 0) {
121+
if (state.decoder)
122+
chunk = state.decoder.write(chunk);
123+
124+
// update the buffer info.
125+
state.length += state.objectMode ? 1 : chunk.length;
126+
state.buffer.push(chunk);
127+
128+
if (state.needReadable)
129+
emitReadable(stream);
130+
131+
maybeReadMore(stream, state);
132+
}
133+
134+
return needMoreData(state);
135+
}
136+
137+
138+
139+
// if it's past the high water mark, we can push in some more.
140+
// Also, if we have no data yet, we can stand some
141+
// more bytes. This is to work around cases where hwm=0,
142+
// such as the repl. Also, if the push() triggered a
143+
// readable event, and the user called read(largeNumber) such that
144+
// needReadable was set, then we ought to push more, so that another
145+
// 'readable' event will be triggered.
146+
function needMoreData(state) {
147+
return !state.ended &&
148+
(state.needReadable ||
149+
state.length < state.highWaterMark ||
150+
state.length === 0);
151+
}
152+
123153
// backwards compatibility.
124154
Readable.prototype.setEncoding = function(enc) {
125155
if (!StringDecoder)
@@ -263,15 +293,20 @@ Readable.prototype.read = function(n) {
263293
return ret;
264294
};
265295

296+
// This is the function passed to _read(n,cb) as the callback.
297+
// It should be called exactly once for every _read() call.
266298
function onread(stream, er, chunk) {
267299
var state = stream._readableState;
268300
var sync = state.sync;
269301

270-
// If we get something that is not a buffer, string, null, or undefined,
271-
// and we're not in objectMode, then that's an error.
272-
// Otherwise stream chunks are all considered to be of length=1, and the
273-
// watermarks determine how many objects to keep in the buffer, rather than
274-
// how many bytes or characters.
302+
if (er)
303+
stream.emit('error', er);
304+
else
305+
stream.push(chunk);
306+
}
307+
308+
function chunkInvalid(state, chunk) {
309+
var er = null;
275310
if (!Buffer.isBuffer(chunk) &&
276311
'string' !== typeof chunk &&
277312
chunk !== null &&
@@ -280,68 +315,26 @@ function onread(stream, er, chunk) {
280315
!er) {
281316
er = new TypeError('Invalid non-string/buffer chunk');
282317
}
318+
return er;
319+
}
283320

284-
state.reading = false;
285-
if (er)
286-
return stream.emit('error', er);
287321

288-
if (chunk === null || chunk === undefined) {
289-
// eof
290-
state.ended = true;
291-
if (state.decoder) {
292-
chunk = state.decoder.end();
293-
if (chunk && chunk.length) {
294-
state.buffer.push(chunk);
295-
state.length += state.objectMode ? 1 : chunk.length;
296-
}
322+
function onreadEof(stream, state) {
323+
state.ended = true;
324+
if (state.decoder) {
325+
var chunk = state.decoder.end();
326+
if (chunk && chunk.length) {
327+
state.buffer.push(chunk);
328+
state.length += state.objectMode ? 1 : chunk.length;
297329
}
298-
299-
// if we've ended and we have some data left, then emit
300-
// 'readable' now to make sure it gets picked up.
301-
if (state.length > 0)
302-
emitReadable(stream);
303-
else
304-
endReadable(stream);
305-
return;
306-
}
307-
308-
// at this point, if we got a zero-length buffer or string,
309-
// and we're not in object-mode, then there's really no point
310-
// continuing. it means that there is nothing to read right
311-
// now, but as we have not received the EOF-signaling null,
312-
// we're not ended. we've already unset the reading flag,
313-
// so just get out of here.
314-
if (!state.objectMode &&
315-
(chunk || typeof chunk === 'string') &&
316-
0 === chunk.length)
317-
return;
318-
319-
if (state.decoder)
320-
chunk = state.decoder.write(chunk);
321-
322-
// update the buffer info.
323-
state.length += state.objectMode ? 1 : chunk.length;
324-
state.buffer.push(chunk);
325-
326-
// if we haven't gotten any data,
327-
// and we haven't ended, then don't bother telling the user
328-
// that it's time to read more data. Otherwise, emitting 'readable'
329-
// probably will trigger another stream.read(), which can trigger
330-
// another _read(n,cb) before this one returns!
331-
if (state.length === 0) {
332-
state.reading = true;
333-
stream._read(state.bufferSize, state.onread);
334-
return;
335330
}
336331

337-
if (state.needReadable)
332+
// if we've ended and we have some data left, then emit
333+
// 'readable' now to make sure it gets picked up.
334+
if (state.length > 0)
338335
emitReadable(stream);
339-
else if (state.sync)
340-
process.nextTick(function() {
341-
maybeReadMore(stream, state);
342-
});
343336
else
344-
maybeReadMore(stream, state);
337+
endReadable(stream);
345338
}
346339

347340
// Don't emit readable right away in sync mode, because this can trigger
@@ -365,17 +358,26 @@ function emitReadable(stream) {
365358
function emitReadable_(stream) {
366359
var state = stream._readableState;
367360
stream.emit('readable');
368-
maybeReadMore(stream, state);
369361
}
370362

363+
364+
// at this point, the user has presumably seen the 'readable' event,
365+
// and called read() to consume some data. that may have triggered
366+
// in turn another _read(n,cb) call, in which case reading = true if
367+
// it's in progress.
368+
// However, if we're not ended, or reading, and the length < hwm,
369+
// then go ahead and try to read some more right now preemptively.
371370
function maybeReadMore(stream, state) {
372-
// at this point, the user has presumably seen the 'readable' event,
373-
// and called read() to consume some data. that may have triggered
374-
// in turn another _read(n,cb) call, in which case reading = true if
375-
// it's in progress.
376-
// However, if we're not ended, or reading, and the length < hwm,
377-
// then go ahead and try to read some more right now preemptively.
378-
if (!state.reading && !state.ending && !state.ended &&
371+
if (state.sync)
372+
process.nextTick(function() {
373+
maybeReadMore_(stream, state);
374+
});
375+
else
376+
maybeReadMore_(stream, state);
377+
}
378+
379+
function maybeReadMore_(stream, state) {
380+
if (!state.reading && !state.ended &&
379381
state.length < state.highWaterMark) {
380382
stream.read(0);
381383
}

0 commit comments

Comments
 (0)