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

这篇具有很好参考价值的文章主要介绍了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,则缓冲区为满,这可以用条件表达式(写指针==(读指针异或缓冲区长度))来判断。

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

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

一、基本步骤

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. 缓冲区中填充指定数据长度的数据

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

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

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

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

图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]

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

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

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

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

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

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

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

 图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. 缓冲区中填充一个数据

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

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

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

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

图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文章来源地址https://www.toymoban.com/news/detail-660711.html

#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;
}

到了这里,关于RT-Thread 的环形缓冲区 ---- 镜像指示位的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 8.缓冲区管理

    双缓冲区:TC+M 假设初始状态缓冲区1满,缓冲区2空,工作区为空。 刚开始缓冲区2为空,所以设备可以向缓冲区2中冲入数据耗时T,另一方面刚开始缓冲区1中是满的,所以刚开始就可以把缓冲区1中的数据传送到工作区中,M时刻工作区被充满,CPU就开始处理数据耗时C,处理完

    2024年02月11日
    浏览(27)
  • 【Linux】理解缓冲区

    我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关! C接口的函数被打印了两次系统接口前后只是打印了一次:和fork函数有关,fork会创建子进程。在创建子进程的时候,数据会被处理成两份,父子进程发生写时拷

    2024年01月23日
    浏览(44)
  • Redis 缓冲区

    缓冲区的应用场景 : 客户端与服务器端的通信时,暂存客户端发送的命令数据,或暂存服务器端返给客户端的数据结果 主从节点间进行数据同步时,暂存主节点接收的写命令和数据 缓冲区 : 避免客户端和服务器端的请求发送和处理速度不匹配 服务器给每个连接的客户端都准

    2024年02月07日
    浏览(58)
  • 【Linux】文件缓冲区

    提到文件缓冲区这个概念我们好像并不陌生,但是我们对于这个概念好像又是模糊的存在脑海中,之间我们在介绍c语言文件操作已经简单的提过这个概念,今天我们不妨深入理解什么是文件缓冲区 通过自己实现库中的一些文件操作函数更加深入的理解文件缓冲区 自定义实现

    2024年02月10日
    浏览(44)
  • 理解缓冲区

    对于这样的代码,首先可以肯定的是 printf 语句先于 sleep 执行,既然如此那么就应该是先打印语句然后进行休眠,下面看看结果: 但这里却是先休眠以后再打印语句,这是因为存在一个叫缓冲区的东西,当我们要向外设写入数据(让显示器显示就是向显示器写入数据)时会将

    2023年04月25日
    浏览(63)
  • C/C++缓冲区

    什么是缓冲区? 程序和磁盘文件之间不能直接交换数据,必须通过内存中一个被称为文件缓冲区的区域来中转。ANSIC标准规定,系统会自动为每个正在使用的文件在内存中开辟一个缓冲区,缓冲区的大小随机器而异。 缓冲区有什么作用? 假设我们在家中休息看电视吃零食,

    2024年02月15日
    浏览(37)
  • SEED-缓冲区溢出攻击

    实验环境:SEED-Ubuntu20.04虚拟机 a) 缓冲区溢出原理 **缓冲区溢出攻击原理:**利用溢出的数据改变源程序的控制流,如覆盖返回地址 b) 分析生成badfile文件的exploit.py程序 Shellcode部分 字节数组末尾处填入shellcode c) 编译目标服务器上具有缓冲区溢出漏洞的stack.c程序,并将其缓冲

    2024年02月07日
    浏览(35)
  • 【Linux】深入理解缓冲区

    目录 什么是缓冲区 为什么要有缓冲区 缓冲区刷新策略 缓冲区在哪里  手动设计一个用户层缓冲区 缓冲区本质上一块内存区域,用来保存临时数据。 缓冲区在各种计算任务中都广泛应用,包括输入/输出操作、网络通信、图像处理、音频处理等。 这块内存区域是由 谁提供的

    2024年02月15日
    浏览(54)
  • 【linux】重定向+缓冲区

    自我名言 : 只有努力,才能追逐梦想,只有努力,才不会欺骗自己。 喜欢的点赞,收藏,关注一下把! close(1),为什么没有打印新建文件fd呢? printf(“%dn”,fd); printf会把内容打印到stdout文件中。 但是close(1)关闭标准输出stdout—显示器,int fd=open();新打开的文件fd是1。 st

    2024年02月08日
    浏览(37)
  • [操作系统安全]缓冲区溢出

    一、C栈帧结构 函数调用内存中的三个区域,代码区、静态数据区、动态数据区(压栈和清栈就是在这个区域完成的)。 CPU 中有三个寄存器,分别是eip、ebp和esp。eip永远指向代码区中将要执行的下一条指令,执行方式包括顺序执行和跳转;ebp和esp用于管理栈空间,ebp指向栈底

    2024年02月03日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包