From 558b0eb749ccae2d786b7045db227fe9434852dc Mon Sep 17 00:00:00 2001 From: Darius Maitia Date: Thu, 12 Sep 2024 10:04:40 -0300 Subject: [PATCH 1/4] issue(key expr): refactoring API --- .../kotlin/io/zenoh/keyexpr/KeyExpr.kt | 37 ++++++------------- .../kotlin/io/zenoh/QueryableTest.kt | 15 ++++---- .../kotlin/io/zenoh/UserAttachmentTest.kt | 7 ++-- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt index e6654f088..1bac7474a 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt @@ -17,7 +17,6 @@ package io.zenoh.keyexpr import io.zenoh.Session import io.zenoh.SessionDeclaration import io.zenoh.jni.JNIKeyExpr -import io.zenoh.selector.Selector /** * # Address space @@ -111,41 +110,27 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn return JNIKeyExpr.includes(this, other) } - /** - * Undeclare the key expression if it was previously declared on the specified [session]. - * - * @param session The session from which the key expression was previously declared. - * @return A [Result] with the operation status. - */ - fun undeclare(session: Session): Result { - return session.undeclare(this) - } - - /** - * Returns true if the [KeyExpr] has still associated a native key expression allowing it to perform operations. - */ - fun isValid(): Boolean { - return jniKeyExpr != null - } - - fun intoSelector(): Selector { - return Selector(this) - } - override fun toString(): String { return keyExpr } /** - * Closes the key expression. Operations performed on this key expression won't be valid anymore after this call. + * Equivalent to [undeclare]. This function is automatically called when using try with resources. + * + * @see undeclare */ override fun close() { - jniKeyExpr?.close() - jniKeyExpr = null + undeclare() } + /** + * If the key expression was declared from a [Session], then [undeclare] frees the native key expression associated + * to this instance. The KeyExpr instance is downgraded into a normal KeyExpr, which still allows performing + * operations on it, but without the inner optimizations. + */ override fun undeclare() { - close() + jniKeyExpr?.close() + jniKeyExpr = null } override fun equals(other: Any?): Boolean { diff --git a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/QueryableTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/QueryableTest.kt index 3c3e85ec1..747ce3396 100644 --- a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/QueryableTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/QueryableTest.kt @@ -23,6 +23,7 @@ import io.zenoh.query.Reply import io.zenoh.query.ReplyError import io.zenoh.queryable.Query import io.zenoh.sample.Sample +import io.zenoh.selector.Selector import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay @@ -73,7 +74,7 @@ class QueryableTest { var reply: Reply? = null val delay = Duration.ofMillis(1000) withTimeout(delay) { - session.get(testKeyExpr.intoSelector(), callback = { reply = it }, timeout = delay) + session.get(Selector(testKeyExpr), callback = { reply = it }, timeout = delay) } assertNotNull(reply) @@ -90,7 +91,7 @@ class QueryableTest { delay(500) val receivedReplies = ArrayList() - session.get(testKeyExpr.intoSelector(), callback = { reply: Reply -> + session.get(Selector(testKeyExpr), callback = { reply: Reply -> receivedReplies.add(reply) }) @@ -107,7 +108,7 @@ class QueryableTest { val queryable = session.declareQueryable(testKeyExpr, callback = { query -> receivedQuery = query }).getOrThrow() - session.get(testKeyExpr.intoSelector(), callback = {}) + session.get(Selector(testKeyExpr), callback = {}) delay(100) assertNotNull(receivedQuery) @@ -118,7 +119,7 @@ class QueryableTest { receivedQuery = null val payload = "Test value".into() val attachment = "Attachment".into() - session.get(testKeyExpr.intoSelector(), callback = {}, payload = payload, encoding = Encoding.ZENOH_STRING, attachment = attachment) + session.get(Selector(testKeyExpr), callback = {}, payload = payload, encoding = Encoding.ZENOH_STRING, attachment = attachment) delay(100) assertNotNull(receivedQuery) @@ -142,7 +143,7 @@ class QueryableTest { }).getOrThrow() var receivedReply: Reply? = null - session.get(testKeyExpr.intoSelector(), callback = { receivedReply = it }, timeout = Duration.ofMillis(10)) + session.get(Selector(testKeyExpr), callback = { receivedReply = it }, timeout = Duration.ofMillis(10)) queryable.close() @@ -163,7 +164,7 @@ class QueryableTest { }).getOrThrow() var receivedReply: Reply? = null - session.get(testKeyExpr.intoSelector(), callback = { receivedReply = it }, timeout = Duration.ofMillis(10)) + session.get(Selector(testKeyExpr), callback = { receivedReply = it }, timeout = Duration.ofMillis(10)) Thread.sleep(1000) queryable.close() @@ -185,7 +186,7 @@ class QueryableTest { query.replyDel(testKeyExpr, timestamp = timestamp, qos = qos) }).getOrThrow() var receivedReply: Reply? = null - session.get(testKeyExpr.intoSelector(), callback = { receivedReply = it }, timeout = Duration.ofMillis(10)) + session.get(Selector(testKeyExpr), callback = { receivedReply = it }, timeout = Duration.ofMillis(10)) queryable.close() diff --git a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/UserAttachmentTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/UserAttachmentTest.kt index 6257ee563..d7d7a2f6b 100644 --- a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/UserAttachmentTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/UserAttachmentTest.kt @@ -20,6 +20,7 @@ import io.zenoh.protocol.ZBytes import io.zenoh.protocol.into import io.zenoh.query.Reply import io.zenoh.sample.Sample +import io.zenoh.selector.Selector import java.time.Duration import kotlin.test.* @@ -142,7 +143,7 @@ class UserAttachmentTest { }).getOrThrow() session.get( - keyExpr.intoSelector(), + Selector(keyExpr), callback = {}, attachment = attachmentZBytes, timeout = Duration.ofMillis(1000) @@ -162,7 +163,7 @@ class UserAttachmentTest { query.reply(keyExpr, payload, attachment = attachmentZBytes) }).getOrThrow() - session.get(keyExpr.intoSelector(), callback = { + session.get(Selector(keyExpr), callback = { reply = it }, timeout = Duration.ofMillis(1000)).getOrThrow() @@ -180,7 +181,7 @@ class UserAttachmentTest { query.reply(keyExpr, payload) }).getOrThrow() - session.get(keyExpr.intoSelector(), callback = { + session.get(Selector(keyExpr), callback = { reply = it }, timeout = Duration.ofMillis(1000)).getOrThrow() From 8998f30fb19bfb467a92d6a5bf85a68de9b282a3 Mon Sep 17 00:00:00 2001 From: Darius Maitia Date: Thu, 12 Sep 2024 12:13:26 -0300 Subject: [PATCH 2/4] issue(key expr): adding relationTo function --- zenoh-jni/src/key_expr.rs | 38 ++++++++++++++++++- .../kotlin/io/zenoh/jni/JNIKeyExpr.kt | 15 ++++++++ .../kotlin/io/zenoh/keyexpr/KeyExpr.kt | 25 ++++++++++++ .../io/zenoh/keyexpr/SetIntersectionLevel.kt | 26 +++++++++++++ .../commonTest/kotlin/io/zenoh/KeyExprTest.kt | 33 ++++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/SetIntersectionLevel.kt diff --git a/zenoh-jni/src/key_expr.rs b/zenoh-jni/src/key_expr.rs index ae1756546..cb0fabecf 100644 --- a/zenoh-jni/src/key_expr.rs +++ b/zenoh-jni/src/key_expr.rs @@ -16,7 +16,7 @@ use std::ops::Deref; use std::sync::Arc; use jni::objects::JClass; -use jni::sys::{jboolean, jstring}; +use jni::sys::{jboolean, jint, jstring}; use jni::{objects::JString, JNIEnv}; use zenoh::key_expr::KeyExpr; @@ -145,6 +145,42 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_includesVia }) } +/// Returns the integer representation of the intersection level of the key expression 1 and key expression 2, +/// from the perspective of key expression 1. +/// +/// # Params: +/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_2`: String representation of the key expression 2. +/// +/// # Safety +/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing +/// key expressions that were declared from a session (in that case the key expression has a pointer associated). +/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers +/// remain valid after the call to this function. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_relationToViaJNI( + mut env: JNIEnv, + _: JClass, + key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_1: JString, + key_expr_ptr_2: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_2: JString, +) -> jint { + || -> Result { + let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; + let key_expr_2 = process_kotlin_key_expr(&mut env, &key_expr_str_2, key_expr_ptr_2)?; + Ok(key_expr_1.relation_to(&key_expr_2) as jint) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + -1 as jint + }) +} + /// Frees a declared key expression. /// /// # Parameters diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt index 553df26fb..251d7c634 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt @@ -16,6 +16,7 @@ package io.zenoh.jni import io.zenoh.ZenohLoad import io.zenoh.keyexpr.KeyExpr +import io.zenoh.keyexpr.SetIntersectionLevel internal class JNIKeyExpr(internal val ptr: Long) { @@ -46,6 +47,16 @@ internal class JNIKeyExpr(internal val ptr: Long) { keyExprB.keyExpr ) + fun relationTo(keyExpr: KeyExpr, other: KeyExpr): SetIntersectionLevel { + val intersection = relationToViaJNI( + keyExpr.jniKeyExpr?.ptr ?: 0, + keyExpr.keyExpr, + other.jniKeyExpr?.ptr ?: 0, + other.keyExpr + ) + return SetIntersectionLevel.fromInt(intersection) + } + @Throws(Exception::class) private external fun tryFromViaJNI(keyExpr: String): String @@ -57,6 +68,10 @@ internal class JNIKeyExpr(internal val ptr: Long) { @Throws(Exception::class) private external fun includesViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Boolean + + @Throws(Exception::class) + private external fun relationToViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Int + } fun close() { diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt index 1bac7474a..1bf130f99 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt @@ -110,6 +110,31 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn return JNIKeyExpr.includes(this, other) } + /** + * Returns the relation between 'this' and other from 'this''s point of view (SetIntersectionLevel::Includes + * signifies that self includes other). Note that this is slower than [intersects] and [includes], + * so you should favor these methods for most applications. + */ + fun relationTo(other: KeyExpr): SetIntersectionLevel { + return JNIKeyExpr.relationTo(this, other) + } + + /** + * Joins both sides, inserting a / in between them. + * This should be your preferred method when concatenating path segments. + */ + fun join(other: String): KeyExpr { + TODO() + } + + /** + * Performs string concatenation and returns the result as a KeyExpr if possible. + * You should probably prefer [join] as Zenoh may then take advantage of the hierachical separation it inserts. + */ + fun concat(other: String): KeyExpr { + TODO() + } + override fun toString(): String { return keyExpr } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/SetIntersectionLevel.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/SetIntersectionLevel.kt new file mode 100644 index 000000000..5a687300e --- /dev/null +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/SetIntersectionLevel.kt @@ -0,0 +1,26 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +package io.zenoh.keyexpr + +enum class SetIntersectionLevel(internal val value: Int) { + DISJOINT(0), + INTERSECTS(1), + INCLUDES(2), + EQUALS(3); + + companion object { + internal fun fromInt(value: Int) = SetIntersectionLevel.entries.first { it.value == value } + } +} diff --git a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt index 2d2eeafb6..f2b9a9b94 100644 --- a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt @@ -16,6 +16,7 @@ package io.zenoh import io.zenoh.exceptions.SessionException import io.zenoh.keyexpr.KeyExpr +import io.zenoh.keyexpr.SetIntersectionLevel import io.zenoh.keyexpr.intoKeyExpr import kotlin.test.* @@ -127,4 +128,36 @@ class KeyExprTest { keyExpr.close() keyExpr2.close() } + + @Test + fun `relationTo returns includes test`() { + val keyExprA = KeyExpr.tryFrom("A/**").getOrThrow() + val keyExprB = KeyExpr.tryFrom("A/B/C").getOrThrow() + + assertEquals(SetIntersectionLevel.INCLUDES, keyExprA.relationTo(keyExprB)) + } + + @Test + fun `relationTo returns intersect test`() { + val keyExprA = KeyExpr.tryFrom("A/*/C/D").getOrThrow() + val keyExprB = KeyExpr.tryFrom("A/B/C/*").getOrThrow() + + assertEquals(SetIntersectionLevel.INTERSECTS, keyExprA.relationTo(keyExprB)) + } + + @Test + fun `relationTo returns equals test`() { + val keyExprA = KeyExpr.tryFrom("A/B/C").getOrThrow() + val keyExprB = KeyExpr.tryFrom("A/B/C").getOrThrow() + + assertEquals(SetIntersectionLevel.EQUALS, keyExprA.relationTo(keyExprB)) + } + + @Test + fun `relationTo returns disjoint test`() { + val keyExprA = KeyExpr.tryFrom("A/B/C").getOrThrow() + val keyExprB = KeyExpr.tryFrom("D/E/F").getOrThrow() + + assertEquals(SetIntersectionLevel.DISJOINT, keyExprA.relationTo(keyExprB)) + } } From 5e4400a1d4e42e3bb3c07a1affc6de5f588dffb5 Mon Sep 17 00:00:00 2001 From: Darius Maitia Date: Thu, 12 Sep 2024 13:11:13 -0300 Subject: [PATCH 3/4] issue(key expr): adding join function --- zenoh-jni/src/key_expr.rs | 53 ++++++++++++++++--- .../kotlin/io/zenoh/jni/JNIKeyExpr.kt | 6 +++ .../kotlin/io/zenoh/keyexpr/KeyExpr.kt | 4 +- .../commonTest/kotlin/io/zenoh/KeyExprTest.kt | 9 ++++ 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/zenoh-jni/src/key_expr.rs b/zenoh-jni/src/key_expr.rs index cb0fabecf..b6d831c89 100644 --- a/zenoh-jni/src/key_expr.rs +++ b/zenoh-jni/src/key_expr.rs @@ -23,7 +23,7 @@ use zenoh::key_expr::KeyExpr; use crate::errors::Error; use crate::errors::Result; use crate::utils::decode_string; -use crate::{jni_error, key_expr_error, throw_exception}; +use crate::{jni_error, key_expr_error, session_error, throw_exception}; /// Validates the provided `key_expr` to be a valid key expression, returning it back /// in case of success or throwing an exception in case of failure. @@ -79,9 +79,9 @@ pub extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_autocanonizeViaJNI /// /// # Params: /// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_str_1`: String representation of the key expression 1. /// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_2`: String representation of the key expression 2. +/// - `key_expr_str_2`: String representation of the key expression 2. /// /// # Safety /// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing @@ -114,9 +114,9 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_intersectsV /// /// # Params: /// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_str_1`: String representation of the key expression 1. /// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_2`: String representation of the key expression 2. +/// - `key_expr_str_2`: String representation of the key expression 2. /// /// # Safety /// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing @@ -150,9 +150,9 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_includesVia /// /// # Params: /// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_str_1`: String representation of the key expression 1. /// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_2`: String representation of the key expression 2. +/// - `key_expr_str_2`: String representation of the key expression 2. /// /// # Safety /// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing @@ -181,6 +181,45 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_relationToV }) } +/// Joins key expression 1 with key expression 2, where key_expr_2 is a string. Returns the string representation +/// of the result, or throws an exception in case of failure. +/// +/// # Params: +/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_2`: String representation of the key expression 2. +/// +/// # Safety +/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing +/// key expressions that were declared from a session (in that case the key expression has a pointer associated). +/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers +/// remain valid after the call to this function. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_joinViaJNI( + mut env: JNIEnv, + _class: JClass, + key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_1: JString, + key_expr_2: JString, +) -> jstring { + || -> Result { + let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; + let key_expr_str = decode_string(&mut env, &key_expr_2)?; + let result = key_expr_1 + .join(key_expr_str.as_str()) + .map_err(|err| session_error!(err))?; + env.new_string(result.to_string()) + .map(|kexp| kexp.as_raw()) + .map_err(|err| jni_error!(err)) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + JString::default().as_raw() + }) +} + /// Frees a declared key expression. /// /// # Parameters diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt index 251d7c634..917465607 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt @@ -57,6 +57,10 @@ internal class JNIKeyExpr(internal val ptr: Long) { return SetIntersectionLevel.fromInt(intersection) } + fun joinViaJNI(keyExpr: KeyExpr, other: String): Result = runCatching { + KeyExpr(joinViaJNI(keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, other)) + } + @Throws(Exception::class) private external fun tryFromViaJNI(keyExpr: String): String @@ -72,6 +76,8 @@ internal class JNIKeyExpr(internal val ptr: Long) { @Throws(Exception::class) private external fun relationToViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Int + @Throws(Exception::class) + private external fun joinViaJNI(ptrA: Long, keyExprA: String, other: String): String } fun close() { diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt index 1bf130f99..606722dd4 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt @@ -123,8 +123,8 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * Joins both sides, inserting a / in between them. * This should be your preferred method when concatenating path segments. */ - fun join(other: String): KeyExpr { - TODO() + fun join(other: String): Result { + return JNIKeyExpr.joinViaJNI(this, other) } /** diff --git a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt index f2b9a9b94..2d4523e98 100644 --- a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt @@ -160,4 +160,13 @@ class KeyExprTest { assertEquals(SetIntersectionLevel.DISJOINT, keyExprA.relationTo(keyExprB)) } + + @Test + fun `join test`() { + val keyExprA = KeyExpr.tryFrom("A/B").getOrThrow() + val keyExprExpected = KeyExpr.tryFrom("A/B/C/D").getOrThrow() + + val keyExprJoined = keyExprA.join("C/D").getOrThrow() + assertEquals(keyExprExpected, keyExprJoined) + } } From f0fd17a0868a180eabf4e3499c150723bcea0305 Mon Sep 17 00:00:00 2001 From: Darius Maitia Date: Thu, 12 Sep 2024 13:22:39 -0300 Subject: [PATCH 4/4] issue(key expr): adding concat function --- zenoh-jni/src/key_expr.rs | 43 ++++++++++++++++++- .../kotlin/io/zenoh/jni/JNIKeyExpr.kt | 7 +++ .../kotlin/io/zenoh/keyexpr/KeyExpr.kt | 6 +-- .../commonTest/kotlin/io/zenoh/KeyExprTest.kt | 9 ++++ 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/zenoh-jni/src/key_expr.rs b/zenoh-jni/src/key_expr.rs index b6d831c89..a329e618b 100644 --- a/zenoh-jni/src/key_expr.rs +++ b/zenoh-jni/src/key_expr.rs @@ -206,9 +206,48 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_joinViaJNI( ) -> jstring { || -> Result { let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; - let key_expr_str = decode_string(&mut env, &key_expr_2)?; + let key_expr_2_str = decode_string(&mut env, &key_expr_2)?; let result = key_expr_1 - .join(key_expr_str.as_str()) + .join(key_expr_2_str.as_str()) + .map_err(|err| session_error!(err))?; + env.new_string(result.to_string()) + .map(|kexp| kexp.as_raw()) + .map_err(|err| jni_error!(err)) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + JString::default().as_raw() + }) +} + +/// Concats key_expr_1 with key_expr_2, where key_expr_2 is a string. Returns the string representation +/// of the result, or throws an exception in case of failure. +/// +/// # Params: +/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_2`: String representation of the key expression 2. +/// +/// # Safety +/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing +/// key expressions that were declared from a session (in that case the key expression has a pointer associated). +/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers +/// remain valid after the call to this function. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_concatViaJNI( + mut env: JNIEnv, + _class: JClass, + key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_1: JString, + key_expr_2: JString, +) -> jstring { + || -> Result { + let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; + let key_expr_2_str = decode_string(&mut env, &key_expr_2)?; + let result = key_expr_1 + .concat(key_expr_2_str.as_str()) .map_err(|err| session_error!(err))?; env.new_string(result.to_string()) .map(|kexp| kexp.as_raw()) diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt index 917465607..ea0f54760 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt @@ -61,6 +61,10 @@ internal class JNIKeyExpr(internal val ptr: Long) { KeyExpr(joinViaJNI(keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, other)) } + fun concatViaJNI(keyExpr: KeyExpr, other: String): Result = runCatching { + KeyExpr(concatViaJNI(keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, other)) + } + @Throws(Exception::class) private external fun tryFromViaJNI(keyExpr: String): String @@ -78,6 +82,9 @@ internal class JNIKeyExpr(internal val ptr: Long) { @Throws(Exception::class) private external fun joinViaJNI(ptrA: Long, keyExprA: String, other: String): String + + @Throws(Exception::class) + private external fun concatViaJNI(ptrA: Long, keyExprA: String, other: String): String } fun close() { diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt index 606722dd4..97ac77880 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt @@ -129,10 +129,10 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn /** * Performs string concatenation and returns the result as a KeyExpr if possible. - * You should probably prefer [join] as Zenoh may then take advantage of the hierachical separation it inserts. + * You should probably prefer [join] as Zenoh may then take advantage of the hierarchical separation it inserts. */ - fun concat(other: String): KeyExpr { - TODO() + fun concat(other: String): Result { + return JNIKeyExpr.concatViaJNI(this, other) } override fun toString(): String { diff --git a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt index 2d4523e98..6753adc76 100644 --- a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt @@ -169,4 +169,13 @@ class KeyExprTest { val keyExprJoined = keyExprA.join("C/D").getOrThrow() assertEquals(keyExprExpected, keyExprJoined) } + + @Test + fun `concat test`() { + val keyExprA = KeyExpr.tryFrom("A/B").getOrThrow() + val keyExprExpected = KeyExpr.tryFrom("A/B/C/D").getOrThrow() + + val keyExprConcat = keyExprA.concat("/C/D").getOrThrow() + assertEquals(keyExprExpected, keyExprConcat) + } }