From 0d3af40eb065b0ab2c6106700f61fe0cfd538dec Mon Sep 17 00:00:00 2001 From: zaaarf Date: Fri, 16 Aug 2024 01:21:21 +0200 Subject: [PATCH] feat(java): expose hash function, use OptionalLong in TextChange --- dist/java/src/mp/code/Utils.java | 10 ++++ dist/java/src/mp/code/data/TextChange.java | 12 ++-- src/ffi/java/buffer.rs | 64 ++++------------------ src/ffi/java/mod.rs | 1 + src/ffi/java/utils.rs | 17 ++++++ 5 files changed, 42 insertions(+), 62 deletions(-) create mode 100644 dist/java/src/mp/code/Utils.java create mode 100644 src/ffi/java/utils.rs diff --git a/dist/java/src/mp/code/Utils.java b/dist/java/src/mp/code/Utils.java new file mode 100644 index 0000000..7174ba7 --- /dev/null +++ b/dist/java/src/mp/code/Utils.java @@ -0,0 +1,10 @@ +package mp.code; + +public class Utils { + /** + * Hashes the given {@link String} using CodeMP's hashing algorithm (xxh3). + * @param input the string to hash + * @return the hash + */ + public static native long hash(String input); +} diff --git a/dist/java/src/mp/code/data/TextChange.java b/dist/java/src/mp/code/data/TextChange.java index e50b6f0..f4e614d 100644 --- a/dist/java/src/mp/code/data/TextChange.java +++ b/dist/java/src/mp/code/data/TextChange.java @@ -1,21 +1,17 @@ package mp.code.data; +import java.util.OptionalLong; + public class TextChange { public final long start; public final long end; public final String content; - private final long hash; // xxh3 hash + public final OptionalLong hash; // xxh3 hash - public TextChange(long start, long end, String content, long hash) { + public TextChange(long start, long end, String content, OptionalLong hash) { this.start = start; this.end = end; this.content = content; this.hash = hash; } - - private static native long hash(String content); - public boolean hashMatches(String content) { - // 0 is Rust default value and a very unlikely hash - return hash == 0L || this.hash == hash(content); - } } diff --git a/src/ffi/java/buffer.rs b/src/ffi/java/buffer.rs index 02e9807..01dec82 100644 --- a/src/ffi/java/buffer.rs +++ b/src/ffi/java/buffer.rs @@ -63,71 +63,27 @@ fn recv_jni(env: &mut JNIEnv, change: Option) -> jobject None => JObject::default(), Some(event) => { let content = env.new_string(event.content).jexcept(env); + + let hash = env.find_class("java/util/OptionalLong").and_then(|class| { + if let Some(h) = event.hash { + env.call_static_method(class, "of", "(J)Ljava/util/OptionalLong;", &[JValueGen::Long(h)]) + } else { + env.call_static_method(class, "empty", "()Ljava/util/OptionalLong;", &[]) + } + }).and_then(|o| o.l()).jexcept(env); env.find_class("mp/code/data/TextChange") .and_then(|class| { env.new_object( class, - "(JJLjava/lang/String;J)V", + "(JJLjava/lang/String;Ljava/util/OptionalLong;)V", &[ JValueGen::Long(jlong::from(event.start)), JValueGen::Long(jlong::from(event.end)), JValueGen::Object(&content), - JValueGen::Long(event.hash.unwrap_or_default()) + JValueGen::Object(&hash) ] ) }).jexcept(env) } }.as_raw() } - -/// Receives from Java, converts and sends a [crate::api::TextChange]. -#[no_mangle] -pub extern "system" fn Java_mp_code_BufferController_send<'local>( - mut env: JNIEnv, - _class: JClass<'local>, - self_ptr: jlong, - input: JObject<'local> -) { - let start = env.get_field(&input, "start", "J").and_then(|s| s.j()).jexcept(&mut env); - let end = env.get_field(&input, "end", "J").and_then(|e| e.j()).jexcept(&mut env); - let content = env.get_field(&input, "content", "Ljava/lang/String;") - .and_then(|c| c.l()) - .map(|c| c.into()) - .jexcept(&mut env); - let content = unsafe { env.get_string_unchecked(&content) } - .map(|c| c.to_string_lossy().to_string()) - .jexcept(&mut env); - - let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) }; - RT.block_on(controller.send(crate::api::TextChange { - start: start as u32, - end: end as u32, - content, - hash: None - })).jexcept(&mut env); -} - -/// Called by the Java GC to drop a [crate::buffer::Controller]. -#[no_mangle] -pub extern "system" fn Java_mp_code_BufferController_free( - _env: JNIEnv, - _class: JClass, - self_ptr: jlong, -) { - let _ = unsafe { Box::from_raw(self_ptr as *mut crate::cursor::Controller) }; -} - - -/// Calculates the XXH3 hash for a given String. -#[no_mangle] -pub extern "system" fn Java_mp_code_data_TextChange_hash<'local>( - mut env: JNIEnv, - _class: JClass<'local>, - content: JString<'local>, -) -> jlong { - let content: String = env.get_string(&content) - .map(|s| s.into()) - .jexcept(&mut env); - let hash = crate::ext::hash(content.as_bytes()); - i64::from_ne_bytes(hash.to_ne_bytes()) -} diff --git a/src/ffi/java/mod.rs b/src/ffi/java/mod.rs index 2d6d9b3..982f116 100644 --- a/src/ffi/java/mod.rs +++ b/src/ffi/java/mod.rs @@ -2,6 +2,7 @@ pub mod client; pub mod workspace; pub mod cursor; pub mod buffer; +pub mod utils; lazy_static::lazy_static! { pub(crate) static ref RT: tokio::runtime::Runtime = tokio::runtime::Runtime::new().expect("could not create tokio runtime"); diff --git a/src/ffi/java/utils.rs b/src/ffi/java/utils.rs new file mode 100644 index 0000000..ef421c4 --- /dev/null +++ b/src/ffi/java/utils.rs @@ -0,0 +1,17 @@ +use jni::{objects::{JClass, JString}, sys::jlong, JNIEnv}; + +use super::JExceptable; + +/// Calculates the XXH3 hash for a given String. +#[no_mangle] +pub extern "system" fn Java_mp_code_Utils_hash<'local>( + mut env: JNIEnv, + _class: JClass<'local>, + content: JString<'local>, +) -> jlong { + let content: String = env.get_string(&content) + .map(|s| s.into()) + .jexcept(&mut env); + let hash = crate::ext::hash(content.as_bytes()); + i64::from_ne_bytes(hash.to_ne_bytes()) +}