std/io/read_delim.ts
Asher Gomez 385a11f4b4
deprecation(io/unstable): deprecate readDelim() (#6022)
* deprecation(io/unstable): deprecate `readDelim()`

* update
2024-09-23 20:30:11 +09:00

104 lines
2.9 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { concat } from "@std/bytes/concat";
import type { Reader } from "./types.ts";
/** 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 = lps[prefixEnd - 1]!;
}
}
return lps;
}
/**
* Read delimited bytes from a {@linkcode Reader} through an
* {@linkcode AsyncIterableIterator} of {@linkcode Uint8Array}.
*
* @example Usage
* ```ts
* import { readDelim } from "@std/io/read-delim";
* import { assert } from "@std/assert/assert"
*
* using fileReader = await Deno.open("README.md");
*
* for await (const chunk of readDelim(fileReader, new TextEncoder().encode("\n"))) {
* assert(chunk instanceof Uint8Array);
* }
* ```
*
* @param reader The reader to read from
* @param delim The delimiter to read until
* @returns The {@linkcode AsyncIterableIterator} of {@linkcode Uint8Array}s.
*
* @deprecated Use
* {@linkcode https://jsr.io/@std/streams/doc/byte-slice-stream/~/ByteSliceStream | ByteSliceStream}
* instead. This will be removed in 0.225.0.
*/
export async function* readDelim(
reader: Reader,
delim: Uint8Array,
): AsyncIterableIterator<Uint8Array> {
// Avoid unicode problems
const delimLen = delim.length;
const delimLPS = createLPS(delim);
let chunks = new Uint8Array();
const bufSize = Math.max(1024, delimLen + 1);
// Modified KMP
let inspectIndex = 0;
let matchIndex = 0;
while (true) {
const inspectArr = new Uint8Array(bufSize);
const result = await reader.read(inspectArr);
if (result === null) {
// Yield last chunk.
yield chunks;
return;
} else if (result < 0) {
// Discard all remaining and silently fail.
return;
}
chunks = concat([chunks, inspectArr.slice(0, result)]);
let localIndex = 0;
while (inspectIndex < chunks.length) {
if (inspectArr[localIndex] === delim[matchIndex]) {
inspectIndex++;
localIndex++;
matchIndex++;
if (matchIndex === delimLen) {
// Full match
const matchEnd = inspectIndex - delimLen;
const readyBytes = chunks.slice(0, matchEnd);
yield readyBytes;
// Reset match, different from KMP.
chunks = chunks.slice(inspectIndex);
inspectIndex = 0;
matchIndex = 0;
}
} else {
if (matchIndex === 0) {
inspectIndex++;
localIndex++;
} else {
matchIndex = delimLPS[matchIndex - 1]!;
}
}
}
}
}