Last active
April 23, 2017 21:14
-
-
Save Starl1ght/0156b1d32ec283951a25f82ad793fd4c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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