Skip to content

Instantly share code, notes, and snippets.

@Bak-Jin-Hyeong
Last active January 22, 2018 01:15
Show Gist options
  • Save Bak-Jin-Hyeong/49954f7985632cbb59042d581f3596bc to your computer and use it in GitHub Desktop.
Save Bak-Jin-Hyeong/49954f7985632cbb59042d581f3596bc to your computer and use it in GitHub Desktop.
#include "CircularBuffer.h"
#include <climits>
#include <cassert>
#include <algorithm>
CircularBuffer::CircularBuffer() = default;
CircularBuffer::~CircularBuffer() = default;
CircularBuffer::CircularBuffer(void* buffer_address, size_t length)
: buffer_(static_cast<char*>(buffer_address))
, allocated_size_(static_cast<uint32_t>(length))
{
assert(length <= UINT_MAX);
}
CircularBuffer::CircularBuffer(CircularBuffer&& other)
: buffer_(other.buffer_)
, allocated_size_(other.allocated_size_)
, read_position_(other.read_position_)
, write_position_(other.write_position_)
{
other.buffer_ = nullptr;
other.allocated_size_ = 0;
other.read_position_ = 0;
other.write_position_ = 0;
}
CircularBuffer& CircularBuffer::operator=(CircularBuffer&& rhs)
{
if (this != &rhs)
{
buffer_ = rhs.buffer_;
allocated_size_ = rhs.allocated_size_;
read_position_ = rhs.read_position_;
write_position_ = rhs.write_position_;
rhs.buffer_ = nullptr;
rhs.allocated_size_ = 0;
rhs.read_position_ = 0;
rhs.write_position_ = 0;
}
return *this;
}
void CircularBuffer::Swap(CircularBuffer& other)
{
std::swap(buffer_, other.buffer_);
std::swap(allocated_size_, other.allocated_size_);
std::swap(read_position_, other.read_position_);
std::swap(write_position_, other.write_position_);
}
void CircularBuffer::Clear()
{
read_position_ = 0;
write_position_ = 0;
}
void CircularBuffer::Teardown()
{
buffer_ = nullptr;
allocated_size_ = 0;
read_position_ = 0;
write_position_ = 0;
}
bool CircularBuffer::Full() const
{
return WritableSize() == 0;
}
bool CircularBuffer::Empty() const
{
return read_position_ == write_position_;
}
uint32_t CircularBuffer::Capacity() const
{
if (allocated_size_ > 0)
{
return allocated_size_ - 1;
}
return 0;
}
uint32_t CircularBuffer::ReadableSize() const
{
if (write_position_ >= read_position_)
{
return write_position_ - read_position_;
}
else
{
return allocated_size_ - read_position_ + write_position_;
}
}
uint32_t CircularBuffer::WritableSize() const
{
if (allocated_size_ == 0)
{
return 0;
}
if (write_position_ >= read_position_)
{
return allocated_size_ - write_position_ + read_position_ - 1;
}
else
{
return read_position_ - write_position_ - 1;
}
}
CircularBuffer::ConstRegion CircularBuffer::ReadableRegion() const
{
const auto remain = ReadableSize();
if (remain == 0)
{
return{};
}
const auto* p = buffer_;
const auto before_wrap = allocated_size_ - read_position_;
if (remain <= before_wrap)
{
ConstRegion result{};
result.base = p;
result.contiguous[0].offset = read_position_;
result.contiguous[0].size = remain;
result.num_contiguous = 1;
return result;
}
else
{
ConstRegion result{};
result.base = p;
result.contiguous[0].offset = read_position_;
result.contiguous[0].size = before_wrap;
result.contiguous[1].offset = 0;
result.contiguous[1].size = remain - before_wrap;
result.num_contiguous = 2;
return result;
}
}
CircularBuffer::Region CircularBuffer::WritableRegion()
{
const auto remain = WritableSize();
if (remain == 0)
{
return{};
}
const auto p = buffer_;
const auto before_wrap = allocated_size_ - write_position_;
if (remain <= before_wrap)
{
Region result{};
result.base = p;
result.contiguous[0].offset = write_position_;
result.contiguous[0].size = remain;
result.num_contiguous = 1;
return result;
}
else
{
Region result{};
result.base = p;
result.contiguous[0].offset = write_position_;
result.contiguous[0].size = before_wrap;
result.contiguous[1].offset = 0;
result.contiguous[1].size = remain - before_wrap;
result.num_contiguous = 2;
return result;
}
}
uint32_t CircularBuffer::ConsumeRead(size_t size)
{
const auto remain = ReadableSize();
const auto consuming =
(size <= remain) ? static_cast<uint32_t>(size) : remain;
const auto before_wrap = allocated_size_ - read_position_;
if (consuming < before_wrap)
{
read_position_ += consuming;
}
else
{
read_position_ = consuming - before_wrap;
}
return consuming;
}
uint32_t CircularBuffer::ConsumeWrite(size_t size)
{
const auto remain = WritableSize();
const auto consuming =
(size <= remain) ? static_cast<uint32_t>(size) : remain;
const auto before_wrap = allocated_size_ - write_position_;
if (consuming < before_wrap)
{
write_position_ += consuming;
}
else
{
write_position_ = consuming - before_wrap;
}
return consuming;
}
uint32_t CircularBuffer::Read(void* target, size_t target_size)
{
const auto remain = ReadableSize();
const auto consuming =
(target_size <= remain) ? static_cast<uint32_t>(target_size) : remain;
if (consuming == 0)
{
return 0;
}
const auto* p = buffer_;
const auto before_wrap = allocated_size_ - read_position_;
if (consuming < before_wrap)
{
memcpy(target, p + read_position_, consuming);
read_position_ += consuming;
}
else
{
memcpy(target, p + read_position_, before_wrap);
const auto after_wrap = consuming - before_wrap;
memcpy(static_cast<char*>(target) + before_wrap, p, after_wrap);
read_position_ = after_wrap;
}
return consuming;
}
uint32_t CircularBuffer::Write(const void* source, size_t source_size)
{
const auto remain = WritableSize();
const auto consuming =
(source_size <= remain) ? static_cast<uint32_t>(source_size) : remain;
if (consuming == 0)
{
return 0;
}
const auto p = buffer_;
const auto before_wrap = allocated_size_ - write_position_;
if (consuming < before_wrap)
{
memcpy(p + write_position_, source, consuming);
write_position_ += consuming;
}
else
{
memcpy(p + write_position_, source, before_wrap);
const auto after_wrap = consuming - before_wrap;
memcpy(p, static_cast<const char*>(source) + before_wrap, after_wrap);
write_position_ = after_wrap;
}
return consuming;
}
CircularBuffer::ConstRegion CircularBuffer::WriteEx(
const void* source, size_t source_size)
{
const auto remain = WritableSize();
const auto consuming =
(source_size <= remain) ? static_cast<uint32_t>(source_size) : remain;
if (consuming == 0)
{
return {};
}
ConstRegion r{};
r.base = buffer_;
const auto p = buffer_;
const auto before_wrap = allocated_size_ - write_position_;
if (consuming < before_wrap)
{
r.num_contiguous = 1;
r.contiguous[0].offset = write_position_;
r.contiguous[0].size = consuming;
memcpy(p + write_position_, source, consuming);
write_position_ += consuming;
}
else
{
r.num_contiguous = 2;
r.contiguous[0].offset = write_position_;
r.contiguous[0].size = before_wrap;
memcpy(p + write_position_, source, before_wrap);
const auto after_wrap = consuming - before_wrap;
r.contiguous[1].offset = 0;
r.contiguous[1].size = after_wrap;
memcpy(p, static_cast<const char*>(source) + before_wrap, after_wrap);
write_position_ = after_wrap;
}
return r;
}
uint32_t CircularBuffer::Write(const CircularBuffer& source, size_t source_size)
{
const auto readable = source.ReadableRegion();
const auto source_buffer_base = static_cast<const char*>(readable.base);
uint32_t copied = 0;
for (int i = 0; i < readable.num_contiguous; ++i)
{
if (source_size == 0)
{
break;
}
auto r = readable.contiguous[i];
const auto w = (r.size < source_size) ? r.size : source_size;
const auto written = Write(source_buffer_base + r.offset, w);
copied += written;
if (written < w)
{
break;
}
source_size -= written;
}
return copied;
}
char* CircularBuffer::AllocatedBase() const
{
return buffer_;
}
uint32_t CircularBuffer::AllocatedSize() const
{
return allocated_size_;
}
#ifndef CIRCULARBUFFER__H__
#define CIRCULARBUFFER__H__
#include <cstdint>
class CircularBuffer
{
public:
struct ContiguousRegion
{
uint32_t offset;
uint32_t size;
};
struct ConstRegion
{
int num_contiguous = 0;
const void* base = nullptr;
ContiguousRegion contiguous[2]{};
};
struct Region {
int num_contiguous = 0;
void* base = nullptr;
ContiguousRegion contiguous[2]{};
};
CircularBuffer();
CircularBuffer(void* buffer_address, size_t length);
CircularBuffer(CircularBuffer&&);
~CircularBuffer();
CircularBuffer& operator=(CircularBuffer&&);
void Swap(CircularBuffer& other);
void Clear();
void Teardown();
bool Full() const;
bool Empty() const;
uint32_t Capacity() const;
uint32_t ReadableSize() const;
uint32_t WritableSize() const;
ConstRegion ReadableRegion() const;
Region WritableRegion();
uint32_t ConsumeRead(size_t size);
uint32_t ConsumeWrite(size_t size);
uint32_t Read(void* target, size_t target_size);
uint32_t Write(const void* source, size_t source_size);
ConstRegion WriteEx(const void* source, size_t source_size);
uint32_t Write(const CircularBuffer& source, size_t source_size);
char* AllocatedBase() const;
uint32_t AllocatedSize() const;
private:
char* buffer_ = nullptr;
uint32_t allocated_size_ = 0;
uint32_t write_position_ = 0;
uint32_t read_position_ = 0;
private:
CircularBuffer(const CircularBuffer&) = delete;
CircularBuffer& operator=(const CircularBuffer&) = delete;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment