Merge "Nativehelper: Export header-only version of helpers"
am: 7678ec555b

Change-Id: I676c6db9a8843daf5d391ef0e448a4138a021df5
diff --git a/Android.bp b/Android.bp
index a76b782..99a8597 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,6 +20,12 @@
 }
 
 cc_library_headers {
+    name: "libnativehelper_header_only",
+    host_supported: true,
+    export_include_dirs: ["header_only_include"],
+}
+
+cc_library_headers {
     name: "jni_platform_headers",
     host_supported: true,
     export_include_dirs: ["platform_include"],
@@ -41,8 +47,16 @@
         },
     },
 
-    header_libs: ["jni_headers", "jni_platform_headers"],
-    export_header_lib_headers: ["jni_headers"],
+    header_libs: [
+        "jni_headers",
+        "libnativehelper_header_only",
+        "jni_platform_headers"
+    ],
+    export_header_lib_headers: [
+        "jni_headers",
+        "libnativehelper_header_only",
+        "jni_platform_headers",
+    ],
 
     shared_libs: [
         "liblog",
@@ -52,7 +66,7 @@
         "-fvisibility=protected",
     ],
 
-    export_include_dirs: ["include", "platform_include"],
+    export_include_dirs: ["include"],
 }
 
 //
@@ -64,11 +78,15 @@
 cc_library_shared {
     name: "libnativehelper_compat_libc++",
     export_include_dirs: [
+        "header_only_include",
         "include",
         "include/nativehelper", // TODO(b/63762847): remove
     ],
     cflags: ["-Werror"],
-    include_dirs: ["libnativehelper/platform_include"],
+    include_dirs: [
+        "libnativehelper/header_only_include",
+        "libnativehelper/platform_include",
+    ],
     srcs: [
         "JNIHelp.cpp",
         "JniConstants.cpp",
diff --git a/header_only_include/nativehelper/nativehelper_utils.h b/header_only_include/nativehelper/nativehelper_utils.h
new file mode 100644
index 0000000..da0c647
--- /dev/null
+++ b/header_only_include/nativehelper/nativehelper_utils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#ifndef NATIVEHELPER_MACROS_H_
+#define NATIVEHELPER_MACROS_H_
+
+#if defined(__cplusplus)
+
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if __cplusplus >= 201103L
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&) = delete;  \
+  void operator=(const TypeName&) = delete
+#else
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);  \
+  void operator=(const TypeName&)
+#endif  // __has_feature(cxx_deleted_functions)
+#endif  // !defined(DISALLOW_COPY_AND_ASSIGN)
+
+#ifndef NATIVEHELPER_JNIHELP_H_
+// This seems a header-only include. Provide NPE throwing.
+static inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
+    if (env->ExceptionCheck()) {
+        // Drop any pending exception.
+        env->ExceptionClear();
+    }
+
+    jclass e_class = env->FindClass("java/lang/NullPointerException");
+    if (e_class == nullptr) {
+        return -1;
+    }
+
+    if (env->ThrowNew(e_class, msg) != JNI_OK) {
+        env->DeleteLocalRef(e_class);
+        return -1;
+    }
+
+    env->DeleteLocalRef(e_class);
+    return 0;
+}
+#endif  // NATIVEHELPER_JNIHELP_H_
+
+#endif  // defined(__cplusplus)
+
+#endif  // NATIVEHELPER_MACROS_H_
diff --git a/header_only_include/nativehelper/scoped_bytes.h b/header_only_include/nativehelper/scoped_bytes.h
new file mode 100644
index 0000000..abeb395
--- /dev/null
+++ b/header_only_include/nativehelper/scoped_bytes.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_BYTES_H_
+#define SCOPED_BYTES_H_
+
+#include "jni.h"
+#include "nativehelper_utils.h"
+
+/**
+ * ScopedBytesRO and ScopedBytesRW attempt to paper over the differences between byte[]s and
+ * ByteBuffers. This in turn helps paper over the differences between non-direct ByteBuffers backed
+ * by byte[]s, direct ByteBuffers backed by bytes[]s, and direct ByteBuffers not backed by byte[]s.
+ * (On Android, this last group only contains MappedByteBuffers.)
+ */
+template<bool readOnly>
+class ScopedBytes {
+public:
+    ScopedBytes(JNIEnv* env, jobject object)
+    : mEnv(env), mObject(object), mByteArray(NULL), mPtr(NULL)
+    {
+        if (mObject == NULL) {
+            jniThrowNullPointerException(mEnv, NULL);
+        } else if (mEnv->IsInstanceOf(mObject, JniConstants::byteArrayClass)) {
+            mByteArray = reinterpret_cast<jbyteArray>(mObject);
+            mPtr = mEnv->GetByteArrayElements(mByteArray, NULL);
+        } else {
+            mPtr = reinterpret_cast<jbyte*>(mEnv->GetDirectBufferAddress(mObject));
+        }
+    }
+
+    ~ScopedBytes() {
+        if (mByteArray != NULL) {
+            mEnv->ReleaseByteArrayElements(mByteArray, mPtr, readOnly ? JNI_ABORT : 0);
+        }
+    }
+
+private:
+    JNIEnv* const mEnv;
+    const jobject mObject;
+    jbyteArray mByteArray;
+
+protected:
+    jbyte* mPtr;
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(ScopedBytes);
+};
+
+class ScopedBytesRO : public ScopedBytes<true> {
+public:
+    ScopedBytesRO(JNIEnv* env, jobject object) : ScopedBytes<true>(env, object) {}
+    const jbyte* get() const {
+        return mPtr;
+    }
+};
+
+class ScopedBytesRW : public ScopedBytes<false> {
+public:
+    ScopedBytesRW(JNIEnv* env, jobject object) : ScopedBytes<false>(env, object) {}
+    jbyte* get() {
+        return mPtr;
+    }
+};
+
+#endif  // SCOPED_BYTES_H_
diff --git a/header_only_include/nativehelper/scoped_local_frame.h b/header_only_include/nativehelper/scoped_local_frame.h
new file mode 100644
index 0000000..70797f4
--- /dev/null
+++ b/header_only_include/nativehelper/scoped_local_frame.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_LOCAL_FRAME_H_
+#define SCOPED_LOCAL_FRAME_H_
+
+#include "jni.h"
+
+class ScopedLocalFrame {
+public:
+    explicit ScopedLocalFrame(JNIEnv* env) : mEnv(env) {
+        mEnv->PushLocalFrame(128);
+    }
+
+    ~ScopedLocalFrame() {
+        mEnv->PopLocalFrame(NULL);
+    }
+
+private:
+    JNIEnv* const mEnv;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedLocalFrame);
+};
+
+#endif  // SCOPED_LOCAL_FRAME_H_
diff --git a/header_only_include/nativehelper/scoped_local_ref.h b/header_only_include/nativehelper/scoped_local_ref.h
new file mode 100644
index 0000000..458c87f
--- /dev/null
+++ b/header_only_include/nativehelper/scoped_local_ref.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_LOCAL_REF_H_
+#define SCOPED_LOCAL_REF_H_
+
+#include <cstddef>
+
+#include "jni.h"
+#include "nativehelper_utils.h"
+
+// A smart pointer that deletes a JNI local reference when it goes out of scope.
+template<typename T>
+class ScopedLocalRef {
+public:
+    ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
+    }
+
+    ~ScopedLocalRef() {
+        reset();
+    }
+
+    void reset(T ptr = NULL) {
+        if (ptr != mLocalRef) {
+            if (mLocalRef != NULL) {
+                mEnv->DeleteLocalRef(mLocalRef);
+            }
+            mLocalRef = ptr;
+        }
+    }
+
+    T release() __attribute__((warn_unused_result)) {
+        T localRef = mLocalRef;
+        mLocalRef = NULL;
+        return localRef;
+    }
+
+    T get() const {
+        return mLocalRef;
+    }
+
+// Some better C++11 support.
+#if __cplusplus >= 201103L
+    // Move constructor.
+    ScopedLocalRef(ScopedLocalRef&& s) : mEnv(s.mEnv), mLocalRef(s.release()) {
+    }
+
+    explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) {
+    }
+
+    // We do not expose an empty constructor as it can easily lead to errors
+    // using common idioms, e.g.:
+    //   ScopedLocalRef<...> ref;
+    //   ref.reset(...);
+
+    // Move assignment operator.
+    ScopedLocalRef& operator=(ScopedLocalRef&& s) {
+        reset(s.release());
+        mEnv = s.mEnv;
+        return *this;
+    }
+
+    // Allows "if (scoped_ref == nullptr)"
+    bool operator==(std::nullptr_t) const {
+        return mLocalRef == nullptr;
+    }
+
+    // Allows "if (scoped_ref != nullptr)"
+    bool operator!=(std::nullptr_t) const {
+        return mLocalRef != nullptr;
+    }
+#endif
+
+private:
+    JNIEnv* mEnv;
+    T mLocalRef;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
+};
+
+#endif  // SCOPED_LOCAL_REF_H_
diff --git a/header_only_include/nativehelper/scoped_primitive_array.h b/header_only_include/nativehelper/scoped_primitive_array.h
new file mode 100644
index 0000000..d6840c2
--- /dev/null
+++ b/header_only_include/nativehelper/scoped_primitive_array.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_PRIMITIVE_ARRAY_H_
+#define SCOPED_PRIMITIVE_ARRAY_H_
+
+#include "jni.h"
+#include "nativehelper_utils.h"
+
+#ifdef POINTER_TYPE
+#error POINTER_TYPE is defined.
+#else
+#define POINTER_TYPE(T) T*  /* NOLINT */
+#endif
+
+#ifdef REFERENCE_TYPE
+#error REFERENCE_TYPE is defined.
+#else
+#define REFERENCE_TYPE(T) T&  /* NOLINT */
+#endif
+
+// 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); \
+    }
+
+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); \
+    }
+
+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 POINTER_TYPE
+#undef REFERENCE_TYPE
+
+#endif  // SCOPED_PRIMITIVE_ARRAY_H_
diff --git a/header_only_include/nativehelper/scoped_string_chars.h b/header_only_include/nativehelper/scoped_string_chars.h
new file mode 100644
index 0000000..4debb2a
--- /dev/null
+++ b/header_only_include/nativehelper/scoped_string_chars.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef SCOPED_STRING_CHARS_H_
+#define SCOPED_STRING_CHARS_H_
+
+#include "jni.h"
+#include "nativehelper_utils.h"
+
+// A smart pointer that provides access to a jchar* given a JNI jstring.
+// Unlike GetStringChars, we throw NullPointerException rather than abort if
+// passed a null jstring, and get will return NULL.
+// This makes the correct idiom very simple:
+//
+//   ScopedStringChars name(env, java_name);
+//   if (name.get() == NULL) {
+//     return NULL;
+//   }
+class ScopedStringChars {
+ public:
+  ScopedStringChars(JNIEnv* env, jstring s) : env_(env), string_(s), size_(0) {
+    if (s == NULL) {
+      chars_ = NULL;
+      jniThrowNullPointerException(env, NULL);
+    } else {
+      chars_ = env->GetStringChars(string_, NULL);
+      if (chars_ != NULL) {
+        size_ = env->GetStringLength(string_);
+      }
+    }
+  }
+
+  ~ScopedStringChars() {
+    if (chars_ != NULL) {
+      env_->ReleaseStringChars(string_, chars_);
+    }
+  }
+
+  const jchar* get() const {
+    return chars_;
+  }
+
+  size_t size() const {
+    return size_;
+  }
+
+  const jchar& operator[](size_t n) const {
+    return chars_[n];
+  }
+
+ private:
+  JNIEnv* const env_;
+  const jstring string_;
+  const jchar* chars_;
+  size_t size_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedStringChars);
+};
+
+#endif  // SCOPED_STRING_CHARS_H_
diff --git a/header_only_include/nativehelper/scoped_utf_chars.h b/header_only_include/nativehelper/scoped_utf_chars.h
new file mode 100644
index 0000000..75cbe50
--- /dev/null
+++ b/header_only_include/nativehelper/scoped_utf_chars.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_UTF_CHARS_H_
+#define SCOPED_UTF_CHARS_H_
+
+#include <string.h>
+
+#include "jni.h"
+#include "nativehelper_utils.h"
+
+// A smart pointer that provides read-only access to a Java string's UTF chars.
+// Unlike GetStringUTFChars, we throw NullPointerException rather than abort if
+// passed a null jstring, and c_str will return nullptr.
+// This makes the correct idiom very simple:
+//
+//   ScopedUtfChars name(env, java_name);
+//   if (name.c_str() == nullptr) {
+//     return nullptr;
+//   }
+class ScopedUtfChars {
+ public:
+  ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
+    if (s == nullptr) {
+      utf_chars_ = nullptr;
+      jniThrowNullPointerException(env, nullptr);
+    } else {
+      utf_chars_ = env->GetStringUTFChars(s, nullptr);
+    }
+  }
+
+  ScopedUtfChars(ScopedUtfChars&& rhs) :
+      env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
+    rhs.env_ = nullptr;
+    rhs.string_ = nullptr;
+    rhs.utf_chars_ = nullptr;
+  }
+
+  ~ScopedUtfChars() {
+    if (utf_chars_) {
+      env_->ReleaseStringUTFChars(string_, utf_chars_);
+    }
+  }
+
+  ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
+    if (this != &rhs) {
+      // Delete the currently owned UTF chars.
+      this->~ScopedUtfChars();
+
+      // Move the rhs ScopedUtfChars and zero it out.
+      env_ = rhs.env_;
+      string_ = rhs.string_;
+      utf_chars_ = rhs.utf_chars_;
+      rhs.env_ = nullptr;
+      rhs.string_ = nullptr;
+      rhs.utf_chars_ = nullptr;
+    }
+    return *this;
+  }
+
+  const char* c_str() const {
+    return utf_chars_;
+  }
+
+  size_t size() const {
+    return strlen(utf_chars_);
+  }
+
+  const char& operator[](size_t n) const {
+    return utf_chars_[n];
+  }
+
+ private:
+  JNIEnv* env_;
+  jstring string_;
+  const char* utf_chars_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
+};
+
+#endif  // SCOPED_UTF_CHARS_H_
diff --git a/include/nativehelper/ScopedBytes.h b/include/nativehelper/ScopedBytes.h
index fec46e8..7cb2ad0 100644
--- a/include/nativehelper/ScopedBytes.h
+++ b/include/nativehelper/ScopedBytes.h
@@ -18,61 +18,6 @@
 #define SCOPED_BYTES_H_included
 
 #include "JNIHelp.h"
