buildWal function
Build a SQLite WAL file containing a single committed transaction that overrides the given pages.
pageSizemust equal the database's page size.pageOverridesmaps 1-based page numbers → fully-formed page bytes (each must be exactlypageSizelong). Frame ordering follows the ascending page-number order, which is sufficient for SQLite to replay correctly because the last frame is the commit frame.dbSizeAfterCommitis the post-commit database size in pages — this value is written into the commit frame'scommitfield. For a pure overlay (no extension) pass the existing page count.salt1/salt2/checkpointSeqdefault to deterministic values for reproducibility; pass your own when chaining multiple commits.- Always emits the little-endian-checksum WAL variant (magic 0x377f0682), matching what SQLite produces on x86/ARM.
Implementation
Uint8List buildWal({
required int pageSize,
required Map<int, Uint8List> pageOverrides,
required int dbSizeAfterCommit,
int salt1 = 0x12345678,
int salt2 = 0x9abcdef0,
int checkpointSeq = 0,
}) {
if (pageSize <= 0 || pageSize & (pageSize - 1) != 0) {
throw ArgumentError('pageSize must be a power of two, got $pageSize');
}
for (final e in pageOverrides.entries) {
if (e.value.length != pageSize) {
throw ArgumentError('page ${e.key} is ${e.value.length} bytes; '
'expected $pageSize');
}
}
if (pageOverrides.isEmpty) {
throw ArgumentError('pageOverrides must not be empty');
}
final pageNos = pageOverrides.keys.toList()..sort();
final totalLen =
_walHeaderSize + pageNos.length * (_walFrameHeaderSize + pageSize);
final out = Uint8List(totalLen);
final bd = ByteData.sublistView(out);
// Header.
bd.setUint32(0, _walMagicLE); // little-endian variant
bd.setUint32(4, 3007000); // file format
bd.setUint32(8, pageSize);
bd.setUint32(12, checkpointSeq);
bd.setUint32(16, salt1);
bd.setUint32(20, salt2);
// Header checksum is over header[0..23].
var (ck1, ck2) = _walChecksum(0, 0, out, 0, 24, false);
bd.setUint32(24, ck1);
bd.setUint32(28, ck2);
// Frames.
var off = _walHeaderSize;
for (var i = 0; i < pageNos.length; i++) {
final pno = pageNos[i];
final isLast = i == pageNos.length - 1;
bd.setUint32(off + 0, pno);
bd.setUint32(off + 4, isLast ? dbSizeAfterCommit : 0);
bd.setUint32(off + 8, salt1);
bd.setUint32(off + 12, salt2);
// Page bytes go in first so the checksum sees the final layout.
out.setRange(off + _walFrameHeaderSize,
off + _walFrameHeaderSize + pageSize, pageOverrides[pno]!);
// Cumulative checksum: first 8 bytes of frame header, then page.
(ck1, ck2) = _walChecksum(ck1, ck2, out, off, 8, false);
(ck1, ck2) =
_walChecksum(ck1, ck2, out, off + _walFrameHeaderSize, pageSize, false);
bd.setUint32(off + 16, ck1);
bd.setUint32(off + 20, ck2);
off += _walFrameHeaderSize + pageSize;
}
return out;
}