Forge
markdownf8555a15
1# ADR-0003: Native C ABI (`libwitdb`) for pywitdb
2
3**Статус:** принято
4**Дата:** 2026-06-06
5**Репозиторий:** [AI-Guiders/pywitdb](https://github.com/AI-Guiders/pywitdb)
6**Реализация:** [AI-Guiders/WitDatabase](https://github.com/AI-Guiders/WitDatabase) → `Sources/Core/OutWit.Database.Native`
7**Связано:** [ADR-0001](0001-binding-architecture-bridge-and-native.md), [ADR-0002](0002-bridge-provider-keys-and-reopen.md)
8
9---
10
11## Решение (одной фразой)
12
13**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).
14
15---
16
17## ABI versioning
18
19| Константа | Значение |
20|-----------|----------|
21| `WITDB_ABI_VERSION` | **1** (header + `witdb_abi_version()`) |
22
23Minor-compatible: patch-level добавления (новые error codes в конце enum) — OK. Breaking → bump ABI version + отдельная `.so`/`.dll`.
24
25---
26
27## Соглашения
28
29| Параметр | Значение |
30|----------|----------|
31| Calling convention | **cdecl** (`CallConvCdecl`) |
32| Strings | UTF-8, null-terminated |
33| Binary blobs | `(const uint8_t* data, uint32_t length)` |
34| Handles | `uintptr_t` opaque (`0` = invalid) |
35| Thread safety | Отдельные handles — не shared across threads без внешней синхронизации |
36| Allocations | `witdb_get` / `witdb_txn_get` → буфер через `witdb_buffer_free` |
37
38---
39
40## Error codes (`WitDbStatus`)
41
42| Code | Name | Смысл |
43|------|------|-------|
44| 0 | `OK` | Успех |
45| 1 | `INVALID_ARGUMENT` | NULL pointer, zero length где запрещено |
46| 2 | `NOT_FOUND` | Файл/store не найден (`create=false`) |
47| 3 | `PASSWORD_REQUIRED` | Encrypted store, password не передан |
48| 4 | `WRONG_PASSWORD` | Decrypt/auth failure |
49| 5 | `CONFIG_MISMATCH` | `ConfigurationMismatchException` |
50| 6 | `UNKNOWN_PROVIDER` | `ProviderNotFoundException` (ADR-0002 Tier-3) |
51| 7 | `TXN_NOT_SUPPORTED` | Store без transactions |
52| 8 | `TXN_ACTIVE` | Вложенная txn на том же db handle |
53| 9 | `STORE_ERROR` | Прочая ошибка движка |
54| 10 | `INVALID_HANDLE` | Битый handle |
55
56`witdb_last_error_message()` — UTF-8 detail для последней ошибки в потоке (best-effort).
57
58---
59
60## API surface (v1)
61
62```c
63// include/witdb.h
64uint32_t witdb_abi_version(void);
65
66WitDbStatus witdb_open(
67 const char* path,
68 const char* password, // NULL = none
69 uint8_t create_if_missing, // 1 = CreateOrOpen semantics
70 uintptr_t* out_db);
71
72WitDbStatus witdb_close(uintptr_t db);
73
74WitDbStatus witdb_get(uintptr_t db,
75 const uint8_t* key, uint32_t key_len,
76 uint8_t** out_value, uint32_t* out_value_len); // NULL/0 if missing
77
78WitDbStatus witdb_put(uintptr_t db,
79 const uint8_t* key, uint32_t key_len,
80 const uint8_t* value, uint32_t value_len);
81
82WitDbStatus witdb_delete(uintptr_t db,
83 const uint8_t* key, uint32_t key_len,
84 uint8_t* out_deleted);
85
86WitDbStatus witdb_txn_begin(uintptr_t db, uintptr_t* out_txn);
87WitDbStatus witdb_txn_commit(uintptr_t txn);
88WitDbStatus witdb_txn_rollback(uintptr_t txn);
89// txn get/put/delete — mirror db ops on txn handle
90
91void witdb_buffer_free(uint8_t* ptr);
92const char* witdb_last_error_message(void);
93```
94
95**Non-goals v1:** `scan`, SQL, indexes, async — только KV + single txn per db handle.
96
97---
98
99## Open semantics
100
101Делегирование `WitDatabase` static API (как bridge ADR-0002):
102
103| `create_if_missing` | password | Core call |
104|---------------------|----------|-----------|
105| 1 | NULL | `CreateOrOpen(path)` |
106| 1 | set | `CreateOrOpen(path, password)` |
107| 0 | NULL | `Open(path)` |
108| 0 | set | `Open(path, password)` |
109
110`OutWit.Database.Core.BouncyCastle` linked + `EnsureRegistered()` at first `witdb_open` (Tier-2 chacha).
111
112**Native open policy:** `WithoutFileLocking()` — cross-process `FileLock` + `Thread.Sleep` внутри UCO entry unsafe; in-process ACID сохраняется. Для multi-process writer — bridge или shared RDBMS (ADR-0002).
113
114**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).
115
116---
117
118## Build & artifacts
119
120```bash
121dotnet publish Sources/Core/OutWit.Database.Native/OutWit.Database.Native.csproj \
122 -c Release -f net10.0 -r win-x64
123```
124
125**Runtime:** NativeAOT publish targets **net10.0** (aligned with `OutWit.Database.Core` multi-TFM).
126
127Output: `witdb.dll` (+ platform equivalents: `libwitdb.so`, `libwitdb.dylib`).
128
129RID matrix (CI later): `win-x64`, `linux-x64`, `osx-arm64`, `osx-x64`.
130
131---
132
133## pywitdb integration
134
135| Module | Роль |
136|--------|------|
137| `pywitdb._native.lib` | `ctypes.CDLL`, resolve DLL (`WITDB_NATIVE_PATH` → package `native/<rid>/`) |
138| `pywitdb._native.db` | `NativeDatabase` implements `Database` |
139| `pywitdb.open(..., backend="auto")` | native if DLL + ABI match, иначе bridge |
140
141Env override: `WITDB_NATIVE_PATH` = полный путь к `witdb.dll` / `libwitdb.so`.
142
143---
144
145## Upstream PR
146
147После стабилизации contract tests в fork — PR **`AI-Guiders/WitDatabase` → `dmitrat/WitDatabase`** с пакетом `OutWit.Database.Native` и `include/witdb.h`.
148
149---
150
151## Contract tests
152
1531. **Native.Tests** (C#): P/Invoke round-trip против published `witdb.dll`.
1542. **pywitdb** `tests/test_native.py`: skip if DLL absent; else KV + txn.
1553. **Cross-lang:** C# Core writes fixture → Python native reads (Tier-1b).
156
View only · write via MCP/CIDE