code:
// The MIT License (MIT)
//
// 	Copyright (c) 2017-2021 Sergey Makeev
//
// 	Permission is hereby granted, free of charge, to any person obtaining a copy
// 	of this software and associated documentation files (the "Software"), to deal
// 	in the Software without restriction, including without limitation the rights
// 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// 	copies of the Software, and to permit persons to whom the Software is
// 	furnished to do so, subject to the following conditions:
//
//      The above copyright notice and this permission notice shall be included in
// 	all copies or substantial portions of the Software.
//
// 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// 	THE SOFTWARE.
#pragma once

#include <algorithm>
#include <array>
#include <atomic>
#include <cstring>
#include <initializer_list>
#include <memory>
#include <stdint.h>

#if __SANITIZE_ADDRESS__ == 1
# include "sanitizer/asan_interface.h"
#endif

#define SMMALLOC_STATS_SUPPORT

#if __GNUC__ || __INTEL_COMPILER

#define SM_UNLIKELY(expr) __builtin_expect(!!(expr), (0))
#define SM_LIKELY(expr) __builtin_expect(!!(expr), (1))

#else

#define SM_UNLIKELY(expr) (expr)
#define SM_LIKELY(expr) (expr)

#endif

#ifdef _DEBUG
#define SMMALLOC_ENABLE_ASSERTS
#endif

#if defined(_M_X64) || _LP64
#define SMMMALLOC_X64
#define SMM_MAX_CACHE_ITEMS_COUNT (7)
#else
#define SMMMALLOC_X86
#define SMM_MAX_CACHE_ITEMS_COUNT (10)
#endif

#define SMM_CACHE_LINE_SIZE (64)
#define SMM_MAX_BUCKET_COUNT (8)

#define SMMALLOC_UNUSED(x) (void)(x)
#define SMMALLOC_USED_IN_ASSERT(x) (void)(x)

#ifdef _MSC_VER
#define INLINE __forceinline
//#define INLINE inline
#else
//#define INLINE inline __attribute__((always_inline))
#define INLINE inline
#endif

#ifdef _MSC_VER
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE __attribute__((__noinline__))
#endif

#ifdef SMMALLOC_ENABLE_ASSERTS
#include <assert.h>
//#define SM_ASSERT(x) assert(x)
#define SM_ASSERT(cond)                                                                                \
    do                                                                                \
    {                                                                                \
        if (!(cond))                                                                                \
            __debugbreak();                                                                                \
    } while (0)
#else
#define SM_ASSERT(x)
#endif

namespace sm
{
#ifdef SMMALLOC_STATS_SUPPORT
struct AllocatorStats
{
    std::atomic<size_t> cacheHitCount;
    std::atomic<size_t> hitCount;
    std::atomic<size_t> missCount;
    std::atomic<size_t> freeCount;

    AllocatorStats()
    {
        cacheHitCount.store(0);
        hitCount.store(0);
        missCount.store(0);
        freeCount.store(0);
    }
};

#endif

enum CacheWarmupOptions
{
    CACHE_COLD = 0,
    CACHE_WARM = 1,
    CACHE_HOT = 2,
};

namespace internal
{
struct TlsPoolBucket;
}

internal::TlsPoolBucket* GetTlsBucket(size_t index);

INLINE bool IsAligned(size_t v, size_t alignment)
{
    size_t lowBits = v & (alignment - 1);
    return (lowBits == 0);
}

INLINE size_t Align(size_t val, size_t alignment)
{
    SM_ASSERT((alignment & (alignment - 1)) == 0 && "Invalid alignment. Must be power of two.");
    size_t r = (val + (alignment - 1)) & ~(alignment - 1);
    SM_ASSERT(IsAligned(r, alignment) && "Alignment failed.");
    return r;
}

struct GenericAllocator
{
    typedef void* TInstance;

    static TInstance Invalid();
    static bool IsValid(TInstance instance);

    static TInstance Create();
    static void Destroy(TInstance instance);

    static void* Alloc(TInstance instance, size_t bytesCount, size_t alignment);
    static void Free(TInstance instance, void* p);
    static void* Realloc(TInstance instance, void* p, size_t bytesCount, size_t alignment);
    static size_t GetUsableSpace(TInstance instance, void* p);

    struct Deleter
    {
        explicit Deleter(GenericAllocator::TInstance _instance)
            : instance(_instance)
        {
        }

        INLINE void operator()(uint8_t* p) { GenericAllocator::Free(instance, p); }
        GenericAllocator::TInstance instance;
    };
};

class Allocator
{
  public:
    static const size_t kMinValidAlignment = 16;

  private:
    static const size_t kMaxValidAlignment = 16384;

    friend struct internal::TlsPoolBucket;

    INLINE bool IsReadable(void* p) const { return (uintptr_t(p) > kMaxValidAlignment); }

    struct PoolBucket
    {
        union TaggedIndex
        {
            struct
            {
                uint32_t tag;
                uint32_t offset;
            } p;
            uint64_t u;
            static const uint64_t Invalid = UINT64_MAX;
        };

        std::atomic<uint64_t> head;
        std::atomic<uint32_t> globalTag;
        uint8_t* pData;
        uint8_t* pBufferEnd;

#ifdef SMMALLOC_STATS_SUPPORT
        AllocatorStats stats;
#endif
        PoolBucket()
            : head(TaggedIndex::Invalid)
            , globalTag(0)
            , pData(nullptr)
            , pBufferEnd(nullptr)
        {
        }

        void Create(size_t elementSize);

