{"id":13398576,"url":"https://github.com/target/libdart","last_synced_at":"2025-03-14T02:31:25.475Z","repository":{"id":47149966,"uuid":"184644295","full_name":"target/libdart","owner":"target","description":"A High Performance, Network Optimized, JSON Library","archived":true,"fork":false,"pushed_at":"2024-01-20T00:52:29.000Z","size":3454,"stargazers_count":82,"open_issues_count":1,"forks_count":9,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-10T05:57:36.521Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/target.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-02T20:05:27.000Z","updated_at":"2024-09-01T20:48:05.000Z","dependencies_parsed_at":"2024-01-18T11:02:46.153Z","dependency_job_id":"39ac1c3b-3acc-4452-a9bb-8690917d6bdd","html_url":"https://github.com/target/libdart","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/target%2Flibdart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/target%2Flibdart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/target%2Flibdart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/target%2Flibdart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/target","download_url":"https://codeload.github.com/target/libdart/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243511660,"owners_count":20302595,"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":[],"created_at":"2024-07-30T19:00:28.767Z","updated_at":"2025-03-14T02:31:24.656Z","avatar_url":"https://github.com/target.png","language":"C++","readme":"![Dart Logo](benchmark/logo.png)\n==============\n[![Build Status](https://travis-ci.com/target/libdart.svg?branch=master)](https://travis-ci.com/target/libdart)\n[![Build status](https://ci.appveyor.com/api/projects/status/fji5sgka5toa7ieq/branch/master?svg=true)](https://ci.appveyor.com/project/Cfretz244/libdart-lud7s/branch/master)\n[![Coverage Status](https://coveralls.io/repos/github/target/libdart/badge.svg?branch=master)](https://coveralls.io/github/target/libdart?branch=master)\n\n## A High Performance, Easy to Use, Network Optimized, JSON Library\n**Dart** is both a wire-level binary **JSON** protocol, along with a high performance,\nand surprisingly high level, **C++** API to interact with that **JSON**.\nIt is primarily optimized for efficiency of receiver interaction and on-the-wire\nrepresentation size, however, it also allows for dynamic modification when necessary.\n\n**Dart** can be used in any application as a dead-simple, fast, and lightweight\n**JSON** parser, but it first and foremost targets real-time stream processing engines\nin a schema-less environment.\n\nIt offers logarithmic complexity of object key-lookup, guarantees stability of object\niteration, scales [extremely well](PERFORMANCE.md#lookup-finalized-random-fields-1)\nas packet sizes increase, requires **zero** receiver-side memory allocations/parsing/unpacking\nfor read-only interactions, and exposes two interfaces, a header-only **C++14** interface for\ntypical use, and an **ABI** stable **C89** interface for binding against.\n\nAlthough not a **JSON** parser itself, **Dart** leverages the fastest general purpose\n**JSON** parsers available ([source](https://github.com/miloyip/nativejson-benchmark)),\n[RapidJSON](https://github.com/Tencent/rapidjson)\nand [sajson](https://github.com/chadaustin/sajson), for format conversion both into,\nand out of, **JSON**.\n\nFinally, as **Dart** can also be useful when working with config files, it also\nsupports parsing **YAML** via [libyaml](https://github.com/yaml/libyaml.git).\n\n## Contents\n- [Quick Start](#quick-start)\n- [Installation](#compilation-and-installation)\n- [Performance](#performance)\n- [Basic Usage](#basic-usage)\n- [Strongly Typed API](#strongly-typed-api)\n- [API Lifecycle](#api-lifecycle)\n- [Explicit API Lifecycle](#explicit-api-lifecycle)\n- [Mutability and Copy-on-Write](#mutability-and-copy-on-write)\n- [Disabling Refcounting](#refcounted-by-default-disabled-by-request)\n- [Advanced Usage](#customization-points-and-advanced-usage)\n\n## Quick Start\nThis readme covers a wide variety of information for the library, but for the impatient among us,\nhere are some at-a-glance examples. For examples of how to use the **C** binding layer,\nsee our [bindings](BINDINGS.md) document.\n\n**Dart** makes parsing a **JSON** string dead-simple, and crazy [fast](PARSING.md):\n```c++\n#include \u003cdart.h\u003e\n#include \u003ciostream\u003e\n\nint main() {\n  // Fancy string literal is a raw literal.\n  auto json = dart::parse(R\"({\"msg\":\"hello from dart!\"})\");\n  std::cout \u003c\u003c json[\"msg\"].to_json() \u003c\u003c std::endl;\n}\n\n// =\u003e \"hello from dart!\"\n```\n\n**Dart** automatically understands most built-in and `std` types\n(and can be extended to work with any type), making it extremely\neasy and natural to build **JSON**.\n```c++\n#include \u003cdart.h\u003e\n#include \u003ciostream\u003e\n\nint main() {\n  // A simple example with some built-in types.\n  dart::array arr {1, \"two\", 3.14159, true, nullptr};\n  std::cout \u003c\u003c arr \u003c\u003c std::endl;\n\n  // A more complex example with some complex STL types.\n  using clock = std::chrono::system_clock;\n  using value = std::variant\u003cdouble, std::string, clock::time_point\u003e;\n  using sequence = std::vector\u003cvalue\u003e;\n  using map = std::map\u003cstd::string, sequence\u003e;\n\n  // Dart recursively decomposes the type and figures it out.\n  arr.push_back(map {{\"args\", {3.14159, 2.99792, \"top\", \"secret\", clock::now()}}});\n  std::cout \u003c\u003c arr \u003c\u003c std::endl;\n}\n\n// =\u003e [1,\"two\",3.14159,true,null]\n// =\u003e [1,\"two\",3.14159,true,null,{\"args\":[3.14159,2.99792,\"top\",\"secret\",\"2020-02-25T10:58:37.000Z\"]}]\n```\n\nThe **Dart** container types (`dart::object` and `dart::array`) model the API\nof the `std` equivalents (`std::map` and `std::vector` respectively), allowing\nfor extremely expressive, idiomatic, and type-safe interaction with a dynamically\ntyped notation language from a statically typed programming language:\n```c++\n#include \u003cdart.h\u003e\n#include \u003ciostream\u003e\n#include \u003calgorithm\u003e\n\nint main() {\n  // Build the same array as in the last example, but incrementally.\n  dart::array arr;\n  arr.push_back(\"two\").push_front(1);\n  arr.insert(2, 3.14159);\n  arr.resize(5, true);\n  arr.set(4, nullptr);\n  std::cout \u003c\u003c arr \u003c\u003c std::endl;\n\n  // Search for a particular element and erase the rest.\n  auto it = std::find(std::begin(arr), std::end(arr), 3.14159);\n  while (it != std::end(arr)) {\n    it = arr.erase(it);\n  }\n  std::cout \u003c\u003c arr \u003c\u003c std::endl;\n}\n\n// =\u003e [1,\"two\",3.14159,true,null]\n// =\u003e [1, \"two\"]\n```\n\n**Dart** also makes it extremely easy to efficiently share data across machines/processes,\nwithout the receiver needing to reparse the data.\n```c++\n#include \u003cdart.h\u003e\n\n// Function sends data over the network,\n// through shared memory,\n// into a file,\n// wherever.\nsize_t send_data(void const* bytes, size_t len);\n\nint main() {\n  // Suppose we have some very important data\n  dart::object data {\"albums\", dart::array {\"dark side\", \"meddle\", \"animals\"}};\n\n  // It can be finalized into a contiguous, architecture-independent, representation.\n  data.finalize();\n\n  // We can then pass the raw bytes along.\n  // On the receiver end, a new packet instance can be constructed from this buffer\n  // without any additional work or parsing.\n  auto bytes = data.get_bytes();\n  auto sent = send_data(bytes.data(), bytes.size());\n  assert(sent == bytes.size());\n}\n```\n\n## Compilation and Installation\n**Dart** is implemented using modern **C++**, and requires both Microsoft's Guidelines\nSupport Library [GSL](https://github.com/Microsoft/GSL), and a **C++14** enabled toolchain\n(`clang` \u003e= **3.8**, `gcc` \u003e= **5.0**, apple's `clang` \u003e= **8.3**, `MSVC` ~\u003e **19.22**).\nSupport for **C++11**  may be added in the future, but is not currently being pursued.\n\n**Dart** utilizes `cmake` for its build process and currently primarily targets\nLinux/macOS, but has experimental (and improving) support for Windows.\nOn Linux/macOS, it can be built in the following way:\n```bash\n# Clone it down.\ngit clone https://github.com/target/libdart.git\ncd libdart/\n\n# Create the cmake build directory and prepare a build\n# with tests enabled.\nmkdir build\ncd build\n\n# Build, test, and install (assuming a 4 core machine).\n# Running tests isn't mandatory, but is highly recommended.\n# Dart is primarily a header-only library, but also includes\n# an ABI-stable pure C binding layer which can be built and\n# installed with -Dbuild_abi=ON\ncmake .. # -Dbuild_abi=ON\nmake -j 4\nctest\nmake install\n\n# Generate documentation (if desired).\n# Doxygen must have already been installed.\n# Generates documentation inside the directory \"docs\"\ncd ..\ndoxygen\n```\nFor instructions on building for windows, see our [windows](WINDOWS.md) build instructions.\n\n**Dart** can optionally leverage [RapidJSON](https://github.com/Tencent/rapidjson),\n[sajson](https://github.com/chadaustin/sajson), \nand [libyaml](https://github.com/yaml/libyaml.git), and will attempt to detect installations\nautomatically while building, but can be independently specified with `-DDART_HAS_RAPIDJSON`,\n`-DDART_USE_SAJSON`, and `-DDART_HAS_YAML` preprocessor flags.\n\n\n## Performance\n**TL;DR**: **Dart**'s performance is excellent, but to see detailed breakdowns for different\nworkflows, see our [performance](PERFORMANCE.md) document.\nFor those interested in where this performance comes from, see our\n[implementation](IMPLEMENTATION.md) document.\n\n**JSON** parsing performance is a big enough topic to be given its own document, which\ncan be found here: [parsing performance](PARSING.md).\n\n## Basic Usage\nOverly detailed usage examples can be obtained from the `test/` directory, or by building the\nincluded documentation, but some examples of basic usage are included below. For examples of\nhow to use the **C** binding layer, see our [bindings](BINDINGS.md) document.\n\n`dart::packet` is the primary class of the library, the most generic, and is likely to be the\nmost commonly interacted with. It's capable of representing any **JSON** type, has a _wide_\nvariety of conversions defined to make interaction easier, and also contains a large number\nof accessors/introspection functions to work with the values it represents.\n\nAn example of working with an some imaginary **HTTP** response encoded as **JSON**:\n```c++\n// Get some JSON from somewhere.\nstd::string json = input.read();\n\n// Get a packet.\ndart::packet pkt = dart::packet::from_json(json);\n\n// operator[] always returns a new dart::packet instance by value\n// operator\u003c\u003c stringifies the packet and is defined for all types\ndart::packet header = pkt[\"header\"];\nstd::cout \u003c\u003c header[\"User-Agent\"] \u003c\u003c std::endl;\nstd::cout \u003c\u003c header[\"Content-Type\"] \u003c\u003c std::endl;\n\n// Type-specific accessors return machine types and will throw if\n// the packet instance is not of the requested type.\nauto resp = pkt[\"response\"];\nswitch (resp[\"code\"].integer()) {\n  case 200:\n    {\n      // Explicit casts will also throw if not of the correct type\n      std::string_view body {resp[\"body\"]};\n\n      // Optional accesses will never throw, regardless of runtime type\n      double elapsed = resp[\"elapsed_millis\"].decimal_or(0.0);\n      process_success(body, elapsed);\n      break;\n    }\n  case 300:\n    // Object and array types support C++11 range-for\n    // If using range-for with an object, will iterate over values\n    // Will throw if resp[\"resources\"] is not of object or array type\n    for (auto res : resp[\"resources\"]) {\n      // Numeric subscript operator will throw if \"mods\" isn't an array,\n      // but will return a null packet instance if index is out of bounds.\n      // \"at\" member function will always throw if out of bounds\n      resource_moved(res[\"name\"].strv(), res[\"mods\"][0], res[\"paths\"].at(0));\n    }\n    break;\n  case 400:\n    {\n      char const* err = resp[\"user_error\"].str();\n      std::string_view warn = resp[\"warning\"].strv_or(\"\");\n      process_failure(err, warn);\n      break;\n    }\n  default:\n    {\n      // If wishing to iterate over both the keys and values of an object,\n      // kvbegin() returns a tuple of iterators, which is convenient with C++17.\n      // Could also call key_begin() or begin() to get either individually\n      auto ctxt = resp[\"context\"];\n      for (auto [k, v] = ctxt.kvbegin(); v != ctxt.end(); ++k, ++v) {\n        std::string msg = \"Encountered internal error with context \";\n        msg += k-\u003estr();\n        msg += \": \";\n        msg += v-\u003estr();\n        report_internal_error(std::move(msg));\n      }\n      printf(\"Encountered internal error %s\\n\", resp[\"error\"].str_or(\"Unknown error\"));\n    }\n}\n```\n\nBuild a **JSON** object from scratch:\n```c++\n// Create a base object.\n// dart::packet::make_object can take arbitrarily many pairs of arguments.\ndart::packet obj = dart::packet::make_object(\"hello\", \"goodbye\", \"answer\", 42);\n\n// Put some more stuff in it.\nobj.add_field(\"c\", 2.99792);\nobj.add_field(\"none\", nullptr);\nobj.add_field(\"truth\", true);\nobj.add_field(\"lies\", false);\nobj.add_field(\"fib\", dart::packet::make_array(1, 1, 2, 3, 5, 8, 13));\n\n// Send it somewhere.\ndo_something(obj.to_json());\n```\n\nPreparing a **JSON** object to be sent over the network:\n```c++\n// Assuming the packet from the previous example:\nauto buffer = obj.finalize().get_bytes();\n\n// Write the buffer to some output:\nsocket.write(buffer.data(), buffer.size());\n```\n\n## Strongly Typed API\n**Dart** is first and foremost a dynamically typed library, for interacting with a\ndynamically typed notation language, but as **C++** is a statically typed language, it\ncan be useful to statically know the type of a variable at compile-time.\n\nTo enable this use-case, **Dart** exposes a secondary interface, fully interoperable\nwith the first, that enables static type enforcement.\n```c++\n// dart::object is an alias for dart::packet::object.\n// It implements a subset of the dart::packet API that pertains to objects\n// (no calls like integer(), push_back(), etc)\ndart::object obj {\"c\", 2.99792};\n\n// dart::array is an alias for dart::packet::array.\n// It implements a subset of the dart::packet API that pertains to arrays\n// (no calls like decimal(), add_field(), etc)\ndart::array fib {1, 1, 2, 3, 5, 8};\nobj.add_field(\"data\", std::move(fib));\n\n// dart::string is an alias for dart::packet::string.\n// It implements a subset of the dart::packet API that pertains to strings\n// Also implements some type-specific convenience operators that dart::packet doesn't\ndart::string base = \"hello world\";\ndart::string msg = base + \", hello life\";\nmsg += \"!\";\nobj.add_field(\"msg\", std::move(msg));\n\n// dart::number is an alias for dart::packet::number.\n// It implements a subset of the dart::packet API that pertains to numbers.\n// Also implements some type-specific convenience operators that dart::packet doesn't\ndart::number pi = 3.14159;\ndart::number twicepi = pi * 2;\ntwicepi /= 2;\nobj.add_field(\"pi\", std::move(twicepi));\n\n// dart::flag is an alias for dart::packet::flag.\n// It implements a subset of the dart::packet API that pertains to booleans.\ndart::flag truth = true;\nobj.add_field(\"truth\", std::move(truth));\n\n// dart::null also exists for completeness, but isn't very useful\nobj.add_field(\"none\", dart::null {});\n\n// {\"c\":2.99792,\"pi\":3.14159,\"msg\":\"hello world, hello life!\",\"data\":[1,1,2,3,5,8],\"none\":null,\"truth\":true}\nstd::cout \u003c\u003c obj \u003c\u003c std::endl;\n```\n\nA variety of conversions between the APIs are defined, allowing types to implicitly\ndecay into other types which can safely subsume them, and requiring explicit casts\nwhere the conversion might fail.\n\nTo give a concrete example, `dart::object` can decay into `dart::packet` implicitly,\nas `dart::packet` is more general and the operation will always succeed,\nbut `dart::packet` to `dart::object` requires a cast as it might throw.\n\n_All_ **Dart** types are inter-comparable.\n```c++\n// Examples of some implicit/explicit conversions.\ndart::object typed_obj {\"rick\", \"sanchez\", \"morty\", \"smith\"};\ndart::packet untyped_obj = typed_obj;\ndart::object retyped_obj {untyped_obj};\n\n// Since a JSON object can contain any type,\n// dart::object can't know what the type of a particular key is,\n// and therefore still returns dart::packet from its subscript operator.\ndart::packet untyped_name = typed_obj[\"rick\"];\ndart::string typed_name {typed_obj[\"rick\"]};\n\n// If we know in advance that a key will refer to a particular type,\n// we can tell Dart like so\ndart::string retyped_name = typed_obj[\"rick\"].as\u003cdart::string\u003e();\n\n// We can also cast to non-dart types using this method\nstd::string name = typed_obj[\"rick\"].as\u003cstd::string\u003e();\n\n// Finally, if we're not sure, we can use\nstd::optional\u003cstd::string\u003e opt_name = typed_obj[\"rick\"].maybe_as\u003cstd::string\u003e();\n\nassert(untyped_name == typed_name);\nassert(typed_name == retyped_name);\nassert(typed_name == name);\nassert(name == *opt_name);\nassert(typed_obj == untyped_obj);\nassert(typed_obj != typed_name);\nassert(untyped_obj != untyped_name);\n```\nIt is worth noting that while this API is included as a convenience, to allow **Dart** to\nnaturally express data across a variety of different domains and development workflows,\nit does _not_ come with any performance improvement.\n\n## API Lifecycle\nIn addition to representing any type, `dart::packet` can be in one of two distinct states:\n\"finalized\" and non-\"finalized\"\n\nFinalized packets are represented internally as a contiguous buffer of bytes, are immutable,\nand are immediately ready to be sent over a network connection. In exchange for mutability,\nfinalized packets come with **_significant_** performance improvements, with object key-lookups\nin particular seeing a 200%-300% performance increase, and object comparisons speeding up\nby about 100x.\n\n```c++\n// Get an object, starts out mutable.\nauto data = dart::packet::make_object(\"status\", 200, \"err\", nullptr);\nassert(!data.is_finalized());\ndata.add_field(\"message\", \"OK\");\n\n// Transition it into the finalized state.\n// Finalizing a packet requires making a single allocation, walking across the object tree,\n// and then freeing the original tree.\nassert(!data.is_finalized());\ndata.finalize(); // \u003c-- could also use data.lower();\nassert(data.is_finalized());\n\n// Key lookups are now much faster, but we can no longer mutate the packet.\n// The following line would throw:\ndata.add_field(\"can't\", \"do it\"); // \u003c-- BOOM\n\n// We can also now get access to a contiguous buffer of bytes representing the packet,\n// ready to be written out to a file/over the network/etc.\ngsl::span\u003cgsl::byte const\u003e bytes = data.get_bytes();\nfile.write(bytes.data(), bytes.size());\n\n// We can transition back to being a mutable packet, ableit expensively, by calling:\nassert(data.is_finalized());\ndata.definalize(); // \u003c-- could also use data.lift();\nassert(!data.is_finalized());\n\ndata.add_field(\"can\", \"do it\"); // \u003c-- no explosion\n```\n\n## Explicit API Lifecycle\nThe fact that `dart::packet` can interoperably juggle data across two very different\ninternal representations is convenient for many use-cases, but this necessarily comes\nwith an inability to reason at compile-time as to whether a particular packet can be\nmutated. For this reason, **Dart** exposes two other types that explicitly document\nthis division at compile-time: `dart::buffer` and `dart::heap`.\n\n`dart::packet` is internally implemented in terms of `dart::buffer` and `dart::heap`,\nand is implicitly covertible with either, which allows for expressive, safe, and\nperformant usage across a wide variety of domains.\n\n```c++\n// Function takes a dart object that is definitely mutable\nvoid a_high_level_function(dart::heap obj);\n\n// Function takes a dart object that is definitely immutable and can be\n// used as a buffer of bytes.\nvoid a_lower_level_network_function(dart::buffer buf);\n\n// Get a packet using the mutable api.\nauto pkt = dart::heap::make_object(\"hello\", \"world\");\n\n// Do stuff with it.\npkt.add_field(\"missing\", nullptr);\na_high_level_function(pkt);\n\n// Say that we now know that we want an immutable representation,\n// we can describe that in code using dart::buffer.\n// dart::buffer exposes a smaller subset of the API presented by dart::packet,\n// but it still behaves like any other dart object.\ndart::buffer finalized = pkt.finalize();\nassert(finalized.size() == 1U);\nassert(finalized[\"hello\"] == \"world\");\na_lower_level_network_function(finalized);\n\n// We can also convert back and forth as we choose.\n// In general:\n// *  dart::heap|dart::buffer -\u003e dart::packet is essentially free and does not\n//    require a cast.\n// *  dart::heap -\u003e dart::buffer requires a single allocation and walking across\n//    the object tree, and is still relatively cheap, but requires a cast.\n// *  dart::buffer -\u003e dart::heap requires allocations for as at least as many nodes\n//    in the tree and is very expensive; it also requires a cast.\ndart::packet copy = pkt;\ncopy = finalized;\ndart::heap unfinalized {finalized};\ndart::buffer refinalized {unfinalized};\n```\n\nThe explicit API lifecycle also mixes with the strongly typed API, allowing for\ncode like the following:\n```c++\n// Definitely an object, might be mutable (starts out mutable).\ndart::packet::object dynamic {\"pi\", 3.14159, \"c\", 2.99792};\n\n// Definitely a mutable object.\ndart::heap::object mut {\"pi\", 3.14159, \"c\", 2.99792};\n\n// Definitely an immutable object.\ndart::buffer::object immut {\"pi\", 3.14159, \"c\", 2.99792};\n\n// Definitely an array, might be mutable (starts out mutable).\ndart::packet::array dynarr {1, \"fish\", 2, \"fish\"};\n\n// You get the point.\ndart::heap::array mutarr {\"red\", \"fish\", \"blue\", \"fish\"};\n\n// This also interplays with conversions as expected\ndart::buffer buff = immut;\ndart::packet::object dynimmut = immut;\ndart::packet pkt = dynimmut;\n\nassert(dynamic == mut);\nassert(mut == immut);\nassert(immut == dynamic);\nassert(buff == immut);\nassert(dynimmut == immut);\nassert(pkt == buff);\nassert(dynamic != dynarr);\nassert(mut != mutarr);\n```\n\n## Mutability and Copy-on-Write\nBy default, to keep memory in scope safely, **Dart** uses thread safe reference counting\nbased on `std::shared_ptr`. This behavior is a configuration point for the library which\nwill be covered later.\n\nTo allow easy embedding in threaded applications, **Dart** follows a **copy-on-write**\ndata model in non-finalized mode, allowing for frictionless mutation of shared data.\nThis \"just works\" for the most part without the user being very involved, but it can have\nsome surprising implications:\n\nExample of it \"just working\":\n```c++\n// Create a base object.\ndart::object orig {\"hello\", \"world\"};\nassert(orig.refcount() == 1U);\n\n// Copy it.\n// At this point, copy and orig share the same underlying representation, only an atomic\n// increment was performed.\nauto copy = orig;\nassert(orig.refcount() == 2U);\nassert(copy.refcount() == orig.refcount());\n\n// Mutate the copy.\n// At this point, copy performs a shallow copy-out, copying only the immediate\n// level of an arbitrarily deep tree maintained by orig.\ncopy.add_field(\"hello\", \"life\");\nassert(orig.refcount() == 1U);\nassert(copy.refcount() == 1U);\nassert(copy != orig);\n\n// Will output:\n// {\"hello\":\"world\"}\n// {\"hello\":\"life\"}\nstd::cout \u003c\u003c orig \u003c\u003c std::endl;\nstd::cout \u003c\u003c copy \u003c\u003c std::endl;\n```\n\nExample of a surprising implication:\n```c++\n// Get an object with a nested array.\ndart::object obj {\"nested\", dart::array {\"surprise\"}};\n\n// Attempt to remove something from the array.\n// Subscript operator returns a temporary packet instance, which is then mutated, copying out\n// in the processes, and is then destroyed at the semicolon, taking its modifications with it.\n// If you're on a supported compiler, this line will yield a warning about a discarded\n// result.\nobj[\"nested\"].pop_back();\n\n// The original object was not modified, and so the following will abort.\nassert(obj[\"nested\"].empty()); // \u003c-- BOOM\n\n// What should have been done is the following:\nauto nested = obj[\"nested\"].pop_back();\nobj.add_field(\"nested\", std::move(nested));\n\n// This behavior also means that assigning to the subscript operator will not insert\n// as the user might expect, instead assigning to a temporary null value.\n// To avoid confusion on this point, the case is explicitly called out by the library.\nobj[\"oops\"] = dart::string {\"ouch\"}; // \u003c-- WILL NOT COMPILE\n```\n\n## Refcounted by Default, Disabled by Request\nThread-safe reference counting is ideal for many applications. It strikes a nice\nbalance between performance (predictable memory usage, no GC-pauses, etc) and safety,\nwhile also making it trivially easy to share data across threads. Nothing is for free,\nhowever, and there will always be use-cases that demand every last ounce of available\nperformance; **C++** has never been a \"one size fits all\" kind of language.\n\nAs stated previously, **Dart** has a dedicated reference counter API that allows the\nuser to generically customize every facet of how **Dart** implements reference counting\n(covered in-depth in the [advanced](ADVANCED.md) section), however, sometimes it's even\nuseful to be able to selectively _disable_ reference counting for just a particular section\nof code; the **Dart** `view` API allows for this.\n\nAll of the types mentioned thus far (`dart::heap`, `dart::buffer`, `dart::packet`,\nand their strongly typed counterparts) contain the nested type `view`, which is\nrespectively interoperable, and exports a read-only subset of the associated API.\n\nAs a motivating example:\n```c++\n#include \u003cdart.h\u003e\n#include \u003cstdlib.h\u003e\n\n// A short function that samples a couple values\nint64_t work_hard(dart::packet points, int samples = 10) {\n  // Sample some packets \"randomly\"\n  int64_t total = 0;\n  auto len = points.size();\n  for (int i = 0; i \u003c samples; ++i) {\n    total += points[rand() % len].numeric();\n  }\n  return total;\n}\n\n// A computationally heavy function that frequently accesses keys\nint64_t work_really_hard(dart::packet::view points) {\n  int64_t total = 0;\n  auto end = std::end(points);\n  auto start = std::begin(points);\n  while (start != end) {\n    for (auto curr = start; curr != end; ++curr) {\n      total += curr-\u003enumeric_or(0);\n    }\n    ++start;\n  }\n  return total;\n}\n\n// Decides how important the data is and dispatches accordingly\nint64_t process_data(dart::packet data) {\n  auto points = data[\"points\"];\n  if (data[\"really_important\"]) {\n    return work_really_hard(points);\n  } else {\n    return work_hard(points);\n  }\n}\n```\nAs you can see, we've added a new type, `dart::packet::view`, which is implicitly\nconstructible from any instance of `dart::packet`.\nOn the surface, `view` types appear to behave like any other **Dart** type, however,\ninternally they do _**not**_ participate in reference counting, instead caching a\npointer to the instance they were constructed from, which, in the right circumstances,\ncan be a huge performance win.\n\nBecause we're using a `view` type, `work_really_hard` has become a pure function,\nspinning over its input without mutation and accumulating into a local (probably register backed)\nbefore returning; `work_hard`, by contrast, does a lot of unnecessary refcount manipulation\nfor little clear benefit.\n\nJudicious use of `view` types can result in significant performance wins, however, as they\ndo _**not**_ participate in reference counting, they can also dangle and corrupt memory very\neasily if not handled appropriately (`work_really_hard` caching `points` in a static/global\nvariable would be trouble).\nA fairly safe pattern with `view` types is to exclusively pass them down the stack (returning\na `view` requires thought) and think _very_ carefully before putting them in containers.\n\nFinally, a `view` type can be transitioned back into its fully-fledge counterpart at any point\nin time by calling `dart::packet::as_owner` like so:\n```c++\n#include \u003cdart.h\u003e\n#include \u003cunordered_map\u003e\n\nstd::unordered_map\u003cstd::string, dart::packet\u003e cache;\n\ndart::packet update_cache(dart::packet::view data) {\n  bool dummy;\n  auto found = cache.find(data[\"name\"].str());\n  if (found == cache.end()) {\n    std::tie(found, dummy) = cache.emplace(data[\"name\"].str(), data[\"values\"].as_owner());\n  } else {\n    found-\u003esecond = data[\"values\"].as_owner();\n  }\n  return found-\u003esecond;\n}\n```\n\n## Customization Points and Advanced Usage\nFor a guide on the customization points the library exposes, see our\n[Advanced Usage](ADVANCED.md) document.\n","funding_links":[],"categories":["C++","JSON"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarget%2Flibdart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftarget%2Flibdart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarget%2Flibdart/lists"}