{"id":13766394,"url":"https://github.com/biojppm/rapidyaml","last_synced_at":"2025-05-14T11:12:55.133Z","repository":{"id":39420709,"uuid":"116296061","full_name":"biojppm/rapidyaml","owner":"biojppm","description":"Rapid YAML - a library to parse and emit YAML, and do it fast.","archived":false,"fork":false,"pushed_at":"2025-04-11T11:20:57.000Z","size":8566,"stargazers_count":622,"open_issues_count":36,"forks_count":111,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-11T12:09:19.171Z","etag":null,"topics":["cplusplus","cpp11","custom-allocator","emitter","javascript-library","json","parser","python-library","rapid","serialization","stl","yaml"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/biojppm.png","metadata":{"files":{"readme":"README.md","changelog":"changelog/0.1.0.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-01-04T18:54:22.000Z","updated_at":"2025-04-10T19:52:46.000Z","dependencies_parsed_at":"2023-10-15T15:10:06.104Z","dependency_job_id":"d7d0afb0-74d9-4267-8c28-1c4aef426403","html_url":"https://github.com/biojppm/rapidyaml","commit_stats":{"total_commits":1404,"total_committers":34,"mean_commits":"41.294117647058826","dds":0.07692307692307687,"last_synced_commit":"c183e50c67cf4db1bfed342a1b68b7071eafa3ae"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Frapidyaml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Frapidyaml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Frapidyaml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Frapidyaml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/biojppm","download_url":"https://codeload.github.com/biojppm/rapidyaml/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248420034,"owners_count":21100296,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cplusplus","cpp11","custom-allocator","emitter","javascript-library","json","parser","python-library","rapid","serialization","stl","yaml"],"created_at":"2024-08-03T16:00:54.927Z","updated_at":"2025-04-11T14:38:47.086Z","avatar_url":"https://github.com/biojppm.png","language":"C++","readme":"# Rapid YAML\n[![MIT Licensed](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/biojppm/rapidyaml/blob/master/LICENSE.txt)\n[![release](https://img.shields.io/github/v/release/biojppm/rapidyaml?color=g\u0026include_prereleases\u0026label=release%20\u0026sort=semver)](https://github.com/biojppm/rapidyaml/releases)\n[![Documentation Status](https://readthedocs.org/projects/rapidyaml/badge/?version=latest)](https://rapidyaml.readthedocs.io/latest/?badge=latest)\n\n[![PyPI](https://img.shields.io/pypi/v/rapidyaml?color=g)](https://pypi.org/project/rapidyaml/)\n\n[![Coveralls](https://coveralls.io/repos/github/biojppm/rapidyaml/badge.svg?branch=master)](https://coveralls.io/github/biojppm/rapidyaml)\n[![Codecov](https://codecov.io/gh/biojppm/rapidyaml/branch/master/graph/badge.svg?branch=master)](https://codecov.io/gh/biojppm/rapidyaml)\n\nOr ryml, for short. ryml is a C++ library to parse and emit YAML,\nand do it fast, on everything from x64 to bare-metal chips without\noperating system. (If you are looking to use your programs with a YAML tree\nas a configuration tree with override facilities, take a look at\n[c4conf](https://github.com/biojppm/c4conf)).\n\nryml parses both read-only and in-situ source buffers; the resulting\ndata nodes hold only views to sub-ranges of the source buffer. No\nstring copies or duplications are done, and no virtual functions are\nused. The data tree is a flat index-based structure stored in a single\narray. Serialization happens only at your direct request, after\nparsing / before emitting. Internally, the data tree representation\nstores only string views and has no knowledge of types, but of course,\nevery node can have a YAML type tag. ryml makes it easy and fast to\nread and modify the data tree.\n\nryml is available as a single header file, or it can be used as a\nsimple library with cmake -- both separately (ie\nbuild-\u003einstall-\u003e`find_package()`) or together with your project (ie with\n`add_subdirectory()`). (See below for examples).\n\nryml can use custom global and per-tree memory allocators and error\nhandler callbacks, and is exception-agnostic. ryml provides a default\nimplementation for the allocator (using `std::malloc()`) and error\nhandlers (using using either exceptions, `longjmp()` or\n`std::abort()`), but you can opt out and provide your own memory\nallocation and eg, exception-throwing callbacks.\n\nryml does not depend on the STL, ie, it does not use any std container\nas part of its data structures), but it can serialize and deserialize\nthese containers into the data tree, with the use of optional\nheaders. ryml ships with [c4core](https://github.com/biojppm/c4core), a\nsmall C++ utilities multiplatform library.\n\nryml is written in C++11, and compiles cleanly with:\n* Visual Studio 2015 and later\n* clang++ 3.9 and later\n* g++ 4.8 and later\n* Intel Compiler\n\nryml's API documentation is [available at\nReadTheDocs](https://rapidyaml.readthedocs.io/latest/).\n\nryml is [extensively unit-tested in Linux, Windows and\nMacOS](https://github.com/biojppm/rapidyaml/actions). The tests cover\nx64, x86, wasm (emscripten), arm, aarch64, ppc64le and s390x\narchitectures, and include analysing ryml with:\n  * valgrind\n  * clang-tidy\n  * gcc/clang sanitizers:\n    * memory\n    * address\n    * undefined behavior\n\nryml also [runs in\nbare-metal](https://github.com/biojppm/rapidyaml/issues/193), and\n[RISC-V\narchitectures](https://github.com/biojppm/c4core/pull/69). Both of\nthese are pending implementation of CI actions for continuous\nvalidation, but ryml has been proven to work there.\n\nryml is [available in Python](https://pypi.org/project/rapidyaml/),\nand can very easily be compiled to JavaScript through emscripten (see\nbelow).\n\nSee also [the changelog](https://github.com/biojppm/rapidyaml/tree/master/changelog)\nand [the roadmap](https://github.com/biojppm/rapidyaml/tree/master/ROADMAP.md).\n\n\u003c!-- endpythonreadme --\u003e\n\n\n------\n\n## Table of contents\n* [License](#license)\n* [Is it rapid?](#is-it-rapid)\n  * [Comparison with yaml-cpp](#comparison-with-yaml-cpp)\n  * [Performance reading JSON](#performance-reading-json)\n  * [Performance emitting](#performance-emitting)\n* [Quick start](#quick-start)\n* [Using ryml in your project](#using-ryml-in-your-project)\n  * [Package managers](#package-managers)\n  * [Single header file](#single-header-file)\n  * [As a library](#as-a-library)\n  * [Quickstart samples](#quickstart-samples)\n  * [CMake build settings for ryml](#cmake-build-settings-for-ryml)\n     * [Forcing ryml to use a different c4core version](#forcing-ryml-to-use-a-different-c4core-version)\n* [Other languages](#other-languages)\n  * [JavaScript](#javascript)\n  * [Python](#python)\n* [YAML standard conformance](#yaml-standard-conformance)\n  * [Test suite status](#test-suite-status)\n* [Known limitations](#known-limitations)\n* [Alternative libraries](#alternative-libraries)\n\n------\n## License\n\nryml is permissively licensed under the [MIT license](LICENSE.txt).\n\n\n------\n\n## Is it rapid?\n\nYou bet! On a i7-6800K CPU @3.40GHz:\n * ryml parses YAML at about ~150MB/s on Linux and ~100MB/s on Windows (vs2017). \n * **ryml parses JSON at about ~450MB/s on Linux**, faster than sajson (didn't\n   try yet on Windows).\n * compared against the other existing YAML libraries for C/C++:\n   * ryml is in general between 2 and 3 times faster than [libyaml](https://github.com/yaml/libyaml)\n   * ryml is in general between 10 and 70 times faster than\n     [yaml-cpp](https://github.com/jbeder/yaml-cpp), and in some cases as\n     much as 100x and [even\n     200x](https://github.com/biojppm/c4core/pull/16#issuecomment-700972614) faster.\n\n[Here's the benchmark](./bm/bm_parse.cpp). Using different\napproaches within ryml (in-situ/read-only vs. with/without reuse), a YAML /\nJSON buffer is repeatedly parsed, and compared against other libraries.\n\n### Comparison with yaml-cpp\n\nThe first result set is for Windows, and is using a [appveyor.yml config\nfile](./bm/cases/appveyor.yml). A comparison of these results is\nsummarized on the table below:\n\n| Read rates (MB/s)            | ryml   | yamlcpp | compared     |\n|------------------------------|--------|---------|--------------|\n| appveyor / vs2017 / Release  | 101.5  | 5.3     |  20x / 5.2%  |\n| appveyor / vs2017 / Debug    |   6.4  | 0.0844  |  76x / 1.3%  |\n\n\nThe next set of results is taken in Linux, comparing g++ 8.2 and clang++ 7.0.1 in\nparsing a YAML buffer from a [travis.yml config\nfile](./bm/cases/travis.yml) or a JSON buffer from a [compile_commands.json\nfile](./bm/cases/compile_commands.json). You\ncan [see the full results here](./bm/results/parse.linux.i7_6800K.md).\nSummarizing:\n\n| Read rates (MB/s)           | ryml   | yamlcpp | compared   |\n|-----------------------------|--------|---------|------------|\n| json   / clang++ / Release  | 453.5  | 15.1    |  30x / 3%  |\n| json   /     g++ / Release  | 430.5  | 16.3    |  26x / 4%  |\n| json   / clang++ / Debug    |  61.9  | 1.63    |  38x / 3%  |\n| json   /     g++ / Debug    |  72.6  | 1.53    |  47x / 2%  |\n| travis / clang++ / Release  | 131.6  | 8.08    |  16x / 6%  |\n| travis /     g++ / Release  | 176.4  | 8.23    |  21x / 5%  |\n| travis / clang++ / Debug    |  10.2  | 1.08    |   9x / 1%  |\n| travis /     g++ / Debug    |  12.5  | 1.01    |  12x / 8%  |\n\nThe 450MB/s read rate for JSON puts ryml squarely in the same ballpark\nas [RapidJSON](https://github.com/Tencent/rapidjson) and other fast json\nreaders\n([data from here](https://lemire.me/blog/2018/05/03/how-fast-can-you-parse-json/)).\nEven parsing full YAML is at ~150MB/s, which is still in that performance\nballpark, albeit at its lower end. This is something to be proud of, as the\nYAML specification is much more complex than JSON: [23449 vs 1969 words](https://www.arp242.net/yaml-config.html#its-pretty-complex).\n\n\n### Performance reading JSON\n\nSo how does ryml compare against other JSON readers? Well, it's one of the\nfastest! \n\nThe benchmark is the [same as above](./bm/parse.cpp), and it is reading\nthe [compile_commands.json](./bm/cases/compile_commands.json), The `_arena`\nsuffix notes parsing a read-only buffer (so buffer copies are performed),\nwhile the `_inplace` suffix means that the source buffer can be parsed in\nplace. The `_reuse` means the data tree and/or parser are reused on each\nbenchmark repeat.\n\nHere's what we get with g++ 8.2:\n\n| Benchmark             | Release,MB/s | Debug,MB/s  |\n|:----------------------|-------------:|------------:|\n| rapidjson_arena       |       509.9  |       43.4  |\n| rapidjson_inplace     |      1329.4  |       68.2  |\n| sajson_inplace        |       434.2  |      176.5  |\n| sajson_arena          |       430.7  |      175.6  |\n| jsoncpp_arena         |       183.6  |    ? 187.9  |\n| nlohmann_json_arena   |       115.8  |       21.5  |\n| yamlcpp_arena         |        16.6  |        1.6  |\n| libyaml_arena         |       113.9  |       35.7  |\n| libyaml_arena_reuse   |       114.6  |       35.9  |\n| ryml_arena            |       388.6  |       36.9  |\n| ryml_inplace          |       393.7  |       36.9  |\n| ryml_arena_reuse      |       446.2  |       74.6  |\n| ryml_inplace_reuse    |       457.1  |       74.9  |\n\nYou can verify that (at least for this test) ryml beats most json\nparsers at their own game, with the only exception of\n[rapidjson](https://github.com/Tencent/rapidjson). And actually, in\nDebug, [rapidjson](https://github.com/Tencent/rapidjson) is slower\nthan ryml, and [sajson](https://github.com/chadaustin/sajson)\nmanages to be faster (but not sure about jsoncpp; need to scrutinize there\nthe suspicious fact that the Debug result is faster than the Release result).\n\n\n### Performance emitting\n\n[Emitting benchmarks](bm/bm_emit.cpp) also show similar speedups from\nthe existing libraries, also anecdotally reported by some users [(eg,\nhere's a user reporting 25x speedup from\nyaml-cpp)](https://github.com/biojppm/rapidyaml/issues/28#issue-553855608). Also, in\nsome cases (eg, block folded multiline scalars), the speedup is as\nhigh as 200x (eg, 7.3MB/s -\u003e 1.416MG/s).\n\n\n### CI results and request for files\n\nWhile a more effective way of showing the benchmark results is not\navailable yet, you can browse through the [runs of the benchmark\nworkflow in the\nCI](https://github.com/biojppm/rapidyaml/actions/workflows/benchmarks.yml)\nto scroll through the results for yourself.\n\nAlso, if you have a case where ryml behaves very nicely or not as\nnicely as claimed above, we would definitely like to see it! Please\nopen an issue, or submit a pull request adding the file to\n[bm/cases](bm/cases), or just send us the files.\n\n\n------\n\n## Quick start\n\nIf you're wondering whether ryml's speed comes at a usage cost, you\nneed not: with ryml, you can have your cake and eat it too. Being\nrapid is definitely NOT the same as being unpractical, so ryml was\nwritten with easy AND efficient usage in mind, and comes with a two\nlevel API for accessing and traversing the data tree.\n\nThe following snippet is a very quick overview taken from quickstart\nsample ([see on\ndoxygen](https://rapidyaml.readthedocs.io/latest/group__doc__quickstart.html)/[see\non github](samples/quickstart.cpp). After cloning ryml\n(don't forget the `--recursive` flag for git), you can very easily\nbuild and run this executable using any of the build samples, eg the\n[`add_subdirectory()` sample](samples/add_subdirectory/) (see [the relevant section](#quickstart-samples)).\n\n```cpp\n// Parse YAML code in place, potentially mutating the buffer:\nchar yml_buf[] = \"{foo: 1, bar: [2, 3], john: doe}\";\nryml::Tree tree = ryml::parse_in_place(yml_buf);\n\n// ryml has a two-level API:\n//\n// The lower level index API is based on the indices of nodes,\n// where the node's id is the node's position in the tree's data\n// array. This API is very efficient, but somewhat difficult to use:\nsize_t root_id = tree.root_id();\nsize_t bar_id = tree.find_child(root_id, \"bar\"); // need to get the index right\nCHECK(tree.is_map(root_id)); // all of the index methods are in the tree\nCHECK(tree.is_seq(bar_id));  // ... and receive the subject index\n\n// The node API is a lightweight abstraction sitting on top of the\n// index API, but offering a much more convenient interaction:\nryml::ConstNodeRef root = tree.rootref();  // a const node reference\nryml::ConstNodeRef bar = tree[\"bar\"];\nCHECK(root.is_map());\nCHECK(bar.is_seq());\n\n// The resulting tree stores only string views to the YAML source buffer.\nCHECK(root[\"foo\"] == \"1\");\nCHECK(root[\"foo\"].key().str == yml_buf + 1);\nCHECK(bar[0] == \"2\");\nCHECK(root[\"john\"] == \"doe\");\n\n//------------------------------------------------------------------\n// To get actual values, you need to deserialize the nodes.\n// Deserializing: use operator\u003e\u003e\n{\n    int foo = 0, bar0 = 0, bar1 = 0;\n    std::string john_str;\n    std::string bar_str;\n    root[\"foo\"] \u003e\u003e foo;\n    root[\"bar\"][0] \u003e\u003e bar0;\n    root[\"bar\"][1] \u003e\u003e bar1;\n    root[\"john\"] \u003e\u003e john_str; // requires from_chars(std::string). see API doc.\n    root[\"bar\"] \u003e\u003e ryml::key(bar_str); // to deserialize the key, use the tag function ryml::key()\n    CHECK(foo == 1);\n    CHECK(bar0 == 2);\n    CHECK(bar1 == 3);\n    CHECK(john_str == \"doe\");\n    CHECK(bar_str == \"bar\");\n}\n\n//------------------------------------------------------------------\n// To modify existing nodes, use operator= or operator\u003c\u003c.\n\n// operator= assigns an existing string to the receiving node.\n// The contents are NOT copied, and this pointer will be in effect\n// until the tree goes out of scope! So BEWARE to only assign from\n// strings outliving the tree.\nwroot[\"foo\"] = \"says you\";\nwroot[\"bar\"][0] = \"-2\";\nwroot[\"bar\"][1] = \"-3\";\nwroot[\"john\"] = \"ron\";\n// Now the tree is _pointing_ at the memory of the strings above.\n// In this case it is OK because those are static strings and will\n// outlive the tree.\nCHECK(root[\"foo\"].val() == \"says you\");\nCHECK(root[\"bar\"][0].val() == \"-2\");\nCHECK(root[\"bar\"][1].val() == \"-3\");\nCHECK(root[\"john\"].val() == \"ron\");\n// But WATCHOUT: do not assign from temporary objects:\n// {\n//     std::string crash(\"will dangle\");\n//     root[\"john\"] = ryml::to_csubstr(crash);\n// }\n// CHECK(root[\"john\"] == \"dangling\"); // CRASH! the string was deallocated\n\n// operator\u003c\u003c first serializes the input to the tree's arena, then\n// assigns the serialized string to the receiving node. This avoids\n// constraints with the lifetime, since the arena lives with the tree.\nCHECK(tree.arena().empty());\nwroot[\"foo\"] \u003c\u003c \"says who\";  // requires to_chars(). see serialization samples below.\nwroot[\"bar\"][0] \u003c\u003c 20;\nwroot[\"bar\"][1] \u003c\u003c 30;\nwroot[\"john\"] \u003c\u003c \"deere\";\nCHECK(root[\"foo\"].val() == \"says who\");\nCHECK(root[\"bar\"][0].val() == \"20\");\nCHECK(root[\"bar\"][1].val() == \"30\");\nCHECK(root[\"john\"].val() == \"deere\");\nCHECK(tree.arena() == \"says who2030deere\"); // the result of serializations to the tree arena\n\n\n//------------------------------------------------------------------\n// Adding new nodes:\n\n// adding a keyval node to a map:\nCHECK(root.num_children() == 5);\nwroot[\"newkeyval\"] = \"shiny and new\"; // using these strings\nwroot.append_child() \u003c\u003c ryml::key(\"newkeyval (serialized)\") \u003c\u003c \"shiny and new (serialized)\"; // serializes and assigns the serialization\nCHECK(root.num_children() == 7);\nCHECK(root[\"newkeyval\"].key() == \"newkeyval\");\nCHECK(root[\"newkeyval\"].val() == \"shiny and new\");\nCHECK(root[\"newkeyval (serialized)\"].key() == \"newkeyval (serialized)\");\nCHECK(root[\"newkeyval (serialized)\"].val() == \"shiny and new (serialized)\");\n\n\n//------------------------------------------------------------------\n// Emitting:\n\nryml::csubstr expected_result = R\"(foo: says who\nbar:\n- 20\n- 30\n- oh so nice\n- oh so nice (serialized)\njohn: in_scope\nfloat: 2.4\ndigits: 2.400000\nnewkeyval: shiny and new\nnewkeyval (serialized): shiny and new (serialized)\nnewseq: []\nnewseq (serialized): []\nnewmap: {}\nnewmap (serialized): {}\nI am something: indeed\n)\";\n\n// emit to a FILE*\nryml::emit_yaml(tree, stdout);\n// emit to a stream\nstd::stringstream ss;\nss \u003c\u003c tree;\nstd::string stream_result = ss.str();\n// emit to a buffer:\nstd::string str_result = ryml::emitrs_yaml\u003cstd::string\u003e(tree);\n// can emit to any given buffer:\nchar buf[1024];\nryml::csubstr buf_result = ryml::emit_yaml(tree, buf);\n// now check\nCHECK(buf_result == expected_result);\nCHECK(str_result == expected_result);\nCHECK(stream_result == expected_result);\n\n//------------------------------------------------------------------\n// UTF8\nryml::Tree langs = ryml::parse_in_arena(R\"(\nen: Planet (Gas)\nfr: Planète (Gazeuse)\nru: Планета (Газ)\nja: 惑星（ガス）\nzh: 行星（气体）\n# UTF8 decoding only happens in double-quoted strings,\n# as per the YAML standard\ndecode this: \"\\u263A \\xE2\\x98\\xBA\"\nand this as well: \"\\u2705 \\U0001D11E\"\nnot decoded: '\\u263A \\xE2\\x98\\xBA'\nneither this: '\\u2705 \\U0001D11E'\n)\");\n// in-place UTF8 just works:\nCHECK(langs[\"en\"].val() == \"Planet (Gas)\");\nCHECK(langs[\"fr\"].val() == \"Planète (Gazeuse)\");\nCHECK(langs[\"ru\"].val() == \"Планета (Газ)\");\nCHECK(langs[\"ja\"].val() == \"惑星（ガス）\");\nCHECK(langs[\"zh\"].val() == \"行星（气体）\");\n// and \\x \\u \\U codepoints are decoded, but only when they appear\n// inside double-quoted strings, as dictated by the YAML\n// standard:\nCHECK(langs[\"decode this\"].val() == \"☺ ☺\");\nCHECK(langs[\"and this as well\"].val() == \"✅ 𝄞\");\nCHECK(langs[\"not decoded\"].val() == \"\\\\u263A \\\\xE2\\\\x98\\\\xBA\");\nCHECK(langs[\"neither this\"].val() == \"\\\\u2705 \\\\U0001D11E\");\n\n\n//------------------------------------------------------------------\n// Getting the location of nodes in the source:\n//\n// Location tracking is opt-in:\nryml::Parser parser(ryml::ParserOptions().locations(true));\n// Now the parser will start by building the accelerator structure:\nryml::Tree tree2 = parser.parse_in_arena(\"expected.yml\", expected_result);\n// ... and use it when querying\nryml::Location loc = parser.location(tree2[\"bar\"][1]);\nCHECK(parser.location_contents(loc).begins_with(\"30\"));\nCHECK(loc.line == 3u);\nCHECK(loc.col == 4u);\n```\n\n\n------\n\n## Using ryml in your project\n\nNote that ryml uses submodules. Take care to use the `--recursive`\nflag when cloning the repo, to ensure ryml's submodules are checked\nout as well:\n```bash\ngit clone --recursive https://github.com/biojppm/rapidyaml\n```\nIf you omit `--recursive`, after cloning you will have to do `git\nsubmodule update --init --recursive` to ensure ryml's submodules are\nchecked out.\n\n\n### Single header file\nryml is provided chiefly as a cmake library project, but it can also\nbe used as a single header file, and there is a [tool to\namalgamate](./tools/amalgamate.py) the code into a single header\nfile. The amalgamated header file is provided with each release, but\nyou can also generate a customized file suiting your particular needs\n(or commit):\n\n```console\n[user@host rapidyaml]$ python3 tools/amalgamate.py -h\nusage: amalgamate.py [-h] [--c4core | --no-c4core] [--fastfloat | --no-fastfloat] [--stl | --no-stl] [output]\n\npositional arguments:\n  output          output file. defaults to stdout\n\noptional arguments:\n  -h, --help      show this help message and exit\n  --c4core        amalgamate c4core together with ryml. this is the default.\n  --no-c4core     amalgamate c4core together with ryml. the default is --c4core.\n  --fastfloat     enable fastfloat library. this is the default.\n  --no-fastfloat  enable fastfloat library. the default is --fastfloat.\n  --stl           enable stl interop. this is the default.\n  --no-stl        enable stl interop. the default is --stl.\n```\n\nThe amalgamated header file contains all the function declarations and\ndefinitions. To use it in the project, `#include` the header at will\nin any header or source file in the project, but in one source file,\nand only in that one source file, `#define` the macro\n`RYML_SINGLE_HDR_DEFINE_NOW` **before including the header**. This\nwill enable the function definitions. For example:\n```c++\n// foo.h\n#include \u003cryml_all.hpp\u003e\n\n// foo.cpp\n// ensure that foo.h is not included before this define!\n#define RYML_SINGLE_HDR_DEFINE_NOW\n#include \u003cryml_all.hpp\u003e\n```\n\nIf you wish to package the single header into a shared library, then\nyou will need to define the preprocessor symbol `RYML_SHARED` during\ncompilation.\n\n\n### As a library\nThe single header file is a good approach to quickly try the library,\nbut if you wish to make good use of CMake and its tooling ecosystem,\n(and get better compile times), then ryml has you covered.\n\nAs with any other cmake library, you have the option to integrate ryml into\nyour project's build setup, thereby building ryml together with your\nproject, or -- prior to configuring your project -- you can have ryml\ninstalled either manually or through package managers.\n\nCurrently [cmake](https://cmake.org/) is required to build ryml; we\nrecommend a recent cmake version, at least 3.13.\n\n\n### Package managers\n\nryml is available in most package managers (thanks to all the\ncontributors!) and linux distributions. But please be aware: those\npackages are maintained downstream of this repository, so if you have\nissues with the package, file a report with the respective maintainer.\n\nHere's a quick roundup (not maintained):\n* Package managers:\n  * [conan](https://conan.io/center/recipes/rapidyaml)\n  * [vcpkg](https://vcpkg.io/en/packages.html): `vcpkg install ryml`\n  * [PyPI](https://pypi.org/project/rapidyaml/)\n  * [brew](https://github.com/Homebrew/homebrew-core/blob/master/Formula/r/rapidyaml.rb)\n* Linux distributions:\n  * Arch Linux/Manjaro:\n    * [rapidyaml (aarch64)](https://archlinuxarm.org/packages/aarch64/rapidyaml)\n    * [rapidyaml-git (AUR)](https://aur.archlinux.org/packages/rapidyaml-git/)\n    * [python-rapidyaml-git (AUR)](https://aur.archlinux.org/packages/python-rapidyaml-git/)\n  * [Fedora Linux](https://getfedora.org/)/[EPEL](https://docs.fedoraproject.org/en-US/epel/):\n    * `dnf install rapidyaml-devel`\n    * `dnf install python3-rapidyaml`\n  * [Gentoo](https://packages.gentoo.org/packages/dev-cpp/rapidyaml)\n  * [OpenSuse](https://build.openbuildservice.org/package/show/Emulators/rapidyaml)\n  * [Slackbuilds](https://slackbuilds.org/repository/15.0/libraries/rapidyaml/)\n  * [AltLinux](https://packages.altlinux.org/en/sisyphus/srpms/rapidyaml/3006055151670528141)\n\nAlthough package managers are very useful for quickly getting up to\nspeed, the advised way is still to bring ryml as a submodule of your\nproject, building both together. This makes it easy to track any\nupstream changes in ryml. Also, ryml is small and quick to build, so\nthere's not much of a cost for building it with your project.\n\n\n### Quickstart samples\n\nThese samples show different ways of getting ryml into your application. All the\nsamples use [the same quickstart executable\nsource](./samples/quickstart.cpp), but are built in different ways,\nshowing several alternatives to integrate ryml into your project. We\nalso encourage you to refer to the [quickstart source](./samples/quickstart.cpp) itself, which\nextensively covers most of the functionality that you may want out of\nryml.\n\nEach sample brings a `run.sh` script with the sequence of commands\nrequired to successfully build and run the application (this is a bash\nscript and runs in Linux and MacOS, but it is also possible to run in\nWindows via Git Bash or the WSL). Click on the links below to find out\nmore about each sample:\n\n| Sample name        | ryml is part of build?   | cmake file   | commands     |\n|:-------------------|--------------------------|:-------------|:-------------|\n| [`singleheader`](./samples/singleheader) | **yes**\u003cbr\u003eryml brought as a single header file,\u003cbr\u003enot as a library | [`CMakeLists.txt`](./samples/singleheader/CMakeLists.txt) | [`run.sh`](./samples/singleheader/run.sh) |\n| [`singleheaderlib`](./samples/singleheaderlib) | **yes**\u003cbr\u003eryml brought as a library\u003cbr\u003ebut from the single header file | [`CMakeLists.txt`](./samples/singleheaderlib/CMakeLists.txt) | [`run_shared.sh` (shared library)](./samples/singleheaderlib/run_shared.sh)\u003cbr\u003e [`run_static.sh` (static library)](./samples/singleheaderlib/run_static.sh) |\n| [`add_subdirectory`](./samples/add_subdirectory) | **yes**                      | [`CMakeLists.txt`](./samples/add_subdirectory/CMakeLists.txt) | [`run.sh`](./samples/add_subdirectory/run.sh) |\n| [`fetch_content`](./samples/fetch_content)      | **yes**                      | [`CMakeLists.txt`](./samples/fetch_content/CMakeLists.txt) | [`run.sh`](./samples/fetch_content/run.sh) |\n| [`find_package`](./samples/find_package)        | **no**\u003cbr\u003eneeds prior install or package  | [`CMakeLists.txt`](./samples/find_package/CMakeLists.txt) | [`run.sh`](./samples/find_package/run.sh) |\n\n\n### CMake build settings for ryml\nThe following cmake variables can be used to control the build behavior of\nryml:\n\n  * `RYML_WITH_TAB_TOKENS=ON/OFF`. Enable/disable support for tabs as\n    valid container tokens after `:` and `-`. Defaults to `OFF`,\n    because this may cost up to 10% in processing time.\n  * `RYML_DEFAULT_CALLBACKS=ON/OFF`. Enable/disable ryml's default\n    implementation of error and allocation callbacks. Defaults to `ON`.\n  * `RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS=ON/OFF` - Enable/disable\n    the same-named macro, which will make the default error handler\n    provided by ryml throw a `std::runtime_error` exception.\n  * `RYML_USE_ASSERT` - enable assertions in the code regardless of\n    build type. This is disabled by default. Failed assertions will\n    trigger a call to the error callback.\n  * `RYML_STANDALONE=ON/OFF`. ryml uses\n    [c4core](https://github.com/biojppm/c4core), a C++ library with low-level\n    multi-platform utilities for C++. When `RYML_STANDALONE=ON`, c4core is\n    incorporated into ryml as if it is the same library. Defaults to `ON`.\n  * `RYML_INSTALL=ON/OFF`. enable/disable install target. Defaults to `ON`.\n\nIf you're developing ryml or just debugging problems with ryml itself, the\nfollowing cmake variables can be helpful:\n  * `RYML_DEV=ON/OFF`: a bool variable which enables development targets such as\n    unit tests, benchmarks, etc. Defaults to `OFF`.\n  * `RYML_DBG=ON/OFF`: a bool variable which enables verbose prints from\n    parsing code; can be useful to figure out parsing problems. Defaults to\n    `OFF`.\n\n#### Forcing ryml to use a different c4core version\n\nryml is strongly coupled to c4core, and this is reinforced by the fact\nthat c4core is a submodule of the current repo. However, it is still\npossible to use a c4core version different from the one in the repo\n(of course, only if there are no incompatibilities between the\nversions). You can find out how to achieve this by looking at the\n[`custom_c4core` sample](./samples/custom_c4core/CMakeLists.txt).\n\n\n------\n\n## Other languages\n\nOne of the aims of ryml is to provide an efficient YAML API for other\nlanguages. JavaScript is fully available, and there is already a\ncursory implementation for Python using only the low-level API. After\nironing out the general approach, other languages are likely to\nfollow (all of this is possible because we're using\n[SWIG](http://www.swig.org/), which makes it easy to do so).\n\n### JavaScript\n\nA JavaScript+WebAssembly port is available, compiled through [emscripten](https://emscripten.org/).\n\n\n### Python\n\n(Note that this is a work in progress. Additions will be made and\nthings will be changed.). The python port is using only the\nindex-based low-level API, which works with node indices and string\nviews. This API is fast, but you may find it hard to use: it does not\nbuild a python structure of dicts/seqs/scalars, and all the scalars\nare strings, and not typed. With that said, it is really fast, and\nonce you have the tree you can still walk over the tree to create the\nnative python structure. Have a look at this [test\nfile](api/python/tests/test_readme.py) to see how the python API\nworks, and to judge whether it may be useful to your case.\n\nAs for performance, in a [timeit benchmark](api/python/bm/parse_bm.py)\ncompared against [PyYaml](https://pyyaml.org/) and\n[ruamel.yaml](https://yaml.readthedocs.io/en/latest/), ryml parses\nquicker by generally 100x and up to 400x:\n\n```\n+----------------------------------------+-------+----------+----------+-----------+\n| style_seqs_blck_outer1000_inner100.yml | count | time(ms) | avg(ms)  | avg(MB/s) |\n+----------------------------------------+-------+----------+----------+-----------+\n| parse:RuamelYamlParse                  |     1 | 4564.812 | 4564.812 |     0.173 |\n| parse:PyYamlParse                      |     1 | 2815.426 | 2815.426 |     0.280 |\n| parse:RymlParseInArena                 |    38 |  588.024 |   15.474 |    50.988 |\n| parse:RymlParseInArenaReuse            |    38 |  466.997 |   12.289 |    64.202 |\n| parse:RymlParseInPlace                 |    38 |  579.770 |   15.257 |    51.714 |\n| parse:RymlParseInPlaceReuse            |    38 |  462.932 |   12.182 |    64.765 |\n+----------------------------------------+-------+----------+----------+-----------+\n```\n(Note that the parse timings above are somewhat biased towards ryml, because\nit does not perform any type conversions in Python-land: return types\nare merely `memoryviews` to the source buffer, possibly copied to the tree's\narena).\n\nAs for emitting, the improvement can be as high as 3000x:\n```\n+----------------------------------------+-------+-----------+-----------+-----------+\n| style_maps_blck_outer1000_inner100.yml | count |  time(ms) |  avg(ms)  | avg(MB/s) |\n+----------------------------------------+-------+-----------+-----------+-----------+\n| emit_yaml:RuamelYamlEmit               |     1 | 18149.288 | 18149.288 |     0.054 |\n| emit_yaml:PyYamlEmit                   |     1 |  2683.380 |  2683.380 |     0.365 |\n| emit_yaml:RymlEmitToNewBuffer          |    88 |   861.726 |     9.792 |    99.976 |\n| emit_yaml:RymlEmitReuse                |    88 |   437.931 |     4.976 |   196.725 |\n+----------------------------------------+-------+-----------+-----------+-----------+\n```\n\n\n------\n\n## YAML standard conformance\n\nryml is feature complete with regards to the YAML specification. All\nthe YAML features are well covered in the unit tests, and expected to\nwork, unless in the exceptions noted below.\n\nOf course, there are many dark corners in YAML, and there certainly\ncan appear cases which ryml fails to parse. Your [bug reports or pull\nrequests](https://github.com/biojppm/rapidyaml/issues) are very\nwelcome.\n\nSee also [the roadmap](./ROADMAP.md) for a list of future work.\n\n\n### Known limitations\n\nryml deliberately makes no effort to follow the YAML standard in the\nfollowing situations:\n\n* ryml's tree does NOT accept containers as map keys: keys stored in\n  the tree must always be scalars. HOWEVER, this is a limitation only\n  of the final tree. The event-based parse engine DOES parse container\n  keys, as it is is meant to be used by other programming languages to\n  create their native data-structures, and it is fully tested and\n  fully conformant (other than the general error permissiveness noted\n  below).\n* Tab characters after `:` and `-` are not accepted tokens, unless\n  ryml is compiled with the macro `RYML_WITH_TAB_TOKENS`. This\n  requirement exists because checking for tabs introduces branching\n  into the parser's hot code and in some cases costs as much as 10%\n  in parsing time.\n* Non-unique map keys are allowed. Enforcing key uniqueness in the\n  parser or in the tree would cause log-linear parsing complexity (for\n  root children on a mostly flat tree), and would increase code size\n  through added structural, logical and cyclomatic complexity. So\n  enforcing uniqueness in the parser would hurt users who may not care\n  about it (they may not care either because non-uniqueness is OK for\n  their use case, or because it is impossible to occur). On the other\n  hand, any user who requires uniqueness can easily enforce it by\n  doing a post-parse walk through the tree. So choosing to not enforce\n  key uniqueness adheres to the spirit of \"don't pay for what you\n  don't use\".\n* `%YAML` directives have no effect and are ignored.\n* `%TAG` directives are limited to a default maximum of 4 instances\n  per `Tree`. To increase this maximum, define the preprocessor symbol\n  `RYML_MAX_TAG_DIRECTIVES` to a suitable value. This arbitrary limit\n  reflects the usual practice of having at most 1 or 2 tag directives;\n  also, be aware that this feature is under consideration for removal\n  in YAML 1.3.\n* Byte Order Marks: while ryml correctly handles BOMs at the beginning\n  of the stream or documents (as per the standard), BOMs inside\n  scalars are ignored. The [standard mandates that they should be\n  quoted](https://yaml.org/spec/1.2.2/#52-character-encodings) when\n  emitted, this is not done.\n* ryml tends to be on the permissive side in several cases where the\n  YAML standard dictates that there should be an error; in many of these\n  cases, ryml will tolerate the input. This may be good or bad, but in\n  any case is being improved on, meaning ryml will grow progressively\n  less tolerant of YAML errors in the coming releases. So we strongly\n  suggest to stay away from those dark corners of YAML which are\n  generally a source of problems; this is good practice anyway.\n\nIf you do run into trouble and would like to investigate conformance\nof your YAML code, **beware** of existing online YAML linters, many of\nwhich are not fully conformant. Instead, try using\n[https://play.yaml.io](https://play.yaml.io), an amazingly useful tool\nwhich lets you dynamically input your YAML and continuously see the\nresults from all the existing parsers (kudos to @ingydotnet and the\npeople from the YAML test suite). And of course, if you detect\nanything wrong with ryml, please [open an\nissue](https://github.com/biojppm/rapidyaml/issues) so that we can\nimprove.\n\n\n### Test suite status\n\nAs part of its CI testing, ryml uses the [YAML test\nsuite](https://github.com/yaml/yaml-test-suite). (See also the test\nsuite results at\n[https://matrix.yaml.info/](https://matrix.yaml.info/), but be aware\nthat the results there may be using an older version of ryml.) This is\nan extensive and merciless set of reference cases covering the full\nYAML spec. Each of these cases has several subparts:\n * `in-yaml`: mildly, plainly or extremely difficult-to-parse YAML\n * `in-json`: equivalent JSON (where possible/meaningful)\n * `out-yaml`: equivalent standard YAML\n * `emit-yaml`: equivalent standard YAML\n * `events`: reference events according to the YAML standard\n\nWhen testing, ryml parses each of the yaml/json parts, then emits the\nparsed tree, then parses the emitted result and verifies that emission\nis idempotent, ie that the round trip emitted result is semantically\nthe same as its input without any loss of information.\n\nTo ensure consistency, this happens over four successive levels of\nparse-\u003eemit round trips. And to ensure correctness, each of the stages\nis compared against the `events` spec from the test, which constitutes\nthe reference. The tests also check for equality between the reference\nevents in the test case and the events emitted by ryml from the data\ntree parsed from the test case input. All of this is then carried out\nwith several variations: both unix `\\n` vs windows `\\r\\n` line\nendings, emitting to string, file or streams, which results in ~250\ntests per case part.\n\nWith multiple parts per case and ~400 reference\ncases in the test suite, this makes over several hundred thousand\nindividual tests to which ryml is subjected, which are added to the\nunit tests in ryml, which also employ the same extensive combinatorial\napproach.\n\nAlso, note that in [their own words](http://matrix.yaml.info/), the\ntests from the YAML test suite *contain a lot of edge cases that don't\nplay such an important role in real world examples*. And yet, despite\nthe extreme focus of the test suite, currently ryml only fails a minor\nfraction of the test cases, mostly related with the deliberate\nlimitations noted above.\n\nOther than those limitations, by far the main issue with ryml is that\nseveral standard-mandated parse errors fail to materialize (this will\nbe addressed in the coming releases). For the up-to-date list of ryml\nfailures in the test-suite, refer to the [list of known\nexceptions](test/test_suite/test_suite_parts.cpp) from ryml's test\nsuite runner, which is used as part of ryml's CI setup.\n\n\n\n------\n\n## Alternative libraries\n\nWhy this library? Because none of the existing libraries was quite\nwhat I wanted. When I started this project in 2018, I was aware of these two\nalternative C/C++ libraries:\n\n  * [libyaml](https://github.com/yaml/libyaml). This is a bare C\n    library. It does not create a representation of the data tree, so\n    I don't see it as practical. My initial idea was to wrap parsing\n    and emitting around libyaml's convenient event handling, but to my\n    surprise I found out it makes heavy use of allocations and string\n    duplications when parsing. I briefly pondered on sending PRs to\n    reduce these allocation needs, but not having a permanent tree to\n    store the parsed data was too much of a downside.\n  * [yaml-cpp](https://github.com/jbeder/yaml-cpp). This library may\n    be full of functionality, but is heavy on the use of\n    node-pointer-based structures like `std::map`, allocations, string\n    copies, polymorphism and slow C++ stream serializations. This is\n    generally a sure way of making your code slower, and strong\n    evidence of this can be seen in the benchmark results above.\n\nRecently [libfyaml](https://github.com/pantoniou/libfyaml)\nappeared. This is a newer C library, fully conformant to the YAML\nstandard with an amazing 100% success in the test suite; it also offers\nthe tree as a data structure. As a downside, it does not work in\nWindows, and it is also multiple times slower parsing and emitting.\n\nWhen performance and low latency are important, using contiguous\nstructures for better cache behavior and to prevent the library from\ntrampling caches, parsing in place and using non-owning strings is of\ncentral importance. Hence this Rapid YAML library which, with minimal\ncompromise, bridges the gap from efficiency to usability. This library\ntakes inspiration from\n[RapidJSON](https://github.com/Tencent/rapidjson) and\n[RapidXML](http://rapidxml.sourceforge.net/).\n \n","funding_links":[],"categories":["Yaml","Automation"],"sub_categories":["Controllers"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiojppm%2Frapidyaml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbiojppm%2Frapidyaml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiojppm%2Frapidyaml/lists"}