ZeroBuffer is a language-agnostic, high-performance inter-process communication protocol designed for zero-copy data exchange between processes.
The protocol defines a shared memory ring buffer with semaphore-based synchronization, supporting single writer and single reader patterns. It's optimized for streaming applications where low latency and high throughput are critical.
-
Shared Memory Buffer - Named shared memory segment containing:
- Operation Info Exchange Block (OIEB)
- Metadata Block
- Payload Block (Ring Buffer)
-
Semaphores - Two named semaphores for synchronization:
sem-w-{name}- Signals data is available for readingsem-r-{name}- Signals space is available for writing
-
File Lock - Platform-specific lock file for resource management
+------------------------+ 0x0000
| OIEB (128 bytes) |
| - 64-byte aligned |
+------------------------+ 0x0080
| Metadata Block |
| - Variable size |
| - 64-byte aligned |
+------------------------+
| Payload Block |
| - Ring buffer |
| - Variable size |
| - 64-byte aligned |
+------------------------+
The OIEB is a 128-byte structure that manages the shared memory buffer state. It provides version information and tracks buffer usage for cross-language compatibility:
| Offset | Field Name | Size | Description |
|---|---|---|---|
| 0x00 | oieb_size | 4B | Total OIEB size (uint32, always 128) |
| 0x04 | version | 4B | Protocol version (4 bytes: major.minor.patch.reserved) |
| 0x08 | metadata_size | 8B | Total metadata block size (uint64) |
| 0x10 | metadata_free_bytes | 8B | Free bytes in metadata block (uint64) |
| 0x18 | metadata_written_bytes | 8B | Written bytes in metadata block (uint64) |
| 0x20 | payload_size | 8B | Total payload block size (uint64) |
| 0x28 | payload_free_bytes | 8B | Free bytes in payload block (uint64) |
| 0x30 | payload_write_pos | 8B | Current write position in ring buffer (uint64) |
| 0x38 | payload_read_pos | 8B | Current read position in ring buffer (uint64) |
| 0x40 | payload_written_count | 8B | Number of frames written (uint64) |
| 0x48 | payload_read_count | 8B | Number of frames read (uint64) |
| 0x50 | writer_pid | 8B | Writer process ID (uint64, 0 if none) |
| 0x58 | reader_pid | 8B | Reader process ID (uint64, 0 if none) |
| 0x60 | reserved_1 | 8B | Reserved for future use (uint64) |
| 0x68 | reserved_2 | 8B | Reserved for future use (uint64) |
| 0x70 | reserved_3 | 8B | Reserved for future use (uint64) |
| 0x78 | reserved_4 | 8B | Reserved for future use (uint64) |
The version field is a 4-byte structure that identifies the protocol version:
struct ProtocolVersion {
uint8_t major; // Major version (breaking changes)
uint8_t minor; // Minor version (new features, backward compatible)
uint8_t patch; // Patch version (bug fixes)
uint8_t reserved; // Reserved for future use (must be 0)
};
Version Field Format:
- Byte 0: Major version (0-255)
- Byte 1: Minor version (0-255)
- Byte 2: Patch version (0-255)
- Byte 3: Reserved (must be 0)
Version Compatibility Rules:
- Different major versions are incompatible
- Same major, newer minor: Reader can read if it understands all features
- Same major and minor: Fully compatible regardless of patch
Protocol Requirements:
- Total structure size: 128 bytes (v1.x.x.x), 1024 bytes (v2.x.x.x)
- Byte order: Little-endian
- Alignment: 64-byte boundary
- Current version: 1.0.0.0
- Process IDs used for liveness detection
- Reserved fields must be set to 0
- OIEB size: 128 bytes
- Payload location: Always in same shared memory segment
- Single buffer architecture
- OIEB size: 1024 bytes
- New fields:
0x60:payload_location(8B) - Enum: 0=same memory, 1=external shared memory0x68:payload_shm_name(264B) - External shared memory name (8B size prefix + max 256B string)0x170-0x3FF: Additional reserved space for future extensions
- Enables:
- Shared payload buffer between request/response channels (DuplexChannel optimization)
- True zero-copy for in-place modifications
- 50% memory reduction for duplex operations
- External memory mapping for large payloads
- C++ - Native implementation in
cpp/directory - C# - .NET implementation in
csharp/directory - Python - Python implementation in
python/directory
All implementations follow the same protocol specification and are fully interoperable.
See the README.md in each language directory for implementation-specific build and usage instructions.
The metadata is stored in the shared memory metadata segment with the following format:
- 8 bytes: Size prefix (uint64_t, little-endian) - indicates the size of the actual metadata content
- N bytes: Actual metadata content
All Reader implementations (get_metadata() methods) return the metadata content WITHOUT the 8-byte size prefix. This provides a consistent API across all languages and hides internal protocol details.
C++ API:
// Reader returns raw metadata without prefix
std::vector<uint8_t> Reader::get_metadata() const;
// Writer adds 8-byte prefix internally
void Writer::set_metadata(const void* data, size_t size);C# API:
// Reader returns raw metadata without prefix
ReadOnlySpan<byte> Reader.GetMetadata();
// Writer adds 8-byte prefix internally
void Writer.SetMetadata(ReadOnlySpan<byte> data);Python API:
# Reader returns raw metadata without prefix
def get_metadata() -> Optional[memoryview]:
# Writer adds 8-byte prefix internally
def set_metadata(data: Union[bytes, bytearray, memoryview]) -> None:When writing metadata:
- Application passes raw metadata content (e.g., JSON string)
- Writer implementation automatically adds the 8-byte size prefix
- Total stored size = 8 bytes (prefix) + N bytes (content)
When reading metadata:
- Reader implementation reads the 8-byte size prefix internally
- Reader returns only the actual content to the application
- Application receives raw metadata without any prefix
If you write JSON metadata {"format": "RGB", "width": 640, "height": 480}:
- Application calls:
writer.set_metadata(json_bytes) - Writer stores in shared memory:
[8-byte size=45][45 bytes of JSON] - Reader returns to application:
[45 bytes of JSON](prefix stripped)
For complete protocol specification, see README.md