-
-/**
- * ScopedBytesRO and ScopedBytesRW attempt to paper over the differences between byte[]s and
- * ByteBuffers. This in turn helps paper over the differences between non-direct ByteBuffers backed
- * by byte[]s, direct ByteBuffers backed by bytes[]s, and direct ByteBuffers not backed by byte[]s.
- * (On Android, this last group only contains MappedByteBuffers.)
- */
-template<bool readOnly>
-class ScopedBytes {
-public:
-    ScopedBytes(JNIEnv* env, jobject object)
-    : mEnv(env), mObject(object), mByteArray(NULL), mPtr(NULL)
-    {
-        if (mObject == NULL) {
-            jniThrowNullPointerException(mEnv, NULL);
-        } else if (mEnv->IsInstanceOf(mObject, JniConstants::byteArrayClass)) {
-            mByteArray = reinterpret_cast<jbyteArray>(mObject);
-            mPtr = mEnv->GetByteArrayElements(mByteArray, NULL);
-        } else {
-            mPtr = reinterpret_cast<jbyte*>(mEnv->GetDirectBufferAddress(mObject));
-        }
-    }
-
-    ~ScopedBytes() {
-        if (mByteArray != NULL) {
-            mEnv->ReleaseByteArrayElements(mByteArray, mPtr, readOnly ? JNI_ABORT : 0);
-        }
-    }
-
-private:
-    JNIEnv* const mEnv;
-    const jobject mObject;
-    jbyteArray mByteArray;
-
-protected:
-    jbyte* mPtr;
-
-private:
-    DISALLOW_COPY_AND_ASSIGN(ScopedBytes);
-};
-
-class ScopedBytesRO : public ScopedBytes<true> {
-public:
-    ScopedBytesRO(JNIEnv* env, jobject object) : ScopedBytes<true>(env, object) {}
-    const jbyte* get() const {
-        return mPtr;
-    }
-};
-
-class ScopedBytesRW : public ScopedBytes<false> {
-public:
-    ScopedBytesRW(JNIEnv* env, jobject object) : ScopedBytes<false>(env, object) {}
-    jbyte* get() {
-        return mPtr;
-    }
-};
+#include <nativehelper/scoped_bytes.h>
 
 #endif  // SCOPED_BYTES_H_included
