[simdjson] 填充字符串 | `document` 对象 | on-demand 模式

第二章:填充字符串

在第一章解析器中,我们学习了simdjson::dom::parsersimdjson::ondemand::parser作为可复用内存的JSON解析工具

本章将深入解析JSON数据输入的核心要求——“填充字符串”。

为何需要填充?

simdjson通过SIMD(单指令多数据)指令实现高性能解析。

这些指令要求以固定字节块(如32或64字节)处理数据,可能越界访问内存。

若JSON数据位于内存末尾且未预留空间,将导致段错误。

为此,simdjson要求输入数据必须包含SIMDJSON_PADDING(默认为64字节)的填充空间。

// 来自include/simdjson/base.h
/*** JSON解析所需的填充字节数*/
constexpr size_t SIMDJSON_PADDING = 64;

例如:100字节的JSON数据需要至少164字节的缓冲区,前100字节存储数据,后64字节为填充(通常置零)。

填充数据管理

simdjson提供两种核心类型处理填充:

1. simdjson::padded_string

内存自主管理型,保证数据尾部包含填充空间。创建时会自动分配新缓冲区并复制数据。

#include <simdjson.h>int main() {// 从C风格字符串创建const char* json_cstr = "{\"name\":\"simdjson\"}";simdjson::padded_string s1(json_cstr, strlen(json_cstr));// 从std::string创建std::string json_std = "{\"count\":42}";simdjson::padded_string s2(json_std);// 使用_padded字面量(推荐)auto s3 = R"({"active":true})"_padded;// 数据访问std::cout << "数据长度:" << s3.size() << std::endl;  // 实际JSON长度return 0;
}

注意:

  • size()返回原始数据长度(不含填充)
  • 属于移动语义类型,不可复制
  • _padded字面量简化创建过程

2. simdjson::padded_string_view

非拥有型视图,适用于已有填充缓冲区的情况。需开发者保证缓冲区有效性。

#include <simdjson.h>
#include <vector>int main() {// 手动创建填充缓冲区std::string source = "{\"value\":99}";std::vector<char> buffer(source.size() + SIMDJSON_PADDING, 0);memcpy(buffer.data(), source.data(), source.size());// 创建视图simdjson::padded_string_view view(buffer.data(), source.size(), buffer.size());// 使用pad()工具处理std::stringstd::string dynamic_str = "{\"id\":123}";auto padded_view = simdjson::pad(dynamic_str);  // 修改原字符串容量// 解析示例simdjson::dom::parser parser;auto doc = parser.parse(view);return 0;
}

注意:

  • pad()会修改原字符串,追加空格至满足填充要求
  • 必须确保底层缓冲区在视图使用期间有效

解析器集成

两种类型均可直接用于解析方法:

// DOM解析示例
simdjson::dom::parser parser;
auto json = R"({"key":"value"})"_padded;
auto result = parser.parse(json);// On-Demand解析(强制要求填充)
simdjson::ondemand::parser ondemand_parser;
auto doc = ondemand_parser.iterate(json);

内存模型

在这里插入图片描述

类型对比指南

