Compare commits

..

2 Commits

Author SHA1 Message Date
1087a2c1c7 merge v0.2.2: kotlin 2.2.20 for all targets 2025-10-20 21:02:04 +02:00
ec91c0800d v0.1.8: kotlin 2.2.20 for all targets 2025-10-20 21:01:36 +02:00
16 changed files with 500 additions and 1405 deletions

1
.gitignore vendored
View File

@ -7,4 +7,3 @@
.kotlin .kotlin
/.gigaide/gigaide.properties /.gigaide/gigaide.properties
/java_pid40366.hprof /java_pid40366.hprof
/local.properties

View File

@ -4,11 +4,22 @@ Multiplatform binary tools collection, including portable serialization of the c
many useful tools to work with binary data, like CRC family checksums, dumps, etc. It works well also in the browser and many useful tools to work with binary data, like CRC family checksums, dumps, etc. It works well also in the browser and
in native targets. in native targets.
# Important note # Recent changes
Currently published version 0.3.0 for all platform is fully compatible with breaking kotlinx.datetime/kotlin.time migration as of Kotlin 2.2.21 and is __a recommended version to use__. - 0.1.12 published on all platforms. many small additions.
Sorry for inconveniences, it is all caused by strange ideas of the Kotlin team. - 0.1.11 added interesting collection classes: auto-sorted list with comparator, expirable cache, etc.
- 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
tools.
-
- 0.1.0: uses modern kotlin 1.9.*, fixes problem with singleton or empty/object serialization
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 # Documentation

View File

