mirror of
https://github.com/djhackersdev/minime.git
synced 2026-07-19 08:41:44 -05:00
util: Add promise-based stream wrapper
You'd think something like this would exist on npm somewhere.
This commit is contained in:
parent
ef10c1c191
commit
01128979db
4
src/util/stream/index.ts
Normal file
4
src/util/stream/index.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import IoByteStream from "./stream";
|
||||
|
||||
export * from "./types";
|
||||
export default IoByteStream;
|
||||
78
src/util/stream/reader.ts
Normal file
78
src/util/stream/reader.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { Readable } from "stream";
|
||||
|
||||
export default function makeReader(stm: Readable) {
|
||||
let remain = Buffer.alloc(0);
|
||||
let busy = false;
|
||||
|
||||
return function read(nbytes: number): Promise<Buffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (busy) {
|
||||
return reject(new Error("Already reading"));
|
||||
}
|
||||
|
||||
//
|
||||
// Synchronous completion case
|
||||
//
|
||||
|
||||
const result = Buffer.alloc(nbytes);
|
||||
const step = Math.min(remain.length, nbytes);
|
||||
|
||||
remain.copy(result, 0, 0, step);
|
||||
remain = remain.slice(step);
|
||||
|
||||
if (step === nbytes) {
|
||||
return resolve(result);
|
||||
}
|
||||
|
||||
//
|
||||
// Asynchronous completion case
|
||||
//
|
||||
|
||||
let pos = step;
|
||||
let finish: () => void;
|
||||
|
||||
const onReadable = () => {
|
||||
while (true) {
|
||||
const chunk = stm.read() as (Buffer | null);
|
||||
|
||||
if (chunk === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
const step = Math.min(result.length - pos, chunk.length);
|
||||
|
||||
chunk.copy(result, pos, 0, step);
|
||||
remain = chunk.slice(step);
|
||||
pos += step;
|
||||
|
||||
if (pos === result.length) {
|
||||
finish();
|
||||
resolve(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onEnd = () => {
|
||||
finish();
|
||||
resolve(result.slice(0, pos));
|
||||
};
|
||||
|
||||
const onError = error => {
|
||||
finish();
|
||||
reject(error);
|
||||
};
|
||||
|
||||
finish = () => {
|
||||
stm.off("end", onEnd);
|
||||
stm.off("error", onError);
|
||||
stm.off("readable", onReadable);
|
||||
busy = false;
|
||||
};
|
||||
|
||||
busy = true;
|
||||
stm.on("end", onEnd);
|
||||
stm.on("error", onError);
|
||||
stm.on("readable", onReadable);
|
||||
});
|
||||
};
|
||||
}
|
||||
41
src/util/stream/stream.ts
Normal file
41
src/util/stream/stream.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { Duplex } from "stream";
|
||||
|
||||
import makeReader from "./reader";
|
||||
import { ByteStream } from "./types";
|
||||
import makeWriter from "./writer";
|
||||
|
||||
export default class IoByteStream implements ByteStream {
|
||||
private _stm?: Duplex;
|
||||
private readonly _read: (nbytes: number) => Promise<Buffer>;
|
||||
private readonly _write: (buf: Buffer) => Promise<void>;
|
||||
|
||||
constructor(stm: Duplex) {
|
||||
this._stm = stm;
|
||||
this._read = makeReader(stm);
|
||||
this._write = makeWriter(stm);
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this._stm !== undefined) {
|
||||
this._stm.destroy();
|
||||
}
|
||||
|
||||
this._stm = undefined;
|
||||
}
|
||||
|
||||
read(nbytes: number): Promise<Buffer> {
|
||||
if (this._stm === undefined) {
|
||||
throw new Error("Stream is closed");
|
||||
}
|
||||
|
||||
return this._read(nbytes);
|
||||
}
|
||||
|
||||
write(buf: Buffer): Promise<void> {
|
||||
if (this._stm === undefined) {
|
||||
throw new Error("Stream is closed");
|
||||
}
|
||||
|
||||
return this._write(buf);
|
||||
}
|
||||
}
|
||||
13
src/util/stream/types.ts
Normal file
13
src/util/stream/types.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export interface ReadableByteStream {
|
||||
close(): void;
|
||||
|
||||
read(nbytes: number): Promise<Buffer>;
|
||||
}
|
||||
|
||||
export interface WritableByteStream {
|
||||
close(): void;
|
||||
|
||||
write(buf: Buffer): Promise<void>;
|
||||
}
|
||||
|
||||
export type ByteStream = ReadableByteStream & WritableByteStream;
|
||||
25
src/util/stream/writer.ts
Normal file
25
src/util/stream/writer.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { Writable } from "stream";
|
||||
|
||||
export default function makeWriter(stm: Writable) {
|
||||
let busy = false;
|
||||
|
||||
return function write(buf: Buffer): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (busy) {
|
||||
return reject(new Error("Already writing"));
|
||||
}
|
||||
|
||||
busy = true;
|
||||
|
||||
stm.write(buf, error => {
|
||||
busy = false;
|
||||
|
||||
if (error !== undefined) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user