react-native-mmkv 2.7.0 → 2.9.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 (53) hide show
  1. package/MMKV/Core/Core.xcodeproj/project.pbxproj +15 -19
  2. package/MMKV/Core/Core.xcodeproj/xcshareddata/xcschemes/Core.xcscheme +1 -1
  3. package/MMKV/Core/Core.xcodeproj/xcshareddata/xcschemes/MMKVWatchCore.xcscheme +1 -1
  4. package/MMKV/Core/MMBuffer.cpp +18 -0
  5. package/MMKV/Core/MMBuffer.h +1 -0
  6. package/MMKV/Core/MMKV.cpp +173 -33
  7. package/MMKV/Core/MMKV.h +44 -0
  8. package/MMKV/Core/MMKVMetaInfo.hpp +18 -0
  9. package/MMKV/Core/MMKVPredef.h +2 -2
  10. package/MMKV/Core/MMKV_IO.cpp +477 -68
  11. package/MMKV/Core/MMKV_OSX.cpp +65 -14
  12. package/MMKV/Core/MemoryFile_Win32.cpp +39 -35
  13. package/MMKV/Core/MiniPBCoder.cpp +3 -7
  14. package/MMKV/Core/MiniPBCoder_OSX.cpp +4 -4
  15. package/MMKV/Core/PBUtility.cpp +1 -1
  16. package/MMKV/Core/PBUtility.h +7 -0
  17. package/MMKV/Core/aes/AESCrypt.h +1 -1
  18. package/MMKV/Core/aes/openssl/openssl_aes-armv4.S +4 -0
  19. package/README.md +14 -6
  20. package/android/build.gradle +2 -0
  21. package/android/src/main/AndroidManifest.xml +1 -2
  22. package/android/src/main/cpp/cpp-adapter.cpp +5 -0
  23. package/cpp/TypedArray.cpp +187 -199
  24. package/cpp/TypedArray.h +112 -109
  25. package/ios/JSIUtils.mm +2 -1
  26. package/ios/MmkvModule.mm +12 -7
  27. package/lib/commonjs/MMKV.js +0 -10
  28. package/lib/commonjs/MMKV.js.map +1 -1
  29. package/lib/commonjs/createMMKV.web.js +55 -12
  30. package/lib/commonjs/createMMKV.web.js.map +1 -1
  31. package/lib/commonjs/hooks.js +7 -0
  32. package/lib/commonjs/hooks.js.map +1 -1
  33. package/lib/module/MMKV.js +0 -10
  34. package/lib/module/MMKV.js.map +1 -1
  35. package/lib/module/createMMKV.web.js +55 -12
  36. package/lib/module/createMMKV.web.js.map +1 -1
  37. package/lib/module/hooks.js +7 -0
  38. package/lib/module/hooks.js.map +1 -1
  39. package/lib/typescript/MMKV.d.ts +0 -12
  40. package/lib/typescript/MMKV.d.ts.map +1 -1
  41. package/lib/typescript/createMMKV.web.d.ts.map +1 -1
  42. package/lib/typescript/hooks.d.ts.map +1 -1
  43. package/package.json +15 -10
  44. package/src/MMKV.ts +248 -0
  45. package/src/PlatformChecker.ts +7 -0
  46. package/src/createMMKV.mock.ts +33 -0
  47. package/src/createMMKV.ts +70 -0
  48. package/src/createMMKV.web.ts +119 -0
  49. package/src/createTextEncoder.ts +16 -0
  50. package/src/hooks.ts +232 -0
  51. package/src/index.ts +2 -0
  52. package/MMKV/LICENSE.TXT +0 -193
  53. package/MMKV/README.md +0 -291
@@ -37,6 +37,7 @@
37
37
  #include <algorithm>
38
38
  #include <cassert>
39
39
  #include <cstring>
40
+ #include <ctime>
40
41
 
41
42
  #ifdef MMKV_IOS
42
43
  # include "MMKV_OSX.h"
@@ -52,14 +53,10 @@ using namespace std;
52
53
  using namespace mmkv;
53
54
  using KVHolderRet_t = std::pair<bool, KeyValueHolder>;
54
55
 
55
- constexpr uint32_t Fixed32Size = pbFixed32Size();
56
-
57
56
  MMKV_NAMESPACE_BEGIN
58
57
 
