164 lines
4.8 KiB
Kotlin

package net.sergeych.parsec3
import net.sergeych.boss_serialization.BossDecoder
import net.sergeych.boss_serialization_mp.BossEncoder
import net.sergeych.mp_logger.LogTag
import net.sergeych.mp_logger.exception
import net.sergeych.mp_logger.info
import net.sergeych.mptools.encodeToHex
import net.sergeych.mptools.toDump
import kotlin.reflect.KProperty
import kotlin.reflect.KType
import kotlin.reflect.typeOf
/**
* Generic storage of binary content. PArsec uses boss encoding to store everything in it
* in te convenient way. See [KVStorage.stored] delegate. The [MemoryKVStorage] allows to
* store values in-memory and then connect some permanent stprage to it in a transparent way.
*/
interface KVStorage {
operator fun get(key: String): ByteArray?
operator fun set(key: String, value: ByteArray?)
/**
* Check whether key is in storage.
* Default implementation uses [keys]. You may override it for performance
*/
operator fun contains(key: String): Boolean = key in keys
val keys: Set<String>
/**
* Get number of object in the storage
* Default implementation uses [keys]. You may override it for performance
*/
val size: Int get() = keys.size
/**
* Clears all objects in the storage
* Default implementation uses [keys]. You may override it for performance
*/
fun clear() {
for (k in keys) this[k] = null
}
/**
* Default implementation uses [keys]. You may override it for performance
*/
fun isEmpty() = size == 0
/**
* Default implementation uses [keys]. You may override it for performance
*/
fun isNotEmpty() = size != 0
fun addAll(other: KVStorage) {
for (k in other.keys) {
this[k] = other[k]
}
}
}
inline operator fun <reified T> KVStorage.invoke(defaultValue: T, overrideName: String? = null) =
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
inline fun <reified T> KVStorage.stored(defaultValue: T, overrideName: String? = null) =
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
inline fun <reified T> KVStorage.optStored(defaultValue: T? = null, overrideName: String? = null) =
KVStorageDelegate<T?>(this, typeOf<T>(), defaultValue, overrideName)
class KVStorageDelegate<T>(
private val storage: KVStorage,
private val type: KType,
private val defaultValue: T,
private val overrideName: String? = null,
): LogTag("KVSD") {
private fun name(property: KProperty<*>): String = overrideName ?: property.name
private var cachedValue: T = defaultValue
private var cacheReady = false
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 = try {
info { " get ${name(property)}: ${type} [{${data.encodeToHex()}" }
BossDecoder.decodeFrom<T>(type, data)
.also { println("decoded as $it") }
} catch (e: Exception) {
exception { "failed to decode ${name(property)}: ${type}" to e }
defaultValue
}
cacheReady = true
return cachedValue
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
// if (!cacheReady || value != cachedValue) {
cachedValue = value
cacheReady = true
println("set ${name(property)} to ${BossEncoder.encode(type, value).toDump()}")
storage[name(property)] = BossEncoder.encode(type, value)
// }
}
}
/**
* Memory storage allows to connect existing in-memory content to some permanent storage on the fly
*/
class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage {
// is used when connected:
private var underlying: KVStorage? = null
// is used while underlying is null:
private val data = mutableMapOf<String, ByteArray>()
@Suppress("unused")
fun connectToStorage(other: KVStorage) {
other.addAll(this)
underlying = other
data.clear()
}
override fun get(key: String): ByteArray? {
underlying?.let {
return it[key]
}
return data[key]
}
override fun set(key: String, value: ByteArray?) {
underlying?.let { it[key] = value } ?: run {
if (value != null) data[key] = value
else data.remove(key)
}
}
override fun contains(key: String): Boolean {
underlying?.let { return key in it }
return key in data
}
override val keys: Set<String>
get() = underlying?.keys ?: data.keys
override fun clear() {
underlying?.clear() ?: data.clear()
}
init {
copyFrom?.let { addAll(it) }
}
}
expect fun defaultNamedStorage(name: String): KVStorage