Merge "Rewrite scoped arrays with template and introduced nullable one." am: 3204e23c06
am: 2446e5dfef
Change-Id: Ie9b3a6882975af0ef844c245a9ed984071c335a6
diff --git a/header_only_include/nativehelper/scoped_primitive_array.h b/header_only_include/nativehelper/scoped_primitive_array.h
index d6840c2..17f18a3 100644
--- a/header_only_include/nativehelper/scoped_primitive_array.h
+++ b/header_only_include/nativehelper/scoped_primitive_array.h
@@ -17,6 +17,10 @@
#ifndef SCOPED_PRIMITIVE_ARRAY_H_
#define SCOPED_PRIMITIVE_ARRAY_H_
+#include <type_traits>
+
+#include <sys/types.h> // For ssize_t
+
#include "jni.h"
#include "nativehelper_utils.h"
@@ -26,122 +30,184 @@
#define POINTER_TYPE(T) T* /* NOLINT */
#endif
-#ifdef REFERENCE_TYPE
-#error REFERENCE_TYPE is defined.
-#else
-#define REFERENCE_TYPE(T) T& /* NOLINT */
-#endif
+template<typename JType> struct ScopedPrimitiveArrayTraits {};
-// ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO,
-// ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide
-// convenient read-only access to Java arrays from JNI code. This is cheaper than read-write
-// access and should be used by default.
-#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \
- class Scoped ## NAME ## ArrayRO { \
- public: \
- explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \
- : mEnv(env), mJavaArray(NULL), mRawArray(NULL), mSize(0) {} \
- Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
- : mEnv(env) { \
- if (javaArray == NULL) { \
- mJavaArray = NULL; \
- mSize = 0; \
- mRawArray = NULL; \
- jniThrowNullPointerException(mEnv, NULL); \
- } else { \
- reset(javaArray); \
- } \
- } \
- ~Scoped ## NAME ## ArrayRO() { \
- if (mRawArray != NULL && mRawArray != mBuffer) { \
- mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \
- } \
- } \
- void reset(PRIMITIVE_TYPE ## Array javaArray) { \
- mJavaArray = javaArray; \
- mSize = mEnv->GetArrayLength(mJavaArray); \
- if (mSize <= buffer_size) { \
- mEnv->Get ## NAME ## ArrayRegion(mJavaArray, 0, mSize, mBuffer); \
- mRawArray = mBuffer; \
- } else { \
- mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
- } \
- } \
- const PRIMITIVE_TYPE* get() const { return mRawArray; } \
- PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
- const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
- size_t size() const { return mSize; } \
- private: \
- static const jsize buffer_size = 1024; \
- JNIEnv* const mEnv; \
- PRIMITIVE_TYPE ## Array mJavaArray; \
- POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
- jsize mSize; \
- PRIMITIVE_TYPE mBuffer[buffer_size]; \
- DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRO); \
- }
+#define ARRAY_TRAITS(ARRAY_TYPE, JTYPE, NAME) \
+template<> struct ScopedPrimitiveArrayTraits<JTYPE> { \
+public: \
+ static inline void getArrayRegion(JNIEnv* env, ARRAY_TYPE array, size_t start, \
+ size_t len, POINTER_TYPE(JTYPE) out) { \
+ env->Get ## NAME ## ArrayRegion(array, start, len, out); \
+ } \
+ \
+ static inline POINTER_TYPE(JTYPE) getArrayElements(JNIEnv* env, ARRAY_TYPE array) { \
+ return env->Get ## NAME ## ArrayElements(array, nullptr); \
+ } \
+ \
+ static inline void releaseArrayElements(JNIEnv* env, ARRAY_TYPE array, \
+ POINTER_TYPE(JTYPE) buffer, jint mode) { \
+ env->Release ## NAME ## ArrayElements(array, buffer, mode); \
+ } \
+ static inline size_t getArrayLength(JNIEnv* env, ARRAY_TYPE array) { \
+ return env->GetArrayLength(array); \
+ } \
+ static inline void fatalError(JNIEnv* env, const char*msg) { \
+ env->FatalError(msg); \
+ } \
+ using ArrayType = ARRAY_TYPE; \
+}; \
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short);
+ARRAY_TRAITS(jbooleanArray, jboolean, Boolean)
+ARRAY_TRAITS(jbyteArray, jbyte, Byte)
+ARRAY_TRAITS(jcharArray, jchar, Char)
+ARRAY_TRAITS(jdoubleArray, jdouble, Double)
+ARRAY_TRAITS(jfloatArray, jfloat, Float)
+ARRAY_TRAITS(jintArray, jint, Int)
+ARRAY_TRAITS(jlongArray, jlong, Long)
+ARRAY_TRAITS(jshortArray, jshort, Short)
-#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO
-
-// ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW,
-// ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide
-// convenient read-write access to Java arrays from JNI code. These are more expensive,
-// since they entail a copy back onto the Java heap, and should only be used when necessary.
-#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \
- class Scoped ## NAME ## ArrayRW { \
- public: \
- explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \
- : mEnv(env), mJavaArray(NULL), mRawArray(NULL) {} \
- Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
- : mEnv(env), mJavaArray(javaArray), mRawArray(NULL) { \
- if (mJavaArray == NULL) { \
- jniThrowNullPointerException(mEnv, NULL); \
- } else { \
- mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
- } \
- } \
- ~Scoped ## NAME ## ArrayRW() { \
- if (mRawArray) { \
- mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \
- } \
- } \
- void reset(PRIMITIVE_TYPE ## Array javaArray) { \
- mJavaArray = javaArray; \
- mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
- } \
- const PRIMITIVE_TYPE* get() const { return mRawArray; } \
- PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
- const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
- POINTER_TYPE(PRIMITIVE_TYPE) get() { return mRawArray; } \
- REFERENCE_TYPE(PRIMITIVE_TYPE) operator[](size_t n) { return mRawArray[n]; } \
- size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \
- private: \
- JNIEnv* const mEnv; \
- PRIMITIVE_TYPE ## Array mJavaArray; \
- POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
- DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRW); \
- }
-
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long);
-INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short);
-
-#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW
+#undef ARRAY_TRAITS
#undef POINTER_TYPE
-#undef REFERENCE_TYPE
+
+template<typename JType, bool kNullable>
+class ScopedArrayRO {
+public:
+ using Traits = ScopedPrimitiveArrayTraits<JType>;
+ using ArrayType = typename Traits::ArrayType;
+ using const_iterator = const JType*;
+
+ // Provides read-only access to Java array from JNI code.
+ // env must not be nullptr.
+ // If kNullable is false, this aborts if javaArray is nullptr.
+ ScopedArrayRO(JNIEnv* env, ArrayType javaArray) : mEnv(env), mJavaArray(javaArray) {
+ if (mJavaArray == nullptr) {
+ mSize = -1;
+ mRawArray = nullptr;
+ if (!kNullable) {
+ Traits::fatalError(mEnv, "javaArray is null");
+ }
+ } else {
+ mSize = Traits::getArrayLength(mEnv, mJavaArray);
+ if (mSize <= BUFFER_SIZE) {
+ Traits::getArrayRegion(mEnv, mJavaArray, 0, mSize, mBuffer);
+ mRawArray = mBuffer;
+ } else {
+ mRawArray = Traits::getArrayElements(mEnv, mJavaArray);
+ }
+ }
+ }
+
+ ~ScopedArrayRO() {
+ if (mRawArray != nullptr && mRawArray != mBuffer) {
+ Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, JNI_ABORT);
+ }
+ }
+
+ const JType* get() const { return mRawArray; }
+ ArrayType getJavaArray() const { return mJavaArray; }
+ const JType& operator[](size_t n) const { return mRawArray[n]; }
+ const_iterator begin() const { return get(); }
+ const_iterator end() const {
+ return (kNullable && mRawArray == nullptr) ? get() : get() + mSize;
+ }
+
+ using SizeT = typename std::conditional<kNullable, ssize_t, size_t>::type;
+ // In case of nonnull array, the return type is size_t.
+ // In case of nullable array, the return type is ssize_t. Then, will return -1 if this is
+ // constructed with null array.
+ SizeT size() const { return mSize; }
+
+private:
+ // 1024 since there is stack frame size limitation (4096 bytes).
+ constexpr static jsize BUFFER_SIZE = 1024 / sizeof(JType);
+
+ JNIEnv* const mEnv;
+ ArrayType mJavaArray;
+ JType* mRawArray;
+ SizeT mSize;
+
+ // Speed-up JNI array access for small arrays, see I703d7346de732199be1feadbead021c6647a554a
+ // for more details.
+ JType mBuffer[BUFFER_SIZE];
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedArrayRO);
+};
+
+// Scoped***ArrayRO provide convenient read-only access to Java array from JNI code.
+// This is cheaper than read-write access and should be used by default.
+// These abort if nullptr is passed.
+using ScopedBooleanArrayRO = ScopedArrayRO<jboolean, false>;
+using ScopedByteArrayRO = ScopedArrayRO<jbyte, false>;
+using ScopedCharArrayRO = ScopedArrayRO<jchar, false>;
+using ScopedDoubleArrayRO = ScopedArrayRO<jdouble, false>;
+using ScopedFloatArrayRO = ScopedArrayRO<jfloat, false>;
+using ScopedIntArrayRO = ScopedArrayRO<jint, false>;
+using ScopedLongArrayRO = ScopedArrayRO<jlong, false>;
+using ScopedShortArrayRO = ScopedArrayRO<jshort, false>;
+
+// ScopedNullable***ArrayRO also provide convenient read-only access to Java array from JNI code.
+// These accept nullptr. In that case, get() returns nullptr and size() returns -1.
+using ScopedNullableBooleanArrayRO = ScopedArrayRO<jboolean, true>;
+using ScopedNullableByteArrayRO = ScopedArrayRO<jbyte, true>;
+using ScopedNullableCharArrayRO = ScopedArrayRO<jchar, true>;
+using ScopedNullableDoubleArrayRO = ScopedArrayRO<jdouble, true>;
+using ScopedNullableFloatArrayRO = ScopedArrayRO<jfloat, true>;
+using ScopedNullableIntArrayRO = ScopedArrayRO<jint, true>;
+using ScopedNullableLongArrayRO = ScopedArrayRO<jlong, true>;
+using ScopedNullableShortArrayRO = ScopedArrayRO<jshort, true>;
+
+template<typename JType>
+class ScopedArrayRW {
+public:
+ using Traits = ScopedPrimitiveArrayTraits<JType>;
+ using ArrayType = typename Traits::ArrayType;
+ using const_iterator = const JType*;
+ using iterator = JType*;
+
+ ScopedArrayRW(JNIEnv* env, ArrayType javaArray) : mEnv(env), mJavaArray(javaArray) {
+ if (mJavaArray == nullptr) {
+ Traits::fatalError(mEnv, "javaArray is null");
+ } else {
+ mSize = Traits::getArrayLength(mEnv, mJavaArray);
+ mRawArray = Traits::getArrayElements(mEnv, mJavaArray);
+ }
+ }
+ ~ScopedArrayRW() {
+ if (mRawArray != nullptr) {
+ Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, 0);
+ }
+ }
+
+ const JType* get() const { return mRawArray; }
+ ArrayType getJavaArray() const { return mJavaArray; }
+ const JType& operator[](size_t n) const { return mRawArray[n]; }
+ const_iterator cbegin() const { return get(); }
+ const_iterator cend() const { return get() + mSize; }
+ JType* get() { return mRawArray; }
+ JType& operator[](size_t n) { return mRawArray[n]; }
+ iterator begin() { return get(); }
+ iterator end() { return get() + mSize; }
+ size_t size() const { return mSize; }
+
+private:
+ JNIEnv* const mEnv;
+ ArrayType mJavaArray;
+ JType* mRawArray;
+ jsize mSize;
+ DISALLOW_COPY_AND_ASSIGN(ScopedArrayRW);
+};
+
+// Scoped***ArrayRW provide convenient read-write access to Java arrays from JNI code.
+// These are more expensive, since they entail a copy back onto the Java heap, and should only be
+// used when necessary.
+// These abort if nullptr is passed.
+using ScopedBooleanArrayRW = ScopedArrayRW<jboolean>;
+using ScopedByteArrayRW = ScopedArrayRW<jbyte>;
+using ScopedCharArrayRW = ScopedArrayRW<jchar>;
+using ScopedDoubleArrayRW = ScopedArrayRW<jdouble>;
+using ScopedFloatArrayRW = ScopedArrayRW<jfloat>;
+using ScopedIntArrayRW = ScopedArrayRW<jint>;
+using ScopedLongArrayRW = ScopedArrayRW<jlong>;
+using ScopedShortArrayRW = ScopedArrayRW<jshort>;
#endif // SCOPED_PRIMITIVE_ARRAY_H_
diff --git a/tests/Android.bp b/tests/Android.bp
index e6cbf5c..d30b4d2 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -4,7 +4,10 @@
name: "JniInvocation_test",
test_suites: ["device-tests"],
host_supported: true,
- srcs: ["JniInvocation_test.cpp"],
+ srcs: [
+ "JniInvocation_test.cpp",
+ "scoped_primitive_array_test.cpp"
+ ],
cflags: ["-Wall", "-Werror"],
shared_libs: ["libnativehelper"],
}
diff --git a/tests/scoped_primitive_array_test.cpp b/tests/scoped_primitive_array_test.cpp
new file mode 100644
index 0000000..cba258c
--- /dev/null
+++ b/tests/scoped_primitive_array_test.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/scoped_primitive_array.h>
+
+#include <gtest/gtest.h>
+
+struct TestType { char dummy[1]; };
+using jTestTypeArray = void*;
+
+const jTestTypeArray LARGE_ARRAY = reinterpret_cast<jTestTypeArray>(0x1);
+const jTestTypeArray SMALL_ARRAY = reinterpret_cast<jTestTypeArray>(0x2);
+
+constexpr size_t LARGE_ARRAY_SIZE = 8192;
+constexpr size_t SMALL_ARRAY_SIZE = 32;
+
+struct TestContext {
+ TestType* dummyPtr;
+
+ int getArrayElementsCallCount = 0;
+ int releaseArrayElementsCallCount = 0;
+ bool aborted = false;
+ bool elementsUpdated = false;
+
+ void resetCallCount() {
+ getArrayElementsCallCount = 0;
+ releaseArrayElementsCallCount = 0;
+ aborted = false;
+ elementsUpdated = false;
+ }
+
+ bool memoryUpdated() const {
+ return releaseArrayElementsCallCount > 0 && elementsUpdated;
+ }
+};
+
+// Mock implementation of the ScopedPrimitiveArrayTraits.
+// JNIEnv is abused for passing TestContext.
+template<> struct ScopedPrimitiveArrayTraits<TestType> {
+public:
+ static inline void getArrayRegion(JNIEnv*, jTestTypeArray, size_t, size_t, TestType*) {}
+
+ static inline TestType* getArrayElements(JNIEnv* env, jTestTypeArray) {
+ TestContext* ctx = reinterpret_cast<TestContext*>(env);
+ ctx->getArrayElementsCallCount++;
+ return ctx->dummyPtr;
+ }
+
+ static inline void releaseArrayElements(JNIEnv* env, jTestTypeArray, TestType* buffer,
+ jint mode) {
+ TestContext* ctx = reinterpret_cast<TestContext*>(env);
+ if (ctx->dummyPtr == buffer) {
+ ctx->releaseArrayElementsCallCount++;
+ }
+ ctx->elementsUpdated = (mode != JNI_ABORT);
+ }
+
+ static inline size_t getArrayLength(JNIEnv*, jTestTypeArray array) {
+ return array == LARGE_ARRAY ? LARGE_ARRAY_SIZE : SMALL_ARRAY_SIZE;
+ }
+
+ static inline void fatalError(JNIEnv* env, const char*) {
+ reinterpret_cast<TestContext*>(env)->aborted = true;
+ }
+
+ using ArrayType = jTestTypeArray;
+};
+
+TEST(ScopedPrimitiveArrayTest, testNonNullArray) {
+ std::unique_ptr<TestType[]> dummyTestType = std::make_unique<TestType[]>(LARGE_ARRAY_SIZE);
+
+ TestContext context;
+ context.dummyPtr = dummyTestType.get();
+
+ JNIEnv* env = reinterpret_cast<JNIEnv*>(&context);
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRO<TestType, false /* non null */> array(env, SMALL_ARRAY);
+ EXPECT_NE(nullptr, array.get());
+ EXPECT_EQ(SMALL_ARRAY, array.getJavaArray());
+ EXPECT_NE(nullptr, array.begin());
+ EXPECT_NE(nullptr, array.end());
+ EXPECT_EQ(array.end(), array.begin() + SMALL_ARRAY_SIZE);
+ EXPECT_EQ(SMALL_ARRAY_SIZE, array.size());
+ }
+ EXPECT_EQ(context.getArrayElementsCallCount, context.releaseArrayElementsCallCount);
+ EXPECT_FALSE(context.memoryUpdated());
+ EXPECT_FALSE(context.aborted);
+ }
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRO<TestType, false /* non null */> array(env, LARGE_ARRAY);
+
+ EXPECT_EQ(context.dummyPtr, array.get());
+ EXPECT_EQ(LARGE_ARRAY, array.getJavaArray());
+ EXPECT_EQ(context.dummyPtr, array.begin());
+ EXPECT_EQ(context.dummyPtr + LARGE_ARRAY_SIZE, array.end());
+ EXPECT_EQ(LARGE_ARRAY_SIZE, array.size());
+ }
+ EXPECT_EQ(context.getArrayElementsCallCount, context.releaseArrayElementsCallCount);
+ EXPECT_FALSE(context.memoryUpdated());
+ EXPECT_FALSE(context.aborted);
+ }
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRO<TestType, false /* non null */> array(env, nullptr);
+ EXPECT_TRUE(context.aborted);
+ }
+ }
+}
+
+TEST(ScopedPrimitiveArrayTest, testNullableArray) {
+ std::unique_ptr<TestType[]> dummyTestType = std::make_unique<TestType[]>(LARGE_ARRAY_SIZE);
+
+ TestContext context;
+ context.dummyPtr = dummyTestType.get();
+
+ JNIEnv* env = reinterpret_cast<JNIEnv*>(&context);
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRO<TestType, true /* nullable */> array(env, SMALL_ARRAY);
+ EXPECT_NE(nullptr, array.get());
+ EXPECT_EQ(SMALL_ARRAY, array.getJavaArray());
+ EXPECT_NE(nullptr, array.begin());
+ EXPECT_NE(nullptr, array.end());
+ EXPECT_EQ(array.end(), array.begin() + SMALL_ARRAY_SIZE);
+ EXPECT_EQ(SMALL_ARRAY_SIZE, (size_t) array.size());
+ }
+ EXPECT_EQ(context.getArrayElementsCallCount, context.releaseArrayElementsCallCount);
+ EXPECT_FALSE(context.memoryUpdated());
+ EXPECT_FALSE(context.aborted);
+ }
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRO<TestType, true /* nullable */> array(env, LARGE_ARRAY);
+ EXPECT_EQ(context.dummyPtr, array.get());
+ EXPECT_EQ(LARGE_ARRAY, array.getJavaArray());
+ EXPECT_EQ(context.dummyPtr, array.begin());
+ EXPECT_EQ(context.dummyPtr + LARGE_ARRAY_SIZE, array.end());
+ EXPECT_EQ(LARGE_ARRAY_SIZE, (size_t) array.size());
+ }
+ EXPECT_EQ(context.getArrayElementsCallCount, context.releaseArrayElementsCallCount);
+ EXPECT_FALSE(context.memoryUpdated());
+ EXPECT_FALSE(context.aborted);
+ }
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRO<TestType, true /* nullable*/> array(env, nullptr);
+ EXPECT_EQ(nullptr, array.get());
+ EXPECT_EQ(nullptr, array.getJavaArray());
+ EXPECT_EQ(nullptr, array.begin());
+ EXPECT_EQ(nullptr, array.end());
+ EXPECT_EQ(-1, array.size());
+ }
+ EXPECT_EQ(context.getArrayElementsCallCount, context.releaseArrayElementsCallCount);
+ EXPECT_FALSE(context.memoryUpdated());
+ EXPECT_FALSE(context.aborted);
+ }
+}
+
+TEST(ScopedPrimitiveArrayTest, testArrayRW) {
+ std::unique_ptr<TestType[]> dummyTestType = std::make_unique<TestType[]>(LARGE_ARRAY_SIZE);
+
+ TestContext context;
+ context.dummyPtr = dummyTestType.get();
+
+ JNIEnv* env = reinterpret_cast<JNIEnv*>(&context);
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRW<TestType> array(env, SMALL_ARRAY);
+ EXPECT_NE(nullptr, array.get());
+ EXPECT_EQ(SMALL_ARRAY, array.getJavaArray());
+ EXPECT_NE(nullptr, array.begin());
+ EXPECT_NE(nullptr, array.end());
+ EXPECT_EQ(array.end(), array.begin() + SMALL_ARRAY_SIZE);
+ EXPECT_EQ(SMALL_ARRAY_SIZE, (size_t) array.size());
+ }
+ EXPECT_EQ(context.getArrayElementsCallCount, context.releaseArrayElementsCallCount);
+ EXPECT_TRUE(context.memoryUpdated());
+ EXPECT_FALSE(context.aborted);
+ }
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRW<TestType> array(env, LARGE_ARRAY);
+ EXPECT_EQ(context.dummyPtr, array.get());
+ EXPECT_EQ(LARGE_ARRAY, array.getJavaArray());
+ EXPECT_EQ(context.dummyPtr, array.begin());
+ EXPECT_EQ(context.dummyPtr + LARGE_ARRAY_SIZE, array.end());
+ EXPECT_EQ(LARGE_ARRAY_SIZE, (size_t) array.size());
+ }
+ EXPECT_EQ(context.getArrayElementsCallCount, context.releaseArrayElementsCallCount);
+ EXPECT_TRUE(context.memoryUpdated());
+ EXPECT_FALSE(context.aborted);
+ }
+ {
+ context.resetCallCount();
+ {
+ ScopedArrayRW<TestType> array(env, nullptr);
+ EXPECT_TRUE(context.aborted);
+ }
+ }
+}