DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
sqlite_format.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file sqlite_format.cpp
6 * @brief SQLite3 on-disk file-format parsers (see sqlite_format.h).
7 */
8
10
11#if DETWS_ENABLE_SQLITE
12
13#include <string.h>
14
15namespace
16{
17uint16_t be16(const uint8_t *p)
18{
19 return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
20}
21uint32_t be32(const uint8_t *p)
22{
23 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
24}
25// The 16-byte file magic, including the trailing NUL.
26const char SQLITE_MAGIC[16] = {'S', 'Q', 'L', 'i', 't', 'e', ' ', 'f', 'o', 'r', 'm', 'a', 't', ' ', '3', '\0'};
27
28bool is_pow2(uint32_t v)
29{
30 return v && (v & (v - 1)) == 0;
31}
32} // namespace
33
34size_t sqlite_varint_decode(const uint8_t *buf, size_t len, uint64_t *out)
35{
36 uint64_t v = 0;
37 for (size_t i = 0; i < 8; i++)
38 {
39 if (i >= len)
40 return 0; // incomplete varint
41 v = (v << 7) | (uint8_t)(buf[i] & 0x7f);
42 if ((buf[i] & 0x80) == 0)
43 {
44 *out = v;
45 return i + 1;
46 }
47 }
48 if (len < 9)
49 return 0;
50 v = (v << 8) | buf[8]; // 9th byte carries all 8 bits
51 *out = v;
52 return 9;
53}
54
55uint64_t sqlite_serial_type_size(uint64_t t)
56{
57 switch (t)
58 {
59 case 0:
60 return 0; // NULL
61 case 1:
62 return 1;
63 case 2:
64 return 2;
65 case 3:
66 return 3;
67 case 4:
68 return 4;
69 case 5:
70 return 6;
71 case 6:
72 return 8;
73 case 7:
74 return 8; // IEEE 754 float64
75 case 8:
76 return 0; // integer 0
77 case 9:
78 return 0; // integer 1
79 case 10:
80 case 11:
81 return 0; // reserved for internal use
82 default:
83 // >=12 even -> BLOB of (t-12)/2; >=13 odd -> TEXT of (t-13)/2. Floor division covers both.
84 return t >= 12 ? (t - 12) / 2 : 0;
85 }
86}
87
88bool sqlite_parse_db_header(const uint8_t *buf, size_t len, SqliteDbHeader *out)
89{
90 if (len < 100 || memcmp(buf, SQLITE_MAGIC, 16) != 0)
91 return false;
92
93 uint16_t raw_ps = be16(buf + 16);
94 uint32_t page_size = (raw_ps == 1) ? 65536u : raw_ps;
95 // A valid page size is a power of two in [512, 65536].
96 if (page_size < 512 || page_size > 65536 || !is_pow2(page_size))
97 return false;
98
99 out->page_size = page_size;
100 out->write_version = buf[18];
101 out->read_version = buf[19];
102 out->reserved_per_page = buf[20];
103 out->file_change_counter = be32(buf + 24);
104 out->page_count = be32(buf + 28);
105 out->freelist_first = be32(buf + 32);
106 out->freelist_count = be32(buf + 36);
107 out->schema_cookie = be32(buf + 40);
108 out->schema_format = be32(buf + 44);
109 out->text_encoding = be32(buf + 56);
110 out->user_version = be32(buf + 60);
111 out->application_id = be32(buf + 68);
112 out->sqlite_version = be32(buf + 96);
113 return true;
114}
115
116bool sqlite_parse_btree_header(const uint8_t *page, size_t page_len, size_t offset, SqliteBtreeHeader *out)
117{
118 if (offset + 8 > page_len)
119 return false;
120 const uint8_t *p = page + offset;
121 uint8_t type = p[0];
122 bool interior =
123 (type == SqliteBtree::SQLITE_BTREE_INTERIOR_INDEX || type == SqliteBtree::SQLITE_BTREE_INTERIOR_TABLE);
124 bool leaf = (type == SqliteBtree::SQLITE_BTREE_LEAF_INDEX || type == SqliteBtree::SQLITE_BTREE_LEAF_TABLE);
125 if (!interior && !leaf)
126 return false;
127 uint8_t hdr = interior ? 12 : 8;
128 if (offset + hdr > page_len)
129 return false;
130
131 out->type = type;
132 out->first_freeblock = be16(p + 1);
133 out->cell_count = be16(p + 3);
134 uint16_t ccs = be16(p + 5);
135 out->cell_content_start = (ccs == 0) ? 65536u : ccs;
136 out->frag_free_bytes = p[7];
137 out->right_most_page = interior ? be32(p + 8) : 0;
138 out->header_size = hdr;
139 return true;
140}
141
142uint32_t sqlite_cell_pointer(const uint8_t *page, size_t page_len, const SqliteBtreeHeader *bh, size_t page_offset,
143 uint16_t i)
144{
145 if (i >= bh->cell_count)
146 return 0;
147 size_t at = page_offset + bh->header_size + (size_t)i * 2;
148 if (at + 2 > page_len)
149 return 0;
150 return be16(page + at); // cell pointers are offsets from the start of the page
151}
152
153bool sqlite_parse_table_leaf_cell(const uint8_t *page, size_t page_len, uint32_t page_size, uint8_t reserved,
154 uint32_t cell_off, SqliteTableLeafCell *out)
155{
156 if (cell_off >= page_len)
157 return false;
158 uint64_t payload_len = 0;
159 uint64_t rowid = 0;
160 size_t n1 = sqlite_varint_decode(page + cell_off, page_len - cell_off, &payload_len);
161 if (n1 == 0)
162 return false;
163 size_t n2 = sqlite_varint_decode(page + cell_off + n1, page_len - cell_off - n1, &rowid);
164 if (n2 == 0)
165 return false;
166
167 // SQLite overflow threshold for a table b-tree leaf (fileformat2.html section 1.6).
168 uint32_t usable = page_size - reserved;
169 uint32_t max_local = usable - 35;
170 uint32_t min_local = ((usable - 12) * 32) / 255 - 23;
171 uint32_t local;
172 if (payload_len <= max_local)
173 {
174 local = (uint32_t)payload_len;
175 }
176 else
177 {
178 uint32_t k = min_local + (uint32_t)((payload_len - min_local) % (usable - 4));
179 local = (k <= max_local) ? k : min_local;
180 }
181
182 out->rowid = rowid;
183 out->payload_len = (uint32_t)payload_len;
184 out->local_off = cell_off + (uint32_t)(n1 + n2);
185 out->local_len = local;
186 out->has_overflow = local < payload_len;
187 if (out->local_off + local > page_len)
188 return false; // the claimed local payload does not fit the page
189 return true;
190}
191
192bool sqlite_read_payload(SqlitePageReader read, void *ctx, uint32_t page_size, uint8_t reserved,
193 const uint8_t *leaf_page, const SqliteTableLeafCell *cell, uint8_t *out, uint32_t out_cap,
194 uint8_t *work_page)
195{
196 if (cell->payload_len > out_cap)
197 return false; // fail closed rather than overrun the caller buffer
198 if ((size_t)cell->local_off + cell->local_len > page_size)
199 return false;
200
201 memcpy(out, leaf_page + cell->local_off, cell->local_len);
202 uint32_t got = cell->local_len;
203 if (!cell->has_overflow)
204 return got == cell->payload_len; // wholly in-page: nothing to follow
205
206 // The 4-byte first-overflow-page number sits immediately after the local prefix.
207 size_t ptr_off = (size_t)cell->local_off + cell->local_len;
208 if (ptr_off + 4 > page_size)
209 return false;
210 uint32_t next = be32(leaf_page + ptr_off);
211
212 uint32_t usable = page_size - reserved;
213 uint32_t content = usable - 4; // per overflow page: 4-byte next pointer + content
214 if (content == 0)
215 return false;
216 // A chain cannot legitimately be longer than this; the bound turns a corrupt/looping
217 // next-pointer into a clean failure instead of an unbounded read.
218 uint32_t max_pages = cell->payload_len / content + 2;
219
220 for (uint32_t pages = 0; next != 0 && got < cell->payload_len; pages++)
221 {
222 // Unreachable belt-and-suspenders: `got` grows by `content` every iteration, so it reaches
223 // payload_len in fewer than max_pages steps - kept only as a guard against future logic changes.
224 if (pages >= max_pages)
225 return false; // GCOVR_EXCL_LINE broken / looping chain (provably not hit; see above)
226 if (!read(ctx, next, work_page, page_size))
227 return false;
228 uint32_t nnext = be32(work_page);
229 uint32_t chunk = cell->payload_len - got;
230 if (chunk > content)
231 chunk = content;
232 // Also unreachable: got < payload_len <= out_cap and chunk <= payload_len - got, so got + chunk
233 // never exceeds out_cap - the entry check at the top already bounded the write.
234 if (got + chunk > out_cap)
235 return false; // GCOVR_EXCL_LINE
236 memcpy(out + got, work_page + 4, chunk);
237 got += chunk;
238 next = nnext;
239 }
240 return got == cell->payload_len;
241}
242
243bool sqlite_record_begin(SqliteRecordCursor *c, const uint8_t *rec, uint32_t rec_len)
244{
245 uint64_t hdr_len = 0;
246 size_t n = sqlite_varint_decode(rec, rec_len, &hdr_len);
247 if (n == 0 || hdr_len > rec_len || hdr_len < n)
248 return false;
249 c->rec = rec;
250 c->rec_len = rec_len;
251 c->hdr_pos = (uint32_t)n;
252 c->hdr_end = (uint32_t)hdr_len;
253 c->val_pos = (uint32_t)hdr_len;
254 return true;
255}
256
257bool sqlite_record_next(SqliteRecordCursor *c, uint64_t *serial_type, const uint8_t **val, uint32_t *val_len)
258{
259 if (c->hdr_pos >= c->hdr_end)
260 return false;
261 uint64_t st = 0;
262 size_t n = sqlite_varint_decode(c->rec + c->hdr_pos, c->hdr_end - c->hdr_pos, &st);
263 if (n == 0)
264 return false;
265 uint32_t sz = (uint32_t)sqlite_serial_type_size(st);
266 if (c->val_pos + sz > c->rec_len)
267 return false; // value runs past the record (a truncated / overflowing row)
268 *serial_type = st;
269 *val = c->rec + c->val_pos;
270 *val_len = sz;
271 c->hdr_pos += (uint32_t)n;
272 c->val_pos += sz;
273 return true;
274}
275
276int64_t sqlite_column_int(uint64_t serial_type, const uint8_t *val, uint32_t val_len)
277{
278 if (serial_type == 8)
279 return 0;
280 if (serial_type == 9)
281 return 1;
282 if (serial_type < 1 || serial_type > 6)
283 return 0;
284 size_t nbytes = (size_t)sqlite_serial_type_size(serial_type);
285 if (val_len < nbytes)
286 return 0;
287 uint64_t u = 0;
288 for (size_t i = 0; i < nbytes; i++)
289 u = (u << 8) | val[i];
290 size_t bits = nbytes * 8;
291 if (bits < 64 && (u & (1ULL << (bits - 1))))
292 u |= ~((1ULL << bits) - 1); // sign-extend from the stored width
293 return (int64_t)u;
294}
295
296double sqlite_column_float(const uint8_t *val, uint32_t val_len)
297{
298 if (val_len < 8)
299 return 0.0;
300 uint64_t u = 0;
301 for (int i = 0; i < 8; i++)
302 u = (u << 8) | val[i];
303 double d = 0.0;
304 memcpy(&d, &u, 8); // u holds the big-endian-read IEEE-754 bit pattern as a native u64
305 return d;
306}
307
308namespace
309{
310// The page byte offset of an interior page's b-tree header (100 only for page 1, the schema root).
311size_t page_hdr_off(uint32_t pgno)
312{
313 return pgno == 1 ? 100 : 0;
314}
315
316// Child page pointer to descend for child index `i` of an interior table page: the left-child of cell i,
317// or the right-most pointer when i == cell_count. 0 on a bad pointer.
318uint32_t interior_child(const uint8_t *page, size_t page_len, const SqliteBtreeHeader *h, size_t off, uint16_t i)
319{
320 if (i >= h->cell_count)
321 return h->right_most_page;
322 uint32_t cp = sqlite_cell_pointer(page, page_len, h, off, i);
323 if (cp == 0 || (size_t)cp + 4 > page_len)
324 return 0;
325 return be32(page + cp); // an interior-table cell starts with the u32 left-child page number
326}
327
328// Descend leftmost from `pgno`, pushing interior frames, until a leaf table page is loaded into c->leaf.
329bool cursor_descend(SqliteTableCursor *c, uint32_t pgno)
330{
331 for (;;)
332 {
333 if (!c->read(c->ctx, pgno, c->work, c->page_size))
334 return false;
335 size_t off = page_hdr_off(pgno);
336 SqliteBtreeHeader h;
337 if (!sqlite_parse_btree_header(c->work, c->page_size, off, &h))
338 return false;
339 if (h.type == SqliteBtree::SQLITE_BTREE_LEAF_TABLE)
340 {
341 memcpy(c->leaf, c->work, c->page_size);
342 c->leaf_hdr = h;
343 c->leaf_off = (uint32_t)off;
344 c->leaf_pgno = pgno;
345 c->leaf_count = h.cell_count;
346 c->leaf_cell = 0;
347 return true;
348 }
349 if (h.type != SqliteBtree::SQLITE_BTREE_INTERIOR_TABLE)
350 return false; // an index b-tree or garbage - not a table scan
351 if (c->depth >= SQLITE_BTREE_MAX_DEPTH)
352 return false;
353 uint32_t child = interior_child(c->work, c->page_size, &h, off, 0);
354 if (child == 0)
355 return false;
356 c->stack_pg[c->depth] = pgno;
357 c->stack_idx[c->depth] = 1; // child 0 taken; next is 1
358 c->depth++;
359 pgno = child;
360 }
361}
362} // namespace
363
364bool sqlite_table_cursor_begin(SqliteTableCursor *c, SqlitePageReader read, void *ctx, uint32_t page_size,
365 uint8_t reserved, uint32_t rootpage, uint8_t *leaf_buf, uint8_t *work_buf)
366{
367 c->read = read;
368 c->ctx = ctx;
369 c->page_size = page_size;
370 c->reserved = reserved;
371 c->leaf = leaf_buf;
372 c->work = work_buf;
373 c->ovf_buf = nullptr;
374 c->ovf_cap = 0;
375 c->depth = 0;
376 c->leaf_count = 0;
377 c->leaf_cell = 0;
378 return cursor_descend(c, rootpage);
379}
380
381void sqlite_table_cursor_set_overflow_buf(SqliteTableCursor *c, uint8_t *buf, uint32_t cap)
382{
383 c->ovf_buf = buf;
384 c->ovf_cap = cap;
385}
386
387// Begin reading a row record from one leaf cell: straight from the leaf page, or (for an overflowing
388// cell) reassembled into the caller's overflow buffer first. Extracted to keep the cursor loop flat.
389static bool sqlite_cursor_begin_row(SqliteTableCursor *c, const SqliteTableLeafCell *cell, SqliteRecordCursor *row)
390{
391 if (!cell->has_overflow || !c->ovf_buf)
392 return sqlite_record_begin(row, c->leaf + cell->local_off, cell->local_len);
393 // c->work is free here (we are at a leaf, not descending), so it serves as the scratch page.
394 if (!sqlite_read_payload(c->read, c->ctx, c->page_size, c->reserved, c->leaf, cell, c->ovf_buf, c->ovf_cap,
395 c->work))
396 return false;
397 return sqlite_record_begin(row, c->ovf_buf, cell->payload_len);
398}
399
400bool sqlite_table_cursor_next(SqliteTableCursor *c, uint64_t *rowid, SqliteRecordCursor *row)
401{
402 for (;;)
403 {
404 if (c->leaf_cell < c->leaf_count)
405 {
406 uint32_t cp = sqlite_cell_pointer(c->leaf, c->page_size, &c->leaf_hdr, c->leaf_off, c->leaf_cell);
407 c->leaf_cell++;
408 if (cp == 0)
409 continue;
410 SqliteTableLeafCell cell;
411 if (!sqlite_parse_table_leaf_cell(c->leaf, c->page_size, c->page_size, c->reserved, cp, &cell))
412 continue;
413 if (!sqlite_cursor_begin_row(c, &cell, row))
414 continue;
415 *rowid = cell.rowid;
416 return true;
417 }
418 // Current leaf is exhausted: advance the descent stack to the next subtree.
419 if (c->depth == 0)
420 return false; // whole table consumed
421 int top = c->depth - 1;
422 if (!c->read(c->ctx, c->stack_pg[top], c->work, c->page_size))
423 return false;
424 size_t off = page_hdr_off(c->stack_pg[top]);
425 SqliteBtreeHeader h;
426 if (!sqlite_parse_btree_header(c->work, c->page_size, off, &h))
427 return false;
428 if (c->stack_idx[top] <= h.cell_count)
429 {
430 uint32_t child = interior_child(c->work, c->page_size, &h, off, c->stack_idx[top]);
431 c->stack_idx[top]++;
432 if (child == 0 || !cursor_descend(c, child))
433 return false;
434 }
435 else
436 {
437 c->depth--; // this interior frame's children are all visited; pop to its parent
438 }
439 }
440}
441
442// ---------------------------------------------------------------------------
443// Writer (bounded): build a fresh single-table database image.
444// ---------------------------------------------------------------------------
445
446namespace
447{
448void wr_be16(uint8_t *p, uint16_t v)
449{
450 p[0] = (uint8_t)(v >> 8);
451 p[1] = (uint8_t)v;
452}
453void wr_be32(uint8_t *p, uint32_t v)
454{
455 p[0] = (uint8_t)(v >> 24);
456 p[1] = (uint8_t)(v >> 16);
457 p[2] = (uint8_t)(v >> 8);
458 p[3] = (uint8_t)v;
459}
460
461// Number of bytes a varint of value v occupies (mirror of sqlite_varint_encode's length).
462size_t varint_len(uint64_t v)
463{
464 if (v <= 0x7fULL)
465 return 1;
466 if (v <= 0x3fffULL)
467 return 2;
468 if (v <= 0x1fffffULL)
469 return 3;
470 if (v <= 0xfffffffULL)
471 return 4;
472 if (v <= 0x7ffffffffULL)
473 return 5;
474 if (v <= 0x3ffffffffffULL)
475 return 6;
476 if (v <= 0x1ffffffffffffULL)
477 return 7;
478 if (v <= 0xffffffffffffffULL)
479 return 8;
480 return 9;
481}
482
483// The serial type + value byte length for a column value. Integers get the minimal encoding.
484void value_serial(const SqliteValue *v, uint64_t *st, uint32_t *vlen)
485{
486 switch (v->type)
487 {
488 case SqliteColType::SQLITE_COL_NULL:
489 *st = 0;
490 *vlen = 0;
491 return;
492 case SqliteColType::SQLITE_COL_FLOAT:
493 *st = 7;
494 *vlen = 8;
495 return;
496 case SqliteColType::SQLITE_COL_TEXT:
497 *st = 13 + (uint64_t)2 * v->len;
498 *vlen = v->len;
499 return;
500 case SqliteColType::SQLITE_COL_BLOB:
501 *st = 12 + (uint64_t)2 * v->len;
502 *vlen = v->len;
503 return;
504 case SqliteColType::SQLITE_COL_INT:
505 default:
506 break;
507 }
508 int64_t x = v->i;
509 if (x == 0)
510 {
511 *st = 8;
512 *vlen = 0;
513 }
514 else if (x == 1)
515 {
516 *st = 9;
517 *vlen = 0;
518 }
519 else if (x >= -128 && x <= 127)
520 {
521 *st = 1;
522 *vlen = 1;
523 }
524 else if (x >= -32768 && x <= 32767)
525 {
526 *st = 2;
527 *vlen = 2;
528 }
529 else if (x >= -8388608 && x <= 8388607)
530 {
531 *st = 3;
532 *vlen = 3;
533 }
534 else if (x >= -2147483648LL && x <= 2147483647LL)
535 {
536 *st = 4;
537 *vlen = 4;
538 }
539 else if (x >= -140737488355328LL && x <= 140737488355327LL)
540 {
541 *st = 5;
542 *vlen = 6;
543 }
544 else
545 {
546 *st = 6;
547 *vlen = 8;
548 }
549}
550
551// Write a column's value bytes (big-endian for ints/floats) into out; returns the count.
552uint32_t write_value(const SqliteValue *v, uint64_t st, uint32_t vlen, uint8_t *out)
553{
554 if (v->type == SqliteColType::SQLITE_COL_TEXT || v->type == SqliteColType::SQLITE_COL_BLOB)
555 {
556 if (vlen)
557 memcpy(out, v->data, vlen);
558 return vlen;
559 }
560 if (v->type == SqliteColType::SQLITE_COL_FLOAT)
561 {
562 uint64_t u = 0;
563 memcpy(&u, &v->f, 8); // native bit pattern -> emit big-endian
564 for (int i = 7; i >= 0; i--)
565 *out++ = (uint8_t)(u >> (i * 8));
566 return 8;
567 }
568 if (st >= 1 && st <= 6) // signed big-endian integer of `vlen` bytes
569 {
570 uint64_t u = (uint64_t)v->i;
571 for (int i = (int)vlen - 1; i >= 0; i--)
572 out[(int)vlen - 1 - i] = (uint8_t)(u >> (i * 8));
573 return vlen;
574 }
575 return 0; // NULL / 0 / 1 constants carry no bytes
576}
577
578// Total encoded length of a record (header + values), or 0 on invalid input.
579uint32_t record_len(const SqliteValue *cols, uint32_t n)
580{
581 uint32_t st_len = 0;
582 uint32_t val_len = 0;
583 for (uint32_t c = 0; c < n; c++)
584 {
585 uint64_t st = 0;
586 uint32_t vl = 0;
587 value_serial(&cols[c], &st, &vl);
588 st_len += (uint32_t)varint_len(st);
589 val_len += vl;
590 }
591 // header_size counts its own length varint, which can grow the total - resolve the fixed point.
592 size_t hs_varlen = 1;
593 for (;;)
594 {
595 uint64_t hs = hs_varlen + st_len;
596 if (varint_len(hs) == hs_varlen)
597 break;
598 hs_varlen = varint_len(hs);
599 }
600 return (uint32_t)hs_varlen + st_len + val_len;
601}
602
603// Write a leaf-table page (b-tree header at hdr_off, then the rows as cells) into `page`. hdr_off is 100 for
604// page 1, else 0. Fails closed if a row would overflow the page or the cells + pointer array do not fit.
605bool write_leaf_page(uint8_t *page, uint32_t page_size, uint32_t hdr_off, const SqliteRow *rows, uint32_t nrows)
606{
607 const uint32_t usable = page_size; // reserved = 0 for images we build
608 const uint32_t max_local = usable - 35;
609
610 // Measure each cell up front so we can lay them out and fail closed before writing.
611 uint32_t total = 0;
612 for (uint32_t r = 0; r < nrows; r++)
613 {
614 uint32_t rl = record_len(rows[r].cols, rows[r].ncols);
615 if (rl == 0 && rows[r].ncols != 0)
616 return false;
617 if (rl > max_local)
618 return false; // would need an overflow page - out of scope for the bounded writer
619 total += (uint32_t)varint_len(rl) + (uint32_t)varint_len(rows[r].rowid) + rl;
620 }
621 uint32_t header_end = hdr_off + 8 + 2 * nrows; // 8-byte leaf header + 2-byte cell pointer each
622 if ((uint64_t)header_end + total > page_size)
623 return false; // does not fit one leaf page
624
625 uint32_t content_start = page_size - total;
626
627 // b-tree leaf-table header.
628 uint8_t *h = page + hdr_off;
629 h[0] = (uint8_t)SqliteBtree::SQLITE_BTREE_LEAF_TABLE;
630 wr_be16(h + 1, 0); // first freeblock
631 wr_be16(h + 3, (uint16_t)nrows); // cell count
632 wr_be16(h + 5, (uint16_t)(content_start == 65536 ? 0 : content_start));
633 h[7] = 0; // fragmented free bytes
634
635 // Pack cells from the top of the content area downward; pointer array (rowid order) follows the header.
636 uint32_t off = page_size;
637 for (uint32_t r = 0; r < nrows; r++)
638 {
639 uint32_t rl = record_len(rows[r].cols, rows[r].ncols);
640 uint32_t cell_len = (uint32_t)varint_len(rl) + (uint32_t)varint_len(rows[r].rowid) + rl;
641 off -= cell_len;
642 uint8_t *cp = page + off;
643 size_t k = sqlite_varint_encode(rl, cp, cell_len);
644 k += sqlite_varint_encode(rows[r].rowid, cp + k, cell_len - k);
645 uint32_t w = sqlite_encode_record(rows[r].cols, rows[r].ncols, cp + k, cell_len - (uint32_t)k);
646 if (w != rl)
647 return false; // internal invariant: measured length must match written length
648 wr_be16(page + hdr_off + 8 + 2 * r, (uint16_t)off); // cell pointer for row r
649 }
650 return true;
651}
652} // namespace
653
654size_t sqlite_varint_encode(uint64_t v, uint8_t *out, size_t cap)
655{
656 size_t n = varint_len(v);
657 if (n > cap)
658 return 0;
659 if (n == 9)
660 {
661 // Bytes 0-7 carry bits 63..8 (7 bits each, high bit = continuation); byte 8 carries the low 8 bits.
662 for (int i = 0; i < 8; i++)
663 out[i] = (uint8_t)(0x80 | (uint8_t)((v >> (57 - 7 * i)) & 0x7f));
664 out[8] = (uint8_t)v;
665 return 9;
666 }
667 // The low 7 bits of each of the n bytes; high bit set on all but the last.
668 for (size_t i = 0; i < n; i++)
669 {
670 uint8_t bits = (uint8_t)((v >> (7 * (n - 1 - i))) & 0x7f);
671 out[i] = (i + 1 < n) ? (uint8_t)(bits | 0x80) : bits;
672 }
673 return n;
674}
675
676uint32_t sqlite_encode_record(const SqliteValue *cols, uint32_t n, uint8_t *out, uint32_t out_cap)
677{
678 uint32_t total = record_len(cols, n);
679 if (total == 0 && n != 0)
680 return 0;
681 if (total > out_cap)
682 return 0;
683
684 // Compute the header size (length varint + serial-type varints), same fixed point as record_len.
685 uint32_t st_len = 0;
686 for (uint32_t c = 0; c < n; c++)
687 {
688 uint64_t st = 0;
689 uint32_t vl = 0;
690 value_serial(&cols[c], &st, &vl);
691 st_len += (uint32_t)varint_len(st);
692 }
693 size_t hs_varlen = 1;
694 for (;;)
695 {
696 uint64_t hs = hs_varlen + st_len;
697 if (varint_len(hs) == hs_varlen)
698 break;
699 hs_varlen = varint_len(hs);
700 }
701 uint32_t header_size = (uint32_t)hs_varlen + st_len;
702
703 uint32_t pos = (uint32_t)sqlite_varint_encode(header_size, out, out_cap);
704 // Serial-type varints (the header body).
705 for (uint32_t c = 0; c < n; c++)
706 {
707 uint64_t st = 0;
708 uint32_t vl = 0;
709 value_serial(&cols[c], &st, &vl);
710 pos += (uint32_t)sqlite_varint_encode(st, out + pos, out_cap - pos);
711 }
712 // Value bytes, in column order.
713 for (uint32_t c = 0; c < n; c++)
714 {
715 uint64_t st = 0;
716 uint32_t vl = 0;
717 value_serial(&cols[c], &st, &vl);
718 pos += write_value(&cols[c], st, vl, out + pos);
719 }
720 return pos;
721}
722
723uint32_t sqlite_build_table_db(uint32_t page_size, const char *table_name, const char *create_sql,
724 const SqliteRow *rows, uint32_t nrows, uint8_t *out, uint32_t out_cap)
725{
726 if (page_size < 512 || page_size > 65536 || !is_pow2(page_size))
727 return 0;
728 if ((uint64_t)page_size * 2 > out_cap || !table_name || !create_sql)
729 return 0;
730
731 memset(out, 0, (size_t)page_size * 2);
732
733 // --- Page 1: the 100-byte database header ---
734 memcpy(out, SQLITE_MAGIC, 16);
735 wr_be16(out + 16, (uint16_t)(page_size == 65536 ? 1 : page_size));
736 out[18] = 1; // write version (legacy)
737 out[19] = 1; // read version (legacy)
738 out[20] = 0; // reserved bytes per page
739 out[21] = 64; // max embedded payload fraction
740 out[22] = 32; // min embedded payload fraction
741 out[23] = 32; // leaf payload fraction
742 wr_be32(out + 24, 1); // file change counter
743 wr_be32(out + 28, 2); // page count
744 wr_be32(out + 40, 1); // schema cookie
745 wr_be32(out + 44, 4); // schema format number
746 wr_be32(out + 56, 1); // text encoding = UTF-8
747 wr_be32(out + 92, 1); // version-valid-for (== file change counter)
748 wr_be32(out + 96, 3046001); // SQLITE_VERSION_NUMBER that wrote the file
749
750 // --- Page 1: the sqlite_schema row for our table (type,name,tbl_name,rootpage,sql) ---
751 uint32_t name_len = (uint32_t)strnlen(table_name, out_cap);
752 uint32_t sql_len = (uint32_t)strnlen(create_sql, out_cap);
753 SqliteValue master[5];
754 master[0] = {SqliteColType::SQLITE_COL_TEXT, 0, 0, (const uint8_t *)"table", 5};
755 master[1] = {SqliteColType::SQLITE_COL_TEXT, 0, 0, (const uint8_t *)table_name, name_len};
756 master[2] = {SqliteColType::SQLITE_COL_TEXT, 0, 0, (const uint8_t *)table_name, name_len};
757 master[3] = {SqliteColType::SQLITE_COL_INT, 2, 0, nullptr, 0}; // rootpage = 2
758 master[4] = {SqliteColType::SQLITE_COL_TEXT, 0, 0, (const uint8_t *)create_sql, sql_len};
759 SqliteRow master_row = {1, master, 5};
760 if (!write_leaf_page(out, page_size, 100, &master_row, 1))
761 return 0;
762
763 // --- Page 2: the table's leaf b-tree with the caller's rows ---
764 if (!write_leaf_page(out + page_size, page_size, 0, rows, nrows))
765 return 0;
766
767 return page_size * 2;
768}
769
770#endif // DETWS_ENABLE_SQLITE
uint32_t be32(const uint8_t *p)
Definition ghash.h:39
Reader for the SQLite3 on-disk file format (DETWS_ENABLE_SQLITE).