File indexing completed on 2024-06-23 05:08:44

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KANDROIDEXTRAS_JNIARRAY_H
0008 #define KANDROIDEXTRAS_JNIARRAY_H
0009 
0010 #include "jniargument.h"
0011 #include "jnireturnvalue.h"
0012 #include "jnitypetraits.h"
0013 
0014 #include <QJniEnvironment>
0015 
0016 namespace KAndroidExtras {
0017 
0018 ///@cond internal
0019 namespace Internal {
0020 
0021 /** Primitive type array type traits. */
0022 template <typename T> struct array_trait {
0023     typedef jobjectArray type;
0024 };
0025 
0026 #define MAKE_ARRAY_TRAIT(base_type, type_name) \
0027 template <> struct array_trait<base_type> { \
0028     typedef base_type ## Array type; \
0029     static inline type newArray(QJniEnvironment &env, jsize size) { return env->New ## type_name ## Array(size); } \
0030     static inline base_type* getArrayElements(QJniEnvironment &env, type array, jboolean *isCopy) { return env->Get ## type_name ## ArrayElements(array, isCopy); } \
0031     static inline void releaseArrayElements(QJniEnvironment &env, type array, base_type *data, jint mode) { return env->Release ## type_name ## ArrayElements(array, data, mode); } \
0032     static inline void setArrayRegion(QJniEnvironment &env, type array, jsize start, jsize length, const base_type *data) { env->Set ## type_name ## ArrayRegion(array, start, length, data); } \
0033 };
0034 
0035 MAKE_ARRAY_TRAIT(jboolean, Boolean)
0036 MAKE_ARRAY_TRAIT(jbyte, Byte)
0037 MAKE_ARRAY_TRAIT(jchar, Char)
0038 MAKE_ARRAY_TRAIT(jshort, Short)
0039 MAKE_ARRAY_TRAIT(jint, Int)
0040 MAKE_ARRAY_TRAIT(jlong, Long)
0041 MAKE_ARRAY_TRAIT(jfloat, Float)
0042 MAKE_ARRAY_TRAIT(jdouble, Double)
0043 
0044 #undef MAKE_ARRAY_TRAIT
0045 
0046 /** Meta function for retrieving a JNI array .*/
0047 template <typename Container, typename Value, bool is_primitive> struct FromArray {};
0048 
0049 template <typename Container>
0050 struct FromArray<Container, QJniObject, false>
0051 {
0052     inline auto operator()(const QJniObject &array) const
0053     {
0054         if (!array.isValid()) {
0055             return Container{};
0056         }
0057         const auto a = static_cast<jobjectArray>(array.object());
0058         QJniEnvironment env;
0059         const auto size = env->GetArrayLength(a);
0060         Container r;
0061         r.reserve(size);
0062         for (auto i = 0; i < size; ++i) {
0063             r.push_back(QJniObject::fromLocalRef(env->GetObjectArrayElement(a, i)));
0064         }
0065         return r;
0066     }
0067 };
0068 
0069 template <typename Container, typename Value>
0070 struct FromArray<Container, Value, false>
0071 {
0072     inline auto operator()(const QJniObject &array) const
0073     {
0074         if (!array.isValid()) {
0075             return Container{};
0076         }
0077         const auto a = static_cast<jobjectArray>(array.object());
0078         QJniEnvironment env;
0079         const auto size = env->GetArrayLength(a);
0080         Container r;
0081         r.reserve(size);
0082         for (auto i = 0; i < size; ++i) {
0083             r.push_back(Jni::reverse_converter<Value>::type::convert(QJniObject::fromLocalRef(env->GetObjectArrayElement(a, i))));
0084         }
0085         return r;
0086     }
0087 };
0088 
0089 // specializations for primitive types
0090 template <typename Container, typename Value>
0091 struct FromArray<Container, Value, true>
0092 {
0093     typedef array_trait<Value> _t;
0094     inline auto operator()(const QJniObject &array) const
0095     {
0096         if (!array.isValid()) {
0097             return Container{};
0098         }
0099 
0100         const auto a = static_cast<typename _t::type>(array.object());
0101         QJniEnvironment env;
0102         const auto size = env->GetArrayLength(a);
0103         Container r;
0104         r.reserve(size);
0105 
0106         auto data = _t::getArrayElements(env, a, nullptr);
0107         std::copy(data, data + size, std::back_inserter(r));
0108         _t::releaseArrayElements(env, a, data, JNI_ABORT);
0109 
0110         return r;
0111     }
0112 };
0113 
0114 // array wrapper, common base for primitive and non-primitive types
0115 template <typename T>
0116 class ArrayImplBase {
0117 public:
0118     typedef T value_type;
0119     typedef jsize size_type;
0120     typedef jsize difference_type;
0121 
0122     ArrayImplBase() = default;
0123     inline ArrayImplBase(const QJniObject &array) : m_array(array) {}
0124     ArrayImplBase(const ArrayImplBase &) = default;
0125     ArrayImplBase(ArrayImplBase &&) = default;
0126 
0127     inline size_type size() const
0128     {
0129         if (!m_array.isValid()) {
0130             return 0;
0131         }
0132         const auto a = static_cast<typename _t::type>(m_array.object());
0133         QJniEnvironment env;
0134         return env->GetArrayLength(a);
0135     }
0136 
0137     inline operator QJniObject() const {
0138         return m_array;
0139     }
0140     inline QJniObject jniHandle() const { return m_array; }
0141 
0142 protected:
0143     typedef array_trait<T> _t;
0144 
0145     typename _t::type handle() const { return static_cast<typename _t::type>(m_array.object()); }
0146 
0147     QJniObject m_array;
0148 };
0149 
0150 template <typename T, bool is_primitive>
0151 class ArrayImpl {};
0152 
0153 // array wrapper for primitive types
0154 template <typename T>
0155 class ArrayImpl<T, true> : public ArrayImplBase<T>
0156 {
0157     static_assert(!Internal::is_invalid_primitive_type<T>::value, "Using an incompatible primitive type!");
0158 public:
0159     inline ArrayImpl(const QJniObject &array)
0160         : ArrayImplBase<T>(array)
0161     {
0162         // ### do this on demand?
0163         getArrayElements();
0164     }
0165 
0166     /** Create a new array with @p size elements. */
0167     inline explicit ArrayImpl(jsize size)
0168     {
0169         QJniEnvironment env;
0170         ArrayImplBase<T>::m_array = QJniObject::fromLocalRef(ArrayImplBase<T>::_t::newArray(env, size));
0171         getArrayElements();
0172     }
0173 
0174     ArrayImpl() = default;
0175     ArrayImpl(const ArrayImpl&) = delete; // ### ref count m_data and allow copying?
0176     ArrayImpl(ArrayImpl&&) = default;
0177     ~ArrayImpl()
0178     {
0179         QJniEnvironment env;
0180         ArrayImplBase<T>::_t::releaseArrayElements(env, this->handle(), m_data, JNI_ABORT);
0181     }
0182 
0183     T operator[](jsize index) const
0184     {
0185         return m_data[index];
0186     }
0187 
0188     T* begin() const { return m_data; }
0189     T* end() const { return m_data + ArrayImplBase<T>::size(); }
0190 
0191 private:
0192     inline void getArrayElements()
0193     {
0194         if (!ArrayImplBase<T>::m_array.isValid()) {
0195             return;
0196         }
0197         QJniEnvironment env;
0198         m_data = ArrayImplBase<T>::_t::getArrayElements(env, this->handle(), nullptr);
0199     }
0200 
0201     T *m_data = nullptr;
0202 };
0203 
0204 // array wrapper for non-primitive types
0205 template <typename T>
0206 class ArrayImpl<T, false> : public ArrayImplBase<T>
0207 {
0208 public:
0209     using ArrayImplBase<T>::ArrayImplBase;
0210 
0211     /** Create a new array with @p size elements initialized with @p value. */
0212     explicit inline ArrayImpl(jsize size, typename Internal::argument<T>::type value)
0213     {
0214         QJniEnvironment env;
0215         auto clazz = env.findClass(Jni::typeName<T>());
0216         ArrayImplBase<T>::m_array = QJniObject::fromLocalRef(env->NewObjectArray(size, clazz, Internal::argument<T>::toCallArgument(value)));
0217     }
0218 
0219     /** Create a new array with @p size null elements. */
0220     explicit inline ArrayImpl(jsize size, std::nullptr_t = nullptr)
0221     {
0222         QJniEnvironment env;
0223         auto clazz = env.findClass(Jni::typeName<T>());
0224         ArrayImplBase<T>::m_array = QJniObject::fromLocalRef(env->NewObjectArray(size, clazz, nullptr));
0225     }
0226 
0227     ArrayImpl() = default;
0228     ArrayImpl(const ArrayImpl&) = default;
0229     ArrayImpl(ArrayImpl&&) = default;
0230 
0231     auto operator[](jsize index) const
0232     {
0233         QJniEnvironment env;
0234         return Internal::return_wrapper<T>::toReturnValue(QJniObject::fromLocalRef(env->GetObjectArrayElement(this->handle(), index)));
0235     }
0236 
0237     class ref
0238     {
0239     public:
0240         inline operator auto()
0241         {
0242             QJniEnvironment env;
0243             return Internal::return_wrapper<T>::toReturnValue(QJniObject::fromLocalRef(env->GetObjectArrayElement(c.handle(), index)));
0244         }
0245         inline ref& operator=(typename Internal::argument<T>::type v)
0246         {
0247             QJniEnvironment env;
0248             env->SetObjectArrayElement(c.handle(), index, Internal::argument<T>::toCallArgument(v));
0249             return *this;
0250         }
0251     private:
0252         ArrayImpl<T, false> &c;
0253         jsize index;
0254 
0255         friend class ArrayImpl<T, false>;
0256         inline ref(jsize _i, ArrayImpl<T, false> &_c) : c(_c), index(_i) {}
0257     };
0258     ref operator[](jsize index)
0259     {
0260         return ref(index, *this);
0261     }
0262 
0263     class const_iterator
0264     {
0265         const ArrayImpl<T, false> &c;
0266         jsize i = 0;
0267     public:
0268         typedef jsize difference_type;
0269         typedef T value_type;
0270         typedef T& reference;
0271         typedef std::random_access_iterator_tag iterator_category;
0272         typedef T* pointer;
0273 
0274         const_iterator(const ArrayImpl<T, false> &_c, jsize _i) : c(_c), i(_i) {}
0275 
0276         difference_type operator-(const_iterator other) const { return i - other.i; }
0277 
0278         const_iterator& operator++() { ++i; return *this; }
0279         const_iterator operator++(int) { return const_iterator(c, i++); }
0280 
0281         bool operator==(const_iterator other) const { return i == other.i; }
0282         bool operator!=(const_iterator other) const { return i != other.i; }
0283 
0284         auto operator*() const {
0285             return c[i];
0286         }
0287     };
0288 
0289     const_iterator begin() const { return const_iterator(*this, 0); }
0290     const_iterator end() const { return const_iterator(*this, ArrayImplBase<T>::size()); }
0291 
0292 };
0293 
0294 }
0295 ///@endcond
0296 
0297 namespace Jni {
0298 
0299 /** Convert a JNI array to a C++ container.
0300     *  Container value types can be any of
0301     *  - QJniObject
0302     *  - a primitive JNI type
0303     *  - a type with a conversion defined with @c JNI_DECLARE_CONVERTER
0304     */
0305 template <typename Container> constexpr __attribute__((__unused__)) Internal::FromArray<Container, typename Container::value_type, Jni::is_primitive_type<typename Container::value_type>::value> fromArray = {};
0306 
0307 /** Container-like wrapper for JNI arrays. */
0308 template <typename T>
0309 class Array : public Internal::ArrayImpl<T, Jni::is_primitive_type<T>::value> {
0310 public:
0311     using Internal::ArrayImpl<T, Jni::is_primitive_type<T>::value>::ArrayImpl;
0312     template <typename Container>
0313     inline operator Container() const {
0314         // ### should this be re-implemented in terms of Jni::Array API rather than direct JNI access?
0315         return Jni::fromArray<Container>(this->m_array);
0316     }
0317 };
0318 }
0319 
0320 }
0321 
0322 #endif