summaryrefslogtreecommitdiffhomepage
path: root/src/handle-page.inl
blob: 8a8bd48940e91850474e67060183748324d5d93a (plain)
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 "handle-page.hpp"
#include "compat/assert.hpp"
#include "compat/pretty-function.hpp"

namespace floormat::impl_handle {

template<typename Obj, uint32_t PageSize>
void Page<Obj, PageSize>::do_deallocate(Item<Obj, PageSize>& item)
{
    item.object.~Obj();
}

template<typename Obj, uint32_t PageSize>
Page<Obj, PageSize>::~Page() noexcept
{
    fm_assert(used_count == 0);
}

template<typename Obj, uint32_t PageSize>
Page<Obj, PageSize>::Page(uint32_t start_index):
    used_map{ValueInit, PageSize},
    start_index{start_index},
    first_free{start_index}
{
    fm_assert(size_t{start_index} + size_t{PageSize} <= size_t{1u<<31} && start_index + PageSize > start_index);
    auto val = start_index;
    for (auto i = 0u; i < PageSize; i++)
    {
        auto& o = storage.data()[i];
        o.handle = {val++, 0};
        o.next = val;
    }
}

template<typename Obj, uint32_t PageSize>
template<typename... Xs>
requires requires (Xs&&... xs) {
    Obj{forward<Xs>(xs)...};
}
Item<Obj, PageSize>& Page<Obj, PageSize>::allocate(Xs&&... xs)
{
    fm_assert(used_count < PageSize);
    auto first_freeʹ = first_free - start_index;
    fm_debug_assert(first_freeʹ < PageSize);
    fm_debug_assert(!used_map[first_freeʹ]);
    auto& item = storage[first_freeʹ];
    first_free = item.next;
    used_count++;
    used_map.set(first_freeʹ, true);
    new (&item.object) Obj{ forward<Xs>(xs)... };
    return item;
}

template<typename Obj, uint32_t PageSize>
void Page<Obj, PageSize>::deallocate(Handle<Obj, PageSize> obj)
{
    auto index = obj.index - start_index;
    fm_debug_assert(index < PageSize);
    fm_assert(used_map[index]);
    fm_debug_assert(used_count > 0);
    fm_debug_assert(first_free != (uint32_t)-1);
    auto& item = storage[index];
    auto ctr = item.handle.counter++;
    if (ctr == (uint32_t)-1) [[unlikely]]
        Debug{} << "counter" << FM_PRETTY_FUNCTION << "overflowed";
    fm_assert(obj.counter == ctr);
    item.next = first_free;
    first_free = obj.index;
    used_count--;
    used_map.set(index, false);
    do_deallocate(item);
}

template<typename Obj, uint32_t PageSize>
void Page<Obj, PageSize>::deallocate_all()
{
    for (auto i = 0u; i < PageSize; i++)
        if (used_map[i])
            do_deallocate(storage[i]);
    auto val = start_index;
    for (auto i = 0u; i < PageSize; i++)
    {
        auto& o = storage.data()[i];
        o.handle = {val++, 0};
        o.next = val;
    }
    first_free = start_index;
    used_count = 0;
    used_map = BitArray{ValueInit, PageSize};
}

template<typename Obj, uint32_t PageSize>
bool Page<Obj, PageSize>::is_empty()
{
    return used_count == 0;
}

template<typename Obj, uint32_t PageSize>
bool Page<Obj, PageSize>::is_full()
{
    return used_count == PageSize;
}

} // namespace floormat::impl_handle