/****************************************************************************** 版权所有 (C), 2018-2099, Radkil_Std ****************************************************************************** 文 件 名 : ringbuffer.c 版 本 号 : 初稿 作 者 : Radkil 生成日期 : 2023年3月26日 最近修改 : 功能描述 : ringbuffer环形缓冲区实现 修改历史 : 1.日 期 : 2023年3月26日 作 者 : Radkil 修改内容 : 创建文件 ******************************************************************************/ #include #include #include #include "ringbuffer.h" #include "common.h" /*----------------------------------------------* * 外部变量说明 * *----------------------------------------------*/ /*----------------------------------------------* * 外部函数原型说明 * *----------------------------------------------*/ /*----------------------------------------------* * 内部函数原型说明 * *----------------------------------------------*/ /*----------------------------------------------* * 全局变量 * *----------------------------------------------*/ /*----------------------------------------------* * 模块级变量 * *----------------------------------------------*/ /*----------------------------------------------* * 常量定义 * *----------------------------------------------*/ /*----------------------------------------------* * 宏定义 * *----------------------------------------------*/ /** * @brief Initialize the ring buffer object. * * @param rb A pointer to the ring buffer object. * @param pool A pointer to the buffer. * @param size The size of the buffer in bytes. */ void rd_RingbufferInit(rd_ringbuf_t *rb, char *pool, int size) { if (!rb || !pool || size <= 0) return; /* 原子方式初始化索引与镜像位 */ ATOMIC_STORE(&rb->m_sReadIndex, 0, unsigned short, ATOMIC_ORDER_RELAXED); ATOMIC_STORE(&rb->m_sWriteIndex, 0, unsigned short, ATOMIC_ORDER_RELAXED); ATOMIC_STORE(&rb->m_bReadMirror, 0, unsigned char, ATOMIC_ORDER_RELAXED); ATOMIC_STORE(&rb->m_bWriteMirror, 0, unsigned char, ATOMIC_ORDER_RELAXED); rb->m_pcBufPtr = pool; rb->m_iBufsize = RD_ALIGN_DOWN(size, RD_ALIGN_SIZE); } /* 计算可写空间(原子读取索引) */ int rd_RingbufferSpaceLen(rd_ringbuf_t *rb) { if (!rb) return -1; unsigned short wi = ATOMIC_LOAD(&rb->m_sWriteIndex, unsigned short, ATOMIC_ORDER_ACQUIRE); unsigned short ri = ATOMIC_LOAD(&rb->m_sReadIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char wb = ATOMIC_LOAD(&rb->m_bWriteMirror, unsigned char, ATOMIC_ORDER_ACQUIRE); unsigned char rb_m = ATOMIC_LOAD(&rb->m_bReadMirror, unsigned char, ATOMIC_ORDER_RELAXED); if (wb == rb_m) { return rb->m_iBufsize - (wi - ri); } else { return ri - wi; } } /* 获取环形缓冲区状态(读/写索引) */ static enum ERingBufState rd_RingbufferStatus(rd_ringbuf_t *rb) { if (!rb) return RINGBUFFER_EMPTY; unsigned short wi = ATOMIC_LOAD(&rb->m_sWriteIndex, unsigned short, ATOMIC_ORDER_ACQUIRE); unsigned short ri = ATOMIC_LOAD(&rb->m_sReadIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char wb = ATOMIC_LOAD(&rb->m_bWriteMirror, unsigned char, ATOMIC_ORDER_ACQUIRE); unsigned char rb_m = ATOMIC_LOAD(&rb->m_bReadMirror, unsigned char, ATOMIC_ORDER_RELAXED); if (wi == ri) { return (wb == rb_m) ? RINGBUFFER_EMPTY : RINGBUFFER_FULL; } return RINGBUFFER_HALFFULL; } /** * @brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will discard out-of-range data. * * @param rb A pointer to the ring buffer object. * @param ptr A pointer to the data buffer. * @param length The size of data in bytes. * * @return Return the data size we put into the ring buffer. */ int rd_RingbufferPut(rd_ringbuf_t *rb, const char *ptr, int length) { if (!rb || !ptr || length <= 0) return RD_INVALUE; int space = rd_RingbufferSpaceLen(rb); if (space == 0) return 0; if (space < length) length = space; unsigned short wi = ATOMIC_LOAD(&rb->m_sWriteIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char wb = ATOMIC_LOAD(&rb->m_bWriteMirror, unsigned char, ATOMIC_ORDER_RELAXED); /* 写入不跨越尾部 */ if (rb->m_iBufsize - wi > length) { RD_MEMCPY(&rb->m_pcBufPtr[wi], ptr, length); wi += length; } else { int first = rb->m_iBufsize - wi; RD_MEMCPY(&rb->m_pcBufPtr[wi], ptr, first); RD_MEMCPY(&rb->m_pcBufPtr[0], ptr + first, length - first); wb ^= 1; /* 翻转镜像位 */ wi = length - first; } ATOMIC_STORE(&rb->m_sWriteIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); ATOMIC_STORE(&rb->m_bWriteMirror, wb, unsigned char, ATOMIC_ORDER_RELEASE); return length; } /** * @brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will overwrite the existing data in the ring buffer. * * @param rb A pointer to the ring buffer object. * @param ptr A pointer to the data buffer. * @param length The size of data in bytes. * * @return Return the data size we put into the ring buffer. */ int rd_RingbufferPutForce(rd_ringbuf_t *rb, const char *ptr, int length) { if (!rb || !ptr || length <= 0) return RD_INVALUE; int space = rd_RingbufferSpaceLen(rb); if (length > rb->m_iBufsize) { ptr += length - rb->m_iBufsize; length = rb->m_iBufsize; } unsigned short wi = ATOMIC_LOAD(&rb->m_sWriteIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char wb = ATOMIC_LOAD(&rb->m_bWriteMirror, unsigned char, ATOMIC_ORDER_RELAXED); if (rb->m_iBufsize - wi > length) { RD_MEMCPY(&rb->m_pcBufPtr[wi], ptr, length); wi += length; if (length > space) { ATOMIC_STORE(&rb->m_sReadIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); } } else { int first = rb->m_iBufsize - wi; RD_MEMCPY(&rb->m_pcBufPtr[wi], ptr, first); RD_MEMCPY(&rb->m_pcBufPtr[0], ptr + first, length - first); wb ^= 1; wi = length - first; if (length > space) { unsigned short ri = ATOMIC_LOAD(&rb->m_sReadIndex, unsigned short, ATOMIC_ORDER_RELAXED); if (wi <= ri) wb ^= 1; ATOMIC_STORE(&rb->m_sReadIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); } } ATOMIC_STORE(&rb->m_sWriteIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); ATOMIC_STORE(&rb->m_bWriteMirror, wb, unsigned char, ATOMIC_ORDER_RELEASE); return length; } /** * @brief Get data from the ring buffer. * * @param rb A pointer to the ring buffer. * @param ptr A pointer to the data buffer. * @param length The size of the data we want to read from the ring buffer. * * @return Return the data size we read from the ring buffer. */ int rd_RingbufferGet(rd_ringbuf_t *rb, char *ptr, int length) { if (!rb || !ptr || length <= 0) return RD_INVALUE; int avail = rd_RingbufferDataLen(rb); if (avail == 0) return 0; if (length > avail) length = avail; unsigned short ri = ATOMIC_LOAD(&rb->m_sReadIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char rb_m = ATOMIC_LOAD(&rb->m_bReadMirror, unsigned char, ATOMIC_ORDER_RELAXED); if (rb->m_iBufsize - ri > length) { RD_MEMCPY(ptr, &rb->m_pcBufPtr[ri], length); ri += length; } else { int first = rb->m_iBufsize - ri; RD_MEMCPY(ptr, &rb->m_pcBufPtr[ri], first); RD_MEMCPY(ptr + first, &rb->m_pcBufPtr[0], length - first); rb_m ^= 1; ri = length - first; } ATOMIC_STORE(&rb->m_sReadIndex, ri, unsigned short, ATOMIC_ORDER_RELEASE); ATOMIC_STORE(&rb->m_bReadMirror, rb_m, unsigned char, ATOMIC_ORDER_RELEASE); return length; } /** * @brief Get the first readable byte of the ring buffer. * * @param rb A pointer to the ringbuffer. * @param ptr When this function return, *ptr is a pointer to the first readable byte of the ring buffer. * * @note It is recommended to read only one byte, otherwise it may cause buffer overflow. * * @return Return the size of the ring buffer. */ int rd_RingbufferPeak(rd_ringbuf_t *rb, char **ptr) { if (!rb) return RD_INVALUE; *ptr = NULL; int avail = rd_RingbufferDataLen(rb); if (avail == 0) return 0; unsigned short ri = ATOMIC_LOAD(&rb->m_sReadIndex, unsigned short, ATOMIC_ORDER_RELAXED); *ptr = &rb->m_pcBufPtr[ri]; return avail; } /** * @brief Put a byte into the ring buffer. If ring buffer is full, this operation will fail. * * @param rb A pointer to the ring buffer object. * @param ch A byte put into the ring buffer. * * @return Return the data size we put into the ring buffer. The ring buffer is full if returns 0. Otherwise, it will return 1. */ int rd_RingbufferPutchar(rd_ringbuf_t *rb, const char ch) { if (!rb) return RD_INVALUE; if (!rd_RingbufferSpaceLen(rb)) return 0; unsigned short wi = ATOMIC_LOAD(&rb->m_sWriteIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char wb = ATOMIC_LOAD(&rb->m_bWriteMirror, unsigned char, ATOMIC_ORDER_RELAXED); rb->m_pcBufPtr[wi] = ch; if (wi + 1 == rb->m_iBufsize) { wb ^= 1; wi = 0; } else { wi++; } ATOMIC_STORE(&rb->m_sWriteIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); ATOMIC_STORE(&rb->m_bWriteMirror, wb, unsigned char, ATOMIC_ORDER_RELEASE); return 1; } /** * @brief Put a byte into the ring buffer. If ring buffer is full, it will discard an old data and put into a new data. * * @param rb A pointer to the ring buffer object. * @param ch A byte put into the ring buffer. * * @return Return the data size we put into the ring buffer. Always return 1. */ int rd_RingbufferPutcharForce(rd_ringbuf_t *rb, const char ch) { if (!rb) return RD_INVALUE; enum ERingBufState old_state = rd_RingbufferStatus(rb); unsigned short wi = ATOMIC_LOAD(&rb->m_sWriteIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char wb = ATOMIC_LOAD(&rb->m_bWriteMirror, unsigned char, ATOMIC_ORDER_RELAXED); rb->m_pcBufPtr[wi] = ch; if (wi + 1 == rb->m_iBufsize) { wb ^= 1; wi = 0; if (old_state == RINGBUFFER_FULL) { unsigned char rb_m = ATOMIC_LOAD(&rb->m_bReadMirror, unsigned char, ATOMIC_ORDER_RELAXED); rb_m ^= 1; ATOMIC_STORE(&rb->m_bReadMirror, rb_m, unsigned char, ATOMIC_ORDER_RELEASE); ATOMIC_STORE(&rb->m_sReadIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); } } else { wi++; if (old_state == RINGBUFFER_FULL) { ATOMIC_STORE(&rb->m_sReadIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); } } ATOMIC_STORE(&rb->m_sWriteIndex, wi, unsigned short, ATOMIC_ORDER_RELEASE); ATOMIC_STORE(&rb->m_bWriteMirror, wb, unsigned char, ATOMIC_ORDER_RELEASE); return 1; } /** * @brief Get a byte from the ring buffer. * * @param rb The pointer to the ring buffer object. * @param ch A pointer to the buffer, used to store one byte. * * @return 0 The ring buffer is empty. * @return 1 Success */ int rd_RingbufferGetchar(rd_ringbuf_t *rb, char *ch) { if (!rb) return RD_INVALUE; if (!rd_RingbufferDataLen(rb)) return 0; unsigned short ri = ATOMIC_LOAD(&rb->m_sReadIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char rb_m = ATOMIC_LOAD(&rb->m_bReadMirror, unsigned char, ATOMIC_ORDER_RELAXED); *ch = rb->m_pcBufPtr[ri]; if (ri + 1 == rb->m_iBufsize) { rb_m ^= 1; ri = 0; } else { ri++; } ATOMIC_STORE(&rb->m_sReadIndex, ri, unsigned short, ATOMIC_ORDER_RELEASE); ATOMIC_STORE(&rb->m_bReadMirror, rb_m, unsigned char, ATOMIC_ORDER_RELEASE); return 1; } /** * @brief Get the size of data in the ring buffer in bytes. * * @param rb The pointer to the ring buffer object. * * @return Return the size of data in the ring buffer in bytes. */ int rd_RingbufferDataLen(rd_ringbuf_t *rb) { if (!rb) return RD_INVALUE; unsigned short wi = ATOMIC_LOAD(&rb->m_sWriteIndex, unsigned short, ATOMIC_ORDER_ACQUIRE); unsigned short ri = ATOMIC_LOAD(&rb->m_sReadIndex, unsigned short, ATOMIC_ORDER_RELAXED); unsigned char wb = ATOMIC_LOAD(&rb->m_bWriteMirror, unsigned char, ATOMIC_ORDER_ACQUIRE); unsigned char rb_m = ATOMIC_LOAD(&rb->m_bReadMirror, unsigned char, ATOMIC_ORDER_RELAXED); if (wi == ri) { return (wb == rb_m) ? 0 : rb->m_iBufsize; } if (wb == rb_m) { return wi - ri; } else { return rb->m_iBufsize - (ri - wi); } } /** * @brief Reset the ring buffer object, and clear all contents in the buffer. * * @param rb A pointer to the ring buffer object. */ void rd_RingbufferReset(rd_ringbuf_t *rb) { if (!rb) return; ATOMIC_STORE(&rb->m_sReadIndex, 0, unsigned short, ATOMIC_ORDER_RELAXED); ATOMIC_STORE(&rb->m_sWriteIndex, 0, unsigned short, ATOMIC_ORDER_RELAXED); ATOMIC_STORE(&rb->m_bReadMirror, 0, unsigned char, ATOMIC_ORDER_RELAXED); ATOMIC_STORE(&rb->m_bWriteMirror, 0, unsigned char, ATOMIC_ORDER_RELAXED); } /** * @brief Create a ring buffer object with a given size. * * @param size The size of the buffer in bytes. * * @return Return a pointer to ring buffer object. When the return value is RT_NULL, it means this creation failed. */ rd_ringbuf_t *rd_RingbufferCreate(int size) { if (size <= 0) return NULL; size = RD_ALIGN_DOWN(size, RD_ALIGN_SIZE); rd_ringbuf_t *rb = (rd_ringbuf_t *)RD_MALLOC(sizeof(rd_ringbuf_t)); if (!rb) return NULL; char *pool = (char *)RD_MALLOC(size); if (!pool) { RD_FREE(rb); return NULL; } rd_RingbufferInit(rb, pool, size); return rb; } /** * @brief Destroy the ring buffer object, which is created by rt_ringbuffer_create() . * * @param rb A pointer to the ring buffer object. */ void rd_RingbufferDestroy(rd_ringbuf_t *rb) { if (!rb) return; RD_FREE(rb->m_pcBufPtr); RD_FREE(rb); }