summaryrefslogtreecommitdiff
path: root/std/io/bufio.ts
diff options
context:
space:
mode:
authorNayeem Rahman <muhammed.9939@gmail.com>2020-02-18 00:51:13 +0000
committerGitHub <noreply@github.com>2020-02-17 19:51:13 -0500
commit5a3292047c42b8a65d164f127fc57e57046fadf7 (patch)
treea7612e75eb56a25ace057cab9eacafcd266c48d8 /std/io/bufio.ts
parent7b9f6e9c456175fd8a6c11049a2f5f723e909d03 (diff)
feat(std/io): Export readDelim(), readStringDelim() and readLines() from bufio.ts (#4019)
Co-authored-by: Bartek IwaƄczuk <biwanczuk@gmail.com>
Diffstat (limited to 'std/io/bufio.ts')
-rw-r--r--std/io/bufio.ts99
1 files changed, 99 insertions, 0 deletions
diff --git a/std/io/bufio.ts b/std/io/bufio.ts
index f721022f3..c0a52ac6a 100644
--- a/std/io/bufio.ts
+++ b/std/io/bufio.ts
@@ -508,3 +508,102 @@ export class BufWriter implements Writer {
return nn;
}
}
+
+/** Generate longest proper prefix which is also suffix array. */
+function createLPS(pat: Uint8Array): Uint8Array {
+ const lps = new Uint8Array(pat.length);
+ lps[0] = 0;
+ let prefixEnd = 0;
+ let i = 1;
+ while (i < lps.length) {
+ if (pat[i] == pat[prefixEnd]) {
+ prefixEnd++;
+ lps[i] = prefixEnd;
+ i++;
+ } else if (prefixEnd === 0) {
+ lps[i] = 0;
+ i++;
+ } else {
+ prefixEnd = pat[prefixEnd - 1];
+ }
+ }
+ return lps;
+}
+
+/** Read delimited bytes from a Reader. */
+export async function* readDelim(
+ reader: Reader,
+ delim: Uint8Array
+): AsyncIterableIterator<Uint8Array> {
+ // Avoid unicode problems
+ const delimLen = delim.length;
+ const delimLPS = createLPS(delim);
+
+ let inputBuffer = new Deno.Buffer();
+ const inspectArr = new Uint8Array(Math.max(1024, delimLen + 1));
+
+ // Modified KMP
+ let inspectIndex = 0;
+ let matchIndex = 0;
+ while (true) {
+ const result = await reader.read(inspectArr);
+ if (result === Deno.EOF) {
+ // Yield last chunk.
+ yield inputBuffer.bytes();
+ return;
+ }
+ if ((result as number) < 0) {
+ // Discard all remaining and silently fail.
+ return;
+ }
+ const sliceRead = inspectArr.subarray(0, result as number);
+ await Deno.writeAll(inputBuffer, sliceRead);
+
+ let sliceToProcess = inputBuffer.bytes();
+ while (inspectIndex < sliceToProcess.length) {
+ if (sliceToProcess[inspectIndex] === delim[matchIndex]) {
+ inspectIndex++;
+ matchIndex++;
+ if (matchIndex === delimLen) {
+ // Full match
+ const matchEnd = inspectIndex - delimLen;
+ const readyBytes = sliceToProcess.subarray(0, matchEnd);
+ // Copy
+ const pendingBytes = sliceToProcess.slice(inspectIndex);
+ yield readyBytes;
+ // Reset match, different from KMP.
+ sliceToProcess = pendingBytes;
+ inspectIndex = 0;
+ matchIndex = 0;
+ }
+ } else {
+ if (matchIndex === 0) {
+ inspectIndex++;
+ } else {
+ matchIndex = delimLPS[matchIndex - 1];
+ }
+ }
+ }
+ // Keep inspectIndex and matchIndex.
+ inputBuffer = new Deno.Buffer(sliceToProcess);
+ }
+}
+
+/** Read delimited strings from a Reader. */
+export async function* readStringDelim(
+ reader: Reader,
+ delim: string
+): AsyncIterableIterator<string> {
+ const encoder = new TextEncoder();
+ const decoder = new TextDecoder();
+ for await (const chunk of readDelim(reader, encoder.encode(delim))) {
+ yield decoder.decode(chunk);
+ }
+}
+
+/** Read strings line-by-line from a Reader. */
+export async function* readLines(
+ reader: Reader
+): AsyncIterableIterator<string> {
+ yield* readStringDelim(reader, "\n");
+}