Images¶
save() writes a document as an image: a byte buffer that load() reads back into a document without parsing — no lexing, no building the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where parse_copy() still has to scan the whole input, load() turns that scan into a copy of the node index alone.
Why¶
A document that is parsed once and then read many times — a configuration loaded at startup, a template rendered on every request, a large reference dataset a worker process needs in memory — pays for parsing once but can amortize save()'s cost across every later load. That makes images useful for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache), and load() it on every later use instead of parsing the source text again. They are just as useful for handing a parsed document to another process (or a forked worker) running the same build of the library, since load() turns the transfer into a copy of the node index plus pointers into the received bytes, not a re-parse.
Choosing a check¶
load() takes an image_check that trades validation against speed:
image_check::full(the default) checks everything the parser itself guarantees, so a checked image is exactly as safe to read and serialize as a freshly parsed document — the right choice whenever the image did not come straight from this process's ownsave(), such as a file or a network peer.image_check::boundsonly checks structure and bounds — cheaper, since it skips scanning the text and the decoded strings — and fits a cache the process trusts, one it wrote and reads back itself.image_check::noneskips validation entirely, for an image trusted as much as the process's own memory.
See load()'s Notes for exactly what each level does and does not guarantee.
Example: cache a parsed configuration as an image
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
#include <nlohmann/json_view.hpp>
using nlohmann::json_document;
int main()
{
std::cout << std::boolalpha;
// a configuration a service parses once and then caches as an image, so
// that later requests can load() it instead of parsing the text again
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]})";
const json_document config = json_document::parse(text);
// save() turns the parsed document into a byte buffer: a 64-byte header,
// the node index, the source text, and the decoded strings
const std::vector<std::uint8_t> image = config.save();
std::cout << image.size() << '\n';
// the same document always saves to the same bytes
std::cout << (image == json_document::parse(text).save()) << '\n';
// loading the image back needs no parsing, yet dumps exactly what
// parsing the text produced
const json_document reloaded = json_document::load(image);
std::cout << (reloaded.root().dump() == config.root().dump()) << '\n';
}
Output:
316
true
true
Experimental
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared stable. It is little-endian only (on big-endian targets, save() and load() do not exist), and tied to the library build that wrote it — use it to cache a document or to hand one to another process running the same build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by a future library version.
Credits¶
The idea of a document you can read without parsing comes from zero-copy formats such as FlatBuffers and YaFF; the check load() runs follows the idea of FlatBuffers' Verifier. No code is taken from either.
See also¶
- Zero-copy JSON views - overview
save()- write a document as an imageload()- read an image back into a document
Version history¶
- Added in version 3.13.0 unreleased.