boltffi_bindgen 0.24.1

Code generation library for BoltFFI - generates Swift, Kotlin, and TypeScript bindings
Documentation
@Suppress("FunctionName")
private object Native {
    init {
        val preferredLibrary = "{{ lib_name }}_jni"
        val fallbackLibrary = "{{ lib_name }}"
{%- if desktop_loader %}
        val vmName = System.getProperty("java.vm.name").orEmpty()
        val isAndroidRuntime =
            vmName.contains("dalvik", ignoreCase = true) ||
            vmName.contains("art", ignoreCase = true)
        if (isAndroidRuntime) {
            System.loadLibrary(fallbackLibrary)
        } else {
            loadDesktopLibraries(preferredLibrary, fallbackLibrary)
        }
{%- else %}
        System.loadLibrary(fallbackLibrary)
{%- endif %}
    }

{%- if desktop_loader %}
    @Volatile
    private var bundledLibraryDirectory: java.io.File? = null

    private fun loadDesktopLibraries(preferredLibrary: String, fallbackLibrary: String) {
        var preferredFailure = tryLoadDesktopLibrary(preferredLibrary)
        if (preferredFailure == null) {
            return
        }

        if (tryLoadOptionalDesktopLibrary(fallbackLibrary)) {
            preferredFailure = tryLoadDesktopLibrary(preferredLibrary)
            if (preferredFailure == null) {
                return
            }
        }

        throw preferredFailure
    }

    private fun tryLoadDesktopLibrary(libraryName: String): UnsatisfiedLinkError? {
        try {
            if (loadBundledLibraryIfPresent(libraryName) || loadExternalLibraryIfPresent(libraryName)) {
                return null
            }
            return UnsatisfiedLinkError("Could not load native library '$libraryName'")
        } catch (error: UnsatisfiedLinkError) {
            return error
        }
    }

    private fun tryLoadOptionalDesktopLibrary(libraryName: String): Boolean {
        return try {
            loadBundledLibraryIfPresent(libraryName) || loadExternalLibraryIfPresent(libraryName)
        } catch (_: UnsatisfiedLinkError) {
            false
        }
    }

    private fun loadExternalLibraryIfPresent(libraryName: String): Boolean {
        return try {
            System.loadLibrary(libraryName)
            true
        } catch (_: UnsatisfiedLinkError) {
            false
        }
    }

    private fun loadBundledLibraryIfPresent(libraryName: String): Boolean {
        val mappedName = System.mapLibraryName(libraryName)
        for (resourcePath in bundledLibraryResourceCandidates(mappedName)) {
            Native::class.java.getResourceAsStream(resourcePath)?.use { input ->
                val extracted = extractBundledLibrary(resourcePath, input)
                System.load(extracted.absolutePath)
                return true
            }
        }
        return false
    }

    private fun extractBundledLibrary(
        resourcePath: String,
        input: java.io.InputStream,
    ): java.io.File {
        val fileName = resourcePath.substringAfterLast('/')
        val extracted = java.io.File(bundledLibraryDirectory(), fileName)
        if (!extracted.isFile) {
            java.io.FileOutputStream(extracted).use { output ->
                input.copyTo(output)
            }
            extracted.deleteOnExit()
        }
        return extracted
    }

    private fun bundledLibraryDirectory(): java.io.File {
        bundledLibraryDirectory?.let { return it }
        synchronized(this) {
            bundledLibraryDirectory?.let { return it }
            val created = java.io.File.createTempFile("boltffi-native-", "")
            if (!created.delete() || !created.mkdir()) {
                throw java.io.IOException("failed to create temp directory for bundled native extraction")
            }
            created.deleteOnExit()
            bundledLibraryDirectory = created
            return created
        }
    }

    private fun bundledLibraryResourceCandidates(mappedName: String): List<String> {
        val candidates = mutableListOf<String>()
        for (directory in desktopNativeDirectories()) {
            candidates += "/$directory/$mappedName"
            candidates += "/native/$directory/$mappedName"
        }
        candidates += "/$mappedName"
        return candidates
    }

    private fun desktopNativeDirectories(): List<String> {
        val osName = System.getProperty("os.name").orEmpty().lowercase()
        val osArch = System.getProperty("os.arch").orEmpty().lowercase()
        return when {
            (osName.contains("mac") || osName.contains("darwin")) &&
                (osArch == "aarch64" || osArch == "arm64") ->
                listOf("darwin-arm64", "darwin-aarch64")
            (osName.contains("mac") || osName.contains("darwin")) && osArch == "x86_64" ->
                listOf("darwin-x86_64", "darwin-x86-64")
            osName.contains("linux") && (osArch == "x86_64" || osArch == "amd64") ->
                listOf("linux-x86_64", "linux-x86-64")
            osName.contains("linux") && (osArch == "aarch64" || osArch == "arm64") ->
                listOf("linux-aarch64", "linux-arm64")
            osName.contains("windows") && (osArch == "x86_64" || osArch == "amd64") ->
                listOf("windows-x86_64", "windows-x86-64", "win32-x86_64")
            else -> emptyList()
        }
    }
{%- endif %}

