"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"];

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;
exports.refreshCacheOptions = refreshCacheOptions;

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _mobx = require("mobx");

var _configure = require("../configure");

var _Yallist = _interopRequireDefault(require("./Yallist"));

// 双向链表 -> 缓存更新性能优化
var MAX = Symbol('max'); // 最大值

var LENGTH = Symbol('length'); // 长度

var LENGTH_CALCULATOR = Symbol('lengthCalculator'); // 长度计算

var ALLOW_STALE = Symbol('allowStale'); // 允许过时

var MAX_AGE = Symbol('maxAge'); // 最大时效（缓存时长）

var DISPOSE = Symbol('dispose'); // 处置 处理

var NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet'); // 不可处理

var LIST = Symbol('list'); // 列表

var CACHE = Symbol('cache'); // 缓存

var UPDATE_AGE_ON_GET = Symbol('updateAgeOnGet'); // 获取更新时效

var naiveLength = function naiveLength() {
  return 1;
};
/**
 * 是否过时
 * @param self
 * @param hit
 */


function isStale(self, hit) {
  if (!hit || !hit.maxAge && !self[MAX_AGE]) return false;
  var diff = Date.now() - hit.now;
  return hit.maxAge ? diff > hit.maxAge : self[MAX_AGE] && diff > self[MAX_AGE];
}
/**
 * 删除
 * @param self
 * @param node
 */


function _del(self, node) {
  if (node) {
    var hit = node.value;
    if (self[DISPOSE]) self[DISPOSE](hit.key, hit.value);
    self[LENGTH] -= hit.length;
    self[CACHE]["delete"](hit.key);
    self[LIST].removeNode(node);
  }
}
/**
 * 获取
 * @param self
 * @param key
 * @param doUse
 */


function _get(self, key, doUse) {
  var node = self[CACHE].get(key);

  if (node) {
    var hit = node.value;

    if (isStale(self, hit)) {
      _del(self, node);

      if (!self[ALLOW_STALE]) return undefined;
    } else if (doUse) {
      if (self[UPDATE_AGE_ON_GET]) node.value.now = Date.now();
      self[LIST].unshiftNode(node);
    }

    return hit.value;
  }
}
/**
 * 修整
 * @param self
 */


function trim(self) {
  if (self[LENGTH] > self[MAX]) {
    for (var walker = self[LIST].tail; self[LENGTH] > self[MAX] && walker !== null;) {
      // We know that we're about to delete this one, and also
      // what the next least recently used key will be, so just
      // go ahead and set it now.
      var prev = walker.prev;

      _del(self, walker);

      walker = prev;
    }
  }
}
/**
 * 遍历步骤
 * @param self
 * @param fn
 * @param node
 * @param thisp
 */


function forEachStep(self, fn, node, thisp) {
  var hit = node.value;

  if (isStale(self, hit)) {
    _del(self, node);

    if (!self[ALLOW_STALE]) hit = undefined;
  }

  if (hit) fn.call(thisp, hit.value, hit.key, self);
}
/**
 * 构造 Entry 类
 */


var Entry = /*#__PURE__*/(0, _createClass2["default"])(function Entry(key, value, length, now) {
  var maxAge = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
  (0, _classCallCheck2["default"])(this, Entry);
  this.key = key;
  this.value = value;
  this.length = length;
  this.now = now;
  this.maxAge = maxAge;
});
/**
 * 导出 Cache 类
 */

