Compare commits

..

2 Commits

Author SHA1 Message Date
40fe234070 improved KVStorage support 2023-01-20 02:55:12 +01:00
361d3f0b2b improved KVStorage support 2023-01-20 02:54:36 +01:00
7 changed files with 115 additions and 11 deletions

View File

@ -2,7 +2,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
Current stable version is __0.4.2-SNAPSHOT__. The full-dpulex RPC over websock is ok, the security level is still disabled until the protocol part will be sabilized. Current stable version is __0.4.3-SNAPSHOT__. The full-dpulex RPC over websock is ok, the security level is still disabled until the protocol part will be sabilized.
This is a connection-agnostic, kotlin multiplaftorm library providing full-duplex RPC type binary protocol, effective to work with binary data, such as encrypted data, keys, multimedia, etc. Default implementation uses websockets transport (binary frames) available on all supported platofrms (currently, JS and JVM). This is a connection-agnostic, kotlin multiplaftorm library providing full-duplex RPC type binary protocol, effective to work with binary data, such as encrypted data, keys, multimedia, etc. Default implementation uses websockets transport (binary frames) available on all supported platofrms (currently, JS and JVM).
@ -46,7 +46,7 @@ repsitories {
//... //...
dependencies { dependencies {
api("net.sergeych:parsec3:0.4.2-SNAPSHOT") api("net.sergeych:parsec3:0.4.3-SNAPSHOT")
} }
``` ```

View File

@ -10,7 +10,7 @@ plugins {
} }
group = "net.sergeych" group = "net.sergeych"
version = "0.4.2-SNAPSHOT" version = "0.4.3-SNAPSHOT"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -16,15 +16,40 @@ import kotlin.reflect.typeOf
interface KVStorage { interface KVStorage {
operator fun get(key: String): ByteArray? operator fun get(key: String): ByteArray?
operator fun set(key: String, value: ByteArray?) operator fun set(key: String, value: ByteArray?)
operator fun contains(key: String): Boolean
/**
* 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>
val keys: Collection<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() { fun clear() {
for (k in keys) this[k] = null 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) { fun addAll(other: KVStorage) {
for (k in other.keys) { for (k in other.keys) {
this[k] = other[k] this[k] = other[k]
@ -38,6 +63,8 @@ inline operator fun <reified T> KVStorage.invoke(defaultValue: T,overrideName: S
inline fun <reified T> KVStorage.stored(defaultValue: T, overrideName: String? = null) = inline fun <reified T> KVStorage.stored(defaultValue: T, overrideName: String? = null) =
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName) 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>( class KVStorageDelegate<T>(
private val storage: KVStorage, private val storage: KVStorage,
@ -110,7 +137,7 @@ class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage {
return key in data return key in data
} }
override val keys: Collection<String> override val keys: Set<String>
get() = underlying?.keys ?: data.keys get() = underlying?.keys ?: data.keys
override fun clear() { override fun clear() {

View File

@ -0,0 +1,39 @@
package net.sergeych.parsec3
import net.sergeych.mp_tools.decodeBase64Compact
import net.sergeych.mp_tools.encodeToBase64Compact
import org.w3c.dom.Storage
import org.w3c.dom.set
/**
* Default KV storage in browser. Use if with `localStorage` or `sessionStorage`. Uses
* prefix for storage values to not to collide with other data
*/
class BrowserKVStorage(keyPrefix: String, private val bst: Storage) : KVStorage {
private val prefix = "$keyPrefix:"
fun k(key: String) = "$prefix$key"
override fun get(key: String): ByteArray? {
return bst.getItem(k(key))?.decodeBase64Compact()
}
override fun set(key: String, value: ByteArray?) {
val corrected = k(key)
if (value == null)
bst.removeItem(corrected)
else
bst.set(corrected, value.encodeToBase64Compact())
}
override val keys: Set<String>
get() {
val kk = mutableListOf<String>()
for (i in 0 until bst.length) {
val k = bst.key(i) ?: break
if( k.startsWith(prefix)) {
kk += k.substring(prefix.length)
}
}
return kk.toSet()
}
}

View File

@ -1,5 +1,7 @@
package net.sergeych.parsec3 package net.sergeych.parsec3
actual fun defaultNamedStorage(name: String): KVStorage { import kotlinx.browser.localStorage
TODO("Not yet implemented")
}
@Suppress("unused")
actual fun defaultNamedStorage(name: String): KVStorage = BrowserKVStorage(name, localStorage)

View File

@ -0,0 +1,36 @@
import kotlinx.browser.sessionStorage
import net.sergeych.parsec3.BrowserKVStorage
import net.sergeych.parsec3.optStored
import kotlin.test.*
class testBrowserKVStorage {
@Test
fun testStorage() {
val st = BrowserKVStorage("tpr", sessionStorage)
var foo: String? by st.optStored()
assertNull(foo)
foo = "bar"
assertEquals("bar", foo)
println(st.keys)
assertTrue { "foo" in st.keys }
assertFalse { "bar" in st.keys }
var bar: Int? by st.optStored()
assertNull(bar)
bar = 42
assertEquals(42, bar)
assertTrue { "foo" in st.keys }
assertTrue { "bar" in st.keys }
assertEquals("bar", foo)
val st2 = BrowserKVStorage("tpr", sessionStorage)
val foo2: String? by st.optStored(overrideName = "foo")
assertEquals("bar", foo2)
assertTrue(st2.isNotEmpty())
assertTrue(st.isNotEmpty())
st.clear()
assertTrue(st.isEmpty())
}
}

View File

@ -97,7 +97,7 @@ internal class WsServerKtTest {
@Test @Test
fun testWsServerReconnect() { fun testWsServerReconnect() {
embeddedServer(Netty, port = 8080) { embeddedServer(Netty, port = 8090) {
parsec3TransportServer( parsec3TransportServer(
TestApiServer, TestApiServer,
) { ) {
@ -112,7 +112,7 @@ internal class WsServerKtTest {
}.start(wait = false) }.start(wait = false)
Log.connectConsole(Log.Level.DEBUG) Log.connectConsole(Log.Level.DEBUG)
val client = Parsec3WSClient("ws://localhost:8080/api/p3", TestApiClient) { val client = Parsec3WSClient("ws://localhost:8090/api/p3", TestApiClient) {
on(api.bar) { on(api.bar) {
"bar:$it" "bar:$it"
} }