Compare commits
No commits in common. "master" and "v0.1.6" have entirely different histories.
10
README.md
10
README.md
@ -6,10 +6,6 @@ in native targets.
|
|||||||
|
|
||||||
# Recent changes
|
# Recent changes
|
||||||
|
|
||||||
- 0.1.7 built with kotlin 2.0.20 which contains important fix in wasmJS
|
|
||||||
|
|
||||||
- 0.1.6 add many useful features, added support to wasmJS and all other platforms. Note to wasmJS: it appears to be a bug in wasm compiler so BipackDecoder could cause wasm loading problem.
|
|
||||||
|
|
||||||
- 0.1.1: added serialized KVStorage with handy implementation on JVM and JS platforms and some required synchronization
|
- 0.1.1: added serialized KVStorage with handy implementation on JVM and JS platforms and some required synchronization
|
||||||
tools.
|
tools.
|
||||||
-
|
-
|
||||||
@ -17,10 +13,6 @@ in native targets.
|
|||||||
|
|
||||||
The last 1.8-based version is 0.0.8. Some fixes are not yet backported to it pls leave an issue of needed.
|
The last 1.8-based version is 0.0.8. Some fixes are not yet backported to it pls leave an issue of needed.
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
Aside of the samples in this readme please see [library documentation](https://code.sergeych.net/docs/mp_bintools/).
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
Add our maven:
|
Add our maven:
|
||||||
@ -37,7 +29,7 @@ And add dependency to the proper place in your project like this:
|
|||||||
```kotlin
|
```kotlin
|
||||||
dependencies {
|
dependencies {
|
||||||
// ...
|
// ...
|
||||||
implementation("net.sergeych:mp_bintools:0.1.7")
|
implementation("net.sergeych:mp_bintools:0.1.0")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "2.0.20"
|
kotlin("multiplatform") version "2.0.0"
|
||||||
kotlin("plugin.serialization") version "2.0.20"
|
kotlin("plugin.serialization") version "2.0.0"
|
||||||
id("org.jetbrains.dokka") version "1.9.20"
|
id("org.jetbrains.dokka") version "1.9.20"
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
@ -8,7 +8,7 @@ plugins {
|
|||||||
val serialization_version = "1.6.5-SNAPSHOT"
|
val serialization_version = "1.6.5-SNAPSHOT"
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.1.8-SNAPSHOT"
|
version = "0.1.6-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -18,24 +18,40 @@ repositories {
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(8)
|
jvmToolchain(8)
|
||||||
jvm()
|
jvm {
|
||||||
|
// compilations.all {
|
||||||
|
// kotlinOptions.jvmTarget = "1.8"
|
||||||
|
// }
|
||||||
|
withJava()
|
||||||
|
testRuns["test"].executionTask.configure {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
}
|
||||||
js {
|
js {
|
||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
|
|
||||||
macosArm64()
|
// macosArm64()
|
||||||
iosX64()
|
// iosX64()
|
||||||
iosArm64()
|
// iosArm64()
|
||||||
macosX64()
|
// macosX64()
|
||||||
iosSimulatorArm64()
|
// iosSimulatorArm64()
|
||||||
linuxX64()
|
linuxX64()
|
||||||
linuxArm64()
|
linuxArm64()
|
||||||
mingwX64()
|
mingwX64()
|
||||||
|
// val hostOs = System.getProperty("os.name")
|
||||||
|
// val isMingwX64 = hostOs.startsWith("Windows")
|
||||||
|
// val nativeTarget = when {
|
||||||
|
// hostOs == "Mac OS X" -> macosX64("native")
|
||||||
|
// hostOs == "Linux" -> linuxX64("native")
|
||||||
|
// isMingwX64 -> mingwX64("native")
|
||||||
|
// else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
|
||||||
|
// }
|
||||||
|
|
||||||
wasmJs {
|
wasmJs {
|
||||||
browser()
|
browser()
|
||||||
binaries.executable()
|
// binaries.executable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
kotlin.js.compiler=ir
|
kotlin.js.compiler=ir
|
||||||
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,80 +0,0 @@
|
|||||||
package net.sergeych.bintools
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlin.math.min
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bytes sequence with comparison, concatenation, and string representation,
|
|
||||||
* could be used as hash keys for pure binary values, etc.
|
|
||||||
*/
|
|
||||||
@Suppress("unused")
|
|
||||||
@Serializable
|
|
||||||
class ByteChunk(val data: UByteArray): Comparable<ByteChunk> {
|
|
||||||
|
|
||||||
val size: Int get() = data.size
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Per-byte comparison also of different length. From two chunks
|
|
||||||
* of different size but equal beginning, the shorter is considered
|
|
||||||
* the smaller.
|
|
||||||
*/
|
|
||||||
override fun compareTo(other: ByteChunk): Int {
|
|
||||||
val limit = min(size, other.size)
|
|
||||||
for( i in 0 ..< limit) {
|
|
||||||
val own = data[i]
|
|
||||||
val their = other.data[i]
|
|
||||||
if( own < their) return -1
|
|
||||||
else if( own > their) return 1
|
|
||||||
}
|
|
||||||
if( size < other.size ) return -1
|
|
||||||
if( size > other.size ) return 1
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Equal chunks means content equality.
|
|
||||||
*/
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other !is ByteChunk) return false
|
|
||||||
|
|
||||||
return data contentEquals other.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Content-based hash code
|
|
||||||
*/
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return data.contentHashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hex representation of data
|
|
||||||
*/
|
|
||||||
override fun toString(): String = hex
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hex encoded data
|
|
||||||
*/
|
|
||||||
val hex by lazy { data.encodeToHex() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* human-readable dump
|
|
||||||
*/
|
|
||||||
val dump by lazy { data.toDump() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Concatenate two chunks and return new one
|
|
||||||
*/
|
|
||||||
operator fun plus(other: ByteChunk): ByteChunk = ByteChunk(data + other.data)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromHex(hex: String): ByteChunk = ByteChunk(hex.decodeHex().asUByteArray())
|
|
||||||
fun random(size: Int=16): ByteChunk = Random.nextBytes(size).asChunk()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ByteArray.asChunk() = ByteChunk(this.asUByteArray())
|
|
||||||
@Suppress("unused")
|
|
||||||
fun UByteArray.asChunk() = ByteChunk(this)
|
|
@ -2,18 +2,11 @@ package net.sergeych.bintools
|
|||||||
|
|
||||||
import net.sergeych.bipack.BipackDecoder
|
import net.sergeych.bipack.BipackDecoder
|
||||||
import net.sergeych.bipack.BipackEncoder
|
import net.sergeych.bipack.BipackEncoder
|
||||||
import net.sergeych.mp_logger.LogTag
|
|
||||||
import net.sergeych.mp_logger.Loggable
|
|
||||||
import net.sergeych.mp_logger.debug
|
|
||||||
import net.sergeych.synctools.AtomicCounter
|
|
||||||
import net.sergeych.synctools.ProtectedOp
|
import net.sergeych.synctools.ProtectedOp
|
||||||
import net.sergeych.synctools.WaitHandle
|
import net.sergeych.synctools.WaitHandle
|
||||||
import net.sergeych.synctools.withLock
|
import net.sergeych.synctools.withLock
|
||||||
|
|
||||||
private val ac = AtomicCounter(0)
|
class DataKVStorage(private val provider: DataProvider) : KVStorage {
|
||||||
|
|
||||||
class DataKVStorage(private val provider: DataProvider) : KVStorage,
|
|
||||||
Loggable by LogTag("DKVS${ac.incrementAndGet()}") {
|
|
||||||
|
|
||||||
data class Lock(val name: String) {
|
data class Lock(val name: String) {
|
||||||
private val exclusive = ProtectedOp()
|
private val exclusive = ProtectedOp()
|
||||||
@ -27,6 +20,8 @@ class DataKVStorage(private val provider: DataProvider) : KVStorage,
|
|||||||
exclusive.withLock {
|
exclusive.withLock {
|
||||||
if (readerCount == 0) {
|
if (readerCount == 0) {
|
||||||
return f()
|
return f()
|
||||||
|
} else {
|
||||||
|
println("can't lock $this: count is $readerCount")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pulses.await()
|
pulses.await()
|
||||||
@ -54,16 +49,19 @@ class DataKVStorage(private val provider: DataProvider) : KVStorage,
|
|||||||
access.withLock {
|
access.withLock {
|
||||||
// TODO: read keys
|
// TODO: read keys
|
||||||
for (fn in provider.list()) {
|
for (fn in provider.list()) {
|
||||||
debug { "Scanning: $fn" }
|
println("Scanning: $fn")
|
||||||
if (fn.endsWith(".d")) {
|
if (fn.endsWith(".d")) {
|
||||||
val id = fn.dropLast(2).toInt(16)
|
val id = fn.dropLast(2).toInt(16)
|
||||||
debug { "found data record: $fn -> $id" }
|
println("found data record: $fn -> $id")
|
||||||
|
|
||||||
val name = provider.read(fn) { BipackDecoder.decode<String>(it) }
|
val name = provider.read(fn) { BipackDecoder.decode<String>(it) }
|
||||||
|
println("Key=$name")
|
||||||
keyIds[name] = id
|
keyIds[name] = id
|
||||||
if (id > lastId) lastId = id
|
if (id > lastId) lastId = id
|
||||||
} else debug { "ignoring record $fn" }
|
} else println("ignoring record $fn")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println("initialized, ${keyIds.size} records found, lastId=$lastId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -99,7 +97,7 @@ class DataKVStorage(private val provider: DataProvider) : KVStorage,
|
|||||||
lock.lockExclusive { provider.write(recordName(id), f) }
|
lock.lockExclusive { provider.write(recordName(id), f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteEntry(name: String) {
|
fun deleteEntry(name: String) {
|
||||||
// fast pre-check:
|
// fast pre-check:
|
||||||
if (name !in keyIds) return
|
if (name !in keyIds) return
|
||||||
// global lock: we can't now detect concurrent delete + write ops, so exclusive:
|
// global lock: we can't now detect concurrent delete + write ops, so exclusive:
|
||||||
|
@ -119,6 +119,7 @@ class KVStorageDelegate<T>(
|
|||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
if (cacheReady) return cachedValue
|
if (cacheReady) return cachedValue
|
||||||
val data = storage.get(name(property))
|
val data = storage.get(name(property))
|
||||||
|
println("Got data: ${data?.toDump()}")
|
||||||
if (data == null)
|
if (data == null)
|
||||||
cachedValue = defaultValue
|
cachedValue = defaultValue
|
||||||
else
|
else
|
||||||
@ -131,6 +132,7 @@ class KVStorageDelegate<T>(
|
|||||||
// if (!cacheReady || value != cachedValue) {
|
// if (!cacheReady || value != cachedValue) {
|
||||||
cachedValue = value
|
cachedValue = value
|
||||||
cacheReady = true
|
cacheReady = true
|
||||||
|
println("set ${name(property)} to ${BipackEncoder.encode(serializer, value).toDump()}")
|
||||||
storage[name(property)] = BipackEncoder.encode(serializer, value)
|
storage[name(property)] = BipackEncoder.encode(serializer, value)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
@ -113,8 +113,6 @@ fun Collection<Byte>.encodeToHex(separator: String = " "): String = joinToString
|
|||||||
|
|
||||||
fun ByteArray.toDump(wide: Boolean = false): String = toDumpLines(wide).joinToString("\n")
|
fun ByteArray.toDump(wide: Boolean = false): String = toDumpLines(wide).joinToString("\n")
|
||||||
|
|
||||||
fun UByteArray.toDump(wide: Boolean = false): String = asByteArray().toDumpLines(wide).joinToString("\n")
|
|
||||||
|
|
||||||
fun ByteArray.toDumpLines(wide: Boolean = false): List<String> {
|
fun ByteArray.toDumpLines(wide: Boolean = false): List<String> {
|
||||||
|
|
||||||
val lineSize = if (wide) 32 else 16
|
val lineSize = if (wide) 32 else 16
|
||||||
|
@ -30,7 +30,6 @@ data class Foobar2(val bar: Int, val foo: Int, val other: Int = -1)
|
|||||||
@Framed
|
@Framed
|
||||||
data class FoobarF1(val bar: Int, val foo: Int = 117)
|
data class FoobarF1(val bar: Int, val foo: Int = 117)
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@Framed
|
@Framed
|
||||||
@SerialName("bipack.FoobarF1")
|
@SerialName("bipack.FoobarF1")
|
||||||
@ -441,21 +440,4 @@ class BipackEncoderTest {
|
|||||||
println(y)
|
println(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class T1(@Fixed val i: Byte)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testFixedByte() {
|
|
||||||
fun t1(i: Int) {
|
|
||||||
val packed = BipackEncoder.encode(T1(i.toByte()))
|
|
||||||
println(packed.toDump())
|
|
||||||
assertEquals(1, packed.size)
|
|
||||||
assertEquals(i, BipackDecoder.decode<T1>(packed).i.toInt())
|
|
||||||
}
|
|
||||||
t1(127)
|
|
||||||
t1(-127)
|
|
||||||
t1(1)
|
|
||||||
t1(-1)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,34 +0,0 @@
|
|||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package net.sergeych.synctools
|
|
||||||
|
|
||||||
import java.nio.channels.CompletionHandler
|
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.resumeWithException
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to handle Java continuation with Kotlin coroutines.
|
|
||||||
* Usage sample:
|
|
||||||
* ```kotlin
|
|
||||||
* val socket = withContext(Dispatchers.IO) {
|
|
||||||
* AsynchronousSocketChannel.open()
|
|
||||||
* }
|
|
||||||
* suspendCoroutine { cont ->
|
|
||||||
* socket.connect(address.socketAddress, cont, VoidCompletionHandler)
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
open class ContinuationHandler<T> : CompletionHandler<T, Continuation<T>> {
|
|
||||||
override fun completed(result: T, attachment: Continuation<T>) {
|
|
||||||
attachment.resume(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun failed(exc: Throwable, attachment: Continuation<T>) {
|
|
||||||
attachment.resumeWithException(exc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object VoidCompletionHandler : ContinuationHandler<Void>()
|
|
||||||
|
|
||||||
object IntCompletionHandler : ContinuationHandler<Int>()
|
|
9
src/wasmJsTest/kotlin/EmptyTest.kt
Normal file
9
src/wasmJsTest/kotlin/EmptyTest.kt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class EmptyTest {
|
||||||
|
@Test
|
||||||
|
fun emptyTest() {
|
||||||
|
println("hello!")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,6 +5,10 @@ import kotlin.test.assertNull
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class StorageTest {
|
class StorageTest {
|
||||||
|
@Test
|
||||||
|
fun emptyTest() {
|
||||||
|
println("hello")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun storageTest() {
|
fun storageTest() {
|
||||||
@ -23,6 +27,7 @@ class StorageTest {
|
|||||||
assertEquals(answer, 42)
|
assertEquals(answer, 42)
|
||||||
answer = 43
|
answer = 43
|
||||||
|
|
||||||
|
println("----------------------------------------------------------------")
|
||||||
val s2 = defaultNamedStorage("test_mp_bintools")
|
val s2 = defaultNamedStorage("test_mp_bintools")
|
||||||
val foo1 by s2.stored("?", "foo")
|
val foo1 by s2.stored("?", "foo")
|
||||||
val answer1: Int? by s2.optStored("answer")
|
val answer1: Int? by s2.optStored("answer")
|
||||||
|
Loading…
Reference in New Issue
Block a user