Forge

docs/ / adr/0003-native-c-abi.md · branch master

ADR-0003: Native C ABI (libwitdb) for pywitdb

Статус: принято
Дата: 2026-06-06
Репозиторий: AI-Guiders/pywitdb
Реализация: AI-Guiders/WitDatabaseSources/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/WitDatabasedmitrat/WitDatabase с пакетом OutWit.Database.Native и include/witdb.h.


Contract tests

  1. Native.Tests (C#): P/Invoke round-trip против published witdb.dll.
  2. pywitdb tests/test_native.py: skip if DLL absent; else KV + txn.
  3. Cross-lang: C# Core writes fixture → Python native reads (Tier-1b).
View only · write via MCP/CIDE