#ifndef GRAPHICS_LRULIST_H #define GRAPHICS_LRULIST_H #include #include /* Implementation details: - The first entry has the previous index pointing to itself to simplify eviction of the last entry. - Invalidation is simply done by incrementing the entry's epoch. This is trivially safe. */ template class LruList final { private: struct Entry { unsigned previousIndex, nextIndex; unsigned epoch, lastUsed; T data; }; std::vector pool{1 << 10}; std::size_t size = 0; unsigned headIndex = 0, tailIndex = 0; unsigned currentEpoch = 0; public: struct Handle { unsigned index; unsigned epoch; }; LruList(); void tick(); Handle add(T data); bool isAlive(Handle handle); bool ping(Handle handle); void remove(Handle handle); void evictLast(); T* getData(Handle handle); const T* getData(Handle handle) const; T* getLast(); const T* getLast() const; unsigned getLastEntryAge() const; }; template LruList::LruList() { const unsigned poolSize = static_cast(pool.size()); for (unsigned i = 0; i != poolSize; ++i) pool[i].nextIndex = i + 1; } template void LruList::tick() { ++currentEpoch; } template typename LruList::Handle LruList::add(const T data) { unsigned poolSize = static_cast(pool.size()); if (size == poolSize) { poolSize <<= 1; pool.resize(poolSize); for (unsigned i = static_cast(size); i != poolSize; ++i) pool[i].nextIndex = i + 1; pool[tailIndex].nextIndex = static_cast(size); } if (size == 0) { Entry &newEntry = pool[headIndex]; newEntry.previousIndex = headIndex; newEntry.epoch = currentEpoch; newEntry.lastUsed = currentEpoch; newEntry.data = data; size = 1; // No need to update head and tail indices here, they are already correct. return {headIndex, currentEpoch}; } Entry &tail = pool[tailIndex]; const unsigned newIndex = tail.nextIndex; Entry &newEntry = pool[newIndex]; tail.nextIndex = newEntry.nextIndex; newEntry = {newIndex, headIndex, currentEpoch, currentEpoch, data}; pool[headIndex].previousIndex = newIndex; headIndex = newIndex; ++size; return {newIndex, currentEpoch}; } template bool LruList::isAlive(const Handle handle) { return pool[handle.index] == handle.epoch; } template bool LruList::ping(const Handle handle) { Entry &entry = pool[handle.index]; if (entry.epoch != handle.epoch) return false; if (entry.lastUsed == currentEpoch) return true; entry.lastUsed = currentEpoch; if (handle.index == headIndex) return true; pool[entry.previousIndex].nextIndex = entry.nextIndex; if (handle.index == tailIndex) tailIndex = entry.previousIndex; else pool[entry.nextIndex].previousIndex = entry.previousIndex; pool[headIndex].previousIndex = handle.index; entry.previousIndex = handle.index; entry.nextIndex = headIndex; headIndex = handle.index; return true; } template void LruList::remove(const Handle handle) { Entry &entry = pool[handle.index]; if (entry.epoch != handle.epoch) return; ++entry.epoch; if (size == 1) { size = 0; return; } if (handle.index == headIndex) { Entry &tail = pool[tailIndex]; entry.nextIndex = tail.nextIndex; tail.nextIndex = headIndex; headIndex = entry.nextIndex; } else if (handle.index == tailIndex) { tailIndex = entry.previousIndex; } else { pool[entry.previousIndex].nextIndex = entry.nextIndex; pool[entry.nextIndex].previousIndex = entry.previousIndex; Entry &tail = pool[tailIndex]; entry.nextIndex = tail.nextIndex; tail.nextIndex = handle.index; } --size; } template void LruList::evictLast() { if (size == 0) return; Entry &entry = pool[tailIndex]; ++entry.epoch; tailIndex = entry.previousIndex; --size; } template T* LruList::getData(const Handle handle) { Entry &entry = pool[handle.index]; return entry.epoch == handle.epoch ? &entry.data : nullptr; } template const T* LruList::getData(const Handle handle) const { Entry &entry = pool[handle.index]; return entry.epoch == handle.epoch ? &entry.data : nullptr; } template T* LruList::getLast() { return size == 0 ? nullptr : &pool[tailIndex].data; } template const T* LruList::getLast() const { return size == 0 ? nullptr : &pool[tailIndex].data; } template unsigned LruList::getLastEntryAge() const { return size == 0 ? 0 : currentEpoch - pool[tailIndex].lastUsed; } #endif // GRAPHICS_LRULIST_H