Skip to content

REPE Buffer Handling

The glaze/rpc/repe/buffer.hpp and glaze/rpc/repe/repe.hpp headers provide ASIO-independent functions for serializing and deserializing REPE messages to and from raw byte buffers. This enables use of REPE messages in contexts beyond direct socket I/O, such as:

  • Plugin systems with C ABI boundaries
  • Message queues and brokers
  • In-memory RPC routing
  • Testing and debugging
  • WebSocket/HTTP transport layers

Include

#include "glaze/rpc/repe/buffer.hpp"
#include "glaze/rpc/repe/repe.hpp"  // For zero-copy types

Constants

repe_magic

The REPE protocol magic bytes used to identify valid REPE messages.

inline constexpr uint16_t repe_magic = 0x1507; // 5383

Header Utilities

finalize_header

After modifying a message's query or body, call finalize_header to update the header length fields:

glz::repe::message msg{};
msg.query = "/api/endpoint";
msg.body = R"({"key": "value"})";

glz::repe::finalize_header(msg);
// msg.header.query_length, body_length, and length are now set correctly

Error Handling

encode_error

Encodes an error into a REPE message:

glz::repe::message response{};

// Simple error (clears body)
glz::repe::encode_error(glz::error_code::parse_error, response);

// Error with message
glz::repe::encode_error(glz::error_code::invalid_header, response, "Custom error description");

decode_error

Extracts a formatted error string from a REPE message:

glz::repe::message msg{};
// ... receive message with error ...

if (msg.error()) {
    std::string error_str = glz::repe::decode_error(msg);
    // Returns: "REPE error: <error_code> | <error_message>"
}

decode_message

Deserializes a message body into a C++ structure with error handling:

struct Response {
    int value;
    std::string name;
};

glz::repe::message msg{};
// ... receive message ...

Response result{};
auto error = glz::repe::decode_message(result, msg);

if (error) {
    std::cerr << *error << '\n';  // Contains formatted error
} else {
    // result is populated
}

Serialization

to_buffer

Serializes a repe::message to wire-format bytes:

glz::repe::message msg{};
msg.query = "/api/call";
msg.body = R"({"param": 42})";
glz::repe::finalize_header(msg);

// Returns a new string
std::string wire_data = glz::repe::to_buffer(msg);

// Or serialize into existing buffer (reuses memory)
std::string buffer;
glz::repe::to_buffer(msg, buffer);

Deserialization

from_buffer

Deserializes wire-format bytes into a repe::message:

std::string wire_data = /* received bytes */;

glz::repe::message msg{};
auto ec = glz::repe::from_buffer(wire_data, msg);

if (ec == glz::error_code::none) {
    // msg.header, msg.query, and msg.body are populated
}

The function validates: - Buffer contains at least a complete header - Magic bytes match repe_magic (0x1507) - Version is supported (currently version 1) - Buffer contains complete query and body data

Error codes: - error_code::none - Success - error_code::invalid_header - Buffer too small or invalid magic bytes - error_code::version_mismatch - Unsupported REPE version - error_code::invalid_body - Buffer truncated (incomplete query/body)

Overloads:

// From raw pointer + size
glz::error_code from_buffer(const char* data, size_t size, message& msg);

// From string_view
glz::error_code from_buffer(std::string_view data, message& msg);

Zero-Copy API

For high-performance scenarios, Glaze provides a zero-copy API that avoids intermediate allocations. Instead of deserializing into a repe::message (which copies query and body strings), you can use views into the original buffer.

request_view

A view into a parsed REPE request. Query and body are std::string_view pointing into the original buffer.

struct request_view {
    header hdr;                    // Header (copied for alignment safety)
    std::string_view query;        // View into original buffer
    std::string_view body;         // View into original buffer

    [[nodiscard]] uint64_t id() const noexcept;
    [[nodiscard]] bool is_notify() const noexcept;
    [[nodiscard]] error_code error() const noexcept;
};

parse_request

Parses a buffer into a request_view with zero-copy for query and body:

std::span<const char> buffer = /* received bytes */;

auto result = glz::repe::parse_request(buffer);
if (!result) {
    // result.ec contains the error code
    handle_error(result.ec);
    return;
}

const auto& req = result.request;
// req.query and req.body are views into the original buffer
process_request(req.query, req.body);