特性padded_stringpadded_string_view
内存所有权自主管理依赖外部缓冲区
填充保证自动创建预先存在
适用场景新数据创建已有缓冲区复用
性能影响可能内存拷贝零拷贝
易用性高(推荐新手中(需内存管理经验)

核心要点

  1. 安全第一SIMD指令越界访问可能引发段错误,填充是必须的
  2. 性能权衡:padded_string简化开发但可能内存拷贝,padded_string_view适合高性能场景
  3. 生命周期管理:解析结果依赖原始缓冲区,需确保数据持续有效
  4. 工具链整合_padded字面量和pad()函数提升开发效率

掌握填充字符串机制是使用simdjson的关键下一步,我们将在第三章文档结构中学习如何访问解析后的数据。

下一章:文档结构


第三章:文档(Document)

在前几章中,我们学习了用于解析 JSON 的核心工具:解析器(Parser),以及如何通过填充字符串(Padded String)格式准备 JSON 数据以实现安全快速处理。

现在我们已经准备好解析器并将 JSON 数据放入填充字符串中,执行解析操作会得到什么?在按需(On-Demand)API 中,这就是 simdjson::ondemand::document 的用武之地。

可以document 对象视为初始解析步骤(iterate 调用)的结果

这是探索所提供 JSON 数据的入口点。

什么是按需模式中的文档?

与可能立即在内存中构建完整 JSON 文档树结构的传统 JSON 解析器不同,simdjson 按需 API 采用了不同的方法。

当调用 parser.iterate(padded_string) 时,我们得到的是 simdjson::ondemand::document 对象。

这个 document 并非整个 JSON 的完全解析表示,而更像是定位在 JSON 数据起始位置的智能迭代器游标。它持有导航 JSON 结构和按需解析值所需的必要信息。

  • 想象你的 JSON 数据是装满嵌套在盒子和袋子里的物品的大型宝箱。document 不是宝箱内所有内容的描述,而是打开宝箱的钥匙和指示第一个主容器(JSON 根值)位置的地图。

  • 我们使用这张地图(document 的方法)找到第一个容器,然后通过进一步指令打开它并发现内部物品,仅在需要时挖掘宝物。

这种"按需挖掘"的特性使得按需 API 内存效率极高,尤其适用于只需少量数据的大型 JSON 文件。

获取第一个文档

让我们回顾前几章的简单示例,重点观察获得的 document 对象:

#include <simdjson.h>
#include <iostream>int main() {// 1. 创建解析器实例(我们的工具)simdjson::ondemand::parser parser;// 2. 准备填充字符串格式的 JSON 数据(我们的原材料)// 按需模式需要填充输入simdjson::padded_string json_data = R"({"message": "hello world", "status": true})"_padded;// 3. 使用解析器"遍历"填充数据// 返回需要检查错误的结果对象simdjson::simdjson_result<simdjson::ondemand::document> result = parser.iterate(json_data);// 4. 检查遍历步骤是否成功if (result.error()) {std::cerr << "解析初始化失败: " << result.error() << std::endl;return EXIT_FAILURE;}// 5. 从结果中获取文档对象simdjson::ondemand::document doc = std::move(result.value()); // 使用 std::move 提高效率std::cout << "成功获取文档对象!" << std::endl;// 现在'doc'是我们进入 JSON 的入口// 实际上还未真正*解析*内容,只是设置了迭代器return EXIT_SUCCESS;
}

代码解析:

  • parser.iterate(json_data) 是关键函数调用,接收包含 JSON 的填充字符串
  • 返回 simdjson::simdjson_result<simdjson::ondemand::document>,这是 simdjson 处理潜在错误的方式
  • 检查 result.error() 确保文档迭代器设置成功,此步骤包括快速扫描 JSON 的基本结构有效性并构建内部索引(有时称为"tape")
  • 成功时通过 std::move(result.value()) 获取 simdjson::ondemand::document 对象

获得 doc 对象后,我们即可开始用它访问 JSON 数据。

探索文档根节点

document 对象表示 JSON 结构的根节点。JSON 文档的根可以是任意有效 JSON 值:对象 {}、数组 []、字符串 "abc"、数值 123、布尔值 truenull

document 提供以下方法判断根值类型:

  • doc.type():返回根 JSON 值的类型(如 json_type::object, json_type::array 等)
  • doc.get_object():尝试以 JSON 对象形式访问根节点
  • doc.get_array():尝试以 JSON 数组形式访问根节点
  • doc.get_string(), doc.get_int64(), doc.get_double(), doc.get_bool(), doc.is_null():尝试以标量值形式访问根节点

当调用这些 get_...() 方法时,simdjson 才会真正执行根节点的解析。让我们扩展示例来检查类型并访问根对象:

