Skip to content

Instantly share code, notes, and snippets.

@Starl1ght
Last active April 23, 2017 21:14
Show Gist options
  • Save Starl1ght/0156b1d32ec283951a25f82ad793fd4c to your computer and use it in GitHub Desktop.
Save Starl1ght/0156b1d32ec283951a25f82ad793fd4c to your computer and use it in GitHub Desktop.
#include <memory>
#include <vector>
#include <iterator>
#include <map>
#include <iostream>
#include <string>
#include <numeric>
template <typename T, size_t SIZE = 20>
class RingAllocator {
public:
RingAllocator() = default;
RingAllocator(const RingAllocator<T, SIZE>&) = delete;
RingAllocator(RingAllocator<T, SIZE>&&) = delete;
RingAllocator<T, SIZE>& operator=(const RingAllocator<T, SIZE>&) = delete;
RingAllocator<T, SIZE>& operator=(RingAllocator<T, SIZE>&&) = delete;
// Static T* allocate(size_t elems, T* addr);
static T* allocate(size_t elems);
static void deallocate(T* ptr, size_t /* elems */);
template <typename...ARGS>
static void construct(T* addr, ARGS&&...args);
static void destroy(T* addr);
static size_t max_size() { return SIZE; }
private:
constexpr static size_t m_arrSize { SIZE * sizeof(T)};
static std::map<T*, size_t> m_map;
static char m_rawMemory[m_arrSize];
static T* const m_memory;
};
template<typename T, size_t SIZE>
std::map<T*, size_t> RingAllocator<T, SIZE>::m_map;
template<typename T, size_t SIZE>
char RingAllocator<T, SIZE>::m_rawMemory[RingAllocator<T, SIZE>::m_arrSize];
template<typename T, size_t SIZE>
T* const RingAllocator<T, SIZE>::m_memory = reinterpret_cast<T*>(RingAllocator<T, SIZE>::m_rawMemory);
template <typename T, size_t SIZE>
T* RingAllocator<T, SIZE>::allocate(size_t elems) {
if (m_map.empty()) {
if (elems > SIZE) {
throw std::bad_alloc{};
}
m_map[m_memory] = elems;
std::cout << "ALLOC AT " << 0 << " | ELEMS " << elems << '\n';
return m_memory;
}
auto endIter = m_map.cbegin();
std::advance(endIter, m_map.size() - 1);
for (auto iter = m_map.cbegin(); iter != endIter; ++iter) {
auto nextIter = iter;
++nextIter;
if (iter->first + iter->second + elems > nextIter->first) {
continue;
}
m_map[iter->first + iter->second] = elems;
std::cout << "ALLOC AT " << iter->first + iter->second - m_memory << " | ELEMS " << elems << '\n';
return iter->first + iter->second;
}
const auto& lastElem = *m_map.rbegin();
if (lastElem.first + lastElem.second + elems > m_memory + SIZE) {
throw std::bad_alloc{};
}
m_map[lastElem.first + lastElem.second] = elems;
std::cout << "ALLOC AT " << lastElem.first + lastElem.second - m_memory << " | ELEMS " << elems << '\n';
return lastElem.first + lastElem.second;
}
template <typename T, size_t SIZE>
void RingAllocator<T, SIZE>::deallocate(T* ptr, size_t) {
std::cout << "FREE at " << ptr - m_memory << '\n';
m_map.erase(m_map.find(ptr));
}
template <typename T, size_t SIZE>
template <typename ... ARGS>
void RingAllocator<T, SIZE>::construct(T* addr, ARGS&&... args) {
new (addr) T(std::forward<ARGS>(args)...);
}
template <typename T, size_t SIZE>
void RingAllocator<T, SIZE>::destroy(T* addr) {
addr->~T();
}
// ============= BUFFER
template <typename T, typename Allocator = std::allocator<T>>
class RingBuffer final {
friend void std::swap(RingBuffer<T, Allocator>& left, RingBuffer<T, Allocator>& right) noexcept;
public:
RingBuffer(size_t size);
RingBuffer(std::initializer_list<T>&& lst);
RingBuffer(const RingBuffer<T, Allocator>& copy) /* noexcept */;
RingBuffer(RingBuffer<T, Allocator>&& move) noexcept;
~RingBuffer();
RingBuffer& operator=(const RingBuffer<T, Allocator>& copy);
RingBuffer& operator=(RingBuffer<T, Allocator>&& move) noexcept;
bool operator==(const RingBuffer<T, Allocator>& comp) const;
template <typename...ARGS>
bool Emplace(ARGS&&...args);
bool Push(const T& arg);
bool Push(T&& arg);
bool Pop(T& out);
class Iterator final {
friend class RingBuffer;
public:
Iterator& operator++() {
m_head = (m_head + 1) % m_cap;
return *this;
}
const T& operator*() const {
return *(m_mem + m_head);
}
bool operator!=(const Iterator& comp) {
return !((m_mem == comp.m_mem) && (m_head == comp.m_head));
}
private:
Iterator(size_t cap, size_t head, T* memory) :
m_cap(cap), m_head(head), m_mem(memory) {}
const size_t m_cap;
size_t m_head;
T* const m_mem;
};
Iterator begin() {
return Iterator(m_capacity, m_headIdx, m_memory);
}
Iterator end() {
return Iterator(m_capacity, (m_headIdx + m_currSize) % m_capacity, m_memory);
}
private:
/* const */ size_t m_capacity{ 0 };
size_t m_headIdx{ 0 };
size_t m_currSize{ 0 };
T* /* const */ m_memory{ nullptr };
};
template <typename T, typename Allocator>
RingBuffer<T, Allocator>::RingBuffer(size_t size) :
m_capacity(size + 1), m_memory(Allocator{}.allocate(m_capacity))
{}
template <typename T, typename Allocator>
RingBuffer<T, Allocator>::RingBuffer(std::initializer_list<T>&& lst) :
m_capacity(lst.size() + 1), m_memory(Allocator{}.allocate(m_capacity))
{
for (auto&& elem : lst) {
Push(std::move(elem));
}
}
template <typename T, typename Allocator>
RingBuffer<T, Allocator>::RingBuffer(RingBuffer<T, Allocator>&& move) noexcept {
std::swap(*this, move);
}
template <typename T, typename Allocator>
RingBuffer<T, Allocator>::RingBuffer(const RingBuffer<T, Allocator>& copy) /* noexcept */ :
m_capacity(copy.m_capacity), m_memory(Allocator{}.allocate(m_capacity))
{
for (size_t i = 0; i < copy.m_currSize; ++i) {
const size_t idx = (i + copy.m_headIdx) % copy.m_capacity;
Push(copy.m_memory[idx]);
}
}
template <typename T, typename Allocator>
RingBuffer<T, Allocator>::~RingBuffer() {
if (m_capacity == 0) return;
const size_t end = (m_headIdx + m_currSize) % m_capacity;
for (size_t i = m_headIdx; i != end; i = (i + 1) % m_capacity) {
Allocator{}.destroy(m_memory + i);
}
Allocator{}.deallocate(m_memory, m_capacity);
}
#ifndef P4_PROGRAMMER_FOR_HIRE
template <typename T, typename Allocator>
RingBuffer<T, Allocator>& RingBuffer<T, Allocator>::operator=(const RingBuffer<T, Allocator>& copy) {
RingBuffer<T, Allocator> tmp{ copy };
std::swap(*this, tmp);
return *this;
}
template <typename T, typename Allocator>
RingBuffer<T, Allocator>& RingBuffer<T, Allocator>::operator=(RingBuffer<T, Allocator>&& move) noexcept {
std::swap(*this, move);
return *this;
}
#else
template <typename T, typename Allocator>
RingBuffer<T, Allocator>& RingBuffer<T, Allocator>::operator=(const RingBuffer<T, Allocator>& copy) {
static_assert(noexcept(RingBuffer<T, Allocator>{ copy }), "Make copy ctor noexcept!");
this->~RingBuffer();
new (this) RingBuffer<T, Allocator>{ copy };
return *this;
}
template <typename T, typename Allocator>
RingBuffer<T, Allocator>& RingBuffer<T, Allocator>::operator=(RingBuffer<T, Allocator>&& move) noexcept {
static_assert(noexcept(RingBuffer<T, Allocator>{ std::move(move)}), "Make move ctor noexcept!");
this->~RingBuffer();
new (this) RingBuffer<T, Allocator>{ std::move(move) };
return *this;
}
#endif
template <typename T, typename Allocator>
bool RingBuffer<T, Allocator>::operator==(const RingBuffer<T, Allocator>& comp) const {
if (m_currSize != comp.m_currSize) {
return false;
}
for (size_t i = 0; i < m_currSize; ++i) {
const size_t myIdx{ (i + m_headIdx) % m_capacity };
const size_t compIdx{ (i + comp.m_headIdx) % comp.m_capacity };
if (m_memory[myIdx] != comp.m_memory[compIdx]) {
return false;
}
}
return true;
}
template <typename T, typename Allocator>
bool RingBuffer<T, Allocator>::Push(const T& arg) {
if (m_currSize >= m_capacity - 1) {
return false;
}
T* const placementPtr = m_memory + (m_headIdx + m_currSize++) % m_capacity;
Allocator{}.construct(placementPtr, arg);
return true;
}
template <typename T, typename Allocator>
bool RingBuffer<T, Allocator>::Push(T&& arg) {
if (m_currSize >= m_capacity - 1) {
return false;
}
T* const placementPtr = m_memory + (m_headIdx + m_currSize++) % m_capacity;
Allocator{}.construct(placementPtr, std::move(arg));
return true;
}
template <typename T, typename Allocator>
template <typename...ARGS>
bool RingBuffer<T, Allocator>::Emplace(ARGS&&...args) {
if (m_currSize >= m_capacity - 1) {
return false;
}
T* const placementPtr = m_memory + (m_headIdx + m_currSize++) % m_capacity;
Allocator{}.construct(placementPtr, std::forward<ARGS>(args)...);
return true;
}
template <typename T, typename Allocator>
bool RingBuffer<T, Allocator>::Pop(T& out) {
if (m_currSize == 0) {
return false;
}
out = std::move(m_memory[m_headIdx]);
Allocator{}.destroy(m_memory + m_headIdx);
m_headIdx = (m_headIdx + 1) % m_capacity;
--m_currSize;
return true;
}
/* Not used in in-place operator=*/
namespace std {
template <typename T, typename Allocator>
void swap(RingBuffer<T, Allocator>& left, RingBuffer<T, Allocator>& right) noexcept {
std::swap(left.m_memory, right.m_memory);
std::swap(left.m_currSize, right.m_currSize);
std::swap(left.m_capacity, right.m_capacity);
std::swap(left.m_headIdx, right.m_headIdx);
}
}
int main() {
{
RingBuffer<int> defAlloc { 1, 2, 3, 4};
RingBuffer<int, RingAllocator<int>> r1{ 1,2,3,4,5 };
auto r2(r1);
auto r3(std::move(r2));
r2 = r3;
for (auto&& elem : RingBuffer<std::string>{ "Simple", "Forward", "Iterator"}) {
std::cout << elem << '\n';
}
}
system("pause");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment