react-native-mmkv 2.10.2 → 2.12.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/Core/CodedInputData.cpp +28 -4
  2. package/MMKV/Core/CodedInputData.h +5 -1
  3. package/MMKV/Core/CodedOutputData.cpp +12 -0
  4. package/MMKV/Core/CodedOutputData.h +6 -0
  5. package/MMKV/Core/MMBuffer.cpp +7 -0
  6. package/MMKV/Core/MMBuffer.h +3 -0
  7. package/MMKV/Core/MMKV.cpp +114 -19
  8. package/MMKV/Core/MMKV.h +69 -33
  9. package/MMKV/Core/MMKVPredef.h +1 -1
  10. package/MMKV/Core/MMKV_Android.cpp +10 -8
  11. package/MMKV/Core/MMKV_IO.cpp +386 -60
  12. package/MMKV/Core/MMKV_OSX.cpp +51 -30
  13. package/MMKV/Core/MMKV_OSX.h +10 -4
  14. package/MMKV/Core/MemoryFile.cpp +31 -11
  15. package/MMKV/Core/MemoryFile.h +8 -3
  16. package/MMKV/Core/MemoryFile_Android.cpp +2 -2
  17. package/MMKV/Core/MemoryFile_OSX.cpp +2 -3
  18. package/MMKV/Core/MemoryFile_Win32.cpp +8 -5
  19. package/MMKV/Core/MiniPBCoder.cpp +11 -0
  20. package/MMKV/Core/MiniPBCoder.h +3 -0
  21. package/MMKV/Core/MiniPBCoder_OSX.cpp +8 -0
  22. package/MMKV/Core/ThreadLock.cpp +5 -0
  23. package/MMKV/Core/ThreadLock.h +4 -0
  24. package/MMKV/Core/aes/openssl/openssl_aes-armv4.S +8 -0
  25. package/MMKV/Core/core.vcxproj +3 -3
  26. package/MMKV/README.md +3 -3
  27. package/README.md +9 -1
  28. package/android/build.gradle +21 -1
  29. package/android/src/hasNamespace/AndroidManifest.xml +3 -0
  30. package/android/src/main/cpp/MmkvHostObject.cpp +191 -204
  31. package/android/src/main/cpp/MmkvHostObject.h +2 -2
  32. package/android/src/main/cpp/cpp-adapter.cpp +62 -49
  33. package/cpp/TypedArray.cpp +69 -76
  34. package/cpp/TypedArray.h +66 -91
  35. package/img/banner-dark.png +0 -0
  36. package/img/banner-light.png +0 -0
  37. package/ios/JSIUtils.h +27 -39
  38. package/ios/JSIUtils.mm +110 -138
  39. package/ios/MmkvHostObject.h +2 -2
  40. package/ios/MmkvHostObject.mm +188 -205
  41. package/ios/MmkvModule.mm +68 -52
  42. package/lib/commonjs/createMMKV.web.js +1 -1
  43. package/lib/commonjs/createMMKV.web.js.map +1 -1
  44. package/lib/commonjs/hooks.js +16 -13
  45. package/lib/commonjs/hooks.js.map +1 -1
  46. package/lib/module/createMMKV.web.js +1 -1
  47. package/lib/module/createMMKV.web.js.map +1 -1
  48. package/lib/module/hooks.js +16 -13
  49. package/lib/module/hooks.js.map +1 -1
  50. package/lib/typescript/createMMKV.web.d.ts.map +1 -1
  51. package/lib/typescript/hooks.d.ts.map +1 -1
  52. package/package.json +13 -3
  53. package/react-native-mmkv.podspec +1 -1
  54. package/src/createMMKV.web.ts +4 -2
  55. package/src/hooks.ts +19 -16
  56. package/MMKV/LICENSE.TXT +0 -193
@@ -52,6 +52,8 @@
52
52
  using namespace std;
53
53
  using namespace mmkv;
54
54
  using KVHolderRet_t = std::pair<bool, KeyValueHolder>;
55
+ extern ThreadLock *g_instanceLock;
56
+ extern unordered_map<string, MMKV *> *g_instanceDic;
55
57
 
56
58
  MMKV_NAMESPACE_BEGIN
57
59
 
@@ -65,7 +67,7 @@ void MMKV::loadFromFile() {
65
67
  }
66
68
  #endif
67
69
  if (!m_file->isFileValid()) {
68
- m_file->reloadFromFile();
70
+ m_file->reloadFromFile(m_expectedCapacity);
69
71
  }
