{"id":13774251,"url":"https://github.com/extism/c-pdk","last_synced_at":"2025-04-08T07:32:20.843Z","repository":{"id":59332305,"uuid":"529327885","full_name":"extism/c-pdk","owner":"extism","description":"Extism Plug-in Development Kit (PDK) for C","archived":false,"fork":false,"pushed_at":"2024-10-22T17:14:25.000Z","size":2784,"stargazers_count":13,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-23T08:42:01.681Z","etag":null,"topics":["extism","wasm"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/extism.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-26T16:20:43.000Z","updated_at":"2025-03-19T05:04:40.000Z","dependencies_parsed_at":"2023-10-14T19:27:29.870Z","dependency_job_id":"3044998d-9f0d-4448-adb5-8da68a147d5c","html_url":"https://github.com/extism/c-pdk","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fc-pdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fc-pdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fc-pdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fc-pdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/extism","download_url":"https://codeload.github.com/extism/c-pdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247796295,"owners_count":20997545,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["extism","wasm"],"created_at":"2024-08-03T17:01:25.045Z","updated_at":"2025-04-08T07:32:20.518Z","avatar_url":"https://github.com/extism.png","language":"C","funding_links":[],"categories":["\u003ca name=\"extism\"\u003e\u003c/a\u003e[Extism](https://github.com/extism/extism) \u003csup\u003e[top⇈](#contents)\u003c/sup\u003e"],"sub_categories":[],"readme":"# Extism C PDK\n\nThis project contains a tool that can be used to create [Extism Plug-ins](https://extism.org/docs/concepts/plug-in) in C.\n\n## Installation\n\nThe Extism C PDK is a single header library. Just copy [extism-pdk.h](https://github.com/extism/c-pdk/blob/main/extism-pdk.h) into your project or add this repo as a Git submodule:\n\n```shell\ngit submodule add https://github.com/extism/c-pdk extism-pdk\n```\n\n## Getting Started\n\nThe goal of writing an [Extism plug-in](https://extism.org/docs/concepts/plug-in) is to compile your C code to a Wasm module with exported functions that the host application can invoke.\nThe first thing you should understand is creating an export.\n\n### Exports\n\nLet's write a simple program that exports a `greet` function which will take a name as a string and return a greeting string. Paste this into a file `plugin.c`:\n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n#include \u003cstdint.h\u003e\n\n#define Greet_Max_Input 1024\nstatic const char Greeting[] = \"Hello, \";\n\nint32_t EXTISM_EXPORTED_FUNCTION(greet) {\n  uint64_t inputLen = extism_input_length();\n  if (inputLen \u003e Greet_Max_Input) {\n    inputLen = Greet_Max_Input;\n  }\n\n  // Load input\n  static uint8_t inputData[Greet_Max_Input];\n  extism_load_input(0, inputData, inputLen);\n\n  // Allocate memory to store greeting and name\n  const uint64_t greetingLen = sizeof(Greeting) - 1;\n  const uint64_t outputLen = greetingLen + inputLen;\n  ExtismHandle handle = extism_alloc(outputLen);\n  extism_store_to_handle(handle, 0, Greeting, greetingLen);\n  extism_store_to_handle(handle, greetingLen, inputData, inputLen);\n\n  // Set output\n  extism_output_set_from_handle(handle, 0, outputLen);\n  return 0;\n}\n```\n\nThe `EXTISM_EXPORTED_FUNCTION` macro simplifies declaring an Extism function that will be exported to the host.\n\nThe `load`, `store`, and `alloc` functions are used to load from, store to and allocate Extism memory. Extism eases passing data to and from the host and the plug-in by managing memory isolated from both the host and the plug-in/Wasm module. For more details, see the [Memory](https://extism.org/docs/concepts/memory) concept page.\n\nSince we don't need any system access for this, we can compile this directly with clang:\n\n```shell\nclang -o plugin.wasm --target=wasm32-unknown-unknown -nostdlib -Wl,--no-entry plugin.c\n```\n\nThe above command may fail if ran with system clang. It's highly recommended to use clang from the [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) instead. The `wasi-sdk` also includes a libc implementation targeting WASI, necessary for plugins that need the C standard library.\n\nLet's break down the command a little:\n\n- `--target=wasm32-unknown-unknown` configures the correct Webassembly target\n- `-nostdlib` tells the compiler not to link the standard library\n- `-Wl,--no-entry` is a linker flag to tell the linker there is no `_start` function\n\nWe can now test `plugin.wasm` using the [Extism CLI](https://github.com/extism/cli)'s `call`\ncommand:\n\n```bash\nextism call plugin.wasm greet --input=\"Benjamin\"\n# =\u003e Hello, Benjamin\n```\n\n### More Exports: Error Handling\n\nWe catch any exceptions thrown and return them as errors to the host. Suppose we want to re-write our greeting module to never greet Benjamins:\n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n#include \u003cstdbool.h\u003e\n#include \u003cstdint.h\u003e\n#include \u003cstring.h\u003e\n\n#define Greet_Max_Input 1024\nstatic const char Greeting[] = \"Hello, \";\n\nstatic bool is_benjamin(const char *name) {\n  return strcasecmp(name, \"benjamin\") == 0;\n}\n\nint32_t EXTISM_EXPORTED_FUNCTION(greet) {\n  uint64_t inputLen = extism_input_length();\n  const uint64_t greetMaxString = Greet_Max_Input - 1;\n  if (inputLen \u003e greetMaxString) {\n    inputLen = greetMaxString;\n  }\n\n  // Load input\n  static uint8_t inputData[Greet_Max_Input];\n  extism_load_input(0, inputData, inputLen);\n  inputData[inputLen] = '\\0';\n\n  // Check if the input matches \"benjamin\", if it does\n  // return an error\n  if (is_benjamin((const char *)inputData)) {\n    ExtismHandle err = extism_alloc_buf_from_sz(\"ERROR\");\n    extism_error_set(err);\n    return -1;\n  }\n\n  // Allocate memory to store greeting and name\n  const uint64_t greetingLen = sizeof(Greeting) - 1;\n  const uint64_t outputLen = greetingLen + inputLen;\n  ExtismHandle handle = extism_alloc(outputLen);\n  extism_store_to_handle(handle, 0, Greeting, greetingLen);\n  extism_store_to_handle(handle, greetingLen, inputData, inputLen);\n\n  // Set output\n  extism_output_set_from_handle(handle, 0, outputLen);\n  return 0;\n}\n```\n\nThis time we will compile our example using [wasi-sdk](https://github.com/WebAssembly/wasi-sdk), since we used the `\u003cstring.h\u003e` header file. And because we are targeting \n`wasm32-wasi`, we will need to add the `-mexec-model=reactor` flag to be able to export specific functions instead of a single `_start` function:\n\n```bash\n$WASI_SDK_PATH/bin/clang -o plugin.wasm plugin.c -mexec-model=reactor\nextism call plugin.wasm greet --input=\"Benjamin\" --wasi\n# =\u003e Error: ERROR\necho $? # print last status code\n# =\u003e 1\nextism call plugin.wasm greet --input=\"Zach\" --wasi\n# =\u003e Hello, Zach\necho $?\n# =\u003e 0\n```\n\n### Configs\n\nConfigs are key-value pairs that can be passed in by the host when creating a\nplug-in. These can be useful to statically configure the plug-in with some data that exists across every function call. Here is a trivial example using `extism_config_get`:\n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n#include \u003cstdint.h\u003e\n#include \u003cstdlib.h\u003e\n\n#define Greet_Max_Input 1024\nstatic const char Greeting[] = \"Hello, \";\n\nint32_t EXTISM_EXPORTED_FUNCTION(greet) {\n  ExtismHandle key = extism_alloc_buf_from_sz(\"user\");\n  ExtismHandle value = extism_config_get(key);\n\n  if (value == 0) {\n    ExtismHandle err = extism_alloc_buf_from_sz(\"Invalid key\");\n    extism_error_set(err);\n    return -1;\n  }\n\n  const uint64_t valueLen = extism_length(value);\n\n  // Load config value\n  uint8_t *valueData = malloc(valueLen);\n  if (valueData == NULL) {\n    ExtismHandle err = extism_alloc_buf_from_sz(\"OOM\");\n    extism_error_set(err);\n    return -1;\n  }\n  extism_load_from_handle(value, 0, valueData, valueLen);\n\n  // Allocate memory to store greeting and name\n  const uint64_t greetingLen = sizeof(Greeting) - 1;\n  const uint64_t outputLen = greetingLen + valueLen;\n  ExtismHandle handle = extism_alloc(outputLen);\n  extism_store_to_handle(handle, 0, Greeting, greetingLen);\n  extism_store_to_handle(handle, greetingLen, valueData, valueLen);\n  free(valueData);\n\n  // Set output\n  extism_output_set_from_handle(handle, 0, outputLen);\n  return 0;\n}\n```\n\nTo test it, the [Extism CLI](https://github.com/extism/cli) has a `--config` option that lets you pass in `key=value` pairs:\n\n\n```bash\nextism call plugin.wasm greet --config user=Benjamin\n# =\u003e Hello, Benjamin\n```\n\n### Variables\n\nVariables are another key-value mechanism but it's a mutable data store that\nwill persist across function calls. These variables will persist as long as the\nhost has loaded and not freed the plug-in. \nYou can use `extism_var_get`, and `extism_var_set` to manipulate vars:\n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n#include \u003cstdint.h\u003e\n\nint32_t EXTISM_EXPORTED_FUNCTION(count) {\n  ExtismHandle key = extism_alloc_buf_from_sz(\"count\");\n  ExtismHandle value = extism_var_get(key);\n\n  uint64_t count = 0;\n  if (value != 0) {\n    extism_load_from_handle(value, 0, \u0026count, sizeof(uint64_t));\n  }\n  count += 1;\n\n  // Allocate a new value if it isn't saved yet\n  if (value == 0) {\n    value = extism_alloc(sizeof(uint64_t));\n  }\n\n  // Update the memory block\n  extism_store_to_handle(value, 0, \u0026count, sizeof(uint64_t));\n\n  // Set the variable\n  extism_var_set(key, value);\n\n  return 0;\n}\n```\n\n### Logging\n\nThe `extism_log*` functions can be used to emit logs:\n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n#include \u003cstdint.h\u003e\n\nint32_t EXTISM_EXPORTED_FUNCTION(log_stuff) {\n  ExtismHandle msg = extism_alloc_buf_from_sz(\"Hello!\");\n  extism_log_info(msg);\n  extism_log_debug(msg);\n  extism_log_warn(msg);\n  extism_log_error(msg);\n  extism_log_sz(\"Hello!\", ExtismLogInfo);\n  return 0;\n}\n```\n\nRunning it, you need to pass a log-level flag:\n\n```\nextism call plugin.wasm log_stuff --log-level=info\n# =\u003e 2023/10/17 14:25:00 Hello!\n# =\u003e 2023/10/17 14:25:00 Hello!\n# =\u003e 2023/10/17 14:25:00 Hello!\n# =\u003e 2023/10/17 14:25:00 Hello!\n# =\u003e 2023/10/17 14:25:00 Hello!\n# =\u003e 2023/10/17 14:25:00 Hello!\n```\n\n### HTTP\n\nHTTP calls can be made using `extism_http_request`: \n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n#include \u003cstdint.h\u003e\n#include \u003cstring.h\u003e\n\nint32_t EXTISM_EXPORTED_FUNCTION(call_http) {\n  const char *reqStr = \"{\\\n    \\\"method\\\": \\\"GET\\\",\\\n    \\\"url\\\": \\\"https://jsonplaceholder.typicode.com/todos/1\\\"\\\n  }\";\n\n  ExtismHandle req = extism_alloc_buf_from_sz(reqStr);\n  ExtismHandle res = extism_http_request(req, 0);\n\n  if (extism_http_status_code() != 200) {\n    return -1;\n  }\n\n  extism_output_set_from_handle(res, 0, extism_length(res));\n  return 0;\n}\n```\n\nTo test it you will need to pass `--allow-host jsonplaceholder.typicode.com` to the `extism` CLI, otherwise the HTTP request will\nbe rejected.\n\n## Imports (Host Functions)\n\nLike any other code module, Wasm not only let's you export functions to the outside world, you can\nimport them too. Host Functions allow a plug-in to import functions defined in the host. For example,\nif you host application is written in Python, it can pass a Python function down to your C plug-in\nwhere you can invoke it.\n\nThis topic can get fairly complicated and we have not yet fully abstracted the Wasm knowledge you need\nto do this correctly. So we recommend reading out [concept doc on Host Functions](https://extism.org/docs/concepts/host-functions) before you get started.\n\n### A Simple Example\n\nHost functions have a similar interface as exports. You just need to declare them as `extern` on the top of your header file. You only declare the interface as it is the host's responsibility to provide the implementation:\n\n```c\nextern ExtismHandle a_python_func(ExtismHandle);\n```\n\nA namespace may be set for an import using the `IMPORT` macro in `extism-pdk.h`:\n\n```c\nIMPORT(\"my_module\", \"a_python_func\") extern ExtismHandle a_python_func(ExtismHandle);\n```\n\n\u003e **Note**: The types we accept here are the same as the exports as the interface also uses the [convert crate](https://docs.rs/extism-convert/latest/extism_convert/).\n\nTo call this function, we pass an Extism handle and receive one back:\n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n#include \u003cstdint.h\u003e\n\nint32_t EXTISM_EXPORTED_FUNCTION(hello_from_python) {\n  ExtismHandle arg = extism_alloc_buf_from_sz(\"Hello!\");\n  ExtismHandle res = a_python_func(arg);\n  extism_free(arg);\n  extism_output_set_from_handle(res, 0, extism_length(res));\n  return 0;\n}\n```\n\n### Testing it out\n\nWe can't really test this from the Extism CLI as something must provide the implementation. So let's\nwrite out the Python side here. Check out the [docs for Host SDKs](https://extism.org/docs/concepts/host-sdk) to implement a host function in a language of your choice.\n\n```python\nfrom extism import host_fn, Plugin\n\n@host_fn()\ndef a_python_func(input: str) -\u003e str:\n    # just printing this out to prove we're in Python land\n    print(\"Hello from Python!\")\n\n    # let's just add \"!\" to the input string\n    # but you could imagine here we could add some\n    # applicaiton code like query or manipulate the database\n    # or our application APIs\n    return input + \"!\"\n```\n\nNow when we load the plug-in we pass the host function:\n \n```python\nmanifest = {\"wasm\": [{\"path\": \"/path/to/plugin.wasm\"}]}\nplugin = Plugin(manifest, functions=[a_python_func], wasi=True)\nresult = plugin.call('hello_from_python', b'').decode('utf-8')\nprint(result)\n```\n\n```bash\npython3 app.py\n# =\u003e Hello from Python!\n# =\u003e An argument to send to Python!\n```\n\n## Building\n\nOne source file must contain the implementation:\n\n```c\n#define EXTISM_IMPLEMENTATION\n#include \"extism-pdk.h\"\n```\n\nAll other source files using the pdk must include the header without `#define EXTISM_IMPLEMENTATION`\n\nThe C PDK does not require building with `libc`, but additional functions can be enabled when `libc` is available. `#define EXTISM_USE_LIBC` in each file before including the pdk (everywhere it is included) or, when compiling, pass it as a flag to clang: `-D EXTISM_USE_LIBC`\n\nThe low-level API that operates on `ExtismPointer` is no longer included by default, `#define EXTISM_ENABLE_LOW_LEVEL_API` in each file before including the pdk (everywhere it is included) or, when compiling, pass it as a flag to clang: `-D EXTISM_ENABLE_LOW_LEVEL_API` . Updating to use the `ExtismHandle`-based API is highly recommended.\n\nThe C PDK may be used from C++, however, the implementation must be built with a C compiler. See `cplusplus` in `tests/Makefile` for an example.\n\n## Exports (details)\n\nThe `EXTISM_EXPORTED_FUNCTION` macro is not essential to create a plugin function and export it to the host. You may instead write a function and then export it when linking. For example, the first example may have the following signature instead:\n\n```c\nint32_t greet(void)\n```\n\nThen, it can be built and linked with:\n\n```bash\n$WASI_SDK_PATH/bin/clang -o plugin.wasm --target=wasm32-unknown-unknown -nostdlib -Wl,--no-entry -Wl,--export=greet plugin.c\n```\n\nNote the `-Wl,--export=greet`\n\nExports names do not necessarily have to match the function name either. Going back to the first example again. Try:\n\n```c\nEXTISM_EXPORT_AS(\"greet\") int32_t internal_name_for_greet(void)\n```\n\nand build with:\n\n```bash\n$WASI_SDK_PATH/bin/clang -o plugin.wasm --target=wasm32-unknown-unknown -nostdlib -Wl,--no-entry plugin.c\n```\n\n## Reach Out!\n\nHave a question or just want to drop in and say hi? [Hop on the Discord](https://extism.org/discord)!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fc-pdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fextism%2Fc-pdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fc-pdk/lists"}