        INLINE void* Alloc()
        {
            uint8_t* p = nullptr;
            TaggedIndex headValue;
            headValue.u = head.load();
            while (true)
            {
                // list is empty, exiting
                if (headValue.u == TaggedIndex::Invalid)
                {
                    return nullptr;
                }

                // get head pointer
                p = (pData + headValue.p.offset);
                // read head->next value
                TaggedIndex nextValue = *((TaggedIndex*)(p));

                /*
                    ABA problem here.

                    pop = Alloc
                    push = Free

                    1. Thread 0 begins a pop and sees "A" as the top, followed by "B".
                    2. Thread 1 begins and completes a pop, returning "A".
                    3. Thread 1 begins and completes a push of "D".
                    4. Thread 1 pushes "A" back onto the stack and completes.
                    5. Thread 0 sees that "A" is on top and returns "A", setting the new top to "B".
                    6. Node D is lost.

                    Visual representation here
                    http://15418.courses.cs.cmu.edu/spring2013/lecture/lockfree/slide_016

                    Solive ABA problem using taggged indexes.
                    Each time when thread push to the stack, unique 'tag' increased.
                    And prevent invalid CAS operation.

                */

                // try to swap head to head->next
                if (head.compare_exchange_strong(headValue.u, nextValue.u))
                {
                    // success
                    break;
                }
                // can't swap values, head is changed (now headValue has new head loaded) try again
            }

            // return memory block memory
            return p;
        }

        INLINE void FreeInterval(void* _pHead, void* _pTail)
        {
            uint8_t* pHead = (uint8_t*)_pHead;
            uint8_t* pTail = (uint8_t*)_pTail;

            // attach already linked list pHead->pTail to lock free list
            // replace head and link tail with old list head

            /*

            Need to use atomic increment here.
            Without atomic increment the operation++ works like this:
            1. uint32_t temp = load(addr);
            2. temp++;
            3. store(addr, temp);

            if one of the thread will superseded before step #3, it will actually roll back the counter (for all other threads) by writing
            the old value into globalTag.
            */
            uint32_t tag = globalTag.fetch_add(1, std::memory_order_relaxed);

            TaggedIndex nodeValue;
            nodeValue.p.offset = (uint32_t)(pHead - pData);
            nodeValue.p.tag = tag;

            TaggedIndex headValue;
            headValue.u = head.load();

            while (true)
            {
                // store current head as node->next
                *((TaggedIndex*)(pTail)) = headValue;

                // try to swap node and head
                if (head.compare_exchange_strong(headValue.u, nodeValue.u))
                {
                    // succes
                    break;
                }
                // can't swap values, head is changed (now headValue has new head loaded) try again
            }
        }

        INLINE bool IsMyAlloc(void* p) const { return (p >= pData && p < pBufferEnd); }
    };

  public:
    void CreateThreadCache(CacheWarmupOptions warmupOptions, std::initializer_list<uint32_t> options);
    void DestroyThreadCache();

  private:
    size_t bucketsCount;
    size_t bucketSizeInBytes;
    uint8_t* pBufferEnd;
    std::array<uint8_t*, SMM_MAX_BUCKET_COUNT> bucketsDataBegin;
    std::array<PoolBucket, SMM_MAX_BUCKET_COUNT> buckets;
    std::unique_ptr<uint8_t, GenericAllocator::Deleter> pBuffer;
    GenericAllocator::TInstance gAllocator;

#ifdef SMMALLOC_STATS_SUPPORT
    std::atomic<size_t> globalMissCount;
#endif

    INLINE void* AllocFromCache(internal::TlsPoolBucket* __restrict _self) const;

    template <bool useCacheL0> INLINE bool ReleaseToCache(internal::TlsPoolBucket* __restrict _self, void* _p);

    INLINE size_t FindBucket(const void* p) const
    {
        // if p is our pointer it located inside pBuffer and pBufferEnd
        // pBuffer and bucketsDataBegin[0] the same pointers
        uintptr_t index = (uintptr_t)p - (uintptr_t)bucketsDataBegin[0];

        // if p is less than pBuffer we get very huge number due to overflow and this is valid result, since bucketIndex checked before use
        // if p is greater than pBufferEnd we get huge number and this is valid result too, since bucketIndex checked before use

        size_t r = (index / bucketSizeInBytes);
        return r;
    }

    INLINE PoolBucket* GetBucketByIndex(size_t bucketIndex)
    {
        if (bucketIndex >= bucketsCount)
        {
            return nullptr;
        }
        return &buckets[bucketIndex];
    }

    INLINE const PoolBucket* GetBucketByIndex(size_t bucketIndex) const
    {
        if (bucketIndex >= bucketsCount)
        {
            return nullptr;
        }
        return &buckets[bucketIndex];
    }

    template <bool enableStatistic> INLINE void* Allocate(size_t _bytesCount, size_t alignment)
    {
        SM_ASSERT(alignment <= kMaxValidAlignment);

        // http://www.cplusplus.com/reference/cstdlib/malloc/
        // If size is zero, the return value depends on the particular library implementation (it may or may not be a null pointer),
        // but the returned pointer shall not be dereferenced.
        if (SM_UNLIKELY(_bytesCount == 0))
        {
            return (void*)alignment;
        }

        size_t bytesCount = (_bytesCount < alignment) ? alignment : _bytesCount;
        size_t bucketIndex = ((bytesCount - 1) >> 4);

        if (bucketIndex < bucketsCount)
        {
            // try to handle allocation using local thread cache
            void* pRes = AllocFromCache(GetTlsBucket(bucketIndex));
            if (pRes)
            {
#ifdef SMMALLOC_STATS_SUPPORT
                if (enableStatistic)
                {
                    buckets[bucketIndex & (SMM_MAX_BUCKET_COUNT-1)].stats.cacheHitCount.fetch_add(1, std::memory_order_relaxed);
                }
#endif
#if __SANITIZE_ADDRESS__ == 1
                    ASAN_UNPOISON_MEMORY_REGION(pRes, Align(_bytesCount, 8));
#endif
                return pRes;
            }
        }

        while (bucketIndex < bucketsCount)
        {
            void* pRes = buckets[bucketIndex].Alloc();
            if (pRes)
            {
#ifdef SMMALLOC_STATS_SUPPORT
                if (enableStatistic)
                {
                    buckets[bucketIndex & (SMM_MAX_BUCKET_COUNT-1)].stats.hitCount.fetch_add(1, std::memory_order_relaxed);
                }
#endif
#if __SANITIZE_ADDRESS__ == 1
                    ASAN_UNPOISON_MEMORY_REGION(pRes, Align(_bytesCount, 8));
#endif
                return pRes;
            }
            else
            {
#ifdef SMMALLOC_STATS_SUPPORT
                if (enableStatistic)
                {
                    buckets[bucketIndex & (SMM_MAX_BUCKET_COUNT-1)].stats.missCount.fetch_add(1, std::memory_order_relaxed);
                }
#endif
            }

            // try next bucket
            bucketIndex++;
        }

#ifdef SMMALLOC_STATS_SUPPORT
        if (enableStatistic)
        {
            globalMissCount.fetch_add(1, std::memory_order_relaxed);
        }
#endif
        // fallback to generic allocator
        return GenericAllocator::Alloc(gAllocator, _bytesCount, alignment);
    }

