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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Lucas带你手撕机器学习——线性回归

什么是线性回归 线性回归是机器学习中的基础算法之一&#xff0c;用于预测一个连续的输出值。它假设输入特征与输出值之间的关系是线性关系&#xff0c;即目标变量是输入变量的线性组合。我们可以从代码实现的角度来学习线性回归&#xff0c;包括如何使用 Python 进行简单的线…

2024 最新版1200道互联网大厂Java面试题附答案详解

很多 Java 工程师的技术不错&#xff0c;但是一面试就头疼&#xff0c;10 次面试 9 次都是被刷&#xff0c;过的那次还是去了家不知名的小公司。 问题就在于&#xff1a;面试有技巧&#xff0c;而你不会把自己的能力表达给面试官。 应届生&#xff1a;你该如何准备简历&#…

4、CSS3笔记

文章目录 四、CSS3CSS3简介css3概述CSS3私有前缀什么是私有前缀为什么要有私有前缀常见浏览器私有前缀 CSS3基本语法CSS3新增长度单位CSS3新增颜色设置方式CSS3新增选择器CSS3新增盒模型相关属性box-sizing 怪异盒模型resize 调整盒子大小box-shadow 盒子阴影opacity 不透明度 …

【ChatGPT插件漏洞三连发之一】未授权恶意插件安装

漏洞 要了解第一个漏洞&#xff0c;我们必须首先向您展示 OAuth 身份验证的工作原理&#xff1a; 假设您是 Dan&#xff0c;并且您想使用您的 Facebook 帐户连接到 Example.com。当您点击“使用Facebook登录”时会发生什么&#xff1f; 在步骤 2-3 中&#xff1a; 在 Dan 单…

QT枚举类型转字符串和使用QDebug<<重载输出私有枚举类型

一 将QT自带的枚举类型转换为QString 需要的头文件&#xff1a; #include <QMetaObject> #include <QMetaEnum> 测试代码 const QMetaObject *metaObject &QImage::staticMetaObject;QMetaEnum metaEnum metaObject->enumerator(metaObject->indexOf…

【ubuntu18.04】ubuntu18.04升级cmake-3.29.8及还原系统自带cmake操作说明

参考链接 cmake升级、更新&#xff08;ubuntu18.04&#xff09;-CSDN博客 升级cmake操作说明 下载链接 Download CMake 下载版本 下载软件包 cmake-3.30.3-linux-x86_64.tar.gz 拷贝软件包到虚拟机 cp /var/run/vmblock-fuse/blockdir/jrY8KS/cmake-3.29.8-linux-x86_64…

详解mac系统通过brew安装mongodb与使用

本文目录 一、通过brew安装MongoDB二、mongodb使用示例1、启动数据库2、创建/删除数据库3、创建/删除集合 三、MongoDB基本概念1&#xff09;数据库 (database)2&#xff09;集合 &#xff08;collection&#xff09;3) 文档&#xff08;document&#xff09;4&#xff09;mong…

什么是感知与计算融合?

感知与计算融合&#xff08;Perception-Computing Fusion&#xff09;是指将感知技术&#xff08;如传感器、摄像头等&#xff09;与计算技术&#xff08;如数据处理、人工智能等&#xff09;有机结合&#xff0c;以实现对环境的更深层次理解和智能反应的过程。该技术广泛应用于…

基于ISO13400实现的并行刷写策略

一 背景及挑战 随着车辆智能化的逐渐普及&#xff0c;整车控制器数量的急剧增加&#xff0c;加之软件版本的迭代愈发频繁&#xff0c;使整车控制器刷写的数据量变得越来越大。面对如此多的控制器刷写&#xff0c;通过传统的控制器顺序刷写则易出现刷写时间过长的情况&#xff…

将本地文件上传到GIT上

上传文件时&#xff0c;先新建一个空文件&#xff0c;进行本地库初始化&#xff0c;再进行远程库克隆&#xff0c;将要上传的文件放到克隆下来的文件夹里边&#xff0c;再进行后续操作 1.在本地创建文件夹&#xff0c;将要上传的文件放在该文件下 2.在该文件页面中打开Git Bas…

免登录H5快手商城系统/抖音小店商城全开源运营版本

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 最近因为直播需要然后在互站花500买了一套仿抖音的商城系统&#xff0c;感觉确实还可以&#xff0c;反正都买了所以就分享给有需要的人 以下是互站那边的网站介绍可以了看一下&#…

【路径规划】基于蚁群算法的飞行冲突解脱

摘要 飞行冲突解脱是空中交通管理中的重要问题&#xff0c;确保飞机之间安全的距离避免冲突尤为重要。本文提出了一种基于蚁群算法的飞行冲突解脱方法&#xff0c;通过优化飞行器的路径&#xff0c;实现冲突的有效解脱。蚁群算法是一种模拟蚂蚁觅食行为的启发式算法&#xff0…

大厂为什么要禁止使用数据库自增主键

大表为何不能用自增主键&#xff1f; 数据库自增主键&#xff0c;以mysql为例&#xff0c;设置表的ID列为自动递增&#xff0c;便可以在插入数据时&#xff0c;ID字段值自动从1开始自动增长&#xff0c;不需要人为干预。 在小公司&#xff0c;或者自己做项目时&#xff0c;设置…

爬虫基础--requests模块

1、requests模块的认识 requests模块的认识请跳转到 requests请求库使用_使用requests库-CSDN博客 2、爬取数据 这里我们以b站动漫追番人数为例。 首先进去b站官网 鼠标右键点击检查或者键盘的F12&#xff0c;进入开发者模式。&#xff08;这里我使用的是谷歌浏览器为例&#…

二分查找_ x 的平方根搜索插入位置山脉数组的峰顶索引

x 的平方根 在0~X中肯定有数的平方大于X&#xff0c;这是肯定的。我们需要从中找出一个数的平方最接近X且不大于X。0~X递增&#xff0c;它们的平方也是递增的&#xff0c;这样我们就可以用二分查找。 我们找出的数的平方是<或者恰好X&#xff0c;所以把0~X的平方分为<X …

Elasticsearch是做什么的?

初识elasticsearch 官方网站&#xff1a;Elasticsearch&#xff1a;官方分布式搜索和分析引擎 | Elastic Elasticsearch是做什么的&#xff1f; Elasticsearch 是一个分布式搜索和分析引擎&#xff0c;专门用于处理大规模数据的实时搜索、分析和存储。它基于 Apache Lucene …

文言文编程,没错,尤雨溪都点赞了

文言文编程&#xff0c;没错&#xff0c;尤雨溪都点赞了 在现代编程语言百花齐放的今天&#xff0c;居然有人选择用古典汉语来写代码&#xff1f;这就是文言编程语言 Wenyan-lang&#xff0c;一种让你在写代码时&#xff0c;仿佛重回古代&#xff0c;挥毫泼墨般潇洒。本文将带你…

Ubuntu22.04安装RTX3080

Ubuntu22.04安装RTX3080 1 安装基础环境 更新依赖包 sudo apt-get update sudo apt-get upgrade2 安装驱动 &#xff08;1&#xff09;查看适合的显卡驱动 # 查看可用的驱动 sudo ubuntu-drivers devices# 返回值&#xff0c;推荐版本&#xff1a;nvidia-driver-550 ERROR…

提升C#异步性能:如何正确使用ConfigureAwait(false)避免上下文捕获

前言 在C#开发中&#xff0c;异步编程非常普遍&#xff0c;async/await模式极大地简化了异步任务的编写。然而&#xff0c;随之而来的是一些隐蔽的性能和上下文切换问题。在某些情况下&#xff0c;默认的上下文捕获行为可能会导致性能损耗&#xff0c;特别是在UI应用中&#x…

步骤详解:弹性公网ipv6如何申请?

弹性公网ipv6如何申请&#xff1f;申请弹性公网IPv6的步骤包括&#xff1a;首先登录私有网络控制台&#xff0c;选择弹性网卡并进入实例详情页。在IPv6地址管理标签页中分配IPv6地址&#xff0c;然后通过操作栏下的按钮释放或调整IPv6地址的公网访问能力。最后&#xff0c;配置…