diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2019-05-11 16:05:56 +0200 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2019-05-11 10:05:56 -0400 |
commit | 2c6b93e0a0c7927dc4dd66c1f681a78a6d79b8f1 (patch) | |
tree | bd68b9304f17ba9bcbc3941e51d7cb930523fb99 /js | |
parent | cb93246f6d02c34306e7c9e9c567f96b9bffc875 (diff) |
fix: edge case in toAsyncIterator (#2335)
Diffstat (limited to 'js')
-rw-r--r-- | js/files_test.ts | 32 | ||||
-rw-r--r-- | js/io.ts | 16 |
2 files changed, 47 insertions, 1 deletions
diff --git a/js/files_test.ts b/js/files_test.ts index 2b91b9cab..620d95ecd 100644 --- a/js/files_test.ts +++ b/js/files_test.ts @@ -29,6 +29,38 @@ testPerm({ read: true }, async function filesToAsyncIterator(): Promise<void> { assertEquals(totalSize, 12); }); +test(async function readerToAsyncIterator(): Promise<void> { + // ref: https://github.com/denoland/deno/issues/2330 + const encoder = new TextEncoder(); + + class TestReader implements Deno.Reader { + private offset = 0; + private buf = new Uint8Array(encoder.encode(this.s)); + + constructor(private readonly s: string) {} + + async read(p: Uint8Array): Promise<Deno.ReadResult> { + const n = Math.min(p.byteLength, this.buf.byteLength - this.offset); + p.set(this.buf.slice(this.offset, this.offset + n)); + this.offset += n; + + return { + nread: n, + eof: this.offset === this.buf.byteLength + }; + } + } + + const reader = new TestReader("hello world!"); + + let totalSize = 0; + for await (const buf of Deno.toAsyncIterator(reader)) { + totalSize += buf.byteLength; + } + + assertEquals(totalSize, 12); +}); + testPerm({ write: false }, async function writePermFailure(): Promise<void> { const filename = "tests/hello.txt"; const writeModes: Deno.OpenMode[] = ["w", "a", "x"]; @@ -144,6 +144,14 @@ export async function copy(dst: Writer, src: Reader): Promise<number> { */ export function toAsyncIterator(r: Reader): AsyncIterableIterator<Uint8Array> { const b = new Uint8Array(1024); + // Keep track if end-of-file has been reached, then + // signal that iterator is done during subsequent next() + // call. This is required because `r` can return a `ReadResult` + // with data read and EOF reached. But if iterator returns + // `done` then `value` is discarded. + // + // See https://github.com/denoland/deno/issues/2330 for reference. + let sawEof = false; return { [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array> { @@ -151,10 +159,16 @@ export function toAsyncIterator(r: Reader): AsyncIterableIterator<Uint8Array> { }, async next(): Promise<IteratorResult<Uint8Array>> { + if (sawEof) { + return { value: new Uint8Array(), done: true }; + } + const result = await r.read(b); + sawEof = result.eof; + return { value: b.subarray(0, result.nread), - done: result.eof + done: false }; } }; |