kv_helper.h 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 defines helper methods for Java API write methods
  7. //
  8. #pragma once
  9. #include <jni.h>
  10. #include <cstring>
  11. #include <exception>
  12. #include <functional>
  13. #include <string>
  14. #include "rocksdb/rocksdb_namespace.h"
  15. #include "rocksdb/slice.h"
  16. #include "rocksdb/status.h"
  17. #include "rocksjni/portal.h"
  18. namespace ROCKSDB_NAMESPACE {
  19. /**
  20. * @brief Exception class used to make the flow of key/value (Put(), Get(),
  21. * Merge(), ...) calls clearer.
  22. *
  23. * This class is used by Java API JNI methods in try { save/fetch } catch { ...
  24. * } style.
  25. *
  26. */
  27. class KVException : public std::exception {
  28. public:
  29. // These values are expected on Java API calls to represent the result of a
  30. // Get() which has failed; a negative length is returned to indicate an error.
  31. static const int kNotFound = -1; // the key was not found in RocksDB
  32. static const int kStatusError =
  33. -2; // there was some other error fetching the value for the key
  34. /**
  35. * @brief Throw a KVException (and potentially a Java exception) if the
  36. * RocksDB status is "bad"
  37. *
  38. * @param env JNI environment needed to create a Java exception
  39. * @param status RocksDB status to examine
  40. */
  41. static void ThrowOnError(JNIEnv* env, const Status& status) {
  42. if (status.ok()) {
  43. return;
  44. }
  45. if (status.IsNotFound()) {
  46. // IsNotFound does not generate a Java Exception, any other bad status
  47. // does..
  48. throw KVException(kNotFound);
  49. }
  50. ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, status);
  51. throw KVException(kStatusError);
  52. }
  53. /**
  54. * @brief Throw a KVException and a Java exception
  55. *
  56. * @param env JNI environment needed to create a Java exception
  57. * @param message content of the exception we will throw
  58. */
  59. static void ThrowNew(JNIEnv* env, const std::string& message) {
  60. ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, message);
  61. throw KVException(kStatusError);
  62. }
  63. /**
  64. * @brief Throw a KVException if there is already a Java exception in the JNI
  65. * enviroment
  66. *
  67. * @param env
  68. */
  69. static void ThrowOnError(JNIEnv* env) {
  70. if (env->ExceptionCheck()) {
  71. throw KVException(kStatusError);
  72. }
  73. }
  74. KVException(jint code) : kCode_(code){};
  75. virtual const char* what() const noexcept {
  76. return "Exception raised by JNI. There may be a Java exception in the "
  77. "JNIEnv. Please check!";
  78. }
  79. jint Code() const { return kCode_; }
  80. private:
  81. jint kCode_;
  82. };
  83. /**
  84. * @brief Construct a slice with the contents of a Java byte array
  85. *
  86. * The slice refers to an array into which the Java byte array's whole region is
  87. * copied
  88. */
  89. class JByteArraySlice {
  90. public:
  91. JByteArraySlice(JNIEnv* env, const jbyteArray& jarr, const jint jarr_off,
  92. const jint jarr_len)
  93. : arr_(new jbyte[jarr_len]),
  94. slice_(reinterpret_cast<char*>(arr_), jarr_len) {
  95. env->GetByteArrayRegion(jarr, jarr_off, jarr_len, arr_);
  96. KVException::ThrowOnError(env);
  97. };
  98. ~JByteArraySlice() {
  99. slice_.clear();
  100. delete[] arr_;
  101. };
  102. Slice& slice() { return slice_; }
  103. private:
  104. jbyte* arr_;
  105. Slice slice_;
  106. };
  107. /**
  108. * @brief Construct a slice with the contents of a direct Java ByterBuffer
  109. *
  110. * The slice refers directly to the contents of the buffer, no copy is made.
  111. *
  112. */
  113. class JDirectBufferSlice {
  114. public:
  115. JDirectBufferSlice(JNIEnv* env, const jobject& jbuffer,
  116. const jint jbuffer_off, const jint jbuffer_len)
  117. : slice_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
  118. jbuffer_off,
  119. jbuffer_len) {
  120. KVException::ThrowOnError(env);
  121. jlong capacity = env->GetDirectBufferCapacity(jbuffer);
  122. if (capacity < jbuffer_off + jbuffer_len) {
  123. auto message = "Direct buffer offset " + std::to_string(jbuffer_off) +
  124. " + length " + std::to_string(jbuffer_len) +
  125. " exceeds capacity " + std::to_string(capacity);
  126. KVException::ThrowNew(env, message);
  127. slice_.clear();
  128. }
  129. }
  130. ~JDirectBufferSlice() { slice_.clear(); };
  131. Slice& slice() { return slice_; }
  132. private:
  133. Slice slice_;
  134. };
  135. /**
  136. * @brief Wrap a pinnable slice with a method to retrieve the contents back into
  137. * Java
  138. *
  139. * The Java Byte Array version sets the byte array's region from the slice
  140. */
  141. class JByteArrayPinnableSlice {
  142. public:
  143. /**
  144. * @brief Construct a new JByteArrayPinnableSlice object referring to an
  145. * existing java byte buffer
  146. *
  147. * @param env
  148. * @param jbuffer
  149. * @param jbuffer_off
  150. * @param jbuffer_len
  151. */
  152. JByteArrayPinnableSlice(JNIEnv* env, const jbyteArray& jbuffer,
  153. const jint jbuffer_off, const jint jbuffer_len)
  154. : env_(env),
  155. jbuffer_(jbuffer),
  156. jbuffer_off_(jbuffer_off),
  157. jbuffer_len_(jbuffer_len){};
  158. /**
  159. * @brief Construct an empty new JByteArrayPinnableSlice object
  160. *
  161. */
  162. JByteArrayPinnableSlice(JNIEnv* env) : env_(env){};
  163. PinnableSlice& pinnable_slice() { return pinnable_slice_; }
  164. ~JByteArrayPinnableSlice() { pinnable_slice_.Reset(); };
  165. /**
  166. * @brief copy back contents of the pinnable slice into the Java ByteBuffer
  167. *
  168. * @return jint min of size of buffer and number of bytes in value for
  169. * requested key
  170. */
  171. jint Fetch() {
  172. const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
  173. const jint result_len = std::min(jbuffer_len_, pinnable_len);
  174. env_->SetByteArrayRegion(
  175. jbuffer_, jbuffer_off_, result_len,
  176. reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
  177. KVException::ThrowOnError(
  178. env_); // exception thrown: ArrayIndexOutOfBoundsException
  179. return pinnable_len;
  180. };
  181. /**
  182. * @brief create a new Java buffer and copy the result into it
  183. *
  184. * @return jbyteArray the java buffer holding the result
  185. */
  186. jbyteArray NewByteArray() {
  187. const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
  188. jbyteArray jbuffer =
  189. ROCKSDB_NAMESPACE::JniUtil::createJavaByteArrayWithSizeCheck(
  190. env_, pinnable_slice_.data(), pinnable_len);
  191. KVException::ThrowOnError(env_); // OutOfMemoryError
  192. return jbuffer;
  193. }
  194. private:
  195. JNIEnv* env_;
  196. jbyteArray jbuffer_;
  197. jint jbuffer_off_;
  198. jint jbuffer_len_;
  199. PinnableSlice pinnable_slice_;
  200. };
  201. /**
  202. * @brief Wrap a pinnable slice with a method to retrieve the contents back into
  203. * Java
  204. *
  205. * The Java Direct Buffer version copies the memory of the buffer from the slice
  206. */
  207. class JDirectBufferPinnableSlice {
  208. public:
  209. JDirectBufferPinnableSlice(JNIEnv* env, const jobject& jbuffer,
  210. const jint jbuffer_off, const jint jbuffer_len)
  211. : buffer_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
  212. jbuffer_off),
  213. jbuffer_len_(jbuffer_len) {
  214. jlong capacity = env->GetDirectBufferCapacity(jbuffer);
  215. if (capacity < jbuffer_off + jbuffer_len) {
  216. auto message =
  217. "Invalid value argument. Capacity is less than requested region. "
  218. "offset " +
  219. std::to_string(jbuffer_off) + " + length " +
  220. std::to_string(jbuffer_len) + " exceeds capacity " +
  221. std::to_string(capacity);
  222. KVException::ThrowNew(env, message);
  223. }
  224. }
  225. PinnableSlice& pinnable_slice() { return pinnable_slice_; }
  226. ~JDirectBufferPinnableSlice() { pinnable_slice_.Reset(); };
  227. /**
  228. * @brief copy back contents of the pinnable slice into the Java DirectBuffer
  229. *
  230. * @return jint min of size of buffer and number of bytes in value for
  231. * requested key
  232. */
  233. jint Fetch() {
  234. const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
  235. const jint result_len = std::min(jbuffer_len_, pinnable_len);
  236. memcpy(buffer_, pinnable_slice_.data(), result_len);
  237. return pinnable_len;
  238. };
  239. private:
  240. char* buffer_;
  241. jint jbuffer_len_;
  242. PinnableSlice pinnable_slice_;
  243. };
  244. } // namespace ROCKSDB_NAMESPACE