  public:
    Allocator(GenericAllocator::TInstance allocator);

    void Init(uint32_t bucketsCount, size_t bucketSizeInBytes);

    INLINE void* Alloc(size_t _bytesCount, size_t alignment) { return Allocate<true>(_bytesCount, alignment); }

    INLINE void Free(void* p)
    {
        // Assume that p is the pointer that is allocated by passing the zero size.
        if (SM_UNLIKELY(!IsReadable(p)))
        {
            return;
        }

        size_t bucketIndex = FindBucket(p);
        if (bucketIndex < bucketsCount)
        {
#ifdef SMMALLOC_STATS_SUPPORT
            buckets[bucketIndex].stats.freeCount.fetch_add(1, std::memory_order_relaxed);
#endif

            if (ReleaseToCache<true>(GetTlsBucket(bucketIndex), p))
            {
                return;
            }

            PoolBucket* bucket = &buckets[bucketIndex];
            bucket->FreeInterval(p, p);
#if 0 //__SANITIZE_ADDRESS__ == 1
                ASAN_POISON_MEMORY_REGION(p, GetBucketElementSize(bucketIndex));
#endif
            return;
        }

        // fallback to generic allocator
        GenericAllocator::Free(gAllocator, (uint8_t*)p);
    }

    INLINE void* Realloc(void* p, size_t bytesCount, size_t alignment)
    {
        // Assume that p is the pointer that is allocated by passing the zero size. So no real reallocation required.
        if (!IsReadable(p))
        {
            return Alloc(bytesCount, alignment);
        }

        if (bytesCount == 0)
        {
            Free(p);
            return nullptr;
        }

        size_t bucketIndex = FindBucket(p);
        if (bucketIndex < bucketsCount)
        {
            size_t elementSize = GetBucketElementSize(bucketIndex);
            if (bytesCount <= elementSize)
            {
                // reuse existing memory
#if __SANITIZE_ADDRESS__ == 1
                    ASAN_UNPOISON_MEMORY_REGION(p, Align(bytesCount, 8));
#endif
                return p;
            }

            // alloc new memory block and move memory
            void* p2 = Alloc(bytesCount, alignment);

            // Assume that p is the pointer that is allocated by passing the zero size. No preserve memory conents is requried.
            if (IsReadable(p))
            {
                // move the memory block to the new location
                std::memmove(p2, p, elementSize);
            }

            Free(p);

            return p2;
        }

        if (bytesCount == 0)
        {
            // http://www.cplusplus.com/reference/cstdlib/realloc/
            // If size is zero, the return value depends on the particular library implementation (it may or may not be a null pointer),
            // but the returned pointer shall not be dereferenced.
            if (IsReadable(p))
            {
                GenericAllocator::Free(gAllocator, p);
            }
            return nullptr;
        }

        // check if we need to realloc from generic allocator to smmalloc
        size_t __bytesCount = (bytesCount < alignment) ? alignment : bytesCount;
        size_t __bucketIndex = ((__bytesCount - 1) >> 4);
        if (__bucketIndex < bucketsCount)
        {
            void* p2 = Alloc(bytesCount, alignment);
            // Assume that p is the pointer that is allocated by passing the zero size. No preserve memory conents is requried.
            if (IsReadable(p))
            {
                size_t numBytes = GenericAllocator::GetUsableSpace(gAllocator, p);
                // move the memory block to the new location
                std::memmove(p2, p, std::min(numBytes, bytesCount));
            }

            GenericAllocator::Free(gAllocator, p);
            return p2;
        }

        SM_ASSERT(IsReadable(p));
        return GenericAllocator::Realloc(gAllocator, p, bytesCount, alignment);
    }

    INLINE size_t GetUsableSize(void* p)
    {
        // Assume that p is the pointer that is allocated by passing the zero size.
        if (!IsReadable(p))
        {
            return 0;
        }

        size_t bucketIndex = FindBucket(p);
        if (bucketIndex < bucketsCount)
        {
            size_t elementSize = GetBucketElementSize(bucketIndex);
            return elementSize;
        }

        return GenericAllocator::GetUsableSpace(gAllocator, p);
    }

    INLINE int32_t GetBucketIndex(void* _p)
    {
        if (!IsMyAlloc(_p))
        {
            return -1;
        }

        size_t bucketIndex = FindBucket(_p);
        if (bucketIndex >= bucketsCount)
        {
            return -1;
        }

        return (int32_t)bucketIndex;
    }

    INLINE bool IsMyAlloc(const void* p) const { return (p >= pBuffer.get() && p < pBufferEnd); }

    INLINE size_t GetBucketsCount() const { return bucketsCount; }

    INLINE uint32_t GetBucketElementSize(size_t bucketIndex) const { return (uint32_t)((bucketIndex + 1) * 16); }

    INLINE uint32_t GetBucketElementsCount(size_t bucketIndex) const
    {
        if (bucketIndex >= bucketsCount)
        {
            return 0;
        }

        size_t oneElementSize = GetBucketElementSize(bucketIndex);
        return (uint32_t)(bucketSizeInBytes / oneElementSize);
    }

#ifdef SMMALLOC_STATS_SUPPORT

