Last active
June 2, 2025 04:03
-
-
Save jollyjoker992/c8fddd4728f28ae10915de7bb8acee66 to your computer and use it in GitHub Desktop.
Jni with C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <jni.h> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <iostream> | |
| #include <set> | |
| #include <vector> | |
| #include "../../../deps/bc-ur/src/bc-ur.hpp" | |
| using namespace ur; | |
| // ============== JNI utilities ============== | |
| // @reference: https://github.com/facebook/rocksdb/blob/master/java/rocksjni/portal.h | |
| class JavaClass { | |
| public: | |
| /** | |
| * Gets and initializes a Java Class | |
| * | |
| * @param env A pointer to the Java environment | |
| * @param jclazz_name The fully qualified JNI name of the Java Class | |
| * e.g. "java/lang/String" | |
| * | |
| * @return The Java Class or nullptr if one of the | |
| * ClassFormatError, ClassCircularityError, NoClassDefFoundError, | |
| * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown | |
| */ | |
| static jclass getJClass(JNIEnv* env, const char* jclazz_name) { | |
| jclass jclazz = env->FindClass(jclazz_name); | |
| assert(jclazz != nullptr); | |
| return jclazz; | |
| } | |
| }; | |
| // Java Exception template | |
| template<class DERIVED> class JavaException : public JavaClass { | |
| public: | |
| /** | |
| * Create and throw a java exception with the provided message | |
| * | |
| * @param env A pointer to the Java environment | |
| * @param msg The message for the exception | |
| * | |
| * @return true if an exception was thrown, false otherwise | |
| */ | |
| static bool ThrowNew(JNIEnv* env, const std::string& msg) { | |
| jclass jclazz = DERIVED::getJClass(env); | |
| if(jclazz == nullptr) { | |
| // exception occurred accessing class | |
| std::cerr << "JavaException::ThrowNew - Error: unexpected exception!" << std::endl; | |
| return env->ExceptionCheck(); | |
| } | |
| const jint rs = env->ThrowNew(jclazz, msg.c_str()); | |
| if(rs != JNI_OK) { | |
| // exception could not be thrown | |
| std::cerr << "JavaException::ThrowNew - Fatal: could not throw exception!" << std::endl; | |
| return env->ExceptionCheck(); | |
| } | |
| return true; | |
| } | |
| }; | |
| // The portal class for java.lang.IllegalArgumentException | |
| class IllegalArgumentExceptionJni : | |
| public JavaException<IllegalArgumentExceptionJni> { | |
| public: | |
| /** | |
| * Get the Java Class java.lang.IllegalArgumentException | |
| * | |
| * @param env A pointer to the Java environment | |
| * | |
| * @return The Java Class or nullptr if one of the | |
| * ClassFormatError, ClassCircularityError, NoClassDefFoundError, | |
| * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown | |
| */ | |
| static jclass getJClass(JNIEnv* env) { | |
| return JavaException::getJClass(env, "java/lang/IllegalArgumentException"); | |
| } | |
| /** | |
| * Create and throw a Java IllegalArgumentException with the provided error message | |
| * | |
| * If s.ok() == true, then this function will not throw any exception. | |
| * | |
| * @param env A pointer to the Java environment | |
| * @param s The error message | |
| * | |
| * @return true if an exception was thrown, false otherwise | |
| */ | |
| static bool ThrowNew(JNIEnv* env, const std::string& s) { | |
| // get the IllegalArgumentException class | |
| jclass jclazz = getJClass(env); | |
| if(jclazz == nullptr) { | |
| // exception occurred accessing class | |
| std::cerr << "IllegalArgumentExceptionJni::ThrowNew/class - Error: unexpected exception!" << std::endl; | |
| return env->ExceptionCheck(); | |
| } | |
| return JavaException::ThrowNew(env, s); | |
| } | |
| }; | |
| // ============= Primitive types utilities ============== | |
| class PrimitiveJni : public JavaClass { | |
| public: | |
| static std::string copyStdString(JNIEnv* env, jstring js, | |
| jboolean* has_exception) { | |
| const char *utf = env->GetStringUTFChars(js, nullptr); | |
| if(utf == nullptr) { | |
| // exception thrown: OutOfMemoryError | |
| env->ExceptionCheck(); | |
| *has_exception = JNI_TRUE; | |
| return std::string(); | |
| } else if(env->ExceptionCheck()) { | |
| // exception thrown | |
| env->ReleaseStringUTFChars(js, utf); | |
| *has_exception = JNI_TRUE; | |
| return std::string(); | |
| } | |
| std::string name(utf); | |
| env->ReleaseStringUTFChars(js, utf); | |
| *has_exception = JNI_FALSE; | |
| return name; | |
| } | |
| static jstring toJavaString(JNIEnv* env, const std::string* string, | |
| const bool treat_empty_as_null = false) { | |
| if (string == nullptr) { | |
| return nullptr; | |
| } | |
| if (treat_empty_as_null && string->empty()) { | |
| return nullptr; | |
| } | |
| return env->NewStringUTF(string->c_str()); | |
| } | |
| }; | |
| // ============= Portal for specific object =================== | |
| // java/util/ArrayList | |
| class ArrayListJni : public JavaClass { | |
| public: | |
| static jclass getJClass(JNIEnv* env) { | |
| return JavaClass::getJClass(env, "java/util/ArrayList"); | |
| } | |
| static jmethodID getSize(JNIEnv* env) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| static jmethodID mid = | |
| env->GetMethodID(jclazz, "size", "()I"); | |
| assert(mid != nullptr); | |
| return mid; | |
| } | |
| static jmethodID getValue(JNIEnv* env) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| static jmethodID mid = | |
| env->GetMethodID(jclazz, "get", "(I)Ljava/lang/Object;"); | |
| assert(mid != nullptr); | |
| return mid; | |
| } | |
| static jmethodID addValue(JNIEnv* env) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| static jmethodID mid = | |
| env->GetMethodID(jclazz, "add", "(Ljava/lang/Object;)Z"); | |
| assert(mid != nullptr); | |
| return mid; | |
| } | |
| static jobject toJIntArrayList(JNIEnv* env, const std::vector<uint8_t>& value) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| jmethodID mid = env->GetMethodID(jclazz, "<init>", "(I)V"); | |
| assert(mid != nullptr); | |
| static jobject result = env->NewObject(jclazz, mid, value.size()); | |
| if (env->ExceptionCheck()) { | |
| if (result != nullptr) { | |
| env->DeleteLocalRef(result); | |
| } | |
| return nullptr; | |
| } | |
| for (uint8_t u: value) { | |
| auto element = (jint)u; | |
| env->CallBooleanMethod(result, addValue(env), element); | |
| if (env->ExceptionCheck()) { | |
| if (result != nullptr) { | |
| env->DeleteLocalRef(result); | |
| } | |
| return nullptr; | |
| } | |
| } | |
| return result; | |
| } | |
| static std::vector<uint8_t> toCUInt8StdVt(JNIEnv* env, jobject obj) { | |
| jint len = env->CallIntMethod(obj, getSize(env)); | |
| static std::vector<uint8_t> result; | |
| result.reserve(len); | |
| for (jint i=0; i<len; i++) { | |
| auto* element = (uint8_t*)env->CallObjectMethod(obj, getValue(env), i); | |
| result.emplace_back(*element); | |
| } | |
| return result; | |
| } | |
| static jobject toJLongArrayList(JNIEnv* env, const std::set<size_t>& value) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| jmethodID mid = env->GetMethodID(jclazz, "<init>", "(I)V"); | |
| assert(mid != nullptr); | |
| static jobject result = env->NewObject(jclazz, mid, value.size()); | |
| for (uint8_t u: value) { | |
| auto element = (jlong)u; | |
| env->CallBooleanMethod(result, addValue(env), element); | |
| if (env->ExceptionCheck()) { | |
| if (result != nullptr) { | |
| env->DeleteLocalRef(result); | |
| } | |
| return nullptr; | |
| } | |
| } | |
| return result; | |
| } | |
| static std::set<size_t> toCSizetStdSet(JNIEnv* env, jobject obj) { | |
| jint len = env->CallIntMethod(obj, getSize(env)); | |
| static std::set<size_t> result; | |
| for (jint i=0; i<len; i++) { | |
| auto* element = (size_t*)env->CallObjectMethod(obj, getValue(env), i); | |
| result.insert(*element); | |
| } | |
| return result; | |
| } | |
| }; | |
| // com.bc.bcursample.UR | |
| class URJni : public JavaClass { | |
| public: | |
| static jclass getJClass(JNIEnv* env) { | |
| return JavaClass::getJClass(env, "com/bc/bcursample/UR"); | |
| } | |
| static jmethodID getCbor(JNIEnv* env) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| static jmethodID mid = | |
| env->GetMethodID(jclazz, "getCbor", "()Ljava/util/ArrayList;"); | |
| assert(mid != nullptr); | |
| return mid; | |
| } | |
| static jmethodID getType(JNIEnv* env) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| static jmethodID mid = | |
| env->GetMethodID(jclazz, "getType", "()Ljava/lang/String;"); | |
| assert(mid != nullptr); | |
| return mid; | |
| } | |
| static jobject toJUR(JNIEnv* env, const std::string& type, ByteVector cbor) { | |
| jclass jclazz = getJClass(env); | |
| assert(jclazz != nullptr); | |
| jmethodID mid = env->GetMethodID(jclazz, "<init>", "(Ljava/lang/String;Ljava/util/ArrayList;)V"); | |
| assert(mid != nullptr); | |
| jstring jtype = PrimitiveJni::toJavaString(env, &type); | |
| jobject jcbor = ArrayListJni::toJIntArrayList(env, cbor); | |
| static jobject obj = env->NewObject(jclazz, mid, jtype, jcbor); | |
| if (env->ExceptionCheck()) { | |
| if (obj != nullptr) { | |
| env->DeleteLocalRef(obj); | |
| } | |
| } | |
| return obj; | |
| } | |
| static UR* toCUR(JNIEnv* env, jobject obj) { | |
| jstring jtype = static_cast<jstring>(env->CallObjectMethod(obj, URJni::getType(env))); | |
| jobject jcbor = env->CallObjectMethod(obj, URJni::getCbor(env)); | |
| std::vector<uint8_t> cbor = ArrayListJni::toCUInt8StdVt(env, jcbor); | |
| std::string type = PrimitiveJni::copyStdString(env, jtype, nullptr); | |
| static UR result (type, (ByteVector const&)cbor); | |
| return &result; | |
| } | |
| }; | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| JNIEXPORT jstring JNICALL | |
| Java_com_bc_bcursample_BCUR_UREncoder_1encode(JNIEnv *env, jclass clazz, jobject ur) { | |
| if (ur == nullptr) { | |
| IllegalArgumentExceptionJni::ThrowNew(env, "Java UR is null" ); | |
| return nullptr; | |
| } | |
| jstring jresult = nullptr; | |
| UR *arg1 = nullptr; | |
| std::string result; | |
| (void)env; | |
| (void)clazz; | |
| arg1 = URJni::toCUR(env, ur); | |
| result = UREncoder::encode(*arg1); | |
| jresult = env->NewStringUTF((&result)->c_str()); | |
| return jresult; | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment