#pragma once #include #include #include namespace utils { template requires(std::is_trivially_copyable_v && std::is_same_v>) class safe_object_accessor { public: safe_object_accessor(const std::span buffer, const size_t offset) : buffer_(buffer), offset_(offset) { } /***************************************************************************** * Object is copied to make sure platform-dependent alignment requirements * are respected ****************************************************************************/ T get(const size_t element_index = 0) const { T value{}; memcpy(&value, get_valid_pointer(element_index), size); return value; } void set(const T value, const size_t element_index = 0) const { memcpy(get_valid_pointer(element_index), &value, size); } private: static constexpr auto size = sizeof(T); std::span buffer_{}; size_t offset_{}; S* get_valid_pointer(const size_t element_index) const { const auto start_offset = offset_ + (size * element_index); const auto end_offset = start_offset + size; if (end_offset > buffer_.size()) { throw std::runtime_error("Buffer accessor overflow"); } return buffer_.data() + start_offset; } }; template requires(std::is_same_v>) class safe_buffer_accessor { public: safe_buffer_accessor(const std::span buffer) : buffer_(buffer) { } template safe_buffer_accessor(const safe_buffer_accessor& obj) : buffer_(obj.get_buffer()) { } template safe_object_accessor as(const size_t offset) const { return {this->buffer_, offset}; } T* get_pointer_for_range(const size_t offset, const size_t size) const { this->validate(offset, size); return this->buffer_.data() + offset; } void validate(const size_t offset, const size_t size) const { const auto end = offset + size; if (end > buffer_.size()) { throw std::runtime_error("Buffer accessor overflow"); } } template std::basic_string as_string(const size_t offset) const { safe_object_accessor string_accessor{this->buffer_, offset}; std::basic_string result{}; while (true) { auto value = string_accessor.get(result.size()); if (!value) { return result; } result.push_back(std::move(value)); } } std::span get_buffer() const { return this->buffer_; } private: const std::span buffer_{}; }; }