@khoohaoyit/image-size
Version:
A stream based image size extractor in Node
131 lines (130 loc) • 6.19 kB
JavaScript
"use strict";
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _StreamReader_instances, _StreamReader_pruneImmediate, _StreamReader_handleRead;
Object.defineProperty(exports, "__esModule", { value: true });
exports.StreamReader = void 0;
const stream_1 = require("stream");
class StreamReader extends stream_1.Transform {
constructor(...args) {
super(...args);
_StreamReader_instances.add(this);
this.pendingRead = [];
this.bufferOffset = 0;
this.bufferSize = 0;
this.buffers = [];
_StreamReader_pruneImmediate.set(this, void 0);
}
peek(options, afterWrite) {
const [offset, size] = 'offset' in options
? [options.offset, options.size]
: [options.start, options.end - options.start];
if (this.bufferOffset > offset)
return Promise.reject(new Error('Unable to read after it has processed'));
return new Promise((rs, rj) => __classPrivateFieldGet(this, _StreamReader_instances, "m", _StreamReader_handleRead).call(this, {
offset, size,
resolveCallback: rs,
rejectCallback: rj,
blocking: false,
afterWrite,
}));
}
readRange(options) {
const [offset, size] = 'offset' in options
? [options.offset, options.size]
: [options.start, options.end - options.start];
if (this.bufferOffset > offset)
return Promise.reject(new Error('Unable to read after it has processed'));
return new Promise((rs, rj) => __classPrivateFieldGet(this, _StreamReader_instances, "m", _StreamReader_handleRead).call(this, {
offset, size,
resolveCallback: rs,
rejectCallback: rj,
blocking: true,
}));
}
_transform(chunk, encoding, callback) {
this.buffers.push(chunk);
this.bufferSize += chunk.length;
__classPrivateFieldGet(this, _StreamReader_instances, "m", _StreamReader_handleRead).call(this, undefined, true);
callback(null, chunk);
}
_final(callback) {
var _a;
const error = (_a = this.errored) !== null && _a !== void 0 ? _a : new Error('No more data to read');
for (const { rejectCallback: rjCb } of this.pendingRead.splice(0))
rjCb(error);
setImmediate(callback);
}
}
exports.StreamReader = StreamReader;
_StreamReader_pruneImmediate = new WeakMap(), _StreamReader_instances = new WeakSet(), _StreamReader_handleRead = function _StreamReader_handleRead(newEntry, isOnWrite) {
if (newEntry)
this.pendingRead.push(newEntry);
const doneRead = [];
for (const entry of newEntry ? [newEntry] : this.pendingRead) {
const { offset, size, blocking, afterWrite, resolveCallback: callback, } = entry;
if (afterWrite && !isOnWrite)
continue;
if (blocking) {
if (offset + size > this.bufferOffset + this.bufferSize)
continue; // range not fully buffered
}
else if (offset >= this.bufferOffset + this.bufferSize)
continue; // range not arrived yet
const currentBuffers = [];
let targetSize = size;
let skip = offset - this.bufferOffset;
for (const chunk of this.buffers) {
if (skip >= chunk.length) {
skip -= chunk.length;
continue;
}
const trimmedChunk = chunk.subarray(Math.max(skip, 0));
skip -= chunk.length;
if (targetSize >= trimmedChunk.length) {
targetSize -= trimmedChunk.length;
currentBuffers.push(trimmedChunk);
continue;
}
currentBuffers.push(trimmedChunk.subarray(0, targetSize));
break;
}
callback(Buffer.concat(currentBuffers));
doneRead.push(entry);
}
for (const entry of doneRead)
this.pendingRead.splice(this.pendingRead.indexOf(entry), 1);
if (!__classPrivateFieldGet(this, _StreamReader_pruneImmediate, "f"))
__classPrivateFieldSet(this, _StreamReader_pruneImmediate, setImmediate(() => {
let extraOffset = this.pendingRead.reduce((minimum, { offset }) => Math.min(minimum, offset), Infinity) - this.bufferOffset;
let removeCount = 0;
let append = null;
for (const chunk of this.buffers) {
++removeCount;
if (extraOffset >= chunk.length) {
extraOffset -= chunk.length;
this.bufferSize -= chunk.length;
this.bufferOffset += chunk.length;
continue;
}
const remain = chunk.subarray(extraOffset);
this.bufferSize -= extraOffset;
this.bufferOffset += extraOffset;
append = remain;
break;
}
this.buffers.splice(0, removeCount);
if (append)
this.buffers.unshift(append);
__classPrivateFieldSet(this, _StreamReader_pruneImmediate, undefined, "f");
}), "f");
};