RT-Thread 的环形缓冲区 ---- 镜像指示位

可以看一下这篇我写的博客,了解一下大概: 

RingBuffer 环形缓冲区----镜像指示位_呵呵哒( ̄▽ ̄)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132340883?spm=1001.2014.3001.5501

【回顾】缓冲区变满在环形缓冲区(ring buffer)中会实际发生,一般会有两种处理策略:

        🐞① 覆盖老数据

        🐞② 抛出“异常”

镜像指示位:缓冲区的长度如果是n,逻辑地址空间则为0至n-1;那么,规定n至2n-1为镜像逻辑地址空间。本策略规定读写指针的地址空间为0至2n-1,其中低半部分对应于常规的逻辑地址空间,高半部分对应于镜像逻辑地址空间。当指针值大于等于2n时,使其折返(wrapped)到ptr-2n。使用一位表示写指针或读指针是否进入了虚拟的镜像存储区:置位表示进入,不置位表示没进入还在基本存储区。

        在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;如果二者的指示位不同,说明缓冲区为满。这种方法优点是测试缓冲区满/空很简单;不需要做取余数操作;读写线程可以分别设计专用算法策略,能实现精致的并发控制。缺点是读写指针各需要额外的一位作为指示位。

        如果缓冲区长度是2的幂,则本方法可以省略镜像指示位。如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满,这可以用条件表达式(写指针==(读指针异或缓冲区长度))来判断。

----(来自百度百科)

一、基本步骤

1.数据结构

typedef struct ringbuffer
{
    uint8 *buffer_ptr;
    uint16 read_mirror : 1;
    uint16 read_index : 15;
    uint16 write_mirror : 1;
    uint16 write_index : 15;
    /* as we use msb of index as mirror bit, the size should be signed and
     * could only be positive. */
    uint16 size;
}ringbuff;

 2.缓冲区初始化

// 缓冲区初始化 
void rb_init(ringbuff *rb,uint8 *pool,uint16 size){
    /* initialize read and write index */
    rb->read_mirror = rb->read_index = 0;
    rb->write_mirror = rb->write_index = 0;

    /* set buffer pool and size */
    rb->buffer_ptr = pool;
    rb->size = DATA_ALIGN_DOWN(size, DATA_ALIGN_SIZE);
}

 3.创建一个ringbuffer

// 创建一个ringbuff
ringbuff* rb_create(uint16_t size) {
    ringbuff *rb;
    uint8_t *pool;

    size = DATA_ALIGN_DOWN(size, DATA_ALIGN_SIZE);// 大小做字节对齐

    rb = (ringbuff *)malloc(sizeof(ringbuff));// 申请内存
    if (rb == NULL)
        goto exit;

    pool = (uint8_t *)malloc(size);// 申请数据缓冲区内存
    if (pool == NULL) {
        free(rb);
        rb = NULL;
        goto exit;
    }
    rb_init(rb, pool, size);// 初始化 ringbuff

exit:
    return rb;
}

 4.销毁环形缓冲区

// 摧毁 ringbuff
void rb_destroy(ringbuff *rb) {   
    cout<<"销毁ringbuff~"<<endl;    
    free(rb->buffer_ptr);
    free(rb);// 释放申请的内存
}

二、缓冲区中填充指定数据长度的数据

5. 缓冲区中填充指定数据长度的数据

举个例子:(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据)

图1:当环形缓冲区为空时,读索引和写索引指向相同的位置(这里初始化为0);

图2:写操作:想往(rb->buffer_size = 8)缓冲区中写入15个元素:123456789ABCDEF,但写入的数据长度(length)超过缓冲区空闲长度(space_length)了。解决方法:RT-Thread(覆盖老数据策略)就是只截取后8位数据放入缓冲区。

可知length=15,space_length=8,满足length > space_length,让ptr = &ptr[length - rb->buffer_size]

图3:由图2的操作可以得到以上的环形缓冲区的数据内容分布

图4:读操作,读取缓冲区4个元素:89AB,修改读索引

图5:写操作,写入缓冲区4个元素:1234,修改写索引

 图6:读操作,读取缓冲区4个元素:CDEF,修改读索引

🐞① 当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据