70
72
  if (!m_file->isFileValid()) {
71
73
  MMKVError("file [%s] not valid", m_path.c_str());
@@ -132,6 +134,9 @@ void MMKV::loadFromFile() {
132
134
 
133
135
  // read from last m_position
134
136
  void MMKV::partialLoadFromFile() {
137
+ if (!m_file->isFileValid()) {
138
+ return;
139
+ }
135
140
  m_metaInfo->read(m_metaFile->getMemory());
136
141
 
137
142
  size_t oldActualSize = m_actualSize;
@@ -187,7 +192,8 @@ void MMKV::loadMetaInfoAndCheck() {
187
192
  m_metaInfo->read(m_metaFile->getMemory());
188
193
  // the meta file is in specious status
189
194
  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);
195
+ MMKVWarning("meta file [%s] in specious state, version %u, flags 0x%llx", m_mmapID.c_str(),
196
+ m_metaInfo->m_version, m_metaInfo->m_flags);
191
197
 
192
198
  // MMKVVersionActualSize is the last version we don't check meta file
193
199
  m_metaInfo->m_version = MMKVVersionActualSize;
@@ -197,6 +203,10 @@ void MMKV::loadMetaInfoAndCheck() {
197
203
 
198
204
  if (m_metaInfo->m_version >= MMKVVersionFlag) {
199
205
  m_enableKeyExpire = m_metaInfo->hasFlag(MMKVMetaInfo::EnableKeyExipre);
206
+ if (m_enableKeyExpire && m_enableCompareBeforeSet) {
207
+ MMKVError("enableCompareBeforeSet will be invalid when Expiration is on");
208
+ m_enableCompareBeforeSet = false;
209
+ }
200
210
  MMKVInfo("meta file [%s] has flag [%llu]", m_mmapID.c_str(), m_metaInfo->m_flags);
201
211
  } else {
202
212
  if (m_metaInfo->m_flags != 0) {
@@ -253,8 +263,8 @@ void MMKV::checkDataValid(bool &loadFromFile, bool &needFullWriteback) {
253
263
  loadFromFile = true;
254
264
  } else {
255
265
  checkLastConfirmedInfo();
256
-
257
266
  if (!loadFromFile) {
267
+
258
268
  auto strategic = onMMKVCRCCheckFail(m_mmapID);
259
269
  if (strategic == OnErrorRecover) {
260
270
  loadFromFile = true;
@@ -392,22 +402,25 @@ bool MMKV::ensureMemorySize(size_t newSize) {
392
402
  }
393
403
  // try a full rewrite to make space
394
404
  auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);
395
- return expandAndWriteBack(newSize, std::move(preparedData));
405
+ // dic.empty() means inserting key-value for the first time, no need to call msync()
406
+ return expandAndWriteBack(newSize, std::move(preparedData), m_crypter ? !m_dicCrypt->empty() : !m_dic->empty());
396
407
  }
397
408
  return true;
398
409
  }
399
410
 
400
411
  // try a full rewrite to make space
401
- bool MMKV::expandAndWriteBack(size_t newSize, std::pair<mmkv::MMBuffer, size_t> preparedData) {
412
+ bool MMKV::expandAndWriteBack(size_t newSize, std::pair<mmkv::MMBuffer, size_t> preparedData, bool needSync) {
402
413
  auto fileSize = m_file->getFileSize();
403
414
  auto sizeOfDic = preparedData.second;
404
415
  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);
416
+ size_t nowDicCount = m_crypter ? m_dicCrypt->size() : m_dic->size();
417
+ size_t laterDicCount = std::max<size_t>(1, nowDicCount + 1);
418
+ // or use <cmath> ceil()
419
+ size_t avgItemSize = (lenNeeded + laterDicCount - 1) / laterDicCount;
420
+ size_t futureUsage = avgItemSize * std::max<size_t>(8, laterDicCount / 2);
408
421
  // 1. no space for a full rewrite, double it
409
422
  // 2. or space is not large enough for future usage, double it to avoid frequently full rewrite
410
- if (lenNeeded >= fileSize || (lenNeeded + futureUsage) >= fileSize) {
423
+ if (lenNeeded >= fileSize || (needSync && (lenNeeded + futureUsage) >= fileSize)) {
411
424
  size_t oldSize = fileSize;
412
425
  do {
413
426
  fileSize *= 2;
@@ -416,6 +429,7 @@ bool MMKV::expandAndWriteBack(size_t newSize, std::pair<mmkv::MMBuffer, size_t>
416
429
  oldSize, fileSize, newSize, futureUsage);
417
430
 
418
431
  // if we can't extend size, rollback to old state
432
+ // this is a good place to mock enlarging file failure
419
433
  if (!m_file->truncate(fileSize)) {
420
434
  return false;
421
435
  }
@@ -426,7 +440,7 @@ bool MMKV::expandAndWriteBack(size_t newSize, std::pair<mmkv::MMBuffer, size_t>
426
440
  return false;
427
441
  }
428
442
  }
429
- return doFullWriteBack(std::move(preparedData), nullptr);
443
+ return doFullWriteBack(std::move(preparedData), nullptr, needSync);
430
444
  }
431
445
 
432
446
  size_t MMKV::readActualSize() {
@@ -573,10 +587,21 @@ bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
573
587
  }
574
588
  auto itr = m_dicCrypt->find(key);
575
589
  if (itr != m_dicCrypt->end()) {
590
+ bool onlyOneKey = !m_isInterProcess && m_dicCrypt->size() == 1;
576
591
  # ifdef MMKV_APPLE
577
- auto ret = appendDataWithKey(data, key, itr->second, isDataHolder);
592
+ KVHolderRet_t ret;
593
+ if (onlyOneKey) {
594
+ ret = overrideDataWithKey(data, key, itr->second, isDataHolder);
595
+ } else {
596
+ ret = appendDataWithKey(data, key, itr->second, isDataHolder);
597
+ }
578
598
  # else
579
- auto ret = appendDataWithKey(data, key, isDataHolder);
599
+ KVHolderRet_t ret;
600
+ if (onlyOneKey) {
601
+ ret = overrideDataWithKey(data, key, isDataHolder);
602
+ } else {
603
+ ret = appendDataWithKey(data, key, isDataHolder);
604
+ }
580
605
  # endif
581
606
  if (!ret.first) {
582
607
  return false;
@@ -597,11 +622,17 @@ bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
597
622
  } else {
598
623
  // in case filterExpiredKeys() is triggered
599
624
  m_dicCrypt->emplace(key, std::move(kvHolder));
600
- retain_key(key);
625
+ mmkv_retain_key(key);
601
626
  }
602
627
  }
603
628
  } else {
604
- auto ret = appendDataWithKey(data, key, isDataHolder);
629
+ bool needOverride = !m_isInterProcess && m_dicCrypt->empty() && m_actualSize > 0;
630
+ KVHolderRet_t ret;
631
+ if (needOverride) {
632
+ ret = overrideDataWithKey(data, key, isDataHolder);
633
+ } else {
634
+ ret = appendDataWithKey(data, key, isDataHolder);
635
+ }
605
636
  if (!ret.first) {
606
637
  return false;
607
638
  }
@@ -614,21 +645,58 @@ bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
614
645
  } else {
615
646
  m_dicCrypt->emplace(key, KeyValueHolderCrypt(std::move(data)));
616
647
  }
617
- retain_key(key);
648
+ mmkv_retain_key(key);
618
649
  }
619
650
  } else
620
651
  #endif // MMKV_DISABLE_CRYPT
621
652
  {
622
653
  auto itr = m_dic->find(key);
623
654
  if (itr != m_dic->end()) {
655
+ // compare data before appending to file
656
+ if (isCompareBeforeSetEnabled()) {
657
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
658
+ MMBuffer oldValueData = itr->second.toMMBuffer(basePtr);
659
+ if (isDataHolder) {
660
+ CodedInputData inputData(oldValueData.getPtr(), oldValueData.length());
661
+ try {
662
+ // read extra holder header bytes and to real MMBuffer
663
+ oldValueData = CodedInputData::readRealData(oldValueData);
664
+ if (oldValueData == data) {
665
+ // MMKVInfo("[key] %s, set the same data", key.c_str());
666
+ return true;
667
+ }
668
+ } catch (std::exception &exception) {
669
+ MMKVError("compareBeforeSet exception: %s", exception.what());
670
+ } catch (...) {
671
+ MMKVError("compareBeforeSet fail");
672
+ }
673
+ } else {
674
+ if (oldValueData == data) {
675
+ // MMKVInfo("[key] %s, set the same data", key.c_str());
676
+ return true;
677
+ }
678
+ }
679
+ }
680
+
681
+ bool onlyOneKey = !m_isInterProcess && m_dic->size() == 1;
624
682
  if (likely(!m_enableKeyExpire)) {
625
- auto ret = appendDataWithKey(data, itr->second, isDataHolder);
683
+ KVHolderRet_t ret;
684
+ if (onlyOneKey) {
685
+ ret = overrideDataWithKey(data, itr->second, isDataHolder);
686
+ } else {
687
+ ret = appendDataWithKey(data, itr->second, isDataHolder);
688
+ }
626
689
  if (!ret.first) {
627
690
  return false;
628
691
  }
629
692
  itr->second = std::move(ret.second);
630
693
  } else {
631
- auto ret = appendDataWithKey(data, key, isDataHolder);
694
+ KVHolderRet_t ret;
695
+ if (onlyOneKey) {
696
+ ret = overrideDataWithKey(data, key, isDataHolder);
697
+ } else {
698
+ ret = appendDataWithKey(data, key, isDataHolder);
699
+ }
632
700
  if (!ret.first) {
633
701
  return false;
634
702
  }
@@ -638,16 +706,22 @@ bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
638
706
  } else {
639
707
  // in case filterExpiredKeys() is triggered
640
708
  m_dic->emplace(key, std::move(ret.second));
641
- retain_key(key);
709
+ mmkv_retain_key(key);
642
710
  }
643
711
  }
644
712
  } else {
645
- auto ret = appendDataWithKey(data, key, isDataHolder);
713
+ bool needOverride = !m_isInterProcess && m_dic->empty() && m_actualSize > 0;
714
+ KVHolderRet_t ret;
715
+ if (needOverride) {
716
+ ret = overrideDataWithKey(data, key, isDataHolder);
717
+ } else {
718
+ ret = appendDataWithKey(data, key, isDataHolder);
719
+ }
646
720
  if (!ret.first) {
647
721
  return false;
648
722
  }
649
723
  m_dic->emplace(key, std::move(ret.second));
650
- retain_key(key);
724
+ mmkv_retain_key(key);
651
725
  }
652
726
  }
653
727
  m_hasFullWriteback = false;
@@ -772,6 +846,9 @@ MMKV::doAppendDataWithKey(const MMBuffer &data, const MMBuffer &keyData, bool is
772
846
  } catch (std::exception &e) {
773
847
  MMKVError("%s", e.what());
774
848
  return make_pair(false, KeyValueHolder());
849
+ } catch (...) {
850
+ MMKVError("append fail");
851
+ return make_pair(false, KeyValueHolder());
775
852
  }
776
853
 
777
854
  auto offset = static_cast<uint32_t>(m_actualSize);
@@ -787,6 +864,97 @@ MMKV::doAppendDataWithKey(const MMBuffer &data, const MMBuffer &keyData, bool is
787
864
  return make_pair(true, KeyValueHolder(originKeyLength, valueLength, offset));
788
865
  }
789
866
 
867
+ KVHolderRet_t MMKV::doOverrideDataWithKey(const MMBuffer &data,
868
+ const MMBuffer &keyData,
869
+ bool isDataHolder,
870
+ uint32_t originKeyLength) {
871
+ auto isKeyEncoded = (originKeyLength < keyData.length());
872
+ auto keyLength = static_cast<uint32_t>(keyData.length());
873
+ auto valueLength = static_cast<uint32_t>(data.length());
874
+ if (isDataHolder) {
875
+ valueLength += pbRawVarint32Size(valueLength);
876
+ }
877
+ // size needed to encode the key
878
+ size_t size = isKeyEncoded ? keyLength : (keyLength + pbRawVarint32Size(keyLength));
879
+ // size needed to encode the value
880
+ size += valueLength + pbRawVarint32Size(valueLength);
881
+
882
+ if (!checkSizeForOverride(size)) {
883
+ return doAppendDataWithKey(data, keyData, isDataHolder, originKeyLength);
884
+ }
885
+
886
+ // we don't not support override in multi-process mode
887
+ // SCOPED_LOCK(m_exclusiveProcessLock);
888
+
889
+ #ifdef MMKV_IOS
890
+ auto ret = guardForBackgroundWriting(m_output->curWritePointer(), size);
891
+ if (!ret.first) {
892
+ return make_pair(false, KeyValueHolder());
893
+ }
894
+ #endif
895
+ #ifndef MMKV_DISABLE_CRYPT
896
+ if (m_crypter) {
897
+ if (m_metaInfo->m_version >= MMKVVersionRandomIV) {
898
+ m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));
899
+ } else {
900
+ m_crypter->resetIV();
901
+ }
902
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(valueLength)) {
903
+ m_crypter->getCurStatus(t_status);
904
+ }
905
+ }
906
+ #endif
907
+ try {
908
+ // write ItemSizeHolder
909
+ m_output->setPosition(0);
910
+ m_output->writeRawVarint32(ItemSizeHolder);
911
+ m_actualSize = ItemSizeHolderSize;
912
+
913
+ if (isKeyEncoded) {
914
+ m_output->writeRawData(keyData);
915
+ } else {
916
+ m_output->writeData(keyData);
917
+ }
918
+ if (isDataHolder) {
919
+ m_output->writeRawVarint32((int32_t) valueLength);
920
+ }
921
+ m_output->writeData(data); // note: write size of data
922
+ } catch (std::exception &e) {
923
+ MMKVError("%s", e.what());
924
+ return make_pair(false, KeyValueHolder());
925
+ } catch (...) {
926
+ MMKVError("append fail");
927
+ return make_pair(false, KeyValueHolder());
928
+ }
929
+
930
+ auto offset = static_cast<uint32_t>(m_actualSize);
931
+ m_actualSize += size;
932
+ auto ptr = (uint8_t *) m_file->getMemory() + Fixed32Size;
933
+ #ifndef MMKV_DISABLE_CRYPT
934
+ if (m_crypter) {
935
+ m_crypter->encrypt(ptr, ptr, m_actualSize);
936
+ }
937
+ #endif
938
+ recaculateCRCDigestOnly();
939
+
940
+ return make_pair(true, KeyValueHolder(originKeyLength, valueLength, offset));
941
+ }
942
+
943
+ bool MMKV::checkSizeForOverride(size_t size) {
944
+ if (!isFileValid()) {
945
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
946
+ return false;
947
+ }
948
+
949
+ // only override if the file can hole it without ftruncate()
950
+ auto fileSize = m_file->getFileSize();
951
+ auto spaceNeededForOverride = size + Fixed32Size + ItemSizeHolderSize;
952
+ if (size > fileSize || spaceNeededForOverride > fileSize) {
953
+ return false;
954
+ }
955
+ return true;
956
+ }
957
+
790
958
  KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, bool isDataHolder) {
791
959
  #ifdef MMKV_APPLE
792
960
  auto oData = [key dataUsingEncoding:NSUTF8StringEncoding];
@@ -797,6 +965,16 @@ KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, bool
797
965
  return doAppendDataWithKey(data, keyData, isDataHolder, static_cast<uint32_t>(keyData.length()));
798
966
  }
