nativeDarwin.io.realm.kotlin.internal.interop.RealmInterop.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cinterop Show documentation
Show all versions of cinterop Show documentation
Wrapper for interacting with Realm Kotlin native code. This artifact is not supposed to be consumed directly, but through 'io.realm.kotlin:gradle-plugin:1.13.0' instead.
/*
* Copyright 2020 Realm Inc.
*
* 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.
*/
// TODO https://github.com/realm/realm-kotlin/issues/889
@file:Suppress("TooGenericExceptionThrown", "TooGenericExceptionCaught")
package io.realm.kotlin.internal.interop
import io.realm.kotlin.internal.interop.Constants.ENCRYPTION_KEY_LENGTH
import io.realm.kotlin.internal.interop.sync.ApiKeyWrapper
import io.realm.kotlin.internal.interop.sync.AppError
import io.realm.kotlin.internal.interop.sync.AuthProvider
import io.realm.kotlin.internal.interop.sync.CoreCompensatingWriteInfo
import io.realm.kotlin.internal.interop.sync.CoreConnectionState
import io.realm.kotlin.internal.interop.sync.CoreSubscriptionSetState
import io.realm.kotlin.internal.interop.sync.CoreSyncSessionState
import io.realm.kotlin.internal.interop.sync.CoreUserState
import io.realm.kotlin.internal.interop.sync.MetadataMode
import io.realm.kotlin.internal.interop.sync.NetworkTransport
import io.realm.kotlin.internal.interop.sync.ProgressDirection
import io.realm.kotlin.internal.interop.sync.Response
import io.realm.kotlin.internal.interop.sync.SyncError
import io.realm.kotlin.internal.interop.sync.SyncSessionResyncMode
import io.realm.kotlin.internal.interop.sync.SyncUserIdentity
import kotlinx.atomicfu.AtomicBoolean
import kotlinx.atomicfu.atomic
import kotlinx.cinterop.AutofreeScope
import kotlinx.cinterop.BooleanVar
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.ByteVarOf
import kotlinx.cinterop.CArrayPointer
import kotlinx.cinterop.COpaquePointer
import kotlinx.cinterop.CPointed
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.CPointerVar
import kotlinx.cinterop.CPointerVarOf
import kotlinx.cinterop.CValue
import kotlinx.cinterop.CVariable
import kotlinx.cinterop.LongVar
import kotlinx.cinterop.MemScope
import kotlinx.cinterop.StableRef
import kotlinx.cinterop.UIntVar
import kotlinx.cinterop.ULongVar
import kotlinx.cinterop.ULongVarOf
import kotlinx.cinterop.addressOf
import kotlinx.cinterop.alloc
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.asStableRef
import kotlinx.cinterop.cValue
import kotlinx.cinterop.convert
import kotlinx.cinterop.cstr
import kotlinx.cinterop.get
import kotlinx.cinterop.getBytes
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.pointed
import kotlinx.cinterop.ptr
import kotlinx.cinterop.readBytes
import kotlinx.cinterop.readValue
import kotlinx.cinterop.refTo
import kotlinx.cinterop.set
import kotlinx.cinterop.staticCFunction
import kotlinx.cinterop.toCStringArray
import kotlinx.cinterop.toKString
import kotlinx.cinterop.useContents
import kotlinx.cinterop.usePinned
import kotlinx.cinterop.value
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.mongodb.kbson.BsonObjectId
import org.mongodb.kbson.ObjectId
import platform.posix.memcpy
import platform.posix.posix_errno
import platform.posix.pthread_threadid_np
import platform.posix.size_t
import platform.posix.size_tVar
import platform.posix.strerror
import platform.posix.uint64_t
import platform.posix.uint8_tVar
import realm_wrapper.realm_app_error_t
import realm_wrapper.realm_app_user_apikey_t
import realm_wrapper.realm_binary_t
import realm_wrapper.realm_class_info_t
import realm_wrapper.realm_class_key_tVar
import realm_wrapper.realm_clear_last_error
import realm_wrapper.realm_clone
import realm_wrapper.realm_dictionary_t
import realm_wrapper.realm_error_t
import realm_wrapper.realm_find_property
import realm_wrapper.realm_flx_sync_subscription_set_state_e
import realm_wrapper.realm_get_last_error
import realm_wrapper.realm_http_header_t
import realm_wrapper.realm_http_request_method
import realm_wrapper.realm_http_request_t
import realm_wrapper.realm_http_response_t
import realm_wrapper.realm_link_t
import realm_wrapper.realm_list_t
import realm_wrapper.realm_object_id_t
import realm_wrapper.realm_object_t
import realm_wrapper.realm_property_info_t
import realm_wrapper.realm_query_arg_t
import realm_wrapper.realm_release
import realm_wrapper.realm_results_t
import realm_wrapper.realm_scheduler_t
import realm_wrapper.realm_set_t
import realm_wrapper.realm_string_t
import realm_wrapper.realm_sync_client_metadata_mode
import realm_wrapper.realm_sync_session_resync_mode
import realm_wrapper.realm_sync_session_state_e
import realm_wrapper.realm_sync_session_stop_policy_e
import realm_wrapper.realm_t
import realm_wrapper.realm_user_identity
import realm_wrapper.realm_user_t
import realm_wrapper.realm_value_t
import realm_wrapper.realm_value_type
import realm_wrapper.realm_version_id_t
import realm_wrapper.realm_work_queue_t
import kotlin.collections.set
import kotlin.native.internal.createCleaner
@SharedImmutable
actual val INVALID_CLASS_KEY: ClassKey by lazy { ClassKey(realm_wrapper.RLM_INVALID_CLASS_KEY.toLong()) }
@SharedImmutable
actual val INVALID_PROPERTY_KEY: PropertyKey by lazy { PropertyKey(realm_wrapper.RLM_INVALID_PROPERTY_KEY) }
private fun throwOnError() {
memScoped {
val error = alloc()
if (realm_get_last_error(error.ptr)) {
throw CoreErrorConverter.asThrowable(
categoriesNativeValue = error.categories.toInt(),
errorCodeNativeValue = error.error.value.toInt(),
messageNativeValue = error.message?.toKString(),
path = error.path?.toKString(),
userError = error.usercode_error?.asStableRef()?.get()
).also {
error.usercode_error?.let { disposeUserData(it) }
realm_clear_last_error()
}
}
}
}
private fun checkedBooleanResult(boolean: Boolean): Boolean {
if (!boolean) throwOnError(); return boolean
}
private fun checkedPointerResult(pointer: CPointer?): CPointer? {
if (pointer == null) throwOnError(); return pointer
}
/**
* Class with a pointer reference and its status. It breaks the reference cycle between CPointerWrapper
* and its GC cleaner, otherwise the cleaner would never be invoked.
*
* See leaking cleaner: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.ref/create-cleaner.html
*/
data class ReleasablePointer(
private val _ptr: CPointer?,
val released: AtomicBoolean = atomic(false)
) {
fun release() {
if (released.compareAndSet(expect = false, update = true)) {
realm_release(_ptr)
}
}
val ptr: CPointer?
get() {
return if (!released.value) {
_ptr
} else {
throw POINTER_DELETED_ERROR
}
}
}
// FIXME API-INTERNAL Consider making NativePointer/CPointerWrapper generic to enforce typing
class CPointerWrapper(ptr: CPointer?, managed: Boolean = true) : NativePointer {
val _ptr = ReleasablePointer(
checkedPointerResult(ptr)
)
val ptr: CPointer? = _ptr.ptr
@OptIn(ExperimentalStdlibApi::class)
val cleaner = if (managed) {
createCleaner(_ptr) {
it.release()
}
} else null
override fun release() {
_ptr.release()
}
override fun isReleased(): Boolean = _ptr.released.value
}
// Convenience type cast
inline fun NativePointer.cptr(): CPointer {
return (this as CPointerWrapper).ptr as CPointer
}
fun realm_binary_t.set(memScope: AutofreeScope, binary: ByteArray): realm_binary_t {
size = binary.size.toULong()
data = memScope.allocArray(binary.size)
binary.forEachIndexed { index, byte ->
data!![index] = byte.toUByte()
}
return this
}
fun realm_string_t.set(memScope: AutofreeScope, s: String): realm_string_t {
val cstr = s.cstr
data = cstr.getPointer(memScope)
size = cstr.getBytes().size.toULong() - 1UL // realm_string_t is not zero-terminated
return this
}
/**
* Note that `realm_string_t` is allowed to represent `null`, so only use this extension
* method if there is an invariant guaranteeing it won't be.
*
* @throws NullPointerException if `realm_string_t` is null.
*/
fun realm_string_t.toKotlinString(): String {
if (size == 0UL) {
return ""
}
val data: CPointer>? = this.data
val readBytes: ByteArray? = data?.readBytes(this.size.toInt())
return readBytes?.decodeToString(0, size.toInt(), throwOnInvalidSequence = false)!!
}
fun realm_string_t.toNullableKotlinString(): String? {
return if (data == null) {
null
} else {
return toKotlinString()
}
}
fun String.toRString(memScope: MemScope) = cValue {
set(memScope, this@toRString)
}
@Suppress("LargeClass", "FunctionNaming")
actual object RealmInterop {
private inline fun stableUserDataWithErrorPropagation(
userdata: CPointer?,
block: T.() -> Boolean
): Boolean = try {
block(stableUserData(userdata).get())
} catch (e: Throwable) {
// register the error so it is accessible later
realm_wrapper.realm_register_user_code_callback_error(StableRef.create(e).asCPointer())
false // indicates the callback failed
}
actual fun realm_value_get(value: RealmValue): Any? = value.value
actual fun realm_get_version_id(realm: RealmPointer): Long {
memScoped {
val info = alloc()
val found = alloc()
checkedBooleanResult(
realm_wrapper.realm_get_version_id(
realm.cptr(),
found.ptr,
info.ptr
)
)
return if (found.value) {
info.version.toLong()
} else {
throw IllegalStateException("No VersionId was available. Reading the VersionId requires a valid read transaction.")
}
}
}
actual fun realm_get_num_versions(realm: RealmPointer): Long {
memScoped {
val versionsCount = alloc()
checkedBooleanResult(
realm_wrapper.realm_get_num_versions(
realm.cptr(),
versionsCount.ptr
)
)
return versionsCount.value.toLong()
}
}
actual fun realm_refresh(realm: RealmPointer) {
memScoped {
// Only returns `true` if the version changed, `false` if the version
// was already at the latest. Errors will be represented by the actual
// return value, so just ignore this out parameter.
val didRefresh = alloc()
checkedBooleanResult(realm_wrapper.realm_refresh(realm.cptr(), didRefresh.ptr))
}
}
actual fun realm_get_library_version(): String {
return realm_wrapper.realm_get_library_version().safeKString("library_version")
}
actual fun realm_schema_new(schema: List>>): RealmSchemaPointer {
val count = schema.size
memScoped {
val cclasses = allocArray(count)
val cproperties = allocArray>(count)
for ((i, entry) in schema.withIndex()) {
val (clazz, properties) = entry
val computedCount = properties.count { it.isComputed }
// Class
cclasses[i].apply {
name = clazz.name.cstr.ptr
primary_key = clazz.primaryKey.cstr.ptr
num_properties = (properties.size - computedCount).toULong()
num_computed_properties = computedCount.toULong()
flags = clazz.flags
}
cproperties[i] =
allocArray(properties.size).getPointer(memScope)
for ((j, property) in properties.withIndex()) {
cproperties[i]!![j].apply {
name = property.name.cstr.ptr
public_name = property.publicName.cstr.ptr
link_target = property.linkTarget.cstr.ptr
link_origin_property_name = property.linkOriginPropertyName.cstr.ptr
type = property.type.nativeValue
collection_type = property.collectionType.nativeValue
flags = property.flags
}
}
}
return CPointerWrapper(
realm_wrapper.realm_schema_new(
cclasses,
count.toULong(),
cproperties
)
)
}
}
actual fun realm_config_new(): RealmConfigurationPointer {
return CPointerWrapper(realm_wrapper.realm_config_new())
}
actual fun realm_config_set_path(config: RealmConfigurationPointer, path: String) {
realm_wrapper.realm_config_set_path(config.cptr(), path)
}
actual fun realm_config_set_schema_mode(config: RealmConfigurationPointer, mode: SchemaMode) {
realm_wrapper.realm_config_set_schema_mode(
config.cptr(),
mode.nativeValue
)
}
actual fun realm_config_set_schema_version(config: RealmConfigurationPointer, version: Long) {
realm_wrapper.realm_config_set_schema_version(
config.cptr(),
version.toULong()
)
}
actual fun realm_config_set_max_number_of_active_versions(
config: RealmConfigurationPointer,
maxNumberOfVersions: Long
) {
realm_wrapper.realm_config_set_max_number_of_active_versions(
config.cptr(),
maxNumberOfVersions.toULong()
)
}
actual fun realm_config_set_encryption_key(config: RealmConfigurationPointer, encryptionKey: ByteArray) {
memScoped {
val encryptionKeyPointer = encryptionKey.refTo(0).getPointer(memScope)
realm_wrapper.realm_config_set_encryption_key(
config.cptr(),
encryptionKeyPointer as CPointer,
encryptionKey.size.toULong()
)
}
}
actual fun realm_config_get_encryption_key(config: RealmConfigurationPointer): ByteArray? {
memScoped {
val encryptionKey = ByteArray(ENCRYPTION_KEY_LENGTH)
val encryptionKeyPointer = encryptionKey.refTo(0).getPointer(memScope)
val keyLength = realm_wrapper.realm_config_get_encryption_key(
config.cptr(),
encryptionKeyPointer as CPointer
)
if (keyLength == ENCRYPTION_KEY_LENGTH.toULong()) {
return encryptionKey
}
return null
}
}
actual fun realm_config_set_should_compact_on_launch_function(
config: RealmConfigurationPointer,
callback: CompactOnLaunchCallback
) {
realm_wrapper.realm_config_set_should_compact_on_launch_function(
config.cptr(),
staticCFunction { userdata, total, used ->
stableUserDataWithErrorPropagation(userdata) {
invoke(
total.toLong(),
used.toLong()
)
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
)
}
actual fun realm_config_set_automatic_backlink_handling(
config: RealmConfigurationPointer,
enabled: Boolean
) {
realm_wrapper.realm_config_set_automatic_backlink_handling(
config.cptr(),
enabled,
)
}
actual fun realm_config_set_migration_function(
config: RealmConfigurationPointer,
callback: MigrationCallback
) {
realm_wrapper.realm_config_set_migration_function(
config.cptr(),
staticCFunction { userData, oldRealm, newRealm, schema ->
stableUserDataWithErrorPropagation(userData) {
migrate(
// These realm/schema pointers are only valid for the duraction of the
// migration so don't let ownership follow the NativePointer-objects
CPointerWrapper(oldRealm, false),
CPointerWrapper(newRealm, false),
CPointerWrapper(schema, false),
)
true
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
)
}
actual fun realm_config_set_data_initialization_function(
config: RealmConfigurationPointer,
callback: DataInitializationCallback
) {
realm_wrapper.realm_config_set_data_initialization_function(
config.cptr(),
staticCFunction { userData, _ ->
stableUserDataWithErrorPropagation(userData) {
invoke()
true
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
)
}
actual fun realm_config_set_in_memory(config: RealmConfigurationPointer, inMemory: Boolean) {
realm_wrapper.realm_config_set_in_memory(config.cptr(), inMemory)
}
actual fun realm_config_set_schema(config: RealmConfigurationPointer, schema: RealmSchemaPointer) {
realm_wrapper.realm_config_set_schema(config.cptr(), schema.cptr())
}
actual fun realm_schema_validate(schema: RealmSchemaPointer, mode: SchemaValidationMode): Boolean {
return checkedBooleanResult(
realm_wrapper.realm_schema_validate(
schema.cptr(),
mode.nativeValue.toULong()
)
)
}
actual fun realm_open(config: RealmConfigurationPointer, scheduler: RealmSchedulerPointer): Pair {
val fileCreated = atomic(false)
val callback = DataInitializationCallback {
fileCreated.value = true
}
realm_wrapper.realm_config_set_data_initialization_function(
config.cptr(),
staticCFunction { userdata, _ ->
stableUserData(userdata).get().invoke()
true
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
)
// TODO Consider just grabbing the current dispatcher by
// val dispatcher = runBlocking { coroutineContext[CoroutineDispatcher.Key] }
// but requires opting in for @ExperimentalStdlibApi, and have really gotten it to play
// for default cases.
realm_wrapper.realm_config_set_scheduler(config.cptr(), scheduler.cptr())
val realmPtr = CPointerWrapper(realm_wrapper.realm_open(config.cptr()))
// Ensure that we can read version information, etc.
realm_begin_read(realmPtr)
return Pair(realmPtr, fileCreated.value)
}
actual fun realm_create_scheduler(): RealmSchedulerPointer {
// If there is no notification dispatcher use the default scheduler.
// Re-verify if this is actually needed when notification scheduler is fully in place.
val scheduler = checkedPointerResult(realm_wrapper.realm_scheduler_make_default())
return CPointerWrapper(scheduler)
}
actual fun realm_create_scheduler(dispatcher: CoroutineDispatcher): RealmSchedulerPointer {
printlntid("createSingleThreadDispatcherScheduler")
val scheduler = SingleThreadDispatcherScheduler(tid(), dispatcher)
val capi_scheduler: CPointer = checkedPointerResult(
realm_wrapper.realm_scheduler_new(
// userdata: kotlinx.cinterop.CValuesRef<*>?,
scheduler.ref,
// free: realm_wrapper.realm_free_userdata_func_t? /* = kotlinx.cinterop.CPointer? */) -> kotlin.Unit>>? */,
staticCFunction { userdata ->
printlntid("free")
val stableSchedulerRef: StableRef? = userdata?.asStableRef()
stableSchedulerRef?.get()?.cancel()
stableSchedulerRef?.dispose()
},
// notify: realm_wrapper.realm_scheduler_notify_func_t? /* = kotlinx.cinterop.CPointer? */) -> kotlin.Unit>>? */,
staticCFunction?, Unit> { userdata, work_queue ->
// Must be thread safe
val scheduler =
userdata!!.asStableRef().get()
printlntid("$scheduler notify")
try {
scheduler.notify(work_queue)
} catch (e: Exception) {
// Should never happen, but is included for development to get some indicators
// on errors instead of silent crashes.
e.printStackTrace()
}
},
// is_on_thread: realm_wrapper.realm_scheduler_is_on_thread_func_t? /* = kotlinx.cinterop.CPointer? */) -> kotlin.Boolean>>? */,
staticCFunction { userdata ->
// Must be thread safe
val scheduler =
userdata!!.asStableRef().get()
printlntid("is_on_thread[$scheduler] ${scheduler.threadId} " + tid())
scheduler.threadId == tid()
},
// is_same_as: realm_wrapper.realm_scheduler_is_same_as_func_t? /* = kotlinx.cinterop.CPointer? */, kotlinx.cinterop.COpaquePointer? /* = kotlinx.cinterop.CPointer? */) -> kotlin.Boolean>>? */,
staticCFunction { userdata, other ->
userdata == other
},
// can_deliver_notifications: realm_wrapper.realm_scheduler_can_deliver_notifications_func_t? /* = kotlinx.cinterop.CPointer? */) -> kotlin.Boolean>>? */,
staticCFunction { _ -> true },
)
) ?: error("Couldn't create scheduler")
scheduler.setScheduler(capi_scheduler)
return CPointerWrapper(capi_scheduler)
}
actual fun realm_open_synchronized(config: RealmConfigurationPointer): RealmAsyncOpenTaskPointer {
return CPointerWrapper(realm_wrapper.realm_open_synchronized(config.cptr()))
}
actual fun realm_async_open_task_start(task: RealmAsyncOpenTaskPointer, callback: AsyncOpenCallback) {
realm_wrapper.realm_async_open_task_start(
task.cptr(),
staticCFunction { userData, realm, error ->
memScoped {
var exception: Throwable? = null
if (error != null) {
val err = alloc()
realm_wrapper.realm_get_async_error(error, err.ptr)
exception = CoreErrorConverter.asThrowable(
categoriesNativeValue = err.categories.toInt(),
errorCodeNativeValue = err.error.value.toInt(),
messageNativeValue = err.message?.toKString(),
path = err.path?.toKString(),
userError = err.usercode_error?.asStableRef()?.get()
)
err.usercode_error?.let { disposeUserData(it) }
} else {
realm_release(realm)
}
safeUserData(userData).invoke(exception)
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData ->
disposeUserData(userData)
}
)
}
actual fun realm_async_open_task_cancel(task: RealmAsyncOpenTaskPointer) {
realm_wrapper.realm_async_open_task_cancel(task.cptr())
}
actual fun realm_add_realm_changed_callback(realm: LiveRealmPointer, block: () -> Unit): RealmCallbackTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_add_realm_changed_callback(
realm.cptr(),
staticCFunction { userData ->
safeUserData<() -> Unit>(userData)()
},
StableRef.create(block).asCPointer(),
staticCFunction { userdata ->
disposeUserData<(LiveRealmPointer, SyncErrorCallback) -> Unit>(userdata)
}
),
managed = false
)
}
actual fun realm_add_schema_changed_callback(realm: LiveRealmPointer, block: (RealmSchemaPointer) -> Unit): RealmCallbackTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_add_schema_changed_callback(
realm.cptr(),
staticCFunction { userData, schema ->
safeUserData<(RealmSchemaPointer) -> Unit>(userData)(CPointerWrapper(realm_clone(schema)))
},
StableRef.create(block).asCPointer(),
staticCFunction { userdata ->
disposeUserData<(RealmSchemaT, SyncErrorCallback) -> Unit>(userdata)
}
),
managed = false
)
}
actual fun realm_freeze(liveRealm: LiveRealmPointer): FrozenRealmPointer {
return CPointerWrapper(realm_wrapper.realm_freeze(liveRealm.cptr()))
}
actual fun realm_is_frozen(realm: RealmPointer): Boolean {
return realm_wrapper.realm_is_frozen(realm.cptr())
}
actual fun realm_close(realm: RealmPointer) {
checkedBooleanResult(realm_wrapper.realm_close(realm.cptr()))
}
actual fun realm_delete_files(path: String) {
memScoped {
val deleted = alloc()
checkedBooleanResult(realm_wrapper.realm_delete_files(path, deleted.ptr))
if (!deleted.value) {
throw IllegalStateException("It's not allowed to delete the file associated with an open Realm. Remember to call 'close()' on the instances of the realm before deleting its file: $path")
}
}
}
actual fun realm_compact(realm: RealmPointer): Boolean {
memScoped {
val compacted = alloc()
checkedBooleanResult(realm_wrapper.realm_compact(realm.cptr(), compacted.ptr))
return compacted.value
}
}
actual fun realm_convert_with_config(
realm: RealmPointer,
config: RealmConfigurationPointer,
mergeWithExisting: Boolean
) {
memScoped {
checkedBooleanResult(
realm_wrapper.realm_convert_with_config(
realm.cptr(),
config.cptr(),
mergeWithExisting
)
)
}
}
actual fun realm_get_schema(realm: RealmPointer): RealmSchemaPointer {
return CPointerWrapper(realm_wrapper.realm_get_schema(realm.cptr()))
}
actual fun realm_get_schema_version(realm: RealmPointer): Long {
return realm_wrapper.realm_get_schema_version(realm.cptr()).toLong()
}
actual fun realm_get_num_classes(realm: RealmPointer): Long {
return realm_wrapper.realm_get_num_classes(realm.cptr()).toLong()
}
actual fun realm_get_class_keys(realm: RealmPointer): List {
memScoped {
val max = realm_get_num_classes(realm)
val keys = allocArray(max)
val outCount = alloc()
checkedBooleanResult(realm_wrapper.realm_get_class_keys(realm.cptr(), keys, max.convert(), outCount.ptr))
if (max != outCount.value.toLong()) {
error("Invalid schema: Insufficient keys; got ${outCount.value}, expected $max")
}
return (0 until max).map { ClassKey(keys[it].toLong()) }
}
}
actual fun realm_find_class(realm: RealmPointer, name: String): ClassKey? {
memScoped {
val found = alloc()
val classInfo = alloc()
checkedBooleanResult(
realm_wrapper.realm_find_class(
realm.cptr(),
name,
found.ptr,
classInfo.ptr
)
)
return if (found.value) {
ClassKey(classInfo.key.toLong())
} else {
null
}
}
}
actual fun realm_get_class(realm: RealmPointer, classKey: ClassKey): ClassInfo {
memScoped {
val classInfo = alloc()
realm_wrapper.realm_get_class(realm.cptr(), classKey.key.toUInt(), classInfo.ptr)
return with(classInfo) {
ClassInfo(
name.safeKString("name"),
primary_key?.toKString() ?: SCHEMA_NO_VALUE,
num_properties.convert(),
num_computed_properties.convert(),
ClassKey(key.toLong()),
flags
)
}
}
}
actual fun realm_get_class_properties(
realm: RealmPointer,
classKey: ClassKey,
max: Long
): List {
memScoped {
val properties = allocArray(max)
val outCount = alloc()
realm_wrapper.realm_get_class_properties(
realm.cptr(),
classKey.key.convert(),
properties,
max.convert(),
outCount.ptr
)
outCount.value.toLong().let { count ->
return if (count > 0) {
(0 until outCount.value.toLong()).map {
with(properties[it]) {
PropertyInfo(
name.safeKString("name"),
public_name.safeKString("public_name"),
PropertyType.from(type.toInt()),
CollectionType.from(collection_type.toInt()),
link_target.safeKString("link_target"),
link_origin_property_name.safeKString("link_origin_property_name"),
PropertyKey(key),
flags
)
}
}
} else {
emptyList()
}
}
}
}
internal actual fun realm_release(p: RealmNativePointer) {
realm_wrapper.realm_release((p as CPointerWrapper).ptr)
}
actual fun realm_equals(p1: RealmNativePointer, p2: RealmNativePointer): Boolean {
return realm_wrapper.realm_equals((p1 as CPointerWrapper).ptr, (p2 as CPointerWrapper).ptr)
}
actual fun realm_is_closed(realm: RealmPointer): Boolean {
return realm_wrapper.realm_is_closed(realm.cptr())
}
actual fun realm_begin_read(realm: RealmPointer) {
checkedBooleanResult(realm_wrapper.realm_begin_read(realm.cptr()))
}
actual fun realm_begin_write(realm: LiveRealmPointer) {
checkedBooleanResult(realm_wrapper.realm_begin_write(realm.cptr()))
}
actual fun realm_commit(realm: LiveRealmPointer) {
checkedBooleanResult(realm_wrapper.realm_commit(realm.cptr()))
}
actual fun realm_rollback(realm: LiveRealmPointer) {
checkedBooleanResult(realm_wrapper.realm_rollback(realm.cptr()))
}
actual fun realm_is_in_transaction(realm: RealmPointer): Boolean {
return realm_wrapper.realm_is_writable(realm.cptr())
}
actual fun realm_update_schema(realm: LiveRealmPointer, schema: RealmSchemaPointer) {
checkedBooleanResult(realm_wrapper.realm_update_schema(realm.cptr(), schema.cptr()))
}
actual fun realm_object_create(realm: LiveRealmPointer, classKey: ClassKey): RealmObjectPointer {
return CPointerWrapper(
realm_wrapper.realm_object_create(
realm.cptr(),
classKey.key.toUInt()
)
)
}
actual fun realm_object_create_with_primary_key(
realm: LiveRealmPointer,
classKey: ClassKey,
primaryKeyTransport: RealmValue
): RealmObjectPointer {
return CPointerWrapper(
realm_wrapper.realm_object_create_with_primary_key(
realm.cptr(),
classKey.key.toUInt(),
primaryKeyTransport.value.readValue()
)
)
}
actual fun realm_object_get_or_create_with_primary_key(
realm: LiveRealmPointer,
classKey: ClassKey,
primaryKeyTransport: RealmValue
): RealmObjectPointer {
memScoped {
val found = alloc()
return CPointerWrapper(
realm_wrapper.realm_object_get_or_create_with_primary_key(
realm.cptr(),
classKey.key.toUInt(),
primaryKeyTransport.value.readValue(),
found.ptr
)
)
}
}
actual fun realm_object_is_valid(obj: RealmObjectPointer): Boolean {
return realm_wrapper.realm_object_is_valid(obj.cptr())
}
actual fun realm_object_get_key(obj: RealmObjectPointer): ObjectKey {
return ObjectKey(realm_wrapper.realm_object_get_key(obj.cptr()))
}
actual fun realm_object_resolve_in(obj: RealmObjectPointer, realm: RealmPointer): RealmObjectPointer? {
memScoped {
val objectPointer = allocArray>(1)
checkedBooleanResult(
realm_wrapper.realm_object_resolve_in(obj.cptr(), realm.cptr(), objectPointer)
)
return objectPointer[0]?.let {
return CPointerWrapper(it)
}
}
}
actual fun realm_object_as_link(obj: RealmObjectPointer): Link {
val link: CValue = realm_wrapper.realm_object_as_link(obj.cptr())
link.useContents {
return Link(ClassKey(this.target_table.toLong()), this.target)
}
}
actual fun realm_object_get_table(obj: RealmObjectPointer): ClassKey {
return ClassKey(realm_wrapper.realm_object_get_table(obj.cptr()).toLong())
}
actual fun realm_get_col_key(realm: RealmPointer, classKey: ClassKey, col: String): PropertyKey {
memScoped {
return PropertyKey(propertyInfo(realm, classKey, col).key)
}
}
actual fun MemAllocator.realm_get_value(
obj: RealmObjectPointer,
key: PropertyKey
): RealmValue {
val struct = allocRealmValueT()
checkedBooleanResult(realm_wrapper.realm_get_value(obj.cptr(), key.key, struct.ptr))
return RealmValue(struct)
}
actual fun realm_set_value(
obj: RealmObjectPointer,
key: PropertyKey,
value: RealmValue,
isDefault: Boolean
) {
checkedBooleanResult(
realm_wrapper.realm_set_value(
obj.cptr(),
key.key,
value.value.readValue(),
isDefault
)
)
}
actual fun realm_set_embedded(obj: RealmObjectPointer, key: PropertyKey): RealmObjectPointer {
return CPointerWrapper(realm_wrapper.realm_set_embedded(obj.cptr(), key.key))
}
actual fun realm_object_add_int(obj: RealmObjectPointer, key: PropertyKey, value: Long) {
checkedBooleanResult(realm_wrapper.realm_object_add_int(obj.cptr(), key.key, value))
}
actual fun realm_object_get_parent(
obj: RealmObjectPointer,
block: (ClassKey, RealmObjectPointer) -> T
): T {
memScoped {
val objectPointerArray = allocArray>(1)
val classKeyPointerArray = allocArray(1)
checkedBooleanResult(
realm_wrapper.realm_object_get_parent(
`object` = obj.cptr(),
parent = objectPointerArray,
class_key = classKeyPointerArray
)
)
val classKey = ClassKey(classKeyPointerArray[0].toLong())
val objectPointer = CPointerWrapper(objectPointerArray[0])
return block(classKey, objectPointer)
}
}
actual fun realm_get_list(obj: RealmObjectPointer, key: PropertyKey): RealmListPointer {
return CPointerWrapper(realm_wrapper.realm_get_list(obj.cptr(), key.key))
}
actual fun realm_get_backlinks(obj: RealmObjectPointer, sourceClassKey: ClassKey, sourcePropertyKey: PropertyKey): RealmResultsPointer {
return CPointerWrapper(realm_wrapper.realm_get_backlinks(obj.cptr(), sourceClassKey.key.toUInt(), sourcePropertyKey.key))
}
actual fun realm_list_size(list: RealmListPointer): Long {
memScoped {
val size = alloc()
checkedBooleanResult(realm_wrapper.realm_list_size(list.cptr(), size.ptr))
return size.value.toLong()
}
}
actual fun MemAllocator.realm_list_get(
list: RealmListPointer,
index: Long
): RealmValue {
val struct = allocRealmValueT()
checkedBooleanResult(realm_wrapper.realm_list_get(list.cptr(), index.toULong(), struct.ptr))
return RealmValue(struct)
}
actual fun realm_list_add(list: RealmListPointer, index: Long, transport: RealmValue) {
checkedBooleanResult(
realm_wrapper.realm_list_insert(
list.cptr(),
index.toULong(),
transport.value.readValue()
)
)
}
actual fun realm_list_insert_embedded(list: RealmListPointer, index: Long): RealmObjectPointer {
return CPointerWrapper(realm_wrapper.realm_list_insert_embedded(list.cptr(), index.toULong()))
}
actual fun realm_list_set(
list: RealmListPointer,
index: Long,
inputTransport: RealmValue
) {
checkedBooleanResult(
realm_wrapper.realm_list_set(
list.cptr(),
index.toULong(),
inputTransport.value.readValue()
)
)
}
actual fun MemAllocator.realm_list_set_embedded(
list: RealmListPointer,
index: Long
): RealmValue {
val struct = allocRealmValueT()
// Returns the new object as a Link to follow convention of other getters and allow to
// reuse the converter infrastructure
val embedded = realm_wrapper.realm_list_set_embedded(list.cptr(), index.toULong())
val outputStruct = realm_wrapper.realm_object_as_link(embedded).useContents {
struct.type = realm_value_type.RLM_TYPE_LINK
struct.link.apply {
this.target_table = [email protected]_table
this.target = [email protected]
}
struct
}
return RealmValue(outputStruct)
}
actual fun realm_list_clear(list: RealmListPointer) {
checkedBooleanResult(realm_wrapper.realm_list_clear(list.cptr()))
}
actual fun realm_list_remove_all(list: RealmListPointer) {
checkedBooleanResult(realm_wrapper.realm_list_remove_all(list.cptr()))
}
actual fun realm_list_erase(list: RealmListPointer, index: Long) {
checkedBooleanResult(realm_wrapper.realm_list_erase(list.cptr(), index.toULong()))
}
actual fun realm_list_resolve_in(list: RealmListPointer, realm: RealmPointer): RealmListPointer? {
memScoped {
val listPointer = allocArray>(1)
checkedBooleanResult(
realm_wrapper.realm_list_resolve_in(list.cptr(), realm.cptr(), listPointer)
)
return listPointer[0]?.let {
CPointerWrapper(it)
}
}
}
actual fun realm_list_is_valid(list: RealmListPointer): Boolean {
return realm_wrapper.realm_list_is_valid(list.cptr())
}
actual fun realm_get_set(obj: RealmObjectPointer, key: PropertyKey): RealmSetPointer {
return CPointerWrapper(realm_wrapper.realm_get_set(obj.cptr(), key.key))
}
actual fun realm_set_size(set: RealmSetPointer): Long {
memScoped {
val size = alloc()
checkedBooleanResult(realm_wrapper.realm_set_size(set.cptr(), size.ptr))
return size.value.toLong()
}
}
actual fun realm_set_clear(set: RealmSetPointer) {
checkedBooleanResult(realm_wrapper.realm_set_clear(set.cptr()))
}
actual fun realm_set_insert(set: RealmSetPointer, transport: RealmValue): Boolean {
memScoped {
val inserted = alloc()
checkedBooleanResult(
realm_wrapper.realm_set_insert(
set.cptr(),
transport.value.readValue(),
null,
inserted.ptr
)
)
return inserted.value
}
}
// Returning a non-nullable transport here goes against the approach that increases
// performance (since we need to call getType on the transport object). This is needed though
// because this function is called when calling 'iterator.remove' and causes issues when telling
// the C-API to delete a null transport created within the scope. We need to investigate further
// how to improve this.
actual fun MemAllocator.realm_set_get(set: RealmSetPointer, index: Long): RealmValue {
val struct = allocRealmValueT()
checkedBooleanResult(realm_wrapper.realm_set_get(set.cptr(), index.toULong(), struct.ptr))
return RealmValue(struct)
}
actual fun realm_set_find(set: RealmSetPointer, transport: RealmValue): Boolean {
// TODO optimize: use MemAllocator
memScoped {
val index = alloc()
val found = alloc()
checkedBooleanResult(
realm_wrapper.realm_set_find(
set.cptr(),
transport.value.readValue(),
index.ptr,
found.ptr
)
)
return found.value
}
}
actual fun realm_set_erase(set: RealmSetPointer, transport: RealmValue): Boolean {
// TODO optimize: use MemAllocator
memScoped {
val erased = alloc()
checkedBooleanResult(
realm_wrapper.realm_set_erase(
set.cptr(),
transport.value.readValue(),
erased.ptr
)
)
return erased.value
}
}
actual fun realm_set_remove_all(set: RealmSetPointer) {
checkedBooleanResult(realm_wrapper.realm_set_remove_all(set.cptr()))
}
actual fun realm_set_resolve_in(set: RealmSetPointer, realm: RealmPointer): RealmSetPointer? {
memScoped {
val setPointer = allocArray>(1)
checkedBooleanResult(
realm_wrapper.realm_set_resolve_in(set.cptr(), realm.cptr(), setPointer)
)
return setPointer[0]?.let {
CPointerWrapper(it)
}
}
}
actual fun realm_set_is_valid(set: RealmSetPointer): Boolean {
return realm_wrapper.realm_set_is_valid(set.cptr())
}
actual fun realm_get_dictionary(
obj: RealmObjectPointer,
key: PropertyKey
): RealmMapPointer {
val ptr = realm_wrapper.realm_get_dictionary(obj.cptr(), key.key)
return CPointerWrapper(ptr)
}
actual fun realm_dictionary_clear(dictionary: RealmMapPointer) {
checkedBooleanResult(
realm_wrapper.realm_dictionary_clear(dictionary.cptr())
)
}
actual fun realm_dictionary_size(dictionary: RealmMapPointer): Long {
memScoped {
val size = alloc()
checkedBooleanResult(realm_wrapper.realm_dictionary_size(dictionary.cptr(), size.ptr))
return size.value.toLong()
}
}
actual fun realm_dictionary_to_results(
dictionary: RealmMapPointer
): RealmResultsPointer {
return CPointerWrapper(realm_wrapper.realm_dictionary_to_results(dictionary.cptr()))
}
actual fun MemAllocator.realm_dictionary_find(
dictionary: RealmMapPointer,
mapKey: RealmValue
): RealmValue {
val struct = allocRealmValueT()
// TODO optimize: use MemAllocator
memScoped {
val found = alloc()
checkedBooleanResult(
realm_wrapper.realm_dictionary_find(
dictionary.cptr(),
mapKey.value.readValue(),
struct.ptr,
found.ptr
)
)
// Core will always return a realm_value_t, even if the value was not found, in which case
// the type of the struct will be RLM_TYPE_NULL. This way we signal our converters not to
// worry about nullability and just translate the struct types to their corresponding Kotlin
// types.
return RealmValue(struct)
}
}
actual fun MemAllocator.realm_dictionary_get(
dictionary: RealmMapPointer,
pos: Int
): Pair {
val keyTransport = allocRealmValueT()
val valueTransport = allocRealmValueT()
checkedBooleanResult(
realm_wrapper.realm_dictionary_get(
dictionary.cptr(),
pos.toULong(),
keyTransport.ptr,
valueTransport.ptr
)
)
return Pair(RealmValue(keyTransport), RealmValue(valueTransport))
}
actual fun MemAllocator.realm_dictionary_insert(
dictionary: RealmMapPointer,
mapKey: RealmValue,
value: RealmValue
): Pair {
val previousValue = realm_dictionary_find(dictionary, mapKey)
// TODO optimize: use MemAllocator
memScoped {
realm_dictionary_find(dictionary, mapKey)
val index = alloc()
val inserted = alloc()
checkedBooleanResult(
realm_wrapper.realm_dictionary_insert(
dictionary.cptr(),
mapKey.value.readValue(),
value.value.readValue(),
index.ptr,
inserted.ptr
)
)
return Pair(previousValue, inserted.value)
}
}
actual fun MemAllocator.realm_dictionary_erase(
dictionary: RealmMapPointer,
mapKey: RealmValue
): Pair {
val previousValue = realm_dictionary_find(dictionary, mapKey)
// TODO optimize: use MemAllocator
memScoped {
val erased = alloc()
checkedBooleanResult(
realm_wrapper.realm_dictionary_erase(
dictionary.cptr(),
mapKey.value.readValue(),
erased.ptr
)
)
return Pair(previousValue, erased.value)
}
}
actual fun realm_dictionary_contains_key(
dictionary: RealmMapPointer,
mapKey: RealmValue
): Boolean {
// TODO optimize: use MemAllocator
memScoped {
val found = alloc()
checkedBooleanResult(
realm_wrapper.realm_dictionary_contains_key(
dictionary.cptr(),
mapKey.value.readValue(),
found.ptr
)
)
return found.value
}
}
actual fun realm_dictionary_contains_value(
dictionary: RealmMapPointer,
value: RealmValue
): Boolean {
// TODO optimize: use MemAllocator
memScoped {
val index = alloc()
checkedBooleanResult(
realm_wrapper.realm_dictionary_contains_value(
dictionary.cptr(),
value.value.readValue(),
index.ptr
)
)
return index.value.toLong() != -1L
}
}
actual fun MemAllocator.realm_dictionary_insert_embedded(
dictionary: RealmMapPointer,
mapKey: RealmValue
): RealmValue {
val struct = allocRealmValueT()
// Returns the new object as a Link to follow convention of other getters and allow to
// reuse the converter infrastructure
val embedded = realm_wrapper.realm_dictionary_insert_embedded(
dictionary.cptr(),
mapKey.value.readValue()
)
val outputStruct = realm_wrapper.realm_object_as_link(embedded).useContents {
struct.type = realm_value_type.RLM_TYPE_LINK
struct.link.apply {
this.target_table = [email protected]_table
this.target = [email protected]
}
struct
}
return RealmValue(outputStruct)
}
actual fun realm_dictionary_get_keys(dictionary: RealmMapPointer): RealmResultsPointer {
memScoped {
val size = alloc()
val keysPointer = allocArray>(1)
checkedBooleanResult(
realm_wrapper.realm_dictionary_get_keys(dictionary.cptr(), size.ptr, keysPointer)
)
return keysPointer[0]?.let {
CPointerWrapper(it)
} ?: throw IllegalArgumentException("There was an error retrieving the dictionary keys.")
}
}
actual fun realm_dictionary_resolve_in(
dictionary: RealmMapPointer,
realm: RealmPointer
): RealmMapPointer? {
memScoped {
val dictionaryPointer = allocArray>(1)
checkedBooleanResult(
realm_wrapper.realm_dictionary_resolve_in(
dictionary.cptr(),
realm.cptr(),
dictionaryPointer
)
)
return dictionaryPointer[0]?.let {
CPointerWrapper(it)
}
}
}
actual fun realm_dictionary_is_valid(dictionary: RealmMapPointer): Boolean {
return realm_wrapper.realm_dictionary_is_valid(dictionary.cptr())
}
actual fun realm_query_parse(
realm: RealmPointer,
classKey: ClassKey,
query: String,
args: RealmQueryArgumentList
): RealmQueryPointer {
return CPointerWrapper(
realm_wrapper.realm_query_parse(
realm.cptr(),
classKey.key.toUInt(),
query,
args.size,
args.head.ptr
)
)
}
actual fun realm_query_parse_for_results(
results: RealmResultsPointer,
query: String,
args: RealmQueryArgumentList
): RealmQueryPointer {
return CPointerWrapper(
realm_wrapper.realm_query_parse_for_results(
results.cptr(),
query,
args.size,
args.head.ptr
)
)
}
actual fun realm_query_parse_for_list(
list: RealmListPointer,
query: String,
args: RealmQueryArgumentList
): RealmQueryPointer {
return CPointerWrapper(
realm_wrapper.realm_query_parse_for_list(
list.cptr(),
query,
args.size,
args.head.ptr
)
)
}
actual fun realm_query_parse_for_set(
set: RealmSetPointer,
query: String,
args: RealmQueryArgumentList
): RealmQueryPointer {
return CPointerWrapper(
realm_wrapper.realm_query_parse_for_set(
set.cptr(),
query,
args.size,
args.head.ptr
)
)
}
actual fun realm_query_find_first(query: RealmQueryPointer): Link? {
memScoped {
val found = alloc()
val value = alloc()
checkedBooleanResult(
realm_wrapper.realm_query_find_first(
query.cptr(),
value.ptr,
found.ptr
)
)
if (!found.value) {
return null
}
if (value.type != realm_value_type.RLM_TYPE_LINK) {
error("Query did not return link but ${value.type}")
}
return Link(ClassKey(value.link.target_table.toLong()), value.link.target)
}
}
actual fun realm_query_find_all(query: RealmQueryPointer): RealmResultsPointer {
return CPointerWrapper(realm_wrapper.realm_query_find_all(query.cptr()))
}
actual fun realm_query_count(query: RealmQueryPointer): Long {
memScoped {
val count = alloc()
checkedBooleanResult(realm_wrapper.realm_query_count(query.cptr(), count.ptr))
return count.value.toLong()
}
}
actual fun realm_query_append_query(
query: RealmQueryPointer,
filter: String,
args: RealmQueryArgumentList
): RealmQueryPointer {
return CPointerWrapper(
realm_wrapper.realm_query_append_query(
query.cptr(),
filter,
args.size,
args.head.ptr
)
)
}
actual fun realm_query_get_description(query: RealmQueryPointer): String {
return realm_wrapper.realm_query_get_description(query.cptr()).safeKString()
}
actual fun realm_results_get_query(results: RealmResultsPointer): RealmQueryPointer {
return CPointerWrapper(realm_wrapper.realm_results_get_query(results.cptr()))
}
actual fun realm_results_resolve_in(
results: RealmResultsPointer,
realm: RealmPointer
): RealmResultsPointer {
return CPointerWrapper(
realm_wrapper.realm_results_resolve_in(
results.cptr(),
realm.cptr()
)
)
}
actual fun realm_results_count(results: RealmResultsPointer): Long {
memScoped {
val count = alloc()
checkedBooleanResult(realm_wrapper.realm_results_count(results.cptr(), count.ptr))
return count.value.toLong()
}
}
actual fun MemAllocator.realm_results_average(
results: RealmResultsPointer,
propertyKey: PropertyKey
): Pair {
val struct = allocRealmValueT()
// TODO optimize: integrate allocation of other native types in MemAllocator too
memScoped {
val found = cValue().ptr
checkedBooleanResult(
realm_wrapper.realm_results_average(
results.cptr(),
propertyKey.key,
struct.ptr,
found
)
)
return found.pointed.value to RealmValue(struct)
}
}
actual fun MemAllocator.realm_results_sum(
results: RealmResultsPointer,
propertyKey: PropertyKey
): RealmValue {
val struct = allocRealmValueT()
checkedBooleanResult(
realm_wrapper.realm_results_sum(
results.cptr(),
propertyKey.key,
struct.ptr,
null
)
)
val transport = RealmValue(struct)
return transport
}
actual fun MemAllocator.realm_results_max(
results: RealmResultsPointer,
propertyKey: PropertyKey
): RealmValue {
val struct = allocRealmValueT()
checkedBooleanResult(
realm_wrapper.realm_results_max(
results.cptr(),
propertyKey.key,
struct.ptr,
null
)
)
return RealmValue(struct)
}
actual fun MemAllocator.realm_results_min(
results: RealmResultsPointer,
propertyKey: PropertyKey
): RealmValue {
val struct = allocRealmValueT()
checkedBooleanResult(
realm_wrapper.realm_results_min(
results.cptr(),
propertyKey.key,
struct.ptr,
null
)
)
return RealmValue(struct)
}
actual fun MemAllocator.realm_results_get(results: RealmResultsPointer, index: Long): RealmValue {
val value = allocRealmValueT()
checkedBooleanResult(
realm_wrapper.realm_results_get(
results.cptr(),
index.toULong(),
value.ptr
)
)
return RealmValue(value)
}
actual fun realm_get_object(realm: RealmPointer, link: Link): RealmObjectPointer {
val ptr = checkedPointerResult(
realm_wrapper.realm_get_object(
realm.cptr(),
link.classKey.key.toUInt(),
link.objKey
)
)
return CPointerWrapper(ptr)
}
actual fun realm_object_find_with_primary_key(
realm: RealmPointer,
classKey: ClassKey,
transport: RealmValue
): RealmObjectPointer? {
val ptr = memScoped {
val found = alloc()
realm_wrapper.realm_object_find_with_primary_key(
realm.cptr(),
classKey.key.toUInt(),
transport.value.readValue(),
found.ptr
)
}
val checkedPtr = checkedPointerResult(ptr)
return if (checkedPtr != null) CPointerWrapper(checkedPtr) else null
}
actual fun realm_results_delete_all(results: RealmResultsPointer) {
checkedBooleanResult(realm_wrapper.realm_results_delete_all(results.cptr()))
}
actual fun realm_object_delete(obj: RealmObjectPointer) {
checkedBooleanResult(realm_wrapper.realm_object_delete(obj.cptr()))
}
actual fun realm_create_key_paths_array(realm: RealmPointer, clazz: ClassKey, keyPaths: List): RealmKeyPathArrayPointer {
memScoped {
val userKeyPaths: CPointer>>> = keyPaths.toCStringArray(this)
val keyPathPointer = realm_wrapper.realm_create_key_path_array(realm.cptr(), clazz.key.toUInt(), keyPaths.size.toULong(), userKeyPaths)
return CPointerWrapper(keyPathPointer)
}
}
actual fun realm_object_add_notification_callback(
obj: RealmObjectPointer,
keyPaths: RealmKeyPathArrayPointer?,
callback: Callback
): RealmNotificationTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_object_add_notification_callback(
obj.cptr(),
// Use the callback as user data
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
userdata?.asStableRef>()
?.dispose()
?: error("Notification callback data should never be null")
},
keyPaths?.cptr(),
staticCFunction { userdata, change -> // Change callback
try {
userdata?.asStableRef>()
?.get()
?.onChange(CPointerWrapper(realm_clone(change), managed = true))
?: error("Notification callback data should never be null")
} catch (e: Exception) {
// TODO API-NOTIFICATION Consider catching errors and propagate to error
// callback like the C-API error callback below
// https://github.com/realm/realm-kotlin/issues/889
e.printStackTrace()
}
},
),
managed = false
)
}
actual fun realm_results_add_notification_callback(
results: RealmResultsPointer,
keyPaths: RealmKeyPathArrayPointer?,
callback: Callback
): RealmNotificationTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_results_add_notification_callback(
results.cptr(),
// Use the callback as user data
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
userdata?.asStableRef>()
?.dispose()
?: error("Notification callback data should never be null")
},
keyPaths?.cptr(),
staticCFunction { userdata, change -> // Change callback
try {
userdata?.asStableRef>()
?.get()
?.onChange(CPointerWrapper(realm_clone(change), managed = true))
?: error("Notification callback data should never be null")
} catch (e: Exception) {
// TODO API-NOTIFICATION Consider catching errors and propagate to error
// callback like the C-API error callback below
// https://github.com/realm/realm-kotlin/issues/889
e.printStackTrace()
}
},
),
managed = false
)
}
actual fun realm_list_add_notification_callback(
list: RealmListPointer,
keyPaths: RealmKeyPathArrayPointer?,
callback: Callback
): RealmNotificationTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_list_add_notification_callback(
list.cptr(),
// Use the callback as user data
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
userdata?.asStableRef>()?.dispose()
?: error("Notification callback data should never be null")
},
keyPaths?.cptr(),
staticCFunction { userdata, change -> // Change callback
try {
userdata?.asStableRef>()
?.get()
?.onChange(CPointerWrapper(realm_clone(change), managed = true))
?: error("Notification callback data should never be null")
} catch (e: Exception) {
// TODO API-NOTIFICATION Consider catching errors and propagate to error
// callback like the C-API error callback below
// https://github.com/realm/realm-kotlin/issues/889
e.printStackTrace()
}
},
),
managed = false
)
}
actual fun realm_set_add_notification_callback(
set: RealmSetPointer,
keyPaths: RealmKeyPathArrayPointer?,
callback: Callback
): RealmNotificationTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_set_add_notification_callback(
set.cptr(),
// Use the callback as user data
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
userdata?.asStableRef>()
?.dispose()
?: error("Notification callback data should never be null")
},
keyPaths?.cptr(),
staticCFunction { userdata, change -> // Change callback
try {
userdata?.asStableRef>()
?.get()
?.onChange(CPointerWrapper(realm_clone(change), managed = true))
?: error("Notification callback data should never be null")
} catch (e: Exception) {
// TODO API-NOTIFICATION Consider catching errors and propagate to error
// callback like the C-API error callback below
// https://github.com/realm/realm-kotlin/issues/889
e.printStackTrace()
}
},
),
managed = false
)
}
actual fun realm_dictionary_add_notification_callback(
map: RealmMapPointer,
keyPaths: RealmKeyPathArrayPointer?,
callback: Callback
): RealmNotificationTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_dictionary_add_notification_callback(
map.cptr(),
// Use the callback as user data
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
userdata?.asStableRef>()
?.dispose()
?: error("Notification callback data should never be null")
},
keyPaths?.cptr(),
staticCFunction { userdata, change -> // Change callback
try {
userdata?.asStableRef>()
?.get()
?.onChange(CPointerWrapper(realm_clone(change), managed = true))
?: error("Notification callback data should never be null")
} catch (e: Exception) {
// TODO API-NOTIFICATION Consider catching errors and propagate to error
// callback like the C-API error callback below
// https://github.com/realm/realm-kotlin/issues/889
e.printStackTrace()
}
},
),
managed = false
)
}
actual fun realm_object_changes_get_modified_properties(change: RealmChangesPointer): List {
val propertyCount = realm_wrapper.realm_object_changes_get_num_modified_properties(change.cptr())
memScoped {
val propertyKeys = allocArray(propertyCount.toLong())
realm_wrapper.realm_object_changes_get_modified_properties(change.cptr(), propertyKeys, propertyCount)
return (0 until propertyCount.toInt()).map { PropertyKey(propertyKeys[it].toLong()) }
}
}
private inline fun MemScope.initArray(size: CArrayPointer) = allocArray(size[0].toInt())
actual fun realm_collection_changes_get_indices(change: RealmChangesPointer, builder: CollectionChangeSetBuilder) {
memScoped {
val insertionCount = allocArray(1)
val deletionCount = allocArray(1)
val modificationCount = allocArray(1)
val movesCount = allocArray(1)
val collectionWasErased = alloc()
realm_wrapper.realm_collection_changes_get_num_changes(
change.cptr(),
deletionCount,
insertionCount,
modificationCount,
movesCount,
collectionWasErased.ptr
)
val deletionIndices = initArray(deletionCount)
val insertionIndices = initArray(insertionCount)
val modificationIndices = initArray(modificationCount)
val modificationIndicesAfter = initArray(modificationCount)
val moves = initArray(movesCount)
realm_wrapper.realm_collection_changes_get_changes(
change.cptr(),
deletionIndices,
deletionCount[0],
insertionIndices,
insertionCount[0],
modificationIndices,
modificationCount[0],
modificationIndicesAfter,
modificationCount[0],
moves,
movesCount[0]
)
builder.initIndicesArray(builder::insertionIndices, insertionCount, insertionIndices)
builder.initIndicesArray(builder::deletionIndices, deletionCount, deletionIndices)
builder.initIndicesArray(builder::modificationIndices, modificationCount, modificationIndices)
builder.initIndicesArray(builder::modificationIndicesAfter, modificationCount, modificationIndicesAfter)
builder.movesCount = movesCount[0].toInt()
}
}
actual fun realm_collection_changes_get_ranges(change: RealmChangesPointer, builder: CollectionChangeSetBuilder) {
memScoped {
val insertRangesCount = allocArray(1)
val deleteRangesCount = allocArray(1)
val modificationRangesCount = allocArray(1)
val movesCount = allocArray(1)
realm_wrapper.realm_collection_changes_get_num_ranges(
change.cptr(),
deleteRangesCount,
insertRangesCount,
modificationRangesCount,
movesCount
)
val insertionRanges = initArray(insertRangesCount)
val modificationRanges = initArray(modificationRangesCount)
val modificationRangesAfter = initArray(modificationRangesCount)
val deletionRanges = initArray(deleteRangesCount)
val moves = initArray(movesCount)
realm_wrapper.realm_collection_changes_get_ranges(
change.cptr(),
deletionRanges,
deleteRangesCount[0],
insertionRanges,
insertRangesCount[0],
modificationRanges,
modificationRangesCount[0],
modificationRangesAfter,
modificationRangesCount[0],
moves,
movesCount[0]
)
builder.initRangesArray(builder::deletionRanges, deleteRangesCount, deletionRanges)
builder.initRangesArray(builder::insertionRanges, insertRangesCount, insertionRanges)
builder.initRangesArray(builder::modificationRanges, modificationRangesCount, modificationRanges)
builder.initRangesArray(builder::modificationRangesAfter, modificationRangesCount, modificationRangesAfter)
}
}
actual fun realm_dictionary_get_changes(
change: RealmChangesPointer,
builder: DictionaryChangeSetBuilder
) {
// TODO optimize - integrate within mem allocator?
memScoped {
val deletions = allocArray(1)
val insertions = allocArray(1)
val modifications = allocArray(1)
realm_wrapper.realm_dictionary_get_changes(
change.cptr(),
deletions,
insertions,
modifications
)
val deletionStructs = allocArray(deletions[0].toInt())
val insertionStructs = allocArray(insertions[0].toInt())
val modificationStructs = allocArray(modifications[0].toInt())
realm_wrapper.realm_dictionary_get_changed_keys(
change.cptr(),
deletionStructs,
deletions,
insertionStructs,
insertions,
modificationStructs,
modifications
)
val deletedKeys = (0 until deletions[0].toInt()).map {
deletionStructs[it].string.toKotlinString()
}
val insertedKeys = (0 until insertions[0].toInt()).map {
insertionStructs[it].string.toKotlinString()
}
val modifiedKeys = (0 until modifications[0].toInt()).map {
modificationStructs[it].string.toKotlinString()
}
builder.initDeletions(deletedKeys.toTypedArray())
builder.initInsertions(insertedKeys.toTypedArray())
builder.initModifications(modifiedKeys.toTypedArray())
}
}
actual fun realm_app_get(
appConfig: RealmAppConfigurationPointer,
syncClientConfig: RealmSyncClientConfigurationPointer,
basePath: String
): RealmAppPointer {
return CPointerWrapper(realm_wrapper.realm_app_create(appConfig.cptr(), syncClientConfig.cptr()), managed = true)
}
actual fun realm_app_get_current_user(app: RealmAppPointer): RealmUserPointer? {
val currentUserPtr: CPointer? = realm_wrapper.realm_app_get_current_user(app.cptr())
return nativePointerOrNull(currentUserPtr)
}
actual fun realm_app_get_all_users(app: RealmAppPointer): List {
memScoped {
// We get the current amount of users by providing a `null` array and `out_n`
// argument. Then the current count is written to `out_n`.
// See https://github.com/realm/realm-core/blob/master/src/realm.h#L2634
val capacityCount: ULongVarOf = alloc()
checkedBooleanResult(
realm_wrapper.realm_app_get_all_users(
app.cptr(),
null,
0UL,
capacityCount.ptr
)
)
// Read actual users. We don't care about the small chance of missing a new user
// between these two calls as that indicate two sections of user code running on
// different threads and not coordinating.
val actualUsersCount: ULongVarOf = alloc()
val users = allocArray>(capacityCount.value.toInt())
checkedBooleanResult(realm_wrapper.realm_app_get_all_users(app.cptr(), users, capacityCount.value, actualUsersCount.ptr))
val result: MutableList = mutableListOf()
for (i in 0 until actualUsersCount.value.toInt()) {
users[i]?.let { ptr: CPointer ->
result.add(CPointerWrapper(ptr, managed = true))
}
}
return result
}
}
actual fun realm_app_log_in_with_credentials(
app: RealmAppPointer,
credentials: RealmCredentialsPointer,
callback: AppCallback
) {
realm_wrapper.realm_app_log_in_with_credentials(
app.cptr(),
credentials.cptr(),
staticCFunction { userData, user, error: CPointer? ->
// Remember to clone user object or else it will go out of scope right after we leave this callback
handleAppCallback(userData, error) { CPointerWrapper(realm_clone(user)) }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata -> disposeUserData>(userdata) }
)
}
actual fun realm_app_user_apikey_provider_client_create_apikey(
app: RealmAppPointer,
user: RealmUserPointer,
name: String,
callback: AppCallback
) {
checkedBooleanResult(
realm_wrapper.realm_app_user_apikey_provider_client_create_apikey(
app.cptr(),
user.cptr(),
name,
staticCFunction { userData: CPointer?, apiKey: CPointer?, error: CPointer? ->
handleAppCallback(userData, error) {
apiKey!!.pointed.let {
ApiKeyWrapper(
ObjectId(
it.id.bytes.readBytes(OBJECT_ID_BYTES_SIZE),
),
it.key.safeKString(),
it.name.safeKString(),
it.disabled
)
}
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata -> disposeUserData>(userdata) }
)
)
}
actual fun realm_app_user_apikey_provider_client_delete_apikey(
app: RealmAppPointer,
user: RealmUserPointer,
id: BsonObjectId,
callback: AppCallback,
) {
checkedBooleanResult(
realm_wrapper.realm_app_user_apikey_provider_client_delete_apikey(
app.cptr(),
user.cptr(),
id.realm_object_id_t(),
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData>(userData) }
)
)
}
actual fun realm_app_user_apikey_provider_client_disable_apikey(
app: RealmAppPointer,
user: RealmUserPointer,
id: BsonObjectId,
callback: AppCallback,
) {
checkedBooleanResult(
realm_wrapper.realm_app_user_apikey_provider_client_disable_apikey(
app.cptr(),
user.cptr(),
id.realm_object_id_t(),
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData>(userData) }
)
)
}
actual fun realm_app_user_apikey_provider_client_enable_apikey(
app: RealmAppPointer,
user: RealmUserPointer,
id: BsonObjectId,
callback: AppCallback,
) {
checkedBooleanResult(
realm_wrapper.realm_app_user_apikey_provider_client_enable_apikey(
app.cptr(),
user.cptr(),
id.realm_object_id_t(),
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData>(userData) }
)
)
}
actual fun realm_app_user_apikey_provider_client_fetch_apikey(
app: RealmAppPointer,
user: RealmUserPointer,
id: ObjectId,
callback: AppCallback
) {
checkedBooleanResult(
realm_wrapper.realm_app_user_apikey_provider_client_fetch_apikey(
app.cptr(),
user.cptr(),
id.realm_object_id_t(),
staticCFunction { userData: CPointer?, apiKey: CPointer?, error: CPointer? ->
handleAppCallback(userData, error) {
apiKey!!.pointed.let {
ApiKeyWrapper(
ObjectId(
it.id.bytes.readBytes(OBJECT_ID_BYTES_SIZE),
),
null,
it.name.safeKString(),
it.disabled
)
}
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata -> disposeUserData>(userdata) }
)
)
}
actual fun realm_app_user_apikey_provider_client_fetch_apikeys(
app: RealmAppPointer,
user: RealmUserPointer,
callback: AppCallback>,
) {
checkedBooleanResult(
realm_wrapper.realm_app_user_apikey_provider_client_fetch_apikeys(
app.cptr(),
user.cptr(),
staticCFunction { userData: CPointer?, apiKeys: CPointer?, count: size_t, error: CPointer? ->
handleAppCallback(userData, error) {
val result = arrayOfNulls(count.toInt())
for (i in 0 until count.toInt()) {
apiKeys!![i].let {
result[i] = ApiKeyWrapper(
ObjectId(
it.id.bytes.readBytes(OBJECT_ID_BYTES_SIZE),
),
null,
it.name.safeKString(),
it.disabled
)
}
}
result
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata -> disposeUserData>>(userdata) }
)
)
}
actual fun realm_app_log_out(
app: RealmAppPointer,
user: RealmUserPointer,
callback: AppCallback
) {
checkedBooleanResult(
realm_wrapper.realm_app_log_out(
app.cptr(),
user.cptr(),
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata -> disposeUserData>(userdata) }
)
)
}
actual fun realm_app_remove_user(
app: RealmAppPointer,
user: RealmUserPointer,
callback: AppCallback
) {
checkedBooleanResult(
realm_wrapper.realm_app_remove_user(
app.cptr(),
user.cptr(),
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData>(userdata)
}
)
)
}
actual fun realm_app_delete_user(
app: RealmAppPointer,
user: RealmUserPointer,
callback: AppCallback
) {
checkedBooleanResult(
realm_wrapper.realm_app_delete_user(
app.cptr(),
user.cptr(),
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData>(userdata)
}
)
)
}
actual fun realm_app_link_credentials(
app: RealmAppPointer,
user: RealmUserPointer,
credentials: RealmCredentialsPointer,
callback: AppCallback
) {
checkedBooleanResult(
realm_wrapper.realm_app_link_user(
app.cptr(),
user.cptr(),
credentials.cptr(),
staticCFunction { userData, user, error: CPointer? ->
// Remember to clone user object or else it will go out of scope right after we leave this callback
handleAppCallback(userData, error) { CPointerWrapper(realm_clone(user)) }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData>(userdata)
}
)
)
}
actual fun realm_clear_cached_apps() {
realm_wrapper.realm_clear_cached_apps()
}
actual fun realm_app_sync_client_get_default_file_path_for_realm(
app: RealmAppPointer,
syncConfig: RealmSyncConfigurationPointer,
overriddenName: String?
): String {
val cPath = realm_wrapper.realm_app_sync_client_get_default_file_path_for_realm(
syncConfig.cptr(),
overriddenName
)
return cPath.safeKString()
.also { realm_wrapper.realm_free(cPath) }
}
actual fun realm_user_get_all_identities(user: RealmUserPointer): List {
memScoped {
val count = AuthProvider.values().size
val properties = allocArray(count)
val outCount = alloc()
realm_wrapper.realm_user_get_all_identities(
user.cptr(),
properties,
count.convert(),
outCount.ptr
)
outCount.value.toLong().let { count ->
return if (count > 0) {
(0 until outCount.value.toLong()).map {
with(properties[it]) {
SyncUserIdentity(this.id!!.toKString(), AuthProvider.of(this.provider_type))
}
}
} else {
emptyList()
}
}
}
}
actual fun realm_user_get_identity(user: RealmUserPointer): String {
return realm_wrapper.realm_user_get_identity(user.cptr()).safeKString("identity")
}
actual fun realm_user_is_logged_in(user: RealmUserPointer): Boolean {
return realm_wrapper.realm_user_is_logged_in(user.cptr())
}
actual fun realm_user_log_out(user: RealmUserPointer) {
checkedBooleanResult(realm_wrapper.realm_user_log_out(user.cptr()))
}
actual fun realm_user_get_state(user: RealmUserPointer): CoreUserState {
return CoreUserState.of(realm_wrapper.realm_user_get_state(user.cptr()))
}
actual fun realm_user_get_profile(user: RealmUserPointer): String =
realm_wrapper.realm_user_get_profile_data(user.cptr()).safeKString()
actual fun realm_user_get_custom_data(user: RealmUserPointer): String? =
realm_wrapper.realm_user_get_custom_data(user.cptr())?.toKString()
actual fun realm_user_refresh_custom_data(
app: RealmAppPointer,
user: RealmUserPointer,
callback: AppCallback
) {
checkedBooleanResult(
realm_wrapper.realm_app_refresh_custom_data(
app = app.cptr(),
user = user.cptr(),
callback = staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
userdata = StableRef.create(callback).asCPointer(),
userdata_free = staticCFunction { userData -> disposeUserData>(userData) }
)
)
}
actual fun realm_sync_client_config_new(): RealmSyncClientConfigurationPointer {
return CPointerWrapper(realm_wrapper.realm_sync_client_config_new())
}
actual fun realm_sync_client_config_set_default_binding_thread_observer(
syncClientConfig: RealmSyncClientConfigurationPointer,
appId: String
) {
realm_wrapper.realm_sync_client_config_set_default_binding_thread_observer(
config = syncClientConfig.cptr(),
on_thread_create = staticCFunction { _ ->
// Do nothing
},
on_thread_destroy = staticCFunction { _ ->
// Do nothing. Threads in Kotlin Native are cleaned up correctly without us
// having to do anything.
},
on_error = staticCFunction { userdata, error ->
// TODO Wait for https://github.com/realm/realm-core/issues/4194 to correctly
// log errors. For now, just throw an Error as exceptions from the Sync Client
// indicate that something is fundamentally wrong on the Sync Thread.
// In Realm Java this has only been reported during development of new
// features, so throwing an Error seems appropriate to increase visibility.
val threadId = safeUserData(userdata)
throw Error("[SyncThread-$threadId] Error on sync thread: ${error?.toKString()}")
},
user_data = StableRef.create(appId).asCPointer(),
free_userdata = staticCFunction { userdata ->
disposeUserData(userdata)
}
)
}
actual fun realm_sync_client_config_set_base_file_path(
syncClientConfig: RealmSyncClientConfigurationPointer,
basePath: String
) {
realm_wrapper.realm_sync_client_config_set_base_file_path(syncClientConfig.cptr(), basePath)
}
actual fun realm_sync_client_config_set_multiplex_sessions(syncClientConfig: RealmSyncClientConfigurationPointer, enabled: Boolean) {
realm_wrapper.realm_sync_client_config_set_multiplex_sessions(syncClientConfig.cptr(), enabled)
}
actual fun realm_set_log_callback(level: CoreLogLevel, callback: LogCallback) {
realm_wrapper.realm_set_log_callback(
staticCFunction { userData, logLevel, message ->
val userDataLogCallback = safeUserData(userData)
userDataLogCallback.log(logLevel.toShort(), message?.toKString())
},
level.priority.toUInt(),
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData<() -> LogCallback>(userData) }
)
}
actual fun realm_set_log_level(level: CoreLogLevel) {
realm_wrapper.realm_set_log_level(level.priority.toUInt())
}
actual fun realm_sync_client_config_set_metadata_mode(
syncClientConfig: RealmSyncClientConfigurationPointer,
metadataMode: MetadataMode
) {
realm_wrapper.realm_sync_client_config_set_metadata_mode(
syncClientConfig.cptr(),
realm_sync_client_metadata_mode.byValue(metadataMode.metadataValue.toUInt())
)
}
actual fun realm_sync_client_config_set_metadata_encryption_key(
syncClientConfig: RealmSyncClientConfigurationPointer,
encryptionKey: ByteArray
) {
memScoped {
val encryptionKeyPointer = encryptionKey.refTo(0).getPointer(memScope)
realm_wrapper.realm_sync_client_config_set_metadata_encryption_key(
syncClientConfig.cptr(),
encryptionKeyPointer as CPointer
)
}
}
actual fun realm_sync_client_config_set_user_agent_binding_info(
syncClientConfig: RealmSyncClientConfigurationPointer,
bindingInfo: String
) {
realm_wrapper.realm_sync_client_config_set_user_agent_binding_info(
syncClientConfig.cptr(),
bindingInfo
)
}
actual fun realm_sync_client_config_set_user_agent_application_info(
syncClientConfig: RealmSyncClientConfigurationPointer,
applicationInfo: String
) {
realm_wrapper.realm_sync_client_config_set_user_agent_application_info(
syncClientConfig.cptr(),
applicationInfo
)
}
actual fun realm_sync_client_config_set_connect_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_connect_timeout(syncClientConfig.cptr(), timeoutMs)
}
actual fun realm_sync_client_config_set_connection_linger_time(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_connection_linger_time(syncClientConfig.cptr(), timeoutMs)
}
actual fun realm_sync_client_config_set_ping_keepalive_period(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_ping_keepalive_period(syncClientConfig.cptr(), timeoutMs)
}
actual fun realm_sync_client_config_set_pong_keepalive_timeout(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_pong_keepalive_timeout(syncClientConfig.cptr(), timeoutMs)
}
actual fun realm_sync_client_config_set_fast_reconnect_limit(syncClientConfig: RealmSyncClientConfigurationPointer, timeoutMs: ULong) {
realm_wrapper.realm_sync_client_config_set_fast_reconnect_limit(syncClientConfig.cptr(), timeoutMs)
}
actual fun realm_sync_config_set_error_handler(
syncConfig: RealmSyncConfigurationPointer,
errorHandler: SyncErrorCallback
) {
realm_wrapper.realm_sync_config_set_error_handler(
syncConfig.cptr(),
staticCFunction { userData, syncSession, error ->
val syncError: SyncError = error.useContents {
val code = CoreError(
this.status.categories.toInt(),
this.status.error.value.toInt(),
this.status.message.safeKString()
)
val userInfoMap = (0 until user_info_length.toInt())
.mapNotNull {
user_info_map?.get(it)
}.mapNotNull {
when {
it.key != null && it.value != null ->
Pair(it.key.safeKString(), it.value.safeKString())
else -> null
}
}.toMap()
val compensatingWrites =
Array(compensating_writes_length.toInt()) { index ->
compensating_writes!![index].let { compensatingWriteInfo ->
CoreCompensatingWriteInfo(
reason = compensatingWriteInfo.reason.safeKString(),
objectName = compensatingWriteInfo.object_name.safeKString(),
primaryKey = RealmValue(compensatingWriteInfo.primary_key)
)
}
}
SyncError(
errorCode = code,
originalFilePath = userInfoMap[c_original_file_path_key.safeKString()],
recoveryFilePath = userInfoMap[c_recovery_file_path_key.safeKString()],
isFatal = is_fatal,
isUnrecognizedByClient = is_unrecognized_by_client,
isClientResetRequested = is_client_reset_requested,
compensatingWrites = compensatingWrites
)
}
val errorCallback = safeUserData(userData)
val session = CPointerWrapper(realm_clone(syncSession))
errorCallback.onSyncError(session, syncError)
},
StableRef.create(errorHandler).asCPointer(),
staticCFunction { userdata ->
disposeUserData<(RealmSyncSessionPointer, SyncErrorCallback) -> Unit>(userdata)
}
)
}
actual fun realm_sync_config_set_resync_mode(
syncConfig: RealmSyncConfigurationPointer,
resyncMode: SyncSessionResyncMode
) {
realm_wrapper.realm_sync_config_set_resync_mode(
syncConfig.cptr(),
realm_sync_session_resync_mode.byValue(resyncMode.nativeValue)
)
}
actual fun realm_sync_config_set_before_client_reset_handler(
syncConfig: RealmSyncConfigurationPointer,
beforeHandler: SyncBeforeClientResetHandler
) {
realm_wrapper.realm_sync_config_set_before_client_reset_handler(
syncConfig.cptr(),
staticCFunction { userData, beforeRealm ->
val beforeCallback = safeUserData(userData)
val beforeDb = CPointerWrapper(beforeRealm, false)
// Check if exceptions have been thrown, return true if all went as it should
try {
beforeCallback.onBeforeReset(beforeDb)
true
} catch (e: Throwable) {
println(e.message)
false
}
},
StableRef.create(beforeHandler).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
)
}
actual fun realm_sync_config_set_after_client_reset_handler(
syncConfig: RealmSyncConfigurationPointer,
afterHandler: SyncAfterClientResetHandler
) {
realm_wrapper.realm_sync_config_set_after_client_reset_handler(
syncConfig.cptr(),
staticCFunction { userData, beforeRealm, afterRealm, didRecover ->
val afterCallback = safeUserData(userData)
val beforeDb = CPointerWrapper(beforeRealm, false)
// afterRealm is wrapped inside a ThreadSafeReference so the pointer needs to be resolved
val afterRealmPtr = realm_wrapper.realm_from_thread_safe_reference(afterRealm, null)
val afterDb = CPointerWrapper(afterRealmPtr, false)
// Check if exceptions have been thrown, return true if all went as it should
try {
afterCallback.onAfterReset(beforeDb, afterDb, didRecover)
true
} catch (e: Throwable) {
println(e.message)
false
} finally {
realm_wrapper.realm_close(afterRealmPtr)
}
},
StableRef.create(afterHandler).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
)
}
actual fun realm_sync_immediately_run_file_actions(app: RealmAppPointer, syncPath: String): Boolean {
memScoped {
val didRun = alloc()
checkedBooleanResult(
realm_wrapper.realm_sync_immediately_run_file_actions(app.cptr(), syncPath, didRun.ptr)
)
return didRun.value
}
}
actual fun realm_sync_session_get(realm: RealmPointer): RealmSyncSessionPointer {
return CPointerWrapper(realm_wrapper.realm_sync_session_get(realm.cptr()))
}
actual fun realm_sync_session_wait_for_download_completion(
syncSession: RealmSyncSessionPointer,
callback: SyncSessionTransferCompletionCallback
) {
realm_wrapper.realm_sync_session_wait_for_download_completion(
syncSession.cptr(),
staticCFunction?, Unit> { userData, error ->
handleCompletionCallback(userData, error)
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData<(RealmSyncSessionPointer, SyncSessionTransferCompletionCallback) -> Unit>(userdata)
}
)
}
actual fun realm_sync_session_wait_for_upload_completion(
syncSession: RealmSyncSessionPointer,
callback: SyncSessionTransferCompletionCallback
) {
realm_wrapper.realm_sync_session_wait_for_upload_completion(
syncSession.cptr(),
staticCFunction?, Unit> { userData, error ->
handleCompletionCallback(userData, error)
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData<(RealmSyncSessionPointer, SyncSessionTransferCompletionCallback) -> Unit>(userdata)
}
)
}
actual fun realm_sync_session_state(syncSession: RealmSyncSessionPointer): CoreSyncSessionState {
val value: realm_sync_session_state_e =
realm_wrapper.realm_sync_session_get_state(syncSession.cptr())
return CoreSyncSessionState.of(value)
}
actual fun realm_sync_connection_state(syncSession: RealmSyncSessionPointer): CoreConnectionState =
CoreConnectionState.of(
realm_wrapper.realm_sync_session_get_connection_state(syncSession.cptr()).value.toInt()
)
actual fun realm_sync_session_pause(syncSession: RealmSyncSessionPointer) {
realm_wrapper.realm_sync_session_pause(syncSession.cptr())
}
actual fun realm_sync_session_resume(syncSession: RealmSyncSessionPointer) {
realm_wrapper.realm_sync_session_resume(syncSession.cptr())
}
actual fun realm_sync_session_handle_error_for_testing(
syncSession: RealmSyncSessionPointer,
error: ErrorCode,
errorMessage: String,
isFatal: Boolean
) {
realm_wrapper.realm_sync_session_handle_error_for_testing(
syncSession.cptr(),
error.asNativeEnum,
errorMessage,
isFatal
)
}
actual fun realm_sync_session_register_progress_notifier(
syncSession: RealmSyncSessionPointer,
direction: ProgressDirection,
isStreaming: Boolean,
callback: ProgressCallback,
): RealmNotificationTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_sync_session_register_progress_notifier(
syncSession.cptr(),
staticCFunction { userData, transferred_bytes, total_bytes ->
safeUserData(userData).run {
onChange(transferred_bytes.toLong(), total_bytes.toLong())
}
},
direction.nativeValue,
isStreaming,
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
),
managed = false
)
}
actual fun realm_sync_session_register_connection_state_change_callback(
syncSession: RealmSyncSessionPointer,
callback: ConnectionStateChangeCallback,
): RealmNotificationTokenPointer {
return CPointerWrapper(
realm_wrapper.realm_sync_session_register_connection_state_change_callback(
syncSession.cptr(),
staticCFunction { userData, oldState, newState ->
safeUserData(userData).run {
onChange(oldState.value.toInt(), newState.value.toInt())
}
},
StableRef.create(callback).asCPointer(),
staticCFunction { userdata ->
disposeUserData(userdata)
}
),
managed = false
)
}
private fun handleCompletionCallback(
userData: CPointer?,
error: CPointer?
) {
val completionCallback = safeUserData(userData)
if (error != null) {
val category = error.pointed.categories.toInt()
val value: Int = error.pointed.error.value.toInt()
val message = error.pointed.message.safeKString()
completionCallback.invoke(CoreError(category, value, message))
} else {
completionCallback.invoke(null)
}
}
actual fun realm_network_transport_new(networkTransport: NetworkTransport): RealmNetworkTransportPointer {
return CPointerWrapper(
realm_wrapper.realm_http_transport_new(
newRequestLambda,
StableRef.create(networkTransport).asCPointer(),
staticCFunction { userdata: CPointer? ->
disposeUserData(userdata)
}
)
)
}
@Suppress("LongParameterList")
actual fun realm_app_config_new(
appId: String,
networkTransport: RealmNetworkTransportPointer,
baseUrl: String?,
connectionParams: SyncConnectionParams
): RealmAppConfigurationPointer {
val appConfig = realm_wrapper.realm_app_config_new(appId, networkTransport.cptr())
baseUrl?.let { realm_wrapper.realm_app_config_set_base_url(appConfig, it) }
// Sync Connection Parameters
realm_wrapper.realm_app_config_set_sdk(appConfig, connectionParams.sdkName)
realm_wrapper.realm_app_config_set_sdk_version(appConfig, connectionParams.sdkVersion)
realm_wrapper.realm_app_config_set_platform_version(appConfig, connectionParams.platformVersion)
realm_wrapper.realm_app_config_set_device_name(appConfig, connectionParams.device)
realm_wrapper.realm_app_config_set_device_version(appConfig, connectionParams.deviceVersion)
realm_wrapper.realm_app_config_set_framework_name(appConfig, connectionParams.framework)
realm_wrapper.realm_app_config_set_framework_version(appConfig, connectionParams.frameworkVersion)
realm_wrapper.realm_app_config_set_bundle_id(appConfig, connectionParams.bundleId)
return CPointerWrapper(appConfig)
}
actual fun realm_app_config_set_base_url(appConfig: RealmAppConfigurationPointer, baseUrl: String) {
realm_wrapper.realm_app_config_set_base_url(appConfig.cptr(), baseUrl)
}
actual fun realm_app_credentials_new_anonymous(reuseExisting: Boolean): RealmCredentialsPointer {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_anonymous(reuseExisting))
}
actual fun realm_app_credentials_new_email_password(
username: String,
password: String
): RealmCredentialsPointer {
memScoped {
val realmStringPassword = password.toRString(this)
return CPointerWrapper(
realm_wrapper.realm_app_credentials_new_email_password(
username,
realmStringPassword
)
)
}
}
actual fun realm_app_credentials_new_api_key(key: String): RealmCredentialsPointer {
memScoped {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_api_key(key))
}
}
actual fun realm_app_credentials_new_apple(idToken: String): RealmCredentialsPointer {
memScoped {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_apple(idToken))
}
}
actual fun realm_app_credentials_new_facebook(accessToken: String): RealmCredentialsPointer {
memScoped {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_facebook(accessToken))
}
}
actual fun realm_app_credentials_new_google_id_token(idToken: String): RealmCredentialsPointer {
memScoped {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_google_id_token(idToken))
}
}
actual fun realm_app_credentials_new_google_auth_code(authCode: String): RealmCredentialsPointer {
memScoped {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_google_auth_code(authCode))
}
}
actual fun realm_app_credentials_new_jwt(jwtToken: String): RealmCredentialsPointer {
memScoped {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_jwt(jwtToken))
}
}
actual fun realm_app_credentials_new_custom_function(serializedEjsonPayload: String): RealmCredentialsPointer {
memScoped {
return CPointerWrapper(realm_wrapper.realm_app_credentials_new_function(serializedEjsonPayload))
}
}
actual fun realm_auth_credentials_get_provider(credentials: RealmCredentialsPointer): AuthProvider {
return AuthProvider.of(realm_wrapper.realm_auth_credentials_get_provider(credentials.cptr()))
}
actual fun realm_user_get_access_token(user: RealmUserPointer): String {
return realm_wrapper.realm_user_get_access_token(user.cptr()).safeKString()
}
actual fun realm_user_get_refresh_token(user: RealmUserPointer): String {
return realm_wrapper.realm_user_get_refresh_token(user.cptr()).safeKString()
}
actual fun realm_user_get_device_id(user: RealmUserPointer): String {
return realm_wrapper.realm_user_get_device_id(user.cptr()).safeKString()
}
actual fun realm_app_credentials_serialize_as_json(credentials: RealmCredentialsPointer): String {
return realm_wrapper
.realm_app_credentials_serialize_as_json(credentials.cptr())
.safeKString("credentials")
}
actual fun realm_app_email_password_provider_client_register_email(
app: RealmAppPointer,
email: String,
password: String,
callback: AppCallback
) {
memScoped {
checkedBooleanResult(
realm_wrapper.realm_app_email_password_provider_client_register_email(
app.cptr(),
email,
password.toRString(this),
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData>(userData) }
)
)
}
}
actual fun realm_app_email_password_provider_client_confirm_user(
app: RealmAppPointer,
token: String,
tokenId: String,
callback: AppCallback
) {
memScoped {
checkedBooleanResult(
realm_wrapper.realm_app_email_password_provider_client_confirm_user(
app.cptr(),
token,
tokenId,
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData>(userData) }
)
)
}
}
actual fun realm_app_email_password_provider_client_resend_confirmation_email(
app: RealmAppPointer,
email: String,
callback: AppCallback
) {
memScoped {
checkedBooleanResult(
realm_wrapper.realm_app_email_password_provider_client_resend_confirmation_email(
app.cptr(),
email,
staticCFunction { userData, error ->
handleAppCallback(userData, error) { /* No-op, returns Unit */ }
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData