react-native-mmkv 2.0.0 → 2.0.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.
@@ -34,7 +34,7 @@
34
34
  #include <vector>
35
35
  #include <unordered_map>
36
36
 
37
- constexpr auto MMKV_VERSION = "v1.2.10";
37
+ constexpr auto MMKV_VERSION = "v1.2.12";
38
38
 
39
39
  #ifdef DEBUG
40
40
  # define MMKV_DEBUG
@@ -88,6 +88,7 @@ constexpr auto MMKV_PATH_SLASH = L"\\";
88
88
  using MMKVFileHandle_t = HANDLE;
89
89
  using MMKVPath_t = std::wstring;
90
90
  extern MMKVPath_t string2MMKVPath_t(const std::string &str);
91
+ extern std::string MMKVPath_t2String(const MMKVPath_t &str);
91
92
 
92
93
  # ifndef MMKV_EMBED_ZLIB
93
94
  # define MMKV_EMBED_ZLIB 1
@@ -100,6 +101,7 @@ constexpr auto MMKV_PATH_SLASH = "/";
100
101
  using MMKVFileHandle_t = int;
101
102
  using MMKVPath_t = std::string;
102
103
  # define string2MMKVPath_t(str) (str)
104
+ # define MMKVPath_t2String(str) (str)
103
105
 
104
106
  # ifndef MMKV_EMBED_ZLIB
105
107
  # define MMKV_EMBED_ZLIB 0
@@ -226,15 +228,15 @@ constexpr size_t AES_KEY_BITSET_LEN = 128;
226
228
  #else
227
229
  #define MMKV_ABI "armeabi"
228
230
  #endif
229
- #elif defined(__i386__)
231
+ #elif defined(__i386__) || defined(_M_IX86)
230
232
  #define MMKV_ABI "x86"
231
- #elif defined(__x86_64__)
233
+ #elif defined(__x86_64__) || defined(_M_X64)
232
234
  #define MMKV_ABI "x86_64"
233
235
  #elif defined(__mips64)
234
236
  #define MMKV_ABI "mips64"
235
237
  #elif defined(__mips__)
236
238
  #define MMKV_ABI "mips"
237
- #elif defined(__aarch64__)
239
+ #elif defined(__aarch64__) || defined(_M_ARM64)
238
240
  #define MMKV_ABI "arm64-v8a"
239
241
  #else
240
242
  #define MMKV_ABI "unknown"
@@ -30,6 +30,7 @@
30
30
  # include "ScopedLock.hpp"
31
31
  # include "ThreadLock.h"
32
32
  # include <unistd.h>
33
+ # include "MMKV_IO.h"
33
34
 
34
35
  using namespace std;
35
36
  using namespace mmkv;
@@ -37,12 +38,8 @@ using namespace mmkv;
37
38
  extern unordered_map<string, MMKV *> *g_instanceDic;
38
39
  extern ThreadLock *g_instanceLock;
39
40
 
40
- extern string mmapedKVKey(const string &mmapID, string *rootPath);
41
- extern string mappedKVPathWithID(const string &mmapID, MMKVMode mode, string *rootPath);
42
- extern string crcPathWithID(const string &mmapID, MMKVMode mode, string *rootPath);
43
-
44
41
  MMKV::MMKV(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath)
45
- : m_mmapID(mmapedKVKey(mmapID, rootPath)) // historically Android mistakenly use mmapKey as mmapID
42
+ : m_mmapID((mode & MMKV_BACKUP) ? mmapID : mmapedKVKey(mmapID, rootPath)) // historically Android mistakenly use mmapKey as mmapID
46
43
  , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
47
44
  , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
48
45
  , m_dic(nullptr)
@@ -139,7 +136,6 @@ MMKV::MMKV(const string &mmapID, int ashmemFD, int ashmemMetaFD, string *cryptKe
139
136
  }
140
137
 
