nim-ffi/examples/timer/android/jni/my_timer_jni.c
Ivan FB d554443121
feat(examples): Android example consumes the generated Kotlin/JNI wrapper
Regenerates the wrapper (MyTimerNode.kt) and JNI shim (my_timer_jni.c) via
`nimble genbindings_kotlin`, removing the hand-written sources, and points
the instrumented test at the derived MyTimerNode class.

Validated by cross-compiling both ABIs with the NDK: arm64-v8a + x86_64 build
clean, the four Java_org_logos_mytimer_MyTimerNode_* symbols are exported, and
libmy_timer_jni.so correctly NEEDS libmy_timer.so. (The Kotlin/Gradle layer
still runs on a device/emulator.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:54:31 +02:00

129 lines
4.0 KiB
C

// Generated by nim-ffi Kotlin/JNI codegen. Do not edit by hand.
//
// JNI bridge exposing the library's native (zero-serialization) C ABI to Kotlin.
// The library calls back on its own FFI thread; each bridge function blocks on a
// condvar until the callback fires, then returns a plain Java value — so the
// Kotlin side sees a simple synchronous API. A struct return is read out of the
// typed C-POD inside the callback (valid only there).
#include "my_timer.h"
#include <jni.h>
#include <pthread.h>
#include <stdint.h>
#include <string.h>
typedef struct {
int ret, done;
char text[1024]; // string return / error text
char fields[2][256]; // string fields of a struct return
pthread_mutex_t mu;
pthread_cond_t cv;
} Resp;
static void resp_init(Resp *r) {
memset(r, 0, sizeof(*r));
pthread_mutex_init(&r->mu, NULL);
pthread_cond_init(&r->cv, NULL);
}
static void resp_destroy(Resp *r) {
pthread_mutex_destroy(&r->mu);
pthread_cond_destroy(&r->cv);
}
static void resp_wait(Resp *r) {
pthread_mutex_lock(&r->mu);
while (!r->done) pthread_cond_wait(&r->cv, &r->mu);
pthread_mutex_unlock(&r->mu);
}
static void copy_raw(char *dst, size_t cap, const char *msg, size_t len) {
size_t n = len < cap - 1 ? len : cap - 1;
if (msg && n) memcpy(dst, msg, n);
dst[n] = '\0';
}
static void ack_cb(int ret, const char *msg, size_t len, void *ud) {
Resp *r = ud;
pthread_mutex_lock(&r->mu);
r->ret = ret;
if (ret == RET_ERR) copy_raw(r->text, sizeof(r->text), msg, len);
r->done = 1;
pthread_cond_signal(&r->cv);
pthread_mutex_unlock(&r->mu);
}
static void string_cb(int ret, const char *msg, size_t len, void *ud) {
Resp *r = ud;
pthread_mutex_lock(&r->mu);
r->ret = ret;
copy_raw(r->text, sizeof(r->text), msg, len);
r->done = 1;
pthread_cond_signal(&r->cv);
pthread_mutex_unlock(&r->mu);
}
static void echo_cb(int ret, const char *msg, size_t len, void *ud) {
Resp *r = ud;
pthread_mutex_lock(&r->mu);
r->ret = ret;
if (ret == RET_OK) {
const EchoResponse *e = (const EchoResponse *)msg;
strncpy(r->fields[0], e->echoed, sizeof(r->fields[0]) - 1);
strncpy(r->fields[1], e->timerName, sizeof(r->fields[1]) - 1);
} else {
copy_raw(r->text, sizeof(r->text), msg, len);
}
r->done = 1;
pthread_cond_signal(&r->cv);
pthread_mutex_unlock(&r->mu);
}
JNIEXPORT jlong JNICALL
Java_org_logos_mytimer_MyTimerNode_nativeCreate(JNIEnv *env, jobject thiz, jstring j_name) {
const char *name = (*env)->GetStringUTFChars(env, j_name, NULL);
TimerConfig config = {.name = name};
Resp r;
resp_init(&r);
void *ctx = my_timer_create(config, ack_cb, &r);
resp_wait(&r);
(*env)->ReleaseStringUTFChars(env, j_name, name);
resp_destroy(&r);
(void)thiz;
return (jlong)(intptr_t)ctx;
}
JNIEXPORT jobjectArray JNICALL
Java_org_logos_mytimer_MyTimerNode_nativeEcho(JNIEnv *env, jobject thiz, jlong ctx, jstring j_message, jlong delayMs) {
const char *message = (*env)->GetStringUTFChars(env, j_message, NULL);
EchoRequest req = {.message = message, .delayMs = (int64_t)delayMs};
Resp r;
resp_init(&r);
if (my_timer_echo((void *)(intptr_t)ctx, echo_cb, &r, req) == RET_OK)
resp_wait(&r);
(*env)->ReleaseStringUTFChars(env, j_message, message);
jclass strClass = (*env)->FindClass(env, "java/lang/String");
jobjectArray arr = (*env)->NewObjectArray(env, 2, strClass, NULL);
(*env)->SetObjectArrayElement(env, arr, 0, (*env)->NewStringUTF(env, r.fields[0]));
(*env)->SetObjectArrayElement(env, arr, 1, (*env)->NewStringUTF(env, r.fields[1]));
resp_destroy(&r);
(void)thiz;
return arr;
}
JNIEXPORT jstring JNICALL
Java_org_logos_mytimer_MyTimerNode_nativeVersion(JNIEnv *env, jobject thiz, jlong ctx) {
Resp r;
resp_init(&r);
if (my_timer_version((void *)(intptr_t)ctx, string_cb, &r) == RET_OK)
resp_wait(&r);
jstring out = (*env)->NewStringUTF(env, r.text);
resp_destroy(&r);
(void)thiz;
return out;
}
JNIEXPORT void JNICALL
Java_org_logos_mytimer_MyTimerNode_nativeDestroy(JNIEnv *env, jobject thiz, jlong ctx) {
my_timer_destroy((void *)(intptr_t)ctx);
(void)env;
(void)thiz;
}