Storage format porting
Ahtola.Core/Storage implements the on-disk SQLite file format and the WAL.
This is where silent corruption bugs live: a wrong offset, a flipped flag, or
an off-by-one in varint decoding produces files that read fine in our engine
but are unreadable by SQLite/Turso (or vice versa). Byte-exactness with
SQLite/Turso is the contract. See docs/wal-interoperability-contract.md
for the WAL interop target.
Where things live
| Concern | Ahtola (C#) | Turso (Rust) |
|---|---|---|
| Database header | SqliteDatabaseHeader.cs |
turso-src/core/storage/sqlite3_ondisk/header.rs (and mod.rs) |
| Pager / page store | SqlitePager.cs, SqlitePageStore.cs, SqlitePagerReadCache.cs |
turso-src/core/storage/pager.rs |
| Page allocator / freelist | SqlitePageAllocator.cs, SqliteFreelist.cs |
turso-src/core/storage/ allocator/freelist |
| B-tree pages (table/index, interior/leaf) | SqliteTableInteriorPage.cs, SqliteTableLeafPage*.cs, SqliteIndexInteriorPage.cs, SqliteIndexLeaf*.cs, SqliteBtree*.cs |
turso-src/core/storage/sqlite3_ondisk/btree.rs |
| Cell pointer array / payload layout / overflow | SqliteCellPointerArray.cs, SqlitePayloadLayout.cs, SqliteOverflowChainReader.cs, SqliteOverflowPageView.cs |
turso-src/core/storage/sqlite3_ondisk/ |
| Varint | SqliteVarint.cs |
turso-src/core/storage/sqlite3_ondisk/varint.rs |
| Record codec | SqliteRecordCodec.cs |
turso-src/core/storage/sqlite3ondisk record serial |
| WAL + WAL index + locks | SqliteWal.cs, SqliteWalIndex.cs, SqliteWalByteRangeLock.cs, SqliteWalSharedMemoryLocks.cs, SqliteWalReadSnapshotCoordinator.cs, SqliteWalWriterCheckpointCoordinator.cs |
turso-src/core/storage/wal.rs, turso-src/core/wal/ |
| Page size / encryption | SqlitePageSize.cs, AhtolaPageEncryption.cs, AhtolaEncryptionFileSystem.cs |
turso-src/core/storage/ |
Porting rules
- Match offsets, sizes, and flag bits exactly. The SQLite file format
header has fixed offsets (page size at offset 16, text encoding at 56, …).
Cross-check every field against
turso-src/core/storage/sqlite3_ondisk/and the SQLite file-format docs referenced in the WAL contract. - Varint encoding is Huffman-ish big-endian, high-bit continuation. Do not
"simplify"
SqliteVarint. Mirror the upstream decode/encode edge cases (1..9 byte forms, the 9-byte form's full 64-bit payload). - WAL framing is the interop contract. Frame headers (page number, commit
marker, checksums, salt-1/salt-2) must match byte-for-byte; the checksum
algorithm and salt handling are specified in
docs/wal-interoperability-contract.md. A WAL file Ahtola writes must be readable by the Turso Rust engine and vice versa. - Locks/shmem are the one allowed P/Invoke. Byte-range locks and the
shared-memory mapping in
SqliteWalByteRangeLock.cs/PhysicalSqliteWalSharedMemoryMapping.csare the intentional OS interop inStorage(see thepure-managed-closureskill). Do not spread P/Invoke beyond this. - In-memory vs physical:
InMemoryFileSystem.cs/PhysicalFileSystem.csimplementIFileSystem. New storage code should go throughIFileSystem, not call OS APIs directly.
Verification
- Add/extend a conformance case that round-trips the affected structure through disk and re-reads it.
- For WAL changes, cross-check against the Turso Rust engine per the WAL contract — do not just "our engine reads what we wrote."