├── .editorconfig ├── .gitignore ├── Makefile ├── test ├── index.html ├── package.json ├── fuse.ts ├── tsconfig.json └── index.ts ├── tsconfig.json ├── strategy.ts ├── util.ts ├── defer.ts ├── writable_stream_test.ts ├── README.md ├── readable_stream_request.ts ├── readable_stream_test.ts ├── readable_stream_byob_reader.ts ├── misc.ts ├── readable_stream_reader.ts ├── transform_stream.ts ├── transform_stream_controller.ts ├── writable_stream_writer.ts ├── writable_stream_controller.ts ├── readable_stream_controller.ts ├── writable_stream.ts ├── readable_stream.ts └── readable_byte_stream_controller.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.ts] 2 | indent_size = 2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | deno.d.ts 2 | .idea 3 | commands 4 | dist 5 | node_modules 6 | .fusebox 7 | .idea -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | deno readable_stream_test.ts 4 | deno writable_stream_test.ts -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "fuse-box": "^3.6.0", 8 | "typescript": "^3.2.4" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fuse.ts: -------------------------------------------------------------------------------- 1 | const { FuseBox } = require("fuse-box"); 2 | 3 | const fuse = FuseBox.init({ 4 | homeDir: ".", 5 | output: "dist/$name.js" 6 | }); 7 | 8 | fuse.bundle("app").instructions(`> index.ts`); 9 | 10 | fuse.run(); 11 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES7", 5 | "jsx": "react", 6 | "lib": [ 7 | "es2018", 8 | "esnext", 9 | "dom" 10 | ], 11 | "importHelpers": true, 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true 14 | } 15 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "baseUrl": ".", 5 | "lib": [ 6 | "es2018", 7 | "es2017.typedarrays", 8 | "es2017", 9 | "es2016", 10 | "dom", 11 | "dom.iterable" 12 | ], 13 | "paths": { 14 | "deno": ["./deno.d.ts"] 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /strategy.ts: -------------------------------------------------------------------------------- 1 | export interface QueuingStrategy { 2 | readonly highWaterMark?: number; 3 | readonly size?: (chunk) => number; 4 | } 5 | 6 | export class ByteLengthQueuingStrategy implements QueuingStrategy { 7 | constructor({ highWaterMark }) { 8 | this.highWaterMark = highWaterMark; 9 | } 10 | 11 | highWaterMark: number; 12 | 13 | size(chunk: { byteLength: number }): number { 14 | return chunk.byteLength; 15 | } 16 | } 17 | 18 | export class CountQueuingStrategy implements QueuingStrategy { 19 | constructor({ highWaterMark }) { 20 | this.highWaterMark = highWaterMark; 21 | } 22 | 23 | highWaterMark: number; 24 | 25 | size(_): number { 26 | return 1; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /util.ts: -------------------------------------------------------------------------------- 1 | export function Assert(cond: boolean, desc?: string) { 2 | if (cond === false) throw new Error(desc); 3 | } 4 | 5 | export function isArrayBufferView(a): a is ArrayBufferView { 6 | return ( 7 | a instanceof Int8Array || 8 | a instanceof Uint8Array || 9 | a instanceof Uint8ClampedArray || 10 | a instanceof Int16Array || 11 | a instanceof Uint16Array || 12 | a instanceof Int32Array || 13 | a instanceof Uint32Array || 14 | a instanceof Float32Array || 15 | a instanceof Float64Array || 16 | a instanceof DataView 17 | ); 18 | } 19 | 20 | export function isAbortSignal(x): x is domTypes.AbortSignal { 21 | return typeof x === "object" && x.hasOwnProperty("aborted"); 22 | } 23 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | (() => { 2 | const src = "012356789"; 3 | const stream = new ReadableStream( 4 | { 5 | start(controller) { 6 | let i = 0; 7 | let id; 8 | id = setInterval(() => { 9 | if (i >= src.length) { 10 | controller.close(); 11 | clearInterval(id); 12 | return; 13 | } 14 | const chunk = src[i++]; 15 | // キューに追加する。 16 | controller.enqueue(chunk); 17 | }, 1000); 18 | } 19 | }, 20 | { 21 | size(chunk) { 22 | console.log("size, ", chunk); 23 | return 1; 24 | } 25 | } 26 | ); 27 | const rd = stream.getReader(); 28 | let result = ""; 29 | const readChunk = ({ value, done }: { value: string; done: boolean }) => { 30 | console.log(`value: ${value}, done: ${done}`); 31 | result += value; 32 | if (!done) rd.read().then(readChunk); 33 | }; 34 | rd.read().then(readChunk); 35 | })(); 36 | -------------------------------------------------------------------------------- /defer.ts: -------------------------------------------------------------------------------- 1 | export const PromiseState = Symbol("PromiseState"); 2 | export type Defer = { 3 | resolve(t?: T); 4 | reject(e?); 5 | [PromiseState]: string; 6 | } & Promise; 7 | 8 | export function defer(): Defer { 9 | let res, rej; 10 | const promise = new Promise((resolve, reject) => { 11 | res = resolve; 12 | rej = reject; 13 | }); 14 | const src = { resolve: null, reject: null, [PromiseState]: "pending" }; 15 | src.resolve = (...args) => { 16 | res(...args); 17 | src[PromiseState] = "resolved"; 18 | }; 19 | src.reject = (...args) => { 20 | rej(...args); 21 | src[PromiseState] = "rejected"; 22 | }; 23 | return Object.assign(promise, src); 24 | } 25 | 26 | export function rejectDefer(e): Defer { 27 | return Object.assign(Promise.reject(e), { 28 | resolve: () => {}, 29 | reject: () => {}, 30 | [PromiseState]: "rejected" as "rejected" 31 | }); 32 | } 33 | 34 | export function resolveDefer(e) { 35 | return Object.assign(Promise.resolve(e), { 36 | resolve: () => {}, 37 | reject: () => {}, 38 | [PromiseState]: "resolved" as "resolved" 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /writable_stream_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEqual } from "https://deno.land/x/pretty_assert/mod.ts"; 2 | import { test } from "https://deno.land/x/testing@v0.2.6/mod.ts"; 3 | import { WritableStream } from "./writable_stream.ts"; 4 | import "./writable_stream_controller.ts" 5 | import "./writable_stream_writer.ts" 6 | import { ReadableStream } from "./readable_stream.ts"; 7 | 8 | test(async function testWritableStream() { 9 | const src = [0, 1, 2, 3, 4, 5]; 10 | let i = 0; 11 | const chunks = []; 12 | const readable = new ReadableStream({ 13 | pull: controller => { 14 | controller.enqueue(src[i]); 15 | i++; 16 | if (i >= src.length) { 17 | controller.close(); 18 | } 19 | } 20 | }); 21 | const writable = new WritableStream({ 22 | write: chunk => { 23 | chunks.push(chunk); 24 | } 25 | }); 26 | await readable.pipeTo(writable); 27 | assertEqual(chunks, src); 28 | assertEqual(readable.state, "closed"); 29 | assertEqual(writable.state, "closed"); 30 | }); 31 | 32 | test(async function testWritableStreamError() { 33 | const chunks = []; 34 | const readable = new ReadableStream({ 35 | pull: controller => { 36 | controller.error("error"); 37 | } 38 | }); 39 | const writable = new WritableStream({ 40 | write: chunk => { 41 | chunks.push(chunk); 42 | } 43 | }); 44 | await readable.pipeTo(writable); 45 | assertEqual(readable.state, "errored"); 46 | assertEqual(writable.state, "errored"); 47 | }); 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deno-streams 2 | [WIP] WHATWG streams API by TypeScript for deno 3 | 4 | See: https://streams.spec.whatwg.org/ 5 | 6 | # compatibility table 7 | 8 | - 🔰ReadableStream 9 | - 🔰 `new ReadableStream(underlyingSource = {}, strategy = {})` 10 | - 🔰 `get locked` 11 | - 🔰 `cancel(reason)`n 12 | - 🔰 `pipeThrough({ writable, readable }, { preventClose, preventAbort, preventCancel, signal } = {})` 13 | - 🔰 `pipeTo(dest, { preventClose, preventAbort, preventCancel, signal } = {})` 14 | - 🔰 `tee()` 15 | - 🔰WritableStream 16 | - 🔰`new WritableStream( underlyingSink = {}, strategy = {} )` 17 | - 🔰`get locked` 18 | - 🔰`abort( reason )` 19 | - 🔰`getWriter()` 20 | - 🔰TransformStream 21 | - 🔰 `new TransformStream( transformer = {}, writableStrategy = {}, readableStrategy = {} )` 22 | - 🔰 `get readable` 23 | - 🔰 `get writable` 24 | - 🔰ByteLengthQueuingStrategy 25 | - 🔰CountQueuingStrategy 26 | # Usage 27 | 28 | ```ts 29 | 30 | import { ReadableStream } from "https://denopkg.com/keroxp/deno-streams/readable_stream.ts" 31 | import { WritableStream } from "https://denopkg.com/keroxp/deno-streams/writable_stream.ts" 32 | 33 | const src = [0,1,2,3,4] 34 | let i = 0 35 | const dest = [] 36 | const readable = new ReadableStream({ 37 | pull: controller => { 38 | controller.enqueue(src[i++]) 39 | if (i >= src.length) controller.close() 40 | } 41 | }) 42 | const writable = new WritableStream({ 43 | write: chunk => { 44 | dest.push(chunk) 45 | } 46 | }) 47 | await readable.pipeTo(writable) 48 | console.log(dest) // => [0,1,2,3,4] 49 | ``` 50 | -------------------------------------------------------------------------------- /readable_stream_request.ts: -------------------------------------------------------------------------------- 1 | import { IsDetachedBuffer } from "./misc.ts"; 2 | import { Assert } from "./util.ts"; 3 | import { 4 | ReadableByteStreamController, 5 | IsReadableByteStreamController, 6 | ReadableByteStreamControllerRespond, 7 | ReadableByteStreamControllerRespondWithNewView 8 | } from "./readable_byte_stream_controller.ts"; 9 | 10 | export interface ReadableStreamBYOBRequest { 11 | readonly view: Uint8Array; 12 | 13 | respond(bytesWritten: number): void; 14 | 15 | respondWithNewView(view: Uint8Array): void; 16 | } 17 | 18 | export class ReadableStreamBYOBRequestImpl 19 | implements ReadableStreamBYOBRequest { 20 | constructor() { 21 | throw new TypeError(); 22 | } 23 | 24 | associatedReadableByteStreamController: ReadableByteStreamController; 25 | _view: Uint8Array; 26 | get view() { 27 | if (!IsReadableStreamBYOBRequest(this)) { 28 | throw new TypeError(); 29 | } 30 | return this._view; 31 | } 32 | 33 | respond(bytesWritten: number) { 34 | if (!IsReadableStreamBYOBRequest(this)) { 35 | throw new TypeError(); 36 | } 37 | if (this.associatedReadableByteStreamController === void 0) { 38 | throw new TypeError(); 39 | } 40 | if (IsDetachedBuffer(this._view)) { 41 | throw new TypeError(); 42 | } 43 | return ReadableByteStreamControllerRespond( 44 | this.associatedReadableByteStreamController, 45 | bytesWritten 46 | ); 47 | } 48 | 49 | respondWithNewView(view) { 50 | if (!IsReadableStreamBYOBRequest(this)) { 51 | throw new TypeError(); 52 | } 53 | if (this.associatedReadableByteStreamController === void 0) { 54 | throw new TypeError(); 55 | } 56 | if (typeof view !== "object") { 57 | throw new TypeError(); 58 | } 59 | // if (view.hasOwnProperty("ViewedArrayBuffer")) { 60 | // throw new TypeError(); 61 | // } 62 | if (IsDetachedBuffer(this._view)) { 63 | throw new TypeError(); 64 | } 65 | return ReadableByteStreamControllerRespondWithNewView( 66 | this.associatedReadableByteStreamController, 67 | view 68 | ); 69 | } 70 | } 71 | 72 | export function IsReadableStreamBYOBRequest(x): x is ReadableStreamBYOBRequest { 73 | return ( 74 | typeof x === "object" && 75 | x.hasOwnProperty("associatedReadableByteStreamController") 76 | ); 77 | } 78 | 79 | export function SetUpReadableStreamBYOBRequest( 80 | request: ReadableStreamBYOBRequestImpl, 81 | controller: ReadableByteStreamController, 82 | view 83 | ) { 84 | Assert(IsReadableByteStreamController(controller)); 85 | Assert(typeof view === "object"); 86 | Assert(view.hasOwnProperty("ViewedArrayBuffer")); 87 | Assert(view.ViewedArrayBuffer !== null); 88 | request.associatedReadableByteStreamController = controller; 89 | request._view = view; 90 | } 91 | -------------------------------------------------------------------------------- /readable_stream_test.ts: -------------------------------------------------------------------------------- 1 | import {assertEqual, test} from "https://deno.land/x/testing@v0.2.6/mod.ts"; 2 | import {ReadableStream} from "./readable_stream.ts"; 3 | import {ReadableStreamBYOBReader} from "./readable_stream_byob_reader.ts"; 4 | import {ReadableStreamDefaultReader} from "./readable_stream_reader.ts"; 5 | 6 | test(async function testReadableStream() { 7 | const src = [0, 1, 2, 3, 4, 5, 6]; 8 | let i = 0; 9 | const stream = new ReadableStream({ 10 | start: controller => { 11 | controller.enqueue(src[i++]) 12 | }, 13 | pull: controller => { 14 | controller.enqueue(src[i++]); 15 | if (i >= src.length) { 16 | controller.close(); 17 | return; 18 | } 19 | } 20 | }); 21 | const reader = stream.getReader() as ReadableStreamDefaultReader; 22 | for (let i = 0; i < src.length + 1; i++) { 23 | const {value, done} = await reader.read(); 24 | if (i < 7) { 25 | assertEqual(value, i); 26 | } else { 27 | assertEqual(true, done); 28 | } 29 | } 30 | }); 31 | 32 | test(async function testReadableStream2() { 33 | const src = [0, 1, 2, 3, 4, 5]; 34 | let i = 0; 35 | const stream = new ReadableStream( 36 | { 37 | pull: controller => { 38 | console.log(controller.enqueue); 39 | controller.enqueue(src.slice(i, i + 2)); 40 | i += 2; 41 | if (i >= src.length) { 42 | controller.close(); 43 | return; 44 | } 45 | } 46 | }, 47 | { 48 | size: (chunk: number[]) => { 49 | return chunk.length; 50 | } 51 | } 52 | ); 53 | const reader = stream.getReader()as ReadableStreamDefaultReader; 54 | for (let i = 0; i < src.length + 1; i += 2) { 55 | const {value, done} = await reader.read(); 56 | if (i < src.length) { 57 | assertEqual(value, [i, i + 1]); 58 | } else { 59 | assertEqual(true, done); 60 | } 61 | } 62 | }); 63 | 64 | test(async function testReadableStream3() { 65 | const src = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); 66 | const stream = new ReadableStream({ 67 | type: "bytes", 68 | start: controller => { 69 | controller.enqueue(src); 70 | }, 71 | pull: controller => { 72 | controller.close(); 73 | } 74 | }); 75 | const reader = stream.getReader({mode: "byob"}); 76 | assertEqual(reader.constructor, ReadableStreamBYOBReader); 77 | const buf = new Uint8Array(4); 78 | const res1 = await reader.read(buf); 79 | assertEqual(res1.done, false); 80 | assertEqual([...buf], [0, 1, 2, 3]); 81 | const res2 = await reader.read(buf); 82 | assertEqual(res2.done, false); 83 | assertEqual([...buf], [4, 5, 6, 7]); 84 | const res3 = await reader.read(buf); 85 | assertEqual(res3.done, true); 86 | assertEqual(stream.state, "closed"); 87 | }); 88 | 89 | test(async function testReadableStream4() { 90 | const src = new Uint16Array([0x1234, 0x5678]); 91 | const stream = new ReadableStream({ 92 | type: "bytes", 93 | start: controller => { 94 | controller.enqueue(src); 95 | }, 96 | pull: controller => { 97 | controller.close(); 98 | } 99 | }); 100 | const reader = stream.getReader({mode: "byob"}); 101 | assertEqual(reader.constructor, ReadableStreamBYOBReader); 102 | const buf = new Uint8Array(2); 103 | const res1 = await reader.read(buf); 104 | assertEqual(res1.done, false); 105 | let view = new DataView(buf.buffer); 106 | assertEqual(view.getInt16(0, true), 0x1234); 107 | const res2 = await reader.read(buf); 108 | view = new DataView(buf.buffer); 109 | assertEqual(res2.done, false); 110 | assertEqual(view.getInt16(0, true), 0x5678); 111 | const res3 = await reader.read(buf); 112 | assertEqual(res3.done, true); 113 | assertEqual(stream.state, "closed"); 114 | }); 115 | -------------------------------------------------------------------------------- /readable_stream_byob_reader.ts: -------------------------------------------------------------------------------- 1 | import {Defer} from "./defer.ts"; 2 | import { 3 | IsReadableStream, 4 | IsReadableStreamLocked, 5 | ReadableStream, 6 | ReadableStreamReadResult 7 | } from "./readable_stream.ts"; 8 | import {Assert, isArrayBufferView} from "./util.ts"; 9 | import { 10 | ReadableStreamReader, 11 | ReadableStreamReaderGenericCancel, 12 | ReadableStreamReaderGenericInitialize, 13 | ReadableStreamReaderGenericRelease 14 | } from "./readable_stream_reader.ts"; 15 | import { 16 | IsReadableByteStreamController, 17 | ReadableByteStreamControllerPullInto, 18 | ReadableByteStreamController 19 | } from "./readable_byte_stream_controller.ts"; 20 | 21 | export class ReadableStreamBYOBReader 22 | implements ReadableStreamReader { 23 | readIntoRequests: { promise: Defer; forAuthorCode: boolean }[]; 24 | 25 | constructor(stream: ReadableStream) { 26 | if (!IsReadableStream(stream)) { 27 | throw new TypeError(); 28 | } 29 | if (!IsReadableByteStreamController(stream.readableStreamController)) { 30 | throw new TypeError(); 31 | } 32 | if (IsReadableStreamLocked(stream)) { 33 | throw new TypeError(); 34 | } 35 | ReadableStreamReaderGenericInitialize(this, stream); 36 | this.readIntoRequests = []; 37 | } 38 | 39 | get closed(): Promise { 40 | if (!IsReadableStreamBYOBReader(this)) { 41 | return Promise.reject(new TypeError()); 42 | } 43 | return this.closedPromise; 44 | } 45 | 46 | closedPromise: Defer; 47 | ownerReadableStream: ReadableStream; 48 | 49 | cancel(reason): Promise { 50 | if (!IsReadableStreamBYOBReader(this)) { 51 | return Promise.reject(new TypeError()); 52 | } 53 | if (this.ownerReadableStream === void 0) { 54 | return Promise.reject(new TypeError()); 55 | } 56 | return ReadableStreamReaderGenericCancel(this, reason); 57 | } 58 | 59 | read( 60 | view: T 61 | ): Promise> { 62 | if (!IsReadableStreamBYOBReader(this)) { 63 | return Promise.reject(new TypeError()); 64 | } 65 | if (this.ownerReadableStream === void 0) { 66 | return Promise.reject(new TypeError()); 67 | } 68 | if (typeof view !== "object") { 69 | return Promise.reject(new TypeError()); 70 | } 71 | if (!isArrayBufferView(view)) { 72 | throw new TypeError("view is not ArrayBufferView: " + view); 73 | } 74 | return ReadableStreamBYOBReaderRead(this, view, true); 75 | } 76 | 77 | releaseLock() { 78 | if (!IsReadableStreamBYOBReader(this)) { 79 | return Promise.reject(new TypeError()); 80 | } 81 | if (this.ownerReadableStream === void 0) { 82 | return Promise.reject(new TypeError()); 83 | } 84 | if (this.readIntoRequests.length > 0) { 85 | throw new TypeError(); 86 | } 87 | ReadableStreamReaderGenericRelease(this); 88 | } 89 | } 90 | 91 | export function IsReadableStreamBYOBReader(a): a is ReadableStreamBYOBReader { 92 | return typeof a === "object" && a.hasOwnProperty("readIntoRequests"); 93 | } 94 | 95 | export function ReadableStreamBYOBReaderRead( 96 | reader: ReadableStreamBYOBReader, 97 | view: ArrayBufferView, 98 | forAuthorCode?: boolean 99 | ) { 100 | if (forAuthorCode === void 0) { 101 | forAuthorCode = false; 102 | } 103 | const stream = reader.ownerReadableStream; 104 | Assert(stream !== void 0); 105 | stream.disturbed = true; 106 | if (stream.state === "errored") { 107 | return Promise.reject(stream.storedError); 108 | } 109 | Assert(stream.state === "readable"); 110 | return ReadableByteStreamControllerPullInto( 111 | stream.readableStreamController as ReadableByteStreamController, 112 | view, 113 | forAuthorCode 114 | ); 115 | } 116 | -------------------------------------------------------------------------------- /misc.ts: -------------------------------------------------------------------------------- 1 | import { Assert } from "./util.ts"; 2 | import { UnderlyingSource } from "./readable_stream.ts"; 3 | 4 | export interface Queueable { 5 | queue: any[]; 6 | queueTotalSize: number; 7 | } 8 | 9 | export function isQueuable(x): x is Queueable { 10 | return ( 11 | typeof x === "object" && 12 | x.hasOwnProperty("queue") && 13 | x.hasOwnProperty("queueTotalSize") 14 | ); 15 | } 16 | 17 | export function DequeueValue(container: Queueable) { 18 | Assert(isQueuable(container)); 19 | Assert(container.queue.length > 0); 20 | const pair = container.queue.shift(); 21 | container.queueTotalSize -= pair.size; 22 | if (container.queueTotalSize < 0) { 23 | container.queueTotalSize = 0; 24 | } 25 | return pair.value; 26 | } 27 | 28 | export function EnqueueValueWithSize( 29 | container: Queueable, 30 | value: any, 31 | size: number 32 | ) { 33 | Assert(isQueuable(container)); 34 | if (!Number.isFinite(size) || size < 0) { 35 | throw new RangeError("invalid size: " + size); 36 | } 37 | container.queue.push({ value, size }); 38 | container.queueTotalSize += size; 39 | } 40 | 41 | export function PeekQueueValue(container: Queueable) { 42 | Assert(isQueuable(container)); 43 | Assert(container.queue.length > 0); 44 | return container.queue[0].value; 45 | } 46 | 47 | export function ResetQueue(container: Queueable) { 48 | container.queue = []; 49 | container.queueTotalSize = 0; 50 | } 51 | 52 | export function CreateAlgorithmFromUnderlyingMethod( 53 | underlyingObject: UnderlyingSource, 54 | methodName: string | symbol, 55 | algoArgCount: number, 56 | ...extraArgs 57 | ): (...args) => any { 58 | Assert(underlyingObject !== void 0); 59 | //assert(IsP) 60 | Assert(algoArgCount === 0 || algoArgCount === 1); 61 | Assert(Array.isArray(extraArgs)); 62 | const method = underlyingObject[methodName]; 63 | if (method !== void 0) { 64 | if (typeof method["call"] !== "function") { 65 | throw new TypeError(); 66 | } 67 | if (algoArgCount === 0) { 68 | return () => PromiseCall(method, underlyingObject, ...extraArgs); 69 | } 70 | return arg => PromiseCall(method, underlyingObject, arg, ...extraArgs); 71 | } 72 | return () => Promise.resolve(void 0); 73 | } 74 | 75 | export function InvokeOrNoop(O, P: string | symbol, ...args) { 76 | Assert(O !== void 0); 77 | Assert(typeof P === "string" || typeof P === "symbol"); 78 | Assert(Array.isArray(args)); 79 | const method = O[P]; 80 | if (method === void 0) { 81 | return void 0; 82 | } 83 | return method.call(O, ...args); 84 | } 85 | 86 | export function IsFiniteNonNegativeNumber(v) { 87 | return IsNonNegativeNumber(v) && v == Number.POSITIVE_INFINITY; 88 | } 89 | 90 | export function IsNonNegativeNumber(v) { 91 | return typeof v === "number" && !Number.isNaN(v) && v >= 0; 92 | } 93 | 94 | export function PromiseCall(F: { call: (o, ...args) => any }, V, ...args) { 95 | Assert(typeof F.call === "function"); 96 | Assert(V !== void 0); 97 | Assert(Array.isArray(args)); 98 | try { 99 | const ret = F.call(V, ...args); 100 | return Promise.resolve(ret); 101 | } catch (e) { 102 | return Promise.reject(e); 103 | } 104 | } 105 | 106 | export function TransferArrayBuffer(O: ArrayBuffer): ArrayBuffer { 107 | Assert(typeof O === "object"); 108 | // TODO: native transferring needed 109 | return O; 110 | } 111 | 112 | export function ValidateAndNormalizeHighWaterMark(highWaterMark?: number) { 113 | if (highWaterMark === void 0) { 114 | highWaterMark = 0; 115 | } 116 | if (Number.isNaN(highWaterMark) || highWaterMark < 0) { 117 | throw new TypeError(); 118 | } 119 | return highWaterMark; 120 | } 121 | 122 | export function MakeSizeAlgorithmFromSizeFunction(size?: (chunk) => number) { 123 | if (size === void 0) { 124 | return _ => 1; 125 | } 126 | if (typeof size.call !== "function") { 127 | throw new TypeError(); 128 | } 129 | return chunk => size.call(void 0, chunk); 130 | } 131 | 132 | export function IsDetachedBuffer(v): boolean { 133 | return false; 134 | } 135 | -------------------------------------------------------------------------------- /readable_stream_reader.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsReadableStream, 3 | IsReadableStreamLocked, 4 | ReadableStream, 5 | ReadableStreamCancel, 6 | ReadableStreamCreateReadResult, 7 | ReadableStreamReadResult 8 | } from "./readable_stream.ts"; 9 | import {defer, Defer} from "./defer.ts"; 10 | import {Assert} from "./util.ts"; 11 | import {ReadableStreamBYOBReader} from "./readable_stream_byob_reader.ts"; 12 | 13 | export interface ReadableStreamReader { 14 | readonly closed: Promise; 15 | 16 | cancel(reason?): Promise; 17 | 18 | read(view?: ArrayBufferView): Promise>; 19 | 20 | releaseLock(): Promise; 21 | } 22 | 23 | export class ReadableStreamDefaultReader 24 | implements ReadableStreamReader { 25 | readRequests: { promise: Defer; forAuthorCode }[]; 26 | 27 | constructor(stream: ReadableStream) { 28 | if (!IsReadableStream(stream)) { 29 | throw new TypeError(); 30 | } 31 | if (IsReadableStreamLocked(stream)) { 32 | throw new TypeError(); 33 | } 34 | ReadableStreamReaderGenericInitialize(this, stream); 35 | this.readRequests = []; 36 | } 37 | 38 | get closed(): Promise { 39 | if (!IsReadableStreamDefaultReader(this)) { 40 | return Promise.reject(new TypeError()); 41 | } 42 | return this.closedPromise; 43 | } 44 | 45 | closedPromise: Defer; 46 | ownerReadableStream: ReadableStream; 47 | 48 | cancel(reason?): Promise { 49 | if (!IsReadableStreamDefaultReader(this)) { 50 | return Promise.reject(new TypeError()); 51 | } 52 | if (this.ownerReadableStream === void 0) { 53 | return Promise.reject(new TypeError()); 54 | } 55 | return ReadableStreamReaderGenericCancel(this, reason); 56 | } 57 | 58 | read(): Promise> { 59 | if (!IsReadableStreamDefaultReader(this)) { 60 | return Promise.reject(new TypeError()); 61 | } 62 | if (this.ownerReadableStream === void 0) { 63 | return Promise.reject(new TypeError()); 64 | } 65 | return ReadableStreamDefaultReaderRead(this, true); 66 | } 67 | 68 | releaseLock() { 69 | if (!IsReadableStreamDefaultReader(this)) { 70 | return Promise.reject(new TypeError()); 71 | } 72 | if (this.ownerReadableStream === void 0) { 73 | return Promise.reject(new TypeError()); 74 | } 75 | if (this.readRequests.length > 0) { 76 | throw new TypeError(); 77 | } 78 | ReadableStreamReaderGenericRelease(this); 79 | } 80 | } 81 | 82 | export function IsReadableStreamDefaultReader( 83 | a 84 | ): a is ReadableStreamDefaultReader { 85 | return typeof a === "object" && a.hasOwnProperty("readRequests"); 86 | } 87 | 88 | export function ReadableStreamReaderGenericCancel( 89 | reader: ReadableStreamBYOBReader | ReadableStreamDefaultReader, 90 | reason 91 | ) { 92 | const stream = reader.ownerReadableStream; 93 | Assert(stream !== void 0); 94 | return ReadableStreamCancel(stream, reason); 95 | } 96 | 97 | export function ReadableStreamReaderGenericInitialize( 98 | reader: ReadableStreamBYOBReader | ReadableStreamDefaultReader, 99 | stream: ReadableStream 100 | ) { 101 | reader.ownerReadableStream = stream; 102 | stream.reader = reader; 103 | if (stream.state === "readable") { 104 | reader.closedPromise = defer(); 105 | } else if (stream.state === "closed") { 106 | reader.closedPromise = defer(); 107 | reader.closedPromise.resolve(void 0); 108 | } else { 109 | Assert(stream.state === "errored"); 110 | reader.closedPromise = defer(); 111 | reader.closedPromise.reject(stream.storedError); 112 | } 113 | } 114 | 115 | export function ReadableStreamReaderGenericRelease( 116 | reader: ReadableStreamBYOBReader | ReadableStreamDefaultReader 117 | ) { 118 | Assert(reader.ownerReadableStream !== void 0); 119 | Assert(reader.ownerReadableStream.reader === reader); 120 | if (reader.ownerReadableStream.state === "readable") { 121 | reader.closedPromise.reject(new TypeError()); 122 | } else { 123 | reader.closedPromise.reject(new TypeError()); 124 | } 125 | reader.ownerReadableStream.reader = void 0; 126 | reader.ownerReadableStream = void 0; 127 | } 128 | 129 | export function ReadableStreamDefaultReaderRead( 130 | reader: ReadableStreamDefaultReader, 131 | forAuthorCode: boolean = false 132 | ): Promise<{ value; done: boolean }> { 133 | const stream = reader.ownerReadableStream; 134 | Assert(stream !== void 0); 135 | stream.disturbed = true; 136 | if (stream.state === "closed") { 137 | return Promise.resolve( 138 | ReadableStreamCreateReadResult(void 0, true, forAuthorCode) 139 | ); 140 | } 141 | if (stream.state === "errored") { 142 | return Promise.reject(stream.storedError); 143 | } 144 | Assert(stream.state === "readable"); 145 | return stream.readableStreamController.PullSteps(forAuthorCode); 146 | } 147 | -------------------------------------------------------------------------------- /transform_stream.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SizeAlgorithm, 3 | StartAlgorithm, 4 | ReadableStream, 5 | CreateReadableStream 6 | } from "./readable_stream.ts"; 7 | import { defer, Defer } from "./defer.ts"; 8 | import { CreateWritableStream, WritableStream } from "./writable_stream.ts"; 9 | import { 10 | InvokeOrNoop, 11 | IsNonNegativeNumber, 12 | MakeSizeAlgorithmFromSizeFunction, 13 | ValidateAndNormalizeHighWaterMark 14 | } from "./misc.ts"; 15 | import { 16 | SetUpTransformStreamDefaultController, 17 | SetUpTransformStreamDefaultControllerFromTransformer, 18 | TransformStreamController, 19 | TransformStreamDefaultController, 20 | TransformStreamDefaultControllerClearAlgorithms, 21 | TransformStreamDefaultSinkAbortAlgorithm, 22 | TransformStreamDefaultSinkCloseAlgorithm, 23 | TransformStreamDefaultSinkWriteAlgorithm, 24 | TransformStreamDefaultSourcePullAlgorithm 25 | } from "./transform_stream_controller.ts"; 26 | import { Assert } from "./util.ts"; 27 | import { ReadableStreamDefaultControllerError } from "./readable_stream_controller.ts"; 28 | import { WritableStreamDefaultControllerErrorIfNeeded } from "./writable_stream_controller.ts"; 29 | import { QueuingStrategy } from "./strategy.ts"; 30 | 31 | export type Transformer = { 32 | start?: (controller) => any; 33 | transform?: (chunk, controller: TransformStreamController) => Promise; 34 | flush?: (controller: TransformStreamController) => Promise; 35 | writableType?: undefined; 36 | readableType?: undefined; 37 | }; 38 | 39 | export class TransformStream { 40 | constructor( 41 | transformer: Transformer, 42 | writableStrategy: QueuingStrategy, 43 | readableStrategy: QueuingStrategy 44 | ) { 45 | let writableSizeFunction = writableStrategy.size; 46 | let writableHighWaterMark = writableStrategy.highWaterMark; 47 | let readableSizeFunction = readableStrategy.size; 48 | let readableHighWaterMark = readableStrategy.highWaterMark; 49 | const { writableType } = transformer; 50 | if (writableType !== void 0) { 51 | throw new RangeError("writable type should not be defined"); 52 | } 53 | writableSizeFunction = MakeSizeAlgorithmFromSizeFunction( 54 | writableSizeFunction 55 | ); 56 | writableHighWaterMark = ValidateAndNormalizeHighWaterMark( 57 | writableHighWaterMark 58 | ); 59 | const { readableType } = transformer; 60 | if (readableType !== void 0) { 61 | throw new RangeError("readable type should not be defined"); 62 | } 63 | readableSizeFunction = MakeSizeAlgorithmFromSizeFunction( 64 | readableSizeFunction 65 | ); 66 | readableHighWaterMark = ValidateAndNormalizeHighWaterMark( 67 | readableHighWaterMark 68 | ); 69 | const startPromise = defer(); 70 | InitializeTransformStream( 71 | this, 72 | startPromise, 73 | writableHighWaterMark, 74 | writableSizeFunction, 75 | readableHighWaterMark, 76 | readableSizeFunction 77 | ); 78 | SetUpTransformStreamDefaultControllerFromTransformer(this, transformer); 79 | startPromise.resolve( 80 | InvokeOrNoop(transformer, "start", this.transformStreamController) 81 | ); 82 | } 83 | 84 | get readable(): ReadableStream { 85 | if (!IsTransformStream(this)) { 86 | throw new TypeError("this is not transform stream"); 87 | } 88 | return this._readable; 89 | } 90 | 91 | get writable(): WritableStream { 92 | if (!IsTransformStream(this)) { 93 | throw new TypeError("this is not transform stream"); 94 | } 95 | return this._writable; 96 | } 97 | 98 | backpressure: boolean; 99 | backpressureChangePromise: Defer; 100 | _readable: ReadableStream; 101 | transformStreamController: TransformStreamDefaultController; 102 | _writable: WritableStream; 103 | } 104 | 105 | export type FlushAlgorithm = () => Promise; 106 | export type TransformAlgorithm = (chunk: T) => Promise; 107 | 108 | export function CreateTransformStream( 109 | startAlgorithm: StartAlgorithm, 110 | transformAlgorithm: TransformAlgorithm, 111 | flushAlgorithm: FlushAlgorithm, 112 | writableHighWaterMark: number = 1, 113 | writableSizeAlgorithm: SizeAlgorithm = () => 1, 114 | readableHighWaterMark: number = 1, 115 | readableSizeAlgorithm: SizeAlgorithm = () => 1 116 | ): TransformStream { 117 | Assert(IsNonNegativeNumber(writableHighWaterMark)); 118 | Assert(IsNonNegativeNumber(readableHighWaterMark)); 119 | const stream = Object.create(TransformStream.prototype); 120 | const startPromise = defer(); 121 | InitializeTransformStream( 122 | stream, 123 | startPromise, 124 | writableHighWaterMark, 125 | writableSizeAlgorithm, 126 | readableHighWaterMark, 127 | readableSizeAlgorithm 128 | ); 129 | const controller = Object.create(TransformStreamDefaultController.prototype); 130 | SetUpTransformStreamDefaultController( 131 | stream, 132 | controller, 133 | transformAlgorithm, 134 | flushAlgorithm 135 | ); 136 | startPromise.resolve(startPromise()); 137 | return stream; 138 | } 139 | 140 | export function InitializeTransformStream( 141 | stream: TransformStream, 142 | startPromise: Defer, 143 | writableHighWaterMark: number, 144 | writableSizeAlgorithm: SizeAlgorithm, 145 | readableHighWaterMark: number, 146 | readableSizeAlgorithm: SizeAlgorithm 147 | ) { 148 | const startAlgorithm = () => startPromise; 149 | const writeAlgorithm = chunk => 150 | TransformStreamDefaultSinkWriteAlgorithm(stream, chunk); 151 | const abortAlgorithm = reason => 152 | TransformStreamDefaultSinkAbortAlgorithm(stream, reason); 153 | const closeAlgorithm = () => TransformStreamDefaultSinkCloseAlgorithm(stream); 154 | stream._writable = CreateWritableStream( 155 | startAlgorithm, 156 | writeAlgorithm, 157 | closeAlgorithm, 158 | abortAlgorithm, 159 | writableHighWaterMark, 160 | writableSizeAlgorithm 161 | ); 162 | const pullAlgorithm = () => TransformStreamDefaultSourcePullAlgorithm(stream); 163 | const cancelAlgorithm = reason => 164 | TransformStreamErrorWritableAndUnblockWrite(stream, reason); 165 | stream._readable = CreateReadableStream( 166 | startAlgorithm, 167 | pullAlgorithm, 168 | cancelAlgorithm, 169 | readableHighWaterMark, 170 | readableSizeAlgorithm 171 | ); 172 | stream.backpressure = void 0; 173 | stream.backpressureChangePromise = void 0; 174 | TransformStreamSetBackpressure(stream, true); 175 | stream.transformStreamController = void 0; 176 | } 177 | 178 | export function IsTransformStream(x): x is TransformStream { 179 | return typeof x === "object" && x.hasOwnProperty("transformStreamController"); 180 | } 181 | 182 | export function TransformStreamError(stream: TransformStream, e) { 183 | ReadableStreamDefaultControllerError( 184 | stream.readable.readableStreamController, 185 | e 186 | ); 187 | TransformStreamErrorWritableAndUnblockWrite(stream, e); 188 | } 189 | 190 | export function TransformStreamErrorWritableAndUnblockWrite( 191 | stream: TransformStream, 192 | e 193 | ) { 194 | TransformStreamDefaultControllerClearAlgorithms( 195 | stream.transformStreamController 196 | ); 197 | WritableStreamDefaultControllerErrorIfNeeded( 198 | stream.writable.writableStreamController, 199 | e 200 | ); 201 | if (stream.backpressure) { 202 | TransformStreamSetBackpressure(stream, false); 203 | } 204 | } 205 | 206 | export function TransformStreamSetBackpressure( 207 | stream: TransformStream, 208 | backpressure: boolean 209 | ) { 210 | Assert(stream.backpressure !== backpressure); 211 | if (stream.backpressureChangePromise !== void 0) { 212 | stream.backpressureChangePromise.resolve(); 213 | } 214 | stream.backpressureChangePromise = defer(); 215 | stream.backpressure = backpressure; 216 | } 217 | -------------------------------------------------------------------------------- /transform_stream_controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ReadableStreamDefaultControllerCanCloseOrEnqueue, 3 | ReadableStreamDefaultControllerClose, 4 | ReadableStreamDefaultControllerEnqueue, 5 | ReadableStreamDefaultControllerGetDesiredSize, 6 | ReadableStreamDefaultControllerHasBackpressure 7 | } from "./readable_stream_controller"; 8 | import { 9 | FlushAlgorithm, 10 | IsTransformStream, 11 | TransformAlgorithm, 12 | Transformer, 13 | TransformStream, 14 | TransformStreamError, 15 | TransformStreamErrorWritableAndUnblockWrite, 16 | TransformStreamSetBackpressure 17 | } from "./transform_stream"; 18 | import { Assert } from "./util"; 19 | import { CreateAlgorithmFromUnderlyingMethod, PromiseCall } from "./misc"; 20 | import { read, write } from "deno"; 21 | 22 | export interface TransformStreamController { 23 | readonly desiredSize: number; 24 | 25 | enqueue(chunk: T); 26 | 27 | error(reason); 28 | 29 | terminate(); 30 | } 31 | 32 | export class TransformStreamDefaultController 33 | implements TransformStreamController { 34 | constructor() { 35 | throw new TypeError( 36 | "TransformStreamDefaultController is not constructable" 37 | ); 38 | } 39 | 40 | get desiredSize() { 41 | if (!IsTransformStreamDefaultController(this)) { 42 | throw new TypeError("this is not TransformStreamDefaultController"); 43 | } 44 | return ReadableStreamDefaultControllerGetDesiredSize( 45 | this.controlledTransformStream.readable.readableStreamController 46 | ); 47 | } 48 | 49 | enqueue(chunk: T) { 50 | if (!IsTransformStreamDefaultController(this)) { 51 | throw new TypeError("this is not TransformStreamDefaultController"); 52 | } 53 | TransformStreamDefaultControllerEnqueue(this, chunk); 54 | } 55 | 56 | error(reason) { 57 | if (!IsTransformStreamDefaultController(this)) { 58 | throw new TypeError("this is not TransformStreamDefaultController"); 59 | } 60 | TransformStreamDefaultControllerError(this, reason); 61 | } 62 | 63 | terminate() { 64 | if (!IsTransformStreamDefaultController(this)) { 65 | throw new TypeError("this is not TransformStreamDefaultController"); 66 | } 67 | TransformStreamDefaultControllerTerminate(this); 68 | } 69 | 70 | controlledTransformStream: TransformStream; 71 | flushAlgorithm: FlushAlgorithm; 72 | transformAlgorithm: TransformAlgorithm; 73 | } 74 | 75 | export function IsTransformStreamDefaultController( 76 | x 77 | ): x is TransformStreamDefaultController { 78 | return typeof x === "object" && x.hasOwnProperty("controlledTransformStream"); 79 | } 80 | 81 | export function SetUpTransformStreamDefaultController( 82 | stream: TransformStream, 83 | controller: TransformStreamDefaultController, 84 | transformAlgorithm: TransformAlgorithm, 85 | flushAlgorithm: FlushAlgorithm 86 | ) { 87 | Assert(IsTransformStream(stream)); 88 | Assert(stream.transformStreamController === void 0); 89 | controller.controlledTransformStream = stream; 90 | stream.transformStreamController = controller; 91 | controller.transformAlgorithm = transformAlgorithm; 92 | controller.flushAlgorithm = flushAlgorithm; 93 | } 94 | 95 | export function SetUpTransformStreamDefaultControllerFromTransformer( 96 | stream: TransformStream, 97 | transformer: Transformer 98 | ) { 99 | Assert(transformer !== void 0); 100 | const controller = Object.create(TransformStreamDefaultController.prototype); 101 | let transformAlgorithm: TransformAlgorithm = async chunk => { 102 | try { 103 | TransformStreamDefaultControllerEnqueue(controller, chunk); 104 | } catch (e) { 105 | throw void 0; 106 | } 107 | return; 108 | }; 109 | const method = transformer.transform; 110 | if (method !== void 0) { 111 | if (typeof method.call !== "function") { 112 | throw new TypeError("transformer.transform is not callable"); 113 | } 114 | transformAlgorithm = async chunk => 115 | PromiseCall(method, transformer, chunk, controller); 116 | } 117 | const flushAlgorithm = CreateAlgorithmFromUnderlyingMethod( 118 | transformer, 119 | "flush", 120 | 0, 121 | controller 122 | ); 123 | SetUpTransformStreamDefaultController( 124 | stream, 125 | controller, 126 | transformAlgorithm, 127 | flushAlgorithm 128 | ); 129 | } 130 | 131 | export function TransformStreamDefaultControllerClearAlgorithms( 132 | controller: TransformStreamDefaultController 133 | ) { 134 | controller.transformAlgorithm = void 0; 135 | controller.flushAlgorithm = void 0; 136 | } 137 | 138 | export function TransformStreamDefaultControllerEnqueue( 139 | controller: TransformStreamDefaultController, 140 | chunk 141 | ) { 142 | const stream = controller.controlledTransformStream; 143 | const readableController = stream.readable.readableStreamController; 144 | if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController)) { 145 | throw new TypeError("readable stream controller cannot close on enqueue"); 146 | } 147 | try { 148 | ReadableStreamDefaultControllerEnqueue(readableController, chunk); 149 | } catch (e) { 150 | TransformStreamErrorWritableAndUnblockWrite(stream, e); 151 | throw stream.readable.storedError; 152 | } 153 | const backpressure = ReadableStreamDefaultControllerHasBackpressure( 154 | readableController 155 | ); 156 | if (backpressure !== stream.backpressure) { 157 | Assert(backpressure); 158 | TransformStreamSetBackpressure(stream, true); 159 | } 160 | } 161 | 162 | export function TransformStreamDefaultControllerError( 163 | controller: TransformStreamDefaultController, 164 | e 165 | ) { 166 | TransformStreamError(controller.controlledTransformStream, e); 167 | } 168 | 169 | export function TransformStreamDefaultControllerPerformTransform( 170 | controller: TransformStreamDefaultController, 171 | chunk 172 | ) { 173 | controller.transformAlgorithm(chunk).catch(r => { 174 | TransformStreamError(controller.controlledTransformStream, r); 175 | throw r; 176 | }); 177 | } 178 | 179 | export function TransformStreamDefaultControllerTerminate( 180 | controller: TransformStreamDefaultController 181 | ) { 182 | const stream = controller.controlledTransformStream; 183 | const readableController = stream.readable.readableStreamController; 184 | if (ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController)) { 185 | ReadableStreamDefaultControllerClose(readableController); 186 | } 187 | const error = new TypeError("stream ended"); 188 | TransformStreamErrorWritableAndUnblockWrite(stream, error); 189 | } 190 | 191 | export function TransformStreamDefaultSinkWriteAlgorithm( 192 | stream: TransformStream, 193 | chunk 194 | ) { 195 | Assert(stream.writable.state === "writable"); 196 | const controller = stream.transformStreamController; 197 | if (stream.backpressure) { 198 | const p = stream.backpressureChangePromise; 199 | Assert(p !== void 0); 200 | return p.then(() => { 201 | const writable = stream.writable; 202 | const { state } = writable; 203 | if (state === "erroring") { 204 | throw writable.storedError; 205 | } 206 | Assert(state === "writable"); 207 | return TransformStreamDefaultControllerPerformTransform( 208 | controller, 209 | chunk 210 | ); 211 | }); 212 | } 213 | return TransformStreamDefaultControllerPerformTransform(controller, chunk); 214 | } 215 | 216 | export async function TransformStreamDefaultSinkAbortAlgorithm( 217 | stream: TransformStream, 218 | reason 219 | ) { 220 | TransformStreamError(stream, reason); 221 | } 222 | 223 | export function TransformStreamDefaultSinkCloseAlgorithm( 224 | stream: TransformStream 225 | ) { 226 | const { readable } = stream; 227 | const controller = stream.transformStreamController; 228 | const flushPromise = controller.flushAlgorithm(); 229 | TransformStreamDefaultControllerClearAlgorithms(controller); 230 | return flushPromise 231 | .then(() => { 232 | if (readable.state === "errored") { 233 | throw readable.storedError; 234 | } 235 | const readableController = readable.readableStreamController; 236 | if ( 237 | ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController) 238 | ) { 239 | ReadableStreamDefaultControllerClose(readableController); 240 | } 241 | }) 242 | .catch(r => { 243 | TransformStreamError(stream, r); 244 | throw readable.storedError; 245 | }); 246 | } 247 | 248 | export function TransformStreamDefaultSourcePullAlgorithm( 249 | stream: TransformStream 250 | ) { 251 | Assert(stream.backpressure); 252 | Assert(stream.backpressureChangePromise !== void 0); 253 | TransformStreamSetBackpressure(stream, false); 254 | return stream.backpressureChangePromise; 255 | } 256 | -------------------------------------------------------------------------------- /writable_stream_writer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsWritableStream, 3 | IsWritableStreamLocked, 4 | WritableStream, 5 | WritableStreamAbort, 6 | WritableStreamAddWriteRequest, 7 | WritableStreamCloseQueuedOrInFlight 8 | } from "./writable_stream.ts"; 9 | import { 10 | defer, 11 | Defer, 12 | PromiseState, 13 | rejectDefer, 14 | resolveDefer 15 | } from "./defer.ts"; 16 | import { Assert } from "./util.ts"; 17 | import { 18 | WritableStreamDefaultControllerClose, 19 | WritableStreamDefaultControllerGetChunkSize, 20 | WritableStreamDefaultControllerGetDesiredSize, 21 | WritableStreamDefaultControllerWrite 22 | } from "./writable_stream_controller.ts"; 23 | 24 | export interface WritableStreamWriter { 25 | readonly closed; 26 | readonly desiredSize; 27 | readonly ready; 28 | 29 | abort(reason); 30 | 31 | close(); 32 | 33 | releaseLock(); 34 | 35 | write(chunk: T); 36 | } 37 | 38 | export class WritableStreamDefaultWriter implements WritableStreamWriter { 39 | constructor(stream: WritableStream) { 40 | if (!IsWritableStream(stream)) { 41 | throw new TypeError("stream is not writable stream"); 42 | } 43 | if (IsWritableStreamLocked(stream)) { 44 | throw new TypeError("stream is locked"); 45 | } 46 | this.ownerWritableStream = stream; 47 | stream.writer = this; 48 | const { state } = stream; 49 | if (state === "writable") { 50 | if (!WritableStreamCloseQueuedOrInFlight(stream) && stream.backpressure) { 51 | this.readyPromise = defer(); 52 | } else { 53 | this.readyPromise = resolveDefer(void 0); 54 | } 55 | this.closedPromise = defer(); 56 | } else if (state === "erroring") { 57 | this.readyPromise = rejectDefer(stream.storedError); 58 | this.closedPromise = defer(); 59 | } else if (state === "closed") { 60 | this.readyPromise = resolveDefer(void 0); 61 | this.closedPromise = resolveDefer(void 0); 62 | } else { 63 | Assert(state === "errored"); 64 | const { storedError } = stream; 65 | this.readyPromise = rejectDefer(storedError); 66 | this.closedPromise = rejectDefer(storedError); 67 | } 68 | } 69 | 70 | get closed(): Promise { 71 | if (!IsWritableStreamDefaultWriter(this)) { 72 | return Promise.reject( 73 | new TypeError("this is not WritableStreamDefaultWriter") 74 | ); 75 | } 76 | return this.closedPromise; 77 | } 78 | 79 | get desiredSize() { 80 | if (!IsWritableStreamDefaultWriter(this)) { 81 | throw new TypeError("this is not WritableStreamDefaultWriter"); 82 | } 83 | if (this.ownerWritableStream === void 0) { 84 | throw new TypeError("stream is undefined"); 85 | } 86 | return WritableStreamDefaultWriterGetDesiredSize(this); 87 | } 88 | 89 | get ready(): Promise { 90 | if (!IsWritableStreamDefaultWriter(this)) { 91 | return Promise.reject( 92 | new TypeError("this is not WritableStreamDefaultWriter") 93 | ); 94 | } 95 | return this.readyPromise; 96 | } 97 | 98 | async abort(reason) { 99 | if (!IsWritableStreamDefaultWriter(this)) { 100 | throw new TypeError("this is not WritableStreamDefaultWriter"); 101 | } 102 | if (this.ownerWritableStream === void 0) { 103 | throw new TypeError("stream is undefined"); 104 | } 105 | return WritableStreamDefaultWriterAbort(this, reason); 106 | } 107 | 108 | async close() { 109 | if (!IsWritableStreamDefaultWriter(this)) { 110 | throw new TypeError(); 111 | } 112 | const stream = this.ownerWritableStream; 113 | if (stream === void 0) { 114 | throw new TypeError(); 115 | } 116 | if (WritableStreamCloseQueuedOrInFlight(stream)) { 117 | throw new TypeError(); 118 | } 119 | return WritableStreamDefaultWriterClose(this); 120 | } 121 | 122 | releaseLock() { 123 | if (!IsWritableStreamDefaultWriter(this)) { 124 | throw new TypeError(); 125 | } 126 | const stream = this.ownerWritableStream; 127 | if (stream === void 0) { 128 | throw new TypeError(); 129 | } 130 | Assert(stream.writer !== void 0); 131 | WritableStreamDefaultWriterRelease(this); 132 | } 133 | 134 | write(chunk) { 135 | if (!IsWritableStreamDefaultWriter(this)) { 136 | throw new TypeError(); 137 | } 138 | const stream = this.ownerWritableStream; 139 | if (stream === void 0) { 140 | throw new TypeError(); 141 | } 142 | return WritableStreamDefaultWriterWrite(this, chunk); 143 | } 144 | 145 | closedPromise: Defer; 146 | ownerWritableStream: WritableStream; 147 | readyPromise: Defer; 148 | } 149 | 150 | export function IsWritableStreamDefaultWriter( 151 | x 152 | ): x is WritableStreamDefaultWriter { 153 | return typeof x === "object" && x.hasOwnProperty("ownerWritableStream"); 154 | } 155 | 156 | export function WritableStreamDefaultWriterAbort( 157 | writer: WritableStreamDefaultWriter, 158 | reason 159 | ) { 160 | Assert(writer.ownerWritableStream !== void 0); 161 | return WritableStreamAbort(writer.ownerWritableStream, reason); 162 | } 163 | 164 | export async function WritableStreamDefaultWriterClose( 165 | writer: WritableStreamDefaultWriter 166 | ): Promise { 167 | const stream = writer.ownerWritableStream; 168 | Assert(stream !== void 0); 169 | const { state } = stream; 170 | if (state === "closed" || state === "errored") { 171 | throw new TypeError(`stream is ${state}`); 172 | } 173 | Assert(state === "writable" || state === "erroring"); 174 | Assert(!WritableStreamCloseQueuedOrInFlight(stream)); 175 | const promise = defer(); 176 | stream.closeRequest = promise; 177 | if (stream.backpressure && state == "writable") { 178 | writer.readyPromise.resolve(); 179 | } 180 | WritableStreamDefaultControllerClose(stream.writableStreamController); 181 | return promise; 182 | } 183 | 184 | export async function WritableStreamDefaultWriterCloseWithErrorPropagation( 185 | writer: WritableStreamDefaultWriter 186 | ) { 187 | const stream = writer.ownerWritableStream; 188 | Assert(stream !== void 0); 189 | const { state } = stream; 190 | if (WritableStreamCloseQueuedOrInFlight(stream) || state === "closed") { 191 | return void 0; 192 | } 193 | if (state === "errored") { 194 | throw stream.storedError; 195 | } 196 | Assert(state === "writable" || state === "erroring"); 197 | return WritableStreamDefaultWriterClose(writer); 198 | } 199 | 200 | export function WritableStreamDefaultWriterEnsureClosedPromiseRejected( 201 | writer: WritableStreamDefaultWriter, 202 | error 203 | ) { 204 | if (writer.closedPromise[PromiseState] === "pending") { 205 | writer.closedPromise.reject(error); 206 | } else { 207 | writer.closedPromise = rejectDefer(error); 208 | } 209 | } 210 | 211 | export function WritableStreamDefaultWriterEnsureReadyPromiseRejected( 212 | writer: WritableStreamDefaultWriter, 213 | error 214 | ) { 215 | if (writer.readyPromise[PromiseState] === "pending") { 216 | writer.readyPromise.reject(error); 217 | } else { 218 | writer.readyPromise = rejectDefer(error); 219 | } 220 | } 221 | 222 | export function WritableStreamDefaultWriterGetDesiredSize( 223 | writer: WritableStreamDefaultWriter 224 | ) { 225 | const stream = writer.ownerWritableStream; 226 | const { state } = stream; 227 | if (state === "errored" || state === "erroring") { 228 | return null; 229 | } 230 | if (state === "closed") { 231 | return 0; 232 | } 233 | return WritableStreamDefaultControllerGetDesiredSize( 234 | stream.writableStreamController 235 | ); 236 | } 237 | 238 | export function WritableStreamDefaultWriterRelease( 239 | writer: WritableStreamDefaultWriter 240 | ) { 241 | const stream = writer.ownerWritableStream; 242 | Assert(stream !== void 0, "stream is undefined"); 243 | Assert(stream.writer === writer, "writer is not identical"); 244 | const releasedError = new TypeError(); 245 | WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError); 246 | WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError); 247 | stream.writer = void 0; 248 | writer.ownerWritableStream = void 0; 249 | } 250 | 251 | export async function WritableStreamDefaultWriterWrite( 252 | writer: WritableStreamDefaultWriter, 253 | chunk 254 | ) { 255 | const stream = writer.ownerWritableStream; 256 | Assert(stream !== void 0); 257 | const controller = stream.writableStreamController; 258 | const chunkSize = WritableStreamDefaultControllerGetChunkSize( 259 | controller, 260 | chunk 261 | ); 262 | if (stream !== writer.ownerWritableStream) { 263 | throw new TypeError("different stream"); 264 | } 265 | const { state } = stream; 266 | if (state === "errored") { 267 | throw stream.storedError; 268 | } 269 | if (WritableStreamCloseQueuedOrInFlight(stream) || state === "closed") { 270 | throw new TypeError( 271 | `stream is ${state === "closed" ? "closed" : "closing"}` 272 | ); 273 | } 274 | if (state === "erroring") { 275 | throw stream.storedError; 276 | } 277 | Assert(state === "writable"); 278 | const promise = WritableStreamAddWriteRequest(stream); 279 | WritableStreamDefaultControllerWrite(controller, chunk, chunkSize); 280 | return promise; 281 | } 282 | -------------------------------------------------------------------------------- /writable_stream_controller.ts: -------------------------------------------------------------------------------- 1 | import { SizeAlgorithm, StartAlgorithm } from "./readable_stream.ts"; 2 | import { 3 | WritableStream, 4 | AbortAlgorithm, 5 | CloseAlgorithm, 6 | IsWritableStream, 7 | WriteAlgorithm, 8 | WritableStreamUpdateBackpressure, 9 | WritableStreamDealWithRejection, 10 | WritableStreamCloseQueuedOrInFlight, 11 | WritableStreamFinishErroring, 12 | WritableStreamMarkCloseRequestInFlight, 13 | WritableStreamFinishInFlightClose, 14 | WritableStreamFinishInFlightCloseWithError, 15 | WritableStreamMarkFirstWriteRequestInFlight, 16 | WritableStreamFinishInFlightWrite, 17 | WritableStreamStartErroring 18 | } from "./writable_stream.ts"; 19 | import { Assert } from "./util.ts"; 20 | import { 21 | CreateAlgorithmFromUnderlyingMethod, 22 | DequeueValue, 23 | EnqueueValueWithSize, 24 | InvokeOrNoop, 25 | PeekQueueValue, 26 | ResetQueue 27 | } from "./misc.ts"; 28 | 29 | export interface WritableStreamController { 30 | error(e); 31 | } 32 | 33 | export const ErrorSteps = Symbol("ErrorSteps"); 34 | export const AbortSteps = Symbol("AbortSteps"); 35 | 36 | export function createWritableStreamDefaultController< 37 | T 38 | >(): WritableStreamDefaultController { 39 | const ret = Object.create(WritableStreamDefaultController.prototype); 40 | ret[ErrorSteps] = () => ResetQueue(ret); 41 | ret[AbortSteps] = reason => { 42 | const result = ret.abortAlgorithm(reason); 43 | WritableStreamDefaultControllerClearAlgorithms(ret); 44 | return result; 45 | }; 46 | return ret; 47 | } 48 | 49 | export class WritableStreamDefaultController 50 | implements WritableStreamController { 51 | abortAlgorithm: AbortAlgorithm; 52 | closeAlgorithm: CloseAlgorithm; 53 | controlledWritableStream: WritableStream; 54 | queue: ("close" | { chunk: T })[]; 55 | queueTotalSize: number; 56 | started: boolean; 57 | strategyHWM: number; 58 | strategySizeAlgorithm: SizeAlgorithm; 59 | writeAlgorithm: WriteAlgorithm; 60 | 61 | constructor() { 62 | throw new TypeError(); 63 | } 64 | 65 | error(e) { 66 | if (!IsWritableStreamDefaultController(this)) { 67 | throw new TypeError("this is not WritableStreamDefaultController"); 68 | } 69 | const { state } = this.controlledWritableStream; 70 | if (state !== "writable") { 71 | return; 72 | } 73 | WritableStreamDefaultControllerError(this, e); 74 | } 75 | } 76 | 77 | export function IsWritableStreamDefaultController( 78 | x 79 | ): x is WritableStreamDefaultController { 80 | return typeof x === "object" && x.hasOwnProperty("controlledWritableStream"); 81 | } 82 | 83 | export function SetUpWritableStreamDefaultController(params: { 84 | stream: WritableStream; 85 | controller: WritableStreamDefaultController; 86 | startAlgorithm: StartAlgorithm; 87 | writeAlgorithm: WriteAlgorithm; 88 | closeAlgorithm: CloseAlgorithm; 89 | abortAlgorithm: AbortAlgorithm; 90 | highWaterMark: number; 91 | sizeAlgorithm: SizeAlgorithm; 92 | }) { 93 | const { 94 | stream, 95 | controller, 96 | startAlgorithm, 97 | writeAlgorithm, 98 | closeAlgorithm, 99 | abortAlgorithm, 100 | highWaterMark, 101 | sizeAlgorithm 102 | } = params; 103 | Assert(IsWritableStream(stream)); 104 | Assert(stream.writableStreamController === void 0); 105 | controller.controlledWritableStream = stream; 106 | stream.writableStreamController = controller; 107 | ResetQueue(controller); 108 | controller.started = false; 109 | controller.strategySizeAlgorithm = sizeAlgorithm; 110 | controller.strategyHWM = highWaterMark; 111 | controller.writeAlgorithm = writeAlgorithm; 112 | controller.closeAlgorithm = closeAlgorithm; 113 | controller.abortAlgorithm = abortAlgorithm; 114 | const backpressure = WritableStreamDefaultControllerGetBackpressure( 115 | controller 116 | ); 117 | WritableStreamUpdateBackpressure(stream, backpressure); 118 | Promise.resolve(startAlgorithm()) 119 | .then(() => { 120 | Assert(stream.state === "writable" || stream.state === "erroring"); 121 | controller.started = true; 122 | WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); 123 | }) 124 | .catch(r => { 125 | Assert(stream.state === "writable" || stream.state === "erroring"); 126 | controller.started = true; 127 | WritableStreamDealWithRejection(stream, r); 128 | }); 129 | } 130 | 131 | export function SetUpWritableStreamDefaultControllerFromUnderlyingSink( 132 | stream: WritableStream, 133 | underlyingSink, 134 | highWaterMark: number, 135 | sizeAlgorithm: SizeAlgorithm 136 | ) { 137 | Assert(underlyingSink !== void 0); 138 | const controller = createWritableStreamDefaultController(); 139 | const startAlgorithm = () => 140 | InvokeOrNoop(underlyingSink, "start", controller); 141 | const writeAlgorithm = CreateAlgorithmFromUnderlyingMethod( 142 | underlyingSink, 143 | "write", 144 | 1, 145 | controller 146 | ); 147 | const closeAlgorithm = CreateAlgorithmFromUnderlyingMethod( 148 | underlyingSink, 149 | "close", 150 | 0 151 | ); 152 | const abortAlgorithm = CreateAlgorithmFromUnderlyingMethod( 153 | underlyingSink, 154 | "abort", 155 | 1 156 | ); 157 | SetUpWritableStreamDefaultController({ 158 | stream, 159 | controller, 160 | startAlgorithm, 161 | writeAlgorithm, 162 | closeAlgorithm, 163 | abortAlgorithm, 164 | highWaterMark, 165 | sizeAlgorithm 166 | }); 167 | } 168 | 169 | export function WritableStreamDefaultControllerClearAlgorithms( 170 | controller: WritableStreamDefaultController 171 | ) { 172 | controller.writeAlgorithm = void 0; 173 | controller.closeAlgorithm = void 0; 174 | controller.abortAlgorithm = void 0; 175 | controller.strategySizeAlgorithm = void 0; 176 | } 177 | 178 | export function WritableStreamDefaultControllerClose( 179 | controller: WritableStreamDefaultController 180 | ) { 181 | EnqueueValueWithSize(controller, "close", 0); 182 | WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); 183 | } 184 | 185 | export function WritableStreamDefaultControllerGetChunkSize( 186 | controller: WritableStreamDefaultController, 187 | chunk 188 | ): number { 189 | try { 190 | return controller.strategySizeAlgorithm(chunk); 191 | } catch (e) { 192 | WritableStreamDefaultControllerErrorIfNeeded(controller, e); 193 | return 1; 194 | } 195 | } 196 | 197 | export function WritableStreamDefaultControllerGetDesiredSize( 198 | controller: WritableStreamDefaultController 199 | ): number { 200 | return controller.strategyHWM - controller.queueTotalSize; 201 | } 202 | 203 | export function WritableStreamDefaultControllerWrite( 204 | controller: WritableStreamDefaultController, 205 | chunk, 206 | chunkSize: number 207 | ) { 208 | const writeRecord = { chunk }; 209 | try { 210 | EnqueueValueWithSize(controller, writeRecord, chunkSize); 211 | } catch (e) { 212 | WritableStreamDefaultControllerErrorIfNeeded(controller, e); 213 | return; 214 | } 215 | const stream = controller.controlledWritableStream; 216 | if ( 217 | !WritableStreamCloseQueuedOrInFlight(stream) && 218 | stream.state === "writable" 219 | ) { 220 | const backpressure = WritableStreamDefaultControllerGetBackpressure( 221 | controller 222 | ); 223 | WritableStreamUpdateBackpressure(stream, backpressure); 224 | } 225 | WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); 226 | } 227 | 228 | export function WritableStreamDefaultControllerAdvanceQueueIfNeeded( 229 | controller: WritableStreamDefaultController 230 | ) { 231 | const stream = controller.controlledWritableStream; 232 | if (!controller.started) { 233 | return; 234 | } 235 | if (stream.inFlightWriteRequest !== void 0) { 236 | return; 237 | } 238 | const { state } = stream; 239 | if (state === "closed" || state === "errored") { 240 | return; 241 | } 242 | if (state === "erroring") { 243 | WritableStreamFinishErroring(stream); 244 | return; 245 | } 246 | if (controller.queue.length === 0) { 247 | return; 248 | } 249 | const writeRecord = PeekQueueValue(controller); 250 | if (writeRecord === "close") { 251 | WritableStreamDefaultControllerProcessClose(controller); 252 | } else { 253 | WritableStreamDefaultControllerProcessWrite(controller, writeRecord.chunk); 254 | } 255 | } 256 | 257 | export function WritableStreamDefaultControllerErrorIfNeeded( 258 | controller: WritableStreamDefaultController, 259 | error 260 | ) { 261 | if (controller.controlledWritableStream.state === "writable") { 262 | WritableStreamDefaultControllerError(controller, error); 263 | } 264 | } 265 | 266 | export function WritableStreamDefaultControllerProcessClose( 267 | controller: WritableStreamDefaultController 268 | ) { 269 | const stream = controller.controlledWritableStream; 270 | WritableStreamMarkCloseRequestInFlight(stream); 271 | DequeueValue(controller); 272 | Assert(controller.queue.length === 0); 273 | const sinkClosePromise = controller.closeAlgorithm(); 274 | WritableStreamDefaultControllerClearAlgorithms(controller); 275 | sinkClosePromise 276 | .then(() => { 277 | WritableStreamFinishInFlightClose(stream); 278 | }) 279 | .catch(r => { 280 | WritableStreamFinishInFlightCloseWithError(stream, r); 281 | }); 282 | } 283 | 284 | export function WritableStreamDefaultControllerProcessWrite( 285 | controller: WritableStreamDefaultController, 286 | chunk 287 | ) { 288 | const stream = controller.controlledWritableStream; 289 | WritableStreamMarkFirstWriteRequestInFlight(stream); 290 | const sinkWritePromise = controller.writeAlgorithm(chunk); 291 | sinkWritePromise.then(() => { 292 | WritableStreamFinishInFlightWrite(stream); 293 | const { state } = stream; 294 | Assert(state === "writable" || state === "erroring"); 295 | DequeueValue(controller); 296 | if (!WritableStreamCloseQueuedOrInFlight(stream) && state === "writable") { 297 | const bp = WritableStreamDefaultControllerGetBackpressure(controller); 298 | WritableStreamUpdateBackpressure(stream, bp); 299 | } 300 | WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); 301 | }); 302 | } 303 | 304 | export function WritableStreamDefaultControllerGetBackpressure( 305 | controller: WritableStreamDefaultController 306 | ) { 307 | return WritableStreamDefaultControllerGetDesiredSize(controller) <= 0; 308 | } 309 | 310 | export function WritableStreamDefaultControllerError( 311 | controller: WritableStreamDefaultController, 312 | error 313 | ) { 314 | const stream = controller.controlledWritableStream; 315 | Assert(stream.state === "writable"); 316 | WritableStreamDefaultControllerClearAlgorithms(controller); 317 | WritableStreamStartErroring(stream, error); 318 | } 319 | -------------------------------------------------------------------------------- /readable_stream_controller.ts: -------------------------------------------------------------------------------- 1 | import { Assert } from "./util.ts"; 2 | 3 | import { ReadableStreamBYOBRequest } from "./readable_stream_request.ts"; 4 | import { 5 | CancelAlgorithm, 6 | IsReadableStreamLocked, 7 | PullAlgorithm, 8 | ReadableStream, 9 | ReadableStreamAddReadRequest, 10 | ReadableStreamClose, 11 | ReadableStreamCreateReadResult, 12 | ReadableStreamError, 13 | ReadableStreamFulfillReadRequest, 14 | ReadableStreamGetNumReadRequests, 15 | SizeAlgorithm, 16 | StartAlgorithm, 17 | UnderlyingSource 18 | } from "./readable_stream.ts"; 19 | 20 | import { 21 | CreateAlgorithmFromUnderlyingMethod, 22 | DequeueValue, 23 | EnqueueValueWithSize, 24 | InvokeOrNoop, 25 | ResetQueue 26 | } from "./misc.ts"; 27 | 28 | export type PullIntoDescriptor = { 29 | buffer: ArrayBuffer; 30 | byteOffset: number; 31 | bytesFilled: number; 32 | byteLength: number; 33 | elementSize: number; 34 | ctor: any; 35 | readerType: string; 36 | }; 37 | 38 | export interface ReadableStreamController { 39 | readonly byobRequest?: ReadableStreamBYOBRequest; 40 | 41 | readonly desiredSize: number; 42 | 43 | close(): void; 44 | 45 | enqueue(chunk: T): void; 46 | 47 | error(e): void; 48 | } 49 | 50 | export abstract class ReadableStreamControllerBase { 51 | autoAllocateChunkSize: number; 52 | 53 | cancelAlgorithm: CancelAlgorithm; 54 | 55 | closeRequested: boolean; 56 | pullAgain: boolean; 57 | 58 | pullAlgorithm: PullAlgorithm; 59 | 60 | pulling: boolean; 61 | pendingPullIntos: PullIntoDescriptor[]; 62 | queue: { 63 | buffer: ArrayBuffer; 64 | byteLength: number; 65 | byteOffset: number; 66 | }[]; 67 | queueTotalSize; 68 | started: boolean; 69 | strategyHWM: number; 70 | 71 | // 72 | } 73 | 74 | export class ReadableStreamDefaultController 75 | extends ReadableStreamControllerBase 76 | implements ReadableStreamController { 77 | constructor() { 78 | super(); 79 | throw new TypeError(); 80 | } 81 | 82 | controlledReadableStream: ReadableStream; 83 | strategySizeAlgorithm: (chunk) => number; 84 | 85 | get desiredSize(): number { 86 | if (!IsReadableStreamDefaultController(this)) { 87 | throw new TypeError(); 88 | } 89 | return ReadableStreamDefaultControllerGetDesiredSize(this); 90 | } 91 | 92 | close(): void { 93 | if (!IsReadableStreamDefaultController(this)) { 94 | throw new TypeError(); 95 | } 96 | if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(this)) { 97 | throw new TypeError(); 98 | } 99 | ReadableStreamDefaultControllerClose(this); 100 | } 101 | 102 | enqueue(chunk: T): void { 103 | if (!IsReadableStreamDefaultController(this)) { 104 | throw new TypeError(); 105 | } 106 | if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(this)) { 107 | throw new TypeError(); 108 | } 109 | return ReadableStreamDefaultControllerEnqueue(this, chunk); 110 | } 111 | 112 | error(e): void { 113 | if (!IsReadableStreamDefaultController(this)) { 114 | throw new TypeError(); 115 | } 116 | ReadableStreamDefaultControllerError(this, e); 117 | } 118 | 119 | CancelSteps(reason): Promise { 120 | ResetQueue(this); 121 | const result = this.cancelAlgorithm(reason); 122 | ReadableStreamDefaultControllerClearAlgorithms(this); 123 | return result; 124 | } 125 | 126 | PullSteps(forAuthorCode?: boolean): Promise { 127 | const stream = this.controlledReadableStream; 128 | if (this.queue.length > 0) { 129 | const chunk = DequeueValue(this); 130 | if (this.closeRequested && this.queue.length === 0) { 131 | ReadableStreamDefaultControllerClearAlgorithms(this); 132 | ReadableStreamClose(stream); 133 | } else { 134 | ReadableStreamDefaultControllerCallPullIfNeeded(this); 135 | } 136 | return Promise.resolve( 137 | ReadableStreamCreateReadResult(chunk, false, forAuthorCode) 138 | ); 139 | } 140 | const pendingPromise = ReadableStreamAddReadRequest(stream, forAuthorCode); 141 | ReadableStreamDefaultControllerCallPullIfNeeded(this); 142 | return pendingPromise; 143 | } 144 | } 145 | 146 | export function IsReadableStreamDefaultController( 147 | x 148 | ): x is ReadableStreamDefaultController { 149 | return typeof x === "object" && x.hasOwnProperty("controlledReadableStream"); 150 | } 151 | 152 | export function ReadableStreamDefaultControllerCallPullIfNeeded( 153 | controller: ReadableStreamDefaultController 154 | ) { 155 | const shouldPull = ReadableStreamDefaultControllerShouldCallPull(controller); 156 | if (!shouldPull) { 157 | return; 158 | } 159 | if (controller.pulling) { 160 | controller.pullAgain = true; 161 | return; 162 | } 163 | Assert(!controller.pullAgain); 164 | controller.pulling = true; 165 | controller 166 | .pullAlgorithm() 167 | .then(() => { 168 | controller.pulling = false; 169 | if (controller.pullAgain) { 170 | controller.pullAgain = false; 171 | ReadableStreamDefaultControllerCallPullIfNeeded(controller); 172 | } 173 | }) 174 | .catch(r => { 175 | ReadableStreamDefaultControllerError(controller, r); 176 | }); 177 | } 178 | 179 | export function ReadableStreamDefaultControllerShouldCallPull( 180 | controller: ReadableStreamDefaultController 181 | ) { 182 | const stream = controller.controlledReadableStream; 183 | if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) { 184 | return false; 185 | } 186 | if (!controller.started) { 187 | return false; 188 | } 189 | if ( 190 | IsReadableStreamLocked(stream) && 191 | ReadableStreamGetNumReadRequests(stream) > 0 192 | ) { 193 | return true; 194 | } 195 | const desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller); 196 | Assert(desiredSize !== null); 197 | return desiredSize > 0; 198 | } 199 | 200 | export function ReadableStreamDefaultControllerClearAlgorithms( 201 | controller: ReadableStreamDefaultController 202 | ) { 203 | controller.pullAlgorithm = void 0; 204 | controller.cancelAlgorithm = void 0; 205 | controller.strategySizeAlgorithm = void 0; 206 | } 207 | 208 | export function ReadableStreamDefaultControllerClose(controller) { 209 | const stream = controller.controlledReadableStream; 210 | Assert(ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)); 211 | controller.closeRequested = true; 212 | if (controller.queue.length === 0) { 213 | ReadableStreamDefaultControllerClearAlgorithms(controller); 214 | ReadableStreamClose(stream); 215 | } 216 | } 217 | 218 | export function ReadableStreamDefaultControllerEnqueue(controller, chunk) { 219 | if (IsReadableStreamDefaultController(controller)) { 220 | const stream = controller.controlledReadableStream; 221 | Assert(ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)); 222 | if ( 223 | IsReadableStreamLocked(stream) && 224 | ReadableStreamGetNumReadRequests(stream) > 0 225 | ) { 226 | ReadableStreamFulfillReadRequest(stream, chunk, false); 227 | } else { 228 | let result: number; 229 | try { 230 | result = controller.strategySizeAlgorithm(chunk); 231 | } catch (e) { 232 | ReadableStreamDefaultControllerError(controller, e); 233 | return e; 234 | } 235 | const chunkSize = result; 236 | try { 237 | EnqueueValueWithSize(controller, chunk, chunkSize); 238 | } catch (e) { 239 | ReadableStreamDefaultControllerError(controller, e); 240 | return e; 241 | } 242 | ReadableStreamDefaultControllerCallPullIfNeeded(controller); 243 | } 244 | } 245 | } 246 | 247 | export function ReadableStreamDefaultControllerError(controller, e) { 248 | if (IsReadableStreamDefaultController(controller)) { 249 | const stream = controller.controlledReadableStream; 250 | if (stream.state !== "readable") { 251 | return; 252 | } 253 | ResetQueue(controller); 254 | ReadableStreamDefaultControllerClearAlgorithms(controller); 255 | ReadableStreamError(stream, e); 256 | } 257 | } 258 | 259 | export function ReadableStreamDefaultControllerGetDesiredSize( 260 | controller: ReadableStreamDefaultController 261 | ): number | null { 262 | const stream = controller.controlledReadableStream; 263 | const state = stream.state; 264 | if (state === "errored") { 265 | return null; 266 | } 267 | if (state === "closed") { 268 | return 0; 269 | } 270 | return controller.strategyHWM - controller.queueTotalSize; 271 | } 272 | 273 | export function ReadableStreamDefaultControllerHasBackpressure( 274 | controller: ReadableStreamDefaultController 275 | ): boolean { 276 | return !ReadableStreamDefaultControllerShouldCallPull(controller); 277 | } 278 | 279 | export function ReadableStreamDefaultControllerCanCloseOrEnqueue( 280 | controller: ReadableStreamDefaultController 281 | ): boolean { 282 | const state = controller.controlledReadableStream.state; 283 | return !controller.closeRequested && state === "readable"; 284 | } 285 | 286 | export function SetUpReadableStreamDefaultController(params: { 287 | stream: ReadableStream; 288 | controller: ReadableStreamDefaultController; 289 | startAlgorithm: StartAlgorithm; 290 | pullAlgorithm: PullAlgorithm; 291 | cancelAlgorithm: CancelAlgorithm; 292 | highWaterMark: number; 293 | sizeAlgorithm: SizeAlgorithm; 294 | }) { 295 | const { 296 | stream, 297 | controller, 298 | startAlgorithm, 299 | pullAlgorithm, 300 | cancelAlgorithm 301 | } = params; 302 | let { highWaterMark, sizeAlgorithm } = params; 303 | Assert(stream.readableStreamController === void 0); 304 | controller.controlledReadableStream = stream; 305 | controller.queue = void 0; 306 | controller.queueTotalSize = void 0; 307 | ResetQueue(controller); 308 | controller.started = false; 309 | controller.closeRequested = false; 310 | controller.pullAgain = false; 311 | controller.pulling = false; 312 | controller.strategySizeAlgorithm = sizeAlgorithm; 313 | controller.strategyHWM = highWaterMark; 314 | controller.pullAlgorithm = pullAlgorithm; 315 | controller.cancelAlgorithm = cancelAlgorithm; 316 | stream.readableStreamController = controller; 317 | Promise.resolve(startAlgorithm()) 318 | .then(() => { 319 | controller.started = true; 320 | Assert(controller.pulling == false); 321 | Assert(controller.pullAgain == false); 322 | ReadableStreamDefaultControllerCallPullIfNeeded(controller); 323 | }) 324 | .catch(r => { 325 | ReadableStreamDefaultControllerError(controller, r); 326 | }); 327 | } 328 | 329 | export function SetUpReadableStreamDefaultControllerFromUnderlyingSource(params: { 330 | stream: ReadableStream; 331 | underlyingSource: UnderlyingSource; 332 | highWaterMark: number; 333 | sizeAlgorithm: SizeAlgorithm; 334 | }) { 335 | const { stream, underlyingSource, highWaterMark, sizeAlgorithm } = params; 336 | Assert(underlyingSource !== void 0); 337 | const controller = Object.create(ReadableStreamDefaultController.prototype); 338 | const startAlgorithm = () => 339 | InvokeOrNoop(underlyingSource, "start", controller); 340 | const pullAlgorithm = CreateAlgorithmFromUnderlyingMethod( 341 | underlyingSource, 342 | "pull", 343 | 0, 344 | controller 345 | ); 346 | const cancelAlgorithm = CreateAlgorithmFromUnderlyingMethod( 347 | underlyingSource, 348 | "cancel", 349 | 1 350 | ); 351 | SetUpReadableStreamDefaultController({ 352 | stream, 353 | controller, 354 | startAlgorithm, 355 | pullAlgorithm, 356 | cancelAlgorithm, 357 | highWaterMark, 358 | sizeAlgorithm 359 | }); 360 | } 361 | -------------------------------------------------------------------------------- /writable_stream.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WritableStreamDefaultWriter, 3 | WritableStreamDefaultWriterEnsureReadyPromiseRejected, 4 | WritableStreamWriter 5 | } from "./writable_stream_writer.ts"; 6 | import { defer, Defer } from "./defer.ts"; 7 | import { SizeAlgorithm, StartAlgorithm } from "./readable_stream.ts"; 8 | import { 9 | IsNonNegativeNumber, 10 | MakeSizeAlgorithmFromSizeFunction, 11 | ValidateAndNormalizeHighWaterMark 12 | } from "./misc.ts"; 13 | import { Assert } from "./util.ts"; 14 | import { 15 | AbortSteps, 16 | createWritableStreamDefaultController, 17 | ErrorSteps, 18 | SetUpWritableStreamDefaultController, 19 | SetUpWritableStreamDefaultControllerFromUnderlyingSink, 20 | WritableStreamDefaultController 21 | } from "./writable_stream_controller.ts"; 22 | import { QueuingStrategy } from "./strategy.ts"; 23 | 24 | export type WriteAlgorithm = (chunk: T) => any; 25 | export type CloseAlgorithm = () => any; 26 | export type AbortAlgorithm = (reason) => any; 27 | 28 | export class WritableStream { 29 | constructor( 30 | underlyingSink: { 31 | start?: StartAlgorithm; 32 | write?: WriteAlgorithm; 33 | close?: CloseAlgorithm; 34 | abort?: AbortAlgorithm; 35 | type?: string; 36 | }, 37 | strategy: QueuingStrategy = {} 38 | ) { 39 | InitializeWritableStream(this); 40 | let { size, highWaterMark } = strategy; 41 | const { type } = underlyingSink; 42 | if (type !== void 0) { 43 | throw new RangeError("type should not be specified yet"); 44 | } 45 | const sizeAlgorithm = MakeSizeAlgorithmFromSizeFunction(size); 46 | if (highWaterMark === void 0) { 47 | highWaterMark = 1; 48 | } 49 | highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); 50 | SetUpWritableStreamDefaultControllerFromUnderlyingSink( 51 | this, 52 | underlyingSink, 53 | highWaterMark, 54 | sizeAlgorithm 55 | ); 56 | } 57 | 58 | get locked() { 59 | if (!IsWritableStream(this)) { 60 | throw new TypeError("this is not writable stream"); 61 | } 62 | return IsWritableStreamLocked(this); 63 | } 64 | 65 | async abort(reason) { 66 | if (!IsWritableStream(this)) { 67 | throw new TypeError("this is not writable stream"); 68 | } 69 | if (IsWritableStreamLocked(this)) { 70 | throw new TypeError("stream locked"); 71 | } 72 | return WritableStreamAbort(this, reason); 73 | } 74 | 75 | getWriter(): WritableStreamWriter { 76 | if (!IsWritableStream(this)) { 77 | throw new TypeError("this is not writable stream"); 78 | } 79 | return AcquireWritableStreamDefaultWriter(this); 80 | } 81 | 82 | backpressure; 83 | closeRequest: Defer; 84 | inFlightWriteRequest: Defer; 85 | inFlightCloseRequest: Defer; 86 | pendingAbortRequest: { 87 | promise: Defer; 88 | reason; 89 | wasAlreadyErroring: boolean; 90 | }; 91 | state: "writable" | "closed" | "erroring" | "errored"; 92 | storedError: Error; 93 | writableStreamController: WritableStreamDefaultController; 94 | writer: WritableStreamDefaultWriter; 95 | writeRequests: Defer[]; 96 | } 97 | 98 | export function AcquireWritableStreamDefaultWriter( 99 | stream: WritableStream 100 | ): WritableStreamDefaultWriter { 101 | return new WritableStreamDefaultWriter(stream); 102 | } 103 | 104 | export function CreateWritableStream( 105 | startAlgorithm: StartAlgorithm, 106 | writeAlgorithm: WriteAlgorithm, 107 | closeAlgorithm: CloseAlgorithm, 108 | abortAlgorithm: AbortAlgorithm, 109 | highWaterMark: number = 1, 110 | sizeAlgorithm: SizeAlgorithm = () => 1 111 | ) { 112 | Assert(IsNonNegativeNumber(highWaterMark)); 113 | const stream = Object.create(WritableStream.prototype); 114 | InitializeWritableStream(stream); 115 | const controller = createWritableStreamDefaultController(); 116 | SetUpWritableStreamDefaultController({ 117 | stream, 118 | controller, 119 | startAlgorithm, 120 | writeAlgorithm, 121 | closeAlgorithm, 122 | abortAlgorithm, 123 | highWaterMark, 124 | sizeAlgorithm 125 | }); 126 | } 127 | 128 | export function InitializeWritableStream(stream: WritableStream) { 129 | stream.state = "writable"; 130 | stream.storedError = void 0; 131 | stream.writer = void 0; 132 | stream.writableStreamController = void 0; 133 | stream.inFlightCloseRequest = void 0; 134 | stream.closeRequest = void 0; 135 | stream.pendingAbortRequest = void 0; 136 | stream.writeRequests = []; 137 | stream.backpressure = false; 138 | } 139 | 140 | export function IsWritableStream(x): x is WritableStream { 141 | return typeof x === "object" && x.hasOwnProperty("writableStreamController"); 142 | } 143 | 144 | export function IsWritableStreamLocked(stream: WritableStream) { 145 | Assert(IsWritableStream(stream)); 146 | return stream.writer !== void 0; 147 | } 148 | 149 | export async function WritableStreamAbort( 150 | stream: WritableStream, 151 | reason 152 | ): Promise { 153 | const { state } = stream; 154 | if (state === "closed" || state === "errored") { 155 | return void 0; 156 | } 157 | if (stream.pendingAbortRequest !== void 0) { 158 | return stream.pendingAbortRequest.promise; 159 | } 160 | Assert(stream.state === "writable" || stream.state === "erroring"); 161 | let wasAlreadyErroring = false; 162 | if (state === "erroring") { 163 | wasAlreadyErroring = true; 164 | reason = void 0; 165 | } 166 | const promise = defer(); 167 | stream.pendingAbortRequest = { 168 | promise, 169 | reason, 170 | wasAlreadyErroring 171 | }; 172 | if (!wasAlreadyErroring) { 173 | WritableStreamStartErroring(stream, reason); 174 | } 175 | return promise; 176 | } 177 | 178 | export function WritableStreamAddWriteRequest(stream: WritableStream) { 179 | Assert(IsWritableStreamLocked(stream)); 180 | Assert(stream.state === "writable"); 181 | const promise = defer(); 182 | stream.writeRequests.push(promise); 183 | return promise; 184 | } 185 | 186 | export function WritableStreamDealWithRejection(stream: WritableStream, error) { 187 | const { state } = stream; 188 | if (state === "writable") { 189 | WritableStreamStartErroring(stream, error); 190 | return; 191 | } 192 | Assert(state === "erroring"); 193 | WritableStreamFinishErroring(stream); 194 | } 195 | 196 | export function WritableStreamStartErroring(stream: WritableStream, reason) { 197 | Assert(stream.storedError === void 0); 198 | Assert(stream.state === "writable"); 199 | const controller = stream.writableStreamController; 200 | Assert(controller !== void 0); 201 | stream.state = "erroring"; 202 | stream.storedError = reason; 203 | const { writer } = stream; 204 | if (writer !== void 0) { 205 | WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason); 206 | } 207 | if (!WritableStreamHasOperationMarkedInFlight(stream) && controller.started) { 208 | WritableStreamFinishErroring(stream); 209 | } 210 | } 211 | 212 | export function WritableStreamFinishErroring(stream: WritableStream) { 213 | Assert(stream.state === "erroring"); 214 | Assert(!WritableStreamHasOperationMarkedInFlight(stream)); 215 | stream.state = "errored"; 216 | stream.writableStreamController[ErrorSteps](); 217 | const { storedError } = stream; 218 | stream.writeRequests.forEach(p => p.reject(storedError)); 219 | stream.writeRequests = []; 220 | if (stream.pendingAbortRequest === void 0) { 221 | WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); 222 | return; 223 | } 224 | const abortRequest = stream.pendingAbortRequest; 225 | stream.pendingAbortRequest = void 0; 226 | if (abortRequest.wasAlreadyErroring) { 227 | abortRequest.promise.reject(storedError); 228 | WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); 229 | return; 230 | } 231 | const promise = stream.writableStreamController[AbortSteps]( 232 | abortRequest.reason 233 | ); 234 | promise 235 | .then(() => { 236 | abortRequest.promise.resolve(void 0); 237 | WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); 238 | }) 239 | .catch(r => { 240 | abortRequest.promise.reject(r); 241 | WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); 242 | }); 243 | } 244 | 245 | export function WritableStreamFinishInFlightWrite(stream: WritableStream) { 246 | Assert(stream.inFlightWriteRequest !== void 0); 247 | stream.inFlightWriteRequest.resolve(void 0); 248 | stream.inFlightWriteRequest = void 0; 249 | } 250 | 251 | export function WritableStreamFinishInFlightWriteWithError( 252 | stream: WritableStream, 253 | error 254 | ) { 255 | Assert(stream.inFlightWriteRequest !== void 0); 256 | stream.inFlightWriteRequest.resolve(void 0); 257 | stream.inFlightWriteRequest = void 0; 258 | Assert(stream.state === "writable" || stream.state === "erroring"); 259 | WritableStreamDealWithRejection(stream, error); 260 | } 261 | 262 | export function WritableStreamFinishInFlightClose(stream: WritableStream) { 263 | Assert(stream.inFlightCloseRequest !== void 0); 264 | stream.inFlightCloseRequest.resolve(void 0); 265 | stream.inFlightCloseRequest = void 0; 266 | const { state } = stream; 267 | Assert(stream.state === "writable" || stream.state === "erroring"); 268 | if (state === "erroring") { 269 | stream.storedError = void 0; 270 | if (stream.pendingAbortRequest !== void 0) { 271 | stream.pendingAbortRequest.promise.resolve(void 0); 272 | stream.pendingAbortRequest = void 0; 273 | } 274 | } 275 | stream.state = "closed"; 276 | const { writer } = stream; 277 | if (writer !== void 0) { 278 | writer.closedPromise.resolve(void 0); 279 | } 280 | Assert(stream.pendingAbortRequest === void 0); 281 | Assert(stream.storedError === void 0); 282 | } 283 | 284 | export function WritableStreamFinishInFlightCloseWithError( 285 | stream: WritableStream, 286 | error 287 | ) { 288 | Assert(stream.inFlightCloseRequest !== void 0); 289 | stream.inFlightCloseRequest.resolve(void 0); 290 | stream.inFlightCloseRequest = void 0; 291 | Assert(stream.state === "writable" || stream.state === "erroring"); 292 | if (stream.pendingAbortRequest !== void 0) { 293 | stream.pendingAbortRequest.promise.reject(error); 294 | stream.pendingAbortRequest = void 0; 295 | } 296 | WritableStreamDealWithRejection(stream, error); 297 | } 298 | 299 | export function WritableStreamCloseQueuedOrInFlight(stream: WritableStream) { 300 | return !( 301 | stream.closeRequest === void 0 || stream.inFlightCloseRequest === void 0 302 | ); 303 | } 304 | 305 | export function WritableStreamHasOperationMarkedInFlight( 306 | stream: WritableStream 307 | ) { 308 | return !( 309 | stream.inFlightWriteRequest === void 0 && 310 | stream.inFlightCloseRequest === void 0 311 | ); 312 | } 313 | 314 | export function WritableStreamMarkCloseRequestInFlight(stream: WritableStream) { 315 | Assert(stream.inFlightCloseRequest === void 0); 316 | Assert(stream.closeRequest !== void 0); 317 | stream.inFlightCloseRequest = stream.closeRequest; 318 | stream.closeRequest = void 0; 319 | } 320 | 321 | export function WritableStreamMarkFirstWriteRequestInFlight( 322 | stream: WritableStream 323 | ) { 324 | Assert(stream.inFlightWriteRequest === void 0); 325 | Assert(stream.writeRequests.length > 0); 326 | const writerRequest = stream.writeRequests.shift(); 327 | stream.inFlightWriteRequest = writerRequest; 328 | } 329 | 330 | export function WritableStreamRejectCloseAndClosedPromiseIfNeeded( 331 | stream: WritableStream 332 | ) { 333 | Assert(stream.state === "errored"); 334 | if (stream.pendingAbortRequest !== void 0) { 335 | Assert(stream.inFlightCloseRequest !== void 0); 336 | stream.closeRequest.reject(stream.storedError); 337 | stream.closeRequest = void 0; 338 | } 339 | const { writer } = stream; 340 | if (writer !== void 0) { 341 | writer.closedPromise.reject(stream.storedError); 342 | } 343 | } 344 | 345 | export function WritableStreamUpdateBackpressure( 346 | stream: WritableStream, 347 | backpressure: boolean 348 | ) { 349 | Assert(stream.state === "writable"); 350 | Assert(!WritableStreamCloseQueuedOrInFlight(stream)); 351 | const { writer } = stream; 352 | if (writer !== void 0 && backpressure !== stream.backpressure) { 353 | if (backpressure) { 354 | writer.readyPromise = defer(); 355 | } else { 356 | Assert(!backpressure); 357 | writer.readyPromise.resolve(void 0); 358 | } 359 | } 360 | stream.backpressure = backpressure; 361 | } 362 | -------------------------------------------------------------------------------- /readable_stream.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsReadableStreamDefaultReader, 3 | ReadableStreamDefaultReader, 4 | ReadableStreamDefaultReaderRead, 5 | ReadableStreamReader, 6 | ReadableStreamReaderGenericRelease 7 | } from "./readable_stream_reader.ts"; 8 | import { Assert, isAbortSignal } from "./util.ts"; 9 | import { 10 | ReadableStreamController, 11 | ReadableStreamDefaultController, 12 | ReadableStreamDefaultControllerClose, 13 | ReadableStreamDefaultControllerEnqueue, 14 | ReadableStreamDefaultControllerError, 15 | SetUpReadableStreamDefaultController, 16 | SetUpReadableStreamDefaultControllerFromUnderlyingSource 17 | } from "./readable_stream_controller.ts"; 18 | import { 19 | IsNonNegativeNumber, 20 | MakeSizeAlgorithmFromSizeFunction, 21 | ValidateAndNormalizeHighWaterMark 22 | } from "./misc.ts"; 23 | import { defer } from "./defer.ts"; 24 | import { 25 | AcquireWritableStreamDefaultWriter, 26 | IsWritableStream, 27 | IsWritableStreamLocked, 28 | WritableStream, 29 | WritableStreamAbort, 30 | WritableStreamCloseQueuedOrInFlight 31 | } from "./writable_stream.ts"; 32 | import { 33 | WritableStreamDefaultWriterCloseWithErrorPropagation, 34 | WritableStreamDefaultWriterGetDesiredSize, 35 | WritableStreamDefaultWriterRelease 36 | } from "./writable_stream_writer.ts"; 37 | import { QueuingStrategy } from "./strategy.ts"; 38 | import { 39 | IsReadableByteStreamController, 40 | SetUpReadableByteStreamController, 41 | SetUpReadableByteStreamControllerFromUnderlyingSource, 42 | ReadableByteStreamController 43 | } from "./readable_byte_stream_controller.ts"; 44 | import { 45 | IsReadableStreamBYOBReader, 46 | ReadableStreamBYOBReader 47 | } from "./readable_stream_byob_reader.ts"; 48 | 49 | export type UnderlyingSource = { 50 | type?: "bytes"; 51 | autoAllocateChunkSize?: number; 52 | start?: (controller: ReadableStreamController) => any; 53 | pull?: (controller: ReadableStreamController) => any; 54 | cancel?: CancelAlgorithm; 55 | }; 56 | 57 | export type StartAlgorithm = () => any; 58 | export type PullAlgorithm = () => Promise; 59 | export type CancelAlgorithm = (reason) => Promise; 60 | export type SizeAlgorithm = (chunk) => number; 61 | 62 | export type ReadableStreamReadResult = { value: T; done: boolean }; 63 | 64 | export class ReadableStream { 65 | disturbed: boolean; 66 | readableStreamController: 67 | | ReadableByteStreamController 68 | | ReadableStreamDefaultController; 69 | reader: ReadableStreamDefaultReader | ReadableStreamBYOBReader; 70 | state: "readable" | "closed" | "errored"; 71 | storedError: Error; 72 | 73 | constructor( 74 | underlyingSource: UnderlyingSource = {}, 75 | strategy: QueuingStrategy = {} 76 | ) { 77 | InitializeReadableStream(this); 78 | let { highWaterMark, size } = strategy; 79 | const { type } = underlyingSource; 80 | if (type === "bytes") { 81 | if (size !== void 0) { 82 | throw new RangeError(); 83 | } 84 | if (highWaterMark === void 0) { 85 | highWaterMark = 0; 86 | } 87 | highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); 88 | SetUpReadableByteStreamControllerFromUnderlyingSource( 89 | this, 90 | underlyingSource, 91 | highWaterMark 92 | ); 93 | } else if (type === void 0) { 94 | const sizeAlgorithm = MakeSizeAlgorithmFromSizeFunction(size); 95 | if (highWaterMark === void 0) { 96 | highWaterMark = 0; 97 | } 98 | highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); 99 | SetUpReadableStreamDefaultControllerFromUnderlyingSource({ 100 | stream: this, 101 | underlyingSource, 102 | highWaterMark, 103 | sizeAlgorithm 104 | }); 105 | } else { 106 | throw new RangeError(); 107 | } 108 | } 109 | 110 | get locked(): boolean { 111 | if (!IsReadableStream(this)) { 112 | throw new TypeError(); 113 | } 114 | return IsReadableStreamLocked(this); 115 | } 116 | 117 | cancel(reason?): Promise { 118 | if (IsReadableStream(this)) { 119 | return Promise.reject(new TypeError()); 120 | } 121 | if (IsReadableStreamLocked(this)) { 122 | return Promise.reject(new TypeError()); 123 | } 124 | return ReadableStreamCancel(this, reason); 125 | } 126 | 127 | getReader( 128 | params: { mode?: "byob" } = {} 129 | ): ReadableStreamBYOBReader | ReadableStreamReader { 130 | if (!IsReadableStream(this)) { 131 | throw new TypeError(); 132 | } 133 | if (params.mode === void 0) { 134 | return AcquireReadableStreamDefaultReader(this); 135 | } 136 | if (params.mode === "byob") { 137 | return AcquireReadableStreamBYOBReader(this); 138 | } 139 | throw new RangeError(); 140 | } 141 | 142 | pipeThrough( 143 | { 144 | writable, 145 | readable 146 | }: { 147 | writable: WritableStream; 148 | readable: ReadableStream; 149 | }, 150 | { 151 | preventClose, 152 | preventAbort, 153 | preventCancel, 154 | signal 155 | }: { 156 | preventClose?: boolean; 157 | preventAbort?: boolean; 158 | preventCancel?: boolean; 159 | signal?: domTypes.AbortSignal; 160 | } = {} 161 | ) { 162 | if (!IsReadableStream(this)) { 163 | throw new TypeError("this is not ReadableStream"); 164 | } 165 | if (!IsWritableStream(writable)) { 166 | throw new TypeError("writable is not WritableStream"); 167 | } 168 | if (!IsReadableStream(readable)) { 169 | throw new TypeError("readable is not ReadableStream"); 170 | } 171 | preventClose = !!preventClose; 172 | preventAbort = !!preventAbort; 173 | preventCancel = !!preventCancel; 174 | if (signal !== void 0 && !isAbortSignal(signal)) { 175 | throw new TypeError("signal is not instance of AbortSignal"); 176 | } 177 | if (IsReadableStreamLocked(this)) { 178 | throw new TypeError("this stream is locked"); 179 | } 180 | if (IsWritableStreamLocked(writable)) { 181 | throw new TypeError("writable is locked"); 182 | } 183 | ReadableStreamPipeTo( 184 | this, 185 | writable, 186 | preventClose, 187 | preventAbort, 188 | preventCancel, 189 | signal 190 | ); 191 | return readable; 192 | } 193 | 194 | async pipeTo( 195 | dest: WritableStream, 196 | { 197 | preventClose, 198 | preventAbort, 199 | preventCancel, 200 | signal 201 | }: { 202 | preventClose?: boolean; 203 | preventAbort?: boolean; 204 | preventCancel?: boolean; 205 | signal?; 206 | } = {} 207 | ): Promise { 208 | if (!IsReadableStream(this)) { 209 | throw new TypeError("this is not ReadableStream"); 210 | } 211 | if (!IsWritableStream(dest)) { 212 | throw new TypeError("dest is not WritableStream"); 213 | } 214 | preventClose = !!preventClose; 215 | preventAbort = !!preventAbort; 216 | preventCancel = !!preventCancel; 217 | if (signal !== void 0 && !isAbortSignal(signal)) { 218 | throw new TypeError("signal is not instance of AbortSignal"); 219 | } 220 | if (IsReadableStreamLocked(this)) { 221 | throw new TypeError("this stream is locked"); 222 | } 223 | if (IsWritableStreamLocked(dest)) { 224 | throw new TypeError("writable is locked"); 225 | } 226 | return ReadableStreamPipeTo( 227 | this, 228 | dest, 229 | preventClose, 230 | preventCancel, 231 | preventAbort, 232 | signal 233 | ); 234 | } 235 | 236 | tee(): [ReadableStream, ReadableStream] { 237 | if (!IsReadableStream(this)) { 238 | throw new TypeError(); 239 | } 240 | return ReadableStreamTee(this, false); 241 | } 242 | } 243 | 244 | function AcquireReadableStreamBYOBReader( 245 | stream: ReadableStream 246 | ): ReadableStreamBYOBReader { 247 | return new ReadableStreamBYOBReader(stream); 248 | } 249 | 250 | function AcquireReadableStreamDefaultReader( 251 | stream: ReadableStream 252 | ): ReadableStreamDefaultReader { 253 | return new ReadableStreamDefaultReader(stream); 254 | } 255 | 256 | export function CreateReadableStream( 257 | startAlgorithm: StartAlgorithm, 258 | pullAlgorithm: PullAlgorithm, 259 | cancelAlgorithm: CancelAlgorithm, 260 | highWaterMark: number = 1, 261 | sizeAlgorithm: SizeAlgorithm = () => 1 262 | ): ReadableStream { 263 | Assert(IsNonNegativeNumber(highWaterMark)); 264 | const stream = Object.create(ReadableStream.prototype); 265 | InitializeReadableStream(stream); 266 | const controller = Object.create(ReadableStreamDefaultController.prototype); 267 | SetUpReadableStreamDefaultController({ 268 | stream, 269 | controller, 270 | startAlgorithm, 271 | pullAlgorithm, 272 | cancelAlgorithm, 273 | highWaterMark, 274 | sizeAlgorithm 275 | }); 276 | return stream; 277 | } 278 | 279 | export function CreateReadableByteStream( 280 | startAlgorithm: StartAlgorithm, 281 | pullAlgorithm: PullAlgorithm, 282 | cancelAlgorithm: CancelAlgorithm, 283 | highWaterMark: number = 1, 284 | autoAllocateChunkSize?: number 285 | ) { 286 | Assert(IsNonNegativeNumber(highWaterMark)); 287 | if (autoAllocateChunkSize !== void 0) { 288 | Assert(Number.isInteger(autoAllocateChunkSize)); 289 | Assert(autoAllocateChunkSize > 0); 290 | } 291 | const stream = Object.create(ReadableStream.prototype); 292 | InitializeReadableStream(stream); 293 | const controller = Object.create(ReadableByteStreamController.prototype); 294 | SetUpReadableByteStreamController( 295 | stream, 296 | controller, 297 | startAlgorithm, 298 | pullAlgorithm, 299 | cancelAlgorithm, 300 | highWaterMark, 301 | autoAllocateChunkSize 302 | ); 303 | return stream; 304 | } 305 | 306 | export function InitializeReadableStream(stream: ReadableStream) { 307 | stream.state = "readable"; 308 | stream.reader = void 0; 309 | stream.storedError = void 0; 310 | stream.disturbed = false; 311 | } 312 | 313 | export function IsReadableStream(x): x is ReadableStream { 314 | return typeof x === "object" && x.hasOwnProperty("readableStreamController"); 315 | } 316 | 317 | export function IsReadableStreamDisturbed(stream: ReadableStream): boolean { 318 | Assert(IsReadableStream(stream)); 319 | return stream.disturbed; 320 | } 321 | 322 | export function IsReadableStreamLocked(stream: ReadableStream): boolean { 323 | Assert(IsReadableStream(stream)); 324 | return stream.reader !== void 0; 325 | } 326 | 327 | export function ReadableStreamTee( 328 | stream: ReadableStream, 329 | cloneForBranch2: boolean 330 | ): [ReadableStream, ReadableStream] { 331 | Assert(IsReadableStream(stream)); 332 | Assert(typeof cloneForBranch2 === "boolean"); 333 | const reader = AcquireReadableStreamDefaultReader(stream); 334 | let closedOrErrored = false; 335 | let canceled1 = false; 336 | let canceled2 = false; 337 | let reason1 = void 0; 338 | let reason2 = void 0; 339 | let branch1: ReadableStream = void 0; 340 | let branch2: ReadableStream = void 0; 341 | let cancelPromise = defer(); 342 | const pullAlgorithm: PullAlgorithm = () => { 343 | return ReadableStreamDefaultReaderRead(reader).then( 344 | (result: { value; done: boolean }) => { 345 | Assert(typeof result === "object"); 346 | const { value, done } = result; 347 | Assert(typeof done === "boolean"); 348 | if (done && !closedOrErrored) { 349 | if (!canceled1) { 350 | ReadableStreamDefaultControllerClose( 351 | branch1.readableStreamController 352 | ); 353 | } 354 | if (!canceled2) { 355 | ReadableStreamDefaultControllerClose( 356 | branch2.readableStreamController 357 | ); 358 | } 359 | } 360 | if (closedOrErrored) { 361 | return; 362 | } 363 | let [value1, value2] = [value, value]; 364 | if (!canceled2 && cloneForBranch2) { 365 | //value2 <- ?StructuredDeserialize( ? StructuredSerialize( value2 ), 現在の Realm Record ) 366 | } 367 | if (!canceled1) { 368 | ReadableStreamDefaultControllerEnqueue( 369 | branch1.readableStreamController, 370 | value1 371 | ); 372 | } 373 | if (!canceled2) { 374 | ReadableStreamDefaultControllerEnqueue( 375 | branch1.readableStreamController, 376 | value2 377 | ); 378 | } 379 | } 380 | ); 381 | }; 382 | const cancel1Algorithm: CancelAlgorithm = reason => { 383 | canceled1 = true; 384 | reason1 = reason; 385 | if (canceled2) { 386 | const compositeReason = [reason1, reason2]; 387 | const cancelResult = ReadableStreamCancel(stream, compositeReason); 388 | cancelPromise.resolve(cancelResult); 389 | } 390 | return cancelPromise; 391 | }; 392 | const cancel2Algorithm: CancelAlgorithm = reason => { 393 | canceled2 = true; 394 | reason2 = reason; 395 | if (canceled1) { 396 | const compositeReason = [reason1, reason2]; 397 | const cancelResult = ReadableStreamCancel(stream, compositeReason); 398 | cancelPromise.resolve(cancelResult); 399 | } 400 | return cancelPromise; 401 | }; 402 | const startAlgorithm: StartAlgorithm = () => void 0; 403 | branch1 = CreateReadableStream( 404 | startAlgorithm, 405 | pullAlgorithm, 406 | cancel1Algorithm 407 | ); 408 | branch2 = CreateReadableStream( 409 | startAlgorithm, 410 | pullAlgorithm, 411 | cancel2Algorithm 412 | ); 413 | reader.closedPromise.catch(r => { 414 | if (!closedOrErrored) { 415 | ReadableStreamDefaultControllerError(branch1.readableStreamController, r); 416 | ReadableStreamDefaultControllerError(branch2.readableStreamController, r); 417 | closedOrErrored = true; 418 | } 419 | }); 420 | return [branch1, branch2]; 421 | } 422 | 423 | export async function ReadableStreamPipeTo( 424 | source: ReadableStream, 425 | dest: WritableStream, 426 | preventClose: boolean, 427 | preventAbort: boolean, 428 | preventCancel: boolean, 429 | signal? 430 | ) { 431 | Assert(IsReadableStream(source)); 432 | Assert(IsWritableStream(dest)); 433 | Assert(typeof preventCancel === "boolean"); 434 | Assert(typeof preventAbort === "boolean"); 435 | Assert(typeof preventClose === "boolean"); 436 | Assert(signal === void 0 || isAbortSignal(signal)); 437 | Assert(!IsReadableStreamLocked(source)); 438 | Assert(!IsWritableStreamLocked(dest)); 439 | let reader: ReadableStreamBYOBReader | ReadableStreamDefaultReader; 440 | if (IsReadableByteStreamController(source.readableStreamController)) { 441 | reader = AcquireReadableStreamBYOBReader(source); 442 | } else { 443 | reader = AcquireReadableStreamDefaultReader(source); 444 | } 445 | const writer = AcquireWritableStreamDefaultWriter(dest); 446 | let shutingDown = false; 447 | const promsie = defer(); 448 | let abortAlgorithm; 449 | if (!signal) { 450 | abortAlgorithm = () => { 451 | let error = new Error("aborted"); 452 | const actions = []; 453 | if (!preventAbort) { 454 | actions.push(async () => { 455 | if (dest.state === "writable") { 456 | return WritableStreamAbort(dest, error); 457 | } 458 | }); 459 | } 460 | if (!preventCancel) { 461 | actions.push(async () => { 462 | if (source.state === "readable") { 463 | return ReadableStreamCancel(source, error); 464 | } 465 | }); 466 | } 467 | shutdown(error, () => Promise.all(actions.map(p => p()))); 468 | if (signal.aborted) { 469 | abortAlgorithm(); 470 | return promsie; 471 | } 472 | signal.addEventListener("onabort", abortAlgorithm); 473 | }; 474 | } 475 | const finalize = (error?) => { 476 | WritableStreamDefaultWriterRelease(writer); 477 | ReadableStreamReaderGenericRelease(reader); 478 | if (signal) { 479 | signal.removeEventListener("onabort", abortAlgorithm); 480 | } 481 | if (error) { 482 | promsie.reject(error); 483 | } else { 484 | promsie.resolve(); 485 | } 486 | }; 487 | const shutdown = (err?, action?: () => Promise) => { 488 | if (shutingDown) { 489 | return; 490 | } 491 | shutingDown = true; 492 | if ( 493 | dest.state === "writable" || 494 | !WritableStreamCloseQueuedOrInFlight(dest) 495 | ) { 496 | } 497 | if (!action) { 498 | finalize(err); 499 | return; 500 | } 501 | action() 502 | .then(() => finalize(err)) 503 | .catch(finalize); 504 | }; 505 | (async () => { 506 | while (true) { 507 | const desiredSize = WritableStreamDefaultWriterGetDesiredSize(writer); 508 | if (desiredSize === null || desiredSize <= 0) { 509 | return; 510 | } 511 | if (source.state === "errored") { 512 | if (!preventAbort) { 513 | shutdown(source.storedError, () => { 514 | return WritableStreamAbort(dest, source.storedError); 515 | }); 516 | } else { 517 | shutdown(source.storedError); 518 | } 519 | } else if (dest.state === "errored") { 520 | if (!preventCancel) { 521 | shutdown(dest.storedError, () => { 522 | return ReadableStreamCancel(source, dest.storedError); 523 | }); 524 | } else { 525 | shutdown(dest.storedError); 526 | } 527 | } else if (source.state === "closed") { 528 | if (!preventClose) { 529 | shutdown(void 0, () => { 530 | return WritableStreamDefaultWriterCloseWithErrorPropagation(writer); 531 | }); 532 | } else { 533 | shutdown(); 534 | } 535 | } else if ( 536 | WritableStreamCloseQueuedOrInFlight(dest) || 537 | dest.state === "closed" 538 | ) { 539 | const destClosed = new TypeError(); 540 | if (!preventCancel) { 541 | shutdown(destClosed, () => { 542 | return ReadableStreamCancel(source, destClosed); 543 | }); 544 | } else { 545 | shutdown(destClosed); 546 | } 547 | } 548 | if (IsReadableStreamBYOBReader(reader)) { 549 | let view = new Uint8Array(desiredSize); 550 | const { done } = await reader.read(view); 551 | if (done) break; 552 | await writer.write(view); 553 | } else { 554 | const { value, done } = await reader.read(); 555 | if (done) break; 556 | await writer.write(value); 557 | } 558 | } 559 | })(); 560 | return promsie; 561 | } 562 | 563 | export function ReadableStreamAddReadIntoRequest( 564 | stream: ReadableStream, 565 | forAuthorCode 566 | ) { 567 | Assert(IsReadableStreamBYOBReader(stream.reader)); 568 | const reader = stream.reader as ReadableStreamBYOBReader; 569 | Assert(stream.state === "readable" || stream.state === "closed"); 570 | const promise = defer(); 571 | const readIntoRequest = { promise, forAuthorCode }; 572 | reader.readIntoRequests.push(readIntoRequest); 573 | return promise; 574 | } 575 | 576 | export function ReadableStreamAddReadRequest( 577 | stream: ReadableStream, 578 | forAuthorCode 579 | ): Promise<{ value; done: boolean }> { 580 | Assert(IsReadableStreamDefaultReader(stream.reader)); 581 | const reader = stream.reader as ReadableStreamDefaultReader; 582 | Assert(stream.state === "readable" || stream.state === "closed"); 583 | const promise = defer<{ value; done: boolean }>(); 584 | const readIntoRequest = { promise, forAuthorCode }; 585 | reader.readRequests.push(readIntoRequest); 586 | return promise; 587 | } 588 | 589 | export function ReadableStreamCancel( 590 | stream: ReadableStream, 591 | reason 592 | ): Promise { 593 | stream.disturbed = true; 594 | if (stream.state === "closed") { 595 | return Promise.reject(void 0); 596 | } 597 | if (stream.state === "errored") { 598 | return Promise.reject(stream.storedError); 599 | } 600 | ReadableStreamClose(stream); 601 | const sourceCancelPromise = stream.readableStreamController.cancelAlgorithm( 602 | reason 603 | ); 604 | return sourceCancelPromise.then(() => void 0); 605 | } 606 | 607 | export function ReadableStreamClose(stream: ReadableStream) { 608 | Assert(stream.state === "readable"); 609 | stream.state = "closed"; 610 | const reader = stream.reader; 611 | if (reader === void 0) { 612 | return; 613 | } 614 | if (IsReadableStreamDefaultReader(reader)) { 615 | for (let req of reader.readRequests) { 616 | const resolved = ReadableStreamCreateReadResult( 617 | void 0, 618 | true, 619 | req.forAuthorCode 620 | ); 621 | req.promise.resolve(resolved); 622 | } 623 | reader.readRequests = []; 624 | } 625 | reader.closedPromise.resolve(void 0); 626 | } 627 | 628 | export function ReadableStreamCreateReadResult( 629 | value, 630 | done: boolean, 631 | forAuthorCode: boolean 632 | ): ReadableStreamReadResult { 633 | const ret = forAuthorCode ? Object.create({}) : Object.create(null); 634 | ret["value"] = value as T; 635 | ret["done"] = done; 636 | return { value, done }; 637 | } 638 | 639 | export function ReadableStreamError(stream: ReadableStream, e) { 640 | Assert(IsReadableStream(stream)); 641 | Assert(stream.state === "readable"); 642 | stream.state = "errored"; 643 | stream.storedError = e; 644 | const reader = stream.reader; 645 | if (stream.reader === void 0) { 646 | return; 647 | } 648 | if (IsReadableStreamDefaultReader(reader)) { 649 | for (const req of reader.readRequests) { 650 | req.promise.reject(e); 651 | } 652 | reader.readRequests = []; 653 | } else if (IsReadableStreamBYOBReader(reader)) { 654 | for (const req of reader.readIntoRequests) { 655 | req.promise.reject(e); 656 | } 657 | reader.readIntoRequests = []; 658 | } 659 | reader.closedPromise.reject(e); 660 | //TODO: Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. 661 | } 662 | 663 | export function ReadableStreamFulfillReadIntoRequest( 664 | stream: ReadableStream, 665 | chunk, 666 | done 667 | ) { 668 | const reader = stream.reader; 669 | const req = (reader).readIntoRequests.shift(); 670 | req.promise.resolve( 671 | ReadableStreamCreateReadResult(chunk, done, req.forAuthorCode) 672 | ); 673 | } 674 | 675 | export function ReadableStreamFulfillReadRequest( 676 | stream: ReadableStream, 677 | chunk, 678 | done 679 | ) { 680 | const reader = stream.reader; 681 | const req = (>reader).readRequests.shift(); 682 | req.promise.resolve( 683 | ReadableStreamCreateReadResult(chunk, done, req.forAuthorCode) 684 | ); 685 | } 686 | 687 | export function ReadableStreamGetNumReadIntoRequests(stream: ReadableStream) { 688 | return (stream.reader).readIntoRequests.length; 689 | } 690 | 691 | export function ReadableStreamGetNumReadRequests(stream) { 692 | return (>stream.reader).readRequests.length; 693 | } 694 | 695 | export function ReadableStreamHasBYOBReader(stream: ReadableStream): boolean { 696 | const reader = stream.reader; 697 | if (reader === void 0) { 698 | return false; 699 | } 700 | return IsReadableStreamBYOBReader(reader); 701 | } 702 | 703 | export function ReadableStreamHasDefaultReader(stream): boolean { 704 | const reader = stream.reader; 705 | if (reader === void 0) { 706 | return false; 707 | } 708 | return IsReadableStreamDefaultReader(reader); 709 | } 710 | -------------------------------------------------------------------------------- /readable_byte_stream_controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CancelAlgorithm, 3 | IsReadableStreamLocked, 4 | PullAlgorithm, 5 | ReadableStream, 6 | ReadableStreamAddReadIntoRequest, 7 | ReadableStreamAddReadRequest, 8 | ReadableStreamClose, 9 | ReadableStreamCreateReadResult, 10 | ReadableStreamError, 11 | ReadableStreamFulfillReadIntoRequest, 12 | ReadableStreamFulfillReadRequest, 13 | ReadableStreamGetNumReadIntoRequests, 14 | ReadableStreamGetNumReadRequests, 15 | ReadableStreamHasBYOBReader, 16 | ReadableStreamHasDefaultReader, 17 | ReadableStreamReadResult, 18 | StartAlgorithm, 19 | UnderlyingSource 20 | } from "./readable_stream.ts"; 21 | import { 22 | ReadableStreamBYOBRequest, 23 | ReadableStreamBYOBRequestImpl, 24 | SetUpReadableStreamBYOBRequest 25 | } from "./readable_stream_request.ts"; 26 | import { 27 | CreateAlgorithmFromUnderlyingMethod, 28 | InvokeOrNoop, 29 | IsFiniteNonNegativeNumber, 30 | ResetQueue, 31 | TransferArrayBuffer, 32 | ValidateAndNormalizeHighWaterMark 33 | } from "./misc.ts"; 34 | import { Assert, isArrayBufferView } from "./util.ts"; 35 | import { 36 | PullIntoDescriptor, 37 | ReadableStreamController, 38 | ReadableStreamControllerBase 39 | } from "./readable_stream_controller.ts"; 40 | 41 | export class ReadableByteStreamController extends ReadableStreamControllerBase 42 | implements ReadableStreamController { 43 | constructor() { 44 | super(); 45 | throw new TypeError(); 46 | } 47 | 48 | controlledReadableByteStream: ReadableStream; 49 | _byobRequest: ReadableStreamBYOBRequestImpl; 50 | get byobRequest(): ReadableStreamBYOBRequest { 51 | if (!IsReadableByteStreamController(this)) { 52 | throw new TypeError(); 53 | } 54 | if (this._byobRequest === void 0 && this.pendingPullIntos.length > 0) { 55 | const firstDescriptor = this.pendingPullIntos[0]; 56 | const { buffer, byteOffset, bytesFilled, byteLength } = firstDescriptor; 57 | const view = new Uint8Array( 58 | buffer, 59 | byteOffset + bytesFilled, 60 | byteLength - bytesFilled 61 | ); 62 | const byobRequest = Object.create( 63 | ReadableStreamBYOBRequestImpl.prototype 64 | ); 65 | SetUpReadableStreamBYOBRequest(byobRequest, this, view); 66 | this._byobRequest = byobRequest; 67 | } 68 | return this._byobRequest; 69 | } 70 | 71 | get desiredSize(): number { 72 | if (!IsReadableByteStreamController(this)) { 73 | throw new TypeError(); 74 | } 75 | return ReadableByteStreamControllerGetDesiredSize(this); 76 | } 77 | 78 | close(): void { 79 | if (!IsReadableByteStreamController(this)) { 80 | throw new TypeError(); 81 | } 82 | if (this.closeRequested) { 83 | throw new TypeError(); 84 | } 85 | if (this.controlledReadableByteStream.state !== "readable") { 86 | throw new TypeError(); 87 | } 88 | ReadableByteStreamControllerClose(this); 89 | } 90 | 91 | enqueue(chunk: ArrayBufferView): void { 92 | if (!IsReadableByteStreamController(this)) { 93 | throw new TypeError(); 94 | } 95 | if (this.closeRequested) { 96 | throw new TypeError(); 97 | } 98 | if (this.controlledReadableByteStream.state !== "readable") { 99 | throw new TypeError(); 100 | } 101 | if (typeof chunk !== "object") { 102 | throw new TypeError(); 103 | } 104 | if (!isArrayBufferView(chunk)) { 105 | throw new TypeError("chunk is not ArrayBufferView: " + chunk); 106 | } 107 | ReadableByteStreamControllerEnqueue(this, chunk); 108 | } 109 | 110 | error(e): void { 111 | if (!IsReadableByteStreamController(this)) { 112 | throw new TypeError(); 113 | } 114 | ReadableByteStreamControllerError(this, e); 115 | } 116 | 117 | CancelSteps(reason): Promise { 118 | ResetQueue(this); 119 | const result = this.cancelAlgorithm(reason); 120 | ReadableByteStreamControllerClearAlgorithms(this); 121 | return result; 122 | } 123 | 124 | PullSteps( 125 | forAuthorCode?: boolean 126 | ): Promise> { 127 | const stream = this.controlledReadableByteStream; 128 | Assert(ReadableStreamHasDefaultReader(stream)); 129 | if (this.queueTotalSize > 0) { 130 | Assert(ReadableStreamGetNumReadRequests(stream) === 0); 131 | const entry = this.queue.shift(); 132 | this.queueTotalSize -= entry.byteLength; 133 | ReadableByteStreamControllerHandleQueueDrain(this); 134 | const view = new Uint8Array( 135 | entry.buffer, 136 | entry.byteOffset, 137 | entry.byteLength 138 | ); 139 | return Promise.resolve( 140 | ReadableStreamCreateReadResult(view, false, forAuthorCode) 141 | ); 142 | } 143 | const { autoAllocateChunkSize } = this; 144 | if (autoAllocateChunkSize !== void 0) { 145 | let buffer: ArrayBuffer; 146 | try { 147 | buffer = new ArrayBuffer(autoAllocateChunkSize); 148 | } catch (e) { 149 | return Promise.reject(e); 150 | } 151 | const pullIntoDescriptor: PullIntoDescriptor = { 152 | buffer, 153 | byteOffset: 0, 154 | byteLength: autoAllocateChunkSize, 155 | bytesFilled: 0, 156 | elementSize: 1, 157 | ctor: Uint8Array, 158 | readerType: "default" 159 | }; 160 | this.pendingPullIntos.push(pullIntoDescriptor); 161 | } 162 | const promise = ReadableStreamAddReadRequest(stream, forAuthorCode); 163 | ReadableByteStreamControllerCallPullIfNeeded(this); 164 | return promise; 165 | } 166 | } 167 | 168 | export function IsReadableByteStreamController( 169 | x 170 | ): x is ReadableByteStreamController { 171 | return ( 172 | typeof x === "object" && x.hasOwnProperty("controlledReadableByteStream") 173 | ); 174 | } 175 | 176 | export function ReadableByteStreamControllerCallPullIfNeeded( 177 | controller: ReadableByteStreamController 178 | ) { 179 | const shouldPull = ReadableByteStreamControllerShouldCallPull(controller); 180 | if (!shouldPull) { 181 | return; 182 | } 183 | if (controller.pulling) { 184 | controller.pullAgain = true; 185 | return; 186 | } 187 | Assert(!controller.pullAgain); 188 | controller.pulling = true; 189 | controller 190 | .pullAlgorithm() 191 | .then(() => { 192 | controller.pulling = false; 193 | if (controller.pullAgain) { 194 | controller.pullAgain = false; 195 | ReadableByteStreamControllerCallPullIfNeeded(controller); 196 | } 197 | }) 198 | .catch(r => { 199 | ReadableByteStreamControllerError(controller, r); 200 | }); 201 | } 202 | 203 | export function ReadableByteStreamControllerClearAlgorithms( 204 | controller: ReadableByteStreamController 205 | ) { 206 | controller.pullAlgorithm = void 0; 207 | controller.cancelAlgorithm = void 0; 208 | } 209 | 210 | export function ReadableByteStreamControllerClearPendingPullIntos( 211 | controller: ReadableByteStreamController 212 | ) { 213 | ReadableByteStreamControllerInvalidateBYOBRequest(controller); 214 | controller.pendingPullIntos = []; 215 | } 216 | 217 | export function ReadableByteStreamControllerClose( 218 | controller: ReadableByteStreamController 219 | ) { 220 | const stream = controller.controlledReadableByteStream; 221 | Assert(controller.closeRequested === false); 222 | Assert(stream.state === "readable"); 223 | if (controller.queueTotalSize > 0) { 224 | controller.closeRequested = true; 225 | return; 226 | } 227 | if (controller.pendingPullIntos.length > 0) { 228 | const firstPengingPullInfo = controller.pendingPullIntos[0]; 229 | if (firstPengingPullInfo.bytesFilled > 0) { 230 | const e = new TypeError(); 231 | ReadableByteStreamControllerError(controller, e); 232 | throw e; 233 | } 234 | } 235 | ReadableByteStreamControllerClearAlgorithms(controller); 236 | ReadableStreamClose(stream); 237 | } 238 | 239 | export function ReadableByteStreamControllerCommitPullIntoDescriptor( 240 | stream: ReadableStream, 241 | pullIntoDescriptor: PullIntoDescriptor 242 | ) { 243 | Assert(stream.state !== "errored"); 244 | let done = false; 245 | if (stream.state === "closed") { 246 | Assert(pullIntoDescriptor.bytesFilled === 0); 247 | done = true; 248 | } 249 | const filledView = ReadableByteStreamControllerConvertPullIntoDescriptor( 250 | pullIntoDescriptor 251 | ); 252 | if (pullIntoDescriptor.readerType === "default") { 253 | ReadableStreamFulfillReadRequest(stream, filledView, done); 254 | } else { 255 | Assert(pullIntoDescriptor.readerType === "byob"); 256 | ReadableStreamFulfillReadIntoRequest(stream, filledView, done); 257 | } 258 | } 259 | 260 | export function ReadableByteStreamControllerConvertPullIntoDescriptor( 261 | pullIntoDescriptor: PullIntoDescriptor 262 | ) { 263 | const { bytesFilled, elementSize } = pullIntoDescriptor; 264 | Assert(bytesFilled <= pullIntoDescriptor.byteLength); 265 | Assert(bytesFilled % pullIntoDescriptor.elementSize === 0); 266 | return new pullIntoDescriptor.ctor( 267 | pullIntoDescriptor.buffer, 268 | pullIntoDescriptor.byteOffset, 269 | bytesFilled / elementSize 270 | ); 271 | } 272 | 273 | export function ReadableByteStreamControllerEnqueue( 274 | controller: ReadableByteStreamController, 275 | chunk: ArrayBufferView 276 | ) { 277 | const stream = controller.controlledReadableByteStream; 278 | Assert(controller.closeRequested === false); 279 | Assert(stream.state === "readable"); 280 | const { buffer } = chunk; 281 | const { byteOffset, byteLength } = chunk; 282 | const transferredBuffer = TransferArrayBuffer(buffer); 283 | if (ReadableStreamHasDefaultReader(stream)) { 284 | if (ReadableStreamGetNumReadRequests(stream) === 0) { 285 | ReadableByteStreamControllerEnqueueChunkToQueue( 286 | controller, 287 | transferredBuffer, 288 | byteOffset, 289 | byteLength 290 | ); 291 | } else { 292 | Assert(controller.queue.length === 0, "l=0"); 293 | const transferredView = new Uint8Array( 294 | transferredBuffer, 295 | byteOffset, 296 | byteLength 297 | ); 298 | ReadableStreamFulfillReadRequest(stream, transferredView, false); 299 | } 300 | } else if (ReadableStreamHasBYOBReader(stream)) { 301 | ReadableByteStreamControllerEnqueueChunkToQueue( 302 | controller, 303 | transferredBuffer, 304 | byteOffset, 305 | byteLength 306 | ); 307 | ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( 308 | controller 309 | ); 310 | } else { 311 | Assert( 312 | IsReadableStreamLocked(stream) === false, 313 | "stream should not be locked" 314 | ); 315 | ReadableByteStreamControllerEnqueueChunkToQueue( 316 | controller, 317 | transferredBuffer, 318 | byteOffset, 319 | byteLength 320 | ); 321 | } 322 | ReadableByteStreamControllerCallPullIfNeeded(controller); 323 | } 324 | 325 | export function ReadableByteStreamControllerEnqueueChunkToQueue( 326 | controller: ReadableByteStreamController, 327 | buffer: ArrayBuffer, 328 | byteOffset: number, 329 | byteLength: number 330 | ) { 331 | controller.queue.push({ 332 | buffer, 333 | byteOffset, 334 | byteLength 335 | }); 336 | controller.queueTotalSize += byteLength; 337 | } 338 | 339 | export function ReadableByteStreamControllerError( 340 | controller: ReadableByteStreamController, 341 | e 342 | ) { 343 | const stream = controller.controlledReadableByteStream; 344 | if (stream.state !== "readable") { 345 | return; 346 | } 347 | ReadableByteStreamControllerClearPendingPullIntos(controller); 348 | ResetQueue(controller); 349 | ReadableByteStreamControllerClearAlgorithms(controller); 350 | ReadableStreamError(controller.controlledReadableByteStream, e); 351 | } 352 | 353 | export function ReadableByteStreamControllerFillHeadPullIntoDescriptor( 354 | controller: ReadableByteStreamController, 355 | size: number, 356 | pullIntoDescriptor: PullIntoDescriptor 357 | ) { 358 | Assert( 359 | controller.pendingPullIntos.length === 0 || 360 | controller.pendingPullIntos[0] === pullIntoDescriptor 361 | ); 362 | ReadableByteStreamControllerInvalidateBYOBRequest(controller); 363 | pullIntoDescriptor.bytesFilled += size; 364 | } 365 | 366 | export function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue( 367 | controller: ReadableByteStreamController, 368 | pullIntoDescriptor: PullIntoDescriptor 369 | ): boolean { 370 | const { elementSize } = pullIntoDescriptor; 371 | const currentAlignedBytes = 372 | pullIntoDescriptor.bytesFilled - 373 | (pullIntoDescriptor.bytesFilled % elementSize); 374 | const maxBytesToCopy = Math.min( 375 | controller.queueTotalSize, 376 | pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled 377 | ); 378 | const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy; 379 | const maxAlignedBytes = maxBytesFilled - (maxBytesFilled % elementSize); 380 | let totalBytesToCopyRemaining = maxBytesToCopy; 381 | let ready = false; 382 | if (maxAlignedBytes > currentAlignedBytes) { 383 | totalBytesToCopyRemaining = 384 | maxAlignedBytes - pullIntoDescriptor.bytesFilled; 385 | ready = true; 386 | } 387 | const { queue } = controller; 388 | while (totalBytesToCopyRemaining > 0) { 389 | const headOfQueue = queue[0]; 390 | const bytesToCopy = Math.min( 391 | totalBytesToCopyRemaining, 392 | headOfQueue.byteLength 393 | ); 394 | const destStart = 395 | pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; 396 | const srcView = new Uint8Array( 397 | headOfQueue.buffer, 398 | headOfQueue.byteOffset, 399 | headOfQueue.byteLength 400 | ); 401 | const destView = new Uint8Array( 402 | pullIntoDescriptor.buffer, 403 | destStart, 404 | bytesToCopy 405 | ); 406 | for (let i = 0; i < bytesToCopy; i++) { 407 | destView[i] = srcView[i]; 408 | } 409 | if (headOfQueue.byteLength === bytesToCopy) { 410 | queue.shift(); 411 | } else { 412 | headOfQueue.byteOffset += bytesToCopy; 413 | headOfQueue.byteLength -= bytesToCopy; 414 | } 415 | controller.queueTotalSize -= bytesToCopy; 416 | ReadableByteStreamControllerFillHeadPullIntoDescriptor( 417 | controller, 418 | bytesToCopy, 419 | pullIntoDescriptor 420 | ); 421 | totalBytesToCopyRemaining -= bytesToCopy; 422 | } 423 | if (ready === false) { 424 | Assert(controller.queueTotalSize === 0); 425 | Assert(pullIntoDescriptor.bytesFilled > 0); 426 | Assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize); 427 | } 428 | return ready; 429 | } 430 | 431 | export function ReadableByteStreamControllerGetDesiredSize( 432 | controller: ReadableByteStreamController 433 | ): number | null { 434 | const stream = controller.controlledReadableByteStream; 435 | const { state } = stream; 436 | if (state === "errored") { 437 | return null; 438 | } 439 | if (state === "closed") { 440 | return 0; 441 | } 442 | return controller.strategyHWM - controller.queueTotalSize; 443 | } 444 | 445 | export function ReadableByteStreamControllerHandleQueueDrain( 446 | controller: ReadableByteStreamController 447 | ) { 448 | Assert(controller.controlledReadableByteStream.state === "readable"); 449 | if (controller.queueTotalSize === 0 && controller.closeRequested) { 450 | ReadableByteStreamControllerClearAlgorithms(controller); 451 | ReadableStreamClose(controller.controlledReadableByteStream); 452 | } else { 453 | ReadableByteStreamControllerCallPullIfNeeded(controller); 454 | } 455 | } 456 | 457 | export function ReadableByteStreamControllerInvalidateBYOBRequest( 458 | controller: ReadableByteStreamController 459 | ) { 460 | if (controller._byobRequest === void 0) { 461 | return; 462 | } 463 | controller._byobRequest.associatedReadableByteStreamController = void 0; 464 | controller._byobRequest._view = void 0; 465 | controller._byobRequest = void 0; 466 | } 467 | 468 | export function ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( 469 | controller: ReadableByteStreamController 470 | ) { 471 | Assert(controller.closeRequested === false); 472 | while (controller.pendingPullIntos.length > 0) { 473 | if (controller.queueTotalSize === 0) { 474 | return; 475 | } 476 | const pullIntoDescriptor = controller.pendingPullIntos[0]; 477 | if ( 478 | ReadableByteStreamControllerFillPullIntoDescriptorFromQueue( 479 | controller, 480 | pullIntoDescriptor 481 | ) === true 482 | ) { 483 | ReadableByteStreamControllerShiftPendingPullInto(controller); 484 | ReadableByteStreamControllerCommitPullIntoDescriptor( 485 | controller.controlledReadableByteStream, 486 | pullIntoDescriptor 487 | ); 488 | } 489 | } 490 | } 491 | 492 | const TypedArraySizeMap = { 493 | Int8Array: [1, Int8Array], 494 | Uint8Array: [1, Uint8Array], 495 | Uint8ClampedArray: [1, Uint8ClampedArray], 496 | Int16Array: [2, Int16Array], 497 | Uint16Array: [2, Uint16Array], 498 | Int32Array: [4, Int32Array], 499 | Uint32Array: [4, Uint32Array], 500 | Float32Array: [4, Float32Array], 501 | Float64Array: [8, Float64Array] 502 | }; 503 | 504 | export function ReadableByteStreamControllerPullInto( 505 | controller: ReadableByteStreamController, 506 | view: ArrayBufferView, 507 | forAuthorCode?: boolean 508 | ): Promise { 509 | const stream = controller.controlledReadableByteStream; 510 | let elementSize = 1; 511 | let ctor = DataView; 512 | const ctorName = view.constructor.name; 513 | if (TypedArraySizeMap[ctorName]) { 514 | [elementSize, ctor] = TypedArraySizeMap[ctorName]; 515 | } 516 | const { byteOffset, byteLength } = view; 517 | const buffer = TransferArrayBuffer(view.buffer); 518 | const pullIntoDescriptor: PullIntoDescriptor = { 519 | buffer, 520 | byteOffset, 521 | byteLength, 522 | bytesFilled: 0, 523 | elementSize, 524 | ctor, 525 | readerType: "byob" 526 | }; 527 | if (controller.pendingPullIntos.length > 0) { 528 | controller.pendingPullIntos.push(pullIntoDescriptor); 529 | return ReadableStreamAddReadIntoRequest(stream, forAuthorCode); 530 | } 531 | if (stream.state === "closed") { 532 | const emptyView = new ctor( 533 | pullIntoDescriptor.buffer, 534 | pullIntoDescriptor.byteOffset, 535 | 0 536 | ); 537 | return Promise.resolve( 538 | ReadableStreamCreateReadResult(emptyView, true, forAuthorCode) 539 | ); 540 | } 541 | if (controller.queueTotalSize > 0) { 542 | if ( 543 | ReadableByteStreamControllerFillPullIntoDescriptorFromQueue( 544 | controller, 545 | pullIntoDescriptor 546 | ) 547 | ) { 548 | const filedView = ReadableByteStreamControllerConvertPullIntoDescriptor( 549 | pullIntoDescriptor 550 | ); 551 | ReadableByteStreamControllerHandleQueueDrain(controller); 552 | return Promise.resolve( 553 | ReadableStreamCreateReadResult(filedView, false, forAuthorCode) 554 | ); 555 | } 556 | if (controller.closeRequested) { 557 | const e = new TypeError(); 558 | ReadableByteStreamControllerError(controller, e); 559 | return Promise.reject(e); 560 | } 561 | } 562 | controller.pendingPullIntos.push(pullIntoDescriptor); 563 | const promise = ReadableStreamAddReadIntoRequest(stream, forAuthorCode); 564 | ReadableByteStreamControllerCallPullIfNeeded(controller); 565 | return promise; 566 | } 567 | 568 | export function ReadableByteStreamControllerRespond( 569 | controller: ReadableByteStreamController, 570 | bytesWritten: number 571 | ): void { 572 | if (IsFiniteNonNegativeNumber(bytesWritten) === false) { 573 | throw new RangeError(); 574 | } 575 | Assert(controller.pendingPullIntos.length > 0); 576 | ReadableByteStreamControllerRespondInternal(controller, bytesWritten); 577 | } 578 | 579 | export function ReadableByteStreamControllerRespondInClosedState( 580 | controller: ReadableByteStreamController, 581 | firstDescriptor: PullIntoDescriptor 582 | ) { 583 | firstDescriptor.buffer = TransferArrayBuffer(firstDescriptor.buffer); 584 | Assert(firstDescriptor.bytesFilled === 0); 585 | const stream = controller.controlledReadableByteStream; 586 | if (ReadableStreamHasBYOBReader(stream)) { 587 | while (ReadableStreamGetNumReadIntoRequests(stream) > 0) { 588 | const pullIntoDescriptor = ReadableByteStreamControllerShiftPendingPullInto( 589 | controller 590 | ); 591 | ReadableByteStreamControllerCommitPullIntoDescriptor( 592 | stream, 593 | pullIntoDescriptor 594 | ); 595 | } 596 | } 597 | } 598 | 599 | export function ReadableByteStreamControllerRespondInReadableState( 600 | controller: ReadableByteStreamController, 601 | bytesWritten: number, 602 | pullIntoDescriptor: PullIntoDescriptor 603 | ) { 604 | if ( 605 | pullIntoDescriptor.bytesFilled + bytesWritten > 606 | pullIntoDescriptor.byteLength 607 | ) { 608 | throw new RangeError(); 609 | } 610 | ReadableByteStreamControllerFillHeadPullIntoDescriptor( 611 | controller, 612 | bytesWritten, 613 | pullIntoDescriptor 614 | ); 615 | if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) { 616 | return; 617 | } 618 | ReadableByteStreamControllerShiftPendingPullInto(controller); 619 | const remainderSize = 620 | pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize; 621 | if (remainderSize > 0) { 622 | const end = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; 623 | const remainder = CloneArrayBuffer( 624 | pullIntoDescriptor.buffer, 625 | end - remainderSize, 626 | remainderSize 627 | ); 628 | ReadableByteStreamControllerEnqueueChunkToQueue( 629 | controller, 630 | remainder, 631 | 0, 632 | remainder.byteLength 633 | ); 634 | } 635 | pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer); 636 | pullIntoDescriptor.bytesFilled = 637 | pullIntoDescriptor.bytesFilled - remainderSize; 638 | ReadableByteStreamControllerCommitPullIntoDescriptor( 639 | controller.controlledReadableByteStream, 640 | pullIntoDescriptor 641 | ); 642 | ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller); 643 | } 644 | 645 | function CloneArrayBuffer( 646 | srcBuffer: ArrayBuffer, 647 | srcByteOffset: number, 648 | srcLength: number 649 | ): ArrayBuffer { 650 | const ret = new ArrayBuffer(srcLength); 651 | const retView = new DataView(ret); 652 | const srcView = new DataView(srcBuffer, srcByteOffset, srcLength); 653 | for (let i = 0; i < srcLength; i++) { 654 | retView[i] = srcView[i]; 655 | } 656 | return ret; 657 | } 658 | 659 | export function ReadableByteStreamControllerRespondInternal( 660 | controller: ReadableByteStreamController, 661 | bytesWritten: number 662 | ) { 663 | const firstDescriptor = controller.pendingPullIntos[0]; 664 | const stream = controller.controlledReadableByteStream; 665 | if (stream.state === "closed") { 666 | if (bytesWritten !== 0) { 667 | throw new TypeError(); 668 | } 669 | ReadableByteStreamControllerRespondInClosedState( 670 | controller, 671 | firstDescriptor 672 | ); 673 | } else { 674 | Assert(stream.state === "readable"); 675 | ReadableByteStreamControllerRespondInReadableState( 676 | controller, 677 | bytesWritten, 678 | firstDescriptor 679 | ); 680 | } 681 | ReadableByteStreamControllerCallPullIfNeeded(controller); 682 | } 683 | 684 | export function ReadableByteStreamControllerRespondWithNewView( 685 | controller: ReadableByteStreamController, 686 | view 687 | ) { 688 | Assert(controller.pendingPullIntos.length > 0); 689 | const firstDescriptor = controller.pendingPullIntos[0]; 690 | if ( 691 | firstDescriptor.byteOffset + firstDescriptor.bytesFilled !== 692 | view.ByteOffset 693 | ) { 694 | throw new RangeError(); 695 | } 696 | if (firstDescriptor.byteLength !== view.ByteLength) { 697 | throw new RangeError(); 698 | } 699 | firstDescriptor.buffer = view.ViewedArrayBuffer; 700 | ReadableByteStreamControllerRespondInternal(controller, view.ByteLength); 701 | } 702 | 703 | export function ReadableByteStreamControllerShiftPendingPullInto( 704 | controller: ReadableByteStreamController 705 | ): PullIntoDescriptor { 706 | const descriptor = controller.pendingPullIntos.shift(); 707 | ReadableByteStreamControllerInvalidateBYOBRequest(controller); 708 | return descriptor; 709 | } 710 | 711 | export function ReadableByteStreamControllerShouldCallPull( 712 | controller: ReadableByteStreamController 713 | ) { 714 | const stream = controller.controlledReadableByteStream; 715 | if (stream.state !== "readable") { 716 | return false; 717 | } 718 | if (controller.closeRequested === true) { 719 | return false; 720 | } 721 | if (controller.started === false) { 722 | return false; 723 | } 724 | if ( 725 | ReadableStreamHasDefaultReader(stream) && 726 | ReadableStreamGetNumReadRequests(stream) > 0 727 | ) { 728 | return true; 729 | } 730 | if ( 731 | ReadableStreamHasBYOBReader(stream) && 732 | ReadableStreamGetNumReadIntoRequests(stream) > 0 733 | ) { 734 | return true; 735 | } 736 | const desiredSize = ReadableByteStreamControllerGetDesiredSize(controller); 737 | Assert(desiredSize !== null); 738 | if (desiredSize > 0) { 739 | return true; 740 | } 741 | return false; 742 | } 743 | 744 | export function SetUpReadableByteStreamController( 745 | stream: ReadableStream, 746 | controller: ReadableByteStreamController, 747 | startAlgorithm: StartAlgorithm, 748 | pullAlgorithm: PullAlgorithm, 749 | cancelAlgorithm: CancelAlgorithm, 750 | highWaterMark: number, 751 | autoAllocateChunkSize: number 752 | ) { 753 | Assert(stream.readableStreamController === void 0); 754 | if (autoAllocateChunkSize !== void 0) { 755 | Assert(Number.isInteger(autoAllocateChunkSize)); 756 | Assert(autoAllocateChunkSize > 0); 757 | } 758 | controller.controlledReadableByteStream = stream; 759 | controller.pullAgain = false; 760 | controller.pulling = false; 761 | ReadableByteStreamControllerClearPendingPullIntos(controller); 762 | ResetQueue(controller); 763 | controller.closeRequested = false; 764 | controller.started = false; 765 | controller.strategyHWM = ValidateAndNormalizeHighWaterMark(highWaterMark); 766 | controller.pullAlgorithm = pullAlgorithm; 767 | controller.cancelAlgorithm = cancelAlgorithm; 768 | controller.autoAllocateChunkSize = autoAllocateChunkSize; 769 | controller.pendingPullIntos = []; 770 | stream.readableStreamController = controller; 771 | Promise.resolve(startAlgorithm()) 772 | .then(() => { 773 | controller.started = true; 774 | Assert(!controller.pulling); 775 | Assert(!controller.pullAgain); 776 | ReadableByteStreamControllerCallPullIfNeeded(controller); 777 | }) 778 | .catch(r => { 779 | ReadableByteStreamControllerError(controller, r); 780 | }); 781 | } 782 | 783 | export function SetUpReadableByteStreamControllerFromUnderlyingSource( 784 | stream: ReadableStream, 785 | underlyingByteSource: UnderlyingSource, 786 | highWaterMark: number 787 | ) { 788 | Assert(underlyingByteSource !== void 0); 789 | const controller = Object.create(ReadableByteStreamController.prototype); 790 | const startAlgorithm = () => 791 | InvokeOrNoop(underlyingByteSource, "start", controller); 792 | const pullAlgorithm = CreateAlgorithmFromUnderlyingMethod( 793 | underlyingByteSource, 794 | "pull", 795 | 0, 796 | controller 797 | ); 798 | const cancelAlgorithm = CreateAlgorithmFromUnderlyingMethod( 799 | underlyingByteSource, 800 | "cancel", 801 | 1 802 | ); 803 | const { autoAllocateChunkSize } = underlyingByteSource; 804 | if (autoAllocateChunkSize !== void 0) { 805 | if (!Number.isInteger(autoAllocateChunkSize) || autoAllocateChunkSize < 0) { 806 | throw new RangeError(); 807 | } 808 | } 809 | SetUpReadableByteStreamController( 810 | stream, 811 | controller, 812 | startAlgorithm, 813 | pullAlgorithm, 814 | cancelAlgorithm, 815 | highWaterMark, 816 | autoAllocateChunkSize 817 | ); 818 | } 819 | --------------------------------------------------------------------------------