Skip to content

Commit

Permalink
WIP QDBM multiple implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
russhwolf committed Oct 13, 2024
1 parent 5aa0f10 commit c0c5439
Show file tree
Hide file tree
Showing 12 changed files with 876 additions and 0 deletions.
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
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 @@ -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")
Expand All @@ -27,6 +29,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 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<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

0 comments on commit c0c5439

Please sign in to comment.