OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-booking / node_modules / nat-sampler / index.js

65 lines · javascript ref:
module.exports = class NatSampler {
  constructor () {
    this.host = null
    this.port = 0
    this.size = 0

    this._a = null
    this._b = null
    this._threshold = 0
    this._top = 0
    this._samples = []
  }

  add (host, port) {
    const a = this._bump(host, port, 2)
    const b = this._bump(host, 0, 1)

    if (this._samples.length < 32) {
      this.size++
      this._threshold = this.size - (this.size < 4 ? 0 : this.size < 8 ? 1 : this.size < 12 ? 2 : 3)
      this._samples.push(a, b)
      this._top += 2
    } else {
      if (this._top === 32) this._top = 0

      const oa = this._samples[this._top]
      this._samples[this._top++] = a
      oa.hits--

      const ob = this._samples[this._top]
      this._samples[this._top++] = b
      ob.hits--
    }

    if (this._a === null || this._a.hits < a.hits) this._a = a
    if (this._b === null || this._b.hits < b.hits) this._b = b

    if (this._a.hits >= this._threshold) {
      this.host = this._a.host
      this.port = this._a.port
    } else if (this._b.hits >= this._threshold) {
      this.host = this._b.host
      this.port = 0
    } else {
      this.host = null
      this.port = 0
    }

    return a.hits
  }

  _bump (host, port, inc) {
    for (let i = 0; i < 4; i++) {
      const j = (this._top - inc - (2 * i)) & 31
      if (j >= this._samples.length) return { host, port, hits: 1 }
      const s = this._samples[j]
      if (s.port === port && s.host === host) {
        s.hits++
        return s
      }
    }
    return { host, port, hits: 1 }
  }
}