weigh-js
Version:
A lightweight TypeScript library for estimating memory usage of JavaScript objects.
315 lines • 12.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Profiler = exports.ArrayStrategy = exports.RegExpStrategy = exports.WeakRefStrategy = exports.FunctionSizeStrategy = exports.DataViewStrategy = exports.ArrayBufferStrategy = exports.TypedArraySizeStrategy = exports.URLSizeStrategy = void 0;
class DefaultSizeStrategy {
supports(_value) {
return true;
}
sizeOf(value, profiler) {
if (value === null)
return 4 /* eMemorySize.NULL */;
if (value === undefined)
return 4 /* eMemorySize.UNDEFINED */;
const type = typeof value;
switch (type) {
case "string": return value.length * 2 /* eMemorySize.STRING_CHAR */;
case "number": return 8 /* eMemorySize.NUMBER */;
case "boolean": return 4 /* eMemorySize.BOOLEAN */;
case "bigint": return 8 /* eMemorySize.BIGINT */;
case "function": return 96 /* eMemorySize.DEFAULT_FUNCTION */ + value.toString().length * 2 /* eMemorySize.STRING_CHAR */;
case "symbol": return value.description ? (value.description.length * 2 /* eMemorySize.STRING_CHAR */) : 0;
}
if (profiler.isVisited(value))
return 8 /* eMemorySize.REFERENCE */;
profiler.markVisited(value);
if (Array.isArray(value))
return value.reduce((sum, item) => sum + profiler.computeSize(item), 0);
if (value instanceof Map) {
let size = 64 /* eMemorySize.DEFAULT_MAP */;
for (const { 0: key, 1: val } of value)
size += profiler.computeSize(key) + profiler.computeSize(val);
return size;
}
if (value instanceof Set) {
let size = 64 /* eMemorySize.DEFAULT_SET */;
for (const item of value)
size += profiler.computeSize(item);
return size;
}
if (Buffer.isBuffer(value))
return value.length;
if (value instanceof Date)
return 32 /* eMemorySize.DATE */;
if (value instanceof RegExp)
return value.toString().length * 2 /* eMemorySize.STRING_CHAR */;
let size = 24 /* eMemorySize.DEFAULT_OBJECT */;
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
size += key.length * 2 /* eMemorySize.STRING_CHAR */;
size += profiler.computeSize(value[key]);
}
}
return size;
}
}
class URLSizeStrategy {
supports(value) {
return value?.toString?.() && typeof value.toString === "function" && /^https?:/.test(value.toString());
}
sizeOf(value, _profiler) {
return 24 /* eMemorySize.DEFAULT_OBJECT */ + value.toString().length * 2 /* eMemorySize.STRING_CHAR */;
}
}
exports.URLSizeStrategy = URLSizeStrategy;
class TypedArraySizeStrategy {
constructor() {
this.constructors = [
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array,
BigInt64Array,
BigUint64Array,
];
}
supports(value) {
return this.constructors.some((ctor) => value instanceof ctor);
}
sizeOf(value, _profiler) {
return value.byteLength;
}
}
exports.TypedArraySizeStrategy = TypedArraySizeStrategy;
class ArrayBufferStrategy {
supports(value) {
return value instanceof ArrayBuffer;
}
sizeOf(value, _profiler) {
return value.byteLength;
}
}
exports.ArrayBufferStrategy = ArrayBufferStrategy;
class DataViewStrategy {
supports(value) {
return value instanceof DataView;
}
sizeOf(value, _profiler) {
return value.byteLength;
}
}
exports.DataViewStrategy = DataViewStrategy;
class FunctionSizeStrategy {
supports(value) {
return typeof value === "function";
}
sizeOf(fn, profiler) {
const fnWithProps = fn;
let size = 96 /* eMemorySize.DEFAULT_FUNCTION */;
size += fn.toString().length * 2 /* eMemorySize.STRING_CHAR */;
for (const key in fnWithProps) {
if (Object.prototype.hasOwnProperty.call(fnWithProps, key)) {
size += key.length * 2 /* eMemorySize.STRING_CHAR */;
size += profiler.computeSize(fnWithProps[key]);
}
}
if (fn.prototype && typeof fn.prototype === "object")
size += profiler.computeSize(fn.prototype);
return size;
}
}
exports.FunctionSizeStrategy = FunctionSizeStrategy;
class WeakRefStrategy {
supports(value) {
return value instanceof WeakRef;
}
sizeOf(_value, _profiler) {
return 8 /* eMemorySize.REFERENCE */ + 16 /* eMemorySize.DEFAULT_WEAKREF */;
}
}
exports.WeakRefStrategy = WeakRefStrategy;
class RegExpStrategy {
supports(value) {
return value instanceof RegExp;
}
sizeOf(value, profiler) {
let size = 64 /* eMemorySize.DEFAULT_REGEXP */;
size += value.source.length * 2 /* eMemorySize.STRING_CHAR */;
size += value.flags.length * 2 /* eMemorySize.STRING_CHAR */;
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key) && key !== "source" && key !== "flags" && key !== "lastIndex") {
size += key.length * 2 /* eMemorySize.STRING_CHAR */;
size += profiler.computeSize(value[key]);
}
}
size += 8 /* eMemorySize.NUMBER */;
return size;
}
}
exports.RegExpStrategy = RegExpStrategy;
class ArrayStrategy {
supports(value) {
return Array.isArray(value);
}
sizeOf(value, profiler) {
let size = 32 /* eMemorySize.DEFAULT_ARRAY */;
for (const item of value)
size += profiler.computeSize(item);
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key) && key !== "length") {
size += key.length * 2 /* eMemorySize.STRING_CHAR */;
size += profiler.computeSize(value[key]);
}
}
return size;
}
}
exports.ArrayStrategy = ArrayStrategy;
class Profiler {
constructor(options) {
this.visited = new WeakSet();
const { strategies, } = options ?? {};
this.strategies = strategies ?? [
new ArrayStrategy(),
new RegExpStrategy(),
new WeakRefStrategy(),
new URLSizeStrategy(),
new TypedArraySizeStrategy(),
new ArrayBufferStrategy(),
new DataViewStrategy(),
new FunctionSizeStrategy(),
new DefaultSizeStrategy(),
];
}
sizeOf(value) {
this.visited = new WeakSet();
return this.computeSize(value);
}
computeSize(value) {
const strategy = this.strategies.find((s) => s.supports(value));
if (!strategy)
return 0;
return strategy.sizeOf(value, this);
}
isVisited(value) {
return typeof value === "object" && value !== null && this.visited.has(value);
}
markVisited(value) {
typeof value === "object" && value !== null && this.visited.add(value);
}
addStrategy(strategy) {
this.strategies.unshift(strategy);
}
computeDetailedSize(value) {
this.visited = new WeakSet();
return this._computeDetailed(value);
}
_computeDetailed(value) {
const type = Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
if (value === null || value === undefined)
return { type, size: 4 /* eMemorySize.UNDEFINED */ };
if (typeof value === "string")
return { type: "string", size: value.length * 2 /* eMemorySize.STRING_CHAR */ };
if (typeof value === "number")
return { type: "number", size: 8 /* eMemorySize.NUMBER */ };
if (typeof value === "boolean")
return { type: "boolean", size: 4 /* eMemorySize.BOOLEAN */ };
if (typeof value === "bigint")
return { type: "bigint", size: 8 /* eMemorySize.BIGINT */ };
if (typeof value === "symbol") {
return {
type: "symbol",
size: value.description ? value.description.length * 2 /* eMemorySize.STRING_CHAR */ : 0,
};
}
if (typeof value === "function") {
if (this.isVisited(value))
return { type: "function", size: 8 /* eMemorySize.REFERENCE */ };
this.markVisited(value);
let size = value.toString().length * 2 /* eMemorySize.STRING_CHAR */;
const children = {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
const child = this._computeDetailed(value[key]);
size += key.length * 2 /* eMemorySize.STRING_CHAR */ + child.size;
children[key] = child;
}
}
if (value.prototype && typeof value.prototype === "object") {
const proto = this._computeDetailed(value.prototype);
size += proto.size;
children["prototype"] = proto;
}
return { type: "function", size, children };
}
if (this.isVisited(value))
return { type, size: 8 /* eMemorySize.REFERENCE */ };
this.markVisited(value);
if (Array.isArray(value)) {
let size = 0;
const children = {};
value.forEach((item, index) => {
const detail = this._computeDetailed(item);
children[index] = detail;
size += detail.size;
});
return { type: "array", size, children };
}
if (value instanceof Map) {
let size = 0;
const children = {};
for (const [k, v] of value) {
const keyDetail = this._computeDetailed(k);
const valDetail = this._computeDetailed(v);
const keyStr = `key:${String(k)}`;
const valStr = `val:${String(k)}`;
children[keyStr] = keyDetail;
children[valStr] = valDetail;
size += keyDetail.size + valDetail.size;
}
return { type: "map", size, children };
}
if (value instanceof Set) {
let size = 0;
const children = {};
let i = 0;
for (const item of value) {
const detail = this._computeDetailed(item);
children[i++] = detail;
size += detail.size;
}
return { type: "set", size, children };
}
if (Buffer.isBuffer(value))
return { type: "buffer", size: value.length };
if (value instanceof Date)
return { type: "date", size: 32 /* eMemorySize.DATE */ };
if (value instanceof RegExp)
return { type: "regexp", size: value.toString().length * 2 /* eMemorySize.STRING_CHAR */ };
let size = 0;
const children = {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
const child = this._computeDetailed(value[key]);
size += key.length * 2 /* eMemorySize.STRING_CHAR */ + child.size;
children[key] = child;
}
}
return { type: "object", size, children };
}
static formatSize(bytes, unit = "B" /* eMemoryUnit.BYTE */) {
switch (unit) {
case "B" /* eMemoryUnit.BYTE */: return `${bytes} B`;
case "KB" /* eMemoryUnit.KILOBYTE */: return `${(bytes / 1024).toFixed(2)} KB`;
case "MB" /* eMemoryUnit.MEGABYTE */: return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
case "GB" /* eMemoryUnit.GIGABYTE */: return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
default: return `${bytes} B`;
}
}
}
exports.Profiler = Profiler;
//# sourceMappingURL=weigh.js.map