C SDK
0.4 NEW — пишем плагин на C, компилируем через clang из wasi-sdk. Single-header SDK + одна
.c реализация.Установка тулчейна
# wasi-sdk 25 для arm64 macOS:
curl -L -o wasi-sdk.tar.gz \
https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-arm64-macos.tar.gz
mkdir -p ~/wasi-sdk
tar -xzf wasi-sdk.tar.gz -C ~/wasi-sdk --strip-components=1
~/wasi-sdk/bin/clang --versionДля Linux/x86_64 — другой архив; см. WebAssembly/wasi-sdk releases.
Apple clang (Xcode) не подойдёт — у него нет wasi-libc и поддержки wasm32-wasi.
Минимальный плагин
wasm_modules/hello-c/manifest.json:
{
"id": "hello_c",
"name": "Hello (C)",
"version": "0.1.0",
"target": "server",
"language": "c",
"abi_version": 1
}wasm_modules/hello-c/main.c:
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "lampac.h"
__attribute__((export_name("alloc")))
void *alloc(uint32_t size) { return lampac_alloc(size); }
__attribute__((export_name("abi_version")))
uint32_t abi_version(void) { return 1; }
__attribute__((export_name("handle")))
uint64_t handle(uint32_t ptr, uint32_t length) {
lampac_invocation inv = lampac_read_invocation(ptr, length);
lampac_info("hello from C");
uint32_t qlen = 0;
const char *q = lampac_query_get(&inv, "q", &qlen);
char body[256];
int n = snprintf(body, sizeof(body),
"{\"type\":\"movie\",\"data\":[{\"name\":\"Hello, %.*s\"}]}",
(int)qlen, q ? q : "");
return lampac_write_response(body, (uint32_t)n);
}wasm_modules/hello-c/Makefile:
WASI_SDK ?= $(HOME)/wasi-sdk
CLANG := $(WASI_SDK)/bin/clang
SDK_DIR := ../../wasm_sdk/c
CFLAGS := --target=wasm32-wasi -mexec-model=reactor -O2 -nostartfiles -I$(SDK_DIR)
LDFLAGS := -Wl,--no-entry \
-Wl,--export=alloc \
-Wl,--export=handle \
-Wl,--export=abi_version
build:
$(CLANG) $(CFLAGS) $(LDFLAGS) -o plugin.wasm $(SDK_DIR)/lampac.c main.c
Сборка:
WASI_SDK=$HOME/wasi-sdk make build
# Output: plugin.wasm — ~120 КБЧто есть в SDK
wasm_sdk/c/lampac.h объявляет, lampac.c реализует:
Логирование
lampac_debug("…");
lampac_info("…");
lampac_warn("…");
lampac_error("…");
// или с явной длиной:
lampac_log(LAMPAC_INFO, ptr, len);HTTP
lampac_http_request req = {
.method = "GET", // optional, default "GET"
.url = "https://api.example.com/x",
.body = NULL, // optional
.timeout_ms = 5000,
};
lampac_http_response resp = lampac_http(req);
// resp.status, resp.body (uint8_t*, body_len), resp.err
Proxy URL
const char *url = lampac_proxy_url("https://cdn.example.com/m.m3u8", "hello_c");
// pointer valid for the rest of this handle() call
Кеш
const char *value = "hello";
lampac_cache_set("key", 3, (const uint8_t *)value, 5, 60);
uint32_t out_len = 0;
const uint8_t *cached = lampac_cache_get("key", 3, &out_len);Invocation
lampac_invocation inv = lampac_read_invocation(ptr, length);
// inv.raw, inv.path, inv.request_ip, inv.config_json
uint32_t qlen = 0;
const char *q = lampac_query_get(&inv, "id", &qlen);
const char *url = lampac_json_string(inv.config_json, inv.config_len, "apiHost", &qlen);Ответ
return lampac_write_response(json_bytes, json_len);Размер
C-плагин уровня echo собирается в ~120 КБ с -O2. Сравнимо с Rust release; больше Zig из-за wasi-libc’овых деталей (snprintf, memcpy и т.д. тащат немного кода).
Можно сократить ещё:
-Ozвместо-O2(~85 КБ)--no-standard-librariesесли откажешься от libc и напишешь своиmemcpy/snprintf(~25 КБ)
Подводные камни
-mexec-model=reactorобязателен — это говорит clang эмитить_initializeвместо_start, иначе host вызовет_startи модуль закроется после первого вызова.__attribute__((export_name("…")))для каждого экспорта — иначе clang эмитит имена с__префиксом, host их не найдёт.memmemотсутствует в wasi-libc — SDK реализует свойlampac_memmem. Если копируете код из glibc-окружения, заменяйте.- Bump arena 64 КиБ — при переполнении SDK падает в
malloc(), но это медленно и тащит лишний код. Если плагин делает много мелких аллокаций, поднимитеLAMPAC_ARENA_SIZEвlampac.c.
Полный пример
См. wasm_modules/echo_c — все host-импорты разом, тот же контракт что у других echo-плагинов.
Browser Studio
WASM Studio поддерживает C из коробки — выбирай c в dropdown’е и кодь в Monaco. Бэкенд дёргает clang из $WASI_SDK (по умолчанию $HOME/wasi-sdk).