diff --git a/include/nativehelper/ScopedLocalFrame.h b/include/nativehelper/ScopedLocalFrame.h
index a74611f..57873f2 100644
--- a/include/nativehelper/ScopedLocalFrame.h
+++ b/include/nativehelper/ScopedLocalFrame.h
@@ -17,22 +17,6 @@
 #ifndef SCOPED_LOCAL_FRAME_H_included
 #define SCOPED_LOCAL_FRAME_H_included
 
-#include "JNIHelp.h"
-
-class ScopedLocalFrame {
-public:
-    explicit ScopedLocalFrame(JNIEnv* env) : mEnv(env) {
-        mEnv->PushLocalFrame(128);
-    }
-
-    ~ScopedLocalFrame() {
-        mEnv->PopLocalFrame(NULL);
-    }
-
-private:
-    JNIEnv* const mEnv;
-
-    DISALLOW_COPY_AND_ASSIGN(ScopedLocalFrame);
-};
+#include <nativehelper/scoped_local_frame.h>
 
 #endif  // SCOPED_LOCAL_FRAME_H_included
diff --git a/include/nativehelper/ScopedLocalRef.h b/include/nativehelper/ScopedLocalRef.h
index 4018f20..0fb03d7 100644
--- a/include/nativehelper/ScopedLocalRef.h
+++ b/include/nativehelper/ScopedLocalRef.h
@@ -17,78 +17,7 @@
 #ifndef SCOPED_LOCAL_REF_H_included
 #define SCOPED_LOCAL_REF_H_included
 