#include <simdjson.h>
#include <iostream>int main() {simdjson::ondemand::parser parser;simdjson::padded_string json_data = R"({"message": "hello world", "status": true})"_padded;simdjson::simdjson_result<simdjson::ondemand::document> result = parser.iterate(json_data);if (result.error()) {std::cerr << "解析初始化失败: " << result.error() << std::endl;return EXIT_FAILURE;}simdjson::ondemand::document doc = std::move(result.value());// 6. 检查文档根节点类型simdjson::simdjson_result<simdjson::ondemand::json_type> root_type_result = doc.type();if (root_type_result.error()) {std::cerr << "获取根类型错误: " << root_type_result.error() << std::endl;return EXIT_FAILURE;}simdjson::ondemand::json_type root_type = root_type_result.value();if (root_type == simdjson::ondemand::json_type::object) {std::cout << "根节点是 JSON 对象。" << std::endl;// 7. 以对象形式访问根节点simdjson::simdjson_result<simdjson::ondemand::object> obj_result = doc.get_object();if (obj_result.error()) {std::cerr << "获取根对象错误: " << obj_result.error() << std::endl;return EXIT_FAILURE;}simdjson::ondemand::object root_object = obj_result.value();std::cout << "成功访问根对象。" << std::endl;// 后续章节将学习如何使用此'root_object'// 访问"message"和"status"等字段} else {std::cout << "根节点不是对象,类型代码: " << int(root_type) << std::endl;}// 重要提示:'doc'对象、解析器和 json_data 必须保持有效// 只要仍在使用从'doc'派生的任何数据return EXIT_SUCCESS;
}

源码安装库文件:

git clone https://github.com/simdjson/simdjson.git
cd simdjson
mkdir build && cd build
cmake ..
make -j
sudo make install

编译:

g++ -std=c++17 -o simdjson_test/test_simdjson simdjson_test/test_simdjson.cpp -lsimdjson

输出结果:
在这里插入图片描述

此示例展示了如何获取文档、检查类型并以预期类型(本例为 object)访问根节点。获得的 simdjson::ondemand::object 是导航对象内部的入口点,我们将在第五章详细讲解。

注意:即使检查类型和访问根值也会返回 simdjson_result。在按需 API 中,验证和解析是渐进式进行的,在导航或提取数据的任何步骤都可能因 JSON 结构或值无效而产生错误。错误处理至关重要,后续将有专门章节讲解(错误处理)。

文档与依赖关系

必须牢记:simdjson::ondemand::document 对象并非已解析数据的独立副本,而是原始填充 JSON 字符串的视图,依赖解析器的内部状态(如 tape/索引)。

这意味着:

  1. 只要仍在使用从文档获得的任何 objectarrayvaluestring_view 实例,simdjson::ondemand::parser 对象必须保持存活且未被修改
  2. 包含 JSON 数据的原始 simdjson::padded_string(或 padded_string_view 指向的缓冲区)必须保持有效且未被修改
  3. 每个解析器对象同一时间只能激活一个文档对象。若再次调用 parser.iterate(),新文档将使旧文档失效

实现原理(简化版)

调用 parser.iterate(padded_string) 时,解析器会进行初始化工作,主要包括识别大括号、中括号、逗号和引号等结构元素,并构建内部索引(“tape”)。此阶段不会完全解析字符串、数值或数组/对象的内容。

返回的 simdjson::ondemand::document 对象本质上是包装了解析器内部状态(特别是 json_iterator)和填充输入字符串起始位置的指针。

