大多数循环缓冲区假设每次只读/写一个对象,我发现以 (const char *bytes, size_t byte_count) 形式对二进制数据进行操作的唯一链接是 http://www.asawicki.info/news_1468_circular_buffer_of_raw_binary_data_in_c.html ,我觉得不对,而且有点长。什么是正确的实现方式?
我自己创建了一个。但它仍然很长。谁能分享一个更优雅的版本?或者您能指出我的代码有什么可以改进的地方吗?
class Pipe{
Pipe(size_t capacity): _capacity(capacity){ init(); }
~Pipe(){delete [] _buf; }
size_t read(char* data, size_t bytes);
size_t write(const char* data, size_t bytes);
private:
//only _capacity-1 is used, one is to identify full or empty.
void init(){_buf = new char[_capacity];
_wptr = 0; _rptr = 0; _used_size = 0;
}
char* _buf;
size_t _capacity, _wptr, _rptr, _used_size;
bool isFull(){return (_wptr + 1 ) % (_capacity) == _rptr;}
bool isEmpty(){return _wptr == _rptr;}
};
size_t Pipe::read(char* data, size_t bytes){
if (isEmpty() || bytes == 0) return 0;
size_t bytes_read1 = 0, bytes_read2 = 0;
if (_rptr>=_wptr+1) { //two piece can be read
bytes_read1 = min(bytes, _capacity - _rptr);
memcpy(data, _buf + _rptr, bytes_read1);
_rptr += bytes_read1;
bytes -= bytes_read1;
if (_rptr == _capacity) _rptr = 0;
if (bytes > 0){
bytes_read2 = min(bytes, _wptr);
memcpy(_buf + _rptr, data, bytes_read2);
_rptr += bytes_read2;
bytes -= bytes_read2;
}
}
else{//one piece can be read
bytes_read1 = min(bytes, _wptr - _rptr);
memcpy(_buf + _wptr, data, bytes_read1);
_rptr += bytes_read1;
bytes -= bytes_read1;
}
return bytes_read1 + bytes_read2;
}
size_t Pipe::write(const char* data, size_t bytes){
if (isFull() || bytes == 0) return 0;
size_t bytes_write1 = 0, bytes_write2 = 0;
if (_wptr>=_rptr) { //two piece can be written
bytes_write1 = min(bytes, _capacity - _wptr);
memcpy(_buf + _wptr, data, bytes_write1);
_wptr += bytes_write1;
bytes -= bytes_write1;
if (_wptr == _capacity) _wptr = 0;
if (bytes > 0){ //_wptr must be 0 here.
bytes_write2 = min(bytes, _rptr-1);//-1 bcz there is one
slot to check empty/full
memcpy(_buf + _wptr, data+ bytes_write1, bytes_write2);
_wptr += bytes_write2;
bytes -= bytes_write2;
}
}
else{ //one piece can be written
bytes_write1 = min(bytes, _rptr - _wptr -1);
memcpy(_buf + _wptr, data, bytes_write1);
_wptr += bytes_write1;
bytes -= bytes_write1;
}
return bytes_write1 + bytes_write2;
}
最佳答案
OP 中的代码可以通过排除所有条件来简化。保留原始接口(interface)和用于实现的memcpy
(只有constructor/destructor/read/write公开,未使用的_used_size
可能会被丢弃)。
size_t Pipe::read(char* data, size_t bytes)
{
bytes = min(bytes, getUsed());
const size_t bytes_read1 = min(bytes, _capacity - _rptr);
memcpy(data, _buf + _rptr, bytes_read1);
memcpy(data + bytes_read1, _buf, bytes - bytes_read1);
updateIndex(_rptr, bytes);
return bytes;
}
size_t Pipe::write(const char* data, size_t bytes)
{
bytes = min(bytes, getFree());
const size_t bytes_write1 = min(bytes, _capacity - _wptr);
memcpy(_buf + _wptr, data, bytes_write1);
memcpy(_buf, data + bytes_write1, bytes - bytes_write1);
updateIndex(_wptr, bytes);
return bytes;
}
这里使用的几个私有(private)方法可能有这个简单的实现:
size_t Pipe::getUsed()
{ return (_capacity - _rptr + _wptr) % _capacity; }
size_t Pipe::getFree()
{ return (_capacity - 1 - _wptr + _rptr) % _capacity; }
void Pipe::updateIndex(size_t& index, size_t bytes)
{ index = (index + bytes) % _capacity; }
此实现有一个缺点:当 _capacity
接近最大 size_t
值(因为溢出)时它会被破坏。这可以通过在免费/使用的计算和索引更新中用条件替换模数来解决。以下是对 read
中使用的方法的修改:
size_t Pipe::getUsed()
{
if (_wptr >= _rptr)
return _wptr - _rptr;
else
return _capacity - _rptr + _wptr;
}
void Pipe::updateIndex(size_t& index, size_t bytes)
{
if (bytes >= _capacity - index)
index = index + bytes - _capacity;
else
index = index + bytes;
}
关于c++ - 实现循环缓冲区以在一次调用中写入/读取任意数量的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21245256/