141
138
  MMKV *MMKV::mmkvWithID(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath) {
142
-
143
139
  if (mmapID.empty()) {
144
140
  return nullptr;
145
141
  }
@@ -1048,9 +1048,7 @@ void MMKV::clearAll() {
1048
1048
  SCOPED_LOCK(m_lock);
1049
1049
  SCOPED_LOCK(m_exclusiveProcessLock);
1050
1050
 
1051
- if (m_needLoadFromFile) {
1052
- m_file->reloadFromFile();
1053
- }
1051
+ checkLoadData();
1054
1052
 
1055
1053
  if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
1056
1054
  MMKVInfo("nothing to clear for [%s]", m_mmapID.c_str());
@@ -26,9 +26,9 @@
26
26
 
27
27
  MMKV_NAMESPACE_BEGIN
28
28
 
29
- std::string mmapedKVKey(const std::string &mmapID, MMKVPath_t *rootPath = nullptr);
30
- MMKVPath_t mappedKVPathWithID(const std::string &mmapID, MMKVMode mode, MMKVPath_t *rootPath);
31
- MMKVPath_t crcPathWithID(const std::string &mmapID, MMKVMode mode, MMKVPath_t *rootPath);
29
+ std::string mmapedKVKey(const std::string &mmapID, const MMKVPath_t *rootPath = nullptr);
30
+ MMKVPath_t mappedKVPathWithID(const std::string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath);
31
+ MMKVPath_t crcPathWithID(const std::string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath);
32
32
 
33
33
  MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID);
34
34
  MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID);
@@ -27,42 +27,112 @@
27
27
  # include "MMKVLog.h"
28
28
  # include "ScopedLock.hpp"
29
29
  # include <cerrno>
30
+ # include <utility>
30
31
  # include <fcntl.h>
31
32
  # include <sys/mman.h>
32
33
  # include <sys/stat.h>
33
34
  # include <unistd.h>
35
+ # include <sys/file.h>
36
+ # include <dirent.h>
37
+ # include <cstring>
34
38
 
35
39
  using namespace std;
36
40
 
