What changed
Why v2 is not backward-compatible
v1’s ring buffer abstraction was the wrong level. The ring had a 4KB slot limit, forced serialization, and couldn’t support streaming or variable-length data. The data path was:notify() and wait() are still there for synchronization.
Conceptual migration
v1 pattern: push/pop through ring
v2 pattern: write/read directly, notify on update
Language-specific migration
Bun / Deno
v1 used Zig-compiled.so/.dylib per platform. v2 uses a single Rust-compiled libzinc_core.{so,dylib}.
Node.js
v1 used Zig FFI viaffi-napi. v2 uses napi-rs for a compiled native .node addon, no FFI overhead.
Python
v1 called the Zig library throughctypes. v2 uses cffi (faster FFI) with numpy zero-copy support built-in.
What v1 didn’t have that v2 adds
- Cross-language zero-copy. Python numpy, Go slice, C++ span, Java ByteBuffer, C# Span, all backed by the same physical pages.
- Type-safe refcounting. Atomic refcount in the header prevents use-after-unlink.
- Notification ring. v2 has an internal 256-slot MPSC notification ring, so multiple notifies between waits aren’t lost.
- Error codes. The C ABI returns standard
errnovalues (EEXIST,ENOENT,EINVAL,ETIMEDOUT). - Version check.
zinc_version()returns(major << 16) | minorfor compatibility checks.
Still the same
shm_open+mmapis the core mechanism. Same POSIX backing.notify/waitpattern. Same mental model.- POSIX-only. Windows is still not supported.
- MIT licensed. Same license.
Migration checklist
- Replace any
ring.push()/ring.poll()calls with region write + notify/wait - Pick a data layout for your shared region (flat struct, offset table, protobuf, cap’n proto, your choice)
- Add a
notify()after writes, add await()before reads - Handle
Timeoutfromwait(), v2 times out cleanly, v1’s polling would just return empty - If using polling, replace
setIntervalwithwait()calls in a dedicated thread/worker - Drop the Zig runtime dependency, v2 only needs
libzinc_corefrom the Rust build
