Last active
January 22, 2018 01:15
-
-
Save Bak-Jin-Hyeong/49954f7985632cbb59042d581f3596bc 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 "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_; | |
} |
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
#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