% cat bld #!/bin/sh set -e D=$HOME/hare/hare E=$D/.cache export HARECACHE=$E bha() { harec $1.ha | qbe | as -o $1.o - } bha sqe ; bha lcs ; bha hallocimpl ; bha hvfsimpl ; bha hassert tcc \ -I. \ -DSQLITE_OS_OTHER=1 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_ZERO_MALLOC \ -DSQLITE_OMIT_LOCALTIME \ -DSQLITE_OMIT_AUTOINIT \ -DLONGDOUBLE_TYPE=double \ -c sqlite3.c tcc -c halloc.c tcc -c hvfs.c ld -T $D/rt/hare.sc sqlite3.o \ sqe.o lcs.o \ hvfs.o hvfsimpl.o \ halloc.o hallocimpl.o \ hassert.o \ $E/rt/rt-linux.a $E/ascii/ascii-any.o $E/bufio/bufio-any.o $E/bytes/bytes-any.o $E/compress/flate/compress_flate-any.o $E/compress/zlib/compress_zlib-any.o $E/crypto/crypto-any.o $E/crypto/aes/crypto_aes-any.o $E/crypto/argon2/crypto_argon2-any.o $E/crypto/blake2b/crypto_blake2b-any.o $E/crypto/chacha/crypto_chacha-any.o $E/crypto/cipher/crypto_cipher-any.o $E/crypto/hmac/crypto_hmac-any.o $E/crypto/mac/crypto_mac-any.o $E/crypto/math/crypto_math-any.o $E/crypto/poly1305/crypto_poly1305-any.o $E/crypto/salsa/crypto_salsa-any.o $E/crypto/sha1/crypto_sha1-any.o $E/crypto/sha256/crypto_sha256-any.o $E/crypto/sha512/crypto_sha512-any.o $E/crypto/curve25519/crypto_curve25519-any.o $E/dirs/dirs-any.o $E/encoding/base64/encoding_base64-any.o $E/encoding/hex/encoding_hex-any.o $E/encoding/utf8/encoding_utf8-any.o $E/endian/endian-any.o $E/errors/errors-any.o $E/fmt/fmt-any.o $E/fnmatch/fnmatch-any.o $E/format/elf/format_elf-any.o $E/format/ini/format_ini-any.o $E/format/xml/format_xml-any.o $E/fs/fs-any.o $E/getopt/getopt-any.o $E/hare/ast/hare_ast-any.o $E/hare/lex/hare_lex-any.o $E/hare/module/hare_module-any.o $E/hare/parse/hare_parse-any.o $E/hare/types/hare_types-any.o $E/hare/unit/hare_unit-any.o $E/hare/unparse/hare_unparse-any.o $E/hash/hash-any.o $E/hash/adler32/hash_adler32-any.o $E/hash/crc16/hash_crc16-any.o $E/hash/crc32/hash_crc32-any.o $E/hash/crc64/hash_crc64-any.o $E/hash/fnv/hash_fnv-any.o $E/math/math-any.o $E/math/random/math_random-any.o $E/net/dial/net_dial-any.o $E/net/dns/net_dns-any.o $E/path/path-any.o $E/shlex/shlex-any.o $E/slices/slices-any.o $E/sort/sort-any.o $E/strconv/strconv-any.o $E/strings/strings-any.o $E/strio/strio-any.o $E/types/types-any.o $E/unix/hosts/unix_hosts-any.o $E/unix/passwd/unix_passwd-any.o $E/unix/resolvconf/unix_resolvconf-any.o $E/uuid/uuid-any.o $E/rt/rt-linux.a $E/crypto/random/crypto_random-linux.o $E/io/io-linux.o $E/iobus/io_uring/iobus_io_uring-linux.o $E/linux/linux-linux.o $E/linux/signalfd/linux_signalfd-linux.o $E/linux/io_uring/linux_io_uring-linux.o $E/linux/vdso/linux_vdso-linux.o $E/net/net-linux.o $E/net/ip/net_ip-linux.o $E/net/tcp/net_tcp-linux.o $E/net/udp/net_udp-linux.o $E/net/unix/net_unix-linux.o $E/os/os-linux.o $E/os/exec/os_exec-linux.o $E/temp/temp-linux.o $E/time/time-linux.o $E/unix/unix-linux.o $E/unix/poll/unix_poll-linux.o $E/unix/tty/unix_tty-linux.o % cat sqe.ha use fmt; use rt; use strings; type sqlite3 = *void; def SQLITE_OK: int = 0; type sqlite_callback = fn(_: *void, argc: int, argv: [*]*char, colv: [*]*char) int; fn sqlite3_open(filename: *char, db: *sqlite3) int; fn sqlite3_exec(db: sqlite3, stmt: *char, f: *sqlite_callback, dat: *void, err: **char) int; fn sqlite3_free(_: *void) void; fn sqlite3_close(db: sqlite3) void; fn sqlite3_initialize() int; fn sqlite3_os_init() int; fn sqlite3_errmsg(db: sqlite3) *char; fn cstr(s: str) *char = strings::to_c(s); fn tostr(p: *char) str = strings::fromc_unsafe(p); fn callback(dat: *void, argc: int, argv: [*]*char, colv: [*]*char) int = { for (let i = 0i; i < argc; i += 1) { const k = tostr(colv[i]); const v = tostr(argv[i]); fmt::printfln("{} = {}", k, v)!; }; fmt::println()!; return 0; }; const stmts: str = "CREATE TABLE test (a num, b text);" "INSERT INTO test VALUES (1, 'ix'), (2, 'jy'), (3, 'kz'), (4, 'lw');" "SELECT * FROM test;"; export fn main() void = { sqlite3_initialize(); let db: sqlite3 = 0: uintptr; defer sqlite3_close(db); let i = sqlite3_open(cstr(":memory:"), &db); if (i != SQLITE_OK) { fmt::printfln("sqlite3_open: {}", tostr(sqlite3_errmsg(db)))!; return; }; let s: *char = 0: uintptr; let i = sqlite3_exec(db, cstr(stmts), &callback, 0: uintptr, &s); if (i != SQLITE_OK) { fmt::printfln("sqlite3_exec: {}", tostr(s))!; return; }; }; % cat lcs.ha use rt; use strings; export fn memcpy(dst: *void, src: *void, n: size) *void = { rt::memcpy(dst, src, n); return dst; }; export fn memmove(dst: *void, src: *void, n: size) *void = { rt::memmove(dst, src, n); return dst; }; export fn strlen(s: *const char) size = strings::cstrlen(s); export fn strcspn(s: *const u8, r: *const u8) size = { let i = 0z; for (*s != 0; s += 1: uintptr) { for (let t = r; *t != 0; t += 1: uintptr) { if (*s == *t) { return i; }; }; }; return i; }; export fn strrchr(s: *const i8, ch: int) nullable *const i8 = { let pos: nullable *const i8 = null; for (*s != 0; s += 1: uintptr) { if (*s: int == ch) { pos = s; }; }; return pos; }; export fn memcmp(s1: *void, s2: *void, n: size) int = { let s1 = s1: *u8; let s2 = s2: *u8; for (let i = 0z; i < n; i += 1) { if (*s1 != *s2) { return *s1: int - *s2: int; }; s1 += 1: uintptr; s2 += 1: uintptr; }; return 0; }; export fn strncmp(s1: *const char, s2: *const char, n: size) int = { let s1 = s1: *u8; let s2 = s2: *u8; for (let i = 0z; i < n; i += 1) { if (*s1 == 0 || *s2 == 0) return 0; if (*s1 != *s2) { return *s1: int - *s2: int; }; s1 += 1: uintptr; s2 += 1: uintptr; }; return 0; }; export fn memset(ptr: *u8, c: int, n: size) *u8 = { let buf = ptr: *[*]u8; const b = c: u8; for (let i = 0z; i < n; i += 1) { buf[i] = b; }; return ptr; }; export fn strcmp(p1: *u8, p2: *u8) int = { for (*p1 != 0 && *p2 != 0) { if (*p1 != *p2) { return *p1: int - *p2: int; }; p1 += 1: uintptr; p2 += 1: uintptr; }; return 0; }; export fn hare_assert(n: int) void = { assert(n != 0); }; fn hare_sqlite3_set_mem_methods() int; fn hare_sqlite3_vfs_register() int; export fn sqlite3_os_init() int = { return 0; }; @init fn hare_sqlite3_init() void = { hare_sqlite3_set_mem_methods(); hare_sqlite3_vfs_register(); }; export fn sqlite3_os_end() int = { return 0; }; % cat hallocimpl.ha use rt; fn i2sz(n: int) size = { assert(n >= 0); return n: size; }; export fn hare_mem_malloc(sz: int) nullable *void = rt::malloc(i2sz(sz)); export fn hare_mem_free(m: *void) void = rt::free_(m); export fn hare_mem_realloc(m: *void, nsz: int) nullable *void = rt::realloc(m, i2sz(nsz)); export fn hare_mem_size(m: *void) int = { const s = (m: uintptr - size(size): uintptr): *size; return *s: int; }; export fn hare_mem_roundup(n: int) int = { const r = if (n & 7 > 0) 1 else 0; return 8 * ((n / 8) + r); }; export fn hare_mem_init(p: *void) int = 0; export fn hare_mem_shutdown(p: *void) void = void; % cat hvfsimpl.ha use crypto::random; use rt; use time; use types; export fn hare_vfs_sleep(vfs: *void, ms: int) int = { const ns = (ms: i64) * time::MICROSECOND; time::sleep(ns); return 0; }; export fn hare_vfs_current_time(vfs: *void, t: *f64) int = { *t = time::unix(time::now(time::clock::MONOTONIC)): f64 / 86400.0 + 2440587.5; return 0; }; export fn hare_vfs_current_time_int64(vfs: *void, t: *i64) int = { const now = time::now(time::clock::MONOTONIC); const now_ms = now.nsec / time::MILLISECOND; *t = now_ms + 2440587 * 24 * time::HOUR + 12 * time::HOUR; return 0; }; export fn hare_vfs_randomness(vfs: *void, sz: int, buf: *void) int = { assert(sz >= 0); const abuf = types::slice { data = buf, length = sz: size, capacity = sz: size, }; const sbuf = *(&abuf: *[]u8); crypto::random::buffer(sbuf); return 0; }; % cat hassert.ha use fmt; use strings; export fn hare_assert_failed__(file: *char, line: size, func: *char, expr: *char) void = { const file = strings::fromc_unsafe(file); const func = strings::fromc_unsafe(func); const expr = strings::fromc_unsafe(expr); fmt::printfln("Assertion failed: {}\nIn {}:{} function {}\n", expr, file, line, func)!; abort(); }; % cat assert.h #ifndef ASSERT_H_DEFINED #define ASSERT_H_DEFINED #include void hare_assert_failed__(char *file, size_t line, const char *func, char *expr); #define assert(X) (void)0 #define hassert(X) \ ((void)((X) || (hare_assert_failed__(__FILE__, __LINE__, __func__, #X), 0))) #endif /* ASSERT_H_DEFINED */ % cat halloc.c #include "sqlite3.h" void *hare_mem_malloc(int); void hare_mem_free(void *); void *hare_mem_realloc(void *, int); int hare_mem_size(void *); int hare_mem_roundup(int); int hare_mem_init(void *); void hare_mem_shutdown(void *); struct sqlite3_mem_methods hare_sqlite3_mem_methods = { .xMalloc = hare_mem_malloc, .xFree = hare_mem_free, .xRealloc = hare_mem_realloc, .xSize = hare_mem_size, .xRoundup = hare_mem_roundup, .xInit = hare_mem_init, .xShutdown = hare_mem_shutdown, .pAppData = 0, }; int hare_sqlite3_set_mem_methods() { return sqlite3_config(SQLITE_CONFIG_MALLOC, &hare_sqlite3_mem_methods); } % cat hvfs.c #include "sqlite3.h" int hare_vfs_randomness(sqlite3_vfs *, int nByte, char *zOut); int hare_vfs_sleep(sqlite3_vfs *, int microseconds); int hare_vfs_current_time(sqlite3_vfs *, double *); int hare_vfs_current_time_int64(sqlite3_vfs *, sqlite3_int64 *); struct hare_file { struct sqlite3_file file; // ... }; struct sqlite3_vfs hare_sqlite3_vfs = { .iVersion = 2, .szOsFile = sizeof(struct hare_file), .mxPathname = 4096, .pNext = 0, // changed by sqlite itself .zName = "hare_vfs", .pAppData = 0, // will be set by xOpen .xOpen = 0, .xDelete = 0, .xAccess = 0, .xFullPathname = 0, .xDlOpen = 0, .xDlError = 0, .xDlSym = 0, .xDlClose = 0, .xRandomness = &hare_vfs_randomness, .xSleep = &hare_vfs_sleep, .xCurrentTime = &hare_vfs_current_time, .xGetLastError = 0, .xCurrentTimeInt64 = &hare_vfs_current_time_int64, }; int hare_sqlite3_vfs_register(void) { return sqlite3_vfs_register(&hare_sqlite3_vfs, 1); } % ./bld % ./a.out a = 1 b = ix a = 2 b = jy a = 3 b = kz a = 4 b = lw