Skip to content

Commit 065a7e1

Browse files
committed
refactor(ffi): split jni_bridge.rs into a module directory
Replaces the single `jni_bridge.rs` file with a `jni_bridge/` directory containing focused sub-modules: - `mod.rs` — re-exports only, no logic - `core.rs` — database lifecycle and core JNI functions - `array.rs` — JNI bindings for the array engine Follows the project convention that mod.rs files contain only `pub mod` and `pub use` declarations.
1 parent 2c03a7a commit 065a7e1

3 files changed

Lines changed: 312 additions & 4 deletions

File tree

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
//! JNI entry points for the array engine.
2+
//!
3+
//! Byte arrays carry zerompk-encoded payloads. The Kotlin layer
4+
//! serialises / deserialises using the same zerompk codec.
5+
6+
use jni::JNIEnv;
7+
use jni::objects::{JByteArray, JObject, JString};
8+
use jni::sys::{jbyteArray, jint, jlong};
9+
10+
use super::super::{NODEDB_ERR_FAILED, NODEDB_OK};
11+
use super::core::get_handle;
12+
13+
fn jbytearray_to_vec(env: &mut JNIEnv, arr: &JByteArray) -> Option<Vec<u8>> {
14+
let len = env.get_array_length(arr).ok()? as usize;
15+
let mut buf = vec![0i8; len];
16+
env.get_byte_array_region(arr, 0, &mut buf).ok()?;
17+
Some(buf.into_iter().map(|b| b as u8).collect())
18+
}
19+
20+
/// Create an ND sparse array.
21+
///
22+
/// `schema_msgpack` — zerompk-encoded `ArraySchema`.
23+
#[unsafe(no_mangle)]
24+
pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_nativeArrayCreate(
25+
mut env: JNIEnv,
26+
_obj: JObject,
27+
handle: jlong,
28+
name: JString,
29+
schema_msgpack: JByteArray,
30+
) -> jint {
31+
let Some(h) = get_handle(handle) else {
32+
return NODEDB_ERR_FAILED;
33+
};
34+
let name_str: String = match env.get_string(&name) {
35+
Ok(s) => s.into(),
36+
Err(_) => return NODEDB_ERR_FAILED,
37+
};
38+
let Some(schema_bytes) = jbytearray_to_vec(&mut env, &schema_msgpack) else {
39+
return NODEDB_ERR_FAILED;
40+
};
41+
let schema = match zerompk::from_msgpack::<nodedb_array::schema::ArraySchema>(&schema_bytes) {
42+
Ok(s) => s,
43+
Err(_) => return NODEDB_ERR_FAILED,
44+
};
45+
46+
match h.db.create_array(&name_str, schema) {
47+
Ok(()) => NODEDB_OK,
48+
Err(_) => NODEDB_ERR_FAILED,
49+
}
50+
}
51+
52+
/// Write a cell into array `name` at `coord`.
53+
///
54+
/// `coord_msgpack` — zerompk-encoded `Vec<CoordValue>`.
55+
/// `payload_msgpack` — zerompk-encoded `Vec<CellValue>`.
56+
#[unsafe(no_mangle)]
57+
pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_nativeArrayPutCell(
58+
mut env: JNIEnv,
59+
_obj: JObject,
60+
handle: jlong,
61+
name: JString,
62+
coord_msgpack: JByteArray,
63+
payload_msgpack: JByteArray,
64+
valid_from_ms: jlong,
65+
valid_until_ms: jlong,
66+
) -> jint {
67+
let Some(h) = get_handle(handle) else {
68+
return NODEDB_ERR_FAILED;
69+
};
70+
let name_str: String = match env.get_string(&name) {
71+
Ok(s) => s.into(),
72+
Err(_) => return NODEDB_ERR_FAILED,
73+
};
74+
let Some(coord_bytes) = jbytearray_to_vec(&mut env, &coord_msgpack) else {
75+
return NODEDB_ERR_FAILED;
76+
};
77+
let Some(payload_bytes) = jbytearray_to_vec(&mut env, &payload_msgpack) else {
78+
return NODEDB_ERR_FAILED;
79+
};
80+
let coord = match zerompk::from_msgpack::<Vec<nodedb_array::types::coord::value::CoordValue>>(
81+
&coord_bytes,
82+
) {
83+
Ok(c) => c,
84+
Err(_) => return NODEDB_ERR_FAILED,
85+
};
86+
let attrs = match zerompk::from_msgpack::<Vec<nodedb_array::types::cell_value::value::CellValue>>(
87+
&payload_bytes,
88+
) {
89+
Ok(a) => a,
90+
Err(_) => return NODEDB_ERR_FAILED,
91+
};
92+
93+
let system_from_ms = std::time::SystemTime::now()
94+
.duration_since(std::time::UNIX_EPOCH)
95+
.map(|d| d.as_millis() as i64)
96+
.unwrap_or(0);
97+
98+
match h.db.array_put_cell(
99+
&name_str,
100+
coord,
101+
attrs,
102+
system_from_ms,
103+
valid_from_ms,
104+
valid_until_ms,
105+
) {
106+
Ok(()) => NODEDB_OK,
107+
Err(_) => NODEDB_ERR_FAILED,
108+
}
109+
}
110+
111+
/// Slice query returning zerompk-encoded `Vec<CellPayload>` as a byte array.
112+
///
113+
/// `ranges_msgpack` — zerompk-encoded `Vec<Option<DimRange>>`.
114+
/// `as_of_ms` — system-time AS-OF; pass `i64::MAX` for current live snapshot.
115+
#[unsafe(no_mangle)]
116+
pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_nativeArraySlice(
117+
mut env: JNIEnv,
118+
_obj: JObject,
119+
handle: jlong,
120+
name: JString,
121+
ranges_msgpack: JByteArray,
122+
as_of_ms: jlong,
123+
) -> jbyteArray {
124+
let h = match get_handle(handle) {
125+
Some(h) => h,
126+
None => return std::ptr::null_mut(),
127+
};
128+
let name_str: String = match env.get_string(&name) {
129+
Ok(s) => s.into(),
130+
Err(_) => return std::ptr::null_mut(),
131+
};
132+
let Some(ranges_bytes) = jbytearray_to_vec(&mut env, &ranges_msgpack) else {
133+
return std::ptr::null_mut();
134+
};
135+
let ranges = match zerompk::from_msgpack::<Vec<Option<nodedb_array::query::slice::DimRange>>>(
136+
&ranges_bytes,
137+
) {
138+
Ok(r) => r,
139+
Err(_) => return std::ptr::null_mut(),
140+
};
141+
142+
let as_of = if as_of_ms == i64::MAX {
143+
None
144+
} else {
145+
Some(as_of_ms)
146+
};
147+
148+
let cells = match h.db.array_slice(&name_str, ranges, as_of) {
149+
Ok(c) => c,
150+
Err(_) => return std::ptr::null_mut(),
151+
};
152+
let encoded = match zerompk::to_msgpack_vec(&cells) {
153+
Ok(b) => b,
154+
Err(_) => return std::ptr::null_mut(),
155+
};
156+
let signed: Vec<i8> = encoded.into_iter().map(|b| b as i8).collect();
157+
match env.new_byte_array(signed.len() as i32) {
158+
Ok(arr) => {
159+
if env.set_byte_array_region(&arr, 0, &signed).is_err() {
160+
return std::ptr::null_mut();
161+
}
162+
arr.into_raw()
163+
}
164+
Err(_) => std::ptr::null_mut(),
165+
}
166+
}
167+
168+
/// Read a single cell payload as zerompk-encoded `CellPayload`, or null if not found.
169+
///
170+
/// `coord_msgpack` — zerompk-encoded `Vec<CoordValue>`.
171+
/// `as_of_ms` — system-time AS-OF; pass `i64::MAX` for current live snapshot.
172+
#[unsafe(no_mangle)]
173+
pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_nativeArrayReadCoord(
174+
mut env: JNIEnv,
175+
_obj: JObject,
176+
handle: jlong,
177+
name: JString,
178+
coord_msgpack: JByteArray,
179+
as_of_ms: jlong,
180+
) -> jbyteArray {
181+
let h = match get_handle(handle) {
182+
Some(h) => h,
183+
None => return std::ptr::null_mut(),
184+
};
185+
let name_str: String = match env.get_string(&name) {
186+
Ok(s) => s.into(),
187+
Err(_) => return std::ptr::null_mut(),
188+
};
189+
let Some(coord_bytes) = jbytearray_to_vec(&mut env, &coord_msgpack) else {
190+
return std::ptr::null_mut();
191+
};
192+
let coord = match zerompk::from_msgpack::<Vec<nodedb_array::types::coord::value::CoordValue>>(
193+
&coord_bytes,
194+
) {
195+
Ok(c) => c,
196+
Err(_) => return std::ptr::null_mut(),
197+
};
198+
199+
let as_of = if as_of_ms == i64::MAX {
200+
None
201+
} else {
202+
Some(as_of_ms)
203+
};
204+
205+
let cell = match h.db.array_read_coord(&name_str, &coord, as_of) {
206+
Ok(c) => c,
207+
Err(_) => return std::ptr::null_mut(),
208+
};
209+
let Some(payload) = cell else {
210+
return std::ptr::null_mut();
211+
};
212+
let encoded = match zerompk::to_msgpack_vec(&payload) {
213+
Ok(b) => b,
214+
Err(_) => return std::ptr::null_mut(),
215+
};
216+
let signed: Vec<i8> = encoded.into_iter().map(|b| b as i8).collect();
217+
match env.new_byte_array(signed.len() as i32) {
218+
Ok(arr) => {
219+
if env.set_byte_array_region(&arr, 0, &signed).is_err() {
220+
return std::ptr::null_mut();
221+
}
222+
arr.into_raw()
223+
}
224+
Err(_) => std::ptr::null_mut(),
225+
}
226+
}
227+
228+
/// Soft-delete a cell (tombstone at current system time).
229+
///
230+
/// `coord_msgpack` — zerompk-encoded `Vec<CoordValue>`.
231+
#[unsafe(no_mangle)]
232+
pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_nativeArrayDeleteCell(
233+
mut env: JNIEnv,
234+
_obj: JObject,
235+
handle: jlong,
236+
name: JString,
237+
coord_msgpack: JByteArray,
238+
) -> jint {
239+
let Some(h) = get_handle(handle) else {
240+
return NODEDB_ERR_FAILED;
241+
};
242+
let name_str: String = match env.get_string(&name) {
243+
Ok(s) => s.into(),
244+
Err(_) => return NODEDB_ERR_FAILED,
245+
};
246+
let Some(coord_bytes) = jbytearray_to_vec(&mut env, &coord_msgpack) else {
247+
return NODEDB_ERR_FAILED;
248+
};
249+
let coord = match zerompk::from_msgpack::<Vec<nodedb_array::types::coord::value::CoordValue>>(
250+
&coord_bytes,
251+
) {
252+
Ok(c) => c,
253+
Err(_) => return NODEDB_ERR_FAILED,
254+
};
255+
256+
let system_from_ms = std::time::SystemTime::now()
257+
.duration_since(std::time::UNIX_EPOCH)
258+
.map(|d| d.as_millis() as i64)
259+
.unwrap_or(0);
260+
261+
match h.db.array_delete_cell(&name_str, coord, system_from_ms) {
262+
Ok(()) => NODEDB_OK,
263+
Err(_) => NODEDB_ERR_FAILED,
264+
}
265+
}
266+
267+
/// GDPR erasure: permanently remove cell content.
268+
///
269+
/// `coord_msgpack` — zerompk-encoded `Vec<CoordValue>`.
270+
#[unsafe(no_mangle)]
271+
pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_nativeArrayGdprEraseCell(
272+
mut env: JNIEnv,
273+
_obj: JObject,
274+
handle: jlong,
275+
name: JString,
276+
coord_msgpack: JByteArray,
277+
) -> jint {
278+
let Some(h) = get_handle(handle) else {
279+
return NODEDB_ERR_FAILED;
280+
};
281+
let name_str: String = match env.get_string(&name) {
282+
Ok(s) => s.into(),
283+
Err(_) => return NODEDB_ERR_FAILED,
284+
};
285+
let Some(coord_bytes) = jbytearray_to_vec(&mut env, &coord_msgpack) else {
286+
return NODEDB_ERR_FAILED;
287+
};
288+
let coord = match zerompk::from_msgpack::<Vec<nodedb_array::types::coord::value::CoordValue>>(
289+
&coord_bytes,
290+
) {
291+
Ok(c) => c,
292+
Err(_) => return NODEDB_ERR_FAILED,
293+
};
294+
295+
let system_from_ms = std::time::SystemTime::now()
296+
.duration_since(std::time::UNIX_EPOCH)
297+
.map(|d| d.as_millis() as i64)
298+
.unwrap_or(0);
299+
300+
match h.db.array_gdpr_erase_cell(&name_str, coord, system_from_ms) {
301+
Ok(()) => NODEDB_OK,
302+
Err(_) => NODEDB_ERR_FAILED,
303+
}
304+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use jni::objects::JFloatArray;
77
use jni::objects::{JClass, JObject, JString};
88
use jni::sys::{jint, jlong, jstring};
99

10-
use super::{NODEDB_ERR_FAILED, NODEDB_OK, NodeDbHandle};
10+
use super::super::{NODEDB_ERR_FAILED, NODEDB_OK, NodeDbHandle};
1111

12-
fn get_handle(ptr: jlong) -> Option<&'static NodeDbHandle> {
12+
pub(super) fn get_handle(ptr: jlong) -> Option<&'static NodeDbHandle> {
1313
if ptr == 0 {
1414
return None;
1515
}
@@ -31,7 +31,7 @@ pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_00024Companion_nativeOpen
3131
Ok(c) => c,
3232
Err(_) => return 0,
3333
};
34-
let handle = unsafe { super::nodedb_open(path_c.as_ptr(), peer_id as u64) };
34+
let handle = unsafe { super::super::nodedb_open(path_c.as_ptr(), peer_id as u64) };
3535
handle as jlong
3636
}
3737

@@ -42,7 +42,7 @@ pub extern "system" fn Java_com_nodedb_lite_NodeDbLite_nativeClose(
4242
handle: jlong,
4343
) {
4444
if handle != 0 {
45-
unsafe { super::nodedb_close(handle as *mut NodeDbHandle) };
45+
unsafe { super::super::nodedb_close(handle as *mut NodeDbHandle) };
4646
}
4747
}
4848

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! JNI bridge — Kotlin/Android native method implementations.
2+
3+
pub mod array;
4+
pub mod core;

0 commit comments

Comments
 (0)