#ifndef TURBO_LIST_HPP #define TURBO_LIST_HPP #include #include #include #ifndef TL_NOINLINE #define TL_NOINLINE __attribute__((noinline)) #endif /* TL_NOINLINE */ #ifndef TL_LIKELY #define TL_LIKELY(x) __builtin_expect(!!(x), 1) #endif /* TL_LIKELY */ #ifndef TL_UNLIKELY #define TL_UNLIKELY(x) __builtin_expect(!!(x), 0) #endif /* TL_UNLIKELY */ typedef void*(MallocLike)(size_t); typedef void(FreeLike)(void*); template class TurboList { T *old; T *nex; uint32_t mid; // non-inclusive . . . m uint32_t end; // non-inclusive e . . . . uint32_t capacity; TL_NOINLINE T& grow_and_insert(T elem) noexcept { // assert(mid == 0); if(old) FREE(old); old = nex; mid = end; capacity *= 2; nex = (T *) MALLOC(this->capacity * sizeof(T)); // Will go into the INSERT code path here return insert(elem); } public: inline TurboList(uint32_t initial_size = 0, uint32_t initial_cap = 16) noexcept : old(nullptr), mid(0), end(initial_size), capacity(initial_cap) { nex = (T *) MALLOC(this->capacity * sizeof(T)); } inline ~TurboList() noexcept { if(nex) FREE(nex); if(old) FREE(old); } inline T& operator[](uint32_t i) noexcept { // This seem to be more often compiled to cmov // branchless conditional codes this way.. T *base = (i < mid) ? old : nex; return base[i]; } /** This is much faster than operator[] if you do small amounts of work per access */ inline void iterate(void(callback)(T&)) noexcept { // old for(uint32_t i = 0; i < mid; ++i) { callback(old[i]); } // nex for(uint32_t i = mid; i < end; ++i) { callback(nex[i]); } } inline T& insert(T elem) noexcept { if(TL_LIKELY(end < capacity)) { // INSERT /* Same as this: if(mid > 0) { nex[mid - 1] = old[mid - 1]; --mid; } */ bool hasmid = (mid > 0); mid -= hasmid; nex[mid] = hasmid ? old[mid] : nex[mid]; return (nex[end++] = elem); } else { // GROW return grow_and_insert(elem); } } template inline T& emplace(Args&&... args) { if(TL_LIKELY(end < capacity)) { // INSERT /* Same as this: if(mid > 0) { nex[mid - 1] = old[mid - 1]; --mid; } */ bool hasmid = (mid > 0); mid -= hasmid; nex[mid] = hasmid ? old[mid] : nex[mid]; // Placement new return *new (nex + end++) T(std::forward(args)...); } else { // GROW // // Rem.: I just chose this to be less optimized than // it is possible by making a copy and reusing // the existing grow and insert code instead of // writing a new "grow_and_emplace" again. // // This happens rarely so its probably fine and // makes less template instantiations, smaller binary. return grow_and_insert(T(std::forward(args)...)); } } // TODO: finalize() call which memcpy remaining elements of old into nex and then frees old and sets nullptr + mid = 0; inline uint32_t size() noexcept { return end; } }; #endif /* TURBO_LIST_HPP */