37
41
  namespace mmkv {
38
42
 
39
- static bool getFileSize(int fd, size_t &size);
43
+ extern bool getFileSize(int fd, size_t &size);
40
44
 
41
45
  # ifdef MMKV_ANDROID
42
46
  extern size_t ASharedMemory_getSize(int fd);
43
47
  # else
44
- MemoryFile::MemoryFile(const MMKVPath_t &path) : m_name(path), m_fd(-1), m_ptr(nullptr), m_size(0) {
48
+ File::File(MMKVPath_t path, OpenFlag flag) : m_path(std::move(path)), m_fd(-1), m_flag(flag) {
49
+ open();
50
+ }
51
+
52
+ MemoryFile::MemoryFile(MMKVPath_t path) : m_diskFile(std::move(path), OpenFlag::ReadWrite | OpenFlag::Create), m_ptr(nullptr), m_size(0) {
45
53
  reloadFromFile();
46
54
  }
47
- # endif // MMKV_ANDROID
55
+ # endif // !defined(MMKV_ANDROID)
48
56
 
49
57
  # ifdef MMKV_IOS
50
58
  void tryResetFileProtection(const string &path);
51
59
  # endif
52
60
 
61
+ static int OpenFlag2NativeFlag(OpenFlag flag) {
62
+ int native = O_CLOEXEC;
63
+ if (flag & OpenFlag::ReadWrite) {
64
+ native |= O_RDWR;
65
+ } else if (flag & OpenFlag::ReadOnly) {
66
+ native |= O_RDONLY;
67
+ } else if (flag & OpenFlag::WriteOnly) {
68
+ native |= O_WRONLY;
69
+ }
70
+
71
+ if (flag & OpenFlag::Create) {
72
+ native |= O_CREAT;
73
+ }
74
+ if (flag & OpenFlag::Excel) {
75
+ native |= O_EXCL;
76
+ }
77
+ if (flag & OpenFlag::Truncate) {
78
+ native |= O_TRUNC;
79
+ }
80
+ return native;
81
+ }
82
+
83
+ bool File::open() {
84
+ # ifdef MMKV_ANDROID
85
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
86
+ return isFileValid();
87
+ }
88
+ # endif
89
+ if (isFileValid()) {
90
+ return true;
91
+ }
92
+ m_fd = ::open(m_path.c_str(), OpenFlag2NativeFlag(m_flag), S_IRWXU);
93
+ if (!isFileValid()) {
94
+ MMKVError("fail to open [%s], %d(%s)", m_path.c_str(), errno, strerror(errno));
95
+ return false;
96
+ }
97
+ MMKVInfo("open fd[%p], %s", m_fd, m_path.c_str());
98
+ return true;
99
+ }
100
+
101
+ void File::close() {
102
+ if (isFileValid()) {
103
+ MMKVInfo("closing fd[%p], %s", m_fd, m_path.c_str());
104
+ if (::close(m_fd) == 0) {
105
+ m_fd = -1;
106
+ } else {
107
+ MMKVError("fail to close [%s], %d(%s)", m_path.c_str(), errno, strerror(errno));
108
+ }
109
+ }
110
+ }
111
+
112
+ size_t File::getActualFileSize() const {
113
+ # ifdef MMKV_ANDROID
114
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
115
+ return ASharedMemory_getSize(m_fd);
116
+ }
117
+ # endif
118
+ size_t size = 0;
119
+ mmkv::getFileSize(m_fd, size);
120
+ return size;
121
+ }
122
+
53
123
  bool MemoryFile::truncate(size_t size) {
54
- if (m_fd < 0) {
124
+ if (!m_diskFile.isFileValid()) {
55
125
  return false;
56
126
  }
57
127
  if (size == m_size) {
58
128
  return true;
59
129
  }
60
130
  # ifdef MMKV_ANDROID
61
- if (m_fileType == MMFILE_TYPE_ASHMEM) {
131
+ if (m_diskFile.m_fileType == MMFILE_TYPE_ASHMEM) {
62
132
  if (size > m_size) {
63
- MMKVError("ashmem %s reach size limit:%zu, consider configure with larger size", m_name.c_str(), m_size);
133
+ MMKVError("ashmem %s reach size limit:%zu, consider configure with larger size", m_diskFile.m_path.c_str(), m_size);
64
134
  } else {
65
- MMKVInfo("no way to trim ashmem %s from %zu to smaller size %zu", m_name.c_str(), m_size, size);
135
+ MMKVInfo("no way to trim ashmem %s from %zu to smaller size %zu", m_diskFile.m_path.c_str(), m_size, size);
66
136
  }
67
137
  return false;
68
138
  }
@@ -75,14 +145,14 @@ bool MemoryFile::truncate(size_t size) {
75
145
  m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
76
146
  }
77
147
 
78
- if (::ftruncate(m_fd, static_cast<off_t>(m_size)) != 0) {
79
- MMKVError("fail to truncate [%s] to size %zu, %s", m_name.c_str(), m_size, strerror(errno));
148
+ if (::ftruncate(m_diskFile.m_fd, static_cast<off_t>(m_size)) != 0) {
149
+ MMKVError("fail to truncate [%s] to size %zu, %s", m_diskFile.m_path.c_str(), m_size, strerror(errno));
80
150
  m_size = oldSize;
81
151
  return false;
82
152
  }
83
153
  if (m_size > oldSize) {
84
- if (!zeroFillFile(m_fd, oldSize, m_size - oldSize)) {
85
- MMKVError("fail to zeroFile [%s] to size %zu, %s", m_name.c_str(), m_size, strerror(errno));
154
+ if (!zeroFillFile(m_diskFile.m_fd, oldSize, m_size - oldSize)) {
155
+ MMKVError("fail to zeroFile [%s] to size %zu, %s", m_diskFile.m_path.c_str(), m_size, strerror(errno));
86
156
  m_size = oldSize;
87
157
  return false;
88
158
  }
@@ -90,7 +160,7 @@ bool MemoryFile::truncate(size_t size) {
90
160
 
91
161
  if (m_ptr) {
92
162
  if (munmap(m_ptr, oldSize) != 0) {
93
- MMKVError("fail to munmap [%s], %s", m_name.c_str(), strerror(errno));
163
+ MMKVError("fail to munmap [%s], %s", m_diskFile.m_path.c_str(), strerror(errno));
94
164
  }
95
165
  }
96
166
  auto ret = mmap();
@@ -106,15 +176,15 @@ bool MemoryFile::msync(SyncFlag syncFlag) {
106
176
  if (ret == 0) {
107
177
  return true;
108
178
  }
109
- MMKVError("fail to msync [%s], %s", m_name.c_str(), strerror(errno));
179
+ MMKVError("fail to msync [%s], %s", m_diskFile.m_path.c_str(), strerror(errno));
110
180
  }
111
181
  return false;
112
182
  }
113
183
 
114
184
  bool MemoryFile::mmap() {
115
- m_ptr = (char *) ::mmap(m_ptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
185
+ m_ptr = (char *) ::mmap(m_ptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_diskFile.m_fd, 0);
116
186
  if (m_ptr == MAP_FAILED) {
117
- MMKVError("fail to mmap [%s], %s", m_name.c_str(), strerror(errno));
187
+ MMKVError("fail to mmap [%s], %s", m_diskFile.m_path.c_str(), strerror(errno));
118
188
  m_ptr = nullptr;
119
189
  return false;
120
190
  }
@@ -129,20 +199,19 @@ void MemoryFile::reloadFromFile() {
129
199
  }
130
200
  # endif
131
201
  if (isFileValid()) {
132
- MMKVWarning("calling reloadFromFile while the cache [%s] is still valid", m_name.c_str());
202
+ MMKVWarning("calling reloadFromFile while the cache [%s] is still valid", m_diskFile.m_path.c_str());
133
203
  MMKV_ASSERT(0);
134
204
  clearMemoryCache();
135
205
  }
136
206
 
137
- m_fd = open(m_name.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU);
138
- if (m_fd < 0) {
139
- MMKVError("fail to open:%s, %s", m_name.c_str(), strerror(errno));
207
+ if (!m_diskFile.open()) {
208
+ MMKVError("fail to open:%s, %s", m_diskFile.m_path.c_str(), strerror(errno));
140
209
  } else {
141
- FileLock fileLock(m_fd);
210
+ FileLock fileLock(m_diskFile.m_fd);
142
211
  InterProcessLock lock(&fileLock, ExclusiveLockType);
143
212
  SCOPED_LOCK(&lock);
144
213
 
145
- mmkv::getFileSize(m_fd, m_size);
214
+ mmkv::getFileSize(m_diskFile.m_fd, m_size);
146
215
  // round up to (n * pagesize)
147
216
  if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
148
217
  size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
@@ -154,44 +223,28 @@ void MemoryFile::reloadFromFile() {
154
223
  }
155
224
  }
156
225
  # ifdef MMKV_IOS
157
- tryResetFileProtection(m_name);
226
+ tryResetFileProtection(m_diskFile.m_path);
158
227
  # endif
159
228
  }
160
229
  }
161
230
 
162
231
  void MemoryFile::doCleanMemoryCache(bool forceClean) {
163
232
  # ifdef MMKV_ANDROID
164
- if (m_fileType == MMFILE_TYPE_ASHMEM && !forceClean) {
233
+ if (m_diskFile.m_fileType == MMFILE_TYPE_ASHMEM && !forceClean) {
165
234
  return;
166
235
  }
167
236
  # endif
168
237
  if (m_ptr && m_ptr != MAP_FAILED) {
169
238
  if (munmap(m_ptr, m_size) != 0) {
170
- MMKVError("fail to munmap [%s], %s", m_name.c_str(), strerror(errno));
239
+ MMKVError("fail to munmap [%s], %s", m_diskFile.m_path.c_str(), strerror(errno));
171
240
  }
172
241
  }
173
242
  m_ptr = nullptr;
174
243
 
175
- if (m_fd >= 0) {
176
- if (::close(m_fd) != 0) {
177
- MMKVError("fail to close [%s], %s", m_name.c_str(), strerror(errno));
178
- }
179
- }
180
- m_fd = -1;
244
+ m_diskFile.close();
181
245
  m_size = 0;
182
246
  }
183
247
 
184
- size_t MemoryFile::getActualFileSize() {
185
- # ifdef MMKV_ANDROID
186
- if (m_fileType == MMFILE_TYPE_ASHMEM) {
187
- return ASharedMemory_getSize(m_fd);
188
- }
189
- # endif
190
- size_t size = 0;
191
- mmkv::getFileSize(m_fd, size);
192
- return size;
193
- }
194
-
195
248
  bool isFileExist(const string &nsFilePath) {
196
249
  if (nsFilePath.empty()) {
197
250
  return false;
@@ -286,7 +339,7 @@ bool zeroFillFile(int fd, size_t startPos, size_t size) {
286
339
  return true;
287
340
  }
288
341
 
289
- static bool getFileSize(int fd, size_t &size) {
342
+ bool getFileSize(int fd, size_t &size) {
290
343
  struct stat st = {};
291
344
  if (fstat(fd, &st) != -1) {
292
345
  size = (size_t) st.st_size;
@@ -299,6 +352,186 @@ size_t getPageSize() {
299
352
  return static_cast<size_t>(getpagesize());
300
353
  }
301
354
 
355
+ #ifndef MMKV_APPLE
356
+
357
+ static pair<MMKVPath_t, int> createUniqueTempFile(const char *prefix) {
358
+ char path[PATH_MAX];
359
+ #ifdef MMKV_ANDROID
360
+ snprintf(path, PATH_MAX, "%s/%s.XXXXXX", g_android_tmpDir.c_str(), prefix);
361
+ #else
362
+ snprintf(path, PATH_MAX, "%s/%s.XXXXXX", P_tmpdir, prefix);
363
+ #endif
364
+
365
+ auto fd = mkstemp(path);
366
+ if (fd < 0) {
367
+ MMKVError("fail to create unique temp file [%s], %d(%s)", path, errno, strerror(errno));
368
+ return {"", fd};
369
+ }
370
+ MMKVDebug("create unique temp file [%s] with fd[%d]", path, fd);
371
+ return {MMKVPath_t(path), fd};
372
+ }
373
+
374
+ #if !defined(MMKV_ANDROID) && !defined(MMKV_LINUX)
375
+
376
+ bool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
377
+ if (::rename(srcPath.c_str(), dstPath.c_str()) != 0) {
378
+ MMKVError("fail to rename [%s] to [%s], %d(%s)", srcPath.c_str(), dstPath.c_str(), errno, strerror(errno));
379
+ return false;
380
+ }
381
+ return true;
382
+ }
383
+
384
+ bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate) {
385
+ if (dstFD < 0) {
386
+ return false;
387
+ }
388
+ bool ret = false;
389
+ File srcFile(srcPath, OpenFlag::ReadOnly);
390
+ if (!srcFile.isFileValid()) {
391
+ return false;
392
+ }
393
+ auto bufferSize = getPageSize();
394
+ auto buffer = (char *) malloc(bufferSize);
395
+ if (!buffer) {
396
+ MMKVError("fail to malloc size %zu, %d(%s)", bufferSize, errno, strerror(errno));
397
+ goto errorOut;
398
+ }
399
+ lseek(dstFD, 0, SEEK_SET);
400
+
401
+ // the POSIX standard don't have sendfile()/fcopyfile() equivalent, do it the hard way
402
+ while (true) {
403
+ auto sizeRead = read(srcFile.getFd(), buffer, bufferSize);
404
+ if (sizeRead < 0) {
405
+ MMKVError("fail to read file [%s], %d(%s)", srcPath.c_str(), errno, strerror(errno));
406
+ goto errorOut;
407
+ }
408
+
409
+ size_t totalWrite = 0;
410
+ do {
411
+ auto sizeWrite = write(dstFD, buffer + totalWrite, sizeRead - totalWrite);
412
+ if (sizeWrite < 0) {
413
+ MMKVError("fail to write fd [%d], %d(%s)", dstFD, errno, strerror(errno));
414
+ goto errorOut;
415
+ }
416
+ totalWrite += sizeWrite;
417
+ } while (totalWrite < sizeRead);
418
+
419
+ if (sizeRead < bufferSize) {
420
+ break;
421
+ }
422
+ }
423
+ if (needTruncate) {
424
+ size_t dstFileSize = 0;
425
+ getFileSize(dstFD, dstFileSize);
426
+ auto srcFileSize = srcFile.getActualFileSize();
427
+ if ((dstFileSize != srcFileSize) && (::ftruncate(dstFD, static_cast<off_t>(srcFileSize)) != 0)) {
428
+ MMKVError("fail to truncate [%d] to size [%zu], %d(%s)", dstFD, srcFileSize, errno, strerror(errno));
429
+ goto errorOut;
430
+ }
431
+ }
432
+
433
+ ret = true;
434
+ MMKVInfo("copy content from %s to fd[%d] finish", srcPath.c_str(), dstFD);
435
+
436
+ errorOut:
437
+ free(buffer);
438
+ return ret;
439
+ }
440
+
441
+ #endif // !defined(MMKV_ANDROID) && !defined(MMKV_LINUX)
442
+
443
+ // copy to a temp file then rename it
444
+ // this is the best we can do under the POSIX standard
445
+ bool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
446
+ auto pair = createUniqueTempFile("MMKV");
447
+ auto tmpFD = pair.second;
448
+ auto &tmpPath = pair.first;
449
+ if (tmpFD < 0) {
450
+ return false;
451
+ }
452
+
453
+ bool renamed = false;
454
+ if (copyFileContent(srcPath, tmpFD, false)) {
455
+ MMKVInfo("copyfile [%s] to [%s]", srcPath.c_str(), tmpPath.c_str());
456
+ renamed = tryAtomicRename(tmpPath, dstPath);
457
+ if (renamed) {
458
+ MMKVInfo("copyfile [%s] to [%s] finish.", srcPath.c_str(), dstPath.c_str());
459
+ }
460
+ }
461
+
462
+ ::close(tmpFD);
463
+ if (!renamed) {
464
+ ::unlink(tmpPath.c_str());
465
+ }
466
+ return renamed;
467
+ }
468
+
469
+ bool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
470
+ File dstFile(dstPath, OpenFlag::WriteOnly | OpenFlag::Create | OpenFlag::Truncate);
471
+ if (!dstFile.isFileValid()) {
472
+ return false;
473
+ }
474
+ auto ret = copyFileContent(srcPath, dstFile.getFd(), false);
475
+ if (!ret) {
476
+ MMKVError("fail to copyfile(): target file %s", dstPath.c_str());
477
+ } else {
478
+ MMKVInfo("copy content from %s to [%s] finish", srcPath.c_str(), dstPath.c_str());
479
+ }
480
+ return ret;
481
+ }
482
+
483
+ bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD) {
484
+ return copyFileContent(srcPath, dstFD, true);
485
+ }
486
+
487
+ #endif // !defined(MMKV_APPLE)
488
+
489
+ void walkInDir(const MMKVPath_t &dirPath, WalkType type, const function<void(const MMKVPath_t&, WalkType)> &walker) {
490
+ auto folderPathStr = dirPath.data();
491
+ DIR *dir = opendir(folderPathStr);
492
+ if (!dir) {
493
+ MMKVError("opendir failed: %d(%s), %s", errno, strerror(errno), dirPath.c_str());
494
+ return;
495
+ }
496
+
497
+ char childPath[PATH_MAX];
498
+ size_t folderPathLength = dirPath.size();
499
+ strncpy(childPath, folderPathStr, folderPathLength + 1);
500
+ if (folderPathStr[folderPathLength - 1] != '/') {
501
+ childPath[folderPathLength] = '/';
502
+ folderPathLength++;
503
+ }
504
+
505
+ while (auto child = readdir(dir)) {
506
+ if ((child->d_type & DT_REG) && (type & WalkFile)) {
507
+ #ifdef _DIRENT_HAVE_D_NAMLEN
508
+ stpcpy(childPath + folderPathLength, child->d_name);
509
+ childPath[folderPathLength + child->d_namlen] = 0;
510
+ #else
511
+ strcpy(childPath + folderPathLength, child->d_name);
512
+ #endif
513
+ walker(childPath, WalkFile);
514
+ } else if ((child->d_type & DT_DIR) && (type & WalkFolder)) {
515
+ #ifdef _DIRENT_HAVE_D_NAMLEN
516
+ if ((child->d_namlen == 1 && child->d_name[0] == '.') ||
517
+ (child->d_namlen == 2 && child->d_name[0] == '.' && child->d_name[1] == '.')) {
518
+ continue;
519
+ }
520
+ stpcpy(childPath + folderPathLength, child->d_name);
521
+ childPath[folderPathLength + child->d_namlen] = 0;
522
+ #else
523
+ if (strcmp(child->d_name, ".") == 0 || strcmp(child->d_name, "..") == 0) {
524
+ continue;
525
+ }
526
+ strcpy(childPath + folderPathLength, child->d_name);
527
+ #endif
528
+ walker(childPath, WalkFolder);
529
+ }
530
+ }
531
+
532
+ closedir(dir);
533
+ }
534
+
302
535
  } // namespace mmkv
303
536
 
304
- #endif // MMKV_WIN32
537
+ #endif // !defined(MMKV_WIN32)
@@ -23,12 +23,14 @@
23
23
  #ifdef __cplusplus
24
24
 
25
25
  #include "MMKVPredef.h"
26
+ #include <functional>
26
27
 
27
28
  #ifdef MMKV_ANDROID
28
29
  MMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID);
29
30
 
30
31
  namespace mmkv {
31
32
  extern int g_android_api;
33
+ extern std::string g_android_tmpDir;
32
34
 
33
35
  enum FileType : bool { MMFILE_TYPE_FILE = false, MMFILE_TYPE_ASHMEM = true };
34
36
  } // namespace mmkv
@@ -36,9 +38,66 @@ enum FileType : bool { MMFILE_TYPE_FILE = false, MMFILE_TYPE_ASHMEM = true };
36
38
 
37
39
  namespace mmkv {
38
40
 
39
- class MemoryFile {
40
- MMKVPath_t m_name;
41
+ enum class OpenFlag : uint32_t {
42
+ ReadOnly = 1 << 0,
43
+ WriteOnly = 1 << 1,
44
+ ReadWrite = ReadOnly | WriteOnly,
45
+ Create = 1 << 2,
46
+ Excel = 1 << 3, // fail if Create is set but the file already exist
47
+ Truncate = 1 << 4,
48
+ };
49
+
50
+ static inline OpenFlag operator | (OpenFlag left, OpenFlag right) {
51
+ return static_cast<OpenFlag>(static_cast<uint32_t>(left) | static_cast<uint32_t>(right));
52
+ }
53
+
54
+ static inline bool operator & (OpenFlag left, OpenFlag right) {
55
+ return ((static_cast<uint32_t>(left) & static_cast<uint32_t>(right)) != 0);
56
+ }
57
+
58
+ class File {
59
+ MMKVPath_t m_path;
41
60
  MMKVFileHandle_t m_fd;
61
+ public:
62
+ const OpenFlag m_flag;
63
+ #ifndef MMKV_ANDROID
64
+ explicit File(MMKVPath_t path, OpenFlag flag);
65
+ #else
66
+ File(MMKVPath_t path, OpenFlag flag, size_t size = 0, FileType fileType = MMFILE_TYPE_FILE);
67
+ explicit File(MMKVFileHandle_t ashmemFD);
68
+
69
+ size_t m_size;
70
+ const FileType m_fileType;
71
+ #endif // MMKV_ANDROID
72
+
73
+ ~File() { close(); }
74
+
75
+ bool open();
76
+
77
+ void close();
78
+
79
+ MMKVFileHandle_t getFd() { return m_fd; }
80
+
81
+ const MMKVPath_t &getPath() const { return m_path; }
82
+
83
+ #ifndef MMKV_WIN32
84
+ bool isFileValid() const { return m_fd >= 0; }
85
+ #else
86
+ bool isFileValid() const { return m_fd != INVALID_HANDLE_VALUE; }
87
+ #endif
88
+
89
+ // get the actual file size on disk
90
+ size_t getActualFileSize() const;
91
+
92
+ // just forbid it for possibly misuse
93
+ explicit File(const File &other) = delete;
94
+ File &operator=(const File &other) = delete;
95
+
96
+ friend class MemoryFile;
97
+ };
98
+
99
+ class MemoryFile {
100
+ File m_diskFile;
42
101
  #ifdef MMKV_WIN32
43
102
  HANDLE m_fileMapping;
44
103
  #endif
@@ -51,9 +110,9 @@ class MemoryFile {
51
110
 
52
111
  public:
53
112
  #ifndef MMKV_ANDROID
54
- explicit MemoryFile(const MMKVPath_t &path);
113
+ explicit MemoryFile(MMKVPath_t path);
55
114
  #else
56
- MemoryFile(const MMKVPath_t &path, size_t size, FileType fileType);
115
+ MemoryFile(MMKVPath_t path, size_t size, FileType fileType);
57
116
  explicit MemoryFile(MMKVFileHandle_t ashmemFD);
58
117
 
59
118
  const FileType m_fileType;
@@ -64,13 +123,13 @@ public:
64
123
  size_t getFileSize() const { return m_size; }
65
124
 
66
125
  // get the actual file size on disk
67
- size_t getActualFileSize();
126
+ size_t getActualFileSize() const { return m_diskFile.getActualFileSize(); }
68
127
 
69
128
  void *getMemory() { return m_ptr; }
70
129
 
71
- const MMKVPath_t &getName() { return m_name; }
130
+ const MMKVPath_t &getPath() { return m_diskFile.getPath(); }
72
131
 
73
- MMKVFileHandle_t getFd() { return m_fd; }
132
+ MMKVFileHandle_t getFd() { return m_diskFile.getFd(); }
74
133
 
75
134
  // the newly expanded file content will be zeroed
76
135
  bool truncate(size_t size);
@@ -83,9 +142,9 @@ public:
83
142
  void clearMemoryCache() { doCleanMemoryCache(false); }
84
143
 
85
144
  #ifndef MMKV_WIN32
86
- bool isFileValid() { return m_fd >= 0 && m_size > 0 && m_ptr; }
145
+ bool isFileValid() { return m_diskFile.isFileValid() && m_size > 0 && m_ptr; }
87
146
  #else
88
- bool isFileValid() { return m_fd != INVALID_HANDLE_VALUE && m_size > 0 && m_fileMapping && m_ptr; }
147
+ bool isFileValid() { return m_diskFile.isFileValid() && m_size > 0 && m_fileMapping && m_ptr; }
89
148
  #endif
90
149
 
91
150
  // just forbid it for possibly misuse
@@ -100,6 +159,23 @@ extern bool isFileExist(const MMKVPath_t &nsFilePath);
100
159
  extern MMBuffer *readWholeFile(const MMKVPath_t &path);
101
160
  extern bool zeroFillFile(MMKVFileHandle_t fd, size_t startPos, size_t size);
102
161
  extern size_t getPageSize();
162
+
163
+ extern bool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath);
164
+
165
+ // copy file by potentially renaming target file, might change file inode
166
+ extern bool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath);
167
+
168
+ // copy file by source file content, keep file inode the same
169
+ extern bool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath);
170
+ extern bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD);
171
+ extern bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate);
172
+
173
+ enum WalkType : uint32_t {
174
+ WalkFile = 1 << 0,
175
+ WalkFolder = 1 << 1,
176
+ };
177
+ extern void walkInDir(const MMKVPath_t &dirPath, WalkType type, const std::function<void(const MMKVPath_t&, WalkType)> &walker);
178
+
103
179
  } // namespace mmkv
104
180
 
105
181
  #endif