Skip to main content

ri/java/classes/
validation.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Validation Module JNI Bindings
19//!
20//! JNI bindings for Ri validation classes.
21//!
22//! ## Security
23//!
24//! This module uses a secure pointer registry to prevent:
25//! - Use-After-Free: Pointers are validated before access
26//! - Double-Free: Free operations are idempotent
27//! - Invalid Pointer Dereference: All pointers are tracked
28
29use jni::JNIEnv;
30use jni::objects::{JClass, JString};
31use jni::sys::{jlong, jboolean, jint, jstring};
32use std::sync::Arc;
33use dashmap::DashSet;
34use std::any::TypeId;
35
36use crate::validation::{
37    RiValidationModule, RiValidationError, RiValidationResult,
38    RiSanitizer, RiSanitizationConfig, RiSchemaValidator,
39};
40use crate::java::exception::check_not_null;
41
42lazy_static::lazy_static! {
43    static ref VALIDATION_MODULE_REGISTRY: DashSet<usize> = DashSet::new();
44    static ref VALIDATION_ERROR_REGISTRY: DashSet<usize> = DashSet::new();
45    static ref VALIDATION_RESULT_REGISTRY: DashSet<usize> = DashSet::new();
46    static ref SANITIZATION_CONFIG_REGISTRY: DashSet<usize> = DashSet::new();
47    static ref SANITIZER_REGISTRY: DashSet<usize> = DashSet::new();
48    static ref SCHEMA_VALIDATOR_REGISTRY: DashSet<usize> = DashSet::new();
49}
50
51fn register_ptr(registry: &DashSet<usize>, ptr: usize) {
52    registry.insert(ptr);
53}
54
55fn unregister_ptr(registry: &DashSet<usize>, ptr: usize) -> bool {
56    registry.remove(&ptr).is_some()
57}
58
59fn is_valid_ptr(registry: &DashSet<usize>, ptr: usize) -> bool {
60    ptr != 0 && registry.contains(&ptr)
61}
62
63// =============================================================================
64// RiValidationModule JNI Bindings
65// =============================================================================
66
67#[no_mangle]
68pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationModule_new0(
69    _env: JNIEnv,
70    _class: JClass,
71) -> jlong {
72    let module = Box::new(RiValidationModule::new());
73    let ptr = Box::into_raw(module) as jlong;
74    register_ptr(&VALIDATION_MODULE_REGISTRY, ptr as usize);
75    ptr
76}
77
78#[no_mangle]
79pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationModule_validate0(
80    mut env: JNIEnv,
81    _class: JClass,
82    ptr: jlong,
83    data: JString,
84) -> jlong {
85    if !is_valid_ptr(&VALIDATION_MODULE_REGISTRY, ptr as usize) {
86        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiValidationModule pointer")
87            .unwrap_or(());
88        return 0;
89    }
90
91    let data_str: String = match env.get_string(&data) {
92        Ok(s) => s.into(),
93        Err(_) => {
94            env.throw_new("java/lang/IllegalArgumentException", "Failed to get data string")
95                .unwrap_or(());
96            return 0;
97        }
98    };
99
100    let module = unsafe { &*(ptr as *const RiValidationModule) };
101    let result = Box::new(module.validate(&data_str));
102    let result_ptr = Box::into_raw(result) as jlong;
103    register_ptr(&VALIDATION_RESULT_REGISTRY, result_ptr as usize);
104    result_ptr
105}
106
107#[no_mangle]
108pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationModule_free0(
109    _env: JNIEnv,
110    _class: JClass,
111    ptr: jlong,
112) {
113    if unregister_ptr(&VALIDATION_MODULE_REGISTRY, ptr as usize) {
114        unsafe {
115            let _ = Box::from_raw(ptr as *mut RiValidationModule);
116        }
117    }
118}
119
120// =============================================================================
121// RiValidationError JNI Bindings
122// =============================================================================
123
124#[no_mangle]
125pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationError_getField0<'local>(
126    mut env: JNIEnv<'local>,
127    _class: JClass<'local>,
128    ptr: jlong,
129) -> jstring {
130    if !is_valid_ptr(&VALIDATION_ERROR_REGISTRY, ptr as usize) {
131        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiValidationError pointer")
132            .unwrap_or(());
133        return std::ptr::null_mut();
134    }
135
136    let error = unsafe { &*(ptr as *const RiValidationError) };
137    match env.new_string(&error.field) {
138        Ok(s) => s.into_raw(),
139        Err(_) => std::ptr::null_mut(),
140    }
141}
142
143#[no_mangle]
144pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationError_getMessage0<'local>(
145    mut env: JNIEnv<'local>,
146    _class: JClass<'local>,
147    ptr: jlong,
148) -> jstring {
149    if !is_valid_ptr(&VALIDATION_ERROR_REGISTRY, ptr as usize) {
150        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiValidationError pointer")
151            .unwrap_or(());
152        return std::ptr::null_mut();
153    }
154
155    let error = unsafe { &*(ptr as *const RiValidationError) };
156    match env.new_string(&error.message) {
157        Ok(s) => s.into_raw(),
158        Err(_) => std::ptr::null_mut(),
159    }
160}
161
162#[no_mangle]
163pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationError_getCode0<'local>(
164    mut env: JNIEnv<'local>,
165    _class: JClass<'local>,
166    ptr: jlong,
167) -> jstring {
168    if !is_valid_ptr(&VALIDATION_ERROR_REGISTRY, ptr as usize) {
169        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiValidationError pointer")
170            .unwrap_or(());
171        return std::ptr::null_mut();
172    }
173
174    let error = unsafe { &*(ptr as *const RiValidationError) };
175    match env.new_string(&error.code) {
176        Ok(s) => s.into_raw(),
177        Err(_) => std::ptr::null_mut(),
178    }
179}
180
181#[no_mangle]
182pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationError_getSeverity0(
183    mut env: JNIEnv,
184    _class: JClass,
185    ptr: jlong,
186) -> jint {
187    if !is_valid_ptr(&VALIDATION_ERROR_REGISTRY, ptr as usize) {
188        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiValidationError pointer")
189            .unwrap_or(());
190        return 0;
191    }
192
193    let error = unsafe { &*(ptr as *const RiValidationError) };
194    error.severity as jint
195}
196
197#[no_mangle]
198pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationError_free0(
199    _env: JNIEnv,
200    _class: JClass,
201    ptr: jlong,
202) {
203    if unregister_ptr(&VALIDATION_ERROR_REGISTRY, ptr as usize) {
204        unsafe {
205            let _ = Box::from_raw(ptr as *mut RiValidationError);
206        }
207    }
208}
209
210// =============================================================================
211// RiValidationResult JNI Bindings
212// =============================================================================
213
214#[no_mangle]
215pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationResult_isValid0(
216    mut env: JNIEnv,
217    _class: JClass,
218    ptr: jlong,
219) -> jboolean {
220    if !is_valid_ptr(&VALIDATION_RESULT_REGISTRY, ptr as usize) {
221        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiValidationResult pointer")
222            .unwrap_or(());
223        return 0;
224    }
225
226    let result = unsafe { &*(ptr as *const RiValidationResult) };
227    result.is_valid as jboolean
228}
229
230#[no_mangle]
231pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationResult_getErrors0(
232    mut env: JNIEnv,
233    _class: JClass,
234    ptr: jlong,
235) -> jlong {
236    if !is_valid_ptr(&VALIDATION_RESULT_REGISTRY, ptr as usize) {
237        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiValidationResult pointer")
238            .unwrap_or(());
239        return 0;
240    }
241
242    let result = unsafe { &*(ptr as *const RiValidationResult) };
243    let errors = Box::new(result.errors.clone());
244    let errors_ptr = Box::into_raw(errors) as jlong;
245    register_ptr(&VALIDATION_ERROR_REGISTRY, errors_ptr as usize);
246    errors_ptr
247}
248
249#[no_mangle]
250pub extern "system" fn Java_com_dunimd_ri_validation_RiValidationResult_free0(
251    _env: JNIEnv,
252    _class: JClass,
253    ptr: jlong,
254) {
255    if unregister_ptr(&VALIDATION_RESULT_REGISTRY, ptr as usize) {
256        unsafe {
257            let _ = Box::from_raw(ptr as *mut RiValidationResult);
258        }
259    }
260}
261
262// =============================================================================
263// RiSanitizationConfig JNI Bindings
264// =============================================================================
265
266#[no_mangle]
267pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_new0(
268    _env: JNIEnv,
269    _class: JClass,
270) -> jlong {
271    let config = Box::new(RiSanitizationConfig::default());
272    let ptr = Box::into_raw(config) as jlong;
273    register_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize);
274    ptr
275}
276
277#[no_mangle]
278pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_isTrimWhitespace0(
279    mut env: JNIEnv,
280    _class: JClass,
281    ptr: jlong,
282) -> jboolean {
283    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
284        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
285            .unwrap_or(());
286        return 0;
287    }
288
289    let config = unsafe { &*(ptr as *const RiSanitizationConfig) };
290    config.trim_whitespace as jboolean
291}
292
293#[no_mangle]
294pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_setTrimWhitespace0(
295    mut env: JNIEnv,
296    _class: JClass,
297    ptr: jlong,
298    enabled: jboolean,
299) {
300    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
301        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
302            .unwrap_or(());
303        return;
304    }
305
306    let config = unsafe { &mut *(ptr as *mut RiSanitizationConfig) };
307    config.trim_whitespace = enabled != 0;
308}
309
310#[no_mangle]
311pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_isLowercase0(
312    mut env: JNIEnv,
313    _class: JClass,
314    ptr: jlong,
315) -> jboolean {
316    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
317        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
318            .unwrap_or(());
319        return 0;
320    }
321
322    let config = unsafe { &*(ptr as *const RiSanitizationConfig) };
323    config.lowercase as jboolean
324}
325
326#[no_mangle]
327pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_setLowercase0(
328    mut env: JNIEnv,
329    _class: JClass,
330    ptr: jlong,
331    enabled: jboolean,
332) {
333    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
334        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
335            .unwrap_or(());
336        return;
337    }
338
339    let config = unsafe { &mut *(ptr as *mut RiSanitizationConfig) };
340    config.lowercase = enabled != 0;
341}
342
343#[no_mangle]
344pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_isUppercase0(
345    mut env: JNIEnv,
346    _class: JClass,
347    ptr: jlong,
348) -> jboolean {
349    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
350        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
351            .unwrap_or(());
352        return 0;
353    }
354
355    let config = unsafe { &*(ptr as *const RiSanitizationConfig) };
356    config.uppercase as jboolean
357}
358
359#[no_mangle]
360pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_setUppercase0(
361    mut env: JNIEnv,
362    _class: JClass,
363    ptr: jlong,
364    enabled: jboolean,
365) {
366    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
367        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
368            .unwrap_or(());
369        return;
370    }
371
372    let config = unsafe { &mut *(ptr as *mut RiSanitizationConfig) };
373    config.uppercase = enabled != 0;
374}
375
376#[no_mangle]
377pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_isRemoveHtmlTags0(
378    mut env: JNIEnv,
379    _class: JClass,
380    ptr: jlong,
381) -> jboolean {
382    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
383        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
384            .unwrap_or(());
385        return 0;
386    }
387
388    let config = unsafe { &*(ptr as *const RiSanitizationConfig) };
389    config.remove_html_tags as jboolean
390}
391
392#[no_mangle]
393pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_setRemoveHtmlTags0(
394    mut env: JNIEnv,
395    _class: JClass,
396    ptr: jlong,
397    enabled: jboolean,
398) {
399    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
400        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
401            .unwrap_or(());
402        return;
403    }
404
405    let config = unsafe { &mut *(ptr as *mut RiSanitizationConfig) };
406    config.remove_html_tags = enabled != 0;
407}
408
409#[no_mangle]
410pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_isEscapeSpecialChars0(
411    mut env: JNIEnv,
412    _class: JClass,
413    ptr: jlong,
414) -> jboolean {
415    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
416        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
417            .unwrap_or(());
418        return 0;
419    }
420
421    let config = unsafe { &*(ptr as *const RiSanitizationConfig) };
422    config.escape_special_chars as jboolean
423}
424
425#[no_mangle]
426pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_setEscapeSpecialChars0(
427    mut env: JNIEnv,
428    _class: JClass,
429    ptr: jlong,
430    enabled: jboolean,
431) {
432    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
433        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
434            .unwrap_or(());
435        return;
436    }
437
438    let config = unsafe { &mut *(ptr as *mut RiSanitizationConfig) };
439    config.escape_special_chars = enabled != 0;
440}
441
442#[no_mangle]
443pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizationConfig_free0(
444    _env: JNIEnv,
445    _class: JClass,
446    ptr: jlong,
447) {
448    if unregister_ptr(&SANITIZATION_CONFIG_REGISTRY, ptr as usize) {
449        unsafe {
450            let _ = Box::from_raw(ptr as *mut RiSanitizationConfig);
451        }
452    }
453}
454
455// =============================================================================
456// RiSanitizer JNI Bindings
457// =============================================================================
458
459#[no_mangle]
460pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizer_new0(
461    _env: JNIEnv,
462    _class: JClass,
463) -> jlong {
464    let sanitizer = Box::new(RiSanitizer::new());
465    let ptr = Box::into_raw(sanitizer) as jlong;
466    register_ptr(&SANITIZER_REGISTRY, ptr as usize);
467    ptr
468}
469
470#[no_mangle]
471pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizer_newWithConfig0(
472    mut env: JNIEnv,
473    _class: JClass,
474    config_ptr: jlong,
475) -> jlong {
476    if !is_valid_ptr(&SANITIZATION_CONFIG_REGISTRY, config_ptr as usize) {
477        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizationConfig pointer")
478            .unwrap_or(());
479        return 0;
480    }
481
482    let config = unsafe { &*(config_ptr as *const RiSanitizationConfig) };
483    let sanitizer = Box::new(RiSanitizer::with_config(config.clone()));
484    let ptr = Box::into_raw(sanitizer) as jlong;
485    register_ptr(&SANITIZER_REGISTRY, ptr as usize);
486    ptr
487}
488
489#[no_mangle]
490pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizer_sanitize0<'local>(
491    mut env: JNIEnv<'local>,
492    _class: JClass<'local>,
493    ptr: jlong,
494    input: JString,
495) -> jstring {
496    if !is_valid_ptr(&SANITIZER_REGISTRY, ptr as usize) {
497        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizer pointer")
498            .unwrap_or(());
499        return std::ptr::null_mut();
500    }
501
502    let input_str: String = match env.get_string(&input) {
503        Ok(s) => s.into(),
504        Err(_) => {
505            env.throw_new("java/lang/IllegalArgumentException", "Failed to get input string")
506                .unwrap_or(());
507            return std::ptr::null_mut();
508        }
509    };
510
511    let sanitizer = unsafe { &*(ptr as *const RiSanitizer) };
512    let result = sanitizer.sanitize(&input_str);
513    match env.new_string(&result) {
514        Ok(s) => s.into_raw(),
515        Err(_) => std::ptr::null_mut(),
516    }
517}
518
519#[no_mangle]
520pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizer_sanitizeEmail0<'local>(
521    mut env: JNIEnv<'local>,
522    _class: JClass<'local>,
523    ptr: jlong,
524    input: JString,
525) -> jstring {
526    if !is_valid_ptr(&SANITIZER_REGISTRY, ptr as usize) {
527        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizer pointer")
528            .unwrap_or(());
529        return std::ptr::null_mut();
530    }
531
532    let input_str: String = match env.get_string(&input) {
533        Ok(s) => s.into(),
534        Err(_) => {
535            env.throw_new("java/lang/IllegalArgumentException", "Failed to get input string")
536                .unwrap_or(());
537            return std::ptr::null_mut();
538        }
539    };
540
541    let sanitizer = unsafe { &*(ptr as *const RiSanitizer) };
542    let result = sanitizer.sanitize_email(&input_str);
543    match env.new_string(&result) {
544        Ok(s) => s.into_raw(),
545        Err(_) => std::ptr::null_mut(),
546    }
547}
548
549#[no_mangle]
550pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizer_sanitizeFilename0<'local>(
551    mut env: JNIEnv<'local>,
552    _class: JClass<'local>,
553    ptr: jlong,
554    input: JString,
555) -> jstring {
556    if !is_valid_ptr(&SANITIZER_REGISTRY, ptr as usize) {
557        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSanitizer pointer")
558            .unwrap_or(());
559        return std::ptr::null_mut();
560    }
561
562    let input_str: String = match env.get_string(&input) {
563        Ok(s) => s.into(),
564        Err(_) => {
565            env.throw_new("java/lang/IllegalArgumentException", "Failed to get input string")
566                .unwrap_or(());
567            return std::ptr::null_mut();
568        }
569    };
570
571    let sanitizer = unsafe { &*(ptr as *const RiSanitizer) };
572    let result = sanitizer.sanitize_filename(&input_str);
573    match env.new_string(&result) {
574        Ok(s) => s.into_raw(),
575        Err(_) => std::ptr::null_mut(),
576    }
577}
578
579#[no_mangle]
580pub extern "system" fn Java_com_dunimd_ri_validation_RiSanitizer_free0(
581    _env: JNIEnv,
582    _class: JClass,
583    ptr: jlong,
584) {
585    if unregister_ptr(&SANITIZER_REGISTRY, ptr as usize) {
586        unsafe {
587            let _ = Box::from_raw(ptr as *mut RiSanitizer);
588        }
589    }
590}
591
592// =============================================================================
593// RiSchemaValidator JNI Bindings
594// =============================================================================
595
596#[no_mangle]
597pub extern "system" fn Java_com_dunimd_ri_validation_RiSchemaValidator_new0(
598    mut env: JNIEnv,
599    _class: JClass,
600    schema_json: JString,
601) -> jlong {
602    let schema_str: String = match env.get_string(&schema_json) {
603        Ok(s) => s.into(),
604        Err(_) => {
605            env.throw_new("java/lang/IllegalArgumentException", "Failed to get schema string")
606                .unwrap_or(());
607            return 0;
608        }
609    };
610
611    match RiSchemaValidator::new(&schema_str) {
612        Ok(validator) => {
613            let ptr = Box::into_raw(Box::new(validator)) as jlong;
614            register_ptr(&SCHEMA_VALIDATOR_REGISTRY, ptr as usize);
615            ptr
616        }
617        Err(e) => {
618            env.throw_new("java/lang/IllegalArgumentException", &format!("Failed to create validator: {}", e))
619                .unwrap_or(());
620            0
621        }
622    }
623}
624
625#[no_mangle]
626pub extern "system" fn Java_com_dunimd_ri_validation_RiSchemaValidator_validate0(
627    mut env: JNIEnv,
628    _class: JClass,
629    ptr: jlong,
630    data_json: JString,
631) -> jlong {
632    if !is_valid_ptr(&SCHEMA_VALIDATOR_REGISTRY, ptr as usize) {
633        env.throw_new("java/lang/IllegalStateException", "Invalid or freed RiSchemaValidator pointer")
634            .unwrap_or(());
635        return 0;
636    }
637
638    let data_str: String = match env.get_string(&data_json) {
639        Ok(s) => s.into(),
640        Err(_) => {
641            env.throw_new("java/lang/IllegalArgumentException", "Failed to get data string")
642                .unwrap_or(());
643            return 0;
644        }
645    };
646
647    let validator = unsafe { &*(ptr as *const RiSchemaValidator) };
648    let result = Box::new(validator.validate(&data_str));
649    let result_ptr = Box::into_raw(result) as jlong;
650    register_ptr(&VALIDATION_RESULT_REGISTRY, result_ptr as usize);
651    result_ptr
652}
653
654#[no_mangle]
655pub extern "system" fn Java_com_dunimd_ri_validation_RiSchemaValidator_free0(
656    _env: JNIEnv,
657    _class: JClass,
658    ptr: jlong,
659) {
660    if unregister_ptr(&SCHEMA_VALIDATOR_REGISTRY, ptr as usize) {
661        unsafe {
662            let _ = Box::from_raw(ptr as *mut RiSchemaValidator);
663        }
664    }
665}