Improve db2 api.

This commit is contained in:
lzwdgc 2024-02-16 16:55:07 +03:00
parent 0bd3e6e64b
commit b586253e4c
2 changed files with 243 additions and 63 deletions

View file

@ -19,6 +19,8 @@
#include "common.h" #include "common.h"
#include "mmap.h" #include "mmap.h"
#include <primitives/templates2/type_name.h>
struct db2 { struct db2 {
using char20 = char[0x20]; using char20 = char[0x20];
@ -82,43 +84,204 @@ struct db2 {
}; };
path fn; path fn;
primitives::templates2::mmap_file<uint8_t> fdat, find, ftab;
tab *tab_{};
ind *ind_{};
dat *dat_{};
int codepage{1251}; int codepage{1251};
db2() = default; template <typename T>
db2(const path &fn) : fn{fn} { struct file {
open(fn, primitives::templates2::mmap_file<uint8_t>::ro{}); path fn;
} primitives::templates2::mmap_file<uint8_t> f;
T *data;
void open(const path &fn, auto mode) { file(auto &&base) : fn{path{base} += "."s} {
close(); fn += type_name<T>();
this->fn = fn; f.open(fn, primitives::templates2::mmap_file<uint8_t>::rw{});
fdat.open(path{fn} += ".dat", mode); data = (T *)f.p;
find.open(path{fn} += ".ind", mode); }
ftab.open(path{fn} += ".tab", mode); };
// actual db
struct files {
struct table {
files &f;
db2::tab::table t;
dat_ = (dat *)find.p; auto find_field(auto &&name, auto ftype) {
ind_ = (ind *)find.p; auto fields = f.tab_.data->fields();
tab_ = (tab *)ftab.p; auto field_db_cp = f.db.utf8_to_dbstr(name);
} auto itf = std::ranges::find_if(fields, [&](auto &v) {
void open(auto mode) { return v.table_id == t.id && field_db_cp == v.name;
open(fn, mode); });
} if (itf == fields.end()) {
void close() { if (field_db_cp.size() + 1 > sizeof(tab::field::name)) {
fdat.close(); throw std::runtime_error{"too long field name: "s + field_db_cp};
find.close(); }
ftab.close(); auto p = f.tab_.f.alloc_raw(f.tab_.f.sz + sizeof(tab::field));
} auto &newfield = *(tab::field *)p;
memset(&newfield, 0, sizeof(newfield));
newfield.table_id = t.id;
newfield.id = ++f.tab_.data->n_fields;
strcpy(newfield.name, field_db_cp.data());
newfield.type = (decltype(newfield.type))ftype;
return newfield;
}
if (itf->type != ftype) {
throw std::runtime_error{"field type mismatch: "s + field_db_cp};
}
return *itf;
}
auto find_value(auto &&name) {
auto values = f.ind_.data->values();
auto value_db_cp = f.db.utf8_to_dbstr(name);
auto itv = std::ranges::find_if(values, [&](auto &v) {
return v.table_id == t.id && value_db_cp == v.name;
});
if (itv == values.end()) {
db2::ind::value i{};
i.table_id = t.id;
if (value_db_cp.size() + 1 > sizeof(i.name)) {
throw std::runtime_error{"too long value name: "s + value_db_cp};
}
memcpy(i.name, value_db_cp.data(), value_db_cp.size());
i.offset = f.dat_.f.sz;
++f.ind_.data->n_values;
auto p = f.ind_.f.alloc_raw(f.ind_.f.sz + sizeof(i));
memcpy(p, &i, sizeof(i));
return value{*this,*(db2::ind::value *)p};
}
return value{*this,*itv};
}
};
struct value {
table t;
db2::ind::value &v;
void add_value(std::string_view table, std::string_view value, auto && ... fields1) { static auto field_type(auto &&v) {
auto tbl = tab_->tables(); if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
auto fields = tab_->fields(); return field_type::integer;
auto values = ind_->values(); } else if constexpr (std::same_as<std::decay_t<decltype(v)>, float>) {
return field_type::float_;
} else {
return field_type::string;
}
}
void set_field(auto &&name, auto &&v) {
using T = std::decay_t<decltype(v)>;
auto f = t.find_field(name, field_type(v));
dat::field_value_base newfield{f.id};
if constexpr (std::same_as<std::decay_t<decltype(v)>, int> ||
std::same_as<std::decay_t<decltype(v)>, float>) {
newfield.size = sizeof(T);
} else {
newfield.size = t.f.db.utf8_to_dbstr(v).size() + 1;
}
uint32_t newfieldsize = sizeof(newfield) + newfield.size;
std::vector<uint8_t> data(this->v.size + newfieldsize);
auto dp = data.data();
auto base = t.f.dat_.f.p + this->v.offset;
auto p = base;
while (p < base + this->v.size) {
auto &header = *(dat::field_value_base *)p;
auto len = sizeof(header) + header.size;
if (header.field_id != f.id) {
memcpy(dp, p, len);
dp += len;
} else {
if constexpr (std::same_as<std::decay_t<decltype(v)>, int> ||
std::same_as<std::decay_t<decltype(v)>, float>) {
if (header.size == newfield.size && memcmp(p + sizeof(header), &v, sizeof(v)) == 0) {
return;
}
} else {
if (header.size == newfield.size &&
strcmp((const char *)p + sizeof(header), t.f.db.utf8_to_dbstr(v).data()) == 0) {
return;
}
}
}
p += len;
}
*(dat::field_value_base *)dp = newfield;
dp += sizeof(newfield);
if constexpr (std::same_as<std::decay_t<decltype(v)>, int> ||
std::same_as<std::decay_t<decltype(v)>, float>) {
*(T *)dp = v;
dp += sizeof(v);
} else {
auto s = t.f.db.utf8_to_dbstr(v);
memcpy(dp, s.data(), s.size());
dp += s.size() + 1;
}
auto reallen = dp - data.data();
this->v.size = reallen;
this->v.offset = t.f.dat_.f.sz;
memcpy(t.f.dat_.f.alloc_raw(t.f.dat_.f.sz + reallen), data.data(), reallen);
}
};
template <typename T>
struct setter {
T field;
value value;
void operator=(auto &&v) {
value.set_field(field, v);
}
};
auto calc_fields_size = [&](this auto &&f, std::string_view field_name, auto &&n, auto &&v, auto &&...fields) { db2 &db;
file<tab> tab_;
file<ind> ind_;
file<dat> dat_;
files(auto &&db, auto &&base) : db{db}, tab_{base}, ind_{base}, dat_{base} {}
auto get_files() const {
return std::set<path>{tab_.fn,ind_.fn,dat_.fn};
}
auto find_table(auto &&name) {
auto tbl = tab_.data->tables();
auto table_db_cp = db.utf8_to_dbstr(name);
auto it = std::ranges::find_if(tbl, [&](auto &v) {
return v.name == table_db_cp;
});
if (it == tbl.end()) {
if (table_db_cp.size() + 1 > sizeof(tab::table::name)) {
throw std::runtime_error{"too long table name: "s + table_db_cp};
}
tab_.f.alloc_raw(tab_.f.sz + sizeof(tab::table));
auto base = tab_.f.p + sizeof(tab) + tab_.data->n_tables * sizeof(tab::table);
memmove(base + sizeof(tab::table), base, tab_.f.sz - (base - tab_.f.p + sizeof(tab::table)));
auto &newtab = *(tab::table *)base;
memset(&newtab, 0, sizeof(newtab));
newtab.id = ++tab_.data->n_tables;
strcpy(newtab.name, table_db_cp.data());
return table{*this, newtab};
}
return table{*this,*it};
}
// [] not in msvc yet
auto operator()(auto &&tname, auto &&vname, auto &&fname) {
auto tbl = find_table(tname);
auto value = tbl.find_value(vname);
if constexpr (std::is_convertible_v<decltype(fname), const char *>) {
return setter<const char *>{fname, value};
} else {
return setter<std::string>{fname, value};
}
}
};
auto open() {
return files{*this,fn};
}
void add_value(auto &&table, auto &&value, auto && ... fields1) {
auto f = open();
auto tbl = f.tab_.data->tables();
auto fields = f.tab_.data->fields();
auto values = f.ind_.data->values();
auto table_db_cp = utf8_to_dbstr(table);
auto value_db_cp = utf8_to_dbstr(value);
auto calc_fields_size = [&](this auto &&f, auto &&field_name, auto &&n, auto &&v, auto &&...fields) {
if (field_name == n) { if (field_name == n) {
if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) { if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
return sizeof(db2::dat::field_value_base) + sizeof(int); return sizeof(db2::dat::field_value_base) + sizeof(int);
@ -140,7 +303,7 @@ struct db2 {
return sizeof(db2::dat::field_value_base) + 1; return sizeof(db2::dat::field_value_base) + 1;
} }
}; };
auto write_fields = [&](this auto &&f, auto &&p, auto &&field, std::string_view field_name, auto &&n, auto &&v, auto &&...fields) { auto write_fields = [&](this auto &&f, auto &&p, auto &&field, auto &&field_name, auto &&n, auto &&v, auto &&...fields) {
if (field_name == n) { if (field_name == n) {
if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) { if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
if (field.type != db2::field_type::integer) { if (field.type != db2::field_type::integer) {
@ -199,17 +362,17 @@ struct db2 {
} }
}; };
auto it = std::ranges::find_if(tbl, [&](auto &v){return v.name == table;}); auto it = std::ranges::find_if(tbl, [&](auto &v){return v.name == table_db_cp;});
if (it == tbl.end()) { if (it == tbl.end()) {
throw std::runtime_error{"no such table"}; throw std::runtime_error{"no such table: "s + table_db_cp};
} }
auto &t = *it; auto &t = *it;
auto itv = std::ranges::find_if(values, [&](auto &v){return v.table_id == t.id && value == v.name;}); auto itv = std::ranges::find_if(values, [&](auto &v){return v.table_id == t.id && value_db_cp == v.name;});
if (itv == values.end()) { if (itv == values.end()) {
db2::ind::value i{}; db2::ind::value i{};
i.table_id = t.id; i.table_id = t.id;
memcpy(i.name, value.data(), value.size()); memcpy(i.name, value_db_cp.data(), value_db_cp.size());
i.offset = fdat.sz; i.offset = f.dat_.f.sz;
for (auto &&f : fields) { for (auto &&f : fields) {
if (f.table_id != t.id) { if (f.table_id != t.id) {
continue; continue;
@ -218,11 +381,11 @@ struct db2 {
i.size += calc_fields_size(fn, fields1...); i.size += calc_fields_size(fn, fields1...);
} }
++ind_->n_values; ++f.ind_.data->n_values;
auto p = find.alloc_raw(find.sz + sizeof(i)); auto p = f.ind_.f.alloc_raw(f.ind_.f.sz + sizeof(i));
memcpy(p, &i, sizeof(i)); memcpy(p, &i, sizeof(i));
p = fdat.alloc_raw(fdat.sz + i.size); p = f.dat_.f.alloc_raw(f.dat_.f.sz + i.size);
for (auto &&f : fields) { for (auto &&f : fields) {
if (f.table_id != t.id) { if (f.table_id != t.id) {
continue; continue;
@ -233,22 +396,15 @@ struct db2 {
} }
} }
template <typename T> template <typename T>
void edit_value(std::string_view table, std::string_view value, std::string_view field, const T &field_value) { void edit_value(auto &&table, auto &&value, auto &&field, const T &field_value) {
auto tbl = tab_->tables(); auto f = open();
auto fields = tab_->fields(); auto fields = f.tab_.data->fields();
auto values = ind_->values(); auto values = f.ind_.data->values();
auto table_db_cp = utf8_to_dbstr(table.data()); auto value_db_cp = utf8_to_dbstr(value);
auto value_db_cp = utf8_to_dbstr(value.data()); auto field_db_cp = utf8_to_dbstr(field);
auto field_db_cp = utf8_to_dbstr(field.data());
auto it = std::ranges::find_if(tbl, [&](auto &v) { /*auto t = f.find_table(table);
return table_db_cp == v.name;
});
if (it == tbl.end()) {
throw std::runtime_error{"no such table: "s + table_db_cp};
}
auto &t = *it;
auto itv = std::ranges::find_if(values, [&](auto &v) { auto itv = std::ranges::find_if(values, [&](auto &v) {
return v.table_id == t.id && value_db_cp == v.name; return v.table_id == t.id && value_db_cp == v.name;
}); });
@ -262,8 +418,8 @@ struct db2 {
throw std::runtime_error{"no such field: "s + field_db_cp}; throw std::runtime_error{"no such field: "s + field_db_cp};
} }
auto p = fdat.p + itv->offset; auto p = f.dat_.f.p + itv->offset;
while (p < fdat.p + itv->offset + itv->size) { while (p < f.dat_.f.p + itv->offset + itv->size) {
auto &header = *(dat::field_value_base*)p; auto &header = *(dat::field_value_base*)p;
p += sizeof(header); p += sizeof(header);
if (header.field_id == itf->id) { if (header.field_id == itf->id) {
@ -281,12 +437,15 @@ struct db2 {
return; return;
} }
p += header.size; p += header.size;
} }*/
throw std::runtime_error{"no such field"}; throw std::runtime_error{"no such field"};
} }
private: private:
std::string utf8_to_dbstr(const char *s) const { std::string utf8_to_dbstr(const char *s) const {
return str2str(s, CP_UTF8, codepage); return utf8_to_dbstr((const char8_t *)s);
}
std::string utf8_to_dbstr(const char8_t *s) const {
return str2str((const char *)s, CP_UTF8, codepage);
} }
}; };

View file

@ -36,9 +36,30 @@ int main(int argc, char *argv[])
cl::ParseCommandLineOptions(argc, argv); cl::ParseCommandLineOptions(argc, argv);
db2 db{db_fn}; db2 db{db_fn};
auto tbl = db.tab_->tables(); auto f = db.open();
auto fields = db.tab_->fields();
auto values = db.ind_->values(); auto tbl2 = f.find_table(u8"Ãëàéäåðû");
auto valuexxx = tbl2.find_value("GL_S3_PS_FINDER2");
auto valuexxx2 = tbl2.find_value("GL_TEST_XXX");
valuexxx2.set_field("NAME", "X");
valuexxx2.set_field("NAME", "X");
valuexxx2.set_field("NAME", "X");
valuexxx2.set_field("NAME", "X");
valuexxx2.set_field("NAME", "X");
valuexxx2.set_field("NA", "X");
valuexxx2.set_field("VW", 5);
valuexxx2.set_field("VW2", 5.3f);
valuexxx2.set_field("VW2", 6.3f);
valuexxx2.set_field("VW2", 6.3f);
valuexxx2.set_field("VW2", 6.3f);
valuexxx2.set_field("VW2", 6.3f);
f(u8"Ãëàéäåðû", "GL_S3_PS_FINDER2", "NAME") = "xx";
f(u8"Ãëàéäåðû", "GL_S3_PS_FINDER2", "VW2") = 4.3f;
auto tbl = f.tab_.data->tables();
auto fields = f.tab_.data->fields();
auto values = f.ind_.data->values();
std::string spaceval(4, ' '); std::string spaceval(4, ' ');
std::string spacefield(8, ' '); std::string spacefield(8, ' ');
@ -46,8 +67,8 @@ int main(int argc, char *argv[])
std::println("{}:", t.name); std::println("{}:", t.name);
for (auto &&v : values | std::views::filter([&](auto &v){return v.table_id == t.id;})) { for (auto &&v : values | std::views::filter([&](auto &v){return v.table_id == t.id;})) {
std::println("{}{}:", spaceval, v.name); std::println("{}{}:", spaceval, v.name);
auto max = db.fdat.p + v.offset + v.size; auto max = f.dat_.f.p + v.offset + v.size;
auto p = db.fdat.p + v.offset; auto p = f.dat_.f.p + v.offset;
while (p < max) { while (p < max) {
auto vb = (db2::dat::field_value_base*)p; auto vb = (db2::dat::field_value_base*)p;
p += sizeof(db2::dat::field_value_base); p += sizeof(db2::dat::field_value_base);