Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(io/bufio): fix handling of trailing new line #990

Merged
merged 2 commits into from
Jul 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions io/bufio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
type Reader = Deno.Reader;
type Writer = Deno.Writer;
type WriterSync = Deno.WriterSync;
import { copy } from "../bytes/mod.ts";
import { concat, copy } from "../bytes/mod.ts";
import { assert } from "../_util/assert.ts";
import { BytesList } from "../bytes/bytes_list.ts";
import { writeAll, writeAllSync } from "./util.ts";
Expand Down Expand Up @@ -252,6 +252,9 @@ export class BufReader implements Reader {
try {
line = await this.readSlice(LF);
} catch (err) {
if (err instanceof Deno.errors.BadResource) {
throw err;
}
let { partial } = err;
assert(
partial instanceof Uint8Array,
Expand Down Expand Up @@ -707,13 +710,21 @@ export async function* readLines(
ignoreBOM?: boolean;
},
): AsyncIterableIterator<string> {
for await (let chunk of readStringDelim(reader, "\n", decoderOpts)) {
// Finding a CR at the end of the line is evidence of a
// "\r\n" at the end of the line. The "\r" part should be
// removed too.
if (chunk.endsWith("\r")) {
chunk = chunk.slice(0, -1);
const bufReader = new BufReader(reader);
let chunks: Uint8Array[] = [];
const decoder = new TextDecoder(decoderOpts?.encoding, decoderOpts);
while (true) {
const res = await bufReader.readLine();
if (!res) {
if (chunks.length > 0) {
yield decoder.decode(concat(...chunks));
}
break;
}
chunks.push(res.line);
if (!res.more) {
yield decoder.decode(concat(...chunks));
chunks = [];
}
yield chunk;
}
}
28 changes: 25 additions & 3 deletions io/bufio_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import { assert, assertEquals, fail } from "../testing/asserts.ts";
import {
assert,
assertEquals,
assertThrowsAsync,
fail,
} from "../testing/asserts.ts";
import {
BufferFullError,
BufReader,
Expand Down Expand Up @@ -242,6 +247,15 @@ Deno.test("bufioReadLine", async function () {
await testReadLine(testInputrn);
});

Deno.test("bufioReadLineBadResource", async () => {
const file = await Deno.open("README.md");
const bufReader = new BufReader(file);
file.close();
assertThrowsAsync(async () => {
await bufReader.readLine();
}, Deno.errors.BadResource);
});

Deno.test("[io] readStringDelim basic", async () => {
const delim = "!#$%&()=~";
const exp = [
Expand Down Expand Up @@ -480,6 +494,7 @@ Deno.test("readStringDelimAndLines", async function () {
assertEquals(chunks_, ["Hello World", "Hello World 2", "Hello World 3"]);

const linesData = new Buffer(enc.encode("0\n1\n2\n3\n4\n5\n6\n7\n8\n9"));
const linesDataWithTrailingNewLine = new Buffer(enc.encode("1\n2\n3\n"));
// consider data with windows newlines too
const linesDataWindows = new Buffer(
enc.encode("0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9"),
Expand All @@ -493,6 +508,14 @@ Deno.test("readStringDelimAndLines", async function () {
assertEquals(lines_.length, 10);
assertEquals(lines_, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]);

lines_.length = 0;
for await (const l of readLines(linesDataWithTrailingNewLine)) {
lines_.push(l);
}

assertEquals(lines_.length, 3);
assertEquals(lines_, ["1", "2", "3"]); // No empty line at the end

// Now test for "windows" lines
lines_.length = 0;
for await (const l of readLines(linesDataWindows)) {
Expand All @@ -512,7 +535,7 @@ Deno.test("readLinesWithEncodingISO-8859-15", async function () {

Deno.close(file_.rid);

assertEquals(lines_.length, 13);
assertEquals(lines_.length, 12);
assertEquals(lines_, [
"\u0020!\"#$%&'()*+,-./",
"0123456789:;<=>?",
Expand All @@ -526,7 +549,6 @@ Deno.test("readLinesWithEncodingISO-8859-15", async function () {
"ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß",
"àáâãäåæçèéêëìíîï",
"ðñòóôõö÷øùúûüýþÿ",
"",
]);
});

Expand Down