--- /dev/null
+#pragma once
+
+#include "int.qh"
+
+// Databases (hash tables)
+const int DB_BUCKETS = 8192;
+void db_save(int db, string filename)
+{
+ int fh = fopen(filename, FILE_WRITE);
+ if (fh < 0)
+ {
+ LOG_WARNINGF("^1Can't write DB to %s\n", filename);
+ return;
+ }
+ fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
+ for (int i = 0, n = buf_getsize(db); i < n; ++i)
+ fputs(fh, strcat(bufstr_get(db, i), "\n"));
+ fclose(fh);
+}
+
+typedef int HashMap;
+
+int db_create()
+{
+ return buf_create();
+}
+#define HM_NEW(this) (this = db_create())
+
+void db_put(int db, string key, string value);
+
+int db_load(string filename)
+{
+ int db = buf_create();
+ if (db < 0) return -1;
+ int fh = fopen(filename, FILE_READ);
+ if (fh < 0) return db;
+ string l = fgets(fh);
+ if (stoi(l) == DB_BUCKETS)
+ {
+ for (int i = 0; (l = fgets(fh)); ++i)
+ {
+ if (l != "") bufstr_set(db, i, l);
+ }
+ }
+ else
+ {
+ // different count of buckets, or a dump?
+ // need to reorganize the database then (SLOW)
+ //
+ // note: we also parse the first line (l) in case the DB file is
+ // missing the bucket count
+ do
+ {
+ int n = tokenizebyseparator(l, "\\");
+ for (int j = 2; j < n; j += 2)
+ db_put(db, argv(j - 1), uri_unescape(argv(j)));
+ }
+ while ((l = fgets(fh)));
+ }
+ fclose(fh);
+ return db;
+}
+
+void db_dump(int db, string filename)
+{
+ int fh = fopen(filename, FILE_WRITE);
+ if (fh < 0) LOG_FATALF("Can't dump DB to %s\n");
+ fputs(fh, "0\n");
+ for (int i = 0, n = buf_getsize(db); i < n; ++i)
+ {
+ int m = tokenizebyseparator(bufstr_get(db, i), "\\");
+ for (int j = 2; j < m; j += 2)
+ fputs(fh, strcat("\\", argv(j - 1), "\\", argv(j), "\n"));
+ }
+ fclose(fh);
+}
+
+void db_close(int db)
+{
+ buf_del(db);
+}
+#define HM_DELETE(this) db_close(this)
+
+string db_get(int db, string key)
+{
+ int h = crc16(false, key) % DB_BUCKETS;
+ return uri_unescape(infoget(bufstr_get(db, h), key));
+}
+#define HM_gets(this, k) db_get(this, k)
+
+#define db_remove(db, key) db_put(db, key, "")
+
+void db_put(int db, string key, string value)
+{
+ int h = crc16(false, key) % DB_BUCKETS;
+ bufstr_set(db, h, infoadd(bufstr_get(db, h), key, uri_escape(value)));
+}
+#define HM_sets(this, key, val) db_put(this, key, val)
+
+void db_test()
+{
+ LOG_INFO("LOAD...\n");
+ int db = db_load("foo.db");
+ LOG_INFO("LOADED. FILL...\n");
+ for (int i = 0; i < DB_BUCKETS; ++i)
+ db_put(db, ftos(random()), "X");
+ LOG_INFO("FILLED. SAVE...\n");
+ db_save(db, "foo.db");
+ LOG_INFO("SAVED. CLOSE...\n");
+ db_close(db);
+ LOG_INFO("CLOSED.\n");
+}