-#include <cstddef>
-
-#include "jni.h"
-#include "JNIHelp.h"  // for DISALLOW_COPY_AND_ASSIGN.
-
-// A smart pointer that deletes a JNI local reference when it goes out of scope.
-template<typename T>
-class ScopedLocalRef {
-public:
-    ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
-    }
-
-    ~ScopedLocalRef() {
-        reset();
-    }
-
-    void reset(T ptr = NULL) {
-        if (ptr != mLocalRef) {
-            if (mLocalRef != NULL) {
-                mEnv->DeleteLocalRef(mLocalRef);
-            }
-            mLocalRef = ptr;
-        }
-    }
-
-    T release() __attribute__((warn_unused_result)) {
-        T localRef = mLocalRef;
-        mLocalRef = NULL;
-        return localRef;
-    }
-
-    T get() const {
-        return mLocalRef;
-    }
-
-// Some better C++11 support.
-#if __cplusplus >= 201103L
-    // Move constructor.
-    ScopedLocalRef(ScopedLocalRef&& s) : mEnv(s.mEnv), mLocalRef(s.release()) {
-    }
-
-    explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) {
-    }
-
-    // We do not expose an empty constructor as it can easily lead to errors
-    // using common idioms, e.g.:
-    //   ScopedLocalRef<...> ref;
-    //   ref.reset(...);
-
-    // Move assignment operator.
-    ScopedLocalRef& operator=(ScopedLocalRef&& s) {
-        reset(s.release());
-        mEnv = s.mEnv;
-        return *this;
-    }
-
-    // Allows "if (scoped_ref == nullptr)"
-    bool operator==(std::nullptr_t) const {
-        return mLocalRef == nullptr;
-    }
-
-    // Allows "if (scoped_ref != nullptr)"
-    bool operator!=(std::nullptr_t) const {
-        return mLocalRef != nullptr;
-    }
-#endif
-
-private:
-    JNIEnv* mEnv;
-    T mLocalRef;
-
-    DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
-};
+#include "JNIHelp.h"
+#include <nativehelper/scoped_local_ref.h>
 
 #endif  // SCOPED_LOCAL_REF_H_included