799
967
 
968
+ KVHolderRet_t MMKV::overrideDataWithKey(const MMBuffer &data, MMKVKey_t key, bool isDataHolder) {
969
+ #ifdef MMKV_APPLE
970
+ auto oData = [key dataUsingEncoding:NSUTF8StringEncoding];
971
+ auto keyData = MMBuffer(oData, MMBufferNoCopy);
972
+ #else
973
+ auto keyData = MMBuffer((void *) key.data(), key.size(), MMBufferNoCopy);
974
+ #endif
975
+ return doOverrideDataWithKey(data, keyData, isDataHolder, static_cast<uint32_t>(keyData.length()));
976
+ }
977
+
800
978
  KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, const KeyValueHolder &kvHolder, bool isDataHolder) {
801
979
  SCOPED_LOCK(m_exclusiveProcessLock);
802
980
 
@@ -822,7 +1000,35 @@ KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, const KeyValueHolder
822
1000
  return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);
823
1001
  }
824
1002
 
825
- bool MMKV::fullWriteback(AESCrypt *newCrypter) {
1003
+ // only one key in dict, do not append, just rewrite from beginning
1004
+ KVHolderRet_t MMKV::overrideDataWithKey(const MMBuffer &data, const KeyValueHolder &kvHolder, bool isDataHolder) {
1005
+ // we don't not support override in multi-process mode
1006
+ // SCOPED_LOCK(m_exclusiveProcessLock);
1007
+
1008
+ uint32_t keyLength = kvHolder.keySize;
1009
+ // size needed to encode the key
1010
+ size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);
1011
+
1012
+ // ensureMemorySize() (inside doAppendDataWithKey() which be called from doOverrideDataWithKey())
1013
+ // might change kvHolder.offset, so have to do it early
1014
+ {
1015
+ auto valueLength = static_cast<uint32_t>(data.length());
1016
+ if (isDataHolder) {
1017
+ valueLength += pbRawVarint32Size(valueLength);
1018
+ }
1019
+ auto size = rawKeySize + valueLength + pbRawVarint32Size(valueLength);
1020
+ bool hasEnoughSize = checkSizeForOverride(size);
1021
+ if (!hasEnoughSize) {
1022
+ return appendDataWithKey(data, kvHolder, isDataHolder);
1023
+ }
1024
+ }
1025
+ auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;
1026
+ MMBuffer keyData(basePtr + kvHolder.offset, rawKeySize, MMBufferNoCopy);
1027
+
1028
+ return doOverrideDataWithKey(data, keyData, isDataHolder, keyLength);
1029
+ }
1030
+
1031
+ bool MMKV::fullWriteback(AESCrypt *newCrypter, bool onlyWhileExpire) {
826
1032
  if (m_hasFullWriteback) {
827
1033
  return true;
828
1034
  }
@@ -835,10 +1041,14 @@ bool MMKV::fullWriteback(AESCrypt *newCrypter) {
835
1041
  }
836
1042
 
837
1043
  if (unlikely(m_enableKeyExpire)) {
838
- filterExpiredKeys();
1044
+ auto expiredCount = filterExpiredKeys();
1045
+ if (onlyWhileExpire && expiredCount == 0) {
1046
+ return true;
1047
+ }
839
1048
  }
840
1049
 
841
- if (m_crypter ? m_dicCrypt->empty() : m_dic->empty()) {
1050
+ auto isEmpty = m_crypter ? m_dicCrypt->empty() : m_dic->empty();
1051
+ if (isEmpty) {
842
1052
  clearAll();
843
1053
  return true;
844
1054
  }
@@ -1012,25 +1222,25 @@ static void fullWriteBackWholeData(MMBuffer allData, size_t totalSize, CodedOutp
1012
1222
  if (allData.length() > 0) {
1013
1223
  auto dataSize = CodedInputData(allData.getPtr(), allData.length()).readUInt32();
1014
1224
  if (dataSize > 0) {
1015
- auto dataPtr = (uint8_t *)allData.getPtr() + pbRawVarint32Size(dataSize);
1225
+ auto dataPtr = (uint8_t *) allData.getPtr() + pbRawVarint32Size(dataSize);
1016
1226
  memcpy(output->curWritePointer(), dataPtr, dataSize);
1017
1227
  output->seek(dataSize);
1018
1228
  }
1019
1229
  }
1020
- [[maybe_unused]] auto writtenSize = (size_t) (output->curWritePointer() - originOutputPtr);
1230
+ [[maybe_unused]] auto writtenSize = (size_t)(output->curWritePointer() - originOutputPtr);
1021
1231
  assert(writtenSize == totalSize);
1022
1232
  }
1023
1233
 
1024
1234
  #ifndef MMKV_DISABLE_CRYPT
1025
- bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *newCrypter) {
1235
+ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *newCrypter, bool needSync) {
1026
1236
  auto ptr = (uint8_t *) m_file->getMemory();
1027
1237
  auto totalSize = prepared.second;
1028
- #ifdef MMKV_IOS
1238
+ # ifdef MMKV_IOS
1029
1239
  auto ret = guardForBackgroundWriting(ptr + Fixed32Size, totalSize);
1030
1240
  if (!ret.first) {
1031
1241
  return false;
1032
1242
  }
1033
- #endif
1243
+ # endif
1034
1244
 
1035
1245
  uint8_t newIV[AES_KEY_LEN];
1036
1246
  auto encrypter = (newCrypter == InvalidCryptPtr) ? nullptr : (newCrypter ? newCrypter : m_crypter);
@@ -1061,22 +1271,24 @@ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *newCrypter
1061
1271
  recaculateCRCDigestWithIV(nullptr);
1062
1272
  }
1063
1273
  m_hasFullWriteback = true;
1064
- // make sure lastConfirmedMetaInfo is saved
1065
- sync(MMKV_SYNC);
1274
+ // make sure lastConfirmedMetaInfo is saved if needed
1275
+ if (needSync) {
1276
+ sync(MMKV_SYNC);
1277
+ }
1066
1278
  return true;
1067
1279
  }
