nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
260 lines (219 loc) • 7.19 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/stream_base_commons.js
import { Buffer } from "nstdlib/lib/buffer";
import { FastBuffer } from "nstdlib/lib/internal/buffer";
import {
WriteWrap,
kReadBytesOrError,
kArrayBufferOffset,
kBytesWritten,
kLastWriteWasAsync,
streamBaseState,
} from "nstdlib/stub/binding/stream_wrap";
import { UV_EOF } from "nstdlib/stub/binding/uv";
import { ErrnoException } from "nstdlib/lib/internal/errors";
import { symbols as __symbols__ } from "nstdlib/lib/internal/async_hooks";
import {
kTimeout,
setUnrefTimeout,
getTimerDuration,
} from "nstdlib/lib/internal/timers";
import { isUint8Array } from "nstdlib/lib/internal/util/types";
import { clearTimeout } from "nstdlib/lib/timers";
import { validateFunction } from "nstdlib/lib/internal/validators";
const { owner_symbol } = __symbols__;
const kMaybeDestroy = Symbol("kMaybeDestroy");
const kUpdateTimer = Symbol("kUpdateTimer");
const kAfterAsyncWrite = Symbol("kAfterAsyncWrite");
const kHandle = Symbol("kHandle");
const kBoundSession = Symbol("kBoundSession");
const kSession = Symbol("kSession");
const kBuffer = Symbol("kBuffer");
const kBufferGen = Symbol("kBufferGen");
const kBufferCb = Symbol("kBufferCb");
function handleWriteReq(req, data, encoding) {
const { handle } = req;
switch (encoding) {
case "buffer": {
const ret = handle.writeBuffer(req, data);
if (streamBaseState[kLastWriteWasAsync]) req.buffer = data;
return ret;
}
case "latin1":
case "binary":
return handle.writeLatin1String(req, data);
case "utf8":
case "utf-8":
return handle.writeUtf8String(req, data);
case "ascii":
return handle.writeAsciiString(req, data);
case "ucs2":
case "ucs-2":
case "utf16le":
case "utf-16le":
return handle.writeUcs2String(req, data);
default: {
const buffer = Buffer.from(data, encoding);
const ret = handle.writeBuffer(req, buffer);
if (streamBaseState[kLastWriteWasAsync]) req.buffer = buffer;
return ret;
}
}
}
function onWriteComplete(status) {
{
/* debug */
}
const stream = this.handle[owner_symbol];
if (stream.destroyed) {
if (typeof this.callback === "function") this.callback(null);
return;
}
// TODO (ronag): This should be moved before if(stream.destroyed)
// in order to avoid swallowing error.
if (status < 0) {
const ex = new ErrnoException(status, "write", this.error);
if (typeof this.callback === "function") this.callback(ex);
else stream.destroy(ex);
return;
}
stream[kUpdateTimer]();
stream[kAfterAsyncWrite](this);
if (typeof this.callback === "function") this.callback(null);
}
function createWriteWrap(handle, callback) {
const req = new WriteWrap();
req.handle = handle;
req.oncomplete = onWriteComplete;
req.async = false;
req.bytes = 0;
req.buffer = null;
req.callback = callback;
return req;
}
function writevGeneric(self, data, cb) {
const req = createWriteWrap(self[kHandle], cb);
const allBuffers = data.allBuffers;
let chunks;
if (allBuffers) {
chunks = data;
for (let i = 0; i < data.length; i++) data[i] = data[i].chunk;
} else {
chunks = new Array(data.length << 1);
for (let i = 0; i < data.length; i++) {
const entry = data[i];
chunks[i * 2] = entry.chunk;
chunks[i * 2 + 1] = entry.encoding;
}
}
const err = req.handle.writev(req, chunks, allBuffers);
// Retain chunks
if (err === 0) req._chunks = chunks;
afterWriteDispatched(req, err, cb);
return req;
}
function writeGeneric(self, data, encoding, cb) {
const req = createWriteWrap(self[kHandle], cb);
const err = handleWriteReq(req, data, encoding);
afterWriteDispatched(req, err, cb);
return req;
}
function afterWriteDispatched(req, err, cb) {
req.bytes = streamBaseState[kBytesWritten];
req.async = !!streamBaseState[kLastWriteWasAsync];
if (err !== 0) return cb(new ErrnoException(err, "write", req.error));
if (!req.async && typeof req.callback === "function") {
req.callback();
}
}
function onStreamRead(arrayBuffer) {
const nread = streamBaseState[kReadBytesOrError];
const handle = this;
const stream = this[owner_symbol];
stream[kUpdateTimer]();
if (nread > 0 && !stream.destroyed) {
let ret;
let result;
const userBuf = stream[kBuffer];
if (userBuf) {
result = stream[kBufferCb](nread, userBuf) !== false;
const bufGen = stream[kBufferGen];
if (bufGen !== null) {
const nextBuf = bufGen();
if (isUint8Array(nextBuf)) stream[kBuffer] = ret = nextBuf;
}
} else {
const offset = streamBaseState[kArrayBufferOffset];
const buf = new FastBuffer(arrayBuffer, offset, nread);
result = stream.push(buf);
}
if (!result) {
handle.reading = false;
if (!stream.destroyed) {
const err = handle.readStop();
if (err) stream.destroy(new ErrnoException(err, "read"));
}
}
return ret;
}
if (nread === 0) {
return;
}
// After seeing EOF, most streams will be closed permanently,
// and will not deliver any more read events after this point.
// (equivalently, it should have called readStop on itself already).
// Some streams may be reset and explicitly started again with a call
// to readStart, such as TTY.
if (nread !== UV_EOF) {
// CallJSOnreadMethod expects the return value to be a buffer.
// Ref: https://github.com/nodejs/node/pull/34375
stream.destroy(new ErrnoException(nread, "read"));
return;
}
// Defer this until we actually emit end
if (stream._readableState.endEmitted) {
if (stream[kMaybeDestroy]) stream[kMaybeDestroy]();
} else {
if (stream[kMaybeDestroy]) stream.on("end", stream[kMaybeDestroy]);
// Push a null to signal the end of data.
// Do it before `maybeDestroy` for correct order of events:
// `end` -> `close`
stream.push(null);
stream.read(0);
}
}
function setStreamTimeout(msecs, callback) {
if (this.destroyed) return this;
this.timeout = msecs;
// Type checking identical to timers.enroll()
msecs = getTimerDuration(msecs, "msecs");
// Attempt to clear an existing timer in both cases -
// even if it will be rescheduled we don't want to leak an existing timer.
clearTimeout(this[kTimeout]);
if (msecs === 0) {
if (callback !== undefined) {
validateFunction(callback, "callback");
this.removeListener("timeout", callback);
}
} else {
this[kTimeout] = setUnrefTimeout(this._onTimeout.bind(this), msecs);
if (this[kSession]) this[kSession][kUpdateTimer]();
if (this[kBoundSession]) this[kBoundSession][kUpdateTimer]();
if (callback !== undefined) {
validateFunction(callback, "callback");
this.once("timeout", callback);
}
}
return this;
}
export { writevGeneric };
export { writeGeneric };
export { onStreamRead };
export { kAfterAsyncWrite };
export { kMaybeDestroy };
export { kUpdateTimer };
export { kHandle };
export { kSession };
export { setStreamTimeout };
export { kBuffer };
export { kBufferCb };
export { kBufferGen };