简洁高效的linux kfifo环形缓冲区

23 篇文章 71 订阅

代码来自于:https://blog.csdn.net/vertor11/article/details/53741681,侵删。

struct kfifo{
    uint8_t *buffer;
    uint32_t in;    // 输入指针
    uint32_t out;   // 输出指针
    uint32_t size;  // 缓冲区大小,必须为2的次幂
}

/*判断n是否为2的幂*/
static bool is_power_of_2(unsigned int n)
{
    return (n != 0 && ((n & (n - 1)) == 0));
}

/*将数字a向上取整为2的次幂*/
static uint32_t roundup_power_of_2(uint32_t a)
{
    if (a == 0)
        return 0;

    uint32_t position = 0;
    for (int i = a; i != 0; i >>= 1)
        position++;

    return (uint32_t)(1 << position);
}

/*全局变量*/
struct kfifo fifo;

/*环形缓冲区初始化*/
void kfifo_init(uint32_t size)
{
    if (!is_power_of_2(size))
        size = roundup_power_of_2(_size);     //把size的大小向上取为2的次幂

    fifo->buffer = (unsigned char *)(malloc(size * sizeof(unsigned char)));
    fifo->in = 0;
    fifo->out = 0;
    fifo->size = size;
}

/*返回实际写入缓冲区中的数据*/
uint32_t Write_kfifo(const uint8_t *data, uint32_t len)
{
    unsigned int l;

    /*当前缓冲区空闲空间*/
    len = min(len,fifo->size - fifo->in + fifo->out);               //取写入大小和空间剩余大小的较小者

    /*当前in位置到buffer末尾的长度*/
    l = min(len, fifo->size - (fifo->in  & (fifo->size - 1)));

    /*首先复制数据到[in,buffer的末尾]*/
    memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), data, l);

    /*复制剩余的数据(如果有)到[buffer的起始位置,...]*/
    memcpy(fifo->buffer, data + l, len - l);

    fifo->in += len; // 直接加,不做取模运算。当溢出时,从buffer的开始位置重新开始

    return len;
}

/*返回实际读取的数据长度*/
uint32_t Read_kfifo(uint8_t *data, uint32_t len)
{
    unsigned int l;

    /*缓冲区中的数据长度: 注意都是无符号数*/
    len = min(len, fifo->in - fifo->out);  

    // 首先从[out,buffer end]读取数据
    l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
    memcpy(data, fifo->buffer + (fifo->out & (fifo->size - 1)), l);

    // 从[buffer start,...]读取数据
    memcpy(data + l, fifo->buffer, len - l);

    fifo->out += len; // 直接加,不做取模运算。溢出后,从buffer的起始位置重新开始

    return len;
}

代码有几个特别精妙的地方,讲解一下:

1、模型如下:

fifo->out到fifo->in之间保存着数据。buffer头到fifo->out和fifo->in到fifo->size是空的。

2、把size的大小设置为2的幂次,这样对fifo->size的取模运算可以转化为与运算:

    fifo->in % fifo->size 可以转化为 fifo->in & (fifo->size – 1)

3、利用了数据类型溢出,又回到0开始的特性。缓冲区数据的读写,fifo->in和fifo->out都一直在增加,知道超过uint32_t的最大值,然后又回到0。这样避免了多余的语句。

4、利用了无符号数保存负值的特性。从上面的模型中知道,fifo->in - fifo->out得到缓冲区中保存数据的长度,如果fifo->in到了数据类型的最大值,又回到0,也就是说fifo->in小于fifo->out会发生什么呢?

假如fifo->in和fifo->out都是uint8_t类型的数,设fifo->in=5,fifo->out=250,fifo->in - fifo->out=5 - 250=11,刚好是250绕过255回到5的长度。(具体原因看这篇:)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值