diff --git a/include/nativehelper/ScopedPrimitiveArray.h b/include/nativehelper/ScopedPrimitiveArray.h
index 46f1786..626b64f 100644
--- a/include/nativehelper/ScopedPrimitiveArray.h
+++ b/include/nativehelper/ScopedPrimitiveArray.h
@@ -18,129 +18,6 @@
 #define SCOPED_PRIMITIVE_ARRAY_H_included
 
 #include "JNIHelp.h"
-
-#ifdef POINTER_TYPE
-#error POINTER_TYPE is defined.
-#else
-#define POINTER_TYPE(T) T*  /* NOLINT */
-#endif
-
-#ifdef REFERENCE_TYPE
-#error REFERENCE_TYPE is defined.
-#else
-#define REFERENCE_TYPE(T) T&  /* NOLINT */
-#endif
-
-// 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); \
-    }
-
-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); \
-    }
-
-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 POINTER_TYPE
-#undef REFERENCE_TYPE
+#include <nativehelper/scoped_primitive_array.h>
 
 #endif  // SCOPED_PRIMITIVE_ARRAY_H_included
diff --git a/include/nativehelper/ScopedStringChars.h b/include/nativehelper/ScopedStringChars.h
index 688291d..59c405c 100644
--- a/include/nativehelper/ScopedStringChars.h
+++ b/include/nativehelper/ScopedStringChars.h
@@ -18,55 +18,6 @@
 #define SCOPED_STRING_CHARS_H_included
 
 #include "JNIHelp.h"
