记录 环形缓冲区RingBuffer实现(基于Linux内核Kfifo)

说明

该专栏的上一篇文章记录了一种缓冲区的实现,可以先移步阅读 记录 队列数据缓冲区(FiFoBuffer)
总的来说,缓冲区的设计基本思路大同小异。甚至申请一块内存,通过memcpy memmove两个函数就能实现基本的缓存功能(就是有点粗糙,效率没那么高)。因此根据应用解决的问题场景不同,所以某些细节实现、改进优化也是有所不同。
此环形缓存借鉴Linux内核的Kfifo队列实现,这里修改了代码,封装为缓冲区类,可以直接复制使用。这里必须赞一下,编程是门艺术,Linux内核代码写的确实牛逼,代码简洁、清晰、巧妙、高效,很少花里胡哨的骚操作,直达本质。同时字里行间体现深厚的计算机底层功力,难以望其项背。

代码

相关环形缓存的文章较多,原理也并不是很复杂。不同的是,Linux内核的Kfifo实现个人认为做到了比较极致的境界,不在赘述,直接上代码。

  1. RingBuffer.h
#ifndef RINGBUFFER_H
#define RINGBUFFER_H

#include <mutex>

//判断x是否是2的次方
#define is_power_of_two(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))

//取a和b中最小值
#define Min(a, b) (((a) < (b)) ? (a) : (b))

//环形缓存
class RingBuffer
{
    
    
public:
    RingBuffer();
    ~RingBuffer();

    //根据传入size 初始化缓冲区
    bool initBuffer(uint32_t bufferSize);
    //释放缓冲区
    void freeBuffer();
    //重置缓冲区(不需要重新申请内存)
    void resetBuffer();

    //缓存区是否为空
    bool isEmpty();
    //缓存区是否已满
    bool isFull();
    //返回可读数据长度
    uint32_t getReadableLen();
    //返回剩余空闲长度
    uint32_t getRemainLen();
    //返回缓冲区总长度
    uint32_t getBufferSize();

    //缓存区写入数据, 返回实际写入大小
    uint32_t writeBuffer(char *inBuf, uint32_t inSize);
    //缓存区读出数据 返回实际读出大小
    uint32_t readBuffer(char *outBuf, uint32_t outSize);

private:
    uint8_t *buffer;        //缓冲区
    uint32_t bufferSize;    //缓冲区总大小
    uint32_t write;         //写入位置
    uint32_t read;          //读出位置
    std::mutex mutex;       //互斥锁
};

#endif // RINGBUFFER_H
  1. RingBuffer.cpp
#include "ringbuffer.h"

RingBuffer::RingBuffer()
{
    
    
    buffer = nullptr;
    bufferSize = 0;
    write = 0;
    read = 0;
}

RingBuffer::~RingBuffer()
{
    
    
    freeBuffer();
}

bool RingBuffer::initBuffer(uint32_t size)
{
    
    
    //需要保证为2的次幂 取余运算转换为与运算 提升效率,即write%bufferSize == write &(bufferSize-1)
    if (!is_power_of_two(size))
    {
    
    
        if (size < 2)
            size = 2;

        //向上取2的次幂
        int i = 0;
        for (; size != 0; i++)
            size >>= 1;

        size = 1U << i;
    }

    std::lock_guard<std::mutex> lock(mutex);
    buffer = new uint8_t[size];
    if(buffer == nullptr)
        return false;

    memset(buffer, 0, size);
    bufferSize = size;
    write = 0;
    read = 0;
    return true;
}

void RingBuffer::freeBuffer()
{
    
    
    std::lock_guard<std::mutex> lock(mutex);
    bufferSize = 0;
    write = 0;
    read = 0;

    if (buffer != nullptr)
    {
    
    
        delete[] buffer;
        buffer = nullptr;
    }
}

void RingBuffer::resetBuffer()
{
    
    
    std::lock_guard<std::mutex> lock(mutex);
    write = 0;
    read = 0;
    memset(buffer, 0, bufferSize);
}

bool RingBuffer::isEmpty()
{
    
    
    std::lock_guard<std::mutex> lock(mutex);
    return write == read;
}

bool RingBuffer::isFull()
{
    
    
    std::lock_guard<std::mutex> lock(mutex);
    return bufferSize == (write - read);
}

uint32_t RingBuffer::getReadableLen()
{
    
    
    std::lock_guard<std::mutex> lock(mutex);
    return write - read;
}

uint32_t RingBuffer::getRemainLen()
{
    
    
    std::lock_guard<std::mutex> lock(mutex);
    return bufferSize - (write - read);
}

uint32_t RingBuffer::getBufferSize()
{
    
    
    std::lock_guard<std::mutex> lock(mutex);
    return bufferSize;
}

uint32_t RingBuffer::writeBuffer(char *inBuf, uint32_t inSize)
{
    
    
    std::lock_guard<std::mutex> lock(mutex);

    if(buffer == nullptr || inBuf == nullptr || inSize == 0)
        return -1;

    //写入数据大小和缓冲区剩余空间大小 取最小值为最终写入大小
    inSize = Min(inSize, bufferSize - (write - read));

    //写数据如果写到末尾仍未写完的情况,那么回到头部继续写
    uint32_t len = Min(inSize, bufferSize - (write & (bufferSize - 1)));
    //区间为写指针位置到缓冲区末端
    memcpy(buffer + (write & (bufferSize - 1)), inBuf, len);
    //回到缓冲区头部继续写剩余数据
    memcpy(buffer, inBuf + len, inSize - len);

    //无符号溢出则为 0
    write += inSize;
    return inSize;
}

uint32_t RingBuffer::readBuffer(char *outBuf, uint32_t outSize)
{
    
    
    std::lock_guard<std::mutex> lock(mutex);

    if(buffer == nullptr || outBuf == nullptr || outSize == 0)
        return -1;

    //读出数据大小和缓冲区可读数据大小 取最小值为最终读出大小
    outSize = Min(outSize, write - read);

    //读数据如果读到末尾仍未读完的情况, 那么回到头部继续读
    uint32_t len = Min(outSize, bufferSize - (read & (bufferSize - 1)));
    //区间为读指针位置到缓冲区末端
    memcpy(outBuf, buffer + (read & (bufferSize - 1)), len);
    //回到缓冲区头部继续读剩余数据
    memcpy(outBuf + len, buffer, outSize - len);

    //无符号溢出则为 0
    read += outSize;
    return outSize;
}

猜你喜欢

转载自blog.csdn.net/T__zxt/article/details/127314299
今日推荐