Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.10)
project(CircularBuffer)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -pthread")

find_package(GTest REQUIRED)
include_directories(include test ${GTEST_INCLUDE_DIRS})
add_executable(CircularBuffer test/circular_buffer_test.cpp)
target_link_libraries(CircularBuffer ${GTEST_LIBRARIES} pthread gtest gtest_main)
add_test(CircularBuffer CircularBuffer)
add_custom_target(circular_buffer ALL DEPENDS CircularBuffer COMMAND CircularBuffer)
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,33 @@
3. Generic data type (e.g. uint8_t)
4. Two put(data) overflow behaviors: overwrite and discard data.
2. Write tests showing your circular buffer in action.

## Install CMake and GTest
### CMake
https://cmake.org/install/

### GTest
Linux:
https://github.com/smistad/GTest
Windows:
https://github.com/iat-cener/tonatiuh/wiki/Installing-Google-Test-For-Windows

Note: Compiler used to compiling GTest and this Circular buffer project needs to be same

Use clang(clang 6) or gcc(gcc8) which supports C++17

## Build
Go to circular_buffer path. Execute the following commands.
1. cmake CMakeLists.txt
2. make

CircularBuffer is built and run, an executable 'CircularBuffer' will be created.

CMakeLists.txt has command to run the CircularBuffer Testcases for each build to encourage Test driven development.

## Run

In the circular_buffer path, 'CircularBuffer' executable will be created after successful build.

Execute the following command to run the testcases.
./CircularBuffer
61 changes: 61 additions & 0 deletions include/circular_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H

#include <cstddef>
#include <mutex>
namespace Circular {
namespace Buffer {

constexpr auto kMaxBufferSize{50};
constexpr auto kOverRide{true};
template <typename T>
class CircularBuffer {
public:
CircularBuffer() = default;
CircularBuffer(const CircularBuffer&) = delete;
CircularBuffer(CircularBuffer&&) = delete;
CircularBuffer& operator=(CircularBuffer&&) = delete;
CircularBuffer& operator=(const CircularBuffer&) = delete;
constexpr auto Capacity() const { return kMaxBufferSize; }
constexpr auto Empty() const { return (!full_ && (head_ == tail_)); }
constexpr auto Full() const { return full_; }
constexpr auto Size() const {
size_t buffer_size{kMaxBufferSize};
if (full_) {
buffer_size = kMaxBufferSize + head_ - tail_;
} else {
buffer_size = head_ - tail_;
}
return buffer_size;
}
constexpr void PushBack(const T& data, bool override = kOverRide) {
std::lock_guard<std::mutex> lock(circular_buffer_mutex_);
if (full_) {
if (override) {
PutDataFull(data);
}
} else {
PutData(data);
}
full_ = head_ == tail_;
}
constexpr T operator[](const size_t& index) const { return buffer_[index]; }

private:
constexpr void PutData(const T& data) {
buffer_[head_] = data;
head_ = (head_ + 1) % kMaxBufferSize;
}
constexpr void PutDataFull(const T& data) {
PutData(data);
tail_ = (tail_ + 1) % kMaxBufferSize;
}
T buffer_[kMaxBufferSize];
std::mutex circular_buffer_mutex_;
size_t head_{0};
size_t tail_{0};
bool full_{false};
};
} // namespace Buffer
} // namespace Circular
#endif
84 changes: 84 additions & 0 deletions test/circular_buffer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include <circular_buffer.h>
#include <gtest/gtest.h>
namespace Circular {
namespace Buffer {
namespace {
using Type = std::int64_t;
class CircularBufferTest : public ::testing::Test {
public:
void SetUp() override {}
void TearDown() override {}
CircularBuffer<Type> circular_buffer_;
};

TEST_F(CircularBufferTest, CapacityTest) {
EXPECT_EQ(kMaxBufferSize, circular_buffer_.Capacity());
}

class BufferSizeTest : public ::testing::TestWithParam<Type> {
public:
void SetUp() override {
result_size_ = GetParam() % kMaxBufferSize;
for (auto index{0u}; index < result_size_; ++index) {
circular_buffer_.PushBack(index);
}
if (0 == result_size_) {
result_empty_ = true;
}
if (kMaxBufferSize == result_size_) {
result_full_ = true;
}
}
void TearDown() override {}
size_t result_size_{0};
bool result_empty_{false};
bool result_full_{false};
CircularBuffer<Type> circular_buffer_;
};

TEST_P(BufferSizeTest, SizeTest) {
EXPECT_EQ(result_size_, circular_buffer_.Size());
}
TEST_P(BufferSizeTest, EmptyTest) {
EXPECT_EQ(result_empty_, circular_buffer_.Empty());
}
TEST_P(BufferSizeTest, FullTest) {
EXPECT_EQ(result_full_, circular_buffer_.Full());
}
INSTANTIATE_TEST_CASE_P(SizeParam, BufferSizeTest,
::testing::Values(0, kMaxBufferSize + 1));

class BufferDataTest : public ::testing::TestWithParam<std::tuple<Type, bool>> {
public:
void SetUp() override {
auto buffer_size = std::get<0>(GetParam());
auto override_flag = std::get<1>(GetParam());
for (auto index{0}; index < buffer_size; ++index) {
circular_buffer_.PushBack(index, override_flag);
}
if (override_flag) {
result_start_index_ = buffer_size - kMaxBufferSize;
result_end_index_ = buffer_size;
} else {
result_end_index_ = buffer_size % kMaxBufferSize;
}
}
void TearDown() override {}
size_t result_start_index_{0};
size_t result_end_index_{0};
CircularBuffer<Type> circular_buffer_;
};

TEST_P(BufferDataTest, VerifyBuffer) {
for (auto index{result_start_index_}; index < result_end_index_; ++index) {
EXPECT_EQ(index, circular_buffer_[(index) % kMaxBufferSize]);
}
}
INSTANTIATE_TEST_CASE_P(OverrideDiscardParam, BufferDataTest,
::testing::Combine(::testing::Values(0, kMaxBufferSize +
1),
::testing::Bool()));

} // namespace
} // namespace Buffer
} // namespace Circular