当调用 doc.get_object()doc.type() 等方法时,document 对象使用存储的指针与解析器状态及原始 JSON 数据进行交互。它利用索引快速跳转到 JSON 的相关部分,并执行满足请求所需的最小解析(例如确认根节点是’{',并为对象字段设置迭代器)。

字符串的实际数据(std::string_view)和导航的结构(objectarrayvalue)都是与原始填充字符串和解析器状态绑定的临时视图。

dom::documentondemand::document 对比(简注)

在第一章中,我们简要展示了使用 dom::parser::parse 返回 dom::element 的 DOM 示例。虽然 simdjson 在内部确实有 dom::document 类型(simdjson::dom::parser 持有该类型),但 DOM API 中主要面向用户的结果通常是表示完全解析树节点的 dom::element

相比之下,simdjson::ondemand::document 是按需 API 中的核心用户对象,是 iterate 调用的直接结果,也是惰性导航的起点。

不持有完整的解析树,而是迭代解析过程的句柄。

对于使用按需模式(推荐方式)的初学者,初始阶段主要交互对象是 simdjson::ondemand::parsersimdjson::padded_string(或 padded_string_view)和 simdjson::ondemand::document

流程图:

在这里插入图片描述


🎢初始阶段的on-demand 模式

simdjson 库在初始阶段聚焦于 simdjson::ondemand::parsersimdjson::padded_stringsimdjson::ondemand::document 的设计,主要基于性能优化、内存安全性和接口简洁性的综合考量:

  1. 性能导向的解析器设计
    ondemand::parser 采用 SIMD 指令集加速 JSON 解析,直接操作原始数据而非预解析为 DOM 树。这种延迟加载(lazy parsing)策略避免一次性解析整个文档,仅当访问特定字段时才处理对应数据,极大减少内存占用和初始化开销。

  2. 内存安全的数据容器
    padded_stringpadded_string_view 为 JSON 数据添加尾部填充(padding),确保 SIMD 指令能安全读取超出实际数据末尾的缓冲区。这种设计消除了边界检查开销,同时防止内存越界访问。

  3. 按需文档模型
    ondemand::document 作为轻量级视图,提供对 JSON 数据的惰性访问。它不持有数据所有权,而是基于解析器的内部状态动态生成字段值,避免了传统 DOM 模型的全量内存分配。

交互流程

解析流程通常遵循以下模式:

  1. 创建 parser 实例并复用(避免重复分配资源)
  2. 加载 JSON 数据到 padded_string(或直接映射为 padded_string_view
  3. 通过 parser.iterate() 生成 document 视图
  4. document 上执行具体字段访问
simdjson::ondemand::parser parser;
auto json = simdjson::padded_string::load("data.json");
auto doc = parser.iterate(json);
std::string_view title = doc["title"];

与其他组件的对比

  • 与 DOM API 的区别
    传统 DOM 解析(如 simdjson::document)需完整解析整个 JSON 到内存树,而 on-demand 模式将解析延迟到字段访问时,更适合流式处理或大型文件。

  • 与 SAX 模型的差异
    SAX 需要实现回调函数处理事件,on-demand 则提供更直观的键值访问接口,同时保留相似的性能特性。

这种设计使初始阶段既能保持高性能,又能通过简洁的接口降低使用复杂度,符合现代 C++ 库零开销抽象的原则。

总结

simdjson::ondemand::documentparser.iterate() 的返回对象,代表按需 API 中 JSON 数据的根节点。

  • 关键特性在于它并非完全解析的树结构,而是允许按需解析 JSON 值的迭代器。

我们使用 document 对象作为导航 JSON 数据的起点,通常通过检查其类型并调用 get_object()get_array() 等方法来开始遍历结构。

请牢记依赖关系:文档对象、创建它的解析器以及原始填充字符串数据必须保持有效且在作用域内,才能安全使用从文档获得的任何数据。

现在我们已经掌握如何获取和访问 JSON 文档根节点,下一步是理解单个 JSON 值(如字符串、数值或嵌套对象/数组)的表示和访问方式,这将是下一章数值(Value)的主题。

下一章:数值(Value)

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

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

相关文章

扭蛋机小程序开发:开启线上娱乐新风尚

在当今数字化浪潮席卷的时代&#xff0c;娱乐方式正经历着前所未有的变革。传统的扭蛋机&#xff0c;那充满惊喜与期待的实体装置&#xff0c;曾是无数人童年回忆中的欢乐源泉。如今&#xff0c;随着科技的飞速发展&#xff0c;扭蛋机小程序开发应运而生&#xff0c;将这份经典…

【React Native】布局和 Stack 、Slot

布局和Stack 点击链接后&#xff0c;页面切换时最好是有动画效果。页面一般都有头部&#xff0c;里面有页面的标题之类的东西。 在app目录里&#xff0c;新建一个_layout.js文件&#xff0c;这是项目的布局文件。 这个名字是固定的&#xff0c;前面必须有一个_ 。 布局的意…

3C电子产品蓝光三维扫描检测方案-中科米堆CASAIM

随着3C电子产品向轻薄化、精密化方向发展&#xff0c;传统的二维检测技术已难以满足现代制造业对产品精度的高标准要求。特别是在智能手机、平板电脑等消费电子领域&#xff0c;微小的结构偏差都可能导致产品组装困难或性能下降。当前行业内普遍面临检测效率低、数据采集不完整…

Docker 镜像原理

Union FS(联合文件系统) Union File System 是一种分层、轻量级并且高性能的文件系统&#xff0c;它支持对文件系统的修改作为一次提交来一层层的叠加&#xff0c;同时可以将不同目录挂载到同一个虚拟文件系统下。UnionFS 是一种为 Linux&#xff0c;FreeBSD 和 NetBSD 操作系统…

为什么IoTDB成为物联网场景的技术优选?

在物联网、工业监控等领域&#xff0c;时序数据的高效管理成为技术架构设计的关键环节。时序数据库作为专门处理带时间戳数据的系统&#xff0c;其选型需兼顾性能、兼容性与场景适配性。本文将从技术角度解析 IoTDB 的设计理念与实践方法&#xff0c;为时序数据库选型提供参考。…

js中的微任务和宏任务的理解

在JavaScript中&#xff0c;微任务&#xff08;Microtask&#xff09;和宏任务&#xff08;Macrotask&#xff09;是异步任务执行机制的重要组成部分&#xff0c;它们共同构成了JavaScript事件循环&#xff08;Event Loop&#xff09;的核心逻辑。理解这两个概念对于编写高性能…

Spring-AI系列-AI模型-Model

原文-知识库&#xff0c;欢迎大家评论互动 AI Model API Portable ModelAPI across AI providers for Chat, Text to Image, Audio Transcription, Text to Speech, and Embedding models. Both synchronous and stream API options are supported. Dropping down to access mo…

MySQL查询今天、昨天、上周、近30天、去年等的数据的方法

目录 常用的MySQL查询今天、昨天、上周、近30天、去年等数据的方法 0、Sql server中DateDiff()用法 1、MySQL的DATE_SUB()函数 定义和用法 语法 实例 2、MySQL的TO_DAYS(date) 3、MySQL的DATE() 函数 定义和用法 4、MySQL NOW() 函数 定义和用法 语法 实例 例子 …

Linux —— B / 基础开发工具

一、软件包管理器1.1什么是软件包1.2 Linux软件生态1.3 yum具体操作1.3.1 查看软件包1.3.2 安装软件1.3.3 卸载软件1.3.4 注意事项1.4 安装源二、编辑器Vim2-1 Linux编辑器-vim使用2-2 vim的基本概念2-3 vim的基本操作2-4 vim正常模式命令集2-5 vim末行模式命令集2-6 vim操作总…

SQL,在join中,on和where的区别

0.结论 两个表在&#xff0c;join时&#xff0c;首先做一个笛卡尔积&#xff0c;on后面的条件是对这个笛卡尔积做一个过滤形成一张临时表&#xff0c;如果没有where就直接返回结果&#xff0c;如果有where就对上一步的临时表再进行过滤。 先on&#xff0c;再join&#xff0c;再…

SD-WAN在储能网络中的应用,传统方案如何借力智能化升级?(附网络架构图)

一、储能网络的建设挑战在储能项目中&#xff0c;网络系统通常需要实现以下目标&#xff1a;高可靠性&#xff1a;实时采集和传输储能设备状态数据&#xff0c;链路中断可能导致系统故障。灵活扩展&#xff1a;分布式站点部署广泛&#xff0c;传统网络扩展需重新铺设线路&#…

Oracle11.2.0.4 RAC迁移升级Oracle19.3 RAC

问题描述 填写问题的基础信息。 系统名称 Oracle11.2.0.4迁移升级Oracle19.3 IP地址 操作系统 Centos7.5 数据库 Oracle11.2.0.4迁移升级Oracle19.3 症状表现 问题的症状表现如下 需要将单机的Oracle11.2.0.4环境升级到Oracle19.3.0RAC环境&#xff0c;采用迁移升级的…

SAP-ABAP:SAP的‘cl_http_utility=>escape_url‘对URL进行安全编码方法详解

SAP的’cl_http_utility>escape_url’对URL进行安全编码方法详解 核心作用&#xff1a;对 URL 进行安全编码&#xff0c;将特殊字符转换为 %XX 格式&#xff0c;确保符合 HTTP 传输规范。1. 功能与作用 ✅ URL 安全编码 将非安全字符转换为十六进制 ASCII 码&#xff08;%XX…

基于HarmonyOS的智能灯光控制系统设计:从定时触发到动作联动全流程实战

摘要 随着智能家居的快速普及&#xff0c;人们对居住环境的智能化需求越来越高&#xff0c;其中智能灯光控制是最基础、也是最常用的功能之一。从最初的远程控制发展到如今能“感知环境、自动响应”的智能灯光系统&#xff0c;背后依赖的是强大的系统联动能力。鸿蒙系统作为面向…

ROS1/Linux——linux虚拟机主ip地址:网络信息不可用

ROS1/Linux——linux虚拟机主ip地址&#xff1a;网络信息不可用 文章目录ROS1/Linux——linux虚拟机主ip地址&#xff1a;网络信息不可用参考亿点链接问题描述最终解决方案参考亿点链接 Unable to fetch some archives, maybe run apt-get update or try with –fix-missingli…

ssl相关命令生成证书

当前环境 OpenSSL 3.5.1 1 Jul 2025 (Library: OpenSSL 3.5.1 1 Jul 2025) GmSSL 3.1.2 Dev 本地gmssl命令 #生成证书公私钥对 gmssl sm2keygen -pass 1234 -out sm2.key -pubout sm2pub.pem #使用certgen命令生成自签名证书cert.crt gmssl certgen -C CN -ST Beijing -L Ha…

TensorFlow深度学习实战——DCGAN详解与实现

TensorFlow深度学习实战——DCGAN详解与实现0. 前言1. DCGAN 架构2. 构建 DCGAN 生成手写数字图像2.1 生成器与判别器架构2.2 构建 DCGAN相关链接0. 前言 深度卷积生成对抗网络 (Deep Convolutional Generative Adversarial Network, DCGAN) 是一种基于生成对抗网络 (Generati…

SpringBoot 使用MyBatisPlus

引入依赖<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.3.0</version> </dependency>写一个interface 继承basemapMapper public in…

Git 中如何查看提交历史?常用命令有哪些?

回答重点在 Git 中&#xff0c;我们可以使用 git log 命令来查看提交历史。这个命令会列出所有的提交记录&#xff0c;显示每个提交的哈希值、作者信息、提交时间和提交信息。常用的 git log 命令及其选项有&#xff1a;1&#xff09; git log &#xff1a;显示完整的提交历史。…

Flink数据流高效写入MySQL实战

这段代码展示了如何使用 Apache Flink 将数据流写入 MySQL 数据库&#xff0c;并使用了 JdbcSink 来实现自定义的 Sink 逻辑。以下是对代码的详细解析和说明&#xff1a;代码结构包声明&#xff1a;package sink定义了代码所在的包。导入依赖&#xff1a;导入了必要的 Flink 和…