当前位置: 首页 > news >正文

C++编程:实现一个基于原始指针的环形缓冲区(RingBuffer)缓存串口数据

文章目录

    • 0. 引言
    • 1. 使用示例
    • 2. 流程图
      • 2.1 追加数据流程
      • 2.2 获取空闲块流程
      • 2.3 处理特殊字符流程
      • 2.4 释放块流程
      • 2.5 获取下一个使用块流程
    • 3. 代码详解
      • 3.1 Block 结构体
      • 3.2 RingBuffer 类
      • 3.3 主要方法解析
        • append 方法
        • currentUsed 和 currentUsing 方法
        • release 方法
        • nextUsed 方法
      • 3.4 私有方法解析
        • nextFree 方法
        • checkSpecial 方法
    • 4. 内存管理与拷贝控制
    • 5. 完整代码

0. 引言

本文将介绍一个解析串口数据的环形缓冲区,使用’$‘或者’#'分割串口消息。

主要设计考虑包括:

  1. 内存管理:选用内存块数组,而不是选用一整块的内存+读写指针的方式,以减少读写数据时的资源竞争。使用原始指针手动分配和释放内存,确保每个 Block 管理自己的缓冲区。
    确保资源的安全转移。
  2. 状态管理:每个 Block 通过 status 成员变量表示其当前状态(空闲、使用中或已使用)。

1. 使用示例

以下是一个简单的使用示例,展示如何创建 RingBuffer 实例、追加数据、访问当前使用的 Block 以及释放 Block

