From 1f38c59e66f39cc24c697879482a3b947e340ee4 Mon Sep 17 00:00:00 2001 From: hope Date: Thu, 6 Apr 2023 17:19:02 +0300 Subject: [PATCH 01/14] cache rewrite? [Needs folder restructure] --- .../dev/kord/cache/api/api/EntryCache.kt | 58 +++++++++++++++++++ .../kotlin/dev/kord/cache/api/api/Index.kt | 14 +++++ .../kotlin/dev/kord/cache/api/api/Relation.kt | 41 +++++++++++++ .../dev/kord/cache/api/api/TypedCache.kt | 23 ++++++++ .../dev/kord/cache/api/api/TypedSetCache.kt | 44 ++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/api/EntryCache.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/api/Relation.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedCache.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/EntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/api/EntryCache.kt new file mode 100644 index 0000000..c9d86a4 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/api/EntryCache.kt @@ -0,0 +1,58 @@ +package dev.kord.core.cache.api + +/** + * A cache that associates a [Value] with an [Index]. + */ +public interface EntryCache { + + /** + * Returns the [Value] associated with the specified [Index] key in this cache, or null if there is no + * cached value for the given [Index]. + */ + public fun get(key: Index): Value? + + /** + * Returns the first [Value] that satisfies the given [transform] function, or null if none is found. + */ + public fun get(transform: (Value) -> Boolean): Value? + + /** + * Associates the given [value] with a new [Index] in this cache. If the cache previously contained a + * value associated with the same [Index], the old value is replaced by [value]. + * Returns the newly generated [Index] for [value]. + */ + public fun put(value: Value): Index + + /** + * Discards any cached [Value] that satisfies the given [transform] function. + */ + public fun discardIf(transform: (Value) -> Boolean) + + /** + * Discards the cached [Value] associated with the given [Index], if it exists. + * Returns the discarded [Value], or null if it was not found. + */ + public fun discard(index: Index): Value? + + /** + * Discards all entries in the cache. + */ + public fun discardAll() + + /** + * Adds an observer cache to this cache. Whenever a value is discarded from this cache, the + * observer cache will also discard any values that are related to it. + */ + public fun relatesTo(other: EntryCache, handler: RelationHandler) + + /** + * Returns the [Relation] object for this cache, which contains information about any other + * caches that are observing this one. + */ + public fun getRelations(): Relation + + /** + * Returns a defensive copy of the cache entries as a [Map] of [Index] to [Value]. + */ + public fun asMap(): Map +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt new file mode 100644 index 0000000..148f25f --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt @@ -0,0 +1,14 @@ +package dev.kord.core.cache.api + +/** + * A unique identifier for a cached value. + * It implements [Comparable] to allow for indexing and sorting. + */ +public interface Index : Comparable { + + /** + * Computes and returns the hash code for this index. + */ + override fun hashCode(): Int + +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/Relation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/api/Relation.kt new file mode 100644 index 0000000..8d8977f --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/api/Relation.kt @@ -0,0 +1,41 @@ +package dev.kord.core.cache.api + +/** + * A typealias for a function that determines the relationship between two entities of type `T` and `R` + * in a bi-directional link. + * The `value` parameter represents the entity of type `T` and the `friend` parameter represents the entity of type `R`. + * The function should return `true` if the two entities are related, or `false` otherwise. + */ +public typealias RelationHandler = (value: T, friend: R) -> Boolean + +/** + * A `Relation` is a bi-directional link between two entities of type `T` and `R`. + * A `Relation` is defined by a set of `RelationHandler`s which determine the relationship + * between two entities. + * + * @param T the type of the first entity in the relation. + */ +public interface Relation { + + /** + * Removes the given [value] from all caches related to this relation. + * + * @param value the entity to remove from the relation. + */ + public fun discard(value: T) + + /** + * Returns the typed cache associated with this relation. + * + * @return the typed cache associated with this relation. + */ + public fun getCaches(): TypedCache + + /** + * Associates an [EntryCache] of type `T` with this relation. + * + * @param cache the cache to associate with this relation. + * @param handler the `RelationHandler` that defines the relationship between entities of type `T` and `R`. + */ + public fun to(cache: EntryCache, handler: RelationHandler) +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedCache.kt new file mode 100644 index 0000000..03016cf --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedCache.kt @@ -0,0 +1,23 @@ +package dev.kord.core.cache.api + +/** + * An interface representing a cache that can store multiple types of data by associating them with their types. + */ +public interface TypedCache { + + /** + * Returns the [EntryCache] associated with the type [T]. + */ + public fun getType(): EntryCache + + + /** + * Adds the given [EntryCache] to this [TypedCache]. + */ + public fun putCache(cache: EntryCache) + + /** + * Returns a [Set] of all the [EntryCache]s stored in this [TypedCache]. + */ + public fun toSet(): Set> +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt new file mode 100644 index 0000000..2106805 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt @@ -0,0 +1,44 @@ +package dev.kord.core.cache.api + +/** + * An implementation of [TypedCache] that stores a set of [EntryCache]s. + */ +public class TypedSetCache : TypedCache { + + /** + * The set of [EntryCache]s stored in this [TypedSetCache]. + */ + private val types: MutableSet> = mutableSetOf() + + /** + * Returns the [EntryCache] for the specified type, or `null` if it is not found. + */ + private fun getTypeOrNull(): EntryCache? { + return toSet().filterIsInstance>().singleOrNull() + } + + /** + * Returns the [EntryCache] for the specified type, throwing an exception if it is not found. + * @throws IllegalArgumentException if a cache for the specified type is not found. + */ + override fun getType(): EntryCache { + val instance = getTypeOrNull() + require(instance != null) { "Cache for type T not found" } + return instance + } + + /** + * Adds the specified [EntryCache] to the [TypedSetCache]. + * @throws IllegalArgumentException if a cache for the same type already exists. + */ + override fun putCache(cache: EntryCache) { + require(getTypeOrNull() == null) { "There must be only one cache of the same type" } + @Suppress("UNCHECKED_CAST") + types.add(cache as EntryCache) + } + + /** + * Returns a set of all [EntryCache]s stored in this [TypedSetCache]. + */ + public override fun toSet(): Set> = types +} From 92006ddf2097098ab5f8344a3c1e6c4f43d75ce1 Mon Sep 17 00:00:00 2001 From: hope Date: Thu, 6 Apr 2023 23:37:56 +0300 Subject: [PATCH 02/14] yeet these suspends --- .../kotlin/dev/kord/cache/api/api/Index.kt | 14 ------ .../dev/kord/cache/api/api/TypedSetCache.kt | 44 ------------------- .../api/{api => observables}/EntryCache.kt | 20 ++++----- .../dev/kord/cache/api/observables/Index.kt | 5 +++ .../api/{api => observables}/Relation.kt | 12 ++--- .../api/{api => observables}/TypedCache.kt | 8 ++-- 6 files changed, 22 insertions(+), 81 deletions(-) delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt rename api/src/commonMain/kotlin/dev/kord/cache/api/{api => observables}/EntryCache.kt (72%) create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt rename api/src/commonMain/kotlin/dev/kord/cache/api/{api => observables}/Relation.kt (77%) rename api/src/commonMain/kotlin/dev/kord/cache/api/{api => observables}/TypedCache.kt (64%) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt deleted file mode 100644 index 148f25f..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/api/Index.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.kord.core.cache.api - -/** - * A unique identifier for a cached value. - * It implements [Comparable] to allow for indexing and sorting. - */ -public interface Index : Comparable { - - /** - * Computes and returns the hash code for this index. - */ - override fun hashCode(): Int - -} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt deleted file mode 100644 index 2106805..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedSetCache.kt +++ /dev/null @@ -1,44 +0,0 @@ -package dev.kord.core.cache.api - -/** - * An implementation of [TypedCache] that stores a set of [EntryCache]s. - */ -public class TypedSetCache : TypedCache { - - /** - * The set of [EntryCache]s stored in this [TypedSetCache]. - */ - private val types: MutableSet> = mutableSetOf() - - /** - * Returns the [EntryCache] for the specified type, or `null` if it is not found. - */ - private fun getTypeOrNull(): EntryCache? { - return toSet().filterIsInstance>().singleOrNull() - } - - /** - * Returns the [EntryCache] for the specified type, throwing an exception if it is not found. - * @throws IllegalArgumentException if a cache for the specified type is not found. - */ - override fun getType(): EntryCache { - val instance = getTypeOrNull() - require(instance != null) { "Cache for type T not found" } - return instance - } - - /** - * Adds the specified [EntryCache] to the [TypedSetCache]. - * @throws IllegalArgumentException if a cache for the same type already exists. - */ - override fun putCache(cache: EntryCache) { - require(getTypeOrNull() == null) { "There must be only one cache of the same type" } - @Suppress("UNCHECKED_CAST") - types.add(cache as EntryCache) - } - - /** - * Returns a set of all [EntryCache]s stored in this [TypedSetCache]. - */ - public override fun toSet(): Set> = types -} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/EntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt similarity index 72% rename from api/src/commonMain/kotlin/dev/kord/cache/api/api/EntryCache.kt rename to api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt index c9d86a4..05e9852 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/api/EntryCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt @@ -1,4 +1,4 @@ -package dev.kord.core.cache.api +package dev.kord.cache.api.observables /** * A cache that associates a [Value] with an [Index]. @@ -9,50 +9,50 @@ public interface EntryCache { * Returns the [Value] associated with the specified [Index] key in this cache, or null if there is no * cached value for the given [Index]. */ - public fun get(key: Index): Value? + public suspend fun get(key: Index): Value? /** * Returns the first [Value] that satisfies the given [transform] function, or null if none is found. */ - public fun get(transform: (Value) -> Boolean): Value? + public suspend fun get(transform: (Value) -> Boolean): Value? /** * Associates the given [value] with a new [Index] in this cache. If the cache previously contained a * value associated with the same [Index], the old value is replaced by [value]. * Returns the newly generated [Index] for [value]. */ - public fun put(value: Value): Index + public suspend fun put(value: Value): Index /** * Discards any cached [Value] that satisfies the given [transform] function. */ - public fun discardIf(transform: (Value) -> Boolean) + public suspend fun discardIf(transform: (Value) -> Boolean) /** * Discards the cached [Value] associated with the given [Index], if it exists. * Returns the discarded [Value], or null if it was not found. */ - public fun discard(index: Index): Value? + public suspend fun discard(index: Index): Value? /** * Discards all entries in the cache. */ - public fun discardAll() + public suspend fun discardAll() /** * Adds an observer cache to this cache. Whenever a value is discarded from this cache, the * observer cache will also discard any values that are related to it. */ - public fun relatesTo(other: EntryCache, handler: RelationHandler) + public suspend fun relatesTo(other: EntryCache, handler: RelationHandler) /** * Returns the [Relation] object for this cache, which contains information about any other * caches that are observing this one. */ - public fun getRelations(): Relation + public suspend fun getRelations(): Relation /** * Returns a defensive copy of the cache entries as a [Map] of [Index] to [Value]. */ - public fun asMap(): Map + public suspend fun asMap(): Map } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt new file mode 100644 index 0000000..f45c3cd --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt @@ -0,0 +1,5 @@ +package dev.kord.cache.api.observables + +interface Index: Comparable { + override fun hashCode(): Int +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/Relation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt similarity index 77% rename from api/src/commonMain/kotlin/dev/kord/cache/api/api/Relation.kt rename to api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt index 8d8977f..0888100 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/api/Relation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt @@ -1,4 +1,4 @@ -package dev.kord.core.cache.api +package dev.kord.cache.api.observables /** * A typealias for a function that determines the relationship between two entities of type `T` and `R` @@ -22,14 +22,8 @@ public interface Relation { * * @param value the entity to remove from the relation. */ - public fun discard(value: T) + public suspend fun discard(value: T) - /** - * Returns the typed cache associated with this relation. - * - * @return the typed cache associated with this relation. - */ - public fun getCaches(): TypedCache /** * Associates an [EntryCache] of type `T` with this relation. @@ -37,5 +31,5 @@ public interface Relation { * @param cache the cache to associate with this relation. * @param handler the `RelationHandler` that defines the relationship between entities of type `T` and `R`. */ - public fun to(cache: EntryCache, handler: RelationHandler) + public suspend fun to(cache: EntryCache, handler: RelationHandler) } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt similarity index 64% rename from api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedCache.kt rename to api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt index 03016cf..f0196df 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/api/TypedCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt @@ -1,4 +1,4 @@ -package dev.kord.core.cache.api +package dev.kord.cache.api.observables /** * An interface representing a cache that can store multiple types of data by associating them with their types. @@ -8,16 +8,16 @@ public interface TypedCache { /** * Returns the [EntryCache] associated with the type [T]. */ - public fun getType(): EntryCache + public suspend fun getType(): EntryCache /** * Adds the given [EntryCache] to this [TypedCache]. */ - public fun putCache(cache: EntryCache) + public suspend fun putCache(cache: EntryCache) /** * Returns a [Set] of all the [EntryCache]s stored in this [TypedCache]. */ - public fun toSet(): Set> + public suspend fun toSet(): Set> } From aeee5312e3f35aeb558052202f76af6f887f0e4d Mon Sep 17 00:00:00 2001 From: hope Date: Sun, 9 Apr 2023 14:54:29 +0300 Subject: [PATCH 03/14] Add implementations --- .../cache/api/observables/BasicRelation.kt | 27 ++++++ .../kord/cache/api/observables/DataCache.kt | 44 +++++++++ .../kord/cache/api/observables/IndexCache.kt | 92 +++++++++++++++++++ .../cache/api/observables/IndexFactory.kt | 5 + .../kord/cache/api/observables/TypedCache.kt | 2 +- 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt new file mode 100644 index 0000000..4825b36 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt @@ -0,0 +1,27 @@ +package dev.kord.cache.api.observables + +class BasicRelation : Relation { + private val relations = mutableSetOf>() + private val caches = mutableSetOf>() + + override suspend fun discard(value: T) { + caches.onEach { other -> other.discardIf { friend -> + relations.any { relateTo -> relateTo(value, friend) } + } + } + } + + override suspend fun to(cache: EntryCache, handler: RelationHandler) { + caches.add(cache as EntryCache<*>) + relations.add(safe(handler)) + } + + private fun safe(relationHandler: RelationHandler): RelationHandler { + return obj@{ value: T, friend: Any -> + @Suppress("UNCHECKED_CAST") + val safeCast = friend as? R + if(safeCast != null) return@obj relationHandler(value, safeCast) + else return@obj false + } + } +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt new file mode 100644 index 0000000..aac79a9 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt @@ -0,0 +1,44 @@ +package dev.kord.cache.api.observables + +/** + * An implementation of [TypedCache] that stores a set of [EntryCache]s. + */ +public class DataCache : TypedCache { + + /** + * The set of [EntryCache]s stored in this [TypedSetCache]. + */ + private val types: MutableSet> = mutableSetOf() + + /** + * Returns the [EntryCache] for the specified type, or `null` if it is not found. + */ + private suspend fun getTypeOrNull(): EntryCache? { + return toSet().filterIsInstance>().singleOrNull() + } + + /** + * Returns the [EntryCache] for the specified type, throwing an exception if it is not found. + * @throws IllegalArgumentException if a cache for the specified type is not found. + */ + override suspend fun getType(): EntryCache { + val instance = getTypeOrNull() + require(instance != null) { "Cache for type T not found" } + return instance + } + + /** + * Adds the specified [EntryCache] to the [TypedSetCache]. + * @throws IllegalArgumentException if a cache for the same type already exists. + */ + override suspend fun putCache(cache: EntryCache) { + require(getTypeOrNull() == null) { "There must be only one cache of the same type" } + @Suppress("UNCHECKED_CAST") + types.add(cache as EntryCache) + } + + /** + * Returns a set of all [EntryCache]s stored in this [TypedSetCache]. + */ + public override suspend fun toSet(): Set> = types.toSet() +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt new file mode 100644 index 0000000..bfd8d99 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt @@ -0,0 +1,92 @@ +package dev.kord.cache.api.observables + +/** + * An implementation of the [EntryCache] interface that uses an index system based on the [IndexFactory] provided. + * This implementation is thread-safe. + * + * @param indexFactory The factory that generates indices for values stored in this cache. + * @param relation The relation between this cache and other caches, used to discard related entries. + */ +public class IndexCache( + public val relation: Relation, + public val indexFactory: IndexFactory +) : EntryCache { + + private val source: HashMap = hashMapOf() + + /** + * Gets the value associated with the given [key]. + * + * @return The value associated with the given [key], or `null` if not found. + */ + override suspend fun get(key: Index): Value? { + return source[key] + } + + /** + * Gets the first value that matches the [transform] function. + * + * @return The first value that matches the [transform] function, or `null` if not found. + */ + override suspend fun get(transform: (Value) -> Boolean): Value? { + return source.values.singleOrNull(transform) + } + + /** + * Discards all values that match the [transform] function. + * + * @param transform The function used to determine which values to discard. + */ + override suspend fun discardIf(transform: (Value) -> Boolean) { + val value = get(transform) ?: return + val index = indexFactory.createIndexFor(value) + source.remove(index) + } + + + /** + * Discards the value associated with the given [index]. + * + * @return The value associated with the given [index], or `null` if not found. + */ + override suspend fun discard(index: Index): Value? { + return source.remove(index) + } + + /** + * Adds the given [value] to the cache and returns its associated [Index]. + * + * @return The [Index] associated with the added [value]. + */ + override suspend fun put(value: Value): Index { + val index = indexFactory.createIndexFor(value) + source[index] = value + return index + } + + /** + * Discards all entries from this cache. + */ + override suspend fun discardAll() { + source.onEach { relation.discard(it.value) } + source.clear() + } + + override suspend fun getRelations(): Relation { + return relation + } + + override suspend fun relatesTo(cache: EntryCache, handler: RelationHandler) { + relation.to(cache, handler) + } + + + /** + * Returns a defensive copy of the underlying [ConcurrentMap]. + * + * @return A defensive copy of the underlying [ConcurrentMap]. + */ + override suspend fun asMap(): Map { + return source.toMap() + } +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt new file mode 100644 index 0000000..f0e3446 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt @@ -0,0 +1,5 @@ +package dev.kord.cache.api.observables + +interface IndexFactory { + public fun createIndexFor(value: Value): Index +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt index f0196df..c1c13bc 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt @@ -19,5 +19,5 @@ public interface TypedCache { /** * Returns a [Set] of all the [EntryCache]s stored in this [TypedCache]. */ - public suspend fun toSet(): Set> + public suspend fun toSet(): Set> } From 01d98ad558bca95ef29d08e63fc810eec6d1dbb6 Mon Sep 17 00:00:00 2001 From: hope Date: Fri, 14 Apr 2023 14:36:01 +0300 Subject: [PATCH 04/14] Stately Concurrent Map --- .../dev/kord/cache/api/observables/IndexCache.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt index bfd8d99..77bdf05 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt @@ -1,5 +1,7 @@ package dev.kord.cache.api.observables +import co.touchlab.stately.collections.ConcurrentMutableMap + /** * An implementation of the [EntryCache] interface that uses an index system based on the [IndexFactory] provided. * This implementation is thread-safe. @@ -12,7 +14,7 @@ public class IndexCache( public val indexFactory: IndexFactory ) : EntryCache { - private val source: HashMap = hashMapOf() + private val source: ConcurrentMutableMap = ConcurrentMutableMap() /** * Gets the value associated with the given [key]. @@ -76,15 +78,15 @@ public class IndexCache( return relation } - override suspend fun relatesTo(cache: EntryCache, handler: RelationHandler) { - relation.to(cache, handler) + override suspend fun relatesTo(other: EntryCache, handler: RelationHandler) { + relation.to(other, handler) } /** - * Returns a defensive copy of the underlying [ConcurrentMap]. + * Returns a defensive copy of the underlying [ConcurrentMutableMap]. * - * @return A defensive copy of the underlying [ConcurrentMap]. + * @return A defensive copy of the underlying [ConcurrentMutableMap]. */ override suspend fun asMap(): Map { return source.toMap() From e5672d6b81906d4650524a6704c174f0ffb6443e Mon Sep 17 00:00:00 2001 From: hope Date: Fri, 14 Apr 2023 16:47:13 +0300 Subject: [PATCH 05/14] optimizations --- .../kotlin/dev/kord/cache/api/observables/BasicRelation.kt | 3 +++ .../kotlin/dev/kord/cache/api/observables/IndexCache.kt | 1 + 2 files changed, 4 insertions(+) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt index 4825b36..6efbb37 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt @@ -9,6 +9,9 @@ class BasicRelation : Relation { relations.any { relateTo -> relateTo(value, friend) } } } + relations.onEach { relatesTo -> + caches.onEach { other -> other.discardIf { friend -> relatesTo(value, friend) } } + } } override suspend fun to(cache: EntryCache, handler: RelationHandler) { diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt index 77bdf05..84b3932 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt @@ -41,6 +41,7 @@ public class IndexCache( */ override suspend fun discardIf(transform: (Value) -> Boolean) { val value = get(transform) ?: return + relation.discard(value) val index = indexFactory.createIndexFor(value) source.remove(index) } From 6b704e0fb1a5eddc1690e15cd6acbf91e636f4c9 Mon Sep 17 00:00:00 2001 From: hope Date: Fri, 14 Apr 2023 16:57:49 +0300 Subject: [PATCH 06/14] remove old discard logic --- .../kotlin/dev/kord/cache/api/observables/BasicRelation.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt index 6efbb37..567984a 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt @@ -5,10 +5,6 @@ class BasicRelation : Relation { private val caches = mutableSetOf>() override suspend fun discard(value: T) { - caches.onEach { other -> other.discardIf { friend -> - relations.any { relateTo -> relateTo(value, friend) } - } - } relations.onEach { relatesTo -> caches.onEach { other -> other.discardIf { friend -> relatesTo(value, friend) } } } From 4d569b258e5f59034e227df7e2b582dab87671ca Mon Sep 17 00:00:00 2001 From: hope Date: Sun, 16 Apr 2023 00:20:13 +0300 Subject: [PATCH 07/14] apply factory and discard -> remove changes --- .../cache/api/observables/BasicRelation.kt | 4 ++-- .../kord/cache/api/observables/DataCache.kt | 9 ++++--- .../kord/cache/api/observables/EntryCache.kt | 18 +++++++------- .../dev/kord/cache/api/observables/Index.kt | 1 + .../kord/cache/api/observables/IndexCache.kt | 24 +++++++++---------- .../cache/api/observables/IndexFactory.kt | 5 ---- .../kord/cache/api/observables/Relation.kt | 2 +- 7 files changed, 29 insertions(+), 34 deletions(-) delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt index 567984a..a0d2773 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt @@ -4,9 +4,9 @@ class BasicRelation : Relation { private val relations = mutableSetOf>() private val caches = mutableSetOf>() - override suspend fun discard(value: T) { + override suspend fun remove(value: T) { relations.onEach { relatesTo -> - caches.onEach { other -> other.discardIf { friend -> relatesTo(value, friend) } } + caches.onEach { other -> other.removeIf { friend -> relatesTo(value, friend) } } } } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt index aac79a9..76ee354 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt @@ -8,13 +8,13 @@ public class DataCache : TypedCache { /** * The set of [EntryCache]s stored in this [TypedSetCache]. */ - private val types: MutableSet> = mutableSetOf() + private val types: MutableSet> = mutableSetOf() /** * Returns the [EntryCache] for the specified type, or `null` if it is not found. */ private suspend fun getTypeOrNull(): EntryCache? { - return toSet().filterIsInstance>().singleOrNull() + return toSet().firstNotNullOfOrNull { it as? EntryCache } } /** @@ -33,12 +33,11 @@ public class DataCache : TypedCache { */ override suspend fun putCache(cache: EntryCache) { require(getTypeOrNull() == null) { "There must be only one cache of the same type" } - @Suppress("UNCHECKED_CAST") - types.add(cache as EntryCache) + types.add(cache) } /** * Returns a set of all [EntryCache]s stored in this [TypedSetCache]. */ - public override suspend fun toSet(): Set> = types.toSet() + public override suspend fun toSet(): Set> = types.toSet() } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt index 05e9852..d33020e 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt @@ -24,24 +24,24 @@ public interface EntryCache { public suspend fun put(value: Value): Index /** - * Discards any cached [Value] that satisfies the given [transform] function. + * removes any cached [Value] that satisfies the given [transform] function. */ - public suspend fun discardIf(transform: (Value) -> Boolean) + public suspend fun removeIf(transform: (Value) -> Boolean) /** - * Discards the cached [Value] associated with the given [Index], if it exists. - * Returns the discarded [Value], or null if it was not found. + * removes the cached [Value] associated with the given [Index], if it exists. + * Returns the removeed [Value], or null if it was not found. */ - public suspend fun discard(index: Index): Value? + public suspend fun remove(index: Index): Value? /** - * Discards all entries in the cache. + * removes all entries in the cache. */ - public suspend fun discardAll() + public suspend fun removeAll() /** - * Adds an observer cache to this cache. Whenever a value is discarded from this cache, the - * observer cache will also discard any values that are related to it. + * Adds an observer cache to this cache. Whenever a value is removeed from this cache, the + * observer cache will also remove any values that are related to it. */ public suspend fun relatesTo(other: EntryCache, handler: RelationHandler) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt index f45c3cd..7db0152 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt @@ -1,5 +1,6 @@ package dev.kord.cache.api.observables +typealias IndexFactory = (value: Value) -> Index interface Index: Comparable { override fun hashCode(): Int } \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt index 84b3932..0dcb276 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt @@ -7,7 +7,7 @@ import co.touchlab.stately.collections.ConcurrentMutableMap * This implementation is thread-safe. * * @param indexFactory The factory that generates indices for values stored in this cache. - * @param relation The relation between this cache and other caches, used to discard related entries. + * @param relation The relation between this cache and other caches, used to remove related entries. */ public class IndexCache( public val relation: Relation, @@ -35,24 +35,24 @@ public class IndexCache( } /** - * Discards all values that match the [transform] function. + * removes all values that match the [transform] function. * - * @param transform The function used to determine which values to discard. + * @param transform The function used to determine which values to remove. */ - override suspend fun discardIf(transform: (Value) -> Boolean) { + override suspend fun removeIf(transform: (Value) -> Boolean) { val value = get(transform) ?: return - relation.discard(value) - val index = indexFactory.createIndexFor(value) + relation.remove(value) + val index = indexFactory(value) source.remove(index) } /** - * Discards the value associated with the given [index]. + * removes the value associated with the given [index]. * * @return The value associated with the given [index], or `null` if not found. */ - override suspend fun discard(index: Index): Value? { + override suspend fun remove(index: Index): Value? { return source.remove(index) } @@ -62,16 +62,16 @@ public class IndexCache( * @return The [Index] associated with the added [value]. */ override suspend fun put(value: Value): Index { - val index = indexFactory.createIndexFor(value) + val index = indexFactory(value) source[index] = value return index } /** - * Discards all entries from this cache. + * removes all entries from this cache. */ - override suspend fun discardAll() { - source.onEach { relation.discard(it.value) } + override suspend fun removeAll() { + source.onEach { relation.remove(it.value) } source.clear() } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt deleted file mode 100644 index f0e3446..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexFactory.kt +++ /dev/null @@ -1,5 +0,0 @@ -package dev.kord.cache.api.observables - -interface IndexFactory { - public fun createIndexFor(value: Value): Index -} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt index 0888100..76187e0 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt @@ -22,7 +22,7 @@ public interface Relation { * * @param value the entity to remove from the relation. */ - public suspend fun discard(value: T) + public suspend fun remove(value: T) /** From c648eb51248d3bc4977563e8cf4b30e808bf611e Mon Sep 17 00:00:00 2001 From: hope Date: Sun, 16 Apr 2023 00:29:46 +0300 Subject: [PATCH 08/14] rename get and remove toMap call --- .../dev/kord/cache/api/observables/EntryCache.kt | 12 +++--------- .../dev/kord/cache/api/observables/IndexCache.kt | 10 +++------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt index d33020e..5ef022e 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt @@ -14,7 +14,7 @@ public interface EntryCache { /** * Returns the first [Value] that satisfies the given [transform] function, or null if none is found. */ - public suspend fun get(transform: (Value) -> Boolean): Value? + public suspend fun firstOrNull(transform: (Value) -> Boolean): Value? /** * Associates the given [value] with a new [Index] in this cache. If the cache previously contained a @@ -30,7 +30,7 @@ public interface EntryCache { /** * removes the cached [Value] associated with the given [Index], if it exists. - * Returns the removeed [Value], or null if it was not found. + * Returns the removed [Value], or null if it was not found. */ public suspend fun remove(index: Index): Value? @@ -40,17 +40,11 @@ public interface EntryCache { public suspend fun removeAll() /** - * Adds an observer cache to this cache. Whenever a value is removeed from this cache, the + * Adds an observer cache to this cache. Whenever a value is removed from this cache, the * observer cache will also remove any values that are related to it. */ public suspend fun relatesTo(other: EntryCache, handler: RelationHandler) - /** - * Returns the [Relation] object for this cache, which contains information about any other - * caches that are observing this one. - */ - public suspend fun getRelations(): Relation - /** * Returns a defensive copy of the cache entries as a [Map] of [Index] to [Value]. */ diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt index 0dcb276..196911e 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt @@ -30,7 +30,7 @@ public class IndexCache( * * @return The first value that matches the [transform] function, or `null` if not found. */ - override suspend fun get(transform: (Value) -> Boolean): Value? { + override suspend fun firstOrNull(transform: (Value) -> Boolean): Value? { return source.values.singleOrNull(transform) } @@ -40,7 +40,7 @@ public class IndexCache( * @param transform The function used to determine which values to remove. */ override suspend fun removeIf(transform: (Value) -> Boolean) { - val value = get(transform) ?: return + val value = firstOrNull(transform) ?: return relation.remove(value) val index = indexFactory(value) source.remove(index) @@ -75,10 +75,6 @@ public class IndexCache( source.clear() } - override suspend fun getRelations(): Relation { - return relation - } - override suspend fun relatesTo(other: EntryCache, handler: RelationHandler) { relation.to(other, handler) } @@ -90,6 +86,6 @@ public class IndexCache( * @return A defensive copy of the underlying [ConcurrentMutableMap]. */ override suspend fun asMap(): Map { - return source.toMap() + return source } } \ No newline at end of file From dbe4d507129626cbeed218c176d780f9f98647d9 Mon Sep 17 00:00:00 2001 From: hope Date: Sun, 16 Apr 2023 08:22:43 +0300 Subject: [PATCH 09/14] restructure cache interfaces --- .../dev/kord/cache/api/observables/Cache.kt | 33 ++++++++++++ .../kord/cache/api/observables/DataCache.kt | 43 ---------------- .../kord/cache/api/observables/EntryCache.kt | 50 +++---------------- .../dev/kord/cache/api/observables/Index.kt | 6 --- .../kord/cache/api/observables/IndexCache.kt | 41 ++++++--------- .../cache/api/observables/MapEntryCache.kt | 30 +++++++++++ .../kord/cache/api/observables/Relation.kt | 4 +- .../kord/cache/api/observables/TypedCache.kt | 23 --------- 8 files changed, 89 insertions(+), 141 deletions(-) create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt new file mode 100644 index 0000000..2f58c75 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt @@ -0,0 +1,33 @@ +package dev.kord.cache.api.observables + +interface Cache { + + /** + * Removes any cached value [T] that satisfies the given [transform] function. + * @param transform A function that takes a value of type [T] and returns a boolean. + */ + + /** + * Returns the first value that satisfies the given [transform] function, or null if none is found. + * @param transform A function that takes a value of type [T] and returns a boolean. + * @return The first value that satisfies the [transform] function, or null if none is found. + */ + public suspend fun firstOrNull(transform: (T) -> Boolean): T? + + + public suspend fun removeIf(transform: (T) -> Boolean) + + /** + * Removes all entries in the cache. + */ + public suspend fun removeAll() + + /** + * Adds an observer cache to this cache. Whenever a value is removed from this cache, the + * observer cache will also remove any values that are related to it. + * @param other The observer cache. + * @param handler A [RelationHandler] that specifies how to remove related values from the observer cache. + */ + public suspend fun relatesTo(other: EntryCache, handler: RelationHandler) + +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt deleted file mode 100644 index 76ee354..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt +++ /dev/null @@ -1,43 +0,0 @@ -package dev.kord.cache.api.observables - -/** - * An implementation of [TypedCache] that stores a set of [EntryCache]s. - */ -public class DataCache : TypedCache { - - /** - * The set of [EntryCache]s stored in this [TypedSetCache]. - */ - private val types: MutableSet> = mutableSetOf() - - /** - * Returns the [EntryCache] for the specified type, or `null` if it is not found. - */ - private suspend fun getTypeOrNull(): EntryCache? { - return toSet().firstNotNullOfOrNull { it as? EntryCache } - } - - /** - * Returns the [EntryCache] for the specified type, throwing an exception if it is not found. - * @throws IllegalArgumentException if a cache for the specified type is not found. - */ - override suspend fun getType(): EntryCache { - val instance = getTypeOrNull() - require(instance != null) { "Cache for type T not found" } - return instance - } - - /** - * Adds the specified [EntryCache] to the [TypedSetCache]. - * @throws IllegalArgumentException if a cache for the same type already exists. - */ - override suspend fun putCache(cache: EntryCache) { - require(getTypeOrNull() == null) { "There must be only one cache of the same type" } - types.add(cache) - } - - /** - * Returns a set of all [EntryCache]s stored in this [TypedSetCache]. - */ - public override suspend fun toSet(): Set> = types.toSet() -} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt index 5ef022e..90311ce 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt @@ -1,52 +1,18 @@ package dev.kord.cache.api.observables /** - * A cache that associates a [Value] with an [Index]. + * A cache for a single value of type [T]. */ -public interface EntryCache { - - /** - * Returns the [Value] associated with the specified [Index] key in this cache, or null if there is no - * cached value for the given [Index]. - */ - public suspend fun get(key: Index): Value? - - /** - * Returns the first [Value] that satisfies the given [transform] function, or null if none is found. - */ - public suspend fun firstOrNull(transform: (Value) -> Boolean): Value? - - /** - * Associates the given [value] with a new [Index] in this cache. If the cache previously contained a - * value associated with the same [Index], the old value is replaced by [value]. - * Returns the newly generated [Index] for [value]. - */ - public suspend fun put(value: Value): Index - - /** - * removes any cached [Value] that satisfies the given [transform] function. - */ - public suspend fun removeIf(transform: (Value) -> Boolean) - - /** - * removes the cached [Value] associated with the given [Index], if it exists. - * Returns the removed [Value], or null if it was not found. - */ - public suspend fun remove(index: Index): Value? - - /** - * removes all entries in the cache. - */ - public suspend fun removeAll() - +public interface EntryCache { /** - * Adds an observer cache to this cache. Whenever a value is removed from this cache, the - * observer cache will also remove any values that are related to it. + * Puts the [value] into the cache. + * @param value The value to be cached. */ - public suspend fun relatesTo(other: EntryCache, handler: RelationHandler) + public suspend fun put(value: T) /** - * Returns a defensive copy of the cache entries as a [Map] of [Index] to [Value]. + * Returns a defensive copy of the cache entries [T]. + * @return A collection of the cached values. */ - public suspend fun asMap(): Map + public suspend fun getAll(): Collection } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt deleted file mode 100644 index 7db0152..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Index.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.kord.cache.api.observables - -typealias IndexFactory = (value: Value) -> Index -interface Index: Comparable { - override fun hashCode(): Int -} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt index 196911e..7229219 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt @@ -6,22 +6,20 @@ import co.touchlab.stately.collections.ConcurrentMutableMap * An implementation of the [EntryCache] interface that uses an index system based on the [IndexFactory] provided. * This implementation is thread-safe. * - * @param indexFactory The factory that generates indices for values stored in this cache. * @param relation The relation between this cache and other caches, used to remove related entries. */ -public class IndexCache( +public class IndexCache( public val relation: Relation, - public val indexFactory: IndexFactory -) : EntryCache { +) : MapEntryCache { - private val source: ConcurrentMutableMap = ConcurrentMutableMap() + private val source: ConcurrentMutableMap = ConcurrentMutableMap() /** * Gets the value associated with the given [key]. * * @return The value associated with the given [key], or `null` if not found. */ - override suspend fun get(key: Index): Value? { + override suspend fun get(key: Key): Value? { return source[key] } @@ -31,7 +29,7 @@ public class IndexCache( * @return The first value that matches the [transform] function, or `null` if not found. */ override suspend fun firstOrNull(transform: (Value) -> Boolean): Value? { - return source.values.singleOrNull(transform) + return source.values.find(transform) } /** @@ -40,33 +38,26 @@ public class IndexCache( * @param transform The function used to determine which values to remove. */ override suspend fun removeIf(transform: (Value) -> Boolean) { - val value = firstOrNull(transform) ?: return - relation.remove(value) - val index = indexFactory(value) - source.remove(index) + val entry = source.entries.find { (_, value) -> transform(value) } ?: return + relation.remove(entry.value) + source.remove(entry.key) } /** - * removes the value associated with the given [index]. + * removes the value associated with the given [key]. * - * @return The value associated with the given [index], or `null` if not found. + * @return The value associated with the given [key], or `null` if not found. */ - override suspend fun remove(index: Index): Value? { - return source.remove(index) + override suspend fun remove(key: Key) { + source.remove(key) } - /** - * Adds the given [value] to the cache and returns its associated [Index]. - * - * @return The [Index] associated with the added [value]. - */ - override suspend fun put(value: Value): Index { - val index = indexFactory(value) - source[index] = value - return index + override suspend fun put(key: Key, value: Value) { + source[key] = value } + /** * removes all entries from this cache. */ @@ -85,7 +76,7 @@ public class IndexCache( * * @return A defensive copy of the underlying [ConcurrentMutableMap]. */ - override suspend fun asMap(): Map { + override suspend fun getAll(): Map { return source } } \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt new file mode 100644 index 0000000..d7551ce --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt @@ -0,0 +1,30 @@ +package dev.kord.cache.api.observables + +/** + * A cache for a map of entries where each [Value] is associated with a unique [Key]. + * */ +interface MapEntryCache : Cache { + + /** + * Returns the value associated with the given [key] in the cache, or null if no such entry exists. + */ + suspend fun get(key: Key): Value? + + /** + * Removes the entry associated with the given [key] from the cache, if it exists. + */ + suspend fun remove(key: Key) + + /** + * Returns a [Map] containing all entries in the cache. + */ + suspend fun getAll(): Map + + /** + * Puts the [value] into the cache associated with a [key]. + * @param key The key to associate the [value] with. + * @param value The value to be cached. + */ + public suspend fun put(key: Key, value: Value) + +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt index 76187e0..8ae7641 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt @@ -2,14 +2,14 @@ package dev.kord.cache.api.observables /** * A typealias for a function that determines the relationship between two entities of type `T` and `R` - * in a bi-directional link. + * in a uni-directional link. * The `value` parameter represents the entity of type `T` and the `friend` parameter represents the entity of type `R`. * The function should return `true` if the two entities are related, or `false` otherwise. */ public typealias RelationHandler = (value: T, friend: R) -> Boolean /** - * A `Relation` is a bi-directional link between two entities of type `T` and `R`. + * A `Relation` is a uni-directional link between two entities of type `T` and `R`. * A `Relation` is defined by a set of `RelationHandler`s which determine the relationship * between two entities. * diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt deleted file mode 100644 index c1c13bc..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/TypedCache.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.kord.cache.api.observables - -/** - * An interface representing a cache that can store multiple types of data by associating them with their types. - */ -public interface TypedCache { - - /** - * Returns the [EntryCache] associated with the type [T]. - */ - public suspend fun getType(): EntryCache - - - /** - * Adds the given [EntryCache] to this [TypedCache]. - */ - public suspend fun putCache(cache: EntryCache) - - /** - * Returns a [Set] of all the [EntryCache]s stored in this [TypedCache]. - */ - public suspend fun toSet(): Set> -} From e58e3a717fb31c6ca4f28d07c7772dde5315f7a1 Mon Sep 17 00:00:00 2001 From: hope Date: Sun, 16 Apr 2023 23:57:29 +0300 Subject: [PATCH 10/14] what about this? --- .../cache/api/observables/BasicRelation.kt | 6 +- .../dev/kord/cache/api/observables/Cache.kt | 33 ----------- .../kord/cache/api/observables/DataCache.kt | 59 +++++++++++++++++++ .../kord/cache/api/observables/EntryCache.kt | 18 ------ .../kord/cache/api/observables/IndexCache.kt | 24 ++++---- .../cache/api/observables/MapEntryCache.kt | 30 ---------- .../kord/cache/api/observables/Relation.kt | 4 +- 7 files changed, 76 insertions(+), 98 deletions(-) delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt create mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt delete mode 100644 api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt index a0d2773..e018007 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt @@ -2,7 +2,7 @@ package dev.kord.cache.api.observables class BasicRelation : Relation { private val relations = mutableSetOf>() - private val caches = mutableSetOf>() + private val caches = mutableSetOf>() override suspend fun remove(value: T) { relations.onEach { relatesTo -> @@ -10,8 +10,8 @@ class BasicRelation : Relation { } } - override suspend fun to(cache: EntryCache, handler: RelationHandler) { - caches.add(cache as EntryCache<*>) + override suspend fun to(cache: DataCache<*, R>, handler: RelationHandler) { + caches.add(cache as DataCache<*, T>) relations.add(safe(handler)) } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt deleted file mode 100644 index 2f58c75..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt +++ /dev/null @@ -1,33 +0,0 @@ -package dev.kord.cache.api.observables - -interface Cache { - - /** - * Removes any cached value [T] that satisfies the given [transform] function. - * @param transform A function that takes a value of type [T] and returns a boolean. - */ - - /** - * Returns the first value that satisfies the given [transform] function, or null if none is found. - * @param transform A function that takes a value of type [T] and returns a boolean. - * @return The first value that satisfies the [transform] function, or null if none is found. - */ - public suspend fun firstOrNull(transform: (T) -> Boolean): T? - - - public suspend fun removeIf(transform: (T) -> Boolean) - - /** - * Removes all entries in the cache. - */ - public suspend fun removeAll() - - /** - * Adds an observer cache to this cache. Whenever a value is removed from this cache, the - * observer cache will also remove any values that are related to it. - * @param other The observer cache. - * @param handler A [RelationHandler] that specifies how to remove related values from the observer cache. - */ - public suspend fun relatesTo(other: EntryCache, handler: RelationHandler) - -} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt new file mode 100644 index 0000000..ecbf880 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt @@ -0,0 +1,59 @@ +package dev.kord.cache.api.observables + +/** + * A cache for a map of entries where each [Value] is associated with a unique [Key]. + * */ +interface DataCache { + + /** + * Returns the value associated with the given [key] in the cache, or null if no such entry exists. + */ + suspend fun get(key: Key): Value? + + /** + * Removes the entry associated with the given [key] from the cache, if it exists. + */ + suspend fun remove(key: Key) + + /** + * Returns a [Map] containing all entries in the cache. + */ + suspend fun getAll(): Map + + /** + * Puts the [value] into the cache associated with a [key]. + * @param key The key to associate the [value] with. + * @param value The value to be cached. + */ + public suspend fun put(key: Key, value: Value) + + + /** + * Removes any cached value [T] that satisfies the given [transform] function. + * @param transform A function that takes a value of type [T] and returns a boolean. + */ + + /** + * Returns the first value that satisfies the given [transform] function, or null if none is found. + * @param transform A function that takes a value of type [T] and returns a boolean. + * @return The first value that satisfies the [transform] function, or null if none is found. + */ + public suspend fun firstOrNull(predicate: (Value) -> Boolean): Value? + + + public suspend fun removeIf(predicate: (Value) -> Boolean) + + /** + * Removes all entries in the cache. + */ + public suspend fun removeAll() + + /** + * Adds an observer cache to this cache. Whenever a value is removed from this cache, the + * observer cache will also remove any values that are related to it. + * @param other The observer cache. + * @param handler A [RelationHandler] that specifies how to remove related values from the observer cache. + */ + public suspend fun relatesTo(other: DataCache<*, R>, handler: RelationHandler) + +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt deleted file mode 100644 index 90311ce..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/EntryCache.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.kord.cache.api.observables - -/** - * A cache for a single value of type [T]. - */ -public interface EntryCache { - /** - * Puts the [value] into the cache. - * @param value The value to be cached. - */ - public suspend fun put(value: T) - - /** - * Returns a defensive copy of the cache entries [T]. - * @return A collection of the cached values. - */ - public suspend fun getAll(): Collection -} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt index 7229219..a3ad0d7 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt @@ -3,14 +3,14 @@ package dev.kord.cache.api.observables import co.touchlab.stately.collections.ConcurrentMutableMap /** - * An implementation of the [EntryCache] interface that uses an index system based on the [IndexFactory] provided. + * An implementation of the [DataCache] with [ConcurrentMutableMap]. * This implementation is thread-safe. * * @param relation The relation between this cache and other caches, used to remove related entries. */ -public class IndexCache( +public class ConcurrentCache( public val relation: Relation, -) : MapEntryCache { +) : DataCache { private val source: ConcurrentMutableMap = ConcurrentMutableMap() @@ -24,21 +24,21 @@ public class IndexCache( } /** - * Gets the first value that matches the [transform] function. + * Gets the first value that matches the [predicate] function. * - * @return The first value that matches the [transform] function, or `null` if not found. + * @return The first value that matches the [predicate] function, or `null` if not found. */ - override suspend fun firstOrNull(transform: (Value) -> Boolean): Value? { - return source.values.find(transform) + override suspend fun firstOrNull(predicate: (Value) -> Boolean): Value? { + return source.values.find(predicate) } /** - * removes all values that match the [transform] function. + * removes all values that match the [predicate] function. * - * @param transform The function used to determine which values to remove. + * @param predicate The function used to determine which values to remove. */ - override suspend fun removeIf(transform: (Value) -> Boolean) { - val entry = source.entries.find { (_, value) -> transform(value) } ?: return + override suspend fun removeIf(predicate: (Value) -> Boolean) { + val entry = source.entries.find { (_, value) -> predicate(value) } ?: return relation.remove(entry.value) source.remove(entry.key) } @@ -66,7 +66,7 @@ public class IndexCache( source.clear() } - override suspend fun relatesTo(other: EntryCache, handler: RelationHandler) { + override suspend fun relatesTo(other: DataCache<*, R>, handler: RelationHandler) { relation.to(other, handler) } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt deleted file mode 100644 index d7551ce..0000000 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/MapEntryCache.kt +++ /dev/null @@ -1,30 +0,0 @@ -package dev.kord.cache.api.observables - -/** - * A cache for a map of entries where each [Value] is associated with a unique [Key]. - * */ -interface MapEntryCache : Cache { - - /** - * Returns the value associated with the given [key] in the cache, or null if no such entry exists. - */ - suspend fun get(key: Key): Value? - - /** - * Removes the entry associated with the given [key] from the cache, if it exists. - */ - suspend fun remove(key: Key) - - /** - * Returns a [Map] containing all entries in the cache. - */ - suspend fun getAll(): Map - - /** - * Puts the [value] into the cache associated with a [key]. - * @param key The key to associate the [value] with. - * @param value The value to be cached. - */ - public suspend fun put(key: Key, value: Value) - -} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt index 8ae7641..54b91ea 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt @@ -26,10 +26,10 @@ public interface Relation { /** - * Associates an [EntryCache] of type `T` with this relation. + * Associates an [DataCache] of type `T` with this relation. * * @param cache the cache to associate with this relation. * @param handler the `RelationHandler` that defines the relationship between entities of type `T` and `R`. */ - public suspend fun to(cache: EntryCache, handler: RelationHandler) + public suspend fun to(cache: DataCache<*, R>, handler: RelationHandler) } From c65823a5d65eba2418951e02f53459ef25c83acc Mon Sep 17 00:00:00 2001 From: hope Date: Mon, 17 Apr 2023 12:11:13 +0300 Subject: [PATCH 11/14] changes --- api/api/api.api | 134 ++++++++++++++++++ .../cache/api/observables/BasicRelation.kt | 11 +- .../observables/{DataCache.kt => Cache.kt} | 32 ++--- .../{IndexCache.kt => ConcurrentCache.kt} | 29 ++-- .../kord/cache/api/observables/Relation.kt | 4 +- map/api/map.api | 2 +- 6 files changed, 173 insertions(+), 39 deletions(-) rename api/src/commonMain/kotlin/dev/kord/cache/api/observables/{DataCache.kt => Cache.kt} (56%) rename api/src/commonMain/kotlin/dev/kord/cache/api/observables/{IndexCache.kt => ConcurrentCache.kt} (74%) diff --git a/api/api/api.api b/api/api/api.api index 9aefa40..05cd392 100644 --- a/api/api/api.api +++ b/api/api/api.api @@ -216,3 +216,137 @@ public final class dev/kord/cache/api/meta/TypeStatisticsLogger { public final fun logQuery ()V } +public final class dev/kord/cache/api/observables/BasicRelation : dev/kord/cache/api/observables/Relation { + public fun ()V + public fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun to (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/Cache { + public abstract fun filter (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun get (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun relatesTo (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAny (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun set (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class dev/kord/cache/api/observables/ConcurrentCache : dev/kord/cache/api/observables/Cache { + public fun (Ldev/kord/cache/api/observables/Relation;)V + public fun filter (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun get (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun getRelation ()Ldev/kord/cache/api/observables/Relation; + public fun relatesTo (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeAny (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun set (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/DataCache { + public abstract fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun get (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun put (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun relatesTo (Ldev/kord/cache/api/observables/DataCache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeIf (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/EntryCache { + public abstract fun asMap (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun get (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun put (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun relatesTo (Ldev/kord/cache/api/observables/EntryCache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeIf (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/Index : java/lang/Comparable { + public abstract fun hashCode ()I +} + +public final class dev/kord/cache/api/observables/IndexCache : dev/kord/cache/api/observables/EntryCache { + public fun (Ldev/kord/cache/api/observables/Relation;Lkotlin/jvm/functions/Function1;)V + public fun asMap (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun get (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun getIndexFactory ()Lkotlin/jvm/functions/Function1; + public final fun getRelation ()Ldev/kord/cache/api/observables/Relation; + public fun put (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun relatesTo (Ldev/kord/cache/api/observables/EntryCache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun remove (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeIf (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/IndexFactory { + public abstract fun createIndexFor (Ljava/lang/Object;)Ldev/kord/cache/api/observables/Index; +} + +public final class dev/kord/cache/api/observables/MemberData { + public fun (II)V + public final fun component1 ()I + public final fun component2 ()I + public final fun copy (II)Ldev/kord/cache/api/observables/MemberData; + public static synthetic fun copy$default (Ldev/kord/cache/api/observables/MemberData;IIILjava/lang/Object;)Ldev/kord/cache/api/observables/MemberData; + public fun equals (Ljava/lang/Object;)Z + public final fun getGuildId ()I + public final fun getUserId ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/kord/cache/api/observables/MemberDataIndexFactory : dev/kord/cache/api/observables/IndexFactory { + public static final field INSTANCE Ldev/kord/cache/api/observables/MemberDataIndexFactory; + public fun createIndexFor (Ldev/kord/cache/api/observables/MemberData;)Ldev/kord/cache/api/observables/Index; + public synthetic fun createIndexFor (Ljava/lang/Object;)Ldev/kord/cache/api/observables/Index; +} + +public abstract interface class dev/kord/cache/api/observables/Relation { + public abstract fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun to (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class dev/kord/cache/api/observables/SnowflakeSetIndex : dev/kord/cache/api/observables/Index { + public fun (Ljava/util/Set;)V + public fun compareTo (Ldev/kord/cache/api/observables/Index;)I + public synthetic fun compareTo (Ljava/lang/Object;)I + public final fun getSnowflakes ()Ljava/util/Set; + public fun hashCode ()I +} + +public final class dev/kord/cache/api/observables/TestKt { + public static final fun main (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun main ([Ljava/lang/String;)V +} + +public abstract interface class dev/kord/cache/api/observables/TypedCache { + public abstract fun getType (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun putCache (Ldev/kord/cache/api/observables/EntryCache;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun toSet (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class dev/kord/cache/api/observables/UserData { + public fun (I)V + public final fun component1 ()I + public final fun copy (I)Ldev/kord/cache/api/observables/UserData; + public static synthetic fun copy$default (Ldev/kord/cache/api/observables/UserData;IILjava/lang/Object;)Ldev/kord/cache/api/observables/UserData; + public fun equals (Ljava/lang/Object;)Z + public final fun getUserId ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/kord/cache/api/observables/UserIndexFactory : dev/kord/cache/api/observables/IndexFactory { + public static final field INSTANCE Ldev/kord/cache/api/observables/UserIndexFactory; + public fun createIndexFor (Ldev/kord/cache/api/observables/UserData;)Ldev/kord/cache/api/observables/Index; + public synthetic fun createIndexFor (Ljava/lang/Object;)Ldev/kord/cache/api/observables/Index; +} + diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt index e018007..94a6dec 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt @@ -2,16 +2,16 @@ package dev.kord.cache.api.observables class BasicRelation : Relation { private val relations = mutableSetOf>() - private val caches = mutableSetOf>() + private val caches = mutableSetOf>() override suspend fun remove(value: T) { relations.onEach { relatesTo -> - caches.onEach { other -> other.removeIf { friend -> relatesTo(value, friend) } } + caches.onEach { other -> other.removeAny { friend -> relatesTo(value, friend) } } } } - override suspend fun to(cache: DataCache<*, R>, handler: RelationHandler) { - caches.add(cache as DataCache<*, T>) + override suspend fun to(cache: Cache<*, R>, handler: RelationHandler) { + caches.add(cache) relations.add(safe(handler)) } @@ -19,8 +19,7 @@ class BasicRelation : Relation { return obj@{ value: T, friend: Any -> @Suppress("UNCHECKED_CAST") val safeCast = friend as? R - if(safeCast != null) return@obj relationHandler(value, safeCast) - else return@obj false + safeCast != null && relationHandler(value, safeCast) } } } \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt similarity index 56% rename from api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt rename to api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt index ecbf880..10d2eb3 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/DataCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt @@ -1,9 +1,11 @@ package dev.kord.cache.api.observables +import kotlinx.coroutines.flow.Flow + /** * A cache for a map of entries where each [Value] is associated with a unique [Key]. * */ -interface DataCache { +interface Cache { /** * Returns the value associated with the given [key] in the cache, or null if no such entry exists. @@ -16,32 +18,30 @@ interface DataCache { suspend fun remove(key: Key) /** - * Returns a [Map] containing all entries in the cache. + * Filters [Value]s by the given [predicate] + * Returns a lazy [Flow] of [Value]s */ - suspend fun getAll(): Map + suspend fun filter(predicate: (Value) -> Boolean): Flow /** - * Puts the [value] into the cache associated with a [key]. + * Sets the [value] into the cache associated with a [key]. * @param key The key to associate the [value] with. * @param value The value to be cached. */ - public suspend fun put(key: Key, value: Value) - + suspend fun set(key: Key, value: Value) /** - * Removes any cached value [T] that satisfies the given [transform] function. - * @param transform A function that takes a value of type [T] and returns a boolean. + * Returns the first value that satisfies the given [predicate] function, or null if none is found. + * @param predicate A function that takes a [Value] and returns a boolean. + * @return The first value that satisfies the [predicate] function, or null if none is found. */ + public suspend fun firstOrNull(predicate: (Value) -> Boolean): Value? /** - * Returns the first value that satisfies the given [transform] function, or null if none is found. - * @param transform A function that takes a value of type [T] and returns a boolean. - * @return The first value that satisfies the [transform] function, or null if none is found. + * Removes any entry in the cache which matches the given [predicate]. + * @param predicate The predicate to match each [Value] against. */ - public suspend fun firstOrNull(predicate: (Value) -> Boolean): Value? - - - public suspend fun removeIf(predicate: (Value) -> Boolean) + public suspend fun removeAny(predicate: (Value) -> Boolean) /** * Removes all entries in the cache. @@ -54,6 +54,6 @@ interface DataCache { * @param other The observer cache. * @param handler A [RelationHandler] that specifies how to remove related values from the observer cache. */ - public suspend fun relatesTo(other: DataCache<*, R>, handler: RelationHandler) + public suspend fun relatesTo(other: Cache<*, R>, handler: RelationHandler) } diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt similarity index 74% rename from api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt rename to api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt index a3ad0d7..dcd0c59 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/IndexCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt @@ -1,16 +1,19 @@ package dev.kord.cache.api.observables import co.touchlab.stately.collections.ConcurrentMutableMap +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow /** - * An implementation of the [DataCache] with [ConcurrentMutableMap]. + * An implementation of the [Cache] with [ConcurrentMutableMap]. * This implementation is thread-safe. * * @param relation The relation between this cache and other caches, used to remove related entries. */ public class ConcurrentCache( public val relation: Relation, -) : DataCache { +) : Cache { private val source: ConcurrentMutableMap = ConcurrentMutableMap() @@ -37,12 +40,19 @@ public class ConcurrentCache( * * @param predicate The function used to determine which values to remove. */ - override suspend fun removeIf(predicate: (Value) -> Boolean) { + override suspend fun removeAny(predicate: (Value) -> Boolean) { val entry = source.entries.find { (_, value) -> predicate(value) } ?: return relation.remove(entry.value) source.remove(entry.key) } + override suspend fun filter(predicate: (Value) -> Boolean): Flow = flow { + for(value in source.values) { + if(predicate(value)) { emit(value) } + } + } + + /** * removes the value associated with the given [key]. @@ -53,7 +63,7 @@ public class ConcurrentCache( source.remove(key) } - override suspend fun put(key: Key, value: Value) { + override suspend fun set(key: Key, value: Value) { source[key] = value } @@ -66,17 +76,8 @@ public class ConcurrentCache( source.clear() } - override suspend fun relatesTo(other: DataCache<*, R>, handler: RelationHandler) { + override suspend fun relatesTo(other: Cache<*, R>, handler: RelationHandler) { relation.to(other, handler) } - - /** - * Returns a defensive copy of the underlying [ConcurrentMutableMap]. - * - * @return A defensive copy of the underlying [ConcurrentMutableMap]. - */ - override suspend fun getAll(): Map { - return source - } } \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt index 54b91ea..3b32ca5 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt @@ -26,10 +26,10 @@ public interface Relation { /** - * Associates an [DataCache] of type `T` with this relation. + * Associates an [Cache] of type `T` with this relation. * * @param cache the cache to associate with this relation. * @param handler the `RelationHandler` that defines the relationship between entities of type `T` and `R`. */ - public suspend fun to(cache: DataCache<*, R>, handler: RelationHandler) + public suspend fun to(cache: Cache<*, R>, handler: RelationHandler) } diff --git a/map/api/map.api b/map/api/map.api index 1155fd1..28b1dcd 100644 --- a/map/api/map.api +++ b/map/api/map.api @@ -40,7 +40,7 @@ public final class dev/kord/cache/map/MapLikeCollection$DefaultImpls { public static fun contains (Ldev/kord/cache/map/MapLikeCollection;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class dev/kord/cache/map/MapLikeCollectionJvm { +public final class dev/kord/cache/map/MapLikeCollectionJvmx { public static final fun weakHashMap (Ldev/kord/cache/map/MapLikeCollection$Companion;)Ldev/kord/cache/map/MapLikeCollection; } From ad761b5f11783488952bf27cda231194e8360c0d Mon Sep 17 00:00:00 2001 From: hope Date: Mon, 17 Apr 2023 16:52:49 +0300 Subject: [PATCH 12/14] no intermediate collections please. --- .../cache/api/observables/ConcurrentCache.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt index dcd0c59..4abb389 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt @@ -2,7 +2,6 @@ package dev.kord.cache.api.observables import co.touchlab.stately.collections.ConcurrentMutableMap import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow /** @@ -32,7 +31,7 @@ public class ConcurrentCache( * @return The first value that matches the [predicate] function, or `null` if not found. */ override suspend fun firstOrNull(predicate: (Value) -> Boolean): Value? { - return source.values.find(predicate) + return source.values.firstOrNull(predicate) } /** @@ -41,9 +40,10 @@ public class ConcurrentCache( * @param predicate The function used to determine which values to remove. */ override suspend fun removeAny(predicate: (Value) -> Boolean) { - val entry = source.entries.find { (_, value) -> predicate(value) } ?: return - relation.remove(entry.value) - source.remove(entry.key) + source.entries + .asSequence() + .filter { (_, value) -> predicate(value) } + .forEach { (key, _) -> remove(key) } } override suspend fun filter(predicate: (Value) -> Boolean): Flow = flow { @@ -60,6 +60,8 @@ public class ConcurrentCache( * @return The value associated with the given [key], or `null` if not found. */ override suspend fun remove(key: Key) { + val value = source[key] ?: return + relation.remove(value) source.remove(key) } @@ -72,10 +74,15 @@ public class ConcurrentCache( * removes all entries from this cache. */ override suspend fun removeAll() { - source.onEach { relation.remove(it.value) } - source.clear() + val iterator = source.iterator() + while (iterator.hasNext()) { + val (_, value) = iterator.next() + relation.remove(value) + iterator.remove() + } } + override suspend fun relatesTo(other: Cache<*, R>, handler: RelationHandler) { relation.to(other, handler) } From 828be4c2f52e0aec06f22032e3b9e3dae27a4cb0 Mon Sep 17 00:00:00 2001 From: hope Date: Mon, 17 Apr 2023 17:04:18 +0300 Subject: [PATCH 13/14] Sequence Instead of Flow This will allow us to perform intermediate sort operations --- .../kotlin/dev/kord/cache/api/observables/Cache.kt | 2 +- .../kord/cache/api/observables/ConcurrentCache.kt | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt index 10d2eb3..d3f2bf5 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt @@ -21,7 +21,7 @@ interface Cache { * Filters [Value]s by the given [predicate] * Returns a lazy [Flow] of [Value]s */ - suspend fun filter(predicate: (Value) -> Boolean): Flow + suspend fun filter(predicate: (Value) -> Boolean): Sequence /** * Sets the [value] into the cache associated with a [key]. diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt index 4abb389..4b7860f 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt @@ -1,8 +1,6 @@ package dev.kord.cache.api.observables import co.touchlab.stately.collections.ConcurrentMutableMap -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow /** * An implementation of the [Cache] with [ConcurrentMutableMap]. @@ -46,12 +44,10 @@ public class ConcurrentCache( .forEach { (key, _) -> remove(key) } } - override suspend fun filter(predicate: (Value) -> Boolean): Flow = flow { - for(value in source.values) { - if(predicate(value)) { emit(value) } - } - } - + override suspend fun filter(predicate: (Value) -> Boolean): Sequence = + source.asSequence() + .filter { (_, value) -> predicate(value) } + .map { it.value } /** From 518703690f8d3b17b2596796cdb4b8dab80c5518 Mon Sep 17 00:00:00 2001 From: hope Date: Mon, 17 Apr 2023 17:11:32 +0300 Subject: [PATCH 14/14] why call entries? --- .../kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt index 4b7860f..cf22a5f 100644 --- a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt @@ -38,8 +38,7 @@ public class ConcurrentCache( * @param predicate The function used to determine which values to remove. */ override suspend fun removeAny(predicate: (Value) -> Boolean) { - source.entries - .asSequence() + source.asSequence() .filter { (_, value) -> predicate(value) } .forEach { (key, _) -> remove(key) } }