@ -1,30 +1,26 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins { plugins {
kotlin("multiplatform") version "2.2.21" kotlin("multiplatform") version "2.2.20"
kotlin("plugin.serialization") version "2.2.21" kotlin("plugin.serialization") version "2.2.20"
id("org.jetbrains.dokka") version "1.9.20" id("org.jetbrains.dokka") version "1.9.20"
id("com.android.library") version "8.7.2"
`maven-publish` `maven-publish`
} }
val serialization_version = "1.9.0"
group = "net.sergeych" group = "net.sergeych"
version = "0.3.2-SNAPSHOT" version = "0.2.2"
repositories { repositories {
google()
mavenCentral() mavenCentral()
mavenLocal() mavenLocal()
maven("https://maven.universablockchain.com/") maven("https://maven.universablockchain.com/")
} }
kotlin { kotlin {
jvmToolchain(17) jvmToolchain(8)
jvm() jvm()
androidTarget() {
// Ensure Android variant is published to Maven so consumers get androidMain APIs
publishLibraryVariants("release")
}
js { js {
browser() browser()
nodejs() nodejs()
@ -37,6 +33,7 @@ kotlin {
iosSimulatorArm64() iosSimulatorArm64()
linuxX64() linuxX64()
linuxArm64() linuxArm64()
mingwX64()
@OptIn(ExperimentalWasmDsl::class) @OptIn(ExperimentalWasmDsl::class)
wasmJs { wasmJs {
@ -56,15 +53,15 @@ kotlin {
languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi") languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
languageSettings.optIn("kotlin.ExperimentalUnsignedTypes") languageSettings.optIn("kotlin.ExperimentalUnsignedTypes")
languageSettings.optIn("kotlin.contracts.ExperimentalContracts") languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
languageSettings.optIn("kotlin.time.ExperimentalTime")
} }
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
// this is actually a bug: we need only the core, but bare core causes strange errors // this is actually a bug: we need only the core, but bare core causes strange errors
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
api("net.sergeych:mp_stools:[1.6.3,)") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1") api("net.sergeych:mp_stools:[1.6.1,)")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
} }
} }
val nativeMain by creating { val nativeMain by creating {
@ -88,27 +85,19 @@ kotlin {
} }
val jvmMain by getting val jvmMain by getting
val jvmTest by getting val jvmTest by getting
val androidMain by getting {
dependencies {
// Android base64 and preferences are in the SDK; no extra deps required
}
}
val jsMain by getting { val jsMain by getting {
dependencies { dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-browser:0.5.0")
} }
} }
val jsTest by getting val jsTest by getting
// 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") implementation("org.jetbrains.kotlinx:kotlinx-browser:0.5.0")
}
}
val wasmJsTest by getting {
dependencies {
// implementation("org.jetbrains.kotlinx:kotlinx-browser:0.3")
} }
} }
val wasmJsTest by getting
} }
} }
@ -129,27 +118,6 @@ publishing {
} }
} }
} }
android {
namespace = "net.sergeych.bintools"
compileSdk = 34
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
// Publish only release variant of the Android target so consumers (Android apps)
// resolve the platform artifact instead of common metadata, which is required
// to access Android-only APIs like net.sergeych.bintools.AndroidKV
publishing {
singleVariant("release") {
withSourcesJar()
}
}
}
tasks.dokkaHtml.configure { tasks.dokkaHtml.configure {
outputDirectory.set(buildDir.resolve("dokka")) outputDirectory.set(buildDir.resolve("dokka"))
dokkaSourceSets { dokkaSourceSets {

View File

@ -1,8 +1,3 @@
kotlin.code.style=official kotlin.code.style=official
kotlin.mpp.applyDefaultHierarchyTemplate=false kotlin.mpp.applyDefaultHierarchyTemplate=false
kotlin.daemon.jvmargs=-Xmx3072M
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8
org.gradle.configuration-cache=true
org.gradle.caching=true

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,3 @@
pluginManagement {
repositories {
google()
gradlePluginPortal()
mavenCentral()
mavenLocal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
mavenLocal()
maven("https://maven.universablockchain.com/")
}
}
rootProject.name = "mp_bintools" rootProject.name = "mp_bintools"

View File

@ -1,65 +0,0 @@
package net.sergeych.bintools
import android.content.Context
import android.content.SharedPreferences
import net.sergeych.bintools.AndroidKV.init
import net.sergeych.mp_tools.decodeBase64Compact
import net.sergeych.mp_tools.encodeToBase64Compact
/**
* Simple holder to provide Application [Context] for this library.
*
* Call [init] from your app (e.g., in `Application.onCreate`) before using
* [defaultNamedStorage].
*/
object AndroidKV {
@Volatile
private var appContext: Context? = null
fun init(context: Context) {
appContext = context.applicationContext
}
internal fun requireContext(): Context =
appContext ?: error("AndroidKV is not initialized. Call AndroidKV.init(applicationContext) first.")
}
/**
* Implementation of [KVStorage] backed by [SharedPreferences].
* Values are stored as base64-encoded strings to fit preferences capabilities.
*/
class SharedPreferencesKVStorage(
private val prefs: SharedPreferences,
keyPrefix: String = ""
) : KVStorage {
private val prefix = if (keyPrefix.isEmpty()) "" else "$keyPrefix:"
private fun k(key: String) = "$prefix$key"
override fun get(key: String): ByteArray? =
prefs.getString(k(key), null)?.decodeBase64Compact()
override fun set(key: String, value: ByteArray?) {
val e = prefs.edit()
val kk = k(key)
if (value == null) e.remove(kk)
else e.putString(kk, value.encodeToBase64Compact())
e.apply()
}
override val keys: Set<String>
get() {
val allKeys = prefs.all.keys
if (prefix.isEmpty()) return allKeys
return allKeys.filter { it.startsWith(prefix) }
.map { it.removePrefix(prefix) }
.toSet()
}
}
actual fun defaultNamedStorage(name: String): KVStorage {
val ctx = AndroidKV.requireContext()
val prefs = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
// We use separate preferences file per name, so prefix can be empty
return SharedPreferencesKVStorage(prefs)
}

View File

@ -1,17 +0,0 @@
package net.sergeych.synctools
import java.util.concurrent.locks.ReentrantLock
/**
* Android actual implementation mirrors JVM using [ReentrantLock].
*/
actual fun ProtectedOp(): ProtectedOpImplementation = object : ProtectedOpImplementation {
private val access = ReentrantLock()
override fun lock() {
access.lock()
}
override fun unlock() {
access.unlock()
}
}

View File

@ -1,22 +0,0 @@
package net.sergeych.synctools
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
actual class WaitHandle {
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
private val access = Object()
actual fun await(milliseconds: Long): Boolean {
return synchronized(access) {
try {
access.wait(milliseconds)
true
} catch (_: InterruptedException) {
false
}
}
}
actual fun wakeUp() {
synchronized(access) { access.notifyAll() }
}
}

View File

@ -218,19 +218,7 @@ class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage {
* - otherwise, the folder will be created in "`~/.local_storage`" parent directory * - otherwise, the folder will be created in "`~/.local_storage`" parent directory
* (which also will be created if needed). * (which also will be created if needed).
* *
* - In Android (new!) it is implemented using preferences, but initializing is needed in * - For the native platorms it is not yet implemented (but will be soon).
* main activity `onCreate`, somewhat like:
* ```kotlin
* override fun onCreate(savedInstanceState: Bundle?) {
* super.onCreate(savedInstanceState)
* // set the Context which Preferences will be used for
* // defaultNamedStorage
* net.sergeych.bintools.AndroidKV.init(this)
* }
* ```
* see also `AndroidKV` and `SharedPreferencesKVStorage` for Android
*
* - For the native platforms it is not yet implemented (but will be soon).
* *
* See [DataKVStorage] and [DataProvider] to implement a KVStorage on filesystems and like, * See [DataKVStorage] and [DataProvider] to implement a KVStorage on filesystems and like,
* and `FileDataProvider` class on JVM target. * and `FileDataProvider` class on JVM target.

View File

@ -1,5 +1,6 @@
package net.sergeych.bipack package net.sergeych.bipack
import kotlinx.datetime.Instant
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@ -11,7 +12,6 @@ import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import net.sergeych.bintools.* import net.sergeych.bintools.*
import kotlin.time.Instant
/** /**
* Decode BiPack format. Note that it relies on [DataSource] so can throw [DataSource.EndOfData] * Decode BiPack format. Note that it relies on [DataSource] so can throw [DataSource.EndOfData]
@ -74,7 +74,7 @@ class BipackDecoder(
} }
override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T { override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
return if (deserializer == serializer<Instant>()) return if (deserializer == Instant.serializer())
Instant.fromEpochMilliseconds(decodeLong()) as T Instant.fromEpochMilliseconds(decodeLong()) as T
else else
super.decodeSerializableValue(deserializer) super.decodeSerializableValue(deserializer)

View File

@ -1,5 +1,6 @@
package net.sergeych.bipack package net.sergeych.bipack
import kotlinx.datetime.Instant
import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.AbstractEncoder import kotlinx.serialization.encoding.AbstractEncoder
@ -8,7 +9,6 @@ import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import net.sergeych.bintools.* import net.sergeych.bintools.*
import kotlin.time.Instant
class BipackEncoder(val output: DataSink) : AbstractEncoder() { class BipackEncoder(val output: DataSink) : AbstractEncoder() {

View File

@ -2,11 +2,11 @@ package net.sergeych.collections
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import net.sergeych.mptools.withReentrantLock import net.sergeych.mptools.withReentrantLock
import kotlin.time.Clock
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import kotlin.time.Instant
/** /**
* MRU cache with expiration, with safe async concurrent access. * MRU cache with expiration, with safe async concurrent access.

View File

@ -1,8 +1,8 @@
package net.sergecyh.diwan.tools package net.sergecyh.diwan.tools
import kotlin.time.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Instant
/** /**
* Value with expiration. * Value with expiration.

View File

@ -1,8 +1,8 @@
package net.sergecyh.diwan.tools package net.sergecyh.diwan.tools
import kotlin.time.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Instant
/** /**
* Experimental. * Experimental.

View File

@ -1,5 +1,7 @@
package bipack package bipack
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.sergeych.bintools.encodeToHex import net.sergeych.bintools.encodeToHex
@ -11,8 +13,6 @@ import kotlin.test.Test
import kotlin.test.assertContentEquals import kotlin.test.assertContentEquals
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.time.Clock
import kotlin.time.Instant
@Serializable @Serializable
data class Foobar1N(val bar: Int, val foo: Int = 117) data class Foobar1N(val bar: Int, val foo: Int = 117)