Skip to content

Commit c5a5353

Browse files
committed
sample attachment accessor
1 parent eb737db commit c5a5353

5 files changed

Lines changed: 55 additions & 16 deletions

File tree

zenoh-flat-jni/build.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ fn main() {
109109
// per-call `zZbytesFromVec` crossing. (No identity arm: the SDK never
110110
// holds a ZZBytes handle for these.)
111111
.constructor(pq!(z_zbytes_from_vec))
112+
// Combined ACCESSOR for ZZBytes (output expansion): an `Option<&ZZBytes>`
113+
// return (e.g. z_sample_attachment) is decomposed into its bytes
114+
// (`z_zbytes_to_bytes` → ByteArray) and delivered to a builder lambda —
115+
// no transient handle + `close()`. No identity record (the SDK ZBytes
116+
// holds the ByteArray, not the native handle).
117+
.combined_accessor(pq!(ZZBytes))
118+
.combined_accessor_record(pq!(z_zbytes_to_bytes))
112119
.ptr_class(pq!(ZEncoding))
113120
.package_fun(pq!(z_encoding_id))
114121
.package_fun(pq!(z_encoding_schema))
@@ -177,6 +184,14 @@ fn main() {
177184
.ptr_class(pq!(ZTimestamp))
178185
.package_fun(pq!(z_timestamp_ntp64))
179186
.package_fun(pq!(z_timestamp_id))
187+
// Combined ACCESSOR for ZTimestamp (output expansion): an
188+
// `Option<&ZTimestamp>` return (z_sample_timestamp) is decomposed into
189+
// its NTP64 value (`z_timestamp_ntp64` → i64 → boxed Long) and delivered
190+
// to a builder lambda — no second `zTimestampNtp64` crossing, no
191+
// transient handle. No identity record (zenoh-java's TimeStamp keeps the
192+
// Long, not the native handle).
193+
.combined_accessor(pq!(ZTimestamp))
194+
.combined_accessor_record(pq!(z_timestamp_ntp64))
180195
.package("sample")
181196
.enum_class(pq!(SampleKind))
182197
.ptr_class(pq!(ZSample))
@@ -186,10 +201,12 @@ fn main() {
186201
.package_fun(pq!(z_sample_encoding))
187202
.package_fun(pq!(z_sample_kind))
188203
.package_fun(pq!(z_sample_timestamp))
204+
.expand_output() // Option<&ZTimestamp> → builder (Long ntp64) → R?
189205
.package_fun(pq!(z_sample_express))
190206
.package_fun(pq!(z_sample_priority))
191207
.package_fun(pq!(z_sample_congestion_control))
192208
.package_fun(pq!(z_sample_attachment))
209+
.expand_output() // Option<&ZZBytes> → builder (ByteArray) → R?
193210
.package("pubsub")
194211
.ptr_class(pq!(ZPublisher))
195212
.package_fun(pq!(z_publisher_put))

zenoh-flat-jni/generated-kotlin/io/zenoh/jni/JNINative.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import io.zenoh.jni.sample.SampleKind
2525
import io.zenoh.jni.sample.ZSample
2626
import io.zenoh.jni.scouting.ZScout
2727
import io.zenoh.jni.session.ZSession
28-
import io.zenoh.jni.time.ZTimestamp
2928

3029
internal object JNINative {
3130
external fun initAndroidLogs(filter: String, errorSink: Any)
@@ -132,15 +131,15 @@ internal object JNINative {
132131
external fun zReplyReplierEid(r: Long, errorSink: Any): Int
133132
external fun zReplyReplierZid(r: Long, errorSink: Any): ByteArray?
134133
external fun zReplySample(r: Long, errorSink: Any): Long
135-
external fun zSampleAttachment(s: Long, errorSink: Any): Long
134+
external fun zSampleAttachment(s: Long, build: Any, errorSink: Any): Any?
136135
external fun zSampleCongestionControl(s: Long, errorSink: Any): Int
137136
external fun zSampleEncoding(s: Long, errorSink: Any): Long
138137
external fun zSampleExpress(s: Long, errorSink: Any): Boolean
139138
external fun zSampleKeyExpr(s: Long, build: Any, errorSink: Any): Any?
140139
external fun zSampleKind(s: Long, errorSink: Any): Int
141140
external fun zSamplePayload(s: Long, errorSink: Any): Long
142141
external fun zSamplePriority(s: Long, errorSink: Any): Int
143-
external fun zSampleTimestamp(s: Long, errorSink: Any): Long
142+
external fun zSampleTimestamp(s: Long, build: Any, errorSink: Any): Any?
144143
external fun zScout(whatami: Int, config: Long, callback: ZHelloCallback, onClose: Callback, errorSink: Any): Long
145144
external fun zSessionDeclareKeyexpr(session: Long, keyExpr: String, errorSink: Any): Long
146145
external fun zSessionDeclarePublisher(session: Long, keyExprSel: Int, keyExpr0: String?, keyExpr1: Long, congestionControl: Int?, priority: Int?, express: Boolean?, reliability: Int?, errorSink: Any): Long

zenoh-flat-jni/generated-kotlin/io/zenoh/jni/sample/JNIsample.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import io.zenoh.jni.qos.CongestionControl
1111
import io.zenoh.jni.qos.Priority
1212
import io.zenoh.jni.sample.SampleKind
1313
import io.zenoh.jni.sample.ZSample
14-
import io.zenoh.jni.time.ZTimestamp
1514
import io.zenoh.jni.withSortedHandleLocks
1615
import io.zenoh.jni.JNINative
1716

@@ -64,13 +63,14 @@ public fun zSampleKind(s: ZSample): SampleKind {
6463
return __ret
6564
}
6665

67-
public fun zSampleTimestamp(s: ZSample): ZTimestamp? {
66+
@Suppress("UNCHECKED_CAST")
67+
public fun <R> zSampleTimestamp(s: ZSample, build: (Long) -> R): R? {
6868
val __err = ErrorHolder()
6969
val __sink = ErrorSink { __m -> __err.message = __m }
7070
val __ret = withSortedHandleLocks(s) {
7171
val s_ptr = s.ptr
7272
if (s_ptr == 0L) throw ZException("Operation on a closed native handle.")
73-
JNINative.zSampleTimestamp(s_ptr, __sink).let { if (it == 0L) null else ZTimestamp(it) }
73+
(JNINative.zSampleTimestamp(s_ptr, build, __sink) as R?)
7474
}
7575
__err.message?.let { throw ZException(it) }
7676
return __ret
@@ -112,13 +112,14 @@ public fun zSampleCongestionControl(s: ZSample): CongestionControl {
112112
return __ret
113113
}
114114

115-
public fun zSampleAttachment(s: ZSample): ZZBytes? {
115+
@Suppress("UNCHECKED_CAST")
116+
public fun <R> zSampleAttachment(s: ZSample, build: (ByteArray) -> R): R? {
116117
val __err = ErrorHolder()
117118
val __sink = ErrorSink { __m -> __err.message = __m }
118119
val __ret = withSortedHandleLocks(s) {
119120
val s_ptr = s.ptr
120121
if (s_ptr == 0L) throw ZException("Operation on a closed native handle.")
121-
JNINative.zSampleAttachment(s_ptr, __sink).let { if (it == 0L) null else ZZBytes(it) }
122+
(JNINative.zSampleAttachment(s_ptr, build, __sink) as R?)
122123
}
123124
__err.message?.let { throw ZException(it) }
124125
return __ret

zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,19 @@ data class Sample(
6666
val payload = ZBytes.fromHandle(io.zenoh.jni.sample.zSamplePayload(zs))
6767
val encoding = Encoding.fromHandle(io.zenoh.jni.sample.zSampleEncoding(zs))
6868
val kind = io.zenoh.jni.sample.zSampleKind(zs).toPublic()
69-
val timestamp = io.zenoh.jni.sample.zSampleTimestamp(zs)?.let { ts ->
70-
try {
71-
TimeStamp(io.zenoh.jni.time.zTimestampNtp64(ts))
72-
} finally {
73-
ts.close()
74-
}
75-
}
69+
// Output expansion: the native layer extracts the timestamp's NTP64
70+
// value and delivers it to this builder in one JNI crossing (no
71+
// second `zTimestampNtp64` call, no transient handle to close).
72+
// Returns null when the sample has no timestamp.
73+
val timestamp = io.zenoh.jni.sample.zSampleTimestamp(zs) { ntp64 -> TimeStamp(ntp64) }
7674
val qos = QoS(
7775
CongestionControl.fromJni(io.zenoh.jni.sample.zSampleCongestionControl(zs)),
7876
Priority.fromJni(io.zenoh.jni.sample.zSamplePriority(zs)),
7977
io.zenoh.jni.sample.zSampleExpress(zs)
8078
)
81-
val attachment = io.zenoh.jni.sample.zSampleAttachment(zs)?.let { ZBytes.fromHandle(it) }
79+
// Output expansion: the attachment bytes are delivered directly to
80+
// this builder (no transient handle + `close()`); null when absent.
81+
val attachment = io.zenoh.jni.sample.zSampleAttachment(zs) { bytes -> ZBytes(bytes) }
8282
return Sample(keyExpr, payload, encoding, kind, timestamp, qos, attachment)
8383
}
8484
}

zenoh-java/src/jvmTest/java/io/zenoh/SubscriberTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.ArrayList;
3535

3636
import static org.junit.Assert.assertEquals;
37+
import static org.junit.Assert.assertNull;
3738
import static org.junit.Assert.assertTrue;
3839

3940
@RunWith(JUnit4.class)
@@ -128,6 +129,27 @@ public void subscriber_sampleKeyExprDeliversHandleAndString() throws ZError {
128129
subscriber.close();
129130
}
130131

132+
/**
133+
* Output (data) expansion, nullable path: a sample published without a
134+
* timestamp / attachment exercises the `Option<&T>` "None ⇒ null result"
135+
* branch of zSampleTimestamp / zSampleAttachment (builder skipped). The
136+
* default session adds no timestamp, so both must come back null.
137+
*/
138+
@Test
139+
public void subscriber_sampleNullableFieldsAreNull() throws ZError {
140+
var receivedSamples = new ArrayList<Sample>();
141+
var subscriber = session.declareSubscriber(testKeyExpr, receivedSamples::add);
142+
143+
session.put(testKeyExpr, ZBytes.from("no-timestamp"));
144+
145+
assertEquals(1, receivedSamples.size());
146+
Sample s = receivedSamples.get(0);
147+
assertNull(s.getTimestamp());
148+
assertNull(s.getAttachment());
149+
150+
subscriber.close();
151+
}
152+
131153
@Test
132154
public void subscriber_runsWithHandler() throws ZError {
133155
var handler = new QueueHandler<Sample>();

0 commit comments

Comments
 (0)