2024-08-27 15:39:00 +02:00
|
|
|
#ifndef TURBO_LIST_HPP
|
|
|
|
#define TURBO_LIST_HPP
|
2024-08-27 10:13:29 +02:00
|
|
|
|
2024-08-27 09:32:53 +02:00
|
|
|
#include<cstdint>
|
|
|
|
#include<cstdlib>
|
2024-08-27 10:13:29 +02:00
|
|
|
#include<cassert>
|
2024-08-27 09:32:53 +02:00
|
|
|
|
2024-08-27 11:08:58 +02:00
|
|
|
#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 */
|
|
|
|
|
2024-08-27 17:37:31 +02:00
|
|
|
typedef void*(MallocLike)(size_t);
|
|
|
|
typedef void(FreeLike)(void*);
|
|
|
|
|
|
|
|
template<typename T, MallocLike MALLOC = malloc, FreeLike FREE = free>
|
2024-08-27 09:32:53 +02:00
|
|
|
class TurboList {
|
2024-08-27 15:54:03 +02:00
|
|
|
T *old;
|
|
|
|
T *nex;
|
2024-08-27 09:32:53 +02:00
|
|
|
|
|
|
|
uint32_t mid; // non-inclusive . . . m
|
|
|
|
uint32_t end; // non-inclusive e . . . .
|
|
|
|
|
|
|
|
uint32_t capacity;
|
2024-08-27 11:08:58 +02:00
|
|
|
|
2024-08-30 13:34:06 +02:00
|
|
|
TL_NOINLINE T& grow_and_insert(T elem) noexcept {
|
2024-08-27 11:08:58 +02:00
|
|
|
// assert(mid == 0);
|
2024-08-27 17:37:31 +02:00
|
|
|
if(old) FREE(old);
|
2024-08-27 11:08:58 +02:00
|
|
|
old = nex;
|
|
|
|
mid = end;
|
|
|
|
capacity *= 2;
|
2024-09-03 03:34:49 +02:00
|
|
|
nex = (T *) MALLOC(this->capacity * sizeof(T));
|
2024-08-27 11:08:58 +02:00
|
|
|
|
|
|
|
// Will go into the INSERT code path here
|
2024-08-30 13:34:06 +02:00
|
|
|
return insert(elem);
|
2024-08-27 11:08:58 +02:00
|
|
|
}
|
|
|
|
|
2024-08-27 09:32:53 +02:00
|
|
|
public:
|
2024-08-27 15:39:00 +02:00
|
|
|
inline TurboList(uint32_t initial_size = 0, uint32_t initial_cap = 16) noexcept :
|
2024-08-27 09:32:53 +02:00
|
|
|
old(nullptr),
|
|
|
|
mid(0),
|
2024-08-27 15:39:00 +02:00
|
|
|
end(initial_size),
|
2024-08-27 11:28:46 +02:00
|
|
|
capacity(initial_cap) {
|
2024-08-27 09:32:53 +02:00
|
|
|
|
2024-09-03 03:34:49 +02:00
|
|
|
nex = (T *) MALLOC(this->capacity * sizeof(T));
|
2024-08-27 09:32:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline ~TurboList() noexcept {
|
2024-08-27 17:37:31 +02:00
|
|
|
if(nex) FREE(nex);
|
|
|
|
if(old) FREE(old);
|
2024-08-27 09:32:53 +02:00
|
|
|
}
|
2024-08-27 10:13:29 +02:00
|
|
|
|
2024-08-27 15:54:03 +02:00
|
|
|
inline T& operator[](uint32_t i) noexcept {
|
2024-09-03 03:34:49 +02:00
|
|
|
// This seem to be more often compiled to cmov
|
|
|
|
// branchless conditional codes this way..
|
|
|
|
T *base = (i < mid) ? old : nex;
|
|
|
|
return base[i];
|
2024-08-27 10:13:29 +02:00
|
|
|
}
|
|
|
|
|
2024-08-28 13:16:13 +02:00
|
|
|
/** 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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-30 13:34:06 +02:00
|
|
|
inline T& insert(T elem) noexcept {
|
2024-08-27 15:39:00 +02:00
|
|
|
if(TL_LIKELY(end < capacity)) {
|
|
|
|
|
2024-08-27 10:13:29 +02:00
|
|
|
// INSERT
|
2024-08-27 11:20:20 +02:00
|
|
|
|
|
|
|
/* Same as this:
|
2024-08-27 10:13:29 +02:00
|
|
|
if(mid > 0) {
|
|
|
|
nex[mid - 1] = old[mid - 1];
|
|
|
|
--mid;
|
|
|
|
}
|
2024-08-27 11:20:20 +02:00
|
|
|
*/
|
|
|
|
bool hasmid = (mid > 0);
|
|
|
|
mid -= hasmid;
|
|
|
|
nex[mid] = hasmid ? old[mid] : nex[mid];
|
|
|
|
|
2024-08-30 13:34:06 +02:00
|
|
|
return (nex[end++] = elem);
|
2024-08-27 10:13:29 +02:00
|
|
|
} else {
|
|
|
|
// GROW
|
2024-08-30 13:34:06 +02:00
|
|
|
return grow_and_insert(elem);
|
2024-08-27 10:13:29 +02:00
|
|
|
}
|
|
|
|
}
|
2024-08-27 10:29:55 +02:00
|
|
|
|
2024-08-30 10:30:35 +02:00
|
|
|
template<typename... Args>
|
2024-08-30 13:34:06 +02:00
|
|
|
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>(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>(args)...));
|
|
|
|
}
|
2024-08-30 10:30:35 +02:00
|
|
|
}
|
|
|
|
|
2024-08-30 13:37:25 +02:00
|
|
|
// TODO: finalize() call which memcpy remaining elements of old into nex and then frees old and sets nullptr + mid = 0;
|
|
|
|
|
2024-08-27 10:29:55 +02:00
|
|
|
inline uint32_t size() noexcept {
|
2024-08-30 10:27:54 +02:00
|
|
|
return end;
|
2024-08-27 10:29:55 +02:00
|
|
|
}
|
2024-08-27 09:32:53 +02:00
|
|
|
};
|
2024-08-27 10:13:29 +02:00
|
|
|
|
2024-08-27 15:39:00 +02:00
|
|
|
#endif /* TURBO_LIST_HPP */
|