    @JvmStatic external fun {{ prefix }}_free_string(ptr: Long)
    @JvmStatic external fun {{ prefix }}_last_error_message(): ByteArray

{%- if has_async_runtime %}
    @JvmStatic fun boltffiFutureContinuationCallback(handle: Long, pollResult: Byte) {
        val continuation = boltffiContinuationMap.tryRemove(handle)
        if (continuation == null) {
            System.err.println("BoltFFI: boltffiFutureContinuationCallback: handle $handle not found (already completed or cancelled)")
            return
        }
        BoltFFIScope.launch {
            try {
                continuation.resume(pollResult)
            } catch (e: Exception) {
                System.err.println("BoltFFI: boltffiFutureContinuationCallback: resume failed for handle $handle: $e")
            }
        }
    }
{%- endif %}
{%- for function in functions %}
{%- if function.is_async() %}
    @JvmStatic external fun {{ function.ffi_name }}({% for param in function.params %}{{ param.name }}: {{ param.jni_type }}{% if !loop.last %}, {% endif %}{% endfor %}): Long
    @JvmStatic external fun {{ function.ffi_poll() }}(future: Long, contHandle: Long)
    @JvmStatic external fun {{ function.ffi_complete() }}(future: Long): {{ function.complete_return_jni_type() }}
    @JvmStatic external fun {{ function.ffi_cancel() }}(future: Long)
    @JvmStatic external fun {{ function.ffi_free() }}(future: Long)
{%- else %}
    @JvmStatic external fun {{ function.ffi_name }}({% for param in function.params %}{{ param.name }}: {{ param.jni_type }}{% if !loop.last %}, {% endif %}{% endfor %}): {{ function.return_jni_type }}
{%- endif %}
{%- endfor %}
{%- for function in wire_functions %}
    @JvmStatic external fun {{ function.ffi_name }}({% for param in function.params %}{{ param.name }}: {{ param.jni_type }}{% if !loop.last %}, {% endif %}{% endfor %}): {{ function.return_jni_type }}
{%- endfor %}
{%- for class in classes %}
{%- for ctor in class.ctors %}
    @JvmStatic external fun {{ ctor.ffi_name }}({% for param in ctor.params %}{{ param.name }}: {{ param.jni_type }}{% if !loop.last %}, {% endif %}{% endfor %}): Long
{%- endfor %}
    @JvmStatic external fun {{ class.ffi_free }}(handle: Long)
{%- for method in class.async_methods %}
    @JvmStatic external fun {{ method.ffi_name }}({% if method.include_handle %}handle: Long{% if !method.params.is_empty() %}, {% endif %}{% endif %}{% for param in method.params %}{{ param.name }}: {{ param.jni_type }}{% if !loop.last %}, {% endif %}{% endfor %}): Long
    @JvmStatic external fun {{ method.ffi_poll }}({% if method.include_handle %}handle: Long, {% endif %}future: Long, contHandle: Long)
    @JvmStatic external fun {{ method.ffi_complete }}({% if method.include_handle %}handle: Long, {% endif %}future: Long): {{ method.return_jni_type }}
    @JvmStatic external fun {{ method.ffi_cancel }}({% if method.include_handle %}handle: Long, {% endif %}future: Long)
    @JvmStatic external fun {{ method.ffi_free }}({% if method.include_handle %}handle: Long, {% endif %}future: Long)
{%- endfor %}
{%- for method in class.sync_methods %}
    @JvmStatic external fun {{ method.ffi_name }}({% if method.include_handle %}handle: Long{% if !method.params.is_empty() %}, {% endif %}{% endif %}{% for param in method.params %}{{ param.name }}: {{ param.jni_type }}{% if !loop.last %}, {% endif %}{% endfor %}): {{ method.return_jni_type }}
{%- endfor %}
{%- for stream in class.streams %}
    @JvmStatic external fun {{ stream.subscribe }}(handle: Long): Long
    @JvmStatic external fun {{ stream.poll }}(subscription: Long, contHandle: Long)
    @JvmStatic external fun {{ stream.pop_batch }}(subscription: Long, maxCount: Long): ByteArray?
    @JvmStatic external fun {{ stream.wait }}(subscription: Long, timeout: Int): Int
    @JvmStatic external fun {{ stream.unsubscribe }}(subscription: Long)
    @JvmStatic external fun {{ stream.free }}(subscription: Long)
{%- endfor %}
{%- endfor %}
{%- for invoker in async_callback_invokers %}
    @JvmStatic external fun {{ invoker.name }}(callbackPtr: Long, callbackData: Long{% if invoker.has_result() %}, result: {{ invoker.jni_type() }}{% endif %})
    @JvmStatic external fun {{ invoker.name }}Failure(callbackPtr: Long, callbackData: Long)
{%- endfor %}
{%- for callback in callbacks %}
{%- if callback.supports_proxy_wrap %}
    @JvmStatic external fun {{ callback.proxy_release_name }}(handle: Long)
{%- for method in callback.proxy_native_methods %}
    @JvmStatic external fun {{ method.ffi_name }}(handle: Long{% if !method.params.is_empty() %}, {% endif %}{% for param in method.params %}{{ param.name }}: {{ param.jni_type }}{% if !loop.last %}, {% endif %}{% endfor %}): {{ method.return_jni_type }}
{%- endfor %}
{%- endif %}
{%- endfor %}
}