    size_t GetGlobalMissCount() const { return globalMissCount.load(std::memory_order_relaxed); }

    const AllocatorStats* GetBucketStats(size_t bucketIndex) const
    {
        const PoolBucket* bucket = GetBucketByIndex(bucketIndex);
        if (!bucket)
        {
            return nullptr;
        }
        return &bucket->stats;
    }

#endif

    GenericAllocator::TInstance GetGenericAllocatorInstance() { return gAllocator; }
};

namespace internal
{
struct TlsPoolBucket
{
    uint8_t* pBucketData;           // 8 (4)
    uint32_t* pStorageL1;           // 8 (4)
    Allocator::PoolBucket* pBucket; // 8 (4)

    std::array<uint32_t, SMM_MAX_CACHE_ITEMS_COUNT> storageL0; //

    uint32_t maxElementsCount; // 4
    uint32_t numElementsL1;    // 4
    uint8_t numElementsL0;     // 1

    // sizeof(storageL0) + 33 bytes

    INLINE uint32_t GetElementsCount() const { return numElementsL1 + numElementsL0; }

    void Init(uint32_t* pCacheStack, uint32_t maxElementsNum, CacheWarmupOptions warmupOptions, Allocator* alloc, size_t bucketIndex);
    uint32_t* Destroy();

    INLINE void ReturnL1CacheToMaster(uint32_t count)
    {
        if (count == 0)
        {
            return;
        }

        SM_ASSERT(pBucket != nullptr);
        if (numElementsL1 == 0)
        {
            return;
        }

        count = std::min(count, numElementsL1);

        uint32_t localTag = 0xFFFFFF;
        uint32_t firstElementToReturn = (numElementsL1 - count);
        uint32_t offset = pStorageL1[firstElementToReturn];
        uint8_t* pHead = pBucketData + offset;
        uint8_t* pPrevBlockMemory = pHead;

        for (uint32_t i = (firstElementToReturn + 1); i < numElementsL1; i++, localTag++)
        {
            offset = pStorageL1[i];

            Allocator::PoolBucket::TaggedIndex* pTag = (Allocator::PoolBucket::TaggedIndex*)pPrevBlockMemory;
            pTag->p.tag = localTag;
            pTag->p.offset = offset;

            uint8_t* pBlockMemory = pBucketData + offset;
            pPrevBlockMemory = pBlockMemory;
        }

        uint8_t* pTail = pPrevBlockMemory;
        pBucket->FreeInterval(pHead, pTail);

        numElementsL1 -= count;
    }
};

static_assert(std::is_pod<TlsPoolBucket>::value == true, "TlsPoolBucket must be POD type, stored in TLS");
static_assert(sizeof(TlsPoolBucket) <= 64, "TlsPoolBucket sizeof must be less than CPU cache line");
} // namespace internal

INLINE void* Allocator::AllocFromCache(internal::TlsPoolBucket* __restrict _self) const
{
    if (_self->numElementsL0 > 0)
    {
        SM_ASSERT(_self->pBucketData != nullptr);
        _self->numElementsL0--;
        uint32_t offset = _self->storageL0[_self->numElementsL0];
        return _self->pBucketData + offset;
    }

    if (_self->numElementsL1 > 0)
    {
        SM_ASSERT(_self->pBucketData != nullptr);
        SM_ASSERT(_self->numElementsL0 == 0);
        _self->numElementsL1--;
        uint32_t offset = _self->pStorageL1[_self->numElementsL1];
        return _self->pBucketData + offset;
    }
    return nullptr;
}

template <bool useCacheL0> INLINE bool Allocator::ReleaseToCache(internal::TlsPoolBucket* __restrict _self, void* _p)
{

    if (_self->maxElementsCount == 0)
    {
        // empty cache bucket
        return false;
    }

    SM_ASSERT(_self->pBucket != nullptr);
    SM_ASSERT(_self->pBucketData != nullptr);

    // get offset
    uint8_t* p = (uint8_t*)_p;
    SM_ASSERT(p >= _self->pBucketData && p < _self->pBucket->pBufferEnd);
    uint32_t offset = (uint32_t)(p - _self->pBucketData);

    if (useCacheL0)
    {
        // use L0 storage if available
        if (_self->numElementsL0 < SMM_MAX_CACHE_ITEMS_COUNT)
        {
            _self->storageL0[_self->numElementsL0] = offset;
            _self->numElementsL0++;
            return true;
        }
    }

    if (_self->numElementsL1 < _self->maxElementsCount)
    {
        // use L1 storage if available
        _self->pStorageL1[_self->numElementsL1] = offset;
        _self->numElementsL1++;
        return true;
    }

    //
    // too many elements in cache (return half of the cache to master)
    //    minimizing worst case scenario, cache is full and thread continues to Free a lot of blocks.
    //               and each Free() call leads to an operation with the global lock-free pool

    uint32_t halfOfElements = (_self->numElementsL1 >> 1);
    _self->ReturnL1CacheToMaster(halfOfElements);

    // use L1 storage
    _self->pStorageL1[_self->numElementsL1] = offset;
    _self->numElementsL1++;
    return true;
}

} // namespace sm

#define SMMALLOC_CSTYLE_FUNCS

#ifdef SMMALLOC_CSTYLE_FUNCS

#define SMMALLOC_DLL

#if defined(_WIN32) && defined(SMMALLOC_DLL)
#define SMMALLOC_API __declspec(dllexport)
#else
#define SMMALLOC_API extern
#endif

