17void put_u32(uint8_t *p, uint32_t v)
20 p[1] = (uint8_t)(v >> 8);
21 p[2] = (uint8_t)(v >> 16);
22 p[3] = (uint8_t)(v >> 24);
24void put_u64(uint8_t *p, uint64_t v)
26 for (
int i = 0; i < 8; i++)
27 p[i] = (uint8_t)(v >> (8 * i));
29uint32_t get_u32(
const uint8_t *p)
31 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
33uint64_t get_u64(
const uint8_t *p)
36 for (
int i = 0; i < 8; i++)
37 v |= (uint64_t)p[i] << (8 * i);
42const size_t SUPER_USED = 28;
45bool dev_read(
const WalDev &d, uint64_t off, uint8_t *buf,
size_t len)
47 return d.read && d.read(d.ctx, off, buf, len) == len;
49bool dev_write(
const WalDev &d, uint64_t off,
const uint8_t *buf,
size_t len)
51 return d.write && d.write(d.ctx, off, buf, len) == len;
55bool write_super(WalStore *s,
int ab, uint64_t gen, uint64_t head, uint64_t seq)
57 uint8_t sb[WAL_SUPER_SIZE];
58 memset(sb, 0,
sizeof(sb));
59 put_u32(sb + 0, WAL_SUPER_MAGIC);
61 put_u64(sb + 12, head);
62 put_u64(sb + 20, seq);
63 put_u32(sb + 28, wal_crc32(sb, SUPER_USED));
64 return dev_write(s->dev, (uint64_t)ab * WAL_SUPER_SIZE, sb,
sizeof(sb));
68bool read_super(
const WalStore *s,
int ab, uint64_t &gen, uint64_t &head, uint64_t &seq)
70 uint8_t sb[WAL_SUPER_SIZE];
71 if (!dev_read(s->dev, (uint64_t)ab * WAL_SUPER_SIZE, sb,
sizeof(sb)))
73 if (get_u32(sb + 0) != WAL_SUPER_MAGIC)
75 if (get_u32(sb + 28) != wal_crc32(sb, SUPER_USED))
77 gen = get_u64(sb + 4);
78 head = get_u64(sb + 12);
79 seq = get_u64(sb + 20);
81 if (head > s->data_cap)
88void wal_replay_tail(WalStore *s)
90 uint8_t hdr[WAL_RECORD_HEADER];
94 uint64_t off = s->head;
95 if (off + WAL_RECORD_HEADER > s->data_cap)
97 if (!dev_read(s->dev, s->data_off + off, hdr, WAL_RECORD_HEADER))
99 if (get_u32(hdr + 0) != WAL_MAGIC)
101 uint64_t seq = get_u64(hdr + 4);
102 uint32_t plen = get_u32(hdr + 12);
103 uint32_t crc_stored = get_u32(hdr + 16);
104 if (off + (uint64_t)WAL_RECORD_HEADER + plen > s->data_cap)
107 uint32_t crc = wal_crc32_update(wal_crc32_init(), hdr, 16);
108 uint64_t pos = s->data_off + off + WAL_RECORD_HEADER;
109 uint32_t left = plen;
113 size_t n = left <
sizeof(chunk) ? left : sizeof(chunk);
114 if (!dev_read(s->dev, pos, chunk, n))
119 crc = wal_crc32_update(crc, chunk, n);
123 if (!read_ok || wal_crc32_final(crc) != crc_stored)
125 s->head = off + (uint64_t)WAL_RECORD_HEADER + plen;
126 if (seq + 1 > s->next_seq)
127 s->next_seq = seq + 1;
132bool wal_store_format(WalStore *s,
const WalDev *dev)
134 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)))
150 if (!write_super(s, 0, 1, 0, 0))
152 return s->dev.sync ? s->dev.sync(s->dev.ctx) :
true;
155bool wal_store_mount(WalStore *s,
const WalDev *dev)
157 if (!dev || dev->size <= WAL_DATA_OFFSET)
159 memset(s, 0,
sizeof(*s));
161 s->data_off = WAL_DATA_OFFSET;
162 s->data_cap = dev->size - WAL_DATA_OFFSET;
170 bool okA = read_super(s, 0, genA, headA, seqA);
171 bool okB = read_super(s, 1, genB, headB, seqB);
176 if (okA && (!okB || genA >= genB))
180 s->committed = headA;
187 s->committed = headB;
190 s->head = s->committed;
197bool wal_store_append(WalStore *s,
const uint8_t *payload, uint32_t len)
199 uint64_t need = (uint64_t)WAL_RECORD_HEADER + len;
200 if (s->head + need > s->data_cap)
203 uint8_t hdr[WAL_RECORD_HEADER];
204 put_u32(hdr + 0, WAL_MAGIC);
205 put_u64(hdr + 4, s->next_seq);
206 put_u32(hdr + 12, len);
207 uint32_t crc = wal_crc32_update(wal_crc32_init(), hdr, 16);
208 crc = wal_crc32_update(crc, payload, len);
209 put_u32(hdr + 16, wal_crc32_final(crc));
211 uint64_t at = s->data_off + s->head;
212 if (!dev_write(s->dev, at, hdr, WAL_RECORD_HEADER))
214 if (len && !dev_write(s->dev, at + WAL_RECORD_HEADER, payload, len))
221bool wal_store_checkpoint(WalStore *s)
224 if (s->dev.sync && !s->dev.sync(s->dev.ctx))
226 int target = 1 - s->ab;
227 uint64_t gen = s->gen + 1;
228 if (!write_super(s, target, gen, s->head, s->next_seq))
230 if (s->dev.sync && !s->dev.sync(s->dev.ctx))
235 s->committed = s->head;
239size_t wal_store_scan(WalStore *s, WalStoreRecordCb cb,
void *ctx, uint8_t *scratch,
size_t scratch_len)
241 if (scratch_len < WAL_RECORD_HEADER)
245 while (off + WAL_RECORD_HEADER <= s->head)
247 if (!dev_read(s->dev, s->data_off + off, scratch, WAL_RECORD_HEADER))
249 if (get_u32(scratch) != WAL_MAGIC)
251 uint32_t plen = get_u32(scratch + 12);
252 size_t total = (size_t)WAL_RECORD_HEADER + plen;
253 if (off + total > s->head || total > scratch_len)
255 if (!dev_read(s->dev, s->data_off + off, scratch, total))
257 uint32_t crc = wal_crc32_update(wal_crc32_init(), scratch, 16);
258 crc = wal_crc32_update(crc, scratch + WAL_RECORD_HEADER, plen);
259 if (wal_crc32_final(crc) != get_u32(scratch + 16))
262 cb(get_u64(scratch + 4), off, scratch + WAL_RECORD_HEADER, plen, ctx);
269bool wal_store_pread(WalStore *s, uint64_t off, uint8_t *buf,
size_t len)
271 if (off + len > s->data_cap)
273 return dev_read(s->dev, s->data_off + off, buf, len);
Durable write-ahead store: A/B superblock + checkpoint over a block-device seam (DETWS_ENABLE_WAL).