Skip to main content
Zinc v2 is a complete rewrite. If you used the Zig-based v1, this page covers what changed and how to migrate.

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:
v2 removes the ring. Instead, you get a raw pointer to shared memory. Layout bytes however you want. 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 via ffi-napi. v2 uses napi-rs for a compiled native .node addon, no FFI overhead.

Python

v1 called the Zig library through ctypes. 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 errno values (EEXIST, ENOENT, EINVAL, ETIMEDOUT).
  • Version check. zinc_version() returns (major << 16) | minor for compatibility checks.

Still the same

  • shm_open + mmap is the core mechanism. Same POSIX backing.
  • notify / wait pattern. 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 a wait() before reads
  • Handle Timeout from wait(), v2 times out cleanly, v1’s polling would just return empty
  • If using polling, replace setInterval with wait() calls in a dedicated thread/worker
  • Drop the Zig runtime dependency, v2 only needs libzinc_core from the Rust build