#ifdef __cplusplus
extern "C"
{
#endif

    //
    // C style api
    //
    ////////////////////////////////////////////////////////////////////////////////

    typedef sm::Allocator* sm_allocator;

    SMMALLOC_API INLINE sm_allocator _sm_allocator_create(uint32_t bucketsCount, size_t bucketSizeInBytes)
    {
        sm::GenericAllocator::TInstance instance = sm::GenericAllocator::Create();
        if (!sm::GenericAllocator::IsValid(instance))
        {
            return nullptr;
        }

        // allocate memory for an allocator
        size_t align = __alignof(sm::Allocator);
        align = sm::Align(align, SMM_CACHE_LINE_SIZE);

        void* pBuffer = sm::GenericAllocator::Alloc(instance, sizeof(sm::Allocator), align);

        // placement new
        sm::Allocator* allocator = new (pBuffer) sm::Allocator(instance);

        // initialize
        allocator->Init(bucketsCount, bucketSizeInBytes);

        return allocator;
    }

    SMMALLOC_API INLINE void _sm_allocator_destroy(sm_allocator allocator)
    {
        if (allocator == nullptr)
        {
            return;
        }

        sm::GenericAllocator::TInstance instance = allocator->GetGenericAllocatorInstance();

        // call dtor
        allocator->~Allocator();

        // free memory
        sm::GenericAllocator::Free(instance, allocator);

        // destroy generic allocator
        sm::GenericAllocator::Destroy(instance);
    }

    SMMALLOC_API INLINE void _sm_allocator_thread_cache_create(sm_allocator allocator, sm::CacheWarmupOptions warmupOptions,
                                                               std::initializer_list<uint32_t> options)
    {
        if (allocator == nullptr)
        {
            return;
        }

        allocator->CreateThreadCache(warmupOptions, options);
    }

    SMMALLOC_API INLINE void _sm_allocator_thread_cache_destroy(sm_allocator allocator)
    {
        if (allocator == nullptr)
        {
            return;
        }

        allocator->DestroyThreadCache();
    }

    SMMALLOC_API INLINE void* _sm_malloc(sm_allocator allocator, size_t bytesCount, size_t alignment)
    {
        return allocator->Alloc(bytesCount, alignment);
    }

    SMMALLOC_API INLINE void _sm_free(sm_allocator allocator, void* p) { return allocator->Free(p); }

    SMMALLOC_API INLINE void* _sm_realloc(sm_allocator allocator, void* p, size_t bytesCount, size_t alignment)
    {
        return allocator->Realloc(p, bytesCount, alignment);
    }

    SMMALLOC_API INLINE size_t _sm_msize(sm_allocator allocator, void* p) { return allocator->GetUsableSize(p); }

    SMMALLOC_API INLINE int32_t _sm_mbucket(sm_allocator allocator, void* p) { return allocator->GetBucketIndex(p); }

#ifdef __cplusplus
}
#endif

#endif

repo_name: orochi/nblood

license: mit


code:
/*
 * softsurface.h
 *  An 8-bit rendering surface that can quickly upscale and blit 8-bit paletted buffers to an external 32-bit screen buffer.
 *
 * Copyright � 2018, Alex Dawson. All rights reserved.
 */

#ifndef SOFTSURFACE_H_
#define SOFTSURFACE_H_

#include "compat.h"

// Initialize the softsurface with the Software renderer's buffer resolution.
// If the Software renderer's resolution and the actual resolution don't match,
//  softsurface will still render at the full size of the screen.
// If a surface already exists, softsurface_destroy() will be automatically called before re-initializing.
// Returns whether or not the softsurface could be successfully initialized.
bool softsurface_initialize(vec2_t bufferResolution,
                            vec2_t destBufferResolution);

// Destroy an existing surface.
void softsurface_destroy();

// Sets the palette to contain the RGBA byte buffer pointed to by pPalette.
// destRedMask/destGreenMask/destBlueMask mask the bits that represent each colour component in the destination buffer's pixel format.
// If the surface is not initialized, the function returns immediately.
void softsurface_setPalette(void* pPalette,
                            uint32_t destRedMask,
                            uint32_t destGreenMask,
                            uint32_t destBlueMask);

// Returns a pointer to the start of the surface's 8-bit pixel buffer
// Returns NULL if the surface is not initialized.
uint8_t* softsurface_getBuffer();

// Returns the resolution of the surface's buffer
vec2_t softsurface_getBufferResolution();

// Returns the resolution of the destination buffer
vec2_t softsurface_getDestinationBufferResolution();

// Blit the surface's pixel buffer to the destination buffer using the palette set with softsurface_setPalette().
// If the surface is not initialized, the function returns immediately.
void softsurface_blitBuffer(uint32_t* destBuffer,
                            uint32_t destBpp);

#endif /* SOFTSURFACE_H_ */


code:
// resource ids
#define WIN_STARTWIN		1000
#define WIN_STARTWINPAGE_CONFIG 2000
#define WIN_STARTWIN_BITMAP	100	// banner bitmap
#define WIN_STARTWIN_TABCTL	101
#define WIN_STARTWIN_CANCEL	IDCANCEL
#define WIN_STARTWIN_START	IDOK

#define WIN_STARTWIN_MESSAGES	104	// output list box

#define RSRC_ICON		100
#define RSRC_BMP		200

// config page
#define IDCFULLSCREEN	100
#define IDC2DVMODE	101
#define IDC3DVMODE	102
#define IDCALWAYSSHOW	103
#define IDCCWD          105


code:
#ifndef texcache_h_
# define texcache_h_

#include "mio.hpp"
#include "polymost.h"
#include "mdsprite.h"

