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: 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 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 61 InvalidateCacheOnDestroy::InvalidateCacheOnDestroy(jsi::Runtime &runtime) { 62 key = reinterpret_cast<uintptr_t>(&runtime); 63 } 64 InvalidateCacheOnDestroy::~InvalidateCacheOnDestroy() { 65 propNameIDCache.invalidate(key); 66 } 67 68 TypedArrayKind getTypedArrayKindForName(const std::string &name); 69 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 80 TypedArrayBase::TypedArrayBase(jsi::Runtime &runtime, const jsi::Object &obj) 81 : jsi::Object(jsi::Value(runtime, obj).asObject(runtime)) {} 82 83 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 92 size_t TypedArrayBase::size(jsi::Runtime &runtime) const { 93 return getProperty(runtime, propNameIDCache.get(runtime, Prop::Length)).asNumber(); 94 } 95 96 size_t TypedArrayBase::length(jsi::Runtime &runtime) const { 97 return getProperty(runtime, propNameIDCache.get(runtime, Prop::Length)).asNumber(); 98 } 99 100 size_t TypedArrayBase::byteLength(jsi::Runtime &runtime) const { 101 return getProperty(runtime, propNameIDCache.get(runtime, Prop::ByteLength)).asNumber(); 102 } 103 104 size_t TypedArrayBase::byteOffset(jsi::Runtime &runtime) const { 105 return getProperty(runtime, propNameIDCache.get(runtime, Prop::ByteOffset)).asNumber(); 106 } 107 108 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 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 119 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 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 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 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 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> 184 TypedArray<T>::TypedArray(jsi::Runtime &runtime, size_t size) : TypedArrayBase(runtime, size, T){}; 185 186 template <TypedArrayKind T> 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> 193 TypedArray<T>::TypedArray(TypedArrayBase &&base) : TypedArrayBase(std::move(base)) {} 194 195 template <TypedArrayKind T> 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> 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 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 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 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