github stephenberry/glaze v7.0.0

latest releases: v8.4.0, v8.3.0, v8.2.0...
8 months ago

v7.0.0 Highlights

  • Cleaner compiler errors with a smaller core glz::opts while still allowing the same compile time customization options.
  • Faster integer to string serialization using larger tables, but also added optimization_level to remove large tables and optimize for size for small, embedded devices.
  • Lazy parsers for JSON and BEVE
  • Much more

Breaking Changes

Options Refactoring

Several options have been renamed and moved to the inheritable options pattern:

  • numberstring_as_number (more descriptive name)
  • rawunquoted (clearer semantics)
  • write_member_functionswrite_function_pointers (reflects support for all function pointer types)

Old names are preserved as deprecated aliases with clear static_assert messages guiding migration.

Core Options Size Reduction

The following options have been removed from glz::opts to reduce template instantiation sizes:

  • indentation_char
  • indentation_width
  • new_lines_in_arrays
  • quoted_num
  • string_as_number (formerly number)
  • unquoted (formerly raw)
  • raw_string
  • structs_as_arrays

Users requiring these options should define them in custom structs that inherit from glz::opts. This change reduces compiler error verbosity and improves build performance for projects using default options.

Context Field Rename

The glz::context field indentation_level has been renamed to depth (#2207). This field tracks nesting depth during both reading (for stack overflow prevention) and writing (for indentation formatting).


New Features

Lazy JSON Parser (#2211)

Introducing glz::lazy_json, a lazy JSON parser that performs zero upfront processing. Creating a lazy_json object is O(1)—it simply stores a pointer to the buffer.

Key Features:

  • On-demand parsing: Only parses bytes when accessed
  • Indexed views: Build an index once in O(n) time, then achieve O(1) element retrieval
  • Direct deserialization: New read_json overload accepts lazy views directly, enabling ~49% faster single-pass struct deserialization
  • Iterator support: Full range-based for loop compatibility for arrays and objects

When to use: Ideal for extracting a small number of fields from large JSON documents.

Basic Usage:

std::string json = R"({"name":"John","age":30,"active":true})";
auto result = glz::lazy_json(json);
if (result) {
    auto& doc = *result;
    auto name = doc["name"].get<std::string_view>();  // Only parses "name"
    auto age = doc["age"].get<int64_t>();             // Only parses "age"
}

Nested Access:

std::string json = R"({"user":{"profile":{"email":"alice@example.com"}}})";
auto result = glz::lazy_json(json);
if (result) {
    auto email = (*result)["user"]["profile"]["email"].get<std::string_view>();
}

Array Iteration:

std::string json = R"({"items":[{"id":1},{"id":2},{"id":3}]})";
auto result = glz::lazy_json(json);
if (result) {
    int64_t sum = 0;
    for (auto item : (*result)["items"]) {
        if (auto id = item["id"].get<int64_t>()) {
            sum += *id;
        }
    }
}

Indexed Views for O(1) Random Access:

auto users = (*result)["users"].index();  // Build index once - O(n)
size_t count = users.size();              // O(1)
auto user500 = users[500];                // O(1) direct access

Lazy BEVE Parser (#2220)

glz::lazy_beve brings the same lazy parsing capabilities to BEVE binary format:

  • On-demand field access via operator[]
  • Type checking methods: is_object(), is_array(), is_string()
  • Value extraction through get<T>()
  • Forward iterators for container traversal
  • Random access indexing via index() method
  • Size queries without full parsing

Query Parameter and URL Encoding Support (#2233)

New glaze/net/url.hpp header with comprehensive URL handling:

  • URL encoding/decoding: Handles percent-encoding (%20 to space, + to space)
  • Query string parsing: parse_urlencoded() for extracting key=value pairs
  • URL component splitting: split_target() to separate paths from query strings
  • Automatic integration: HTTP router populates request.query automatically
  • Zero-allocation options: High-performance parsing without heap allocations

BEVE Size Precomputation (#2206)

New glz::beve_size(value) function calculates exact serialization byte count without performing serialization:

  • glz::beve_size() for tagged serialization
  • glz::beve_size_untagged() for untagged scenarios
  • glz::compressed_int_size() for compressed integer encoding

Use case: Efficient pre-allocation for shared memory IPC scenarios.

BEVE Header Inspection (#2212, #2225)

Inspect BEVE buffer headers without full deserialization:

  • glz::beve_peek_header() returns tag, type, extension type, count, and header size
  • glz::beve_peek_header_at() for inspecting headers at specific offsets
  • Enables pre-allocation, structure validation, and type-based routing

TOML: Array of Tables Support (#2216)

Full TOML 1.0 specification compliance for array-of-tables:

Writing:

[[products]]
name = "Hammer"
sku = 738594937

[[products]]
name = "Nail"
sku = 284758393

Reading: Parser handles [[array_name]] sections with proper nesting support.

Override: glz::inline_table<&T::member> wrapper forces inline {key = value} syntax.

Variant Custom Types (#2208)

Automatic JSON type deduction for custom types in std::variant:

std::variant<std::string, Amount> v;
glz::read_json(v, "42.5");  // Automatically parses as Amount

Glaze now infers JSON types by examining the second parameter of custom read lambdas.

Static Function Pointer Support (#2223)

Function pointers are now fully supported:

  • Works in glz::meta definitions and JSON-RPC registries
  • Serialization to type signature strings with write_function_pointers enabled
  • Fixes stack overflow when registering JSON-RPC methods using static member functions

Improvements

Performance

Optimization Levels (#2214)

New optimization_level option for binary size vs. performance tradeoff:

  • Normal (default): Large lookup tables (40KB) for maximum performance
  • Size: Compact 400-byte tables, ~277KB binary savings for embedded systems

Faster Integer Serialization

Specialized itoa routines for 8/16-bit integer types provide performance improvements for these common types.

Reduced Template Instantiations (#2200)

Core template instantiation optimizations reduce compile times and binary sizes.

Security

Runtime Size Limits (#2199)

Runtime constraints for BEVE and CBOR deserialization:

struct my_context : glz::context {
   size_t max_string_length = 1024;
   size_t max_array_size = 100;
   size_t max_map_size = 50;
};

Runtime allocate_raw_pointers (#2213)

The allocate_raw_pointers option can now be set at runtime for more flexible memory allocation control.

Compatibility

Float Format Fallback (#2204)

float_format now falls back to snprintf on platforms without full std::to_chars floating-point support.


Bug Fixes

  • Fixed HTTP POST body additional read issue (#2235)
  • Fixed renamed_key_size calculation (#2226)
  • Fixed BEVE string key detection and number key parsing in objects (included in #2220)

Migration Guide

Options Changes

If you use custom formatting options, update your code to inherit from glz::opts:

// Before (v6.x)
constexpr glz::opts my_opts{.indentation_width = 4};

// After (v7.0.0)
struct my_opts : glz::opts {
   static constexpr uint8_t indentation_width = 4;
};

Renamed Options

// Before
glz::opts{.number = true}
glz::opts{.raw = true}
glz::opts{.write_member_functions = true}

// After
struct my_opts : glz::opts { bool string_as_number = true; };
struct my_opts : glz::opts { bool unquoted = true; };
struct my_opts : glz::opts { bool write_function_pointers = true; };

Full Changelog: v6.5.1...v7.0.0

Don't miss a new glaze release

NewReleases is sending notifications on new releases.