mirror-chatterino2/src/messages/limitedqueue.hpp
Rasmus Karlsson 6ea3a1df08 Switch some c-style includes to c++-style includes (i.e. stdint.h to
cstdint)

Make MessageElement to a class to fit better with the derived classes.
Make MessageLayoutElement to a class to fit better with the derived
classes.

Remove virtual from override functions

Replace all instances of boost::signals2 with pajlada::Signals. This
lets us properly use clang code model to check for issues.

Add missing virtual destructor to AbstractIrcServer
Add missing virtual destructor to MessageLayoutElement

Remove unused "connectedConnection" connection in TwitchChannel

Fix typo in TrimChannelName function
Fix typo in MessageParseArgs

Replace some raw pointers with unique pointers where it made more sense.
This allowed us to remove some manually written destructors whose only
purpose was to delete that raw pointer.

Reformat: Add namespace comments
Reformat: Add empty empty lines between main namespace beginning and end
Reformat: Re-order includes
Reformat: Fix some includes that used quotes where they should use angle
brackets
Reformat: Replace some typedef's with using's

Filter out more useless warnings
2018-04-11 22:54:34 +02:00

272 lines
7.9 KiB
C++

#pragma once
#include "messages/limitedqueuesnapshot.hpp"
#include <QDebug>
#include <memory>
#include <mutex>
#include <vector>
namespace chatterino {
namespace messages {
//
// Warning:
// - this class is so overengineered it's not even funny anymore
//
// Explanation:
// - messages can be appended until 'limit' is reached
// - when the limit is reached for every message added one will be removed at the start
// - messages can only be added to the start when there is space for them,
// trying to add messages to the start when it's full will not add them
// - you are able to get a "Snapshot" which captures the state of this object
// - adding items to this class does not change the "items" of the snapshot
//
template <typename T>
class LimitedQueue
{
protected:
typedef std::shared_ptr<std::vector<T>> Chunk;
typedef std::shared_ptr<std::vector<Chunk>> ChunkVector;
public:
LimitedQueue(int _limit = 1000)
: limit(_limit)
{
this->clear();
}
void clear()
{
std::lock_guard<std::mutex> lock(this->mutex);
this->chunks = std::make_shared<std::vector<std::shared_ptr<std::vector<T>>>>();
Chunk chunk = std::make_shared<std::vector<T>>();
chunk->resize(this->chunkSize);
this->chunks->push_back(chunk);
this->firstChunkOffset = 0;
this->lastChunkEnd = 0;
}
// return true if an item was deleted
// deleted will be set if the item was deleted
bool pushBack(const T &item, T &deleted)
{
std::lock_guard<std::mutex> lock(this->mutex);
Chunk lastChunk = this->chunks->back();
// still space in the last chunk
if (lastChunk->size() <= this->lastChunkEnd) {
// create new chunk vector
ChunkVector newVector =
std::make_shared<std::vector<std::shared_ptr<std::vector<T>>>>();
// copy chunks
for (Chunk &chunk : *this->chunks) {
newVector->push_back(chunk);
}
// push back new chunk
Chunk newChunk = std::make_shared<std::vector<T>>();
newChunk->resize(this->chunkSize);
newVector->push_back(newChunk);
// replace current chunk vector
this->chunks = newVector;
this->lastChunkEnd = 0;
lastChunk = this->chunks->back();
}
lastChunk->at(this->lastChunkEnd++) = item;
return this->deleteFirstItem(deleted);
}
// returns a vector with all the accepted items
std::vector<T> pushFront(const std::vector<T> &items)
{
std::vector<T> acceptedItems;
if (this->space() > 0) {
std::lock_guard<std::mutex> lock(this->mutex);
// create new vector to clone chunks into
ChunkVector newChunks =
std::make_shared<std::vector<std::shared_ptr<std::vector<T>>>>();
newChunks->resize(this->chunks->size());
// copy chunks except for first one
for (size_t i = 1; i < this->chunks->size(); i++) {
newChunks->at(i) = this->chunks->at(i);
}
// create new chunk for the first one
size_t offset = std::min(this->space(), items.size());
Chunk newFirstChunk = std::make_shared<std::vector<T>>();
newFirstChunk->resize(this->chunks->front()->size() + offset);
for (size_t i = 0; i < offset; i++) {
newFirstChunk->at(i) = items[items.size() - offset + i];
acceptedItems.push_back(items[items.size() - offset + i]);
}
for (size_t i = 0; i < this->chunks->at(0)->size(); i++) {
newFirstChunk->at(i + offset) = this->chunks->at(0)->at(i);
}
newChunks->at(0) = newFirstChunk;
this->chunks = newChunks;
// qDebug() << acceptedItems.size();
// qDebug() << this->chunks->at(0)->size();
if (this->chunks->size() == 1) {
this->lastChunkEnd += offset;
}
}
return acceptedItems;
}
// replace an single item, return index if successful, -1 if unsuccessful
int replaceItem(const T &item, const T &replacement)
{
std::lock_guard<std::mutex> lock(this->mutex);
int x = 0;
for (size_t i = 0; i < this->chunks->size(); i++) {
Chunk &chunk = this->chunks->at(i);
size_t start = i == 0 ? this->firstChunkOffset : 0;
size_t end = i == chunk->size() - 1 ? this->lastChunkEnd : chunk->size();
for (size_t j = start; j < end; j++) {
if (chunk->at(j) == item) {
Chunk newChunk = std::make_shared<std::vector<T>>();
newChunk->resize(chunk->size());
for (size_t k = 0; k < chunk->size(); k++) {
newChunk->at(k) = chunk->at(k);
}
newChunk->at(j) = replacement;
this->chunks->at(i) = newChunk;
return x;
}
x++;
}
}
return -1;
}
// replace an item at index, return true if worked
bool replaceItem(size_t index, const T &replacement)
{
std::lock_guard<std::mutex> lock(this->mutex);
size_t x = 0;
for (size_t i = 0; i < this->chunks->size(); i++) {
Chunk &chunk = this->chunks->at(i);
size_t start = i == 0 ? this->firstChunkOffset : 0;
size_t end = i == chunk->size() - 1 ? this->lastChunkEnd : chunk->size();
for (size_t j = start; j < end; j++) {
if (x == index) {
Chunk newChunk = std::make_shared<std::vector<T>>();
newChunk->resize(chunk->size());
for (size_t k = 0; k < chunk->size(); k++) {
newChunk->at(k) = chunk->at(k);
}
newChunk->at(j) = replacement;
this->chunks->at(i) = newChunk;
return true;
}
x++;
}
}
return false;
}
// void insertAfter(const std::vector<T> &items, const T &index)
messages::LimitedQueueSnapshot<T> getSnapshot()
{
std::lock_guard<std::mutex> lock(this->mutex);
return LimitedQueueSnapshot<T>(this->chunks, this->limit - this->space(),
this->firstChunkOffset, this->lastChunkEnd);
}
private:
size_t space()
{
size_t totalSize = 0;
for (Chunk &chunk : *this->chunks) {
totalSize += chunk->size();
}
totalSize -= this->chunks->back()->size() - this->lastChunkEnd;
if (this->chunks->size() != 1) {
totalSize -= this->firstChunkOffset;
}
return this->limit - totalSize;
}
bool deleteFirstItem(T &deleted)
{
// determine if the first chunk should be deleted
if (space() > 0) {
return false;
}
deleted = this->chunks->front()->at(this->firstChunkOffset);
this->firstChunkOffset++;
// need to delete the first chunk
if (this->firstChunkOffset == this->chunks->front()->size() - 1) {
// copy the chunk vector
ChunkVector newVector =
std::make_shared<std::vector<std::shared_ptr<std::vector<T>>>>();
// delete first chunk
bool first = true;
for (Chunk &chunk : *this->chunks) {
if (!first) {
newVector->push_back(chunk);
}
first = false;
}
this->chunks = newVector;
this->firstChunkOffset = 0;
}
return true;
}
ChunkVector chunks;
std::mutex mutex;
size_t firstChunkOffset;
size_t lastChunkEnd;
size_t limit;
const size_t chunkSize = 100;
};
} // namespace messages
} // namespace chatterino