1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | import { CacheList, defaultConfig, getCurrTime, CacheObject } from './Utils';
|
15 |
|
16 | import { StorageCache } from './StorageCache';
|
17 | import { ICache, CacheConfig, CacheItem, CacheItemOptions } from './types';
|
18 | import { ConsoleLogger as Logger } from '@aws-amplify/core';
|
19 |
|
20 | const logger = new Logger('InMemoryCache');
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | export class InMemoryCacheClass extends StorageCache implements ICache {
|
31 | private cacheList: CacheList[];
|
32 | private curSizeInBytes: number;
|
33 | private maxPriority: number;
|
34 | private cacheSizeLimit: number;
|
35 |
|
36 | |
37 |
|
38 |
|
39 |
|
40 |
|
41 | constructor(config?: CacheConfig) {
|
42 | const cacheConfig = config
|
43 | ? Object.assign({}, defaultConfig, config)
|
44 | : defaultConfig;
|
45 | super(cacheConfig);
|
46 | logger.debug('now we start!');
|
47 | this.cacheList = [];
|
48 | this.curSizeInBytes = 0;
|
49 | this.maxPriority = 5;
|
50 |
|
51 | this.getItem = this.getItem.bind(this);
|
52 | this.setItem = this.setItem.bind(this);
|
53 | this.removeItem = this.removeItem.bind(this);
|
54 |
|
55 |
|
56 | for (let i = 0; i < this.maxPriority; i += 1) {
|
57 | this.cacheList[i] = new CacheList();
|
58 | }
|
59 | }
|
60 |
|
61 | |
62 |
|
63 |
|
64 |
|
65 |
|
66 | private _decreaseCurSizeInBytes(amount: number): void {
|
67 | this.curSizeInBytes -= amount;
|
68 | }
|
69 |
|
70 | |
71 |
|
72 |
|
73 |
|
74 |
|
75 | private _increaseCurSizeInBytes(amount: number): void {
|
76 | this.curSizeInBytes += amount;
|
77 | }
|
78 |
|
79 | |
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | private _isExpired(key: string): boolean {
|
87 | const text: string | null = CacheObject.getItem(key);
|
88 | const item: CacheItem = JSON.parse(text);
|
89 | if (getCurrTime() >= item.expires) {
|
90 | return true;
|
91 | }
|
92 | return false;
|
93 | }
|
94 |
|
95 | |
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | private _removeItem(prefixedKey: string, listIdx: number): void {
|
102 |
|
103 | this.cacheList[listIdx].removeItem(prefixedKey);
|
104 |
|
105 | this._decreaseCurSizeInBytes(
|
106 | JSON.parse(CacheObject.getItem(prefixedKey)).byteSize
|
107 | );
|
108 |
|
109 | CacheObject.removeItem(prefixedKey);
|
110 | }
|
111 |
|
112 | |
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | private _setItem(
|
121 | prefixedKey: string,
|
122 | item: CacheItem,
|
123 | listIdx: number
|
124 | ): void {
|
125 |
|
126 | this.cacheList[listIdx].insertItem(prefixedKey);
|
127 |
|
128 | this._increaseCurSizeInBytes(item.byteSize);
|
129 |
|
130 | CacheObject.setItem(prefixedKey, JSON.stringify(item));
|
131 | }
|
132 |
|
133 | |
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | private _isCacheFull(itemSize: number): boolean {
|
141 | return this.curSizeInBytes + itemSize > this.config.capacityInBytes;
|
142 | }
|
143 |
|
144 | |
145 |
|
146 |
|
147 |
|
148 |
|
149 | private containsKey(key: string): number {
|
150 | const prefixedKey: string = this.config.keyPrefix + key;
|
151 | for (let i = 0; i < this.maxPriority; i += 1) {
|
152 | if (this.cacheList[i].containsKey(prefixedKey)) {
|
153 | return i + 1;
|
154 | }
|
155 | }
|
156 | return -1;
|
157 | }
|
158 |
|
159 | |
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 | public setItem(
|
179 | key: string,
|
180 | value: object | string | number | boolean,
|
181 | options?: CacheItemOptions
|
182 | ): void {
|
183 | const prefixedKey: string = this.config.keyPrefix + key;
|
184 |
|
185 | if (
|
186 | prefixedKey === this.config.keyPrefix ||
|
187 | prefixedKey === this.cacheCurSizeKey
|
188 | ) {
|
189 | logger.warn(`Invalid key: should not be empty or 'CurSize'`);
|
190 | return;
|
191 | }
|
192 |
|
193 | if (typeof value === 'undefined') {
|
194 | logger.warn(`The value of item should not be undefined!`);
|
195 | return;
|
196 | }
|
197 |
|
198 | const cacheItemOptions: CacheItemOptions = {
|
199 | priority:
|
200 | options && options.priority !== undefined
|
201 | ? options.priority
|
202 | : this.config.defaultPriority,
|
203 | expires:
|
204 | options && options.expires !== undefined
|
205 | ? options.expires
|
206 | : this.config.defaultTTL + getCurrTime(),
|
207 | };
|
208 |
|
209 | if (cacheItemOptions.priority < 1 || cacheItemOptions.priority > 5) {
|
210 | logger.warn(
|
211 | `Invalid parameter: priority due to out or range. It should be within 1 and 5.`
|
212 | );
|
213 | return;
|
214 | }
|
215 |
|
216 | const item: CacheItem = this.fillCacheItem(
|
217 | prefixedKey,
|
218 | value,
|
219 | cacheItemOptions
|
220 | );
|
221 |
|
222 |
|
223 | if (item.byteSize > this.config.itemMaxSize) {
|
224 | logger.warn(
|
225 | `Item with key: ${key} you are trying to put into is too big!`
|
226 | );
|
227 | return;
|
228 | }
|
229 |
|
230 |
|
231 | const presentKeyPrio: number = this.containsKey(key);
|
232 | if (presentKeyPrio !== -1) {
|
233 | this._removeItem(prefixedKey, presentKeyPrio - 1);
|
234 | }
|
235 |
|
236 |
|
237 |
|
238 | let cacheListIdx = this.maxPriority - 1;
|
239 | while (this._isCacheFull(item.byteSize) && cacheListIdx >= 0) {
|
240 | if (!this.cacheList[cacheListIdx].isEmpty()) {
|
241 | const popedItemKey = this.cacheList[cacheListIdx].getLastItem();
|
242 | this._removeItem(popedItemKey, cacheListIdx);
|
243 | } else {
|
244 | cacheListIdx -= 1;
|
245 | }
|
246 | }
|
247 |
|
248 | this._setItem(prefixedKey, item, Number(item.priority) - 1);
|
249 | }
|
250 |
|
251 | |
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 | public getItem(key: string, options?: CacheItemOptions): any {
|
264 | let ret: string | null = null;
|
265 | const prefixedKey: string = this.config.keyPrefix + key;
|
266 |
|
267 | if (
|
268 | prefixedKey === this.config.keyPrefix ||
|
269 | prefixedKey === this.cacheCurSizeKey
|
270 | ) {
|
271 | logger.warn(`Invalid key: should not be empty or 'CurSize'`);
|
272 | return null;
|
273 | }
|
274 |
|
275 |
|
276 | const presentKeyPrio: number = this.containsKey(key);
|
277 | if (presentKeyPrio !== -1) {
|
278 | if (this._isExpired(prefixedKey)) {
|
279 |
|
280 | this._removeItem(prefixedKey, presentKeyPrio - 1);
|
281 | } else {
|
282 |
|
283 | ret = CacheObject.getItem(prefixedKey);
|
284 | const item: CacheItem = JSON.parse(ret);
|
285 | this.cacheList[item.priority - 1].refresh(prefixedKey);
|
286 | return item.data;
|
287 | }
|
288 | }
|
289 |
|
290 | if (options && options.callback !== undefined) {
|
291 | const val: object | string | number | boolean = options.callback();
|
292 | if (val !== null) {
|
293 | this.setItem(key, val, options);
|
294 | }
|
295 | return val;
|
296 | }
|
297 | return null;
|
298 | }
|
299 |
|
300 | |
301 |
|
302 |
|
303 |
|
304 |
|
305 | public removeItem(key: string): void {
|
306 | const prefixedKey: string = this.config.keyPrefix + key;
|
307 |
|
308 |
|
309 | const presentKeyPrio: number = this.containsKey(key);
|
310 | if (presentKeyPrio !== -1) {
|
311 | this._removeItem(prefixedKey, presentKeyPrio - 1);
|
312 | }
|
313 | }
|
314 |
|
315 | |
316 |
|
317 |
|
318 | public clear(): void {
|
319 | for (let i = 0; i < this.maxPriority; i += 1) {
|
320 | for (const key of this.cacheList[i].getKeys()) {
|
321 | this._removeItem(key, i);
|
322 | }
|
323 | }
|
324 | }
|
325 |
|
326 | |
327 |
|
328 |
|
329 | public getAllKeys(): string[] {
|
330 | const keys: string[] = [];
|
331 | for (let i = 0; i < this.maxPriority; i += 1) {
|
332 | for (const key of this.cacheList[i].getKeys()) {
|
333 | keys.push(key.substring(this.config.keyPrefix.length));
|
334 | }
|
335 | }
|
336 |
|
337 | return keys;
|
338 | }
|
339 |
|
340 | |
341 |
|
342 |
|
343 |
|
344 |
|
345 | public getCacheCurSize(): number {
|
346 | return this.curSizeInBytes;
|
347 | }
|
348 |
|
349 | |
350 |
|
351 |
|
352 |
|
353 | public createInstance(config: CacheConfig): ICache {
|
354 | return new InMemoryCacheClass(config);
|
355 | }
|
356 | }
|
357 |
|
358 | export const InMemoryCache: ICache = new InMemoryCacheClass();
|
359 |
|
360 |
|
361 |
|
362 | export default InMemoryCache;
|