nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
373 lines (325 loc) • 8.48 kB
JavaScript
// 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 };