/* 缓冲区中填充指定数据长度的数据(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据) */
uint16 rb_put_force(ringbuff *rb,const uint8 *ptr,uint16 length);
// 强制往 ringbuff 写入数据
uint16 rb_put_force(ringbuff *rb,const uint8 *ptr,uint16 length) {
    uint16 space_length = 0;

    space_length = rb_space_len(rb);
    // cout<<"ptr: "<<ptr<<endl;
    // cout<<"space_length: "<<space_length<<endl;
    // cout<<"length: "<<length<<endl;
    if (length > space_length) { 
        ptr = &ptr[length - rb->size];
        length = rb->size;
    }
    // cout<<"ptr: "<<ptr<<endl;
    // cout<<"length: "<<length<<endl;

    if (rb->size - rb->write_index > length)
    {
        // cout<<"lailailailai"<<endl;
        /* read_index - write_index = empty space */
        memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->write_index += length;

        if (length > space_length)
            rb->read_index = rb->write_index;

        return length;
    }

    memcpy(&rb->buffer_ptr[rb->write_index],
           &ptr[0],
           rb->size - rb->write_index);

    memcpy(&rb->buffer_ptr[0],
           &ptr[rb->size - rb->write_index],
           length - (rb->size - rb->write_index));

    /* we are going into the other side of the mirror */
    rb->write_mirror = ~rb->write_mirror;
    rb->write_index = length - (rb->size - rb->write_index);

    if (length > space_length)
    {
        rb->read_mirror = ~rb->read_mirror;
        rb->read_index = rb->write_index;
    }

    return length;
}

🐞② 当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据

/* 缓冲区中填充指定数据长度的数据(当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据) */
uint16 rb_put(ringbuff *rb,const uint8 *ptr,uint16 length);
// 往 ringbuff 写入数据
uint16 rb_put(ringbuff *rb,const uint8 *ptr,uint16 length) {
    uint16 size = 0;

    /* whether has enough space */
    size = rb_space_len(rb);// 获取 ring_buff 中可用空间的大小

    /* no space */
    if (size == 0)
        return 0;// 如果空间不够 直接返回

    /* drop some data */
    if (size < length) // 如果缓存区的空间不够保存这一次数据, 则把能够写入的这一部分数据写进去
        length = size;
	/* One-time write */
    if (rb->size - rb->write_index > length)
    {
        /* read_index - write_index = empty space */
        memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->write_index += length;
        return length;// 返回写入数据的长度
    }
	/* two-time write */
    memcpy(&rb->buffer_ptr[rb->write_index],
           &ptr[0],
           rb->size - rb->write_index);
    memcpy(&rb->buffer_ptr[0],
           &ptr[rb->size - rb->write_index],
           length - (rb->size - rb->write_index));

    /* we are going into the other side of the mirror */
    rb->write_mirror = ~rb->write_mirror;
    rb->write_index = length - (rb->size - rb->write_index);

    return length;
}

6. 缓冲区中获取指定长度的数据(返回实际获取数据的长度)

/* 缓冲区中获取指定长度的数据(返回实际获取数据的长度) */
uint16 rb_get(ringbuff *rb,uint8 *ptr,uint16 length);
// 从 ringbuff 获取数据
uint16 rb_get(ringbuff *rb,uint8 *ptr,uint16 length) {
    uint16 size = 0;

    /* The length of the existing data in the buffer */
    size = rb_data_len(rb);

    /* no data */
    if (size == 0)
        return 0;

    /* less data */
    if (size < length)
        length = size;
    
    cout<<"size: "<<size<<" < "<<"length: " << length<<(size < length)<<endl;

    if (rb->size - rb->read_index > length)
    {
        /* copy all of data */
        memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->read_index += length;
        return length;
    }

    memcpy(&ptr[0],
           &rb->buffer_ptr[rb->read_index],
           rb->size - rb->read_index);
    memcpy(&ptr[rb->size - rb->read_index],
           &rb->buffer_ptr[0],
           length - (rb->size - rb->read_index));

    /* we are going into the other side of the mirror */
    rb->read_mirror = ~rb->read_mirror;
    rb->read_index = length - (rb->size - rb->read_index);

    return length;
}

7.测试和打印

void readprint(ringbuff* rb,uint8 buff[]) {
    cout<<"读取数据:";
    int i = 0;
    while(buff[i]!='\0') {
        cout<<buff[i++];
    }
    print(rb);
}

void writeprint(ringbuff* rb){
    for(int i=0;i<rb->size;i++){
        cout<<rb->buffer_ptr[i];
    }
    print(rb);
}

void test01() {
    ringbuff* rb = rb_create(9);

    const uint8 p[] = "123456789ABCDEF";
    uint32_t len = sizeof(p) / sizeof(char);
    
    cout<<"写入数据:"<<p<<endl;
    // rb_put(rb,p,len-1);
    rb_put_force(rb,p,len-1); 
    writeprint(rb);                       // 89ABCDEF

    uint8 saveBuff[20] = "";
    rb_get(rb,saveBuff,4);   // 89AB
    readprint(rb,saveBuff);
    
    const uint8 p1[] = "1234";
    cout<<"写入数据:"<<p1<<endl;
    rb_put_force(rb,p1,4);    // 1234CDEF
    writeprint(rb);

    memset(saveBuff,0,20);
    rb_get(rb,saveBuff,4);   // CDEF
    
    cout<<"读取数据:";
    readprint(rb,saveBuff);

    // 销毁ringbuff
    rb_destroy(rb);
}
写入数据:123456789ABCDEF
89ABCDEF
rb->write_index: 0
rb->read_index: 0
rb->write_mirror: 1
rb->read_mirror: 0
rb_data_len: 8
rb_space_len: 0

