#pragma once #include "stdint.h" #include "cassert" #include #include "Util/Span.h" template inline T const* ByteToData(uint8_t const* data) { return reinterpret_cast(data); } template inline T* ByteToData(uint8_t* data) { return reinterpret_cast(data); } template class SharedBuffer final { private: struct ControlBlock { ControlBlock(uint32_t size, const MetaData& data) : Size{ size }, Data { data } {} std::atomic Count{ 1 }; uint32_t Size{}; MetaData Data{}; }; static constexpr size_t ControlBlockOffset = 0; static constexpr size_t DataOffset = sizeof(ControlBlock); public: SharedBuffer() = default; SharedBuffer(int size, const MetaData& metaData) { Data = new uint8_t[sizeof(ControlBlock) + size * sizeof(T)]{}; new (GetControlBlock()) ControlBlock(size, metaData); for (size_t i{}; i < size; ++i) { new (&Ptr()[i]) T{}; } } ~SharedBuffer() { Destruct(); } SharedBuffer(const SharedBuffer& other) : Data{ other.Data } { if (Data) ++GetControlBlock()->Count; } SharedBuffer(SharedBuffer&& other) noexcept : Data{ other.Data } { other.Data = nullptr; } SharedBuffer& operator=(const SharedBuffer& other) { if (this != &other) { Destruct(); Data = other.Data; if (Data) ++GetControlBlock()->Count; } return *this; } SharedBuffer& operator=(SharedBuffer&& other) noexcept { if (this != &other) { Destruct(); Data = other.Data; other.Data = nullptr; } return *this; } private: ControlBlock const* GetControlBlock() const { assert(Data); return ByteToData(Data + ControlBlockOffset); } ControlBlock* GetControlBlock() { assert(Data); return ByteToData(Data + ControlBlockOffset); } void Destruct() { if (Data != nullptr && --GetControlBlock()->Count == 0) { uint32_t size = GetSize(); GetControlBlock()->~ControlBlock(); for (size_t i = 0; i < size; ++i) Ptr()[i].~T(); delete[] Data; } } public: T const* Ptr() const { assert(Data); return ByteToData(Data + DataOffset); } T* Ptr() { assert(Data); return ByteToData(Data + DataOffset); } tcb::span GetData() const { return tcb::span(Ptr(), GetSize()); } tcb::span GetData() { return tcb::span(Ptr(), GetSize()); } MetaData const* GetMetaData() const { assert(Data); return &GetControlBlock()->Data; } MetaData* GetMetaData() { assert(Data); return &GetControlBlock()->Data; } T& operator[] (uint32_t index) { assert(index < GetControlBlock()->Size); return GetData()[index]; } const T& operator[] (uint32_t index) const { assert(index < GetControlBlock()->Size); return GetData()[index]; } uint32_t GetSize() const { return GetControlBlock()->Size; } operator bool() const { return Data; } private: uint8_t* Data{}; };