Program Listing for File dataPool.h

↰ Return to documentation for file (PrismEngine/src/dataPool.h)

#pragma once
#include <cstring>
#include <stack>
#include <vector>
#include "dataHandle.h"

namespace prism {
    namespace scene {
        template <typename Handle>
        class DataPool {
        public:

            using DataType = typename Handle::DataType;
            using TagType = typename Handle::TagType;
            using HandleType = Handle;

            struct Item {
                DataType* data = nullptr;
                uint16_t size = 0;
                uint16_t refCount = 0;
                bool isActive = false;

                Item() = default;

                void cleanup() {
                    if (data) { free(data); data = nullptr; }
                }
            };

            struct Stats {
                size_t totalAllocations = 0;
                size_t totalMemory = 0;
                size_t activeItems = 0;
                size_t freeIds = 0;
            };

            template <std::size_t N>
            HandleType add(const std::array<DataType, N>& data) {
                return add(data.data(), static_cast<uint16_t>(N));
            }

            HandleType add(const DataType* data, uint16_t size) {
                if (!data || size == 0) return HandleType::invalid();

                auto* newData = static_cast<DataType*>(malloc(size * sizeof(DataType)));

                if (!newData) return HandleType::invalid();

                uint32_t id = getNextId();

                memcpy(newData, data, size * sizeof(DataType));

                auto& item = items[id];
                item.data = newData;
                item.size = size;
                item.refCount = 0;
                item.isActive = true;
                activeCount++;
                stats.totalAllocations++;
                stats.totalMemory += size * sizeof(DataType);

                return HandleType{id};
            }

            const DataType* get(HandleType handle, uint16_t& outSize) const {
                outSize = 0;
                if (!handle.isValid() || handle.id >= items.size()) return nullptr;

                const auto& item = items[handle.id];
                if (!item.isActive) return nullptr;

                outSize = items[handle.id].size;
                return items[handle.id].data;
            }

            void addRef(uint32_t id) {
                if (id < items.size() && items[id].isActive) {
                    items[id].refCount++;
                }
            }

            void addRef(HandleType handle) {
                if (handle.isValid()) addRef(handle.id);
            }

            void remove(uint32_t id) {
                if (id >= items.size() || !items[id].isActive) return;

                items[id].refCount--;
                if (items[id].refCount == 0) {
                    free(items[id].data);
                    items[id].data = nullptr;
                    items[id].isActive = false;
                    activeCount--;
                    stats.totalAllocations--;
                    stats.totalMemory -= items[id].size * sizeof(DataType);
                    freeIds.push(id);
                }
            }

            void remove(HandleType handle) {
                if (handle.isValid()) remove(handle.id);
            }

            void forceRemove(HandleType handle) {
                if (handle.isValid() && handle.id < items.size() && items[handle.id].isActive) {
                    items[handle.id].refCount = 1;
                    remove(handle);
                }
            }

            Stats getStats() const {
                Stats res = stats;
                res.activeItems = activeCount;
                res.freeIds = freeIds.size();
                return res;
            }

            void cleanup() {
                for (auto& item : items) item.cleanup();
                items.clear();
                while (!freeIds.empty()) freeIds.pop();
                activeCount = 0;
                maxId = 0;
                stats = Stats{};
            }

            ~DataPool() {
                cleanup();
            }

        private:
            uint32_t getNextId() {
                if (!freeIds.empty()) {
                    uint32_t id = freeIds.top();
                    freeIds.pop();
                    return id;
                }

                uint32_t id = maxId;

                maxId++;

                if (id >= items.size()) {
                    items.resize(maxId);
                }

                return id;
            }

            std::vector<Item> items;
            std::stack<uint32_t> freeIds;
            uint32_t maxId = 0;
            size_t activeCount = 0;
            Stats stats;
        };

    }
}