#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace blobng { using byte_type = std::byte; using byte_array = std::span; namespace blob_detail { struct reader { reader () {}; reader(byte_type* data, size_t size) : m_span(data, size) {}; void seek(size_t off) { if (off <= m_span.size() && off >= 0) { pos = off; } else { throw std::runtime_error("blob::reader: oob seek"); } } template T read_t(){ if (pos + sizeof(T) <= m_span.size()){ auto* ret = reinterpret_cast(m_span.data() + pos); pos+=sizeof(T); return *ret; } throw std::runtime_error("blob::reader: oob"); } byte_array m_span; std::size_t pos = 0; }; } struct blob; struct compressed_blob; struct any_blob; struct blob_value { blob_value(byte_array sv) : value_data(sv) {}; uint32_t u32(){ if (value_data.size() == 4) { auto v = (uint32_t*)value_data.data(); return *v; } throw std::runtime_error("blob: tried to read value as u32 with bytes != 4"); } uint64_t u64(){ if (value_data.size() == 8) { auto v = (uint64_t*)value_data.data(); return *v; } throw std::runtime_error("blob: tried to read value as u32 with bytes != 8"); } //std::string_view cstr() { // //} blob ublob(); compressed_blob cblob(); any_blob ablob(); inline byte_array data() { return value_data; } byte_array value_data; }; struct dummy_init_type {}; struct blob { blob(dummy_init_type d) {} // *never* use this blob(void* data, size_t size) : m_reader(reinterpret_cast(data),size) { auto magic = m_reader.read_t(); if (magic != 0x5001){ throw std::runtime_error("blob: bad magic!!"); } totalsize = m_reader.read_t(); slacksize = m_reader.read_t(); } std::optional get_key(std::string_view key_) { byte_array key(reinterpret_cast(const_cast(key_.data())), key_.size()); m_reader.seek(10); while (m_reader.pos < totalsize) { auto keysize = m_reader.read_t(); auto valuesize = m_reader.read_t(); byte_array keydata(m_reader.m_span.data() + m_reader.pos, keysize); if (std::equal(key.begin(),key.end(),keydata.begin(),keydata.end())) { return byte_array (m_reader.m_span.data() + m_reader.pos + keysize, valuesize); } else{ m_reader.seek(m_reader.pos+keysize+valuesize); } } return std::nullopt; } std::optional operator[](std::string_view key) { return get_key(key); } size_t totalsize; size_t slacksize; blob_detail::reader m_reader; }; struct compressed_blob : public blob { compressed_blob(void* compressed_data, size_t comp_size) : blob(dummy_init_type()){ blob_detail::reader temp_reader(reinterpret_cast(compressed_data), comp_size); auto magic = temp_reader.read_t(); if (magic != 0x4301){ throw std::runtime_error("blob: bad magic in compressed blob ctor"); } auto packedsize = temp_reader.read_t(); auto unpackedsize = temp_reader.read_t(); auto compression_level = temp_reader.read_t(); blobdata.resize(unpackedsize); size_t pk = comp_size - 20; size_t max_uncompressed = blobdata.size(); if (zng_uncompress2(reinterpret_cast(blobdata.data()),&max_uncompressed, reinterpret_cast(compressed_data) + 20, &pk) != Z_OK) { throw std::runtime_error("blob: compressed blob failed to decompress"); } m_reader = blob_detail::reader(blobdata.data(),blobdata.size()); auto magic2 = m_reader.read_t(); if (magic2 != 0x5001){ throw std::runtime_error("blob: bad magic after decompression"); } totalsize = m_reader.read_t(); slacksize = m_reader.read_t(); } std::vector blobdata; }; struct any_blob { any_blob(void* compressed_data, size_t comp_size){ blob_detail::reader temp_reader(reinterpret_cast(compressed_data), comp_size); auto magic = temp_reader.read_t(); if (magic == 0x4301) { blob_type = compressed_blob(compressed_data, comp_size); } if (magic == 0x5001) { blob_type = blob(compressed_data,comp_size); } } std::optional get_key(std::string_view key) { if (auto* pval = std::get_if(&blob_type)) { return pval->get_key(key); } else if (auto* pval = std::get_if(&blob_type)) { return pval->get_key(key); } return std::nullopt; } std::optional operator[](std::string_view key) { return get_key(key); } std::variant blob_type; }; using blob_type = blob; inline blob blob_value::ublob(){ return blob_type(reinterpret_cast(value_data.data()), value_data.size()); } inline compressed_blob blob_value::cblob(){ return compressed_blob(reinterpret_cast(value_data.data()), value_data.size()); } inline any_blob blob_value::ablob(){ return any_blob(reinterpret_cast(value_data.data()), value_data.size()); } }