memory_allocator.cpp 4.42 KB
Newer Older
thatPepe's avatar
thatPepe committed
1
2
3
#include "../allocator.hpp"
#include "../utils.hpp"

4
5
6
7
8
9
10
11
12
13
MemoryPool::MemoryPool(size_t initialSize, size_t alignment)
    : _alignment(alignment) {
    // Validate alignment is power of two
    if ((alignment & (alignment - 1)) != 0 || alignment == 0) {
        throw std::invalid_argument("Alignment must be a power of two");
    }

    if (initialSize > 0) {
        allocateNewRegion(initialSize);
    }
thatPepe's avatar
thatPepe committed
14
15
16
17
18
19
20
21
}

MemoryPool::~MemoryPool() {
    for (void *region : _base_regions) {
        RUN_INFINI(infinirtFree(region));
    }
}

22
23
24
25
26
void *MemoryPool::alignPointer(void *ptr) const {
    return reinterpret_cast<void *>(
        (reinterpret_cast<uintptr_t>(ptr) + _alignment - 1) & ~(_alignment - 1));
}

thatPepe's avatar
thatPepe committed
27
void *MemoryPool::alloc(size_t size) {
wooway777's avatar
wooway777 committed
28
29
30
31
    if (size == 0) {
        return nullptr;
    }

32
33
34
35
36
    // Calculate aligned size
    const size_t aligned_size = (size + _alignment - 1) & ~(_alignment - 1);

    // Find the first block with enough space (after alignment)
    auto it = _free_blocks.lower_bound(aligned_size);
thatPepe's avatar
thatPepe committed
37
    if (it == _free_blocks.end()) {
38
39
        allocateNewRegion(aligned_size);
        it = _free_blocks.lower_bound(aligned_size);
thatPepe's avatar
thatPepe committed
40
41
42
43
44
45
46
47
48
49
        if (it == _free_blocks.end()) {
            throw std::bad_alloc();
        }
    }

    auto block_it = it->second;
    Block block = *block_it;
    _free_blocks.erase(it);
    _all_blocks.erase(block_it);

50
51
    // Align the pointer within the block
    void *aligned_ptr = alignPointer(block.ptr);
wooway777's avatar
wooway777 committed
52
    size_t alignment_padding = reinterpret_cast<char *>(aligned_ptr) - reinterpret_cast<char *>(block.ptr);
53
54
55
56
57
58
59
60
61
62
63
64
65

    // Calculate remaining space after allocation
    const size_t remaining = block.size - aligned_size - alignment_padding;

    // Create allocated block
    Block alloc_block(block.base, aligned_ptr, aligned_size, false);
    auto alloc_it = _all_blocks.insert(alloc_block).first;
    _ptr_to_block[aligned_ptr] = alloc_it;

    // Split remaining space if it's large enough
    if (remaining >= _alignment) {
        void *rem_ptr = static_cast<char *>(aligned_ptr) + aligned_size;
        Block rem_block(block.base, rem_ptr, remaining, true);
thatPepe's avatar
thatPepe committed
66
        auto rem_it = _all_blocks.insert(rem_block).first;
67
        _free_blocks.emplace(remaining, rem_it);
thatPepe's avatar
thatPepe committed
68
    } else {
69
70
        // If remaining space is too small, include it in the allocated block
        alloc_block.size += remaining;
thatPepe's avatar
thatPepe committed
71
    }
72
73

    return aligned_ptr;
thatPepe's avatar
thatPepe committed
74
75
76
}

void MemoryPool::release(void *ptr) {
wooway777's avatar
wooway777 committed
77
78
79
80
    if (ptr == nullptr) {
        return;
    }

thatPepe's avatar
thatPepe committed
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    auto it = _ptr_to_block.find(ptr);
    if (it == _ptr_to_block.end()) {
        throw std::runtime_error("Invalid pointer to free");
    }

    auto block_it = it->second;
    Block block = *block_it;
    _all_blocks.erase(block_it);
    block.is_free = true;
    auto new_it = _all_blocks.insert(block).first;
    _ptr_to_block.erase(ptr);
    tryCoalesce(*new_it);
}

void *MemoryPool::allocateNewRegion(size_t size) {
96
    // Allocate exactly the requested size
thatPepe's avatar
thatPepe committed
97
98
99
    void *ptr = nullptr;
    RUN_INFINI(infinirtMalloc(&ptr, size));
    _base_regions.push_back(ptr);
100
101
102

    // Align the pointer within the allocated region
    void *aligned_ptr = alignPointer(ptr);
wooway777's avatar
wooway777 committed
103
    size_t alignment_padding = reinterpret_cast<char *>(aligned_ptr) - reinterpret_cast<char *>(ptr);
104
105
106
    size_t usable_size = size - alignment_padding;

    Block new_block(ptr, aligned_ptr, usable_size, true);
thatPepe's avatar
thatPepe committed
107
    auto it = _all_blocks.insert(new_block).first;
108
109
110
    _free_blocks.emplace(usable_size, it);

    return aligned_ptr;
thatPepe's avatar
thatPepe committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
}

void MemoryPool::tryCoalesce(const Block &block) {
    auto it = _all_blocks.find(block);
    if (it == _all_blocks.end()) {
        return;
    }

    Block merged = *it;
    auto next = std::next(it);
    auto prev = (it == _all_blocks.begin()) ? _all_blocks.end() : std::prev(it);

    _all_blocks.erase(it);
    _free_blocks.erase(merged.size);

    // Coalesce with next
    if (next != _all_blocks.end() && next->is_free && static_cast<char *>(merged.ptr) + merged.size == next->ptr) {
        _free_blocks.erase(next->size);
        merged.size += next->size;
        _all_blocks.erase(next);
    }

    // Coalesce with prev
    if (prev != _all_blocks.end() && prev->is_free && static_cast<char *>(prev->ptr) + prev->size == merged.ptr) {
        _free_blocks.erase(prev->size);
        merged.ptr = prev->ptr;
        merged.size += prev->size;
        merged.base = prev->base;
        _all_blocks.erase(prev);
    }

    merged.is_free = true;
    auto new_it = _all_blocks.insert(merged).first;
    _free_blocks.emplace(merged.size, new_it);
}