[db2] Update.

This commit is contained in:
lzwdgc 2024-04-02 01:34:43 +03:00
parent 8ac4cdc8a8
commit 1124c73b14
2 changed files with 130 additions and 256 deletions

View file

@ -21,6 +21,8 @@
#include <primitives/templates2/type_name.h>
#include <variant>
struct db2 {
using char20 = char[0x20];
@ -56,6 +58,11 @@ struct db2 {
auto base = (field *)(table_base + n_tables);
return std::span{base, base + n_fields};
}
auto fields(int table_id) {
auto table_base = (table *)(&n_fields + 1);
auto base = (field *)(table_base + n_tables);
return std::span{base, base + n_fields} | std::views::filter([=](auto &v){return v.table_id == table_id;});
}
};
// table values (index)
struct ind {
@ -72,6 +79,10 @@ struct db2 {
auto base = (value *)(&n_values + 1);
return std::span{base, base + n_values};
}
auto values(int table_id) {
auto base = (value *)(&n_values + 1);
return std::span{base, base + n_values} | std::views::filter([=](auto &v){return v.table_id == table_id;});
}
};
// field values
struct dat {
@ -100,207 +111,128 @@ struct db2 {
};
// actual db
struct files {
template <typename T, typename V>
struct setter {
T field;
V value;
void operator=(auto &&v) {
value.set_field(field, v);
}
};
struct table {
files &f;
db2::tab::table t;
auto find_field(auto &&name, auto ftype) {
auto fields = f.tab_.data->fields();
auto field_db_cp = f.db.utf8_to_dbstr(name);
auto itf = std::ranges::find_if(fields, [&](auto &v) {
return v.table_id == t.id && field_db_cp == v.name;
});
if (itf == fields.end()) {
if (field_db_cp.size() + 1 > sizeof(tab::field::name)) {
throw std::runtime_error{"too long field name: "s + field_db_cp};
}
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};
}
auto operator()(auto &&name) {
return find_value(name);
}
auto operator()(auto &&vname, auto &&fname) {
auto value = find_value(vname);
if constexpr (std::is_convertible_v<decltype(fname), const char *>) {
return setter<const char *, decltype(value)>{fname, value};
} else {
return setter<std::string, decltype(value)>{fname, value};
}
}
};
struct value {
table t;
db2::ind::value &v;
static auto field_type(auto &&v) {
if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
return field_type::integer;
} 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));
if (f.type != field_type(v)) {
throw std::runtime_error{"field type mismatch: "s + t.f.db.utf8_to_dbstr(name)};
}
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);
}
auto operator()(auto &&name) {
if constexpr (std::is_convertible_v<decltype(name), const char *>) {
return setter<const char *, value>{name, *this};
} else {
return setter<std::string, value>{name, *this};
}
}
};
db2 &db;
file<tab> tab_;
file<ind> ind_;
file<dat> dat_;
files(auto &&db, auto &&base) : db{db}, tab_{base}, ind_{base}, dat_{base} {}
void alloc(auto sz) {
tab_.f.alloc_raw(sz);
ind_.f.alloc_raw(sz);
dat_.f.alloc_raw(sz);
}
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) {
return find_table(tname);
}
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 *, decltype(value)>{fname, value};
} else {
return setter<std::string, decltype(value)>{fname, value};
struct db2_internal {
using db2_memory_value = std::variant<std::string, int, float>;
using db2_memory = std::map<std::string, std::map<std::string, std::map<std::string, db2_memory_value>>>;
db2_memory m;
auto &operator[](this auto &&d, const std::string &s) {
return d.m[s];
}
void save() {
}
};
// converts string to utf8, trims them
auto to_map() const {
auto prepare_string = [](auto &&in) {
auto s = str2utf8(in);
boost::trim(s);
return s;
};
db2_internal m;
auto tbl = tab_.data->tables();
for (auto &&t : tbl) {
auto &jt = m[prepare_string(t.name)];
auto fields = tab_.data->fields(t.id);
for (auto &&v : ind_.data->values(t.id)) {
auto vn = prepare_string(v.name);
if (jt.contains(vn)) {
throw std::logic_error{"duplicate"};
}
auto &jv = jt[vn];
auto p = dat_.f.p + v.offset;
auto max = p + v.size;
while (p < max) {
auto vb = (db2::dat::field_value_base *)p;
p += sizeof(db2::dat::field_value_base);
auto f = std::ranges::find_if(fields, [&](auto &f) { return f.id == vb->field_id; });
if (f == fields.end()) {
throw std::logic_error{"unknown field"};
}
auto fn = prepare_string(f->name);
switch (f->type) {
case db2::field_type::integer:
jv[fn] = *(int *)p;
break;
case db2::field_type::float_:
jv[fn] = *(float *)p;
break;
case db2::field_type::string:
jv[fn] = prepare_string((const char *)p);
break;
default:
throw std::logic_error{"bad type"};
}
p += vb->size;
}
}
}
return m;
}
/*auto to_json() const {
auto prepare_string = [](auto &&in) {
auto s = str2utf8(in);
boost::trim(s);
return s;
};
auto tbl = tab_.data->tables();
nlohmann::json ja;
for (auto &&t : tbl) {
auto &jt = ja[prepare_string(t.name)];
auto fields = tab_.data->fields(t.id);
for (auto &&v : ind_.data->values(t.id)) {
auto vn = prepare_string(v.name);
if (jt.contains(vn)) {
throw std::logic_error{"duplicate"};
}
auto &jv = jt[vn];
auto p = dat_.f.p + v.offset;
auto max = p + v.size;
while (p < max) {
auto vb = (db2::dat::field_value_base *)p;
p += sizeof(db2::dat::field_value_base);
auto f = std::ranges::find_if(fields, [&](auto &f) {
return f.id == vb->field_id;
});
if (f == fields.end()) {
throw std::logic_error{"unknown field"};
}
auto fn = prepare_string(f->name);
switch (f->type) {
case db2::field_type::integer:
jv[fn] = *(int *)p;
break;
case db2::field_type::float_:
jv[fn] = *(float *)p;
break;
case db2::field_type::string:
jv[fn] = prepare_string((const char *)p);
break;
default:
throw std::logic_error{"bad type"};
}
p += vb->size;
}
}
}
}*/
};
auto alloc() {
files{*this,fn}.alloc(0);
}
auto open() {
return files{*this, fn};
}