#ifdef __cplusplus
extern "C" {
#endif

#include "vfs.h"

#ifdef USE_OPENGL

#define TEXCACHEMAGIC "LZ41"
#define GLTEXCACHEADSIZ 8192
#define TEXCACHEHASHSIZE 1024

enum texcacherr_t
{
    TEXCACHERR_NOERROR,
    TEXCACHERR_OUTOFMEMORY,  /* unused */
    TEXCACHERR_BUFFERUNDERRUN,
    TEXCACHERR_DEDXT,
    TEXCACHERR_COMPTEX,
    TEXCACHERR_GETTEXLEVEL,
    TEXCACHEERRORS
};

typedef struct texcacheitem_
{
    char    *name;
    int32_t offset;
    int32_t len;

    struct texcacheitem_ *next;
} texcacheindex;

typedef struct {
    mio::mmap_sink rw_mmap;
    buildvfs_FILE  indexFilePtr;
    buildvfs_FILE  dataFilePtr;

    texcacheindex * first;
    texcacheindex * current;
    texcacheindex **entries;

    pthtyp *list[GLTEXCACHEADSIZ];

    hashtable_t hashes;

    int32_t numentries;
    int32_t entrybufsiz;
    bsize_t dataFilePos;
} globaltexcache;

extern globaltexcache texcache;

extern char TEXCACHEFILE[BMAX_PATH];

extern int32_t texcache_enabled(void);
extern void texcache_freeptrs(void);
extern void texcache_syncmemcache(void);
extern void texcache_init(void);
int texcache_loadoffsets(void);
int texcache_readdata(void *outBuf, int32_t len);
extern pthtyp *texcache_fetch(int32_t dapicnum, int32_t dapalnum, int32_t dashade, int32_t dameth);
extern int32_t texcache_loadskin(const texcacheheader *head, int32_t *doalloc, GLuint *glpic, vec2_t *siz);
extern int32_t texcache_loadtile(const texcacheheader *head, int32_t *doalloc, pthtyp *pth);
extern char const * texcache_calcid(char *outbuf, const char *filename, int32_t len, int32_t dameth, char effect);
extern void texcache_prewritetex(texcacheheader *head);
void texcache_postwritetex(char const * cacheid, int32_t offset);
extern void texcache_writetex_fromdriver(char const * cacheid, texcacheheader *head);
extern int texcache_readtexheader(char const * cacheid, texcacheheader *head, int32_t modelp);
extern void texcache_openfiles(void);
extern void texcache_setupmemcache(void);
extern void texcache_checkgarbage(void);
extern void texcache_setupindex(void);

extern voxmodel_t* voxcache_fetchvoxmodel(const char* const cacheid);
extern void voxcache_writevoxmodel(const char* const cacheid, voxmodel_t* vm);

#endif

#ifdef __cplusplus
}
#endif

#endif


code:
/*
 * tilepacker.h
 *  A k-d tree based bin packer that organizes rectangular tiles to fit
 *  neatly into one texture.
 *
 * Copyright � 2018, Alex Dawson. All rights reserved.
 */

#ifndef TILEPACKER_H_
#define TILEPACKER_H_

#define MAXTILESHEETS 64
#define MAXPACKEDTILES (MAXTILES+1)

typedef struct
{
    uint32_t u, v, width, height;
} TileRect;

typedef struct
{
    uint32_t tilesheetID;
    TileRect rect;
} Tile;

// Initialize the specified tilesheet
// tilesheetId must be less than MAXTILESHEETS
// Re-initializing an existing tilesheet will discard all of its contents
void tilepacker_initTilesheet(uint32_t tilesheetID, uint32_t tilesheetWidth, uint32_t tilesheetHeight);

// Adds a tile into sorted collection to be packed
// uid can be any unique key -- picnums are used for indexed colour textures
void tilepacker_addTile(uint32_t tileUID, uint32_t tileWidth, uint32_t tileHeight);

// Packs the sorted collection of tiles that have been added with tilepacker_addTile()
// Returns true if all nodes could be packed succesfully into the specified tilesheet
// tilepacker_pack can be called again with a different tilesheetId to pack the remaining tiles
char tilepacker_pack(uint32_t tilesheetID);

// Discard the rejected tiles so that they will not be carried over to the next call to tilepacker_pack
void tilepacker_discardRejects();

// Sets pOutput to contain the Tile information for a packed tile with the given tileUID
// Returns true if the Tile has been packed, false otherwise
// If pOutput is NULL, the function solely returns whether or not the Tile has been packed
char tilepacker_getTile(uint32_t tileUID, Tile *pOutput);

// Returns true if the Tile with tileUID has been packed, false otherwise
static inline char tilepacker_isTilePacked(uint32_t tileUID)
{
    return tilepacker_getTile(tileUID, NULL);
};

#endif /* TILEPACKER_H_ */


code:
#pragma once

#ifndef timer_h_
#define timer_h_

#include "compat.h"

// for compatibility
#define timerUninit()

enum buildtimertype
{
    TIMER_AUTO = 0,
    TIMER_QPC,
    TIMER_SDL,
    TIMER_RDTSC,
    NUMTIMERS,
};

uint64_t timerGetPerformanceCounter(void);
uint64_t timerGetPerformanceFrequency(void);

int  timerInit(int const tickspersecond);
void timerUpdateClock(void);

int      timerGetClockRate(void);
double   timerGetFractionalTicks(void);
uint32_t timerGetTicks(void);
uint32_t timer120(void);
uint64_t timerGetNanoTicks(void);
uint64_t timerGetNanoTickRate(void);

void (*timerSetCallback(void (*callback)(void)))(void);

#endif /* timer_h_ */


code:
template<typename T>
class TRACKER_NAME__
{
    public:
        T value;

        FORCE_INLINE T *operator&()
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return &this->value;
        }

        FORCE_INLINE T operator++()
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return ++this->value;
        }

        FORCE_INLINE T operator++(int)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value++;
        }

        FORCE_INLINE T operator--()
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return --this->value;
        }

        FORCE_INLINE T operator--(int)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value--;
        }

        template <typename U> FORCE_INLINE T operator=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value = (T)operand;
        }

        template <typename U> FORCE_INLINE T operator+=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value += (T)operand;
        }

        template <typename U> FORCE_INLINE T operator-=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value -= (T)operand;
        }

        template <typename U> FORCE_INLINE T operator*=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value *= (T)operand;
        }

        template <typename U> FORCE_INLINE T operator/=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value /= (T)operand;
        }

        template <typename U> FORCE_INLINE T operator|=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value |= (T)operand;
        }

        template <typename U> FORCE_INLINE T operator&=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value &= (T)operand;
        }

        template <typename U> FORCE_INLINE T operator^=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value ^= (T)operand;
        }

        template <typename U> FORCE_INLINE T operator<<=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value <<= (T)operand;
        }

        template <typename U> FORCE_INLINE T operator>>=(U operand)
        {
            TRACKER_HOOK_((intptr_t) & this->value);
            return this->value >>= (T)operand;
        }

        FORCE_INLINE operator T() const { return this->value; }

        FORCE_INLINE T cast() const { return this->value; }
};

