Skip to main content

LocalSerialize Guide

Overview

LocalSerialize and LocalDeserialize are lightweight binary serialization classes in the hshm::ipc namespace. They serialize C++ objects into a contiguous std::vector<char> buffer without requiring external dependencies like cereal. They are used internally by the SHM transport in Lightbeam, and are available for general-purpose binary serialization needs.

Header:

#include <hermes_shm/data_structures/serialization/local_serialize.h>

Core Classes

hshm::ipc::LocalSerialize

Serializes objects into a contiguous memory buffer:

template <typename DataT = std::vector<char>>
class LocalSerialize {
public:
DataT& data_;

// Construct and reset buffer
LocalSerialize(DataT& data);

// Construct without resetting buffer
LocalSerialize(DataT& data, bool);

// Operators
template <typename T>
LocalSerialize& operator<<(const T& obj); // Stream-style save

template <typename T>
LocalSerialize& operator&(const T& obj); // Generic save

template <typename... Args>
LocalSerialize& operator()(Args&&... args); // Variadic save

// Low-level binary write
LocalSerialize& write_binary(const char* data, size_t size);
};

hshm::ipc::LocalDeserialize

Deserializes objects from a contiguous memory buffer:

template <typename DataT = std::vector<char>>
class LocalDeserialize {
public:
const DataT& data_;
size_t cur_off_ = 0;

LocalDeserialize(const DataT& data);

// Operators
template <typename T>
LocalDeserialize& operator>>(T& obj); // Stream-style load

template <typename T>
LocalDeserialize& operator&(T& obj); // Generic load

template <typename... Args>
LocalDeserialize& operator()(Args&&... args); // Variadic load

// Low-level binary read
LocalDeserialize& read_binary(char* data, size_t size);
};

Supported Types

LocalSerialize automatically handles the following types:

Type CategoryExamplesSerialization Method
Arithmeticint, float, double, bool, char, size_tDirect binary copy
EnumsAny enum / enum classSerialized as underlying type
std::stringSize-prefixed byte array
std::vector<T>vector<int>, vector<string>Size-prefixed, elements serialized recursively
std::list<T>list<int>Size-prefixed, elements serialized recursively
std::unordered_map<K,V>unordered_map<int,int>Size-prefixed key-value pairs
Nested containersvector<vector<int>>Recursive serialization
Custom typesUser-defined classesVia serialize(), save()/load() methods

Arithmetic type optimization

For std::vector<T> where T is arithmetic (e.g., vector<int>, vector<double>), the entire data block is written/read as a single memcpy call for performance, rather than element-by-element.

Custom Type Serialization

There are four ways to make a custom type serializable. LocalSerialize checks them in this order:

1. Free serialize() function

struct MyStruct {
int x;
std::string name;
};

template <typename Ar>
void serialize(Ar& ar, MyStruct& obj) {
ar & obj.x;
ar & obj.name;
}

2. Free save()/load() functions

struct MyStruct {
int x;
std::string name;
};

template <typename Ar>
void save(Ar& ar, const MyStruct& obj) {
ar << obj.x << obj.name;
}

template <typename Ar>
void load(Ar& ar, MyStruct& obj) {
ar >> obj.x >> obj.name;
}

3. Member serialize() method

struct MyStruct {
int x;
std::string name;

template <typename Ar>
void serialize(Ar& ar) {
ar(x, name);
}
};

4. Member save()/load() methods

struct MyStruct {
int x;
std::string name;

template <typename Ar>
void save(Ar& ar) const {
ar << x << name;
}

template <typename Ar>
void load(Ar& ar) {
ar >> x >> name;
}
};

Type Traits

You can detect at compile time whether a type is serializable:

// True if T can be serialized by archive type Ar
hshm::ipc::is_serializeable_v<Ar, T>

The is_loading and is_saving type traits distinguish serialization direction:

// LocalSerialize:  is_saving = true,  is_loading = false
// LocalDeserialize: is_saving = false, is_loading = true

Examples

Basic Arithmetic Serialization

#include <hermes_shm/data_structures/serialization/local_serialize.h>

int original = 42;

// Serialize
std::vector<char> buffer;
hshm::ipc::LocalSerialize<> serializer(buffer);
serializer << original;

// Deserialize
int restored = 0;
hshm::ipc::LocalDeserialize<> deserializer(buffer);
deserializer >> restored;

assert(restored == 42);

Multiple Values

int val1 = 10;
double val2 = 3.14;
std::string val3 = "hello";

// Serialize using operator()
std::vector<char> buffer;
hshm::ipc::LocalSerialize<> serializer(buffer);
serializer(val1, val2, val3);

// Deserialize using operator()
int r1; double r2; std::string r3;
hshm::ipc::LocalDeserialize<> deserializer(buffer);
deserializer(r1, r2, r3);

assert(r1 == 10);
assert(r2 == 3.14);
assert(r3 == "hello");

Containers

std::vector<int> vec = {1, 2, 3, 4, 5};
std::unordered_map<std::string, std::string> map = {
{"key1", "value1"}, {"key2", "value2"}
};

// Serialize
std::vector<char> buffer;
hshm::ipc::LocalSerialize<> serializer(buffer);
serializer << vec << map;

// Deserialize
std::vector<int> rvec;
std::unordered_map<std::string, std::string> rmap;
hshm::ipc::LocalDeserialize<> deserializer(buffer);
deserializer >> rvec >> rmap;

assert(rvec.size() == 5);
assert(rmap["key1"] == "value1");

Custom Struct

class RequestMeta {
public:
int request_id;
std::string operation;
std::vector<int> data_sizes;

template <typename Ar>
void serialize(Ar& ar) {
ar(request_id, operation, data_sizes);
}
};

RequestMeta original;
original.request_id = 42;
original.operation = "write";
original.data_sizes = {1024, 2048, 4096};

// Serialize
std::vector<char> buffer;
hshm::ipc::LocalSerialize<> serializer(buffer);
serializer << original;

// Deserialize
RequestMeta restored;
hshm::ipc::LocalDeserialize<> deserializer(buffer);
deserializer >> restored;

assert(restored.request_id == 42);
assert(restored.operation == "write");
assert(restored.data_sizes.size() == 3);

Cereal Compatibility

When HSHM_ENABLE_CEREAL is defined, LocalSerialize can also handle cereal::BinaryData<T> wrappers for raw binary data blocks. This allows types that use cereal's binary data API to work transparently with LocalSerialize.

Error Handling

LocalDeserialize::read_binary() checks for out-of-bounds reads. If a read would exceed the buffer size, it logs an error and returns without modifying the output buffer:

LocalDeserialize::read_binary: Attempted to read beyond end of data