Skip to content

Commit 63928aa

Browse files
authored
Add Cache.getAllPresent() (#609)
* Add Cache.getAllPresent() Signed-off-by: BoD <BoD@JRAF.org> * Add comment and avoid nesting indentation level Signed-off-by: BoD <BoD@JRAF.org> * Don't use buildMap Signed-off-by: BoD <BoD@JRAF.org> --------- Signed-off-by: BoD <BoD@JRAF.org>
1 parent b7aa329 commit 63928aa

File tree

5 files changed

+79
-2
lines changed

5 files changed

+79
-2
lines changed

cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/Cache.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@ interface Cache<Key : Any, Value : Any> {
1818

1919
/**
2020
* @return Map of the [Value] associated with each [Key] in [keys]. Returned map only contains entries already present in the cache.
21+
* The default implementation provided here throws a [NotImplementedError] to maintain backward compatibility for existing implementations.
2122
*/
2223
fun getAllPresent(keys: List<*>): Map<Key, Value>
2324

25+
/**
26+
* @return Map of the [Value] associated with each [Key] in the cache.
27+
*/
28+
fun getAllPresent(): Map<Key, Value> = throw NotImplementedError()
29+
2430
/**
2531
* Associates [value] with [key].
2632
* If the cache previously contained a value associated with [key], the old value is replaced by [value].

cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/LocalCache.kt

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,28 @@ internal class LocalCache<K : Any, V : Any>(builder: CacheBuilder<K, V>) {
13791379
}*/
13801380
}
13811381

1382+
fun activeEntries(): Map<K, V> {
1383+
// read-volatile
1384+
if (count.value == 0) return emptyMap()
1385+
reentrantLock.lock()
1386+
return try {
1387+
val activeMap = mutableMapOf<K, V>()
1388+
val table = table.value
1389+
for (i in 0 until table.size) {
1390+
var e = table[i]
1391+
while (e != null) {
1392+
if (e.valueReference?.isActive == true) {
1393+
activeMap[e.key] = e.valueReference?.get()!!
1394+
}
1395+
e = e.next
1396+
}
1397+
}
1398+
activeMap.ifEmpty { emptyMap() }
1399+
} finally {
1400+
reentrantLock.unlock()
1401+
}
1402+
}
1403+
13821404
init {
13831405
threshold = initialCapacity * 3 / 4 // 0.75
13841406
if (!map.customWeigher && threshold.toLong() == maxSegmentWeight) {
@@ -1660,6 +1682,14 @@ internal class LocalCache<K : Any, V : Any>(builder: CacheBuilder<K, V>) {
16601682
return segmentFor(hash).remove(key, hash)
16611683
}
16621684

1685+
fun getAllPresent(): Map<K, V> {
1686+
return buildMap {
1687+
for (segment in segments) {
1688+
segment?.let { putAll(it.activeEntries()) }
1689+
}
1690+
}
1691+
}
1692+
16631693
// Serialization Support
16641694
internal class LocalManualCache<K : Any, V : Any> private constructor(private val localCache: LocalCache<K, V>) :
16651695
Cache<K, V> {
@@ -1683,7 +1713,11 @@ internal class LocalCache<K : Any, V : Any>(builder: CacheBuilder<K, V>) {
16831713
}
16841714

16851715
override fun getAllPresent(keys: List<*>): Map<K, V> {
1686-
TODO("Not yet implemented")
1716+
return localCache.getAllPresent().filterKeys { it in keys }
1717+
}
1718+
1719+
override fun getAllPresent(): Map<K, V> {
1720+
return localCache.getAllPresent()
16871721
}
16881722

16891723
override fun invalidateAll(keys: List<K>) {

cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/StoreMultiCache.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ class StoreMultiCache<Id : Any, Key : StoreKey<Id>, Single : StoreData.Single<Id
8888
return map
8989
}
9090

91+
override fun getAllPresent(): Map<Key, Output> {
92+
return accessor.getAllPresent().mapKeys { (key, _) ->
93+
when (key) {
94+
is StoreKey.Collection<Id> -> key.cast()
95+
is StoreKey.Single<Id> -> key.cast()
96+
else -> throw UnsupportedOperationException(invalidKeyErrorMessage(key))
97+
}
98+
} as Map<Key, Output>
99+
}
100+
91101
override fun invalidateAll(keys: List<Key>) {
92102
keys.forEach { key -> invalidate(key) }
93103
}

cache/src/commonMain/kotlin/org/mobilenativefoundation/store/cache5/StoreMultiCacheAccessor.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,33 @@ class StoreMultiCacheAccessor<Id : Any, Collection : StoreData.Collection<Id, Si
5050
singlesCache.getIfPresent(key)
5151
}
5252

53+
/**
54+
* Retrieves all items from the cache.
55+
*
56+
* This operation is thread-safe.
57+
*/
58+
fun getAllPresent(): Map<StoreKey<Id>, Any> = synchronized(this) {
59+
val result = mutableMapOf<StoreKey<Id>, Any>()
60+
for (key in keys) {
61+
when (key) {
62+
is StoreKey.Single<Id> -> {
63+
val single = singlesCache.getIfPresent(key)
64+
if (single != null) {
65+
result[key] = single
66+
}
67+
}
68+
69+
is StoreKey.Collection<Id> -> {
70+
val collection = collectionsCache.getIfPresent(key)
71+
if (collection != null) {
72+
result[key] = collection
73+
}
74+
}
75+
}
76+
}
77+
result
78+
}
79+
5380
/**
5481
* Stores a collection of items in the cache and updates the key set.
5582
*

cache/src/commonTest/kotlin/org/mobilenativefoundation/store/cache5/CacheTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ class CacheTests {
2020
assertEquals("value", cache.getOrPut("key") { "value" })
2121
}
2222

23-
@Ignore // Not implemented yet
2423
@Test
2524
fun getAllPresent() {
2625
cache.put("key1", "value1")
2726
cache.put("key2", "value2")
2827
assertEquals(mapOf("key1" to "value1", "key2" to "value2"), cache.getAllPresent(listOf("key1", "key2")))
28+
assertEquals(mapOf("key1" to "value1", "key2" to "value2"), cache.getAllPresent())
2929
}
3030

3131
@Ignore // Not implemented yet

0 commit comments

Comments
 (0)