size: 8 < length: 40
读取数据:89AB
rb->write_index: 0
rb->read_index: 4
rb->write_mirror: 1
rb->read_mirror: 0
rb_data_len: 4
rb_space_len: 4

写入数据:1234
1234CDEF
rb->write_index: 4
rb->read_index: 4
rb->write_mirror: 1
rb->read_mirror: 0
rb_data_len: 8
rb_space_len: 0

size: 8 < length: 40
读取数据:读取数据:CDEF
rb->write_index: 4
rb->read_index: 0
rb->write_mirror: 1
rb->read_mirror: 1
rb_data_len: 4
rb_space_len: 4

销毁ringbuff~

三、缓冲区中填充一个数据

5. 缓冲区中填充一个数据

举个例子:(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据)

图1:依次存入1、2、3、4、5、6、7、8、A、B、C、D、E、F这些字符,直到缓冲区为满

图2:依次读出单个字符: 8、9、A、B

🐞① 当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据

/* 缓冲区中填充一个数据(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据) */
uint16 rb_putchar_force(ringbuff *rb, const uint8 ch);
// 往 ringbuff 强制写入一个字符
uint16 rb_putchar_force(ringbuff *rb, const uint8 ch) {
    enum rb_state old_state = rb_status(rb);// 获取状态
    rb->buffer_ptr[rb->write_index] = ch;// 写入数据
    /* flip mirror */
    if (rb->write_index == rb->size-1) {// 检查当前镜像是不是满了
        rb->write_mirror = ~rb->write_mirror; // 翻转写镜像
        rb->write_index = 0;// 翻转之后设置下标为 0
        if (old_state == RINGBUFFER_FULL) {// 如果 ringbuff 的状态是满
            rb->read_mirror = ~rb->read_mirror; // 翻转读镜像
            rb->read_index = rb->write_index; // 设置读下标和写下标一致
        }
    }else{
        rb->write_index++; // 写下标加1
        if (old_state == RINGBUFFER_FULL)
            rb->read_index = rb->write_index;// 如果满,设置读下标等于写下标
    }
    return 1; // 写入一个字符,返回1
}

🐞② 当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据

/* 缓冲区中填充一个数据(当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据) */
uint16 rb_putchar(ringbuff *rb, const uint8 ch);
// 往 ringbuff 中写入一个字符
uint16 rb_putchar(ringbuff *rb, const uint8 ch) {
    /* whether has enough space */
    if (!rb_space_len(rb)) // 没有足够的空间就直接返回了
        return 0;
    rb->buffer_ptr[rb->write_index] = ch;// 把这个字符写入到缓冲区的指定位置
    /* flip mirror */
    if (rb->write_index == rb->size-1) {// 检查写入这个字符后,当前镜像是否写满
        rb->write_mirror = ~rb->write_mirror;// 翻转镜像
        rb->write_index = 0;// 设置下标为0
    }else{
        rb->write_index++; // 下标加1
    }
    return 1;// 写入一个字符,返回 1
}

6. 缓冲区中获取一个数据(返回实际获取数据的长度)

/* 缓冲区中获取一个数据(返回实际获取数据的长度) */
uint16 rb_getchar(ringbuff *rb, uint8 *ch);
// 从ringbuff 获取一个字符
uint16 rb_getchar(ringbuff *rb,uint8 *ch) {
    /* ringbuffer is empty */
    if (!rb_data_len(rb)) // 检查 ringbuff 是否为空
        return 0;
    /* put character */
    *ch = rb->buffer_ptr[rb->read_index];// 获取当前读下标的数据
    if (rb->read_index == rb->size-1) {// 如果当前镜像满了
        rb->read_mirror = ~rb->read_mirror;// 翻转镜像
        rb->read_index = 0; // 设置读数据的下标为0
    } else {
        rb->read_index++; // 下标加1
    }

    return 1;// 读取一个字节,返回1
}

7.测试和打印

#include "rb.h"
#include "rb.cpp"
void print(ringbuff *rb) {
    cout<<endl;
    cout<<"rb->write_index: "<<rb->write_index<<endl;
    cout<<"rb->read_index: "<<rb->read_index<<endl;
    cout<<"rb->write_mirror: "<<rb->write_mirror<<endl;
    cout<<"rb->read_mirror: "<<rb->read_mirror<<endl;
    cout<<"rb_data_len: "<<rb_data_len(rb)<<endl;
    cout<<"rb_space_len: "<<rb_space_len(rb)<<endl;
    cout<<endl;
}

void writeprint(ringbuff* rb){
    for(int i=0;i<rb->size;i++){
        cout<<rb->buffer_ptr[i];
    }
    print(rb);
}