#include "ring_buffer.hpp"int main()
{try{// 创建一个包含10个Block的RingBufferRingBuffer ringBuffer(10);// 追加数据1const char* data1 = "Hello, World!";if (!ringBuffer.append(reinterpret_cast<const uint8_t*>(data1), std::strlen(data1))){std::cerr << "Failed to append data1.\n";}// 追加数据2,包含特殊字符const char* data2 = "Special#Character";if (!ringBuffer.append(reinterpret_cast<const uint8_t*>(data2), std::strlen(data2))){std::cerr << "Failed to append data2.\n";}// 访问当前使用的Blockconst RingBuffer::Block& usedBlock = ringBuffer.currentUsed();std::cout << "Used Block Index: " << usedBlock.index << ", Written: " << usedBlock.written << "\n";// 访问当前正在写入的Blockconst RingBuffer::Block& usingBlock = ringBuffer.currentUsing();std::cout << "Using Block Index: " << usingBlock.index << ", Written: " << usingBlock.written << "\n";// 释放第一个BlockringBuffer.release(0);}catch (const std::exception& ex){std::cerr << "Exception: " << ex.what() << "\n";}return 0;
}

解析:

  1. 创建 RingBuffer:初始化一个包含10个 BlockRingBuffer 实例。
  2. 追加数据
    • 追加字符串 "Hello, World!"
    • 追加包含特殊字符 "#" 的字符串 "Special#Character",触发数据分割逻辑。
  3. 访问 Block
    • 获取并打印当前使用的 Block 的索引和已写入字节数。
    • 获取并打印当前正在写入的 Block 的索引和已写入字节数。
  4. 释放 Block:释放第一个 Block,将其状态标记为 BufFree 并重置已写入字节数。
  5. 异常处理:使用 try-catch 块捕获并处理可能抛出的异常,确保程序的稳定性。

2. 流程图

2.1 追加数据流程

首先,我们来看一下追加数据的整体流程。这是环形缓冲区的核心功能,负责将新数据添加到缓冲区中。

长度超过
长度合适
有足够空间且状态为BufUsing
无足够空间或状态不符
获取成功
获取失败
开始
验证数据长度
输出错误信息
检查当前块空间和状态
数据复制到当前块
尝试获取下一个空闲块
设置新块状态为BufUsing
输出错误信息
结束

流程图解析

  1. 开始:数据追加操作的起点。
  2. 验证数据长度:检查待追加的数据是否超过单个块的大小(BlockSize)。
    • 长度超过:输出错误信息,终止操作。
    • 长度合适:继续下一步。
  3. 检查当前块空间和状态:判断当前块是否有足够的空间且状态为BufUsing(使用中)。
    • 有足够空间且状态正确:将数据复制到当前块。
    • 无足够空间或状态不符:尝试获取下一个空闲块。
  4. 尝试获取下一个空闲块
    • 获取成功:设置新块的状态为BufUsing,继续数据追加。
    • 获取失败:输出错误信息,终止操作。
  5. 结束:数据追加流程结束。

2.2 获取空闲块流程

当当前块空间不足或状态不符时,需要获取下一个空闲块。以下流程图展示了获取空闲块的具体步骤。

flowchart TDA[开始] --> B[检查当前块状态]B --> |状态为BufFree| C[可以使用当前块]B --> |状态不为BufFree| D[检查是否允许重试]D --> |允许重试| E[返回失败]D --> |不允许重试| F[标记当前块为BufUsed]F --> G[检查是否到达缓冲区末尾]G --> |是| H[回绕到第一个块]G --> |否| I[移动到下一个块]H --> J[检查块是否为空]I --> JJ --> |块为空| K[成功获取空闲块]J --> |块不为空| L[输出警告,返回失败]C --> KK --> M[结束]L --> ME --> M

流程图解析

  1. 开始:获取空闲块操作的起点。
  2. 检查当前块状态:判断当前块是否为空闲状态(BufFree)。
    • 状态为BufFree:可以直接使用当前块。
    • 状态不为BufFree:进一步检查是否允许重试。
  3. 检查是否允许重试
    • 允许重试:直接返回失败,不进行进一步操作。
    • 不允许重试:将当前块标记为BufUsed,表示已被使用。
  4. 标记当前块为BufUsed:更新当前块的状态。
  5. 检查是否到达缓冲区末尾
    • :回绕到缓冲区的第一个块。
    • :移动到下一个块。
  6. 检查块是否为空:判断目标块是否为空闲状态。
    • 块为空:成功获取空闲块。
    • 块不为空:输出警告信息,返回失败。
  7. 结束:获取空闲块流程结束。

2.3 处理特殊字符流程

在追加数据过程中,如果数据中包含特殊字符(如#$),需要对数据进行分割处理。以下流程图展示了处理特殊字符的具体步骤。

不包含
包含
获取成功
获取失败
开始
检查数据中是否包含特殊字符
复制整个数据到当前块
复制特殊字符前的数据到当前块
尝试获取新块
设置新块状态为BufUsing
输出错误信息
复制特殊字符到新块
检查是否有剩余数据
复制剩余数据到新块
结束

流程图解析

  1. 开始:处理特殊字符操作的起点。
  2. 检查数据中是否包含特殊字符:扫描数据是否包含#$
    • 不包含:直接将整个数据复制到当前块。
    • 包含:将特殊字符前的数据复制到当前块。
  3. 复制特殊字符前的数据到当前块:部分数据复制到当前块。
  4. 尝试获取新块
    • 获取成功:设置新块状态为BufUsing
    • 获取失败:输出错误信息,终止操作。
  5. 设置新块状态为BufUsing:更新新块的状态。
  6. 复制特殊字符到新块:将特殊字符复制到新块。
  7. 检查是否有剩余数据
    • :将剩余数据复制到新块。
    • :结束操作。
  8. 结束:处理特殊字符流程结束。

2.4 释放块流程

当某个块的数据被处理完毕后,需要将其释放,以便后续数据的追加。以下流程图展示了释放块的具体步骤。

有效
无效
开始
检查块索引是否有效
重置写入字节数
设置块状态为BufFree
结束
输出错误信息

流程图解析

  1. 开始:释放块操作的起点。
  2. 检查块索引是否有效:验证要释放的块索引是否在有效范围内。
    • 有效:继续释放流程。
    • 无效:输出错误信息,终止操作。
  3. 重置写入字节数:将块中的written成员变量重置为0
  4. 设置块状态为BufFree:将块的状态更新为BufFree,表示该块已空闲。
  5. 结束:释放块流程结束。

2.5 获取下一个使用块流程

在某些情况下,需要获取下一个已经使用的块。以下流程图展示了获取下一个使用块的具体步骤。

状态为BufUsed
状态不为BufUsed
状态为BufUsed
状态不为BufUsed
状态为BufUsed
状态不为BufUsed
开始
检查当前使用块的状态
返回当前使用块的索引
检查是否到达缓冲区末尾
检查第一个块的状态
检查下一个块的状态
更新使用块索引为0
返回-1
更新使用块索引为下一个块
返回使用块的索引
结束

流程图解析

  1. 开始:获取下一个使用块操作的起点。
  2. 检查当前使用块的状态:判断当前indexUsed_指向的块是否为BufUsed状态。
    • 状态为BufUsed:返回当前使用块的索引。
    • 状态不为BufUsed:继续检查是否需要回绕。
  3. 检查是否到达缓冲区末尾
    • :检查第一个块的状态。
    • :检查下一个块的状态。
  4. 检查第一个块的状态
    • 状态为BufUsed:更新indexUsed_0,返回索引。
    • 状态不为BufUsed:返回-1,表示没有找到下一个使用块。
  5. 检查下一个块的状态
    • 状态为BufUsed:更新indexUsed_为下一个块的索引,返回索引。
    • 状态不为BufUsed:返回-1,表示没有找到下一个使用块。
  6. 结束:获取下一个使用块流程结束。

3. 代码详解

下面我们将逐步解析 RingBuffer 类的实现,理解其内部工作机制。

3.1 Block 结构体

struct Block
{uint8_t* buf;     ///< Buffer to store data.int8_t   status;  ///< Status of the block (free, using, or used).int32_t  index;   ///< Index of the block within the ring buffer.uint32_t written; ///< Number of bytes written to the block./*** @brief Constructs a Block with a pre-allocated buffer.** Initializes the buffer and sets default values for other members.*/Block() : buf(new uint8_t[BlockSize]), status(BufFree), index(-1), written(0){// Initialize buffer to zero (optional)std::memset(buf, 0, BlockSize);}/*** @brief Destructor to deallocate the buffer.*/~Block() { delete[] buf; }// Delete copy constructor and copy assignment operator to prevent shallow copies.Block(const Block&)            = delete;Block& operator=(const Block&) = delete;// Define move constructor and move assignment operator for safe transfers.Block(Block&& other) noexcept : buf(other.buf), status(other.status), index(other.index), written(other.written){other.buf     = nullptr;other.status  = BufFree;other.index   = -1;other.written = 0;}Block& operator=(Block&& other) noexcept{if (this != &other){delete[] buf;buf     = other.buf;status  = other.status;index   = other.index;written = other.written;other.buf     = nullptr;other.status  = BufFree;other.index   = -1;other.written = 0;}return *this;}static constexpr uint32_t BlockSize = 256; ///< Size of each block in bytes.static constexpr int8_t   BufFree   = 0;   ///< Indicates the block is free.static constexpr int8_t   BufUsing  = 1;   ///< Indicates the block is currently in use.static constexpr int8_t   BufUsed   = 2;   ///< Indicates the block has been used.
};

解析:

  • 成员变量:

    • uint8_t* buf:指向数据缓冲区的原始指针。
    • int8_t status:表示 Block 的当前状态,BufFree(空闲)、BufUsing(使用中)、BufUsed(已使用)。
    • int32_t indexBlockRingBuffer 中的索引。
    • uint32_t written:已写入 Block 的字节数。
  • 构造函数与析构函数:

    • 构造函数中,使用 new 分配固定大小的缓冲区,并初始化其他成员变量。
    • 析构函数中,使用 delete[] 释放缓冲区内存。
  • 拷贝控制:

    • 拷贝构造函数和拷贝赋值运算符被删除,防止浅拷贝带来的内存管理问题。
    • 移动构造函数和移动赋值运算符 被定义,允许 Block 实例的资源安全转移。

3.2 RingBuffer 类

class RingBuffer
{public:// Block 结构体定义.../*** @brief Constructs a RingBuffer with a specified number of blocks.** @param num The number of blocks in the ring buffer. Defaults to 1000.*/explicit RingBuffer(uint32_t num = 1000) : blocks_(num), blockNum_(num), index_(0), indexUsed_(-1){// Initialize each block's index.for (uint32_t i = 0; i < blocks_.size(); ++i){blocks_[i].index = static_cast<int32_t>(i);}// Set the first block's status to 'using'.if (!blocks_.empty()){blocks_[0].status = Block::BufUsing;}}/*** @brief Destructor to deallocate all blocks.*/~RingBuffer() = default; // Blocks are automatically destroyed, and their destructors handle buffer deallocation.// Delete copy constructor and copy assignment operator to prevent shallow copies.RingBuffer(const RingBuffer&)            = delete;RingBuffer& operator=(const RingBuffer&) = delete;// Define move constructor and move assignment operator for safe transfers.RingBuffer(RingBuffer&& other) noexcept: blocks_(std::move(other.blocks_)),blockNum_(other.blockNum_),index_(other.index_),indexUsed_(other.indexUsed_){other.blockNum_  = 0;other.index_     = 0;other.indexUsed_ = -1;}RingBuffer& operator=(RingBuffer&& other) noexcept{if (this != &other){blocks_    = std::move(other.blocks_);blockNum_  = other.blockNum_;index_     = other.index_;indexUsed_ = other.indexUsed_;other.blockNum_  = 0;other.index_     = 0;other.indexUsed_ = -1;}return *this;}// append 方法定义...// currentUsed 和 currentUsing 方法定义...// release 方法定义...// nextUsed 方法定义...private:// nextFree 和 checkSpecial 方法定义...std::vector<Block> blocks_;         ///< Vector of blocks managed by the ring buffer.int32_t            index_     = 0;  ///< Current index for writing data.int32_t            blockNum_  = 0;  ///< Total number of blocks in the ring buffer.int32_t            indexUsed_ = -1; ///< Index of the currently used block.
};

解析:

  • 成员变量:

    • std::vector<Block> blocks_:存储所有 Block 的容器。
    • int32_t index_:当前用于写入数据的 Block 索引。
    • int32_t blockNum_:环形缓冲区中 Block 的总数。
    • int32_t indexUsed_:当前正在使用的 Block 索引,初始化为 -1 表示无 Block 被使用。
  • 构造函数与析构函数:

    • 构造函数中,初始化 blocks_ 向量,并为每个 Block 设置其索引。默认情况下,第一个 Block 的状态设置为 BufUsing,表示其已被使用。
    • 析构函数使用默认实现,依赖于 Block 的析构函数自动释放内存。
  • 拷贝控制:

    • 拷贝构造函数和拷贝赋值运算符被删除,防止浅拷贝导致的资源管理问题。
    • 移动构造函数和移动赋值运算符 被定义,允许 RingBuffer 实例的资源安全转移。

3.3 主要方法解析

append 方法
/*** @brief Appends data to the ring buffer.** This method attempts to append the provided data to the current block.* If there's insufficient space or a retry is requested, it tries to acquire the next free block.* It also handles splitting data when special characters are encountered.** @param buf Pointer to the data to append.* @param length Length of the data in bytes.* @param retry Flag indicating whether to retry acquiring a free block.* @return True if the data was successfully appended; false otherwise.*/
bool append(const uint8_t* buf, uint32_t length, bool retry = false)
{// Validate that the data length does not exceed block size.if (length > Block::BlockSize){std::cerr << "Error: Length " << length << " exceeds block size " << Block::BlockSize << ".\n";return false;}// Check if the current block has enough space and is in the correct status.if (!retry && blocks_[index_].written + length <= Block::BlockSize){if (blocks_[index_].status != Block::BufUsing){std::cerr << "Error: Current block status is not BufUsing.\n";return false;}}else{// Attempt to acquire the next free block.if (!nextFree(retry)){// If acquiring a free block fails, ensure the current block status is correct.if (blocks_[index_].status != Block::BufUsed){std::cerr << "Error: Current block status is not BufUsed.\n";}return false;}// Update the status of the newly acquired block.blocks_[index_].status = Block::BufUsing;}// If the current block is empty, copy the entire buffer.if (blocks_[index_].written == 0){std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf, length);blocks_[index_].written += length;}else{// Check for special characters in the buffer.int specialIdx = checkSpecial(buf, length);if (specialIdx == -1){// No special characters found; attempt to copy the entire buffer.if (blocks_[index_].written + length > Block::BlockSize){std::cerr << "Error: Not enough space to append data without special character.\n";return false;}std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf, length);blocks_[index_].written += length;}else{// Special character found; split the data at the special character.if (blocks_[index_].written + specialIdx > Block::BlockSize){std::cerr << "Error: Not enough space to copy data before special character.\n";return false;}// Copy data up to the special character.std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf, specialIdx);blocks_[index_].written += specialIdx;// Attempt to acquire a new free block for the special character.if (!nextFree(retry)){if (blocks_[index_].status != Block::BufUsed){std::cerr << "Error: Current block status is not BufUsed after finding special character.\n";}return false;}// Update the status and reset the write position for the new block.blocks_[index_].status  = Block::BufUsing;blocks_[index_].written = 0;// Copy the special character to the new block.blocks_[index_].buf[blocks_[index_].written++] = buf[specialIdx];// If there is remaining data after the special character, copy it to the new block.if (static_cast<uint32_t>(specialIdx + 1) < length){uint32_t remainingLength = length - specialIdx - 1;if (remainingLength > Block::BlockSize - blocks_[index_].written){std::cerr << "Error: Not enough space to copy remaining data after special character.\n";return false;}std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf + specialIdx + 1, remainingLength);blocks_[index_].written += remainingLength;}}}return true;
}

解析:

  • 功能:向环形缓冲区追加数据。
  • 步骤
    1. 长度校验:确保待追加的数据长度不超过单个 Block 的大小。
    2. 空间与状态检查:检查当前 Block 是否有足够的空间以及其状态是否为 BufUsing
    3. 获取新块:如果当前块空间不足或请求重试,尝试获取下一个空闲块。
    4. 数据复制
      • 如果当前块为空,直接复制整个数据。
      • 如果当前块已有数据,检查数据中是否包含特殊字符(#$)。
        • 无特殊字符:直接复制数据。
        • 有特殊字符:在特殊字符处分割数据,分别复制到当前块和新获取的块中。
currentUsed 和 currentUsing 方法
/*** @brief Retrieves the currently used block.** @return A constant reference to the currently used block.* @throws std::out_of_range if the indexUsed_ is invalid.*/
const Block& currentUsed() const
{if (indexUsed_ >= 0 && static_cast<size_t>(indexUsed_) < blocks_.size()){return blocks_[indexUsed_];}throw std::out_of_range("currentUsed(): indexUsed_ is out of range.");
}/*** @brief Retrieves the block that is currently being used for writing.** @return A constant reference to the block currently in use.* @throws std::out_of_range if the index_ is invalid.*/
const Block& currentUsing() const
{if (static_cast<size_t>(index_) < blocks_.size()){return blocks_[index_];}throw std::out_of_range("currentUsing(): index_ is out of range.");
}

解析:

  • 功能
    • currentUsed():获取当前正在使用的 Block
    • currentUsing():获取当前用于写入的 Block
  • 实现
    • 通过索引 indexUsed_index_ 访问 blocks_ 向量中的相应 Block
    • 添加了边界检查,若索引无效,抛出 std::out_of_range 异常,防止访问越界。
release 方法
/*** @brief Releases a block, marking it as free and resetting its written bytes.** @param idx The index of the block to release.*/
void release(uint32_t idx)
{if (idx < blocks_.size()){blocks_[idx].written = 0;blocks_[idx].status  = Block::BufFree;}else{std::cerr << "Error: Release index " << idx << " is out of bounds.\n";}
}

解析:

  • 功能:释放指定索引的 Block,将其状态标记为 BufFree 并重置已写入字节数。
  • 实现
    • 检查索引是否在有效范围内。
    • 更新 Blockwrittenstatus 成员。
nextUsed 方法
/*** @brief Retrieves the next used block's index.** @return The index of the next used block, or -1 if no such block exists.*/
int32_t nextUsed()
{// Check if the current used index is valid and marked as used.if (indexUsed_ >= 0 && static_cast<size_t>(indexUsed_) < blocks_.size() &&blocks_[indexUsed_].status == Block::BufUsed){return indexUsed_;}// Handle wrapping around to the beginning of the ring buffer.if (indexUsed_ == static_cast<int32_t>(blockNum_) - 1){if (!blocks_.empty() && blocks_[0].status == Block::BufUsed){indexUsed_ = 0;return indexUsed_;}else{return -1;}}else{// Check the next block in sequence.if ((indexUsed_ + 1) < static_cast<int32_t>(blockNum_) && blocks_[indexUsed_ + 1].status == Block::BufUsed){indexUsed_ += 1;return indexUsed_;}else{return -1;}}
}

解析:

  • 功能:获取下一个已使用的 Block 的索引。
  • 实现
    • 检查当前 indexUsed_ 是否指向一个已使用的 Block
    • 如果在缓冲区末尾,尝试回绕到起始位置。
    • 否则,检查下一个 Block 是否已使用。
    • 若未找到,则返回 -1

3.4 私有方法解析

nextFree 方法
/*** @brief Attempts to acquire the next free block in the ring buffer.** @param retry Flag indicating whether to retry acquiring a free block.* @return True if a free block was successfully acquired; false otherwise.*/
bool nextFree(bool retry = false)
{// If the current block is free, it can be used.if (blocks_[index_].status == Block::BufFree){return true;}else{// If retry is requested and the block is not free, do not attempt further.if (retry){return false;}}// Mark the current block as used since it cannot be reused immediately.blocks_[index_].status = Block::BufUsed;// Handle wrapping around to the first block.if (index_ == static_cast<int32_t>(blockNum_) - 1){index_ = 0;if (blocks_[index_].status != Block::BufFree){std::cerr << "Warning: Block 0 is not free.\n";return false;}}else{// Move to the next block in the sequence.index_ += 1;if (index_ >= static_cast<int32_t>(blockNum_)){std::cerr << "Error: Index exceeded block number.\n";return false;}if (blocks_[index_].status != Block::BufFree){std::cerr << "Warning: Block " << index_ << " is not free.\n";return false;}}return true;
}

解析:

  • 功能:尝试获取下一个空闲的 Block
  • 实现
    • 检查当前 Block 是否为空闲状态。
    • 若不为空闲且不允许重试,标记当前 Block 为已使用,并尝试下一个 Block
    • 添加了回绕逻辑,确保环形缓冲区的循环特性。
    • 在获取失败时,输出警告或错误信息。
checkSpecial 方法
/*** @brief Checks for the presence of special characters in the buffer.** This method scans the buffer for '#' or '$' characters.** @param buf Pointer to the buffer to check.* @param length Length of the buffer in bytes.* @return The index of the first special character found; -1 if none are found.*/
int checkSpecial(const uint8_t* buf, uint32_t length) const
{for (uint32_t i = 0; i < length; ++i){if (buf[i] == '#' || buf[i] == '$'){return static_cast<int>(i);}}return -1;
}

解析:

  • 功能:检查缓冲区中是否存在特殊字符(#$)。
  • 实现
    • 遍历缓冲区数据,查找特殊字符。
    • 若找到,返回其索引;否则,返回 -1

4. 内存管理与拷贝控制

在本实现中,内存管理拷贝控制 是确保 RingBuffer 类安全高效运行的关键因素。

  1. 原始指针管理内存

    • 每个 Block 使用 new 分配固定大小的缓冲区,并在析构函数中使用 delete[] 释放。
    • 这种手动管理内存的方法需要开发者确保在所有情况下都正确分配和释放资源,避免内存泄漏或重复释放。
  2. 拷贝控制

    • 通过 删除拷贝构造函数和拷贝赋值运算符,防止 BlockRingBuffer 实例被浅拷贝,避免多个实例指向同一内存区域。
    • 移动构造函数和移动赋值运算符 的实现,允许资源的安全转移,提高类的灵活性。

5. 完整代码


/*** @brief A ring buffer implementation for managing fixed-size memory blocks without using smart pointers.*/
class RingBuffer
{public:/*** @brief Represents a single block within the ring buffer.*/struct Block{uint8_t* buf;     ///< Buffer to store data.int8_t   status;  ///< Status of the block (free, using, or used).int32_t  index;   ///< Index of the block within the ring buffer.uint32_t written; ///< Number of bytes written to the block./*** @brief Constructs a Block with a pre-allocated buffer.** Initializes the buffer and sets default values for other members.*/Block() : buf(new uint8_t[BlockSize]), status(BufFree), index(-1), written(0){// Initialize buffer to zero (optional)std::memset(buf, 0, BlockSize);}/*** @brief Destructor to deallocate the buffer.*/~Block() { delete[] buf; }// Delete copy constructor and copy assignment operator to prevent shallow copies.Block(const Block&)            = delete;Block& operator=(const Block&) = delete;// Define move constructor and move assignment operator for safe transfers.Block(Block&& other) noexcept : buf(other.buf), status(other.status), index(other.index), written(other.written){other.buf     = nullptr;other.status  = BufFree;other.index   = -1;other.written = 0;}Block& operator=(Block&& other) noexcept{if (this != &other){delete[] buf;buf     = other.buf;status  = other.status;index   = other.index;written = other.written;other.buf     = nullptr;other.status  = BufFree;other.index   = -1;other.written = 0;}return *this;}static constexpr uint32_t BlockSize = 256; ///< Size of each block in bytes.static constexpr int8_t   BufFree   = 0;   ///< Indicates the block is free.static constexpr int8_t   BufUsing  = 1;   ///< Indicates the block is currently in use.static constexpr int8_t   BufUsed   = 2;   ///< Indicates the block has been used.};/*** @brief Constructs a RingBuffer with a specified number of blocks.** @param num The number of blocks in the ring buffer. Defaults to 1000.*/explicit RingBuffer(uint32_t num = 1000) : blocks_(num), blockNum_(num), index_(0), indexUsed_(-1){// Initialize each block's index.for (uint32_t i = 0; i < blocks_.size(); ++i){blocks_[i].index = static_cast<int32_t>(i);}// Set the first block's status to 'using'.if (!blocks_.empty()){blocks_[0].status = Block::BufUsing;}}/*** @brief Destructor to deallocate all blocks.*/~RingBuffer() = default; // Blocks are automatically destroyed, and their destructors handle buffer deallocation.// Delete copy constructor and copy assignment operator to prevent shallow copies.RingBuffer(const RingBuffer&)            = delete;RingBuffer& operator=(const RingBuffer&) = delete;// Define move constructor and move assignment operator for safe transfers.RingBuffer(RingBuffer&& other) noexcept: blocks_(std::move(other.blocks_)),blockNum_(other.blockNum_),index_(other.index_),indexUsed_(other.indexUsed_){other.blockNum_  = 0;other.index_     = 0;other.indexUsed_ = -1;}RingBuffer& operator=(RingBuffer&& other) noexcept{if (this != &other){blocks_    = std::move(other.blocks_);blockNum_  = other.blockNum_;index_     = other.index_;indexUsed_ = other.indexUsed_;other.blockNum_  = 0;other.index_     = 0;other.indexUsed_ = -1;}return *this;}/*** @brief Appends data to the ring buffer.** This method attempts to append the provided data to the current block.* If there's insufficient space or a retry is requested, it tries to acquire the next free block.* It also handles splitting data when special characters are encountered.** @param buf Pointer to the data to append.* @param length Length of the data in bytes.* @param retry Flag indicating whether to retry acquiring a free block.* @return True if the data was successfully appended; false otherwise.*/bool append(const uint8_t* buf, uint32_t length, bool retry = false){// Validate that the data length does not exceed block size.if (length > Block::BlockSize){std::cerr << "Error: Length " << length << " exceeds block size " << Block::BlockSize << ".\n";return false;}// Check if the current block has enough space and is in the correct status.if (!retry && blocks_[index_].written + length <= Block::BlockSize){if (blocks_[index_].status != Block::BufUsing){std::cerr << "Error: Current block status is not BufUsing.\n";return false;}}else{// Attempt to acquire the next free block.if (!nextFree(retry)){// If acquiring a free block fails, ensure the current block status is correct.if (blocks_[index_].status != Block::BufUsed){std::cerr << "Error: Current block status is not BufUsed.\n";}return false;}// Update the status of the newly acquired block.blocks_[index_].status = Block::BufUsing;}// If the current block is empty, copy the entire buffer.if (blocks_[index_].written == 0){std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf, length);blocks_[index_].written += length;}else{// Check for special characters in the buffer.int specialIdx = checkSpecial(buf, length);if (specialIdx == -1){// No special characters found; attempt to copy the entire buffer.if (blocks_[index_].written + length > Block::BlockSize){std::cerr << "Error: Not enough space to append data without special character.\n";return false;}std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf, length);blocks_[index_].written += length;}else{// Special character found; split the data at the special character.if (blocks_[index_].written + specialIdx > Block::BlockSize){std::cerr << "Error: Not enough space to copy data before special character.\n";return false;}// Copy data up to the special character.std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf, specialIdx);blocks_[index_].written += specialIdx;// Attempt to acquire a new free block for the special character.if (!nextFree(retry)){if (blocks_[index_].status != Block::BufUsed){std::cerr << "Error: Current block status is not BufUsed after finding special character.\n";}return false;}// Update the status and reset the write position for the new block.blocks_[index_].status  = Block::BufUsing;blocks_[index_].written = 0;// Copy the special character to the new block.blocks_[index_].buf[blocks_[index_].written++] = buf[specialIdx];// If there is remaining data after the special character, copy it to the new block.if (static_cast<uint32_t>(specialIdx + 1) < length){uint32_t remainingLength = length - specialIdx - 1;if (remainingLength > Block::BlockSize - blocks_[index_].written){std::cerr << "Error: Not enough space to copy remaining data after special character.\n";return false;}std::memcpy(blocks_[index_].buf + blocks_[index_].written, buf + specialIdx + 1, remainingLength);blocks_[index_].written += remainingLength;}}}return true;}/*** @brief Retrieves the currently used block.** @return A constant reference to the currently used block.* @throws std::out_of_range if the indexUsed_ is invalid.*/const Block& currentUsed() const{if (indexUsed_ >= 0 && static_cast<size_t>(indexUsed_) < blocks_.size()){return blocks_[indexUsed_];}throw std::out_of_range("currentUsed(): indexUsed_ is out of range.");}/*** @brief Retrieves the block that is currently being used for writing.** @return A constant reference to the block currently in use.* @throws std::out_of_range if the index_ is invalid.*/const Block& currentUsing() const{if (static_cast<size_t>(index_) < blocks_.size()){return blocks_[index_];}throw std::out_of_range("currentUsing(): index_ is out of range.");}/*** @brief Releases a block, marking it as free and resetting its written bytes.** @param idx The index of the block to release.*/void release(uint32_t idx){if (idx < blocks_.size()){blocks_[idx].written = 0;blocks_[idx].status  = Block::BufFree;}else{std::cerr << "Error: Release index " << idx << " is out of bounds.\n";}}/*** @brief Retrieves the next used block's index.** @return The index of the next used block, or -1 if no such block exists.*/int32_t nextUsed(){// Check if the current used index is valid and marked as used.if (indexUsed_ >= 0 && static_cast<size_t>(indexUsed_) < blocks_.size() &&blocks_[indexUsed_].status == Block::BufUsed){return indexUsed_;}// Handle wrapping around to the beginning of the ring buffer.if (indexUsed_ == static_cast<int32_t>(blockNum_) - 1){if (!blocks_.empty() && blocks_[0].status == Block::BufUsed){indexUsed_ = 0;return indexUsed_;}else{return -1;}}else{// Check the next block in sequence.if ((indexUsed_ + 1) < static_cast<int32_t>(blockNum_) && blocks_[indexUsed_ + 1].status == Block::BufUsed){indexUsed_ += 1;return indexUsed_;}else{return -1;}}}private:/*** @brief Attempts to acquire the next free block in the ring buffer.** @param retry Flag indicating whether to retry acquiring a free block.* @return True if a free block was successfully acquired; false otherwise.*/bool nextFree(bool retry = false){// If the current block is free, it can be used.if (blocks_[index_].status == Block::BufFree){return true;}else{// If retry is requested and the block is not free, do not attempt further.if (retry){return false;}}// Mark the current block as used since it cannot be reused immediately.blocks_[index_].status = Block::BufUsed;// Handle wrapping around to the first block.if (index_ == static_cast<int32_t>(blockNum_) - 1){index_ = 0;if (blocks_[index_].status != Block::BufFree){std::cerr << "Warning: Block 0 is not free.\n";return false;}}else{// Move to the next block in the sequence.index_ += 1;if (index_ >= static_cast<int32_t>(blockNum_)){std::cerr << "Error: Index exceeded block number.\n";return false;}if (blocks_[index_].status != Block::BufFree){std::cerr << "Warning: Block " << index_ << " is not free.\n";return false;}}return true;}/*** @brief Checks for the presence of special characters in the buffer.** This method scans the buffer for '#' or '$' characters.** @param buf Pointer to the buffer to check.* @param length Length of the buffer in bytes.* @return The index of the first special character found; -1 if none are found.*/int checkSpecial(const uint8_t* buf, uint32_t length) const{for (uint32_t i = 0; i < length; ++i){if (buf[i] == '#' || buf[i] == '$'){return static_cast<int>(i);}}return -1;}std::vector<Block> blocks_;         ///< Vector of blocks managed by the ring buffer.int32_t            index_     = 0;  ///< Current index for writing data.int32_t            blockNum_  = 0;  ///< Total number of blocks in the ring buffer.int32_t            indexUsed_ = -1; ///< Index of the currently used block.
};

相关文章:

C++编程:实现一个基于原始指针的环形缓冲区(RingBuffer)缓存串口数据

文章目录 0. 引言1. 使用示例2. 流程图2.1 追加数据流程2.2 获取空闲块流程2.3 处理特殊字符流程2.4 释放块流程2.5 获取下一个使用块流程 3. 代码详解3.1 Block 结构体3.2 RingBuffer 类3.3 主要方法解析append 方法currentUsed 和 currentUsing 方法release 方法nextUsed 方法…...

LangChain 创始人万字科普:手把手教你设计 Agent 用户交互

LangChain 可以算是 LLM 时代做 AI 应用开发必备的框架和平台&#xff0c;从模型选择、数据库链接与各种 Agent 搭建等&#xff0c;AI 应用的搭建、运行和管理都可以在 LangChain 上进行。 某种意义上&#xff0c;LangChain 可能是最了解 Agent&#xff08;智能体&#xff09;…...

Docker 用例:15 种最常见的 Docker 使用方法

容器化应用程序而不是将它们托管在虚拟机上是过去几年一直流行的概念&#xff0c;使容器管理流行起来。Docker 处于这一转变的核心&#xff0c;帮助组织无缝地采用容器化技术。最近&#xff0c;Docker 用例遍布所有行业&#xff0c;无论规模大小和性质如何。 什么是Docker&…...

若依 RuoYi4.6.0 代码审计

环境布置&#xff1a; 到官网下载源码&#xff1a;https://github.com/yangzongzhuan/RuoYi 采用phpstudy集成数据库&#xff0c;5.7版本。JDK1.8。 IDEA打开项目&#xff0c;等待自动加载&#xff0c;修改application-druid.yml配置文件&#xff1a;数据库名&#xff0c;账…...

C语言入门-选择结构

在编程中&#xff0c;我们经常需要根据不同的条件执行不同的操作。C语言为此提供了几种非常实用的选择结构&#xff1a;条件运算符、逻辑运算、if语句和switch语句。接下来&#xff0c;让我们深入探讨这些重要的知识点&#xff0c;帮助你更好地理解和掌握C语言的选择结构。 1.…...

Legion拯救者 刃7000K-26IAB联想台式机T5 26IAB7(90SU,90SV,90SW,90SX)原厂Windows11系统镜像下载

适用机型&#xff1a;【90SW、90SX、90SU、90SV】 链接&#xff1a;https://pan.baidu.com/s/1gJ4ZwWW2orlGYoPk37M-cg?pwd4mvv 提取码&#xff1a;4mvv lenovo联想原装WIN系统自带所有驱动、出厂主题专用壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软…...

代码随想录算法训练营第二十四天|Day24 回溯算法

93.复原IP地址 题目链接/文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1XP4y1U73i/ 思路 char** result; int resultTop; int segments[3]; int isValid(char* s…...

vue elementui table编辑表单时,弹框增加编辑明细数据

需求: 前端进行新增表单时&#xff0c;同时增加表单的明细数据。明细数据部分&#xff0c;通过弹框方式增加或者编辑。 效果图&#xff1a; 代码&#xff1a; <!-- 新增主表弹窗 Begin --><el-dialog:title"titleInfo"top"5vh"centerwidth"…...

springboot集成Redisson做分布式消息队列

这里演示Redisson做分布式消息队列。首先引入 Redisson依赖&#xff0c;官方github <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.17.6</version> </dependen…...

如何通过Lua语言请求接口拿到数据

文章目录 概要http客户端通过请求下载数据 概要 当某个需求是需要在模块内请求接口拿到数据&#xff0c;需要使用http客户端调用接口 http客户端 LuaSOC请求接口官方文档 调用&#xff1a;http.request(method,url,headers,body,opts,ca_file,client_ca, client_key, clien…...

Android 13 SystemUI 隐藏下拉快捷面板部分模块(wifi,bt,nfc等)入口

frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java createTileInternal(tileSpec)方法注释想隐藏的模块即可。...

自由学习记录(14)

unity操作问题 位置&#xff1a;子物体的位置是相对于父物体的。如果你移动父物体&#xff0c;子物体会保持相对于父物体的相对位置&#xff0c;跟着一起移动。 旋转&#xff1a;子物体的旋转也是相对于父物体的。旋转父物体会导致子物体围绕父物体的原点旋转。 缩放&#xf…...

疯狂Spring Boot讲义[推荐1]

《疯狂Spring Boot讲义》是2021年电子工业出版社出版的图书&#xff0c;作者是李刚 《疯狂Spring Boot终极讲义》不是一本介绍类似于PathVariable、MatrixVariable、RequestBody、ResponseBody这些基础注解的图书&#xff0c;它是真正讲解Spring Boot的图书。Spring Boot的核心…...

vue中$nextTick的作用是什么,什么时候使用

$nextTick 是 Vue 提供的一个方法&#xff0c;用于在下一次 DOM 更新周期之后执行回调函数。它通常用于在 Vue 完成数据更新后&#xff0c;需要访问更新后的 DOM 状态时&#xff0c;保证操作的是更新后的 DOM。 工作原理&#xff1a; Vue 是异步更新 DOM 的&#xff0c;当数据…...

Redis实现全局ID生成器

全局ID生成器 为什么要用全局ID生成器 1.当我们使用数据库自增来实现id的生成时,规律过于明显,会给用户暴露很多信息 2.当我们订单量过大时无法用数据库的一张表来存放订单,如果两张表的id都是自增的话,id就会出现重复 什么是全局ID生成器 全局ID生成器,是一种在分布式系统…...

Xshell远程连接工具详解

Xshell是一款在Windows平台上运行的远程连接工具&#xff0c;它支持SSH1、SSH2以及Microsoft Windows平台的TELNET协议。Xshell通过互联网实现对远程主机的安全连接&#xff0c;帮助用户在复杂的网络环境中享受他们的工作。本文将详细介绍Xshell的溯源、最新版本以及它的优势。…...

如何在verilog设计的磁盘阵列控制器中实现不同RAID级别(如RAID 0、RAID 1等)的切换?

以下是一种在Verilog设计的磁盘阵列控制器中实现不同RAID级别(以RAID 0和RAID 1为例)切换的方法: 添加控制信号 在磁盘阵列控制器模块中添加一个输入信号,例如raid_mode,用于选择RAID模式。假设raid_mode = 0表示RAID 0模式,raid_mode = 1表示RAID 1模式。module raid_co…...

基于元神操作系统实现NTFS文件操作(十)

1. 背景 本文补充介绍文件遍历操作的部分附加内容&#xff0c;譬如&#xff0c;过滤掉系统元文件、过滤掉重复的文件项、过滤掉隐藏文件等&#xff0c;并提供了基于元神操作系统的部分实现代码。 2. 方法 &#xff08;1&#xff09;过滤掉系统元文件 NTFS文件系统的前16个元…...

Qt的几个函数方法

void receiveInfo1() {// 假设这是从串口接收到的字符串QString receivedString "23.5C,45%,1012hPa";// 使用逗号分隔符分割字符串QStringList parts receivedString.split(,);// 检查分割后的列表是否有足够的部分if (parts.size() > 3) {QString part1 part…...

openpnp - bug - 散料飞达至少定义2个物料

文章目录 openpnp - bug - 散料飞达至少定义2个物料笔记END openpnp - bug - 散料飞达至少定义2个物料 笔记 散料飞达上定义的物料个数用完了&#xff0c;现在只需要一个料就可以。 用顶部相机去找编带上是否还有一个单独的料&#xff0c;找到了。 定义散料飞达的料为1个&…...

HDFS异常org.apache.hadoop.hdfs.protocol.NSQuotaExceededException

HDFS异常org.apache.hadoop.hdfs.protocol.NSQuotaExceededException 异常信息&#xff1a; Hive:org.apache.hadoop.hdfs.protocol.NSQuotaExceededException: The NameSpace quota (directories and files) of directory /xxxdir is exceeded: quota10000 file count15001N…...

数据库的构成与手写简单数据库的探索

一、引言 在当今数字化的时代&#xff0c;数据库扮演着至关重要的角色。无论是企业管理系统、电子商务平台还是各种移动应用&#xff0c;都离不开数据库的支持。数据库是存储和管理数据的核心工具&#xff0c;它的高效性、可靠性和安全性对于数据的处理和应用至关重要。本文将…...

基于STM32的智能晾衣架设计

引言 随着智能家居的普及&#xff0c;智能晾衣架成为了提升生活便利性的重要设备。智能晾衣架通过集成多个传感器&#xff0c;能够自动感知天气变化、湿度、光照等环境因素&#xff0c;实现自动升降、风干和报警功能&#xff0c;帮助用户更加高效地晾晒衣物。本项目基于STM32设…...

【MAUI】模糊控件(毛玻璃高斯模糊亚克力模糊)

文章目录 XAML.CSToBytes方法使用效果 常试过AcrylicView.MAUI和Sharpnado.MaterialFrame&#xff0c;对于二者教程很少&#xff0c;使用直接写控件然后调属性&#xff0c;没有报错但也并没有效果所幸就自己写一个 XAML <?xml version"1.0" encoding"utf-…...

深度学习:pandas篇

1. Pandas 基础 Pandas 是一个帮助你处理和分析数据的工具 安装 Pandas pip install pandas 导入 Pandas&#xff0c;我们用 pd 来代替 Pandas 的全称&#xff0c;这样以后写代码的时候更简洁 import pandas as pd 建 Series 和 DataFrame Pandas 最基本的两个数据结构是…...

Redis学习文档(Redis基本数据类型【Hash、Set】)

Hash&#xff08;哈希&#xff09; 介绍 Redis 中的 Hash 是一个 String 类型的 field-value&#xff08;键值对&#xff09; 的映射表&#xff0c;特别适合用于存储对象&#xff0c;后续操作的时候&#xff0c;你可以直接修改这个对象中的某些字段的值。 Hash 类似于 JDK1.…...

15分钟学Go 第9天:函数的定义与调用

第9天&#xff1a;函数的定义与调用 欢迎来到第9天的Go语言学习模块&#xff01;今天我们将深入探讨函数的定义与调用&#xff0c;帮助你掌握如何编写和使用函数。学习函数不仅是Go语言的基础&#xff0c;也是程序设计的核心概念之一。这一节将详细介绍函数的结构、参数传递、…...

Java虚拟机:JVM介绍

1024 程序员节日快乐&#xff01;愿您我的代码永远没有 bug &#xff0c;人生永远没有 bug &#xff01; JVM 概述JVM 架构 概述 JVM&#xff08; Java Virtual Machine &#xff0c;Java 虚拟机&#xff09;&#xff0c;是 Java 语言的运行环境&#xff0c;是运行所有 Java 程…...

R数据科学 16.5.3练习题

(1) 编写代码以使用一种映射函数完成以下任务。 a. 计算 mtcars 数据集中每列的均值。 b. 确定 nycflights13::flights 数据集中每列的类型。 c. 计算 iris 数据集中每列唯一值的数量。 d. 分别使用 μ -10、0、10 和 100 的正态分布生成 10 个随机数。 library(purrr) # 计算…...

通过conda install -c nvidia cuda=“11.3.0“ 安装低版本的cuda,但是却安装了高版本的12.4.0

问题 直接通过 conda install -c nvidia cuda"11.3.0"安装得到的却是高版本的 不清楚原理 解决方法 不过我们可以分个安装 runtime toolkit 和 nvcc 安装指定版本的 cudatoolkit 和 nvcc conda install -c nvidia cuda-cudart"11.3.58" conda instal…...

三明市住房与城乡建设部网站/经典模板网站建设

【强制】Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理&#xff0c;比如&#xff1a;NullPointerException&#xff0c;IndexOutOfBoundsException等等。 说明&#xff1a;无法通过预检查的异常除外&#xff0c;比如&#xff0c…...

外贸网站平台排名/seo具体是什么

给定整数n&#xff0c;请问n以内有多少个素数 &#xff1f; eg&#xff1a; 输入&#xff1a; 11 输出&#xff1a; 5&#xff08;2&#xff0c;3&#xff0c;5&#xff0c;7&#xff0c;11共5个&#xff09; 1 //埃氏筛法&#xff0c;筛选n以内素数的个数 2 #include<…...

泰安网站建设如何/照片查询百度图片搜索

左下角阅读原文看CAD视频好课推荐&#xff1a;1、CAD2014&#xff1a;点击查看 2、室内&全屋&#xff1a;点击查看 3、CAD2019&#xff1a;点击查看4、CAD2018&#xff1a;点击查看5、Bim教程&#xff1a;点击查看6、室内手绘&#xff1a;点击查看7、CAD三维&#xff1a;点…...

七牛做网站/电脑优化大师有用吗

linux系统时间有两个&#xff0c;一个是硬件时间&#xff0c;即BIOS时间&#xff0c;就是我们进行CMOS设置时看到的时间&#xff0c;另一个是系统时间&#xff0c;是linux系统Kernel时间。当Linux启动时&#xff0c;系统Kernel会去读取硬件时钟的设置&#xff0c;然后系统时钟就…...

医药企业建设网站需要什么/整站外包优化公司

如果编译器的下载地址偏移和中断里面的中断偏移都设置正确&#xff08;一致且合理&#xff09;&#xff0c;那么剩下的原因可能是中断没处理好 我遇到的问题是&#xff0c;无线下载PWM有一路不能用&#xff0c;但有线下载可以&#xff1a;在引导程序跳转前要__disable_irq() ;…...

免费学做衣服的网站/百度一下百度一下你就知道

随着时间的推移&#xff0c;有的人好长时间不联系了&#xff0c;也有一些人不想去联系&#xff0c;有的右键地址就想着删除&#xff0c;那么如何删除邮件不需要的电子邮件地址呢&#xff1f;这个教程可以试试哦。 1.在Mac OS中打开邮件应用。 2.下拉“窗口”菜单&#xff0c;…...