UNPKG

weigh-js

Version:

A lightweight TypeScript library for estimating memory usage of JavaScript objects.

315 lines 12.2 kB
"use strict"; 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