@creejs/commons-collection 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- function c(t){return !!h(t)&&t>0}function h(t){return null!=t&&"number"==typeof t}var L={assertPositive:q};function q(t,r){if(!c(t))throw new Error(`${r?'"'+r+'" ':" "}Not Positive: ${t}`)}new TextDecoder;new TextEncoder;
1
+ function a$1(t){return "boolean"==typeof t}function c$1(t){return "function"==typeof t}function l$1(t){return null!=t&&"object"==typeof t&&!A(t)}function y(t){return null==t}function p$1(t){return !!m$1(t)&&t>0}function h$1(t){return !!m$1(t)&&t>=0}function g$1(t){return !!m$1(t)&&t<0}function w$1(t){return null===t}function d$1(t){return void 0===t}function m$1(t){return null!=t&&"number"==typeof t}function b$1(t){return null!=t&&"object"==typeof t}function A(t){return null!==t&&"object"==typeof t&&(t.constructor===Object||void 0===t.constructor)}function E(t){return null!=t&&"function"==typeof t.then}function N(t){return null!=t&&"string"==typeof t}function $(t){return null!=t&&"symbol"==typeof t}function O$1(t){return ArrayBuffer.isView(t)&&t.constructor!==DataView}function v$1(t){return t instanceof Int8Array}function j(t){return t instanceof Uint8Array}function P(t){return t instanceof Uint8ClampedArray}function S(t){return t instanceof Int16Array}function x(t){return t instanceof Uint16Array}function U(t){return t instanceof Int32Array}function T(t){return t instanceof Uint32Array}function I(t){return t instanceof Float32Array}function B(t){return t instanceof Float64Array}function k$1(t){return t instanceof BigInt64Array}function L$1(t){return t instanceof BigUint64Array}function F(t){return t instanceof ArrayBuffer}var C={assertNumber:q,assertPositive:_$1,assertNegative:function(t,r){if(!g$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Negative: ${t}`)},assertNotNegative:J,assertBoolean:function(t,r){if(!a$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Boolean: type=${typeof t} value=${Z(t)}`)},assertObject:R,assertPlainObject:function(t,r){if(!A(t))throw new Error(`${r?'"'+r+'" ':""}Not PlainObject: type=${typeof t} value=${Z(t)}`)},assertSymbol:function(t,r){if(!$(t))throw new Error(`${r?'"'+r+'" ':""}Not Symbol: type=${typeof t} value=${Z(t)}`)},assertFunction:W,assertInstance:function(t,r){if(!l$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Class Instance: type=${typeof t} value=${Z(t)}`)},assertPromise:H,assertNil:function(t,r){if(!y(t))throw new Error(`${r?'"'+r+'" ':""}Neither Null nor Undefined: type=${typeof t} value=${Z(t)}`)},assertNotNil:V,assertNull:function(t,r){if(!w$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Null: type=${typeof t} value=${Z(t)}`)},assertNotNull:function(t,r){if(w$1(t))throw new Error((r?'"'+r+'" ':"")+"Should Not Null")},assertUndefined:function(t,r){if(!d$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Undefined: type=${typeof t} value=${Z(t)}`)},assertString:M,assertArray:D,assertStringOrSymbol:function(t,r){if(!N(t)&&!$(t))throw new Error(`${r?'"'+r+'" ':""}Not String or Symbol: type=${typeof t} value=${Z(t)}`)},assertInt8Array:function(t,r){if(v$1(t))throw new Error((r?'"'+r+'" ':"")+"Not Int8Array")},assertUint8Array:function(t,r){if(j(t))throw new Error((r?'"'+r+'" ':"")+"Not Uint8Array")},assertUint8ClampedArray:function(t,r){if(P(t))throw new Error((r?'"'+r+'" ':"")+"Not Uint8ClampedArray")},assertInt16Array:function(t,r){if(S(t))throw new Error((r?'"'+r+'" ':"")+"Not Int16Array")},assertUint16Array:function(t,r){if(x(t))throw new Error((r?'"'+r+'" ':"")+"Not Uint16Array")},assertInt32Array:function(t,r){if(U(t))throw new Error((r?'"'+r+'" ':"")+"Not Int32Array")},assertUint32Array:function(t,r){if(T(t))throw new Error((r?'"'+r+'" ':"")+"Not Uint32Array")},assertFloat32Array:function(t,r){if(I(t))throw new Error((r?'"'+r+'" ':"")+"Not Float32Array")},assertFloat64Array:function(t,r){if(B(t))throw new Error((r?'"'+r+'" ':"")+"Not Float64Array")},assertBigInt64Array:function(t,r){if(k$1(t))throw new Error((r?'"'+r+'" ':"")+"Not BigInt64Array")},assertBigUint64Array:function(t,r){if(L$1(t))throw new Error((r?'"'+r+'" ':"")+"Not BigUint64Array")},assertTypedArray:function(t,r){if(O$1(t))throw new Error((r?'"'+r+'" ':"")+"Not TypedArray")},assertArrayBuffer:z};function D(t,r){if(!Array.isArray(t))throw new Error(`${r?'"'+r+'" ':""}Not Array: type=${typeof t} value=${Z(t)}`)}function M(t,r){if(!N(t))throw new Error(`${r?'"'+r+'" ':""}Not String: type=${typeof t} value=${Z(t)}`)}function q(t,r){if(!m$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Number: type=${typeof t} value=${Z(t)}`)}function _$1(t,r){if(!p$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Positive: ${t}`)}function J(t,r){if(!h$1(t))throw new Error(`${r?'"'+r+'" ':""}Not "0 or Positive": ${t}`)}function R(t,r){if(!b$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Object: type=${typeof t} value=${Z(t)}`)}function W(t,r){if(!c$1(t))throw new Error(`${r?'"'+r+'" ':""}Not Function: type=${typeof t} value=${Z(t)}`)}function H(t,r){if(!E(t))throw new Error(`${r?'"'+r+'" ':""}Not Promise: type=${typeof t} value=${Z(t)}`)}function V(t,r){if(y(t))throw new Error((r?'"'+r+'" ':"")+"Should Not Nil")}function z(t,r){if(!F(t))throw new Error((r?'"'+r+'" ':"")+"Not ArrayBuffer")}function Z(t){if(null===t)return "null";if(void 0===t)return "undefined";let r;try{r=JSON.stringify(t);}catch(e){r=t.toString();}return r}new TextDecoder;new TextEncoder;const dt=1e6;var mt={s2ns:1e9,ms2ns:dt,timestamp:function(){if("undefined"!=typeof performance&&"number"==typeof performance.timeOrigin){const t=performance.timeOrigin,r=performance.now();return Math.ceil((t+r)/dt)}return Date.now()},timestamp64:bt,lapseNano:At,lapseMillis:Et,timeoutNano:function(t,r){return At(t)>r},timeoutMillis:function(t,r){return Et(t)>r}};function bt(){if("undefined"!=typeof performance&&"number"==typeof performance.timeOrigin){const t=performance.timeOrigin,r=performance.now();return BigInt((t+r)*dt)}return BigInt(Date.now()*dt)}function At(t,r){return (r??bt())-t}function Et(t,r){r=r??bt();return BigInt(r-t)/BigInt(dt)}
2
2
 
3
3
  // internal
4
4
  // owned
@@ -12,7 +12,7 @@ function c(t){return !!h(t)&&t>0}function h(t){return null!=t&&"number"==typeof
12
12
  */
13
13
 
14
14
  // module vars
15
- const { assertPositive } = L;
15
+ const { assertPositive } = C;
16
16
 
17
17
  /**
18
18
  * A set that has a fixed capacity and automatically removes the oldest element when the capacity is reached.
@@ -30,9 +30,12 @@ class CappedSet {
30
30
  assertPositive(capacity, 'capacity');
31
31
  this.capacity = capacity;
32
32
  /**
33
+ * 1. key is the Value stored in CappedSet
34
+ * 2. value is a Node
35
+ * 3. JS Map preserve the insertion order
33
36
  * @type {Map<any, Node>}
34
37
  */
35
- this._set = new Map();
38
+ this._map = new Map();
36
39
  /**
37
40
  * @type {Node|undefined}
38
41
  */
@@ -44,21 +47,25 @@ class CappedSet {
44
47
  }
45
48
 
46
49
  get size () {
47
- return this._set.size
50
+ return this._map.size
48
51
  }
49
52
 
50
- // 获取最旧的元素
53
+ /**
54
+ * get the oldest element
55
+ */
51
56
  get oldest () {
52
57
  return this._head?.value
53
58
  }
54
59
 
55
- // 获取最新的元素
60
+ /**
61
+ * get the newest element
62
+ */
56
63
  get newest () {
57
64
  return this._tail?.value
58
65
  }
59
66
 
60
67
  [Symbol.iterator] () {
61
- return this._set.keys()
68
+ return this._map.keys()
62
69
  }
63
70
 
64
71
  /**
@@ -67,8 +74,8 @@ class CappedSet {
67
74
  * @param {*} value - The value to add to the set
68
75
  */
69
76
  add (value) {
70
- if (this._set.has(value)) {
71
- const node = this._set.get(value);
77
+ if (this._map.has(value)) {
78
+ const node = this._map.get(value);
72
79
  node && this._removeNode(node);
73
80
  } else if (this.size >= this.capacity) {
74
81
  this._removeOldest();
@@ -82,7 +89,7 @@ class CappedSet {
82
89
  * @returns {boolean} True if the value exists, false otherwise.
83
90
  */
84
91
  has (value) {
85
- return this._set.has(value)
92
+ return this._map.has(value)
86
93
  }
87
94
 
88
95
  /**
@@ -91,8 +98,8 @@ class CappedSet {
91
98
  * @returns {boolean} True if the value was found and removed, false otherwise.
92
99
  */
93
100
  delete (value) {
94
- if (this._set.has(value)) {
95
- const node = this._set.get(value);
101
+ if (this._map.has(value)) {
102
+ const node = this._map.get(value);
96
103
  node && this._removeNode(node);
97
104
  return true
98
105
  }
@@ -100,7 +107,7 @@ class CappedSet {
100
107
  }
101
108
 
102
109
  clear () {
103
- this._set.clear();
110
+ this._map.clear();
104
111
  this._head = undefined;
105
112
  this._tail = undefined;
106
113
  }
@@ -110,7 +117,7 @@ class CappedSet {
110
117
  * @returns {Iterator<any>} An iterator object that yields the values of the set.
111
118
  */
112
119
  values () {
113
- return this._set.keys()
120
+ return this._map.keys()
114
121
  }
115
122
 
116
123
  /**
@@ -132,7 +139,7 @@ class CappedSet {
132
139
  }
133
140
 
134
141
  this._tail = node;
135
- this._set.set(value, node);
142
+ this._map.set(value, node);
136
143
  }
137
144
 
138
145
  /**
@@ -154,7 +161,7 @@ class CappedSet {
154
161
  this._tail = node.prev;
155
162
  }
156
163
 
157
- this._set.delete(node.value);
164
+ this._map.delete(node.value);
158
165
  }
159
166
 
160
167
  _removeOldest () {
@@ -164,7 +171,539 @@ class CappedSet {
164
171
  }
165
172
  }
166
173
 
167
- var index = { CappedSet };
174
+ var e={isFunction:t,isNil:s};function t(e){return "function"==typeof e}function s(e){return null==e}function n(e){return null!=e&&"string"==typeof e}var r={assertNumber:function(e,t){if(!function(e){return null!=e&&"number"==typeof e}(e))throw new Error(`${t?'"'+t+'" ':""}Not Number: type=${typeof e} value=${i(e)}`)},assertFunction:function(e,s){if(!t(e))throw new Error(`${s?'"'+s+'" ':""}Not Function: type=${typeof e} value=${i(e)}`)},assertNotNil:function(e,t){if(s(e))throw new Error((t?'"'+t+'" ':"")+"Should Not Nil")},assertString:function(e,t){if(!n(e))throw new Error(`${t?'"'+t+'" ':""}Not String: type=${typeof e} value=${i(e)}`)},assertStringOrSymbol:function(e,t){if(!n(e)&&!function(e){return null!=e&&"symbol"==typeof e}(e))throw new Error(`${t?'"'+t+'" ':""}Not String or Symbol: type=${typeof e} value=${i(e)}`)}};function i(e){if(null===e)return "null";if(void 0===e)return "undefined";let t;try{t=JSON.stringify(e);}catch(s){t=e.toString();}return t}new TextDecoder,new TextEncoder;const l="DOwner$#$",{assertFunction:a,assertNotNil:o}=r;class c{constructor(e,t,s=false){o(e,"event"),a(t,"callback"),this._event=e,this._callback=t,this._isOnce=!!s,this._owner=void 0;}set owner(e){this._owner=e;}get owner(){return this._owner===l?void 0:this._owner}get event(){return this._event}get isOnce(){return this._isOnce}isSameCallback(e){return this._callback===e}get callback(){return this._callback}invoke(...e){try{return this._callback(...e)}finally{if(this._isOnce)try{this._event._remove(this);}catch(e){console.warn(e);}}}listener(...e){return this.invoke(...e)}}const{isFunction:h,isNil:u}=e,{assertStringOrSymbol:_,assertFunction:f}=r;class m{static get DefaultOwner(){return l}constructor(e){_(e,"eventName"),this._name=e,this._callbacks=new Set,this._listeners=[],this._callback2Listeners=new Map,this._listener2Owner=new Map,this._owner2Listeners=new Map;}get name(){return this._name}isEmpty(){return 0===this._callbacks.size}rawListeners(){return [...this._listeners]}listenerCount(e){return null==e?this._listeners.length:this._callback2Listeners.get(e)?.size??0}callbacks(){return [...this.rawListeners().map(e=>e.callback)]}emit(...e){if(0===this._listeners.length)return false;for(const t of [...this._listeners])t.invoke(...e);return true}emitOwner(e,...t){if(0===this._listeners.length)return false;const s=this._owner2Listeners.get(e);if(null==s)return false;for(const e of [...s])e.invoke(...t);return true}hasListener(e){return !!h(e)&&this._callbacks.has(e)}hasOwner(e){return !u(e)&&this._owner2Listeners.has(e)}addListener(e,t){return this._addListener(e,t,false,false)}prependListener(e,t){return this._addListener(e,t,false,true)}addOnceListener(e,t){return this._addListener(e,t,true,false)}prependOnceListener(e,t){return this._addListener(e,t,true,true)}_addListener(e,t,s,n){if(u(e))return false;f(e),this._callbacks.has(e)||this._callbacks.add(e),t=t??l;const r=new c(this,e,s);r.owner=t,n?this._listeners.unshift(r):this._listeners.push(r),this._listener2Owner.set(r,t);let i=this._callback2Listeners.get(e);null==i&&(i=new Set,this._callback2Listeners.set(e,i)),i.add(r);let a=this._owner2Listeners.get(t);return null==a&&(a=new Set,this._owner2Listeners.set(t,a)),a.add(r),true}removeListener(e){if(u(e))return false;if(!this._callbacks.has(e))return false;this._callbacks.delete(e);const t=this._callback2Listeners.get(e);if(null==t)return false;this._callback2Listeners.delete(e);for(const e of t){ -1!==this._listeners.indexOf(e)&&this._listeners.splice(this._listeners.indexOf(e),1);const t=this._listener2Owner.get(e);if(null==t)continue;this._listener2Owner.delete(e);const s=this._owner2Listeners.get(t);null!=s&&(s.delete(e),0===s.size&&this._owner2Listeners.delete(t));}return true}_remove(e){const t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1);const{callback:s}=e,n=this._callback2Listeners.get(s);null!=n&&(n.delete(e),0===n.size&&(this._callback2Listeners.delete(s),this._callbacks.delete(s)));const r=this._listener2Owner.get(e);if(null==r)return;this._listener2Owner.delete(e);const i=this._owner2Listeners.get(r);null!=i&&(i.delete(e),0===i.size&&this._owner2Listeners.delete(r));}removeAllListeners(e){if(u(e))return this._callbacks.clear(),this._listeners.length=0,this._callback2Listeners.clear(),this._listener2Owner.clear(),this._owner2Listeners.clear(),this;const t=this._owner2Listeners.get(e);if(null==t)return this;this._owner2Listeners.delete(e);for(const e of t){ -1!==this._listeners.indexOf(e)&&this._listeners.splice(this._listeners.indexOf(e),1),this._listener2Owner.delete(e);const{callback:t}=e,s=this._callback2Listeners.get(t);null!=s&&(s.delete(e),0===s.size&&(this._callback2Listeners.delete(t),this._callbacks.delete(t)));}return this}}const{isNil:d}=e,{assertString:w,assertFunction:L,assertNumber:v,assertStringOrSymbol:g,assertNotNil:p}=r,b=["on","once","addListener","prependListener","prependOnceListener","off","offAll","offOwner","removeAllListeners","removeListener","emit","emitOwner","setMaxListeners","getMaxListeners","hasOwner","listeners","listenerCount","eventNames","rawListeners"];let O=10;class k{static mixin(e){const t=new k;e.__emitter=t;for(const s of b){const n=t[s];e[s]=n.bind(t);}return e}static get defaultMaxListeners(){return O}static set defaultMaxListeners(e){v(e),O=e??10;}constructor(){this._name2Event=new Map,this._maxListeners=O;}addListener(e,t,s){return this.on(e,t,s)}prependListener(e,t,s){w(e),L(t),this._checkMaxListeners(e);return this._getOrCreateEvent(e).prependListener(t,s),this}prependOnceListener(e,t,s){w(e),L(t),this._checkMaxListeners(e);return this._getOrCreateEvent(e).prependOnceListener(t,s),this}emit(e,...t){const s=this._name2Event.get(e);return null!=s&&!s.isEmpty()&&(s.emit(...t),true)}emitOwner(e,t,...s){if(null==t)throw new Error('Missing "owner"');const n=this._name2Event.get(e);return null!=n&&!n.isEmpty()&&(n.emitOwner(t,...s),true)}eventNames(){return [...this._name2Event.keys()]}getMaxListeners(){return this._maxListeners}listenerCount(e,t){g(e,"eventName");const s=this._name2Event.get(e);return null==s||s.isEmpty()?0:s.listenerCount(t)}listeners(e){g(e,"eventName");const t=this._name2Event.get(e);return null==t||t.isEmpty()?[]:t.callbacks()}off(e,t){const s=this._name2Event.get(e);return null==s?this:(s.removeListener(t),s.isEmpty()?(this._name2Event.delete(e),this):this)}offAll(e,t){g(e,"eventName");const s=this._name2Event.get(e);return null==s?this:(s.removeAllListeners(t),s.isEmpty()?(this._name2Event.delete(e),this):this)}offOwner(e){p(e,"owner");const t=[...this._name2Event.values()];for(const s of t)s.removeAllListeners(e),s.isEmpty()&&this._name2Event.delete(s.name);return this}on(e,t,s){w(e),L(t),this._checkMaxListeners(e);return this._getOrCreateEvent(e).addListener(t,s),this}_checkMaxListeners(e){let t=0;0!==this._maxListeners&&this._maxListeners!==1/0&&(t=this.listenerCount(e))>=this._maxListeners&&console.warn(`maxlistenersexceededwarning: Possible EventEmitter memory leak detected. ${t} ${e} listeners added to [${this}]. Use emitter.setMaxListeners() to increase limit`);}once(e,t,s){w(e),L(t);return this._getOrCreateEvent(e).addOnceListener(t,s),this}rawListeners(e){return this._name2Event.get(e)?.rawListeners()||[]}removeAllListeners(e,t){return this.offAll(e,t)}removeListener(e,t){return this.off(e,t)}setMaxListeners(e){if(v(e),e<0)throw new RangeError("maxListeners must >=0");return this._maxListeners=e,this}_getOrCreateEvent(e){if(this._name2Event.has(e))return this._name2Event.get(e);const t=new m(e);return this._name2Event.set(e,t),t}hasOwner(e){if(d(e))return false;for(const t of this._name2Event.values())if(t.hasOwner(e))return true;return false}}
175
+
176
+ // 3rd
177
+ // internal
178
+ // owned
179
+
180
+ /**
181
+ * @typedef {number} Timestamp
182
+ * @typedef {{
183
+ * tickInterval: number, // ms
184
+ * tickCount: number // ms
185
+ * }} TimeWheelCacheOptions
186
+ */
187
+
188
+ // module vars
189
+ const Event$1 = {
190
+ Advance: 'advance',
191
+ Expired: 'expired'
192
+ };
193
+
194
+ /**
195
+ * Not Found Better Implementation in npmjs.com, so do it by ourselves.
196
+ *
197
+ * Key Points:
198
+ * 1. Basic Atom Unit is "tick", 1 tick = 1 tickInterval
199
+ * 3. How many milliseconds does 1 Tick represent is defined by "tickInterval"
200
+ * 4. How many Ticks does the Wheel own is defined by "tickCount"
201
+ */
202
+ class TimeWheelCache extends k {
203
+ get Event () {
204
+ return Event$1
205
+ }
206
+
207
+ /**
208
+ * @param {TimeWheelCacheOptions} [options]
209
+ */
210
+ constructor (options) {
211
+ super();
212
+ this.options = options ?? {};
213
+ /**
214
+ * How many milliseconds does one Tick represent
215
+ */
216
+ this._tickInterval = this.options.tickInterval ?? 1000;
217
+ /**
218
+ * How many Ticks does the Wheel have
219
+ */
220
+ this._tickCount = this.options.tickCount ?? 60;
221
+ /**
222
+ * Slots, one Tick owns one Slot to store key and expire timestamp
223
+ * @type {Map<any, {value: any, slotIndex: number, expireTimestamp: number}>[]}
224
+ */
225
+ this._slots = Array(this._tickCount).fill(undefined).map(() => new Map());
226
+ /**
227
+ * Data Cache
228
+ * @type {Map<any, {value: any, slotIndex: number, expireTimestamp: number}>}
229
+ */
230
+ this._cache = new Map();
231
+ this._currentSlotIndex = 0;
232
+ /** @type {NodeJS.Timeout|undefined} */
233
+ this._timer = undefined;
234
+
235
+ // must start it immediately
236
+ this._startAutoEvict();
237
+ }
238
+
239
+ get tickInterval () {
240
+ return this._tickInterval
241
+ }
242
+
243
+ get tickCount () {
244
+ return this._tickCount
245
+ }
246
+
247
+ /**
248
+ * Max Time to Live, atom unit "millisecond"
249
+ * @returns {number}
250
+ */
251
+ get maxTtl () {
252
+ if (this._maxTtl == null) {
253
+ this._maxTtl = this._tickCount * this._tickInterval; // ms
254
+ }
255
+ return this._maxTtl
256
+ }
257
+
258
+ assertStarted () {
259
+ if (!this.autoEvictRunning) {
260
+ throw new Error(`${this.constructor.name} is not started`)
261
+ }
262
+ }
263
+
264
+ get autoEvictRunning () {
265
+ return this._timer != null
266
+ }
267
+
268
+ _startAutoEvict () {
269
+ this._timer = setInterval(() => this._advance(), this._tickInterval);
270
+ }
271
+
272
+ _stopAutoEvict () {
273
+ this._timer && clearInterval(this._timer);
274
+ this._timer = undefined;
275
+ }
276
+
277
+ _advance () {
278
+ const nextSlotIndex = (this._currentSlotIndex + 1) % this._tickCount;
279
+ const nextSlot = this._slots[nextSlotIndex];
280
+ const currentSlot = this._slots[this._currentSlotIndex];
281
+ this.emit(Event$1.Advance, this._currentSlotIndex, currentSlot, nextSlot);
282
+ if (currentSlot.size > 0) {
283
+ // clear current slot, and expired data
284
+ for (const key of currentSlot.keys()) {
285
+ const wrapped = this._cache.get(key);
286
+ this._cache.delete(key);
287
+ // @ts-ignore
288
+ this.emit(Event$1.Expired, key, wrapped.value, wrapped.expireTimestamp);
289
+ }
290
+ currentSlot.clear();
291
+ }
292
+ this._currentSlotIndex = nextSlotIndex;
293
+ }
294
+
295
+ start () {
296
+ if (this.autoEvictRunning) {
297
+ return
298
+ }
299
+ this._startAutoEvict();
300
+ }
301
+
302
+ stop () {
303
+ if (!this.autoEvictRunning) {
304
+ return
305
+ }
306
+ this._stopAutoEvict();
307
+ }
308
+
309
+ destroy () {
310
+ this._stopAutoEvict();
311
+ this.clear();
312
+ }
313
+
314
+ /**
315
+ * 1. "key" is not Nil
316
+ * 2. "value" is not Nil
317
+ * 3. "ttl" must > 0 && <= maxTtl, maxTtl = tickCount * tickInterval
318
+ * * > 0: CAN NOT go to past
319
+ * * < maxTtl: Wheel is Round, eg.
320
+ * * "Hour Wheel" has 24 Hour-Tick-Mark
321
+ * * CAN NOT distinguish 24 hours and 48 hours
322
+ * 4. Understand the difference between "ttl" and "timestamp"
323
+ * * TTL is Indexed from 1
324
+ * * eg. ttl = 60, should be stored in Slot[59]
325
+ * * Timestamp is Indexed from 0
326
+ * @param {any} key
327
+ * @param {any} value
328
+ * @param {number} ttl Time to Live, atom unit "millisecond"
329
+ * @returns {boolean}
330
+ */
331
+ set (key, value, ttl) {
332
+ this.assertStarted();
333
+ C.assertNotNil(key, 'key');
334
+ C.assertNotNil(value, 'value');
335
+ if (ttl <= 0) {
336
+ throw new Error(`Bad ttl "${ttl}", must > 0`)
337
+ }
338
+ if (ttl > this.maxTtl) {
339
+ throw new Error(`Bad ttl "${ttl}", must <= maxTtl(${this.maxTtl})`)
340
+ }
341
+ // delete existed firstly, Slot may be changed
342
+ if (this._cache.has(key)) {
343
+ this.delete(key);
344
+ }
345
+ // TimeUtils.timestamp() is built on Uptime, not affected by system time change
346
+ const expireTimestamp = mt.timestamp() + ttl;
347
+ // 1 tick at least
348
+ // ttl -1, eg. 1~60s should be stored in Slot[0]
349
+ const ticks = Math.floor((ttl - 1) / this._tickInterval);
350
+ const targetSlotIndex = (this._currentSlotIndex + ticks) % this._tickCount;
351
+ const storedItem = { value, slotIndex: targetSlotIndex, expireTimestamp };
352
+ this._slots[targetSlotIndex].set(key, storedItem);
353
+ this._cache.set(key, storedItem);
354
+ return true
355
+ }
356
+
357
+ /**
358
+ * @param {any} key
359
+ * @returns {boolean}
360
+ */
361
+ delete (key) {
362
+ const wrapped = this._cache.get(key);
363
+ if (!wrapped) {
364
+ return false
365
+ }
366
+ this._cache.delete(key);
367
+ // delete from target slot
368
+ // Time-Wheel may be Minute-Hour-Based, it's too long to leave it in slot
369
+ const { slotIndex } = wrapped;
370
+ const targetSlot = this._slots[slotIndex];
371
+ targetSlot.delete(key);
372
+ return true
373
+ }
374
+
375
+ /**
376
+ * Checks if the cache contains the specified key.
377
+ * @param {any} key - The key to check for existence in the cache.
378
+ * @returns {boolean} - True if the key exists in the cache, false otherwise.
379
+ */
380
+ has (key) {
381
+ const wrapped = this._cache.get(key);
382
+ if (!wrapped) {
383
+ return false
384
+ }
385
+ if (wrapped.expireTimestamp <= mt.timestamp()) {
386
+ this.delete(key);
387
+ this.emit('expired', key, wrapped.value, wrapped.expireTimestamp);
388
+ return false
389
+ }
390
+ return true
391
+ }
392
+
393
+ clear () {
394
+ this._cache.clear();
395
+ this._slots.forEach(slot => slot.clear());
396
+ this._currentSlotIndex = 0;
397
+ }
398
+
399
+ /**
400
+ *
401
+ * @param {any} key
402
+ * @returns {any}
403
+ */
404
+ get (key) {
405
+ const wrapped = this._cache.get(key);
406
+ if (!wrapped) {
407
+ return
408
+ }
409
+ if (wrapped.expireTimestamp <= mt.timestamp()) {
410
+ this.delete(key);
411
+ this.emit('expired', key, wrapped.value, wrapped.expireTimestamp);
412
+ return
413
+ }
414
+ return wrapped.value
415
+ }
416
+
417
+ size () {
418
+ // current slot may contain expired data
419
+ const slot = this._slots[this._currentSlotIndex];
420
+ if (slot.size > 0) {
421
+ const now = mt.timestamp();
422
+ for (const [key, { expireTimestamp }] of slot.entries()) {
423
+ if (expireTimestamp <= now) {
424
+ const wrapped = this._cache.get(key);
425
+ // @ts-ignore
426
+ this.emit('expired', key, wrapped.value, wrapped.expireTimestamp);
427
+ slot.delete(key);
428
+ this._cache.delete(key);
429
+ }
430
+ }
431
+ }
432
+ return this._cache.size
433
+ }
434
+ }
435
+
436
+ // 3rd
437
+ // internal
438
+ /**
439
+ * @typedef {number} Timestamp
440
+ */
441
+
442
+ // module vars
443
+ const SecondInMillisecond = 1_000; // 1s
444
+ const MinuteInMillisecond = 60_000; // 1m, 60s
445
+ const HourInMillisecond = 3_600_000; // 1h, 3600s
446
+ const Hour24InMillisecond = 86_400_000; // 24 hours, 86400s
447
+
448
+ const DowngradType = {
449
+ HourToSecond: 'hour->second',
450
+ HourToMinute: 'hour->minute',
451
+ MinuteToSecond: 'minute->second'
452
+ };
453
+
454
+ const Event = {
455
+ Downgrade: 'downgrade',
456
+ Expired: 'expired'
457
+ };
458
+
459
+ const MaxTtl = Hour24InMillisecond - 1;
460
+
461
+ /**
462
+ * Hour-Minute-Second-Time-Wheel Cache
463
+ * 1. TTL must be less than 24 Hours
464
+ */
465
+ class Hour24TimeWheelCache extends k {
466
+ static get DowngradType () {
467
+ return DowngradType
468
+ }
469
+
470
+ static get Event () {
471
+ return Event
472
+ }
473
+
474
+ /**
475
+ *
476
+ */
477
+ constructor () {
478
+ super();
479
+ /**
480
+ * Second Wheel:
481
+ * 1. 1 Tick Mark is 1 Second, is 1000 Milliseconds
482
+ * 2. 60 Slots, maximumly, SencondWheel can contain 60 Seconds
483
+ * 3. 60 Seconds should be stored in MinuteWheel
484
+ * * 01 00:00:00.XXX -> 00:00:59.XXX in Slot01 Index00
485
+ * * 02 00:00:01.XXX -> 00:00:01.XXX in Slot02 Index01
486
+ * * 60 00:00:59.XXX -> 00:00:59.XXX in Slot60 Index59
487
+ * @type {TimeWheelCache}
488
+ */
489
+ this._secondWheel = new TimeWheelCache({ tickInterval: SecondInMillisecond, tickCount: 60 });
490
+ /**
491
+ * Minute Wheel:
492
+ * 1. 1 Tick Mark is 1 Minute, is 60 * 1000 Milliseconds
493
+ * 2. 60 Slots, maximumly, MinuteWheel can contain 60 Minutes
494
+ * * 01 00:00:00 -> 00:00:59 in Slot01 Index00
495
+ * * 02 00:01:00 -> 00:01:59 in Slot02 Index01
496
+ * * 60 00:59:00 -> 00:59:59 in Slot60 Index59
497
+ * @type {TimeWheelCache}
498
+ */
499
+ this._minuteWheel = new TimeWheelCache({ tickInterval: MinuteInMillisecond, tickCount: 60 });
500
+ /**
501
+ * Hour Wheel:
502
+ * 1. 1 Tick Mark is 1 Hour, is 60 * 60 * 1000 Milliseconds
503
+ * 2. 24 Slots, maximumly, HourWheel can contain 23:59:59
504
+ * * 01 00:00:00 -> 00:59:59 in Slot01 Index00
505
+ * * 02 01:00:00 -> 01:59:59 in Slot02 Index01
506
+ * * 24 23:00:00 -> 23:59:59 in Slot23 Index23
507
+ * @type {TimeWheelCache}
508
+ */
509
+ this._hourWheel = new TimeWheelCache({ tickInterval: HourInMillisecond, tickCount: 24 });
510
+ /**
511
+ * @type {Map<any, TimeWheelCache>}
512
+ */
513
+ this._cache = new Map();
514
+ this._init();
515
+ }
516
+
517
+ /**
518
+ * Max Time to Live, atom unit "millisecond"
519
+ * @returns {number}
520
+ */
521
+ get maxTtl () {
522
+ return MaxTtl
523
+ }
524
+
525
+ get autoEvictRunning () {
526
+ return this._secondWheel.autoEvictRunning || this._minuteWheel.autoEvictRunning || this._hourWheel.autoEvictRunning
527
+ }
528
+
529
+ _init () {
530
+ /**
531
+ * 1. We don't store "Timetick: < 00:00:01 -> 00:59:59" into HourWheel, instead, we store them into MinuteWheel
532
+ * 2. When HourWeel advances, take Elements of next Slot, and downgrade them to MinuteWheel
533
+ */
534
+ this._hourWheel.on('advance', (/** @type {number} */currentSlotIndex,
535
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */currentSlot,
536
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */nextSlot) => {
537
+ for (const [key, { value, expireTimestamp }] of nextSlot) {
538
+ let leftTtl = expireTimestamp - mt.timestamp();
539
+ let downgradeType;
540
+ if (leftTtl <= 0) {
541
+ leftTtl = 1000;
542
+ downgradeType = DowngradType.HourToSecond;
543
+ this._secondWheel.set(key, value, leftTtl);
544
+ this.emit(Event.Downgrade, key, value, downgradeType, leftTtl);
545
+ } else if (leftTtl < MinuteInMillisecond) {
546
+ downgradeType = DowngradType.HourToSecond;
547
+ this._secondWheel.set(key, value, leftTtl);
548
+ this.emit(Event.Downgrade, key, value, downgradeType, leftTtl);
549
+ } else {
550
+ downgradeType = DowngradType.HourToMinute;
551
+ this._minuteWheel.set(key, value, leftTtl);
552
+ }
553
+ this._hourWheel.delete(key);
554
+ this.emit(Event.Downgrade, key, value, downgradeType, leftTtl);
555
+ }
556
+ });
557
+ /**
558
+ * 1. We don't store "Timetick: < 00:01:00 -> 00:01:59" into MinuteWheel, instead, we store them into SecondWheel
559
+ * 2. When MinuteWheel advances, take Elements of next Slot, and downgrade them to SecondWheel
560
+ */
561
+ this._minuteWheel.on('advance', (/** @type {number} */currentSlotIndex,
562
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */currentSlot,
563
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */nextSlot) => {
564
+ // console.log('advance', currentSlotIndex)
565
+ for (const [key, { value, expireTimestamp }] of nextSlot) {
566
+ let leftTtl = expireTimestamp - mt.timestamp();
567
+ if (leftTtl <= 0) {
568
+ leftTtl = 999; // ms
569
+ }
570
+ // console.log('downgrad', key, value, DowngradType.MinuteToSecond, leftTtl)
571
+ this._secondWheel.set(key, value, leftTtl);
572
+ this.emit(Event.Downgrade, key, value, DowngradType.MinuteToSecond, leftTtl);
573
+ this._minuteWheel.delete(key);
574
+ }
575
+ });
576
+
577
+ this._secondWheel.on('expired', (/** @type {any} */key, /** @type {any} */value, /** @type {Timestamp} */expireTimestamp) => {
578
+ this.emit(Event.Expired, key, value, expireTimestamp);
579
+ });
580
+ }
581
+
582
+ start () {
583
+ this._secondWheel.start();
584
+ this._minuteWheel.start();
585
+ this._hourWheel.start();
586
+ }
587
+
588
+ stop () {
589
+ this._secondWheel.stop();
590
+ this._minuteWheel.stop();
591
+ this._hourWheel.stop();
592
+ }
593
+
594
+ destroy () {
595
+ this._secondWheel.destroy();
596
+ this._minuteWheel.destroy();
597
+ this._hourWheel.destroy();
598
+ }
599
+
600
+ /**
601
+ * @param {any} key
602
+ * @param {any} value
603
+ * @param {number} ttl Time To Live, unit "millisencond", ttl should < 24 Hours
604
+ * @returns {boolean}
605
+ */
606
+ set (key, value, ttl) {
607
+ if (ttl <= 0) {
608
+ throw new Error(`Bad ttl "${ttl}", must > 0`)
609
+ }
610
+ /**
611
+ */
612
+ let wheel;
613
+ if (ttl < MinuteInMillisecond) { // Timetick: 00:00:01 -> 00:00:59
614
+ wheel = this._secondWheel;
615
+ } else if (ttl < HourInMillisecond) { // Timetick: < 00:00:01 -> 00:59:59
616
+ wheel = this._minuteWheel;
617
+ } else if (ttl < Hour24InMillisecond) { // Timetick: 01:00:00 -> 23:59:59
618
+ wheel = this._hourWheel;
619
+ } else {
620
+ throw new Error('"ttl" Should <= Millisencod Of 24 Hours')
621
+ }
622
+ wheel.set(key, value, ttl);
623
+ this._cache.set(key, wheel);
624
+ return true
625
+ }
626
+
627
+ /**
628
+ * @param {any} key
629
+ * @returns {boolean}
630
+ */
631
+ delete (key) {
632
+ const wheel = this._cache.get(key);
633
+ if (!wheel) {
634
+ return false
635
+ }
636
+ return wheel.delete(key)
637
+ }
638
+
639
+ /**
640
+ * Checks if the cache contains the specified key.
641
+ * @param {any} key - The key to check for existence in the cache.
642
+ * @returns {boolean} - True if the key exists in the cache, false otherwise.
643
+ */
644
+ has (key) {
645
+ const wheel = this._cache.get(key);
646
+ if (!wheel) { // No wheel, No Data
647
+ return false
648
+ }
649
+ if (wheel.has(key)) { // In Wheel, Has Data
650
+ return true
651
+ }
652
+ // current wheel is second-Wheel, Not existed or expired
653
+ if (wheel === this._secondWheel) {
654
+ return false
655
+ }
656
+ // Not In Wheel, It may be down-graded to lower wheel
657
+ if (wheel === this._hourWheel) { // current wheel is hour, check minute wheel
658
+ return this._minuteWheel.has(key)
659
+ } else if (wheel === this._minuteWheel) { // current wheel is minute, check second wheel
660
+ return this._secondWheel.has(key)
661
+ }
662
+ // It doesn't exist actually
663
+ return false
664
+ }
665
+
666
+ clear () {
667
+ this._secondWheel.clear();
668
+ this._minuteWheel.clear();
669
+ this._hourWheel.clear();
670
+ this._cache.clear();
671
+ }
672
+
673
+ /**
674
+ *
675
+ * @param {any} key
676
+ * @returns {any}
677
+ */
678
+ get (key) {
679
+ const wheel = this._cache.get(key);
680
+ if (!wheel) { // No wheel, No Data
681
+ return
682
+ }
683
+ const value = wheel.get(key);
684
+ if (value != null) { // In Wheel, Has Data
685
+ return value
686
+ }
687
+ // current wheel is second-Wheel, Not existed or expired
688
+ if (wheel === this._secondWheel) {
689
+ return
690
+ }
691
+ // Not In Wheel, It may be down-graded to lower wheel
692
+ if (wheel === this._hourWheel) { // current wheel is hour, check minute wheel
693
+ return this._minuteWheel.get(key)
694
+ } else if (wheel === this._minuteWheel) { // current wheel is minute, check second wheel
695
+ return this._secondWheel.get(key)
696
+ }
697
+ // It doesn't exist actually
698
+ return undefined
699
+ }
700
+
701
+ size () {
702
+ return this._hourWheel.size() + this._minuteWheel.size() + this._secondWheel.size()
703
+ }
704
+ }
705
+
706
+ var index = { CappedSet, TimeWheelCache, Hour24TimeWheelCache };
168
707
 
169
- export { CappedSet, index as default };
708
+ export { CappedSet, Hour24TimeWheelCache, TimeWheelCache, index as default };
170
709
  //# sourceMappingURL=index-dev.js.map