summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2019-05-11 16:05:56 +0200
committerRyan Dahl <ry@tinyclouds.org>2019-05-11 10:05:56 -0400
commit2c6b93e0a0c7927dc4dd66c1f681a78a6d79b8f1 (patch)
treebd68b9304f17ba9bcbc3941e51d7cb930523fb99 /js
parentcb93246f6d02c34306e7c9e9c567f96b9bffc875 (diff)
fix: edge case in toAsyncIterator (#2335)
Diffstat (limited to 'js')
-rw-r--r--js/files_test.ts32
-rw-r--r--js/io.ts16
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"];
diff --git a/js/io.ts b/js/io.ts
index 0b59a0849..60b7e442c 100644
--- a/js/io.ts
+++ b/js/io.ts
@@ -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
};
}
};