#pragma once #include "./cbor-walker.h" #include #include namespace signalsmith { namespace storage { struct StorageDummy { template void operator()(const char *, V &) {} }; template struct StorageScanner { template 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 void value(std::vector &array) { for (auto &item : array) { value(item); } } template void value(V &v) { v.state(*(SubClassCRTP *)this); } }; template struct StorageCborWriter { StorageCborWriter(const signalsmith::cbor::CborWriter &writer, std::vector *buffer=nullptr) : cbor(writer) { if (buffer) buffer->resize(0); cbor.openMap(); } StorageCborWriter(std::vector &cborBuffer) : StorageCborWriter(signalsmith::cbor::CborWriter(cborBuffer), &cborBuffer) {} ~StorageCborWriter() { cbor.close(); } template 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 void writeValue(std::vector &array) { cbor.openArray(array.size()); for (auto &item : array) { writeValue(item); } } #define STORAGE_TYPED_ARRAY(T) \ void writeValue(std::vector &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 void writeValue(Obj &obj) { cbor.openMap(); obj.state(*(SubClassCRTP *)this); cbor.close(); } }; template struct StorageCborReader { using Cbor = signalsmith::cbor::TaggedCborWalker; StorageCborReader(Cbor c) : cbor(c) { if (cbor.isMap()) cbor = cbor.enter(); } StorageCborReader(const std::vector &v) : StorageCborReader(Cbor(v)) {} template 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 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 void readValue(std::vector &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); } #define STORAGE_TYPED_ARRAY(T) \ void readValue(std::vector &array) { \ if (cbor.isTypedArray()) { \ array.resize(cbor.typedArrayLength()); \ cbor.readTypedArray(array); \ } else { \ readValue>(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 : public StorageCborWriter> { using StorageCborWriter>::StorageCborWriter; }; template<> struct StorageCborReader : public StorageCborReader> { using StorageCborReader>::StorageCborReader; }; }} // namespace