265 lines
6.3 KiB
C++
265 lines
6.3 KiB
C++
#pragma once
|
|
|
|
#include "./cbor-walker.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace signalsmith { namespace storage {
|
|
|
|
struct StorageDummy {
|
|
template<class V>
|
|
void operator()(const char *, V &) {}
|
|
};
|
|
|
|
template<class SubClassCRTP>
|
|
struct StorageScanner {
|
|
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) {};
|
|
void value(std::string &str) {};
|
|
|
|
template<class Item>
|
|
void value(std::vector<Item> &array) {
|
|
for (auto &item : array) {
|
|
value(item);
|
|
}
|
|
}
|
|
|
|
template<class V>
|
|
void value(V &v) {
|
|
v.state(*(SubClassCRTP *)this);
|
|
}
|
|
};
|
|
|
|
template<class SubClassCRTP=void>
|
|
struct StorageCborWriter {
|
|
StorageCborWriter(const signalsmith::cbor::CborWriter &writer, std::vector<unsigned char> *buffer=nullptr) : cbor(writer) {
|
|
if (buffer) buffer->resize(0);
|
|
|
|
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) {
|
|
cbor.addUtf8(key);
|
|
writeValue(value);
|
|
}
|
|
|
|
private:
|
|
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);
|
|
}
|
|
|
|
// Support writing C strings (but not reading them), so inherited writers can add supplemental hints/info without allocating
|
|
void writeValue(const char *cStr) {
|
|
cbor.addUtf8(cStr);
|
|
}
|
|
|
|
void writeValue(std::string &str) {
|
|
writeValue(str.c_str());
|
|
}
|
|
|
|
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
|
|
|
|
template<class Obj>
|
|
void writeValue(Obj &obj) {
|
|
cbor.openMap();
|
|
obj.state(*(SubClassCRTP *)this);
|
|
cbor.close();
|
|
}
|
|
};
|
|
|
|
template<class SubClassCRTP=void>
|
|
struct StorageCborReader {
|
|
using Cbor = signalsmith::cbor::TaggedCborWalker;
|
|
|
|
StorageCborReader(Cbor c) : cbor(c) {
|
|
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;
|
|
}
|
|
if (!cbor.isUtf8()) return; // We expect a string key
|
|
// If we have a filter, we *should* be just in front of the appropriate key, but check anyway
|
|
if (!keyMatch(key, (const char *)cbor.bytes(), cbor.length())) {
|
|
return; // key doesn't match
|
|
}
|
|
cbor++;
|
|
readValue(v);
|
|
}
|
|
|
|
private:
|
|
Cbor cbor;
|
|
const char *filterKeyBytes = nullptr;
|
|
size_t filterKeyLength = 0;
|
|
|
|
template<class Obj>
|
|
void readValue(Obj &obj) {
|
|
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 = key;
|
|
obj.state(*(SubClassCRTP *)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) {
|
|
if (!cbor.isUtf8()) v.clear();
|
|
v.assign((const char *)cbor.bytes(), cbor.length());
|
|
++cbor;
|
|
}
|
|
|
|
template<class Item>
|
|
void readVector(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);
|
|
}
|
|
|
|
template<class Item>
|
|
void readValue(std::vector<Item> &array) {
|
|
readVector(array);
|
|
}
|
|
|
|
#define STORAGE_TYPED_ARRAY(T) \
|
|
void readValue(std::vector<T> &array) { \
|
|
if (cbor.isTypedArray()) { \
|
|
array.resize(cbor.typedArrayLength()); \
|
|
cbor.readTypedArray(array); \
|
|
} else { \
|
|
readVector<T>(array); \
|
|
} \
|
|
}
|
|
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
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
// If void, use itself for CRTP
|
|
template<>
|
|
struct StorageCborWriter<void> : public StorageCborWriter<StorageCborWriter<void>> {
|
|
using StorageCborWriter<StorageCborWriter<void>>::StorageCborWriter;
|
|
};
|
|
template<>
|
|
struct StorageCborReader<void> : public StorageCborReader<StorageCborReader<void>> {
|
|
using StorageCborReader<StorageCborReader<void>>::StorageCborReader;
|
|
};
|
|
|
|
}} // namespace
|