| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- // This file defines helper methods for Java API write methods
- //
- #pragma once
- #include <jni.h>
- #include <cstring>
- #include <exception>
- #include <functional>
- #include <string>
- #include "rocksdb/rocksdb_namespace.h"
- #include "rocksdb/slice.h"
- #include "rocksdb/status.h"
- #include "rocksjni/portal.h"
- namespace ROCKSDB_NAMESPACE {
- /**
- * @brief Exception class used to make the flow of key/value (Put(), Get(),
- * Merge(), ...) calls clearer.
- *
- * This class is used by Java API JNI methods in try { save/fetch } catch { ...
- * } style.
- *
- */
- class KVException : public std::exception {
- public:
- // These values are expected on Java API calls to represent the result of a
- // Get() which has failed; a negative length is returned to indicate an error.
- static const int kNotFound = -1; // the key was not found in RocksDB
- static const int kStatusError =
- -2; // there was some other error fetching the value for the key
- /**
- * @brief Throw a KVException (and potentially a Java exception) if the
- * RocksDB status is "bad"
- *
- * @param env JNI environment needed to create a Java exception
- * @param status RocksDB status to examine
- */
- static void ThrowOnError(JNIEnv* env, const Status& status) {
- if (status.ok()) {
- return;
- }
- if (status.IsNotFound()) {
- // IsNotFound does not generate a Java Exception, any other bad status
- // does..
- throw KVException(kNotFound);
- }
- ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, status);
- throw KVException(kStatusError);
- }
- /**
- * @brief Throw a KVException and a Java exception
- *
- * @param env JNI environment needed to create a Java exception
- * @param message content of the exception we will throw
- */
- static void ThrowNew(JNIEnv* env, const std::string& message) {
- ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, message);
- throw KVException(kStatusError);
- }
- /**
- * @brief Throw a KVException if there is already a Java exception in the JNI
- * enviroment
- *
- * @param env
- */
- static void ThrowOnError(JNIEnv* env) {
- if (env->ExceptionCheck()) {
- throw KVException(kStatusError);
- }
- }
- KVException(jint code) : kCode_(code){};
- virtual const char* what() const noexcept {
- return "Exception raised by JNI. There may be a Java exception in the "
- "JNIEnv. Please check!";
- }
- jint Code() const { return kCode_; }
- private:
- jint kCode_;
- };
- /**
- * @brief Construct a slice with the contents of a Java byte array
- *
- * The slice refers to an array into which the Java byte array's whole region is
- * copied
- */
- class JByteArraySlice {
- public:
- JByteArraySlice(JNIEnv* env, const jbyteArray& jarr, const jint jarr_off,
- const jint jarr_len)
- : arr_(new jbyte[jarr_len]),
- slice_(reinterpret_cast<char*>(arr_), jarr_len) {
- env->GetByteArrayRegion(jarr, jarr_off, jarr_len, arr_);
- KVException::ThrowOnError(env);
- };
- ~JByteArraySlice() {
- slice_.clear();
- delete[] arr_;
- };
- Slice& slice() { return slice_; }
- private:
- jbyte* arr_;
- Slice slice_;
- };
- /**
- * @brief Construct a slice with the contents of a direct Java ByterBuffer
- *
- * The slice refers directly to the contents of the buffer, no copy is made.
- *
- */
- class JDirectBufferSlice {
- public:
- JDirectBufferSlice(JNIEnv* env, const jobject& jbuffer,
- const jint jbuffer_off, const jint jbuffer_len)
- : slice_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
- jbuffer_off,
- jbuffer_len) {
- KVException::ThrowOnError(env);
- jlong capacity = env->GetDirectBufferCapacity(jbuffer);
- if (capacity < jbuffer_off + jbuffer_len) {
- auto message = "Direct buffer offset " + std::to_string(jbuffer_off) +
- " + length " + std::to_string(jbuffer_len) +
- " exceeds capacity " + std::to_string(capacity);
- KVException::ThrowNew(env, message);
- slice_.clear();
- }
- }
- ~JDirectBufferSlice() { slice_.clear(); };
- Slice& slice() { return slice_; }
- private:
- Slice slice_;
- };
- /**
- * @brief Wrap a pinnable slice with a method to retrieve the contents back into
- * Java
- *
- * The Java Byte Array version sets the byte array's region from the slice
- */
- class JByteArrayPinnableSlice {
- public:
- /**
- * @brief Construct a new JByteArrayPinnableSlice object referring to an
- * existing java byte buffer
- *
- * @param env
- * @param jbuffer
- * @param jbuffer_off
- * @param jbuffer_len
- */
- JByteArrayPinnableSlice(JNIEnv* env, const jbyteArray& jbuffer,
- const jint jbuffer_off, const jint jbuffer_len)
- : env_(env),
- jbuffer_(jbuffer),
- jbuffer_off_(jbuffer_off),
- jbuffer_len_(jbuffer_len){};
- /**
- * @brief Construct an empty new JByteArrayPinnableSlice object
- *
- */
- JByteArrayPinnableSlice(JNIEnv* env) : env_(env){};
- PinnableSlice& pinnable_slice() { return pinnable_slice_; }
- ~JByteArrayPinnableSlice() { pinnable_slice_.Reset(); };
- /**
- * @brief copy back contents of the pinnable slice into the Java ByteBuffer
- *
- * @return jint min of size of buffer and number of bytes in value for
- * requested key
- */
- jint Fetch() {
- const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
- const jint result_len = std::min(jbuffer_len_, pinnable_len);
- env_->SetByteArrayRegion(
- jbuffer_, jbuffer_off_, result_len,
- reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
- KVException::ThrowOnError(
- env_); // exception thrown: ArrayIndexOutOfBoundsException
- return pinnable_len;
- };
- /**
- * @brief create a new Java buffer and copy the result into it
- *
- * @return jbyteArray the java buffer holding the result
- */
- jbyteArray NewByteArray() {
- const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
- jbyteArray jbuffer =
- ROCKSDB_NAMESPACE::JniUtil::createJavaByteArrayWithSizeCheck(
- env_, pinnable_slice_.data(), pinnable_len);
- KVException::ThrowOnError(env_); // OutOfMemoryError
- return jbuffer;
- }
- private:
- JNIEnv* env_;
- jbyteArray jbuffer_;
- jint jbuffer_off_;
- jint jbuffer_len_;
- PinnableSlice pinnable_slice_;
- };
- /**
- * @brief Wrap a pinnable slice with a method to retrieve the contents back into
- * Java
- *
- * The Java Direct Buffer version copies the memory of the buffer from the slice
- */
- class JDirectBufferPinnableSlice {
- public:
- JDirectBufferPinnableSlice(JNIEnv* env, const jobject& jbuffer,
- const jint jbuffer_off, const jint jbuffer_len)
- : buffer_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
- jbuffer_off),
- jbuffer_len_(jbuffer_len) {
- jlong capacity = env->GetDirectBufferCapacity(jbuffer);
- if (capacity < jbuffer_off + jbuffer_len) {
- auto message =
- "Invalid value argument. Capacity is less than requested region. "
- "offset " +
- std::to_string(jbuffer_off) + " + length " +
- std::to_string(jbuffer_len) + " exceeds capacity " +
- std::to_string(capacity);
- KVException::ThrowNew(env, message);
- }
- }
- PinnableSlice& pinnable_slice() { return pinnable_slice_; }
- ~JDirectBufferPinnableSlice() { pinnable_slice_.Reset(); };
- /**
- * @brief copy back contents of the pinnable slice into the Java DirectBuffer
- *
- * @return jint min of size of buffer and number of bytes in value for
- * requested key
- */
- jint Fetch() {
- const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
- const jint result_len = std::min(jbuffer_len_, pinnable_len);
- memcpy(buffer_, pinnable_slice_.data(), result_len);
- return pinnable_len;
- };
- private:
- char* buffer_;
- jint jbuffer_len_;
- PinnableSlice pinnable_slice_;
- };
- } // namespace ROCKSDB_NAMESPACE
|