@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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- 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;
5
+ 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)}
6
6
 
7
7
  // internal
8
8
  // owned
@@ -16,7 +16,7 @@ function c(t){return !!h(t)&&t>0}function h(t){return null!=t&&"number"==typeof
16
16
  */
17
17
 
18
18
  // module vars
19
- const { assertPositive } = L;
19
+ const { assertPositive } = C;
20
20
 
21
21
  /**
22
22
  * A set that has a fixed capacity and automatically removes the oldest element when the capacity is reached.
@@ -34,9 +34,12 @@ class CappedSet {
34
34
  assertPositive(capacity, 'capacity');
35
35
  this.capacity = capacity;
36
36
  /**
37
+ * 1. key is the Value stored in CappedSet
38
+ * 2. value is a Node
39
+ * 3. JS Map preserve the insertion order
37
40
  * @type {Map<any, Node>}
38
41
  */
39
- this._set = new Map();
42
+ this._map = new Map();
40
43
  /**
41
44
  * @type {Node|undefined}
42
45
  */
@@ -48,21 +51,25 @@ class CappedSet {
48
51
  }
49
52
 
50
53
  get size () {
51
- return this._set.size
54
+ return this._map.size
52
55
  }
53
56
 
54
- // 获取最旧的元素
57
+ /**
58
+ * get the oldest element
59
+ */
55
60
  get oldest () {
56
61
  return this._head?.value
57
62
  }
58
63
 
59
- // 获取最新的元素
64
+ /**
65
+ * get the newest element
66
+ */
60
67
  get newest () {
61
68
  return this._tail?.value
62
69
  }
63
70
 
64
71
  [Symbol.iterator] () {
65
- return this._set.keys()
72
+ return this._map.keys()
66
73
  }
67
74
 
68
75
  /**
@@ -71,8 +78,8 @@ class CappedSet {
71
78
  * @param {*} value - The value to add to the set
72
79
  */
73
80
  add (value) {
74
- if (this._set.has(value)) {
75
- const node = this._set.get(value);
81
+ if (this._map.has(value)) {
82
+ const node = this._map.get(value);
76
83
  node && this._removeNode(node);
77
84
  } else if (this.size >= this.capacity) {
78
85
  this._removeOldest();
@@ -86,7 +93,7 @@ class CappedSet {
86
93
  * @returns {boolean} True if the value exists, false otherwise.
87
94
  */
88
95
  has (value) {
89
- return this._set.has(value)
96
+ return this._map.has(value)
90
97
  }
91
98
 
92
99
  /**
@@ -95,8 +102,8 @@ class CappedSet {
95
102
  * @returns {boolean} True if the value was found and removed, false otherwise.
96
103
  */
97
104
  delete (value) {
98
- if (this._set.has(value)) {
99
- const node = this._set.get(value);
105
+ if (this._map.has(value)) {
106
+ const node = this._map.get(value);
100
107
  node && this._removeNode(node);
101
108
  return true
102
109
  }
@@ -104,7 +111,7 @@ class CappedSet {
104
111
  }
105
112
 
106
113
  clear () {
107
- this._set.clear();
114
+ this._map.clear();
108
115
  this._head = undefined;
109
116
  this._tail = undefined;
110
117
  }
@@ -114,7 +121,7 @@ class CappedSet {
114
121
  * @returns {Iterator<any>} An iterator object that yields the values of the set.
115
122
  */
116
123
  values () {
117
- return this._set.keys()
124
+ return this._map.keys()
118
125
  }
119
126
 
120
127
  /**
@@ -136,7 +143,7 @@ class CappedSet {
136
143
  }
137
144
 
138
145
  this._tail = node;
139
- this._set.set(value, node);
146
+ this._map.set(value, node);
140
147
  }
141
148
 
142
149
  /**
@@ -158,7 +165,7 @@ class CappedSet {
158
165
  this._tail = node.prev;
159
166
  }
160
167
 
161
- this._set.delete(node.value);
168
+ this._map.delete(node.value);
162
169
  }
163
170
 
164
171
  _removeOldest () {
@@ -168,8 +175,542 @@ class CappedSet {
168
175
  }
169
176
  }
170
177
 
171
- var index = { CappedSet };
178
+ 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}}
179
+
180
+ // 3rd
181
+ // internal
182
+ // owned
183
+
184
+ /**
185
+ * @typedef {number} Timestamp
186
+ * @typedef {{
187
+ * tickInterval: number, // ms
188
+ * tickCount: number // ms
189
+ * }} TimeWheelCacheOptions
190
+ */
191
+
192
+ // module vars
193
+ const Event$1 = {
194
+ Advance: 'advance',
195
+ Expired: 'expired'
196
+ };
197
+
198
+ /**
199
+ * Not Found Better Implementation in npmjs.com, so do it by ourselves.
200
+ *
201
+ * Key Points:
202
+ * 1. Basic Atom Unit is "tick", 1 tick = 1 tickInterval
203
+ * 3. How many milliseconds does 1 Tick represent is defined by "tickInterval"
204
+ * 4. How many Ticks does the Wheel own is defined by "tickCount"
205
+ */
206
+ class TimeWheelCache extends k {
207
+ get Event () {
208
+ return Event$1
209
+ }
210
+
211
+ /**
212
+ * @param {TimeWheelCacheOptions} [options]
213
+ */
214
+ constructor (options) {
215
+ super();
216
+ this.options = options ?? {};
217
+ /**
218
+ * How many milliseconds does one Tick represent
219
+ */
220
+ this._tickInterval = this.options.tickInterval ?? 1000;
221
+ /**
222
+ * How many Ticks does the Wheel have
223
+ */
224
+ this._tickCount = this.options.tickCount ?? 60;
225
+ /**
226
+ * Slots, one Tick owns one Slot to store key and expire timestamp
227
+ * @type {Map<any, {value: any, slotIndex: number, expireTimestamp: number}>[]}
228
+ */
229
+ this._slots = Array(this._tickCount).fill(undefined).map(() => new Map());
230
+ /**
231
+ * Data Cache
232
+ * @type {Map<any, {value: any, slotIndex: number, expireTimestamp: number}>}
233
+ */
234
+ this._cache = new Map();
235
+ this._currentSlotIndex = 0;
236
+ /** @type {NodeJS.Timeout|undefined} */
237
+ this._timer = undefined;
238
+
239
+ // must start it immediately
240
+ this._startAutoEvict();
241
+ }
242
+
243
+ get tickInterval () {
244
+ return this._tickInterval
245
+ }
246
+
247
+ get tickCount () {
248
+ return this._tickCount
249
+ }
250
+
251
+ /**
252
+ * Max Time to Live, atom unit "millisecond"
253
+ * @returns {number}
254
+ */
255
+ get maxTtl () {
256
+ if (this._maxTtl == null) {
257
+ this._maxTtl = this._tickCount * this._tickInterval; // ms
258
+ }
259
+ return this._maxTtl
260
+ }
261
+
262
+ assertStarted () {
263
+ if (!this.autoEvictRunning) {
264
+ throw new Error(`${this.constructor.name} is not started`)
265
+ }
266
+ }
267
+
268
+ get autoEvictRunning () {
269
+ return this._timer != null
270
+ }
271
+
272
+ _startAutoEvict () {
273
+ this._timer = setInterval(() => this._advance(), this._tickInterval);
274
+ }
275
+
276
+ _stopAutoEvict () {
277
+ this._timer && clearInterval(this._timer);
278
+ this._timer = undefined;
279
+ }
280
+
281
+ _advance () {
282
+ const nextSlotIndex = (this._currentSlotIndex + 1) % this._tickCount;
283
+ const nextSlot = this._slots[nextSlotIndex];
284
+ const currentSlot = this._slots[this._currentSlotIndex];
285
+ this.emit(Event$1.Advance, this._currentSlotIndex, currentSlot, nextSlot);
286
+ if (currentSlot.size > 0) {
287
+ // clear current slot, and expired data
288
+ for (const key of currentSlot.keys()) {
289
+ const wrapped = this._cache.get(key);
290
+ this._cache.delete(key);
291
+ // @ts-ignore
292
+ this.emit(Event$1.Expired, key, wrapped.value, wrapped.expireTimestamp);
293
+ }
294
+ currentSlot.clear();
295
+ }
296
+ this._currentSlotIndex = nextSlotIndex;
297
+ }
298
+
299
+ start () {
300
+ if (this.autoEvictRunning) {
301
+ return
302
+ }
303
+ this._startAutoEvict();
304
+ }
305
+
306
+ stop () {
307
+ if (!this.autoEvictRunning) {
308
+ return
309
+ }
310
+ this._stopAutoEvict();
311
+ }
312
+
313
+ destroy () {
314
+ this._stopAutoEvict();
315
+ this.clear();
316
+ }
317
+
318
+ /**
319
+ * 1. "key" is not Nil
320
+ * 2. "value" is not Nil
321
+ * 3. "ttl" must > 0 && <= maxTtl, maxTtl = tickCount * tickInterval
322
+ * * > 0: CAN NOT go to past
323
+ * * < maxTtl: Wheel is Round, eg.
324
+ * * "Hour Wheel" has 24 Hour-Tick-Mark
325
+ * * CAN NOT distinguish 24 hours and 48 hours
326
+ * 4. Understand the difference between "ttl" and "timestamp"
327
+ * * TTL is Indexed from 1
328
+ * * eg. ttl = 60, should be stored in Slot[59]
329
+ * * Timestamp is Indexed from 0
330
+ * @param {any} key
331
+ * @param {any} value
332
+ * @param {number} ttl Time to Live, atom unit "millisecond"
333
+ * @returns {boolean}
334
+ */
335
+ set (key, value, ttl) {
336
+ this.assertStarted();
337
+ C.assertNotNil(key, 'key');
338
+ C.assertNotNil(value, 'value');
339
+ if (ttl <= 0) {
340
+ throw new Error(`Bad ttl "${ttl}", must > 0`)
341
+ }
342
+ if (ttl > this.maxTtl) {
343
+ throw new Error(`Bad ttl "${ttl}", must <= maxTtl(${this.maxTtl})`)
344
+ }
345
+ // delete existed firstly, Slot may be changed
346
+ if (this._cache.has(key)) {
347
+ this.delete(key);
348
+ }
349
+ // TimeUtils.timestamp() is built on Uptime, not affected by system time change
350
+ const expireTimestamp = mt.timestamp() + ttl;
351
+ // 1 tick at least
352
+ // ttl -1, eg. 1~60s should be stored in Slot[0]
353
+ const ticks = Math.floor((ttl - 1) / this._tickInterval);
354
+ const targetSlotIndex = (this._currentSlotIndex + ticks) % this._tickCount;
355
+ const storedItem = { value, slotIndex: targetSlotIndex, expireTimestamp };
356
+ this._slots[targetSlotIndex].set(key, storedItem);
357
+ this._cache.set(key, storedItem);
358
+ return true
359
+ }
360
+
361
+ /**
362
+ * @param {any} key
363
+ * @returns {boolean}
364
+ */
365
+ delete (key) {
366
+ const wrapped = this._cache.get(key);
367
+ if (!wrapped) {
368
+ return false
369
+ }
370
+ this._cache.delete(key);
371
+ // delete from target slot
372
+ // Time-Wheel may be Minute-Hour-Based, it's too long to leave it in slot
373
+ const { slotIndex } = wrapped;
374
+ const targetSlot = this._slots[slotIndex];
375
+ targetSlot.delete(key);
376
+ return true
377
+ }
378
+
379
+ /**
380
+ * Checks if the cache contains the specified key.
381
+ * @param {any} key - The key to check for existence in the cache.
382
+ * @returns {boolean} - True if the key exists in the cache, false otherwise.
383
+ */
384
+ has (key) {
385
+ const wrapped = this._cache.get(key);
386
+ if (!wrapped) {
387
+ return false
388
+ }
389
+ if (wrapped.expireTimestamp <= mt.timestamp()) {
390
+ this.delete(key);
391
+ this.emit('expired', key, wrapped.value, wrapped.expireTimestamp);
392
+ return false
393
+ }
394
+ return true
395
+ }
396
+
397
+ clear () {
398
+ this._cache.clear();
399
+ this._slots.forEach(slot => slot.clear());
400
+ this._currentSlotIndex = 0;
401
+ }
402
+
403
+ /**
404
+ *
405
+ * @param {any} key
406
+ * @returns {any}
407
+ */
408
+ get (key) {
409
+ const wrapped = this._cache.get(key);
410
+ if (!wrapped) {
411
+ return
412
+ }
413
+ if (wrapped.expireTimestamp <= mt.timestamp()) {
414
+ this.delete(key);
415
+ this.emit('expired', key, wrapped.value, wrapped.expireTimestamp);
416
+ return
417
+ }
418
+ return wrapped.value
419
+ }
420
+
421
+ size () {
422
+ // current slot may contain expired data
423
+ const slot = this._slots[this._currentSlotIndex];
424
+ if (slot.size > 0) {
425
+ const now = mt.timestamp();
426
+ for (const [key, { expireTimestamp }] of slot.entries()) {
427
+ if (expireTimestamp <= now) {
428
+ const wrapped = this._cache.get(key);
429
+ // @ts-ignore
430
+ this.emit('expired', key, wrapped.value, wrapped.expireTimestamp);
431
+ slot.delete(key);
432
+ this._cache.delete(key);
433
+ }
434
+ }
435
+ }
436
+ return this._cache.size
437
+ }
438
+ }
439
+
440
+ // 3rd
441
+ // internal
442
+ /**
443
+ * @typedef {number} Timestamp
444
+ */
445
+
446
+ // module vars
447
+ const SecondInMillisecond = 1_000; // 1s
448
+ const MinuteInMillisecond = 60_000; // 1m, 60s
449
+ const HourInMillisecond = 3_600_000; // 1h, 3600s
450
+ const Hour24InMillisecond = 86_400_000; // 24 hours, 86400s
451
+
452
+ const DowngradType = {
453
+ HourToSecond: 'hour->second',
454
+ HourToMinute: 'hour->minute',
455
+ MinuteToSecond: 'minute->second'
456
+ };
457
+
458
+ const Event = {
459
+ Downgrade: 'downgrade',
460
+ Expired: 'expired'
461
+ };
462
+
463
+ const MaxTtl = Hour24InMillisecond - 1;
464
+
465
+ /**
466
+ * Hour-Minute-Second-Time-Wheel Cache
467
+ * 1. TTL must be less than 24 Hours
468
+ */
469
+ class Hour24TimeWheelCache extends k {
470
+ static get DowngradType () {
471
+ return DowngradType
472
+ }
473
+
474
+ static get Event () {
475
+ return Event
476
+ }
477
+
478
+ /**
479
+ *
480
+ */
481
+ constructor () {
482
+ super();
483
+ /**
484
+ * Second Wheel:
485
+ * 1. 1 Tick Mark is 1 Second, is 1000 Milliseconds
486
+ * 2. 60 Slots, maximumly, SencondWheel can contain 60 Seconds
487
+ * 3. 60 Seconds should be stored in MinuteWheel
488
+ * * 01 00:00:00.XXX -> 00:00:59.XXX in Slot01 Index00
489
+ * * 02 00:00:01.XXX -> 00:00:01.XXX in Slot02 Index01
490
+ * * 60 00:00:59.XXX -> 00:00:59.XXX in Slot60 Index59
491
+ * @type {TimeWheelCache}
492
+ */
493
+ this._secondWheel = new TimeWheelCache({ tickInterval: SecondInMillisecond, tickCount: 60 });
494
+ /**
495
+ * Minute Wheel:
496
+ * 1. 1 Tick Mark is 1 Minute, is 60 * 1000 Milliseconds
497
+ * 2. 60 Slots, maximumly, MinuteWheel can contain 60 Minutes
498
+ * * 01 00:00:00 -> 00:00:59 in Slot01 Index00
499
+ * * 02 00:01:00 -> 00:01:59 in Slot02 Index01
500
+ * * 60 00:59:00 -> 00:59:59 in Slot60 Index59
501
+ * @type {TimeWheelCache}
502
+ */
503
+ this._minuteWheel = new TimeWheelCache({ tickInterval: MinuteInMillisecond, tickCount: 60 });
504
+ /**
505
+ * Hour Wheel:
506
+ * 1. 1 Tick Mark is 1 Hour, is 60 * 60 * 1000 Milliseconds
507
+ * 2. 24 Slots, maximumly, HourWheel can contain 23:59:59
508
+ * * 01 00:00:00 -> 00:59:59 in Slot01 Index00
509
+ * * 02 01:00:00 -> 01:59:59 in Slot02 Index01
510
+ * * 24 23:00:00 -> 23:59:59 in Slot23 Index23
511
+ * @type {TimeWheelCache}
512
+ */
513
+ this._hourWheel = new TimeWheelCache({ tickInterval: HourInMillisecond, tickCount: 24 });
514
+ /**
515
+ * @type {Map<any, TimeWheelCache>}
516
+ */
517
+ this._cache = new Map();
518
+ this._init();
519
+ }
520
+
521
+ /**
522
+ * Max Time to Live, atom unit "millisecond"
523
+ * @returns {number}
524
+ */
525
+ get maxTtl () {
526
+ return MaxTtl
527
+ }
528
+
529
+ get autoEvictRunning () {
530
+ return this._secondWheel.autoEvictRunning || this._minuteWheel.autoEvictRunning || this._hourWheel.autoEvictRunning
531
+ }
532
+
533
+ _init () {
534
+ /**
535
+ * 1. We don't store "Timetick: < 00:00:01 -> 00:59:59" into HourWheel, instead, we store them into MinuteWheel
536
+ * 2. When HourWeel advances, take Elements of next Slot, and downgrade them to MinuteWheel
537
+ */
538
+ this._hourWheel.on('advance', (/** @type {number} */currentSlotIndex,
539
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */currentSlot,
540
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */nextSlot) => {
541
+ for (const [key, { value, expireTimestamp }] of nextSlot) {
542
+ let leftTtl = expireTimestamp - mt.timestamp();
543
+ let downgradeType;
544
+ if (leftTtl <= 0) {
545
+ leftTtl = 1000;
546
+ downgradeType = DowngradType.HourToSecond;
547
+ this._secondWheel.set(key, value, leftTtl);
548
+ this.emit(Event.Downgrade, key, value, downgradeType, leftTtl);
549
+ } else if (leftTtl < MinuteInMillisecond) {
550
+ downgradeType = DowngradType.HourToSecond;
551
+ this._secondWheel.set(key, value, leftTtl);
552
+ this.emit(Event.Downgrade, key, value, downgradeType, leftTtl);
553
+ } else {
554
+ downgradeType = DowngradType.HourToMinute;
555
+ this._minuteWheel.set(key, value, leftTtl);
556
+ }
557
+ this._hourWheel.delete(key);
558
+ this.emit(Event.Downgrade, key, value, downgradeType, leftTtl);
559
+ }
560
+ });
561
+ /**
562
+ * 1. We don't store "Timetick: < 00:01:00 -> 00:01:59" into MinuteWheel, instead, we store them into SecondWheel
563
+ * 2. When MinuteWheel advances, take Elements of next Slot, and downgrade them to SecondWheel
564
+ */
565
+ this._minuteWheel.on('advance', (/** @type {number} */currentSlotIndex,
566
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */currentSlot,
567
+ /** @type {Map<any, {value:any, expireTimestamp: Timestamp}>} */nextSlot) => {
568
+ // console.log('advance', currentSlotIndex)
569
+ for (const [key, { value, expireTimestamp }] of nextSlot) {
570
+ let leftTtl = expireTimestamp - mt.timestamp();
571
+ if (leftTtl <= 0) {
572
+ leftTtl = 999; // ms
573
+ }
574
+ // console.log('downgrad', key, value, DowngradType.MinuteToSecond, leftTtl)
575
+ this._secondWheel.set(key, value, leftTtl);
576
+ this.emit(Event.Downgrade, key, value, DowngradType.MinuteToSecond, leftTtl);
577
+ this._minuteWheel.delete(key);
578
+ }
579
+ });
580
+
581
+ this._secondWheel.on('expired', (/** @type {any} */key, /** @type {any} */value, /** @type {Timestamp} */expireTimestamp) => {
582
+ this.emit(Event.Expired, key, value, expireTimestamp);
583
+ });
584
+ }
585
+
586
+ start () {
587
+ this._secondWheel.start();
588
+ this._minuteWheel.start();
589
+ this._hourWheel.start();
590
+ }
591
+
592
+ stop () {
593
+ this._secondWheel.stop();
594
+ this._minuteWheel.stop();
595
+ this._hourWheel.stop();
596
+ }
597
+
598
+ destroy () {
599
+ this._secondWheel.destroy();
600
+ this._minuteWheel.destroy();
601
+ this._hourWheel.destroy();
602
+ }
603
+
604
+ /**
605
+ * @param {any} key
606
+ * @param {any} value
607
+ * @param {number} ttl Time To Live, unit "millisencond", ttl should < 24 Hours
608
+ * @returns {boolean}
609
+ */
610
+ set (key, value, ttl) {
611
+ if (ttl <= 0) {
612
+ throw new Error(`Bad ttl "${ttl}", must > 0`)
613
+ }
614
+ /**
615
+ */
616
+ let wheel;
617
+ if (ttl < MinuteInMillisecond) { // Timetick: 00:00:01 -> 00:00:59
618
+ wheel = this._secondWheel;
619
+ } else if (ttl < HourInMillisecond) { // Timetick: < 00:00:01 -> 00:59:59
620
+ wheel = this._minuteWheel;
621
+ } else if (ttl < Hour24InMillisecond) { // Timetick: 01:00:00 -> 23:59:59
622
+ wheel = this._hourWheel;
623
+ } else {
624
+ throw new Error('"ttl" Should <= Millisencod Of 24 Hours')
625
+ }
626
+ wheel.set(key, value, ttl);
627
+ this._cache.set(key, wheel);
628
+ return true
629
+ }
630
+
631
+ /**
632
+ * @param {any} key
633
+ * @returns {boolean}
634
+ */
635
+ delete (key) {
636
+ const wheel = this._cache.get(key);
637
+ if (!wheel) {
638
+ return false
639
+ }
640
+ return wheel.delete(key)
641
+ }
642
+
643
+ /**
644
+ * Checks if the cache contains the specified key.
645
+ * @param {any} key - The key to check for existence in the cache.
646
+ * @returns {boolean} - True if the key exists in the cache, false otherwise.
647
+ */
648
+ has (key) {
649
+ const wheel = this._cache.get(key);
650
+ if (!wheel) { // No wheel, No Data
651
+ return false
652
+ }
653
+ if (wheel.has(key)) { // In Wheel, Has Data
654
+ return true
655
+ }
656
+ // current wheel is second-Wheel, Not existed or expired
657
+ if (wheel === this._secondWheel) {
658
+ return false
659
+ }
660
+ // Not In Wheel, It may be down-graded to lower wheel
661
+ if (wheel === this._hourWheel) { // current wheel is hour, check minute wheel
662
+ return this._minuteWheel.has(key)
663
+ } else if (wheel === this._minuteWheel) { // current wheel is minute, check second wheel
664
+ return this._secondWheel.has(key)
665
+ }
666
+ // It doesn't exist actually
667
+ return false
668
+ }
669
+
670
+ clear () {
671
+ this._secondWheel.clear();
672
+ this._minuteWheel.clear();
673
+ this._hourWheel.clear();
674
+ this._cache.clear();
675
+ }
676
+
677
+ /**
678
+ *
679
+ * @param {any} key
680
+ * @returns {any}
681
+ */
682
+ get (key) {
683
+ const wheel = this._cache.get(key);
684
+ if (!wheel) { // No wheel, No Data
685
+ return
686
+ }
687
+ const value = wheel.get(key);
688
+ if (value != null) { // In Wheel, Has Data
689
+ return value
690
+ }
691
+ // current wheel is second-Wheel, Not existed or expired
692
+ if (wheel === this._secondWheel) {
693
+ return
694
+ }
695
+ // Not In Wheel, It may be down-graded to lower wheel
696
+ if (wheel === this._hourWheel) { // current wheel is hour, check minute wheel
697
+ return this._minuteWheel.get(key)
698
+ } else if (wheel === this._minuteWheel) { // current wheel is minute, check second wheel
699
+ return this._secondWheel.get(key)
700
+ }
701
+ // It doesn't exist actually
702
+ return undefined
703
+ }
704
+
705
+ size () {
706
+ return this._hourWheel.size() + this._minuteWheel.size() + this._secondWheel.size()
707
+ }
708
+ }
709
+
710
+ var index = { CappedSet, TimeWheelCache, Hour24TimeWheelCache };
172
711
 
173
712
  exports.CappedSet = CappedSet;
713
+ exports.Hour24TimeWheelCache = Hour24TimeWheelCache;
714
+ exports.TimeWheelCache = TimeWheelCache;
174
715
  exports.default = index;
175
716
  //# sourceMappingURL=index-dev.cjs.map