20const size_t SUPER_USED = 28;
23bool dev_read(
const WalDev &d, uint64_t off, uint8_t *buf,
size_t len)
25 return d.read && d.read(d.ctx, off, buf, len) == len;
27bool dev_write(
const WalDev &d, uint64_t off,
const uint8_t *buf,
size_t len)
29 return d.write && d.write(d.ctx, off, buf, len) == len;
33bool write_super(WalStore *s,
int ab, uint64_t gen, uint64_t head, uint64_t seq)
35 uint8_t sb[WAL_SUPER_SIZE];
36 memset(sb, 0,
sizeof(sb));
41 pc_wr32le(sb + 28, pc_wal_crc32(sb, SUPER_USED));
42 return dev_write(s->dev, (uint64_t)ab * WAL_SUPER_SIZE, sb,
sizeof(sb));
46bool read_super(
const WalStore *s,
int ab, uint64_t &gen, uint64_t &head, uint64_t &seq)
48 uint8_t sb[WAL_SUPER_SIZE];
49 if (!dev_read(s->dev, (uint64_t)ab * WAL_SUPER_SIZE, sb,
sizeof(sb)))
57 if (
pc_rd32le(sb + 28) != pc_wal_crc32(sb, SUPER_USED))
65 if (head > s->data_cap)
74void pc_wal_replay_tail(WalStore *s)
76 uint8_t hdr[WAL_RECORD_HEADER];
80 uint64_t off = s->head;
81 if (off + WAL_RECORD_HEADER > s->data_cap)
85 if (!dev_read(s->dev, s->data_off + off, hdr, WAL_RECORD_HEADER))
95 uint32_t crc_stored =
pc_rd32le(hdr + 16);
96 if (off + (uint64_t)WAL_RECORD_HEADER + plen > s->data_cap)
101 uint32_t crc = pc_wal_crc32_update(pc_wal_crc32_init(), hdr, 16);
102 uint64_t pos = s->data_off + off + WAL_RECORD_HEADER;
103 uint32_t left = plen;
107 size_t n = left <
sizeof(chunk) ? left : sizeof(chunk);
108 if (!dev_read(s->dev, pos, chunk, n))
113 crc = pc_wal_crc32_update(crc, chunk, n);
117 if (!read_ok || pc_wal_crc32_final(crc) != crc_stored)
121 s->head = off + (uint64_t)WAL_RECORD_HEADER + plen;
122 if (seq + 1 > s->next_seq)
124 s->next_seq = seq + 1;
130bool pc_wal_store_format(WalStore *s,
const WalDev *dev)
132 if (!dev || dev->size <= WAL_DATA_OFFSET)
136 memset(s, 0,
sizeof(*s));
138 s->data_off = WAL_DATA_OFFSET;
139 s->data_cap = dev->size - WAL_DATA_OFFSET;
146 uint8_t zero[WAL_SUPER_SIZE];
147 memset(zero, 0,
sizeof(zero));
148 if (!dev_write(s->dev, (uint64_t)1 * WAL_SUPER_SIZE, zero,
sizeof(zero)))
152 if (!write_super(s, 0, 1, 0, 0))
156 return s->dev.sync ? s->dev.sync(s->dev.ctx) :
true;
159bool pc_wal_store_mount(WalStore *s,
const WalDev *dev)
161 if (!dev || dev->size <= WAL_DATA_OFFSET)
165 memset(s, 0,
sizeof(*s));
167 s->data_off = WAL_DATA_OFFSET;
168 s->data_cap = dev->size - WAL_DATA_OFFSET;
176 bool okA = read_super(s, 0, genA, headA, seqA);
177 bool okB = read_super(s, 1, genB, headB, seqB);
184 if (okA && (!okB || genA >= genB))
188 s->committed = headA;
195 s->committed = headB;
198 s->head = s->committed;
201 pc_wal_replay_tail(s);
205bool pc_wal_store_append(WalStore *s,
const uint8_t *payload, uint32_t len)
207 uint64_t need = (uint64_t)WAL_RECORD_HEADER + len;
208 if (s->head + need > s->data_cap)
213 uint8_t hdr[WAL_RECORD_HEADER];
217 uint32_t crc = pc_wal_crc32_update(pc_wal_crc32_init(), hdr, 16);
218 crc = pc_wal_crc32_update(crc, payload, len);
219 pc_wr32le(hdr + 16, pc_wal_crc32_final(crc));
221 uint64_t at = s->data_off + s->head;
222 if (!dev_write(s->dev, at, hdr, WAL_RECORD_HEADER))
226 if (len && !dev_write(s->dev, at + WAL_RECORD_HEADER, payload, len))
235bool pc_wal_store_checkpoint(WalStore *s)
238 if (s->dev.sync && !s->dev.sync(s->dev.ctx))
242 int target = 1 - s->ab;
243 uint64_t gen = s->gen + 1;
244 if (!write_super(s, target, gen, s->head, s->next_seq))
248 if (s->dev.sync && !s->dev.sync(s->dev.ctx))
255 s->committed = s->head;
259size_t pc_wal_store_scan(WalStore *s, WalStoreRecordCb cb,
void *ctx, uint8_t *scratch,
size_t scratch_len)
261 if (scratch_len < WAL_RECORD_HEADER)
267 while (off + WAL_RECORD_HEADER <= s->head)
269 if (!dev_read(s->dev, s->data_off + off, scratch, WAL_RECORD_HEADER))
278 size_t total = (size_t)WAL_RECORD_HEADER + plen;
279 if (off + total > s->head || total > scratch_len)
283 if (!dev_read(s->dev, s->data_off + off, scratch, total))
287 uint32_t crc = pc_wal_crc32_update(pc_wal_crc32_init(), scratch, 16);
288 crc = pc_wal_crc32_update(crc, scratch + WAL_RECORD_HEADER, plen);
289 if (pc_wal_crc32_final(crc) !=
pc_rd32le(scratch + 16))
295 cb(
pc_rd64le(scratch + 4), off, scratch + WAL_RECORD_HEADER, plen, ctx);
303bool pc_wal_store_pread(WalStore *s, uint64_t off, uint8_t *buf,
size_t len)
305 if (off + len > s->data_cap)
309 return dev_read(s->dev, s->data_off + off, buf, len);
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
size_t pc_wr64le(uint8_t *p, uint64_t v)
Write v little-endian at p.
uint64_t pc_rd64le(const uint8_t *p)
Read a little-endian u64 at p.
uint32_t pc_rd32le(const uint8_t *p)
Read a little-endian u32 at p.
size_t pc_wr32le(uint8_t *p, uint32_t v)
Write v little-endian at p.