react-native-nitro-modules 0.26.1 → 0.26.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@ buildscript {
7
7
  }
8
8
 
9
9
  dependencies {
10
- classpath "com.android.tools.build:gradle:8.10.1"
10
+ classpath "com.android.tools.build:gradle:8.11.0"
11
11
  }
12
12
  }
13
13
 
@@ -51,7 +51,7 @@ android {
51
51
  externalNativeBuild {
52
52
  cmake {
53
53
  cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
54
- arguments "-DANDROID_STL=c++_shared"
54
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
55
55
  abiFilters (*reactNativeArchitectures())
56
56
 
57
57
  buildTypes {
@@ -3,6 +3,7 @@
3
3
  #include "JAnyMap.hpp"
4
4
  #include "JAnyValue.hpp"
5
5
  #include "JArrayBuffer.hpp"
6
+ #include "JHardwareBufferUtils.hpp"
6
7
  #include "JNitroModules.hpp"
7
8
  #include "JPromise.hpp"
8
9
  #include <fbjni/fbjni.h>
@@ -16,6 +17,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
16
17
  JArrayBuffer::registerNatives();
17
18
  JAnyMap::registerNatives();
18
19
  JAnyValue::registerNatives();
20
+ JHardwareBufferUtils::registerNatives();
19
21
  JPromise::registerNatives();
20
22
 
21
23
  // 2. Initialize the React Native TurboModule C++ part
@@ -0,0 +1,93 @@
1
+ //
2
+ // HardwareBufferArrayBuffer.hpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 14.07.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include "ArrayBuffer.hpp"
11
+ #include "JHardwareBufferUtils.hpp"
12
+ #include <android/hardware_buffer.h>
13
+ #include <android/hardware_buffer_jni.h>
14
+ #include <fbjni/ByteBuffer.h>
15
+ #include <fbjni/fbjni.h>
16
+
17
+ namespace margelo::nitro {
18
+
19
+ using namespace facebook;
20
+
21
+ #if __ANDROID_API__ >= 26
22
+ /**
23
+ * Represents an `ArrayBuffer` that holds a `HardwareBuffer`.
24
+ */
25
+ class HardwareBufferArrayBuffer final : public ArrayBuffer {
26
+ public:
27
+ /**
28
+ * Create a new `HardwareBufferArrayBuffer` instance that wraps the given `HardwareBuffer`.
29
+ * This constructor will add a +1 retain count on the given `hardwareBuffer` using
30
+ * `AHardwareBuffer_acquire(...)`, and release it again once it is destructured.
31
+ */
32
+ explicit HardwareBufferArrayBuffer(AHardwareBuffer* hardwareBuffer)
33
+ : _hardwareBuffer(hardwareBuffer), _dataCached(nullptr), _isLocked(false) {
34
+ AHardwareBuffer_acquire(hardwareBuffer);
35
+ }
36
+
37
+ ~HardwareBufferArrayBuffer() override {
38
+ // Hermes GC can destroy JS objects on a non-JNI Thread.
39
+ unlock();
40
+ jni::ThreadScope::WithClassLoader([&] { AHardwareBuffer_release(_hardwareBuffer); });
41
+ }
42
+
43
+ public:
44
+ /**
45
+ * Unlocks the HardwareBuffer if it was locked.
46
+ * Subsequent calls to `data()` will have to lock the buffer again.
47
+ *
48
+ * It is a good practice to call this when the buffer is likely not being
49
+ * read from using this `HardwareBufferArrayBuffer` instance again anytime soon.
50
+ */
51
+ void unlock() {
52
+ if (_isLocked) {
53
+ AHardwareBuffer_unlock(_hardwareBuffer, nullptr);
54
+ _isLocked = false;
55
+ }
56
+ _dataCached = nullptr;
57
+ }
58
+
59
+ public:
60
+ [[nodiscard]] uint8_t* data() override {
61
+ if (_isLocked && _dataCached != nullptr) {
62
+ // We are still locked on the AHardwareBuffer* and have a valid buffer.
63
+ return _dataCached;
64
+ }
65
+ void* buffer;
66
+ int result = AHardwareBuffer_lock(_hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_MASK, -1, nullptr, &buffer);
67
+ if (result != 0) {
68
+ throw std::runtime_error("Failed to read HardwareBuffer bytes!");
69
+ }
70
+ _dataCached = static_cast<uint8_t*>(buffer);
71
+ _isLocked = true;
72
+ return _dataCached;
73
+ }
74
+ [[nodiscard]] size_t size() const override {
75
+ return JHardwareBufferUtils::getHardwareBufferSize(_hardwareBuffer);
76
+ }
77
+ [[nodiscard]] bool isOwner() const noexcept override {
78
+ return true;
79
+ }
80
+
81
+ public:
82
+ [[nodiscard]] AHardwareBuffer* getBuffer() const {
83
+ return _hardwareBuffer;
84
+ }
85
+
86
+ private:
87
+ AHardwareBuffer* _hardwareBuffer;
88
+ uint8_t* _dataCached;
89
+ bool _isLocked;
90
+ };
91
+ #endif
92
+
93
+ } // namespace margelo::nitro
@@ -40,7 +40,7 @@ private:
40
40
  JAnyMap() {
41
41
  _map = std::make_shared<AnyMap>();
42
42
  }
43
- JAnyMap(const std::shared_ptr<AnyMap>& map) : _map(map) {}
43
+ explicit JAnyMap(const std::shared_ptr<AnyMap>& map) : _map(map) {}
44
44
 
45
45
  protected:
46
46
  bool contains(const std::string& key) {
@@ -52,6 +52,17 @@ protected:
52
52
  void clear() {
53
53
  _map->clear();
54
54
  }
55
+ jni::local_ref<jni::JArrayClass<jni::JString>> getAllKeys() {
56
+ auto& map = _map->getMap();
57
+ auto array = jni::JArrayClass<jni::JString>::newArray(map.size());
58
+ size_t index = 0;
59
+ for (const auto& pair : map) {
60
+ auto jKey = jni::make_jstring(pair.first);
61
+ array->setElement(index, *jKey);
62
+ index++;
63
+ }
64
+ return array;
65
+ }
55
66
 
56
67
  protected:
57
68
  bool isNull(const std::string& key) {
@@ -163,6 +174,7 @@ public:
163
174
  makeNativeMethod("contains", JAnyMap::contains),
164
175
  makeNativeMethod("remove", JAnyMap::remove),
165
176
  makeNativeMethod("clear", JAnyMap::clear),
177
+ makeNativeMethod("getAllKeys", JAnyMap::getAllKeys),
166
178
  // is
167
179
  makeNativeMethod("isNull", JAnyMap::isNull),
168
180
  makeNativeMethod("isDouble", JAnyMap::isDouble),
@@ -9,6 +9,9 @@
9
9
 
10
10
  #include "ArrayBuffer.hpp"
11
11
  #include "ByteBufferArrayBuffer.hpp"
12
+ #include "HardwareBufferArrayBuffer.hpp"
13
+ #include <android/hardware_buffer.h>
14
+ #include <android/hardware_buffer_jni.h>
12
15
  #include <fbjni/ByteBuffer.h>
13
16
  #include <fbjni/fbjni.h>
14
17
  #include <functional>
@@ -38,9 +41,23 @@ public:
38
41
  /**
39
42
  * Create a new `JArrayBuffer` that wraps the given `ByteBuffer` from Java.
40
43
  */
41
- static jni::local_ref<JArrayBuffer::jhybriddata> initHybrid(jni::alias_ref<jhybridobject>, jni::alias_ref<jni::JByteBuffer> buffer) {
44
+ static jni::local_ref<JArrayBuffer::jhybriddata> initHybridByteBuffer(jni::alias_ref<jhybridobject>,
45
+ jni::alias_ref<jni::JByteBuffer> buffer) {
42
46
  return makeCxxInstance(buffer);
43
47
  }
48
+ /**
49
+ * Create a new `JArrayBuffer` that wraps the given `HardwareBuffer` from Java.
50
+ */
51
+ static jni::local_ref<JArrayBuffer::jhybriddata> initHybridHardwareBuffer(jni::alias_ref<jhybridobject>,
52
+ jni::alias_ref<jni::JObject> boxedHardwareBuffer) {
53
+ #if __ANDROID_API__ >= 26
54
+ // Cast jobject* to AHardwareBuffer*. It has a retain count of 0 which will be retained in `HardwareBufferArrayBuffer(..)`.
55
+ AHardwareBuffer* hardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedHardwareBuffer.get());
56
+ return makeCxxInstance(hardwareBuffer);
57
+ #else
58
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
59
+ #endif
60
+ }
44
61
 
45
62
  public:
46
63
  /**
@@ -51,6 +68,18 @@ public:
51
68
  return byteBufferArrayBuffer != nullptr;
52
69
  }
53
70
 
71
+ /**
72
+ * Get whether the `ArrayBuffer` is holding data from a `HardwareBuffer`.
73
+ */
74
+ bool getIsHardwareBuffer() {
75
+ #if __ANDROID_API__ >= 26
76
+ auto hardwareBufferArrayBuffer = std::dynamic_pointer_cast<HardwareBufferArrayBuffer>(_arrayBuffer);
77
+ return hardwareBufferArrayBuffer != nullptr;
78
+ #else
79
+ return false;
80
+ #endif
81
+ }
82
+
54
83
  /**
55
84
  * Get whether the `ArrayBuffer` is owning the data and can safely hold onto it longer.
56
85
  */
@@ -67,7 +96,7 @@ public:
67
96
  * `ByteBuffer`. In this case, `getBuffer()` will **copy** the data into a new `ByteBuffer` if
68
97
  * `copyIfNeeded` is `true`, and **wrap** the data into a new `ByteBuffer` if `copyIfNeeded` is false.
69
98
  */
70
- jni::local_ref<jni::JByteBuffer> getByteBuffer(bool copyIfNeeded) {
99
+ [[nodiscard]] jni::local_ref<jni::JByteBuffer> getByteBuffer(bool copyIfNeeded) {
71
100
  auto byteBufferArrayBuffer = std::dynamic_pointer_cast<ByteBufferArrayBuffer>(_arrayBuffer);
72
101
  if (byteBufferArrayBuffer != nullptr) {
73
102
  // It is a `ByteBufferArrayBuffer`, which has a `ByteBuffer` underneath!
@@ -88,6 +117,16 @@ public:
88
117
  }
89
118
  }
90
119
 
120
+ [[nodiscard]] jni::local_ref<jni::JObject> getHardwareBufferBoxed() {
121
+ #if __ANDROID_API__ >= 26
122
+ AHardwareBuffer* buffer = getHardwareBuffer();
123
+ jobject boxed = AHardwareBuffer_toHardwareBuffer(jni::Environment::current(), buffer);
124
+ return jni::make_local(boxed);
125
+ #else
126
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
127
+ #endif
128
+ }
129
+
91
130
  int getBufferSize() {
92
131
  return static_cast<int>(_arrayBuffer->size());
93
132
  }
@@ -96,16 +135,38 @@ public:
96
135
  /**
97
136
  * Get the underlying `ArrayBuffer`.
98
137
  */
99
- std::shared_ptr<ArrayBuffer> getArrayBuffer() const {
138
+ [[nodiscard]] std::shared_ptr<ArrayBuffer> getArrayBuffer() const {
100
139
  return _arrayBuffer;
101
140
  }
141
+ /**
142
+ * Get the underlying `HardwareBuffer` if it has one.
143
+ * This method will throw if this `ArrayBuffer` was not created with a `HardwareBuffer`.
144
+ */
145
+ [[nodiscard]] AHardwareBuffer* getHardwareBuffer() const {
146
+ #if __ANDROID_API__ >= 26
147
+ auto hardwareBufferArrayBuffer = std::dynamic_pointer_cast<HardwareBufferArrayBuffer>(_arrayBuffer);
148
+ if (hardwareBufferArrayBuffer != nullptr) {
149
+ return hardwareBufferArrayBuffer->getBuffer();
150
+ } else {
151
+ throw std::runtime_error("The underlying buffer is not a HardwareBuffer!");
152
+ }
153
+ #else
154
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
155
+ #endif
156
+ }
102
157
 
103
158
  private:
104
- JArrayBuffer(const std::shared_ptr<ArrayBuffer>& arrayBuffer) : _arrayBuffer(arrayBuffer) {}
105
- JArrayBuffer(jni::alias_ref<jni::JByteBuffer> byteBuffer) {
159
+ explicit JArrayBuffer(const std::shared_ptr<ArrayBuffer>& arrayBuffer) : _arrayBuffer(arrayBuffer) {}
160
+ explicit JArrayBuffer(const jni::alias_ref<jni::JByteBuffer>& byteBuffer) {
106
161
  _arrayBuffer = std::make_shared<ByteBufferArrayBuffer>(byteBuffer);
107
162
  }
108
163
 
164
+ #if __ANDROID_API__ >= 26
165
+ explicit JArrayBuffer(AHardwareBuffer* /* 0 retain */ hardwareBuffer) {
166
+ _arrayBuffer = std::make_shared<HardwareBufferArrayBuffer>(hardwareBuffer);
167
+ }
168
+ #endif
169
+
109
170
  private:
110
171
  friend HybridBase;
111
172
  using HybridBase::HybridBase;
@@ -114,9 +175,12 @@ private:
114
175
  public:
115
176
  static void registerNatives() {
116
177
  registerHybrid(
117
- {makeNativeMethod("initHybrid", JArrayBuffer::initHybrid), makeNativeMethod("getByteBuffer", JArrayBuffer::getByteBuffer),
118
- makeNativeMethod("getIsByteBuffer", JArrayBuffer::getIsByteBuffer), makeNativeMethod("getIsOwner", JArrayBuffer::getIsOwner),
119
- makeNativeMethod("getBufferSize", JArrayBuffer::getBufferSize)});
178
+ {makeNativeMethod("initHybrid", JArrayBuffer::initHybridByteBuffer),
179
+ makeNativeMethod("initHybridBoxedHardwareBuffer", JArrayBuffer::initHybridHardwareBuffer),
180
+ makeNativeMethod("getByteBuffer", JArrayBuffer::getByteBuffer), makeNativeMethod("getIsByteBuffer", JArrayBuffer::getIsByteBuffer),
181
+ makeNativeMethod("getHardwareBufferBoxed", JArrayBuffer::getHardwareBufferBoxed),
182
+ makeNativeMethod("getIsHardwareBuffer", JArrayBuffer::getIsHardwareBuffer),
183
+ makeNativeMethod("getIsOwner", JArrayBuffer::getIsOwner), makeNativeMethod("getBufferSize", JArrayBuffer::getBufferSize)});
120
184
  }
121
185
  };
122
186
 
@@ -0,0 +1,95 @@
1
+ //
2
+ // JHardwareBufferUtils.cpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 11.07.25.
6
+ //
7
+
8
+ #include "JHardwareBufferUtils.hpp"
9
+ #include "NitroDefines.hpp"
10
+ #include <android/hardware_buffer_jni.h>
11
+
12
+ namespace margelo::nitro {
13
+
14
+ size_t JHardwareBufferUtils::getHardwareBufferSize(AHardwareBuffer* hardwareBuffer) {
15
+ #if __ANDROID_API__ >= 26
16
+ AHardwareBuffer_Desc description;
17
+ AHardwareBuffer_describe(hardwareBuffer, &description);
18
+ size_t sourceSize = description.height * description.stride;
19
+ return sourceSize;
20
+ #else
21
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
22
+ #endif
23
+ }
24
+
25
+ jni::local_ref<jni::JObject> JHardwareBufferUtils::copyHardwareBufferBoxedNew(jni::alias_ref<jni::JClass>,
26
+ jni::alias_ref<jni::JObject> boxedHardwareBuffer) {
27
+ #if __ANDROID_API__ >= 26
28
+ // 1. Unbox HardwareBuffer from jobject
29
+ AHardwareBuffer* sourceHardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedHardwareBuffer.get());
30
+ // 2. Describe the buffer
31
+ AHardwareBuffer_Desc description;
32
+ AHardwareBuffer_describe(sourceHardwareBuffer, &description);
33
+ // 3. Create a new buffer from the same description
34
+ AHardwareBuffer* destinationHardwareBuffer;
35
+ AHardwareBuffer_allocate(&description, &destinationHardwareBuffer);
36
+ // 4. Copy the data over
37
+ copyHardwareBuffer(sourceHardwareBuffer, destinationHardwareBuffer);
38
+ // 5. Box it into jobject again
39
+ jobject boxed = AHardwareBuffer_toHardwareBuffer(jni::Environment::current(), destinationHardwareBuffer);
40
+ return jni::make_local(boxed);
41
+ #else
42
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
43
+ #endif
44
+ }
45
+
46
+ void JHardwareBufferUtils::copyHardwareBufferBoxed(jni::alias_ref<jni::JClass>, jni::alias_ref<jni::JObject> boxedSourceHardwareBuffer,
47
+ jni::alias_ref<jni::JObject> boxedDestinationHardwareBuffer) {
48
+ #if __ANDROID_API__ >= 26
49
+ // 1. Unbox HardwareBuffer from jobject
50
+ AHardwareBuffer* sourceHardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedSourceHardwareBuffer.get());
51
+ AHardwareBuffer* destinationHardwareBuffer =
52
+ AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedDestinationHardwareBuffer.get());
53
+ // 2. Copy data over from source -> destination
54
+ copyHardwareBuffer(sourceHardwareBuffer, destinationHardwareBuffer);
55
+ #else
56
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
57
+ #endif
58
+ }
59
+
60
+ void JHardwareBufferUtils::copyHardwareBuffer(AHardwareBuffer* sourceHardwareBuffer, AHardwareBuffer* destinationHardwareBuffer) {
61
+ #if __ANDROID_API__ >= 26
62
+ // 1. Get info about source buffer
63
+ size_t sourceSize = getHardwareBufferSize(sourceHardwareBuffer);
64
+
65
+ // 2. Get info about the destination buffer
66
+ #ifdef NITRO_DEBUG
67
+ size_t destinationSize = getHardwareBufferSize(sourceHardwareBuffer);
68
+ if (sourceSize != destinationSize) {
69
+ throw std::runtime_error("Source HardwareBuffer (" + std::to_string(sourceSize) + " bytes) and destination HardwareBuffer (" +
70
+ std::to_string(destinationSize) + " bytes) are not the same size!");
71
+ }
72
+ #endif
73
+
74
+ // 3. Copy data over
75
+ void* sourceData;
76
+ void* destinationData;
77
+ int lockSource = AHardwareBuffer_lock(sourceHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_MASK, -1, nullptr, &sourceData);
78
+ if (lockSource != 0) {
79
+ throw std::runtime_error("Failed to lock source HardwareBuffer! Error: " + std::to_string(lockSource));
80
+ }
81
+ int lockDestination =
82
+ AHardwareBuffer_lock(destinationHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK, -1, nullptr, &destinationData);
83
+ if (lockDestination != 0) {
84
+ AHardwareBuffer_unlock(sourceHardwareBuffer, nullptr);
85
+ throw std::runtime_error("Failed to lock destination HardwareBuffer! Error: " + std::to_string(lockDestination));
86
+ }
87
+ memcpy(destinationData, sourceData, sourceSize);
88
+ AHardwareBuffer_unlock(sourceHardwareBuffer, nullptr);
89
+ AHardwareBuffer_unlock(destinationHardwareBuffer, nullptr);
90
+ #else
91
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
92
+ #endif
93
+ }
94
+
95
+ } // namespace margelo::nitro
@@ -0,0 +1,39 @@
1
+ //
2
+ // JHardwareBufferUtils.hpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 11.07.25.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include <android/hardware_buffer.h>
11
+ #include <fbjni/fbjni.h>
12
+
13
+ namespace margelo::nitro {
14
+
15
+ using namespace facebook;
16
+
17
+ class JHardwareBufferUtils final : public jni::JavaClass<JHardwareBufferUtils> {
18
+ public:
19
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/utils/HardwareBufferUtils;";
20
+
21
+ public:
22
+ static size_t getHardwareBufferSize(AHardwareBuffer* hardwareBuffer);
23
+
24
+ static void copyHardwareBufferBoxed(jni::alias_ref<jni::JClass>, jni::alias_ref<jni::JObject> boxedSourceHardwareBuffer,
25
+ jni::alias_ref<jni::JObject> boxedDestinationHardwareBuffer);
26
+
27
+ static jni::local_ref<jni::JObject> copyHardwareBufferBoxedNew(jni::alias_ref<jni::JClass>,
28
+ jni::alias_ref<jni::JObject> boxedSourceHardwareBuffer);
29
+
30
+ static void copyHardwareBuffer(AHardwareBuffer* sourceHardwareBuffer, AHardwareBuffer* destinationHardwareBuffer);
31
+
32
+ public:
33
+ static void registerNatives() {
34
+ javaClassStatic()->registerNatives({makeNativeMethod("copyHardwareBuffer", JHardwareBufferUtils::copyHardwareBufferBoxed),
35
+ makeNativeMethod("copyHardwareBuffer", JHardwareBufferUtils::copyHardwareBufferBoxedNew)});
36
+ }
37
+ };
38
+
39
+ } // namespace margelo::nitro
@@ -37,6 +37,7 @@ class AnyMap {
37
37
  external fun remove(key: String)
38
38
  @FastNative
39
39
  external fun clear()
40
+ external fun getAllKeys(): Array<String>
40
41
 
41
42
  @FastNative
42
43
  external fun isNull(key: String): Boolean
@@ -1,11 +1,18 @@
1
1
  package com.margelo.nitro.core
2
2
 
3
3
  import androidx.annotation.Keep
4
+ import android.hardware.HardwareBuffer
5
+ import android.os.Build
6
+ import androidx.annotation.RequiresApi
4
7
  import com.facebook.jni.HybridData
5
8
  import com.facebook.proguard.annotations.DoNotStrip
9
+ import com.margelo.nitro.utils.HardwareBufferUtils
6
10
  import dalvik.annotation.optimization.FastNative
7
11
  import java.nio.ByteBuffer
8
12
 
13
+ // AHardwareBuffer* needs to be boxed in jobject*
14
+ typealias BoxedHardwareBuffer = Any
15
+
9
16
  /**
10
17
  * An ArrayBuffer instance shared between native (Kotlin/C++) and JS.
11
18
  *
@@ -41,6 +48,15 @@ class ArrayBuffer {
41
48
  val isByteBuffer: Boolean
42
49
  get() = getIsByteBuffer()
43
50
 
51
+ /**
52
+ * Whether this `ArrayBuffer` is holding a `HardwareBuffer`, or not.
53
+ * - If the `ArrayBuffer` holds a `HardwareBuffer`, `getHardwareBuffer()` can safely be called without copy.
54
+ * - If the `ArrayBuffer` doesn't hold a `HardwareBuffer`, `getHardwareBuffer()` will throw.
55
+ * You will need to call `getByteBuffer(copyIfNeeded)` instead.
56
+ */
57
+ val isHardwareBuffer: Boolean
58
+ get() = getIsHardwareBuffer()
59
+
44
60
  /**
45
61
  * Get the size of bytes in this `ArrayBuffer`.
46
62
  */
@@ -64,6 +80,16 @@ class ArrayBuffer {
64
80
  return getByteBuffer(copyIfNeeded)
65
81
  }
66
82
 
83
+ /**
84
+ * Get the underlying `HardwareBuffer` if this `ArrayBuffer` was created with one.
85
+ * @throws Error if this `ArrayBuffer` was not created with a `HardwareBuffer`.
86
+ */
87
+ @RequiresApi(Build.VERSION_CODES.O)
88
+ fun getHardwareBuffer(): HardwareBuffer {
89
+ val boxed = getHardwareBufferBoxed()
90
+ return boxed as HardwareBuffer
91
+ }
92
+
67
93
  /**
68
94
  * Create a new **owning-** `ArrayBuffer` that holds the given `ByteBuffer`.
69
95
  * The `ByteBuffer` needs to remain valid for as long as the `ArrayBuffer` is alive.
@@ -76,6 +102,18 @@ class ArrayBuffer {
76
102
  mHybridData = initHybrid(byteBuffer)
77
103
  }
78
104
 
105
+ /**
106
+ * Create a new **owning-** `ArrayBuffer` that holds the given `HardwareBuffer`.
107
+ * The `HardwareBuffer` needs to remain valid for as long as the `ArrayBuffer` is alive.
108
+ */
109
+ @RequiresApi(Build.VERSION_CODES.O)
110
+ constructor(hardwareBuffer: HardwareBuffer) {
111
+ if (hardwareBuffer.isClosed) {
112
+ throw Error("Cannot create ArrayBuffer from an already-closed HardwareBuffer!")
113
+ }
114
+ mHybridData = initHybridBoxedHardwareBuffer(hardwareBuffer)
115
+ }
116
+
79
117
  /**
80
118
  * Create a new **non-owning-** `ArrayBuffer` that holds foreign data, potentially coming from JS.
81
119
  */
@@ -87,12 +125,19 @@ class ArrayBuffer {
87
125
  }
88
126
 
89
127
  private external fun initHybrid(buffer: ByteBuffer): HybridData
128
+ @RequiresApi(Build.VERSION_CODES.O)
129
+ private external fun initHybridBoxedHardwareBuffer(hardwareBufferBoxed: BoxedHardwareBuffer): HybridData
130
+
90
131
  private external fun getByteBuffer(copyIfNeeded: Boolean): ByteBuffer
132
+ private external fun getHardwareBufferBoxed(): BoxedHardwareBuffer
133
+
91
134
  @FastNative
92
135
  private external fun getIsOwner(): Boolean
93
136
  @FastNative
94
137
  private external fun getIsByteBuffer(): Boolean
95
138
  @FastNative
139
+ private external fun getIsHardwareBuffer(): Boolean
140
+ @FastNative
96
141
  private external fun getBufferSize(): Int
97
142
 
98
143
  companion object {
@@ -108,8 +153,13 @@ class ArrayBuffer {
108
153
  * Copy the given `ArrayBuffer` into a new **owning** `ArrayBuffer`.
109
154
  */
110
155
  fun copy(other: ArrayBuffer): ArrayBuffer {
111
- val byteBuffer = other.getBuffer(false)
112
- return copy(byteBuffer)
156
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && other.isHardwareBuffer) {
157
+ val hardwareBuffer = other.getHardwareBuffer()
158
+ return copy(hardwareBuffer)
159
+ } else {
160
+ val byteBuffer = other.getBuffer(false)
161
+ return copy(byteBuffer)
162
+ }
113
163
  }
114
164
 
115
165
  /**
@@ -129,6 +179,14 @@ class ArrayBuffer {
129
179
  // 5. Create a new `ArrayBuffer`
130
180
  return ArrayBuffer(newBuffer)
131
181
  }
182
+ /**
183
+ * Copy the given `HardwareBuffer` into a new **owning** `ArrayBuffer`.
184
+ */
185
+ @RequiresApi(Build.VERSION_CODES.O)
186
+ fun copy(hardwareBuffer: HardwareBuffer): ArrayBuffer {
187
+ val copy = HardwareBufferUtils.copyHardwareBuffer(hardwareBuffer)
188
+ return ArrayBuffer(copy)
189
+ }
132
190
 
133
191
  /**
134
192
  * Wrap the given `ByteBuffer` in a new **owning** `ArrayBuffer`.
@@ -137,5 +195,13 @@ class ArrayBuffer {
137
195
  byteBuffer.rewind()
138
196
  return ArrayBuffer(byteBuffer)
139
197
  }
198
+
199
+ /**
200
+ * Wrap the given `HardwareBuffer` in a new **owning** `ArrayBuffer`.
201
+ */
202
+ @RequiresApi(Build.VERSION_CODES.O)
203
+ fun wrap(hardwareBuffer: HardwareBuffer): ArrayBuffer {
204
+ return ArrayBuffer(hardwareBuffer)
205
+ }
140
206
  }
141
207
  }
@@ -0,0 +1,15 @@
1
+ package com.margelo.nitro.utils
2
+
3
+ import android.hardware.HardwareBuffer
4
+ import android.os.Build
5
+ import androidx.annotation.RequiresApi
6
+
7
+ /**
8
+ * Updates this [[HardwareBuffer]] with the data in the given [[hardwareBuffer]].
9
+ * The given [[hardwareBuffer]] has to have the same description and shape as this [[HardwareBuffer]].
10
+ */
11
+ @RequiresApi(Build.VERSION_CODES.O)
12
+ fun HardwareBuffer.updateFrom(hardwareBuffer: HardwareBuffer) {
13
+ HardwareBufferUtils.copyHardwareBuffer(hardwareBuffer, this)
14
+ }
15
+
@@ -0,0 +1,42 @@
1
+ package com.margelo.nitro.utils
2
+
3
+ import android.hardware.HardwareBuffer
4
+ import android.os.Build
5
+ import androidx.annotation.RequiresApi
6
+
7
+ /**
8
+ * HardwareBuffers are special types in JNI (`AHardwareBuffer*`)
9
+ * and have to be boxed to `jobject*`.
10
+ */
11
+ typealias BoxedHardwareBuffer = Any
12
+
13
+ @Suppress("KotlinJniMissingFunction")
14
+ class HardwareBufferUtils {
15
+ companion object {
16
+ @JvmStatic
17
+ @RequiresApi(Build.VERSION_CODES.O)
18
+ private external fun copyHardwareBuffer(sourceHardwareBuffer: BoxedHardwareBuffer): BoxedHardwareBuffer
19
+ @JvmStatic
20
+ @RequiresApi(Build.VERSION_CODES.O)
21
+ private external fun copyHardwareBuffer(sourceHardwareBuffer: BoxedHardwareBuffer, destinationHardwareBuffer: BoxedHardwareBuffer)
22
+
23
+ /**
24
+ * Copies the given [[hardwareBuffer]] into a new, identically shaped [[HardwareBuffer]].
25
+ */
26
+ @Throws
27
+ @RequiresApi(Build.VERSION_CODES.O)
28
+ fun copyHardwareBuffer(hardwareBuffer: HardwareBuffer): HardwareBuffer {
29
+ val resultBoxed = copyHardwareBuffer(hardwareBuffer as Any)
30
+ return resultBoxed as HardwareBuffer
31
+ }
32
+
33
+ /**
34
+ * Copies the given [[source]] [[HardwareBuffer]] into the given [[destination]] [[HardwareBuffer]].
35
+ */
36
+ @Throws
37
+ @RequiresApi(Build.VERSION_CODES.O)
38
+ fun copyHardwareBuffer(source: HardwareBuffer, destination: HardwareBuffer) {
39
+ copyHardwareBuffer(source as Any, destination as Any)
40
+ }
41
+ }
42
+ }
@@ -16,6 +16,14 @@ void AnyMap::remove(const std::string& key) {
16
16
  void AnyMap::clear() noexcept {
17
17
  _map.clear();
18
18
  }
19
+ std::vector<std::string> AnyMap::getAllKeys() const {
20
+ std::vector<std::string> keys;
21
+ keys.reserve(_map.size());
22
+ for (const auto& pair : _map) {
23
+ keys.push_back(pair.first);
24
+ }
25
+ return keys;
26
+ }
19
27
 
20
28
  // Is
21
29
  bool AnyMap::isNull(const std::string& key) const {
@@ -75,6 +75,10 @@ public:
75
75
  * Deletes all keys and values inside the map.
76
76
  */
77
77
  void clear() noexcept;
78
+ /**
79
+ * Get all keys this `AnyMap` instance contains.
80
+ */
81
+ std::vector<std::string> getAllKeys() const;
78
82
 
79
83
  public:
80
84
  /**
@@ -31,6 +31,10 @@ std::shared_ptr<ArrayBuffer> ArrayBuffer::copy(const std::vector<uint8_t>& data)
31
31
  return ArrayBuffer::copy(data.data(), data.size());
32
32
  }
33
33
 
34
+ std::shared_ptr<ArrayBuffer> ArrayBuffer::copy(const std::shared_ptr<ArrayBuffer>& buffer) {
35
+ return ArrayBuffer::copy(buffer->data(), buffer->size());
36
+ }
37
+
34
38
  std::shared_ptr<ArrayBuffer> ArrayBuffer::allocate(size_t size) {
35
39
  uint8_t* data = new uint8_t[size];
36
40
  return ArrayBuffer::wrap(data, size, [=]() { delete[] data; });
@@ -63,6 +63,10 @@ public:
63
63
  * Create a new `NativeArrayBuffer` that copies the given `std::vector`.
64
64
  */
65
65
  static std::shared_ptr<ArrayBuffer> copy(const std::vector<uint8_t>& data);
66
+ /**
67
+ * Create a new `NativeArrayBuffer` that copies the given `std::shared_ptr<ArrayBuffer>`.
68
+ */
69
+ static std::shared_ptr<ArrayBuffer> copy(const std::shared_ptr<ArrayBuffer>& buffer);
66
70
  /**
67
71
  * Create a new `NativeArrayBuffer` that allocates a new buffer of the given size.
68
72
  */
@@ -181,8 +181,9 @@ private:
181
181
  * Get the `NativeState` of the given `value`.
182
182
  */
183
183
  template <typename THybrid>
184
- static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value, FunctionKind funcKind,
185
- const std::string& funcName) {
184
+ static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value,
185
+ [[maybe_unused]] FunctionKind funcKind,
186
+ [[maybe_unused]] const std::string& funcName) {
186
187
  // 1. Convert jsi::Value to jsi::Object
187
188
  #ifdef NITRO_DEBUG
188
189
  if (!value.isObject()) [[unlikely]] {
@@ -22,7 +22,8 @@ private:
22
22
 
23
23
  public:
24
24
  template <typename... Args>
25
- static void log(LogLevel level, const char* tag, const char* format, Args... args) {
25
+ static void log([[maybe_unused]] LogLevel level, [[maybe_unused]] const char* tag, [[maybe_unused]] const char* format,
26
+ [[maybe_unused]] Args... args) {
26
27
  #ifdef NITRO_DEBUG
27
28
  // 1. Make sure args can be passed to sprintf(..)
28
29
  static_assert(all_are_trivially_copyable<Args...>(), "All arguments passed to Logger::log(..) must be trivially copyable! "
@@ -9,7 +9,7 @@
9
9
  #define NitroDefines_h
10
10
 
11
11
  // Sets the version of the native Nitro core library
12
- #define NITRO_VERSION "0.26.1"
12
+ #define NITRO_VERSION "0.26.3"
13
13
 
14
14
  // Sets whether to use debug or optimized production build flags
15
15
  #ifdef DEBUG
@@ -82,6 +82,19 @@ public final class AnyMapHolder {
82
82
  cppPart.pointee.clear()
83
83
  }
84
84
 
85
+ /**
86
+ * Get all keys in this map.
87
+ */
88
+ public func getAllKeys() -> [String] {
89
+ let cppKeys = cppPart.pointee.getAllKeys()
90
+ var keys = [String]()
91
+ keys.reserveCapacity(cppKeys.count)
92
+ for cppKey in cppKeys {
93
+ keys.append(String(cppKey))
94
+ }
95
+ return keys
96
+ }
97
+
85
98
  // pragma MARK: Getters
86
99
 
87
100
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-modules",
3
- "version": "0.26.1",
3
+ "version": "0.26.3",
4
4
  "description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",