var Cache = /*#__PURE__*/function () {
  function Cache(options) {
    (0, _classCallCheck2["default"])(this, Cache);
    if (typeof options === 'number') options = {
      max: options
    };
    if (!options) options = {};
    if (options.max && (typeof options.max !== 'number' || options.max < 0)) throw new TypeError('max must be a non-negative number');
    this[MAX] = options.max || 0;
    var lc = options.length || naiveLength;
    this[LENGTH_CALCULATOR] = typeof lc !== 'function' ? naiveLength : lc;
    this[ALLOW_STALE] = options.stale || false;
    if (options.maxAge && typeof options.maxAge !== 'number') throw new TypeError('maxAge must be a number');
    this[MAX_AGE] = options.maxAge || 0;
    this[DISPOSE] = options.dispose;
    this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false;
    this[UPDATE_AGE_ON_GET] = options.updateAgeOnGet || false;
    this.reset();
  } // resize the cache when the max changes.


  (0, _createClass2["default"])(Cache, [{
    key: "max",
    get: function get() {
      return this[MAX];
    },
    set: function set(mL) {
      if (typeof mL !== 'number' || mL < 0) throw new TypeError('max must be a non-negative number');
      this[MAX] = mL || Infinity;
      trim(this);
    }
  }, {
    key: "allowStale",
    get: function get() {
      return this[ALLOW_STALE];
    },
    set: function set(allowStale) {
      this[ALLOW_STALE] = !!allowStale;
    }
  }, {
    key: "maxAge",
    get: function get() {
      return this[MAX_AGE];
    } // resize the cache when the lengthCalculator changes.
    ,
    set: function set(mA) {
      if (typeof mA !== 'number') throw new TypeError('maxAge must be a non-negative number');
      this[MAX_AGE] = mA;
      trim(this);
    }
  }, {
    key: "lengthCalculator",
    get: function get() {
      return this[LENGTH_CALCULATOR];
    },
    set: function set(lC) {
      var _this = this;

      if (typeof lC !== 'function') lC = naiveLength;

      if (lC !== this[LENGTH_CALCULATOR]) {
        this[LENGTH_CALCULATOR] = lC;
        this[LENGTH] = 0;
        this[LIST].forEach(function (hit) {
          hit.length = _this[LENGTH_CALCULATOR](hit.value, hit.key);
          _this[LENGTH] += hit.length;
        });
      }

      trim(this);
    }
  }, {
    key: "length",
    get: function get() {
      return this[LENGTH];
    }
  }, {
    key: "itemCount",
    get: function get() {
      return this[LIST].length;
    }
  }, {
    key: "rforEach",
    value: function rforEach(fn, thisp) {
      thisp = thisp || this;

      for (var walker = this[LIST].tail; walker !== null;) {
        var prev = walker.prev;
        forEachStep(this, fn, walker, thisp);
        walker = prev;
      }
    }
  }, {
    key: "forEach",
    value: function forEach(fn, thisp) {
      thisp = thisp || this;

      for (var walker = this[LIST].head; walker !== null;) {
        var next = walker.next;
        forEachStep(this, fn, walker, thisp);
        walker = next;
      }
    }
  }, {
    key: "keys",
    value: function keys() {
      return this[LIST].toArray().map(function (k) {
        return k.key;
      });
    }
  }, {
    key: "values",
    value: function values() {
      return this[LIST].toArray().map(function (k) {
        return k.value;
      });
    }
  }, {
    key: "reset",
    value: function reset() {
      var _this2 = this;

      if (this[DISPOSE] && this[LIST] && this[LIST].length) {
        this[LIST].forEach(function (hit) {
          return _this2[DISPOSE](hit.key, hit.value);
        });
      }

      this[CACHE] = new Map(); // hash of items by key

      this[LIST] = new _Yallist["default"](); // list of items in order of use recency

      this[LENGTH] = 0; // length of items in the list
    }
  }, {
    key: "dump",
    value: function dump() {
      var _this3 = this;

      return this[LIST].map(function (hit) {
        return isStale(_this3, hit) ? false : {
          k: hit.key,
          v: hit.value,
          e: hit.now + (hit.maxAge || 0)
        };
      }).toArray().filter(function (h) {
        return h;
      });
    }
  }, {
    key: "dumpLru",
    value: function dumpLru() {
      return this[LIST];
    }
  }, {
    key: "set",
    value: function set(key, value, maxAge) {
      maxAge = maxAge || this[MAX_AGE];
      if (maxAge && typeof maxAge !== 'number') throw new TypeError('maxAge must be a number');
      var now = maxAge ? Date.now() : 0;
      var len = this[LENGTH_CALCULATOR](value, key);

      if (this[CACHE].has(key)) {
        if (len > this[MAX]) {
          _del(this, this[CACHE].get(key));

          return false;
        }

        var node = this[CACHE].get(key);
        var item = node.value;

        if (this[DISPOSE]) {
          if (!this[NO_DISPOSE_ON_SET]) this[DISPOSE](key, item.value);
        }

        item.now = now;
        item.maxAge = maxAge;
        item.value = value;
        this[LENGTH] += len - item.length;
        item.length = len;
        this.get(key);
        trim(this);
        return true;
      }

      var hit = new Entry(key, value, len, now, maxAge);

      if (hit.length > this[MAX]) {
        if (this[DISPOSE]) this[DISPOSE](key, value);
        return false;
      }

      this[LENGTH] += hit.length;
      this[LIST].unshift(hit);
      this[CACHE].set(key, this[LIST].head);
      trim(this);
      return true;
    }
  }, {
    key: "has",
    value: function has(key) {
      if (!this[CACHE].has(key)) return false;
      var hit = this[CACHE].get(key).value;
      return !isStale(this, hit);
    }
  }, {
    key: "get",
    value: function get(key) {
      return _get(this, key, true);
    }
  }, {
    key: "peek",
    value: function peek(key) {
      return _get(this, key, false);
    }
  }, {
    key: "pop",
    value: function pop() {
      var node = this[LIST].tail;
      if (!node) return null;

      _del(this, node);

      return node.value;
    }
  }, {
    key: "del",
    value: function del(key) {
      _del(this, this[CACHE].get(key));
    }
  }, {
    key: "load",
    value: function load(arr) {
      this.reset();
      var now = Date.now(); // A previous serialized cache has the most recent items first

      for (var l = arr.length - 1; l >= 0; l--) {
        var hit = arr[l];
        var expiresAt = hit.e || 0;
        if (expiresAt === 0) // the item was created without expiration in a non aged cache
          this.set(hit.k, hit.v);else {
          var maxAge = expiresAt - now; // dont add already expired items

          if (maxAge > 0) {
            this.set(hit.k, hit.v, maxAge);
          }
        }
      }
    }
  }, {
    key: "prune",
    value: function prune() {
      var _this4 = this;

      this[CACHE].forEach(function (_value, key) {
        return _get(_this4, key, false);
      });
    }
  }]);
  return Cache;
}();

exports["default"] = Cache;

function refreshCacheOptions(cache) {
  return (0, _mobx.reaction)(function () {
    return (0, _configure.getConfig)('lookupCache') || {};
  }, function (lookupCache) {
    if ('max' in lookupCache) {
      cache.max = lookupCache.max;
    }

    if ('maxAge' in lookupCache) {
      cache.maxAge = lookupCache.maxAge;
    }

    if ('stale' in lookupCache) {
      cache.allowStale = lookupCache.stale;
    }

    if ('length' in lookupCache) {
      cache.lengthCalculator = lookupCache.length;
    }
  });
}
//# sourceMappingURL=index.js.map
