mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-07-24 08:43:16 +00:00
169 lines
5.7 KiB
C
169 lines
5.7 KiB
C
#ifndef NIM_FFI_LIB_ECHO_C_ABI_H_INCLUDED
|
|
#define NIM_FFI_LIB_ECHO_C_ABI_H_INCLUDED
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define NIMFFI_RET_OK 0
|
|
#define NIMFFI_RET_ERR 1
|
|
#define NIMFFI_RET_MISSING_CALLBACK 2
|
|
/* Non-terminal: the request is still running. Fires every ~5s with `msg`
|
|
carrying the elapsed milliseconds as decimal text; always followed by a
|
|
terminal RET_OK/RET_ERR. Ignore it unless you want progress. */
|
|
#define NIMFFI_RET_STALE_WARN 3
|
|
|
|
/* `abi = c` wire structs — the C ABI. Strings are borrowed, NUL-terminated
|
|
`const char*` valid only for the duration of the call they cross. */
|
|
typedef struct {
|
|
const char* prefix;
|
|
} EchoConfig;
|
|
typedef struct {
|
|
const char* text;
|
|
} ShoutRequest;
|
|
typedef struct {
|
|
const char* shouted;
|
|
const char* prefix;
|
|
} ShoutResponse;
|
|
typedef struct {
|
|
EchoConfig config;
|
|
} EchoCreateCtorReq;
|
|
typedef struct {
|
|
ShoutRequest req;
|
|
} EchoShoutReq;
|
|
|
|
typedef void (*EchoShoutReplyFn)(int err_code, const ShoutResponse* reply, const char* err_msg, void* user_data);
|
|
typedef void (*EchoVersionReplyFn)(int err_code, const char* reply, const char* err_msg, void* user_data);
|
|
|
|
typedef void (*EchoCreateRawFn)(int err_code, const char* ctx_addr, const char* err_msg, void* user_data);
|
|
/* Raw reply of a scalar-fast-path export: `msg`/`len` are bytes (a string
|
|
return's UTF-8, or the 8-byte native-endian scalar image), not
|
|
NUL-terminated and valid only for the duration of the call. */
|
|
typedef void (*EchoScalarRawFn)(int caller_ret, char* msg, size_t len, void* user_data);
|
|
#ifndef NIMFFI_ABI_DUP_CSTR_N
|
|
#define NIMFFI_ABI_DUP_CSTR_N
|
|
/* NUL-terminated copy of a length-delimited byte run; NULL if it can't. */
|
|
static inline char* nimffi_abi_dup_cstr_n(const char* s, size_t n) {
|
|
if (n == SIZE_MAX) return NULL;
|
|
char* p = (char*)malloc(n + 1);
|
|
if (p) {
|
|
if (n > 0) memcpy(p, s, n);
|
|
p[n] = '\0';
|
|
}
|
|
return p;
|
|
}
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
void* echo_create(const EchoCreateCtorReq* req, EchoCreateRawFn on_created, void* user_data);
|
|
int echo_shout(void* ctx, EchoShoutReplyFn on_reply, void* user_data, const EchoShoutReq* req);
|
|
int echo_version(void* ctx, EchoScalarRawFn callback, void* user_data);
|
|
int echo_destroy(void* ctx);
|
|
|
|
#ifdef __cplusplus
|
|
} /* extern "C" */
|
|
#endif
|
|
|
|
/* High-level context wrapper */
|
|
typedef struct {
|
|
void* ptr;
|
|
} EchoCtx;
|
|
|
|
typedef void (*EchoCreateFn)(int err_code, EchoCtx* ctx, const char* err_msg, void* user_data);
|
|
typedef struct { EchoCreateFn fn; void* user_data; } EchoCreateBox;
|
|
static void echo_create_trampoline(int ret, const char* ctx_addr, const char* err_msg, void* ud) {
|
|
EchoCreateBox* box = (EchoCreateBox*)ud;
|
|
if (!box) return;
|
|
if (ret == NIMFFI_RET_STALE_WARN) return;
|
|
if (!box->fn) { free(box); return; }
|
|
if (ret != 0) {
|
|
box->fn(ret, NULL, err_msg ? err_msg : "FFI create failed", box->user_data);
|
|
free(box);
|
|
return;
|
|
}
|
|
char* endp = NULL;
|
|
unsigned long long a = ctx_addr ? strtoull(ctx_addr, &endp, 10) : 0;
|
|
bool ok = ctx_addr && *ctx_addr && endp && *endp == '\0';
|
|
if (!ok) {
|
|
box->fn(-1, NULL, "FFI create returned non-numeric address", box->user_data);
|
|
free(box);
|
|
return;
|
|
}
|
|
EchoCtx* ctx = (EchoCtx*)calloc(1, sizeof(EchoCtx));
|
|
if (!ctx) {
|
|
box->fn(-1, NULL, "out of memory", box->user_data);
|
|
free(box);
|
|
return;
|
|
}
|
|
ctx->ptr = (void*)(uintptr_t)a;
|
|
box->fn(NIMFFI_RET_OK, ctx, NULL, box->user_data);
|
|
free(box);
|
|
}
|
|
|
|
static inline int echo_ctx_create(const EchoConfig* config, EchoCreateFn on_created, void* user_data) {
|
|
EchoCreateCtorReq ffi_req;
|
|
memset(&ffi_req, 0, sizeof(ffi_req));
|
|
ffi_req.config = *config;
|
|
EchoCreateBox* box = (EchoCreateBox*)malloc(sizeof(EchoCreateBox));
|
|
if (!box) {
|
|
if (on_created) on_created(-1, NULL, "out of memory", user_data);
|
|
return -1;
|
|
}
|
|
box->fn = on_created;
|
|
box->user_data = user_data;
|
|
(void)echo_create(&ffi_req, echo_create_trampoline, box);
|
|
return 0;
|
|
}
|
|
|
|
static inline void echo_ctx_destroy(EchoCtx* ctx) {
|
|
if (!ctx) return;
|
|
if (ctx->ptr) { echo_destroy(ctx->ptr); ctx->ptr = NULL; }
|
|
free(ctx);
|
|
}
|
|
|
|
static inline int echo_ctx_shout(const EchoCtx* ctx, const ShoutRequest* req, EchoShoutReplyFn on_reply, void* user_data) {
|
|
EchoShoutReq ffi_req;
|
|
memset(&ffi_req, 0, sizeof(ffi_req));
|
|
ffi_req.req = *req;
|
|
return echo_shout(ctx->ptr, on_reply, user_data, &ffi_req);
|
|
}
|
|
|
|
typedef struct { EchoVersionReplyFn fn; void* user_data; } EchoVersionScalarBox;
|
|
static void echo_version_scalar_reply(int caller_ret, char* msg, size_t len, void* ud) {
|
|
EchoVersionScalarBox* box = (EchoVersionScalarBox*)ud;
|
|
if (!box) return;
|
|
EchoVersionReplyFn fn = box->fn;
|
|
void* user_data = box->user_data;
|
|
free(box);
|
|
if (!fn) return;
|
|
if (caller_ret != NIMFFI_RET_OK) {
|
|
char* em = nimffi_abi_dup_cstr_n(msg ? msg : "", msg ? len : 0);
|
|
fn(caller_ret, "", em ? em : "FFI call failed", user_data);
|
|
free(em);
|
|
return;
|
|
}
|
|
char* reply = nimffi_abi_dup_cstr_n(msg ? msg : "", msg ? len : 0);
|
|
if (!reply) {
|
|
fn(NIMFFI_RET_ERR, "", "out of memory", user_data);
|
|
return;
|
|
}
|
|
fn(NIMFFI_RET_OK, reply, "", user_data);
|
|
free(reply);
|
|
}
|
|
|
|
static inline int echo_ctx_version(const EchoCtx* ctx, EchoVersionReplyFn on_reply, void* user_data) {
|
|
EchoVersionScalarBox* box = (EchoVersionScalarBox*)malloc(sizeof(EchoVersionScalarBox));
|
|
if (!box) {
|
|
if (on_reply) on_reply(-1, "", "out of memory", user_data);
|
|
return -1;
|
|
}
|
|
box->fn = on_reply;
|
|
box->user_data = user_data;
|
|
return echo_version(ctx->ptr, echo_version_scalar_reply, box);
|
|
}
|
|
|
|
#endif /* NIM_FFI_LIB_ECHO_C_ABI_H_INCLUDED */
|