ObzLib binary data

obz::endian

Explicit helpers for encoding and decoding integer fields in binary buffers.

The Bytes Are The Contract

Binary data becomes interesting when two bits of code need to agree on the exact layout. A game client and server might both understand a small packet that says: this message type, this entity, this position, this animation frame.

The awkward part is that the CPU's native integer layout is not the protocol. The protocol is the bytes. obz::endian keeps that boundary visible.

std::array<std::byte, 12> packet{};

obz::endian::write_be<std::uint16_t>(packet, 0, message_type);
obz::endian::write_be<std::uint32_t>(packet, 2, entity_id);
obz::endian::write_be<std::uint32_t>(packet, 6, position_x);
obz::endian::write_be<std::uint16_t>(packet, 10, animation_frame);

A Small Packet You Can Read

That example is deliberately plain. The offset tells you where each field lives. The function name tells you the byte order. The template argument tells you the field width.

const auto message_type = obz::endian::read_be<std::uint16_t>(packet, 0);
const auto entity_id = obz::endian::read_be<std::uint32_t>(packet, 2);
const auto position_x = obz::endian::read_be<std::uint32_t>(packet, 6);

This is useful for binary protocols, replay files, save chunks, or tests that need to assert the exact bytes produced by a serializer. The code does not ask the reader to remember what the current machine happens to do with integers in memory.

Why This Shape?

The API uses std::span<std::byte> because the data is raw storage, not text and not an integer array. It can point at an array, a vector, a packet buffer, or a slice of a larger file without copying the data.

inline void require_range(std::size_t buffer_size, std::size_t offset, std::size_t width) {
    if (offset > buffer_size || buffer_size - offset < width) {
        throw std::out_of_range("endian operation exceeds buffer bounds");
    }
}

The range check is part of the library rather than a debug-only assertion, so a bad offset is still rejected in a release build. The implementation then reads and writes one byte at a time with shifts and masks, avoiding object-memory copies, alignment assumptions, and hidden host-endian behaviour.

More Libraries