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