You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

需求:支持将非相邻内存缓冲区转为连续视图的数据结构

实现虚拟连续的分散-聚集缓冲区结构

这个需求我之前做流媒体数据处理时碰到过,本质上就是要实现一个虚拟连续的分散-聚集(scatter-gather)缓冲区——把物理上不相邻的内存块,对外暴露成一块逻辑上完全连续的大缓冲区。下面给你拆解具体的实现思路、API逻辑和注意事项:

核心内部数据结构

首先得在类里维护好所有添加进来的缓冲区元数据,方便后续快速定位和计算:

  • 用一个列表存储每个缓冲区的关键信息:起始地址、长度,以及它在逻辑连续空间中的偏移量(用来快速对应逻辑位置和物理缓冲区)
  • 同时维护一个总长度变量,记录所有缓冲区的总字节数,用来快速判断是否能满足get_buffer的请求

用C++的话,内部可以这么定义:

private:
    struct BufferChunk {
        uint8_t* ptr;
        size_t length;
        size_t logical_offset;
    };
    std::vector<BufferChunk> chunks_;
    size_t total_size_ = 0;
    std::mutex mutex_; // 可选,多线程场景加锁用

add_buffer的实现逻辑

这个函数的作用就是把新的物理缓冲区“注册”到我们的虚拟缓冲区里,逻辑很直接:

  1. 先做合法性检查(比如空指针、长度为0的情况,根据需求加错误处理)
  2. 给新缓冲区分配逻辑偏移量(就是当前的总长度,这样逻辑缓冲区就是按添加顺序拼接起来的)
  3. 把元数据加入列表,更新总长度

代码示例:

void add_buffer(uint8_t* buffer, size_t buffer_length) {
    std::lock_guard<std::mutex> lock(mutex_); // 多线程场景加锁
    if (buffer == nullptr || buffer_length == 0) {
        // 这里可以抛异常、返回错误码,或者直接忽略,看你的需求
        return;
    }
    chunks_.push_back({buffer, buffer_length, total_size_});
    total_size_ += buffer_length;
}

get_buffer的核心实现

这个函数是整个结构的关键——要让用户拿到连续的内存地址,不管物理上是不是跨了多个缓冲区。分三种情况处理:

情况1:单个物理缓冲区就能满足需求

遍历所有已注册的缓冲区,如果有某个缓冲区的长度大于等于请求的buffer_size,直接返回它的起始地址,同时把allocated设为false(告诉用户不用自己释放这块内存)。

情况2:需要跨多个物理缓冲区,但总长度足够

如果没有单个缓冲区能容纳请求的大小,但总长度够,那就临时分配一块连续内存,把虚拟缓冲区对应位置的数据(默认从起始位置开始,要是需要支持任意偏移,可以给API加个offset参数)拷贝进去,返回这块临时内存的地址,把allocated设为true(提醒用户用完要自己释放)。

情况3:总长度不够,直接返回空指针

如果请求的大小超过了所有缓冲区的总长度,直接返回nullptr,allocated设为false。

代码示例:

uint8_t* get_buffer(size_t buffer_size, bool& allocated) {
    std::lock_guard<std::mutex> lock(mutex_);
    allocated = false;
    if (buffer_size == 0 || buffer_size > total_size_) {
        return nullptr;
    }

    // 先找能直接容纳的单个缓冲区
    for (const auto& chunk : chunks_) {
        if (chunk.length >= buffer_size) {
            return chunk.ptr;
        }
    }

    // 没有单个缓冲区能容纳,分配临时内存并拷贝数据
    uint8_t* temp_buf = new (std::nothrow) uint8_t[buffer_size];
    if (temp_buf == nullptr) {
        return nullptr;
    }
    allocated = true;

    size_t copied_bytes = 0;
    uint8_t* dest_ptr = temp_buf;
    for (const auto& chunk : chunks_) {
        if (copied_bytes >= buffer_size) {
            break;
        }
        size_t copy_len = std::min(chunk.length, buffer_size - copied_bytes);
        memcpy(dest_ptr, chunk.ptr, copy_len);
        dest_ptr += copy_len;
        copied_bytes += copy_len;
    }

    return temp_buf;
}

额外的扩展与注意事项

  • 内存泄漏防范:当allocated为true时,用户必须记得用delete[]释放返回的临时内存(如果用malloc分配就用free),不然会造成内存泄漏。要是想更安全,可以用智能指针封装,但用户给定的API是返回裸指针,所以只能靠文档提醒。
  • 写入场景支持:上面的实现默认是读取场景,如果用户要通过返回的连续地址写入数据,还需要加一个同步函数,把临时内存的数据写回各个物理缓冲区:
    void sync_to_chunks(uint8_t* temp_buf, size_t buffer_size) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (temp_buf == nullptr || buffer_size == 0) {
            return;
        }
        size_t copied_bytes = 0;
        uint8_t* src_ptr = temp_buf;
        for (auto& chunk : chunks_) {
            if (copied_bytes >= buffer_size) {
                break;
            }
            size_t copy_len = std::min(chunk.length, buffer_size - copied_bytes);
            memcpy(chunk.ptr, src_ptr, copy_len);
            src_ptr += copy_len;
            copied_bytes += copy_len;
        }
    }
    
  • 线程安全:如果你的结构要在多线程环境下使用,一定要加锁保护chunks_和total_size_的读写,不然会出现数据竞争。
  • 缓冲区有效性:要确保通过add_buffer添加的缓冲区,在整个数据结构的生命周期内都是有效的。如果缓冲区可能被提前释放,得考虑加引用计数或者其他内存管理机制。

内容的提问来源于stack exchange,提问作者s4eed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:34:58