如何将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,提问作者Стас
相关产品推荐
相关产品推荐