59
58
  void MMKV::loadFromFile() {
60
- if (m_metaFile->isFileValid()) {
61
- m_metaInfo->read(m_metaFile->getMemory());
62
- }
59
+ loadMetaInfoAndCheck();
63
60
  #ifndef MMKV_DISABLE_CRYPT
64
61
  if (m_crypter) {
65
62
  if (m_metaInfo->m_version >= MMKVVersionRandomIV) {
@@ -164,7 +161,7 @@ void MMKV::partialLoadFromFile() {
164
161
  m_output->seek(addedSize);
165
162
  m_hasFullWriteback = false;
166
163
 
167
- auto count = m_crypter ? m_dicCrypt->size() : m_dic->size();
164
+ [[maybe_unused]] auto count = m_crypter ? m_dicCrypt->size() : m_dic->size();
168
165
  MMKVDebug("partial loaded [%s] with %zu values", m_mmapID.c_str(), count);
169
166
  return;
170
167
  } else {
@@ -178,6 +175,37 @@ void MMKV::partialLoadFromFile() {
178
175
  loadFromFile();
179
176
  }
180
177
 
178
+ void MMKV::loadMetaInfoAndCheck() {
179
+ if (!m_metaFile->isFileValid()) {
180
+ m_metaFile->reloadFromFile();
181
+ }
182
+ if (!m_metaFile->isFileValid()) {
183
+ MMKVError("file [%s] not valid", m_metaFile->getPath().c_str());
184
+ return;
185
+ }
186
+
187
+ m_metaInfo->read(m_metaFile->getMemory());
188
+ // the meta file is in specious status
189
+ if (m_metaInfo->m_version >= MMKVVersionHolder) {
190
+ MMKVWarning("meta file [%s] in specious state, version %u, flags 0x%llx", m_mmapID.c_str(), m_metaInfo->m_version, m_metaInfo->m_flags);
191
+
192
+ // MMKVVersionActualSize is the last version we don't check meta file
193
+ m_metaInfo->m_version = MMKVVersionActualSize;
194
+ m_metaInfo->m_flags = 0;
195
+ m_metaInfo->write(m_metaFile->getMemory());
196
+ }
197
+
198
+ if (m_metaInfo->m_version >= MMKVVersionFlag) {
199
+ m_enableKeyExpire = m_metaInfo->hasFlag(MMKVMetaInfo::EnableKeyExipre);
200
+ MMKVInfo("meta file [%s] has flag [%llu]", m_mmapID.c_str(), m_metaInfo->m_flags);
201
+ } else {
202
+ if (m_metaInfo->m_flags != 0) {
203
+ m_metaInfo->m_flags = 0;
204
+ m_metaInfo->write(m_metaFile->getMemory());
205
+ }
206
+ }
207
+ }
208
+
181
209
  void MMKV::checkDataValid(bool &loadFromFile, bool &needFullWriteback) {
182
210
  // try auto recover from last confirmed location
183
211
  auto fileSize = m_file->getFileSize();
@@ -335,10 +363,20 @@ static pair<MMBuffer, size_t> prepareEncode(const MMKVMapCrypt &dic) {
335
363
  // skip the pb size of buffer
336
364
  auto sizeOfMap = CodedInputData(buffer.getPtr(), buffer.length()).readUInt32();
337
365
  totalSize += sizeOfMap;
338
- return make_pair(move(buffer), totalSize);
366
+ return make_pair(std::move(buffer), totalSize);
339
367
  }
340
368
  #endif
341
369
 
370
+ static pair<MMBuffer, size_t> prepareEncode(MMKVVector &&vec) {
371
+ // make some room for placeholder
372
+ size_t totalSize = ItemSizeHolderSize;
373
+ auto buffer = MiniPBCoder::encodeDataWithObject(vec);
374
+ // skip the pb size of buffer
375
+ auto sizeOfMap = CodedInputData(buffer.getPtr(), buffer.length()).readUInt32();
376
+ totalSize += sizeOfMap;
377
+ return make_pair(std::move(buffer), totalSize);
378
+ }
379
+
342
380
  // since we use append mode, when -[setData: forKey:] many times, space may not be enough
343
381
  // try a full rewrite to make space
344
382
  bool MMKV::ensureMemorySize(size_t newSize) {
@@ -348,38 +386,47 @@ bool MMKV::ensureMemorySize(size_t newSize) {
348
386
  }
349
387
 
350
388
  if (newSize >= m_output->spaceLeft() || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {
389
+ // remove expired keys
390
+ if (m_enableKeyExpire) {
391
+ filterExpiredKeys();
392
+ }
351
393
  // try a full rewrite to make space
352
- auto fileSize = m_file->getFileSize();
353
394
  auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);
354
- auto sizeOfDic = preparedData.second;
355
- size_t lenNeeded = sizeOfDic + Fixed32Size + newSize;
356
- size_t dicCount = m_crypter ? m_dicCrypt->size() : m_dic->size();
357
- size_t avgItemSize = lenNeeded / std::max<size_t>(1, dicCount);
358
- size_t futureUsage = avgItemSize * std::max<size_t>(8, (dicCount + 1) / 2);
359
- // 1. no space for a full rewrite, double it
360
- // 2. or space is not large enough for future usage, double it to avoid frequently full rewrite
361
- if (lenNeeded >= fileSize || (lenNeeded + futureUsage) >= fileSize) {
362
- size_t oldSize = fileSize;
363
- do {
364
- fileSize *= 2;
365
- } while (lenNeeded + futureUsage >= fileSize);
366
- MMKVInfo("extending [%s] file size from %zu to %zu, incoming size:%zu, future usage:%zu", m_mmapID.c_str(),
367
- oldSize, fileSize, newSize, futureUsage);
368
-
369
- // if we can't extend size, rollback to old state
370
- if (!m_file->truncate(fileSize)) {
371
- return false;
372
- }
395
+ return expandAndWriteBack(newSize, std::move(preparedData));
396
+ }
397
+ return true;
398
+ }
373
399
 
374
- // check if we fail to make more space
375
- if (!isFileValid()) {
376
- MMKVWarning("[%s] file not valid", m_mmapID.c_str());
377
- return false;
378
- }
400
+ // try a full rewrite to make space
401
+ bool MMKV::expandAndWriteBack(size_t newSize, std::pair<mmkv::MMBuffer, size_t> preparedData) {
402
+ auto fileSize = m_file->getFileSize();
403
+ auto sizeOfDic = preparedData.second;
404
+ size_t lenNeeded = sizeOfDic + Fixed32Size + newSize;
405
+ size_t dicCount = m_crypter ? m_dicCrypt->size() : m_dic->size();
406
+ size_t avgItemSize = lenNeeded / std::max<size_t>(1, dicCount);
407
+ size_t futureUsage = avgItemSize * std::max<size_t>(8, (dicCount + 1) / 2);
408
+ // 1. no space for a full rewrite, double it
409
+ // 2. or space is not large enough for future usage, double it to avoid frequently full rewrite
410
+ if (lenNeeded >= fileSize || (lenNeeded + futureUsage) >= fileSize) {
411
+ size_t oldSize = fileSize;
412
+ do {
413
+ fileSize *= 2;
414
+ } while (lenNeeded + futureUsage >= fileSize);
415
+ MMKVInfo("extending [%s] file size from %zu to %zu, incoming size:%zu, future usage:%zu", m_mmapID.c_str(),
416
+ oldSize, fileSize, newSize, futureUsage);
417
+
418
+ // if we can't extend size, rollback to old state
419
+ if (!m_file->truncate(fileSize)) {
420
+ return false;
421
+ }
422
+
423
+ // check if we fail to make more space
424
+ if (!isFileValid()) {
425
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
426
+ return false;
379
427
  }
380
- return doFullWriteBack(move(preparedData), nullptr);
381
428
  }
382
- return true;
429
+ return doFullWriteBack(std::move(preparedData), nullptr);
383
430
  }
384
431
 
385
432
  size_t MMKV::readActualSize() {
@@ -450,6 +497,11 @@ bool MMKV::writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool
450
497
  MMKVInfo("[%s] increase sequence to %u, crc %u, actualSize %u", m_mmapID.c_str(), m_metaInfo->m_sequence,
451
498
  m_metaInfo->m_crcDigest, m_metaInfo->m_actualSize);
452
499
  }
500
+ if (m_metaInfo->m_version < MMKVVersionFlag) {
501
+ m_metaInfo->m_flags = 0;
502
+ m_metaInfo->m_version = MMKVVersionFlag;
503
+ needsFullWrite = true;
504
+ }
453
505
  #ifdef MMKV_IOS
454
506
  auto ret = guardForBackgroundWriting(m_metaFile->getMemory(), sizeof(MMKVMetaInfo));
455
507
  if (!ret.first) {
@@ -464,7 +516,7 @@ bool MMKV::writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool
464
516
  return true;
465
517
  }
466
518
 
467
- MMBuffer MMKV::getDataForKey(MMKVKey_t key) {
519
+ MMBuffer MMKV::getRawDataForKey(MMKVKey_t key) {
468
520
  checkLoadData();
469
521
  #ifndef MMKV_DISABLE_CRYPT
470
522
  if (m_crypter) {
@@ -486,6 +538,13 @@ MMBuffer MMKV::getDataForKey(MMKVKey_t key) {
486
538
  return nan;
487
539
  }
488
540
 
541
+ mmkv::MMBuffer MMKV::getDataForKey(MMKVKey_t key) {
542
+ if (unlikely(m_enableKeyExpire)) {
543
+ return getDataWithoutMTimeForKey(key);
544
+ }
545
+ return getRawDataForKey(key);
546
+ }
547
+
489
548
  #ifndef MMKV_DISABLE_CRYPT
490
549
  // for Apple watch simulator
491
550
  # if defined(TARGET_OS_SIMULATOR) && defined(TARGET_CPU_X86)
@@ -522,12 +581,24 @@ bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
522
581
  if (!ret.first) {
523
582
  return false;
524
583
  }
584
+ KeyValueHolderCrypt kvHolder;
525
585
  if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {
526
- KeyValueHolderCrypt kvHolder(ret.second.keySize, ret.second.valueSize, ret.second.offset);
586
+ kvHolder = KeyValueHolderCrypt(ret.second.keySize, ret.second.valueSize, ret.second.offset);
527
587
  memcpy(&kvHolder.cryptStatus, &t_status, sizeof(t_status));
528
- itr->second = move(kvHolder);
529
588
  } else {
530
- itr->second = KeyValueHolderCrypt(move(data));
589
+ kvHolder = KeyValueHolderCrypt(std::move(data));
590
+ }
591
+ if (likely(!m_enableKeyExpire)) {
592
+ itr->second = std::move(kvHolder);
593
+ } else {
594
+ itr = m_dicCrypt->find(key);
595
+ if (itr != m_dicCrypt->end()) {
596
+ itr->second = std::move(kvHolder);
597
+ } else {
598
+ // in case filterExpiredKeys() is triggered
599
+ m_dicCrypt->emplace(key, std::move(kvHolder));
600
+ retain_key(key);
601
+ }
531
602
  }
532
603
  } else {
533
604
  auto ret = appendDataWithKey(data, key, isDataHolder);
@@ -541,31 +612,45 @@ bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
541
612
  memcpy(&(r.first->second.cryptStatus), &t_status, sizeof(t_status));
542
613
  }
543
614
  } else {
544
- m_dicCrypt->emplace(key, KeyValueHolderCrypt(move(data)));
615
+ m_dicCrypt->emplace(key, KeyValueHolderCrypt(std::move(data)));
545
616
  }
617
+ retain_key(key);
546
618
  }
547
619
  } else
548
620
  #endif // MMKV_DISABLE_CRYPT
549
621
  {
550
622
  auto itr = m_dic->find(key);
551
623
  if (itr != m_dic->end()) {
552
- auto ret = appendDataWithKey(data, itr->second, isDataHolder);
553
- if (!ret.first) {
554
- return false;
624
+ if (likely(!m_enableKeyExpire)) {
625
+ auto ret = appendDataWithKey(data, itr->second, isDataHolder);
626
+ if (!ret.first) {
627
+ return false;
628
+ }
629
+ itr->second = std::move(ret.second);
630
+ } else {
631
+ auto ret = appendDataWithKey(data, key, isDataHolder);
632
+ if (!ret.first) {
633
+ return false;
634
+ }
635
+ itr = m_dic->find(key);
636
+ if (itr != m_dic->end()) {
637
+ itr->second = std::move(ret.second);
638
+ } else {
639
+ // in case filterExpiredKeys() is triggered
640
+ m_dic->emplace(key, std::move(ret.second));
641
+ retain_key(key);
642
+ }
555
643
  }
556
- itr->second = std::move(ret.second);
557
644
  } else {
558
645
  auto ret = appendDataWithKey(data, key, isDataHolder);
559
646
  if (!ret.first) {
560
647
  return false;
561
648
  }
562
649
  m_dic->emplace(key, std::move(ret.second));
650
+ retain_key(key);
563
651
  }
564
652
  }
565
653
  m_hasFullWriteback = false;
566
- #ifdef MMKV_APPLE
567
- [key retain];
568
- #endif
569
654
  return true;
570
655
  }
571
656
 
@@ -582,6 +667,13 @@ bool MMKV::removeDataForKey(MMKVKey_t key) {
582
667
  # ifdef MMKV_APPLE
583
668
  auto ret = appendDataWithKey(nan, key, itr->second);
584
669
  if (ret.first) {
670
+ if (unlikely(m_enableKeyExpire)) {
671
+ // filterExpiredKeys() may invalid itr
672
+ itr = m_dicCrypt->find(key);
673
+ if (itr == m_dicCrypt->end()) {
674
+ return true;
675
+ }
676
+ }
585
677
  auto oldKey = itr->first;
586
678
  m_dicCrypt->erase(itr);
587
679
  [oldKey release];
@@ -589,7 +681,11 @@ bool MMKV::removeDataForKey(MMKVKey_t key) {
589
681
  # else
590
682
  auto ret = appendDataWithKey(nan, key);
591
683
  if (ret.first) {
592
- m_dicCrypt->erase(itr);
684
+ if (unlikely(m_enableKeyExpire)) {
685
+ m_dicCrypt->erase(key);
686
+ } else {
687
+ m_dicCrypt->erase(itr);
688
+ }
593
689
  }
594
690
  # endif
595
691
  return ret.first;
@@ -601,14 +697,26 @@ bool MMKV::removeDataForKey(MMKVKey_t key) {
601
697
  if (itr != m_dic->end()) {
602
698
  m_hasFullWriteback = false;
603
699
  static MMBuffer nan;
604
- auto ret = appendDataWithKey(nan, itr->second);
700
+ auto ret = likely(!m_enableKeyExpire) ? appendDataWithKey(nan, itr->second) : appendDataWithKey(nan, key);
605
701
  if (ret.first) {
606
702
  #ifdef MMKV_APPLE
703
+ if (unlikely(m_enableKeyExpire)) {
704
+ // filterExpiredKeys() may invalid itr
705
+ itr = m_dic->find(key);
706
+ if (itr == m_dic->end()) {
707
+ return true;
708
+ }
709
+ }
607
710
  auto oldKey = itr->first;
608
711
  m_dic->erase(itr);
609
712
  [oldKey release];
610
713
  #else
611
- m_dic->erase(itr);
714
+ if (unlikely(m_enableKeyExpire)) {
715
+ // filterExpiredKeys() may invalid itr
716
+ m_dic->erase(key);
717
+ } else {
718
+ m_dic->erase(itr);
719
+ }
612
720
  #endif
613
721
  }
614
722
  return ret.first;
@@ -726,23 +834,28 @@ bool MMKV::fullWriteback(AESCrypt *newCrypter) {
726
834
  return false;
727
835
  }
728
836
 
837
+ if (unlikely(m_enableKeyExpire)) {
838
+ filterExpiredKeys();
839
+ }
840
+
729
841
  if (m_crypter ? m_dicCrypt->empty() : m_dic->empty()) {
730
842
  clearAll();
731
843
  return true;
732
844
  }
733
845
 
846
+ SCOPED_LOCK(m_exclusiveProcessLock);
734
847
  auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);
735
848
  auto sizeOfDic = preparedData.second;
736
- SCOPED_LOCK(m_exclusiveProcessLock);
737
849
  if (sizeOfDic > 0) {
738
850
  auto fileSize = m_file->getFileSize();
739
851
  if (sizeOfDic + Fixed32Size <= fileSize) {
740
- return doFullWriteBack(move(preparedData), newCrypter);
852
+ return doFullWriteBack(std::move(preparedData), newCrypter);
741
853
  } else {
742
854
  assert(0);
743
855
  assert(newCrypter == nullptr);
744
- // ensureMemorySize will extend file & full rewrite, no need to write back again
745
- return ensureMemorySize(sizeOfDic + Fixed32Size - fileSize);
856
+ // expandAndWriteBack() will extend file & full rewrite, no need to write back again
857
+ auto newSize = sizeOfDic + Fixed32Size - fileSize;
858
+ return expandAndWriteBack(newSize, std::move(preparedData));
746
859
  }
747
860
  }
748
861
  return false;
@@ -893,9 +1006,25 @@ static void memmoveDictionary(MMKVMapCrypt &dic,
893
1006
 
894
1007
  #endif // MMKV_DISABLE_CRYPT
895
1008
 
896
- bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> preparedData, AESCrypt *newCrypter) {
1009
+ static void fullWriteBackWholeData(MMBuffer allData, size_t totalSize, CodedOutputData *output) {
1010
+ auto originOutputPtr = output->curWritePointer();
1011
+ output->writeRawVarint32(ItemSizeHolder);
1012
+ if (allData.length() > 0) {
1013
+ auto dataSize = CodedInputData(allData.getPtr(), allData.length()).readUInt32();
1014
+ if (dataSize > 0) {
1015
+ auto dataPtr = (uint8_t *)allData.getPtr() + pbRawVarint32Size(dataSize);
1016
+ memcpy(output->curWritePointer(), dataPtr, dataSize);
1017
+ output->seek(dataSize);
1018
+ }
1019
+ }
1020
+ [[maybe_unused]] auto writtenSize = (size_t) (output->curWritePointer() - originOutputPtr);
1021
+ assert(writtenSize == totalSize);
1022
+ }
1023
+
1024
+ #ifndef MMKV_DISABLE_CRYPT
1025
+ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *newCrypter) {
897
1026
  auto ptr = (uint8_t *) m_file->getMemory();
898
- auto totalSize = preparedData.second;
1027
+ auto totalSize = prepared.second;
899
1028
  #ifdef MMKV_IOS
900
1029
  auto ret = guardForBackgroundWriting(ptr + Fixed32Size, totalSize);
901
1030
  if (!ret.first) {
@@ -903,36 +1032,32 @@ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> preparedData, AESCrypt *newCry
903
1032
  }
904
1033
  #endif
905
1034
 
906
- #ifndef MMKV_DISABLE_CRYPT
907
1035
  uint8_t newIV[AES_KEY_LEN];
908
- auto decrypter = m_crypter;
909
1036
  auto encrypter = (newCrypter == InvalidCryptPtr) ? nullptr : (newCrypter ? newCrypter : m_crypter);
910
1037
  if (encrypter) {
911
1038
  AESCrypt::fillRandomIV(newIV);
912
1039
  encrypter->resetIV(newIV, sizeof(newIV));
913
1040
  }
914
- #endif
915
1041
 
916
1042
  delete m_output;
917
1043
  m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
918
- #ifndef MMKV_DISABLE_CRYPT
919
1044
  if (m_crypter) {
920
- memmoveDictionary(*m_dicCrypt, m_output, ptr, decrypter, encrypter, preparedData);
1045
+ auto decrypter = m_crypter;
1046
+ memmoveDictionary(*m_dicCrypt, m_output, ptr, decrypter, encrypter, prepared);
1047
+ } else if (prepared.first.length() != 0) {
1048
+ auto &preparedData = prepared.first;
1049
+ fullWriteBackWholeData(std::move(preparedData), totalSize, m_output);
1050
+ if (encrypter) {
1051
+ encrypter->encrypt(ptr + Fixed32Size, ptr + Fixed32Size, totalSize);
1052
+ }
921
1053
  } else {
922
- #else
923
- {
924
- auto encrypter = m_crypter;
925
- #endif
926
1054
  memmoveDictionary(*m_dic, m_output, ptr, encrypter, totalSize);
927
1055
  }
928
1056
 
929
1057
  m_actualSize = totalSize;
930
- #ifndef MMKV_DISABLE_CRYPT
931
1058
  if (encrypter) {
932
1059
  recaculateCRCDigestWithIV(newIV);
933
- } else
934
- #endif
935
- {
1060
+ } else {
936
1061
  recaculateCRCDigestWithIV(nullptr);
937
1062
  }
938
1063
  m_hasFullWriteback = true;
@@ -941,9 +1066,41 @@ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> preparedData, AESCrypt *newCry
941
1066
  return true;
942
1067
  }
943
1068
 
1069
+ #else // MMKV_DISABLE_CRYPT
1070
+
1071
+ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *) {
1072
+ auto ptr = (uint8_t *) m_file->getMemory();
1073
+ auto totalSize = prepared.second;
1074
+ #ifdef MMKV_IOS
1075
+ auto ret = guardForBackgroundWriting(ptr + Fixed32Size, totalSize);
1076
+ if (!ret.first) {
1077
+ return false;
1078
+ }
1079
+ #endif
1080
+
1081
+ delete m_output;
1082
+ m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
1083
+ if (prepared.first.length() != 0) {
1084
+ auto &preparedData = prepared.first;
1085
+ fullWriteBackWholeData(std::move(preparedData), totalSize, m_output);
1086
+ } else {
1087
+ constexpr AESCrypt *encrypter = nullptr;
1088
+ memmoveDictionary(*m_dic, m_output, ptr, encrypter, totalSize);
1089
+ }
1090
+
1091
+ m_actualSize = totalSize;
1092
+ recaculateCRCDigestWithIV(nullptr);
1093
+ m_hasFullWriteback = true;
1094
+ // make sure lastConfirmedMetaInfo is saved
1095
+ sync(MMKV_SYNC);
1096
+ return true;
1097
+ }
1098
+ #endif // MMKV_DISABLE_CRYPT
1099
+
944
1100
  #ifndef MMKV_DISABLE_CRYPT
945
1101
  bool MMKV::reKey(const string &cryptKey) {
946
1102
  SCOPED_LOCK(m_lock);
1103
+ SCOPED_LOCK(m_exclusiveProcessLock);
947
1104
  checkLoadData();
948
1105
 
949
1106
  bool ret = false;
@@ -1118,4 +1275,256 @@ bool MMKV::isFileValid(const string &mmapID, MMKVPath_t *relatePath) {
1118
1275
  }
1119
1276
  }
1120
1277
 
1278
+ // ---- auto expire ----
1279
+
1280
+ uint32_t MMKV::getCurrentTimeInSecond() {
1281
+ auto time = ::time(nullptr);
1282
+ return static_cast<uint32_t>(time);
1283
+ }
1284
+
1285
+ bool MMKV::doFullWriteBack(MMKVVector &&vec) {
1286
+ auto preparedData = prepareEncode(std::move(vec));
1287
+
1288
+ // must clean before write-back and after prepareEncode()
1289
+ if (m_crypter) {
1290
+ clearDictionary(m_dicCrypt);
1291
+ } else {
1292
+ clearDictionary(m_dic);
1293
+ }
1294
+
1295
+ bool ret = false;
1296
+ auto sizeOfDic = preparedData.second;
1297
+ auto fileSize = m_file->getFileSize();
1298
+ if (sizeOfDic + Fixed32Size <= fileSize) {
1299
+ ret = doFullWriteBack(std::move(preparedData), nullptr);
1300
+ } else {
1301
+ // expandAndWriteBack() will extend file & full rewrite, no need to write back again
1302
+ auto newSize = sizeOfDic + Fixed32Size - fileSize;
1303
+ ret = expandAndWriteBack(newSize, std::move(preparedData));
1304
+ }
1305
+
1306
+ clearMemoryCache();
1307
+ return ret;
1308
+ }
1309
+
1310
+ bool MMKV::enableAutoKeyExpire(uint32_t expiredInSeconds) {
1311
+ SCOPED_LOCK(m_lock);
1312
+ SCOPED_LOCK(m_exclusiveProcessLock);
1313
+ checkLoadData();
1314
+
1315
+ if (m_expiredInSeconds != expiredInSeconds) {
1316
+ MMKVInfo("expiredInSeconds: %u", expiredInSeconds);
1317
+ m_expiredInSeconds = expiredInSeconds;
1318
+ }
1319
+ m_enableKeyExpire = true;
1320
+ if (m_metaInfo->hasFlag(MMKVMetaInfo::EnableKeyExipre)) {
1321
+ return true;
1322
+ }
1323
+
1324
+ auto autoRecordExpireTime = (m_expiredInSeconds != 0);
1325
+ auto time = autoRecordExpireTime ? getCurrentTimeInSecond() + m_expiredInSeconds : 0;
1326
+ MMKVInfo("turn on recording expire date for all keys inside [%s] from now %u", m_mmapID.c_str(), time);
1327
+ m_metaInfo->setFlag(MMKVMetaInfo::EnableKeyExipre);
1328
+ m_metaInfo->m_version = MMKVVersionFlag;
1329
+
1330
+ if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
1331
+ MMKVInfo("file is new, don't need a full writeback [%s], just update meta file", m_mmapID.c_str());
1332
+ writeActualSize(0, 0, nullptr, IncreaseSequence);
1333
+ m_metaFile->msync(MMKV_SYNC);
1334
+ return true;
1335
+ }
1336
+
1337
+ MMKVVector vec;
1338
+ auto packKeyValue = [&](const MMKVKey_t &key, const MMBuffer &value) {
1339
+ MMBuffer data(value.length() + Fixed32Size);
1340
+ auto ptr = (uint8_t *)data.getPtr();
1341
+ memcpy(ptr, value.getPtr(), value.length());
1342
+ memcpy(ptr + value.length(), &time, Fixed32Size);
1343
+ vec.emplace_back(key, std::move(data));
1344
+ };
1345
+
1346
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
1347
+ #ifndef MMKV_DISABLE_CRYPT
1348
+ if (m_crypter) {
1349
+ for (auto &pair : *m_dicCrypt) {
1350
+ auto &key = pair.first;
1351
+ auto &value = pair.second;
1352
+ auto buffer = value.toMMBuffer(basePtr, m_crypter);
1353
+ packKeyValue(key, buffer);
1354
+ }
1355
+ } else
1356
+ #endif
1357
+ {
1358
+ for (auto &pair : *m_dic) {
1359
+ auto &key = pair.first;
1360
+ auto &value = pair.second;
1361
+ auto buffer = value.toMMBuffer(basePtr);
1362
+ packKeyValue(key, buffer);
1363
+ }
1364
+ }
1365
+
1366
+ return doFullWriteBack(std::move(vec));
1367
+ }
1368
+
1369
+ bool MMKV::disableAutoKeyExpire() {
1370
+ SCOPED_LOCK(m_lock);
1371
+ SCOPED_LOCK(m_exclusiveProcessLock);
1372
+ checkLoadData();
1373
+
1374
+ m_expiredInSeconds = 0;
1375
+ m_enableKeyExpire = false;
1376
+ if (!m_metaInfo->hasFlag(MMKVMetaInfo::EnableKeyExipre)) {
1377
+ return true;
1378
+ }
1379
+
1380
+ MMKVInfo("erase previous recorded expire date for all keys inside [%s]", m_mmapID.c_str());
1381
+ m_metaInfo->unsetFlag(MMKVMetaInfo::EnableKeyExipre);
1382
+ m_metaInfo->m_version = MMKVVersionFlag;
1383
+
1384
+ if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
1385
+ MMKVInfo("file is new, don't need a full write-back [%s], just update meta file", m_mmapID.c_str());
1386
+ writeActualSize(0, 0, nullptr, IncreaseSequence);
1387
+ m_metaFile->msync(MMKV_SYNC);
1388
+ return true;
1389
+ }
1390
+
1391
+ MMKVVector vec;
1392
+ auto packKeyValue = [&](const MMKVKey_t &key, const MMBuffer &value) {
1393
+ assert(value.length() >= Fixed32Size);
1394
+ MMBuffer data(value.length() - Fixed32Size);
1395
+ auto ptr = (uint8_t *)data.getPtr();
1396
+ memcpy(ptr, value.getPtr(), value.length() - Fixed32Size);
1397
+ vec.emplace_back(key, std::move(data));
1398
+ };
1399
+
1400
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
1401
+ #ifndef MMKV_DISABLE_CRYPT
1402
+ if (m_crypter) {
1403
+ for (auto &pair : *m_dicCrypt) {
1404
+ auto &key = pair.first;
1405
+ auto &value = pair.second;
1406
+ auto buffer = value.toMMBuffer(basePtr, m_crypter);
1407
+ packKeyValue(key, buffer);
1408
+ }
1409
+ } else
1410
+ #endif
1411
+ {
1412
+ for (auto &pair : *m_dic) {
1413
+ auto &key = pair.first;
1414
+ auto &value = pair.second;
1415
+ auto buffer = value.toMMBuffer(basePtr);
1416
+ packKeyValue(key, buffer);
1417
+ }
1418
+ }
1419
+
1420
+ return doFullWriteBack(std::move(vec));
1421
+ }
1422
+
1423
+ uint32_t MMKV::getExpireTimeForKey(MMKVKey_t key) {
1424
+ SCOPED_LOCK(m_lock);
1425
+ SCOPED_LOCK(m_sharedProcessLock);
1426
+ checkLoadData();
1427
+
1428
+ if (!m_enableKeyExpire || key_length(key) == 0) {
1429
+ return 0;
1430
+ }
1431
+ auto raw = getRawDataForKey(key);
1432
+ assert(raw.length() == 0 || raw.length() >= Fixed32Size);
1433
+ if (raw.length() < Fixed32Size) {
1434
+ return 0;
1435
+ }
1436
+ auto ptr = (const uint8_t *)raw.getPtr() + raw.length() - Fixed32Size;
1437
+ auto time = *(const uint32_t *)ptr;
1438
+ return time;
1439
+ }
1440
+
1441
+ mmkv::MMBuffer MMKV::getDataWithoutMTimeForKey(MMKVKey_t key) {
1442
+ SCOPED_LOCK(m_lock);
1443
+ SCOPED_LOCK(m_sharedProcessLock);
1444
+ checkLoadData();
1445
+
1446
+ auto raw = getRawDataForKey(key);
1447
+ assert(raw.length() == 0 || raw.length() >= Fixed32Size);
1448
+ if (raw.length() < Fixed32Size) {
1449
+ return raw;
1450
+ }
1451
+ auto newLength = raw.length() - Fixed32Size;
1452
+ if (m_enableKeyExpire) {
1453
+ auto ptr = (const uint8_t *)raw.getPtr() + newLength;
1454
+ auto time = *(const uint32_t *)ptr;
1455
+ if (time != ExpireNever && time <= getCurrentTimeInSecond()) {
1456
+ #ifdef MMKV_APPLE
1457
+ MMKVInfo("deleting expired key [%@] in mmkv [%s], due date %u", key, m_mmapID.c_str(), time);
1458
+ #else
1459
+ MMKVInfo("deleting expired key [%s] in mmkv [%s], due date %u", key.c_str(), m_mmapID.c_str(), time);
1460
+ #endif
1461
+ removeValueForKey(key);
1462
+ return MMBuffer();
1463
+ }
1464
+ }
1465
+ return MMBuffer(std::move(raw), newLength);
1466
+ }
1467
+
1468
+ #define NOOP ((void)0)
1469
+
1470
+ size_t MMKV::filterExpiredKeys() {
1471
+ if (!m_enableKeyExpire || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {
1472
+ return 0;
1473
+ }
1474
+ auto now = getCurrentTimeInSecond();
1475
+ MMKVInfo("filtering expired keys inside [%s] now: %u, m_expiredInSeconds: %u", m_mmapID.c_str(), now, m_expiredInSeconds);
1476
+
1477
+ size_t count = 0;
1478
+ auto basePtr = (uint8_t *)(m_file->getMemory()) + Fixed32Size;
1479
+ #ifndef MMKV_DISABLE_CRYPT
1480
+ if (m_crypter) {
1481
+ for (auto itr = m_dicCrypt->begin(); itr != m_dicCrypt->end(); NOOP) {
1482
+ auto &kvHolder = itr->second;
1483
+ assert(kvHolder.realValueSize() >= Fixed32Size);
1484
+ auto buffer = kvHolder.toMMBuffer(basePtr, m_crypter);
1485
+ auto ptr = (uint8_t*) buffer.getPtr();
1486
+ ptr += buffer.length() - Fixed32Size;
1487
+ auto time = *(const uint32_t *)ptr;
1488
+ if (time != ExpireNever && time <= now) {
1489
+ auto oldKey = itr->first;
1490
+ itr = m_dicCrypt->erase(itr);
1491
+ #ifdef MMKV_APPLE
1492
+ MMKVInfo("deleting expired key [%@], due date %u", oldKey, time);
1493
+ [oldKey release];
1494
+ #else
1495
+ MMKVInfo("deleting expired key [%s], due date %u", oldKey.c_str(), time);
1496
+ #endif
1497
+ count++;
1498
+ } else {
1499
+ itr++;
1500
+ }
1501
+ }
1502
+ } else
1503
+ #endif // !MMKV_DISABLE_CRYPT
1504
+ {
1505
+ for (auto itr = m_dic->begin(); itr != m_dic->end(); NOOP) {
1506
+ auto &kvHolder = itr->second;
1507
+ assert(kvHolder.valueSize >= Fixed32Size);
1508
+ auto ptr = basePtr + kvHolder.offset + kvHolder.computedKVSize;
1509
+ ptr += kvHolder.valueSize - Fixed32Size;
1510
+ auto time = *(const uint32_t *)ptr;
1511
+ if (time != ExpireNever && time <= now) {
1512
+ auto oldKey = itr->first;
1513
+ itr = m_dic->erase(itr);
1514
+ #ifdef MMKV_APPLE
1515
+ MMKVInfo("deleting expired key [%@], due date %u", oldKey, time);
1516
+ [oldKey release];
1517
+ #else
1518
+ MMKVInfo("deleting expired key [%s], due date %u", oldKey.c_str(), time);
1519
+ #endif
1520
+ count++;
1521
+ } else {
1522
+ itr++;
1523
+ }
1524
+ }
1525
+ }
1526
+ MMKVInfo("deleted %zu expired keys inside [%s]", count, m_mmapID.c_str());
1527
+ return count;
1528
+ }
1529
+
1121
1530
  MMKV_NAMESPACE_END