react-native-mmkv 1.6.2 → 2.1.0

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.
Files changed (56) hide show
  1. package/MMKV/CHANGELOG.md +30 -0
  2. package/MMKV/Core/CMakeLists.txt +2 -1
  3. package/MMKV/Core/CodedInputDataCrypt.cpp +1 -1
  4. package/MMKV/Core/Core.xcodeproj/project.pbxproj +7 -4
  5. package/MMKV/Core/InterProcessLock_Win32.cpp +10 -5
  6. package/MMKV/Core/MMBuffer.h +1 -0
  7. package/MMKV/Core/MMKV.cpp +359 -17
  8. package/MMKV/Core/MMKV.h +29 -4
  9. package/MMKV/Core/MMKVLog.cpp +11 -10
  10. package/MMKV/Core/MMKVLog.h +1 -1
  11. package/MMKV/Core/MMKVPredef.h +6 -4
  12. package/MMKV/Core/MMKV_Android.cpp +2 -6
  13. package/MMKV/Core/MMKV_IO.cpp +1 -3
  14. package/MMKV/Core/MMKV_IO.h +3 -3
  15. package/MMKV/Core/MemoryFile.cpp +276 -43
  16. package/MMKV/Core/MemoryFile.h +85 -9
  17. package/MMKV/Core/MemoryFile_Android.cpp +37 -18
  18. package/MMKV/Core/MemoryFile_Linux.cpp +120 -0
  19. package/MMKV/Core/MemoryFile_OSX.cpp +92 -2
  20. package/MMKV/Core/MemoryFile_Win32.cpp +254 -34
  21. package/MMKV/Core/aes/openssl/openssl_aes.h +2 -2
  22. package/MMKV/Core/aes/openssl/openssl_aes_core.cpp +4 -4
  23. package/MMKV/README.md +4 -4
  24. package/README.md +25 -18
  25. package/android/CMakeLists.txt +1 -1
  26. package/android/build.gradle +41 -9
  27. package/android/src/main/cpp/MmkvHostObject.cpp +36 -0
  28. package/android/src/main/java/com/reactnativemmkv/MmkvModule.java +33 -8
  29. package/android/src/main/java/com/reactnativemmkv/MmkvPackage.java +1 -2
  30. package/ios/MmkvHostObject.mm +48 -5
  31. package/ios/MmkvModule.h +5 -0
  32. package/ios/MmkvModule.mm +68 -0
  33. package/lib/commonjs/MMKV.js +16 -1
  34. package/lib/commonjs/MMKV.js.map +1 -1
  35. package/lib/commonjs/createMMKV.js +50 -2
  36. package/lib/commonjs/createMMKV.js.map +1 -1
  37. package/lib/commonjs/createMMKV.web.js +4 -1
  38. package/lib/commonjs/createMMKV.web.js.map +1 -1
  39. package/lib/commonjs/hooks.js +37 -3
  40. package/lib/commonjs/hooks.js.map +1 -1
  41. package/lib/module/MMKV.js +16 -1
  42. package/lib/module/MMKV.js.map +1 -1
  43. package/lib/module/createMMKV.js +48 -2
  44. package/lib/module/createMMKV.js.map +1 -1
  45. package/lib/module/createMMKV.web.js +4 -1
  46. package/lib/module/createMMKV.web.js.map +1 -1
  47. package/lib/module/hooks.js +33 -2
  48. package/lib/module/hooks.js.map +1 -1
  49. package/lib/typescript/MMKV.d.ts +19 -3
  50. package/lib/typescript/createMMKV.d.ts +1 -0
  51. package/lib/typescript/hooks.d.ts +15 -1
  52. package/package.json +1 -1
  53. package/react-native-mmkv.podspec +2 -2
  54. package/android/src/main/java/com/reactnativemmkv/MmkvModulePackage.java +0 -16
  55. package/ios/Mmkv.h +0 -7
  56. package/ios/Mmkv.mm +0 -76
package/MMKV/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # MMKV Change Log
2
2
 
