xaizek / tos (License: GPLv3 only) (since 2018-12-07)
This is an alternative version of sources presented as part of Write Your Own OS video tutorial by Viktor Engelmann.
<root> / src / memorymanagement.cpp (01835d26862428312939d4ef415b73ef4b92d7bf) (2,878B) (mode 100644) [raw]
#include "memorymanagement.hpp"

#include <cstdint>

#include <new>

#include "assert.hpp"

struct MemoryManager::MemoryChunk
{
    MemoryChunk *next;
    MemoryChunk *prev;
    bool allocated;
    std::size_t size;
    std::uint8_t data[];
};

static MemoryManager *activeMemoryManager = nullptr;

MemoryManager::MemoryManager(void *area, std::size_t size)
{
    activeMemoryManager = this;

    if (size < sizeof(MemoryChunk)) {
        head = nullptr;
        return;
    }

    head = new(area) MemoryChunk;

    head->allocated = false;
    head->prev = nullptr;
    head->next = nullptr;
    head->size = size - sizeof(MemoryChunk);
}

MemoryManager::~MemoryManager()
{
    if (activeMemoryManager == this) {
        activeMemoryManager = nullptr;
    }
}

void *
MemoryManager::malloc(std::size_t size)
{
    MemoryChunk *result = nullptr;

    for (MemoryChunk *chunk = head; chunk != nullptr; chunk = chunk->next) {
        if (chunk->size >= size && !chunk->allocated) {
            result = chunk;
            break;
        }
    }

    if (result == nullptr) {
        return nullptr;
    }

    if (result->size >= size + sizeof(MemoryChunk) + 1) {
        MemoryChunk *tail = new(result->data + size) MemoryChunk;

        tail->allocated = false;
        tail->size = result->size - size - sizeof(MemoryChunk);
        tail->prev = result;
        tail->next = result->next;
        if (tail->next != nullptr) {
            tail->next->prev = tail;
        }

        result->size = size;
        result->next = tail;
    }

    result->allocated = true;
    return result->data;
}

void
MemoryManager::free(void *ptr)
{
    MemoryChunk *chunk = static_cast<MemoryChunk *>(ptr) - 1;

    chunk->allocated = false;

    if (chunk->prev != nullptr && !chunk->prev->allocated) {
        chunk->prev->next = chunk->next;
        chunk->prev->size += chunk->size + sizeof(MemoryChunk);
        if (chunk->next != nullptr) {
            chunk->next->prev = chunk->prev;
        }

        chunk = chunk->prev;
    }

    if (chunk->next != nullptr && !chunk->next->allocated) {
        chunk->size += chunk->next->size + sizeof(MemoryChunk);
        chunk->next = chunk->next->next;
        if (chunk->next != nullptr) {
            chunk->next->prev = chunk;
        }
    }
}

void *
operator new(std::size_t size)
{
    kassert(activeMemoryManager != nullptr, "Memory management isn't ready!");
    return activeMemoryManager->malloc(size);
}

void *
operator new[](std::size_t size)
{
    kassert(activeMemoryManager != nullptr, "Memory management isn't ready!");
    return activeMemoryManager->malloc(size);
}

void
operator delete(void *ptr)
{
    if (activeMemoryManager != nullptr) {
        activeMemoryManager->free(ptr);
    }
}

void
operator delete[](void *ptr)
{
    if (activeMemoryManager != nullptr) {
        activeMemoryManager->free(ptr);
    }
}
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/tos

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/tos

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master