Compare commits
No commits in common. "e7f44bb5a0b4666e7ea4e52f41326e3923447d3c" and "1ccb8ea8b30e3c856c81efabc6d9dcfe3e47e119" have entirely different histories.
e7f44bb5a0
...
1ccb8ea8b3
@ -1,14 +1,14 @@
|
|||||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "2.2.20"
|
kotlin("multiplatform") version "2.0.21"
|
||||||
kotlin("plugin.serialization") version "2.2.20"
|
kotlin("plugin.serialization") version "2.0.21"
|
||||||
id("org.jetbrains.dokka") version "1.9.20"
|
id("org.jetbrains.dokka") version "1.9.20"
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.2.1-SNAPSHOT"
|
version = "0.1.12"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -24,13 +24,14 @@ kotlin {
|
|||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
|
|
||||||
// macosArm64()
|
macosArm64()
|
||||||
// iosX64()
|
iosX64()
|
||||||
// iosArm64()
|
iosArm64()
|
||||||
// macosX64()
|
macosX64()
|
||||||
// iosSimulatorArm64()
|
iosSimulatorArm64()
|
||||||
linuxX64()
|
linuxX64()
|
||||||
linuxArm64()
|
linuxArm64()
|
||||||
|
mingwX64()
|
||||||
|
|
||||||
@OptIn(ExperimentalWasmDsl::class)
|
@OptIn(ExperimentalWasmDsl::class)
|
||||||
wasmJs {
|
wasmJs {
|
||||||
@ -89,7 +90,6 @@ kotlin {
|
|||||||
// val nativeTest by getting
|
// val nativeTest by getting
|
||||||
val wasmJsMain by getting {
|
val wasmJsMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-browser-wasm-js:0.5.0")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val wasmJsTest by getting {
|
val wasmJsTest by getting {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
kotlin.js.compiler=ir
|
||||||
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
||||||
kotlin.daemon.jvmargs=-Xmx3072M
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.sergeych.bintools
|
package net.sergeych.bintools
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.sergeych.mp_tools.encodeToBase64Url
|
import net.sergeych.mp_tools.encodeToBase64Compact
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class ByteChunk(val data: UByteArray): Comparable<ByteChunk> {
|
|||||||
/**
|
/**
|
||||||
* hex representation of data
|
* hex representation of data
|
||||||
*/
|
*/
|
||||||
override fun toString(): String = base64
|
override fun toString(): String = hex
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hex encoded data
|
* 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.
|
* Lazy encode to base64 with url alphabet, without trailing fill '=' characters.
|
||||||
*/
|
*/
|
||||||
val base64 by lazy { data.asByteArray().encodeToBase64Url() }
|
val base64 by lazy { data.asByteArray().encodeToBase64Compact() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy (cached) view of [data] as ByteArray
|
* Lazy (cached) view of [data] as ByteArray
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
@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() }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
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