-
-
Notifications
You must be signed in to change notification settings - Fork 241
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
352 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
// | ||
// MmkvHostObject.cpp | ||
// Mmkv | ||
// | ||
// Created by Marc Rousavy on 03.09.21. | ||
// Copyright © 2021 Facebook. All rights reserved. | ||
// | ||
|
||
#include "MmkvHostObject.h" | ||
#include "TypedArray.h" | ||
#include <MMKV.h> | ||
#include <android/log.h> | ||
#include <string> | ||
#include <vector> | ||
|
||
MmkvHostObject::MmkvHostObject(const std::string& instanceId, std::string path, | ||
std::string cryptKey) { | ||
bool hasEncryptionKey = cryptKey.size() > 0; | ||
__android_log_print(ANDROID_LOG_INFO, "RNMMKV", | ||
"Creating MMKV instance \"%s\"... (Path: %s, Encrypted: %b)", | ||
instanceId.c_str(), path.c_str(), hasEncryptionKey); | ||
std::string* pathPtr = path.size() > 0 ? &path : nullptr; | ||
std::string* cryptKeyPtr = cryptKey.size() > 0 ? &cryptKey : nullptr; | ||
instance = MMKV::mmkvWithID(instanceId, mmkv::DEFAULT_MMAP_SIZE, MMKV_SINGLE_PROCESS, cryptKeyPtr, | ||
pathPtr); | ||
|
||
if (instance == nullptr) { | ||
// Check if instanceId is invalid | ||
if (instanceId.empty()) { | ||
throw std::runtime_error("Failed to create MMKV instance! `id` cannot be empty!"); | ||
} | ||
|
||
// Check if encryptionKey is invalid | ||
if (cryptKey.size() > 16) { | ||
throw std::runtime_error( | ||
"Failed to create MMKV instance! `encryptionKey` cannot be longer than 16 bytes!"); | ||
} | ||
|
||
throw std::runtime_error("Failed to create MMKV instance!"); | ||
} | ||
} | ||
|
||
std::vector<jsi::PropNameID> MmkvHostObject::getPropertyNames(jsi::Runtime& rt) { | ||
std::vector<jsi::PropNameID> result; | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("set"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getBoolean"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getBuffer"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getString"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getNumber"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("contains"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("delete"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getAllKeys"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("deleteAll"))); | ||
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("recrypt"))); | ||
return result; | ||
} | ||
|
||
jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { | ||
auto propName = propNameId.utf8(runtime); | ||
auto funcName = "MMKV." + propName; | ||
|
||
if (propName == "set") { | ||
// MMKV.set(key: string, value: string | number | bool | Uint8Array) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
2, // key, value | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (!arguments[0].isString()) { | ||
throw jsi::JSError(runtime, | ||
"MMKV::set: First argument ('key') has to be of type string!"); | ||
} | ||
|
||
auto keyName = arguments[0].getString(runtime).utf8(runtime); | ||
|
||
if (arguments[1].isBool()) { | ||
// bool | ||
instance->set(arguments[1].getBool(), keyName); | ||
} else if (arguments[1].isNumber()) { | ||
// number | ||
instance->set(arguments[1].getNumber(), keyName); | ||
} else if (arguments[1].isString()) { | ||
// string | ||
auto stringValue = arguments[1].getString(runtime).utf8(runtime); | ||
instance->set(stringValue, keyName); | ||
} else if (arguments[1].isObject()) { | ||
// object | ||
auto object = arguments[1].asObject(runtime); | ||
if (isTypedArray(runtime, object)) { | ||
// Uint8Array | ||
auto typedArray = getTypedArray(runtime, object); | ||
auto bufferValue = typedArray.getBuffer(runtime); | ||
mmkv::MMBuffer buffer(bufferValue.data(runtime), bufferValue.size(runtime), | ||
mmkv::MMBufferCopyFlag::MMBufferNoCopy); | ||
instance->set(buffer, keyName); | ||
} else { | ||
// unknown object | ||
throw jsi::JSError( | ||
runtime, "MMKV::set: 'value' argument is an object, but not of type Uint8Array!"); | ||
} | ||
} else { | ||
// unknown type | ||
throw jsi::JSError( | ||
runtime, | ||
"MMKV::set: 'value' argument is not of type bool, number, string or buffer!"); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
if (propName == "getBoolean") { | ||
// MMKV.getBoolean(key: string) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
1, // key | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (!arguments[0].isString()) { | ||
throw jsi::JSError(runtime, "First argument ('key') has to be of type string!"); | ||
} | ||
|
||
auto keyName = arguments[0].getString(runtime).utf8(runtime); | ||
bool hasValue; | ||
auto value = instance->getBool(keyName, false, &hasValue); | ||
if (hasValue) { | ||
return jsi::Value(value); | ||
} else { | ||
return jsi::Value::undefined(); | ||
} | ||
}); | ||
} | ||
|
||
if (propName == "getString") { | ||
// MMKV.getString(key: string) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
1, // key | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (!arguments[0].isString()) { | ||
throw jsi::JSError(runtime, "First argument ('key') has to be of type string!"); | ||
} | ||
|
||
auto keyName = arguments[0].getString(runtime).utf8(runtime); | ||
std::string result; | ||
bool hasValue = instance->getString(keyName, result); | ||
if (hasValue) { | ||
return jsi::Value(runtime, jsi::String::createFromUtf8(runtime, result)); | ||
} else { | ||
return jsi::Value::undefined(); | ||
} | ||
}); | ||
} | ||
|
||
if (propName == "getNumber") { | ||
// MMKV.getNumber(key: string) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
1, // key | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (!arguments[0].isString()) { | ||
throw jsi::JSError(runtime, "First argument ('key') has to be of type string!"); | ||
} | ||
|
||
auto keyName = arguments[0].getString(runtime).utf8(runtime); | ||
bool hasValue; | ||
auto value = instance->getDouble(keyName, 0.0, &hasValue); | ||
if (hasValue) { | ||
return jsi::Value(value); | ||
} else { | ||
return jsi::Value::undefined(); | ||
} | ||
}); | ||
} | ||
|
||
if (propName == "getBuffer") { | ||
// MMKV.getBuffer(key: string) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
1, // key | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (!arguments[0].isString()) { | ||
throw jsi::JSError(runtime, "First argument ('key') has to be of type string!"); | ||
} | ||
|
||
auto keyName = arguments[0].getString(runtime).utf8(runtime); | ||
mmkv::MMBuffer buffer; | ||
bool hasValue = instance->getBytes(keyName, buffer); | ||
if (hasValue) { | ||
auto length = buffer.length(); | ||
TypedArray<TypedArrayKind::Uint8Array> array(runtime, length); | ||
auto data = static_cast<const unsigned char*>(buffer.getPtr()); | ||
std::vector<unsigned char> vector(length); | ||
vector.assign(data, data + length); | ||
array.update(runtime, vector); | ||
return array; | ||
} else { | ||
return jsi::Value::undefined(); | ||
} | ||
}); | ||
} | ||
|
||
if (propName == "contains") { | ||
// MMKV.contains(key: string) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
1, // key | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (!arguments[0].isString()) { | ||
throw jsi::JSError(runtime, "First argument ('key') has to be of type string!"); | ||
} | ||
|
||
auto keyName = arguments[0].getString(runtime).utf8(runtime); | ||
bool containsKey = instance->containsKey(keyName); | ||
return jsi::Value(containsKey); | ||
}); | ||
} | ||
|
||
if (propName == "delete") { | ||
// MMKV.delete(key: string) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
1, // key | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (!arguments[0].isString()) { | ||
throw jsi::JSError(runtime, "First argument ('key') has to be of type string!"); | ||
} | ||
|
||
auto keyName = arguments[0].getString(runtime).utf8(runtime); | ||
instance->removeValueForKey(keyName); | ||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
if (propName == "getAllKeys") { | ||
// MMKV.getAllKeys() | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), 0, | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
auto keys = instance->allKeys(); | ||
auto array = jsi::Array(runtime, keys.size()); | ||
for (int i = 0; i < keys.size(); i++) { | ||
array.setValueAtIndex(runtime, i, keys[i]); | ||
} | ||
return array; | ||
}); | ||
} | ||
|
||
if (propName == "clearAll") { | ||
// MMKV.clearAll() | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), 0, | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
instance->clearAll(); | ||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
if (propName == "recrypt") { | ||
// MMKV.recrypt(encryptionKey) | ||
return jsi::Function::createFromHostFunction( | ||
runtime, jsi::PropNameID::forAscii(runtime, funcName), | ||
1, // encryptionKey | ||
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, | ||
size_t count) -> jsi::Value { | ||
if (arguments[0].isUndefined()) { | ||
// reset encryption key to "no encryption" | ||
instance->reKey(std::string()); | ||
} else if (arguments[0].isString()) { | ||
// reKey(..) with new encryption-key | ||
auto encryptionKey = arguments[0].getString(runtime).utf8(runtime); | ||
instance->reKey(encryptionKey); | ||
} else { | ||
throw jsi::JSError( | ||
runtime, | ||
"First argument ('encryptionKey') has to be of type string (or undefined)!"); | ||
} | ||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// | ||
// MmkvHostObject.h | ||
// Mmkv | ||
// | ||
// Created by Marc Rousavy on 03.09.21. | ||
// Copyright © 2021 Facebook. All rights reserved. | ||
// | ||
|
||
#pragma once | ||
|
||
#include <MMKV.h> | ||
#include <jsi/jsi.h> | ||
|
||
using namespace facebook; | ||
|
||
class JSI_EXPORT MmkvHostObject : public jsi::HostObject { | ||
public: | ||
MmkvHostObject(const std::string& instanceId, std::string path, std::string cryptKey); | ||
|
||
public: | ||
jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override; | ||
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override; | ||
|
||
private: | ||
MMKV* instance; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,38 @@ | ||
#ifndef MMKV_H | ||
#define MMKV_H | ||
// | ||
// react-native-mmkv.h | ||
// react-native-mmkv | ||
// | ||
// Created by Marc Rousavy on 25.03.2024. | ||
// | ||
|
||
namespace mmkv { | ||
double multiply(double a, double b); | ||
} | ||
#pragma once | ||
|
||
#if __has_include(<React-Codegen/RNMmkvSpecJSI.h>) | ||
// CocoaPods include (iOS) | ||
#include <React-Codegen/RNMmkvSpecJSI.h> | ||
#elif __has_include("RNMmkvSpecJSI.h") | ||
// CMake include on Android | ||
#include "RNMmkvSpecJSI.h" | ||
#else | ||
#error Cannot find react-native-mmkv spec! Try cleaning your cache and re-running CodeGen! | ||
#endif | ||
|
||
#include "MmkvHostObject.h" | ||
|
||
namespace facebook::react { | ||
|
||
#endif /* MMKV_H */ | ||
class NativeMmkvModule : public NativeMmkvCxxSpec<NativeMmkvModule> { | ||
public: | ||
NativeMmkvModule(std::shared_ptr<CallInvoker> jsInvoker); | ||
|
||
jsi::Object createMMKV(jsi::Runtime& runtime, jsi::Object config) { | ||
std::string instanceId = bridging::fromJs<std::string>(runtime, config.getProperty(runtime, "id"), jsInvoker_); | ||
std::string path = bridging::fromJs<std::string>(runtime, config.getProperty(runtime, "path"), jsInvoker_); | ||
std::string encryptionKey = bridging::fromJs<std::string>(runtime, config.getProperty(runtime, "encryptionKey"), jsInvoker_); | ||
|
||
auto instance = std::make_shared<MmkvHostObject>(instanceId, path, encryptionKey); | ||
return jsi::Object::createFromHostObject(runtime, instance); | ||
} | ||
}; | ||
|
||
} |
This file was deleted.
Oops, something went wrong.