buildWal function

Uint8List buildWal({
  1. required int pageSize,
  2. required Map<int, Uint8List> pageOverrides,
  3. required int dbSizeAfterCommit,
  4. int salt1 = 0x12345678,
  5. int salt2 = 0x9abcdef0,
  6. int checkpointSeq = 0,
})

Build a SQLite WAL file containing a single committed transaction that overrides the given pages.

  • pageSize must equal the database's page size.
  • pageOverrides maps 1-based page numbers → fully-formed page bytes (each must be exactly pageSize long). Frame ordering follows the ascending page-number order, which is sufficient for SQLite to replay correctly because the last frame is the commit frame.
  • dbSizeAfterCommit is the post-commit database size in pages — this value is written into the commit frame's commit field. For a pure overlay (no extension) pass the existing page count.
  • salt1/salt2/checkpointSeq default 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;
}