#ifndef POOL_HPP_
#define POOL_HPP_
#include "common.hpp"
#ifndef NDEBUG
#include <valgrind/memcheck.h>
#endif

#define MAX_TPOOL_SPARE 1000


template <class T> class TPool {
    private:
        struct tpool_node_s {
            struct tpool_node_s* next;
            T data;
        };

        tpool_node_s* head; ///< spare ones
        unsigned size; ///< only needed for upper spare limit

    public:
        TPool();
        ~TPool();
        static INLINE size_t len() { return sizeof(T); }

        void push(T*);
        T* pop();
};


template <class T> TPool<T>::TPool(): head(NULL), size(0) {
}


template <class T> TPool<T>::~TPool() {
    tpool_node_s* node;
    while ((node = head) != NULL) {
        head = head->next;
        free(node);
        size--;
    }
}


template <class T> void TPool<T>::push(T* data) {
    tpool_node_s* node = (tpool_node_s*)(((char*)data) - (sizeof(tpool_node_s*)));
    if (size >= MAX_TPOOL_SPARE) {
        free(node);
    } else {
#ifndef NDEBUG
        VALGRIND_MAKE_MEM_UNDEFINED(data, sizeof(T));
#endif
        node->next = head;
        head = node;
        size++;
    }
}


template <class T> T* TPool<T>::pop() {
    tpool_node_s* node;
    if (head) {
        node = head;
        head = head->next;
        size--;
    } else {
        node = (tpool_node_s*)malloc(sizeof(tpool_node_s));
    }
    return &node->data;
}


#endif