View file

@ -24,6 +24,7 @@
#include <primitives/sw/main.h>
#include <primitives/sw/settings.h>
#include <primitives/sw/cl.h>
#include <primitives/templates2/overload.h>
#include <nlohmann/json.hpp>
#include <fstream>
@ -38,84 +39,25 @@ int main(int argc, char *argv[])
db2 db{db_fn};
auto f = db.open();
/*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();
auto prepare_string = [](auto &&in) {
auto s = str2utf8(in);
boost::trim(s);
return s;
};
auto tbl = f.to_map();
nlohmann::json ja;
for (auto &&t : tbl) {
auto &jt = ja[prepare_string(t.name)];
for (auto &&v : values | std::views::filter([&](auto &v){return v.table_id == t.id;})) {
auto vn = prepare_string(v.name);
if (jt.contains(vn)) {
throw std::logic_error{"duplicate"};
}
for (auto &&[tn,t] : tbl) {
auto &jt = ja[tn];
for (auto &&[vn,v] : t) {
auto &jv = jt[vn];
auto max = f.dat_.f.p + v.offset + v.size;
auto p = f.dat_.f.p + v.offset;
while (p < max) {
auto vb = (db2::dat::field_value_base*)p;
p += sizeof(db2::dat::field_value_base);
auto f = std::ranges::find_if(fields, [&](auto &f){return f.table_id == t.id && f.id == vb->field_id;});
if (f == fields.end()) {
continue;
}
switch (f->type) {
case db2::field_type::integer: {
auto fv = (int*)p;
p += vb->size;
jv[prepare_string(f->name)] = *fv;
break;
}
case db2::field_type::float_: {
auto fv = (float*)p;
p += vb->size;
jv[prepare_string(f->name)] = *fv;
break;
}
case db2::field_type::string: {
auto fv = (const char*)p;
p += vb->size;
jv[prepare_string(f->name)] = prepare_string(fv);
break;
}
default: {
break;
}
}
for (auto &&[fn, fv] : v) {
std::visit(overload{
[&](const int &v){jv[fn] = v;},
[&](const float &v){jv[fn] = v;},
[&](const std::string &v){jv[fn] = v;},
}, fv);
}
}
}
write_file(path{db_fn} += ".json", ja.dump(1));
db2 x{path{db_fn} += "new"};
x.alloc();
auto newdb = x.open();
for (auto &&[t,vals] : ja.items()) {
for (auto &&[v,fields] : vals.items()) {