UNPKG

whatwg-streams-impl

Version:

WHATWG Streams Implementation for Node.JS

453 lines (369 loc) 13.4 kB
/** * Abstract operations used in ReadableStream, ReadableStreamDefaultReader, ReadableStreamDefaultController, * ReadableStreamBYOBReader and ReadableByteStreamController interfaces. * * Note: Functions are defined in alphabetical name order. */ console.warn('NOT_IMPLEMENTED: ReadableByteStreamControllerPullInto'); console.warn('NOT_IMPLEMENTED: SetUpReadableByteStreamController'); const assert = require('../assert'); const { state, reader, storedError, disturbed, readableStreamController, readRequests, readIntoRequests, closedPromise, ownerReadableStream, controlledReadableStream, CancelSteps, PullSteps, pulling, pullAgain, pullAlgorithm, started, cancelAlgorithm, strategySizeAlgorithm, strategyHWM, queue, queueTotalSize, closeRequested } = require('../symbols'); const {HasInternalSlot, SetPromiseIsHandled} = require('../helpers'); const {CreatePromise, PromiseResolve: Resolve, PromiseReject: Reject} = require('../promises'); const {ResetQueue, EnqueueValueWithSize} = require('../queue'); function InitializeReadableStream(stream) { stream[state] = 'readable'; stream[reader] = undefined; stream[storedError] = undefined; stream[disturbed] = false; } function IsReadableStream(stream) { if (typeof stream !== 'object' || stream == null) { return false; } return HasInternalSlot(stream, readableStreamController); } function IsReadableStreamBYOBReader(x) { if (typeof x !== 'object') { return false; } return HasInternalSlot(x, readIntoRequests); } function IsReadableStreamDefaultController(x) { if (typeof x !== 'object') { return false; } return HasInternalSlot(x, controlledReadableStream); } function IsReadableStreamDefaultReader(x) { if (typeof x !== 'object') { return false; } return HasInternalSlot(x, readRequests); } function IsReadableStreamDisturbed(stream) { assert(IsReadableStream(stream) == true); return stream[disturbed]; } function IsReadableStreamLocked(stream) { assert(IsReadableStream(stream) === true); if (typeof stream[reader] === 'undefined') { return false; } return true; } function ReadableByteStreamControllerPullInto(controller, view, forAuthorCode) { throw new TypeError('NOT_IMPLEMETED: ReadableByteStreamControllerPullInto not implemented'); } function ReadableStreamAddReadRequest(stream, forAuthorCode) { assert(IsReadableStreamDefaultReader(stream[reader]) === true); assert(stream[state] === 'readable'); const promise = CreatePromise(); const readRequest = { promise, forAuthorCode }; stream[reader][readRequests].push(readRequest); return promise; } function ReadableStreamBYOBReaderRead(_reader, view, forAuthorCode) { if (arguments.length < 3) { forAuthorCode = false; } const stream = _reader[ownerReadableStream]; assert(typeof stream !== 'undefined'); stream[disturbed] = true; if (stream[state] === 'errored') { return Promise.reject(stream[storedError]); } return ReadableByteStreamControllerPullInto(stream[readableStreamController], view, forAuthorCode); } function ReadableStreamCancel(stream, reason) { stream[disturbed] = true; if (stream[state] === 'closed') { return Promise.resolve(undefined); } if (stream[state] === 'errored') { return Promise.reject(stream[storedError]); } ReadableStreamClose(stream); const sourceCancelPromise = stream[readableStreamController][CancelSteps](reason); const returnValue = Promise.resolve(undefined); const fulfillmentHandler = () => returnValue; return sourceCancelPromise.then(fulfillmentHandler); } function ReadableStreamClose(stream) { assert(stream[state] === 'readable'); stream[state] = 'closed'; const _reader = stream[reader]; if (typeof _reader === 'undefined') { return; } if (IsReadableStreamDefaultReader(_reader) == true) { _reader[readRequests].forEach((readRequest) => { Resolve(readRequest['promise'], ReadableStreamCreateReadResult(undefined, true, readRequest['forAuthorCode'])); }); _reader[readRequests] = []; } Resolve(_reader[closedPromise], undefined); } function ReadableStreamCreateReadResult(value, done, forAuthorCode) { const proto = (forAuthorCode ? Object.prototype : null); assert(typeof done === 'boolean'); const obj = Object.create(proto); obj['value'] = value; obj['done'] = done; return obj; } function ReadableStreamDefaultControllerCallPullIfNeeded(controller) { const shouldPull = ReadableStreamDefaultControllerShouldCallPull(controller); if (false == shouldPull) { return; } if (controller[pulling] == true) { controller[pullAgain] = true; return; } assert(controller[pullAgain] === false); controller[pulling] = true; const pullPromise = controller[pullAlgorithm](); pullPromise.then( () => { controller[pulling] = false; if (controller[pullAgain] == true) { controller[pullAgain] = false; ReadableStreamDefaultControllerCallPullIfNeeded(controller); } }, (e) => { console.log('ERROR:', e); ReadableStreamDefaultControllerError(controller, e); } ); } function ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) { const _state = controller[controlledReadableStream][state]; if (controller[closeRequested] == false && _state == 'readable') { return true; } return false; } function ReadableStreamDefaultControllerClearAlgorithms(controller) { controller[pullAlgorithm] = undefined; controller[cancelAlgorithm] = undefined; controller[strategySizeAlgorithm] = undefined; } function ReadableStreamDefaultControllerClose(controller) { const stream = controller[controlledReadableStream]; assert(ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)); controller[closeRequested] = true; if (controller[queue].length <= 0) { ReadableStreamDefaultControllerClearAlgorithms(controller); ReadableStreamClose(stream); } } function ReadableStreamDefaultControllerEnqueue(controller, chunk) { const stream = controller[controlledReadableStream]; assert(ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) == true); if (IsReadableStreamLocked(stream) == true && ReadableStreamGetNumReadRequests(stream) > 0) { ReadableStreamFulfillReadRequest(stream, chunk, false); } else { let chunkSize; try { chunkSize = controller[strategySizeAlgorithm](chunk); } catch (e) { ReadableStreamDefaultControllerError(controller, e); throw e; } try { EnqueueValueWithSize(controller, chunk, chunkSize); } catch (e) { ReadableStreamDefaultControllerError(controller, e); throw e; } } ReadableStreamDefaultControllerCallPullIfNeeded(controller); } function ReadableStreamDefaultControllerError(controller, e) { const stream = controller[controlledReadableStream]; if (stream[state] !== 'readable') { return; } ResetQueue(controller); ReadableStreamDefaultControllerClearAlgorithms(controller); ReadableStreamError(stream, e); } function ReadableStreamDefaultControllerGetDesiredSize(controller) { const stream = controller[controlledReadableStream]; const _state = stream[state]; if (_state === 'errored') { return null; } if (_state === 'closed') { return 0; } return (controller[strategyHWM] - controller[queueTotalSize]); } function ReadableStreamDefaultControllerShouldCallPull(controller) { const stream = controller[controlledReadableStream]; if (false == ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) { return false; } if (controller[started] == false) { return false; } if (IsReadableStreamLocked(stream) && ReadableStreamGetNumReadRequests(stream) > 0) { return true; } const desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller); assert(desiredSize !== null); return (desiredSize > 0); } function ReadableStreamDefaultReaderRead(_reader, forAuthorCode) { if (arguments.length < 2) { forAuthorCode = false; } const stream = _reader[ownerReadableStream]; assert(typeof stream !== 'undefined'); stream[disturbed] = true; if (stream[state] === 'closed') { return Promise.resolve(ReadableStreamCreateReadResult(undefined, true, forAuthorCode)); } if (stream[state] === 'errored') { return Promise.reject(stream[storedError]); } assert(stream[state] === 'readable'); return stream[readableStreamController][PullSteps](forAuthorCode); } function ReadableStreamError(stream, e) { assert(IsReadableStream(stream) === true); assert(stream[state] === 'readable'); stream[state] = 'errored'; stream[storedError] = e; const _reader = stream[reader]; if (typeof _reader === 'undefined') { return; } if (IsReadableStreamDefaultReader(_reader) === true) { _reader[readRequests].forEach((readRequest) => { Reject(readRequest['promise'], e); }); _reader[readRequests] = []; } else { assert(IsReadableStreamBYOBReader(_reader)); _reader[readIntoRequests].forEach((readIntoRequest) => { Reject(readIntoRequest['promise'], e); }); _reader[readIntoRequests] = []; } Reject(_reader[closedPromise], e); SetPromiseIsHandled(_reader[closedPromise], true); } function ReadableStreamFulfillReadRequest(stream, chunk, done) { const _reader = stream[reader]; const readRequest = _reader[readRequests].shift(); Resolve(readRequest['promise'], ReadableStreamCreateReadResult(chunk, done, readRequest['forAuthorCode'])); } function ReadableStreamGetNumReadRequests(stream) { return stream[reader][readRequests].length; } function ReadableStreamReaderGenericInitialize(_reader, stream) { _reader[ownerReadableStream] = stream; stream[reader] = _reader; if (stream[state] === 'readable') { _reader[closedPromise] = CreatePromise(); } else if (stream[state] === 'closed') { _reader[closedPromise] = Promise.resolve(undefined); } else { assert(stream[state] === 'errored'); _reader[closedPromise] = Promise.reject(stream[storedError]); SetPromiseIsHandled(_reader[closedPromise], true); } } function ReadableStreamReaderGenericCancel(_reader, reason) { const stream = _reader[ownerReadableStream]; assert(typeof stream !== 'undefined'); return ReadableStreamCancel(stream, reason); } function ReadableStreamReaderGenericRelease(_reader) { assert(typeof _reader[ownerReadableStream] !== 'undefined'); assert(_reader[ownerReadableStream][reader] === _reader); if (_reader[ownerReadableStream][state] === 'readable') { Reject(_reader[closedPromise], new TypeError('Reader released')); } else { _reader[closedPromise] = Promise.reject(new TypeError('Reader released')); } SetPromiseIsHandled(_reader[closedPromise], true); _reader[ownerReadableStream][reader] = undefined; _reader[ownerReadableStream] = undefined; } function SetUpReadableByteStreamController(stream, controller, _startAlgorithm, _pullAlgorithm, _cancelAlgorithm, highWaterMark, autoAllocateChunkSize) { throw new TypeError('NOT_IMPLEMENTED: SetUpReadableByteStreamController not implemented.'); } function SetUpReadableStreamDefaultController(stream, controller, _startAlgorithm, _pullAlgorithm, _cancelAlgorithm, highWaterMark, _sizeAlgorithm) { assert(typeof stream[readableStreamController] === 'undefined'); controller[controlledReadableStream] = stream; controller[queue] = undefined; controller[queueTotalSize] = undefined; ResetQueue(controller); controller[started] = false; controller[closeRequested] = false; controller[pullAgain] = false; controller[pulling] = false; controller[strategySizeAlgorithm] = _sizeAlgorithm; controller[strategyHWM] = highWaterMark; controller[pullAlgorithm] = _pullAlgorithm; controller[cancelAlgorithm] = _cancelAlgorithm; stream[readableStreamController] = controller; const startResult = _startAlgorithm(); const startPromise = Promise.resolve(startResult); startPromise.then( () => { controller[started] = true; assert(controller[pulling] === false); assert(controller[pullAgain] === false); ReadableStreamDefaultControllerCallPullIfNeeded(controller); }, (reason) => { ReadableStreamDefaultControllerError(controller, reason); } ); } module.exports = { InitializeReadableStream, IsReadableStream, IsReadableStreamBYOBReader, IsReadableStreamDefaultController, IsReadableStreamDefaultReader, IsReadableStreamDisturbed, IsReadableStreamLocked, ReadableStreamAddReadRequest, ReadableStreamBYOBReaderRead, ReadableStreamCancel, ReadableStreamClose, ReadableStreamCreateReadResult, ReadableStreamDefaultControllerCallPullIfNeeded, ReadableStreamDefaultControllerCanCloseOrEnqueue, ReadableStreamDefaultControllerClearAlgorithms, ReadableStreamDefaultControllerClose, ReadableStreamDefaultControllerEnqueue, ReadableStreamDefaultControllerError, ReadableStreamDefaultControllerGetDesiredSize, ReadableStreamDefaultControllerShouldCallPull, ReadableStreamDefaultReaderRead, ReadableStreamError, ReadableStreamFulfillReadRequest, ReadableStreamGetNumReadRequests, ReadableStreamReaderGenericInitialize, ReadableStreamReaderGenericCancel, ReadableStreamReaderGenericRelease, SetUpReadableByteStreamController, SetUpReadableStreamDefaultController };