template <typename T> struct is_signed<TRACKER_NAME__<T>>
{
    static constexpr bool value = std::is_signed<T>::value;
};
template <typename T> struct is_unsigned<TRACKER_NAME__<T>>
{
    static constexpr bool value = std::is_unsigned<T>::value;
};


code:
#pragma once

#ifndef vec_h__
#define vec_h__

#include "compat.h"

typedef struct vec2_ {
    int32_t x, y;
#ifdef __cplusplus
    inline bool operator==(struct vec2_ const &c) { return x == c.x && y == c.y; }
    inline bool operator!=(struct vec2_ const &c) { return x != c.x || y != c.y; }

    struct vec2_ &operator+=(const struct vec2_& rhs) { x += rhs.x; y += rhs.y; return *this; }
    struct vec2_ &operator-=(const struct vec2_& rhs) { x -= rhs.x; y -= rhs.y; return *this; }

    const struct vec2_ operator+(const struct vec2_ rhs) const { return { x + rhs.x, y + rhs.y }; }
    const struct vec2_ operator-(const struct vec2_ rhs) const { return { x - rhs.x, y - rhs.y }; }
#endif
} MAY_ALIAS vec2_t;

typedef struct vec2_16_ {
    int16_t x, y;
#ifdef __cplusplus
    inline bool operator==(struct vec2_16_ const &c) { return x == c.x && y == c.y; }
    inline bool operator!=(struct vec2_16_ const &c) { return x != c.x || y != c.y; }

    struct vec2_16_ &operator+=(const struct vec2_16_& rhs) { x += rhs.x; y += rhs.y; return *this; }
    struct vec2_16_ &operator-=(const struct vec2_16_& rhs) { x -= rhs.x; y -= rhs.y; return *this; }

    const struct vec2_16_ operator+(const struct vec2_16_ rhs) const { return { (int16_t)(x + rhs.x), (int16_t)(y + rhs.y) }; }
    const struct vec2_16_ operator-(const struct vec2_16_ rhs) const { return { (int16_t)(x - rhs.x), (int16_t)(y - rhs.y) }; }
#endif // __cplusplus
} MAY_ALIAS vec2_16_t;

typedef struct vec2u_ {
    uint32_t x, y;
#ifdef __cplusplus
    inline bool operator==(struct vec2u_ const &c) { return x == c.x && y == c.y; }
    inline bool operator!=(struct vec2u_ const &c) { return x != c.x || y != c.y; }

    struct vec2u_ &operator+=(const struct vec2u_& rhs) { x += rhs.x; y += rhs.y; return *this; }
    struct vec2u_ &operator-=(const struct vec2u_& rhs) { x -= rhs.x; y -= rhs.y; return *this; }

    const struct vec2u_ operator+(const struct vec2u_ rhs) const { return { x + rhs.x, y + rhs.y }; }
    const struct vec2u_ operator-(const struct vec2u_ rhs) const { return { x - rhs.x, y - rhs.y }; }
#endif // __cplusplus
} vec2u_t;

typedef struct vec2f_ {
    float x, y;
#ifdef __cplusplus
    inline bool operator==(struct vec2f_ const &c) { return x == c.x && y == c.y; }
    inline bool operator!=(struct vec2f_ const &c) { return x != c.x || y != c.y; }

    struct vec2f_ &operator+=(const struct vec2f_& rhs) { x += rhs.x; y += rhs.y; return *this; }
    struct vec2f_ &operator-=(const struct vec2f_& rhs) { x -= rhs.x; y -= rhs.y; return *this; }

    const struct vec2f_ operator+(const struct vec2f_ rhs) const { return { x + rhs.x, y + rhs.y }; }
    const struct vec2f_ operator-(const struct vec2f_ rhs) const { return { x - rhs.x, y - rhs.y }; }
#endif // __cplusplus
} vec2f_t;

typedef struct vec2d_ {
    double x, y;
#ifdef __cplusplus
    inline bool operator==(struct vec2d_ const &c) { return x == c.x && y == c.y; }
    inline bool operator!=(struct vec2d_ const &c) { return x != c.x || y != c.y; }

    struct vec2d_ &operator+=(const struct vec2d_& rhs) { x += rhs.x; y += rhs.y; return *this; }
    struct vec2d_ &operator-=(const struct vec2d_& rhs) { x -= rhs.x; y -= rhs.y; return *this; }

    const struct vec2d_ operator+(const struct vec2d_ rhs) const { return { x + rhs.x, y + rhs.y }; }
    const struct vec2d_ operator-(const struct vec2d_ rhs) const { return { x - rhs.x, y - rhs.y }; }
#endif // __cplusplus
} vec2d_t;

typedef struct vec3_ {
    union {
        struct { int32_t x, y, z; };
        vec2_t xy;
    };
#ifdef __cplusplus
    inline bool operator==(struct vec3_ const &c) { return x == c.x && y == c.y && z == c.z; }
    inline bool operator!=(struct vec3_ const &c) { return x != c.x || y != c.y || z != c.z; }

    struct vec3_ &operator+=(const struct vec3_& rhs) { x += rhs.x; y += rhs.y; z+= rhs.z; return *this; }
    struct vec3_ &operator-=(const struct vec3_& rhs) { x -= rhs.x; y -= rhs.y; z-= rhs.z; return *this; }

    struct vec3_ &operator+=(const struct vec2_& rhs) { xy += rhs; return *this; }
    struct vec3_ &operator-=(const struct vec2_& rhs) { xy -= rhs; return *this; }

    const struct vec3_ operator+(const struct vec3_ rhs) const { return { x + rhs.x, y + rhs.y, z + rhs.z }; }
    const struct vec3_ operator-(const struct vec3_ rhs) const { return { x - rhs.x, y - rhs.y, z + rhs.z }; }
#endif // __cplusplus
} MAY_ALIAS vec3_t;

