ZAP C++

API Reference

Complete C++ API reference for ZAP C++ (ZAP)

API Reference

This reference covers the core ZAP C++ (ZAP) API for building and reading messages.

Core Headers

#include <zap/message.h>          // MessageBuilder, MessageReader
#include <zap/serialize.h>         // writeMessageToFd, readMessageFromFd
#include <zap/serialize-packed.h>  // Packed serialization
#include <zap/pretty-print.h>      // Text output for debugging
#include <zap/dynamic.h>           // Dynamic (reflection) API
#include <zap/schema-loader.h>     // Runtime schema loading

MessageBuilder Classes

MallocMessageBuilder

The primary class for building messages:

#include <zap/message.h>
 
// Default constructor
zap::MallocMessageBuilder message;
 
// With suggested first segment size
zap::MallocMessageBuilder message(1024);  // words
 
// Initialize root
auto root = message.initRoot<MyStruct>();
 
// Get existing root (for modification)
auto root = message.getRoot<MyStruct>();
 
// Clear and reuse
message.clear();

Builder Interface

Generated builder classes provide these methods:

MethodDescription
setField(value)Set a scalar field
initField()Initialize a struct field
initField(size)Initialize a list with given size
getField()Get builder for existing struct field
hasField()Check if pointer field is set
adoptField(orphan)Adopt an orphan into this field
disownField()Remove and return field as orphan

Setting Fields

// Scalar fields
person.setId(123);
person.setName("Alice");
person.setActive(true);
person.setScore(95.5);
 
// Struct fields
auto address = person.initAddress();
address.setStreet("123 Main St");
 
// Or get existing struct
auto existingAddress = person.getAddress();
existingAddress.setCity("Springfield");

List Fields

// Initialize a list with known size
auto phones = person.initPhones(3);
phones[0].setNumber("111");
phones[1].setNumber("222");
phones[2].setNumber("333");
 
// Primitive list
auto scores = result.initScores(5);
scores.set(0, 100);
scores.set(1, 95);
// ...
 
// Set Text list
auto names = person.initNames(2);
names.set(0, "Alice");
names.set(1, "Bob");

MessageReader Classes

StreamFdMessageReader

Read from a file descriptor:

#include <zap/serialize.h>
 
zap::StreamFdMessageReader message(fd);
auto root = message.getRoot<MyStruct>();

PackedFdMessageReader

Read packed format from file descriptor:

#include <zap/serialize-packed.h>
 
zap::PackedFdMessageReader message(fd);
auto root = message.getRoot<MyStruct>();

FlatArrayMessageReader

Read from a flat array (zero-copy if aligned):

kj::ArrayPtr<const zap::word> words = ...;
zap::FlatArrayMessageReader message(words);
auto root = message.getRoot<MyStruct>();

InputStreamMessageReader

Read from any input stream:

kj::InputStream& input = ...;
zap::InputStreamMessageReader message(input);
auto root = message.getRoot<MyStruct>();

Reader Interface

MethodDescription
getField()Get field value (returns default if not set)
hasField()Check if pointer field is set
isField()Check which union member is set
which()Get active union member as enum

Reading Fields

// Scalar fields
uint32_t id = person.getId();
kj::StringPtr name = person.getName();
bool active = person.getActive();
double score = person.getScore();
 
// Struct fields
auto address = person.getAddress();
kj::StringPtr city = address.getCity();
 
// Check if set
if (person.hasAddress()) {
  auto addr = person.getAddress();
}
 
// List fields
for (auto phone : person.getPhones()) {
  kj::StringPtr number = phone.getNumber();
}

Serialization Functions

Standard Format

#include <zap/serialize.h>
 
// Write
zap::writeMessageToFd(fd, message);
zap::writeMessage(outputStream, message);
 
// Read
zap::StreamFdMessageReader reader(fd);
zap::InputStreamMessageReader reader(inputStream);
 
// Convert to flat array
kj::Array<zap::word> words = zap::messageToFlatArray(message);

Packed Format

#include <zap/serialize-packed.h>
 
// Write
zap::writePackedMessageToFd(fd, message);
zap::writePackedMessage(outputStream, message);
 
// Read
zap::PackedFdMessageReader reader(fd);
zap::PackedMessageReader reader(inputStream);

Streaming Messages

// Try to read (returns Maybe)
KJ_IF_MAYBE(reader, zap::tryReadMessage(input)) {
  auto root = reader->getRoot<MyStruct>();
  // Process message
} else {
  // End of stream
}

ReaderOptions

Control reader behavior for security:

zap::ReaderOptions options;
 
