react-native-mmkv 2.0.0 → 2.1.1
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.
- package/MMKV/CHANGELOG.md +30 -0
- package/MMKV/Core/CMakeLists.txt +2 -1
- package/MMKV/Core/CodedInputDataCrypt.cpp +1 -1
- package/MMKV/Core/Core.xcodeproj/project.pbxproj +7 -4
- package/MMKV/Core/InterProcessLock_Win32.cpp +10 -5
- package/MMKV/Core/MMBuffer.h +1 -0
- package/MMKV/Core/MMKV.cpp +359 -17
- package/MMKV/Core/MMKV.h +29 -4
- package/MMKV/Core/MMKVLog.cpp +11 -10
- package/MMKV/Core/MMKVLog.h +1 -1
- package/MMKV/Core/MMKVPredef.h +6 -4
- package/MMKV/Core/MMKV_Android.cpp +2 -6
- package/MMKV/Core/MMKV_IO.cpp +1 -3
- package/MMKV/Core/MMKV_IO.h +3 -3
- package/MMKV/Core/MemoryFile.cpp +276 -43
- package/MMKV/Core/MemoryFile.h +85 -9
- package/MMKV/Core/MemoryFile_Android.cpp +37 -18
- package/MMKV/Core/MemoryFile_Linux.cpp +120 -0
- package/MMKV/Core/MemoryFile_OSX.cpp +92 -2
- package/MMKV/Core/MemoryFile_Win32.cpp +254 -34
- package/MMKV/Core/aes/openssl/openssl_aes.h +2 -2
- package/MMKV/Core/aes/openssl/openssl_aes_core.cpp +4 -4
- package/MMKV/README.md +4 -4
- package/README.md +17 -4
- package/android/CMakeLists.txt +3 -2
- package/android/src/main/cpp/MmkvHostObject.cpp +36 -0
- package/ios/MmkvHostObject.mm +48 -5
- package/ios/MmkvModule.h +0 -2
- package/ios/MmkvModule.mm +16 -16
- package/package.json +26 -2
- package/react-native-mmkv.podspec +1 -1
- package/lib/commonjs/MMKV.js +0 -127
- package/lib/commonjs/MMKV.js.map +0 -1
- package/lib/commonjs/createMMKV.js +0 -30
- package/lib/commonjs/createMMKV.js.map +0 -1
- package/lib/commonjs/createMMKV.web.js +0 -67
- package/lib/commonjs/createMMKV.web.js.map +0 -1
- package/lib/commonjs/hooks.js +0 -142
- package/lib/commonjs/hooks.js.map +0 -1
- package/lib/commonjs/index.js +0 -32
- package/lib/commonjs/index.js.map +0 -1
- package/lib/module/MMKV.js +0 -116
- package/lib/module/MMKV.js.map +0 -1
- package/lib/module/createMMKV.js +0 -19
- package/lib/module/createMMKV.js.map +0 -1
- package/lib/module/createMMKV.web.js +0 -57
- package/lib/module/createMMKV.web.js.map +0 -1
- package/lib/module/hooks.js +0 -127
- package/lib/module/hooks.js.map +0 -1
- package/lib/module/index.js +0 -3
- package/lib/module/index.js.map +0 -1
- package/lib/typescript/MMKV.d.ts +0 -114
- package/lib/typescript/createMMKV.d.ts +0 -5
- package/lib/typescript/createMMKV.web.d.ts +0 -2
- package/lib/typescript/hooks.d.ts +0 -49
- package/lib/typescript/index.d.ts +0 -2
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
# include "ScopedLock.hpp"
|
|
29
29
|
# include "ThreadLock.h"
|
|
30
30
|
# include <cassert>
|
|
31
|
+
# include <strsafe.h>
|
|
31
32
|
|
|
32
33
|
using namespace std;
|
|
33
34
|
|
|
@@ -36,13 +37,73 @@ namespace mmkv {
|
|
|
36
37
|
static bool getFileSize(MMKVFileHandle_t fd, size_t &size);
|
|
37
38
|
static bool ftruncate(MMKVFileHandle_t file, size_t size);
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
File::File(MMKVPath_t path, OpenFlag flag) : m_path(std::move(path)), m_fd(INVALID_HANDLE_VALUE), m_flag(flag) {
|
|
41
|
+
open();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static pair<int, int> OpenFlag2NativeFlag(OpenFlag flag) {
|
|
45
|
+
int access = 0, create = OPEN_EXISTING;
|
|
46
|
+
if (flag & OpenFlag::ReadWrite) {
|
|
47
|
+
access = (GENERIC_READ | GENERIC_WRITE);
|
|
48
|
+
} else if (flag & OpenFlag::ReadOnly) {
|
|
49
|
+
access |= GENERIC_READ;
|
|
50
|
+
} else if (flag & OpenFlag::WriteOnly) {
|
|
51
|
+
access |= GENERIC_WRITE;
|
|
52
|
+
}
|
|
53
|
+
if (flag & OpenFlag::Create) {
|
|
54
|
+
create = OPEN_ALWAYS;
|
|
55
|
+
}
|
|
56
|
+
if (flag & OpenFlag::Excel) {
|
|
57
|
+
access = CREATE_NEW;
|
|
58
|
+
}
|
|
59
|
+
if (flag & OpenFlag::Truncate) {
|
|
60
|
+
access = CREATE_ALWAYS;
|
|
61
|
+
}
|
|
62
|
+
return {access, create};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
bool File::open() {
|
|
66
|
+
if (isFileValid()) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
auto pair = OpenFlag2NativeFlag(m_flag);
|
|
70
|
+
m_fd = CreateFile(m_path.c_str(), pair.first, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
|
71
|
+
pair.second, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
72
|
+
if (!isFileValid()) {
|
|
73
|
+
MMKVError("fail to open:[%ws], %d", m_path.c_str(), GetLastError());
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
MMKVInfo("open fd[%p], %ws", m_fd, m_path.c_str());
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
void File::close() {
|
|
81
|
+
if (isFileValid()) {
|
|
82
|
+
MMKVInfo("closing fd[%p], %ws", m_fd, m_path.c_str());
|
|
83
|
+
if (CloseHandle(m_fd)) {
|
|
84
|
+
m_fd = INVALID_HANDLE_VALUE;
|
|
85
|
+
} else {
|
|
86
|
+
MMKVError("fail to close [%ws], %d", m_path.c_str(), GetLastError());
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
size_t File::getActualFileSize() const {
|
|
92
|
+
size_t size = 0;
|
|
93
|
+
mmkv::getFileSize(m_fd, size);
|
|
94
|
+
return size;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
MemoryFile::MemoryFile(MMKVPath_t path)
|
|
98
|
+
: m_diskFile(std::move(path), OpenFlag::ReadWrite | OpenFlag::Create)
|
|
99
|
+
, m_fileMapping(nullptr)
|
|
100
|
+
, m_ptr(nullptr)
|
|
101
|
+
, m_size(0) {
|
|
41
102
|
reloadFromFile();
|
|
42
103
|
}
|
|
43
104
|
|
|
44
105
|
bool MemoryFile::truncate(size_t size) {
|
|
45
|
-
if (
|
|
106
|
+
if (!m_diskFile.isFileValid()) {
|
|
46
107
|
return false;
|
|
47
108
|
}
|
|
48
109
|
if (size == m_size) {
|
|
@@ -56,14 +117,14 @@ bool MemoryFile::truncate(size_t size) {
|
|
|
56
117
|
m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
|
|
57
118
|
}
|
|
58
119
|
|
|
59
|
-
if (!ftruncate(
|
|
60
|
-
MMKVError("fail to truncate [%ws] to size %zu",
|
|
120
|
+
if (!ftruncate(m_diskFile.getFd(), m_size)) {
|
|
121
|
+
MMKVError("fail to truncate [%ws] to size %zu", m_diskFile.m_path.c_str(), m_size);
|
|
61
122
|
m_size = oldSize;
|
|
62
123
|
return false;
|
|
63
124
|
}
|
|
64
125
|
if (m_size > oldSize) {
|
|
65
|
-
if (!zeroFillFile(
|
|
66
|
-
MMKVError("fail to zeroFile [%ws] to size %zu",
|
|
126
|
+
if (!zeroFillFile(m_diskFile.getFd(), oldSize, m_size - oldSize)) {
|
|
127
|
+
MMKVError("fail to zeroFile [%ws] to size %zu", m_diskFile.m_path.c_str(), m_size);
|
|
67
128
|
m_size = oldSize;
|
|
68
129
|
return false;
|
|
69
130
|
}
|
|
@@ -71,7 +132,7 @@ bool MemoryFile::truncate(size_t size) {
|
|
|
71
132
|
|
|
72
133
|
if (m_ptr) {
|
|
73
134
|
if (!UnmapViewOfFile(m_ptr)) {
|
|
74
|
-
MMKVError("fail to munmap [%ws], %d",
|
|
135
|
+
MMKVError("fail to munmap [%ws], %d", m_diskFile.m_path.c_str(), GetLastError());
|
|
75
136
|
}
|
|
76
137
|
m_ptr = nullptr;
|
|
77
138
|
}
|
|
@@ -90,28 +151,28 @@ bool MemoryFile::msync(SyncFlag syncFlag) {
|
|
|
90
151
|
if (m_ptr) {
|
|
91
152
|
if (FlushViewOfFile(m_ptr, m_size)) {
|
|
92
153
|
if (syncFlag == MMKV_SYNC) {
|
|
93
|
-
if (!FlushFileBuffers(
|
|
94
|
-
MMKVError("fail to FlushFileBuffers [%ws]:%d",
|
|
154
|
+
if (!FlushFileBuffers(m_diskFile.getFd())) {
|
|
155
|
+
MMKVError("fail to FlushFileBuffers [%ws]:%d", m_diskFile.m_path.c_str(), GetLastError());
|
|
95
156
|
return false;
|
|
96
157
|
}
|
|
97
158
|
}
|
|
98
159
|
return true;
|
|
99
160
|
}
|
|
100
|
-
MMKVError("fail to FlushViewOfFile [%ws]:%d",
|
|
161
|
+
MMKVError("fail to FlushViewOfFile [%ws]:%d", m_diskFile.m_path.c_str(), GetLastError());
|
|
101
162
|
return false;
|
|
102
163
|
}
|
|
103
164
|
return false;
|
|
104
165
|
}
|
|
105
166
|
|
|
106
167
|
bool MemoryFile::mmap() {
|
|
107
|
-
m_fileMapping = CreateFileMapping(
|
|
168
|
+
m_fileMapping = CreateFileMapping(m_diskFile.getFd(), nullptr, PAGE_READWRITE, 0, 0, nullptr);
|
|
108
169
|
if (!m_fileMapping) {
|
|
109
|
-
MMKVError("fail to CreateFileMapping [%ws], %d",
|
|
170
|
+
MMKVError("fail to CreateFileMapping [%ws], %d", m_diskFile.m_path.c_str(), GetLastError());
|
|
110
171
|
return false;
|
|
111
172
|
} else {
|
|
112
173
|
m_ptr = (char *) MapViewOfFile(m_fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
|
113
174
|
if (!m_ptr) {
|
|
114
|
-
MMKVError("fail to mmap [%ws], %d",
|
|
175
|
+
MMKVError("fail to mmap [%ws], %d", m_diskFile.m_path.c_str(), GetLastError());
|
|
115
176
|
return false;
|
|
116
177
|
}
|
|
117
178
|
}
|
|
@@ -121,22 +182,17 @@ bool MemoryFile::mmap() {
|
|
|
121
182
|
|
|
122
183
|
void MemoryFile::reloadFromFile() {
|
|
123
184
|
if (isFileValid()) {
|
|
124
|
-
MMKVWarning("calling reloadFromFile while the cache [%ws] is still valid",
|
|
185
|
+
MMKVWarning("calling reloadFromFile while the cache [%ws] is still valid", m_diskFile.m_path.c_str());
|
|
125
186
|
assert(0);
|
|
126
187
|
clearMemoryCache();
|
|
127
188
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
132
|
-
if (m_fd == INVALID_HANDLE_VALUE) {
|
|
133
|
-
MMKVError("fail to open:%ws, %d", m_name.c_str(), GetLastError());
|
|
134
|
-
} else {
|
|
135
|
-
FileLock fileLock(m_fd);
|
|
189
|
+
m_diskFile.open();
|
|
190
|
+
if (m_diskFile.isFileValid()) {
|
|
191
|
+
FileLock fileLock(m_diskFile.getFd());
|
|
136
192
|
InterProcessLock lock(&fileLock, ExclusiveLockType);
|
|
137
193
|
SCOPED_LOCK(&lock);
|
|
138
194
|
|
|
139
|
-
mmkv::getFileSize(
|
|
195
|
+
mmkv::getFileSize(m_diskFile.getFd(), m_size);
|
|
140
196
|
// round up to (n * pagesize)
|
|
141
197
|
if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
|
|
142
198
|
size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
|
|
@@ -159,16 +215,7 @@ void MemoryFile::doCleanMemoryCache(bool forceClean) {
|
|
|
159
215
|
CloseHandle(m_fileMapping);
|
|
160
216
|
m_fileMapping = nullptr;
|
|
161
217
|
}
|
|
162
|
-
|
|
163
|
-
CloseHandle(m_fd);
|
|
164
|
-
m_fd = INVALID_HANDLE_VALUE;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
size_t MemoryFile::getActualFileSize() {
|
|
169
|
-
size_t size = 0;
|
|
170
|
-
mmkv::getFileSize(m_fd, size);
|
|
171
|
-
return size;
|
|
218
|
+
m_diskFile.close();
|
|
172
219
|
}
|
|
173
220
|
|
|
174
221
|
size_t getPageSize() {
|
|
@@ -302,6 +349,170 @@ static bool getFileSize(MMKVFileHandle_t fd, size_t &size) {
|
|
|
302
349
|
return false;
|
|
303
350
|
}
|
|
304
351
|
|
|
352
|
+
static pair<MMKVPath_t, MMKVFileHandle_t> createUniqueTempFile(const wchar_t *prefix) {
|
|
353
|
+
wchar_t lpTempPathBuffer[MAX_PATH];
|
|
354
|
+
// Gets the temp path env string (no guarantee it's a valid path).
|
|
355
|
+
auto dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer);
|
|
356
|
+
if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
|
|
357
|
+
MMKVError("GetTempPath failed %d", GetLastError());
|
|
358
|
+
return {L"", INVALID_HANDLE_VALUE};
|
|
359
|
+
}
|
|
360
|
+
// Generates a temporary file name.
|
|
361
|
+
wchar_t szTempFileName[MAX_PATH];
|
|
362
|
+
if (!GetTempFileName(lpTempPathBuffer, prefix, 0, szTempFileName)) {
|
|
363
|
+
MMKVError("GetTempFileName failed %d", GetLastError());
|
|
364
|
+
return {L"", INVALID_HANDLE_VALUE};
|
|
365
|
+
}
|
|
366
|
+
auto hTempFile = CreateFile(szTempFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
367
|
+
if (hTempFile == INVALID_HANDLE_VALUE) {
|
|
368
|
+
MMKVError("fail to create unique temp file [%ws], %d", szTempFileName, GetLastError());
|
|
369
|
+
return {L"", INVALID_HANDLE_VALUE};
|
|
370
|
+
}
|
|
371
|
+
MMKVDebug("create unique temp file [%ws] with fd[%p]", szTempFileName, hTempFile);
|
|
372
|
+
return {MMKVPath_t(szTempFileName), hTempFile};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
bool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
|
|
376
|
+
if (MoveFileEx(srcPath.c_str(), dstPath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) == 0) {
|
|
377
|
+
MMKVError("MoveFileEx [%ws] to [%ws] failed %d", srcPath.c_str(), dstPath.c_str(), GetLastError());
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate) {
|
|
384
|
+
if (dstFD == INVALID_HANDLE_VALUE) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
bool ret = false;
|
|
388
|
+
File srcFile(srcPath, OpenFlag::ReadOnly);
|
|
389
|
+
if (!srcFile.isFileValid()) {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
auto bufferSize = getPageSize();
|
|
393
|
+
auto buffer = (char *) malloc(bufferSize);
|
|
394
|
+
if (!buffer) {
|
|
395
|
+
MMKVError("fail to malloc size %zu, %d(%s)", bufferSize, errno, strerror(errno));
|
|
396
|
+
goto errorOut;
|
|
397
|
+
}
|
|
398
|
+
SetFilePointer(dstFD, 0, 0, FILE_BEGIN);
|
|
399
|
+
|
|
400
|
+
// the Win32 platform don't have sendfile()/fcopyfile() equivalent, do it the hard way
|
|
401
|
+
while (true) {
|
|
402
|
+
DWORD sizeRead = 0;
|
|
403
|
+
if (!ReadFile(srcFile.getFd(), buffer, bufferSize, &sizeRead, nullptr)) {
|
|
404
|
+
MMKVError("fail to read %ws: %d", srcPath.c_str(), GetLastError());
|
|
405
|
+
goto errorOut;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
DWORD sizeWrite = 0;
|
|
409
|
+
if (!WriteFile(dstFD, buffer, sizeRead, &sizeWrite, nullptr)) {
|
|
410
|
+
MMKVError("fail to write fd [%d], %d", dstFD, GetLastError());
|
|
411
|
+
goto errorOut;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (sizeRead < bufferSize) {
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (needTruncate) {
|
|
419
|
+
size_t dstFileSize = 0;
|
|
420
|
+
getFileSize(dstFD, dstFileSize);
|
|
421
|
+
auto srcFileSize = srcFile.getActualFileSize();
|
|
422
|
+
if ((dstFileSize != srcFileSize) && !ftruncate(dstFD, static_cast<off_t>(srcFileSize))) {
|
|
423
|
+
MMKVError("fail to truncate [%d] to size [%zu]", dstFD, srcFileSize);
|
|
424
|
+
goto errorOut;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
ret = true;
|
|
429
|
+
MMKVInfo("copy content from %ws to fd[%d] finish", srcPath.c_str(), dstFD);
|
|
430
|
+
|
|
431
|
+
errorOut:
|
|
432
|
+
free(buffer);
|
|
433
|
+
return ret;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// copy to a temp file then rename it
|
|
437
|
+
// this is the best we can do on Win32
|
|
438
|
+
bool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
|
|
439
|
+
auto pair = createUniqueTempFile(L"MMKV");
|
|
440
|
+
auto tmpFD = pair.second;
|
|
441
|
+
auto &tmpPath = pair.first;
|
|
442
|
+
if (tmpFD == INVALID_HANDLE_VALUE) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
bool renamed = false;
|
|
447
|
+
if (copyFileContent(srcPath, tmpFD, false)) {
|
|
448
|
+
MMKVInfo("copyed file [%ws] to [%ws]", srcPath.c_str(), tmpPath.c_str());
|
|
449
|
+
CloseHandle(tmpFD);
|
|
450
|
+
renamed = tryAtomicRename(tmpPath.c_str(), dstPath.c_str());
|
|
451
|
+
if (renamed) {
|
|
452
|
+
MMKVInfo("copyfile [%ws] to [%ws] finish.", srcPath.c_str(), dstPath.c_str());
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
CloseHandle(tmpFD);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (!renamed) {
|
|
459
|
+
DeleteFile(tmpPath.c_str());
|
|
460
|
+
}
|
|
461
|
+
return renamed;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
bool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
|
|
465
|
+
File dstFile(dstPath, OpenFlag::WriteOnly | OpenFlag::Create | OpenFlag::Truncate);
|
|
466
|
+
if (!dstFile.isFileValid()) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
auto ret = copyFileContent(srcPath, dstFile.getFd(), false);
|
|
470
|
+
if (!ret) {
|
|
471
|
+
MMKVError("fail to copyfile(): target file %ws", dstPath.c_str());
|
|
472
|
+
} else {
|
|
473
|
+
MMKVInfo("copy content from %ws to [%ws] finish", srcPath.c_str(), dstPath.c_str());
|
|
474
|
+
}
|
|
475
|
+
return ret;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD) {
|
|
479
|
+
return copyFileContent(srcPath, dstFD, true);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
void walkInDir(const MMKVPath_t &dirPath,
|
|
483
|
+
WalkType type,
|
|
484
|
+
const std::function<void(const MMKVPath_t &, WalkType)> &walker) {
|
|
485
|
+
wchar_t szDir[MAX_PATH];
|
|
486
|
+
StringCchCopy(szDir, MAX_PATH, dirPath.c_str());
|
|
487
|
+
StringCchCat(szDir, MAX_PATH, L"\\*");
|
|
488
|
+
|
|
489
|
+
WIN32_FIND_DATA ffd;
|
|
490
|
+
auto hFind = FindFirstFile(szDir, &ffd);
|
|
491
|
+
if (hFind == INVALID_HANDLE_VALUE) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
do {
|
|
496
|
+
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
497
|
+
if (type & WalkFolder) {
|
|
498
|
+
if (wcscmp(ffd.cFileName, L".") == 0 || wcscmp(ffd.cFileName, L"..") == 0) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
walker(dirPath + L"\\" + ffd.cFileName, WalkFolder);
|
|
502
|
+
}
|
|
503
|
+
} else if (type & WalkFile) {
|
|
504
|
+
walker(dirPath + L"\\" + ffd.cFileName, WalkFile);
|
|
505
|
+
}
|
|
506
|
+
} while (FindNextFile(hFind, &ffd) != 0);
|
|
507
|
+
|
|
508
|
+
auto dwError = GetLastError();
|
|
509
|
+
if (dwError != ERROR_NO_MORE_FILES) {
|
|
510
|
+
MMKVError("WalkInDir fail %d", dwError);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
FindClose(hFind);
|
|
514
|
+
}
|
|
515
|
+
|
|
305
516
|
} // namespace mmkv
|
|
306
517
|
|
|
307
518
|
std::wstring string2MMKVPath_t(const std::string &str) {
|
|
@@ -313,4 +524,13 @@ std::wstring string2MMKVPath_t(const std::string &str) {
|
|
|
313
524
|
return result;
|
|
314
525
|
}
|
|
315
526
|
|
|
527
|
+
std::string MMKVPath_t2String(const MMKVPath_t &str) {
|
|
528
|
+
auto length = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, nullptr, 0, 0, 0);
|
|
529
|
+
auto buffer = new char[length];
|
|
530
|
+
WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, buffer, length, 0, 0);
|
|
531
|
+
string result(buffer);
|
|
532
|
+
delete[] buffer;
|
|
533
|
+
return result;
|
|
534
|
+
}
|
|
535
|
+
|
|
316
536
|
#endif // MMKV_WIN32
|
|
@@ -89,13 +89,13 @@ int AES_C_set_decrypt_key(const uint8_t *userKey, const int bits, void *key);
|
|
|
89
89
|
void AES_C_encrypt(const uint8_t *in, uint8_t *out, const void *key);
|
|
90
90
|
void AES_C_decrypt(const uint8_t *in, uint8_t *out, const void *key);
|
|
91
91
|
|
|
92
|
-
} // namespace openssl
|
|
93
|
-
|
|
94
92
|
extern aes_set_encrypt_t AES_set_encrypt_key;
|
|
95
93
|
extern aes_set_decrypt_t AES_set_decrypt_key;
|
|
96
94
|
extern aes_encrypt_t AES_encrypt;
|
|
97
95
|
extern aes_decrypt_t AES_decrypt;
|
|
98
96
|
|
|
97
|
+
} // namespace openssl
|
|
98
|
+
|
|
99
99
|
#endif // __ARM_MAX_ARCH__ <= 7
|
|
100
100
|
|
|
101
101
|
#endif // __linux__
|
|
@@ -43,6 +43,8 @@
|
|
|
43
43
|
|
|
44
44
|
#ifndef MMKV_DISABLE_CRYPT
|
|
45
45
|
|
|
46
|
+
namespace openssl {
|
|
47
|
+
|
|
46
48
|
#if (__ARM_MAX_ARCH__ > 7) && defined(__linux__)
|
|
47
49
|
|
|
48
50
|
aes_set_encrypt_t AES_set_encrypt_key = openssl::AES_C_set_encrypt_key;
|
|
@@ -54,8 +56,6 @@ aes_encrypt_t AES_decrypt = openssl::AES_C_decrypt;
|
|
|
54
56
|
|
|
55
57
|
#if (__ARM_MAX_ARCH__ <= 0) || (__ARM_MAX_ARCH__ > 7 && defined(__linux__))
|
|
56
58
|
|
|
57
|
-
namespace openssl {
|
|
58
|
-
|
|
59
59
|
/*-
|
|
60
60
|
Te0[x] = S [x].[02, 01, 01, 03];
|
|
61
61
|
Te1[x] = S [x].[03, 02, 01, 01];
|
|
@@ -1037,8 +1037,8 @@ void AES_C_decrypt(const uint8_t *in, uint8_t *out, const void *k) {
|
|
|
1037
1037
|
PUTU32(out + 12, s3);
|
|
1038
1038
|
}
|
|
1039
1039
|
|
|
1040
|
-
} // namespace openssl
|
|
1041
|
-
|
|
1042
1040
|
#endif // (__ARM_MAX_ARCH__ < 0) || (__ARM_MAX_ARCH__ > 7 && defined(__linux__))
|
|
1043
1041
|
|
|
1042
|
+
} // namespace openssl
|
|
1043
|
+
|
|
1044
1044
|
#endif // MMKV_DISABLE_CRYPT
|
package/MMKV/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT)
|
|
2
2
|
[](https://github.com/Tencent/MMKV/pulls)
|
|
3
|
-
[](https://github.com/Tencent/MMKV/releases)
|
|
4
4
|
[](https://github.com/Tencent/MMKV/wiki/home)
|
|
5
5
|
|
|
6
6
|
中文版本请参看[这里](./README_CN.md)
|
|
@@ -28,12 +28,12 @@ Add the following lines to `build.gradle` on your app module:
|
|
|
28
28
|
|
|
29
29
|
```gradle
|
|
30
30
|
dependencies {
|
|
31
|
-
implementation 'com.tencent:mmkv
|
|
32
|
-
// replace "1.2.
|
|
31
|
+
implementation 'com.tencent:mmkv:1.2.12'
|
|
32
|
+
// replace "1.2.12" with any available version
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Starting from v1.2.8, MMKV has been **migrated to Maven Central**.
|
|
36
|
+
Starting from v1.2.8, MMKV has been **migrated to Maven Central**.
|
|
37
37
|
For other installation options, see [Android Setup](https://github.com/Tencent/MMKV/wiki/android_setup).
|
|
38
38
|
|
|
39
39
|
### Quick Tutorial
|
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* **~30x faster than AsyncStorage**
|
|
34
34
|
* Uses [**JSI**](https://github.com/react-native-community/discussions-and-proposals/issues/91) instead of the "old" Bridge
|
|
35
35
|
* **iOS**, **Android** and **Web** support
|
|
36
|
+
* Easy to use **React Hooks** API
|
|
36
37
|
|
|
37
38
|
## Sponsors
|
|
38
39
|
|
|
@@ -82,7 +83,7 @@ To create a new instance of the MMKV storage, use the `MMKV` constructor. It is
|
|
|
82
83
|
```js
|
|
83
84
|
import { MMKV } from 'react-native-mmkv'
|
|
84
85
|
|
|
85
|
-
const storage = new MMKV()
|
|
86
|
+
export const storage = new MMKV()
|
|
86
87
|
```
|
|
87
88
|
|
|
88
89
|
This creates a new storage instance using the default MMKV storage ID (`mmkv.default`).
|
|
@@ -92,10 +93,10 @@ This creates a new storage instance using the default MMKV storage ID (`mmkv.def
|
|
|
92
93
|
```js
|
|
93
94
|
import { MMKV } from 'react-native-mmkv'
|
|
94
95
|
|
|
95
|
-
const storage = new MMKV({
|
|
96
|
+
export const storage = new MMKV({
|
|
96
97
|
id: `user-${userId}-storage`,
|
|
97
98
|
path: `${USER_DIRECTORY}/storage`,
|
|
98
|
-
encryptionKey: '
|
|
99
|
+
encryptionKey: 'hunter2'
|
|
99
100
|
})
|
|
100
101
|
```
|
|
101
102
|
|
|
@@ -155,14 +156,26 @@ const jsonUser = storage.getString('user') // { 'username': 'Marc', 'age': 21 }
|
|
|
155
156
|
const userObject = JSON.parse(jsonUser)
|
|
156
157
|
```
|
|
157
158
|
|
|
159
|
+
### Encryption
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
// encrypt all data with a private key
|
|
163
|
+
storage.recrypt('hunter2')
|
|
164
|
+
|
|
165
|
+
// remove encryption
|
|
166
|
+
storage.recrypt(undefined)
|
|
167
|
+
```
|
|
168
|
+
|
|
158
169
|
## Documentation
|
|
159
170
|
|
|
160
171
|
* [Hooks](./docs/HOOKS.md)
|
|
161
172
|
* [Value-change Listeners](./docs/LISTENERS.md)
|
|
162
173
|
* [Migrate from AsyncStorage](./docs/MIGRATE_FROM_ASYNC_STORAGE.md)
|
|
163
|
-
* [Using MMKV with redux-
|
|
174
|
+
* [Using MMKV with redux-persist](./docs/WRAPPER_REDUX.md)
|
|
164
175
|
* [Using MMKV with mobx-persist-storage](./docs/WRAPPER_MOBX.md)
|
|
165
176
|
* [Using MMKV with mobx-persist](./docs/WRAPPER_MOBXPERSIST.md)
|
|
177
|
+
* [Using MMKV with zustand persist-middleware](./docs/WRAPPER_ZUSTAND_PERSIST_MIDDLEWARE.md)
|
|
178
|
+
* [How is this library different from **react-native-mmkv-storage**?](https://github.com/mrousavy/react-native-mmkv/issues/100#issuecomment-886477361)
|
|
166
179
|
|
|
167
180
|
## Limitations
|
|
168
181
|
|
package/android/CMakeLists.txt
CHANGED
|
@@ -16,7 +16,18 @@ MmkvHostObject::MmkvHostObject(const std::string& instanceId, std::string path,
|
|
|
16
16
|
std::string* pathPtr = path.size() > 0 ? &path : nullptr;
|
|
17
17
|
std::string* cryptKeyPtr = cryptKey.size() > 0 ? &cryptKey : nullptr;
|
|
18
18
|
instance = MMKV::mmkvWithID(instanceId, mmkv::DEFAULT_MMAP_SIZE, MMKV_SINGLE_PROCESS, cryptKeyPtr, pathPtr);
|
|
19
|
+
|
|
19
20
|
if (instance == nullptr) {
|
|
21
|
+
// Check if instanceId is invalid
|
|
22
|
+
if (instanceId.empty()) {
|
|
23
|
+
throw std::runtime_error("Failed to create MMKV instance! `id` cannot be empty!");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if encryptionKey is invalid
|
|
27
|
+
if (cryptKey.size() > 16) {
|
|
28
|
+
throw std::runtime_error("Failed to create MMKV instance! `encryptionKey` cannot be longer than 16 bytes!");
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
throw std::runtime_error("Failed to create MMKV instance!");
|
|
21
32
|
}
|
|
22
33
|
}
|
|
@@ -31,6 +42,7 @@ std::vector<jsi::PropNameID> MmkvHostObject::getPropertyNames(jsi::Runtime& rt)
|
|
|
31
42
|
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("delete")));
|
|
32
43
|
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getAllKeys")));
|
|
33
44
|
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("deleteAll")));
|
|
45
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("recrypt")));
|
|
34
46
|
return result;
|
|
35
47
|
}
|
|
36
48
|
|
|
@@ -181,9 +193,33 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
|
|
|
181
193
|
const jsi::Value* arguments,
|
|
182
194
|
size_t count) -> jsi::Value {
|
|
183
195
|
instance->clearAll();
|
|
196
|
+
|
|
184
197
|
return jsi::Value::undefined();
|
|
185
198
|
});
|
|
186
199
|
}
|
|
187
200
|
|
|
201
|
+
if (propName == "recrypt") {
|
|
202
|
+
// MMKV.recrypt(key)
|
|
203
|
+
return jsi::Function::createFromHostFunction(runtime,
|
|
204
|
+
jsi::PropNameID::forAscii(runtime, funcName),
|
|
205
|
+
0,
|
|
206
|
+
[this](jsi::Runtime& runtime,
|
|
207
|
+
const jsi::Value& thisValue,
|
|
208
|
+
const jsi::Value* arguments,
|
|
209
|
+
size_t count) -> jsi::Value {
|
|
210
|
+
if (arguments[0].isUndefined()) {
|
|
211
|
+
// reset encryption key to "no encryption"
|
|
212
|
+
instance->reKey(nullptr);
|
|
213
|
+
} else if (arguments[0].isString()) {
|
|
214
|
+
// reKey(..) with new encryption-key
|
|
215
|
+
auto encryptionKey = arguments[0].getString(runtime).utf8(runtime);
|
|
216
|
+
instance->reKey(encryptionKey);
|
|
217
|
+
} else {
|
|
218
|
+
throw jsi::JSError(runtime, "First argument ('encryptionKey') has to be of type string (or undefined)!");
|
|
219
|
+
}
|
|
220
|
+
return jsi::Value::undefined();
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
188
224
|
return jsi::Value::undefined();
|
|
189
225
|
}
|
package/ios/MmkvHostObject.mm
CHANGED
|
@@ -13,7 +13,25 @@
|
|
|
13
13
|
MmkvHostObject::MmkvHostObject(NSString* instanceId, NSString* path, NSString* cryptKey) {
|
|
14
14
|
NSData* cryptData = cryptKey == nil ? nil : [cryptKey dataUsingEncoding:NSUTF8StringEncoding];
|
|
15
15
|
instance = [MMKV mmkvWithID:instanceId cryptKey:cryptData rootPath:path];
|
|
16
|
+
|
|
16
17
|
if (instance == nil) {
|
|
18
|
+
// Check if instanceId is invalid
|
|
19
|
+
if ([instanceId length] == 0) {
|
|
20
|
+
throw std::runtime_error("Failed to create MMKV instance! `id` cannot be empty!");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check if encryptionKey is invalid
|
|
24
|
+
if (cryptData != nil && [cryptData length] > 16) {
|
|
25
|
+
throw std::runtime_error("Failed to create MMKV instance! `encryptionKey` cannot be longer than 16 bytes!");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check if path is invalid
|
|
29
|
+
NSFileManager* fileManager = [[NSFileManager alloc] init];
|
|
30
|
+
bool pathExists = [fileManager fileExistsAtPath:path isDirectory:nil];
|
|
31
|
+
if (!pathExists) {
|
|
32
|
+
throw std::runtime_error("Failed to create MMKV instance! The given Storage Path does not exist!");
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
throw std::runtime_error("Failed to create MMKV instance!");
|
|
18
36
|
}
|
|
19
37
|
}
|
|
@@ -28,6 +46,7 @@ std::vector<jsi::PropNameID> MmkvHostObject::getPropertyNames(jsi::Runtime& rt)
|
|
|
28
46
|
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("delete")));
|
|
29
47
|
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getAllKeys")));
|
|
30
48
|
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("deleteAll")));
|
|
49
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("recrypt")));
|
|
31
50
|
return result;
|
|
32
51
|
}
|
|
33
52
|
|
|
@@ -73,7 +92,7 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
|
|
|
73
92
|
if (!arguments[0].isString()) throw jsi::JSError(runtime, "First argument ('key') has to be of type string!");
|
|
74
93
|
|
|
75
94
|
auto keyName = convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
|
|
76
|
-
|
|
95
|
+
bool value = [instance getBoolForKey:keyName];
|
|
77
96
|
return jsi::Value(value);
|
|
78
97
|
});
|
|
79
98
|
}
|
|
@@ -114,7 +133,7 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
|
|
|
114
133
|
return jsi::Value(value);
|
|
115
134
|
});
|
|
116
135
|
}
|
|
117
|
-
|
|
136
|
+
|
|
118
137
|
if (propName == "contains") {
|
|
119
138
|
// MMKV.contains(key: string)
|
|
120
139
|
return jsi::Function::createFromHostFunction(runtime,
|
|
@@ -125,13 +144,13 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
|
|
|
125
144
|
const jsi::Value* arguments,
|
|
126
145
|
size_t count) -> jsi::Value {
|
|
127
146
|
if (!arguments[0].isString()) throw jsi::JSError(runtime, "First argument ('key') has to be of type string!");
|
|
128
|
-
|
|
147
|
+
|
|
129
148
|
auto keyName = convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
|
|
130
149
|
bool containsKey = [instance containsKey:keyName];
|
|
131
150
|
return jsi::Value(containsKey);
|
|
132
151
|
});
|
|
133
152
|
}
|
|
134
|
-
|
|
153
|
+
|
|
135
154
|
if (propName == "delete") {
|
|
136
155
|
// MMKV.delete(key: string)
|
|
137
156
|
return jsi::Function::createFromHostFunction(runtime,
|
|
@@ -142,7 +161,7 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
|
|
|
142
161
|
const jsi::Value* arguments,
|
|
143
162
|
size_t count) -> jsi::Value {
|
|
144
163
|
if (!arguments[0].isString()) throw jsi::JSError(runtime, "First argument ('key') has to be of type string!");
|
|
145
|
-
|
|
164
|
+
|
|
146
165
|
auto keyName = convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
|
|
147
166
|
[instance removeValueForKey:keyName];
|
|
148
167
|
return jsi::Value::undefined();
|
|
@@ -177,5 +196,29 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
|
|
|
177
196
|
});
|
|
178
197
|
}
|
|
179
198
|
|
|
199
|
+
if (propName == "recrypt") {
|
|
200
|
+
// MMKV.recrypt(key)
|
|
201
|
+
return jsi::Function::createFromHostFunction(runtime,
|
|
202
|
+
jsi::PropNameID::forAscii(runtime, funcName),
|
|
203
|
+
0,
|
|
204
|
+
[this](jsi::Runtime& runtime,
|
|
205
|
+
const jsi::Value& thisValue,
|
|
206
|
+
const jsi::Value* arguments,
|
|
207
|
+
size_t count) -> jsi::Value {
|
|
208
|
+
if (arguments[0].isUndefined()) {
|
|
209
|
+
// reset encryption key to "no encryption"
|
|
210
|
+
[instance reKey:nil];
|
|
211
|
+
} else if (arguments[0].isString()) {
|
|
212
|
+
// reKey(..) with new encryption-key
|
|
213
|
+
NSString* encryptionKey = convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
|
|
214
|
+
NSData* encryptionKeyBytes = [encryptionKey dataUsingEncoding:NSUTF8StringEncoding];
|
|
215
|
+
[instance reKey:encryptionKeyBytes];
|
|
216
|
+
} else {
|
|
217
|
+
throw jsi::JSError(runtime, "First argument ('encryptionKey') has to be of type string (or undefined)!");
|
|
218
|
+
}
|
|
219
|
+
return jsi::Value::undefined();
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
180
223
|
return jsi::Value::undefined();
|
|
181
224
|
}
|