diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LargeSoftCacheAddressExt.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LargeSoftCacheAddressExt.kt index 717289460..ff65202ab 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LargeSoftCacheAddressExt.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LargeSoftCacheAddressExt.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.model +import com.vitorpamplona.amethyst.commons.model.cache.LargeSoftCache import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.utils.cache.CacheCollectors diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index d792bcb3a..16ae2b961 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Stable import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.commons.model.Channel import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider +import com.vitorpamplona.amethyst.commons.model.cache.LargeSoftCache import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/model/LargeCacheAddressableFilterTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/model/LargeCacheAddressableFilterTest.kt index 965f5ba0c..984f1daac 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/model/LargeCacheAddressableFilterTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/model/LargeCacheAddressableFilterTest.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.model +import com.vitorpamplona.amethyst.commons.model.cache.LargeSoftCache import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.HexKey import junit.framework.TestCase.assertEquals diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ThreadAssembler.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ThreadAssembler.kt index 9fbd7c2fd..657922e00 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ThreadAssembler.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/ThreadAssembler.kt @@ -52,7 +52,7 @@ class ThreadAssembler( ?.getOrNull(1) if (markedAsRoot != null) { // Check to see if there is an error in the tag and the root has replies - val rootNote = cache.getNoteIfExists(markedAsRoot) as? Note + val rootNote = cache.getNoteIfExists(markedAsRoot) if (rootNote?.replyTo?.isEmpty() == true) { return cache.checkGetOrCreateNote(markedAsRoot) } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/ICacheProvider.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/ICacheProvider.kt index 2634196aa..e4b3ccd9a 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/ICacheProvider.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/ICacheProvider.kt @@ -57,7 +57,7 @@ interface ICacheProvider { * @param pubkey The user's public key in hex format * @return The User if exists in cache, null otherwise */ - fun getUserIfExists(pubkey: HexKey): Any? + fun getUserIfExists(pubkey: HexKey): User? /** * Counts users matching a predicate. @@ -75,7 +75,7 @@ interface ICacheProvider { * @param hexKey The note's ID in hex format * @return The Note if exists in cache, null otherwise */ - fun getNoteIfExists(hexKey: HexKey): Any? + fun getNoteIfExists(hexKey: HexKey): Note? /** * Gets an existing Note or creates a new one if it doesn't exist. @@ -123,7 +123,7 @@ interface ICacheProvider { fun findUsersStartingWith( prefix: String, limit: Int = 50, - ): List = emptyList() + ): List = emptyList() /** * Gets or creates a User by public key hex. @@ -132,7 +132,7 @@ interface ICacheProvider { * @param pubkey The user's public key in hex format * @return The User (existing or newly created) */ - fun getOrCreateUser(pubkey: HexKey): Any? + fun getOrCreateUser(pubkey: HexKey): User? fun justConsumeMyOwnEvent(event: Event): Boolean } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/ChatNewMessageState.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/ChatNewMessageState.kt index f9b1086bb..f308bf5b8 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/ChatNewMessageState.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/ChatNewMessageState.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.Stable import androidx.compose.ui.text.input.TextFieldValue import com.vitorpamplona.amethyst.commons.model.IAccount import com.vitorpamplona.amethyst.commons.model.Note -import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.references.references @@ -95,7 +94,7 @@ class ChatNewMessageState( if (currentRoom != null) { _recipientsMissingDmRelays.value = currentRoom.users.any { hexKey -> - val user = cache.getOrCreateUser(hexKey) as? User + val user = cache.getOrCreateUser(hexKey) user?.dmInboxRelays().isNullOrEmpty() } } else { @@ -141,7 +140,7 @@ class ChatNewMessageState( ) { val pTags = room.users.mapNotNull { hexKey -> - (cache.getOrCreateUser(hexKey) as? User)?.toPTag() + cache.getOrCreateUser(hexKey)?.toPTag() } val replyHint = _replyTo.value?.toEventHint() diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/SearchBarState.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/SearchBarState.kt index cdbf2e0d3..657758c47 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/SearchBarState.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/SearchBarState.kt @@ -88,8 +88,7 @@ class SearchBarState( .debounce(debounceMs) .onEach { query -> if (query.length >= 2 && _bech32Results.value.isEmpty()) { - @Suppress("UNCHECKED_CAST") - _cachedUserResults.value = cache.findUsersStartingWith(query, 20) as List + _cachedUserResults.value = cache.findUsersStartingWith(query, 20) } else { _cachedUserResults.value = emptyList() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LargeSoftCache.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/cache/LargeSoftCache.kt similarity index 89% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/model/LargeSoftCache.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/cache/LargeSoftCache.kt index bc7c6cce3..71bd3ba9c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LargeSoftCache.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/cache/LargeSoftCache.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.model +package com.vitorpamplona.amethyst.commons.model.cache import com.vitorpamplona.quartz.utils.cache.CacheOperations import java.lang.ref.WeakReference @@ -59,7 +59,7 @@ class LargeSoftCache : CacheOperations { /** * Puts an object into the cache with a specified key. - * The object is stored as a SoftReference. + * The object is stored as a WeakReference. * * @param key The key to associate with the object. * @param value The object to cache. @@ -105,19 +105,16 @@ class LargeSoftCache : CacheOperations { /** * Proactively cleans up the cache by removing entries whose weakly referenced - * objects have been garbage collected. While `get` handles cleanup on access, - * this method can be called periodically or when memory pressure is high. + * objects have been garbage collected. Single-pass iterator for efficiency. */ fun cleanUp() { - val keysToRemove = mutableMapOf>() - cache.forEach { key, softRef -> - if (softRef.get() == null) { - keysToRemove.put(key, softRef) + val iter = cache.entries.iterator() + while (iter.hasNext()) { + val entry = iter.next() + if (entry.value.get() == null) { + iter.remove() } } - keysToRemove.forEach { key, value -> - cache.remove(key, value) - } } override fun forEach(consumer: BiConsumer) {