1 #include "EXTypedArrayApi.h"
2
3 #include <unordered_map>
4
5 namespace expo {
6 namespace gl_cpp {
7
8 template <TypedArrayKind T>
9 using ContentType = typename typedArrayTypeMap<T>::type;
10
11 enum class Prop {
12 Buffer, // "buffer"
13 Constructor, // "constructor"
14 Name, // "name"
15 Proto, // "__proto__"
16 Length, // "length"
17 ByteLength, // "byteLength"
18 ByteOffset, // "offset"
19 IsView, // "isView"
20 ArrayBuffer, // "ArrayBuffer"
21 Int8Array, // "Int8Array"
22 Int16Array, // "Int16Array"
23 Int32Array, // "Int32Array"
24 Uint8Array, // "Uint8Array"
25 Uint8ClampedArray, // "Uint8ClampedArray"
26 Uint16Array, // "Uint16Array"
27 Uint32Array, // "Uint32Array"
28 Float32Array, // "Float32Array"
29 Float64Array, // "Float64Array"
30 };
31
32 class PropNameIDCache {
33 public:
get(jsi::Runtime & runtime,Prop prop)34 const jsi::PropNameID &get(jsi::Runtime &runtime, Prop prop) {
35 auto key = reinterpret_cast<uintptr_t>(&runtime);
36 if (this->props.find(key) == this->props.end()) {
37 this->props[key] = std::unordered_map<Prop, std::unique_ptr<jsi::PropNameID>>();
38 }
39 if (!this->props[key][prop]) {
40 this->props[key][prop] = std::make_unique<jsi::PropNameID>(createProp(runtime, prop));
41 }
42 return *(this->props[key][prop]);
43 }
44
45 const jsi::PropNameID &getConstructorNameProp(jsi::Runtime &runtime, TypedArrayKind kind);
46
invalidate(uintptr_t key)47 void invalidate(uintptr_t key) {
48 if (props.find(key) != props.end()) {
49 props[key].clear();
50 }
51 }
52
53 private:
54 std::unordered_map<uintptr_t, std::unordered_map<Prop, std::unique_ptr<jsi::PropNameID>>> props;
55
56 jsi::PropNameID createProp(jsi::Runtime &runtime, Prop prop);
57 };
58
59 PropNameIDCache propNameIDCache;
60
InvalidateCacheOnDestroy(jsi::Runtime & runtime)61 InvalidateCacheOnDestroy::InvalidateCacheOnDestroy(jsi::Runtime &runtime) {
62 key = reinterpret_cast<uintptr_t>(&runtime);
63 }
~InvalidateCacheOnDestroy()64 InvalidateCacheOnDestroy::~InvalidateCacheOnDestroy() {
65 propNameIDCache.invalidate(key);
66 }
67
68 TypedArrayKind getTypedArrayKindForName(const std::string &name);
69
TypedArrayBase(jsi::Runtime & runtime,size_t size,TypedArrayKind kind)70 TypedArrayBase::TypedArrayBase(jsi::Runtime &runtime, size_t size, TypedArrayKind kind)
71 : TypedArrayBase(
72 runtime,
73 runtime.global()
74 .getProperty(runtime, propNameIDCache.getConstructorNameProp(runtime, kind))
75 .asObject(runtime)
76 .asFunction(runtime)
77 .callAsConstructor(runtime, {static_cast<double>(size)})
78 .asObject(runtime)) {}
79
TypedArrayBase(jsi::Runtime & runtime,const jsi::Object & obj)80 TypedArrayBase::TypedArrayBase(jsi::Runtime &runtime, const jsi::Object &obj)
81 : jsi::Object(jsi::Value(runtime, obj).asObject(runtime)) {}
82
getKind(jsi::Runtime & runtime) const83 TypedArrayKind TypedArrayBase::getKind(jsi::Runtime &runtime) const {
84 auto constructorName = this->getProperty(runtime, propNameIDCache.get(runtime, Prop::Constructor))
85 .asObject(runtime)
86 .getProperty(runtime, propNameIDCache.get(runtime, Prop::Name))
87 .asString(runtime)
88 .utf8(runtime);
89 return getTypedArrayKindForName(constructorName);
90 };
91
size(jsi::Runtime & runtime) const92 size_t TypedArrayBase::size(jsi::Runtime &runtime) const {
93 return getProperty(runtime, propNameIDCache.get(runtime, Prop::Length)).asNumber();
94 }
95
length(jsi::Runtime & runtime) const96 size_t TypedArrayBase::length(jsi::Runtime &runtime) const {
97 return getProperty(runtime, propNameIDCache.get(runtime, Prop::Length)).asNumber();
98 }
99
byteLength(jsi::Runtime & runtime) const100 size_t TypedArrayBase::byteLength(jsi::Runtime &runtime) const {
101 return getProperty(runtime, propNameIDCache.get(runtime, Prop::ByteLength)).asNumber();
102 }
103
byteOffset(jsi::Runtime & runtime) const104 size_t TypedArrayBase::byteOffset(jsi::Runtime &runtime) const {
105 return getProperty(runtime, propNameIDCache.get(runtime, Prop::ByteOffset)).asNumber();
106 }
107
hasBuffer(jsi::Runtime & runtime) const108 bool TypedArrayBase::hasBuffer(jsi::Runtime &runtime) const {
109 auto buffer = getProperty(runtime, propNameIDCache.get(runtime, Prop::Buffer));
110 return buffer.isObject() && buffer.asObject(runtime).isArrayBuffer(runtime);
111 }
112
toVector(jsi::Runtime & runtime)113 std::vector<uint8_t> TypedArrayBase::toVector(jsi::Runtime &runtime) {
114 auto start = reinterpret_cast<uint8_t *>(getBuffer(runtime).data(runtime) + byteOffset(runtime));
115 auto end = start + byteLength(runtime);
116 return std::vector<uint8_t>(start, end);
117 }
118
getBuffer(jsi::Runtime & runtime) const119 jsi::ArrayBuffer TypedArrayBase::getBuffer(jsi::Runtime &runtime) const {
120 auto buffer = getProperty(runtime, propNameIDCache.get(runtime, Prop::Buffer));
121 if (buffer.isObject() && buffer.asObject(runtime).isArrayBuffer(runtime)) {
122 return buffer.asObject(runtime).getArrayBuffer(runtime);
123 } else {
124 throw std::runtime_error("no ArrayBuffer attached");
125 }
126 }
127
isTypedArray(jsi::Runtime & runtime,const jsi::Object & jsObj)128 bool isTypedArray(jsi::Runtime &runtime, const jsi::Object &jsObj) {
129 auto jsVal = runtime.global()
130 .getProperty(runtime, propNameIDCache.get(runtime, Prop::ArrayBuffer))
131 .asObject(runtime)
132 .getProperty(runtime, propNameIDCache.get(runtime, Prop::IsView))
133 .asObject(runtime)
134 .asFunction(runtime)
135 .callWithThis(runtime, runtime.global(), {jsi::Value(runtime, jsObj)});
136 if (jsVal.isBool()) {
137 return jsVal.getBool();
138 } else {
139 throw std::runtime_error("value is not a boolean");
140 }
141 }
142
getTypedArray(jsi::Runtime & runtime,const jsi::Object & jsObj)143 TypedArrayBase getTypedArray(jsi::Runtime &runtime, const jsi::Object &jsObj) {
144 auto jsVal = runtime.global()
145 .getProperty(runtime, propNameIDCache.get(runtime, Prop::ArrayBuffer))
146 .asObject(runtime)
147 .getProperty(runtime, propNameIDCache.get(runtime, Prop::IsView))
148 .asObject(runtime)
149 .asFunction(runtime)
150 .callWithThis(runtime, runtime.global(), {jsi::Value(runtime, jsObj)});
151 if (jsVal.isBool()) {
152 return TypedArrayBase(runtime, jsObj);
153 } else {
154 throw std::runtime_error("value is not a boolean");
155 }
156 }
157
arrayBufferToVector(jsi::Runtime & runtime,jsi::Object & jsObj)158 std::vector<uint8_t> arrayBufferToVector(jsi::Runtime &runtime, jsi::Object &jsObj) {
159 if (!jsObj.isArrayBuffer(runtime)) {
160 throw std::runtime_error("Object is not an ArrayBuffer");
161 }
162 auto jsArrayBuffer = jsObj.getArrayBuffer(runtime);
163
164 uint8_t *dataBlock = jsArrayBuffer.data(runtime);
165 size_t blockSize =
166 jsArrayBuffer.getProperty(runtime, propNameIDCache.get(runtime, Prop::ByteLength)).asNumber();
167 return std::vector<uint8_t>(dataBlock, dataBlock + blockSize);
168 }
169
arrayBufferUpdate(jsi::Runtime & runtime,jsi::ArrayBuffer & buffer,std::vector<uint8_t> data,size_t offset)170 void arrayBufferUpdate(
171 jsi::Runtime &runtime,
172 jsi::ArrayBuffer &buffer,
173 std::vector<uint8_t> data,
174 size_t offset) {
175 uint8_t *dataBlock = buffer.data(runtime);
176 size_t blockSize = buffer.size(runtime);
177 if (data.size() > blockSize) {
178 throw jsi::JSError(runtime, "ArrayBuffer is to small to fit data");
179 }
180 std::copy(data.begin(), data.end(), dataBlock + offset);
181 }
182
183 template <TypedArrayKind T>
TypedArray(jsi::Runtime & runtime,size_t size)184 TypedArray<T>::TypedArray(jsi::Runtime &runtime, size_t size) : TypedArrayBase(runtime, size, T){};
185
186 template <TypedArrayKind T>
TypedArray(jsi::Runtime & runtime,std::vector<ContentType<T>> data)187 TypedArray<T>::TypedArray(jsi::Runtime &runtime, std::vector<ContentType<T>> data)
188 : TypedArrayBase(runtime, data.size(), T) {
189 update(runtime, data);
190 };
191
192 template <TypedArrayKind T>
TypedArray(TypedArrayBase && base)193 TypedArray<T>::TypedArray(TypedArrayBase &&base) : TypedArrayBase(std::move(base)) {}
194
195 template <TypedArrayKind T>
toVector(jsi::Runtime & runtime)196 std::vector<ContentType<T>> TypedArray<T>::toVector(jsi::Runtime &runtime) {
197 auto start =
198 reinterpret_cast<ContentType<T> *>(getBuffer(runtime).data(runtime) + byteOffset(runtime));
199 auto end = start + size(runtime);
200 return std::vector<ContentType<T>>(start, end);
201 }
202
203 template <TypedArrayKind T>
update(jsi::Runtime & runtime,const std::vector<ContentType<T>> & data)204 void TypedArray<T>::update(jsi::Runtime &runtime, const std::vector<ContentType<T>> &data) {
205 if (data.size() != size(runtime)) {
206 throw jsi::JSError(runtime, "TypedArray can only be updated with a vector of the same size");
207 }
208 uint8_t *rawData = getBuffer(runtime).data(runtime) + byteOffset(runtime);
209 std::copy(data.begin(), data.end(), reinterpret_cast<ContentType<T> *>(rawData));
210 }
211
getConstructorNameProp(jsi::Runtime & runtime,TypedArrayKind kind)212 const jsi::PropNameID &PropNameIDCache::getConstructorNameProp(
213 jsi::Runtime &runtime,
214 TypedArrayKind kind) {
215 switch (kind) {
216 case TypedArrayKind::Int8Array:
217 return get(runtime, Prop::Int8Array);
218 case TypedArrayKind::Int16Array:
219 return get(runtime, Prop::Int16Array);
220 case TypedArrayKind::Int32Array:
221 return get(runtime, Prop::Int32Array);
222 case TypedArrayKind::Uint8Array:
223 return get(runtime, Prop::Uint8Array);
224 case TypedArrayKind::Uint8ClampedArray:
225 return get(runtime, Prop::Uint8ClampedArray);
226 case TypedArrayKind::Uint16Array:
227 return get(runtime, Prop::Uint16Array);
228 case TypedArrayKind::Uint32Array:
229 return get(runtime, Prop::Uint32Array);
230 case TypedArrayKind::Float32Array:
231 return get(runtime, Prop::Float32Array);
232 case TypedArrayKind::Float64Array:
233 return get(runtime, Prop::Float64Array);
234 }
235 }
236
createProp(jsi::Runtime & runtime,Prop prop)237 jsi::PropNameID PropNameIDCache::createProp(jsi::Runtime &runtime, Prop prop) {
238 auto create = [&](const std::string &propName) {
239 return jsi::PropNameID::forUtf8(runtime, propName);
240 };
241 switch (prop) {
242 case Prop::Buffer:
243 return create("buffer");
244 case Prop::Constructor:
245 return create("constructor");
246 case Prop::Name:
247 return create("name");
248 case Prop::Proto:
249 return create("__proto__");
250 case Prop::Length:
251 return create("length");
252 case Prop::ByteLength:
253 return create("byteLength");
254 case Prop::ByteOffset:
255 return create("byteOffset");
256 case Prop::IsView:
257 return create("isView");
258 case Prop::ArrayBuffer:
259 return create("ArrayBuffer");
260 case Prop::Int8Array:
261 return create("Int8Array");
262 case Prop::Int16Array:
263 return create("Int16Array");
264 case Prop::Int32Array:
265 return create("Int32Array");
266 case Prop::Uint8Array:
267 return create("Uint8Array");
268 case Prop::Uint8ClampedArray:
269 return create("Uint8ClampedArray");
270 case Prop::Uint16Array:
271 return create("Uint16Array");
272 case Prop::Uint32Array:
273 return create("Uint32Array");
274 case Prop::Float32Array:
275 return create("Float32Array");
276 case Prop::Float64Array:
277 return create("Float64Array");
278 }
279 }
280
281 std::unordered_map<std::string, TypedArrayKind> nameToKindMap = {
282 {"Int8Array", TypedArrayKind::Int8Array},
283 {"Int16Array", TypedArrayKind::Int16Array},
284 {"Int32Array", TypedArrayKind::Int32Array},
285 {"Uint8Array", TypedArrayKind::Uint8Array},
286 {"Uint8ClampedArray", TypedArrayKind::Uint8ClampedArray},
287 {"Uint16Array", TypedArrayKind::Uint16Array},
288 {"Uint32Array", TypedArrayKind::Uint32Array},
289 {"Float32Array", TypedArrayKind::Float32Array},
290 {"Float64Array", TypedArrayKind::Float64Array},
291 };
292
getTypedArrayKindForName(const std::string & name)293 TypedArrayKind getTypedArrayKindForName(const std::string &name) {
294 return nameToKindMap.at(name);
295 }
296
297 template class TypedArray<TypedArrayKind::Int8Array>;
298 template class TypedArray<TypedArrayKind::Int16Array>;
299 template class TypedArray<TypedArrayKind::Int32Array>;
300 template class TypedArray<TypedArrayKind::Uint8Array>;
301 template class TypedArray<TypedArrayKind::Uint8ClampedArray>;
302 template class TypedArray<TypedArrayKind::Uint16Array>;
303 template class TypedArray<TypedArrayKind::Uint32Array>;
304 template class TypedArray<TypedArrayKind::Float32Array>;
305 template class TypedArray<TypedArrayKind::Float64Array>;
306
307 } // namespace gl_cpp
308 } // namespace expo
309