void test02() {
    ringbuff* rb = rb_create(9);
    cout<<"rb->size: "<<rb->size<<endl;
    const uint8 p[] = "123456789ABCDEF";
    uint32_t len = sizeof(p) / sizeof(char);
    // cout<<len<<endl;
    cout<<"写入数据:"<<p<<endl;
    for(int i=0;i<len-1;i++) {
        // rb_putchar(rb,p[i]); 
        rb_putchar_force(rb,p[i]); 
    }
    writeprint(rb); // 9ABCDEF8                      

    uint8 singlechar = ' ';
    for(int i=0;i<4;i++) {
        rb_getchar(rb,&singlechar);
        cout<<"读单个字符: "<<singlechar<<endl;
    }
    print(rb);

    // 销毁ringbuff
    rb_destroy(rb);
}
rb->size: 8
写入数据:123456789ABCDEF
9ABCDEF8
rb->write_index: 7
rb->read_index: 7
rb->write_mirror: 1
rb->read_mirror: 0
rb_data_len: 8
rb_space_len: 0

读单个字符: 8
读单个字符: 9
读单个字符: A
读单个字符: B

rb->write_index: 7
rb->read_index: 3
rb->write_mirror: 1
rb->read_mirror: 1
rb_data_len: 4
rb_space_len: 4

销毁ringbuff~

四、RT-Thread🦥小结

来自此文总结:ring buffer,一篇文章讲透它? - 知乎 (zhihu.com)

🦝 在多线程中,对同一个环形缓冲区进行读写操作时,需要加上锁,不然存在访问不安全问题;

🦝 当只有一个读线程和一个写线程时,用rb_put和rb_get进行读写操作缓冲区是线程安全的,无需加锁;但是rb_put_force不行,因为其可能对读写索引都进行操作的场景,这个时候再进行rb_get读操作,就是不安全访问;

🦝 读写指针已经在读写(rb_get和rb_put)过程中转换为了读写索引。所以read_index(读索引)和write_index(写索引)可以直接用来操作缓冲区,无需转换;

🦝 read_index 和write_index 的大小区间为[0,n−1],n为缓冲区大小;

🦝 RT-Thread的环形缓冲区不需要buffer大小为2的幂。

五、完整代码

rb.h

#ifndef RB
#define RB

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;

typedef unsigned char           uint8;
typedef signed char             int8;
typedef unsigned short          wchar;
typedef unsigned short int      uint16;
typedef signed short int        int16;
typedef unsigned int            uint32;
typedef signed int              int32;

typedef signed char 			int8_t;
typedef signed short int 		int16_t;
typedef signed int 				int32_t;
typedef unsigned char 			uint8_t;
typedef unsigned short int 		uint16_t;
typedef unsigned int 			uint32_t;

/*
 * Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
 * would return 12.*/
#define DATA_ALIGN_DOWN(size, align)      ((size) & ~((align) - 1))
/* DATA_ALIGN_SIZE*/
#define DATA_ALIGN_SIZE	4

/* ring buffer */
typedef struct ringbuffer
{
    uint8 *buffer_ptr;
    uint16 read_mirror : 1;
    uint16 read_index : 15;
    uint16 write_mirror : 1;
    uint16 write_index : 15;
    /* as we use msb of index as mirror bit, the size should be signed and
     * could only be positive. */
    uint16 size;
}ringbuff;

/* 缓冲区初始化 */
void rb_init(ringbuff *rb,uint8 *pool,uint16 size);
/* 创建一个ringbuff */
ringbuff* rb_create(uint16_t size);
/* 销毁一个ringbuff */
void rb_destroy(ringbuff *rb);

/* 缓冲区中填充指定数据长度的数据(当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据) */
uint16 rb_put(ringbuff *rb,const uint8 *ptr,uint16 length);
/* 缓冲区中填充指定数据长度的数据(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据) */
uint16 rb_put_force(ringbuff *rb,const uint8 *ptr,uint16 length);
/* 缓冲区中填充一个数据(当缓冲区空间小于待写入的数据长度事不覆盖缓冲区原有数据) */
uint16 rb_putchar(ringbuff *rb, const uint8 ch);
/* 缓冲区中填充一个数据(当缓冲区空间小于待写入的数据长度事覆盖缓冲区原有数据) */
uint16 rb_putchar_force(ringbuff *rb, const uint8 ch);

/* 缓冲区中获取指定长度的数据(返回实际获取数据的长度) */
uint16 rb_get(ringbuff *rb,uint8 *ptr,uint16 length);
/* 缓冲区中获取一个数据(返回实际获取数据的长度) */
uint16 rb_getchar(ringbuff *rb, uint8 *ch);

/* 获取 ringbuff 中数据的长度 */
uint16 rb_data_len(ringbuff *rb);

/** return the size of empty space in rb */
#define rb_space_len(rb) ((rb)->size - rb_data_len(rb))

#endif /* RB */

rb.cpp

