File size: 2,961 Bytes
f86fb3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#pragma once

#include <cstddef>
#include <vector>
#include <memory>
#include <new>
#include <cassert>
#include <utility>
#include "cache_line.hpp"

namespace quant::core {

/**
 * Fixed-capacity, pre-allocated Object Pool (Arena Allocator) for zero-allocation hot paths.
 * Eliminates heap fragmentation and malloc/free jitter in trading engines.
 */
template <typename T, size_t Capacity>
class alignas(CACHELINE_SIZE) ObjectPool {
    static_assert(Capacity > 0, "Capacity must be positive");

    union Node {
        alignas(alignof(T)) char storage[sizeof(T)];
        Node* next_free;
    };

public:
    ObjectPool() : free_head_(nullptr), allocated_count_(0) {
        arena_ = std::make_unique<Node[]>(Capacity);
        reset();
    }

    ~ObjectPool() {
        // Any active objects should ideally be destroyed before pool teardown
    }

    ObjectPool(const ObjectPool&) = delete;
    ObjectPool& operator=(const ObjectPool&) = delete;
    ObjectPool(ObjectPool&&) noexcept = default;
    ObjectPool& operator=(ObjectPool&&) noexcept = default;

    void reset() noexcept {
        for (size_t i = 0; i < Capacity - 1; ++i) {
            arena_[i].next_free = &arena_[i + 1];
        }
        arena_[Capacity - 1].next_free = nullptr;
        free_head_ = &arena_[0];
        allocated_count_ = 0;
    }

    template <typename... Args>
    [[nodiscard]] T* allocate(Args&&... args) {
        if (!free_head_) [[unlikely]] {
            return nullptr; // Pool exhausted
        }

        Node* node = free_head_;
        free_head_ = free_head_->next_free;
        ++allocated_count_;

        T* obj = reinterpret_cast<T*>(node->storage);
        ::new (static_cast<void*>(obj)) T(std::forward<Args>(args)...);
        return obj;
    }

    void deallocate(T* obj) noexcept {
        if (!obj) [[unlikely]] return;

        obj->~T();
        Node* node = reinterpret_cast<Node*>(reinterpret_cast<char*>(obj));
        node->next_free = free_head_;
        free_head_ = node;
        --allocated_count_;
    }

    [[nodiscard]] size_t allocated_count() const noexcept { return allocated_count_; }
    [[nodiscard]] size_t available_count() const noexcept { return Capacity - allocated_count_; }
    [[nodiscard]] constexpr size_t capacity() const noexcept { return Capacity; }

    // RAII Deleter for std::unique_ptr
    struct Deleter {
        ObjectPool* pool{nullptr};
        void operator()(T* ptr) const noexcept {
            if (pool && ptr) {
                pool->deallocate(ptr);
            }
        }
    };

    using Ptr = std::unique_ptr<T, Deleter>;

    template <typename... Args>
    [[nodiscard]] Ptr make_unique(Args&&... args) {
        T* raw = allocate(std::forward<Args>(args)...);
        if (!raw) return Ptr(nullptr, Deleter{this});
        return Ptr(raw, Deleter{this});
    }

private:
    std::unique_ptr<Node[]> arena_;
    Node* free_head_;
    size_t allocated_count_;
};

} // namespace quant::core