#include "dever_ffi.h" #include #include #include #include #define MAX_BYTES (16U * 1024U * 1024U) #define MAX_TOKENS 65536U #define MAX_DEPTH 64U typedef struct { size_t start, end, after, count; char kind; } token; typedef struct { const char *data; size_t length, cursor, used; token *tokens; } document; static void whitespace(document *doc) { while (doc->cursor < doc->length && strchr(" \t\r\n", doc->data[doc->cursor]) != NULL) doc->cursor++; } static int string_end(document *doc) { if (doc->data[doc->cursor++] != '"') return 0; while (doc->cursor < doc->length) { unsigned char byte = (unsigned char)doc->data[doc->cursor++]; if (byte == '"') return 1; if (byte < 32) return 0; if (byte == '\\') { if (doc->cursor == doc->length) return 0; byte = (unsigned char)doc->data[doc->cursor++]; if (byte == 'u') { for (unsigned digit = 0; digit < 4; digit++) if (doc->cursor == doc->length || !isxdigit((unsigned char)doc->data[doc->cursor++])) return 0; } else if (strchr("\"\\/bfnrt", byte) == NULL) return 0; } } return 0; } static int number_end(document *doc) { if (doc->data[doc->cursor] == '-') doc->cursor++; if (doc->cursor == doc->length) return 0; if (doc->data[doc->cursor] == '0') doc->cursor++; else { if (doc->data[doc->cursor] < '1' || doc->data[doc->cursor] > '9') return 0; while (doc->cursor < doc->length && isdigit((unsigned char)doc->data[doc->cursor])) doc->cursor++; } if (doc->cursor < doc->length && doc->data[doc->cursor] == '.') { size_t start = ++doc->cursor; while (doc->cursor < doc->length && isdigit((unsigned char)doc->data[doc->cursor])) doc->cursor++; if (doc->cursor == start) return 0; } if (doc->cursor < doc->length && strchr("eE", doc->data[doc->cursor]) != NULL) { doc->cursor++; if (doc->cursor < doc->length && strchr("+-", doc->data[doc->cursor]) != NULL) doc->cursor++; size_t start = doc->cursor; while (doc->cursor < doc->length && isdigit((unsigned char)doc->data[doc->cursor])) doc->cursor++; if (doc->cursor == start) return 0; } return 1; } static int value(document *doc, unsigned depth) { whitespace(doc); if (depth > MAX_DEPTH || doc->used == MAX_TOKENS || doc->cursor == doc->length) return 0; token *item = &doc->tokens[doc->used++]; item->start = doc->cursor; item->kind = doc->data[doc->cursor]; if (item->kind == '"') { if (!string_end(doc)) return 0; } else if (item->kind == '{' || item->kind == '[') { char end = item->kind == '{' ? '}' : ']'; doc->cursor++; whitespace(doc); if (doc->cursor == doc->length) return 0; if (doc->data[doc->cursor] != end) { for (;;) { whitespace(doc); if (item->kind == '{') { if (doc->cursor == doc->length || doc->data[doc->cursor] != '"' || !value(doc, depth + 1)) return 0; whitespace(doc); if (doc->cursor == doc->length || doc->data[doc->cursor++] != ':') return 0; } if (!value(doc, depth + 1)) return 0; item->count++; whitespace(doc); if (doc->cursor == doc->length) return 0; if (doc->data[doc->cursor] == end) break; if (doc->data[doc->cursor++] != ',') return 0; } } doc->cursor++; } else { const char *literal = item->kind == 't' ? "true" : item->kind == 'f' ? "false" : item->kind == 'n' ? "null" : NULL; if (literal != NULL) { size_t size = strlen(literal); if (size > doc->length - doc->cursor || memcmp(doc->data + doc->cursor, literal, size) != 0) return 0; doc->cursor += size; } else if (!number_end(doc)) return 0; } item->end = doc->cursor; item->after = doc->used; return 1; } static int parse(document *doc, const char *data, size_t size) { *doc = (document){.data = data, .length = size}; if (size == 0 || size > MAX_BYTES || memchr(data, 0, size) != NULL) return 0; doc->tokens = calloc(MAX_TOKENS, sizeof(token)); if (doc->tokens == NULL) return 0; if (value(doc, 0)) { whitespace(doc); if (doc->cursor == size) return 1; } free(doc->tokens); doc->tokens = NULL; return 0; } static dever_ffi_slice slice(const document *doc, size_t index) { const token *item = &doc->tokens[index]; return (dever_ffi_slice){doc->data + item->start, item->end - item->start}; } static int text_is(const document *doc, size_t index, const char *text) { if (index >= doc->used || doc->tokens[index].kind != '"') return 0; dever_ffi_slice part = slice(doc, index); return part.size == strlen(text) + 2 && memcmp(part.data + 1, text, (size_t)part.size - 2) == 0; } static size_t field(const document *doc, const char *name) { size_t found = MAX_TOKENS; if (doc->tokens[0].kind != '{') return found; for (size_t index = 1; index < doc->tokens[0].after;) { size_t entry = doc->tokens[index].after; if (text_is(doc, index, name)) { if (found != MAX_TOKENS) return MAX_TOKENS; found = entry; } index = doc->tokens[entry].after; } return found; } static int same(const document *left, size_t left_index, const document *right, size_t right_index) { if (left_index >= left->used || right_index >= right->used) return 0; const token *left_item = &left->tokens[left_index], *right_item = &right->tokens[right_index]; if (left_item->kind != right_item->kind || left_item->count != right_item->count) return 0; if (left_item->kind == '[') { for (size_t offset = 1; offset <= left_item->count; offset++) if (!same(left, left_index + offset, right, right_index + offset)) return 0; return 1; } dever_ffi_slice left_slice = slice(left, left_index), right_slice = slice(right, right_index); return left_slice.size == right_slice.size && memcmp(left_slice.data, right_slice.data, (size_t)left_slice.size) == 0; } static int append(dever_ffi_output *output, dever_ffi_slice input) { if (input.size > output->capacity - output->size) return 0; memcpy(output->data + output->size, input.data, (size_t)input.size); output->size += input.size; return 1; } static int literal(dever_ffi_output *output, const char *text) { return append(output, (dever_ffi_slice){text, strlen(text)}); } static int send(dever_ffi_slice data) { if (data.size == 0 || data.size > MAX_BYTES) return 0; unsigned char size[4] = {(unsigned char)(data.size >> 24), (unsigned char)(data.size >> 16), (unsigned char)(data.size >> 8), (unsigned char)data.size}; return fwrite(size, 1, 4, stdout) == 4 && fwrite(data.data, 1, (size_t)data.size, stdout) == data.size && fflush(stdout) == 0; } static char *receive(size_t *length) { unsigned char size[4]; if (fread(size, 1, 4, stdin) != 4) return NULL; *length = ((uint32_t)size[0] << 24) | ((uint32_t)size[1] << 16) | ((uint32_t)size[2] << 8) | size[3]; if (*length == 0 || *length > MAX_BYTES) return NULL; char *data = malloc(*length); if (data == NULL) return NULL; if (fread(data, 1, *length, stdin) != *length) { free(data); return NULL; } return data; } static int identifier(const document *doc, size_t index, uint64_t *result) { if (index >= doc->used) return 0; dever_ffi_slice input = slice(doc, index); uint64_t number = 0; for (uint64_t offset = 0; offset < input.size; offset++) { unsigned digit = (unsigned char)input.data[offset] - '0'; if (digit > 9 || number > ((uint64_t)INT64_MAX - digit) / 10) return 0; number = number * 10 + digit; } *result = number; return 1; } static int serve(const dever_ffi_handler *handler, const document *manifest, int *opened) { size_t length = 0; char *input = receive(&length); document hello = {0}; char *buffer = malloc(MAX_BYTES), *returned = malloc(MAX_BYTES); int okay = input != NULL && buffer != NULL && returned != NULL && parse(&hello, input, length); const char *fields[] = {"port", "schema", "adapter", "capabilities", "operations"}; if (okay) okay = hello.tokens[0].count == 8 && text_is(&hello, field(&hello, "kind"), "hello") && text_is(&hello, field(&hello, "version"), "dever-component-1"); for (size_t index = 0; okay && index < 5; index++) okay = same(&hello, field(&hello, fields[index]), manifest, field(manifest, fields[index])); size_t setting = okay ? field(&hello, "setting") : MAX_TOKENS; if (okay) okay = setting < hello.used; if (okay && handler->open != NULL) okay = handler->open(handler->context, slice(&hello, setting)) == DEVER_FFI_OK; dever_ffi_output output = {buffer, MAX_BYTES, 0}; if (okay) { *opened = 1; okay = literal(&output, "{\"kind\":\"ready\",\"version\":\"dever-component-1\""); for (size_t index = 0; okay && index < 5; index++) okay = literal(&output, ",\"") && literal(&output, fields[index]) && literal(&output, "\":") && append(&output, slice(manifest, field(manifest, fields[index]))); okay = okay && literal(&output, "}") && send((dever_ffi_slice){output.data, output.size}); } free(hello.tokens); free(input); uint64_t next_id = 1; int cancelled = 0, stopped = 0; while (okay && !stopped) { input = receive(&length); document message = {0}; okay = input != NULL && parse(&message, input, length); if (!okay) { free(input); break; } size_t kind = field(&message, "kind"), id_index = field(&message, "id"); uint64_t request_id = 0; output.size = 0; if (text_is(&message, kind, "shutdown")) { okay = message.tokens[0].count == 1 && send((dever_ffi_slice){"{\"kind\":\"shutdown\"}", 19}); stopped = 1; } else if (text_is(&message, kind, "health")) { okay = message.tokens[0].count == 2 && identifier(&message, id_index, &request_id) && request_id == 0 && send((dever_ffi_slice){input, length}); } else if (text_is(&message, kind, "cancel")) { okay = message.tokens[0].count == 2 && identifier(&message, id_index, &request_id) && request_id > 0 && request_id == next_id - 1 && !cancelled; cancelled = 1; } else if (text_is(&message, kind, "call")) { size_t operation = field(&message, "operation"), payload = field(&message, "payload"); okay = message.tokens[0].count == 4 && identifier(&message, id_index, &request_id) && request_id == next_id && payload < message.used && operation < message.used && message.tokens[operation].kind == '"'; size_t operations = field(manifest, "operations"); int found = 0; for (size_t index = operations + 1; okay && index < manifest->tokens[operations].after; index = manifest->tokens[index].after) if (same(&message, operation, manifest, index)) found = 1; okay = okay && found; if (okay) { next_id++; cancelled = 0; dever_ffi_slice operation_name = slice(&message, operation); operation_name.data++; operation_name.size -= 2; dever_ffi_output result = {returned, MAX_BYTES - 256, 0}; int status = handler->invoke(handler->context, operation_name, slice(&message, payload), &result); document result_doc = {0}; okay = result.data == returned && result.capacity == MAX_BYTES - 256 && result.size <= result.capacity && parse(&result_doc, returned, (size_t)result.size); if (okay && status == DEVER_FFI_OK) { okay = literal(&output, "{\"kind\":\"result\",\"id\":") && append(&output, slice(&message, id_index)) && literal(&output, ",\"payload\":") && append(&output, (dever_ffi_slice){returned, result.size}); } else if (okay && status == DEVER_FFI_ERROR) { size_t error = field(&result_doc, "error"), error_payload = field(&result_doc, "payload"); okay = result_doc.tokens[0].count == 2 && error < result_doc.used && result_doc.tokens[error].kind == '"' && error_payload < result_doc.used; okay = okay && literal(&output, "{\"kind\":\"error\",\"id\":") && append(&output, slice(&message, id_index)) && literal(&output, ",\"error\":") && append(&output, slice(&result_doc, error)) && literal(&output, ",\"payload\":") && append(&output, slice(&result_doc, error_payload)); } else okay = 0; okay = okay && literal(&output, "}") && send((dever_ffi_slice){output.data, output.size}); free(result_doc.tokens); } } else okay = 0; free(message.tokens); free(input); } free(buffer); free(returned); return okay && stopped; } int dever_ffi_serve(const dever_ffi_handler *handler) { if (handler == NULL || handler->abi != DEVER_FFI_ABI_V1 || handler->invoke == NULL) return 1; FILE *file = fopen("contract.json", "rb"); if (file == NULL) return 1; char *data = malloc(MAX_BYTES + 1); if (data == NULL) { fclose(file); return 1; } size_t size = fread(data, 1, MAX_BYTES + 1, file); int okay = !ferror(file) && size <= MAX_BYTES; fclose(file); document manifest = {0}; okay = okay && parse(&manifest, data, size); if (okay) { size_t operations = field(&manifest, "operations"); okay = operations < manifest.used && manifest.tokens[operations].kind == '[' && manifest.tokens[operations].count > 0; const char *names[] = {"port", "schema", "adapter"}; for (size_t index = 0; okay && index < 3; index++) { size_t entry = field(&manifest, names[index]); okay = entry < manifest.used && manifest.tokens[entry].kind == '"'; } size_t capabilities = field(&manifest, "capabilities"); okay = okay && capabilities < manifest.used && manifest.tokens[capabilities].kind == '['; } int opened = 0; okay = okay && serve(handler, &manifest, &opened); if (opened && handler->close != NULL) handler->close(handler->context); free(manifest.tokens); free(data); if (!okay) fputs("native FFI Worker failed\n", stderr); return okay ? 0 : 1; }