Encoding as a pure JVM value + native publisher default + pinned per-put override#484
Merged
Merged
Conversation
An encoding IS its (id, schema) pair — Zenoh's own representation; the textual form is derived from a fixed table. With the id<->name table already JVM-side (the generated ENCODING_* constants) and the companion zenoh-flat-jni change crossing Encoding by value in both directions, the SDK Encoding becomes a plain immutable value: no native handle, no native calls, no lazy caches, no locking, nothing to close. This also removes a native memory leak: received Sample/Query/ReplyError previously retained a cloned Encoding handle with no close path. - Encoding.kt: immutable (id, schema); from()/toString()/withSchema() implement Zenoh's exact conversion rules (incl. the custom-encoding name-preserving withSchema and the render-only CUSTOM<->"" table row); equality on (id, schema), matching Zenoh core. - ZenohId.toString: the little-endian lowercase-hex rule in pure Kotlin. - Callback/fromParts leaves follow the new (encId, encSchema) decomposition; outbound sites pass .id/.schema directly. - Correspondence tests (EncodingCorrespondenceTest, ZenohIdCorrespondenceTest) verify the pure implementations against the native ones across the whole predefined id range, parse/render edge shapes, and random ids — the contract for any JVM-side reimplementation of zenoh-flat API. One test caught and fixed a real divergence (custom-encoding withSchema). Pairs with ZettaScaleLabs/zenoh-flat-jni#4 (CI pin bumped). jvmTest: 105 tests, 0 failures.
Two encoding fast paths over the dual-arm expansion (prebindgen#80, zenoh-flat#3, zenoh-flat-jni#4): - The publisher's default encoding is set NATIVELY once at declare time (PublisherOptions.encoding -> declarePublisher's encoding arm); plain put(payload) passes absent (sel -1), so no encoding data crosses per call. Previously the JVM tier re-sent (id, schema) on every put. - PinnedEncoding (Encoding.pinned()) preallocates the native form; passing it in PutOptions/ReplyOptions/GetOptions crosses only a borrowed handle (native clone = Arc bump) — no schema-string traffic in hot publish loops. close() releases the handle (falls back to the plain (id, schema) arm); a finalizer backstops leaks. A PinnedEncoding equals its plain counterpart. - All outbound sites route through one Encoding?.forWire() helper emitting the selector tuple (-1 absent / 0 value / 1 pinned handle). Tests: PinnedEncodingTest (publisher-default applies natively end-to-end; pinned override reused across puts; post-close fallback), correspondence tests updated to the dual-arm newWithSchema. jvmTest: 107 tests, 0 failures. CI pins: zenoh-flat-jni cbbca40, zenoh-flat d792547.
This was referenced Jul 16, 2026
zenoh-flat-jni is the shared tier reused by both zenoh-java and zenoh-kotlin, so the pure-JVM conversion logic moves there (EncodingCodec, ZenohId.zidString — next to the generated constants they are built from). io.zenoh.bytes.Encoding shrinks to a thin facade: the established constants and API delegating to the shared codec, zero logic. Drop PinnedEncoding and the selector-tuple wire helper: with the publisher's default encoding set natively at declare time, the hot publish loop crosses no encoding data at all, and per-call overrides crossing (id, schema) cost only a short string decode — not worth a resource class in the public API. Encoding params revert to the plain (present, id, schema) crossing. jvmTest: 106 tests, 0 failures (correspondence suites now exercise the shared codec; PublisherEncodingTest verifies the native declare-time default + per-put override end-to-end). CI pins: zenoh-flat-jni 9bc155b, zenoh-flat main (6d22091, #3 merged).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Constituent of the zenoh-flat transition (#482). Reworks the Encoding story on the shared-tier principle: all logic lives in zenoh-flat-jni (reused by zenoh-java and zenoh-kotlin); this repo keeps only the established API facade.
1.
Encodingis a pure JVM value, logic in the shared tierAn encoding is its
(id, schema)pair — Zenoh's own representation. The conversion logic (id↔name table + Zenoh's exact parse/render/with_schemarules) lives in zenoh-flat-jni's sharedEncodingCodec(zenoh-flat-jni#4);io.zenoh.bytes.Encodinghere is a thin facade — the established constants and methods, pure delegation, zero logic. Same forZenohId.toString()(sharedzidString()).Encodinghandle with no close path.EncodingCorrespondenceTest,ZenohIdCorrespondenceTest) verify the shared codecs against the native oracle across the whole predefined id range, parse/render edges, and random ids — one caught a real divergence (custom-encodingwithSchemapreserves the name inside the schema slot).2. Publisher default encoding — set natively at declare, zero crossing per put
PublisherOptions.encodingwas emulated JVM-side by re-sending(id, schema)on every put. Now it's passed once to the generateddeclarePublisher(zenoh-flat#3, merged — exposes zenoh'sPublisherBuilder::encoding), and plainput(payload)passes absent — the native default applies, no encoding data crosses at all. Per-callPutOptions.encodingoverrides still work (crossing the cheap(id, schema)pair).Design note:
PinnedEncodingwas implemented, then deliberately droppedThe branch history contains a
PinnedEncoding(a preallocated native encoding handle crossing per call via a dual-arm expansion), later removed by design decision. Rationale:(id, schema)crossing costs only a short schema-string decode (~tens of ns for schema-bearing encodings, zero for predefined ones) against a put path of ≈1µs — noise in practice.Encodingvalue would hide a native handle in a value class — reintroducing exactly the lifecycle machinery (close/finalize, locking, leak hazard) this PR deletes; a separate resource class earned its ~30 lines only for a narrow niche (schema-bearing per-call overrides in non-publisher hot loops).The enabling generator feature (prebindgen #80: Optional selector-dispatched expansion, selector
-1= absent) is merged and covertest-covered, so the pinned-handle arm can be reintroduced wire-compatibly if a real workload ever demands it. Encoding params meanwhile keep the simple(present, id, schema)crossing.Verification
jvmTest: 106 tests, 0 failures — incl. the correspondence suites andPublisherEncodingTest(native declare-time default + per-put override, end-to-end pub/sub).9bc155b(PR Addressed issue #1 #4 head), zenoh-flatmain(6d22091, Update build for cross-platform builds and add github workflow. Close #2 #3 merged), prebindgenmain(SyncCargo.lockwith Zenoh45e05f0from2024-05-13#80 merged).Merge order: zenoh-flat-jni#4 → this.