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.rhkb.cn/news/452947.html

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

相关文章

【MySQL】清理二进制日志文件 binlog.000XXX 以解决 Ubuntu 系统磁盘空间耗尽的问题

问题描述 在使用Ubuntu系统时&#xff0c;发现磁盘空间异常地被填满。通过使用ncdu工具进行检查&#xff0c;结果显示/var/lib/mysql文件夹占用了高达63GB的存储空间。 进一步查看该目录&#xff0c;发现存在几百个以binlog为前缀的文件。 原因分析 这些以binlog为前缀的文件…

2020年计算机网络408真题解析

第一题&#xff1a; 解析&#xff1a;OSI参考模型网络协议的三要素 网络协议的三要素&#xff1a;语法 &#xff0c;语义&#xff0c;同步&#xff08;时序&#xff09; 语法&#xff1a;定义收发双方所交换信息的格式 语法&#xff1a;定义收发双方所要完成的操作 网页的加载 …

汽车电子笔记之-014:一场FIFO的思考引发将汽车电子DTC相关 - 故障发生前后关键数据记录并回读的功能浅研发

目录 1、概述 2、故障发生前数据记录 2.1、环形数组C语言实现 2.2、FIFO的C语言实现 3、故障发生后数据记录 4、数据存储 4.1、数据进FIFO设计思路 4.2、数据出FIFO设计思路 5、数据回读 1、概述 工作中DTC的冻结帧与扩展数据功能一般用于存储故障发生时刻的一些关键数…

Unity Apple Vision Pro 保姆级开发教程 - Simulator 模拟器使用

教程视频 Apple VisionPro Simulator 模拟器使用教程 Unity Vision Pro 中文课堂教程地址&#xff1a; Unity3D Vision Pro 开发教程【保姆级】 | Unity 中文课堂 ​ VsionOS Simulator 简介 visionOS Simulator 是一个用于开发和测试 visionOS 应用程序的工具。它模拟 Appl…

数仓模型规范设计

模型架构设计 数仓架构一般从宏观上分为三层&#xff1a;操作数据层ODS、公共维度模型层CDM和数据应用层ADS。其中CDM又包含明细数据层DWD、汇总数据层DWS&#xff0c;维度层DIM、根据生产经验这里可在加入数据临时层TMP。架构图如下&#xff1a; ODS 把操作系统的数据几乎无…

高中数学:立体几何-外接球的外心法

文章目录 一、外心法定义二、习题1、例题一2、例题二3、例题三4、例题四 一、外心法定义 依然以三棱锥为例 即&#xff0c;找到三棱锥的外接球的球心&#xff0c;从而可以确定出外接球的半径R。 而三棱锥有四个顶点&#xff0c;这四个顶点必然都在外接球的球面上。 寻找思路…

海蓝色主题移动端后台UI作品集模板源文件分享 figmasketch格式

页面数量&#xff1a;30页 页面尺寸&#xff1a;1920*1080px 发给你的文件&#xff1a;作品集Figma源文件、作品集sketch源文件、部字体文件、高质量作品集包装psd样机文件&#xff08;含手机和电脑样机&#xff09;

设计模式概览

设计模式是一种解决常见编程问题的经验总结&#xff0c;提供了代码的可重用性、可扩展性和可维护性。常见的设计模式有23个&#xff0c;主要分为三大类&#xff1a;创建型模式、结构型模式和行为型模式。下面是这三类设计模式的详细分类和讲解&#xff1a; 一、创建型模式 创建…

linux多窗口调试一些常用命令

在 vim 或 neovim 中使用分屏移动光标的方式&#xff1a; 希望光标从左窗口移动到右侧窗口&#xff1a; 按 Ctrlw 然后按 l&#xff08;小写的 L&#xff09;&#xff0c;光标就会从左边窗口移动到右边窗口。 其它分屏操作&#xff1a; Ctrlw h&#xff1a;移动到左边的窗…

【我的 RT 学习手札】信息收集

相关笔记整理自B站up主泷羽sec全栈渗透测试教学&#xff08;免费&#xff09; 视频链接为泷羽sec的个人空间-泷羽sec个人主页-哔哩哔哩视频 笔记只是方便师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0…

