added more tools, start shifting to kotlin 2.2.0
This commit is contained in:
parent
a121f2c4a6
commit
01d9e0239e
@ -1,14 +1,14 @@
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform") version "2.0.21"
|
||||
kotlin("plugin.serialization") version "2.0.21"
|
||||
kotlin("multiplatform") version "2.2.20"
|
||||
kotlin("plugin.serialization") version "2.2.20"
|
||||
id("org.jetbrains.dokka") version "1.9.20"
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
group = "net.sergeych"
|
||||
version = "0.1.12-SNAPSHOT"
|
||||
version = "0.2.1-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@ -31,7 +31,6 @@ kotlin {
|
||||
// iosSimulatorArm64()
|
||||
linuxX64()
|
||||
linuxArm64()
|
||||
mingwX64()
|
||||
|
||||
@OptIn(ExperimentalWasmDsl::class)
|
||||
wasmJs {
|
||||
@ -90,6 +89,7 @@ kotlin {
|
||||
// val nativeTest by getting
|
||||
val wasmJsMain by getting {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-browser-wasm-js:0.5.0")
|
||||
}
|
||||
}
|
||||
val wasmJsTest by getting {
|
||||
|
@ -1,3 +1,3 @@
|
||||
kotlin.code.style=official
|
||||
kotlin.js.compiler=ir
|
||||
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
||||
kotlin.daemon.jvmargs=-Xmx3072M
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.sergeych.bintools
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.sergeych.mp_tools.encodeToBase64Compact
|
||||
import net.sergeych.mp_tools.encodeToBase64Url
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -53,7 +53,7 @@ class ByteChunk(val data: UByteArray): Comparable<ByteChunk> {
|
||||
/**
|
||||
* hex representation of data
|
||||
*/
|
||||
override fun toString(): String = hex
|
||||
override fun toString(): String = base64
|
||||
|
||||
/**
|
||||
* Hex encoded data
|
||||
@ -68,7 +68,7 @@ class ByteChunk(val data: UByteArray): Comparable<ByteChunk> {
|
||||
/**
|
||||
* Lazy encode to base64 with url alphabet, without trailing fill '=' characters.
|
||||
*/
|
||||
val base64 by lazy { data.asByteArray().encodeToBase64Compact() }
|
||||
val base64 by lazy { data.asByteArray().encodeToBase64Url() }
|
||||
|
||||
/**
|
||||
* Lazy (cached) view of [data] as ByteArray
|
||||
|
24
src/commonMain/kotlin/net/sergeych/tools/Expiring.kt
Normal file
24
src/commonMain/kotlin/net/sergeych/tools/Expiring.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package net.sergecyh.diwan.tools
|
||||
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* Value with expiration.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class Expiring<T>(
|
||||
val value: T,
|
||||
val expiresAt: Instant,
|
||||
) {
|
||||
constructor(value: T, expiresIn: Duration) : this(value, Clock.System.now() + expiresIn)
|
||||
|
||||
/**
|
||||
* @return value if not expired, null otherwise
|
||||
*/
|
||||
fun valueOrNull(): T? = if( isExpired ) value else null
|
||||
|
||||
val isExpired: Boolean get() = expiresAt < Clock.System.now()
|
||||
val isOk: Boolean get() = !isExpired
|
||||
}
|
29
src/commonMain/kotlin/net/sergeych/tools/IndividualLock.kt
Normal file
29
src/commonMain/kotlin/net/sergeych/tools/IndividualLock.kt
Normal file
@ -0,0 +1,29 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.sergecyh.diwan.tools
|
||||
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
/**
|
||||
* Experimental
|
||||
*
|
||||
* set of mutexes associated with keys `K`, so each key can have its own mutex
|
||||
* used with [lock] or [withLock]
|
||||
*/
|
||||
class IndividualLock<K> {
|
||||
|
||||
private val access = Mutex()
|
||||
private val locks = mutableMapOf<K, Mutex>()
|
||||
|
||||
suspend inline fun <T>withLock(key: K, block: ()->T): T = lock(key).use { block() }
|
||||
|
||||
suspend fun lock(key: K): AutoCloseable {
|
||||
val m = access.withLock {
|
||||
locks.getOrPut(key) { Mutex() }
|
||||
}
|
||||
m.lock()
|
||||
return AutoCloseable { m.unlock() }
|
||||
}
|
||||
|
||||
}
|
44
src/commonMain/kotlin/net/sergeych/tools/OncePer.kt
Normal file
44
src/commonMain/kotlin/net/sergeych/tools/OncePer.kt
Normal file
@ -0,0 +1,44 @@
|
||||
package net.sergecyh.diwan.tools
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
|
||||
/**
|
||||
* Coroutine-based unique "once-per-key" executor.
|
||||
*
|
||||
* When [invoke] is called, it checks that there is already invocation in progress
|
||||
* and either wait for it to complete or start a new one.
|
||||
* @param K key value, should have valid `hashCode` and `equals`, e.g., suitable to be a map key
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class OncePer<K> {
|
||||
private val access = Mutex()
|
||||
|
||||
private val queue = mutableMapOf<K,CompletableDeferred<Any>>()
|
||||
|
||||
/**
|
||||
* Execute [f] as unique-per-key [key]. If such invocation is in progress, it suspends than returns
|
||||
* its result. If there is no invocation for such a key, start new invocation.
|
||||
*
|
||||
* __Important note__ all simultaneous invocation should have the same [R] type, or, at least, a type
|
||||
* _castable to [R]_.
|
||||
*/
|
||||
suspend operator fun <R: Any>invoke(key: K, f: suspend ()->R ): R {
|
||||
var mustStart = false
|
||||
val d = access.withLock {
|
||||
queue.getOrPut(key) {
|
||||
CompletableDeferred<Any>().also { mustStart = true }
|
||||
}
|
||||
}
|
||||
if( mustStart ) {
|
||||
d.complete(f())
|
||||
access.withLock {
|
||||
queue.remove(key)
|
||||
}
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return d.await() as R
|
||||
}
|
||||
}
|
27
src/commonMain/kotlin/net/sergeych/tools/RateLimiter.kt
Normal file
27
src/commonMain/kotlin/net/sergeych/tools/RateLimiter.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package net.sergecyh.diwan.tools
|
||||
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* Experimental.
|
||||
*
|
||||
* limit invocations rate of [invoke] to once per [minimalInterval] or less frequent.
|
||||
* Note that it is not a debouncing, it just ignores too frequent calls!
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class RateLimiter(val minimalInterval: Duration) {
|
||||
var lastExecutedAt = Instant.DISTANT_PAST
|
||||
private set
|
||||
|
||||
/**
|
||||
* invoke [f] if the last invocation was earlier than now minus [minimalInterval], otherwise
|
||||
* do nothing.
|
||||
* @return the value returned by [f] if it was actually invoked this time, null otherwise
|
||||
*/
|
||||
suspend operator fun <T> invoke(f: suspend () -> T): T? =
|
||||
if (Clock.System.now() - lastExecutedAt > minimalInterval) {
|
||||
f().also { lastExecutedAt = Clock.System.now() }
|
||||
} else null
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user