Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/security/util/Cache.java
41159 views
1
/*
2
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.security.util;
27
28
import java.util.*;
29
import java.lang.ref.*;
30
31
/**
32
* Abstract base class and factory for caches. A cache is a key-value mapping.
33
* It has properties that make it more suitable for caching than a Map.
34
*
35
* The factory methods can be used to obtain two different implementations.
36
* They have the following properties:
37
*
38
* . keys and values reside in memory
39
*
40
* . keys and values must be non-null
41
*
42
* . maximum size. Replacements are made in LRU order.
43
*
44
* . optional lifetime, specified in seconds.
45
*
46
* . safe for concurrent use by multiple threads
47
*
48
* . values are held by either standard references or via SoftReferences.
49
* SoftReferences have the advantage that they are automatically cleared
50
* by the garbage collector in response to memory demand. This makes it
51
* possible to simple set the maximum size to a very large value and let
52
* the GC automatically size the cache dynamically depending on the
53
* amount of available memory.
54
*
55
* However, note that because of the way SoftReferences are implemented in
56
* HotSpot at the moment, this may not work perfectly as it clears them fairly
57
* eagerly. Performance may be improved if the Java heap size is set to larger
58
* value using e.g. java -ms64M -mx128M foo.Test
59
*
60
* Cache sizing: the memory cache is implemented on top of a LinkedHashMap.
61
* In its current implementation, the number of buckets (NOT entries) in
62
* (Linked)HashMaps is always a power of two. It is recommended to set the
63
* maximum cache size to value that uses those buckets fully. For example,
64
* if a cache with somewhere between 500 and 1000 entries is desired, a
65
* maximum size of 750 would be a good choice: try 1024 buckets, with a
66
* load factor of 0.75f, the number of entries can be calculated as
67
* buckets / 4 * 3. As mentioned above, with a SoftReference cache, it is
68
* generally reasonable to set the size to a fairly large value.
69
*
70
* @author Andreas Sterbenz
71
*/
72
public abstract class Cache<K,V> {
73
74
protected Cache() {
75
// empty
76
}
77
78
/**
79
* Return the number of currently valid entries in the cache.
80
*/
81
public abstract int size();
82
83
/**
84
* Remove all entries from the cache.
85
*/
86
public abstract void clear();
87
88
/**
89
* Add an entry to the cache.
90
*/
91
public abstract void put(K key, V value);
92
93
/**
94
* Get a value from the cache.
95
*/
96
public abstract V get(Object key);
97
98
/**
99
* Remove an entry from the cache.
100
*/
101
public abstract void remove(Object key);
102
103
/**
104
* Pull an entry from the cache.
105
*/
106
public abstract V pull(Object key);
107
108
/**
109
* Set the maximum size.
110
*/
111
public abstract void setCapacity(int size);
112
113
/**
114
* Set the timeout(in seconds).
115
*/
116
public abstract void setTimeout(int timeout);
117
118
/**
119
* accept a visitor
120
*/
121
public abstract void accept(CacheVisitor<K,V> visitor);
122
123
/**
124
* Return a new memory cache with the specified maximum size, unlimited
125
* lifetime for entries, with the values held by SoftReferences.
126
*/
127
public static <K,V> Cache<K,V> newSoftMemoryCache(int size) {
128
return new MemoryCache<>(true, size);
129
}
130
131
/**
132
* Return a new memory cache with the specified maximum size, the
133
* specified maximum lifetime (in seconds), with the values held
134
* by SoftReferences.
135
*/
136
public static <K,V> Cache<K,V> newSoftMemoryCache(int size, int timeout) {
137
return new MemoryCache<>(true, size, timeout);
138
}
139
140
/**
141
* Return a new memory cache with the specified maximum size, unlimited
142
* lifetime for entries, with the values held by standard references.
143
*/
144
public static <K,V> Cache<K,V> newHardMemoryCache(int size) {
145
return new MemoryCache<>(false, size);
146
}
147
148
/**
149
* Return a dummy cache that does nothing.
150
*/
151
@SuppressWarnings("unchecked")
152
public static <K,V> Cache<K,V> newNullCache() {
153
return (Cache<K,V>) NullCache.INSTANCE;
154
}
155
156
/**
157
* Return a new memory cache with the specified maximum size, the
158
* specified maximum lifetime (in seconds), with the values held
159
* by standard references.
160
*/
161
public static <K,V> Cache<K,V> newHardMemoryCache(int size, int timeout) {
162
return new MemoryCache<>(false, size, timeout);
163
}
164
165
/**
166
* Utility class that wraps a byte array and implements the equals()
167
* and hashCode() contract in a way suitable for Maps and caches.
168
*/
169
public static class EqualByteArray {
170
171
private final byte[] b;
172
private int hash;
173
174
public EqualByteArray(byte[] b) {
175
this.b = b;
176
}
177
178
public int hashCode() {
179
int h = hash;
180
if (h == 0 && b.length > 0) {
181
hash = h = Arrays.hashCode(b);
182
}
183
return h;
184
}
185
186
public boolean equals(Object obj) {
187
if (this == obj) {
188
return true;
189
}
190
if (obj instanceof EqualByteArray == false) {
191
return false;
192
}
193
EqualByteArray other = (EqualByteArray)obj;
194
return Arrays.equals(this.b, other.b);
195
}
196
}
197
198
public interface CacheVisitor<K,V> {
199
public void visit(Map<K,V> map);
200
}
201
202
}
203
204
class NullCache<K,V> extends Cache<K,V> {
205
206
static final Cache<Object,Object> INSTANCE = new NullCache<>();
207
208
private NullCache() {
209
// empty
210
}
211
212
public int size() {
213
return 0;
214
}
215
216
public void clear() {
217
// empty
218
}
219
220
public void put(K key, V value) {
221
// empty
222
}
223
224
public V get(Object key) {
225
return null;
226
}
227
228
public void remove(Object key) {
229
// empty
230
}
231
232
public V pull(Object key) {
233
return null;
234
}
235
236
public void setCapacity(int size) {
237
// empty
238
}
239
240
public void setTimeout(int timeout) {
241
// empty
242
}
243
244
public void accept(CacheVisitor<K,V> visitor) {
245
// empty
246
}
247
248
}
249
250
class MemoryCache<K,V> extends Cache<K,V> {
251
252
private static final float LOAD_FACTOR = 0.75f;
253
254
// XXXX
255
private static final boolean DEBUG = false;
256
257
private final Map<K, CacheEntry<K,V>> cacheMap;
258
private int maxSize;
259
private long lifetime;
260
private long nextExpirationTime = Long.MAX_VALUE;
261
262
// ReferenceQueue is of type V instead of Cache<K,V>
263
// to allow SoftCacheEntry to extend SoftReference<V>
264
private final ReferenceQueue<V> queue;
265
266
public MemoryCache(boolean soft, int maxSize) {
267
this(soft, maxSize, 0);
268
}
269
270
public MemoryCache(boolean soft, int maxSize, int lifetime) {
271
this.maxSize = maxSize;
272
this.lifetime = lifetime * 1000;
273
if (soft)
274
this.queue = new ReferenceQueue<>();
275
else
276
this.queue = null;
277
278
cacheMap = new LinkedHashMap<>(1, LOAD_FACTOR, true);
279
}
280
281
/**
282
* Empty the reference queue and remove all corresponding entries
283
* from the cache.
284
*
285
* This method should be called at the beginning of each public
286
* method.
287
*/
288
private void emptyQueue() {
289
if (queue == null) {
290
return;
291
}
292
int startSize = cacheMap.size();
293
while (true) {
294
@SuppressWarnings("unchecked")
295
CacheEntry<K,V> entry = (CacheEntry<K,V>)queue.poll();
296
if (entry == null) {
297
break;
298
}
299
K key = entry.getKey();
300
if (key == null) {
301
// key is null, entry has already been removed
302
continue;
303
}
304
CacheEntry<K,V> currentEntry = cacheMap.remove(key);
305
// check if the entry in the map corresponds to the expired
306
// entry. If not, readd the entry
307
if ((currentEntry != null) && (entry != currentEntry)) {
308
cacheMap.put(key, currentEntry);
309
}
310
}
311
if (DEBUG) {
312
int endSize = cacheMap.size();
313
if (startSize != endSize) {
314
System.out.println("*** Expunged " + (startSize - endSize)
315
+ " entries, " + endSize + " entries left");
316
}
317
}
318
}
319
320
/**
321
* Scan all entries and remove all expired ones.
322
*/
323
private void expungeExpiredEntries() {
324
emptyQueue();
325
if (lifetime == 0) {
326
return;
327
}
328
int cnt = 0;
329
long time = System.currentTimeMillis();
330
if (nextExpirationTime > time) {
331
return;
332
}
333
nextExpirationTime = Long.MAX_VALUE;
334
for (Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
335
t.hasNext(); ) {
336
CacheEntry<K,V> entry = t.next();
337
if (entry.isValid(time) == false) {
338
t.remove();
339
cnt++;
340
} else if (nextExpirationTime > entry.getExpirationTime()) {
341
nextExpirationTime = entry.getExpirationTime();
342
}
343
}
344
if (DEBUG) {
345
if (cnt != 0) {
346
System.out.println("Removed " + cnt
347
+ " expired entries, remaining " + cacheMap.size());
348
}
349
}
350
}
351
352
public synchronized int size() {
353
expungeExpiredEntries();
354
return cacheMap.size();
355
}
356
357
public synchronized void clear() {
358
if (queue != null) {
359
// if this is a SoftReference cache, first invalidate() all
360
// entries so that GC does not have to enqueue them
361
for (CacheEntry<K,V> entry : cacheMap.values()) {
362
entry.invalidate();
363
}
364
while (queue.poll() != null) {
365
// empty
366
}
367
}
368
cacheMap.clear();
369
}
370
371
public synchronized void put(K key, V value) {
372
emptyQueue();
373
long expirationTime = (lifetime == 0) ? 0 :
374
System.currentTimeMillis() + lifetime;
375
if (expirationTime < nextExpirationTime) {
376
nextExpirationTime = expirationTime;
377
}
378
CacheEntry<K,V> newEntry = newEntry(key, value, expirationTime, queue);
379
CacheEntry<K,V> oldEntry = cacheMap.put(key, newEntry);
380
if (oldEntry != null) {
381
oldEntry.invalidate();
382
return;
383
}
384
if (maxSize > 0 && cacheMap.size() > maxSize) {
385
expungeExpiredEntries();
386
if (cacheMap.size() > maxSize) { // still too large?
387
Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
388
CacheEntry<K,V> lruEntry = t.next();
389
if (DEBUG) {
390
System.out.println("** Overflow removal "
391
+ lruEntry.getKey() + " | " + lruEntry.getValue());
392
}
393
t.remove();
394
lruEntry.invalidate();
395
}
396
}
397
}
398
399
public synchronized V get(Object key) {
400
emptyQueue();
401
CacheEntry<K,V> entry = cacheMap.get(key);
402
if (entry == null) {
403
return null;
404
}
405
long time = (lifetime == 0) ? 0 : System.currentTimeMillis();
406
if (entry.isValid(time) == false) {
407
if (DEBUG) {
408
System.out.println("Ignoring expired entry");
409
}
410
cacheMap.remove(key);
411
return null;
412
}
413
return entry.getValue();
414
}
415
416
public synchronized void remove(Object key) {
417
emptyQueue();
418
CacheEntry<K,V> entry = cacheMap.remove(key);
419
if (entry != null) {
420
entry.invalidate();
421
}
422
}
423
424
public synchronized V pull(Object key) {
425
emptyQueue();
426
CacheEntry<K,V> entry = cacheMap.remove(key);
427
if (entry == null) {
428
return null;
429
}
430
431
long time = (lifetime == 0) ? 0 : System.currentTimeMillis();
432
if (entry.isValid(time)) {
433
V value = entry.getValue();
434
entry.invalidate();
435
return value;
436
} else {
437
if (DEBUG) {
438
System.out.println("Ignoring expired entry");
439
}
440
return null;
441
}
442
}
443
444
public synchronized void setCapacity(int size) {
445
expungeExpiredEntries();
446
if (size > 0 && cacheMap.size() > size) {
447
Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
448
for (int i = cacheMap.size() - size; i > 0; i--) {
449
CacheEntry<K,V> lruEntry = t.next();
450
if (DEBUG) {
451
System.out.println("** capacity reset removal "
452
+ lruEntry.getKey() + " | " + lruEntry.getValue());
453
}
454
t.remove();
455
lruEntry.invalidate();
456
}
457
}
458
459
maxSize = size > 0 ? size : 0;
460
461
if (DEBUG) {
462
System.out.println("** capacity reset to " + size);
463
}
464
}
465
466
public synchronized void setTimeout(int timeout) {
467
emptyQueue();
468
lifetime = timeout > 0 ? timeout * 1000L : 0L;
469
470
if (DEBUG) {
471
System.out.println("** lifetime reset to " + timeout);
472
}
473
}
474
475
// it is a heavyweight method.
476
public synchronized void accept(CacheVisitor<K,V> visitor) {
477
expungeExpiredEntries();
478
Map<K,V> cached = getCachedEntries();
479
480
visitor.visit(cached);
481
}
482
483
private Map<K,V> getCachedEntries() {
484
Map<K,V> kvmap = new HashMap<>(cacheMap.size());
485
486
for (CacheEntry<K,V> entry : cacheMap.values()) {
487
kvmap.put(entry.getKey(), entry.getValue());
488
}
489
490
return kvmap;
491
}
492
493
protected CacheEntry<K,V> newEntry(K key, V value,
494
long expirationTime, ReferenceQueue<V> queue) {
495
if (queue != null) {
496
return new SoftCacheEntry<>(key, value, expirationTime, queue);
497
} else {
498
return new HardCacheEntry<>(key, value, expirationTime);
499
}
500
}
501
502
private static interface CacheEntry<K,V> {
503
504
boolean isValid(long currentTime);
505
506
void invalidate();
507
508
K getKey();
509
510
V getValue();
511
512
long getExpirationTime();
513
}
514
515
private static class HardCacheEntry<K,V> implements CacheEntry<K,V> {
516
517
private K key;
518
private V value;
519
private long expirationTime;
520
521
HardCacheEntry(K key, V value, long expirationTime) {
522
this.key = key;
523
this.value = value;
524
this.expirationTime = expirationTime;
525
}
526
527
public K getKey() {
528
return key;
529
}
530
531
public V getValue() {
532
return value;
533
}
534
535
public long getExpirationTime() {
536
return expirationTime;
537
}
538
539
public boolean isValid(long currentTime) {
540
boolean valid = (currentTime <= expirationTime);
541
if (valid == false) {
542
invalidate();
543
}
544
return valid;
545
}
546
547
public void invalidate() {
548
key = null;
549
value = null;
550
expirationTime = -1;
551
}
552
}
553
554
private static class SoftCacheEntry<K,V>
555
extends SoftReference<V>
556
implements CacheEntry<K,V> {
557
558
private K key;
559
private long expirationTime;
560
561
SoftCacheEntry(K key, V value, long expirationTime,
562
ReferenceQueue<V> queue) {
563
super(value, queue);
564
this.key = key;
565
this.expirationTime = expirationTime;
566
}
567
568
public K getKey() {
569
return key;
570
}
571
572
public V getValue() {
573
return get();
574
}
575
576
public long getExpirationTime() {
577
return expirationTime;
578
}
579
580
public boolean isValid(long currentTime) {
581
boolean valid = (currentTime <= expirationTime) && (get() != null);
582
if (valid == false) {
583
invalidate();
584
}
585
return valid;
586
}
587
588
public void invalidate() {
589
clear();
590
key = null;
591
expirationTime = -1;
592
}
593
}
594
595
}
596
597