#include "rb.h"
enum rb_state
{
    RINGBUFFER_EMPTY,
    RINGBUFFER_FULL,
    RINGBUFFER_HALFFULL, /* half full is neither full nor empty */
};
// 获取 ringbuff 的状态
enum rb_state rb_status(ringbuff *rb) {
    if (rb->read_index == rb->write_index)
    {
        if (rb->read_mirror == rb->write_mirror)
            return RINGBUFFER_EMPTY;
        else
            return RINGBUFFER_FULL;
    }
    return RINGBUFFER_HALFFULL;
}

// 获取 ringbuff 中数据的长度
uint16 rb_data_len(ringbuff *rb) {
    switch (rb_status(rb)) {
        case RINGBUFFER_EMPTY:
            return 0;//空就返回 0 
        case RINGBUFFER_FULL:
            return rb->size;// 满就返回缓冲区的大小
        case RINGBUFFER_HALFFULL:// 半满
        default:
            if (rb->write_index > rb->read_index) // 如果在同一镜像
                return rb->write_index - rb->read_index; // 返回下标差
            else
                return rb->size - (rb->read_index - rb->write_index); // 如果不在同一镜像,通过计算获取数据的长度
    };
}

// 缓冲区初始化 
void rb_init(ringbuff *rb,uint8 *pool,uint16 size){
    /* initialize read and write index */
    rb->read_mirror = rb->read_index = 0;
    rb->write_mirror = rb->write_index = 0;

    /* set buffer pool and size */
    rb->buffer_ptr = pool;
    rb->size = DATA_ALIGN_DOWN(size, DATA_ALIGN_SIZE);
}

// 创建一个ringbuff
ringbuff* rb_create(uint16_t size) {
    ringbuff *rb;
    uint8_t *pool;

    size = DATA_ALIGN_DOWN(size, DATA_ALIGN_SIZE);// 大小做字节对齐

    rb = (ringbuff *)malloc(sizeof(ringbuff));// 申请内存
    if (rb == NULL)
        goto exit;

    pool = (uint8_t *)malloc(size);// 申请数据缓冲区内存
    if (pool == NULL) {
        free(rb);
        rb = NULL;
        goto exit;
    }
    rb_init(rb, pool, size);// 初始化 ringbuff

exit:
    return rb;
}

// 往 ringbuff 写入数据
uint16 rb_put(ringbuff *rb,const uint8 *ptr,uint16 length) {
    uint16 size = 0;

    /* whether has enough space */
    size = rb_space_len(rb);// 获取 ring_buff 中可用空间的大小

    /* no space */
    if (size == 0)
        return 0;// 如果空间不够 直接返回

    /* drop some data */
    if (size < length) // 如果缓存区的空间不够保存这一次数据, 则把能够写入的这一部分数据写进去
        length = size;
	/* One-time write */
    if (rb->size - rb->write_index > length)
    {
        /* read_index - write_index = empty space */
        memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->write_index += length;
        return length;// 返回写入数据的长度
    }
	/* two-time write */
    memcpy(&rb->buffer_ptr[rb->write_index],
           &ptr[0],
           rb->size - rb->write_index);
    memcpy(&rb->buffer_ptr[0],
           &ptr[rb->size - rb->write_index],
           length - (rb->size - rb->write_index));

    /* we are going into the other side of the mirror */
    rb->write_mirror = ~rb->write_mirror;
    rb->write_index = length - (rb->size - rb->write_index);

    return length;
}

// 强制往 ringbuff 写入数据
uint16 rb_put_force(ringbuff *rb,const uint8 *ptr,uint16 length) {
    uint16 space_length = 0;

    space_length = rb_space_len(rb);
    // cout<<"ptr: "<<ptr<<endl;
    // cout<<"space_length: "<<space_length<<endl;
    // cout<<"length: "<<length<<endl;
    if (length > space_length) { 
        ptr = &ptr[length - rb->size];
        length = rb->size;
    }
    // cout<<"ptr: "<<ptr<<endl;
    // cout<<"length: "<<length<<endl;

    if (rb->size - rb->write_index > length)
    {
        // cout<<"lailailailai"<<endl;
        /* read_index - write_index = empty space */
        memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->write_index += length;

        if (length > space_length)
            rb->read_index = rb->write_index;

        return length;
    }

    memcpy(&rb->buffer_ptr[rb->write_index],
           &ptr[0],
           rb->size - rb->write_index);

    memcpy(&rb->buffer_ptr[0],
           &ptr[rb->size - rb->write_index],
           length - (rb->size - rb->write_index));

    /* we are going into the other side of the mirror */
    rb->write_mirror = ~rb->write_mirror;
    rb->write_index = length - (rb->size - rb->write_index);

    if (length > space_length)
    {
        rb->read_mirror = ~rb->read_mirror;
        rb->read_index = rb->write_index;
    }

    return length;
}