-
-// A smart pointer that provides access to a jchar* given a JNI jstring.
-// Unlike GetStringChars, we throw NullPointerException rather than abort if
-// passed a null jstring, and get will return NULL.
-// This makes the correct idiom very simple:
-//
-//   ScopedStringChars name(env, java_name);
-//   if (name.get() == NULL) {
-//     return NULL;
-//   }
-class ScopedStringChars {
- public:
-  ScopedStringChars(JNIEnv* env, jstring s) : env_(env), string_(s), size_(0) {
-    if (s == NULL) {
-      chars_ = NULL;
-      jniThrowNullPointerException(env, NULL);
-    } else {
-      chars_ = env->GetStringChars(string_, NULL);
-      if (chars_ != NULL) {
-        size_ = env->GetStringLength(string_);
-      }
-    }
-  }
-
-  ~ScopedStringChars() {
-    if (chars_ != NULL) {
-      env_->ReleaseStringChars(string_, chars_);
-    }
-  }
-
-  const jchar* get() const {
-    return chars_;
-  }
-
-  size_t size() const {
-    return size_;
-  }
-
-  const jchar& operator[](size_t n) const {
-    return chars_[n];
-  }
-
- private:
-  JNIEnv* const env_;
-  const jstring string_;
-  const jchar* chars_;
-  size_t size_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedStringChars);
-};
+#include <nativehelper/scoped_string_chars.h>
 
 #endif  // SCOPED_STRING_CHARS_H_included
diff --git a/include/nativehelper/ScopedUtfChars.h b/include/nativehelper/ScopedUtfChars.h
index aa8489d..f123115 100644
--- a/include/nativehelper/ScopedUtfChars.h
+++ b/include/nativehelper/ScopedUtfChars.h
@@ -18,75 +18,6 @@
 #define SCOPED_UTF_CHARS_H_included
 
 #include "JNIHelp.h"
-#include <string.h>
-
-// A smart pointer that provides read-only access to a Java string's UTF chars.
-// Unlike GetStringUTFChars, we throw NullPointerException rather than abort if
-// passed a null jstring, and c_str will return nullptr.
-// This makes the correct idiom very simple:
-//
-//   ScopedUtfChars name(env, java_name);
-//   if (name.c_str() == nullptr) {
-//     return nullptr;
-//   }
-class ScopedUtfChars {
- public:
-  ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
-    if (s == nullptr) {
-      utf_chars_ = nullptr;
-      jniThrowNullPointerException(env, nullptr);
-    } else {
-      utf_chars_ = env->GetStringUTFChars(s, nullptr);
-    }
-  }
-
-  ScopedUtfChars(ScopedUtfChars&& rhs) :
-      env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
-    rhs.env_ = nullptr;
-    rhs.string_ = nullptr;
-    rhs.utf_chars_ = nullptr;
-  }
-
-  ~ScopedUtfChars() {
-    if (utf_chars_) {
-      env_->ReleaseStringUTFChars(string_, utf_chars_);
-    }
-  }
-
-  ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
-    if (this != &rhs) {
-      // Delete the currently owned UTF chars.
-      this->~ScopedUtfChars();
-
-      // Move the rhs ScopedUtfChars and zero it out.
-      env_ = rhs.env_;
-      string_ = rhs.string_;
-      utf_chars_ = rhs.utf_chars_;
-      rhs.env_ = nullptr;
-      rhs.string_ = nullptr;
-      rhs.utf_chars_ = nullptr;
-    }
-    return *this;
-  }
-
-  const char* c_str() const {
-    return utf_chars_;
-  }
-
-  size_t size() const {
-    return strlen(utf_chars_);
-  }
-
-  const char& operator[](size_t n) const {
-    return utf_chars_[n];
-  }
-
- private:
-  JNIEnv* env_;
-  jstring string_;
-  const char* utf_chars_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
-};
+#include <nativehelper/scoped_utf_chars.h>
 
 #endif  // SCOPED_UTF_CHARS_H_included