3
+ ## v1.2.12 / 2022-01-17
4
+ ### Changes for All platforms
5
+ * Fix a bug that a subsequential `clearAll()` call may fail to take effect in multi-process mode.
6
+ * Hide some OpenSSL symbols to prevent link-time symbol conflict, when an App somehow also static linking OpenSSL.
7
+
8
+ ### Android
9
+ * Upgrade `compileSdkVersion` & `targetSdkVersion` from `30` to `31`.
10
+
11
+ ## v1.2.11 / 2021-10-26
12
+
13
+ ### Android
14
+ * Due to increasing report about crash inside STL, we have decided to make MMKV **static linking** `libc++` **by default**. Starting from v1.2.11, `com.tencent:mmkv-static` is the same as `com.tencent:mmkv`.
15
+ * For those still in need of MMKV with shared linking of `libc++_shared`, you could use `com.tencent:mmkv-shared` instead.
16
+ * Add backup & restore ability.
17
+
18
+ ### iOS / macOS
19
+ * Add backup & restore ability.
20
+ * Support tvOS.
21
+ * Fix a compile error on some old Xcode.
22
+
23
+ ### Flutter (v1.2.12)
24
+ * Add backup & restore ability.
25
+
26
+ ### POSIX / golang / Python
27
+ * Add backup & restore ability.
28
+ * Fix a compile error on Gentoo.
29
+
30
+ ### Win32
31
+ * Add backup & restore ability.
32
+
3
33
  ## v1.2.10 / 2021-06-25
4
34
  This version is mainly for Android & Flutter.
5
35
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  # Sets the minimum version of CMake required to build the native library.
22
22
 
23
- cmake_minimum_required(VERSION 3.8.0)
23
+ cmake_minimum_required(VERSION 3.10.0)
24
24
 
25
25
  IF(APPLE)
26
26
  # tell ranlib to ignore empty compilation units
@@ -102,6 +102,7 @@ add_library(core
102
102
  MemoryFile.h
103
103
  MemoryFile.cpp
104
104
  MemoryFile_Android.cpp
105
+ MemoryFile_Linux.cpp
105
106
  MemoryFile_Win32.cpp
106
107
  MemoryFile_OSX.cpp
107
108
  ThreadLock.h
@@ -108,7 +108,7 @@ void CodedInputDataCrypt::consumeBytes(size_t length, bool discardPreData) {
108
108
  bytesLeftInBuffer = m_decryptBufferSize - m_decryptBufferDecryptLength;
109
109
  }
110
110
  }
111
- // still no enough sapce, try realloc()
111
+ // still no enough space, try realloc()
112
112
  if (bytesLeftInBuffer < length) {
113
113
  auto newSize = m_decryptBufferSize + length;
114
114
  auto newBuffer = realloc(m_decryptBuffer, newSize);
@@ -256,6 +256,7 @@
256
256
  CB58B3FE23AB3035002457F1 /* Frameworks */,
257
257
  CB9563D923AB2D9500ACCD39 /* Products */,
258
258
  );
259
+ indentWidth = 4;
259
260
  sourceTree = "<group>";
260
261
  };
261
262
  CB9563D923AB2D9500ACCD39 /* Products */ = {
@@ -576,8 +577,9 @@
576
577
  PRODUCT_NAME = "$(TARGET_NAME)";
577
578
  SDKROOT = "";
578
579
  SKIP_INSTALL = YES;
579
- SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
580
- TARGETED_DEVICE_FAMILY = "1,2";
580
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx appletvsimulator appletvos";
581
+ TARGETED_DEVICE_FAMILY = "1,2,3";
582
+ TVOS_DEPLOYMENT_TARGET = 13.0;
581
583
  };
582
584
  name = Debug;
583
585
  };
@@ -603,8 +605,9 @@
603
605
  PRODUCT_NAME = "$(TARGET_NAME)";
604
606
  SDKROOT = "";
605
607
  SKIP_INSTALL = YES;
606
- SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
607
- TARGETED_DEVICE_FAMILY = "1,2";
608
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx appletvsimulator appletvos";
609
+ TARGETED_DEVICE_FAMILY = "1,2,3";
610
+ TVOS_DEPLOYMENT_TARGET = 13.0;
608
611
  };
609
612
  name = Release;
610
613
  };
@@ -50,7 +50,10 @@ bool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeed
50
50
  // let's be gentleman: unlock my shared-lock to prevent deadlock
51
51
  auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);
52
52
  if (!ret) {
53
- MMKVError("fail to try unlock first fd=%p, error:%d", m_fd, GetLastError());
53
+ auto lastError = GetLastError();
54
+ if (lastError != ERROR_NOT_LOCKED) {
55
+ MMKVError("fail to try unlock first fd=%p, error:%d", m_fd, lastError);
56
+ }
54
57
  }
55
58
  }
56
59
 
@@ -91,11 +94,13 @@ bool FileLock::platformUnLock(bool unlockToSharedLock) {
91
94
  }
92
95
  auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);
93
96
  if (!ret) {
94
- MMKVError("fail to unlock fd=%p, error:%d", m_fd, GetLastError());
95
- return false;
96
- } else {
97
- return true;
97
+ auto lastError = GetLastError();
98
+ if (lastError != ERROR_NOT_LOCKED) {
99
+ MMKVError("fail to unlock fd=%p, error:%d", m_fd, lastError);
100
+ return false;
101
+ }
98
102
  }