// 从 ringbuff 获取数据
uint16 rb_get(ringbuff *rb,uint8 *ptr,uint16 length) {
    uint16 size = 0;

    /* The length of the existing data in the buffer */
    size = rb_data_len(rb);

    /* no data */
    if (size == 0)
        return 0;

    /* less data */
    if (size < length)
        length = size;
    
    cout<<"size: "<<size<<" < "<<"length: " << length<<(size < length)<<endl;

    if (rb->size - rb->read_index > length)
    {
        /* copy all of data */
        memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
        /* this should not cause overflow because there is enough space for
         * length of data in current mirror */
        rb->read_index += length;
        return length;
    }

    memcpy(&ptr[0],
           &rb->buffer_ptr[rb->read_index],
           rb->size - rb->read_index);
    memcpy(&ptr[rb->size - rb->read_index],
           &rb->buffer_ptr[0],
           length - (rb->size - rb->read_index));

    /* we are going into the other side of the mirror */
    rb->read_mirror = ~rb->read_mirror;
    rb->read_index = length - (rb->size - rb->read_index);

    return length;
}

// 往 ringbuff 中写入一个字符
uint16 rb_putchar(ringbuff *rb, const uint8 ch) {
    /* whether has enough space */
    if (!rb_space_len(rb)) // 没有足够的空间就直接返回了
        return 0;
    rb->buffer_ptr[rb->write_index] = ch;// 把这个字符写入到缓冲区的指定位置
    /* flip mirror */
    if (rb->write_index == rb->size-1) {// 检查写入这个字符后,当前镜像是否写满
        rb->write_mirror = ~rb->write_mirror;// 翻转镜像
        rb->write_index = 0;// 设置下标为0
    }else{
        rb->write_index++; // 下标加1
    }
    return 1;// 写入一个字符,返回 1
}

// 往 ringbuff 强制写入一个字符
uint16 rb_putchar_force(ringbuff *rb, const uint8 ch) {
    enum rb_state old_state = rb_status(rb);// 获取状态
    rb->buffer_ptr[rb->write_index] = ch;// 写入数据
    /* flip mirror */
    if (rb->write_index == rb->size-1) {// 检查当前镜像是不是满了
        rb->write_mirror = ~rb->write_mirror; // 翻转写镜像
        rb->write_index = 0;// 翻转之后设置下标为 0
        if (old_state == RINGBUFFER_FULL) {// 如果 ringbuff 的状态是满
            rb->read_mirror = ~rb->read_mirror; // 翻转读镜像
            rb->read_index = rb->write_index; // 设置读下标和写下标一致
        }
    }else{
        rb->write_index++; // 写下标加1
        if (old_state == RINGBUFFER_FULL)
            rb->read_index = rb->write_index;// 如果满,设置读下标等于写下标
    }
    return 1; // 写入一个字符,返回1
}

// 从ringbuff 获取一个字符
uint16 rb_getchar(ringbuff *rb,uint8 *ch) {
    /* ringbuffer is empty */
    if (!rb_data_len(rb)) // 检查 ringbuff 是否为空
        return 0;
    /* put character */
    *ch = rb->buffer_ptr[rb->read_index];// 获取当前读下标的数据
    if (rb->read_index == rb->size-1) {// 如果当前镜像满了
        rb->read_mirror = ~rb->read_mirror;// 翻转镜像
        rb->read_index = 0; // 设置读数据的下标为0
    } else {
        rb->read_index++; // 下标加1
    }

    return 1;// 读取一个字节,返回1
}

// 摧毁 ringbuff
void rb_destroy(ringbuff *rb) {   
    cout<<"销毁ringbuff~"<<endl;    
    free(rb->buffer_ptr);
    free(rb);// 释放申请的内存
}

test.cpp

#include "rb.h"
#include "rb.cpp"
void print(ringbuff *rb) {
    cout<<endl;
    cout<<"rb->write_index: "<<rb->write_index<<endl;
    cout<<"rb->read_index: "<<rb->read_index<<endl;
    cout<<"rb->write_mirror: "<<rb->write_mirror<<endl;
    cout<<"rb->read_mirror: "<<rb->read_mirror<<endl;
    cout<<"rb_data_len: "<<rb_data_len(rb)<<endl;
    cout<<"rb_space_len: "<<rb_space_len(rb)<<endl;
    cout<<endl;
}

void readprint(ringbuff* rb,uint8 buff[]) {
    cout<<"读取数据:";
    int i = 0;
    while(buff[i]!='\0') {
        cout<<buff[i++];
    }
    print(rb);
}

void writeprint(ringbuff* rb){
    for(int i=0;i<rb->size;i++){
        cout<<rb->buffer_ptr[i];
    }
    print(rb);
}

