ADR-0003: Native C ABI (libwitdb) for pywitdb
Статус: принято
Дата: 2026-06-06
Репозиторий: AI-Guiders/pywitdb
Реализация: AI-Guiders/WitDatabase → Sources/Core/OutWit.Database.Native
Связано: ADR-0001, ADR-0002
Решение (одной фразой)
NativeAOT shared library witdb с cdecl C exports (witdb_*), opaque handles, caller-freed buffers; pywitdb грузит через ctypes; reopen/provider keys — те же Tier-1/2, что ADR-0002 (логика в Core, не в Python).
ABI versioning
| Константа | Значение |
|---|---|
WITDB_ABI_VERSION |
1 (header + witdb_abi_version()) |
Minor-compatible: patch-level добавления (новые error codes в конце enum) — OK. Breaking → bump ABI version + отдельная .so/.dll.
Соглашения
| Параметр | Значение |
|---|---|
| Calling convention | cdecl (CallConvCdecl) |
| Strings | UTF-8, null-terminated |
| Binary blobs | (const uint8_t* data, uint32_t length) |
| Handles | uintptr_t opaque (0 = invalid) |
| Thread safety | Отдельные handles — не shared across threads без внешней синхронизации |
| Allocations | witdb_get / witdb_txn_get → буфер через witdb_buffer_free |
Error codes (WitDbStatus)
| Code | Name | Смысл |
|---|---|---|
| 0 | OK |
Успех |
| 1 | INVALID_ARGUMENT |
NULL pointer, zero length где запрещено |
| 2 | NOT_FOUND |
Файл/store не найден (create=false) |
| 3 | PASSWORD_REQUIRED |
Encrypted store, password не передан |
| 4 | WRONG_PASSWORD |
Decrypt/auth failure |
| 5 | CONFIG_MISMATCH |
ConfigurationMismatchException |
| 6 | UNKNOWN_PROVIDER |
ProviderNotFoundException (ADR-0002 Tier-3) |
| 7 | TXN_NOT_SUPPORTED |
Store без transactions |
| 8 | TXN_ACTIVE |
Вложенная txn на том же db handle |
| 9 | STORE_ERROR |
Прочая ошибка движка |
| 10 | INVALID_HANDLE |
Битый handle |
witdb_last_error_message() — UTF-8 detail для последней ошибки в потоке (best-effort).
API surface (v1)
// include/witdb.h
uint32_t witdb_abi_version(void);
WitDbStatus witdb_open(
const char* path,
const char* password, // NULL = none
uint8_t create_if_missing, // 1 = CreateOrOpen semantics
uintptr_t* out_db);
WitDbStatus witdb_close(uintptr_t db);
WitDbStatus witdb_get(uintptr_t db,
const uint8_t* key, uint32_t key_len,
uint8_t** out_value, uint32_t* out_value_len); // NULL/0 if missing
WitDbStatus witdb_put(uintptr_t db,
const uint8_t* key, uint32_t key_len,
const uint8_t* value, uint32_t value_len);
WitDbStatus witdb_delete(uintptr_t db,
const uint8_t* key, uint32_t key_len,
uint8_t* out_deleted);
WitDbStatus witdb_txn_begin(uintptr_t db, uintptr_t* out_txn);
WitDbStatus witdb_txn_commit(uintptr_t txn);
WitDbStatus witdb_txn_rollback(uintptr_t txn);
// txn get/put/delete — mirror db ops on txn handle
void witdb_buffer_free(uint8_t* ptr);
const char* witdb_last_error_message(void);
Non-goals v1: scan, SQL, indexes, async — только KV + single txn per db handle.
Open semantics
Делегирование WitDatabase static API (как bridge ADR-0002):
create_if_missing |
password | Core call |
|---|---|---|
| 1 | NULL | CreateOrOpen(path) |
| 1 | set | CreateOrOpen(path, password) |
| 0 | NULL | Open(path) |
| 0 | set | Open(path, password) |
OutWit.Database.Core.BouncyCastle linked + EnsureRegistered() at first witdb_open (Tier-2 chacha).
Native open policy: WithoutFileLocking() — cross-process FileLock + Thread.Sleep внутри UCO entry unsafe; in-process ACID сохраняется. Для multi-process writer — bridge или shared RDBMS (ADR-0002).
NativeAOT hosting (bring-up): managed-логика (WitDatabaseBuilder.Build, providers, JSON index metadata) не вызывается на UCO-потоке. ModuleInitializer поднимает dedicated CLR worker; witdb_open маршалит работу туда (WitDbClrThread). UTF-8 строки — WitDbUtf8 (без Marshal.PtrToStringUTF8 в UCO). Trim roots: OutWit.Database.Native/trimming.xml (сузить после contract tests).
Build & artifacts
dotnet publish Sources/Core/OutWit.Database.Native/OutWit.Database.Native.csproj \
-c Release -f net10.0 -r win-x64
Runtime: NativeAOT publish targets net10.0 (aligned with OutWit.Database.Core multi-TFM).
Output: witdb.dll (+ platform equivalents: libwitdb.so, libwitdb.dylib).
RID matrix (CI later): win-x64, linux-x64, osx-arm64, osx-x64.
pywitdb integration
| Module | Роль |
|---|---|
pywitdb._native.lib |
ctypes.CDLL, resolve DLL (WITDB_NATIVE_PATH → package native/<rid>/) |
pywitdb._native.db |
NativeDatabase implements Database |
pywitdb.open(..., backend="auto") |
native if DLL + ABI match, иначе bridge |
Env override: WITDB_NATIVE_PATH = полный путь к witdb.dll / libwitdb.so.
Upstream PR
После стабилизации contract tests в fork — PR AI-Guiders/WitDatabase → dmitrat/WitDatabase с пакетом OutWit.Database.Native и include/witdb.h.
Contract tests
- Native.Tests (C#): P/Invoke round-trip против published
witdb.dll. - pywitdb
tests/test_native.py: skip if DLL absent; else KV + txn. - Cross-lang: C# Core writes fixture → Python native reads (Tier-1b).