forked from Ferdi265/cxx-ring-buffer
initial import
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/
|
18
CMakeLists.txt
Normal file
18
CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
project(test-project CXX)
|
||||||
|
|
||||||
|
option(RING_BUFFER_BUILD_TESTS "build test cases for the ring buffer library" ON)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
add_library(ring-buffer INTERFACE)
|
||||||
|
target_include_directories(ring-buffer INTERFACE include/)
|
||||||
|
|
||||||
|
if(RING_BUFFER_BUILD_TESTS)
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
268
include/ring-buffer.h
Normal file
268
include/ring-buffer.h
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
class ring_buffer_iterator {
|
||||||
|
private:
|
||||||
|
using size_type = typename Container::size_type;
|
||||||
|
using value_type = typename std::iterator_traits<ring_buffer_iterator>::value_type;
|
||||||
|
using difference_type = typename std::iterator_traits<ring_buffer_iterator>::difference_type;
|
||||||
|
using reference = typename std::iterator_traits<ring_buffer_iterator>::reference;
|
||||||
|
using pointer = typename std::iterator_traits<ring_buffer_iterator>::pointer;
|
||||||
|
|
||||||
|
Container* container;
|
||||||
|
size_type front;
|
||||||
|
size_type index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ring_buffer_iterator() = default;
|
||||||
|
~ring_buffer_iterator() = default;
|
||||||
|
ring_buffer_iterator(Container& container, size_type front = 0, size_type index = 0) : container(&container), front(front), index(index) {}
|
||||||
|
ring_buffer_iterator(const ring_buffer_iterator&) = default;
|
||||||
|
ring_buffer_iterator(ring_buffer_iterator&&) = default;
|
||||||
|
ring_buffer_iterator& operator=(const ring_buffer_iterator&) = default;
|
||||||
|
ring_buffer_iterator& operator=(ring_buffer_iterator&&) = default;
|
||||||
|
|
||||||
|
ring_buffer_iterator& operator+=(difference_type n) {
|
||||||
|
index += n;
|
||||||
|
}
|
||||||
|
ring_buffer_iterator& operator-=(difference_type n) {
|
||||||
|
index -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_buffer_iterator operator+(difference_type n) const {
|
||||||
|
return {container, front, index + n};
|
||||||
|
}
|
||||||
|
ring_buffer_iterator operator-(difference_type n) const {
|
||||||
|
return {container, front, index - n};
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*() {
|
||||||
|
return *(container->begin() + (front + index) % container->size());
|
||||||
|
}
|
||||||
|
pointer operator->() {
|
||||||
|
return &**this;
|
||||||
|
}
|
||||||
|
reference operator[](difference_type n) {
|
||||||
|
return *(*this + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_buffer_iterator& operator++() {
|
||||||
|
index++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ring_buffer_iterator operator++(int) {
|
||||||
|
ring_buffer_iterator old = *this;
|
||||||
|
*this++;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
ring_buffer_iterator& operator--() {
|
||||||
|
index--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ring_buffer_iterator operator--(int) {
|
||||||
|
ring_buffer_iterator old = *this;
|
||||||
|
*this--;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const ring_buffer_iterator& other) const {
|
||||||
|
return index == other.index;
|
||||||
|
}
|
||||||
|
bool operator!=(const ring_buffer_iterator& other) const {
|
||||||
|
return index != other.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const ring_buffer_iterator& other) const {
|
||||||
|
return index < other.index;
|
||||||
|
}
|
||||||
|
bool operator>(const ring_buffer_iterator& other) const {
|
||||||
|
return index > other.index;
|
||||||
|
}
|
||||||
|
bool operator<=(const ring_buffer_iterator& other) const {
|
||||||
|
return index <= other.index;
|
||||||
|
}
|
||||||
|
bool operator>=(const ring_buffer_iterator& other) const {
|
||||||
|
return index >= other.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend ring_buffer_iterator operator+( difference_type n, const ring_buffer_iterator& it) {
|
||||||
|
return it + n;
|
||||||
|
}
|
||||||
|
friend void swap(ring_buffer_iterator& a, ring_buffer_iterator& b) {
|
||||||
|
using std::swap;
|
||||||
|
swap(a.container, b.container);
|
||||||
|
swap(a.front, b.front);
|
||||||
|
swap(a.index, b.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
class ring_buffer_const_iterator {
|
||||||
|
private:
|
||||||
|
using size_type = typename Container::size_type;
|
||||||
|
using value_type = typename std::iterator_traits<ring_buffer_const_iterator>::value_type;
|
||||||
|
using difference_type = typename std::iterator_traits<ring_buffer_const_iterator>::difference_type;
|
||||||
|
using reference = typename std::iterator_traits<ring_buffer_const_iterator>::reference;
|
||||||
|
using pointer = typename std::iterator_traits<ring_buffer_const_iterator>::pointer;
|
||||||
|
|
||||||
|
const Container* container;
|
||||||
|
size_type front{};
|
||||||
|
size_type index{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ring_buffer_const_iterator() = default;
|
||||||
|
~ring_buffer_const_iterator() = default;
|
||||||
|
ring_buffer_const_iterator(const Container& container, size_type front = 0, size_type index = 0) : container(&container), front(front), index(index) {}
|
||||||
|
ring_buffer_const_iterator(const ring_buffer_const_iterator&) = default;
|
||||||
|
ring_buffer_const_iterator(ring_buffer_const_iterator&&) = default;
|
||||||
|
ring_buffer_const_iterator& operator=(const ring_buffer_const_iterator&) = default;
|
||||||
|
ring_buffer_const_iterator& operator=(ring_buffer_const_iterator&&) = default;
|
||||||
|
|
||||||
|
ring_buffer_const_iterator& operator+=(difference_type n) {
|
||||||
|
index += n;
|
||||||
|
}
|
||||||
|
ring_buffer_const_iterator& operator-=(difference_type n) {
|
||||||
|
index -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_buffer_const_iterator operator+(difference_type n) const {
|
||||||
|
return {container, front, index + n};
|
||||||
|
}
|
||||||
|
ring_buffer_const_iterator operator-(difference_type n) const {
|
||||||
|
return {container, front, index - n};
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*() const {
|
||||||
|
return *(container->begin() + (front + index) % container->size());
|
||||||
|
}
|
||||||
|
pointer operator->() const {
|
||||||
|
return &**this;
|
||||||
|
}
|
||||||
|
reference operator[](difference_type n) const {
|
||||||
|
return *(*this + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
ring_buffer_const_iterator& operator++() {
|
||||||
|
index++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ring_buffer_const_iterator operator++(int) {
|
||||||
|
ring_buffer_const_iterator old = *this;
|
||||||
|
*this++;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
ring_buffer_const_iterator& operator--() {
|
||||||
|
index--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ring_buffer_const_iterator operator--(int) {
|
||||||
|
ring_buffer_const_iterator old = *this;
|
||||||
|
*this--;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const ring_buffer_const_iterator& other) const {
|
||||||
|
return index == other.index;
|
||||||
|
}
|
||||||
|
bool operator!=(const ring_buffer_const_iterator& other) const {
|
||||||
|
return index != other.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const ring_buffer_const_iterator& other) const {
|
||||||
|
return index < other.index;
|
||||||
|
}
|
||||||
|
bool operator>(const ring_buffer_const_iterator& other) const {
|
||||||
|
return index > other.index;
|
||||||
|
}
|
||||||
|
bool operator<=(const ring_buffer_const_iterator& other) const {
|
||||||
|
return index <= other.index;
|
||||||
|
}
|
||||||
|
bool operator>=(const ring_buffer_const_iterator& other) const {
|
||||||
|
return index >= other.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend ring_buffer_const_iterator operator+( difference_type n, const ring_buffer_const_iterator& it) {
|
||||||
|
return it + n;
|
||||||
|
}
|
||||||
|
friend void swap(ring_buffer_const_iterator& a, ring_buffer_const_iterator& b) {
|
||||||
|
using std::swap;
|
||||||
|
swap(a.container, b.container);
|
||||||
|
swap(a.front, b.front);
|
||||||
|
swap(a.index, b.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
class basic_ring_buffer {
|
||||||
|
public:
|
||||||
|
using value_type = typename Container::value_type;
|
||||||
|
using size_type = typename Container::size_type;
|
||||||
|
using iterator = ring_buffer_iterator<Container>;
|
||||||
|
using const_iterator = ring_buffer_const_iterator<Container>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Container container;
|
||||||
|
size_type front_index{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
basic_ring_buffer() noexcept = default;
|
||||||
|
~basic_ring_buffer() noexcept = default;
|
||||||
|
basic_ring_buffer(const basic_ring_buffer& other) = default;
|
||||||
|
basic_ring_buffer(basic_ring_buffer&& other) = default;
|
||||||
|
basic_ring_buffer& operator=(const basic_ring_buffer& other) = default;
|
||||||
|
basic_ring_buffer& operator=(basic_ring_buffer&& other) = default;
|
||||||
|
|
||||||
|
Container& buffer() {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
const Container& buffer() const {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(const value_type& value) {
|
||||||
|
container[front_index] = value;
|
||||||
|
front_index = (front_index + 1) % container.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
return {container, front_index, 0};
|
||||||
|
}
|
||||||
|
iterator end() {
|
||||||
|
return {container, front_index, container.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cbegin() const {
|
||||||
|
return {container, front_index, 0};
|
||||||
|
}
|
||||||
|
const_iterator cend() const {
|
||||||
|
return {container, front_index, container.size()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <typename Container>
|
||||||
|
class iterator_traits<ring_buffer_iterator<Container>> {
|
||||||
|
public:
|
||||||
|
using difference_type = typename std::make_signed<typename Container::size_type>::type;
|
||||||
|
using value_type = typename Container::value_type;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using reference = value_type&;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
class iterator_traits<ring_buffer_const_iterator<Container>> {
|
||||||
|
public:
|
||||||
|
using difference_type = typename std::make_signed<typename Container::size_type>::type;
|
||||||
|
using value_type = const typename Container::value_type;
|
||||||
|
using pointer = const value_type*;
|
||||||
|
using reference = const value_type&;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
template <typename T, size_t N>
|
||||||
|
using ring_buffer = basic_ring_buffer<std::array<T, N>>;
|
33
test/CMakeLists.txt
Normal file
33
test/CMakeLists.txt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
set(test-targets "")
|
||||||
|
|
||||||
|
file(GLOB test-sources *.cpp)
|
||||||
|
foreach(test-source ${test-sources})
|
||||||
|
get_filename_component(test-name ${test-source} NAME_WE)
|
||||||
|
set(test-target test-${test-name})
|
||||||
|
list(APPEND test-targets ${test-target})
|
||||||
|
|
||||||
|
add_executable(${test-target} ${test-source})
|
||||||
|
target_link_libraries(${test-target} PRIVATE ring-buffer)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(GLOB test-directories *)
|
||||||
|
foreach(test-directory ${test-directories})
|
||||||
|
if(NOT IS_DIRECTORY ${test-directory})
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(test-name ${test-directory})
|
||||||
|
set(test-target test-${test-name})
|
||||||
|
list(APPEND test-targets ${test-target})
|
||||||
|
|
||||||
|
if(IS_DIRECTORY ${test-directory}/src)
|
||||||
|
file(GLOB_RECURSE test-directory-sources ${test-directory}/src/*.cpp)
|
||||||
|
else()
|
||||||
|
file(GLOB test-directory-sources ${test-directory}/*.cpp)
|
||||||
|
endif()
|
||||||
|
add_executable(${test-target} ${test-directory-sources})
|
||||||
|
if(IS_DIRECTORY ${test-directory}/include)
|
||||||
|
target_include_directories(${test-target} PRIVATE ${test-directory}/include)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(${test-target} PRIVATE ring-buffer)
|
||||||
|
endforeach()
|
25
test/simple.cpp
Normal file
25
test/simple.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <ring-buffer.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
ring_buffer<int, 4> buf;
|
||||||
|
|
||||||
|
buf.push_back(1);
|
||||||
|
buf.push_back(2);
|
||||||
|
buf.push_back(3);
|
||||||
|
buf.push_back(4);
|
||||||
|
buf.push_back(5);
|
||||||
|
buf.push_back(6);
|
||||||
|
|
||||||
|
std::cout << "before\n";
|
||||||
|
for (int& i : buf) {
|
||||||
|
std::cout << i << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_back(7);
|
||||||
|
|
||||||
|
std::cout << "after\n";
|
||||||
|
for (int& i : buf) {
|
||||||
|
std::cout << i << "\n";
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user