repo_description: Node.js JavaScript runtime ✨🐢🚀✨
repo_topics: javascript js linux macos mit node nodejs runtime windows
code: 'use strict';
  
  const {
    Map,
    MapPrototypeClear,
    MapPrototypeEntries,
    NumberIsNaN,
    NumberMAX_SAFE_INTEGER,
    ObjectFromEntries,
    ReflectConstruct,
    Symbol,
  } = primordials;
  
  const {
    Histogram: _Histogram,
  } = internalBinding('performance');
  
  const {
    customInspectSymbol: kInspect,
    kEmptyObject,
  } = require('internal/util');
  
  const { inspect } = require('util');
  
  const {
    codes: {
      ERR_ILLEGAL_CONSTRUCTOR,
      ERR_INVALID_ARG_TYPE,
      ERR_INVALID_ARG_VALUE,
      ERR_INVALID_THIS,
      ERR_OUT_OF_RANGE,
    },
  } = require('internal/errors');
  
  const {
    validateInteger,
    validateNumber,
    validateObject,
  } = require('internal/validators');
  
  const kDestroy = Symbol('kDestroy');
  const kHandle = Symbol('kHandle');
  const kMap = Symbol('kMap');
  const kRecordable = Symbol('kRecordable');
  
  const {
    kClone,
    kDeserialize,
    markTransferMode,
  } = require('internal/worker/js_transferable');
  
  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 (NumberIsNaN(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 (NumberIsNaN(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');
      MapPrototypeClear(this[kMap]);
      this[kHandle]?.percentiles(this[kMap]);
      return this[kMap];
    }
  
    /**
     * @readonly
     * @type {Map<number,bigint>}
     */
    get percentilesBigInt() {
      if (!isHistogram(this))
        throw new ERR_INVALID_THIS('Histogram');
      MapPrototypeClear(this[kMap]);
      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: ObjectFromEntries(MapPrototypeEntries(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 ReflectConstruct(
      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 = NumberMAX_SAFE_INTEGER,
      figures = 3,
    } = options;
    if (typeof lowest !== 'bigint')
      validateInteger(lowest, 'options.lowest', 1, NumberMAX_SAFE_INTEGER);
    if (typeof highest !== 'bigint') {
      validateInteger(highest, 'options.highest',
                      2 * lowest, NumberMAX_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));
  }
  
  module.exports = {
    Histogram,
    RecordableHistogram,
    ClonedHistogram,
    ClonedRecordableHistogram,
    isHistogram,
    kDestroy,
    kHandle,
    kMap,
    createHistogram,
  };
  