foundationdb/bindings/java/fdbJNI.cpp

1355 lines
49 KiB
C++
Raw Normal View History

2017-05-26 04:48:44 +08:00
/*
* fdbJNI.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
2017-05-26 04:48:44 +08:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
2017-05-26 04:48:44 +08:00
* http://www.apache.org/licenses/LICENSE-2.0
*
2017-05-26 04:48:44 +08:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <string.h>
2021-05-05 05:32:52 +08:00
#define FDB_API_VERSION 710
2017-05-26 04:48:44 +08:00
#include <foundationdb/fdb_c.h>
#define JNI_NULL nullptr
2017-05-26 04:48:44 +08:00
#if defined(__GNUG__)
#undef JNIEXPORT
#define JNIEXPORT __attribute__((visibility("default")))
2017-05-26 04:48:44 +08:00
#endif
static JavaVM* g_jvm = nullptr;
static thread_local JNIEnv* g_thread_jenv =
nullptr; // Defined for the network thread once it is running, and for any thread that has called registerCallback
2019-01-11 05:13:21 +08:00
static thread_local jmethodID g_IFutureCallback_call_methodID = JNI_NULL;
static thread_local bool is_external = false;
static jclass range_result_summary_class;
static jclass range_result_class;
static jclass string_class;
2020-06-25 06:17:57 +08:00
static jclass key_array_result_class;
static jmethodID key_array_result_init;
static jmethodID range_result_init;
static jmethodID range_result_summary_init;
void detachIfExternalThread(void* ignore) {
if (is_external && g_thread_jenv != nullptr) {
2019-01-11 04:28:14 +08:00
g_thread_jenv = nullptr;
2019-01-11 05:13:21 +08:00
g_IFutureCallback_call_methodID = JNI_NULL;
g_jvm->DetachCurrentThread();
}
}
2017-05-26 04:48:44 +08:00
void throwOutOfMem(JNIEnv* jenv) {
const char* className = "java/lang/OutOfMemoryError";
jclass illegalArgClass = jenv->FindClass(className);
2017-05-26 04:48:44 +08:00
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return;
if (jenv->ThrowNew(illegalArgClass, nullptr) != 0) {
if (!jenv->ExceptionOccurred()) {
2017-05-26 04:48:44 +08:00
jenv->FatalError("Could not throw OutOfMemoryError");
} else {
// This means that an exception is pending. We do not know what it is, but are sure
// that control flow will include throwing that exception into the calling Java code.
}
}
}
static jthrowable getThrowable(JNIEnv* jenv, fdb_error_t e, const char* msg = nullptr) {
jclass excepClass = jenv->FindClass("com/apple/foundationdb/FDBException");
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return JNI_NULL;
jmethodID excepCtor = jenv->GetMethodID(excepClass, "<init>", "(Ljava/lang/String;I)V");
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return JNI_NULL;
const char* fdb_message = msg ? msg : fdb_get_error(e);
2017-05-26 04:48:44 +08:00
jstring m = jenv->NewStringUTF(fdb_message);
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return JNI_NULL;
jthrowable t = (jthrowable)jenv->NewObject(excepClass, excepCtor, m, e);
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return JNI_NULL;
return t;
}
void throwNamedException(JNIEnv* jenv, const char* class_full_name, const char* message) {
jclass exceptionClass = jenv->FindClass(class_full_name);
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return;
if (jenv->ThrowNew(exceptionClass, message) != 0) {
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return;
jenv->FatalError("FDB: Error throwing exception");
}
}
void throwRuntimeEx(JNIEnv* jenv, const char* message) {
throwNamedException(jenv, "java/lang/RuntimeException", message);
2017-05-26 04:48:44 +08:00
}
void throwParamNotNull(JNIEnv* jenv) {
throwNamedException(jenv, "java/lang/IllegalArgumentException", "Argument cannot be null");
2017-05-26 04:48:44 +08:00
}
#ifdef __cplusplus
extern "C" {
#endif
// If the methods are not found, exceptions are thrown and this will return false.
// Returns TRUE on success, false otherwise.
static bool findCallbackMethods(JNIEnv* jenv) {
2017-05-26 04:48:44 +08:00
jclass cls = jenv->FindClass("java/lang/Runnable");
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return false;
g_IFutureCallback_call_methodID = jenv->GetMethodID(cls, "run", "()V");
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return false;
return true;
}
static void callCallback(FDBFuture* f, void* data) {
2019-01-11 05:13:21 +08:00
if (g_thread_jenv == nullptr) {
// We are on an external thread and must attach to the JVM.
// The shutdown hook will later detach this thread.
is_external = true;
if (g_jvm != nullptr && g_jvm->AttachCurrentThreadAsDaemon((void**)&g_thread_jenv, nullptr) == JNI_OK) {
if (!findCallbackMethods(g_thread_jenv)) {
g_thread_jenv->FatalError("FDB: Could not find callback method.\n");
}
} else {
// Can't call FatalError, because we don't have a pointer to the jenv...
// There will be a segmentation fault from the attempt to call the callback method.
fprintf(stderr, "FDB: Could not attach external client thread to the JVM as daemon.\n");
}
}
jobject callback = (jobject)data;
g_thread_jenv->CallVoidMethod(callback, g_IFutureCallback_call_methodID);
g_thread_jenv->DeleteGlobalRef(callback);
}
2017-05-26 04:48:44 +08:00
// Attempts to throw 't', attempts to shut down the JVM if this fails.
void safeThrow(JNIEnv* jenv, jthrowable t) {
if (jenv->Throw(t) != 0) {
2017-05-26 04:48:44 +08:00
jenv->FatalError("FDB: Unable to throw exception");
}
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_NativeFuture_Future_1registerCallback(JNIEnv* jenv,
jobject cls,
jlong future,
jobject callback) {
2017-05-26 04:48:44 +08:00
// SOMEDAY: Do this on module load instead. Can we cache method ids across threads?
if (!g_IFutureCallback_call_methodID) {
if (!findCallbackMethods(jenv)) {
2017-05-26 04:48:44 +08:00
return;
}
}
if (!future || !callback) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBFuture* f = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
2019-01-11 05:13:21 +08:00
// This is documented as not throwing, but simply returning null on OOM.
2017-05-26 04:48:44 +08:00
// As belt and suspenders, we will check for pending exceptions and then,
2019-01-11 05:13:21 +08:00
// if there are none and the result is null, we'll throw our own OOM.
callback = jenv->NewGlobalRef(callback);
if (!callback) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
return;
}
// Here we cache a thread-local reference to jenv
g_thread_jenv = jenv;
fdb_error_t err = fdb_future_set_callback(f, &callCallback, callback);
if (err) {
jenv->DeleteGlobalRef(callback);
safeThrow(jenv, getThrowable(jenv, err));
}
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_NativeFuture_Future_1blockUntilReady(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBFuture* sav = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
fdb_error_t err = fdb_future_block_until_ready(sav);
if (err)
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
JNIEXPORT jthrowable JNICALL Java_com_apple_foundationdb_NativeFuture_Future_1getError(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return JNI_NULL;
}
FDBFuture* sav = (FDBFuture*)future;
fdb_error_t err = fdb_future_get_error(sav);
if (err)
return getThrowable(jenv, err);
else
return JNI_NULL;
2017-05-26 04:48:44 +08:00
}
JNIEXPORT jboolean JNICALL Java_com_apple_foundationdb_NativeFuture_Future_1isReady(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return JNI_FALSE;
}
FDBFuture* var = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
return (jboolean)fdb_future_is_ready(var);
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_NativeFuture_Future_1dispose(JNIEnv* jenv, jobject, jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBFuture* var = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
fdb_future_destroy(var);
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_NativeFuture_Future_1cancel(JNIEnv* jenv, jobject, jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBFuture* var = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
fdb_future_cancel(var);
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_NativeFuture_Future_1releaseMemory(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBFuture* var = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
fdb_future_release_memory(var);
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FutureInt64_FutureInt64_1get(JNIEnv* jenv, jobject, jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBFuture* f = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
int64_t value = 0;
fdb_error_t err = fdb_future_get_int64(f, &value);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return 0;
}
return (jlong)value;
2017-05-26 04:48:44 +08:00
}
JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureStrings_FutureStrings_1get(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return JNI_NULL;
}
FDBFuture* f = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
const char** strings;
2017-05-26 04:48:44 +08:00
int count;
fdb_error_t err = fdb_future_get_string_array(f, &strings, &count);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return JNI_NULL;
}
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return JNI_NULL;
jobjectArray arr = jenv->NewObjectArray(count, string_class, JNI_NULL);
if (!arr) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
return JNI_NULL;
}
for (int i = 0; i < count; i++) {
jstring str = jenv->NewStringUTF(strings[i]);
if (!str) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
return JNI_NULL;
}
jenv->SetObjectArrayElement(arr, i, str);
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return JNI_NULL;
}
return arr;
}
JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureKeyArray_FutureKeyArray_1get(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2020-06-25 06:17:57 +08:00
throwParamNotNull(jenv);
return JNI_NULL;
}
FDBFuture* f = (FDBFuture*)future;
const FDBKey* ks;
2020-06-25 06:17:57 +08:00
int count;
fdb_error_t err = fdb_future_get_key_array(f, &ks, &count);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2020-06-25 06:17:57 +08:00
return JNI_NULL;
}
int totalKeySize = 0;
for (int i = 0; i < count; i++) {
2020-06-25 06:17:57 +08:00
totalKeySize += ks[i].key_length;
}
jbyteArray keyArray = jenv->NewByteArray(totalKeySize);
if (!keyArray) {
if (!jenv->ExceptionOccurred())
2020-06-25 06:17:57 +08:00
throwOutOfMem(jenv);
return JNI_NULL;
}
uint8_t* keys_barr = (uint8_t*)jenv->GetByteArrayElements(keyArray, JNI_NULL);
2020-06-25 06:17:57 +08:00
if (!keys_barr) {
throwRuntimeEx(jenv, "Error getting handle to native resources");
2020-06-25 06:17:57 +08:00
return JNI_NULL;
}
jintArray lengthArray = jenv->NewIntArray(count);
if (!lengthArray) {
if (!jenv->ExceptionOccurred())
2020-06-25 06:17:57 +08:00
throwOutOfMem(jenv);
jenv->ReleaseByteArrayElements(keyArray, (jbyte*)keys_barr, 0);
2020-06-25 06:17:57 +08:00
return JNI_NULL;
}
jint* length_barr = jenv->GetIntArrayElements(lengthArray, JNI_NULL);
if (!length_barr) {
if (!jenv->ExceptionOccurred())
2020-06-25 06:17:57 +08:00
throwOutOfMem(jenv);
jenv->ReleaseByteArrayElements(keyArray, (jbyte*)keys_barr, 0);
2020-06-25 06:17:57 +08:00
return JNI_NULL;
}
int offset = 0;
for (int i = 0; i < count; i++) {
2020-06-25 06:17:57 +08:00
memcpy(keys_barr + offset, ks[i].key, ks[i].key_length);
length_barr[i] = ks[i].key_length;
offset += ks[i].key_length;
}
jenv->ReleaseByteArrayElements(keyArray, (jbyte*)keys_barr, 0);
2020-06-25 06:17:57 +08:00
jenv->ReleaseIntArrayElements(lengthArray, length_barr, 0);
jobject result = jenv->NewObject(key_array_result_class, key_array_result_init, keyArray, lengthArray);
if (jenv->ExceptionOccurred())
2020-06-25 06:17:57 +08:00
return JNI_NULL;
return result;
}
// SOMEDAY: explore doing this more efficiently with Direct ByteBuffers
JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureResults_FutureResults_1get(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return JNI_NULL;
}
FDBFuture* f = (FDBFuture*)future;
const FDBKeyValue* kvs;
2017-05-26 04:48:44 +08:00
int count;
fdb_bool_t more;
fdb_error_t err = fdb_future_get_keyvalue_array(f, &kvs, &count, &more);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return JNI_NULL;
}
int totalKeyValueSize = 0;
for (int i = 0; i < count; i++) {
2017-05-26 04:48:44 +08:00
totalKeyValueSize += kvs[i].key_length + kvs[i].value_length;
}
jbyteArray keyValueArray = jenv->NewByteArray(totalKeyValueSize);
if (!keyValueArray) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
return JNI_NULL;
}
uint8_t* keyvalues_barr = (uint8_t*)jenv->GetByteArrayElements(keyValueArray, JNI_NULL);
if (!keyvalues_barr) {
throwRuntimeEx(jenv, "Error getting handle to native resources");
return JNI_NULL;
}
2017-05-26 04:48:44 +08:00
jintArray lengthArray = jenv->NewIntArray(count * 2);
if (!lengthArray) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
jenv->ReleaseByteArrayElements(keyValueArray, (jbyte*)keyvalues_barr, 0);
2017-05-26 04:48:44 +08:00
return JNI_NULL;
}
jint* length_barr = jenv->GetIntArrayElements(lengthArray, JNI_NULL);
if (!length_barr) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
jenv->ReleaseByteArrayElements(keyValueArray, (jbyte*)keyvalues_barr, 0);
2017-05-26 04:48:44 +08:00
return JNI_NULL;
}
int offset = 0;
for (int i = 0; i < count; i++) {
memcpy(keyvalues_barr + offset, kvs[i].key, kvs[i].key_length);
length_barr[i * 2] = kvs[i].key_length;
2017-05-26 04:48:44 +08:00
offset += kvs[i].key_length;
memcpy(keyvalues_barr + offset, kvs[i].value, kvs[i].value_length);
length_barr[(i * 2) + 1] = kvs[i].value_length;
2017-05-26 04:48:44 +08:00
offset += kvs[i].value_length;
}
jenv->ReleaseByteArrayElements(keyValueArray, (jbyte*)keyvalues_barr, 0);
2017-05-26 04:48:44 +08:00
jenv->ReleaseIntArrayElements(lengthArray, length_barr, 0);
jobject result = jenv->NewObject(range_result_class, range_result_init, keyValueArray, lengthArray, (jboolean)more);
if (jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
return JNI_NULL;
return result;
}
// SOMEDAY: explore doing this more efficiently with Direct ByteBuffers
JNIEXPORT jbyteArray JNICALL Java_com_apple_foundationdb_FutureResult_FutureResult_1get(JNIEnv* jenv,
jobject,
jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return JNI_NULL;
}
FDBFuture* f = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
fdb_bool_t present;
const uint8_t* value;
2017-05-26 04:48:44 +08:00
int length;
fdb_error_t err = fdb_future_get_value(f, &present, &value, &length);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return JNI_NULL;
}
if (!present)
2017-05-26 04:48:44 +08:00
return JNI_NULL;
jbyteArray result = jenv->NewByteArray(length);
if (!result) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
return JNI_NULL;
}
jenv->SetByteArrayRegion(result, 0, length, (const jbyte*)value);
2017-05-26 04:48:44 +08:00
return result;
}
JNIEXPORT jbyteArray JNICALL Java_com_apple_foundationdb_FutureKey_FutureKey_1get(JNIEnv* jenv, jclass, jlong future) {
if (!future) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return JNI_NULL;
}
FDBFuture* f = (FDBFuture*)future;
2017-05-26 04:48:44 +08:00
const uint8_t* value;
2017-05-26 04:48:44 +08:00
int length;
fdb_error_t err = fdb_future_get_key(f, &value, &length);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return JNI_NULL;
}
jbyteArray result = jenv->NewByteArray(length);
if (!result) {
if (!jenv->ExceptionOccurred())
2017-05-26 04:48:44 +08:00
throwOutOfMem(jenv);
return JNI_NULL;
}
jenv->SetByteArrayRegion(result, 0, length, (const jbyte*)value);
2017-05-26 04:48:44 +08:00
return result;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBDatabase_Database_1createTransaction(JNIEnv* jenv,
jobject,
jlong dbPtr) {
if (!dbPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBDatabase* database = (FDBDatabase*)dbPtr;
FDBTransaction* tr;
2017-05-26 04:48:44 +08:00
fdb_error_t err = fdb_database_create_transaction(database, &tr);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return 0;
}
return (jlong)tr;
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBDatabase_Database_1dispose(JNIEnv* jenv, jobject, jlong dPtr) {
if (!dPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
fdb_database_destroy((FDBDatabase*)dPtr);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBDatabase_Database_1setOption(JNIEnv* jenv,
jobject,
jlong dPtr,
jint code,
jbyteArray value) {
if (!dPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBDatabase* c = (FDBDatabase*)dPtr;
uint8_t* barr = nullptr;
2017-05-26 04:48:44 +08:00
int size = 0;
if (value != JNI_NULL) {
barr = (uint8_t*)jenv->GetByteArrayElements(value, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barr) {
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
size = jenv->GetArrayLength(value);
2017-05-26 04:48:44 +08:00
}
fdb_error_t err = fdb_database_set_option(c, (FDBDatabaseOption)code, barr, size);
if (value != JNI_NULL)
jenv->ReleaseByteArrayElements(value, (jbyte*)barr, JNI_ABORT);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
2021-03-25 07:38:42 +08:00
// Get network thread busyness (updated every 1s)
// A value of 0 indicates that the client is more or less idle
// A value of 1 (or more) indicates that the client is saturated
2021-03-25 07:34:34 +08:00
JNIEXPORT jdouble JNICALL Java_com_apple_foundationdb_FDBDatabase_Database_1getMainThreadBusyness(JNIEnv* jenv,
2021-03-25 08:11:11 +08:00
jobject,
jlong dbPtr) {
2021-03-25 07:34:34 +08:00
if (!dbPtr) {
throwParamNotNull(jenv);
return 0;
}
FDBDatabase* database = (FDBDatabase*)dbPtr;
2021-03-25 08:11:11 +08:00
return (jdouble)fdb_database_get_main_thread_busyness(database);
2021-03-25 07:34:34 +08:00
}
JNIEXPORT jboolean JNICALL Java_com_apple_foundationdb_FDB_Error_1predicate(JNIEnv* jenv,
jobject,
jint predicate,
jint code) {
2017-05-26 04:48:44 +08:00
return (jboolean)fdb_error_predicate(predicate, code);
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDB_Database_1create(JNIEnv* jenv,
jobject,
jstring clusterFileName) {
2019-01-11 04:28:14 +08:00
const char* fileName = nullptr;
if (clusterFileName != JNI_NULL) {
2019-01-11 05:13:21 +08:00
fileName = jenv->GetStringUTFChars(clusterFileName, JNI_NULL);
if (jenv->ExceptionOccurred()) {
2017-05-26 04:48:44 +08:00
return 0;
}
}
FDBDatabase* db;
fdb_error_t err = fdb_create_database(fileName, &db);
2017-05-26 04:48:44 +08:00
if (clusterFileName != JNI_NULL) {
jenv->ReleaseStringUTFChars(clusterFileName, fileName);
2017-05-26 04:48:44 +08:00
}
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return 0;
}
return (jlong)db;
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1setVersion(JNIEnv* jenv,
jobject,
jlong tPtr,
jlong version) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
fdb_transaction_set_read_version(tr, version);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1getReadVersion(JNIEnv* jenv,
jobject,
jlong tPtr) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
FDBFuture* f = fdb_transaction_get_read_version(tr);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1get(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray keyBytes,
jboolean snapshot) {
if (!tPtr || !keyBytes) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barr = (uint8_t*)jenv->GetByteArrayElements(keyBytes, JNI_NULL);
if (!barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return 0;
}
FDBFuture* f = fdb_transaction_get(tr, barr, jenv->GetArrayLength(keyBytes), (fdb_bool_t)snapshot);
jenv->ReleaseByteArrayElements(keyBytes, (jbyte*)barr, JNI_ABORT);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1getKey(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray keyBytes,
jboolean orEqual,
jint offset,
jboolean snapshot) {
if (!tPtr || !keyBytes) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barr = (uint8_t*)jenv->GetByteArrayElements(keyBytes, JNI_NULL);
if (!barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return 0;
}
FDBFuture* f =
fdb_transaction_get_key(tr, barr, jenv->GetArrayLength(keyBytes), orEqual, offset, (fdb_bool_t)snapshot);
jenv->ReleaseByteArrayElements(keyBytes, (jbyte*)barr, JNI_ABORT);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1getRange(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray keyBeginBytes,
jboolean orEqualBegin,
jint offsetBegin,
jbyteArray keyEndBytes,
jboolean orEqualEnd,
jint offsetEnd,
jint rowLimit,
jint targetBytes,
jint streamingMode,
jint iteration,
jboolean snapshot,
jboolean reverse) {
if (!tPtr || !keyBeginBytes || !keyEndBytes) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barrBegin = (uint8_t*)jenv->GetByteArrayElements(keyBeginBytes, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrBegin) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return 0;
}
uint8_t* barrEnd = (uint8_t*)jenv->GetByteArrayElements(keyEndBytes, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrEnd) {
jenv->ReleaseByteArrayElements(keyBeginBytes, (jbyte*)barrBegin, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return 0;
}
FDBFuture* f = fdb_transaction_get_range(tr,
barrBegin,
jenv->GetArrayLength(keyBeginBytes),
orEqualBegin,
offsetBegin,
barrEnd,
jenv->GetArrayLength(keyEndBytes),
orEqualEnd,
offsetEnd,
rowLimit,
targetBytes,
(FDBStreamingMode)streamingMode,
iteration,
snapshot,
reverse);
jenv->ReleaseByteArrayElements(keyBeginBytes, (jbyte*)barrBegin, JNI_ABORT);
jenv->ReleaseByteArrayElements(keyEndBytes, (jbyte*)barrEnd, JNI_ABORT);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FutureResults_FutureResults_1getDirect(JNIEnv* jenv,
jobject,
jlong future,
jobject jbuffer,
jint bufferCapacity) {
if (!future) {
throwParamNotNull(jenv);
return;
}
uint8_t* buffer = (uint8_t*)jenv->GetDirectBufferAddress(jbuffer);
if (!buffer) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
return;
}
FDBFuture* f = (FDBFuture*)future;
const FDBKeyValue* kvs;
java-bindings: Use DirectBuffer for `getRange` requests #3682 This patch keeps a batch of Java's DirectBuffers, which can be shared with JNI C world. This means: 1. No need for JNI wrapper to make several JNI calls, to allocate and convert Java objectd to bytes. 2. We already made a PR #3582 to reduce 3 JNI calls for each getRange() i.e. to fetch summary and then results. As mentioned in that PR, this patch also makes similar decision to make `getDirectRange()` call synchronous and instead schedule it asynchronously in Java executor. 3. No need for JNI to dynamically allocate buffers to store KVs. 4. Use one big DirectBuffer to make request and get reponse. `DirectBuffers` give direct access to the memory, and are much fast than the regular non-direct buffers we use. 5. We keep a pool of reasonably big DirectBuffers, which are borrowed and returned by `getRange()` requests. The downside to this are: 1. We have to manually and "carefully" serialize and deserialize the request/response both in C and Java world. It is no longer high-level Java objects. 2. Because `DirectBuffers` are expensive, we can only keep a few of them, which number of outstanding `getRange()` requests are limited by that. 3. Each request, currently uses 2 buffers, one for current chunk and one for outstanding request. 4. The performance bump seems to be excellent for bigger key-values. We didn't observe significant difference for smaller KV sizes (I can't say its better or worse, as from quick glance it didn't look statistically significant to me). Performance is currently measured using `PerformanceTester.java`, which measures throughput for several operations. The results are: ``` 1. Using Key = 16bytes, Value = 100bytes ========================================= Without this PR=> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 349363 73590 316218 342523 406445 540731 get_single_key_range throughput (local client) [keys/s] 30 7685 6455 6981 7744 8129 9773 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 383404 70181 338810 396950 437335 502886 get_single_key_range throughput (local client) [keys/s] 30 7029 5515 6635 7090 7353 8219 ======================================= 2. Using Key = 256bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 132787 102036 122650 130204 138269 202790 get_single_key_range throughput (local client) [keys/s] 90 5833 4894 5396 5690 6061 8986 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 359302 196676 310931 344029 407232 494259 get_single_key_range throughput (local client) [keys/s] 90 7227 5573 6771 7177 7477 10108 ==================================================================================================================== ======================================= 3. Using Key = 128bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 235661 148963 213670 229090 256825 317050 get_single_key_range throughput (local client) [keys/s] 30 10441 6302 10586 10873 10960 11065 ==================================================================================================================== ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 350612 185698 320868 348998 406750 459101 get_single_key_range throughput (local client) [keys/s] 30 10338 6570 10664 10847 10901 11040 ==================================================================================================================== ``` NOTE: These tests were run on a shared VM. Benchmark in each group was run serially, and the groups themselves run at different times. Therefore there might be some skew based on load, but the difference is compelling enough to show that there is performance benefit for larger KV.
2020-08-13 19:28:07 +08:00
int count;
fdb_bool_t more;
fdb_error_t err = fdb_future_get_keyvalue_array(f, &kvs, &count, &more);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
java-bindings: Use DirectBuffer for `getRange` requests #3682 This patch keeps a batch of Java's DirectBuffers, which can be shared with JNI C world. This means: 1. No need for JNI wrapper to make several JNI calls, to allocate and convert Java objectd to bytes. 2. We already made a PR #3582 to reduce 3 JNI calls for each getRange() i.e. to fetch summary and then results. As mentioned in that PR, this patch also makes similar decision to make `getDirectRange()` call synchronous and instead schedule it asynchronously in Java executor. 3. No need for JNI to dynamically allocate buffers to store KVs. 4. Use one big DirectBuffer to make request and get reponse. `DirectBuffers` give direct access to the memory, and are much fast than the regular non-direct buffers we use. 5. We keep a pool of reasonably big DirectBuffers, which are borrowed and returned by `getRange()` requests. The downside to this are: 1. We have to manually and "carefully" serialize and deserialize the request/response both in C and Java world. It is no longer high-level Java objects. 2. Because `DirectBuffers` are expensive, we can only keep a few of them, which number of outstanding `getRange()` requests are limited by that. 3. Each request, currently uses 2 buffers, one for current chunk and one for outstanding request. 4. The performance bump seems to be excellent for bigger key-values. We didn't observe significant difference for smaller KV sizes (I can't say its better or worse, as from quick glance it didn't look statistically significant to me). Performance is currently measured using `PerformanceTester.java`, which measures throughput for several operations. The results are: ``` 1. Using Key = 16bytes, Value = 100bytes ========================================= Without this PR=> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 349363 73590 316218 342523 406445 540731 get_single_key_range throughput (local client) [keys/s] 30 7685 6455 6981 7744 8129 9773 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 383404 70181 338810 396950 437335 502886 get_single_key_range throughput (local client) [keys/s] 30 7029 5515 6635 7090 7353 8219 ======================================= 2. Using Key = 256bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 132787 102036 122650 130204 138269 202790 get_single_key_range throughput (local client) [keys/s] 90 5833 4894 5396 5690 6061 8986 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 359302 196676 310931 344029 407232 494259 get_single_key_range throughput (local client) [keys/s] 90 7227 5573 6771 7177 7477 10108 ==================================================================================================================== ======================================= 3. Using Key = 128bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 235661 148963 213670 229090 256825 317050 get_single_key_range throughput (local client) [keys/s] 30 10441 6302 10586 10873 10960 11065 ==================================================================================================================== ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 350612 185698 320868 348998 406750 459101 get_single_key_range throughput (local client) [keys/s] 30 10338 6570 10664 10847 10901 11040 ==================================================================================================================== ``` NOTE: These tests were run on a shared VM. Benchmark in each group was run serially, and the groups themselves run at different times. Therefore there might be some skew based on load, but the difference is compelling enough to show that there is performance benefit for larger KV.
2020-08-13 19:28:07 +08:00
return;
}
// Capacity for Metadata+Keys+Values
// => sizeof(jint) for total key/value pairs
// => sizeof(jint) to store more flag
// => sizeof(jint) to store key length per KV pair
// => sizeof(jint) to store value length per KV pair
int totalCapacityNeeded = 2 * sizeof(jint);
for (int i = 0; i < count; i++) {
totalCapacityNeeded += kvs[i].key_length + kvs[i].value_length + 2 * sizeof(jint);
if (bufferCapacity < totalCapacityNeeded) {
count = i; /* Only fit first `i` K/V pairs */
more = true;
break;
}
java-bindings: Use DirectBuffer for `getRange` requests #3682 This patch keeps a batch of Java's DirectBuffers, which can be shared with JNI C world. This means: 1. No need for JNI wrapper to make several JNI calls, to allocate and convert Java objectd to bytes. 2. We already made a PR #3582 to reduce 3 JNI calls for each getRange() i.e. to fetch summary and then results. As mentioned in that PR, this patch also makes similar decision to make `getDirectRange()` call synchronous and instead schedule it asynchronously in Java executor. 3. No need for JNI to dynamically allocate buffers to store KVs. 4. Use one big DirectBuffer to make request and get reponse. `DirectBuffers` give direct access to the memory, and are much fast than the regular non-direct buffers we use. 5. We keep a pool of reasonably big DirectBuffers, which are borrowed and returned by `getRange()` requests. The downside to this are: 1. We have to manually and "carefully" serialize and deserialize the request/response both in C and Java world. It is no longer high-level Java objects. 2. Because `DirectBuffers` are expensive, we can only keep a few of them, which number of outstanding `getRange()` requests are limited by that. 3. Each request, currently uses 2 buffers, one for current chunk and one for outstanding request. 4. The performance bump seems to be excellent for bigger key-values. We didn't observe significant difference for smaller KV sizes (I can't say its better or worse, as from quick glance it didn't look statistically significant to me). Performance is currently measured using `PerformanceTester.java`, which measures throughput for several operations. The results are: ``` 1. Using Key = 16bytes, Value = 100bytes ========================================= Without this PR=> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 349363 73590 316218 342523 406445 540731 get_single_key_range throughput (local client) [keys/s] 30 7685 6455 6981 7744 8129 9773 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 383404 70181 338810 396950 437335 502886 get_single_key_range throughput (local client) [keys/s] 30 7029 5515 6635 7090 7353 8219 ======================================= 2. Using Key = 256bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 132787 102036 122650 130204 138269 202790 get_single_key_range throughput (local client) [keys/s] 90 5833 4894 5396 5690 6061 8986 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 359302 196676 310931 344029 407232 494259 get_single_key_range throughput (local client) [keys/s] 90 7227 5573 6771 7177 7477 10108 ==================================================================================================================== ======================================= 3. Using Key = 128bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 235661 148963 213670 229090 256825 317050 get_single_key_range throughput (local client) [keys/s] 30 10441 6302 10586 10873 10960 11065 ==================================================================================================================== ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 350612 185698 320868 348998 406750 459101 get_single_key_range throughput (local client) [keys/s] 30 10338 6570 10664 10847 10901 11040 ==================================================================================================================== ``` NOTE: These tests were run on a shared VM. Benchmark in each group was run serially, and the groups themselves run at different times. Therefore there might be some skew based on load, but the difference is compelling enough to show that there is performance benefit for larger KV.
2020-08-13 19:28:07 +08:00
}
int offset = 0;
// First copy RangeResultSummary, i.e. [keyCount, more]
java-bindings: Use DirectBuffer for `getRange` requests #3682 This patch keeps a batch of Java's DirectBuffers, which can be shared with JNI C world. This means: 1. No need for JNI wrapper to make several JNI calls, to allocate and convert Java objectd to bytes. 2. We already made a PR #3582 to reduce 3 JNI calls for each getRange() i.e. to fetch summary and then results. As mentioned in that PR, this patch also makes similar decision to make `getDirectRange()` call synchronous and instead schedule it asynchronously in Java executor. 3. No need for JNI to dynamically allocate buffers to store KVs. 4. Use one big DirectBuffer to make request and get reponse. `DirectBuffers` give direct access to the memory, and are much fast than the regular non-direct buffers we use. 5. We keep a pool of reasonably big DirectBuffers, which are borrowed and returned by `getRange()` requests. The downside to this are: 1. We have to manually and "carefully" serialize and deserialize the request/response both in C and Java world. It is no longer high-level Java objects. 2. Because `DirectBuffers` are expensive, we can only keep a few of them, which number of outstanding `getRange()` requests are limited by that. 3. Each request, currently uses 2 buffers, one for current chunk and one for outstanding request. 4. The performance bump seems to be excellent for bigger key-values. We didn't observe significant difference for smaller KV sizes (I can't say its better or worse, as from quick glance it didn't look statistically significant to me). Performance is currently measured using `PerformanceTester.java`, which measures throughput for several operations. The results are: ``` 1. Using Key = 16bytes, Value = 100bytes ========================================= Without this PR=> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 349363 73590 316218 342523 406445 540731 get_single_key_range throughput (local client) [keys/s] 30 7685 6455 6981 7744 8129 9773 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ----- ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 383404 70181 338810 396950 437335 502886 get_single_key_range throughput (local client) [keys/s] 30 7029 5515 6635 7090 7353 8219 ======================================= 2. Using Key = 256bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 132787 102036 122650 130204 138269 202790 get_single_key_range throughput (local client) [keys/s] 90 5833 4894 5396 5690 6061 8986 ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 90 359302 196676 310931 344029 407232 494259 get_single_key_range throughput (local client) [keys/s] 90 7227 5573 6771 7177 7477 10108 ==================================================================================================================== ======================================= 3. Using Key = 128bytes, Value = 512bytes ======================================== ** Without this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 235661 148963 213670 229090 256825 317050 get_single_key_range throughput (local client) [keys/s] 30 10441 6302 10586 10873 10960 11065 ==================================================================================================================== ** With this PR ==> Count Avg Min Q1 Median Q3 Max ------------------------------------------------------- ------- ------ ------ ------ -------- ------ ------ get_range throughput (local client) [keys/s] 30 350612 185698 320868 348998 406750 459101 get_single_key_range throughput (local client) [keys/s] 30 10338 6570 10664 10847 10901 11040 ==================================================================================================================== ``` NOTE: These tests were run on a shared VM. Benchmark in each group was run serially, and the groups themselves run at different times. Therefore there might be some skew based on load, but the difference is compelling enough to show that there is performance benefit for larger KV.
2020-08-13 19:28:07 +08:00
memcpy(buffer + offset, &count, sizeof(jint));
offset += sizeof(jint);
memcpy(buffer + offset, &more, sizeof(jint));
offset += sizeof(jint);
for (int i = 0; i < count; i++) {
memcpy(buffer + offset, &kvs[i].key_length, sizeof(jint));
memcpy(buffer + offset + sizeof(jint), &kvs[i].value_length, sizeof(jint));
offset += 2 * sizeof(jint);
memcpy(buffer + offset, kvs[i].key, kvs[i].key_length);
offset += kvs[i].key_length;
memcpy(buffer + offset, kvs[i].value, kvs[i].value_length);
offset += kvs[i].value_length;
}
}
JNIEXPORT jlong JNICALL
Java_com_apple_foundationdb_FDBTransaction_Transaction_1getEstimatedRangeSizeBytes(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray beginKeyBytes,
jbyteArray endKeyBytes) {
if (!tPtr || !beginKeyBytes || !endKeyBytes) {
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
uint8_t* startKey = (uint8_t*)jenv->GetByteArrayElements(beginKeyBytes, JNI_NULL);
if (!startKey) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
return 0;
}
uint8_t* endKey = (uint8_t*)jenv->GetByteArrayElements(endKeyBytes, JNI_NULL);
if (!endKey) {
jenv->ReleaseByteArrayElements(beginKeyBytes, (jbyte*)startKey, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
return 0;
}
FDBFuture* f = fdb_transaction_get_estimated_range_size_bytes(
tr, startKey, jenv->GetArrayLength(beginKeyBytes), endKey, jenv->GetArrayLength(endKeyBytes));
jenv->ReleaseByteArrayElements(beginKeyBytes, (jbyte*)startKey, JNI_ABORT);
jenv->ReleaseByteArrayElements(endKeyBytes, (jbyte*)endKey, JNI_ABORT);
return (jlong)f;
}
JNIEXPORT jlong JNICALL
Java_com_apple_foundationdb_FDBTransaction_Transaction_1getRangeSplitPoints(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray beginKeyBytes,
jbyteArray endKeyBytes,
jlong chunkSize) {
if (!tPtr || !beginKeyBytes || !endKeyBytes) {
2020-06-25 06:17:57 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2020-06-25 06:17:57 +08:00
uint8_t* startKey = (uint8_t*)jenv->GetByteArrayElements(beginKeyBytes, JNI_NULL);
if (!startKey) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2020-06-25 06:17:57 +08:00
return 0;
}
uint8_t* endKey = (uint8_t*)jenv->GetByteArrayElements(endKeyBytes, JNI_NULL);
2020-06-25 06:17:57 +08:00
if (!endKey) {
jenv->ReleaseByteArrayElements(beginKeyBytes, (jbyte*)startKey, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2020-06-25 06:17:57 +08:00
return 0;
}
FDBFuture* f = fdb_transaction_get_range_split_points(
tr, startKey, jenv->GetArrayLength(beginKeyBytes), endKey, jenv->GetArrayLength(endKeyBytes), chunkSize);
jenv->ReleaseByteArrayElements(beginKeyBytes, (jbyte*)startKey, JNI_ABORT);
jenv->ReleaseByteArrayElements(endKeyBytes, (jbyte*)endKey, JNI_ABORT);
2020-06-25 06:17:57 +08:00
return (jlong)f;
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1set(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray keyBytes,
jbyteArray valueBytes) {
if (!tPtr || !keyBytes || !valueBytes) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barrKey = (uint8_t*)jenv->GetByteArrayElements(keyBytes, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrKey) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
uint8_t* barrValue = (uint8_t*)jenv->GetByteArrayElements(valueBytes, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrValue) {
jenv->ReleaseByteArrayElements(keyBytes, (jbyte*)barrKey, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
fdb_transaction_set(tr, barrKey, jenv->GetArrayLength(keyBytes), barrValue, jenv->GetArrayLength(valueBytes));
jenv->ReleaseByteArrayElements(keyBytes, (jbyte*)barrKey, JNI_ABORT);
jenv->ReleaseByteArrayElements(valueBytes, (jbyte*)barrValue, JNI_ABORT);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1clear__J_3B(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray keyBytes) {
if (!tPtr || !keyBytes) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barr = (uint8_t*)jenv->GetByteArrayElements(keyBytes, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
fdb_transaction_clear(tr, barr, jenv->GetArrayLength(keyBytes));
jenv->ReleaseByteArrayElements(keyBytes, (jbyte*)barr, JNI_ABORT);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1clear__J_3B_3B(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray keyBeginBytes,
jbyteArray keyEndBytes) {
if (!tPtr || !keyBeginBytes || !keyEndBytes) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barrKeyBegin = (uint8_t*)jenv->GetByteArrayElements(keyBeginBytes, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrKeyBegin) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
uint8_t* barrKeyEnd = (uint8_t*)jenv->GetByteArrayElements(keyEndBytes, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrKeyEnd) {
jenv->ReleaseByteArrayElements(keyBeginBytes, (jbyte*)barrKeyBegin, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
fdb_transaction_clear_range(
tr, barrKeyBegin, jenv->GetArrayLength(keyBeginBytes), barrKeyEnd, jenv->GetArrayLength(keyEndBytes));
jenv->ReleaseByteArrayElements(keyBeginBytes, (jbyte*)barrKeyBegin, JNI_ABORT);
jenv->ReleaseByteArrayElements(keyEndBytes, (jbyte*)barrKeyEnd, JNI_ABORT);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1mutate(JNIEnv* jenv,
jobject,
jlong tPtr,
jint code,
jbyteArray key,
jbyteArray value) {
if (!tPtr || !key || !value) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barrKey = (uint8_t*)jenv->GetByteArrayElements(key, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrKey) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
uint8_t* barrValue = (uint8_t*)jenv->GetByteArrayElements(value, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barrValue) {
jenv->ReleaseByteArrayElements(key, (jbyte*)barrKey, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
fdb_transaction_atomic_op(
tr, barrKey, jenv->GetArrayLength(key), barrValue, jenv->GetArrayLength(value), (FDBMutationType)code);
2017-05-26 04:48:44 +08:00
jenv->ReleaseByteArrayElements(key, (jbyte*)barrKey, JNI_ABORT);
jenv->ReleaseByteArrayElements(value, (jbyte*)barrValue, JNI_ABORT);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1commit(JNIEnv* jenv,
jobject,
jlong tPtr) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
FDBFuture* f = fdb_transaction_commit(tr);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1setOption(JNIEnv* jenv,
jobject,
jlong tPtr,
jint code,
jbyteArray value) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
uint8_t* barr = nullptr;
2017-05-26 04:48:44 +08:00
int size = 0;
if (value != JNI_NULL) {
barr = (uint8_t*)jenv->GetByteArrayElements(value, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
size = jenv->GetArrayLength(value);
2017-05-26 04:48:44 +08:00
}
fdb_error_t err = fdb_transaction_set_option(tr, (FDBTransactionOption)code, barr, size);
if (value != JNI_NULL)
jenv->ReleaseByteArrayElements(value, (jbyte*)barr, JNI_ABORT);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1getCommittedVersion(JNIEnv* jenv,
jobject,
jlong tPtr) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
int64_t version;
fdb_error_t err = fdb_transaction_get_committed_version(tr, &version);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
return 0;
}
return (jlong)version;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1getApproximateSize(JNIEnv* jenv,
jobject,
jlong tPtr) {
2019-06-27 01:42:24 +08:00
if (!tPtr) {
throwParamNotNull(jenv);
return 0;
}
FDBFuture* f = fdb_transaction_get_approximate_size((FDBTransaction*)tPtr);
return (jlong)f;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1getVersionstamp(JNIEnv* jenv,
jobject,
jlong tPtr) {
2017-05-26 04:48:44 +08:00
if (!tPtr) {
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
FDBFuture* f = fdb_transaction_get_versionstamp(tr);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1getKeyLocations(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray key) {
if (!tPtr || !key) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barr = (uint8_t*)jenv->GetByteArrayElements(key, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return 0;
}
int size = jenv->GetArrayLength(key);
2017-05-26 04:48:44 +08:00
FDBFuture* f = fdb_transaction_get_addresses_for_key(tr, barr, size);
jenv->ReleaseByteArrayElements(key, (jbyte*)barr, JNI_ABORT);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1onError(JNIEnv* jenv,
jobject,
jlong tPtr,
jint errorCode) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
FDBFuture* f = fdb_transaction_on_error(tr, (fdb_error_t)errorCode);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1dispose(JNIEnv* jenv,
jobject,
jlong tPtr) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
fdb_transaction_destroy((FDBTransaction*)tPtr);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1reset(JNIEnv* jenv,
jobject,
jlong tPtr) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
fdb_transaction_reset((FDBTransaction*)tPtr);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT jlong JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1watch(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray key) {
if (!tPtr || !key) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return 0;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* barr = (uint8_t*)jenv->GetByteArrayElements(key, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return 0;
}
int size = jenv->GetArrayLength(key);
FDBFuture* f = fdb_transaction_watch(tr, barr, size);
jenv->ReleaseByteArrayElements(key, (jbyte*)barr, JNI_ABORT);
2017-05-26 04:48:44 +08:00
return (jlong)f;
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1cancel(JNIEnv* jenv,
jobject,
jlong tPtr) {
if (!tPtr) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
fdb_transaction_cancel((FDBTransaction*)tPtr);
2017-05-26 04:48:44 +08:00
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1addConflictRange(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray keyBegin,
jbyteArray keyEnd,
jint conflictType) {
if (!tPtr || !keyBegin || !keyEnd) {
2017-05-26 04:48:44 +08:00
throwParamNotNull(jenv);
return;
}
FDBTransaction* tr = (FDBTransaction*)tPtr;
2017-05-26 04:48:44 +08:00
uint8_t* begin_barr = (uint8_t*)jenv->GetByteArrayElements(keyBegin, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!begin_barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
int begin_size = jenv->GetArrayLength(keyBegin);
2017-05-26 04:48:44 +08:00
uint8_t* end_barr = (uint8_t*)jenv->GetByteArrayElements(keyEnd, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!end_barr) {
jenv->ReleaseByteArrayElements(keyBegin, (jbyte*)begin_barr, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
int end_size = jenv->GetArrayLength(keyEnd);
2017-05-26 04:48:44 +08:00
fdb_error_t err = fdb_transaction_add_conflict_range(
tr, begin_barr, begin_size, end_barr, end_size, (FDBConflictRangeType)conflictType);
2017-05-26 04:48:44 +08:00
jenv->ReleaseByteArrayElements(keyBegin, (jbyte*)begin_barr, JNI_ABORT);
jenv->ReleaseByteArrayElements(keyEnd, (jbyte*)end_barr, JNI_ABORT);
2017-05-26 04:48:44 +08:00
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDB_Select_1API_1version(JNIEnv* jenv, jclass, jint version) {
fdb_error_t err = fdb_select_api_version((int)version);
if (err) {
if (err == 2203) {
2017-05-26 04:48:44 +08:00
int maxSupportedVersion = fdb_get_max_api_version();
char errorStr[1024];
if (FDB_API_VERSION > maxSupportedVersion) {
snprintf(errorStr,
sizeof(errorStr),
"This version of the FoundationDB Java binding is not supported by the installed "
"FoundationDB C library. The binding requires a library that supports API version "
"%d, but the installed library supports a maximum version of %d.",
FDB_API_VERSION,
maxSupportedVersion);
} else {
snprintf(errorStr,
sizeof(errorStr),
"API version %d is not supported by the installed FoundationDB C library.",
version);
2017-05-26 04:48:44 +08:00
}
safeThrow(jenv, getThrowable(jenv, err, errorStr));
} else {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDB_Network_1setOption(JNIEnv* jenv,
jobject,
jint code,
jbyteArray value) {
uint8_t* barr = nullptr;
2017-05-26 04:48:44 +08:00
int size = 0;
if (value != JNI_NULL) {
barr = (uint8_t*)jenv->GetByteArrayElements(value, JNI_NULL);
2017-05-26 04:48:44 +08:00
if (!barr) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
2017-05-26 04:48:44 +08:00
return;
}
size = jenv->GetArrayLength(value);
2017-05-26 04:48:44 +08:00
}
fdb_error_t err = fdb_network_set_option((FDBNetworkOption)code, barr, size);
if (value != JNI_NULL)
jenv->ReleaseByteArrayElements(value, (jbyte*)barr, JNI_ABORT);
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDB_Network_1setup(JNIEnv* jenv, jobject) {
2017-05-26 04:48:44 +08:00
fdb_error_t err = fdb_setup_network();
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDB_Network_1run(JNIEnv* jenv, jobject) {
2017-05-26 04:48:44 +08:00
// initialize things for the callbacks on the network thread
g_thread_jenv = jenv;
if (!g_IFutureCallback_call_methodID) {
if (!findCallbackMethods(jenv))
2017-05-26 04:48:44 +08:00
return;
}
fdb_error_t hookErr = fdb_add_network_thread_completion_hook(&detachIfExternalThread, nullptr);
if (hookErr) {
safeThrow(jenv, getThrowable(jenv, hookErr));
}
2017-05-26 04:48:44 +08:00
fdb_error_t err = fdb_run_network();
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDB_Network_1stop(JNIEnv* jenv, jobject) {
2017-05-26 04:48:44 +08:00
fdb_error_t err = fdb_stop_network();
if (err) {
safeThrow(jenv, getThrowable(jenv, err));
2017-05-26 04:48:44 +08:00
}
}
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
g_jvm = vm;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
} else {
jclass local_range_result_class = env->FindClass("com/apple/foundationdb/RangeResult");
range_result_init = env->GetMethodID(local_range_result_class, "<init>", "([B[IZ)V");
range_result_class = (jclass)(env)->NewGlobalRef(local_range_result_class);
2020-06-25 06:17:57 +08:00
jclass local_key_array_result_class = env->FindClass("com/apple/foundationdb/KeyArrayResult");
key_array_result_init = env->GetMethodID(local_key_array_result_class, "<init>", "([B[I)V");
key_array_result_class = (jclass)(env)->NewGlobalRef(local_key_array_result_class);
2020-06-25 06:17:57 +08:00
jclass local_range_result_summary_class = env->FindClass("com/apple/foundationdb/RangeResultSummary");
range_result_summary_init = env->GetMethodID(local_range_result_summary_class, "<init>", "([BIZ)V");
range_result_summary_class = (jclass)(env)->NewGlobalRef(local_range_result_summary_class);
jclass local_string_class = env->FindClass("java/lang/String");
string_class = (jclass)(env)->NewGlobalRef(local_string_class);
return JNI_VERSION_1_6;
}
}
// Is automatically called once the Classloader is destroyed
void JNI_OnUnload(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return;
} else {
// delete global references so the GC can collect them
if (range_result_summary_class != JNI_NULL) {
env->DeleteGlobalRef(range_result_summary_class);
}
if (range_result_class != JNI_NULL) {
env->DeleteGlobalRef(range_result_class);
}
if (string_class != JNI_NULL) {
env->DeleteGlobalRef(string_class);
}
}
}
2017-05-26 04:48:44 +08:00
#ifdef __cplusplus
}
#endif