From a7662c8cd483475fe7b16bc78b7a56e607dcc434 Mon Sep 17 00:00:00 2001 From: Russell Wolf Date: Sat, 15 Jan 2022 15:32:34 -0500 Subject: [PATCH] WIP QDBM multiple implementations --- .github/workflows/build-linux.yml | 4 + .../api/multiplatform-settings.klib.api | 87 +++++++++ multiplatform-settings/build.gradle.kts | 7 + .../linuxX64Main/kotlin/QdbmDepotSettings.kt | 122 +++++++++++++ .../linuxX64Main/kotlin/QdbmRelicSettings.kt | 170 ++++++++++++++++++ .../linuxX64Main/kotlin/QdbmVillaSettings.kt | 126 +++++++++++++ .../kotlin/QdbmDepotSettingsTest.kt | 102 +++++++++++ .../kotlin/QdbmRelicSettingsTest.kt | 135 ++++++++++++++ .../kotlin/QdbmVillaSettingsTest.kt | 102 +++++++++++ .../src/nativeInterop/cinterop/qdbm-depot.def | 4 + .../src/nativeInterop/cinterop/qdbm-relic.def | 4 + .../src/nativeInterop/cinterop/qdbm-villa.def | 4 + 12 files changed, 867 insertions(+) create mode 100644 multiplatform-settings/src/linuxX64Main/kotlin/QdbmDepotSettings.kt create mode 100644 multiplatform-settings/src/linuxX64Main/kotlin/QdbmRelicSettings.kt create mode 100644 multiplatform-settings/src/linuxX64Main/kotlin/QdbmVillaSettings.kt create mode 100644 multiplatform-settings/src/linuxX64Test/kotlin/QdbmDepotSettingsTest.kt create mode 100644 multiplatform-settings/src/linuxX64Test/kotlin/QdbmRelicSettingsTest.kt create mode 100644 multiplatform-settings/src/linuxX64Test/kotlin/QdbmVillaSettingsTest.kt create mode 100644 multiplatform-settings/src/nativeInterop/cinterop/qdbm-depot.def create mode 100644 multiplatform-settings/src/nativeInterop/cinterop/qdbm-relic.def create mode 100644 multiplatform-settings/src/nativeInterop/cinterop/qdbm-villa.def diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index f214f13f..15f4ec7e 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -25,6 +25,10 @@ jobs: restore-keys: | ${{ runner.os }}-konan- + - name: QDBM install + run: | + sudo apt-get install libqdbm-dev + - name: Linux build run: | ./gradlew build publishToMavenLocal --no-daemon --stacktrace diff --git a/multiplatform-settings/api/multiplatform-settings.klib.api b/multiplatform-settings/api/multiplatform-settings.klib.api index e16a837f..78f07259 100644 --- a/multiplatform-settings/api/multiplatform-settings.klib.api +++ b/multiplatform-settings/api/multiplatform-settings.klib.api @@ -202,6 +202,93 @@ final class com.russhwolf.settings/StorageSettings : com.russhwolf.settings/Sett final val size // com.russhwolf.settings/StorageSettings.size|{}size[0] final fun (): kotlin/Int // com.russhwolf.settings/StorageSettings.size.|(){}[0] } +// Targets: [linuxX64] +final class com.russhwolf.settings/QdbmDepotSettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/QdbmDepotSettings|null[0] + constructor (kotlin/String) // com.russhwolf.settings/QdbmDepotSettings.|(kotlin.String){}[0] + final fun clear() // com.russhwolf.settings/QdbmDepotSettings.clear|clear(){}[0] + final fun getBoolean(kotlin/String, kotlin/Boolean): kotlin/Boolean // com.russhwolf.settings/QdbmDepotSettings.getBoolean|getBoolean(kotlin.String;kotlin.Boolean){}[0] + final fun getBooleanOrNull(kotlin/String): kotlin/Boolean? // com.russhwolf.settings/QdbmDepotSettings.getBooleanOrNull|getBooleanOrNull(kotlin.String){}[0] + final fun getDouble(kotlin/String, kotlin/Double): kotlin/Double // com.russhwolf.settings/QdbmDepotSettings.getDouble|getDouble(kotlin.String;kotlin.Double){}[0] + final fun getDoubleOrNull(kotlin/String): kotlin/Double? // com.russhwolf.settings/QdbmDepotSettings.getDoubleOrNull|getDoubleOrNull(kotlin.String){}[0] + final fun getFloat(kotlin/String, kotlin/Float): kotlin/Float // com.russhwolf.settings/QdbmDepotSettings.getFloat|getFloat(kotlin.String;kotlin.Float){}[0] + final fun getFloatOrNull(kotlin/String): kotlin/Float? // com.russhwolf.settings/QdbmDepotSettings.getFloatOrNull|getFloatOrNull(kotlin.String){}[0] + final fun getInt(kotlin/String, kotlin/Int): kotlin/Int // com.russhwolf.settings/QdbmDepotSettings.getInt|getInt(kotlin.String;kotlin.Int){}[0] + final fun getIntOrNull(kotlin/String): kotlin/Int? // com.russhwolf.settings/QdbmDepotSettings.getIntOrNull|getIntOrNull(kotlin.String){}[0] + final fun getLong(kotlin/String, kotlin/Long): kotlin/Long // com.russhwolf.settings/QdbmDepotSettings.getLong|getLong(kotlin.String;kotlin.Long){}[0] + final fun getLongOrNull(kotlin/String): kotlin/Long? // com.russhwolf.settings/QdbmDepotSettings.getLongOrNull|getLongOrNull(kotlin.String){}[0] + final fun getString(kotlin/String, kotlin/String): kotlin/String // com.russhwolf.settings/QdbmDepotSettings.getString|getString(kotlin.String;kotlin.String){}[0] + final fun getStringOrNull(kotlin/String): kotlin/String? // com.russhwolf.settings/QdbmDepotSettings.getStringOrNull|getStringOrNull(kotlin.String){}[0] + final fun hasKey(kotlin/String): kotlin/Boolean // com.russhwolf.settings/QdbmDepotSettings.hasKey|hasKey(kotlin.String){}[0] + final fun putBoolean(kotlin/String, kotlin/Boolean) // com.russhwolf.settings/QdbmDepotSettings.putBoolean|putBoolean(kotlin.String;kotlin.Boolean){}[0] + final fun putDouble(kotlin/String, kotlin/Double) // com.russhwolf.settings/QdbmDepotSettings.putDouble|putDouble(kotlin.String;kotlin.Double){}[0] + final fun putFloat(kotlin/String, kotlin/Float) // com.russhwolf.settings/QdbmDepotSettings.putFloat|putFloat(kotlin.String;kotlin.Float){}[0] + final fun putInt(kotlin/String, kotlin/Int) // com.russhwolf.settings/QdbmDepotSettings.putInt|putInt(kotlin.String;kotlin.Int){}[0] + final fun putLong(kotlin/String, kotlin/Long) // com.russhwolf.settings/QdbmDepotSettings.putLong|putLong(kotlin.String;kotlin.Long){}[0] + final fun putString(kotlin/String, kotlin/String) // com.russhwolf.settings/QdbmDepotSettings.putString|putString(kotlin.String;kotlin.String){}[0] + final fun remove(kotlin/String) // com.russhwolf.settings/QdbmDepotSettings.remove|remove(kotlin.String){}[0] + final val keys // com.russhwolf.settings/QdbmDepotSettings.keys|{}keys[0] + final fun (): kotlin.collections/Set // com.russhwolf.settings/QdbmDepotSettings.keys.|(){}[0] + final val size // com.russhwolf.settings/QdbmDepotSettings.size|{}size[0] + final fun (): kotlin/Int // com.russhwolf.settings/QdbmDepotSettings.size.|(){}[0] +} +// Targets: [linuxX64] +final class com.russhwolf.settings/QdbmRelicSettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/QdbmRelicSettings|null[0] + constructor (kotlin/String) // com.russhwolf.settings/QdbmRelicSettings.|(kotlin.String){}[0] + final fun clear() // com.russhwolf.settings/QdbmRelicSettings.clear|clear(){}[0] + final fun getBoolean(kotlin/String, kotlin/Boolean): kotlin/Boolean // com.russhwolf.settings/QdbmRelicSettings.getBoolean|getBoolean(kotlin.String;kotlin.Boolean){}[0] + final fun getBooleanOrNull(kotlin/String): kotlin/Boolean? // com.russhwolf.settings/QdbmRelicSettings.getBooleanOrNull|getBooleanOrNull(kotlin.String){}[0] + final fun getDouble(kotlin/String, kotlin/Double): kotlin/Double // com.russhwolf.settings/QdbmRelicSettings.getDouble|getDouble(kotlin.String;kotlin.Double){}[0] + final fun getDoubleOrNull(kotlin/String): kotlin/Double? // com.russhwolf.settings/QdbmRelicSettings.getDoubleOrNull|getDoubleOrNull(kotlin.String){}[0] + final fun getFloat(kotlin/String, kotlin/Float): kotlin/Float // com.russhwolf.settings/QdbmRelicSettings.getFloat|getFloat(kotlin.String;kotlin.Float){}[0] + final fun getFloatOrNull(kotlin/String): kotlin/Float? // com.russhwolf.settings/QdbmRelicSettings.getFloatOrNull|getFloatOrNull(kotlin.String){}[0] + final fun getInt(kotlin/String, kotlin/Int): kotlin/Int // com.russhwolf.settings/QdbmRelicSettings.getInt|getInt(kotlin.String;kotlin.Int){}[0] + final fun getIntOrNull(kotlin/String): kotlin/Int? // com.russhwolf.settings/QdbmRelicSettings.getIntOrNull|getIntOrNull(kotlin.String){}[0] + final fun getLong(kotlin/String, kotlin/Long): kotlin/Long // com.russhwolf.settings/QdbmRelicSettings.getLong|getLong(kotlin.String;kotlin.Long){}[0] + final fun getLongOrNull(kotlin/String): kotlin/Long? // com.russhwolf.settings/QdbmRelicSettings.getLongOrNull|getLongOrNull(kotlin.String){}[0] + final fun getString(kotlin/String, kotlin/String): kotlin/String // com.russhwolf.settings/QdbmRelicSettings.getString|getString(kotlin.String;kotlin.String){}[0] + final fun getStringOrNull(kotlin/String): kotlin/String? // com.russhwolf.settings/QdbmRelicSettings.getStringOrNull|getStringOrNull(kotlin.String){}[0] + final fun hasKey(kotlin/String): kotlin/Boolean // com.russhwolf.settings/QdbmRelicSettings.hasKey|hasKey(kotlin.String){}[0] + final fun putBoolean(kotlin/String, kotlin/Boolean) // com.russhwolf.settings/QdbmRelicSettings.putBoolean|putBoolean(kotlin.String;kotlin.Boolean){}[0] + final fun putDouble(kotlin/String, kotlin/Double) // com.russhwolf.settings/QdbmRelicSettings.putDouble|putDouble(kotlin.String;kotlin.Double){}[0] + final fun putFloat(kotlin/String, kotlin/Float) // com.russhwolf.settings/QdbmRelicSettings.putFloat|putFloat(kotlin.String;kotlin.Float){}[0] + final fun putInt(kotlin/String, kotlin/Int) // com.russhwolf.settings/QdbmRelicSettings.putInt|putInt(kotlin.String;kotlin.Int){}[0] + final fun putLong(kotlin/String, kotlin/Long) // com.russhwolf.settings/QdbmRelicSettings.putLong|putLong(kotlin.String;kotlin.Long){}[0] + final fun putString(kotlin/String, kotlin/String) // com.russhwolf.settings/QdbmRelicSettings.putString|putString(kotlin.String;kotlin.String){}[0] + final fun remove(kotlin/String) // com.russhwolf.settings/QdbmRelicSettings.remove|remove(kotlin.String){}[0] + final val keys // com.russhwolf.settings/QdbmRelicSettings.keys|{}keys[0] + final fun (): kotlin.collections/Set // com.russhwolf.settings/QdbmRelicSettings.keys.|(){}[0] + final val size // com.russhwolf.settings/QdbmRelicSettings.size|{}size[0] + final fun (): kotlin/Int // com.russhwolf.settings/QdbmRelicSettings.size.|(){}[0] +} +// Targets: [linuxX64] +final class com.russhwolf.settings/QdbmVillaSettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/QdbmVillaSettings|null[0] + constructor (kotlin/String) // com.russhwolf.settings/QdbmVillaSettings.|(kotlin.String){}[0] + final fun clear() // com.russhwolf.settings/QdbmVillaSettings.clear|clear(){}[0] + final fun getBoolean(kotlin/String, kotlin/Boolean): kotlin/Boolean // com.russhwolf.settings/QdbmVillaSettings.getBoolean|getBoolean(kotlin.String;kotlin.Boolean){}[0] + final fun getBooleanOrNull(kotlin/String): kotlin/Boolean? // com.russhwolf.settings/QdbmVillaSettings.getBooleanOrNull|getBooleanOrNull(kotlin.String){}[0] + final fun getDouble(kotlin/String, kotlin/Double): kotlin/Double // com.russhwolf.settings/QdbmVillaSettings.getDouble|getDouble(kotlin.String;kotlin.Double){}[0] + final fun getDoubleOrNull(kotlin/String): kotlin/Double? // com.russhwolf.settings/QdbmVillaSettings.getDoubleOrNull|getDoubleOrNull(kotlin.String){}[0] + final fun getFloat(kotlin/String, kotlin/Float): kotlin/Float // com.russhwolf.settings/QdbmVillaSettings.getFloat|getFloat(kotlin.String;kotlin.Float){}[0] + final fun getFloatOrNull(kotlin/String): kotlin/Float? // com.russhwolf.settings/QdbmVillaSettings.getFloatOrNull|getFloatOrNull(kotlin.String){}[0] + final fun getInt(kotlin/String, kotlin/Int): kotlin/Int // com.russhwolf.settings/QdbmVillaSettings.getInt|getInt(kotlin.String;kotlin.Int){}[0] + final fun getIntOrNull(kotlin/String): kotlin/Int? // com.russhwolf.settings/QdbmVillaSettings.getIntOrNull|getIntOrNull(kotlin.String){}[0] + final fun getLong(kotlin/String, kotlin/Long): kotlin/Long // com.russhwolf.settings/QdbmVillaSettings.getLong|getLong(kotlin.String;kotlin.Long){}[0] + final fun getLongOrNull(kotlin/String): kotlin/Long? // com.russhwolf.settings/QdbmVillaSettings.getLongOrNull|getLongOrNull(kotlin.String){}[0] + final fun getString(kotlin/String, kotlin/String): kotlin/String // com.russhwolf.settings/QdbmVillaSettings.getString|getString(kotlin.String;kotlin.String){}[0] + final fun getStringOrNull(kotlin/String): kotlin/String? // com.russhwolf.settings/QdbmVillaSettings.getStringOrNull|getStringOrNull(kotlin.String){}[0] + final fun hasKey(kotlin/String): kotlin/Boolean // com.russhwolf.settings/QdbmVillaSettings.hasKey|hasKey(kotlin.String){}[0] + final fun putBoolean(kotlin/String, kotlin/Boolean) // com.russhwolf.settings/QdbmVillaSettings.putBoolean|putBoolean(kotlin.String;kotlin.Boolean){}[0] + final fun putDouble(kotlin/String, kotlin/Double) // com.russhwolf.settings/QdbmVillaSettings.putDouble|putDouble(kotlin.String;kotlin.Double){}[0] + final fun putFloat(kotlin/String, kotlin/Float) // com.russhwolf.settings/QdbmVillaSettings.putFloat|putFloat(kotlin.String;kotlin.Float){}[0] + final fun putInt(kotlin/String, kotlin/Int) // com.russhwolf.settings/QdbmVillaSettings.putInt|putInt(kotlin.String;kotlin.Int){}[0] + final fun putLong(kotlin/String, kotlin/Long) // com.russhwolf.settings/QdbmVillaSettings.putLong|putLong(kotlin.String;kotlin.Long){}[0] + final fun putString(kotlin/String, kotlin/String) // com.russhwolf.settings/QdbmVillaSettings.putString|putString(kotlin.String;kotlin.String){}[0] + final fun remove(kotlin/String) // com.russhwolf.settings/QdbmVillaSettings.remove|remove(kotlin.String){}[0] + final val keys // com.russhwolf.settings/QdbmVillaSettings.keys|{}keys[0] + final fun (): kotlin.collections/Set // com.russhwolf.settings/QdbmVillaSettings.keys.|(){}[0] + final val size // com.russhwolf.settings/QdbmVillaSettings.size|{}size[0] + final fun (): kotlin/Int // com.russhwolf.settings/QdbmVillaSettings.size.|(){}[0] +} // Targets: [mingwX64] final class com.russhwolf.settings/RegistrySettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/RegistrySettings|null[0] constructor (kotlin/String) // com.russhwolf.settings/RegistrySettings.|(kotlin.String){}[0] diff --git a/multiplatform-settings/build.gradle.kts b/multiplatform-settings/build.gradle.kts index 8d1fc281..02fa37d0 100644 --- a/multiplatform-settings/build.gradle.kts +++ b/multiplatform-settings/build.gradle.kts @@ -17,6 +17,8 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl * limitations under the License. */ +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget + plugins { id("standard-configuration") id("module-publication") @@ -27,6 +29,11 @@ standardConfig { } kotlin { + targets.getByName("linuxX64") { + compilations["main"].cinterops.create("qdbm-depot") + compilations["main"].cinterops.create("qdbm-relic") + compilations["main"].cinterops.create("qdbm-villa") + } @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { freeCompilerArgs.add("-Xexpect-actual-classes") diff --git a/multiplatform-settings/src/linuxX64Main/kotlin/QdbmDepotSettings.kt b/multiplatform-settings/src/linuxX64Main/kotlin/QdbmDepotSettings.kt new file mode 100644 index 00000000..92468417 --- /dev/null +++ b/multiplatform-settings/src/linuxX64Main/kotlin/QdbmDepotSettings.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2022 Russell Wolf + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.russhwolf.settings + +import com.russhwolf.settings.cinterop.qdbm.depot.DEPOT +import com.russhwolf.settings.cinterop.qdbm.depot.DP_DOVER +import com.russhwolf.settings.cinterop.qdbm.depot.DP_OCREAT +import com.russhwolf.settings.cinterop.qdbm.depot.DP_OREADER +import com.russhwolf.settings.cinterop.qdbm.depot.DP_OWRITER +import com.russhwolf.settings.cinterop.qdbm.depot.dpclose +import com.russhwolf.settings.cinterop.qdbm.depot.dpecode +import com.russhwolf.settings.cinterop.qdbm.depot.dperrmsg +import com.russhwolf.settings.cinterop.qdbm.depot.dpget +import com.russhwolf.settings.cinterop.qdbm.depot.dpiterinit +import com.russhwolf.settings.cinterop.qdbm.depot.dpiternext +import com.russhwolf.settings.cinterop.qdbm.depot.dpopen +import com.russhwolf.settings.cinterop.qdbm.depot.dpout +import com.russhwolf.settings.cinterop.qdbm.depot.dpput +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.MemScope +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.toKString + +// TODO clean up error checking? +// TODO allow specifying directory +@OptIn(ExperimentalForeignApi::class) +@ExperimentalSettingsImplementation +public class QdbmDepotSettings(private val path: String) : Settings { + + override val keys: Set + get() = depotOperation { depot -> + depot.foldKeys(mutableListOf()) { list, key -> list.apply { add(key) } }.toSet() + } + + override val size: Int get() = depotOperation { depot -> depot.foldKeys(0) { size, _ -> size + 1 } } + + public override fun clear(): Unit = depotOperation { depot -> depot.forEachKey { dpout(depot, it, -1) } } + public override fun remove(key: String): Unit = depotOperation { depot -> dpout(depot, key, -1) } + public override fun hasKey(key: String): Boolean = depotOperation { depot -> + depot.forEachKey { if (key == it) return true } + return false + } + + public override fun putInt(key: String, value: Int): Unit = saveString(key, value.toString()) + public override fun getInt(key: String, defaultValue: Int): Int = getIntOrNull(key) ?: defaultValue + public override fun getIntOrNull(key: String): Int? = loadString(key)?.toInt() + + public override fun putLong(key: String, value: Long): Unit = saveString(key, value.toString()) + public override fun getLong(key: String, defaultValue: Long): Long = getLongOrNull(key) ?: defaultValue + public override fun getLongOrNull(key: String): Long? = loadString(key)?.toLong() + + public override fun putString(key: String, value: String): Unit = saveString(key, value) + public override fun getString(key: String, defaultValue: String): String = getStringOrNull(key) ?: defaultValue + public override fun getStringOrNull(key: String): String? = loadString(key) + + public override fun putFloat(key: String, value: Float): Unit = saveString(key, value.toString()) + public override fun getFloat(key: String, defaultValue: Float): Float = getFloatOrNull(key) ?: defaultValue + public override fun getFloatOrNull(key: String): Float? = loadString(key)?.toFloat() + + public override fun putDouble(key: String, value: Double): Unit = saveString(key, value.toString()) + public override fun getDouble(key: String, defaultValue: Double): Double = getDoubleOrNull(key) ?: defaultValue + public override fun getDoubleOrNull(key: String): Double? = loadString(key)?.toDouble() + + public override fun putBoolean(key: String, value: Boolean): Unit = saveString(key, value.toString()) + public override fun getBoolean(key: String, defaultValue: Boolean): Boolean = getBooleanOrNull(key) ?: defaultValue + public override fun getBooleanOrNull(key: String): Boolean? = loadString(key)?.toBoolean() + + private inline fun saveString(key: String, value: String): Unit = depotOperation { depot -> + dpput(depot, key, -1, value, -1, DP_DOVER.toInt()) + } + + private inline fun loadString(key: String): String? = depotOperation { depot -> + val output = dpget(depot, key, -1, 0, -1, null) + output?.toKString() + } + + private inline fun CPointer.forEachKey(block: (key: String) -> Unit) { + val depot = this + if (dpiterinit(depot) != 0) { + while (true) { + val key = dpiternext(depot, null)?.toKString() + if (key != null) { + block(key) + } else { + break + } + } + } + } + + private inline fun CPointer.foldKeys(initial: A, block: (accumulator: A, key: String) -> A): A { + var accumulator = initial + forEachKey { accumulator = block(accumulator, it) } + return accumulator + } + + private inline fun depotOperation(action: MemScope.(depot: CPointer) -> T): T = memScoped { + val depot = dpopen(path, (DP_OWRITER or DP_OREADER or DP_OCREAT).toInt(), 0) + if (depot == null) { + val message = dperrmsg(dpecode)?.toKString() + error("error on depot open: $message") + } + val out = action(depot) + dpclose(depot) + out + } +} diff --git a/multiplatform-settings/src/linuxX64Main/kotlin/QdbmRelicSettings.kt b/multiplatform-settings/src/linuxX64Main/kotlin/QdbmRelicSettings.kt new file mode 100644 index 00000000..72a15b41 --- /dev/null +++ b/multiplatform-settings/src/linuxX64Main/kotlin/QdbmRelicSettings.kt @@ -0,0 +1,170 @@ +/* + * Copyright 2022 Russell Wolf + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.russhwolf.settings + +import com.russhwolf.settings.cinterop.qdbm.relic.DBM +import com.russhwolf.settings.cinterop.qdbm.relic.DBM_REPLACE +import com.russhwolf.settings.cinterop.qdbm.relic.datum +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_clearerr +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_close +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_delete +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_error +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_fetch +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_firstkey +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_nextkey +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_open +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_store +import kotlinx.cinterop.ByteVar +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.CValue +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.MemScope +import kotlinx.cinterop.cValue +import kotlinx.cinterop.cstr +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.plus +import kotlinx.cinterop.pointed +import kotlinx.cinterop.reinterpret +import kotlinx.cinterop.toCValues +import kotlinx.cinterop.useContents +import kotlinx.cinterop.value +import platform.posix.O_CREAT +import platform.posix.O_RDWR +import platform.posix.S_IRGRP +import platform.posix.S_IROTH +import platform.posix.S_IRUSR +import platform.posix.S_IWUSR +import platform.posix.errno + +// TODO clean up error checking? +// TODO allow specifying directory +@OptIn(ExperimentalForeignApi::class) +@ExperimentalSettingsImplementation +public class QdbmRelicSettings(private val path: String) : Settings { + + override val keys: Set + get() = dbmOperation { dbm -> + dbm.foldKeys(mutableListOf()) { list, key -> list.apply { add(key.toKString()!!) } }.toSet() + } + + override val size: Int get() = dbmOperation { dbm -> dbm.foldKeys(0) { size, _ -> size + 1 } } + + public override fun clear(): Unit = dbmOperation { dbm -> dbm.forEachKey { dbm_delete(dbm, it) } } + public override fun remove(key: String): Unit = dbmOperation { dbm -> dbm_delete(dbm, datumOf(key)) } + public override fun hasKey(key: String): Boolean = dbmOperation { dbm -> + dbm.forEachKey { if (key == it.toKString()) return true } + return false + } + + public override fun putInt(key: String, value: Int): Unit = saveBytes(key, value.toByteArray()) + public override fun getInt(key: String, defaultValue: Int): Int = getIntOrNull(key) ?: defaultValue + public override fun getIntOrNull(key: String): Int? = loadBytes(key)?.toInt() + + public override fun putLong(key: String, value: Long): Unit = saveBytes(key, value.toByteArray()) + public override fun getLong(key: String, defaultValue: Long): Long = getLongOrNull(key) ?: defaultValue + public override fun getLongOrNull(key: String): Long? = loadBytes(key)?.toLong() + + public override fun putString(key: String, value: String): Unit = saveBytes(key, value.encodeToByteArray()) + public override fun getString(key: String, defaultValue: String): String = getStringOrNull(key) ?: defaultValue + public override fun getStringOrNull(key: String): String? = loadBytes(key)?.decodeToString() + + public override fun putFloat(key: String, value: Float): Unit = saveBytes(key, value.toRawBits().toByteArray()) + public override fun getFloat(key: String, defaultValue: Float): Float = getFloatOrNull(key) ?: defaultValue + public override fun getFloatOrNull(key: String): Float? = loadBytes(key)?.toInt()?.let { Float.fromBits(it) } + + public override fun putDouble(key: String, value: Double): Unit = saveBytes(key, value.toRawBits().toByteArray()) + public override fun getDouble(key: String, defaultValue: Double): Double = getDoubleOrNull(key) ?: defaultValue + public override fun getDoubleOrNull(key: String): Double? = loadBytes(key)?.toLong()?.let { Double.fromBits(it) } + + public override fun putBoolean(key: String, value: Boolean): Unit = saveBytes(key, byteArrayOf(if (value) 1 else 0)) + public override fun getBoolean(key: String, defaultValue: Boolean): Boolean = getBooleanOrNull(key) ?: defaultValue + public override fun getBooleanOrNull(key: String): Boolean? = loadBytes(key)?.get(0)?.equals(0.toByte())?.not() + + private inline fun saveBytes(key: String, bytes: ByteArray): Unit = dbmOperation { dbm -> + dbm_store(dbm, datumOf(key), datumOf(bytes), DBM_REPLACE.toInt()) + } + + private inline fun loadBytes(key: String): ByteArray? = dbmOperation { dbm -> + val datum = dbm_fetch(dbm, datumOf(key)) + datum.toByteArray() + } + + private inline fun CPointer.forEachKey(block: (key: CValue) -> Unit) { + val dbm = this + var key = dbm_firstkey(dbm) + while (key.useContents { dptr != null }) { + block(key) + key = dbm_nextkey(dbm) + } + } + + private inline fun CPointer.foldKeys(initial: A, block: (accumulator: A, key: CValue) -> A): A { + var accumulator = initial + forEachKey { accumulator = block(accumulator, it) } + return accumulator + } + + private inline fun dbmOperation(action: MemScope.(dbm: CPointer) -> T): T = memScoped { + val dbm = dbm_open(path.cstr, O_RDWR or O_CREAT, S_IRUSR or S_IWUSR or S_IRGRP or S_IROTH) + ?: error("Error on dbm_open: $errno") + val out = action(dbm) + val error = dbm_error(dbm) + if (error != 0) { + try { + error("error: $error") + } finally { + dbm_clearerr(dbm) + } + } + dbm_close(dbm) + out + } + + private inline fun ByteArray.toLong(): Long = foldIndexed(0) { index, total: Long, byte: Byte -> + ((0xff.toLong() and byte.toLong()) shl index * Byte.SIZE_BITS) or total + } + + private inline fun ByteArray.toInt(): Int = foldIndexed(0) { index, total: Int, byte: Byte -> + ((0xff and byte.toInt()) shl index * Byte.SIZE_BITS) or total + } + + private inline fun Long.toByteArray(): ByteArray = ByteArray(Long.SIZE_BYTES) { index -> + ((this shr (Byte.SIZE_BITS * index)) and 0xff).toByte() + } + + private inline fun Int.toByteArray(): ByteArray = ByteArray(Int.SIZE_BYTES) { index -> + ((this shr (Byte.SIZE_BITS * index)) and 0xff).toByte() + } + + private inline fun CValue.toKString(): String? = toByteArray()?.decodeToString() + private inline fun CValue.toByteArray(): ByteArray? = useContents { + val size = dsize.toInt() + val firstPtr: CPointer = dptr?.reinterpret() + ?: return null + return ByteArray(size) { + val pointedValue = firstPtr.plus(it)?.pointed?.value + pointedValue ?: 0 + } + } + + private inline fun MemScope.datumOf(string: String): CValue = datumOf(string.encodeToByteArray()) + private inline fun MemScope.datumOf(bytes: ByteArray): CValue = cValue { + val cValues = bytes.toCValues() + dptr = cValues.ptr + dsize = cValues.size.toULong() + } +} diff --git a/multiplatform-settings/src/linuxX64Main/kotlin/QdbmVillaSettings.kt b/multiplatform-settings/src/linuxX64Main/kotlin/QdbmVillaSettings.kt new file mode 100644 index 00000000..cf8c31f9 --- /dev/null +++ b/multiplatform-settings/src/linuxX64Main/kotlin/QdbmVillaSettings.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2022 Russell Wolf + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.russhwolf.settings + +import com.russhwolf.settings.cinterop.qdbm.villa.VILLA +import com.russhwolf.settings.cinterop.qdbm.villa.VL_CMPLEX +import com.russhwolf.settings.cinterop.qdbm.villa.VL_DOVER +import com.russhwolf.settings.cinterop.qdbm.villa.VL_OCREAT +import com.russhwolf.settings.cinterop.qdbm.villa.VL_OREADER +import com.russhwolf.settings.cinterop.qdbm.villa.VL_OWRITER +import com.russhwolf.settings.cinterop.qdbm.villa.dpecode +import com.russhwolf.settings.cinterop.qdbm.villa.dperrmsg +import com.russhwolf.settings.cinterop.qdbm.villa.vlclose +import com.russhwolf.settings.cinterop.qdbm.villa.vlcurfirst +import com.russhwolf.settings.cinterop.qdbm.villa.vlcurkey +import com.russhwolf.settings.cinterop.qdbm.villa.vlcurnext +import com.russhwolf.settings.cinterop.qdbm.villa.vlget +import com.russhwolf.settings.cinterop.qdbm.villa.vlopen +import com.russhwolf.settings.cinterop.qdbm.villa.vlout +import com.russhwolf.settings.cinterop.qdbm.villa.vlput +import com.russhwolf.settings.cinterop.qdbm.villa.vlrnum +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.MemScope +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.toKString + +// TODO clean up error checking? +// TODO allow specifying directory +@OptIn(ExperimentalForeignApi::class) +@ExperimentalSettingsImplementation +public class QdbmVillaSettings(private val path: String) : Settings { + + override val keys: Set + get() = villaOperation { villa -> + villa.foldKeys(mutableListOf()) { list, key -> list.apply { add(key) } }.toSet() + } + + override val size: Int get() = villaOperation { villa -> vlrnum(villa) } + + public override fun clear(): Unit = keys.forEach { remove(it) } + + //villaOperation { villa -> villa.forEachKey { vlout(villa, it, -1) } } + public override fun remove(key: String): Unit = villaOperation { villa -> vlout(villa, key, -1) } + public override fun hasKey(key: String): Boolean = villaOperation { villa -> + villa.forEachKey { if (key == it) return true } + return false + } + + public override fun putInt(key: String, value: Int): Unit = saveString(key, value.toString()) + public override fun getInt(key: String, defaultValue: Int): Int = getIntOrNull(key) ?: defaultValue + public override fun getIntOrNull(key: String): Int? = loadString(key)?.toInt() + + public override fun putLong(key: String, value: Long): Unit = saveString(key, value.toString()) + public override fun getLong(key: String, defaultValue: Long): Long = getLongOrNull(key) ?: defaultValue + public override fun getLongOrNull(key: String): Long? = loadString(key)?.toLong() + + public override fun putString(key: String, value: String): Unit = saveString(key, value) + public override fun getString(key: String, defaultValue: String): String = getStringOrNull(key) ?: defaultValue + public override fun getStringOrNull(key: String): String? = loadString(key) + + public override fun putFloat(key: String, value: Float): Unit = saveString(key, value.toString()) + public override fun getFloat(key: String, defaultValue: Float): Float = getFloatOrNull(key) ?: defaultValue + public override fun getFloatOrNull(key: String): Float? = loadString(key)?.toFloat() + + public override fun putDouble(key: String, value: Double): Unit = saveString(key, value.toString()) + public override fun getDouble(key: String, defaultValue: Double): Double = getDoubleOrNull(key) ?: defaultValue + public override fun getDoubleOrNull(key: String): Double? = loadString(key)?.toDouble() + + public override fun putBoolean(key: String, value: Boolean): Unit = saveString(key, value.toString()) + public override fun getBoolean(key: String, defaultValue: Boolean): Boolean = getBooleanOrNull(key) ?: defaultValue + public override fun getBooleanOrNull(key: String): Boolean? = loadString(key)?.toBoolean() + + private inline fun saveString(key: String, value: String): Unit = villaOperation { villa -> + vlput(villa, key, -1, value, -1, VL_DOVER.toInt()) + } + + private inline fun loadString(key: String): String? = villaOperation { villa -> + val output = vlget(villa, key, -1, null) + output?.toKString() + } + + private inline fun CPointer.forEachKey(block: (key: String) -> Unit) { + val villa = this + if (vlcurfirst(villa) != 0) { + while (true) { + val key = vlcurkey(villa, null) ?: error("error iterating through Villa keys!") + block(key.toKString()) + if (vlcurnext(villa) == 0) { + break + } + } + } + } + + private inline fun CPointer.foldKeys(initial: A, block: (accumulator: A, key: String) -> A): A { + var accumulator = initial + forEachKey { accumulator = block(accumulator, it) } + return accumulator + } + + private inline fun villaOperation(action: MemScope.(villa: CPointer) -> T): T = memScoped { + val villa = vlopen(path, (VL_OWRITER or VL_OREADER or VL_OCREAT).toInt(), VL_CMPLEX) + if (villa == null) { + val message = dperrmsg(dpecode)?.toKString() + error("error on villa open: $message") + } + val out = action(villa) + vlclose(villa) + out + } +} diff --git a/multiplatform-settings/src/linuxX64Test/kotlin/QdbmDepotSettingsTest.kt b/multiplatform-settings/src/linuxX64Test/kotlin/QdbmDepotSettingsTest.kt new file mode 100644 index 00000000..e2df92ba --- /dev/null +++ b/multiplatform-settings/src/linuxX64Test/kotlin/QdbmDepotSettingsTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2022 Russell Wolf + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.russhwolf.settings + +@OptIn(ExperimentalSettingsImplementation::class) +class QdbmDepotSettingsTest : BaseSettingsTest( + platformFactory = object : Settings.Factory { + override fun create(name: String?): Settings = QdbmDepotSettings((name ?: "") + ".depot.db") + }, + hasListeners = false +) { + +// private lateinit var initialDbFiles: List +// +// @BeforeTest +// fun getInitialDbFiles() { +// initialDbFiles = getDbFiles() +// } +// +// @AfterTest +// fun cleanup() { +// val newDbFiles = getDbFiles() - initialDbFiles +// newDbFiles.forEach { +// remove(it) +// } +// } +// +// private fun getDbFiles(): List { +// val directory = opendir("./") ?: return emptyList() +// +// val out = mutableListOf() +// while (true) { +// val entry = readdir(directory) ?: break +// +// val filename = entry.pointed.d_name.toKString() +// if (filename.run { endsWith(".db") || endsWith(".dir") || endsWith(".pag") }) { +// out.add(filename) +// } +// } +// return out +// } + +// @Test +// fun constructor_filename() { +// val filename = "test_dbm" +// val settings = DbmRelicSettings(filename) +// +// memScoped { +// val dbm = dbm_open( +// filename.cstr, +// O_RDWR or O_CREAT, +// S_IRUSR or S_IWUSR or S_IRGRP or S_IROTH +// ) +// ?: error("error: $errno") +// dbm.checkError() +// +// val key = cValue { +// val cValues = "key".encodeToByteArray().toCValues() +// dptr = cValues.ptr +// dsize = cValues.size.toULong() +// } +// val value = cValue { +// val cValues = "value".encodeToByteArray().toCValues() +// dptr = cValues.ptr +// dsize = cValues.size.toULong() +// } +// +// if ( +// dbm_store( +// dbm, +// key, +// value, +// DBM_REPLACE.toInt() +// ) == -1 +// ) { +// dbm.checkError() +// } +// +// dbm_close(dbm) +// } +// +// assertEquals("value", settings["key", ""]) +// } +} + +//private fun CValuesRef.checkError() { +// assertEquals(0, dbm_error(this)) +//} diff --git a/multiplatform-settings/src/linuxX64Test/kotlin/QdbmRelicSettingsTest.kt b/multiplatform-settings/src/linuxX64Test/kotlin/QdbmRelicSettingsTest.kt new file mode 100644 index 00000000..5e70d930 --- /dev/null +++ b/multiplatform-settings/src/linuxX64Test/kotlin/QdbmRelicSettingsTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2022 Russell Wolf + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalForeignApi::class) + +package com.russhwolf.settings + +import com.russhwolf.settings.cinterop.qdbm.relic.DBM +import com.russhwolf.settings.cinterop.qdbm.relic.DBM_REPLACE +import com.russhwolf.settings.cinterop.qdbm.relic.datum +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_close +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_error +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_open +import com.russhwolf.settings.cinterop.qdbm.relic.dbm_store +import kotlinx.cinterop.CValuesRef +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.cValue +import kotlinx.cinterop.cstr +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.pointed +import kotlinx.cinterop.toCValues +import kotlinx.cinterop.toKString +import platform.posix.O_CREAT +import platform.posix.O_RDWR +import platform.posix.S_IRGRP +import platform.posix.S_IROTH +import platform.posix.S_IRUSR +import platform.posix.S_IWUSR +import platform.posix.errno +import platform.posix.opendir +import platform.posix.readdir +import platform.posix.remove +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +@OptIn(ExperimentalSettingsImplementation::class) +class QdbmRelicSettingsTest : BaseSettingsTest( + platformFactory = object : Settings.Factory { + override fun create(name: String?): Settings = QdbmRelicSettings(name ?: "dbm") + }, + hasListeners = false +) { + + private lateinit var initialDbFiles: List + + @BeforeTest + fun getInitialDbFiles() { + initialDbFiles = getDbFiles() + } + + @AfterTest + fun cleanup() { + val newDbFiles = getDbFiles() - initialDbFiles + newDbFiles.forEach { + remove(it) + } + } + + private fun getDbFiles(): List { + val directory = opendir("./") ?: return emptyList() + + val out = mutableListOf() + while (true) { + val entry = readdir(directory) ?: break + + val filename = entry.pointed.d_name.toKString() + if (filename.run { endsWith(".db") || endsWith(".dir") || endsWith(".pag") }) { + out.add(filename) + } + } + return out + } + + @Test + fun constructor_filename() { + val filename = "test_dbm" + val settings = QdbmRelicSettings(filename) + + memScoped { + val dbm = dbm_open( + filename.cstr, + O_RDWR or O_CREAT, + S_IRUSR or S_IWUSR or S_IRGRP or S_IROTH + ) + ?: error("error: $errno") + dbm.checkError() + + val key = cValue { + val cValues = "key".encodeToByteArray().toCValues() + dptr = cValues.ptr + dsize = cValues.size.toULong() + } + val value = cValue { + val cValues = "value".encodeToByteArray().toCValues() + dptr = cValues.ptr + dsize = cValues.size.toULong() + } + + if ( + dbm_store( + dbm, + key, + value, + DBM_REPLACE.toInt() + ) == -1 + ) { + dbm.checkError() + } + + dbm_close(dbm) + } + + assertEquals("value", settings["key", ""]) + } +} + +@ExperimentalForeignApi +private fun CValuesRef.checkError() { + assertEquals(0, dbm_error(this)) +} diff --git a/multiplatform-settings/src/linuxX64Test/kotlin/QdbmVillaSettingsTest.kt b/multiplatform-settings/src/linuxX64Test/kotlin/QdbmVillaSettingsTest.kt new file mode 100644 index 00000000..112a6e91 --- /dev/null +++ b/multiplatform-settings/src/linuxX64Test/kotlin/QdbmVillaSettingsTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2022 Russell Wolf + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.russhwolf.settings + +@OptIn(ExperimentalSettingsImplementation::class) +class QdbmVillaSettingsTest : BaseSettingsTest( + platformFactory = object : Settings.Factory { + override fun create(name: String?): Settings = QdbmVillaSettings((name ?: "") + ".villa.db") + }, + hasListeners = false +) { + +// private lateinit var initialDbFiles: List +// +// @BeforeTest +// fun getInitialDbFiles() { +// initialDbFiles = getDbFiles() +// } +// +// @AfterTest +// fun cleanup() { +// val newDbFiles = getDbFiles() - initialDbFiles +// newDbFiles.forEach { +// remove(it) +// } +// } +// +// private fun getDbFiles(): List { +// val directory = opendir("./") ?: return emptyList() +// +// val out = mutableListOf() +// while (true) { +// val entry = readdir(directory) ?: break +// +// val filename = entry.pointed.d_name.toKString() +// if (filename.run { endsWith(".db") || endsWith(".dir") || endsWith(".pag") }) { +// out.add(filename) +// } +// } +// return out +// } + +// @Test +// fun constructor_filename() { +// val filename = "test_dbm" +// val settings = DbmRelicSettings(filename) +// +// memScoped { +// val dbm = dbm_open( +// filename.cstr, +// O_RDWR or O_CREAT, +// S_IRUSR or S_IWUSR or S_IRGRP or S_IROTH +// ) +// ?: error("error: $errno") +// dbm.checkError() +// +// val key = cValue { +// val cValues = "key".encodeToByteArray().toCValues() +// dptr = cValues.ptr +// dsize = cValues.size.toULong() +// } +// val value = cValue { +// val cValues = "value".encodeToByteArray().toCValues() +// dptr = cValues.ptr +// dsize = cValues.size.toULong() +// } +// +// if ( +// dbm_store( +// dbm, +// key, +// value, +// DBM_REPLACE.toInt() +// ) == -1 +// ) { +// dbm.checkError() +// } +// +// dbm_close(dbm) +// } +// +// assertEquals("value", settings["key", ""]) +// } +} + +//private fun CValuesRef.checkError() { +// assertEquals(0, dbm_error(this)) +//} diff --git a/multiplatform-settings/src/nativeInterop/cinterop/qdbm-depot.def b/multiplatform-settings/src/nativeInterop/cinterop/qdbm-depot.def new file mode 100644 index 00000000..d9f1e1c1 --- /dev/null +++ b/multiplatform-settings/src/nativeInterop/cinterop/qdbm-depot.def @@ -0,0 +1,4 @@ +headers = depot.h +package = com.russhwolf.settings.cinterop.qdbm.depot +compilerOpts = -I/usr/include/qdbm +linkerOpts = -lqdbm -L/usr/lib diff --git a/multiplatform-settings/src/nativeInterop/cinterop/qdbm-relic.def b/multiplatform-settings/src/nativeInterop/cinterop/qdbm-relic.def new file mode 100644 index 00000000..5adee5be --- /dev/null +++ b/multiplatform-settings/src/nativeInterop/cinterop/qdbm-relic.def @@ -0,0 +1,4 @@ +headers = relic.h +package = com.russhwolf.settings.cinterop.qdbm.relic +compilerOpts = -I/usr/include/qdbm +linkerOpts = -lqdbm -L/usr/lib diff --git a/multiplatform-settings/src/nativeInterop/cinterop/qdbm-villa.def b/multiplatform-settings/src/nativeInterop/cinterop/qdbm-villa.def new file mode 100644 index 00000000..fc66314c --- /dev/null +++ b/multiplatform-settings/src/nativeInterop/cinterop/qdbm-villa.def @@ -0,0 +1,4 @@ +headers = villa.h +package = com.russhwolf.settings.cinterop.qdbm.villa +compilerOpts = -I/usr/include/qdbm +linkerOpts = -lqdbm -L/usr/lib