1
0
basics/stfx/storage/storage.h

243 lines
5.5 KiB
C++

#pragma once
#include "./cbor-walker.h"
struct StorageDummy {
template<class V>
void operator()(const char *, V &) {}
};
struct StorageExample {
template<class V>
void operator()(const char */*key*/, V &v) {
value(v);
}
private:
void value(int64_t &v) {};
void value(uint64_t &v) {};
void value(int32_t &v) {};
void value(uint32_t &v) {};
void value(int16_t &v) {};
void value(uint16_t &v) {};
void value(int8_t &v) {};
void value(uint8_t &v) {};
void value(float &v) {};
void value(double &v) {};
void value(bool &v) {};
template<class Item>
void value(std::vector<Item> &array) {
for (auto &item : array) {
value(item);
}
}
template<class V>
void value(V &v) {
v.state(*this);
}
};
namespace _impl {
template<class Fallback, class MaybeVoid=void>
struct VoidFallback {
using T = MaybeVoid;
};
template<class Fallback>
struct VoidFallback<Fallback, void> {
using T = Fallback;
};
}
template<bool compact=false, class SubClassCRTP=void>
struct StorageCborWriter {
StorageCborWriter(const signalsmith::cbor::CborWriter &writer, std::vector<unsigned char> *buffer=nullptr) : cbor(writer) {
if (buffer) buffer->resize(0);
if (compact) {
cbor.addTag(0xCB68ADED); // turns into "2store..." when base64-encoded
cbor.openArray();
} else {
cbor.openMap();
}
}
StorageCborWriter(std::vector<unsigned char> &cborBuffer) : StorageCborWriter(signalsmith::cbor::CborWriter(cborBuffer), &cborBuffer) {}
~StorageCborWriter() {
cbor.close();
}
template<class V>
void operator()(const char *key, V &value) {
if (!compact) cbor.addUtf8(key);
writeValue(value);
}
protected:
signalsmith::cbor::CborWriter cbor;
#define STORAGE_BASIC_INT(V) \
void writeValue(V &value) { \
cbor.addInt(value); \
}
STORAGE_BASIC_INT(int64_t)
STORAGE_BASIC_INT(uint64_t)
STORAGE_BASIC_INT(int32_t)
STORAGE_BASIC_INT(uint32_t)
STORAGE_BASIC_INT(int16_t)
STORAGE_BASIC_INT(uint16_t)
STORAGE_BASIC_INT(int8_t)
STORAGE_BASIC_INT(uint8_t)
#undef STORAGE_BASIC_INT
void writeValue(float &value) {
cbor.addFloat(value);
}
void writeValue(double &value) {
cbor.addFloat(value);
}
void writeValue(bool &value) {
cbor.addBool(value);
}
void writeValue(const char *cStr) {
cbor.addUtf8(cStr);
}
template<class Item>
void writeValue(std::vector<Item> &array) {
cbor.openArray(array.size());
for (auto &item : array) {
writeValue(item);
}
}
#define STORAGE_TYPED_ARRAY(T) \
void writeValue(std::vector<T> &array) { \
cbor.addTypedArray(array.data(), array.size()); \
}
STORAGE_TYPED_ARRAY(uint8_t)
STORAGE_TYPED_ARRAY(int8_t)
STORAGE_TYPED_ARRAY(uint16_t)
STORAGE_TYPED_ARRAY(int16_t)
STORAGE_TYPED_ARRAY(uint32_t)
STORAGE_TYPED_ARRAY(int32_t)
STORAGE_TYPED_ARRAY(uint64_t)
STORAGE_TYPED_ARRAY(int64_t)
STORAGE_TYPED_ARRAY(float)
STORAGE_TYPED_ARRAY(double)
#undef STORAGE_TYPED_ARRAY
using SubStorage = typename _impl::VoidFallback<StorageCborWriter, SubClassCRTP>::T;
template<class Obj>
void writeValue(Obj &obj) {
cbor.openMap();
obj.state(*(SubStorage *)this);
cbor.close();
}
};
template<bool compact=false, class SubClassCRTP=void>
struct StorageCborReader {
using Cbor = signalsmith::cbor::TaggedCborWalker;
StorageCborReader(Cbor c) : cbor(c) {
if (compact) {
if (cbor.isArray()) cbor = cbor.enter();
} else {
if (cbor.isMap()) cbor = cbor.enter();
}
}
StorageCborReader(const std::vector<unsigned char> &v) : StorageCborReader(Cbor(v)) {}
template<class V>
void operator()(const char *key, V &v) {
if (filterKeyBytes != nullptr) {
if (!keyMatch(key, filterKeyBytes, filterKeyLength)) return;
} else if (!compact) {
if (!cbor.isUtf8()) return; // We expect a string key
if (!keyMatch(key, (const char *)cbor.bytes(), cbor.length())) {
return; // key doesn't match
}
cbor++;
}
readValue(v);
}
private:
template<class Obj>
void readValue(Obj &obj) {
if (compact) {
// No keys, no filters
return obj.state(*(SubClass *)this);
}
if (!cbor.isMap()) return;
cbor = cbor.forEachPair([&](Cbor key, Cbor value){
if (!key.isUtf8()) return;
const char *fkb = filterKeyBytes;
size_t fkl = filterKeyLength;
// Temporarily set key, and scan the object for that property
filterKeyBytes = (const char *)key.bytes();
filterKeyLength = key.length();
cbor = value;
obj.state(*(SubClass *)this);
filterKeyBytes = fkb;
filterKeyLength = fkl;
});
}
#define STORAGE_BASIC_TYPE(V) \
void readValue(V &v) { \
v = V(cbor++); \
}
STORAGE_BASIC_TYPE(int64_t)
STORAGE_BASIC_TYPE(uint64_t)
STORAGE_BASIC_TYPE(int32_t)
STORAGE_BASIC_TYPE(uint32_t)
STORAGE_BASIC_TYPE(int16_t)
STORAGE_BASIC_TYPE(uint16_t)
STORAGE_BASIC_TYPE(int8_t)
STORAGE_BASIC_TYPE(uint8_t)
STORAGE_BASIC_TYPE(float)
STORAGE_BASIC_TYPE(double)
STORAGE_BASIC_TYPE(bool)
#undef STORAGE_BASIC_TYPE
void readValue(std::string &v) {
v = (cbor++).utf8();
}
template<class Item>
void readValue(std::vector<Item> &array) {
if (!cbor.isArray()) return;
size_t length = 0;
cbor = cbor.forEach([&](Cbor item, size_t index){
length = index + 1;
if (array.size() < length) array.resize(length);
cbor = item;
readValue(array[index]);
});
array.resize(length);
}
protected:
Cbor cbor;
const char *filterKeyBytes = nullptr;
size_t filterKeyLength = 0;
static bool keyMatch(const char *key, const char *filterKeyBytes, size_t filterKeyLength) {
for (size_t i = 0; i < filterKeyLength; ++i) {
if (key[i] != filterKeyBytes[i]) return false;
}
return key[filterKeyLength] == 0;
}
using SubClass = typename _impl::VoidFallback<StorageCborReader, SubClassCRTP>::T;
};