game-networking-sockets-sys 0.2.0

Rust bindings for Valve GameNetworkingSockets library.
Documentation
# vjson

[![CI](https://github.com/zpostfacto/vjson/actions/workflows/ci.yml/badge.svg)](https://github.com/zpostfacto/vjson/actions/workflows/ci.yml)

`vjson` is a small C++ JSON parser and DOM focusing on good ergonomics.

Design goals:

- **Good ergonomics** when traversing the DOM (see below).  This is the most
  unique goal among the many other libraries available.
- **Small**: one ~750-line header, one ~1700-line .cpp file
- **No external dependencies**.  No boost, no custom containers or
  allocators.  Use STL containers for storage: ``std::vector`` for arrays;
  ``std::map`` for objects.  Strings and keys stored as ``std::string``,
  but access using ``const char *`` is possible in most places.
- **No exceptions/RTTI**.  Also, no ``iostream``.  Safe to use in
  codebases that disable exceptions entirely.
- **DOM-style interface**: read the whole document into some data
  structures at once.  (No SAX-style / streaming interface.)
- **Printing options**: Some basic options for minified or indented.
  (No framework for detailed customization.)
- **Good error messages** if parsing fails, with a line number
  (important for "pretty" / hand-edited JSON) and byte offset (important
  for "minified" JSON).
- **Parsing options**: Comments and trailing commas can be optionally ignored.

Here are some goals this library doesn't have.  (If you need these, try
one of the other libraries below.)

- **Not ultra-fast or efficient**.  This lib aims to not be *grossly* inefficient,
  but it also avoids weird/complicated stuff in the name of efficiency.
  See some of the libs below if you need fast parsing or to load in huge
  documents.
- **Not a header-only library**.  Putting all the parsing implementation guts
  in a header that must be parsed and compiled by every source file that wants
  to load up a JSON document is an anti-pattern.  This adds to build
  time and bloats executables with no benefit to performance.
- Super streamlined syntax for constructing a DOM in C++ code
- No ``istream``, ``FILE*``, iterator interface, etc.  The whole file must
  fit in memory.
- Remember formatting or comments, to support automated modification of
  documents

# Every field access should not require an `if ()` statement

My biggest complaint with other JSON libs is how tedious it can be
to write code to load up a file.  Specifically:

- Code to traverse the DOM should not require an excessive number of ``if``
  statements to handle common situations:
  - An object does not have a value with the specified name
  - An object or array has a value, but it's the wrong type
- Handling "Booleanish" values in a reasonably generous way, such as
  treating a numeric 0 as false.
- Make it easy to deal with 64-bit numbers as strings.  (JSON only
  supports "numbers", which are usually represented as doubles.  A 64-bit
  value encoded as a "number" is very likely to be mutated in transit.)

If you want to be extremely strict about extra/missing keys, values of the
wrong type, etc. then there really is no shortcut for writing detailed,
explicit error handling.  vjson makes it easy to write that kind of code when
the situation calls for it.  But most of us have the more modest aim of
"detect common errors, but otherwise just do 'something reasonable' with
a malformed document, with the least amount of coding effort possible".
That's when vjson shines.  For example:

- If a key is missing, it can supply the default in a single function call.
- If an array or object is missing, return an empty one.
- If a value is present at the given key, but the wrong type, just act as
  if it is missing.
- If an array is only supposed to contain a certain type of value, iterate
  over the elements with that type in an idiomatic way, ignoring any elements
  that are the wrong type.
- When loading/parsing a document that is supposed to be a single JSON object,
  just fail if the input is some other JSON value, and don't make me write
  an explicit check for that case.

For example:

```json
{
    "ranked": 1,
    "server": "Seattle #4",
    "match_id": "14889406635632900096",
    "players": [
        { "name": "Alice", "score": 1500 },
        42,
        { "name": "Bob",   "score": 1200 },
        { "name": "Carl" }
    ],
    "top_scores": []
}
```

here is how we could extra data from this this file using vjson:

```cpp
vjson::Object doc;
vjson::ParseContext ctx;
if ( !doc.ParseJSON( file_contents, &ctx ) )
{
    fprintf( stderr, "Parse failed line %d: %s\n",
        ctx.error_line, ctx.error_message.c_str() );
    return false;
}

// "ranked" is integer 1, not bool.
// InterpretAsBoolAtKey() converts 0/1 to false/true.
bool ranked = doc.InterpretAsBoolAtKey( "ranked", false );
// ranked == true

// "timeout_ms" key is absent.
// DoubleAtKey() returns the supplied default without complaint.
double timeout = doc.DoubleAtKey( "timeout_ms", 5000.0 );
// timeout == 5000.0

// 64-bit IDs must be stored as strings in JSON -- a JSON "number" is a
// double, which only has 53 bits of mantissa and will silently corrupt a
// 64-bit value.  InterpretAsUint64AtKey() reads a string and parses it,
// so the full precision is preserved.
uint64_t match_id = doc.InterpretAsUint64AtKey( "match_id", 0 );
// match_id == 14889406635632900096ull

// "spectators" key is absent entirely.
// ArrayAtKeyOrEmpty() returns a static empty array reference;
// Iter<Object>() produces zero iterations.  Loop body never executes.
for ( const vjson::Object &p : doc.ArrayAtKeyOrEmpty( "spectators" ).Iter<vjson::Object>() )
    printf( "spectator: %s\n", p.StringAtKey( "name", "?" ).c_str() );

// "players" has a stray integer 42 mixed in with the objects.
// Iter<Object>() silently skips any element that isn't an object.
for ( const vjson::Object &p : doc.ArrayAtKeyOrEmpty( "players" ).Iter<vjson::Object>() )
    printf( "%s: %.0f\n", p.StringAtKey( "name", "?" ).c_str(), p.DoubleAtKey( "score", 0.0 ) );
// Prints "Alice: 1500", "Bob: 1200", and "Carl: 0"
// - The stray 42 is silently skipped.
// - Carl's score is missing so the default is used

// "top_scores" is present but empty.  We want the name of the top scorer.
// AtIndex(0) on an empty (or absent) array returns a static null Value;
// CStringAtKey() on a null Value returns the default.  No crash, no if-statement.
const char *leader = doc["top_scores"].AtIndex(0).CStringAtKey( "name", "(nobody)" );
// leader == "(nobody)"
```

# Building

Add ``vjson.cpp`` to your project and compile it.

# Other C++ JSON libraries

Here are a few C++ JSON libraries.

JsonCpp is the library I found that came closest to meeting my needs.
The thing I ended up with meets my needs better (especially the
ergonomics and avoiding all the if() statements when traversing a
DOM), and I like my library better for my needs, but I must admit that
the difference is small enough that if I had found this library
earlier I might not have written mine.

- [JsonCpp]https://github.com/open-source-parsers/jsoncpp Good
  library that is pretty lean.  Uses exceptions, but only if you
  have a bug.  (Importantly: does not use exceptions for bad
  files.)  Has a unique feature to preserve comments and reserialize.
  Slightly bigger than my lib, but satisfies many of the same goals.

Here are some other JSON libraries I looked into.  They don't satisfy
the particular goals I have for most of my projects, but they offer
unique features and tradeoffs and might be more appropriate for your
project.

- [JSON for Modern C++]https://github.com/nlohmann/json A single
  header file (currently ~24K lines).  One design goal was the ability
  to put "JSON" looking syntax directly in C++ code and use Modern C++
  to parse it into a data structure.
- [minijson]https://giacomodrago.github.io/minijson/.  Just a parser,
  no DOM.
- [ThorsSerializer]https://github.com/Loki-Astari/ThorsSerializer not a
  simple DOM.  More like go's approach to JSON serialization, it wants
  you to annotate your classes and load data directly into them.
- [jvar]https://github.com/YasserAsmi/jvar Has a boost dependency.
  Looks like a good, simple alternative if you don't mind that.
- [JSONCONS]https://github.com/danielaparker/jsoncons is a very
  fully-featured library.  Much bigger than mine.  Uses exceptions.
  (Note: no longer has a boost dependency as of recent versions.)
- [rapidjson]https://github.com/Tencent/rapidjson Claims to be very
  fast.  Has a SAX model, which might be useful if you want that.  Too
  big for my needs, nearly 40 headers.
- [simdjson]https://github.com/simdjson/simdjson Parses JSON using
  SIMD instructions; claims to parse faster than disk I/O speed.  If
  you need to parse very large documents at high throughput, this is
  worth a look.
- [Boost.JSON]https://boost.org/libs/json A solid DOM-style library
  that has been part of Boost since 1.75 (2020).  Good choice if you
  are already using Boost.
- [picojson]https://github.com/kazuho/picojson I used this at Valve
  for a while after abandoning ujson.  It uses exceptions and streams.
- [ujson]https://github.com/awangk/ujson A tiny library that I was
  using for a while at Valve.  I found some bugs and reported them,
  the project has been abandoned.  The main reason for mentioning it
  is that it is partly the namesake of this library.