diff options
| author | Seigo Nonaka <nona@google.com> | 2017-12-08 20:30:45 +0000 |
|---|---|---|
| committer | android-build-merger <android-build-merger@google.com> | 2017-12-08 20:30:45 +0000 |
| commit | 16aff263ce2e06a9d10cd61292a9879d562a04ef (patch) | |
| tree | d7c52ce037a9c67a4cfc863d7b76c8e8044b4005 | |
| parent | 241b591a402f41458dc9cda70725acc2b576d7e0 (diff) | |
| parent | 2446e5dfefe08c95c959487c5f491caea06c2973 (diff) | |
| download | platform_libnativehelper-16aff263ce2e06a9d10cd61292a9879d562a04ef.tar.gz platform_libnativehelper-16aff263ce2e06a9d10cd61292a9879d562a04ef.tar.bz2 platform_libnativehelper-16aff263ce2e06a9d10cd61292a9879d562a04ef.zip | |
Merge "Rewrite scoped arrays with template and introduced nullable one." am: 3204e23c06
am: 2446e5dfef
Change-Id: Ie9b3a6882975af0ef844c245a9ed984071c335a6
| -rw-r--r-- | header_only_include/nativehelper/scoped_primitive_array.h | 288 | ||||
| -rw-r--r-- | tests/Android.bp | 5 | ||||
| -rw-r--r-- | tests/scoped_primitive_array_test.cpp | 223 |
3 files changed, 404 insertions, 112 deletions
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 {}; + +#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; \ +}; \ + +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) -// 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); \ +#undef ARRAY_TRAITS +#undef POINTER_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); + } + } } -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); - -#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); \ + ~ScopedArrayRO() { + if (mRawArray != nullptr && mRawArray != mBuffer) { + Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, JNI_ABORT); + } } -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); + 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; + } -#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW -#undef POINTER_TYPE -#undef REFERENCE_TYPE + 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 @@ cc_test { 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); + } + } +} |
