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

如何将boost::beast::multibuffer转为std::istream以解析JSON?

问题解答

不需要使用boost::beast::buffers_to_string和std::stringstream中转数据——我们可以直接基于boost::beast::multibuffer的原生内存块构建输入流,完全避免内存拷贝,节省内存开销。

实现思路

boost::beast::multibuffer由多个连续内存块组成,借助Boost.Iostreams库自定义适配缓冲区序列的输入源,就能让boost::property_tree::read_json直接读取multibuffer中的数据,无需复制整个缓冲区内容。

代码示例

#include <boost/beast.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/categories.hpp>
#include <cstring>

namespace beast = boost::beast;
namespace http = beast::http;
namespace pt = boost::property_tree;
namespace io = boost::iostreams;

// 适配beast缓冲区序列的输入源,支持seek以满足read_json的潜在需求
template <typename Buffers>
class BuffersSource {
public:
    using char_type = char;
    using category = io::input_seekable;

    explicit BuffersSource(const Buffers& buffers) 
        : buffers_(buffers), current_buf_(buffers_.begin()), buf_pos_(0) {}

    std::streamsize read(char* dest, std::streamsize n) {
        if (current_buf_ == buffers_.end()) return 0;

        const auto& current_data = *current_buf_;
        const std::size_t available = current_data.size() - buf_pos_;
        const std::streamsize copy_size = std::min(n, static_cast<std::streamsize>(available));

        std::memcpy(dest, current_data.data() + buf_pos_, copy_size);
        buf_pos_ += copy_size;

        if (buf_pos_ == current_data.size()) {
            ++current_buf_;
            buf_pos_ = 0;
        }

        return copy_size;
    }

    std::streampos seek(std::streamoff offset, std::ios_base::seekdir dir) {
        std::streampos target_pos = 0;
        const std::streampos total_size = get_total_size();

        // 计算目标位置
        if (dir == std::ios_base::beg) {
            target_pos = offset;
        } else if (dir == std::ios_base::cur) {
            target_pos = tell() + offset;
        } else if (dir == std::ios_base::end) {
            target_pos = total_size + offset;
        }

        // 确保位置合法
        target_pos = std::max(std::streampos(0), std::min(target_pos, total_size));

        // 定位到对应的缓冲区和偏移
        current_buf_ = buffers_.begin();
        buf_pos_ = 0;
        std::streampos remaining = target_pos;

        while (current_buf_ != buffers_.end() && remaining > static_cast<std::streampos>(current_buf_->size())) {
            remaining -= current_buf_->size();
            ++current_buf_;
        }

        if (current_buf_ != buffers_.end()) {
            buf_pos_ = static_cast<std::size_t>(remaining);
        }

        return target_pos;
    }

    std::streampos tell() const {
        std::streampos total = 0;
        for (auto it = buffers_.begin(); it != current_buf_; ++it) {
            total += it->size();
        }
        total += buf_pos_;
        return total;
    }

private:
    std::streampos get_total_size() const {
        std::streampos total = 0;
        for (const auto& buf : buffers_) {
            total += buf.size();
        }
        return total;
    }

    const Buffers& buffers_;
    typename Buffers::const_iterator current_buf_;
    std::size_t buf_pos_;
};

// 辅助函数:快速创建适配流
template <typename Buffers>
io::stream<BuffersSource<Buffers>> make_buffers_stream(const Buffers& buffers) {
    return io::stream<BuffersSource<Buffers>>(BuffersSource<Buffers>(buffers));
}

// 实际使用场景
void parse_response(const http::response<http::dynamic_body>& res) {
    pt::ptree property_tree;
    const auto& buffers = res.body().data();
    
    // 直接基于multibuffer创建流,无内存拷贝
    auto stream = make_buffers_stream(buffers);
    pt::read_json(stream, property_tree);

    // 后续使用property_tree处理JSON数据
}

简化版本(无需seek场景)

如果业务场景中read_json不会执行seek操作(比如JSON数据是一次性顺序读取,无需回溯),可以简化输入源实现,去掉seek和tell相关逻辑:

template <typename Buffers>
class SimpleBuffersSource {
public:
    using char_type = char;
    using category = io::input;

    explicit SimpleBuffersSource(const Buffers& buffers) 
        : buffers_(buffers), current_buf_(buffers_.begin()), buf_pos_(0) {}

    std::streamsize read(char* dest, std::streamsize n) {
        if (current_buf_ == buffers_.end()) return 0;

        const auto& current_data = *current_buf_;
        const std::size_t available = current_data.size() - buf_pos_;
        const std::streamsize copy_size = std::min(n, static_cast<std::streamsize>(available));

        std::memcpy(dest, current_data.data() + buf_pos_, copy_size);
        buf_pos_ += copy_size;

        if (buf_pos_ == current_data.size()) {
            ++current_buf_;
            buf_pos_ = 0;
        }

        return copy_size;
    }

private:
    const Buffers& buffers_;
    typename Buffers::const_iterator current_buf_;
    std::size_t buf_pos_;
};

关键说明

  • 上述实现直接遍历multibuffer的内存块读取数据,完全不会复制整个缓冲区内容,内存效率远高于buffers_to_string+std::stringstream的组合。
  • 对于大体积JSON响应,这种无拷贝方式能显著降低内存占用,避免不必要的内存分配与拷贝开销。

内容的提问来源于stack exchange,提问作者Стас

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:33:13