1068
1280
 
1069
1281
  #else // MMKV_DISABLE_CRYPT
1070
1282
 
1071
- bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *) {
1283
+ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *, bool needSync) {
1072
1284
  auto ptr = (uint8_t *) m_file->getMemory();
1073
1285
  auto totalSize = prepared.second;
1074
- #ifdef MMKV_IOS
1286
+ # ifdef MMKV_IOS
1075
1287
  auto ret = guardForBackgroundWriting(ptr + Fixed32Size, totalSize);
1076
1288
  if (!ret.first) {
1077
1289
  return false;
1078
1290
  }
1079
- #endif
1291
+ # endif
1080
1292
 
1081
1293
  delete m_output;
1082
1294
  m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
@@ -1091,8 +1303,10 @@ bool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *) {
1091
1303
  m_actualSize = totalSize;
1092
1304
  recaculateCRCDigestWithIV(nullptr);
1093
1305
  m_hasFullWriteback = true;
1094
- // make sure lastConfirmedMetaInfo is saved
1095
- sync(MMKV_SYNC);
1306
+ // make sure lastConfirmedMetaInfo is saved if needed
1307
+ if (needSync) {
1308
+ sync(MMKV_SYNC);
1309
+ }
1096
1310
  return true;
1097
1311
  }
1098
1312
  #endif // MMKV_DISABLE_CRYPT
