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
/.gigaide/gigaide.properties
/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
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

View File

@ -1,30 +1,26 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
kotlin("multiplatform") version "2.2.21"
kotlin("plugin.serialization") version "2.2.21"
kotlin("multiplatform") version "2.2.20"
kotlin("plugin.serialization") version "2.2.20"
id("org.jetbrains.dokka") version "1.9.20"
id("com.android.library") version "8.7.2"
`maven-publish`
}
val serialization_version = "1.9.0"
group = "net.sergeych"
version = "0.3.2-SNAPSHOT"
version = "0.2.2"
repositories {
google()
mavenCentral()
mavenLocal()
maven("https://maven.universablockchain.com/")
}
kotlin {
jvmToolchain(17)
jvmToolchain(8)
jvm()
androidTarget() {
// Ensure Android variant is published to Maven so consumers get androidMain APIs
publishLibraryVariants("release")
}
js {
browser()
nodejs()
@ -37,6 +33,7 @@ kotlin {
iosSimulatorArm64()
linuxX64()
linuxArm64()
mingwX64()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
@ -56,15 +53,15 @@ kotlin {
languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
languageSettings.optIn("kotlin.ExperimentalUnsignedTypes")
languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
languageSettings.optIn("kotlin.time.ExperimentalTime")
}
val commonMain by getting {
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
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
api("net.sergeych:mp_stools:[1.6.3,)")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
api("net.sergeych:mp_stools:[1.6.1,)")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
}
}
val nativeMain by creating {
@ -88,27 +85,19 @@ kotlin {
}
val jvmMain 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 {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-browser:0.5.0")
}
}
val jsTest by getting
// val nativeTest by getting
val wasmJsMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-browser-wasm-js:0.5.0")
}
}
val wasmJsTest by getting {
dependencies {
// implementation("org.jetbrains.kotlinx:kotlinx-browser:0.3")
implementation("org.jetbrains.kotlinx:kotlinx-browser:0.5.0")
}
}
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 {
outputDirectory.set(buildDir.resolve("dokka"))
dokkaSourceSets {

View File

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

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"

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
* (which also will be created if needed).
*
* - In Android (new!) it is implemented using preferences, but initializing is needed in
* 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).
* - For the native platorms it is not yet implemented (but will be soon).
*
* See [DataKVStorage] and [DataProvider] to implement a KVStorage on filesystems and like,
* and `FileDataProvider` class on JVM target.

View File

@ -1,5 +1,6 @@
package net.sergeych.bipack
import kotlinx.datetime.Instant
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
@ -11,7 +12,6 @@ import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.serializer
import net.sergeych.bintools.*
import kotlin.time.Instant
/**
* 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 {
return if (deserializer == serializer<Instant>())
return if (deserializer == Instant.serializer())
Instant.fromEpochMilliseconds(decodeLong()) as T
else
super.decodeSerializableValue(deserializer)

View File

@ -1,5 +1,6 @@
package net.sergeych.bipack
import kotlinx.datetime.Instant
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.AbstractEncoder
@ -8,7 +9,6 @@ import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.serializer
import net.sergeych.bintools.*
import kotlin.time.Instant
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.withLock
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import net.sergeych.mptools.withReentrantLock
import kotlin.time.Clock
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Instant
/**
* MRU cache with expiration, with safe async concurrent access.

View File

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

View File

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

View File

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