typedef struct vec3_16_ {
    union {
        struct { int16_t x, y, z; };
        vec2_16_t xy;
    };
#ifdef __cplusplus
    inline bool operator==(struct vec3_16_ const &c) { return x == c.x && y == c.y && z == c.z; }
    inline bool operator!=(struct vec3_16_ const &c) { return x != c.x || y != c.y || z != c.z; }

    struct vec3_16_ &operator+=(const struct vec3_16_& rhs) { x += rhs.x; y += rhs.y; z+= rhs.z; return *this; }
    struct vec3_16_ &operator-=(const struct vec3_16_& rhs) { x -= rhs.x; y -= rhs.y; z-= rhs.z; return *this; }

    struct vec3_16_ &operator+=(const struct vec2_16_& rhs) { xy += rhs; return *this; }
    struct vec3_16_ &operator-=(const struct vec2_16_& rhs) { xy -= rhs; return *this; }

    const struct vec3_16_ operator+(const struct vec3_16_ rhs) const { return { (int16_t)(x + rhs.x), (int16_t)(y + rhs.y), (int16_t)(z + rhs.z) }; }
    const struct vec3_16_ operator-(const struct vec3_16_ rhs) const { return { (int16_t)(x - rhs.x), (int16_t)(y - rhs.y), (int16_t)(z + rhs.z) }; }
#endif // __cplusplus
} MAY_ALIAS vec3_16_t;

typedef struct vec3f_ {
    union {
        struct {
            union { float x, d; };
            union { float y, u; };
            union { float z, v; };
        };
        vec2f_t xy;
    };
#ifdef __cplusplus
    inline bool operator==(struct vec3f_ const &c) { return x == c.x && y == c.y && z == c.z; }
    inline bool operator!=(struct vec3f_ const &c) { return x != c.x || y != c.y || z != c.z; }

    struct vec3f_ &operator+=(const struct vec3f_& rhs) { x += rhs.x; y += rhs.y; z+= rhs.z; return *this; }
    struct vec3f_ &operator-=(const struct vec3f_& rhs) { x -= rhs.x; y -= rhs.y; z-= rhs.z; return *this; }

    struct vec3f_ &operator+=(const struct vec2f_& rhs) { xy += rhs; return *this; }
    struct vec3f_ &operator-=(const struct vec2f_& rhs) { xy -= rhs; return *this; }

    const struct vec3f_ operator+(const struct vec3f_ rhs) const { return { x + rhs.x, y + rhs.y, z + rhs.z }; }
    const struct vec3f_ operator-(const struct vec3f_ rhs) const { return { x - rhs.x, y - rhs.y, z + rhs.z }; }
#endif // __cplusplus
} vec3f_t;

EDUKE32_STATIC_ASSERT(sizeof(vec3f_t) == sizeof(float) * 3);

typedef struct vec3d_ {
    union {
        struct {
            union { double x, d; };
            union { double y, u; };
            union { double z, v; };
        };
        vec2d_t xy;
    };
#ifdef __cplusplus
    inline bool operator==(struct vec3d_ const &c) { return x == c.x && y == c.y && z == c.z; }
    inline bool operator!=(struct vec3d_ const &c) { return x != c.x || y != c.y || z != c.z; }

    struct vec3d_ &operator+=(const struct vec3d_& rhs) { x += rhs.x; y += rhs.y; z+= rhs.z; return *this; }
    struct vec3d_ &operator-=(const struct vec3d_& rhs) { x -= rhs.x; y -= rhs.y; z-= rhs.z; return *this; }

    struct vec3d_ &operator+=(const struct vec2d_& rhs) { xy += rhs; return *this; }
    struct vec3d_ &operator-=(const struct vec2d_& rhs) { xy -= rhs; return *this; }

    const struct vec3d_ operator+(const struct vec3d_ rhs) const { return { x + rhs.x, y + rhs.y, z + rhs.z }; }
    const struct vec3d_ operator-(const struct vec3d_ rhs) const { return { x - rhs.x, y - rhs.y, z + rhs.z }; }
#endif // __cplusplus
} vec3d_t;

EDUKE32_STATIC_ASSERT(sizeof(vec3d_t) == sizeof(double) * 3);

typedef struct vec4_ {
    union {
        struct { int32_t x, y, z, a; };
        vec3_t xyz;
        vec2_t xy;
    };
#ifdef __cplusplus
    inline bool operator==(struct vec4_ const &c) { return x == c.x && y == c.y && z == c.z && a == c.a; }
    inline bool operator!=(struct vec4_ const &c) { return x != c.x || y != c.y || z != c.z || a != c.a; }
    struct vec4_ &operator+=(const struct vec4_& rhs) { x += rhs.x; y += rhs.y; z+= rhs.z; a += rhs.a; return *this; }
    struct vec4_ &operator-=(const struct vec4_& rhs) { x -= rhs.x; y -= rhs.y; z-= rhs.z; a -= rhs.a; return *this; }
#endif // __cplusplus
} vec4_t;

typedef struct vec4f_ {
    union {
        struct {
            union { float x, d; };
            union { float y, u; };
            union { float z, v; };
            float w;
        };
        vec3f_t xyz;
        vec2f_t xy;
    };
#ifdef __cplusplus
    inline bool operator==(struct vec4f_ const &c) { return x == c.x && y == c.y && z == c.z && w == c.w; }
    inline bool operator!=(struct vec4f_ const &c) { return x != c.x || y != c.y || z != c.z || w != c.w; }
    struct vec4f_ &operator+=(const struct vec4f_& rhs) { x += rhs.x; y += rhs.y; z+= rhs.z; w += rhs.w; return *this; }
    struct vec4f_ &operator-=(const struct vec4f_& rhs) { x -= rhs.x; y -= rhs.y; z-= rhs.z; w -= rhs.w; return *this; }
#endif // __cplusplus
} vec4f_t;

#endif // vec_h__