From d29bf960aa39c2bc3b3df550be9c7b423f6e35f4 Mon Sep 17 00:00:00 2001 From: sergeych Date: Sat, 30 Nov 2024 13:37:20 +0700 Subject: [PATCH] - no caching on stored delegates, fix problens with memory storage connections + optStored now properly load/store only non-null values * possible concurrent modification in KVStorage.clear fixed --- build.gradle.kts | 20 +++--- .../kotlin/net.sergeych.bintools/KVStorage.kt | 63 ++++++++++--------- src/commonTest/kotlin/bipack/StorageTest.kt | 44 +++++++++++++ src/wasmJsTest/kotlin/StorageTest.kt | 1 - 4 files changed, 86 insertions(+), 42 deletions(-) create mode 100644 src/commonTest/kotlin/bipack/StorageTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 444cc12..b971ebf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,12 @@ plugins { - kotlin("multiplatform") version "2.0.20" - kotlin("plugin.serialization") version "2.0.20" + kotlin("multiplatform") version "2.0.21" + kotlin("plugin.serialization") version "2.0.21" id("org.jetbrains.dokka") version "1.9.20" `maven-publish` } -val serialization_version = "1.6.5-SNAPSHOT" - group = "net.sergeych" -version = "0.1.8-SNAPSHOT" +version = "0.1.9-SNAPSHOT" repositories { mavenCentral() @@ -24,11 +22,11 @@ kotlin { nodejs() } - macosArm64() - iosX64() - iosArm64() - macosX64() - iosSimulatorArm64() +// macosArm64() +// iosX64() +// iosArm64() +// macosX64() +// iosSimulatorArm64() linuxX64() linuxArm64() mingwX64() @@ -55,7 +53,7 @@ kotlin { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") // this is actually a bug: we need only the core, but bare core causes strange errors - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") api("net.sergeych:mp_stools:[1.4.7,)") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") } diff --git a/src/commonMain/kotlin/net.sergeych.bintools/KVStorage.kt b/src/commonMain/kotlin/net.sergeych.bintools/KVStorage.kt index 4d7bc57..0dcf93f 100644 --- a/src/commonMain/kotlin/net.sergeych.bintools/KVStorage.kt +++ b/src/commonMain/kotlin/net.sergeych.bintools/KVStorage.kt @@ -1,11 +1,10 @@ package net.sergeych.bintools +import kotlinx.serialization.KSerializer import kotlinx.serialization.serializer import net.sergeych.bipack.BipackDecoder import net.sergeych.bipack.BipackEncoder import kotlin.reflect.KProperty -import kotlin.reflect.KType -import kotlin.reflect.typeOf /** @@ -43,7 +42,7 @@ interface KVStorage { * Default implementation uses [keys]. You may override it for performance */ fun clear() { - for (k in keys) this[k] = null + for (k in keys.toList()) this[k] = null } /** @@ -93,46 +92,50 @@ inline fun KVStorage.read(key: String): T? = @Suppress("unused") inline fun KVStorage.load(key: String): T? = read(key) -inline operator fun KVStorage.invoke(defaultValue: T,overrideName: String? = null) = - KVStorageDelegate(this, typeOf(), defaultValue, overrideName) +inline operator fun KVStorage.invoke(defaultValue: T,overrideName: String? = null) = + KVStorageDelegate(this, serializer(), defaultValue, overrideName) -inline fun KVStorage.stored(defaultValue: T, overrideName: String? = null) = - KVStorageDelegate(this, typeOf(), defaultValue, overrideName) +inline fun KVStorage.stored(defaultValue: T, overrideName: String? = null) = + KVStorageDelegate(this, serializer(), defaultValue, overrideName) -inline fun KVStorage.optStored(overrideName: String? = null) = - KVStorageDelegate(this, typeOf(), null, overrideName) +inline fun KVStorage.optStored(overrideName: String? = null) = + KVStorageOptDelegate(this, serializer(),overrideName) -class KVStorageDelegate( +class KVStorageDelegate( private val storage: KVStorage, - type: KType, + private val serializer: KSerializer, private val defaultValue: T, private val overrideName: String? = null, ) { - private fun name(property: KProperty<*>): String = overrideName ?: property.name - private var cachedValue: T = defaultValue - private var cacheReady = false - private val serializer = serializer(type) + operator fun getValue(thisRef: Any?, property: KProperty<*>): T = + storage.get(name(property))?.let { BipackDecoder.decode(serializer, it) } + ?: defaultValue - @Suppress("UNCHECKED_CAST") - operator fun getValue(thisRef: Any?, property: KProperty<*>): T { - if (cacheReady) return cachedValue - val data = storage.get(name(property)) - if (data == null) - cachedValue = defaultValue - else - cachedValue = BipackDecoder.decode(data.toDataSource(), serializer) as T - cacheReady = true - return cachedValue - } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { -// if (!cacheReady || value != cachedValue) { - cachedValue = value - cacheReady = true + storage[name(property)] = BipackEncoder.encode(serializer, value) + } +} + +class KVStorageOptDelegate( + private val storage: KVStorage, + private val serializer: KSerializer, + private val overrideName: String? = null, +) { + private fun name(property: KProperty<*>): String = overrideName ?: property.name + + operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = + storage.get(name(property))?.let{ + BipackDecoder.decode(serializer, it) + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + if (value == null) + storage.delete(name(property)) + else storage[name(property)] = BipackEncoder.encode(serializer, value) -// } } } diff --git a/src/commonTest/kotlin/bipack/StorageTest.kt b/src/commonTest/kotlin/bipack/StorageTest.kt new file mode 100644 index 0000000..f980f4c --- /dev/null +++ b/src/commonTest/kotlin/bipack/StorageTest.kt @@ -0,0 +1,44 @@ +package bipack + +import net.sergeych.bintools.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class StorageTest { + @Test + fun storageTest3() { + val s1 = MemoryKVStorage() + val s2 = defaultNamedStorage("test_mp_bintools2") + s2.clear() + + s1.write("foo", "bar") + s1.write("foo2", "bar2") + + s2.write("foo", "foobar") + s2.write("bar", "buzz") + s2.write("reason", 42) + + assertEquals("bar", s1.read("foo")) + assertEquals("bar2", s1.read("foo2")) + assertNull(s1.get("bar")) + + val reason: Int? by s1.optStored() + assertNull(s1.get("reason")) + assertNull(reason) + + s1.connectToStorage(s2) + + // s1 overwrites s2! + assertEquals("bar", s1.read("foo")) + + // don't change + assertEquals("bar2", s1.read("foo2")) + + // pull from s1 + assertEquals("buzz", s1.read("bar")) + + assertEquals(42, s1.read("reason")) + assertEquals(42, reason) + } +} \ No newline at end of file diff --git a/src/wasmJsTest/kotlin/StorageTest.kt b/src/wasmJsTest/kotlin/StorageTest.kt index 90cd813..36033c5 100644 --- a/src/wasmJsTest/kotlin/StorageTest.kt +++ b/src/wasmJsTest/kotlin/StorageTest.kt @@ -34,5 +34,4 @@ class StorageTest { s2.write("test_$i", "payload_$i") } } - } \ No newline at end of file