103
+ return true;
99
104
  }
100
105
 
101
106
  } // namespace mmkv
@@ -26,6 +26,7 @@
26
26
 
27
27
  #include <cstdint>
28
28
  #include <cstdlib>
29
+ #include <cstddef>
29
30
 
30
31
  namespace mmkv {
31
32
 
@@ -38,6 +38,8 @@
38
38
  #include <algorithm>
39
39
  #include <cstdio>
40
40
  #include <cstring>
41
+ #include <unordered_set>
42
+ //#include <unistd.h>
41
43
 
42
44
  #if defined(__aarch64__) && defined(__linux)
43
45
  # include <asm/hwcap.h>
@@ -53,7 +55,7 @@
53
55
  using namespace std;
54
56
  using namespace mmkv;
55
57
 
56
- unordered_map<std::string, MMKV *> *g_instanceDic;
58
+ unordered_map<string, MMKV *> *g_instanceDic;
57
59
  ThreadLock *g_instanceLock;
58
60
  MMKVPath_t g_rootDir;
59
61
  static mmkv::ErrorHandler g_errorHandler;
@@ -61,15 +63,22 @@ size_t mmkv::DEFAULT_MMAP_SIZE;
61
63
 
62
64
  #ifndef MMKV_WIN32
63
65
  constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = "specialCharacter";
66
+ constexpr auto CRC_SUFFIX = ".crc";
64
67
  #else
65
68
  constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = L"specialCharacter";
69
+ constexpr auto CRC_SUFFIX = L".crc";
66
70
  #endif
71
+
67
72
  constexpr uint32_t Fixed32Size = pbFixed32Size();
68
73
 
69
74
  MMKV_NAMESPACE_BEGIN
70
75
 
76
+ static MMKVPath_t encodeFilePath(const string &mmapID, const MMKVPath_t &rootDir);
77
+ bool endsWith(const MMKVPath_t &str, const MMKVPath_t &suffix);
78
+ MMKVPath_t filename(const MMKVPath_t &path);
79
+
71
80
  #ifndef MMKV_ANDROID
72
- MMKV::MMKV(const std::string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath)
81
+ MMKV::MMKV(const string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath)
73
82
  : m_mmapID(mmapID)
74
83
  , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
75
84
  , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
@@ -160,10 +169,10 @@ void initialize() {
160
169
  auto hwcaps = getauxval(AT_HWCAP);
161
170
  # ifndef MMKV_DISABLE_CRYPT
162
171
  if (hwcaps & HWCAP_AES) {
163
- AES_set_encrypt_key = openssl_aes_armv8_set_encrypt_key;
164
- AES_set_decrypt_key = openssl_aes_armv8_set_decrypt_key;
165
- AES_encrypt = openssl_aes_armv8_encrypt;
166
- AES_decrypt = openssl_aes_armv8_decrypt;
172
+ openssl::AES_set_encrypt_key = openssl_aes_armv8_set_encrypt_key;
173
+ openssl::AES_set_decrypt_key = openssl_aes_armv8_set_decrypt_key;
174
+ openssl::AES_encrypt = openssl_aes_armv8_encrypt;
175
+ openssl::AES_decrypt = openssl_aes_armv8_decrypt;
167
176
  MMKVInfo("armv8 AES instructions is supported");
168
177
  } else {
169
178
  MMKVInfo("armv8 AES instructions is not supported");
@@ -198,6 +207,10 @@ void MMKV::initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel) {
198
207
  MMKVInfo("root dir: " MMKV_PATH_FORMAT, g_rootDir.c_str());
199
208
  }
200
209
 
210
+ const MMKVPath_t &MMKV::getRootDir() {
211
+ return g_rootDir;
212
+ }
213
+
201
214
  #ifndef MMKV_ANDROID
202
215
  MMKV *MMKV::mmkvWithID(const string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath) {
203
216
 
@@ -321,7 +334,7 @@ string MMKV::cryptKey() const {
321
334
  if (m_crypter) {
322
335
  char key[AES_KEY_LEN];
323
336
  m_crypter->getKey(key);
324
- return string(key, strnlen(key, AES_KEY_LEN));
337
+ return {key, strnlen(key, AES_KEY_LEN)};
325
338
  }
326
339
  return "";
327
340
  }
@@ -895,6 +908,320 @@ bool MMKV::try_lock() {
895
908
  return m_exclusiveProcessLock->try_lock();
896
909
  }
897
910
 
911
+ // backup
912
+
913
+ static bool backupOneToDirectoryByFilePath(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
914
+ File crcFile(srcPath, OpenFlag::ReadOnly);
915
+ if (!crcFile.isFileValid()) {
916
+ return false;
917
+ }
918
+
919
+ bool ret = false;
920
+ {
921
+ #ifdef MMKV_WIN32
922
+ MMKVInfo("backup one mmkv[%s] from [%ws] to [%ws]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
923
+ #else
924
+ MMKVInfo("backup one mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
925
+ #endif
926
+ FileLock fileLock(crcFile.getFd());
927
+ InterProcessLock lock(&fileLock, SharedLockType);
928
+ SCOPED_LOCK(&lock);
929
+
930
+ ret = copyFile(srcPath, dstPath);
931
+ if (ret) {
932
+ auto srcCRCPath = srcPath + CRC_SUFFIX;
933
+ auto dstCRCPath = dstPath + CRC_SUFFIX;
934
+ ret = copyFile(srcCRCPath, dstCRCPath);
935
+ }
936
+ MMKVInfo("finish backup one mmkv[%s]", mmapKey.c_str());
937
+ }
938
+ return ret;
939
+ }
940
+
941
+ bool MMKV::backupOneToDirectory(const string &mmapKey, const MMKVPath_t &dstPath, const MMKVPath_t &srcPath, bool compareFullPath) {
942
+ // we have to lock the creation of MMKV instance, regardless of in cache or not
943
+ SCOPED_LOCK(g_instanceLock);
944
+ MMKV *kv = nullptr;
945
+ if (!compareFullPath) {
946
+ auto itr = g_instanceDic->find(mmapKey);
947
+ if (itr != g_instanceDic->end()) {
948
+ kv = itr->second;
949
+ }
950
+ } else {
951
+ // mmapKey is actually filename, we can't simply call find()
952
+ for (auto &pair : *g_instanceDic) {
953
+ if (pair.second->m_path == srcPath) {
954
+ kv = pair.second;
955
+ break;
956
+ }
957
+ }
958
+ }
959
+ // get one in cache, do it the easy way
960
+ if (kv) {
961
+ #ifdef MMKV_WIN32
962
+ MMKVInfo("backup one cached mmkv[%s] from [%ws] to [%ws]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
963
+ #else
964
+ MMKVInfo("backup one cached mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
965
+ #endif
966
+ SCOPED_LOCK(kv->m_lock);
967
+ SCOPED_LOCK(kv->m_sharedProcessLock);
968
+
969
+ kv->sync();
970
+ auto ret = copyFile(kv->m_path, dstPath);
971
+ if (ret) {
972
+ auto dstCRCPath = dstPath + CRC_SUFFIX;
973
+ ret = copyFile(kv->m_crcPath, dstCRCPath);
974
+ }
975
+ MMKVInfo("finish backup one mmkv[%s], ret: %d", mmapKey.c_str(), ret);
976
+ return ret;
977
+ }
978
+
979
+ // no luck with cache, do it the hard way
980
+ bool ret = backupOneToDirectoryByFilePath(mmapKey, srcPath, dstPath);
981
+ return ret;
982
+ }
983
+
984
+ bool MMKV::backupOneToDirectory(const string &mmapID, const MMKVPath_t &dstDir, const MMKVPath_t *srcDir) {
985
+ auto rootPath = srcDir ? srcDir : &g_rootDir;
986
+ if (*rootPath == dstDir) {
987
+ return true;
988
+ }
989
+ mkPath(dstDir);
990
+ auto encodePath = encodeFilePath(mmapID, dstDir);
991
+ auto dstPath = dstDir + MMKV_PATH_SLASH + encodePath;
992
+ auto mmapKey = mmapedKVKey(mmapID, rootPath);
993
+ #ifdef MMKV_ANDROID
994
+ // historically Android mistakenly use mmapKey as mmapID
995
+ auto srcPath = *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapKey, *rootPath);
996
+ #else
997
+ auto srcPath = *rootPath + MMKV_PATH_SLASH + encodePath;
998
+ #endif
999
+ return backupOneToDirectory(mmapKey, dstPath, srcPath, false);
1000
+ }
1001
+
1002
+ bool endsWith(const MMKVPath_t &str, const MMKVPath_t &suffix) {
1003
+ if (str.length() >= suffix.length()) {
1004
+ return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
1005
+ } else {
1006
+ return false;
1007
+ }
1008
+ }
1009
+
1010
+ MMKVPath_t filename(const MMKVPath_t &path) {
1011
+ auto startPos = path.rfind(MMKV_PATH_SLASH);
1012
+ startPos++; // don't need to check for npos, because npos+1 == 0
1013
+ auto filename = path.substr(startPos);
1014
+ return filename;
1015
+ }
1016
+
1017
+ size_t MMKV::backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t &srcDir, bool isInSpecialDir) {
1018
+ unordered_set<MMKVPath_t> mmapIDSet;
1019
+ unordered_set<MMKVPath_t> mmapIDCRCSet;
1020
+ walkInDir(srcDir, WalkFile, [&](const MMKVPath_t &filePath, WalkType) {
1021
+ if (endsWith(filePath, CRC_SUFFIX)) {
1022
+ mmapIDCRCSet.insert(filePath);
1023
+ } else {
1024
+ mmapIDSet.insert(filePath);
1025
+ }
1026
+ });
1027
+
1028
+ size_t count = 0;
1029
+ if (!mmapIDSet.empty()) {
1030
+ mkPath(dstDir);
1031
+ auto compareFullPath = isInSpecialDir;
1032
+ for (auto &srcPath : mmapIDSet) {
1033
+ auto srcCRCPath = srcPath + CRC_SUFFIX;
1034
+ if (mmapIDCRCSet.find(srcCRCPath) == mmapIDCRCSet.end()) {
1035
+ #ifdef MMKV_WIN32
1036
+ MMKVWarning("crc not exist [%ws]", srcCRCPath.c_str());
1037
+ #else
1038
+ MMKVWarning("crc not exist [%s]", srcCRCPath.c_str());
1039
+ #endif
1040
+ continue;
1041
+ }
1042
+ auto basename = filename(srcPath);
1043
+ const auto &strBasename = MMKVPath_t2String(basename);
1044
+ auto mmapKey = isInSpecialDir ? strBasename : mmapedKVKey(strBasename, &srcDir);
1045
+ auto dstPath = dstDir + MMKV_PATH_SLASH + basename;
1046
+ if (backupOneToDirectory(mmapKey, dstPath, srcPath, compareFullPath)) {
1047
+ count++;
1048
+ }
1049
+ }
1050
+ }
1051
+ return count;
1052
+ }
1053
+
1054
+ size_t MMKV::backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t *srcDir) {
1055
+ auto rootPath = srcDir ? srcDir : &g_rootDir;
1056
+ if (*rootPath == dstDir) {
1057
+ return true;
1058
+ }
1059
+ auto count = backupAllToDirectory(dstDir, *rootPath, false);
1060
+
1061
+ auto specialSrcDir = *rootPath + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
1062
+ if (isFileExist(specialSrcDir)) {
1063
+ auto specialDstDir = dstDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
1064
+ count += backupAllToDirectory(specialDstDir, specialSrcDir, true);
1065
+ }
1066
+ return count;
1067
+ }
1068
+
1069
+ // restore
1070
+
1071
+ static bool restoreOneFromDirectoryByFilePath(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
1072
+ auto dstCRCPath = dstPath + CRC_SUFFIX;
1073
+ File dstCRCFile(move(dstCRCPath), OpenFlag::ReadWrite | OpenFlag::Create);
1074
+ if (!dstCRCFile.isFileValid()) {
1075
+ return false;
1076
+ }
1077
+
1078
+ bool ret = false;
1079
+ {
1080
+ #ifdef MMKV_WIN32
1081
+ MMKVInfo("restore one mmkv[%s] from [%ws] to [%ws]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
1082
+ #else
1083
+ MMKVInfo("restore one mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
1084
+ #endif
1085
+ FileLock fileLock(dstCRCFile.getFd());
1086
+ InterProcessLock lock(&fileLock, ExclusiveLockType);
1087
+ SCOPED_LOCK(&lock);
1088
+
1089
+ ret = copyFileContent(srcPath, dstPath);
1090
+ if (ret) {
1091
+ auto srcCRCPath = srcPath + CRC_SUFFIX;
1092
+ ret = copyFileContent(srcCRCPath, dstCRCFile.getFd());
1093
+ }
1094
+ MMKVInfo("finish restore one mmkv[%s]", mmapKey.c_str());
1095
+ }
1096
+ return ret;
1097
+ }
1098
+
1099
+ // We can't simply replace the existing file, because other processes might have already open it.
1100
+ // They won't know a difference when the file has been replaced.
1101
+ // We have to let them know by overriding the existing file with new content.
1102
+ bool MMKV::restoreOneFromDirectory(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath, bool compareFullPath) {
1103
+ // we have to lock the creation of MMKV instance, regardless of in cache or not
1104
+ SCOPED_LOCK(g_instanceLock);
1105
+ MMKV *kv = nullptr;
1106
+ if (!compareFullPath) {
1107
+ auto itr = g_instanceDic->find(mmapKey);
1108
+ if (itr != g_instanceDic->end()) {
1109
+ kv = itr->second;
1110
+ }
1111
+ } else {
1112
+ // mmapKey is actually filename, we can't simply call find()
1113
+ for (auto &pair : *g_instanceDic) {
1114
+ if (pair.second->m_path == dstPath) {
1115
+ kv = pair.second;
1116
+ break;
1117
+ }
1118
+ }
1119
+ }
1120
+ // get one in cache, do it the easy way
1121
+ if (kv) {
1122
+ #ifdef MMKV_WIN32
1123
+ MMKVInfo("restore one cached mmkv[%s] from [%ws] to [%ws]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
1124
+ #else
1125
+ MMKVInfo("restore one cached mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
1126
+ #endif
1127
+ SCOPED_LOCK(kv->m_lock);
1128
+ SCOPED_LOCK(kv->m_exclusiveProcessLock);
1129
+
1130
+ kv->sync();
1131
+ auto ret = copyFileContent(srcPath, kv->m_file->getFd());
1132
+ if (ret) {
1133
+ auto srcCRCPath = srcPath + CRC_SUFFIX;
1134
+ ret = copyFileContent(srcCRCPath, kv->m_metaFile->getFd());
1135
+ }
1136
+
1137
+ // reload data after restore
1138
+ kv->clearMemoryCache();
1139
+ kv->loadFromFile();
1140
+ if (kv->m_isInterProcess) {
1141
+ kv->notifyContentChanged();
1142
+ }
1143
+
1144
+ MMKVInfo("finish restore one mmkv[%s], ret: %d", mmapKey.c_str(), ret);
1145
+ return ret;
1146
+ }
1147
+
1148
+ // no luck with cache, do it the hard way
1149
+ bool ret = restoreOneFromDirectoryByFilePath(mmapKey, srcPath, dstPath);
1150
+ return ret;
1151
+ }
1152
+
1153
+ bool MMKV::restoreOneFromDirectory(const string &mmapID, const MMKVPath_t &srcDir, const MMKVPath_t *dstDir) {
1154
+ auto rootPath = dstDir ? dstDir : &g_rootDir;
1155
+ if (*rootPath == srcDir) {
1156
+ return true;
1157
+ }
1158
+ mkPath(*rootPath);
1159
+ auto encodePath = encodeFilePath(mmapID, *rootPath);
1160
+ auto srcPath = srcDir + MMKV_PATH_SLASH + encodePath;
1161
+ auto mmapKey = mmapedKVKey(mmapID, rootPath);
1162
+ #ifdef MMKV_ANDROID
1163
+ // historically Android mistakenly use mmapKey as mmapID
1164
+ auto dstPath = *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapKey, *rootPath);
1165
+ #else
1166
+ auto dstPath = *rootPath + MMKV_PATH_SLASH + encodePath;
1167
+ #endif
1168
+ return restoreOneFromDirectory(mmapKey, srcPath, dstPath, false);
1169
+ }
1170
+
1171
+ size_t MMKV::restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t &dstDir, bool isInSpecialDir) {
1172
+ unordered_set<MMKVPath_t> mmapIDSet;
1173
+ unordered_set<MMKVPath_t> mmapIDCRCSet;
1174
+ walkInDir(srcDir, WalkFile, [&](const MMKVPath_t &filePath, WalkType) {
1175
+ if (endsWith(filePath, CRC_SUFFIX)) {
1176
+ mmapIDCRCSet.insert(filePath);
1177
+ } else {
1178
+ mmapIDSet.insert(filePath);
1179
+ }
1180
+ });
1181
+
1182
+ size_t count = 0;
1183
+ if (!mmapIDSet.empty()) {
1184
+ mkPath(dstDir);
1185
+ auto compareFullPath = isInSpecialDir;
1186
+ for (auto &srcPath : mmapIDSet) {
1187
+ auto srcCRCPath = srcPath + CRC_SUFFIX;
1188
+ if (mmapIDCRCSet.find(srcCRCPath) == mmapIDCRCSet.end()) {
1189
+ #ifdef MMKV_WIN32
1190
+ MMKVWarning("crc not exist [%ws]", srcCRCPath.c_str());
1191
+ #else
1192
+ MMKVWarning("crc not exist [%s]", srcCRCPath.c_str());
1193
+ #endif
1194
+ continue;
1195
+ }
1196
+ auto basename = filename(srcPath);
1197
+ const auto &strBasename = MMKVPath_t2String(basename);
1198
+ auto mmapKey = isInSpecialDir ? strBasename : mmapedKVKey(strBasename, &dstDir);
1199
+ auto dstPath = dstDir + MMKV_PATH_SLASH + basename;
1200
+ if (restoreOneFromDirectory(mmapKey, srcPath, dstPath, compareFullPath)) {
1201
+ count++;
1202
+ }
1203
+ }
1204
+ }
1205
+ return count;
1206
+ }
1207
+
1208
+ size_t MMKV::restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t *dstDir) {
1209
+ auto rootPath = dstDir ? dstDir : &g_rootDir;
1210
+ if (*rootPath == srcDir) {
1211
+ return true;
1212
+ }
1213
+ auto count = restoreAllFromDirectory(srcDir, *rootPath, true);
1214
+
1215
+ auto specialSrcDir = srcDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
1216
+ if (isFileExist(specialSrcDir)) {
1217
+ auto specialDstDir = *rootPath + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
1218
+ count += restoreAllFromDirectory(specialSrcDir, specialDstDir, false);
1219
+ }
1220
+ return count;
1221
+ }
1222
+
1223
+ // callbacks
1224
+
898
1225
  void MMKV::registerErrorHandler(ErrorHandler handler) {
899
1226
  SCOPED_LOCK(g_instanceLock);
900
1227
  g_errorHandler = handler;
@@ -934,7 +1261,7 @@ static string md5(const basic_string<T> &value) {
934
1261
  snprintf(tmp, sizeof(tmp), "%2.2x", ch);
935
1262
  strcat(buf, tmp);
936
1263
  }
937
- return string(buf);
1264
+ return {buf};
938
1265
  }
939
1266
 
940
1267
  static MMKVPath_t encodeFilePath(const string &mmapID) {
@@ -957,14 +1284,35 @@ static MMKVPath_t encodeFilePath(const string &mmapID) {
957
1284
  }
958
1285
  }
959
1286
 
960
- string mmapedKVKey(const string &mmapID, MMKVPath_t *rootPath) {
1287
+ static MMKVPath_t encodeFilePath(const string &mmapID, const MMKVPath_t &rootDir) {
1288
+ const char *specialCharacters = "\\/:*?\"<>|";
1289
+ string encodedID;
1290
+ bool hasSpecialCharacter = false;
1291
+ for (auto ch : mmapID) {
1292
+ if (strchr(specialCharacters, ch) != nullptr) {
1293
+ encodedID = md5(mmapID);
1294
+ hasSpecialCharacter = true;
1295
+ break;
1296
+ }
1297
+ }
1298
+ if (hasSpecialCharacter) {
1299
+ MMKVPath_t path = rootDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
1300
+ mkPath(path);
1301
+
1302
+ return MMKVPath_t(SPECIAL_CHARACTER_DIRECTORY_NAME) + MMKV_PATH_SLASH + string2MMKVPath_t(encodedID);
1303
+ } else {
1304
+ return string2MMKVPath_t(mmapID);
1305
+ }
1306
+ }
1307
+
1308
+ string mmapedKVKey(const string &mmapID, const MMKVPath_t *rootPath) {
961
1309
  if (rootPath && g_rootDir != (*rootPath)) {
962
1310
  return md5(*rootPath + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID));
963
1311
  }
964
1312
  return mmapID;
965
1313
  }
966
1314
 
967
- MMKVPath_t mappedKVPathWithID(const string &mmapID, MMKVMode mode, MMKVPath_t *rootPath) {
1315
+ MMKVPath_t mappedKVPathWithID(const string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath) {
968
1316
  #ifndef MMKV_ANDROID
969
1317
  if (rootPath) {
970
1318
  #else
@@ -977,13 +1325,7 @@ MMKVPath_t mappedKVPathWithID(const string &mmapID, MMKVMode mode, MMKVPath_t *r
977
1325
  return g_rootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID);
978
1326
  }
979
1327
 
980
- #ifndef MMKV_WIN32
981
- constexpr auto CRC_SUFFIX = ".crc";
982
- #else
983
- constexpr auto CRC_SUFFIX = L".crc";
984
- #endif
985
-
986
- MMKVPath_t crcPathWithID(const string &mmapID, MMKVMode mode, MMKVPath_t *rootPath) {
1328
+ MMKVPath_t crcPathWithID(const string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath) {
987
1329
  #ifndef MMKV_ANDROID
988
1330
  if (rootPath) {
989
1331
  #else
package/MMKV/Core/MMKV.h CHANGED
@@ -38,11 +38,12 @@ class ThreadLock;
38
38
  MMKV_NAMESPACE_BEGIN
39
39
 
40
40
  enum MMKVMode : uint32_t {
41
- MMKV_SINGLE_PROCESS = 0x1,
42
- MMKV_MULTI_PROCESS = 0x2,
41
+ MMKV_SINGLE_PROCESS = 1 << 0,
42
+ MMKV_MULTI_PROCESS = 1 << 1,
43
43
  #ifdef MMKV_ANDROID
44
- CONTEXT_MODE_MULTI_PROCESS = 0x4, // in case someone mistakenly pass Context.MODE_MULTI_PROCESS
45
- MMKV_ASHMEM = 0x8,
44
+ CONTEXT_MODE_MULTI_PROCESS = 1 << 2, // in case someone mistakenly pass Context.MODE_MULTI_PROCESS
45
+ MMKV_ASHMEM = 1 << 3,
46
+ MMKV_BACKUP = 1 << 4,
46
47
  #endif
47
48
  };
48
49
 
@@ -146,6 +147,10 @@ class MMKV {
146
147
  #if defined(MMKV_ANDROID) && !defined(MMKV_DISABLE_CRYPT)
147
148
  void checkReSetCryptKey(int fd, int metaFD, std::string *cryptKey);
148
149
  #endif
150
+ static bool backupOneToDirectory(const std::string &mmapKey, const MMKVPath_t &dstPath, const MMKVPath_t &srcPath, bool compareFullPath);
151
+ static size_t backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t &srcDir, bool isInSpecialDir);
152
+ static bool restoreOneFromDirectory(const std::string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath, bool compareFullPath);
153
+ static size_t restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t &dstDir, bool isInSpecialDir);
149
154
 
150
155
  public:
151
156
  // call this before getting any MMKV instance
@@ -322,6 +327,26 @@ public:
322
327
  void unlock();
323
328
  bool try_lock();
324
329
 
330
+ static const MMKVPath_t &getRootDir();
331
+
332
+ // backup one MMKV instance from srcDir to dstDir
333
+ // if srcDir is null, then backup from the root dir of MMKV
334
+ static bool backupOneToDirectory(const std::string &mmapID, const MMKVPath_t &dstDir, const MMKVPath_t *srcDir = nullptr);
335
+
336
+ // restore one MMKV instance from srcDir to dstDir
337
+ // if dstDir is null, then restore to the root dir of MMKV
338
+ static bool restoreOneFromDirectory(const std::string &mmapID, const MMKVPath_t &srcDir, const MMKVPath_t *dstDir = nullptr);
339
+
340
+ // backup all MMKV instance from srcDir to dstDir
341
+ // if srcDir is null, then backup from the root dir of MMKV
342
+ // return count of MMKV successfully backuped
343
+ static size_t backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t *srcDir = nullptr);
344
+
345
+ // restore all MMKV instance from srcDir to dstDir
346
+ // if dstDir is null, then restore to the root dir of MMKV
347
+ // return count of MMKV successfully restored
348
+ static size_t restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t *dstDir = nullptr);
349
+
325
350
  // check if content been changed by other process
326
351
  void checkContentChanged();
327
352
 
@@ -30,15 +30,7 @@ MMKVLogLevel g_currentLogLevel = MMKVLogInfo;
30
30
 
31
31
  mmkv::LogHandler g_logHandler;
32
32
 
33
- MMKV_NAMESPACE_END
34
-
35
- #ifdef ENABLE_MMKV_LOG
36
- # include <cstdarg>
37
- # include <string>
38
-
39
- using namespace mmkv;
40
-
41
- # ifndef __FILE_NAME__
33
+ #ifndef __FILE_NAME__
42
34
  const char *_getFileName(const char *path) {
43
35
  const char *ptr = strrchr(path, '/');
44
36
  if (!ptr) {
@@ -50,7 +42,16 @@ const char *_getFileName(const char *path) {
50
42
  return path;
51
43
  }
52
44
  }
53
- # endif
45
+ #endif
46
+
47
+ MMKV_NAMESPACE_END
48
+
49
+
50
+ #ifdef ENABLE_MMKV_LOG
51
+ # include <cstdarg>
52
+ # include <string>
53
+
54
+ using namespace mmkv;
54
55
 
55
56
  # ifndef MMKV_ANDROID
56
57
 
@@ -45,7 +45,7 @@ extern mmkv::LogHandler g_logHandler;
45
45
  # define __MMKV_FILE_NAME__ __FILE_NAME__
46
46
  # else
47
47
  const char *_getFileName(const char *path);
48
- # define __MMKV_FILE_NAME__ _getFileName(__FILE__)
48
+ # define __MMKV_FILE_NAME__ MMKV_NAMESPACE_PREFIX::_getFileName(__FILE__)
49
49
  # endif
50
50
 
51
51
  # define MMKVError(format, ...) \