root/headers/private/util/BumpAllocator.h
/*
 * Copyright 2024, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */
#ifndef _BUMP_ALLOCATOR_H
#define _BUMP_ALLOCATOR_H


#include <stdlib.h>

#include <OS.h>
#include <SupportDefs.h>


template<size_t InlineDataSize = (16 * sizeof(void*)), size_t SlabSize = 4096>
class BumpAllocator {
public:
        BumpAllocator()
                :
                fCurrentSlab((Slab*)fInlineData)
        {
                fCurrentSlab->previous = NULL;
                fCurrentSlab->total = sizeof(fInlineData) - sizeof(Slab);
                fCurrentSlab->remaining = fCurrentSlab->total;
        }

        ~BumpAllocator()
        {
                if (fCurrentSlab != (Slab*)fInlineData)
                        Free(NULL);

                if (!IsEmpty())
                        debugger("BumpAllocator: deleted with allocations still active!");
        }

        bool IsEmpty() const
        {
                return fCurrentSlab == (Slab*)fInlineData
                        && fCurrentSlab->remaining == fCurrentSlab->total;
        }

        void* Allocate(size_t _size)
        {
                const size_t size = _size + sizeof(Allocation);
                if (size > SlabSize)
                        debugger("BumpAllocator: can't allocate larger than the slab size");
                if (fCurrentSlab->remaining < size) {
                        // We need a new slab.
                        Slab* newSlab = (Slab*)malloc(SlabSize);
                        if (newSlab == NULL)
                                return NULL;

                        newSlab->previous = fCurrentSlab;
                        newSlab->total = SlabSize - sizeof(Slab);
                        newSlab->remaining = newSlab->total;
                        fCurrentSlab = newSlab;
                }

                fCurrentSlab->remaining -= size;
                uint8* pointer = fCurrentSlab->data + fCurrentSlab->remaining;

                Allocation* allocation = (Allocation*)pointer;
                allocation->size = size;
                return allocation->data;
        }

        void Free(void* _pointer)
        {
                if (fCurrentSlab->remaining == fCurrentSlab->total) {
                        // Free the current slab.
                        Slab* previous = fCurrentSlab->previous;
                        free(fCurrentSlab);
                        fCurrentSlab = previous;
                }
                if (_pointer == NULL)
                        return;

                Allocation* allocation = (((Allocation*)_pointer) - 1);

                // This needs to be the last thing allocated.
                uint8* last = fCurrentSlab->data + fCurrentSlab->remaining;
                if ((uint8*)allocation != last) {
                        debugger("BumpAllocator: out-of-order free");
                        return;
                }

                fCurrentSlab->remaining += allocation->size;
        }

private:
#if __cplusplus >= 201103L
#       define FLA_SIZE
#else
#       define FLA_SIZE 0
#endif
        struct Slab {
                Slab*           previous;
                uint32          total;
                uint32          remaining;
                uint8           data[FLA_SIZE];
        };
        struct Allocation {
                uint32          size;   /*!< includes sizeof(Allocation) */
                uint32          _pad;
                uint8           data[FLA_SIZE];
#undef FLA_SIZE
        };
        Slab*                   fCurrentSlab;
        uint8                   fInlineData[InlineDataSize - sizeof(Slab*)];
};

#endif  /* _BUMP_ALLOCATOR_H */