TeaSpeakLibrary/src/protocol/ringbuffer.h

225 lines
6.1 KiB
C++

#pragma once
#include <cstddef>
#include <cstdint>
#include <cassert>
#include <utility>
#include <memory>
#include <mutex>
namespace ts {
namespace protocol {
template <typename T>
struct RingEntry {
bool flag_set = false;
T entry{};
};
template <typename E, size_t capacity_t, typename size_type>
class RingBuffer {
public:
static constexpr size_type _max_index = ~((size_type) 0);
RingBuffer() : _capacity(capacity_t) {
this->_ring_index_full = 0;
this->_ring_base_index = 0;
}
inline size_type current_index() { return this->_ring_index; }
/**
* @param index
* @return -1 underflow | 0 success | 1 overflow
*/
inline int accept_index(size_type index) {
size_t relative_index;
if(this->calculate_index(index, relative_index))
return 0;
if(index < this->current_index())
return -1;
return 1;
}
inline bool index_set(size_type index) {
size_t relative_index = 0;
if(!this->calculate_index(index, relative_index))
return false;
return this->index(relative_index).flag_set;
}
inline E& index_value(size_type index) {
size_t relative_index = 0;
if(!this->calculate_index(index, relative_index))
return {};
return this->index(relative_index).entry;
}
inline size_t current_slot() { return this->_ring_base_index; }
inline bool slot_set(size_t slot) {
return this->index(slot).flag_set;
}
inline E& slot_value(size_t slot) {
return this->index(slot).entry;
}
inline bool front_set() { return this->_slots[this->_ring_base_index].flag_set; }
inline E pop_front() {
auto& slot = this->_slots[this->_ring_base_index];
slot.flag_set = false;
auto entry = std::move(slot.entry);
this->_ring_base_index += 1;
this->_ring_index += 1;
if(this->_ring_base_index >= this->_capacity)
this->_ring_base_index -= this->_capacity;
return entry;
}
inline void push_front(E&& entry) {
/* go to the back of the ring and set this as front */
if(this->_ring_base_index == 0)
this->_ring_base_index = (size_type) this->_capacity;
this->_ring_base_index -= 1;
this->_ring_index -= 1;
auto& slot = this->_slots[this->_ring_base_index];
slot.entry = std::forward<E>(entry);
slot.flag_set = 1;
}
inline bool push_back(E&& entry) {
size_t count = 0;
size_t index = this->_ring_base_index;
while(count < this->_capacity) {
if(index >= this->_capacity)
index -= this->_capacity;
auto& slot = this->_slots[index];
if(slot.flag_set) {
count++;
index++;
continue;
}
slot.entry = std::forward<E>(entry);
slot.flag_set = 1;
break;
}
return count != this->_capacity;
}
inline void set_full_index_to(size_type index) {
if(index > this->_ring_index)
this->_ring_index = index;
else if(index < 100 && this->_ring_index > std::numeric_limits<size_type>::max() - 100) {
this->_ring_index += 200; /* let the index overflow into the generation counter */
this->_ring_index = index; /* set the lover (16) bytes */
}
}
inline bool insert_index(size_type index, E&& entry) {
size_t relative_index = 0;
if(!this->calculate_index(index, relative_index))
return false;
auto& slot = this->index(relative_index);
if(slot.flag_set)
return false;
slot.entry = std::forward<E>(entry);
slot.flag_set = true;
return true;
}
inline size_t capacity() { return this->_capacity; }
inline void clear() {
this->_ring_base_index = 0;
for(RingEntry<E>& element : this->_slots) {
element.flag_set = false;
(void) std::move(element.entry);
}
}
inline void reset() {
this->clear();
this->_ring_index_full = 0;
}
protected:
size_t _capacity;
size_t _ring_base_index; /* index of slot 0 */
union {
uint64_t _ring_index_full;
struct {
static_assert(8 - sizeof(size_type) > 0, "Invalid size type!");
uint8_t padding[8 - sizeof(size_type)];
size_type _ring_index; /* index of the insert index | overflow is welcome here */
};
};
std::array<RingEntry<E>, capacity_t> _slots;
inline RingEntry<E>& index(size_t relative_index) {
assert(relative_index < this->_capacity);
auto index = this->_ring_base_index + relative_index;
if(index >= this->_capacity)
index -= this->_capacity;
return this->_slots[index];
}
inline bool calculate_index(size_type index, size_t& relative_index) {
if(this->_ring_index > index) { /* test if index is an overflow of the counter */
if(index >= (size_type) (this->_ring_index + this->_capacity)) /* not anymore in bounds */
return false;
else
relative_index = index + (_max_index - this->_ring_index + 1);
if(relative_index >= this->_capacity)
return false;
} else if(this->_ring_index < index) {
/* check upper bounds */
relative_index = index - this->_ring_index;
if(relative_index >= this->_capacity)
return false;
} else {
/* index is equal, do nothing */
relative_index = 0;
}
return true;
}
};
template <typename E, uint16_t SIZE = 32, typename PTR_TYPE = std::shared_ptr<E>>
class PacketRingBuffer : public RingBuffer<PTR_TYPE, SIZE, uint16_t> {
public:
std::recursive_timed_mutex buffer_lock;
std::recursive_timed_mutex execute_lock;
inline uint16_t generation(uint16_t packet_id) {
size_t relative_index = 0;
if(!this->calculate_index(packet_id, relative_index))
relative_index = 0; /* okey we dont give a fuck */
return ((this->_ring_index_full + relative_index) >> 16) & 0xFFFF;
}
inline void set_generation(uint16_t value) {
this->_ring_index_full = (value << 16) | (this->_ring_index_full & 0xFFFF);
}
inline void set_generation_packet(uint16_t generation, uint16_t packet) {
this->_ring_index_full = (generation << 16) | (packet & 0xFFFF);
}
inline uint64_t full_index() { return this->_ring_index_full; }
};
}
}