@@ -1102,6 +1316,10 @@ bool MMKV::reKey(const string &cryptKey) {
1102
1316
  SCOPED_LOCK(m_lock);
1103
1317
  SCOPED_LOCK(m_exclusiveProcessLock);
1104
1318
  checkLoadData();
1319
+ if (!isFileValid()) {
1320
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
1321
+ return false;
1322
+ }
1105
1323
 
1106
1324
  bool ret = false;
1107
1325
  if (m_crypter) {
@@ -1167,11 +1385,15 @@ void MMKV::trim() {
1167
1385
  MMKVInfo("prepare to trim %s", m_mmapID.c_str());
1168
1386
 
1169
1387
  checkLoadData();
1388
+ if (!isFileValid()) {
1389
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
1390
+ return;
1391
+ }
1170
1392
 
1171
1393
  if (m_actualSize == 0) {
1172
1394
  clearAll();
1173
1395
  return;
1174
- } else if (m_file->getFileSize() <= DEFAULT_MMAP_SIZE) {
1396
+ } else if (m_file->getFileSize() <= m_expectedCapacity) {
1175
1397
  return;
1176
1398
  }
1177
1399
  SCOPED_LOCK(m_exclusiveProcessLock);
@@ -1182,7 +1404,7 @@ void MMKV::trim() {
1182
1404
  while (fileSize > (m_actualSize + Fixed32Size) * 2) {
1183
1405
  fileSize /= 2;
1184
1406
  }
1185
- fileSize = std::max<size_t>(fileSize, DEFAULT_MMAP_SIZE);
1407
+ fileSize = std::max<size_t>(fileSize, m_expectedCapacity);
1186
1408
  if (oldSize == fileSize) {
1187
1409
  MMKVInfo("there's no need to trim %s with size %zu, actualSize %zu", m_mmapID.c_str(), fileSize, m_actualSize);
1188
1410
  return;
@@ -1202,18 +1424,25 @@ void MMKV::trim() {
1202
1424
  MMKVInfo("finish trim %s from %zu to %zu", m_mmapID.c_str(), oldSize, fileSize);
1203
1425
  }
1204
1426
 
1205
- void MMKV::clearAll() {
1427
+ void MMKV::clearAll(bool keepSpace) {
1206
1428
  MMKVInfo("cleaning all key-values from [%s]", m_mmapID.c_str());
1207
1429
  SCOPED_LOCK(m_lock);
1208
1430
  SCOPED_LOCK(m_exclusiveProcessLock);
1209
1431
 
1210
1432
  checkLoadData();
1433
+ if (!isFileValid()) {
1434
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
1435
+ return;
1436
+ }
1211
1437
 
1212
- if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
1438
+ if (m_file->getFileSize() == m_expectedCapacity && m_actualSize == 0) {
1213
1439
  MMKVInfo("nothing to clear for [%s]", m_mmapID.c_str());
1214
1440
  return;
1215
1441
  }
1216
- m_file->truncate(DEFAULT_MMAP_SIZE);
1442
+
1443
+ if (!keepSpace) {
1444
+ m_file->truncate(m_expectedCapacity);
1445
+ }
1217
1446
 
1218
1447
  #ifndef MMKV_DISABLE_CRYPT
1219
1448
  uint8_t newIV[AES_KEY_LEN];
@@ -1225,9 +1454,10 @@ void MMKV::clearAll() {
1225
1454
  #else
1226
1455
  writeActualSize(0, 0, nullptr, IncreaseSequence);
1227
1456
  #endif
1457
+
1228
1458
  m_metaFile->msync(MMKV_SYNC);
1229
1459
 
1230
- clearMemoryCache();
1460
+ clearMemoryCache(keepSpace);
1231
1461
  loadFromFile();
1232
1462
  }
1233
1463
 
@@ -1275,6 +1505,54 @@ bool MMKV::isFileValid(const string &mmapID, MMKVPath_t *relatePath) {
1275
1505
  }
1276
1506
  }
1277
1507
 
1508
+ bool MMKV::removeStorage(const std::string &mmapID, MMKVPath_t *relatePath) {
1509
+ auto mmapKey = mmapedKVKey(mmapID, relatePath);
1510
+ #ifdef MMKV_ANDROID
1511
+ auto &realID = mmapKey; // historically Android mistakenly use mmapKey as mmapID
1512
+ #else
1513
+ auto &realID = mmapID;
1514
+ #endif
1515
+ MMKVDebug("mmapKey %s", mmapKey.c_str());
1516
+
1517
+ MMKVPath_t kvPath = mappedKVPathWithID(realID, MMKV_SINGLE_PROCESS, relatePath);
1518
+ if (!isFileExist(kvPath)) {
1519
+ MMKVWarning("file not exist %s", kvPath.c_str());
1520
+ return false;
1521
+ }
1522
+ MMKVPath_t crcPath = crcPathWithID(realID, MMKV_SINGLE_PROCESS, relatePath);
1523
+ if (!isFileExist(crcPath)) {
1524
+ MMKVWarning("file not exist %s", crcPath.c_str());
1525
+ return false;
1526
+ }
1527
+
1528
+ MMKVInfo("remove storage [%s]", mmapID.c_str());
1529
+ SCOPED_LOCK(g_instanceLock);
1530
+
1531
+ File crcFile(crcPath, OpenFlag::ReadOnly);
1532
+ if (!crcFile.isFileValid()) {
1533
+ return false;
1534
+ }
1535
+ FileLock fileLock(crcFile.getFd());
1536
+ InterProcessLock lock(&fileLock, ExclusiveLockType);
1537
+ SCOPED_LOCK(&lock);
1538
+
1539
+ auto itr = g_instanceDic->find(mmapKey);
1540
+ if (itr != g_instanceDic->end()) {
1541
+ itr->second->close();
1542
+ // itr is not valid after this
1543
+ }
1544
+
1545
+ #ifndef MMKV_WIN32
1546
+ ::unlink(kvPath.c_str());
1547
+ ::unlink(crcPath.c_str());
1548
+ #else
1549
+ DeleteFile(kvPath.c_str());
1550
+ DeleteFile(crcPath.c_str());
1551
+ #endif
1552
+
1553
+ return true;
1554
+ }
1555
+
1278
1556
  // ---- auto expire ----
1279
1557
 
1280
1558
  uint32_t MMKV::getCurrentTimeInSecond() {
@@ -1311,6 +1589,15 @@ bool MMKV::enableAutoKeyExpire(uint32_t expiredInSeconds) {
1311
1589
  SCOPED_LOCK(m_lock);
1312
1590
  SCOPED_LOCK(m_exclusiveProcessLock);
1313
1591
  checkLoadData();
1592
+ if (!isFileValid() || !m_metaFile->isFileValid()) {
1593
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
1594
+ return false;
1595
+ }
1596
+
1597
+ if (m_enableCompareBeforeSet) {
1598
+ MMKVError("enableCompareBeforeSet will be invalid when Expiration is on");
1599
+ m_enableCompareBeforeSet = false;
1600
+ }
1314
1601
 
1315
1602
  if (m_expiredInSeconds != expiredInSeconds) {
1316
1603
  MMKVInfo("expiredInSeconds: %u", expiredInSeconds);
@@ -1327,7 +1614,7 @@ bool MMKV::enableAutoKeyExpire(uint32_t expiredInSeconds) {
1327
1614
  m_metaInfo->setFlag(MMKVMetaInfo::EnableKeyExipre);
1328
1615
  m_metaInfo->m_version = MMKVVersionFlag;
1329
1616
 
1330
- if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
1617
+ if (m_file->getFileSize() == m_expectedCapacity && m_actualSize == 0) {
1331
1618
  MMKVInfo("file is new, don't need a full writeback [%s], just update meta file", m_mmapID.c_str());
1332
1619
  writeActualSize(0, 0, nullptr, IncreaseSequence);
1333
1620
  m_metaFile->msync(MMKV_SYNC);
@@ -1337,7 +1624,7 @@ bool MMKV::enableAutoKeyExpire(uint32_t expiredInSeconds) {
1337
1624
  MMKVVector vec;
1338
1625
  auto packKeyValue = [&](const MMKVKey_t &key, const MMBuffer &value) {
1339
1626
  MMBuffer data(value.length() + Fixed32Size);
1340
- auto ptr = (uint8_t *)data.getPtr();
1627
+ auto ptr = (uint8_t *) data.getPtr();
1341
1628
  memcpy(ptr, value.getPtr(), value.length());
1342
1629
  memcpy(ptr + value.length(), &time, Fixed32Size);
1343
1630
  vec.emplace_back(key, std::move(data));
@@ -1370,6 +1657,10 @@ bool MMKV::disableAutoKeyExpire() {
1370
1657
  SCOPED_LOCK(m_lock);
1371
1658
  SCOPED_LOCK(m_exclusiveProcessLock);
1372
1659
  checkLoadData();
1660
+ if (!isFileValid() || !m_metaFile->isFileValid()) {
1661
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
1662
+ return false;
1663
+ }
1373
1664
 
1374
1665
  m_expiredInSeconds = 0;
1375
1666
  m_enableKeyExpire = false;
@@ -1381,7 +1672,7 @@ bool MMKV::disableAutoKeyExpire() {
1381
1672
  m_metaInfo->unsetFlag(MMKVMetaInfo::EnableKeyExipre);
1382
1673
  m_metaInfo->m_version = MMKVVersionFlag;
1383
1674
 
1384
- if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
1675
+ if (m_file->getFileSize() == m_expectedCapacity && m_actualSize == 0) {
1385
1676
  MMKVInfo("file is new, don't need a full write-back [%s], just update meta file", m_mmapID.c_str());
1386
1677
  writeActualSize(0, 0, nullptr, IncreaseSequence);
1387
1678
  m_metaFile->msync(MMKV_SYNC);
@@ -1392,7 +1683,7 @@ bool MMKV::disableAutoKeyExpire() {
1392
1683
  auto packKeyValue = [&](const MMKVKey_t &key, const MMBuffer &value) {
1393
1684
  assert(value.length() >= Fixed32Size);
1394
1685
  MMBuffer data(value.length() - Fixed32Size);
1395
- auto ptr = (uint8_t *)data.getPtr();
1686
+ auto ptr = (uint8_t *) data.getPtr();
1396
1687
  memcpy(ptr, value.getPtr(), value.length() - Fixed32Size);
1397
1688
  vec.emplace_back(key, std::move(data));
1398
1689
  };
@@ -1425,7 +1716,7 @@ uint32_t MMKV::getExpireTimeForKey(MMKVKey_t key) {
1425
1716
  SCOPED_LOCK(m_sharedProcessLock);
1426
1717
  checkLoadData();
1427
1718
 
1428
- if (!m_enableKeyExpire || key_length(key) == 0) {
1719
+ if (!m_enableKeyExpire || mmkv_key_length(key) == 0) {
1429
1720
  return 0;
1430
1721
  }
1431
1722
  auto raw = getRawDataForKey(key);
@@ -1433,8 +1724,8 @@ uint32_t MMKV::getExpireTimeForKey(MMKVKey_t key) {
1433
1724
  if (raw.length() < Fixed32Size) {
1434
1725
  return 0;
1435
1726
  }
1436
- auto ptr = (const uint8_t *)raw.getPtr() + raw.length() - Fixed32Size;
1437
- auto time = *(const uint32_t *)ptr;
1727
+ auto ptr = (const uint8_t *) raw.getPtr() + raw.length() - Fixed32Size;
1728
+ auto time = *(const uint32_t *) ptr;
1438
1729
  return time;
1439
1730
  }
1440
1731
 
@@ -1450,8 +1741,8 @@ mmkv::MMBuffer MMKV::getDataWithoutMTimeForKey(MMKVKey_t key) {
1450
1741
  }
1451
1742
  auto newLength = raw.length() - Fixed32Size;
1452
1743
  if (m_enableKeyExpire) {
1453
- auto ptr = (const uint8_t *)raw.getPtr() + newLength;
1454
- auto time = *(const uint32_t *)ptr;
1744
+ auto ptr = (const uint8_t *) raw.getPtr() + newLength;
1745
+ auto time = *(const uint32_t *) ptr;
1455
1746
  if (time != ExpireNever && time <= getCurrentTimeInSecond()) {
1456
1747
  #ifdef MMKV_APPLE
1457
1748
  MMKVInfo("deleting expired key [%@] in mmkv [%s], due date %u", key, m_mmapID.c_str(), time);
@@ -1465,35 +1756,38 @@ mmkv::MMBuffer MMKV::getDataWithoutMTimeForKey(MMKVKey_t key) {
1465
1756
  return MMBuffer(std::move(raw), newLength);
1466
1757
  }
1467
1758
 
1468
- #define NOOP ((void)0)
1759
+ #define NOOP ((void) 0)
1469
1760
 
1470
1761
  size_t MMKV::filterExpiredKeys() {
1471
1762
  if (!m_enableKeyExpire || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {
1472
1763
  return 0;
1473
1764
  }
1765
+ SCOPED_LOCK(m_sharedProcessLock);
1766
+
1474
1767
  auto now = getCurrentTimeInSecond();
1475
- MMKVInfo("filtering expired keys inside [%s] now: %u, m_expiredInSeconds: %u", m_mmapID.c_str(), now, m_expiredInSeconds);
1768
+ MMKVInfo("filtering expired keys inside [%s] now: %u, m_expiredInSeconds: %u", m_mmapID.c_str(), now,
1769
+ m_expiredInSeconds);
1476
1770
 
1477
1771
  size_t count = 0;
1478
- auto basePtr = (uint8_t *)(m_file->getMemory()) + Fixed32Size;
1772
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
1479
1773
  #ifndef MMKV_DISABLE_CRYPT
1480
1774
  if (m_crypter) {
1481
1775
  for (auto itr = m_dicCrypt->begin(); itr != m_dicCrypt->end(); NOOP) {
1482
1776
  auto &kvHolder = itr->second;
1483
1777
  assert(kvHolder.realValueSize() >= Fixed32Size);
1484
1778
  auto buffer = kvHolder.toMMBuffer(basePtr, m_crypter);
1485
- auto ptr = (uint8_t*) buffer.getPtr();
1779
+ auto ptr = (uint8_t *) buffer.getPtr();
1486
1780
  ptr += buffer.length() - Fixed32Size;
1487
- auto time = *(const uint32_t *)ptr;
1781
+ auto time = *(const uint32_t *) ptr;
1488
1782
  if (time != ExpireNever && time <= now) {
1489
1783
  auto oldKey = itr->first;
1490
1784
  itr = m_dicCrypt->erase(itr);
1491
- #ifdef MMKV_APPLE
1785
+ # ifdef MMKV_APPLE
1492
1786
  MMKVInfo("deleting expired key [%@], due date %u", oldKey, time);
1493
1787
  [oldKey release];
1494
- #else
1788
+ # else
1495
1789
  MMKVInfo("deleting expired key [%s], due date %u", oldKey.c_str(), time);
1496
- #endif
1790
+ # endif
1497
1791
  count++;
1498
1792
  } else {
1499
1793
  itr++;
@@ -1507,7 +1801,7 @@ size_t MMKV::filterExpiredKeys() {
1507
1801
  assert(kvHolder.valueSize >= Fixed32Size);
1508
1802
  auto ptr = basePtr + kvHolder.offset + kvHolder.computedKVSize;
1509
1803
  ptr += kvHolder.valueSize - Fixed32Size;
1510
- auto time = *(const uint32_t *)ptr;
1804
+ auto time = *(const uint32_t *) ptr;
1511
1805
  if (time != ExpireNever && time <= now) {
1512
1806
  auto oldKey = itr->first;
1513
1807
  itr = m_dic->erase(itr);
@@ -1523,8 +1817,40 @@ size_t MMKV::filterExpiredKeys() {
1523
1817
  }
1524
1818
  }
1525
1819
  }
1526
- MMKVInfo("deleted %zu expired keys inside [%s]", count, m_mmapID.c_str());
1820
+ if (count != 0) {
1821
+ MMKVInfo("deleted %zu expired keys inside [%s]", count, m_mmapID.c_str());
1822
+ }
1527
1823
  return count;
1528
1824
  }
1529
1825
 
1826
+ bool MMKV::enableCompareBeforeSet() {
1827
+ MMKVInfo("enableCompareBeforeSet for [%s]", m_mmapID.c_str());
1828
+ SCOPED_LOCK(m_lock);
1829
+ SCOPED_LOCK(m_exclusiveProcessLock);
1830
+
1831
+ assert(!m_enableKeyExpire && "enableCompareBeforeSet is invalid when Expiration is on");
1832
+ assert(!m_dicCrypt && "enableCompareBeforeSet is invalid when key encryption is on");
1833
+ if (m_enableKeyExpire || m_dicCrypt) {
1834
+ return false;
1835
+ }
1836
+
1837
+ m_enableCompareBeforeSet = true;
1838
+ return true;
1839
+ }
1840
+
1841
+ bool MMKV::disableCompareBeforeSet() {
1842
+ MMKVInfo("disableCompareBeforeSet for [%s]", m_mmapID.c_str());
1843
+ SCOPED_LOCK(m_lock);
1844
+ SCOPED_LOCK(m_exclusiveProcessLock);
1845
+
1846
+ assert(!m_enableKeyExpire && "disableCompareBeforeSet is invalid when Expiration is on");
1847
+ assert(!m_dicCrypt && "disableCompareBeforeSet is invalid when key encryption is on");
1848
+ if (m_enableKeyExpire || m_dicCrypt) {
1849
+ return false;
1850
+ }
1851
+
1852
+ m_enableCompareBeforeSet = false;
1853
+ return true;
1854
+ }
1855
+
1530
1856
  MMKV_NAMESPACE_END