diff options
Diffstat (limited to 'std/io/bufio.ts')
-rw-r--r-- | std/io/bufio.ts | 99 |
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"); +} |