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