263 lines
6.9 KiB
C
263 lines
6.9 KiB
C
|
/**
|
||
|
* @file cyclevector.h
|
||
|
* @author 9exa
|
||
|
* @brief An user-determined-size array where pushing an item removes one from the other end.
|
||
|
* Useful for not having to reallocate memory for rapidly appended contiguious data (i.e. sampling)
|
||
|
* @version 0.1
|
||
|
* @date 2023-02-21
|
||
|
*
|
||
|
* @copyright Copyright (c) 2023
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#ifndef MENGA_CYCLE_QUEUE
|
||
|
#define MENGA_CYCLE_QUEUE
|
||
|
|
||
|
#include <cstdint>
|
||
|
#include <vector>
|
||
|
#include <cstring>
|
||
|
#include <iostream>
|
||
|
#include "mengumath.h"
|
||
|
|
||
|
namespace Mengu {
|
||
|
|
||
|
template <class T>
|
||
|
class CycleQueue {
|
||
|
private:
|
||
|
T *_data = nullptr;
|
||
|
uint32_t _size = 0;
|
||
|
// what part of the data array is the front of the queue and first to be replaced on a push_back()
|
||
|
uint32_t _front = 0;
|
||
|
uint32_t _capacity = 0;
|
||
|
|
||
|
inline uint32_t start_inc_down1() {
|
||
|
// assert(size != 0)
|
||
|
// equiv to posmod (_front + _size - 1) % _size but faster?????
|
||
|
return _front == 0 ? _size - 1 : _front - 1;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
CycleQueue() {}
|
||
|
CycleQueue(uint32_t size) {
|
||
|
if (size > 0) {
|
||
|
resize(size);
|
||
|
}
|
||
|
}
|
||
|
// implement "copy" so they don't share the same data array
|
||
|
CycleQueue(const CycleQueue &from) {
|
||
|
_capacity = from._capacity;
|
||
|
_data = new T[_capacity];
|
||
|
std::cout << "from something\n";
|
||
|
memcpy(_data, from._data, _capacity * sizeof(T));
|
||
|
|
||
|
_size = from._size;
|
||
|
_front = from._front;
|
||
|
}
|
||
|
|
||
|
~CycleQueue() {
|
||
|
delete[] _data;
|
||
|
}
|
||
|
|
||
|
inline uint32_t size() const {
|
||
|
return _size;
|
||
|
}
|
||
|
|
||
|
|
||
|
inline void push_back(const T &x) {
|
||
|
if (_size == 0) return;
|
||
|
_data[_front] = x;
|
||
|
_front = (_front + 1) % _size;
|
||
|
}
|
||
|
|
||
|
inline void push_front(const T &x) {
|
||
|
if (_size == 0) return;
|
||
|
_front = (_front == 0) ? _size - 1 : _front - 1;
|
||
|
_data[_front] = x;
|
||
|
}
|
||
|
|
||
|
void resize(const uint32_t &new_size) {
|
||
|
if (_capacity < new_size) {
|
||
|
uint32_t new_cap = MAX(_capacity, 1);
|
||
|
while (new_cap < new_size) {
|
||
|
new_cap = new_cap << 1; // if you leave out the new_cap = the optimizer just skips this loop.
|
||
|
// Which makes this infinite loop bug hard to spot
|
||
|
|
||
|
}
|
||
|
reserve(new_cap);
|
||
|
} else if (new_size < _size) {
|
||
|
//move element of an array such that those at the front are removed
|
||
|
uint32_t shift;
|
||
|
uint32_t i;
|
||
|
if (_front <= new_size) {
|
||
|
// shift elements after _front down
|
||
|
shift = _size - new_size;
|
||
|
i = _front;
|
||
|
}
|
||
|
else {
|
||
|
// shift elements before _front up
|
||
|
shift = _front - new_size;
|
||
|
i = 0;
|
||
|
_front = 0;
|
||
|
}
|
||
|
for (; i < new_size; i++) {
|
||
|
_data[i] = _data[i + shift];
|
||
|
}
|
||
|
|
||
|
// set the deleted slots to be ready for future resizes
|
||
|
for (; i < _size; i++) {
|
||
|
_data[i] = T();
|
||
|
}
|
||
|
}
|
||
|
else { // (new_size > size) but reserve() has yet to initialize values
|
||
|
uint32_t i = _size;
|
||
|
for (; i < _front; i++) {
|
||
|
_data[(i + _size) % new_size] = _data[i];
|
||
|
_data[i] = T();
|
||
|
}
|
||
|
}
|
||
|
_size = new_size;
|
||
|
}
|
||
|
|
||
|
inline uint32_t capacity() const {
|
||
|
return _capacity;
|
||
|
}
|
||
|
|
||
|
const T *data() const {
|
||
|
return _data;
|
||
|
}
|
||
|
|
||
|
void reserve(const uint32_t &new_cap) {
|
||
|
if (_capacity < new_cap) {
|
||
|
T *new_data = new T[new_cap];
|
||
|
// copy and initialise new array
|
||
|
uint32_t i = 0;
|
||
|
if (_data != nullptr) {
|
||
|
for (; i < _capacity; i++) {
|
||
|
new_data[i] = std::move(_data[i]);
|
||
|
}
|
||
|
delete[] _data;
|
||
|
|
||
|
}
|
||
|
|
||
|
for (; i< new_cap; i++) {
|
||
|
new_data[i] = T();
|
||
|
}
|
||
|
|
||
|
_data = new_data;
|
||
|
_capacity = new_cap;
|
||
|
if (_size > _capacity) {
|
||
|
resize(_capacity);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// rotates the data array so that ir begins with _friont
|
||
|
void make_contiguous() {
|
||
|
T *new_data = new T[_capacity];
|
||
|
|
||
|
uint32_t i = 0;
|
||
|
|
||
|
if (_data != nullptr) {
|
||
|
for (; i < _size; i++) {
|
||
|
new_data[i] = std::move(_data[(i + _front) % _size]);
|
||
|
}
|
||
|
delete[] _data;
|
||
|
}
|
||
|
|
||
|
for (; i< _capacity; i++) {
|
||
|
new_data[i] = T();
|
||
|
}
|
||
|
|
||
|
_data = new_data;
|
||
|
|
||
|
}
|
||
|
|
||
|
void set(int i, const T &item) {
|
||
|
if (i > _size) {
|
||
|
std::cout << "tried to set item " << i << "on CycleQueue of size i. Out of range"<< std::endl;
|
||
|
}
|
||
|
_data[posmod(i +_front, _size)] = item;
|
||
|
}
|
||
|
|
||
|
const T get(int i) const {
|
||
|
if (i > _size) {
|
||
|
std::cout << "tried to get item " << i << "on CycleQueue of size i. Out of range"<< std::endl;
|
||
|
}
|
||
|
return _data[posmod(i + _front, _size)];
|
||
|
}
|
||
|
|
||
|
// just moves the front of the Ccle queue by an amount
|
||
|
void rotate(int by) {
|
||
|
_front = posmod(_front + by, _size);
|
||
|
}
|
||
|
|
||
|
//// Operators
|
||
|
inline T &operator[](int i) {
|
||
|
return _data[posmod(i + _front, _size)];
|
||
|
}
|
||
|
|
||
|
inline const T &operator[](int i) const {
|
||
|
// std::cout << (i + _front) % _size << std::endl;
|
||
|
return _data[posmod(i + _front, _size)];
|
||
|
}
|
||
|
|
||
|
CycleQueue &operator=(const CycleQueue &from) {
|
||
|
if (from._size != _size) {
|
||
|
resize(from._size);
|
||
|
}
|
||
|
_front = from._front;
|
||
|
memcpy(_data, from._data, _size);
|
||
|
|
||
|
return *this;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
//// Conversions
|
||
|
// converts the first 'size' items into a contiguous array. -1 does the whole queue
|
||
|
T* to_array(T *out, int size = -1) const {
|
||
|
if (size == -1) {
|
||
|
size = _size;
|
||
|
}
|
||
|
for (uint32_t i = 0; i < size; i++) {
|
||
|
out[i] = _data[(i + _front) % _size];
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
// converts the first 'size' items into a vector. -1 does the whole queue
|
||
|
std::vector<T> to_vector(int size = -1) const {
|
||
|
if (size == -1) {
|
||
|
size = _size;
|
||
|
}
|
||
|
|
||
|
std::vector<T> out;
|
||
|
out.resize(size);
|
||
|
for (uint32_t i = 0; i < size; i++) {
|
||
|
out[i] = _data[(i + _front) % _size];
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template<typename T>
|
||
|
std::string to_string(const CycleQueue<T> &cq) {
|
||
|
using std::to_string;
|
||
|
std::string out_string;
|
||
|
|
||
|
if (cq.size() == 0) {
|
||
|
return out_string;
|
||
|
}
|
||
|
|
||
|
out_string.reserve(to_string(cq[0]).size() * cq.size());
|
||
|
for (uint32_t i = 0; i < cq.size(); i++) {
|
||
|
out_string += to_string(cq[i]);
|
||
|
}
|
||
|
return out_string;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
#endif
|