SkillAgentSearch skills...

Alpaca

Serialization library written in C++17 - Pack C++ structs into a compact byte-array without any macros or boilerplate code

Install / Use

/learn @p-ranav/Alpaca

README

<p align="center"> <img height="80" src="https://user-images.githubusercontent.com/8450091/188341036-ac47f2cc-1216-4af6-a2ed-7e103c005971.png"/> </p> <p align="center"> <img src="https://github.com/p-ranav/alpaca/workflows/build/badge.svg" alt="build"/> <a href="https://en.wikipedia.org/wiki/C%2B%2B17"> <img src="https://img.shields.io/badge/C%2B%2B-17-blue.svg" alt="standard"/> </a> <a href="https://github.com/p-ranav/alpaca/blob/master/LICENSE"> <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="license"/> </a> <img src="https://img.shields.io/badge/version-0.2.1-blue.svg?cacheSeconds=2592000" alt="version"/> </p>

Pack C++ structs into a compact byte-array without any macros or boilerplate code.

  • alpaca is header-only serialization library for modern C++, written in C++17
  • No macros or boilerplate, no source code generation, no external dependencies
  • Simple, fast (see benchmarks), and easy to use
  • Supports basic data types, STL containers, unique pointers, recursive data structures, optionals, variants and more
  • Serialize to C-style arrays, std::array, std::vector, or even directly to files
  • Highly configurable at compile time
    • Little endian by default. Configurable to use big endian byte order
    • Variable-length encoding by default for large integer types. Configurable to use fixed-width encoding
    • Optional type hashing and data structure versioning - recursively generates a type hash that is checked during deserialization
    • Optional integrity checking - detects data corruption during deserialization using checksums
  • Samples here
  • Experimental Python support with pybind11-based wrapper module pyalpaca
  • MIT license
#include <alpaca/alpaca.h>

struct Config {
  std::string device;
  std::pair<unsigned, unsigned> resolution;
  std::array<double, 9> K_matrix;
  std::vector<float> distortion_coeffients;
  std::map<std::string, std::variant<uint16_t, std::string, bool>> parameters;
};

// Construct the object
Config c{"/dev/video0", {640, 480}, 
	 {223.28249888247538, 0.0, 152.30570853111396,
	  0.0, 223.8756535707556, 124.5606000035353,
	  0.0, 0.0, 1.0},
	 {-0.44158343539568284, 0.23861463831967872, 0.0016338407443826572,
	  0.0034950038632981604, -0.05239245892096022},
	 {{"start_server", bool{true}},
	  {"max_depth", uint16_t{5}},
	  {"model_path", std::string{"foo/bar.pt"}}}};

// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(c, bytes);

// Deserialize
std::error_code ec;
auto object = alpaca::deserialize<Config>(bytes, ec);
if (!ec) {
  // use object
}

The source for the above example can be found here.

Table of Contents

Usage and API

Serialization

The alpaca::serialize(...) function accepts 2 arguments: an input aggregate class type (typically a struct), and an output container, e.g., std::vector<uint8_t>, std::array<uint8_T, N> etc. Serialization will attempt to pack the aggregate input into the container.

There are two variants to serialize, one of which takes an alpaca::options for additional configuration:

// Serialize a struct T (with N fields) into Container
template <class T, size_t N, class Container>
auto serialize(const T&, Container&) -> size_t /* bytes_written */;

// Serialize a struct T (with N fields) into Container using options O
template <options O, class T, size_t N, class Container>
auto serialize(const T&, Container&) -> size_t /* bytes_written */;

NOTE Under most circumstances, the number of fields in the struct, N, need not be provided. In certain use-cases, e.g., std::optional, the user will need to provide this N for correct operation. More on this here.

Examples of valid serialize calls include:

struct MyStruct { 
  int value; 
};

// Construct object
MyStruct object{5};
// Serialize to a C-style array
uint8_t buffer[10];
auto bytes_written = serialize(object, buffer);
// Serialize to std::array
std::array<uint8_t, 5> bytes;
auto bytes_written = serialize(object, bytes);
// Serialize to std::vector
std::vector<uint8_t> bytes;
auto bytes_written = serialize(object, bytes);
// Serialize to file
std::ofstream os;
os.open("foo.bin", std::ios::out | std::ios::binary);
auto bytes_written = serialize(object, os);
// Serialize with options
std::vector<uint8_t> bytes;
constexpr auto OPTIONS = options::fixed_length_encoding | 
                         options::with_version | 
			 options::with_checksum;
auto bytes_written = serialize<OPTIONS>(object, bytes);

Deserialization

The alpaca::deserialize(...) function, likewise, accepts a container like std::vector<uint8_t> or std::array<uint8_t, N> and an std::error_code that will be set in case of error conditions. Deserialization will attempt to unpack the container of bytes into an aggregate class type, returning the class object.

Deserialization from C-style arrays is supported as well, though in this case, the number of bytes to read from the buffer needs to be provided.

Like serialize(), deserialization has two variants, one of which accepts an alpaca::options template parameter.

// Deserialize a Container into struct T (with N fields)
template <class T, size_t N, class Container>
auto deserialize(Container&, std::error_code&) -> T;

// Deserialize `size` bytes from a Container into struct T (with N fields)
template <class T, size_t N, class Container>
auto deserialize(Container&, const std::size_t, std::error_code&) -> T;

// Deserialize a Container into struct T (with N fields) using options O
template <options O, class T, size_t N, class Container>
auto deserialize(Container&, std::error_code&) -> T;

// Deserialize `size` bytes from a Container into struct T (with N fields) using options O
template <options O, class T, size_t N, class Container>
auto deserialize(Container&, const std::size_t, std::error_code&) -> T;

Examples of valid deserialize calls include:

// Deserialize from flie
std::ifstream is;
is.open("foo.bin", std::ios::in | std::ios::binary);
auto file_size = std::filesystem::file_size("foo.bin");

std::error_code ec;
auto object = deserialize<MyStruct>(is, file_size, ec);
if (!ec) {
  // use object
}
// Deserialize from std::array or std::vector
// Default options
std::error_code ec;
auto object = deserialize<MyStruct>(bytes, ec);
if (!ec) {
  // use object
}
// Deserialize from std::array or std::vector
// Custom options
std::error_code ec;
constexpr auto OPTIONS = options::fixed_length_encoding | 
                         options::with_version |
			 options::with_checksum;
auto object = deserialize<OPTIONS, MyStruct>(bytes, ec);
if (!ec) {
  // use object
}

Examples

Fundamental types

  • Fundamental types, including char, bool, fixed-width integer types like uint16_t, and floating-point types are supported by alpaca
  • For larger integer types including int32_t, alpaca may use variable-length encoding where applicable. If fixed-width encoding is preferred, this can be changed using options::fixed_width_encoding.
  • By default, alpaca uses little endian for the byte order. This can be changed to use big-endian byte order using options::big_endian

Source

struct MyStruct {
  char a;
  int b;
  uint64_t c;
  float d;
  bool e;
};

MyStruct s{'a', 5, 12345, 3.14f, true};

// Serialize
std::vector<uint8_t> bytes;
auto bytes_written = alpaca::serialize(s, bytes); // 9 bytes

// bytes:
// {
//   0x61                  // char 'a'
//   0x05                  // int 5
//   0xb9 0x60             // uint 12345
//   0xc3 0
View on GitHub
GitHub Stars559
CategoryDevelopment
Updated1mo ago
Forks47

Languages

C++

Security Score

100/100

Audited on Feb 22, 2026

No findings