MMKV.cpp 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597
  1. /*
  2. * Tencent is pleased to support the open source community by making
  3. * MMKV available.
  4. *
  5. * Copyright (C) 2018 THL A29 Limited, a Tencent company.
  6. * All rights reserved.
  7. *
  8. * Licensed under the BSD 3-Clause License (the "License"); you may not use
  9. * this file except in compliance with the License. You may obtain a copy of
  10. * the License at
  11. *
  12. * https://opensource.org/licenses/BSD-3-Clause
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. #include "CodedInputData.h"
  21. #include "CodedOutputData.h"
  22. #include "InterProcessLock.h"
  23. #include "KeyValueHolder.h"
  24. #include "MMBuffer.h"
  25. #include "MMKVLog.h"
  26. #include "MMKVMetaInfo.hpp"
  27. #include "MMKV_IO.h"
  28. #include "MemoryFile.h"
  29. #include "MiniPBCoder.h"
  30. #include "PBUtility.h"
  31. #include "ScopedLock.hpp"
  32. #include "ThreadLock.h"
  33. #include "aes/AESCrypt.h"
  34. #include "aes/openssl/openssl_aes.h"
  35. #include "aes/openssl/openssl_md5.h"
  36. #include "crc32/Checksum.h"
  37. #include <algorithm>
  38. #include <cstdio>
  39. #include <cstring>
  40. #include <unordered_set>
  41. //#include <unistd.h>
  42. #include <cassert>
  43. #if defined(__aarch64__) && defined(__linux)
  44. # include <asm/hwcap.h>
  45. # include <sys/auxv.h>
  46. #endif
  47. #ifdef MMKV_APPLE
  48. # if __has_feature(objc_arc)
  49. # error This file must be compiled with MRC. Use -fno-objc-arc flag.
  50. # endif
  51. #endif // MMKV_APPLE
  52. using namespace std;
  53. using namespace mmkv;
  54. unordered_map<string, MMKV *> *g_instanceDic;
  55. ThreadLock *g_instanceLock;
  56. MMKVPath_t g_rootDir;
  57. static mmkv::ErrorHandler g_errorHandler;
  58. size_t mmkv::DEFAULT_MMAP_SIZE;
  59. #ifndef MMKV_WIN32
  60. constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = "specialCharacter";
  61. constexpr auto CRC_SUFFIX = ".crc";
  62. #else
  63. constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = L"specialCharacter";
  64. constexpr auto CRC_SUFFIX = L".crc";
  65. #endif
  66. MMKV_NAMESPACE_BEGIN
  67. static MMKVPath_t encodeFilePath(const string &mmapID, const MMKVPath_t &rootDir);
  68. bool endsWith(const MMKVPath_t &str, const MMKVPath_t &suffix);
  69. MMKVPath_t filename(const MMKVPath_t &path);
  70. #ifndef MMKV_ANDROID
  71. MMKV::MMKV(const string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath)
  72. : m_mmapID(mmapID)
  73. , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
  74. , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
  75. , m_dic(nullptr)
  76. , m_dicCrypt(nullptr)
  77. , m_file(new MemoryFile(m_path))
  78. , m_metaFile(new MemoryFile(m_crcPath))
  79. , m_metaInfo(new MMKVMetaInfo())
  80. , m_crypter(nullptr)
  81. , m_lock(new ThreadLock())
  82. , m_fileLock(new FileLock(m_metaFile->getFd()))
  83. , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
  84. , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
  85. , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0) {
  86. m_actualSize = 0;
  87. m_output = nullptr;
  88. # ifndef MMKV_DISABLE_CRYPT
  89. if (cryptKey && cryptKey->length() > 0) {
  90. m_dicCrypt = new MMKVMapCrypt();
  91. m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
  92. } else {
  93. m_dic = new MMKVMap();
  94. }
  95. # else
  96. m_dic = new MMKVMap();
  97. # endif
  98. m_needLoadFromFile = true;
  99. m_hasFullWriteback = false;
  100. m_crcDigest = 0;
  101. m_lock->initialize();
  102. m_sharedProcessLock->m_enable = m_isInterProcess;
  103. m_exclusiveProcessLock->m_enable = m_isInterProcess;
  104. // sensitive zone
  105. {
  106. SCOPED_LOCK(m_sharedProcessLock);
  107. loadFromFile();
  108. }
  109. }
  110. #endif
  111. MMKV::~MMKV() {
  112. clearMemoryCache();
  113. delete m_dic;
  114. #ifndef MMKV_DISABLE_CRYPT
  115. delete m_dicCrypt;
  116. delete m_crypter;
  117. #endif
  118. delete m_file;
  119. delete m_metaFile;
  120. delete m_metaInfo;
  121. delete m_lock;
  122. delete m_fileLock;
  123. delete m_sharedProcessLock;
  124. delete m_exclusiveProcessLock;
  125. #ifdef MMKV_ANDROID
  126. delete m_fileModeLock;
  127. delete m_sharedProcessModeLock;
  128. delete m_exclusiveProcessModeLock;
  129. #endif
  130. MMKVInfo("destruct [%s]", m_mmapID.c_str());
  131. }
  132. MMKV *MMKV::defaultMMKV(MMKVMode mode, string *cryptKey) {
  133. #ifndef MMKV_ANDROID
  134. return mmkvWithID(DEFAULT_MMAP_ID, mode, cryptKey);
  135. #else
  136. return mmkvWithID(DEFAULT_MMAP_ID, DEFAULT_MMAP_SIZE, mode, cryptKey);
  137. #endif
  138. }
  139. void initialize() {
  140. g_instanceDic = new unordered_map<string, MMKV *>;
  141. g_instanceLock = new ThreadLock();
  142. g_instanceLock->initialize();
  143. mmkv::DEFAULT_MMAP_SIZE = mmkv::getPageSize();
  144. MMKVInfo("version %s, page size %d, arch %s", MMKV_VERSION, DEFAULT_MMAP_SIZE, MMKV_ABI);
  145. // get CPU status of ARMv8 extensions (CRC32, AES)
  146. #if defined(__aarch64__) && defined(__linux__)
  147. auto hwcaps = getauxval(AT_HWCAP);
  148. # ifndef MMKV_DISABLE_CRYPT
  149. if (hwcaps & HWCAP_AES) {
  150. openssl::AES_set_encrypt_key = openssl_aes_armv8_set_encrypt_key;
  151. openssl::AES_set_decrypt_key = openssl_aes_armv8_set_decrypt_key;
  152. openssl::AES_encrypt = openssl_aes_armv8_encrypt;
  153. openssl::AES_decrypt = openssl_aes_armv8_decrypt;
  154. MMKVInfo("armv8 AES instructions is supported");
  155. } else {
  156. MMKVInfo("armv8 AES instructions is not supported");
  157. }
  158. # endif // MMKV_DISABLE_CRYPT
  159. # ifdef MMKV_USE_ARMV8_CRC32
  160. if (hwcaps & HWCAP_CRC32) {
  161. CRC32 = mmkv::armv8_crc32;
  162. MMKVInfo("armv8 CRC32 instructions is supported");
  163. } else {
  164. MMKVInfo("armv8 CRC32 instructions is not supported");
  165. }
  166. # endif // MMKV_USE_ARMV8_CRC32
  167. #endif // __aarch64__ && defined(__linux__)
  168. #if defined(MMKV_DEBUG) && !defined(MMKV_DISABLE_CRYPT)
  169. // AESCrypt::testAESCrypt();
  170. // KeyValueHolderCrypt::testAESToMMBuffer();
  171. #endif
  172. }
  173. ThreadOnceToken_t once_control = ThreadOnceUninitialized;
  174. void MMKV::initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel, mmkv::LogHandler handler) {
  175. g_currentLogLevel = logLevel;
  176. g_logHandler = handler;
  177. ThreadLock::ThreadOnce(&once_control, initialize);
  178. g_rootDir = rootDir;
  179. mkPath(g_rootDir);
  180. MMKVInfo("root dir: " MMKV_PATH_FORMAT, g_rootDir.c_str());
  181. }
  182. const MMKVPath_t &MMKV::getRootDir() {
  183. return g_rootDir;
  184. }
  185. #ifndef MMKV_ANDROID
  186. MMKV *MMKV::mmkvWithID(const string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath) {
  187. if (mmapID.empty()) {
  188. return nullptr;
  189. }
  190. SCOPED_LOCK(g_instanceLock);
  191. auto mmapKey = mmapedKVKey(mmapID, rootPath);
  192. auto itr = g_instanceDic->find(mmapKey);
  193. if (itr != g_instanceDic->end()) {
  194. MMKV *kv = itr->second;
  195. return kv;
  196. }
  197. if (rootPath) {
  198. MMKVPath_t specialPath = (*rootPath) + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
  199. if (!isFileExist(specialPath)) {
  200. mkPath(specialPath);
  201. }
  202. MMKVInfo("prepare to load %s (id %s) from rootPath %s", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());
  203. }
  204. auto kv = new MMKV(mmapID, mode, cryptKey, rootPath);
  205. kv->m_mmapKey = mmapKey;
  206. (*g_instanceDic)[mmapKey] = kv;
  207. return kv;
  208. }
  209. #endif
  210. void MMKV::onExit() {
  211. SCOPED_LOCK(g_instanceLock);
  212. for (auto &pair : *g_instanceDic) {
  213. MMKV *kv = pair.second;
  214. kv->sync();
  215. kv->clearMemoryCache();
  216. delete kv;
  217. pair.second = nullptr;
  218. }
  219. delete g_instanceDic;
  220. g_instanceDic = nullptr;
  221. }
  222. const string &MMKV::mmapID() const {
  223. return m_mmapID;
  224. }
  225. mmkv::ContentChangeHandler g_contentChangeHandler = nullptr;
  226. void MMKV::notifyContentChanged() {
  227. if (g_contentChangeHandler) {
  228. g_contentChangeHandler(m_mmapID);
  229. }
  230. }
  231. void MMKV::checkContentChanged() {
  232. SCOPED_LOCK(m_lock);
  233. checkLoadData();
  234. }
  235. void MMKV::registerContentChangeHandler(mmkv::ContentChangeHandler handler) {
  236. g_contentChangeHandler = handler;
  237. }
  238. void MMKV::unRegisterContentChangeHandler() {
  239. g_contentChangeHandler = nullptr;
  240. }
  241. void MMKV::clearMemoryCache() {
  242. SCOPED_LOCK(m_lock);
  243. if (m_needLoadFromFile) {
  244. return;
  245. }
  246. MMKVInfo("clearMemoryCache [%s]", m_mmapID.c_str());
  247. m_needLoadFromFile = true;
  248. m_hasFullWriteback = false;
  249. clearDictionary(m_dic);
  250. #ifndef MMKV_DISABLE_CRYPT
  251. clearDictionary(m_dicCrypt);
  252. if (m_crypter) {
  253. if (m_metaInfo->m_version >= MMKVVersionRandomIV) {
  254. m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));
  255. } else {
  256. m_crypter->resetIV();
  257. }
  258. }
  259. #endif
  260. delete m_output;
  261. m_output = nullptr;
  262. m_file->clearMemoryCache();
  263. // inter-process lock rely on MetaFile's fd, never close it
  264. // m_metaFile->clearMemoryCache();
  265. m_actualSize = 0;
  266. m_metaInfo->m_crcDigest = 0;
  267. }
  268. void MMKV::close() {
  269. MMKVInfo("close [%s]", m_mmapID.c_str());
  270. SCOPED_LOCK(g_instanceLock);
  271. m_lock->lock();
  272. #ifndef MMKV_ANDROID
  273. auto itr = g_instanceDic->find(m_mmapKey);
  274. #else
  275. auto itr = g_instanceDic->find(m_mmapID);
  276. #endif
  277. if (itr != g_instanceDic->end()) {
  278. g_instanceDic->erase(itr);
  279. }
  280. delete this;
  281. }
  282. #ifndef MMKV_DISABLE_CRYPT
  283. string MMKV::cryptKey() const {
  284. SCOPED_LOCK(m_lock);
  285. if (m_crypter) {
  286. char key[AES_KEY_LEN];
  287. m_crypter->getKey(key);
  288. return {key, strnlen(key, AES_KEY_LEN)};
  289. }
  290. return "";
  291. }
  292. void MMKV::checkReSetCryptKey(const string *cryptKey) {
  293. SCOPED_LOCK(m_lock);
  294. if (m_crypter) {
  295. if (cryptKey && cryptKey->length() > 0) {
  296. string oldKey = this->cryptKey();
  297. if (oldKey != *cryptKey) {
  298. MMKVInfo("setting new aes key");
  299. delete m_crypter;
  300. auto ptr = cryptKey->data();
  301. m_crypter = new AESCrypt(ptr, cryptKey->length());
  302. checkLoadData();
  303. } else {
  304. // nothing to do
  305. }
  306. } else {
  307. MMKVInfo("reset aes key");
  308. delete m_crypter;
  309. m_crypter = nullptr;
  310. checkLoadData();
  311. }
  312. } else {
  313. if (cryptKey && cryptKey->length() > 0) {
  314. MMKVInfo("setting new aes key");
  315. auto ptr = cryptKey->data();
  316. m_crypter = new AESCrypt(ptr, cryptKey->length());
  317. checkLoadData();
  318. } else {
  319. // nothing to do
  320. }
  321. }
  322. }
  323. #endif // MMKV_DISABLE_CRYPT
  324. bool MMKV::isFileValid() {
  325. return m_file->isFileValid();
  326. }
  327. // crc
  328. // assuming m_file is valid
  329. bool MMKV::checkFileCRCValid(size_t actualSize, uint32_t crcDigest) {
  330. auto ptr = (uint8_t *) m_file->getMemory();
  331. if (ptr) {
  332. m_crcDigest = (uint32_t) CRC32(0, (const uint8_t *) ptr + Fixed32Size, (uint32_t) actualSize);
  333. if (m_crcDigest == crcDigest) {
  334. return true;
  335. }
  336. MMKVError("check crc [%s] fail, crc32:%u, m_crcDigest:%u", m_mmapID.c_str(), crcDigest, m_crcDigest);
  337. }
  338. return false;
  339. }
  340. void MMKV::recaculateCRCDigestWithIV(const void *iv) {
  341. auto ptr = (const uint8_t *) m_file->getMemory();
  342. if (ptr) {
  343. m_crcDigest = 0;
  344. m_crcDigest = (uint32_t) CRC32(0, ptr + Fixed32Size, (uint32_t) m_actualSize);
  345. writeActualSize(m_actualSize, m_crcDigest, iv, IncreaseSequence);
  346. }
  347. }
  348. void MMKV::updateCRCDigest(const uint8_t *ptr, size_t length) {
  349. if (ptr == nullptr) {
  350. return;
  351. }
  352. m_crcDigest = (uint32_t) CRC32(m_crcDigest, ptr, (uint32_t) length);
  353. writeActualSize(m_actualSize, m_crcDigest, nullptr, KeepSequence);
  354. }
  355. // set & get
  356. bool MMKV::set(bool value, MMKVKey_t key) {
  357. return set(value, key, m_expiredInSeconds);
  358. }
  359. bool MMKV::set(bool value, MMKVKey_t key, uint32_t expireDuration) {
  360. if (isKeyEmpty(key)) {
  361. return false;
  362. }
  363. size_t size = unlikely(m_enableKeyExpire) ? Fixed32Size + pbBoolSize() : pbBoolSize();
  364. MMBuffer data(size);
  365. CodedOutputData output(data.getPtr(), size);
  366. output.writeBool(value);
  367. if (unlikely(m_enableKeyExpire)) {
  368. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  369. output.writeRawLittleEndian32(UInt32ToInt32(time));
  370. } else {
  371. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  372. }
  373. return setDataForKey(std::move(data), key);
  374. }
  375. bool MMKV::set(int32_t value, MMKVKey_t key) {
  376. return set(value, key, m_expiredInSeconds);
  377. }
  378. bool MMKV::set(int32_t value, MMKVKey_t key, uint32_t expireDuration) {
  379. if (isKeyEmpty(key)) {
  380. return false;
  381. }
  382. size_t size = unlikely(m_enableKeyExpire) ? Fixed32Size + pbInt32Size(value) : pbInt32Size(value);
  383. MMBuffer data(size);
  384. CodedOutputData output(data.getPtr(), size);
  385. output.writeInt32(value);
  386. if (unlikely(m_enableKeyExpire)) {
  387. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  388. output.writeRawLittleEndian32(UInt32ToInt32(time));
  389. } else {
  390. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  391. }
  392. return setDataForKey(std::move(data), key);
  393. }
  394. bool MMKV::set(uint32_t value, MMKVKey_t key) {
  395. return set(value, key, m_expiredInSeconds);
  396. }
  397. bool MMKV::set(uint32_t value, MMKVKey_t key, uint32_t expireDuration) {
  398. if (isKeyEmpty(key)) {
  399. return false;
  400. }
  401. size_t size = unlikely(m_enableKeyExpire) ? Fixed32Size + pbUInt32Size(value) : pbUInt32Size(value);
  402. MMBuffer data(size);
  403. CodedOutputData output(data.getPtr(), size);
  404. output.writeUInt32(value);
  405. if (unlikely(m_enableKeyExpire)) {
  406. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  407. output.writeRawLittleEndian32(UInt32ToInt32(time));
  408. } else {
  409. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  410. }
  411. return setDataForKey(std::move(data), key);
  412. }
  413. bool MMKV::set(int64_t value, MMKVKey_t key) {
  414. return set(value, key, m_expiredInSeconds);
  415. }
  416. bool MMKV::set(int64_t value, MMKVKey_t key, uint32_t expireDuration) {
  417. if (isKeyEmpty(key)) {
  418. return false;
  419. }
  420. size_t size = unlikely(m_enableKeyExpire) ? Fixed32Size + pbInt64Size(value) : pbInt64Size(value);
  421. MMBuffer data(size);
  422. CodedOutputData output(data.getPtr(), size);
  423. output.writeInt64(value);
  424. if (unlikely(m_enableKeyExpire)) {
  425. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  426. output.writeRawLittleEndian32(UInt32ToInt32(time));
  427. } else {
  428. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  429. }
  430. return setDataForKey(std::move(data), key);
  431. }
  432. bool MMKV::set(uint64_t value, MMKVKey_t key) {
  433. return set(value, key, m_expiredInSeconds);
  434. }
  435. bool MMKV::set(uint64_t value, MMKVKey_t key, uint32_t expireDuration) {
  436. if (isKeyEmpty(key)) {
  437. return false;
  438. }
  439. size_t size = unlikely(m_enableKeyExpire) ? Fixed32Size + pbUInt64Size(value) : pbUInt64Size(value);
  440. MMBuffer data(size);
  441. CodedOutputData output(data.getPtr(), size);
  442. output.writeUInt64(value);
  443. if (unlikely(m_enableKeyExpire)) {
  444. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  445. output.writeRawLittleEndian32(UInt32ToInt32(time));
  446. } else {
  447. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  448. }
  449. return setDataForKey(std::move(data), key);
  450. }
  451. bool MMKV::set(float value, MMKVKey_t key) {
  452. return set(value, key, m_expiredInSeconds);
  453. }
  454. bool MMKV::set(float value, MMKVKey_t key, uint32_t expireDuration) {
  455. if (isKeyEmpty(key)) {
  456. return false;
  457. }
  458. size_t size = unlikely(m_enableKeyExpire) ? Fixed32Size + pbFloatSize() : pbFloatSize();
  459. MMBuffer data(size);
  460. CodedOutputData output(data.getPtr(), size);
  461. output.writeFloat(value);
  462. if (unlikely(m_enableKeyExpire)) {
  463. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  464. output.writeRawLittleEndian32(UInt32ToInt32(time));
  465. } else {
  466. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  467. }
  468. return setDataForKey(std::move(data), key);
  469. }
  470. bool MMKV::set(double value, MMKVKey_t key) {
  471. return set(value, key, m_expiredInSeconds);
  472. }
  473. bool MMKV::set(double value, MMKVKey_t key, uint32_t expireDuration) {
  474. if (isKeyEmpty(key)) {
  475. return false;
  476. }
  477. size_t size = unlikely(m_enableKeyExpire) ? Fixed32Size + pbDoubleSize() : pbDoubleSize();
  478. MMBuffer data(size);
  479. CodedOutputData output(data.getPtr(), size);
  480. output.writeDouble(value);
  481. if (unlikely(m_enableKeyExpire)) {
  482. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  483. output.writeRawLittleEndian32(UInt32ToInt32(time));
  484. } else {
  485. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  486. }
  487. return setDataForKey(std::move(data), key);
  488. }
  489. #ifndef MMKV_APPLE
  490. bool MMKV::set(const char *value, MMKVKey_t key) {
  491. return set(value, key, m_expiredInSeconds);
  492. }
  493. bool MMKV::set(const char *value, MMKVKey_t key, uint32_t expireDuration) {
  494. if (!value) {
  495. removeValueForKey(key);
  496. return true;
  497. }
  498. if (likely(!m_enableKeyExpire)) {
  499. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  500. return setDataForKey(MMBuffer((void *) value, strlen(value), MMBufferNoCopy), key, true);
  501. } else {
  502. MMBuffer data((void *) value, strlen(value), MMBufferNoCopy);
  503. if (data.length() > 0) {
  504. auto tmp = MMBuffer(pbMMBufferSize(data) + Fixed32Size);
  505. CodedOutputData output(tmp.getPtr(), tmp.length());
  506. output.writeData(data);
  507. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  508. output.writeRawLittleEndian32(UInt32ToInt32(time));
  509. data = std::move(tmp);
  510. }
  511. return setDataForKey(std::move(data), key);
  512. }
  513. }
  514. bool MMKV::set(const string &value, MMKVKey_t key) {
  515. return set(value, key, m_expiredInSeconds);
  516. }
  517. bool MMKV::set(const string &value, MMKVKey_t key, uint32_t expireDuration) {
  518. if (isKeyEmpty(key)) {
  519. return false;
  520. }
  521. if (likely(!m_enableKeyExpire)) {
  522. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  523. return setDataForKey(MMBuffer((void *) value.data(), value.length(), MMBufferNoCopy), key, true);
  524. } else {
  525. MMBuffer data((void *) value.data(), value.length(), MMBufferNoCopy);
  526. if (data.length() > 0) {
  527. auto tmp = MMBuffer(pbMMBufferSize(data) + Fixed32Size);
  528. CodedOutputData output(tmp.getPtr(), tmp.length());
  529. output.writeData(data);
  530. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  531. output.writeRawLittleEndian32(UInt32ToInt32(time));
  532. data = std::move(tmp);
  533. }
  534. return setDataForKey(std::move(data), key);
  535. }
  536. }
  537. bool MMKV::set(const MMBuffer &value, MMKVKey_t key) {
  538. return set(value, key, m_expiredInSeconds);
  539. }
  540. bool MMKV::set(const MMBuffer &value, MMKVKey_t key, uint32_t expireDuration) {
  541. if (isKeyEmpty(key)) {
  542. return false;
  543. }
  544. // delay write the size needed for encoding value
  545. // avoid memory copying
  546. if (likely(!m_enableKeyExpire)) {
  547. assert(expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first");
  548. return setDataForKey(MMBuffer(value.getPtr(), value.length(), MMBufferNoCopy), key, true);
  549. } else {
  550. MMBuffer data(value.getPtr(), value.length(), MMBufferNoCopy);
  551. if (data.length() > 0) {
  552. auto tmp = MMBuffer(pbMMBufferSize(data) + Fixed32Size);
  553. CodedOutputData output(tmp.getPtr(), tmp.length());
  554. output.writeData(data);
  555. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  556. output.writeRawLittleEndian32(UInt32ToInt32(time));
  557. data = std::move(tmp);
  558. }
  559. return setDataForKey(std::move(data), key);
  560. }
  561. }
  562. bool MMKV::set(const vector<string> &value, MMKVKey_t key) {
  563. return set(value, key, m_expiredInSeconds);
  564. }
  565. bool MMKV::set(const vector<string> &v, MMKVKey_t key, uint32_t expireDuration) {
  566. if (isKeyEmpty(key)) {
  567. return false;
  568. }
  569. auto data = MiniPBCoder::encodeDataWithObject(v);
  570. if (unlikely(m_enableKeyExpire) && data.length() > 0) {
  571. auto tmp = MMBuffer(data.length() + Fixed32Size);
  572. auto ptr = (uint8_t *)tmp.getPtr();
  573. memcpy(ptr, data.getPtr(), data.length());
  574. auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;
  575. memcpy(ptr + data.length(), &time, Fixed32Size);
  576. data = std::move(tmp);
  577. }
  578. return setDataForKey(std::move(data), key);
  579. }
  580. bool MMKV::getString(MMKVKey_t key, string &result) {
  581. if (isKeyEmpty(key)) {
  582. return false;
  583. }
  584. SCOPED_LOCK(m_lock);
  585. SCOPED_LOCK(m_sharedProcessLock);
  586. auto data = getDataForKey(key);
  587. if (data.length() > 0) {
  588. try {
  589. CodedInputData input(data.getPtr(), data.length());
  590. result = input.readString();
  591. return true;
  592. } catch (std::exception &exception) {
  593. MMKVError("%s", exception.what());
  594. }
  595. }
  596. return false;
  597. }
  598. bool MMKV::getBytes(MMKVKey_t key, mmkv::MMBuffer &result) {
  599. if (isKeyEmpty(key)) {
  600. return false;
  601. }
  602. SCOPED_LOCK(m_lock);
  603. SCOPED_LOCK(m_sharedProcessLock);
  604. auto data = getDataForKey(key);
  605. if (data.length() > 0) {
  606. try {
  607. CodedInputData input(data.getPtr(), data.length());
  608. result = std::move(input.readData());
  609. return true;
  610. } catch (std::exception &exception) {
  611. MMKVError("%s", exception.what());
  612. }
  613. }
  614. return false;
  615. }
  616. MMBuffer MMKV::getBytes(MMKVKey_t key) {
  617. if (isKeyEmpty(key)) {
  618. return MMBuffer();
  619. }
  620. SCOPED_LOCK(m_lock);
  621. SCOPED_LOCK(m_sharedProcessLock);
  622. auto data = getDataForKey(key);
  623. if (data.length() > 0) {
  624. try {
  625. CodedInputData input(data.getPtr(), data.length());
  626. return input.readData();
  627. } catch (std::exception &exception) {
  628. MMKVError("%s", exception.what());
  629. }
  630. }
  631. return MMBuffer();
  632. }
  633. bool MMKV::getVector(MMKVKey_t key, vector<string> &result) {
  634. if (isKeyEmpty(key)) {
  635. return false;
  636. }
  637. SCOPED_LOCK(m_lock);
  638. SCOPED_LOCK(m_sharedProcessLock);
  639. auto data = getDataForKey(key);
  640. if (data.length() > 0) {
  641. try {
  642. result = MiniPBCoder::decodeVector(data);
  643. return true;
  644. } catch (std::exception &exception) {
  645. MMKVError("%s", exception.what());
  646. }
  647. }
  648. return false;
  649. }
  650. #endif // MMKV_APPLE
  651. bool MMKV::getBool(MMKVKey_t key, bool defaultValue, bool *hasValue) {
  652. if (isKeyEmpty(key)) {
  653. if (hasValue != nullptr) {
  654. *hasValue = false;
  655. }
  656. return defaultValue;
  657. }
  658. SCOPED_LOCK(m_lock);
  659. SCOPED_LOCK(m_sharedProcessLock);
  660. auto data = getDataForKey(key);
  661. if (data.length() > 0) {
  662. try {
  663. CodedInputData input(data.getPtr(), data.length());
  664. if (hasValue != nullptr) {
  665. *hasValue = true;
  666. }
  667. return input.readBool();
  668. } catch (std::exception &exception) {
  669. MMKVError("%s", exception.what());
  670. }
  671. }
  672. if (hasValue != nullptr) {
  673. *hasValue = false;
  674. }
  675. return defaultValue;
  676. }
  677. int32_t MMKV::getInt32(MMKVKey_t key, int32_t defaultValue, bool *hasValue) {
  678. if (isKeyEmpty(key)) {
  679. if (hasValue != nullptr) {
  680. *hasValue = false;
  681. }
  682. return defaultValue;
  683. }
  684. SCOPED_LOCK(m_lock);
  685. SCOPED_LOCK(m_sharedProcessLock);
  686. auto data = getDataForKey(key);
  687. if (data.length() > 0) {
  688. try {
  689. CodedInputData input(data.getPtr(), data.length());
  690. if (hasValue != nullptr) {
  691. *hasValue = true;
  692. }
  693. return input.readInt32();
  694. } catch (std::exception &exception) {
  695. MMKVError("%s", exception.what());
  696. }
  697. }
  698. if (hasValue != nullptr) {
  699. *hasValue = false;
  700. }
  701. return defaultValue;
  702. }
  703. uint32_t MMKV::getUInt32(MMKVKey_t key, uint32_t defaultValue, bool *hasValue) {
  704. if (isKeyEmpty(key)) {
  705. if (hasValue != nullptr) {
  706. *hasValue = false;
  707. }
  708. return defaultValue;
  709. }
  710. SCOPED_LOCK(m_lock);
  711. SCOPED_LOCK(m_sharedProcessLock);
  712. auto data = getDataForKey(key);
  713. if (data.length() > 0) {
  714. try {
  715. CodedInputData input(data.getPtr(), data.length());
  716. if (hasValue != nullptr) {
  717. *hasValue = true;
  718. }
  719. return input.readUInt32();
  720. } catch (std::exception &exception) {
  721. MMKVError("%s", exception.what());
  722. }
  723. }
  724. if (hasValue != nullptr) {
  725. *hasValue = false;
  726. }
  727. return defaultValue;
  728. }
  729. int64_t MMKV::getInt64(MMKVKey_t key, int64_t defaultValue, bool *hasValue) {
  730. if (isKeyEmpty(key)) {
  731. if (hasValue != nullptr) {
  732. *hasValue = false;
  733. }
  734. return defaultValue;
  735. }
  736. SCOPED_LOCK(m_lock);
  737. SCOPED_LOCK(m_sharedProcessLock);
  738. auto data = getDataForKey(key);
  739. if (data.length() > 0) {
  740. try {
  741. CodedInputData input(data.getPtr(), data.length());
  742. if (hasValue != nullptr) {
  743. *hasValue = true;
  744. }
  745. return input.readInt64();
  746. } catch (std::exception &exception) {
  747. MMKVError("%s", exception.what());
  748. }
  749. }
  750. if (hasValue != nullptr) {
  751. *hasValue = false;
  752. }
  753. return defaultValue;
  754. }
  755. uint64_t MMKV::getUInt64(MMKVKey_t key, uint64_t defaultValue, bool *hasValue) {
  756. if (isKeyEmpty(key)) {
  757. if (hasValue != nullptr) {
  758. *hasValue = false;
  759. }
  760. return defaultValue;
  761. }
  762. SCOPED_LOCK(m_lock);
  763. SCOPED_LOCK(m_sharedProcessLock);
  764. auto data = getDataForKey(key);
  765. if (data.length() > 0) {
  766. try {
  767. CodedInputData input(data.getPtr(), data.length());
  768. if (hasValue != nullptr) {
  769. *hasValue = true;
  770. }
  771. return input.readUInt64();
  772. } catch (std::exception &exception) {
  773. MMKVError("%s", exception.what());
  774. }
  775. }
  776. if (hasValue != nullptr) {
  777. *hasValue = false;
  778. }
  779. return defaultValue;
  780. }
  781. float MMKV::getFloat(MMKVKey_t key, float defaultValue, bool *hasValue) {
  782. if (isKeyEmpty(key)) {
  783. if (hasValue != nullptr) {
  784. *hasValue = false;
  785. }
  786. return defaultValue;
  787. }
  788. SCOPED_LOCK(m_lock);
  789. SCOPED_LOCK(m_sharedProcessLock);
  790. auto data = getDataForKey(key);
  791. if (data.length() > 0) {
  792. try {
  793. CodedInputData input(data.getPtr(), data.length());
  794. if (hasValue != nullptr) {
  795. *hasValue = true;
  796. }
  797. return input.readFloat();
  798. } catch (std::exception &exception) {
  799. MMKVError("%s", exception.what());
  800. }
  801. }
  802. if (hasValue != nullptr) {
  803. *hasValue = false;
  804. }
  805. return defaultValue;
  806. }
  807. double MMKV::getDouble(MMKVKey_t key, double defaultValue, bool *hasValue) {
  808. if (isKeyEmpty(key)) {
  809. if (hasValue != nullptr) {
  810. *hasValue = false;
  811. }
  812. return defaultValue;
  813. }
  814. SCOPED_LOCK(m_lock);
  815. SCOPED_LOCK(m_sharedProcessLock);
  816. auto data = getDataForKey(key);
  817. if (data.length() > 0) {
  818. try {
  819. CodedInputData input(data.getPtr(), data.length());
  820. if (hasValue != nullptr) {
  821. *hasValue = true;
  822. }
  823. return input.readDouble();
  824. } catch (std::exception &exception) {
  825. MMKVError("%s", exception.what());
  826. }
  827. }
  828. if (hasValue != nullptr) {
  829. *hasValue = false;
  830. }
  831. return defaultValue;
  832. }
  833. size_t MMKV::getValueSize(MMKVKey_t key, bool actualSize) {
  834. if (isKeyEmpty(key)) {
  835. return 0;
  836. }
  837. SCOPED_LOCK(m_lock);
  838. SCOPED_LOCK(m_sharedProcessLock);
  839. auto data = getDataForKey(key);
  840. if (actualSize) {
  841. try {
  842. CodedInputData input(data.getPtr(), data.length());
  843. auto length = input.readInt32();
  844. if (length >= 0) {
  845. auto s_length = static_cast<size_t>(length);
  846. if (pbRawVarint32Size(length) + s_length == data.length()) {
  847. return s_length;
  848. }
  849. }
  850. } catch (std::exception &exception) {
  851. MMKVError("%s", exception.what());
  852. }
  853. }
  854. return data.length();
  855. }
  856. int32_t MMKV::writeValueToBuffer(MMKVKey_t key, void *ptr, int32_t size) {
  857. if (isKeyEmpty(key) || size < 0) {
  858. return -1;
  859. }
  860. auto s_size = static_cast<size_t>(size);
  861. SCOPED_LOCK(m_lock);
  862. SCOPED_LOCK(m_sharedProcessLock);
  863. auto data = getDataForKey(key);
  864. try {
  865. CodedInputData input(data.getPtr(), data.length());
  866. auto length = input.readInt32();
  867. auto offset = pbRawVarint32Size(length);
  868. if (length >= 0) {
  869. auto s_length = static_cast<size_t>(length);
  870. if (offset + s_length == data.length()) {
  871. if (s_length <= s_size) {
  872. memcpy(ptr, (uint8_t *) data.getPtr() + offset, s_length);
  873. return length;
  874. }
  875. } else {
  876. if (data.length() <= s_size) {
  877. memcpy(ptr, data.getPtr(), data.length());
  878. return static_cast<int32_t>(data.length());
  879. }
  880. }
  881. }
  882. } catch (std::exception &exception) {
  883. MMKVError("%s", exception.what());
  884. }
  885. return -1;
  886. }
  887. // enumerate
  888. bool MMKV::containsKey(MMKVKey_t key) {
  889. SCOPED_LOCK(m_lock);
  890. checkLoadData();
  891. if (likely(!m_enableKeyExpire)) {
  892. if (m_crypter) {
  893. return m_dicCrypt->find(key) != m_dicCrypt->end();
  894. } else {
  895. return m_dic->find(key) != m_dic->end();
  896. }
  897. }
  898. auto raw = getDataWithoutMTimeForKey(key);
  899. return raw.length() != 0;
  900. }
  901. size_t MMKV::count(bool filterExpire) {
  902. SCOPED_LOCK(m_lock);
  903. checkLoadData();
  904. if (unlikely(filterExpire && m_enableKeyExpire)) {
  905. SCOPED_LOCK(m_exclusiveProcessLock);
  906. fullWriteback(nullptr, true);
  907. }
  908. if (m_crypter) {
  909. return m_dicCrypt->size();
  910. } else {
  911. return m_dic->size();
  912. }
  913. }
  914. size_t MMKV::totalSize() {
  915. SCOPED_LOCK(m_lock);
  916. checkLoadData();
  917. return m_file->getFileSize();
  918. }
  919. size_t MMKV::actualSize() {
  920. SCOPED_LOCK(m_lock);
  921. checkLoadData();
  922. return m_actualSize;
  923. }
  924. void MMKV::removeValueForKey(MMKVKey_t key) {
  925. if (isKeyEmpty(key)) {
  926. return;
  927. }
  928. SCOPED_LOCK(m_lock);
  929. SCOPED_LOCK(m_exclusiveProcessLock);
  930. checkLoadData();
  931. removeDataForKey(key);
  932. }
  933. #ifndef MMKV_APPLE
  934. vector<string> MMKV::allKeys(bool filterExpire) {
  935. SCOPED_LOCK(m_lock);
  936. checkLoadData();
  937. if (unlikely(filterExpire && m_enableKeyExpire)) {
  938. SCOPED_LOCK(m_exclusiveProcessLock);
  939. fullWriteback(nullptr, true);
  940. }
  941. vector<string> keys;
  942. if (m_crypter) {
  943. for (const auto &itr : *m_dicCrypt) {
  944. keys.push_back(itr.first);
  945. }
  946. } else {
  947. for (const auto &itr : *m_dic) {
  948. keys.push_back(itr.first);
  949. }
  950. }
  951. return keys;
  952. }
  953. void MMKV::removeValuesForKeys(const vector<string> &arrKeys) {
  954. if (arrKeys.empty()) {
  955. return;
  956. }
  957. if (arrKeys.size() == 1) {
  958. return removeValueForKey(arrKeys[0]);
  959. }
  960. SCOPED_LOCK(m_lock);
  961. SCOPED_LOCK(m_exclusiveProcessLock);
  962. checkLoadData();
  963. size_t deleteCount = 0;
  964. if (m_crypter) {
  965. for (const auto &key : arrKeys) {
  966. auto itr = m_dicCrypt->find(key);
  967. if (itr != m_dicCrypt->end()) {
  968. m_dicCrypt->erase(itr);
  969. deleteCount++;
  970. }
  971. }
  972. } else {
  973. for (const auto &key : arrKeys) {
  974. auto itr = m_dic->find(key);
  975. if (itr != m_dic->end()) {
  976. m_dic->erase(itr);
  977. deleteCount++;
  978. }
  979. }
  980. }
  981. if (deleteCount > 0) {
  982. m_hasFullWriteback = false;
  983. fullWriteback();
  984. }
  985. }
  986. #endif // MMKV_APPLE
  987. // file
  988. void MMKV::sync(SyncFlag flag) {
  989. SCOPED_LOCK(m_lock);
  990. if (m_needLoadFromFile || !isFileValid()) {
  991. return;
  992. }
  993. SCOPED_LOCK(m_exclusiveProcessLock);
  994. m_file->msync(flag);
  995. m_metaFile->msync(flag);
  996. }
  997. void MMKV::lock() {
  998. SCOPED_LOCK(m_lock);
  999. m_exclusiveProcessLock->lock();
  1000. }
  1001. void MMKV::unlock() {
  1002. SCOPED_LOCK(m_lock);
  1003. m_exclusiveProcessLock->unlock();
  1004. }
  1005. bool MMKV::try_lock() {
  1006. SCOPED_LOCK(m_lock);
  1007. return m_exclusiveProcessLock->try_lock();
  1008. }
  1009. // backup
  1010. static bool backupOneToDirectoryByFilePath(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
  1011. File crcFile(srcPath, OpenFlag::ReadOnly);
  1012. if (!crcFile.isFileValid()) {
  1013. return false;
  1014. }
  1015. bool ret = false;
  1016. {
  1017. #ifdef MMKV_WIN32
  1018. MMKVInfo("backup one mmkv[%s] from [%ls] to [%ls]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1019. #else
  1020. MMKVInfo("backup one mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1021. #endif
  1022. FileLock fileLock(crcFile.getFd());
  1023. InterProcessLock lock(&fileLock, SharedLockType);
  1024. SCOPED_LOCK(&lock);
  1025. ret = copyFile(srcPath, dstPath);
  1026. if (ret) {
  1027. auto srcCRCPath = srcPath + CRC_SUFFIX;
  1028. auto dstCRCPath = dstPath + CRC_SUFFIX;
  1029. ret = copyFile(srcCRCPath, dstCRCPath);
  1030. }
  1031. MMKVInfo("finish backup one mmkv[%s]", mmapKey.c_str());
  1032. }
  1033. return ret;
  1034. }
  1035. bool MMKV::backupOneToDirectory(const string &mmapKey, const MMKVPath_t &dstPath, const MMKVPath_t &srcPath, bool compareFullPath) {
  1036. // we have to lock the creation of MMKV instance, regardless of in cache or not
  1037. SCOPED_LOCK(g_instanceLock);
  1038. MMKV *kv = nullptr;
  1039. if (!compareFullPath) {
  1040. auto itr = g_instanceDic->find(mmapKey);
  1041. if (itr != g_instanceDic->end()) {
  1042. kv = itr->second;
  1043. }
  1044. } else {
  1045. // mmapKey is actually filename, we can't simply call find()
  1046. for (auto &pair : *g_instanceDic) {
  1047. if (pair.second->m_path == srcPath) {
  1048. kv = pair.second;
  1049. break;
  1050. }
  1051. }
  1052. }
  1053. // get one in cache, do it the easy way
  1054. if (kv) {
  1055. #ifdef MMKV_WIN32
  1056. MMKVInfo("backup one cached mmkv[%s] from [%ls] to [%ls]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1057. #else
  1058. MMKVInfo("backup one cached mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1059. #endif
  1060. SCOPED_LOCK(kv->m_lock);
  1061. SCOPED_LOCK(kv->m_sharedProcessLock);
  1062. kv->sync();
  1063. auto ret = copyFile(kv->m_path, dstPath);
  1064. if (ret) {
  1065. auto dstCRCPath = dstPath + CRC_SUFFIX;
  1066. ret = copyFile(kv->m_crcPath, dstCRCPath);
  1067. }
  1068. MMKVInfo("finish backup one mmkv[%s], ret: %d", mmapKey.c_str(), ret);
  1069. return ret;
  1070. }
  1071. // no luck with cache, do it the hard way
  1072. bool ret = backupOneToDirectoryByFilePath(mmapKey, srcPath, dstPath);
  1073. return ret;
  1074. }
  1075. bool MMKV::backupOneToDirectory(const string &mmapID, const MMKVPath_t &dstDir, const MMKVPath_t *srcDir) {
  1076. auto rootPath = srcDir ? srcDir : &g_rootDir;
  1077. if (*rootPath == dstDir) {
  1078. return true;
  1079. }
  1080. mkPath(dstDir);
  1081. auto encodePath = encodeFilePath(mmapID, dstDir);
  1082. auto dstPath = dstDir + MMKV_PATH_SLASH + encodePath;
  1083. auto mmapKey = mmapedKVKey(mmapID, rootPath);
  1084. #ifdef MMKV_ANDROID
  1085. // historically Android mistakenly use mmapKey as mmapID
  1086. auto srcPath = *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapKey, *rootPath);
  1087. #else
  1088. auto srcPath = *rootPath + MMKV_PATH_SLASH + encodePath;
  1089. #endif
  1090. return backupOneToDirectory(mmapKey, dstPath, srcPath, false);
  1091. }
  1092. bool endsWith(const MMKVPath_t &str, const MMKVPath_t &suffix) {
  1093. if (str.length() >= suffix.length()) {
  1094. return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
  1095. } else {
  1096. return false;
  1097. }
  1098. }
  1099. MMKVPath_t filename(const MMKVPath_t &path) {
  1100. auto startPos = path.rfind(MMKV_PATH_SLASH);
  1101. startPos++; // don't need to check for npos, because npos+1 == 0
  1102. auto filename = path.substr(startPos);
  1103. return filename;
  1104. }
  1105. size_t MMKV::backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t &srcDir, bool isInSpecialDir) {
  1106. unordered_set<MMKVPath_t> mmapIDSet;
  1107. unordered_set<MMKVPath_t> mmapIDCRCSet;
  1108. walkInDir(srcDir, WalkFile, [&](const MMKVPath_t &filePath, WalkType) {
  1109. if (endsWith(filePath, CRC_SUFFIX)) {
  1110. mmapIDCRCSet.insert(filePath);
  1111. } else {
  1112. mmapIDSet.insert(filePath);
  1113. }
  1114. });
  1115. size_t count = 0;
  1116. if (!mmapIDSet.empty()) {
  1117. mkPath(dstDir);
  1118. auto compareFullPath = isInSpecialDir;
  1119. for (auto &srcPath : mmapIDSet) {
  1120. auto srcCRCPath = srcPath + CRC_SUFFIX;
  1121. if (mmapIDCRCSet.find(srcCRCPath) == mmapIDCRCSet.end()) {
  1122. #ifdef MMKV_WIN32
  1123. MMKVWarning("crc not exist [%ls]", srcCRCPath.c_str());
  1124. #else
  1125. MMKVWarning("crc not exist [%s]", srcCRCPath.c_str());
  1126. #endif
  1127. continue;
  1128. }
  1129. auto basename = filename(srcPath);
  1130. const auto &strBasename = MMKVPath_t2String(basename);
  1131. auto mmapKey = isInSpecialDir ? strBasename : mmapedKVKey(strBasename, &srcDir);
  1132. auto dstPath = dstDir + MMKV_PATH_SLASH + basename;
  1133. if (backupOneToDirectory(mmapKey, dstPath, srcPath, compareFullPath)) {
  1134. count++;
  1135. }
  1136. }
  1137. }
  1138. return count;
  1139. }
  1140. size_t MMKV::backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t *srcDir) {
  1141. auto rootPath = srcDir ? srcDir : &g_rootDir;
  1142. if (*rootPath == dstDir) {
  1143. return true;
  1144. }
  1145. auto count = backupAllToDirectory(dstDir, *rootPath, false);
  1146. auto specialSrcDir = *rootPath + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
  1147. if (isFileExist(specialSrcDir)) {
  1148. auto specialDstDir = dstDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
  1149. count += backupAllToDirectory(specialDstDir, specialSrcDir, true);
  1150. }
  1151. return count;
  1152. }
  1153. // restore
  1154. static bool restoreOneFromDirectoryByFilePath(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {
  1155. auto dstCRCPath = dstPath + CRC_SUFFIX;
  1156. File dstCRCFile(std::move(dstCRCPath), OpenFlag::ReadWrite | OpenFlag::Create);
  1157. if (!dstCRCFile.isFileValid()) {
  1158. return false;
  1159. }
  1160. bool ret = false;
  1161. {
  1162. #ifdef MMKV_WIN32
  1163. MMKVInfo("restore one mmkv[%s] from [%ls] to [%ls]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1164. #else
  1165. MMKVInfo("restore one mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1166. #endif
  1167. FileLock fileLock(dstCRCFile.getFd());
  1168. InterProcessLock lock(&fileLock, ExclusiveLockType);
  1169. SCOPED_LOCK(&lock);
  1170. ret = copyFileContent(srcPath, dstPath);
  1171. if (ret) {
  1172. auto srcCRCPath = srcPath + CRC_SUFFIX;
  1173. ret = copyFileContent(srcCRCPath, dstCRCFile.getFd());
  1174. }
  1175. MMKVInfo("finish restore one mmkv[%s]", mmapKey.c_str());
  1176. }
  1177. return ret;
  1178. }
  1179. // We can't simply replace the existing file, because other processes might have already open it.
  1180. // They won't know a difference when the file has been replaced.
  1181. // We have to let them know by overriding the existing file with new content.
  1182. bool MMKV::restoreOneFromDirectory(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath, bool compareFullPath) {
  1183. // we have to lock the creation of MMKV instance, regardless of in cache or not
  1184. SCOPED_LOCK(g_instanceLock);
  1185. MMKV *kv = nullptr;
  1186. if (!compareFullPath) {
  1187. auto itr = g_instanceDic->find(mmapKey);
  1188. if (itr != g_instanceDic->end()) {
  1189. kv = itr->second;
  1190. }
  1191. } else {
  1192. // mmapKey is actually filename, we can't simply call find()
  1193. for (auto &pair : *g_instanceDic) {
  1194. if (pair.second->m_path == dstPath) {
  1195. kv = pair.second;
  1196. break;
  1197. }
  1198. }
  1199. }
  1200. // get one in cache, do it the easy way
  1201. if (kv) {
  1202. #ifdef MMKV_WIN32
  1203. MMKVInfo("restore one cached mmkv[%s] from [%ls] to [%ls]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1204. #else
  1205. MMKVInfo("restore one cached mmkv[%s] from [%s] to [%s]", mmapKey.c_str(), srcPath.c_str(), dstPath.c_str());
  1206. #endif
  1207. SCOPED_LOCK(kv->m_lock);
  1208. SCOPED_LOCK(kv->m_exclusiveProcessLock);
  1209. kv->sync();
  1210. auto ret = copyFileContent(srcPath, kv->m_file->getFd());
  1211. if (ret) {
  1212. auto srcCRCPath = srcPath + CRC_SUFFIX;
  1213. // ret = copyFileContent(srcCRCPath, kv->m_metaFile->getFd());
  1214. #ifndef MMKV_ANDROID
  1215. MemoryFile srcCRCFile(srcCRCPath);
  1216. #else
  1217. MemoryFile srcCRCFile(srcCRCPath, DEFAULT_MMAP_SIZE, MMFILE_TYPE_FILE);
  1218. #endif
  1219. if (srcCRCFile.isFileValid()) {
  1220. memcpy(kv->m_metaFile->getMemory(), srcCRCFile.getMemory(), sizeof(MMKVMetaInfo));
  1221. } else {
  1222. ret = false;
  1223. }
  1224. }
  1225. // reload data after restore
  1226. kv->clearMemoryCache();
  1227. kv->loadFromFile();
  1228. if (kv->m_isInterProcess) {
  1229. kv->notifyContentChanged();
  1230. }
  1231. MMKVInfo("finish restore one mmkv[%s], ret: %d", mmapKey.c_str(), ret);
  1232. return ret;
  1233. }
  1234. // no luck with cache, do it the hard way
  1235. bool ret = restoreOneFromDirectoryByFilePath(mmapKey, srcPath, dstPath);
  1236. return ret;
  1237. }
  1238. bool MMKV::restoreOneFromDirectory(const string &mmapID, const MMKVPath_t &srcDir, const MMKVPath_t *dstDir) {
  1239. auto rootPath = dstDir ? dstDir : &g_rootDir;
  1240. if (*rootPath == srcDir) {
  1241. return true;
  1242. }
  1243. mkPath(*rootPath);
  1244. auto encodePath = encodeFilePath(mmapID, *rootPath);
  1245. auto srcPath = srcDir + MMKV_PATH_SLASH + encodePath;
  1246. auto mmapKey = mmapedKVKey(mmapID, rootPath);
  1247. #ifdef MMKV_ANDROID
  1248. // historically Android mistakenly use mmapKey as mmapID
  1249. auto dstPath = *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapKey, *rootPath);
  1250. #else
  1251. auto dstPath = *rootPath + MMKV_PATH_SLASH + encodePath;
  1252. #endif
  1253. return restoreOneFromDirectory(mmapKey, srcPath, dstPath, false);
  1254. }
  1255. size_t MMKV::restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t &dstDir, bool isInSpecialDir) {
  1256. unordered_set<MMKVPath_t> mmapIDSet;
  1257. unordered_set<MMKVPath_t> mmapIDCRCSet;
  1258. walkInDir(srcDir, WalkFile, [&](const MMKVPath_t &filePath, WalkType) {
  1259. if (endsWith(filePath, CRC_SUFFIX)) {
  1260. mmapIDCRCSet.insert(filePath);
  1261. } else {
  1262. mmapIDSet.insert(filePath);
  1263. }
  1264. });
  1265. size_t count = 0;
  1266. if (!mmapIDSet.empty()) {
  1267. mkPath(dstDir);
  1268. auto compareFullPath = isInSpecialDir;
  1269. for (auto &srcPath : mmapIDSet) {
  1270. auto srcCRCPath = srcPath + CRC_SUFFIX;
  1271. if (mmapIDCRCSet.find(srcCRCPath) == mmapIDCRCSet.end()) {
  1272. #ifdef MMKV_WIN32
  1273. MMKVWarning("crc not exist [%ls]", srcCRCPath.c_str());
  1274. #else
  1275. MMKVWarning("crc not exist [%s]", srcCRCPath.c_str());
  1276. #endif
  1277. continue;
  1278. }
  1279. auto basename = filename(srcPath);
  1280. const auto &strBasename = MMKVPath_t2String(basename);
  1281. auto mmapKey = isInSpecialDir ? strBasename : mmapedKVKey(strBasename, &dstDir);
  1282. auto dstPath = dstDir + MMKV_PATH_SLASH + basename;
  1283. if (restoreOneFromDirectory(mmapKey, srcPath, dstPath, compareFullPath)) {
  1284. count++;
  1285. }
  1286. }
  1287. }
  1288. return count;
  1289. }
  1290. size_t MMKV::restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t *dstDir) {
  1291. auto rootPath = dstDir ? dstDir : &g_rootDir;
  1292. if (*rootPath == srcDir) {
  1293. return true;
  1294. }
  1295. auto count = restoreAllFromDirectory(srcDir, *rootPath, true);
  1296. auto specialSrcDir = srcDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
  1297. if (isFileExist(specialSrcDir)) {
  1298. auto specialDstDir = *rootPath + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
  1299. count += restoreAllFromDirectory(specialSrcDir, specialDstDir, false);
  1300. }
  1301. return count;
  1302. }
  1303. // callbacks
  1304. void MMKV::registerErrorHandler(ErrorHandler handler) {
  1305. SCOPED_LOCK(g_instanceLock);
  1306. g_errorHandler = handler;
  1307. }
  1308. void MMKV::unRegisterErrorHandler() {
  1309. SCOPED_LOCK(g_instanceLock);
  1310. g_errorHandler = nullptr;
  1311. }
  1312. void MMKV::registerLogHandler(LogHandler handler) {
  1313. SCOPED_LOCK(g_instanceLock);
  1314. g_logHandler = handler;
  1315. }
  1316. void MMKV::unRegisterLogHandler() {
  1317. SCOPED_LOCK(g_instanceLock);
  1318. g_logHandler = nullptr;
  1319. }
  1320. void MMKV::setLogLevel(MMKVLogLevel level) {
  1321. SCOPED_LOCK(g_instanceLock);
  1322. g_currentLogLevel = level;
  1323. }
  1324. static void mkSpecialCharacterFileDirectory() {
  1325. MMKVPath_t path = g_rootDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
  1326. mkPath(path);
  1327. }
  1328. template <typename T>
  1329. static string md5(const basic_string<T> &value) {
  1330. uint8_t md[MD5_DIGEST_LENGTH] = {};
  1331. char tmp[3] = {}, buf[33] = {};
  1332. openssl::MD5((const uint8_t *) value.c_str(), value.size() * (sizeof(T) / sizeof(uint8_t)), md);
  1333. for (auto ch : md) {
  1334. snprintf(tmp, sizeof(tmp), "%2.2x", ch);
  1335. strcat(buf, tmp);
  1336. }
  1337. return {buf};
  1338. }
  1339. static MMKVPath_t encodeFilePath(const string &mmapID) {
  1340. const char *specialCharacters = "\\/:*?\"<>|";
  1341. string encodedID;
  1342. bool hasSpecialCharacter = false;
  1343. for (auto ch : mmapID) {
  1344. if (strchr(specialCharacters, ch) != nullptr) {
  1345. encodedID = md5(mmapID);
  1346. hasSpecialCharacter = true;
  1347. break;
  1348. }
  1349. }
  1350. if (hasSpecialCharacter) {
  1351. static ThreadOnceToken_t once_control = ThreadOnceUninitialized;
  1352. ThreadLock::ThreadOnce(&once_control, mkSpecialCharacterFileDirectory);
  1353. return MMKVPath_t(SPECIAL_CHARACTER_DIRECTORY_NAME) + MMKV_PATH_SLASH + string2MMKVPath_t(encodedID);
  1354. } else {
  1355. return string2MMKVPath_t(mmapID);
  1356. }
  1357. }
  1358. static MMKVPath_t encodeFilePath(const string &mmapID, const MMKVPath_t &rootDir) {
  1359. const char *specialCharacters = "\\/:*?\"<>|";
  1360. string encodedID;
  1361. bool hasSpecialCharacter = false;
  1362. for (auto ch : mmapID) {
  1363. if (strchr(specialCharacters, ch) != nullptr) {
  1364. encodedID = md5(mmapID);
  1365. hasSpecialCharacter = true;
  1366. break;
  1367. }
  1368. }
  1369. if (hasSpecialCharacter) {
  1370. MMKVPath_t path = rootDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
  1371. mkPath(path);
  1372. return MMKVPath_t(SPECIAL_CHARACTER_DIRECTORY_NAME) + MMKV_PATH_SLASH + string2MMKVPath_t(encodedID);
  1373. } else {
  1374. return string2MMKVPath_t(mmapID);
  1375. }
  1376. }
  1377. string mmapedKVKey(const string &mmapID, const MMKVPath_t *rootPath) {
  1378. if (rootPath && g_rootDir != (*rootPath)) {
  1379. return md5(*rootPath + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID));
  1380. }
  1381. return mmapID;
  1382. }
  1383. MMKVPath_t mappedKVPathWithID(const string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath) {
  1384. #ifndef MMKV_ANDROID
  1385. if (rootPath) {
  1386. #else
  1387. if (mode & MMKV_ASHMEM) {
  1388. return ashmemMMKVPathWithID(encodeFilePath(mmapID));
  1389. } else if (rootPath) {
  1390. #endif
  1391. return *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID);
  1392. }
  1393. return g_rootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID);
  1394. }
  1395. MMKVPath_t crcPathWithID(const string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath) {
  1396. #ifndef MMKV_ANDROID
  1397. if (rootPath) {
  1398. #else
  1399. if (mode & MMKV_ASHMEM) {
  1400. return ashmemMMKVPathWithID(encodeFilePath(mmapID)) + CRC_SUFFIX;
  1401. } else if (rootPath) {
  1402. #endif
  1403. return *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID) + CRC_SUFFIX;
  1404. }
  1405. return g_rootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID) + CRC_SUFFIX;
  1406. }
  1407. MMKVRecoverStrategic onMMKVCRCCheckFail(const string &mmapID) {
  1408. if (g_errorHandler) {
  1409. return g_errorHandler(mmapID, MMKVErrorType::MMKVCRCCheckFail);
  1410. }
  1411. return OnErrorDiscard;
  1412. }
  1413. MMKVRecoverStrategic onMMKVFileLengthError(const string &mmapID) {
  1414. if (g_errorHandler) {
  1415. return g_errorHandler(mmapID, MMKVErrorType::MMKVFileLength);
  1416. }
  1417. return OnErrorDiscard;
  1418. }
  1419. MMKV_NAMESPACE_END