// Limit total data traversed (default: 64 MiB)
options.traversalLimitInWords = 64 * 1024 * 1024;
 
// Limit pointer nesting depth (default: 64)
options.nestingLimit = 128;
 
zap::StreamFdMessageReader reader(fd, options);

Unions

Building

auto shape = message.initRoot<Shape>();
 
// Set one variant (automatically clears others)
auto circle = shape.initCircle();
circle.setRadius(5.0);
 
// Or
auto rect = shape.initRectangle();
rect.setWidth(10.0);
rect.setHeight(20.0);

Reading

auto shape = reader.getRoot<Shape>();
 
switch (shape.which()) {
  case Shape::CIRCLE:
    handleCircle(shape.getCircle());
    break;
  case Shape::RECTANGLE:
    handleRectangle(shape.getRectangle());
    break;
  default:
    // Unknown variant
    break;
}
 
// Or check specific variant
if (shape.isCircle()) {
  auto circle = shape.getCircle();
}

Orphans

Create objects outside the message tree:

// Get orphanage from message
auto& orphanage = message.getOrphanage();
 
// Create orphan
auto orphan = orphanage.newOrphan<Person>();
auto person = orphan.get();
person.setName("Alice");
 
// Adopt into message
parent.adoptChild(kj::mv(orphan));
 
// Disown from message
auto orphan = parent.disownChild();

Dynamic API

Access messages without compile-time schema:

#include <zap/dynamic.h>
#include <zap/schema-loader.h>
 
// Load schema at runtime
zap::SchemaLoader loader;
zap::Schema schema = loader.loadOnce(schemaData);
zap::StructSchema structSchema = schema.asStruct();
 
// Read dynamically
zap::DynamicStruct::Reader reader =
    message.getRoot<zap::DynamicStruct>(structSchema);
 
// Access fields by name
auto nameField = structSchema.getFieldByName("name");
auto name = reader.get(nameField).as<zap::Text>();
 
// Or by string directly
auto value = reader.get("age").as<uint32_t>();

DynamicValue Types

switch (value.getType()) {
  case zap::DynamicValue::INT:
    int64_t i = value.as<int64_t>();
    break;
  case zap::DynamicValue::UINT:
    uint64_t u = value.as<uint64_t>();
    break;
  case zap::DynamicValue::FLOAT:
    double d = value.as<double>();
    break;
  case zap::DynamicValue::BOOL:
    bool b = value.as<bool>();
    break;
  case zap::DynamicValue::TEXT:
    kj::StringPtr s = value.as<zap::Text>();
    break;
  case zap::DynamicValue::DATA:
    kj::ArrayPtr<const kj::byte> d = value.as<zap::Data>();
    break;
  case zap::DynamicValue::STRUCT:
    zap::DynamicStruct::Reader r = value.as<zap::DynamicStruct>();
    break;
  case zap::DynamicValue::LIST:
    zap::DynamicList::Reader l = value.as<zap::DynamicList>();
    break;
  case zap::DynamicValue::ENUM:
    zap::DynamicEnum e = value.as<zap::DynamicEnum>();
    break;
  case zap::DynamicValue::CAPABILITY:
    zap::DynamicCapability::Client c = value.as<zap::DynamicCapability>();
    break;
}

Pretty Printing

#include <zap/pretty-print.h>
 
// Print struct
kj::String text = zap::prettyPrint(reader).flatten();
std::cout << text.cStr() << std::endl;
 
// With indentation
kj::String indented = zap::prettyPrint(reader)
    .setFlat(false)
    .flatten();

Thread Safety

  • MessageBuilder - Not thread-safe for concurrent writes
  • MessageReader - Safe for concurrent reads from multiple threads
  • Multiple readers can share the same underlying message data

Memory Management

Arena Allocation

// Pre-allocate scratch space
kj::byte buffer[4096];
kj::ArrayPtr<kj::byte> scratch(buffer, sizeof(buffer));
 
// Use scratch space for message
zap::MallocMessageBuilder message(
    zap::SUGGESTED_FIRST_SEGMENT_WORDS,
    zap::AllocationStrategy::FIXED_SIZE);

Copying Messages

// Deep copy a message
zap::MallocMessageBuilder copy;
copy.setRoot(reader.getRoot<Person>());

Error Handling

#include <kj/exception.h>
 
try {
  zap::StreamFdMessageReader reader(fd);
  auto root = reader.getRoot<Person>();
  // Process...
} catch (const kj::Exception& e) {
  KJ_LOG(ERROR, "Failed to read message", e.getDescription());
}

Next Steps