void test01() {
    ringbuff* rb = rb_create(9);

    const uint8 p[] = "123456789ABCDEF";
    uint32_t len = sizeof(p) / sizeof(char);
    
    cout<<"写入数据:"<<p<<endl;
    // rb_put(rb,p,len-1);
    rb_put_force(rb,p,len-1); 
    writeprint(rb);                       // 89ABCDEF

    uint8 saveBuff[20] = "";
    rb_get(rb,saveBuff,4);   // 89AB
    readprint(rb,saveBuff);
    
    const uint8 p1[] = "1234";
    cout<<"写入数据:"<<p1<<endl;
    rb_put_force(rb,p1,4);    // 1234CDEF
    writeprint(rb);

    memset(saveBuff,0,20);
    rb_get(rb,saveBuff,4);   // CDEF
    
    cout<<"读取数据:";
    readprint(rb,saveBuff);

    // 销毁ringbuff
    rb_destroy(rb);
}

void test02() {
    ringbuff* rb = rb_create(9);
    cout<<"rb->size: "<<rb->size<<endl;
    const uint8 p[] = "123456789ABCDEF";
    uint32_t len = sizeof(p) / sizeof(char);
    // cout<<len<<endl;
    cout<<"写入数据:"<<p<<endl;
    for(int i=0;i<len-1;i++) {
        // rb_putchar(rb,p[i]); 
        rb_putchar_force(rb,p[i]); 
    }
    writeprint(rb); // 9ABCDEF8                      

    uint8 singlechar = ' ';
    for(int i=0;i<4;i++) {
        rb_getchar(rb,&singlechar);
        cout<<"读单个字符: "<<singlechar<<endl;
    }
    print(rb);

    // 销毁ringbuff
    rb_destroy(rb);
}
int main() {
    // test01();
    test02();
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/82815.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux:shell脚本:基础使用(5)《正则表达式-sed工具》

sed是一种流编辑器&#xff0c;它是文本处理中非常中的工具&#xff0c;能够完美的配合正则表达式使用&#xff0c;功能不同凡响。 处理时&#xff0c;把当前处理的行存储在临时缓冲区中&#xff0c;称为“模式空间”&#xff08;pattern space&#xff09;&#xff0c;接着用s…

微服务概述-7

Shiro 框架 Shiro 是一个用于 Java 应用程序的安全框架。它提供了身份验证、授权、加密和会话管理等功能&#xff0c;可以帮助开发人员构建安全可靠的应用程序。 Java 中针对权限管理常见的有 2 个著名的框架&#xff1a;spring security 和 shiro shiro 基本概念 credentia…

AI引擎助力,CamScanner智能高清滤镜开启扫描新纪元!

文章目录 ⭐ 写在前面⭐ 突破图像处理难点&#xff1a;扫描全能王的独特优势⭐ 耳听为虚&#xff0c;眼见为实⭐ 产品背后的主要核心&#xff1a;AI-Scan助力⭐ 深度学习助力智能文档处理的国际化进程⭐ 品味智能文档处理的轻松与精准 ⭐ 写在前面 在数字化快速发展的今天&…

AE-卡通人物解说动画视频的制作

目录 1.导入卡通人物图片和音频文件 2.新建合成 3.在卡通人物图片上添加效果和表达式 4.在音频文件上添加效果和表达式 5.将卡通人物中的 CC Split2 中分割1 表达式链接到滑块中 6.卡通人物根据音频文件自动匹配口型。 AE制作卡通人物解说视频&#xff0c;卡通人物口型根据…

服务监控平台:SpringBoot Admin入门应用

前言 在日常工作中&#xff0c;我们需要有一款监控平台来帮助我们管理服务&#xff0c;监控服务是否宕机、服务运行指标&#xff08;内存、虚拟机、线程、请求等&#xff09;、监控日志、管理服务&#xff08;服务下线&#xff09;等&#xff0c;SpringBoot Admin作为一款开源…

分布式 | 如何搭建 DBLE 的 JVM 指标监控系统

本篇文章采用 Docker 方式搭建 Grafana Prometheus 实现对 DBLE 的 JVM 相关指标的监控系统。 作者&#xff1a;文韵涵 爱可生 DBLE 团队开发成员&#xff0c;主要负责 DBLE 需求开发&#xff0c;故障排查和社区问题解答。 本文来源&#xff1a;原创投稿 爱可生开源社区出品&a…

湘潭大学 湘大 XTU OJ 1217 A+B VII 题解(非常详细)

链接 1217 题目 题目描述 小明非常高兴你能帮他处理那些罗马数字&#xff0c;他想学着自己写罗马数字&#xff0c;但是他不知道自己到底写对了没有。 请你帮他写个程序&#xff0c;能正确地将10进制数字转换成罗马数字&#xff0c;以便他能进行核对。 罗马数字是使用字母组…

《修图大杀器》PS beta 25.0最新版安装(无需魔法)和Draggan(拖拽式修图)安装

个人网站&#xff1a;https://tianfeng.space 文章目录 psbeta下载安装1.注册2.安装ps beta2.安装神经网络滤镜3.使用 Draggan下载安装 psbeta下载安装 链接&#xff1a;https://pan.baidu.com/s/1XbxSAFoXh0HDz6YbkrAzDg 提取码&#xff1a;e8pn 1.注册 https://account.a…

chatGPT-对话爱因斯坦

引言 阿尔伯特爱因斯坦&#xff08; 1879年 3 月 14 日 – 1955 年 4 月 18 日&#xff09;是一位出生于德国的理论物理学家&#xff0c;被广泛认为成为有史以来最伟大、最有影响力的科学家之一。他以发展相对论而闻名&#xff0c;他还对量子力学做出了重要贡献&#xff0c;因…

通过网络流量报告监控网络性能

实时网络流量监控已被组织广泛采用&#xff0c;作为了解网络性能和拥塞问题的首选技术。但是&#xff0c;有几个网络问题需要一个超越实时流量监控的解决方案。网络中的持续滞后可能会无人值守并影响整个网络的效率&#xff0c;使用网络流量报告将有助于管理网络环境中的风险。…

QT 基本对话框

包括&#xff1a; 1.标准文件对话框 dialog.h #ifndef DIALOG_H #define DIALOG_H#include <QDialog> #include <QTextCodec> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QGridLayout> #include <QFr…

漏洞指北-VulFocus靶场专栏-初级01

漏洞指北-VulFocus靶场专栏-初级 初级001 &#x1f338;海洋CMS代码执行&#xff08;CNVD-2020-22721&#x1f338;step1&#xff1a;进入后台页面 账号密码&#xff1a;admin amdinstep2&#xff1a;点击系统&#xff0c;点击后台IP安全设置,关闭step3 启动burpsuite&#xff…

SQL助你面大厂(窗口函数)

在面试过程中窗口函数的应用可谓是数不胜数&#xff0c;前提你要知道什么是窗口函数&#xff0c;最常用的窗口函数有哪些&#xff1f;语法是什么&#xff1f;分别用的场景是什么&#xff1f;今天会以这三个问题开始我们今天的学习 什么是窗口函数&#xff1f; 所谓的窗口函数就…

SQLite数据库实现数据增删改查

当前文章介绍的设计的主要功能是利用 SQLite 数据库实现宠物投喂器上传数据的存储&#xff0c;并且支持数据的增删改查操作。其中&#xff0c;宠物投喂器上传的数据包括投喂间隔时间、水温、剩余重量等参数。 实现功能&#xff1a; 创建 SQLite 数据库表&#xff0c;用于存储宠…

【OFDM系列】DFT为什么能求频率幅度谱?DFT后的X[k]与x(n)幅度的关系?DFT/IDFT底层数学原理?

文章目录 问题引入铺垫一些小公式DFT公式证明DFT公式分解为4部分先考虑k10的情况:再考虑k1≠0的情况: DFT计算后&#xff0c;X(k)与x(n)的关系&#xff1a; Matlab FFT示例代码IDFT公式证明Matlab调用FFT/IFFT并绘图 问题引入 上面是DFT和IDFT的公式&#xff0c;IDFT先不谈。在…

构建 NodeJS 影院微服务并使用 docker 部署它(02/4)

一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的…

netty(一):NIO——处理消息边界

处理消息边界 为什么要处理边界 因为会存在半包和粘包的问题 1.客户端和服务端约定一个固定长度 优点&#xff1a;简单 缺点&#xff1a;可能造成浪费 2.客户端与服务端约定一个固定分割符 *缺点 效率低 3.先发送长度&#xff0c;再发送数据 TLV格式&#xff1a; type…

Git分布式版本控制系统

目录 2、安装git 2.1 初始环境 2.2 Yum安装Git 2.3 编译安装 2.4 初次运行 Git 前的配置 2.5 初始化及获取 Git 仓库 2.6 Git命令常规操作 2.6.2 添加新文件 2.6.3 删除git内的文件 2.6.4 重命名暂存区数据 2.6.5 查看历史记录 2.6.6 还原历史数据 2.6.7 还原未来…

星际争霸之小霸王之小蜜蜂(四)--事件监听-让小蜜蜂动起来

目录 前言 一、监听按键并作出判断 二、持续移动 三、左右移动 总结&#xff1a; 前言 今天开始正式操控我们的小蜜蜂了&#xff0c;之前学java的时候是有一个函数监听鼠标和键盘的操作&#xff0c;我们通过传过来不同的值进行判断&#xff0c;现在来看看python是否一样的实现…

lvs-DR

lvs-DR数据包流向分析 client向目标VIP发出请求。 DIR根据负载均衡算法一台active的RS&#xff08;RIR1&#xff09;&#xff0c;将RIP1所在的网卡的mac地址作为目标的mac地址&#xff0c;发送到局域网里。 RIRI在局域网中的收到这个帧&#xff0c;拆开后发现目标&#xff08…