- Odin 100%
Replaced the entire keyexpr matching engine with algorithms ported directly from zenoh-rs (classical.rs, include.rs, canon.rs). intersects: recursive chunk-by-chunk with branch on **, $* via star_dsl_intersect. Cleaner than the old forward/backward approach. includes: iterative left-to-right from Rust's LTRIncluder. DSL chunk inclusion splits left by $* and checks pieces appear in order in right. canonize: full implementation. Validates (empty chunks, invalid patterns), collapses $*$* → $*, $*-only → *, **/** → **, **/* → */**. strip_prefix: walks ke and prefix chunks in parallel, handles **. ~250 lines replacing ~400 lines. All 71 tests pass. Zero vet warnings. |
||
|---|---|---|
| examples | ||
| test_live | ||
| test_pubsub | ||
| test_query | ||
| zenoh | ||
| .gitignore | ||
| ARCHITECTURE.md | ||
| DESIGN.md | ||
| README.md | ||
zenoh-odin
Pure Odin implementation of the zenoh protocol. Zero-copy pub/sub, query/reply, and shared memory — no C bindings, no allocations in the hot path.
Status
Alpha. Verified against zenohd 1.8.0 on macOS. Core protocol is complete:
- Pub/sub through zenoh router
- Query/reply through zenoh router
- SHM with cross-process atomic refcounting
- UDP multicast scouting
- Automatic reconnection with exponential backoff
- Raw API for zero-overhead message processing
Installation
Clone the repository and import the zenoh package:
import zenoh "path/to/zenoh-odin/zenoh"
Odin does not have a package manager. Copy the zenoh/ directory into
your project or add it to your import path.
Quick Start
import zenoh "zenoh"
import "core:nbio"
import "core:fmt"
import "core:time"
on_sample :: proc(sample: zenoh.Sample, _: rawptr) {
fmt.printfln("%s: %s", string(sample.key_expr), string(sample.payload))
}
main :: proc() {
nbio.acquire_thread_event_loop()
defer nbio.release_thread_event_loop()
session := zenoh.open(zenoh.Config{endpoint = "localhost:7447"}) or_return
defer zenoh.close(&session)
zenoh.register(&session)
zenoh.subscribe(&session, "demo/**", on_sample)
for !session.closed {
nbio.tick()
time.sleep(1 * time.Millisecond)
}
}
Requires zenohd running on the target endpoint:
# Install (Rust toolchain required)
cargo install zenohd
# Run with defaults
zenohd
# Run with SHM support
cargo install zenohd --features shared-memory
zenohd
Examples
# In one terminal
odin run examples/sub/
# In another terminal
odin run examples/pub/
# Query/reply (runs both sides)
odin run examples/queryable/
All examples default to localhost:7447. Pass an endpoint as the first argument
to override.
Architecture
Single package, poll-based, no threads. The user owns the event loop via
core:nbio (kqueue on macOS, io_uring on Linux, IOCP on Windows).
nbio.tick()
└─ socket readable?
└─ decode TCP frame
└─ decode network message
└─ match key expression
└─ call subscriber handler
Four function calls from IO event to user code. The raw API inlines codec procs into the handler for two.
Two API tiers
Easy API — library decodes the message, calls you with a Sample:
zenoh.subscribe(&session, "test/**", proc(sample: zenoh.Sample, _: rawptr) {
fmt.println(string(sample.payload))
})
Raw API — library gives you raw bytes, you decode what you need with
#force_inline procs. Type-safe: Push_Raw and Request_Raw are distinct
types, wrong reader on wrong message is a compile error:
zenoh.subscribe_raw(&session, "test/**", proc(key: zenoh.Key_Expr, raw: zenoh.Push_Raw, _: rawptr) {
buf := raw
header := zenoh.read_put_header(&buf)
payload := zenoh.read_put_payload(&buf)
// process payload directly — zero intermediate structs
})
Zero allocations in the hot path
- Decode: returns structs by value, strings are slices into the recv buffer
- Encode: writes into caller-provided stack buffers
- Dispatch: linear scan over
#soasubscriber table - Callbacks: data valid for callback duration, no copy
The library only allocates during open() and subscribe() (session state).
Type safety
All wire data uses distinct types. The compiler prevents mixing them up:
Payload :: distinct []u8 // user data
Attachment :: distinct []u8 // side-channel data
Cookie :: distinct []u8 // opaque handshake token
Key_Expr :: distinct string // has wildcard matching semantics
Shared Memory
Zero-copy data sharing between processes on the same host. Negotiated automatically during the Init/Open handshake via a challenge protocol.
Supported on all platforms:
- macOS/BSD: POSIX
shm_open+ external lock file for Rust compatibility - Linux: POSIX
shm_open(kernel-managed lifetimes, no lock file) - Windows:
CreateFileMappingW/MapViewOfFile(auto-cleanup)
SHM payloads are resolved transparently in the subscriber dispatch path. The cross-process refcount is atomically incremented on resolve and decremented when the callback returns.
Requires zenohd built with --features shared-memory.
Wire Compatibility
The codec is verified byte-exact against Rust zenoh-codec 1.8.0 via 17
golden test vectors. All transport messages, network messages, declarations,
and SHM payloads match the reference encoder.
See PROTOCOL.md in the companion Go project for the complete wire format
specification.
Building
Requires Odin 2026-03 or later (core:nbio support).
# Run tests
odin test zenoh/
# Build an example
odin build examples/pub/
# Override subscriber table size at build time
odin build examples/sub/ -define:MAX_SUBSCRIBERS=256
Project Structure
zenoh/
├── types.odin Protocol types, distinct types, tagged unions
├── codec.odin Wire codec: encode/decode all message types
├── codec_test.odin Golden vector tests (17 decode + encode)
├── keyexpr.odin Key expression matching (*, **, $*, @)
├── keyexpr_test.odin 150 matching test cases
├── transport.odin Session: open, subscribe, publish, get, close
├── transport_test.odin Codec round-trip tests
├── link.odin TCP connection with 2-byte LE length framing
├── scout.odin UDP multicast scouting (224.0.0.224:7446)
├── errors.odin Error union (or_return compatible)
├── shm.odin Auth challenge, ShmBufInfo, segment reader
├── shm_posix.odin POSIX shm_open/mmap (Darwin + Linux)
├── shm_darwin.odin macOS lock file for Rust interop
├── shm_linux.odin Linux no-op lock stubs
├── shm_windows.odin Windows CreateFileMapping
└── shm_test.odin Auth segment + ShmBufInfo tests
examples/
├── pub/main.odin Publish every second
├── sub/main.odin Subscribe and print
└── queryable/main.odin Query/reply round-trip
License
** free for personal use **
contact me at ben@mere.dev for any license questions