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 {
ComparatorJniCallback(JNIEnv * env,jobject jcomparator,const ComparatorJniCallbackOptions * options)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
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
~ComparatorJniCallback()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
Name() const223 const char* ComparatorJniCallback::Name() const {
224 return m_name.get();
225 }
226
Compare(const Slice & a,const Slice & b) const227 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
FindShortestSeparator(std::string * start,const Slice & limit) const287 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
FindShortSuccessor(std::string * key) const423 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
MaybeLockForReuse(const std::unique_ptr<port::Mutex> & mutex,const bool cond) const539 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
MaybeUnlockForReuse(const std::unique_ptr<port::Mutex> & mutex,const bool cond) const548 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
GetBuffer(JNIEnv * env,const Slice & src,bool reuse_buffer,ThreadLocalPtr * tl_buf,jobject jreuse_buffer) const557 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
ReuseBuffer(JNIEnv * env,const Slice & src,jobject jreuse_buffer) const590 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
NewBuffer(JNIEnv * env,const Slice & src) const623 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
DeleteBuffer(JNIEnv * env,jobject jbuffer) const634 void ComparatorJniCallback::DeleteBuffer(JNIEnv* env, jobject jbuffer) const {
635 env->DeleteLocalRef(jbuffer);
636 }
637
638 } // namespace ROCKSDB_NAMESPACE
639