Skip to content

Instantly share code, notes, and snippets.

@jollyjoker992
Last active June 2, 2025 04:03
Show Gist options
  • Save jollyjoker992/c8fddd4728f28ae10915de7bb8acee66 to your computer and use it in GitHub Desktop.
Save jollyjoker992/c8fddd4728f28ae10915de7bb8acee66 to your computer and use it in GitHub Desktop.
Jni with C++
#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