11 图书借阅功能实现(Vue3+element plus +Spring Boot)

目录 1 功能描述2 接口地址3 后端代码4 api/book.js中编写借阅图书的接口代码5 BookResourcesVue.vue组件中完成点击事件borrowBook6 功能演示 1 功能描述 普通用户借阅图书&#xff0c;点击借阅按钮&#xff0c;修改图书状态&#xff0c;最多能够借阅3本图书。 2 接口地址 …

保证缓存一致性的常用套路

缓存更新的套路 看到好些人在写更新缓存数据代码时&#xff0c;先删除缓存&#xff0c;然后再更新数据库&#xff0c;而后续的操作会把数据再装载的缓存中。然而&#xff0c;这个是逻辑是错误的。试想&#xff0c;两个并发操作&#xff0c;一个是更新操作&#xff0c;另一个是…

[MyBatis-Plus]扩展功能详解

代码生成 使用MP的步骤是非常固定的几步操作 基于插件, 可以快速的生成基础性的代码 安装插件安装完成后重启IEDA连接数据库 mp是数据库的名字?serverTimezoneUTC 是修复mysql时区, 不加会报错 生成代码 TablePrefix选项是用于去除表名的前缀, 比如根据tb_user表生成实体类U…

恒定电流下有功率密度,功率密度体积分就是恒定电流的功率

体积趋于0时&#xff0c;体积的功率就叫功率密度 恒定电流的 电场乘距离等于电压 电流面密度*面积等于电流注意&#xff1a;电流面密度不是电荷线面体密度&#xff0c;电荷线面体密度用在静电场中&#xff0c;即电荷不运动这种

redo文件误删除后通过逻辑备份进行恢复

问题描述 开发同事让在一个服务器上查找下先前库的备份文件是否存在&#xff0c;如果存在进行下恢复。翻了服务器发现备份文件存在&#xff0c;多愁了一眼竟翻到了该备份文件于2024.6.17日恢复过的日志&#xff0c;赶紧和开发沟通说2024.6.17号已经恢复过了为啥还要恢复&#x…

ESP32_S3驱动舵机servor sg90

ESP32_S3驱动舵机servor sg90 硬件连接图硬件外观[^1]硬件引脚功能图硬件连接引脚对照表硬件接线图 Arduino IDE添加ESP32_S3开发板[^2]安装SERVO3舵机驱动库[^3]下载库ZIP包安装库 ESP32_S3程序下载方式源代码SERVO库自带例程方式二 参考文献 调试ESP32_S3舵机发现舵机不动。查…

多线程编程

使用多线程完成两个文件的拷贝&#xff0c;分支线程1&#xff0c;拷贝前一半&#xff0c;分支线程2拷贝后一半&#xff0c;主线程用于回收分支线程的资源 #include<myhead.h>typedef struct sockaddr_in addr_in_t; typedef struct sockaddr addr_t; typedef struct soc…

Redis --- 第四讲 --- 常用数据结构 --- Hash、List

一、Hash哈希类型的基本介绍。 哈希表&#xff1a;之前学过的所有数据结构中&#xff0c;最最重要的。 1、日常开发中&#xff0c;出场频率非常高。 2、面试中&#xff0c;非常重要的考点。 Redis自身已经是键值对结构了。Redis自身的键值对就是通过哈希的方式来组织的。把…

【MySQL 保姆级教学】数据类型全面讲解(5)

数据类型 1. 数据类型分类1.1 数值类型1.2 文本和二进制类型1.3 日期类型 2 数值类型2.1 TINYINT 类型2.1.1 默认有符号类型2.1.2 无符号类型 2.2 INT 类型2.2.1 默认有符号类型2.2.2 无符号类型 2.3 BIT 类型2.3.1 语法2.3.2 举例 2.4 FLOAT 类型2.4.1 语法2.4.2 默认有符号类…

OpenCV高级图形用户界面(20)更改窗口的标题函数setWindowTitle()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在OpenCV中&#xff0c;cv::setWindowTitle函数用于更改窗口的标题。这使得您可以在程序运行时动态地更改窗口的标题文本。 函数原型 void cv::…