The header is copied via memcpy (48 bytes) for alignment safety. Query and body remain as views.

Error codes: - error_code::none - Success - error_code::invalid_header - Buffer too small or invalid magic bytes - error_code::version_mismatch - Unsupported REPE version - error_code::invalid_body - Buffer truncated

response_builder

Builds REPE responses efficiently, writing directly to a buffer or message:

std::string response_buffer;
glz::repe::response_builder resp{response_buffer};

// Reset with request ID
resp.reset(request_view);  // Copies ID from request

// Set error response
resp.set_error(glz::error_code::invalid_params, "Missing required field");

// Or serialize a value as the body
resp.set_body(my_response_object);  // Uses glz::write internally

// Or set raw body bytes
resp.set_body_raw(R"({"result": 42})", glz::repe::body_format::JSON);

The response_builder can also write directly to a repe::message:

glz::repe::message response_msg;
glz::repe::response_builder resp{response_msg};
resp.reset(request.id());
resp.set_body(my_object);
// response_msg is now ready to send

state_view

Combines a request_view and response_builder for use in RPC procedure handlers:

struct state_view {
    const request_view& in;
    response_builder& out;

    [[nodiscard]] bool notify() const noexcept;   // Is this a notification?
    [[nodiscard]] bool has_body() const noexcept; // Does request have a body?
};

This is used internally by glz::registry for zero-copy procedure dispatch.

Example: Zero-Copy Request Handler

void handle_request(std::span<const char> request, std::string& response_buffer) {
    // Zero-copy parse
    auto result = glz::repe::parse_request(request);
    if (!result) {
        glz::repe::encode_error_buffer(
            glz::error_code::parse_error,
            response_buffer,
            "Failed to parse request"
        );
        return;
    }

    const auto& req = result.request;
    glz::repe::response_builder resp{response_buffer};

    // Route based on query (no allocation - query is a view)
    if (req.query == "/api/status") {
        resp.reset(req);
        resp.set_body_raw(R"({"status": "ok"})", glz::repe::body_format::JSON);
    }
    else if (req.query == "/api/echo") {
        // Echo back the body
        resp.reset(req);
        resp.set_body_raw(req.body, glz::repe::body_format::JSON);
    }
    else {
        resp.reset(req);
        resp.set_error(glz::error_code::method_not_found, "Unknown endpoint");
    }
}

Header-Only Parsing

For routing scenarios where you need to inspect message metadata without fully deserializing:

parse_header

Extracts only the header from raw bytes:

std::string wire_data = /* received bytes */;

glz::repe::header hdr{};
auto ec = glz::repe::parse_header(wire_data, hdr);

if (ec == glz::error_code::none) {
    // Access hdr.id, hdr.query_length, hdr.body_length, etc.
}

extract_query

Extracts the query string without full deserialization:

std::string wire_data = /* received bytes */;

std::string_view query = glz::repe::extract_query(wire_data);

if (!query.empty()) {
    // Route based on query path
    auto* handler = router.find(query);
    handler->forward(wire_data);
}

Returns an empty string_view on error (invalid header, truncated data).

Example: Message Router

#include "glaze/rpc/repe/buffer.hpp"

void route_message(const char* data, size_t size) {
    // Extract query without full deserialization
    auto query = glz::repe::extract_query(data, size);

    if (query.empty()) {
        // Invalid message
        return;
    }

    // Route based on query prefix
    if (query.starts_with("/users/")) {
        users_service.forward(data, size);
    } else if (query.starts_with("/orders/")) {
        orders_service.forward(data, size);
    } else {
        // Unknown route
    }
}

Example: Roundtrip Test

#include "glaze/rpc/repe/buffer.hpp"

void test_roundtrip() {
    // Create message
    glz::repe::message original{};
    original.query = "/api/test";
    original.body = R"({"value": 42})";
    original.header.id = 12345;
    original.header.body_format = glz::repe::body_format::JSON;
    glz::repe::finalize_header(original);

    // Serialize
    std::string wire_data = glz::repe::to_buffer(original);

    // Deserialize
    glz::repe::message restored{};
    auto ec = glz::repe::from_buffer(wire_data, restored);

    assert(ec == glz::error_code::none);
    assert(restored.query == original.query);
    assert(restored.body == original.body);
    assert(restored.header.id == original.header.id);
}