Skip to content

Commit

Permalink
feat(java): expose hash function, use OptionalLong in TextChange
Browse files Browse the repository at this point in the history
  • Loading branch information
zaaarf committed Aug 15, 2024
1 parent 8b704fa commit 0d3af40
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 62 deletions.
10 changes: 10 additions & 0 deletions dist/java/src/mp/code/Utils.java
Original file line number Diff line number Diff line change
@@ -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);
}
12 changes: 4 additions & 8 deletions dist/java/src/mp/code/data/TextChange.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
64 changes: 10 additions & 54 deletions src/ffi/java/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,71 +63,27 @@ fn recv_jni(env: &mut JNIEnv, change: Option<crate::api::TextChange>) -> 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())
}
1 change: 1 addition & 0 deletions src/ffi/java/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
17 changes: 17 additions & 0 deletions src/ffi/java/utils.rs
Original file line number Diff line number Diff line change
@@ -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())
}

0 comments on commit 0d3af40

Please sign in to comment.