-
Notifications
You must be signed in to change notification settings - Fork 214
/
index.js
190 lines (156 loc) · 4.25 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
'use strict';
function Cache () {
var _cache = Object.create(null);
var _hitCount = 0;
var _missCount = 0;
var _size = 0;
var _debug = false;
this.put = function(key, value, time, timeoutCallback) {
if (_debug) {
console.log('caching: %s = %j (@%s)', key, value, time);
}
if (typeof time !== 'undefined' && (typeof time !== 'number' || isNaN(time) || time <= 0)) {
throw new Error('Cache timeout must be a positive number');
} else if (typeof timeoutCallback !== 'undefined' && typeof timeoutCallback !== 'function') {
throw new Error('Cache timeout callback must be a function');
}
var oldRecord = _cache[key];
if (oldRecord) {
clearTimeout(oldRecord.timeout);
} else {
_size++;
}
var record = {
value: value,
expire: time + Date.now()
};
if (!isNaN(record.expire)) {
record.timeout = setTimeout(function() {
_del(key);
if (timeoutCallback) {
timeoutCallback(key, value);
}
}.bind(this), time);
}
_cache[key] = record;
return value;
};
this.del = function(key) {
var canDelete = true;
var oldRecord = _cache[key];
if (oldRecord) {
clearTimeout(oldRecord.timeout);
if (!isNaN(oldRecord.expire) && oldRecord.expire < Date.now()) {
canDelete = false;
}
} else {
canDelete = false;
}
if (canDelete) {
_del(key);
}
return canDelete;
};
function _del(key){
_size--;
delete _cache[key];
}
this.clear = function() {
for (var key in _cache) {
clearTimeout(_cache[key].timeout);
}
_size = 0;
_cache = Object.create(null);
if (_debug) {
_hitCount = 0;
_missCount = 0;
}
};
this.get = function(key) {
var data = _cache[key];
if (typeof data != "undefined") {
if (isNaN(data.expire) || data.expire >= Date.now()) {
if (_debug) _hitCount++;
return data.value;
} else {
// free some space
if (_debug) _missCount++;
_size--;
delete _cache[key];
}
} else if (_debug) {
_missCount++;
}
return null;
};
this.size = function() {
return _size;
};
this.memsize = function() {
var size = 0,
key;
for (key in _cache) {
size++;
}
return size;
};
this.debug = function(bool) {
_debug = bool;
};
this.hits = function() {
return _hitCount;
};
this.misses = function() {
return _missCount;
};
this.keys = function() {
return Object.keys(_cache);
};
this.exportJson = function() {
var plainJsCache = {};
// Discard the `timeout` property.
// Note: JSON doesn't support `NaN`, so convert it to `'NaN'`.
for (var key in _cache) {
var record = _cache[key];
plainJsCache[key] = {
value: record.value,
expire: record.expire || 'NaN',
};
}
return JSON.stringify(plainJsCache);
};
this.importJson = function(jsonToImport, options) {
var cacheToImport = JSON.parse(jsonToImport);
var currTime = Date.now();
var skipDuplicates = options && options.skipDuplicates;
for (var key in cacheToImport) {
if (cacheToImport.hasOwnProperty(key)) {
if (skipDuplicates) {
var existingRecord = _cache[key];
if (existingRecord) {
if (_debug) {
console.log('Skipping duplicate imported key \'%s\'', key);
}
continue;
}
}
var record = cacheToImport[key];
// record.expire could be `'NaN'` if no expiry was set.
// Try to subtract from it; a string minus a number is `NaN`, which is perfectly fine here.
var remainingTime = record.expire - currTime;
if (remainingTime <= 0) {
// Delete any record that might exist with the same key, since this key is expired.
this.del(key);
continue;
}
// Remaining time must now be either positive or `NaN`,
// but `put` will throw an error if we try to give it `NaN`.
remainingTime = remainingTime > 0 ? remainingTime : undefined;
this.put(key, record.value, remainingTime);
}
}
return this.size();
};
}
module.exports = new Cache();
module.exports.Cache = Cache;