c++ - 实现循环缓冲区以在一次调用中写入/读取任意数量的数据

标签 c++ data-structures

大多数循环缓冲区假设每次只读/写一个对象,我发现以 (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/

相关文章:

c++ - Eclipse for C++ 中的 "string could not resolved"错误(Eclipse 无法解析标准库)

c++ - SetupAPI.DLL 到 HID.DLL

algorithm - 通过仅以相同顺序插入节点来从 Preorder 获得 BST

sql - 优化mysql表?

algorithm - 二叉搜索树用于查找多个对象

合并两个最大堆的算法?

c++ - 添加 15 然后应用 &~0xf 有什么作用?

C++ Fstream 输出不起作用

java - 使用c++模板生成JNI函数

java - 存储结果