1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. 2 // This source code is licensed under both the GPLv2 (found in the 3 // COPYING file in the root directory) and Apache 2.0 License 4 // (found in the LICENSE.Apache file in the root directory). 5 // 6 // This file implements the callback "bridge" between Java and C++ for 7 // ROCKSDB_NAMESPACE::Comparator. 8 9 #include "rocksjni/comparatorjnicallback.h" 10 #include "rocksjni/portal.h" 11 12 namespace ROCKSDB_NAMESPACE { 13 ComparatorJniCallback::ComparatorJniCallback( 14 JNIEnv* env, jobject jcomparator, 15 const ComparatorJniCallbackOptions* options) 16 : JniCallback(env, jcomparator), 17 m_options(options) { 18 19 // cache the AbstractComparatorJniBridge class as we will reuse it many times for each callback 20 m_abstract_comparator_jni_bridge_clazz = 21 static_cast<jclass>(env->NewGlobalRef(AbstractComparatorJniBridge::getJClass(env))); 22 23 // Note: The name of a Comparator will not change during it's lifetime, 24 // so we cache it in a global var 25 jmethodID jname_mid = AbstractComparatorJni::getNameMethodId(env); 26 if (jname_mid == nullptr) { 27 // exception thrown: NoSuchMethodException or OutOfMemoryError 28 return; 29 } 30 jstring js_name = (jstring)env->CallObjectMethod(m_jcallback_obj, jname_mid); 31 if (env->ExceptionCheck()) { 32 // exception thrown 33 return; 34 } 35 jboolean has_exception = JNI_FALSE; 36 m_name = JniUtil::copyString(env, js_name, 37 &has_exception); // also releases jsName VerifyChecksum()38 if (has_exception == JNI_TRUE) { 39 // exception thrown 40 return; 41 } 42 43 // cache the ByteBuffer class as we will reuse it many times for each callback 44 m_jbytebuffer_clazz = 45 static_cast<jclass>(env->NewGlobalRef(ByteBufferJni::getJClass(env))); 46 47 m_jcompare_mid = AbstractComparatorJniBridge::getCompareInternalMethodId( 48 env, m_abstract_comparator_jni_bridge_clazz); 49 if (m_jcompare_mid == nullptr) { 50 // exception thrown: NoSuchMethodException or OutOfMemoryError 51 return; 52 } 53 54 m_jshortest_mid = 55 AbstractComparatorJniBridge::getFindShortestSeparatorInternalMethodId( 56 env, m_abstract_comparator_jni_bridge_clazz); 57 if (m_jshortest_mid == nullptr) { 58 // exception thrown: NoSuchMethodException or OutOfMemoryError 59 return; 60 } 61 62 m_jshort_mid = 63 AbstractComparatorJniBridge::getFindShortSuccessorInternalMethodId(env, 64 m_abstract_comparator_jni_bridge_clazz); 65 if (m_jshort_mid == nullptr) { 66 // exception thrown: NoSuchMethodException or OutOfMemoryError 67 return; 68 } 69 70 // do we need reusable buffers? 71 if (m_options->max_reused_buffer_size > -1) { 72 73 if (m_options->reused_synchronisation_type 74 == ReusedSynchronisationType::THREAD_LOCAL) { 75 // buffers reused per thread 76 UnrefHandler unref = [](void* ptr) { 77 ThreadLocalBuf* tlb = reinterpret_cast<ThreadLocalBuf*>(ptr); 78 jboolean attached_thread = JNI_FALSE; 79 JNIEnv* _env = JniUtil::getJniEnv(tlb->jvm, &attached_thread); 80 if (_env != nullptr) { 81 if (tlb->direct_buffer) { 82 void* buf = _env->GetDirectBufferAddress(tlb->jbuf); 83 delete[] static_cast<char*>(buf); 84 } 85 _env->DeleteGlobalRef(tlb->jbuf); 86 JniUtil::releaseJniEnv(tlb->jvm, attached_thread); 87 } 88 }; 89 90 m_tl_buf_a = new ThreadLocalPtr(unref); 91 m_tl_buf_b = new ThreadLocalPtr(unref); 92 93 m_jcompare_buf_a = nullptr; 94 m_jcompare_buf_b = nullptr; 95 m_jshortest_buf_start = nullptr; 96 m_jshortest_buf_limit = nullptr; 97 m_jshort_buf_key = nullptr; 98 99 } else { 100 //buffers reused and shared across threads 101 const bool adaptive = 102 m_options->reused_synchronisation_type == ReusedSynchronisationType::ADAPTIVE_MUTEX; 103 mtx_compare = std::unique_ptr<port::Mutex>(new port::Mutex(adaptive)); 104 mtx_shortest = std::unique_ptr<port::Mutex>(new port::Mutex(adaptive)); 105 mtx_short = std::unique_ptr<port::Mutex>(new port::Mutex(adaptive)); 106 107 m_jcompare_buf_a = env->NewGlobalRef(ByteBufferJni::construct( 108 env, m_options->direct_buffer, m_options->max_reused_buffer_size, 109 m_jbytebuffer_clazz)); 110 if (m_jcompare_buf_a == nullptr) { 111 // exception thrown: OutOfMemoryError 112 return; 113 } 114 115 m_jcompare_buf_b = env->NewGlobalRef(ByteBufferJni::construct( 116 env, m_options->direct_buffer, m_options->max_reused_buffer_size, 117 m_jbytebuffer_clazz)); 118 if (m_jcompare_buf_b == nullptr) { 119 // exception thrown: OutOfMemoryError 120 return; 121 } 122 123 m_jshortest_buf_start = env->NewGlobalRef(ByteBufferJni::construct( 124 env, m_options->direct_buffer, m_options->max_reused_buffer_size, 125 m_jbytebuffer_clazz)); 126 if (m_jshortest_buf_start == nullptr) { 127 // exception thrown: OutOfMemoryError 128 return; 129 } 130 131 m_jshortest_buf_limit = env->NewGlobalRef(ByteBufferJni::construct( 132 env, m_options->direct_buffer, m_options->max_reused_buffer_size, 133 m_jbytebuffer_clazz)); 134 if (m_jshortest_buf_limit == nullptr) { 135 // exception thrown: OutOfMemoryError 136 return; 137 } 138 139 m_jshort_buf_key = env->NewGlobalRef(ByteBufferJni::construct( 140 env, m_options->direct_buffer, m_options->max_reused_buffer_size, 141 m_jbytebuffer_clazz)); 142 if (m_jshort_buf_key == nullptr) { 143 // exception thrown: OutOfMemoryError 144 return; 145 } 146 147 m_tl_buf_a = nullptr; 148 m_tl_buf_b = nullptr; 149 } 150 151 } else { 152 m_jcompare_buf_a = nullptr; 153 m_jcompare_buf_b = nullptr; 154 m_jshortest_buf_start = nullptr; 155 m_jshortest_buf_limit = nullptr; 156 m_jshort_buf_key = nullptr; 157 158 m_tl_buf_a = nullptr; 159 m_tl_buf_b = nullptr; 160 } 161 } 162 163 ComparatorJniCallback::~ComparatorJniCallback() { 164 jboolean attached_thread = JNI_FALSE; 165 JNIEnv* env = getJniEnv(&attached_thread); 166 assert(env != nullptr); 167 168 env->DeleteGlobalRef(m_abstract_comparator_jni_bridge_clazz); 169 170 env->DeleteGlobalRef(m_jbytebuffer_clazz); 171 172 if (m_jcompare_buf_a != nullptr) { 173 if (m_options->direct_buffer) { 174 void* buf = env->GetDirectBufferAddress(m_jcompare_buf_a); 175 delete[] static_cast<char*>(buf); 176 } 177 env->DeleteGlobalRef(m_jcompare_buf_a); 178 } 179 180 if (m_jcompare_buf_b != nullptr) { 181 if (m_options->direct_buffer) { 182 void* buf = env->GetDirectBufferAddress(m_jcompare_buf_b); 183 delete[] static_cast<char*>(buf); 184 } 185 env->DeleteGlobalRef(m_jcompare_buf_b); 186 } 187 188 if (m_jshortest_buf_start != nullptr) { 189 if (m_options->direct_buffer) { 190 void* buf = env->GetDirectBufferAddress(m_jshortest_buf_start); 191 delete[] static_cast<char*>(buf); 192 } 193 env->DeleteGlobalRef(m_jshortest_buf_start); 194 } 195 196 if (m_jshortest_buf_limit != nullptr) { 197 if (m_options->direct_buffer) { 198 void* buf = env->GetDirectBufferAddress(m_jshortest_buf_limit); 199 delete[] static_cast<char*>(buf); 200 } 201 env->DeleteGlobalRef(m_jshortest_buf_limit); 202 } 203 204 if (m_jshort_buf_key != nullptr) { 205 if (m_options->direct_buffer) { 206 void* buf = env->GetDirectBufferAddress(m_jshort_buf_key); 207 delete[] static_cast<char*>(buf); 208 } 209 env->DeleteGlobalRef(m_jshort_buf_key); 210 } 211 212 if (m_tl_buf_a != nullptr) { 213 delete m_tl_buf_a; 214 } 215 216 if (m_tl_buf_b != nullptr) { 217 delete m_tl_buf_b; 218 } 219 220 releaseJniEnv(attached_thread); 221 } 222 223 const char* ComparatorJniCallback::Name() const { 224 return m_name.get(); 225 } 226 227 int ComparatorJniCallback::Compare(const Slice& a, const Slice& b) const { 228 jboolean attached_thread = JNI_FALSE; 229 JNIEnv* env = getJniEnv(&attached_thread); 230 assert(env != nullptr); 231 232 const bool reuse_jbuf_a = 233 static_cast<int64_t>(a.size()) <= m_options->max_reused_buffer_size; 234 const bool reuse_jbuf_b = 235 static_cast<int64_t>(b.size()) <= m_options->max_reused_buffer_size; 236 237 MaybeLockForReuse(mtx_compare, reuse_jbuf_a || reuse_jbuf_b); 238 239 jobject jcompare_buf_a = GetBuffer(env, a, reuse_jbuf_a, m_tl_buf_a, m_jcompare_buf_a); 240 if (jcompare_buf_a == nullptr) { 241 // exception occurred 242 MaybeUnlockForReuse(mtx_compare, reuse_jbuf_a || reuse_jbuf_b); 243 env->ExceptionDescribe(); // print out exception to stderr 244 releaseJniEnv(attached_thread); 245 return 0; 246 } 247 248 jobject jcompare_buf_b = GetBuffer(env, b, reuse_jbuf_b, m_tl_buf_b, m_jcompare_buf_b); 249 if (jcompare_buf_b == nullptr) { 250 // exception occurred 251 if (!reuse_jbuf_a) { 252 DeleteBuffer(env, jcompare_buf_a); 253 } 254 MaybeUnlockForReuse(mtx_compare, reuse_jbuf_a || reuse_jbuf_b); 255 env->ExceptionDescribe(); // print out exception to stderr 256 releaseJniEnv(attached_thread); 257 return 0; 258 } 259 260 jint result = 261 env->CallStaticIntMethod( 262 m_abstract_comparator_jni_bridge_clazz, m_jcompare_mid, 263 m_jcallback_obj, 264 jcompare_buf_a, reuse_jbuf_a ? a.size() : -1, 265 jcompare_buf_b, reuse_jbuf_b ? b.size() : -1); 266 267 if (env->ExceptionCheck()) { 268 // exception thrown from CallIntMethod 269 env->ExceptionDescribe(); // print out exception to stderr 270 result = 0; // we could not get a result from java callback so use 0 271 } 272 273 if (!reuse_jbuf_a) { 274 DeleteBuffer(env, jcompare_buf_a); 275 } 276 if (!reuse_jbuf_b) { 277 DeleteBuffer(env, jcompare_buf_b); 278 } 279 280 MaybeUnlockForReuse(mtx_compare, reuse_jbuf_a || reuse_jbuf_b); 281 282 releaseJniEnv(attached_thread); 283 284 return result; 285 } 286 287 void ComparatorJniCallback::FindShortestSeparator( 288 std::string* start, const Slice& limit) const { 289 if (start == nullptr) { 290 return; 291 } 292 293 jboolean attached_thread = JNI_FALSE; 294 JNIEnv* env = getJniEnv(&attached_thread); 295 assert(env != nullptr); 296 297 const bool reuse_jbuf_start = 298 static_cast<int64_t>(start->length()) <= m_options->max_reused_buffer_size; 299 const bool reuse_jbuf_limit = 300 static_cast<int64_t>(limit.size()) <= m_options->max_reused_buffer_size; 301 302 MaybeLockForReuse(mtx_shortest, reuse_jbuf_start || reuse_jbuf_limit); 303 304 Slice sstart(start->data(), start->length()); 305 jobject j_start_buf = GetBuffer(env, sstart, reuse_jbuf_start, m_tl_buf_a, m_jshortest_buf_start); 306 if (j_start_buf == nullptr) { 307 // exception occurred 308 MaybeUnlockForReuse(mtx_shortest, reuse_jbuf_start || reuse_jbuf_limit); 309 env->ExceptionDescribe(); // print out exception to stderr 310 releaseJniEnv(attached_thread); 311 return; 312 } 313 314 jobject j_limit_buf = GetBuffer(env, limit, reuse_jbuf_limit, m_tl_buf_b, m_jshortest_buf_limit); 315 if (j_limit_buf == nullptr) { 316 // exception occurred 317 if (!reuse_jbuf_start) { 318 DeleteBuffer(env, j_start_buf); 319 } 320 MaybeUnlockForReuse(mtx_shortest, reuse_jbuf_start || reuse_jbuf_limit); 321 env->ExceptionDescribe(); // print out exception to stderr 322 releaseJniEnv(attached_thread); 323 return; 324 } 325 326 jint jstart_len = env->CallStaticIntMethod( 327 m_abstract_comparator_jni_bridge_clazz, m_jshortest_mid, 328 m_jcallback_obj, 329 j_start_buf, reuse_jbuf_start ? start->length() : -1, 330 j_limit_buf, reuse_jbuf_limit ? limit.size() : -1); 331 332 if (env->ExceptionCheck()) { 333 // exception thrown from CallIntMethod 334 env->ExceptionDescribe(); // print out exception to stderr 335 336 } else if (static_cast<size_t>(jstart_len) != start->length()) { 337 // start buffer has changed in Java, so update `start` with the result 338 bool copy_from_non_direct = false; 339 if (reuse_jbuf_start) { 340 // reused a buffer 341 if (m_options->direct_buffer) { 342 // reused direct buffer 343 void* start_buf = env->GetDirectBufferAddress(j_start_buf); 344 if (start_buf == nullptr) { 345 if (!reuse_jbuf_start) { 346 DeleteBuffer(env, j_start_buf); 347 } 348 if (!reuse_jbuf_limit) { 349 DeleteBuffer(env, j_limit_buf); 350 } 351 MaybeUnlockForReuse(mtx_shortest, reuse_jbuf_start || reuse_jbuf_limit); 352 ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew( 353 env, "Unable to get Direct Buffer Address"); 354 env->ExceptionDescribe(); // print out exception to stderr 355 releaseJniEnv(attached_thread); 356 return; 357 } 358 start->assign(static_cast<const char*>(start_buf), jstart_len); 359 360 } else { 361 362 // reused non-direct buffer 363 copy_from_non_direct = true; 364 } 365 } else { 366 // there was a new buffer 367 if (m_options->direct_buffer) { 368 // it was direct... don't forget to potentially truncate the `start` string 369 start->resize(jstart_len); 370 } else { 371 // it was non-direct 372 copy_from_non_direct = true; 373 } 374 } 375 376 if (copy_from_non_direct) { 377 jbyteArray jarray = ByteBufferJni::array(env, j_start_buf, 378 m_jbytebuffer_clazz); 379 if (jarray == nullptr) { 380 if (!reuse_jbuf_start) { 381 DeleteBuffer(env, j_start_buf); 382 } 383 if (!reuse_jbuf_limit) { 384 DeleteBuffer(env, j_limit_buf); 385 } 386 MaybeUnlockForReuse(mtx_shortest, reuse_jbuf_start || reuse_jbuf_limit); 387 env->ExceptionDescribe(); // print out exception to stderr 388 releaseJniEnv(attached_thread); 389 return; 390 } 391 jboolean has_exception = JNI_FALSE; 392 JniUtil::byteString<std::string>(env, jarray, [start, jstart_len](const char* data, const size_t) { 393 return start->assign(data, static_cast<size_t>(jstart_len)); 394 }, &has_exception); 395 env->DeleteLocalRef(jarray); 396 if (has_exception == JNI_TRUE) { 397 if (!reuse_jbuf_start) { 398 DeleteBuffer(env, j_start_buf); 399 } 400 if (!reuse_jbuf_limit) { 401 DeleteBuffer(env, j_limit_buf); 402 } 403 env->ExceptionDescribe(); // print out exception to stderr 404 MaybeUnlockForReuse(mtx_shortest, reuse_jbuf_start || reuse_jbuf_limit); 405 releaseJniEnv(attached_thread); 406 return; 407 } 408 } 409 } 410 411 if (!reuse_jbuf_start) { 412 DeleteBuffer(env, j_start_buf); 413 } 414 if (!reuse_jbuf_limit) { 415 DeleteBuffer(env, j_limit_buf); 416 } 417 418 MaybeUnlockForReuse(mtx_shortest, reuse_jbuf_start || reuse_jbuf_limit); 419 420 releaseJniEnv(attached_thread); 421 } 422 423 void ComparatorJniCallback::FindShortSuccessor( 424 std::string* key) const { 425 if (key == nullptr) { 426 return; 427 } 428 429 jboolean attached_thread = JNI_FALSE; 430 JNIEnv* env = getJniEnv(&attached_thread); 431 assert(env != nullptr); 432 433 const bool reuse_jbuf_key = 434 static_cast<int64_t>(key->length()) <= m_options->max_reused_buffer_size; 435 436 MaybeLockForReuse(mtx_short, reuse_jbuf_key); 437 438 Slice skey(key->data(), key->length()); 439 jobject j_key_buf = GetBuffer(env, skey, reuse_jbuf_key, m_tl_buf_a, m_jshort_buf_key); 440 if (j_key_buf == nullptr) { 441 // exception occurred 442 MaybeUnlockForReuse(mtx_short, reuse_jbuf_key); 443 env->ExceptionDescribe(); // print out exception to stderr 444 releaseJniEnv(attached_thread); 445 return; 446 } 447 448 jint jkey_len = env->CallStaticIntMethod( 449 m_abstract_comparator_jni_bridge_clazz, m_jshort_mid, 450 m_jcallback_obj, 451 j_key_buf, reuse_jbuf_key ? key->length() : -1); 452 453 if (env->ExceptionCheck()) { 454 // exception thrown from CallObjectMethod 455 if (!reuse_jbuf_key) { 456 DeleteBuffer(env, j_key_buf); 457 } 458 MaybeUnlockForReuse(mtx_short, reuse_jbuf_key); 459 env->ExceptionDescribe(); // print out exception to stderr 460 releaseJniEnv(attached_thread); 461 return; 462 463 } 464 465 if (static_cast<size_t>(jkey_len) != key->length()) { 466 // key buffer has changed in Java, so update `key` with the result 467 bool copy_from_non_direct = false; 468 if (reuse_jbuf_key) { 469 // reused a buffer 470 if (m_options->direct_buffer) { 471 // reused direct buffer 472 void* key_buf = env->GetDirectBufferAddress(j_key_buf); 473 if (key_buf == nullptr) { 474 ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew( 475 env, "Unable to get Direct Buffer Address"); 476 if (!reuse_jbuf_key) { 477 DeleteBuffer(env, j_key_buf); 478 } 479 MaybeUnlockForReuse(mtx_short, reuse_jbuf_key); 480 env->ExceptionDescribe(); // print out exception to stderr 481 releaseJniEnv(attached_thread); 482 return; 483 } 484 key->assign(static_cast<const char*>(key_buf), jkey_len); 485 } else { 486 // reused non-direct buffer 487 copy_from_non_direct = true; 488 } 489 } else { 490 // there was a new buffer 491 if (m_options->direct_buffer) { 492 // it was direct... don't forget to potentially truncate the `key` string 493 key->resize(jkey_len); 494 } else { 495 // it was non-direct 496 copy_from_non_direct = true; 497 } 498 } 499 500 if (copy_from_non_direct) { 501 jbyteArray jarray = ByteBufferJni::array(env, j_key_buf, 502 m_jbytebuffer_clazz); 503 if (jarray == nullptr) { 504 505 if (!reuse_jbuf_key) { 506 DeleteBuffer(env, j_key_buf); 507 } 508 MaybeUnlockForReuse(mtx_short, reuse_jbuf_key); 509 env->ExceptionDescribe(); // print out exception to stderr 510 releaseJniEnv(attached_thread); 511 return; 512 } 513 jboolean has_exception = JNI_FALSE; 514 JniUtil::byteString<std::string>(env, jarray, [key, jkey_len](const char* data, const size_t) { 515 return key->assign(data, static_cast<size_t>(jkey_len)); 516 }, &has_exception); 517 env->DeleteLocalRef(jarray); 518 if (has_exception == JNI_TRUE) { 519 if (!reuse_jbuf_key) { 520 DeleteBuffer(env, j_key_buf); 521 } 522 MaybeUnlockForReuse(mtx_short, reuse_jbuf_key); 523 env->ExceptionDescribe(); // print out exception to stderr 524 releaseJniEnv(attached_thread); 525 return; 526 } 527 } 528 } 529 530 if (!reuse_jbuf_key) { 531 DeleteBuffer(env, j_key_buf); 532 } 533 534 MaybeUnlockForReuse(mtx_short, reuse_jbuf_key); 535 536 releaseJniEnv(attached_thread); 537 } 538 539 inline void ComparatorJniCallback::MaybeLockForReuse( 540 const std::unique_ptr<port::Mutex>& mutex, const bool cond) const { 541 // no need to lock if using thread_local 542 if (m_options->reused_synchronisation_type != ReusedSynchronisationType::THREAD_LOCAL 543 && cond) { 544 mutex.get()->Lock(); 545 } 546 } 547 548 inline void ComparatorJniCallback::MaybeUnlockForReuse( 549 const std::unique_ptr<port::Mutex>& mutex, const bool cond) const { 550 // no need to unlock if using thread_local 551 if (m_options->reused_synchronisation_type != ReusedSynchronisationType::THREAD_LOCAL 552 && cond) { 553 mutex.get()->Unlock(); 554 } 555 } 556 557 jobject ComparatorJniCallback::GetBuffer(JNIEnv* env, const Slice& src, 558 bool reuse_buffer, ThreadLocalPtr* tl_buf, jobject jreuse_buffer) const { 559 if (reuse_buffer) { 560 if (m_options->reused_synchronisation_type 561 == ReusedSynchronisationType::THREAD_LOCAL) { 562 563 // reuse thread-local bufffer 564 ThreadLocalBuf* tlb = reinterpret_cast<ThreadLocalBuf*>(tl_buf->Get()); 565 if (tlb == nullptr) { 566 // thread-local buffer has not yet been created, so create it 567 jobject jtl_buf = env->NewGlobalRef(ByteBufferJni::construct( 568 env, m_options->direct_buffer, m_options->max_reused_buffer_size, 569 m_jbytebuffer_clazz)); 570 if (jtl_buf == nullptr) { 571 // exception thrown: OutOfMemoryError 572 return nullptr; 573 } 574 tlb = new ThreadLocalBuf(m_jvm, m_options->direct_buffer, jtl_buf); 575 tl_buf->Reset(tlb); 576 } 577 return ReuseBuffer(env, src, tlb->jbuf); 578 } else { 579 580 // reuse class member buffer 581 return ReuseBuffer(env, src, jreuse_buffer); 582 } 583 } else { 584 585 // new buffer 586 return NewBuffer(env, src); 587 } 588 } 589 590 jobject ComparatorJniCallback::ReuseBuffer( 591 JNIEnv* env, const Slice& src, jobject jreuse_buffer) const { 592 // we can reuse the buffer 593 if (m_options->direct_buffer) { 594 // copy into direct buffer 595 void* buf = env->GetDirectBufferAddress(jreuse_buffer); 596 if (buf == nullptr) { 597 // either memory region is undefined, given object is not a direct java.nio.Buffer, or JNI access to direct buffers is not supported by this virtual machine. 598 ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew( 599 env, "Unable to get Direct Buffer Address"); 600 return nullptr; 601 } 602 memcpy(buf, src.data(), src.size()); 603 } else { 604 // copy into non-direct buffer 605 const jbyteArray jarray = ByteBufferJni::array(env, jreuse_buffer, 606 m_jbytebuffer_clazz); 607 if (jarray == nullptr) { 608 // exception occurred 609 return nullptr; 610 } 611 env->SetByteArrayRegion(jarray, 0, static_cast<jsize>(src.size()), 612 const_cast<jbyte*>(reinterpret_cast<const jbyte*>(src.data()))); 613 if (env->ExceptionCheck()) { 614 // exception occurred 615 env->DeleteLocalRef(jarray); 616 return nullptr; 617 } 618 env->DeleteLocalRef(jarray); 619 } 620 return jreuse_buffer; 621 } 622 623 jobject ComparatorJniCallback::NewBuffer(JNIEnv* env, const Slice& src) const { 624 // we need a new buffer 625 jobject jbuf = ByteBufferJni::constructWith(env, m_options->direct_buffer, 626 src.data(), src.size(), m_jbytebuffer_clazz); 627 if (jbuf == nullptr) { 628 // exception occurred 629 return nullptr; 630 } 631 return jbuf; 632 } 633 634 void ComparatorJniCallback::DeleteBuffer(JNIEnv* env, jobject jbuffer) const { 635 env->DeleteLocalRef(jbuffer); 636 } 637 638 } // namespace ROCKSDB_NAMESPACE 639