Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QDBM proof-of-concept implementations #111

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.gradle.accessors.dm.LibrariesForLibs
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
Expand All @@ -37,6 +38,16 @@ kotlin {
allWarningsAsErrors = true
}

targets.configureEach {
if (this is KotlinNativeTarget && this.name.startsWith("linux")) {
binaries.configureEach {
// Workaround issues with gcc version mismatch
// https://stackoverflow.com/a/78267398
linkerOpts += "--allow-shlib-undefined"
}
}
}

@OptIn(ExperimentalKotlinGradlePluginApi::class)
applyDefaultHierarchyTemplate {
common {
Expand Down
96 changes: 96 additions & 0 deletions multiplatform-settings/api/multiplatform-settings.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,102 @@ final class com.russhwolf.settings/StorageSettings : com.russhwolf.settings/Sett
final fun remove(kotlin/String) // com.russhwolf.settings/StorageSettings.remove|remove(kotlin.String){}[0]
}

// Targets: [linuxX64]
final class com.russhwolf.settings/QdbmDepotSettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/QdbmDepotSettings|null[0]
constructor <init>(kotlin/String) // com.russhwolf.settings/QdbmDepotSettings.<init>|<init>(kotlin.String){}[0]

final val keys // com.russhwolf.settings/QdbmDepotSettings.keys|{}keys[0]
final fun <get-keys>(): kotlin.collections/Set<kotlin/String> // com.russhwolf.settings/QdbmDepotSettings.keys.<get-keys>|<get-keys>(){}[0]
final val size // com.russhwolf.settings/QdbmDepotSettings.size|{}size[0]
final fun <get-size>(): kotlin/Int // com.russhwolf.settings/QdbmDepotSettings.size.<get-size>|<get-size>(){}[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]
}

// Targets: [linuxX64]
final class com.russhwolf.settings/QdbmRelicSettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/QdbmRelicSettings|null[0]
constructor <init>(kotlin/String) // com.russhwolf.settings/QdbmRelicSettings.<init>|<init>(kotlin.String){}[0]

final val keys // com.russhwolf.settings/QdbmRelicSettings.keys|{}keys[0]
final fun <get-keys>(): kotlin.collections/Set<kotlin/String> // com.russhwolf.settings/QdbmRelicSettings.keys.<get-keys>|<get-keys>(){}[0]
final val size // com.russhwolf.settings/QdbmRelicSettings.size|{}size[0]
final fun <get-size>(): kotlin/Int // com.russhwolf.settings/QdbmRelicSettings.size.<get-size>|<get-size>(){}[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]
}

// Targets: [linuxX64]
final class com.russhwolf.settings/QdbmVillaSettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/QdbmVillaSettings|null[0]
constructor <init>(kotlin/String) // com.russhwolf.settings/QdbmVillaSettings.<init>|<init>(kotlin.String){}[0]

final val keys // com.russhwolf.settings/QdbmVillaSettings.keys|{}keys[0]
final fun <get-keys>(): kotlin.collections/Set<kotlin/String> // com.russhwolf.settings/QdbmVillaSettings.keys.<get-keys>|<get-keys>(){}[0]
final val size // com.russhwolf.settings/QdbmVillaSettings.size|{}size[0]
final fun <get-size>(): kotlin/Int // com.russhwolf.settings/QdbmVillaSettings.size.<get-size>|<get-size>(){}[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]
}

// Targets: [mingwX64]
final class com.russhwolf.settings/RegistrySettings : com.russhwolf.settings/Settings { // com.russhwolf.settings/RegistrySettings|null[0]
constructor <init>(kotlin/String) // com.russhwolf.settings/RegistrySettings.<init>|<init>(kotlin.String){}[0]
Expand Down
7 changes: 7 additions & 0 deletions multiplatform-settings/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
* limitations under the License.
*/

import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget

plugins {
id("standard-configuration")
id("module-publication")
Expand All @@ -26,6 +28,11 @@ standardConfig {
}

kotlin {
targets.getByName<KotlinNativeTarget>("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")
Expand Down
122 changes: 122 additions & 0 deletions multiplatform-settings/src/linuxX64Main/kotlin/QdbmDepotSettings.kt
Original file line number Diff line number Diff line change
@@ -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<String>
get() = depotOperation { depot ->
depot.foldKeys(mutableListOf<String>()) { 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 fun saveString(key: String, value: String): Unit = depotOperation { depot ->
dpput(depot, key, -1, value, -1, DP_DOVER.toInt())
}

private fun loadString(key: String): String? = depotOperation { depot ->
val output = dpget(depot, key, -1, 0, -1, null)
output?.toKString()
}

private inline fun CPointer<DEPOT>.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 <A> CPointer<DEPOT>.foldKeys(initial: A, block: (accumulator: A, key: String) -> A): A {
var accumulator = initial
forEachKey { accumulator = block(accumulator, it) }
return accumulator
}

private inline fun <T> depotOperation(action: MemScope.(depot: CPointer<DEPOT>) -> 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
}
}
Loading
Loading