UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

373 lines (325 loc) 8.48 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/histogram.js import { Histogram as _Histogram } from "nstdlib/stub/binding/performance"; import { customInspectSymbol as kInspect, kEmptyObject, } from "nstdlib/lib/internal/util"; import { inspect } from "nstdlib/lib/util"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { validateInteger, validateNumber, validateObject, } from "nstdlib/lib/internal/validators"; import { kClone, kDeserialize, markTransferMode, } from "nstdlib/lib/internal/worker/js_transferable"; const { ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_THIS, ERR_OUT_OF_RANGE, } = __codes__; const kDestroy = Symbol("kDestroy"); const kHandle = Symbol("kHandle"); const kMap = Symbol("kMap"); const kRecordable = Symbol("kRecordable"); function isHistogram(object) { return object?.[kHandle] !== undefined; } const kSkipThrow = Symbol("kSkipThrow"); class Histogram { constructor(skipThrowSymbol = undefined) { if (skipThrowSymbol !== kSkipThrow) { throw new ERR_ILLEGAL_CONSTRUCTOR(); } } [kInspect](depth, options) { if (depth < 0) return this; const opts = { ...options, depth: options.depth == null ? null : options.depth - 1, }; return `Histogram ${inspect( { min: this.min, max: this.max, mean: this.mean, exceeds: this.exceeds, stddev: this.stddev, count: this.count, percentiles: this.percentiles, }, opts, )}`; } /** * @readonly * @type {number} */ get count() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.count(); } /** * @readonly * @type {bigint} */ get countBigInt() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.countBigInt(); } /** * @readonly * @type {number} */ get min() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.min(); } /** * @readonly * @type {bigint} */ get minBigInt() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.minBigInt(); } /** * @readonly * @type {number} */ get max() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.max(); } /** * @readonly * @type {bigint} */ get maxBigInt() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.maxBigInt(); } /** * @readonly * @type {number} */ get mean() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.mean(); } /** * @readonly * @type {number} */ get exceeds() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.exceeds(); } /** * @readonly * @type {bigint} */ get exceedsBigInt() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.exceedsBigInt(); } /** * @readonly * @type {number} */ get stddev() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); return this[kHandle]?.stddev(); } /** * @param {number} percentile * @returns {number} */ percentile(percentile) { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); validateNumber(percentile, "percentile"); if (Number.isNaN(percentile) || percentile <= 0 || percentile > 100) throw new ERR_OUT_OF_RANGE("percentile", "> 0 && <= 100", percentile); return this[kHandle]?.percentile(percentile); } /** * @param {number} percentile * @returns {bigint} */ percentileBigInt(percentile) { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); validateNumber(percentile, "percentile"); if (Number.isNaN(percentile) || percentile <= 0 || percentile > 100) throw new ERR_OUT_OF_RANGE("percentile", "> 0 && <= 100", percentile); return this[kHandle]?.percentileBigInt(percentile); } /** * @readonly * @type {Map<number,number>} */ get percentiles() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); this[kMap].clear(); this[kHandle]?.percentiles(this[kMap]); return this[kMap]; } /** * @readonly * @type {Map<number,bigint>} */ get percentilesBigInt() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); this[kMap].clear(); this[kHandle]?.percentilesBigInt(this[kMap]); return this[kMap]; } /** * @returns {void} */ reset() { if (!isHistogram(this)) throw new ERR_INVALID_THIS("Histogram"); this[kHandle]?.reset(); } [kClone]() { const handle = this[kHandle]; return { data: { handle }, deserializeInfo: "internal/histogram:ClonedHistogram", }; } [kDeserialize]({ handle }) { this[kHandle] = handle; } toJSON() { return { count: this.count, min: this.min, max: this.max, mean: this.mean, exceeds: this.exceeds, stddev: this.stddev, percentiles: Object.fromEntries( Map.prototype.entries.call(this.percentiles), ), }; } } class RecordableHistogram extends Histogram { constructor(skipThrowSymbol = undefined) { if (skipThrowSymbol !== kSkipThrow) { throw new ERR_ILLEGAL_CONSTRUCTOR(); } super(skipThrowSymbol); } /** * @param {number|bigint} val * @returns {void} */ record(val) { if (this[kRecordable] === undefined) throw new ERR_INVALID_THIS("RecordableHistogram"); if (typeof val === "bigint") { this[kHandle]?.record(val); return; } validateInteger(val, "val", 1); this[kHandle]?.record(val); } /** * @returns {void} */ recordDelta() { if (this[kRecordable] === undefined) throw new ERR_INVALID_THIS("RecordableHistogram"); this[kHandle]?.recordDelta(); } /** * @param {RecordableHistogram} other */ add(other) { if (this[kRecordable] === undefined) throw new ERR_INVALID_THIS("RecordableHistogram"); if (other[kRecordable] === undefined) throw new ERR_INVALID_ARG_TYPE("other", "RecordableHistogram", other); this[kHandle]?.add(other[kHandle]); } [kClone]() { const handle = this[kHandle]; return { data: { handle }, deserializeInfo: "internal/histogram:ClonedRecordableHistogram", }; } [kDeserialize]({ handle }) { this[kHandle] = handle; } } function ClonedHistogram(handle) { return Reflect.construct( function () { markTransferMode(this, true, false); this[kHandle] = handle; this[kMap] = new Map(); }, [], Histogram, ); } ClonedHistogram.prototype[kDeserialize] = () => {}; function ClonedRecordableHistogram(handle) { const histogram = new RecordableHistogram(kSkipThrow); markTransferMode(histogram, true, false); histogram[kRecordable] = true; histogram[kMap] = new Map(); histogram[kHandle] = handle; histogram.constructor = RecordableHistogram; return histogram; } ClonedRecordableHistogram.prototype[kDeserialize] = () => {}; function createRecordableHistogram(handle) { return new ClonedRecordableHistogram(handle); } /** * @param {{ * lowest? : number, * highest? : number, * figures? : number * }} [options] * @returns {RecordableHistogram} */ function createHistogram(options = kEmptyObject) { validateObject(options, "options"); const { lowest = 1, highest = Number.MAX_SAFE_INTEGER, figures = 3, } = options; if (typeof lowest !== "bigint") validateInteger(lowest, "options.lowest", 1, Number.MAX_SAFE_INTEGER); if (typeof highest !== "bigint") { validateInteger( highest, "options.highest", 2 * lowest, Number.MAX_SAFE_INTEGER, ); } else if (highest < 2n * lowest) { throw new ERR_INVALID_ARG_VALUE.RangeError("options.highest", highest); } validateInteger(figures, "options.figures", 1, 5); return createRecordableHistogram(new _Histogram(lowest, highest, figures)); } export { Histogram }; export { RecordableHistogram }; export { ClonedHistogram }; export { ClonedRecordableHistogram }; export { isHistogram }; export { kDestroy }; export { kHandle }; export { kMap }; export { createHistogram };