diff --git a/android/app/build.gradle b/android/app/build.gradle
index 3eaaf01b..c202892a 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -240,6 +240,7 @@ dependencies {
implementation "com.google.code.gson:gson:2.8.5"
implementation "com.github.bumptech.glide:glide:4.9.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"
+ implementation "com.tencent:mmkv-static:1.2.1"
}
// Run this once to be able to run the application with BUCK
diff --git a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java b/android/app/src/main/java/chat/rocket/reactnative/Ejson.java
index 0c68e475..233d5f15 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/Ejson.java
@@ -1,8 +1,28 @@
package chat.rocket.reactnative;
-import android.content.SharedPreferences;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.Callback;
-import chat.rocket.userdefaults.RNUserDefaultsModule;
+import com.ammarahmed.mmkv.SecureKeystore;
+import com.tencent.mmkv.MMKV;
+
+import java.math.BigInteger;
+
+class RNCallback implements Callback {
+ public void invoke(Object... args) {
+
+ }
+}
+
+class Utils {
+ static public String toHex(String arg) {
+ try {
+ return String.format("%x", new BigInteger(1, arg.getBytes("UTF-8")));
+ } catch (Exception e) {
+ return "";
+ }
+ }
+}
public class Ejson {
String host;
@@ -12,8 +32,32 @@ public class Ejson {
String messageId;
String notificationType;
+ private MMKV mmkv;
+
private String TOKEN_KEY = "reactnativemeteor_usertoken-";
- private SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext);
+
+ public Ejson() {
+ ReactApplicationContext reactApplicationContext = CustomPushNotification.reactApplicationContext;
+
+ // Start MMKV container
+ MMKV.initialize(reactApplicationContext);
+ SecureKeystore secureKeystore = new SecureKeystore(reactApplicationContext);
+
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
+ String alias = Utils.toHex("com.MMKV.default");
+
+ // Retrieve container password
+ secureKeystore.getSecureKey(alias, new RNCallback() {
+ @Override
+ public void invoke(Object... args) {
+ String error = (String) args[0];
+ if (error == null) {
+ String password = (String) args[1];
+ mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
+ }
+ }
+ });
+ }
public String getAvatarUri() {
if (type == null) {
@@ -23,11 +67,17 @@ public class Ejson {
}
public String token() {
- return sharedPreferences.getString(TOKEN_KEY.concat(userId()), "");
+ if (mmkv != null) {
+ return mmkv.decodeString(TOKEN_KEY.concat(userId()));
+ }
+ return "";
}
public String userId() {
- return sharedPreferences.getString(TOKEN_KEY.concat(serverURL()), "");
+ if (mmkv != null) {
+ return mmkv.decodeString(TOKEN_KEY.concat(serverURL()));
+ }
+ return "";
}
public String serverURL() {
diff --git a/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java b/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java
index 9c4c0374..83ebfe8b 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java
@@ -15,8 +15,6 @@ import java.io.IOException;
import com.facebook.react.bridge.ReactApplicationContext;
-import chat.rocket.userdefaults.RNUserDefaultsModule;
-
class JsonResponse {
Data data;
diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
index 75922913..9f76fb34 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
@@ -1,13 +1,33 @@
package chat.rocket.reactnative;
-import com.facebook.react.ReactActivityDelegate;
-import com.facebook.react.ReactRootView;
-import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import android.os.Bundle;
-import com.facebook.react.ReactFragmentActivity;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.SharedPreferences;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.ReactRootView;
+import com.facebook.react.ReactActivityDelegate;
+import com.facebook.react.ReactFragmentActivity;
+
+import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import com.zoontek.rnbootsplash.RNBootSplash;
+import com.tencent.mmkv.MMKV;
+import com.google.gson.Gson;
+
+class ThemePreferences {
+ String currentTheme;
+ String darkLevel;
+}
+
+class SortPreferences {
+ String sortBy;
+ Boolean groupByType;
+ Boolean showFavorites;
+ Boolean showUnread;
+}
public class MainActivity extends ReactFragmentActivity {
@@ -16,6 +36,55 @@ public class MainActivity extends ReactFragmentActivity {
// https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704067
super.onCreate(null);
RNBootSplash.init(R.drawable.launch_screen, MainActivity.this);
+
+ MMKV.initialize(MainActivity.this);
+
+ // Start the MMKV container
+ MMKV defaultMMKV = MMKV.defaultMMKV();
+ boolean alreadyMigrated = defaultMMKV.decodeBool("alreadyMigrated");
+
+ if (!alreadyMigrated) {
+ // MMKV Instance that will be used by JS
+ MMKV mmkv = MMKV.mmkvWithID("default");
+
+ // SharedPreferences -> MMKV (Migration)
+ SharedPreferences sharedPreferences = getSharedPreferences("react-native", Context.MODE_PRIVATE);
+ mmkv.importFromSharedPreferences(sharedPreferences);
+
+ // SharedPreferences only save strings, so we saved this value as a String and now we'll need to cast into a MMKV object
+
+ // Theme preferences object
+ String THEME_PREFERENCES_KEY = "RC_THEME_PREFERENCES_KEY";
+ String themeJson = sharedPreferences.getString(THEME_PREFERENCES_KEY, "");
+ if (!themeJson.isEmpty()) {
+ ThemePreferences themePreferences = new Gson().fromJson(themeJson, ThemePreferences.class);
+ WritableMap themeMap = new Arguments().createMap();
+ themeMap.putString("currentTheme", themePreferences.currentTheme);
+ themeMap.putString("darkLevel", themePreferences.darkLevel);
+ Bundle bundle = Arguments.toBundle(themeMap);
+ mmkv.encode(THEME_PREFERENCES_KEY, bundle);
+ }
+
+ // Sort preferences object
+ String SORT_PREFS_KEY = "RC_SORT_PREFS_KEY";
+ String sortJson = sharedPreferences.getString(SORT_PREFS_KEY, "");
+ if (!sortJson.isEmpty()) {
+ SortPreferences sortPreferences = new Gson().fromJson(sortJson, SortPreferences.class);
+ WritableMap sortMap = new Arguments().createMap();
+ sortMap.putString("sortBy", sortPreferences.sortBy);
+ sortMap.putBoolean("groupByType", sortPreferences.groupByType);
+ sortMap.putBoolean("showFavorites", sortPreferences.showFavorites);
+ sortMap.putBoolean("showUnread", sortPreferences.showUnread);
+ Bundle bundle = Arguments.toBundle(sortMap);
+ mmkv.encode(SORT_PREFS_KEY, bundle);
+ }
+
+ // Remove all our keys of SharedPreferences
+ sharedPreferences.edit().clear().commit();
+
+ // Mark migration complete
+ defaultMMKV.encode("alreadyMigrated", true);
+ }
}
/**
diff --git a/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java b/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java
index 81c62aeb..7f266b06 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java
@@ -25,7 +25,6 @@ import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
-import chat.rocket.userdefaults.RNUserDefaultsModule;
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
public class ReplyBroadcast extends BroadcastReceiver {
diff --git a/app/constants/userDefaults.js b/app/constants/userDefaults.js
deleted file mode 100644
index e21b7b85..00000000
--- a/app/constants/userDefaults.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export const SERVERS = 'kServers';
-export const TOKEN = 'kAuthToken';
-export const USER_ID = 'kUserId';
-export const SERVER_URL = 'kAuthServerURL';
-export const SERVER_NAME = 'kServerName';
-export const SERVER_ICON = 'kServerIconURL';
diff --git a/app/containers/Passcode/PasscodeEnter.js b/app/containers/Passcode/PasscodeEnter.js
index 3721b9fe..ebd22f36 100644
--- a/app/containers/Passcode/PasscodeEnter.js
+++ b/app/containers/Passcode/PasscodeEnter.js
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from 'react';
import { useAsyncStorage } from '@react-native-community/async-storage';
-import RNUserDefaults from 'rn-user-defaults';
import PropTypes from 'prop-types';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics';
@@ -14,6 +13,7 @@ import {
} from '../../constants/localAuthentication';
import { resetAttempts, biometryAuth } from '../../utils/localAuthentication';
import { getLockedUntil, getDiff } from './utils';
+import UserPreferences from '../../lib/userPreferences';
import I18n from '../../i18n';
const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
@@ -26,7 +26,7 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY);
const fetchPasscode = async() => {
- const p = await RNUserDefaults.get(PASSCODE_KEY);
+ const p = await UserPreferences.getStringAsync(PASSCODE_KEY);
setPasscode(p);
};
diff --git a/app/index.js b/app/index.js
index e4d9e52c..60fd2c93 100644
--- a/app/index.js
+++ b/app/index.js
@@ -2,7 +2,6 @@ import React from 'react';
import { Linking, Dimensions } from 'react-native';
import { AppearanceProvider } from 'react-native-appearance';
import { Provider } from 'react-redux';
-import RNUserDefaults from 'rn-user-defaults';
import { KeyCommandsEmitter } from 'react-native-keycommands';
import RNScreens from 'react-native-screens';
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
@@ -13,6 +12,7 @@ import {
subscribeTheme,
unsubscribeTheme
} from './utils/theme';
+import UserPreferences from './lib/userPreferences';
import EventEmitter from './utils/events';
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
import { deepLinkingOpen } from './actions/deepLinking';
@@ -37,7 +37,6 @@ import InAppNotification from './containers/InAppNotification';
import { ActionSheetProvider } from './containers/ActionSheet';
import debounce from './utils/debounce';
-
RNScreens.enableScreens();
const parseDeepLinking = (url) => {
@@ -106,7 +105,7 @@ export default class Root extends React.Component {
}
init = async() => {
- RNUserDefaults.objectForKey(THEME_PREFERENCES_KEY).then(this.setTheme);
+ UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme);
const [notification, deepLinking] = await Promise.all([initializePushNotifications(), Linking.getInitialURL()]);
const parsedDeepLinkingURL = parseDeepLinking(deepLinking);
store.dispatch(appInitLocalSettings());
diff --git a/app/lib/methods/logout.js b/app/lib/methods/logout.js
index 4064362d..a7c69146 100644
--- a/app/lib/methods/logout.js
+++ b/app/lib/methods/logout.js
@@ -1,30 +1,26 @@
-import RNUserDefaults from 'rn-user-defaults';
import * as FileSystem from 'expo-file-system';
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
-import { SERVERS, SERVER_URL } from '../../constants/userDefaults';
import { getDeviceToken } from '../../notifications/push';
import { extractHostname } from '../../utils/server';
import { BASIC_AUTH_KEY } from '../../utils/fetch';
import database, { getDatabase } from '../database';
import RocketChat from '../rocketchat';
import { useSsl } from '../../utils/url';
+import UserPreferences from '../userPreferences';
async function removeServerKeys({ server, userId }) {
- await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ server }`);
- await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ userId }`);
- await RNUserDefaults.clear(`${ BASIC_AUTH_KEY }-${ server }`);
+ await UserPreferences.removeItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ await UserPreferences.removeItem(`${ RocketChat.TOKEN_KEY }-${ userId }`);
+ await UserPreferences.removeItem(`${ BASIC_AUTH_KEY }-${ server }`);
}
async function removeSharedCredentials({ server }) {
+ // clear certificate for server - SSL Pinning
try {
- const servers = await RNUserDefaults.objectForKey(SERVERS);
- await RNUserDefaults.setObjectForKey(SERVERS, servers && servers.filter(srv => srv[SERVER_URL] !== server));
-
- // clear certificate for server - SSL Pinning
- const certificate = await RNUserDefaults.objectForKey(extractHostname(server));
+ const certificate = await UserPreferences.getMapAsync(extractHostname(server));
if (certificate && certificate.path) {
- await RNUserDefaults.clear(extractHostname(server));
+ await UserPreferences.removeItem(extractHostname(server));
await FileSystem.deleteAsync(certificate.path);
}
} catch (e) {
@@ -36,7 +32,7 @@ async function removeServerData({ server }) {
try {
const batch = [];
const serversDB = database.servers;
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const usersCollection = serversDB.collections.get('users');
if (userId) {
@@ -56,8 +52,8 @@ async function removeServerData({ server }) {
}
async function removeCurrentServer() {
- await RNUserDefaults.clear('currentServer');
- await RNUserDefaults.clear(RocketChat.TOKEN_KEY);
+ await UserPreferences.removeItem(RocketChat.CURRENT_SERVER);
+ await UserPreferences.removeItem(RocketChat.TOKEN_KEY);
}
async function removeServerDatabase({ server }) {
@@ -71,9 +67,9 @@ async function removeServerDatabase({ server }) {
export async function removeServer({ server }) {
try {
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (userId) {
- const resume = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ userId }`);
+ const resume = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ userId }`);
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
await sdk.login({ resume });
@@ -89,7 +85,7 @@ export async function removeServer({ server }) {
await removeServerData({ server });
await removeServerDatabase({ server });
} catch (e) {
- console.log('removePush', e);
+ console.log('removeServer', e);
}
}
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index dac3a378..0d6af725 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -1,7 +1,6 @@
import { InteractionManager } from 'react-native';
import semver from 'semver';
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
-import RNUserDefaults from 'rn-user-defaults';
import { Q } from '@nozbe/watermelondb';
import AsyncStorage from '@react-native-community/async-storage';
@@ -51,8 +50,10 @@ import I18n from '../i18n';
import { twoFactor } from '../utils/twoFactor';
import { selectServerFailure } from '../actions/server';
import { useSsl } from '../utils/url';
+import UserPreferences from './userPreferences';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
+const CURRENT_SERVER = 'currentServer';
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
@@ -63,6 +64,7 @@ const STATUSES = ['offline', 'online', 'away', 'busy'];
const RocketChat = {
TOKEN_KEY,
+ CURRENT_SERVER,
callJitsi,
async subscribeRooms() {
if (!this.roomsSub) {
@@ -89,13 +91,6 @@ const RocketChat = {
// RC 0.51.0
return this.methodCallWrapper(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast });
},
- async getUserToken() {
- try {
- return await RNUserDefaults.get(TOKEN_KEY);
- } catch (error) {
- console.warn(`RNUserDefaults error: ${ error.message }`);
- }
- },
async getWebsocketInfo({ server }) {
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
@@ -307,7 +302,7 @@ const RocketChat = {
// set User info
try {
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const userCollections = serversDB.collections.get('users');
let user = null;
if (userId) {
@@ -1062,17 +1057,13 @@ const RocketChat = {
return JSON.parse(allowCrashReport);
},
async getSortPreferences() {
- const prefs = await RNUserDefaults.objectForKey(SORT_PREFS_KEY);
+ const prefs = await UserPreferences.getMapAsync(SORT_PREFS_KEY);
return prefs;
},
async saveSortPreference(param) {
- try {
- let prefs = await RocketChat.getSortPreferences();
- prefs = { ...prefs, ...param };
- return await RNUserDefaults.setObjectForKey(SORT_PREFS_KEY, prefs);
- } catch (error) {
- console.warn(error);
- }
+ let prefs = await RocketChat.getSortPreferences();
+ prefs = { ...prefs, ...param };
+ return UserPreferences.setMapAsync(SORT_PREFS_KEY, prefs);
},
async getLoginServices(server) {
try {
diff --git a/app/lib/userPreferences.js b/app/lib/userPreferences.js
new file mode 100644
index 00000000..633a5198
--- /dev/null
+++ b/app/lib/userPreferences.js
@@ -0,0 +1,80 @@
+import MMKVStorage from 'react-native-mmkv-storage';
+
+import log from '../utils/log';
+
+const MMKV = new MMKVStorage.Loader()
+ // MODES.MULTI_PROCESS = ACCESSIBLE BY APP GROUP (iOS)
+ .setProcessingMode(MMKVStorage.MODES.MULTI_PROCESS)
+ .withEncryption()
+ .initialize();
+
+class UserPreferences {
+ constructor() {
+ this.mmkv = MMKV;
+
+ this.encryptMigratedData();
+ }
+
+ // It should run only once
+ async encryptMigratedData() {
+ try {
+ const encryptMigration = await this.getBoolAsync('encryptMigration');
+
+ if (!encryptMigration) {
+ // Encrypt the migrated data
+ await this.mmkv.encryption.encrypt();
+
+ // Mark as completed
+ await this.setBoolAsync('encryptMigration', true);
+ }
+ } catch (e) {
+ log(e);
+ }
+ }
+
+ async getStringAsync(key) {
+ try {
+ const value = await this.mmkv.getStringAsync(key);
+ return value;
+ } catch {
+ return null;
+ }
+ }
+
+ setStringAsync(key, value) {
+ return this.mmkv.setStringAsync(key, value);
+ }
+
+ async getBoolAsync(key) {
+ try {
+ const value = await this.mmkv.getBoolAsync(key);
+ return value;
+ } catch {
+ return null;
+ }
+ }
+
+ setBoolAsync(key, value) {
+ return this.mmkv.setBoolAsync(key, value);
+ }
+
+ async getMapAsync(key) {
+ try {
+ const value = await this.mmkv.getMapAsync(key);
+ return value;
+ } catch {
+ return null;
+ }
+ }
+
+ setMapAsync(key, value) {
+ return this.mmkv.setMapAsync(key, value);
+ }
+
+ removeItem(key) {
+ return this.mmkv.removeItem(key);
+ }
+}
+
+const userPreferences = new UserPreferences();
+export default userPreferences;
diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js
index b650a06a..4e30ddfe 100644
--- a/app/sagas/deepLinking.js
+++ b/app/sagas/deepLinking.js
@@ -1,8 +1,8 @@
import {
takeLatest, take, select, put, all, delay
} from 'redux-saga/effects';
-import RNUserDefaults from 'rn-user-defaults';
+import UserPreferences from '../lib/userPreferences';
import Navigation from '../lib/Navigation';
import * as types from '../actions/actionsTypes';
import { selectServerRequest, serverInitAdd } from '../actions/server';
@@ -105,8 +105,8 @@ const handleOpen = function* handleOpen({ params }) {
}
const [server, user] = yield all([
- RNUserDefaults.get('currentServer'),
- RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ host }`)
+ UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER),
+ UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ host }`)
]);
// TODO: needs better test
diff --git a/app/sagas/init.js b/app/sagas/init.js
index 25c77da0..86f4dd26 100644
--- a/app/sagas/init.js
+++ b/app/sagas/init.js
@@ -1,21 +1,14 @@
import { put, takeLatest, all } from 'redux-saga/effects';
-import RNUserDefaults from 'rn-user-defaults';
-import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import RNBootSplash from 'react-native-bootsplash';
-import AsyncStorage from '@react-native-community/async-storage';
+import UserPreferences from '../lib/userPreferences';
import { selectServerRequest } from '../actions/server';
import { setAllPreferences } from '../actions/sortPreferences';
import { toggleCrashReport } from '../actions/crashReport';
import { APP } from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
-import {
- SERVERS, SERVER_ICON, SERVER_NAME, SERVER_URL, TOKEN, USER_ID
-} from '../constants/userDefaults';
-import { isIOS } from '../utils/deviceInfo';
import database from '../lib/database';
-import protectedFunction from '../lib/methods/helpers/protectedFunction';
import { localAuthenticate } from '../utils/localAuthentication';
import { appStart, ROOT_OUTSIDE, appReady } from '../actions/app';
@@ -29,71 +22,15 @@ export const initLocalSettings = function* initLocalSettings() {
const restore = function* restore() {
try {
- let hasMigration;
- if (isIOS) {
- hasMigration = yield AsyncStorage.getItem('hasMigration');
- }
-
- let { token, server } = yield all({
- token: RNUserDefaults.get(RocketChat.TOKEN_KEY),
- server: RNUserDefaults.get('currentServer')
+ const { token, server } = yield all({
+ token: UserPreferences.getStringAsync(RocketChat.TOKEN_KEY),
+ server: UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER)
});
- if (!hasMigration && isIOS) {
- let servers = yield RNUserDefaults.objectForKey(SERVERS);
- // if not have current
- if (servers && servers.length !== 0 && (!token || !server)) {
- server = servers[0][SERVER_URL];
- token = servers[0][TOKEN];
- }
-
- // get native credentials
- if (servers) {
- try {
- // parse servers
- servers = yield Promise.all(servers.map(async(s) => {
- await RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ s[SERVER_URL] }`, s[USER_ID]);
- return ({ id: s[SERVER_URL], name: s[SERVER_NAME], iconURL: s[SERVER_ICON] });
- }));
- const serversDB = database.servers;
- yield serversDB.action(async() => {
- const serversCollection = serversDB.collections.get('servers');
- const allServerRecords = await serversCollection.query().fetch();
-
- // filter servers
- let serversToCreate = servers.filter(i1 => !allServerRecords.find(i2 => i1.id === i2.id));
-
- // Create
- serversToCreate = serversToCreate.map(record => serversCollection.prepareCreate(protectedFunction((s) => {
- s._raw = sanitizedRaw({ id: record.id }, serversCollection.schema);
- Object.assign(s, record);
- })));
-
- const allRecords = serversToCreate;
-
- try {
- await serversDB.batch(...allRecords);
- } catch (e) {
- log(e);
- }
- return allRecords.length;
- });
- } catch (e) {
- log(e);
- }
- }
-
- try {
- yield AsyncStorage.setItem('hasMigration', '1');
- } catch (e) {
- log(e);
- }
- }
-
if (!token || !server) {
yield all([
- RNUserDefaults.clear(RocketChat.TOKEN_KEY),
- RNUserDefaults.clear('currentServer')
+ UserPreferences.removeItem(RocketChat.TOKEN_KEY),
+ UserPreferences.removeItem(RocketChat.CURRENT_SERVER)
]);
yield put(appStart({ root: ROOT_OUTSIDE }));
} else {
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 01ab2845..4bc9aeae 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -1,7 +1,6 @@
import {
put, call, takeLatest, select, take, fork, cancel, race, delay
} from 'redux-saga/effects';
-import RNUserDefaults from 'rn-user-defaults';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import moment from 'moment';
import 'moment/min/locales';
@@ -26,6 +25,7 @@ import { inviteLinksRequest } from '../actions/inviteLinks';
import { showErrorAlert } from '../utils/info';
import { localAuthenticate } from '../utils/localAuthentication';
import { setActiveUsers } from '../actions/activeUsers';
+import UserPreferences from '../lib/userPreferences';
const getServer = state => state.server.server;
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
@@ -88,7 +88,7 @@ const fetchUsersPresence = function* fetchUserPresence() {
const handleLoginSuccess = function* handleLoginSuccess({ user }) {
try {
const adding = yield select(state => state.server.adding);
- yield RNUserDefaults.set(RocketChat.TOKEN_KEY, user.token);
+ yield UserPreferences.setStringAsync(RocketChat.TOKEN_KEY, user.token);
RocketChat.getUserPresence(user.id);
@@ -131,8 +131,8 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
}
});
- yield RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ server }`, user.id);
- yield RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ user.id }`, user.token);
+ yield UserPreferences.setStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`, user.id);
+ yield UserPreferences.setStringAsync(`${ RocketChat.TOKEN_KEY }-${ user.id }`, user.token);
yield put(setUser(user));
EventEmitter.emit('connected');
@@ -182,9 +182,10 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
if (servers.length > 0) {
for (let i = 0; i < servers.length; i += 1) {
const newServer = servers[i].id;
- const token = yield RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ newServer }`);
+ const token = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ newServer }`);
if (token) {
- return yield put(selectServerRequest(newServer));
+ yield put(selectServerRequest(newServer));
+ return;
}
}
}
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index 51728b8a..c3b883c3 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -1,6 +1,5 @@
import { put, takeLatest } from 'redux-saga/effects';
import { Alert } from 'react-native';
-import RNUserDefaults from 'rn-user-defaults';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import semver from 'semver';
@@ -16,9 +15,9 @@ import database from '../lib/database';
import log, { logServerVersion } from '../utils/log';
import { extractHostname } from '../utils/server';
import I18n from '../i18n';
-import { SERVERS, TOKEN, SERVER_URL } from '../constants/userDefaults';
import { BASIC_AUTH_KEY, setBasicAuth } from '../utils/fetch';
import { appStart, ROOT_INSIDE, ROOT_OUTSIDE } from '../actions/app';
+import UserPreferences from '../lib/userPreferences';
import { inquiryReset } from '../actions/inquiry';
const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
@@ -68,8 +67,8 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
try {
yield put(inquiryReset());
const serversDB = database.servers;
- yield RNUserDefaults.set('currentServer', server);
- const userId = yield RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server);
+ const userId = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const userCollections = serversDB.collections.get('users');
let user = null;
if (userId) {
@@ -85,17 +84,12 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
statusText: userRecord.statusText,
roles: userRecord.roles
};
- } catch (e) {
- // We only run it if not has user on DB
- const servers = yield RNUserDefaults.objectForKey(SERVERS);
- const userCredentials = servers && servers.find(srv => srv[SERVER_URL] === server);
- user = userCredentials && {
- token: userCredentials[TOKEN]
- };
+ } catch {
+ // Do nothing
}
}
- const basicAuth = yield RNUserDefaults.get(`${ BASIC_AUTH_KEY }-${ server }`);
+ const basicAuth = yield UserPreferences.getStringAsync(`${ BASIC_AUTH_KEY }-${ server }`);
setBasicAuth(basicAuth);
// Check for running requests and abort them before connecting to the server
@@ -136,7 +130,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
const handleServerRequest = function* handleServerRequest({ server, certificate }) {
try {
if (certificate) {
- yield RNUserDefaults.setObjectForKey(extractHostname(server), certificate);
+ yield UserPreferences.setMapAsync(extractHostname(server), certificate);
}
const serverInfo = yield getServerInfo({ server });
diff --git a/app/share.js b/app/share.js
index 93840b82..d1f83fd8 100644
--- a/app/share.js
+++ b/app/share.js
@@ -5,7 +5,6 @@ import { NavigationContainer } from '@react-navigation/native';
import { AppearanceProvider } from 'react-native-appearance';
import { createStackNavigator } from '@react-navigation/stack';
import { Provider } from 'react-redux';
-import RNUserDefaults from 'rn-user-defaults';
import {
defaultTheme,
@@ -13,6 +12,7 @@ import {
subscribeTheme,
unsubscribeTheme
} from './utils/theme';
+import UserPreferences from './lib/userPreferences';
import Navigation from './lib/ShareNavigation';
import store from './lib/createStore';
import { supportSystemTheme } from './utils/deviceInfo';
@@ -138,9 +138,9 @@ class Root extends React.Component {
}
init = async() => {
- RNUserDefaults.objectForKey(THEME_PREFERENCES_KEY).then(this.setTheme);
- const currentServer = await RNUserDefaults.get('currentServer');
- const token = await RNUserDefaults.get(RocketChat.TOKEN_KEY);
+ UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme);
+
+ const [currentServer, token] = await Promise.all([UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER), UserPreferences.getStringAsync(RocketChat.TOKEN_KEY)]);
if (currentServer && token) {
await localAuthenticate(currentServer);
diff --git a/app/utils/localAuthentication.js b/app/utils/localAuthentication.js
index 37c03fe3..2eb9c7d2 100644
--- a/app/utils/localAuthentication.js
+++ b/app/utils/localAuthentication.js
@@ -2,9 +2,9 @@ import * as LocalAuthentication from 'expo-local-authentication';
import moment from 'moment';
import RNBootSplash from 'react-native-bootsplash';
import AsyncStorage from '@react-native-community/async-storage';
-import RNUserDefaults from 'rn-user-defaults';
import { sha256 } from 'js-sha256';
+import UserPreferences from '../lib/userPreferences';
import store from '../lib/createStore';
import database from '../lib/database';
import { isIOS } from './deviceInfo';
@@ -51,7 +51,7 @@ const openChangePasscodeModal = ({ force }) => new Promise((resolve, reject) =>
export const changePasscode = async({ force = false }) => {
const passcode = await openChangePasscodeModal({ force });
- await RNUserDefaults.set(PASSCODE_KEY, sha256(passcode));
+ await UserPreferences.setStringAsync(PASSCODE_KEY, sha256(passcode));
};
export const biometryAuth = force => LocalAuthentication.authenticateAsync({
@@ -80,7 +80,7 @@ const checkBiometry = async(serverRecord) => {
};
export const checkHasPasscode = async({ force = true, serverRecord }) => {
- const storedPasscode = await RNUserDefaults.get(PASSCODE_KEY);
+ const storedPasscode = await UserPreferences.getStringAsync(PASSCODE_KEY);
if (!storedPasscode) {
await changePasscode({ force });
await checkBiometry(serverRecord);
diff --git a/app/utils/openLink.js b/app/utils/openLink.js
index 9341693a..36f72c0e 100644
--- a/app/utils/openLink.js
+++ b/app/utils/openLink.js
@@ -1,8 +1,8 @@
import { Linking } from 'react-native';
import * as WebBrowser from 'expo-web-browser';
-import RNUserDefaults from 'rn-user-defaults';
import parse from 'url-parse';
+import UserPreferences from '../lib/userPreferences';
import { themes } from '../constants/colors';
export const DEFAULT_BROWSER_KEY = 'DEFAULT_BROWSER_KEY';
@@ -37,7 +37,7 @@ const appSchemeURL = (url, browser) => {
const openLink = async(url, theme = 'light') => {
try {
- const browser = await RNUserDefaults.get(DEFAULT_BROWSER_KEY);
+ const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
if (browser) {
const schemeUrl = appSchemeURL(url, browser.replace(':', ''));
diff --git a/app/views/DefaultBrowserView.js b/app/views/DefaultBrowserView.js
index e51b11f0..d5c14be5 100644
--- a/app/views/DefaultBrowserView.js
+++ b/app/views/DefaultBrowserView.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import {
StyleSheet, FlatList, View, Text, Linking
} from 'react-native';
-import RNUserDefaults from 'rn-user-defaults';
import I18n from '../i18n';
import { withTheme } from '../theme';
@@ -16,6 +15,7 @@ import { CustomIcon } from '../lib/Icons';
import { DEFAULT_BROWSER_KEY } from '../utils/openLink';
import { isIOS } from '../utils/deviceInfo';
import SafeAreaView from '../containers/SafeAreaView';
+import UserPreferences from '../lib/userPreferences';
import { logEvent, events } from '../utils/log';
const DEFAULT_BROWSERS = [
@@ -81,12 +81,8 @@ class DefaultBrowserView extends React.Component {
async componentDidMount() {
this.mounted = true;
- try {
- const browser = await RNUserDefaults.get(DEFAULT_BROWSER_KEY);
- this.setState({ browser });
- } catch {
- // do nothing
- }
+ const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
+ this.setState({ browser });
}
init = () => {
@@ -117,7 +113,7 @@ class DefaultBrowserView extends React.Component {
logEvent(events.DB_CHANGE_DEFAULT_BROWSER, { browser: newBrowser });
try {
const browser = newBrowser !== 'inApp' ? newBrowser : null;
- await RNUserDefaults.set(DEFAULT_BROWSER_KEY, browser);
+ await UserPreferences.setStringAsync(DEFAULT_BROWSER_KEY, browser);
this.setState({ browser });
} catch {
logEvent(events.DB_CHANGE_DEFAULT_BROWSER_F);
diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js
index ec122939..01ab944c 100644
--- a/app/views/NewServerView.js
+++ b/app/views/NewServerView.js
@@ -6,10 +6,10 @@ import {
import { connect } from 'react-redux';
import * as FileSystem from 'expo-file-system';
import DocumentPicker from 'react-native-document-picker';
-import RNUserDefaults from 'rn-user-defaults';
import { Base64 } from 'js-base64';
import parse from 'url-parse';
+import UserPreferences from '../lib/userPreferences';
import EventEmitter from '../utils/events';
import { selectServerRequest, serverRequest } from '../actions/server';
import { inviteLinksClear as inviteLinksClearAction } from '../actions/inviteLinks';
@@ -180,7 +180,7 @@ class NewServerView extends React.Component {
const parsedUrl = parse(text, true);
if (parsedUrl.auth.length) {
const credentials = Base64.encode(parsedUrl.auth);
- await RNUserDefaults.set(`${ BASIC_AUTH_KEY }-${ server }`, credentials);
+ await UserPreferences.setStringAsync(`${ BASIC_AUTH_KEY }-${ server }`, credentials);
setBasicAuth(credentials);
}
} catch {
diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js
index afeb15ee..7eeda332 100644
--- a/app/views/RoomsListView/ServerDropdown.js
+++ b/app/views/RoomsListView/ServerDropdown.js
@@ -5,7 +5,6 @@ import {
import PropTypes from 'prop-types';
import { connect, batch } from 'react-redux';
import equal from 'deep-equal';
-import RNUserDefaults from 'rn-user-defaults';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import { toggleServerDropdown as toggleServerDropdownAction } from '../../actions/rooms';
@@ -26,6 +25,7 @@ import { showConfirmationAlert } from '../../utils/info';
import { logEvent, events } from '../../utils/log';
import { headerHeight } from '../../containers/Header';
import { goRoom } from '../../utils/goRoom';
+import UserPreferences from '../../lib/userPreferences';
const ROW_HEIGHT = 68;
const ANIMATION_DURATION = 200;
@@ -150,7 +150,7 @@ class ServerDropdown extends Component {
this.close();
if (currentServer !== server) {
logEvent(events.RL_CHANGE_SERVER);
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (isMasterDetail) {
goRoom({ item: {}, isMasterDetail });
}
diff --git a/app/views/ThemeView.js b/app/views/ThemeView.js
index e6e07d1f..2686e3b6 100644
--- a/app/views/ThemeView.js
+++ b/app/views/ThemeView.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import {
FlatList, Text, View, StyleSheet
} from 'react-native';
-import RNUserDefaults from 'rn-user-defaults';
import I18n from '../i18n';
import { withTheme } from '../theme';
@@ -16,6 +15,7 @@ import { CustomIcon } from '../lib/Icons';
import { THEME_PREFERENCES_KEY } from '../lib/rocketchat';
import { supportSystemTheme } from '../utils/deviceInfo';
import SafeAreaView from '../containers/SafeAreaView';
+import UserPreferences from '../lib/userPreferences';
import { events, logEvent } from '../utils/log';
const THEME_GROUP = 'THEME_GROUP';
@@ -111,7 +111,7 @@ class ThemeView extends React.Component {
const { setTheme, themePreferences } = this.props;
const newTheme = { ...themePreferences, ...theme };
setTheme(newTheme);
- await RNUserDefaults.setObjectForKey(THEME_PREFERENCES_KEY, newTheme);
+ await UserPreferences.setMapAsync(THEME_PREFERENCES_KEY, newTheme);
};
renderSeparator = () => {
diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist
index 496dfc6c..5705853e 100644
--- a/ios/NotificationService/Info.plist
+++ b/ios/NotificationService/Info.plist
@@ -2,6 +2,8 @@
+ KeychainGroup
+ $(AppIdentifierPrefix)chat.rocket.reactnative
AppGroup
group.ios.chat.rocket
CFBundleDevelopmentRegion
diff --git a/ios/NotificationService/NotificationService-Bridging-Header.h b/ios/NotificationService/NotificationService-Bridging-Header.h
new file mode 100644
index 00000000..cf9f1020
--- /dev/null
+++ b/ios/NotificationService/NotificationService-Bridging-Header.h
@@ -0,0 +1,6 @@
+//
+// Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import "SecureStorage.h"
+#import
diff --git a/ios/NotificationService/NotificationService.entitlements b/ios/NotificationService/NotificationService.entitlements
index f48f06fb..4bfdb2c2 100644
--- a/ios/NotificationService/NotificationService.entitlements
+++ b/ios/NotificationService/NotificationService.entitlements
@@ -6,5 +6,9 @@
group.ios.chat.rocket
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)chat.rocket.reactnative
+
diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift
index bb5352a5..73b49d80 100644
--- a/ios/NotificationService/NotificationService.swift
+++ b/ios/NotificationService/NotificationService.swift
@@ -60,8 +60,30 @@ class NotificationService: UNNotificationServiceExtension {
return
}
+ let mmapID = "default"
+ let instanceID = "com.MMKV.\(mmapID)"
+ let secureStorage = SecureStorage()
+ var cryptKey: Data = Data()
+ // get mmkv instance password from keychain
+ secureStorage.getSecureKey(instanceID.toHex()) { (response) -> () in
+ guard let password = response?[1] as? String else {
+ // kill the process and show notification as it came from APN
+ exit(0)
+ }
+ cryptKey = password.data(using: .utf8)!
+ }
+
+ // Get App Group directory
let suiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
- let userDefaults = UserDefaults(suiteName: suiteName)
+ guard let directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: suiteName) else {
+ return
+ }
+
+ // Set App Group dir
+ MMKV.initialize(rootDir: nil, groupDir: directory.path, logLevel: MMKVLogLevel.none)
+ guard let mmkv = MMKV(mmapID: mmapID, cryptKey: cryptKey, mode: MMKVMode.multiProcess) else {
+ return
+ }
var server = data.host
if (server.last == "/") {
@@ -69,8 +91,8 @@ class NotificationService: UNNotificationServiceExtension {
}
let msgId = data.messageId
- let userId = userDefaults?.string(forKey: "reactnativemeteor_usertoken-\(server)") ?? ""
- let token = userDefaults?.string(forKey: "reactnativemeteor_usertoken-\(userId)") ?? ""
+ let userId = mmkv.string(forKey: "reactnativemeteor_usertoken-\(server)") ?? ""
+ let token = mmkv.string(forKey: "reactnativemeteor_usertoken-\(userId)") ?? ""
if userId.isEmpty || token.isEmpty {
contentHandler(bestAttemptContent)
diff --git a/ios/NotificationService/SecureStorage.h b/ios/NotificationService/SecureStorage.h
new file mode 100644
index 00000000..6cbad0a7
--- /dev/null
+++ b/ios/NotificationService/SecureStorage.h
@@ -0,0 +1,48 @@
+//
+// SecureStorage.h
+// RocketChatRN
+//
+// https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/ios/SecureStorage.h
+//
+
+#if __has_include("RCTBridgeModule.h")
+#import "RCTBridgeModule.h"
+#else
+#import
+#endif
+
+#import
+
+@interface SecureStorage: NSObject
+
+
+
+- (void) setSecureKey: (nonnull NSString *)key value:(nonnull NSString *)value
+ options: (nonnull NSDictionary *)options
+ callback:(nullable RCTResponseSenderBlock)callback;
+- (nullable NSString *) getSecureKey:(nonnull NSString *)key
+ callback:(nullable RCTResponseSenderBlock)callback;
+- (bool) secureKeyExists:(nonnull NSString *)key
+ callback:(nullable RCTResponseSenderBlock)callback;
+- (void) removeSecureKey:(nonnull NSString *)key
+ callback:(nullable RCTResponseSenderBlock)callback;
+
+- (BOOL)searchKeychainCopyMatchingExists:(nonnull NSString *)identifier;
+
+- (nonnull NSString *)searchKeychainCopyMatching:(nonnull NSString *)identifier;
+
+- (nonnull NSMutableDictionary *)newSearchDictionary:(nonnull NSString *)identifier;
+
+- (BOOL)createKeychainValue:(nonnull NSString *)value forIdentifier:(nonnull NSString *)identifier options: (NSDictionary * __nullable)options;
+
+- (BOOL)updateKeychainValue:(nonnull NSString *)password forIdentifier:(nonnull NSString *)identifier options:(NSDictionary * __nullable)options;
+
+- (BOOL)deleteKeychainValue:(nonnull NSString *)identifier;
+
+- (void)clearSecureKeyStore;
+
+- (void)handleAppUninstallation;
+
+
+
+@end
diff --git a/ios/NotificationService/SecureStorage.m b/ios/NotificationService/SecureStorage.m
new file mode 100644
index 00000000..2c7cf156
--- /dev/null
+++ b/ios/NotificationService/SecureStorage.m
@@ -0,0 +1,287 @@
+//
+// SecureStorage.m
+// NotificationService
+//
+// https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/ios/SecureStorage.m
+// Refer to /patches/react-native-mmkv-storage+0.3.5.patch
+
+#if __has_include("RCTBridgeModule.h")
+#import "RCTBridgeModule.h"
+#else
+#import
+#endif
+
+#import "SecureStorage.h"
+
+@implementation SecureStorage : NSObject
+
+NSString *serviceName;
+
+- (void) setSecureKey: (NSString *)key value:(NSString *)value
+ options: (NSDictionary *)options
+ callback:(RCTResponseSenderBlock)callback
+
+{
+
+ @try {
+
+ [self handleAppUninstallation];
+ BOOL status = [self createKeychainValue: value forIdentifier: key options: options];
+ if (status) {
+ callback(@[[NSNull null],@"Key updated successfully" ]);
+
+ } else {
+ BOOL status = [self updateKeychainValue: value forIdentifier: key options: options];
+ if (status) {
+ callback(@[[NSNull null],@"Key updated successfully" ]);
+ } else {
+ callback(@[@"An error occurred", [NSNull null]]);
+ }
+ }
+ }
+ @catch (NSException *exception) {
+ callback(@[exception.reason, [NSNull null]]);
+ }
+}
+
+- (NSString *) getSecureKey:(NSString *)key
+ callback:(RCTResponseSenderBlock)callback
+{
+ @try {
+ [self handleAppUninstallation];
+ NSString *value = [self searchKeychainCopyMatching:key];
+ if (value == nil) {
+ NSString* errorMessage = @"key does not present";
+ if (callback != NULL) {
+ callback(@[errorMessage, [NSNull null]]);
+ }
+
+ return NULL;
+ } else {
+
+ if (callback != NULL) {
+ callback(@[[NSNull null], value]);
+ }
+
+ return value;
+ }
+ }
+ @catch (NSException *exception) {
+ if (callback != NULL) {
+ callback(@[exception.reason, [NSNull null]]);
+ }
+ return NULL;
+ }
+
+}
+
+- (bool) secureKeyExists:(NSString *)key
+ callback:(RCTResponseSenderBlock)callback
+{
+
+ @try {
+ [self handleAppUninstallation];
+ BOOL exists = [self searchKeychainCopyMatchingExists:key];
+ if (exists) {
+ if (callback != NULL) {
+ callback(@[[NSNull null], @true]);
+ }
+
+ return true;
+ } else {
+
+
+ if (callback != NULL) {
+ callback(@[[NSNull null], @false]);
+ }
+ return false;
+ }
+ }
+ @catch(NSException *exception) {
+ if (callback != NULL) {
+ callback(@[exception.reason, [NSNull null]]);
+ }
+
+
+ return NULL;
+ }
+}
+- (void) removeSecureKey:(NSString *)key
+ callback:(RCTResponseSenderBlock)callback
+{
+ @try {
+ BOOL status = [self deleteKeychainValue:key];
+ if (status) {
+ callback(@[[NSNull null], @"key removed successfully"]);
+
+ } else {
+ NSString* errorMessage = @"Could not find the key to delete.";
+
+ callback(@[errorMessage, [NSNull null]]);
+ }
+ }
+ @catch(NSException *exception) {
+ callback(@[exception.reason, [NSNull null]]);
+ }
+}
+
+
+
+
+- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
+ NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
+ // this value is shared by main app and extensions, so, is the best to be used here
+ serviceName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"];
+
+ [searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
+
+ NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
+ [searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrGeneric];
+ [searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
+ [searchDictionary setObject:serviceName forKey:(id)kSecAttrService];
+
+ NSString *keychainGroup = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"KeychainGroup"];
+ [searchDictionary setObject:keychainGroup forKey:(id)kSecAttrAccessGroup];
+
+ return searchDictionary;
+}
+
+- (NSString *)searchKeychainCopyMatching:(NSString *)identifier {
+ NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
+
+ // Add search attributes
+ [searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
+
+ // Add search return types
+ [searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
+
+ NSDictionary *found = nil;
+ CFTypeRef result = NULL;
+ OSStatus status = SecItemCopyMatching((CFDictionaryRef)searchDictionary,
+ (CFTypeRef *)&result);
+
+ NSString *value = nil;
+ found = (__bridge NSDictionary*)(result);
+ if (found) {
+ value = [[NSString alloc] initWithData:found encoding:NSUTF8StringEncoding];
+ }
+ return value;
+}
+
+- (BOOL)searchKeychainCopyMatchingExists:(NSString *)identifier {
+ NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
+
+ // Add search attributes
+ [searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
+
+ // Add search return types
+ [searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
+
+ CFTypeRef result = NULL;
+ OSStatus status = SecItemCopyMatching((CFDictionaryRef)searchDictionary,
+ (CFTypeRef *)&result);
+
+ if (status != errSecItemNotFound) {
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier options: (NSDictionary * __nullable)options {
+ CFStringRef accessible = accessibleValue(options);
+ NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
+
+ NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
+ [dictionary setObject:valueData forKey:(id)kSecValueData];
+ dictionary[(__bridge NSString *)kSecAttrAccessible] = (__bridge id)accessible;
+
+ OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);
+
+ if (status == errSecSuccess) {
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier options:(NSDictionary * __nullable)options {
+
+ CFStringRef accessible = accessibleValue(options);
+ NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
+ NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
+ NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
+ [updateDictionary setObject:passwordData forKey:(id)kSecValueData];
+ updateDictionary[(__bridge NSString *)kSecAttrAccessible] = (__bridge id)accessible;
+ OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,
+ (CFDictionaryRef)updateDictionary);
+
+ if (status == errSecSuccess) {
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)deleteKeychainValue:(NSString *)identifier {
+ NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
+ OSStatus status = SecItemDelete((CFDictionaryRef)searchDictionary);
+ if (status == errSecSuccess) {
+ return YES;
+ }
+ return NO;
+}
+
+- (void)clearSecureKeyStore
+{
+ NSArray *secItemClasses = @[(__bridge id)kSecClassGenericPassword,
+ (__bridge id)kSecAttrGeneric,
+ (__bridge id)kSecAttrAccount,
+ (__bridge id)kSecClassKey,
+ (__bridge id)kSecAttrService];
+ for (id secItemClass in secItemClasses) {
+ NSDictionary *spec = @{(__bridge id)kSecClass: secItemClass};
+ SecItemDelete((__bridge CFDictionaryRef)spec);
+ }
+}
+
+- (void)handleAppUninstallation
+{
+ // use app group user defaults to prevent clear when it's share extension
+ NSString *suiteName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"];
+ NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
+ if (![userDefaults boolForKey:@"RnSksIsAppInstalled"]) {
+ [self clearSecureKeyStore];
+ [userDefaults setBool:YES forKey:@"RnSksIsAppInstalled"];
+ [userDefaults synchronize];
+ }
+}
+
+NSError * secureKeyStoreError(NSString *errMsg)
+{
+ NSError *error = [NSError errorWithDomain:serviceName code:200 userInfo:@{@"reason": errMsg}];
+ return error;
+}
+
+
+
+CFStringRef accessibleValue(NSDictionary *options)
+{
+ if (options && options[@"accessible"] != nil) {
+ NSDictionary *keyMap = @{
+ @"AccessibleWhenUnlocked": (__bridge NSString *)kSecAttrAccessibleWhenUnlocked,
+ @"AccessibleAfterFirstUnlock": (__bridge NSString *)kSecAttrAccessibleAfterFirstUnlock,
+ @"AccessibleAlways": (__bridge NSString *)kSecAttrAccessibleAlways,
+ @"AccessibleWhenPasscodeSetThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
+ @"AccessibleWhenUnlockedThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
+ @"AccessibleAfterFirstUnlockThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
+ @"AccessibleAlwaysThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleAlwaysThisDeviceOnly
+ };
+
+ NSString *result = keyMap[options[@"accessible"]];
+ if (result) {
+ return (__bridge CFStringRef)result;
+ }
+ }
+ return kSecAttrAccessibleAfterFirstUnlock;
+}
+
+@end
+
diff --git a/ios/NotificationService/String+Hex.swift b/ios/NotificationService/String+Hex.swift
new file mode 100644
index 00000000..8518ab6f
--- /dev/null
+++ b/ios/NotificationService/String+Hex.swift
@@ -0,0 +1,13 @@
+//
+// String+Hex.swift
+// NotificationService
+//
+// Created by Djorkaeff Alexandre Vilela Pereira on 8/6/20.
+// Copyright © 2020 Facebook. All rights reserved.
+//
+
+extension String {
+ func toHex() -> String {
+ return unicodeScalars.map{ .init($0.value, radix: 16, uppercase: false) }.joined()
+ }
+}
diff --git a/ios/Podfile b/ios/Podfile
index c1eca515..d7ce866b 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -18,6 +18,11 @@ target 'ShareRocketChatRN' do
all_pods
end
+# used to get user credentials
+target 'NotificationService' do
+ pod 'MMKVAppExtension'
+end
+
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index c72afcec..7a4ea3e2 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -184,6 +184,11 @@ PODS:
- libwebp/mux (1.1.0):
- libwebp/demux
- libwebp/webp (1.1.0)
+ - MMKV (1.2.1):
+ - MMKVCore (~> 1.2.1)
+ - MMKVAppExtension (1.2.1):
+ - MMKVCore (~> 1.2.1)
+ - MMKVCore (1.2.1)
- nanopb (1.30905.0):
- nanopb/decode (= 1.30905.0)
- nanopb/encode (= 1.30905.0)
@@ -370,6 +375,9 @@ PODS:
- react-native-jitsi-meet (2.1.1):
- JitsiMeetSDK (= 2.8.1)
- React
+ - react-native-mmkv-storage (0.3.5):
+ - MMKV (= 1.2.1)
+ - React
- react-native-notifications (2.1.7):
- React
- react-native-orientation-locker (1.1.8):
@@ -495,8 +503,6 @@ PODS:
- React
- RNScreens (2.9.0):
- React
- - RNUserDefaults (1.8.1):
- - React
- RNVectorIcons (7.0.0):
- React
- SDWebImage (5.8.4):
@@ -565,6 +571,7 @@ DEPENDENCIES:
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- KeyCommands (from `../node_modules/react-native-keycommands`)
+ - MMKVAppExtension
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
@@ -582,6 +589,7 @@ DEPENDENCIES:
- "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)"
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-jitsi-meet (from `../node_modules/react-native-jitsi-meet`)
+ - react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
- react-native-notifications (from `../node_modules/react-native-notifications`)
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
@@ -617,7 +625,6 @@ DEPENDENCIES:
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNRootView (from `../node_modules/rn-root-view`)
- RNScreens (from `../node_modules/react-native-screens`)
- - RNUserDefaults (from `../node_modules/rn-user-defaults`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- UMAppLoader (from `../node_modules/unimodules-app-loader/ios`)
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
@@ -658,6 +665,9 @@ SPEC REPOS:
- GoogleUtilities
- JitsiMeetSDK
- libwebp
+ - MMKV
+ - MMKVAppExtension
+ - MMKVCore
- nanopb
- OpenSSL-Universal
- PromisesObjC
@@ -733,6 +743,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-document-picker"
react-native-jitsi-meet:
:path: "../node_modules/react-native-jitsi-meet"
+ react-native-mmkv-storage:
+ :path: "../node_modules/react-native-mmkv-storage"
react-native-notifications:
:path: "../node_modules/react-native-notifications"
react-native-orientation-locker:
@@ -803,8 +815,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/rn-root-view"
RNScreens:
:path: "../node_modules/react-native-screens"
- RNUserDefaults:
- :path: "../node_modules/rn-user-defaults"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
UMAppLoader:
@@ -877,6 +887,9 @@ SPEC CHECKSUMS:
JitsiMeetSDK: 2984eac1343690bf1c0c72bde75b48b0148d0f79
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
+ MMKV: 67253edee25a34edf332f91d73fa94a9e038b971
+ MMKVAppExtension: d792aa7bd301285e2c3100c5ce15aa44fa26456f
+ MMKVCore: fe398984acac1fa33f92795d1b5fd0a334c944af
nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
@@ -895,6 +908,7 @@ SPEC CHECKSUMS:
react-native-cameraroll: ae0a7c0cc8462508855707ff623b1e789b692865
react-native-document-picker: 825552b827012282baf4b7cbf119d3b37a280c90
react-native-jitsi-meet: f89bcb2cfbd5b15403b9c40738036c4f1af45d05
+ react-native-mmkv-storage: 48729fe90e850ef2fdc9d3714b7030c7c51d82b0
react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d
react-native-safe-area-context: 344b969c45af3d8464d36e8dea264942992ef033
@@ -930,7 +944,6 @@ SPEC CHECKSUMS:
RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43
RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494
RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da
- RNUserDefaults: c421fd97ad06b35c16608c5d0fe675db353f632d
RNVectorIcons: da6fe858f5a65d7bbc3379540a889b0b12aa5976
SDWebImage: cf6922231e95550934da2ada0f20f2becf2ceba9
SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8
@@ -951,6 +964,6 @@ SPEC CHECKSUMS:
Yoga: d5bd05a2b6b94c52323745c2c2b64557c8c66f64
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
-PODFILE CHECKSUM: 55c04243097892160d63f79f3a23157165b7ac68
+PODFILE CHECKSUM: 4916aa46fbfaed764171540e6ed76fb07a8a95e7
COCOAPODS: 1.9.3
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h
deleted file mode 100644
index 0d1a36f6..00000000
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2019 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import
-
-@class FIRAnalyticsConfiguration;
-
-@interface FIRConfiguration ()
-
-/**
- * The configuration class for Firebase Analytics. This should be removed once the logic for
- * enabling and disabling Analytics is moved to Analytics.
- */
-@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration;
-
-@end
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h
deleted file mode 100644
index ac5ef2c4..00000000
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2019 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import
-
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Implements the FIRCoreDiagnosticsData protocol to log diagnostics data. */
-@interface FIRDiagnosticsData : NSObject
-
-/** Inserts values into the diagnosticObjects dictionary if the value isn't nil.
- *
- * @param value The value to insert if it's not nil.
- * @param key The key to associate it with.
- */
-- (void)insertValue:(nullable id)value forKey:(NSString *)key;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h b/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
deleted file mode 100644
index 2b0eb710..00000000
--- a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import
-
-#import "FIRCoreDiagnosticsData.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Allows the interoperation of FirebaseCore and FirebaseCoreDiagnostics. */
-@protocol FIRCoreDiagnosticsInterop
-
-/** Sends the given diagnostics data.
- *
- * @param diagnosticsData The diagnostics data object to send.
- */
-+ (void)sendDiagnosticsData:(id)diagnosticsData;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md b/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md
deleted file mode 100644
index 3ddc8fbd..00000000
--- a/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
-
-This repository contains a subset of the Firebase iOS SDK source. It currently
-includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase,
-FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
-FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and
-FirebaseStorage.
-
-The repository also includes GoogleUtilities source. The
-[GoogleUtilities](GoogleUtilities/README.md) pod is
-a set of utilities used by Firebase and other Google products.
-
-Firebase is an app development platform with tools to help you build, grow and
-monetize your app. More information about Firebase can be found at
-[https://firebase.google.com](https://firebase.google.com).
-
-## Installation
-
-See the three subsections for details about three different installation methods.
-1. [Standard pod install](README.md#standard-pod-install)
-1. [Installing from the GitHub repo](README.md#installing-from-github)
-1. [Experimental Carthage](README.md#carthage-ios-only)
-
-### Standard pod install
-
-Go to
-[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
-
-### Installing from GitHub
-
-For releases starting with 5.0.0, the source for each release is also deployed
-to CocoaPods master and available via standard
-[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
-
-These instructions can be used to access the Firebase repo at other branches,
-tags, or commits.
-
-#### Background
-
-See
-[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
-for instructions and options about overriding pod source locations.
-
-#### Accessing Firebase Source Snapshots
-
-All of the official releases are tagged in this repo and available via CocoaPods. To access a local
-source snapshot or unreleased branch, use Podfile directives like the following:
-
-To access FirebaseFirestore via a branch:
-```
-pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
-pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
-```
-
-To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
-
-```
-pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
-pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
-```
-
-### Carthage (iOS only)
-
-Instructions for the experimental Carthage distribution are at
-[Carthage](Carthage.md).
-
-### Rome
-
-Instructions for installing binary frameworks via
-[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
-
-## Development
-
-To develop Firebase software in this repository, ensure that you have at least
-the following software:
-
- * Xcode 10.1 (or later)
- * CocoaPods 1.7.2 (or later)
- * [CocoaPods generate](https://github.com/square/cocoapods-generate)
-
-For the pod that you want to develop:
-
-`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios`
-
-Note: If the CocoaPods cache is out of date, you may need to run
-`pod repo update` before the `pod gen` command.
-
-Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
-those platforms. Since 10.2, Xcode does not properly handle multi-platform
-CocoaPods workspaces.
-
-Firestore has a self contained Xcode project. See
-[Firestore/README.md](Firestore/README.md).
-
-### Development for Catalyst
-* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
-* Check the Mac box in the App-iOS Build Settings
-* Sign the App in the Settings Signing & Capabilities tab
-* Click Pods in the Project Manager
-* Add Signing to the iOS host app and unit test targets
-* Select the Unit-unit scheme
-* Run it to build and test
-
-### Adding a New Firebase Pod
-
-See [AddNewPod.md](AddNewPod.md).
-
-### Code Formatting
-
-To ensure that the code is formatted consistently, run the script
-[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
-before creating a PR.
-
-Travis will verify that any code changes are done in a style compliant way. Install
-`clang-format` and `swiftformat`.
-These commands will get the right versions:
-
-```
-brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb
-brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb
-```
-
-Note: if you already have a newer version of these installed you may need to
-`brew switch` to this version.
-
-To update this section, find the versions of clang-format and swiftformat.rb to
-match the versions in the CI failure logs
-[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
-
-### Running Unit Tests
-
-Select a scheme and press Command-u to build a component and run its unit tests.
-
-#### Viewing Code Coverage
-
-First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
-
-After running the `AllUnitTests_iOS` scheme in Xcode, execute
-`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
-at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
-
-### Running Sample Apps
-In order to run the sample apps and integration tests, you'll need valid
-`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
-files without real values, but can be replaced with real plist files. To get your own
-`GoogleService-Info.plist` files:
-
-1. Go to the [Firebase Console](https://console.firebase.google.com/)
-2. Create a new Firebase project, if you don't already have one
-3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
-identifier (e.g. `com.google.Database-Example`)
-4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
-(e.g. in [Example/Database/App/](Example/Database/App/));
-
-Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
-special Apple capabilities, and you will have to change the sample app to use a unique bundle
-identifier that you can control in your own Apple Developer account.
-
-## Specific Component Instructions
-See the sections below for any special instructions for those components.
-
-### Firebase Auth
-
-If you're doing specific Firebase Auth development, see
-[the Auth Sample README](Example/Auth/README.md) for instructions about
-building and running the FirebaseAuth pod along with various samples and tests.
-
-### Firebase Database
-
-To run the Database Integration tests, make your database authentication rules
-[public](https://firebase.google.com/docs/database/security/quickstart).
-
-### Firebase Storage
-
-To run the Storage Integration tests, follow the instructions in
-[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
-
-#### Push Notifications
-
-Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
-In order to actually test receiving push notifications, you will need to:
-
-1. Change the bundle identifier of the sample app to something you own in your Apple Developer
-account, and enable that App ID for push notifications.
-2. You'll also need to
-[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
-at **Project Settings > Cloud Messaging > [Your Firebase App]**.
-3. Ensure your iOS device is added to your Apple Developer portal as a test device.
-
-#### iOS Simulator
-
-The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
-In order to receive push notifications, you'll have to follow the steps above and run the app on a
-physical device.
-
-## Community Supported Efforts
-
-We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
-very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
-participate in the Firebase community.
-
-### tvOS, macOS, and Catalyst
-Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore,
-FirebaseDatabase, FirebaseMessaging, FirebaseFirestore,
-FirebaseFunctions, FirebaseRemoteConfig, and FirebaseStorage now compile, run unit tests, and work on
-tvOS, macOS, and Catalyst.
-
-For tvOS, checkout the [Sample](Example/tvOSSample).
-
-Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this
-repository is actively developed primarily for iOS. While we can catch basic unit test issues with
-Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you
-encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
-
-To install, add a subset of the following to the Podfile:
-
-```
-pod 'Firebase/ABTesting'
-pod 'Firebase/Auth'
-pod 'Firebase/Database'
-pod 'Firebase/Firestore'
-pod 'Firebase/Functions'
-pod 'Firebase/Messaging'
-pod 'Firebase/RemoteConfig'
-pod 'Firebase/Storage'
-```
-
-#### Additional Catalyst Notes
-
-* FirebaseAuth and FirebaseMessaging require adding `Keychain Sharing Capability`
-to Build Settings.
-* FirebaseFirestore requires signing the
-[gRPC Resource target](https://github.com/firebase/firebase-ios-sdk/issues/3500#issuecomment-518741681).
-
-## Roadmap
-
-See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
-plans and directions.
-
-## Contributing
-
-See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
-iOS SDK.
-
-## License
-
-The contents of this repository is licensed under the
-[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
-
-Your use of Firebase is governed by the
-[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
diff --git a/ios/Pods/Headers/Private/MMKV/MMKV.h b/ios/Pods/Headers/Private/MMKV/MMKV.h
new file mode 120000
index 00000000..4f5e989c
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKV/MMKV.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKV/MMKVHandler.h b/ios/Pods/Headers/Private/MMKV/MMKVHandler.h
new file mode 120000
index 00000000..5a6f0ae7
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKV/MMKVHandler.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKVHandler.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVAppExtension/MMKV.h b/ios/Pods/Headers/Private/MMKVAppExtension/MMKV.h
new file mode 120000
index 00000000..0c040332
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVAppExtension/MMKV.h
@@ -0,0 +1 @@
+../../../MMKVAppExtension/iOS/MMKV/MMKV/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVAppExtension/MMKVHandler.h b/ios/Pods/Headers/Private/MMKVAppExtension/MMKVHandler.h
new file mode 120000
index 00000000..eaae7dc0
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVAppExtension/MMKVHandler.h
@@ -0,0 +1 @@
+../../../MMKVAppExtension/iOS/MMKV/MMKV/MMKVHandler.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/AESCrypt.h b/ios/Pods/Headers/Private/MMKVCore/AESCrypt.h
new file mode 120000
index 00000000..f0fa1cea
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/AESCrypt.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/AESCrypt.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/Checksum.h b/ios/Pods/Headers/Private/MMKVCore/Checksum.h
new file mode 120000
index 00000000..2ba79041
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/Checksum.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/crc32/Checksum.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/CodedInputData.h b/ios/Pods/Headers/Private/MMKVCore/CodedInputData.h
new file mode 120000
index 00000000..1b36d4f5
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/CodedInputData.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/CodedInputData.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/CodedInputDataCrypt.h b/ios/Pods/Headers/Private/MMKVCore/CodedInputDataCrypt.h
new file mode 120000
index 00000000..3ecc4832
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/CodedInputDataCrypt.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/CodedInputDataCrypt.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/CodedOutputData.h b/ios/Pods/Headers/Private/MMKVCore/CodedOutputData.h
new file mode 120000
index 00000000..1c594fb1
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/CodedOutputData.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/CodedOutputData.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/InterProcessLock.h b/ios/Pods/Headers/Private/MMKVCore/InterProcessLock.h
new file mode 120000
index 00000000..86bc340a
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/InterProcessLock.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/InterProcessLock.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/KeyValueHolder.h b/ios/Pods/Headers/Private/MMKVCore/KeyValueHolder.h
new file mode 120000
index 00000000..8f2cd6ba
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/KeyValueHolder.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/KeyValueHolder.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMBuffer.h b/ios/Pods/Headers/Private/MMKVCore/MMBuffer.h
new file mode 120000
index 00000000..af88e07e
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMBuffer.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMBuffer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKV.h b/ios/Pods/Headers/Private/MMKVCore/MMKV.h
new file mode 120000
index 00000000..cc0beb9a
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKV.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKVLog.h b/ios/Pods/Headers/Private/MMKVCore/MMKVLog.h
new file mode 120000
index 00000000..a6df22a4
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKVLog.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVLog.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKVMetaInfo.hpp b/ios/Pods/Headers/Private/MMKVCore/MMKVMetaInfo.hpp
new file mode 120000
index 00000000..37044f68
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKVMetaInfo.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVMetaInfo.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKVPredef.h b/ios/Pods/Headers/Private/MMKVCore/MMKVPredef.h
new file mode 120000
index 00000000..f74a9295
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKVPredef.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVPredef.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKV_IO.h b/ios/Pods/Headers/Private/MMKVCore/MMKV_IO.h
new file mode 120000
index 00000000..b18d8947
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKV_IO.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV_IO.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKV_OSX.h b/ios/Pods/Headers/Private/MMKVCore/MMKV_OSX.h
new file mode 120000
index 00000000..774966c9
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKV_OSX.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV_OSX.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MemoryFile.h b/ios/Pods/Headers/Private/MMKVCore/MemoryFile.h
new file mode 120000
index 00000000..f12f713d
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MemoryFile.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MemoryFile.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MiniPBCoder.h b/ios/Pods/Headers/Private/MMKVCore/MiniPBCoder.h
new file mode 120000
index 00000000..c63b74fd
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MiniPBCoder.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MiniPBCoder.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/PBEncodeItem.hpp b/ios/Pods/Headers/Private/MMKVCore/PBEncodeItem.hpp
new file mode 120000
index 00000000..f6b73e35
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/PBEncodeItem.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/PBEncodeItem.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/PBUtility.h b/ios/Pods/Headers/Private/MMKVCore/PBUtility.h
new file mode 120000
index 00000000..702dc11d
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/PBUtility.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/PBUtility.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/ScopedLock.hpp b/ios/Pods/Headers/Private/MMKVCore/ScopedLock.hpp
new file mode 120000
index 00000000..5af1050e
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/ScopedLock.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ScopedLock.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/ThreadLock.h b/ios/Pods/Headers/Private/MMKVCore/ThreadLock.h
new file mode 120000
index 00000000..07de54af
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/ThreadLock.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ThreadLock.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_aes.h b/ios/Pods/Headers/Private/MMKVCore/openssl_aes.h
new file mode 120000
index 00000000..e0a13cf5
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_aes.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_aes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_aes_locl.h b/ios/Pods/Headers/Private/MMKVCore/openssl_aes_locl.h
new file mode 120000
index 00000000..0240f69a
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_aes_locl.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_aes_locl.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_arm_arch.h b/ios/Pods/Headers/Private/MMKVCore/openssl_arm_arch.h
new file mode 120000
index 00000000..9ddb4b07
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_arm_arch.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_arm_arch.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_md32_common.h b/ios/Pods/Headers/Private/MMKVCore/openssl_md32_common.h
new file mode 120000
index 00000000..39357984
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_md32_common.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md32_common.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_md5.h b/ios/Pods/Headers/Private/MMKVCore/openssl_md5.h
new file mode 120000
index 00000000..a0d159cc
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_md5.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md5.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_md5_locl.h b/ios/Pods/Headers/Private/MMKVCore/openssl_md5_locl.h
new file mode 120000
index 00000000..14f05fcc
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_md5_locl.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md5_locl.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_opensslconf.h b/ios/Pods/Headers/Private/MMKVCore/openssl_opensslconf.h
new file mode 120000
index 00000000..872a2ff7
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_opensslconf.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_opensslconf.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/RNUserDefaults/RNUserDefaults.h b/ios/Pods/Headers/Private/RNUserDefaults/RNUserDefaults.h
deleted file mode 120000
index 5ca38888..00000000
--- a/ios/Pods/Headers/Private/RNUserDefaults/RNUserDefaults.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../node_modules/rn-user-defaults/ios/RNUserDefaults.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/IDStore.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/IDStore.h
new file mode 120000
index 00000000..2033a4eb
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/IDStore.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/IDStore.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/MMKVStorage.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/MMKVStorage.h
new file mode 120000
index 00000000..4a9e0777
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/MMKVStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/MMKVStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/SecureStorage.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/SecureStorage.h
new file mode 120000
index 00000000..a66b4c9a
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/SecureStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/SecureStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageGetters.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageGetters.h
new file mode 120000
index 00000000..9205f0b0
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageGetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageGetters.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageIndexer.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageIndexer.h
new file mode 120000
index 00000000..ea59219f
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageIndexer.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageIndexer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageSetters.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageSetters.h
new file mode 120000
index 00000000..b2a9f86a
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageSetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageSetters.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKV/MMKV.h b/ios/Pods/Headers/Public/MMKV/MMKV.h
new file mode 120000
index 00000000..4f5e989c
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKV/MMKV.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKV/MMKVHandler.h b/ios/Pods/Headers/Public/MMKV/MMKVHandler.h
new file mode 120000
index 00000000..5a6f0ae7
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKV/MMKVHandler.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKVHandler.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVAppExtension/MMKV.h b/ios/Pods/Headers/Public/MMKVAppExtension/MMKV.h
new file mode 120000
index 00000000..0c040332
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVAppExtension/MMKV.h
@@ -0,0 +1 @@
+../../../MMKVAppExtension/iOS/MMKV/MMKV/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVAppExtension/MMKVHandler.h b/ios/Pods/Headers/Public/MMKVAppExtension/MMKVHandler.h
new file mode 120000
index 00000000..eaae7dc0
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVAppExtension/MMKVHandler.h
@@ -0,0 +1 @@
+../../../MMKVAppExtension/iOS/MMKV/MMKV/MMKVHandler.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMBuffer.h b/ios/Pods/Headers/Public/MMKVCore/MMBuffer.h
new file mode 120000
index 00000000..af88e07e
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMBuffer.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMBuffer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMKV.h b/ios/Pods/Headers/Public/MMKVCore/MMKV.h
new file mode 120000
index 00000000..cc0beb9a
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMKV.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMKVLog.h b/ios/Pods/Headers/Public/MMKVCore/MMKVLog.h
new file mode 120000
index 00000000..a6df22a4
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMKVLog.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVLog.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMKVPredef.h b/ios/Pods/Headers/Public/MMKVCore/MMKVPredef.h
new file mode 120000
index 00000000..f74a9295
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMKVPredef.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVPredef.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/PBUtility.h b/ios/Pods/Headers/Public/MMKVCore/PBUtility.h
new file mode 120000
index 00000000..702dc11d
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/PBUtility.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/PBUtility.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/ScopedLock.hpp b/ios/Pods/Headers/Public/MMKVCore/ScopedLock.hpp
new file mode 120000
index 00000000..5af1050e
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/ScopedLock.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ScopedLock.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/ThreadLock.h b/ios/Pods/Headers/Public/MMKVCore/ThreadLock.h
new file mode 120000
index 00000000..07de54af
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/ThreadLock.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ThreadLock.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/openssl_md5.h b/ios/Pods/Headers/Public/MMKVCore/openssl_md5.h
new file mode 120000
index 00000000..a0d159cc
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/openssl_md5.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md5.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/openssl_opensslconf.h b/ios/Pods/Headers/Public/MMKVCore/openssl_opensslconf.h
new file mode 120000
index 00000000..872a2ff7
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/openssl_opensslconf.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_opensslconf.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/RNUserDefaults/RNUserDefaults.h b/ios/Pods/Headers/Public/RNUserDefaults/RNUserDefaults.h
deleted file mode 120000
index 5ca38888..00000000
--- a/ios/Pods/Headers/Public/RNUserDefaults/RNUserDefaults.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../node_modules/rn-user-defaults/ios/RNUserDefaults.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/IDStore.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/IDStore.h
new file mode 120000
index 00000000..2033a4eb
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/IDStore.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/IDStore.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/MMKVStorage.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/MMKVStorage.h
new file mode 120000
index 00000000..4a9e0777
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/MMKVStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/MMKVStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/SecureStorage.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/SecureStorage.h
new file mode 120000
index 00000000..a66b4c9a
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/SecureStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/SecureStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageGetters.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageGetters.h
new file mode 120000
index 00000000..9205f0b0
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageGetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageGetters.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageIndexer.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageIndexer.h
new file mode 120000
index 00000000..ea59219f
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageIndexer.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageIndexer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageSetters.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageSetters.h
new file mode 120000
index 00000000..b2a9f86a
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageSetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageSetters.h
\ No newline at end of file
diff --git a/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json b/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json
deleted file mode 100644
index b54949a2..00000000
--- a/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "name": "RNUserDefaults",
- "version": "1.8.1",
- "summary": "Use `UserDefaults` (iOS) with React Native and `SharedPreferences` on AndroidOS.",
- "description": "Use `UserDefaults` (iOS) with React Native and `SharedPreferences` on AndroidOS.",
- "license": "MIT",
- "authors": "djorkaeffalexandre",
- "homepage": "https://github.com/RocketChat/rn-user-defaults.git",
- "source": {
- "git": "https://github.com/RocketChat/rn-user-defaults.git"
- },
- "requires_arc": true,
- "platforms": {
- "ios": "7.0"
- },
- "preserve_paths": [
- "README.md",
- "package.json",
- "index.js"
- ],
- "source_files": "iOS/*.{h,m}",
- "dependencies": {
- "React": [
-
- ]
- }
-}
diff --git a/ios/Pods/Local Podspecs/react-native-mmkv-storage.podspec.json b/ios/Pods/Local Podspecs/react-native-mmkv-storage.podspec.json
new file mode 100644
index 00000000..1a6d0d81
--- /dev/null
+++ b/ios/Pods/Local Podspecs/react-native-mmkv-storage.podspec.json
@@ -0,0 +1,25 @@
+{
+ "name": "react-native-mmkv-storage",
+ "version": "0.3.5",
+ "summary": "This library aims to provide a fast & reliable solution for you data storage needs in react-native apps. It uses [MMKV](https://github.com/Tencent/MMKV) by Tencent under the hood on Android and iOS both that is used by their WeChat app(more than 1 Billion users). Unlike other storage solutions for React Native, this library lets you store any kind of data type, in any number of database instances, with or without encryption in a very fast and efficient way.",
+ "homepage": "https://github.com/ammarahm-ed/react-native-mmkv-storage",
+ "license": "MIT",
+ "authors": "Ammar Ahmed for @ammarahm-ed",
+ "platforms": {
+ "ios": "9.0"
+ },
+ "source": {
+ "git": "https://github.com/ammarahm-ed/react-native-mmkv-storage",
+ "tag": "V0.3.5"
+ },
+ "source_files": "ios/**/*.{h,m}",
+ "requires_arc": true,
+ "dependencies": {
+ "React": [
+
+ ],
+ "MMKV": [
+ "1.2.1"
+ ]
+ }
+}
diff --git a/ios/Pods/MMKV/LICENSE.TXT b/ios/Pods/MMKV/LICENSE.TXT
new file mode 100644
index 00000000..096acfb2
--- /dev/null
+++ b/ios/Pods/MMKV/LICENSE.TXT
@@ -0,0 +1,189 @@
+Tencent is pleased to support the open source community by making MMKV available.
+Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
+If you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.
+If you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms. Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.
+A copy of the BSD 3-Clause License is included in this file.
+
+Other dependencies and licenses:
+
+Open Source Software Licensed Under the OpenSSL License:
+----------------------------------------------------------------------------------------
+1. OpenSSL 1.1.0i
+Copyright (c) 1998-2018 The OpenSSL Project.
+All rights reserved.
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+
+Terms of the OpenSSL License:
+---------------------------------------------------
+LICENSE ISSUES:
+--------------------------------------------------------------------
+
+The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.
+See below for the actual license texts.
+
+OpenSSL License:
+--------------------------------------------------------------------
+Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. All advertising materials mentioning features or use of this software must display the following acknowledgment:
+"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL" nor may "OpenSSL" appear in their names without prior written permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com).
+
+
+Original SSLeay License:
+--------------------------------------------------------------------
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software must display the following acknowledgement:" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]
+
+
+
+Open Source Software Licensed Under the Apache License, Version 2.0:
+The below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.
+----------------------------------------------------------------------------------------
+1. MultiprocessSharedPreferences v1.0
+Copyright (C) 2014 seven456@gmail.com
+
+
+Terms of the Apache License, Version 2.0:
+--------------------------------------------------------------------
+Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”
+
+“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+d) If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+
+Open Source Software Licensed Under the zlib License:
+The below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.
+----------------------------------------------------------------------------------------
+1. zlib v1.2.11
+Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+
+Terms of the zlib License:
+--------------------------------------------------------------------
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+
+
+Terms of the BSD 3-Clause License:
+--------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ios/Pods/MMKV/README.md b/ios/Pods/MMKV/README.md
new file mode 100644
index 00000000..36310c84
--- /dev/null
+++ b/ios/Pods/MMKV/README.md
@@ -0,0 +1,287 @@
+[![license](https://img.shields.io/badge/license-BSD_3-brightgreen.svg?style=flat)](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT)
+[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/MMKV/pulls)
+[![Release Version](https://img.shields.io/badge/release-1.2.1-brightgreen.svg)](https://github.com/Tencent/MMKV/releases)
+[![Platform](https://img.shields.io/badge/Platform-%20Android%20%7C%20iOS%2FmacOS%20%7C%20Win32%20%7C%20POSIX-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home)
+
+中文版本请参看[这里](./readme_cn.md)
+
+MMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's currently available on **Android**, **iOS/macOS**, **Win32** and **POSIX**.
+
+# MMKV for Android
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of Android to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 50K in binary size**: MMKV adds about 50K per architecture on App size, and much less when zipped (apk).
+
+
+## Getting Started
+
+### Installation Via Maven
+Add the following lines to `build.gradle` on your app module:
+
+```gradle
+dependencies {
+ implementation 'com.tencent:mmkv-static:1.2.1'
+ // replace "1.2.1" with any available version
+}
+```
+
+For other installation options, see [Android Setup](https://github.com/Tencent/MMKV/wiki/android_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.
+Setup MMKV on App startup, say your `Application` class, add these lines:
+
+```Java
+public void onCreate() {
+ super.onCreate();
+
+ String rootDir = MMKV.initialize(this);
+ System.out.println("mmkv root: " + rootDir);
+ //……
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```Java
+import com.tencent.mmkv.MMKV;
+
+MMKV kv = MMKV.defaultMMKV();
+
+kv.encode("bool", true);
+boolean bValue = kv.decodeBool("bool");
+
+kv.encode("int", Integer.MIN_VALUE);
+int iValue = kv.decodeInt("int");
+
+kv.encode("string", "Hello from mmkv");
+String str = kv.decodeString("string");
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [Android Tutorial](https://github.com/Tencent/MMKV/wiki/android_tutorial).
+
+## Performance
+Writing random `int` for 1000 times, we get this chart:
+![](https://github.com/Tencent/MMKV/wiki/assets/profile_android_mini.png)
+For more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/android_benchmark).
+
+# MMKV for iOS/macOS
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of iOS/macOS to achieve best performance.
+
+* **Easy-to-use**. You can use MMKV as you go, no configurations needed. All changes are saved immediately, no `synchronize` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **Less than 30K in binary size**: MMKV adds less than 30K per architecture on App size, and much less when zipped (ipa).
+
+## Getting Started
+
+### Installation Via CocoaPods:
+ 1. Install [CocoaPods](https://guides.CocoaPods.org/using/getting-started.html);
+ 2. Open terminal, `cd` to your project directory, run `pod repo update` to make CocoaPods aware of the latest available MMKV versions;
+ 3. Edit your Podfile, add `pod 'MMKV'` to your app target;
+ 4. Run `pod install`;
+ 5. Open the `.xcworkspace` file generated by CocoaPods;
+ 6. Add `#import ` to your source file and we are done.
+
+For other installation options, see [iOS/macOS Setup](https://github.com/Tencent/MMKV/wiki/iOS_setup).
+
+### Quick Tutorial
+You can use MMKV as you go, no configurations needed. All changes are saved immediately, no `synchronize` calls needed.
+Setup MMKV on App startup, in your `-[MyApp application: didFinishLaunchingWithOptions:]`, add these lines:
+
+```objective-c
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // init MMKV in the main thread
+ [MMKV initializeMMKV:nil];
+
+ //...
+ return YES;
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```objective-c
+MMKV *mmkv = [MMKV defaultMMKV];
+
+[mmkv setBool:YES forKey:@"bool"];
+BOOL bValue = [mmkv getBoolForKey:@"bool"];
+
+[mmkv setInt32:-1024 forKey:@"int32"];
+int32_t iValue = [mmkv getInt32ForKey:@"int32"];
+
+[mmkv setString:@"hello, mmkv" forKey:@"string"];
+NSString *str = [mmkv getStringForKey:@"string"];
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found [here](https://github.com/Tencent/MMKV/wiki/iOS_tutorial).
+
+## Performance
+Writing random `int` for 10000 times, we get this chart:
+![](https://github.com/Tencent/MMKV/wiki/assets/profile_mini.png)
+For more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/iOS_benchmark).
+
+
+# MMKV for Win32
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of Windows to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 10K in binary size**: MMKV adds about 10K on application size, and much less when zipped.
+
+
+## Getting Started
+
+### Installation Via Source
+1. Getting source code from git repository:
+
+ ```
+ git clone https://github.com/Tencent/MMKV.git
+ ```
+
+2. Add `Win32/MMKV/MMKV.vcxproj` to your solution;
+3. Add `MMKV` project to your project's dependencies;
+4. Add `$(OutDir)include` to your project's `C/C++` -> `General` -> `Additional Include Directories`;
+5. Add `$(OutDir)` to your project's `Linker` -> `General` -> `Additional Library Directories`;
+6. Add `MMKV.lib` to your project's `Linker` -> `Input` -> `Additional Dependencies`;
+7. Add `#include ` to your source file and we are done.
+
+
+note:
+
+1. MMKV is compiled with `MT/MTd` runtime by default. If your project uses `MD/MDd`, you should change MMKV's setting to match your project's (`C/C++` -> `Code Generation` -> `Runtime Library`), or vise versa.
+2. MMKV is developed with Visual Studio 2017, change the `Platform Toolset` if you use a different version of Visual Studio.
+
+For other installation options, see [Win32 Setup](https://github.com/Tencent/MMKV/wiki/windows_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.
+Setup MMKV on App startup, say in your `main()`, add these lines:
+
+```C++
+#include
+
+int main() {
+ std::wstring rootDir = getYourAppDocumentDir();
+ MMKV::initializeMMKV(rootDir);
+ //...
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```C++
+auto mmkv = MMKV::defaultMMKV();
+
+mmkv->set(true, "bool");
+std::cout << "bool = " << mmkv->getBool("bool") << std::endl;
+
+mmkv->set(1024, "int32");
+std::cout << "int32 = " << mmkv->getInt32("int32") << std::endl;
+
+mmkv->set("Hello, MMKV for Win32", "string");
+std::string result;
+mmkv->getString("string", result);
+std::cout << "string = " << result << std::endl;
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [Win32 Tutorial](https://github.com/Tencent/MMKV/wiki/windows_tutorial).
+
+# MMKV for POSIX
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of POSIX to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 7K in binary size**: MMKV adds about 7K on application size, and much less when zipped.
+
+
+## Getting Started
+
+### Installation Via CMake
+1. Getting source code from git repository:
+
+ ```
+ git clone https://github.com/Tencent/MMKV.git
+ ```
+2. Edit your `CMakeLists.txt`, add those lines:
+
+ ```cmake
+ add_subdirectory(mmkv/POSIX/src mmkv)
+ target_link_libraries(MyApp
+ mmkv)
+ ```
+3. Add `#include "MMKV.h"` to your source file and we are done.
+
+For other installation options, see [POSIX Setup](https://github.com/Tencent/MMKV/wiki/posix_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.
+Setup MMKV on App startup, say in your `main()`, add these lines:
+
+```C++
+#include "MMKV.h"
+
+int main() {
+ std::string rootDir = getYourAppDocumentDir();
+ MMKV::initializeMMKV(rootDir);
+ //...
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```C++
+auto mmkv = MMKV::defaultMMKV();
+
+mmkv->set(true, "bool");
+std::cout << "bool = " << mmkv->getBool("bool") << std::endl;
+
+mmkv->set(1024, "int32");
+std::cout << "int32 = " << mmkv->getInt32("int32") << std::endl;
+
+mmkv->set("Hello, MMKV for Win32", "string");
+std::string result;
+mmkv->getString("string", result);
+std::cout << "string = " << result << std::endl;
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [POSIX Tutorial](https://github.com/Tencent/MMKV/wiki/posix_tutorial).
+
+## License
+MMKV is published under the BSD 3-Clause license. For details check out the [LICENSE.TXT](./LICENSE.TXT).
+
+## Change Log
+Check out the [CHANGELOG.md](./CHANGELOG.md) for details of change history.
+
+## Contributing
+
+If you are interested in contributing, check out the [CONTRIBUTING.md](./CONTRIBUTING.md), also join our [Tencent OpenSource Plan](https://opensource.tencent.com/contribution).
+
+To give clarity of what is expected of our members, MMKV has adopted the code of conduct defined by the Contributor Covenant, which is widely used. And we think it articulates our values well. For more, check out the [Code of Conduct](./CODE_OF_CONDUCT.md).
+
+## FAQ & Feedback
+Check out the [FAQ](https://github.com/Tencent/MMKV/wiki/FAQ) first. Should there be any questions, don't hesitate to create [issues](https://github.com/Tencent/MMKV/issues).
diff --git a/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKV.h b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKV.h
new file mode 100644
index 00000000..cf5e2f59
--- /dev/null
+++ b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKV.h
@@ -0,0 +1,222 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "MMKVHandler.h"
+
+typedef NS_ENUM(NSUInteger, MMKVMode) {
+ MMKVSingleProcess = 0x1,
+ MMKVMultiProcess = 0x2,
+};
+
+@interface MMKV : NSObject
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir NS_SWIFT_NAME(initialize(rootDir:));
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:logLevel:));
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @param groupDir the root dir of multi-process MMKV, MMKV with MMKVMultiProcess mode will be stored in groupDir/mmkv
+/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:groupDir:logLevel:));
+
+/// a generic purpose instance (in MMKVSingleProcess mode)
++ (nullable instancetype)defaultMMKV;
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID NS_SWIFT_NAME(init(mmapID:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param mode MMKVMultiProcess for multi-process MMKV
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:mode:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(init(mmapID:cryptKey:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param mode MMKVMultiProcess for multi-process MMKV
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:cryptKey:mode:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param relativePath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID relativePath:(nullable NSString *)relativePath NS_SWIFT_NAME(init(mmapID:relativePath:)) __attribute__((deprecated("use +mmkvWithID:rootPath: instead")));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:rootPath:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param relativePath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey relativePath:(nullable NSString *)relativePath NS_SWIFT_NAME(init(mmapID:cryptKey:relativePath:)) __attribute__((deprecated("use +mmkvWithID:cryptKey:rootPath: instead")));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:cryptKey:rootPath:));
+
+// you can call this on applicationWillTerminate, it's totally fine if you don't call
++ (void)onAppTerminate;
+
++ (NSString *)mmkvBasePath;
+
+// if you want to change the base path, do it BEFORE getting any MMKV instance
+// otherwise the behavior is undefined
++ (void)setMMKVBasePath:(NSString *)basePath __attribute__((deprecated("use +initializeMMKV: instead", "+initializeMMKV:")));
+
+// transform plain text into encrypted text, or vice versa by passing newKey = nil
+// you can change existing crypt key with different key
+- (BOOL)reKey:(nullable NSData *)newKey NS_SWIFT_NAME(reset(cryptKey:));
+- (nullable NSData *)cryptKey;
+
+// just reset cryptKey (will not encrypt or decrypt anything)
+// usually you should call this method after other process reKey() the multi-process mmkv
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(checkReSet(cryptKey:));
+
+- (BOOL)setObject:(nullable NSObject *)object forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setBool:(BOOL)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setFloat:(float)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setDouble:(double)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setString:(NSString *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setData:(NSData *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (nullable id)getObjectOfClass:(Class)cls forKey:(NSString *)key NS_SWIFT_NAME(object(of:forKey:));
+
+- (BOOL)getBoolForKey:(NSString *)key __attribute__((swift_name("bool(forKey:)")));
+- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue __attribute__((swift_name("bool(forKey:defaultValue:)")));
+
+- (int32_t)getInt32ForKey:(NSString *)key NS_SWIFT_NAME(int32(forKey:));
+- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue NS_SWIFT_NAME(int32(forKey:defaultValue:));
+
+- (uint32_t)getUInt32ForKey:(NSString *)key NS_SWIFT_NAME(uint32(forKey:));
+- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue NS_SWIFT_NAME(uint32(forKey:defaultValue:));
+
+- (int64_t)getInt64ForKey:(NSString *)key NS_SWIFT_NAME(int64(forKey:));
+- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue NS_SWIFT_NAME(int64(forKey:defaultValue:));
+
+- (uint64_t)getUInt64ForKey:(NSString *)key NS_SWIFT_NAME(uint64(forKey:));
+- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue NS_SWIFT_NAME(uint64(forKey:defaultValue:));
+
+- (float)getFloatForKey:(NSString *)key NS_SWIFT_NAME(float(forKey:));
+- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue NS_SWIFT_NAME(float(forKey:defaultValue:));
+
+- (double)getDoubleForKey:(NSString *)key NS_SWIFT_NAME(double(forKey:));
+- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue NS_SWIFT_NAME(double(forKey:defaultValue:));
+
+- (nullable NSString *)getStringForKey:(NSString *)key NS_SWIFT_NAME(string(forKey:));
+- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue NS_SWIFT_NAME(string(forKey:defaultValue:));
+
+- (nullable NSDate *)getDateForKey:(NSString *)key NS_SWIFT_NAME(date(forKey:));
+- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue NS_SWIFT_NAME(date(forKey:defaultValue:));
+
+- (nullable NSData *)getDataForKey:(NSString *)key NS_SWIFT_NAME(data(forKey:));
+- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue NS_SWIFT_NAME(data(forKey:defaultValue:));
+
+// return the actual size consumption of the key's value
+// Note: might be a little bigger than value's length
+- (size_t)getValueSizeForKey:(NSString *)key NS_SWIFT_NAME(valueSize(forKey:));
+
+// return size written into buffer
+// return -1 on any error
+- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer NS_SWIFT_NAME(writeValue(forKey:buffer:));
+
+- (BOOL)containsKey:(NSString *)key NS_SWIFT_NAME(contains(key:));
+
+- (size_t)count;
+
+- (size_t)totalSize;
+
+- (size_t)actualSize;
+
+- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block;
+- (NSArray *)allKeys;
+
+- (void)removeValueForKey:(NSString *)key NS_SWIFT_NAME(removeValue(forKey:));
+
+- (void)removeValuesForKeys:(NSArray *)arrKeys NS_SWIFT_NAME(removeValues(forKeys:));
+
+- (void)clearAll;
+
+// MMKV's size won't reduce after deleting key-values
+// call this method after lots of deleting if you care about disk usage
+// note that `clearAll` has the similar effect of `trim`
+- (void)trim;
+
+// call this method if the instance is no longer needed in the near future
+// any subsequent call to the instance is undefined behavior
+- (void)close;
+
+// call this method if you are facing memory-warning
+// any subsequent call to the instance will load all key-values from file again
+- (void)clearMemoryCache;
+
+// you don't need to call this, really, I mean it
+// unless you worry about running out of battery
+- (void)sync;
+- (void)async;
+
+// check if content changed by other process
+- (void)checkContentChanged;
+
++ (void)registerHandler:(id)handler;
++ (void)unregiserHandler;
+
+// MMKVLogInfo by default
+// MMKVLogNone to disable all logging
++ (void)setLogLevel:(MMKVLogLevel)logLevel __attribute__((deprecated("use +initializeMMKV:logLevel: instead", "initializeMMKV:nil logLevel")));
+
+// Migrate NSUserDefault data to MMKV
+// return imported count of key-values
+- (uint32_t)migrateFromUserDefaults:(NSUserDefaults *)userDaults NS_SWIFT_NAME(migrateFrom(userDefaults:));
+
+// for CrashProtected Only
++ (BOOL)isFileValid:(NSString *)mmapID NS_SWIFT_NAME(isFileValid(for:));
++ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(isFileValid(for:rootPath:));
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKVHandler.h b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKVHandler.h
new file mode 100644
index 00000000..80fd57c6
--- /dev/null
+++ b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKVHandler.h
@@ -0,0 +1,60 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKVHandler_h
+#define MMKVHandler_h
+#import
+
+typedef NS_ENUM(NSUInteger, MMKVRecoverStrategic) {
+ MMKVOnErrorDiscard = 0,
+ MMKVOnErrorRecover,
+};
+
+typedef NS_ENUM(NSUInteger, MMKVLogLevel) {
+ MMKVLogDebug = 0, // not available for release/product build
+ MMKVLogInfo = 1, // default level
+ MMKVLogWarning,
+ MMKVLogError,
+ MMKVLogNone, // special level used to disable all log messages
+};
+
+// callback is called on the operating thread of the MMKV instance
+@protocol MMKVHandler
+@optional
+
+// by default MMKV will discard all datas on crc32-check failure
+// return `MMKVOnErrorRecover` to recover any data on the file
+- (MMKVRecoverStrategic)onMMKVCRCCheckFail:(NSString *)mmapID;
+
+// by default MMKV will discard all datas on file length mismatch
+// return `MMKVOnErrorRecover` to recover any data on the file
+- (MMKVRecoverStrategic)onMMKVFileLengthError:(NSString *)mmapID;
+
+// by default MMKV will print log using NSLog
+// implement this method to redirect MMKV's log
+- (void)mmkvLogWithLevel:(MMKVLogLevel)level file:(const char *)file line:(int)line func:(const char *)funcname message:(NSString *)message;
+
+// called when content is changed by other process
+// doesn't guarantee real-time notification
+- (void)onMMKVContentChange:(NSString *)mmapID;
+
+@end
+
+#endif /* MMKVHandler_h */
diff --git a/ios/Pods/MMKV/iOS/MMKV/MMKV/libMMKV.mm b/ios/Pods/MMKV/iOS/MMKV/MMKV/libMMKV.mm
new file mode 100644
index 00000000..0abce763
--- /dev/null
+++ b/ios/Pods/MMKV/iOS/MMKV/MMKV/libMMKV.mm
@@ -0,0 +1,699 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2020 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "MMKV.h"
+#import
+#import
+#import
+#import
+#import
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+#import
+#endif
+
+using namespace std;
+
+static NSMutableDictionary *g_instanceDic = nil;
+static mmkv::ThreadLock *g_lock;
+static id g_callbackHandler = nil;
+static bool g_isLogRedirecting = false;
+static NSString *g_basePath = nil;
+static NSString *g_groupPath = nil;
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+static BOOL g_isRunningInAppExtension = NO;
+#endif
+
+static void LogHandler(mmkv::MMKVLogLevel level, const char *file, int line, const char *function, NSString *message);
+static mmkv::MMKVRecoverStrategic ErrorHandler(const string &mmapID, mmkv::MMKVErrorType errorType);
+static void ContentChangeHandler(const string &mmapID);
+
+@implementation MMKV {
+ NSString *m_mmapID;
+ NSString *m_mmapKey;
+ mmkv::MMKV *m_mmkv;
+}
+
+#pragma mark - init
+
+// protect from some old code that don't call +initializeMMKV:
++ (void)initialize {
+ if (self == MMKV.class) {
+ g_instanceDic = [[NSMutableDictionary alloc] init];
+ g_lock = new mmkv::ThreadLock();
+ g_lock->initialize();
+
+ mmkv::MMKV::minimalInit([self mmkvBasePath].UTF8String);
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+ // just in case someone forget to set the MMKV_IOS_EXTENSION macro
+ if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) {
+ g_isRunningInAppExtension = YES;
+ }
+ if (!g_isRunningInAppExtension) {
+ auto appState = [UIApplication sharedApplication].applicationState;
+ auto isInBackground = (appState == UIApplicationStateBackground);
+ mmkv::MMKV::setIsInBackground(isInBackground);
+ MMKVInfo("appState:%ld", (long) appState);
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
+ }
+#endif
+ }
+}
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir {
+ return [MMKV initializeMMKV:rootDir logLevel:MMKVLogInfo];
+}
+
+static BOOL g_hasCalledInitializeMMKV = NO;
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel {
+ if (g_hasCalledInitializeMMKV) {
+ MMKVWarning("already called +initializeMMKV before, ignore this request");
+ return [self mmkvBasePath];
+ }
+ g_hasCalledInitializeMMKV = YES;
+
+ g_basePath = (rootDir != nil) ? rootDir : [self mmkvBasePath];
+ mmkv::MMKV::initializeMMKV(g_basePath.UTF8String, (mmkv::MMKVLogLevel) logLevel);
+
+ return [self mmkvBasePath];
+}
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel {
+ auto ret = [MMKV initializeMMKV:rootDir logLevel:logLevel];
+
+ g_groupPath = [groupDir stringByAppendingPathComponent:@"mmkv"];
+ MMKVInfo("groupDir: %@", g_groupPath);
+
+ return ret;
+}
+
+// a generic purpose instance
++ (instancetype)defaultMMKV {
+ return [MMKV mmkvWithID:(@"" DEFAULT_MMAP_ID) cryptKey:nil rootPath:nil mode:MMKVSingleProcess];
+}
+
+// any unique ID (com.tencent.xin.pay, etc)
++ (instancetype)mmkvWithID:(NSString *)mmapID {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:nil mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode {
+ auto rootPath = (mode == MMKVSingleProcess) ? nil : g_groupPath;
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:rootPath mode:mode];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:nil mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode {
+ auto rootPath = (mode == MMKVSingleProcess) ? nil : g_groupPath;
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:rootPath mode:mode];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:rootPath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID relativePath:(nullable NSString *)relativePath {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:relativePath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:rootPath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey relativePath:(nullable NSString *)relativePath {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:relativePath mode:MMKVSingleProcess];
+}
+
+// relatePath and MMKVMultiProcess mode can't be set at the same time, so we hide this method from public
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode {
+ if (!g_hasCalledInitializeMMKV) {
+ MMKVError("MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods");
+ }
+ if (mmapID.length <= 0) {
+ return nil;
+ }
+
+ SCOPED_LOCK(g_lock);
+
+ if (mode == MMKVMultiProcess) {
+ if (!rootPath) {
+ rootPath = g_groupPath;
+ }
+ if (!rootPath) {
+ MMKVError("Getting a multi-process MMKV [%@] without setting groupDir makes no sense", mmapID);
+ MMKV_ASSERT(0);
+ }
+ }
+ NSString *kvKey = [MMKV mmapKeyWithMMapID:mmapID rootPath:rootPath];
+ MMKV *kv = [g_instanceDic objectForKey:kvKey];
+ if (kv == nil) {
+ kv = [[MMKV alloc] initWithMMapID:mmapID cryptKey:cryptKey rootPath:rootPath mode:mode];
+ if (!kv->m_mmkv) {
+ return nil;
+ }
+ kv->m_mmapKey = kvKey;
+ [g_instanceDic setObject:kv forKey:kvKey];
+ }
+ return kv;
+}
+
+- (instancetype)initWithMMapID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(NSString *)rootPath mode:(MMKVMode)mode {
+ if (self = [super init]) {
+ string pathTmp;
+ if (rootPath.length > 0) {
+ pathTmp = rootPath.UTF8String;
+ }
+ string cryptKeyTmp;
+ if (cryptKey.length > 0) {
+ cryptKeyTmp = string((char *) cryptKey.bytes, cryptKey.length);
+ }
+ string *rootPathPtr = pathTmp.empty() ? nullptr : &pathTmp;
+ string *cryptKeyPtr = cryptKeyTmp.empty() ? nullptr : &cryptKeyTmp;
+ m_mmkv = mmkv::MMKV::mmkvWithID(mmapID.UTF8String, (mmkv::MMKVMode) mode, cryptKeyPtr, rootPathPtr);
+ if (!m_mmkv) {
+ return self;
+ }
+ m_mmapID = [NSString stringWithUTF8String:m_mmkv->mmapID().c_str()];
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+ if (!g_isRunningInAppExtension) {
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onMemoryWarning)
+ name:UIApplicationDidReceiveMemoryWarningNotification
+ object:nil];
+ }
+#endif
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self clearMemoryCache];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+#pragma mark - Application state
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+- (void)onMemoryWarning {
+ MMKVInfo("cleaning on memory warning %@", m_mmapID);
+
+ [self clearMemoryCache];
+}
+
++ (void)didEnterBackground {
+ mmkv::MMKV::setIsInBackground(true);
+ MMKVInfo("isInBackground:%d", true);
+}
+
++ (void)didBecomeActive {
+ mmkv::MMKV::setIsInBackground(false);
+ MMKVInfo("isInBackground:%d", false);
+}
+#endif
+
+- (void)clearAll {
+ m_mmkv->clearAll();
+}
+
+- (void)clearMemoryCache {
+ if (m_mmkv) {
+ m_mmkv->clearMemoryCache();
+ }
+}
+
+- (void)close {
+ SCOPED_LOCK(g_lock);
+ MMKVInfo("closing %@", m_mmapID);
+
+ m_mmkv->close();
+ m_mmkv = nullptr;
+
+ [g_instanceDic removeObjectForKey:m_mmapKey];
+}
+
+- (void)trim {
+ m_mmkv->trim();
+}
+
+#pragma mark - encryption & decryption
+
+#ifndef MMKV_DISABLE_CRYPT
+
+- (nullable NSData *)cryptKey {
+ auto str = m_mmkv->cryptKey();
+ if (str.length() > 0) {
+ return [NSData dataWithBytes:str.data() length:str.length()];
+ }
+ return nil;
+}
+
+- (BOOL)reKey:(nullable NSData *)newKey {
+ string key;
+ if (newKey.length > 0) {
+ key = string((char *) newKey.bytes, newKey.length);
+ }
+ return m_mmkv->reKey(key);
+}
+
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {
+ if (cryptKey.length > 0) {
+ string key = string((char *) cryptKey.bytes, cryptKey.length);
+ m_mmkv->checkReSetCryptKey(&key);
+ } else {
+ m_mmkv->checkReSetCryptKey(nullptr);
+ }
+}
+
+#else
+
+- (nullable NSData *)cryptKey {
+ return nil;
+}
+
+- (BOOL)reKey:(nullable NSData *)newKey {
+ return NO;
+}
+
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+#pragma mark - set & get
+
+- (BOOL)setObject:(nullable NSObject *)object forKey:(NSString *)key {
+ return m_mmkv->set(object, key);
+}
+
+- (BOOL)setBool:(BOOL)value forKey:(NSString *)key {
+ return m_mmkv->set((bool) value, key);
+}
+
+- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setFloat:(float)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setDouble:(double)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setString:(NSString *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (BOOL)setData:(NSData *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (id)getObjectOfClass:(Class)cls forKey:(NSString *)key {
+ return m_mmkv->getObject(key, cls);
+}
+
+- (BOOL)getBoolForKey:(NSString *)key {
+ return [self getBoolForKey:key defaultValue:FALSE];
+}
+- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue {
+ return m_mmkv->getBool(key, defaultValue);
+}
+
+- (int32_t)getInt32ForKey:(NSString *)key {
+ return [self getInt32ForKey:key defaultValue:0];
+}
+- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue {
+ return m_mmkv->getInt32(key, defaultValue);
+}
+
+- (uint32_t)getUInt32ForKey:(NSString *)key {
+ return [self getUInt32ForKey:key defaultValue:0];
+}
+- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue {
+ return m_mmkv->getUInt32(key, defaultValue);
+}
+
+- (int64_t)getInt64ForKey:(NSString *)key {
+ return [self getInt64ForKey:key defaultValue:0];
+}
+- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue {
+ return m_mmkv->getInt64(key, defaultValue);
+}
+
+- (uint64_t)getUInt64ForKey:(NSString *)key {
+ return [self getUInt64ForKey:key defaultValue:0];
+}
+- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue {
+ return m_mmkv->getUInt64(key, defaultValue);
+}
+
+- (float)getFloatForKey:(NSString *)key {
+ return [self getFloatForKey:key defaultValue:0];
+}
+- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue {
+ return m_mmkv->getFloat(key, defaultValue);
+}
+
+- (double)getDoubleForKey:(NSString *)key {
+ return [self getDoubleForKey:key defaultValue:0];
+}
+- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue {
+ return m_mmkv->getDouble(key, defaultValue);
+}
+
+- (nullable NSString *)getStringForKey:(NSString *)key {
+ return [self getStringForKey:key defaultValue:nil];
+}
+- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSString *valueString = [self getObjectOfClass:NSString.class forKey:key];
+ if (!valueString) {
+ valueString = defaultValue;
+ }
+ return valueString;
+}
+
+- (nullable NSDate *)getDateForKey:(NSString *)key {
+ return [self getDateForKey:key defaultValue:nil];
+}
+- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSDate *valueDate = [self getObjectOfClass:NSDate.class forKey:key];
+ if (!valueDate) {
+ valueDate = defaultValue;
+ }
+ return valueDate;
+}
+
+- (nullable NSData *)getDataForKey:(NSString *)key {
+ return [self getDataForKey:key defaultValue:nil];
+}
+- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSData *valueData = [self getObjectOfClass:NSData.class forKey:key];
+ if (!valueData) {
+ valueData = defaultValue;
+ }
+ return valueData;
+}
+
+- (size_t)getValueSizeForKey:(NSString *)key {
+ return m_mmkv->getValueSize(key, false);
+}
+
+- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer {
+ return m_mmkv->writeValueToBuffer(key, buffer.mutableBytes, static_cast(buffer.length));
+}
+
+#pragma mark - enumerate
+
+- (BOOL)containsKey:(NSString *)key {
+ return m_mmkv->containsKey(key);
+}
+
+- (size_t)count {
+ return m_mmkv->count();
+}
+
+- (size_t)totalSize {
+ return m_mmkv->totalSize();
+}
+
+- (size_t)actualSize {
+ return m_mmkv->actualSize();
+}
+
+- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block {
+ m_mmkv->enumerateKeys(block);
+}
+
+- (NSArray *)allKeys {
+ return m_mmkv->allKeys();
+}
+
+- (void)removeValueForKey:(NSString *)key {
+ m_mmkv->removeValueForKey(key);
+}
+
+- (void)removeValuesForKeys:(NSArray *)arrKeys {
+ m_mmkv->removeValuesForKeys(arrKeys);
+}
+
+#pragma mark - Boring stuff
+
+- (void)sync {
+ m_mmkv->sync(mmkv::MMKV_SYNC);
+}
+
+- (void)async {
+ m_mmkv->sync(mmkv::MMKV_ASYNC);
+}
+
+- (void)checkContentChanged {
+ m_mmkv->checkContentChanged();
+}
+
++ (void)onAppTerminate {
+ SCOPED_LOCK(g_lock);
+
+ [g_instanceDic removeAllObjects];
+
+ mmkv::MMKV::onExit();
+}
+
++ (NSString *)mmkvBasePath {
+ if (g_basePath.length > 0) {
+ return g_basePath;
+ }
+
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentPath = (NSString *) [paths firstObject];
+ if ([documentPath length] > 0) {
+ g_basePath = [documentPath stringByAppendingPathComponent:@"mmkv"];
+ return g_basePath;
+ } else {
+ return @"";
+ }
+}
+
++ (void)setMMKVBasePath:(NSString *)basePath {
+ if (basePath.length > 0) {
+ g_basePath = basePath;
+ [MMKV initializeMMKV:basePath];
+
+ // still warn about it
+ g_hasCalledInitializeMMKV = NO;
+
+ MMKVInfo("set MMKV base path to: %@", g_basePath);
+ }
+}
+
+static NSString *md5(NSString *value) {
+ uint8_t md[MD5_DIGEST_LENGTH] = {};
+ char tmp[3] = {}, buf[33] = {};
+ auto data = [value dataUsingEncoding:NSUTF8StringEncoding];
+ openssl::MD5((uint8_t *) data.bytes, data.length, md);
+ for (auto ch : md) {
+ snprintf(tmp, sizeof(tmp), "%2.2x", ch);
+ strcat(buf, tmp);
+ }
+ return [NSString stringWithCString:buf encoding:NSASCIIStringEncoding];
+}
+
++ (NSString *)mmapKeyWithMMapID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {
+ NSString *string = nil;
+ if ([rootPath length] > 0 && [rootPath isEqualToString:[MMKV mmkvBasePath]] == NO) {
+ string = md5([rootPath stringByAppendingPathComponent:mmapID]);
+ } else {
+ string = mmapID;
+ }
+ MMKVDebug("mmapKey: %@", string);
+ return string;
+}
+
++ (BOOL)isFileValid:(NSString *)mmapID {
+ return [self isFileValid:mmapID rootPath:nil];
+}
+
++ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path {
+ if (mmapID.length > 0) {
+ if (path.length > 0) {
+ string rootPath(path.UTF8String);
+ return mmkv::MMKV::isFileValid(mmapID.UTF8String, &rootPath);
+ } else {
+ return mmkv::MMKV::isFileValid(mmapID.UTF8String, nullptr);
+ }
+ }
+ return NO;
+}
+
++ (void)registerHandler:(id)handler {
+ SCOPED_LOCK(g_lock);
+ g_callbackHandler = handler;
+
+ if ([g_callbackHandler respondsToSelector:@selector(mmkvLogWithLevel:file:line:func:message:)]) {
+ g_isLogRedirecting = true;
+ mmkv::MMKV::registerLogHandler(LogHandler);
+ }
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVCRCCheckFail:)] ||
+ [g_callbackHandler respondsToSelector:@selector(onMMKVFileLengthError:)]) {
+ mmkv::MMKV::registerErrorHandler(ErrorHandler);
+ }
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentChange:)]) {
+ mmkv::MMKV::registerContentChangeHandler(ContentChangeHandler);
+ }
+}
+
++ (void)unregiserHandler {
+ SCOPED_LOCK(g_lock);
+
+ g_isLogRedirecting = false;
+ g_callbackHandler = nil;
+
+ mmkv::MMKV::unRegisterLogHandler();
+ mmkv::MMKV::unRegisterErrorHandler();
+ mmkv::MMKV::unRegisterContentChangeHandler();
+}
+
++ (void)setLogLevel:(MMKVLogLevel)logLevel {
+ mmkv::MMKV::setLogLevel((mmkv::MMKVLogLevel) logLevel);
+}
+
+- (uint32_t)migrateFromUserDefaults:(NSUserDefaults *)userDaults {
+ NSDictionary *dic = [userDaults dictionaryRepresentation];
+ if (dic.count <= 0) {
+ MMKVInfo("migrate data fail, userDaults is nil or empty");
+ return 0;
+ }
+ __block uint32_t count = 0;
+ [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
+ if ([key isKindOfClass:[NSString class]]) {
+ NSString *stringKey = key;
+ if ([MMKV tranlateData:obj key:stringKey kv:self]) {
+ count++;
+ }
+ } else {
+ MMKVWarning("unknown type of key:%@", key);
+ }
+ }];
+ return count;
+}
+
++ (BOOL)tranlateData:(id)obj key:(NSString *)key kv:(MMKV *)kv {
+ if ([obj isKindOfClass:[NSString class]]) {
+ return [kv setString:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSData class]]) {
+ return [kv setData:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSDate class]]) {
+ return [kv setDate:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSNumber class]]) {
+ NSNumber *num = obj;
+ CFNumberType numberType = CFNumberGetType((CFNumberRef) obj);
+ switch (numberType) {
+ case kCFNumberCharType:
+ case kCFNumberSInt8Type:
+ case kCFNumberSInt16Type:
+ case kCFNumberSInt32Type:
+ case kCFNumberIntType:
+ case kCFNumberShortType:
+ return [kv setInt32:num.intValue forKey:key];
+ case kCFNumberSInt64Type:
+ case kCFNumberLongType:
+ case kCFNumberNSIntegerType:
+ case kCFNumberLongLongType:
+ return [kv setInt64:num.longLongValue forKey:key];
+ case kCFNumberFloat32Type:
+ return [kv setFloat:num.floatValue forKey:key];
+ case kCFNumberFloat64Type:
+ case kCFNumberDoubleType:
+ return [kv setDouble:num.doubleValue forKey:key];
+ default:
+ MMKVWarning("unknown number type:%ld, key:%@", (long) numberType, key);
+ return NO;
+ }
+ } else if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
+ return [kv setObject:obj forKey:key];
+ } else {
+ MMKVWarning("unknown type of key:%@", key);
+ }
+ return NO;
+}
+
+@end
+
+#pragma makr - callbacks
+
+static void LogHandler(mmkv::MMKVLogLevel level, const char *file, int line, const char *function, NSString *message) {
+ [g_callbackHandler mmkvLogWithLevel:(MMKVLogLevel) level file:file line:line func:function message:message];
+}
+
+static mmkv::MMKVRecoverStrategic ErrorHandler(const string &mmapID, mmkv::MMKVErrorType errorType) {
+ if (errorType == mmkv::MMKVCRCCheckFail) {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVCRCCheckFail:)]) {
+ auto ret = [g_callbackHandler onMMKVCRCCheckFail:[NSString stringWithUTF8String:mmapID.c_str()]];
+ return (mmkv::MMKVRecoverStrategic) ret;
+ }
+ } else {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVFileLengthError:)]) {
+ auto ret = [g_callbackHandler onMMKVFileLengthError:[NSString stringWithUTF8String:mmapID.c_str()]];
+ return (mmkv::MMKVRecoverStrategic) ret;
+ }
+ }
+ return mmkv::OnErrorDiscard;
+}
+
+static void ContentChangeHandler(const string &mmapID) {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentChange:)]) {
+ [g_callbackHandler onMMKVContentChange:[NSString stringWithUTF8String:mmapID.c_str()]];
+ }
+}
diff --git a/ios/Pods/MMKVAppExtension/LICENSE.TXT b/ios/Pods/MMKVAppExtension/LICENSE.TXT
new file mode 100644
index 00000000..096acfb2
--- /dev/null
+++ b/ios/Pods/MMKVAppExtension/LICENSE.TXT
@@ -0,0 +1,189 @@
+Tencent is pleased to support the open source community by making MMKV available.
+Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
+If you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.
+If you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms. Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.
+A copy of the BSD 3-Clause License is included in this file.
+
+Other dependencies and licenses:
+
+Open Source Software Licensed Under the OpenSSL License:
+----------------------------------------------------------------------------------------
+1. OpenSSL 1.1.0i
+Copyright (c) 1998-2018 The OpenSSL Project.
+All rights reserved.
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+
+Terms of the OpenSSL License:
+---------------------------------------------------
+LICENSE ISSUES:
+--------------------------------------------------------------------
+
+The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.
+See below for the actual license texts.
+
+OpenSSL License:
+--------------------------------------------------------------------
+Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. All advertising materials mentioning features or use of this software must display the following acknowledgment:
+"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL" nor may "OpenSSL" appear in their names without prior written permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com).
+
+
+Original SSLeay License:
+--------------------------------------------------------------------
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software must display the following acknowledgement:" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]
+
+
+
+Open Source Software Licensed Under the Apache License, Version 2.0:
+The below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.
+----------------------------------------------------------------------------------------
+1. MultiprocessSharedPreferences v1.0
+Copyright (C) 2014 seven456@gmail.com
+
+
+Terms of the Apache License, Version 2.0:
+--------------------------------------------------------------------
+Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”
+
+“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+d) If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+
+Open Source Software Licensed Under the zlib License:
+The below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.
+----------------------------------------------------------------------------------------
+1. zlib v1.2.11
+Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+
+Terms of the zlib License:
+--------------------------------------------------------------------
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+
+
+Terms of the BSD 3-Clause License:
+--------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ios/Pods/MMKVAppExtension/README.md b/ios/Pods/MMKVAppExtension/README.md
new file mode 100644
index 00000000..36310c84
--- /dev/null
+++ b/ios/Pods/MMKVAppExtension/README.md
@@ -0,0 +1,287 @@
+[![license](https://img.shields.io/badge/license-BSD_3-brightgreen.svg?style=flat)](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT)
+[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/MMKV/pulls)
+[![Release Version](https://img.shields.io/badge/release-1.2.1-brightgreen.svg)](https://github.com/Tencent/MMKV/releases)
+[![Platform](https://img.shields.io/badge/Platform-%20Android%20%7C%20iOS%2FmacOS%20%7C%20Win32%20%7C%20POSIX-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home)
+
+中文版本请参看[这里](./readme_cn.md)
+
+MMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's currently available on **Android**, **iOS/macOS**, **Win32** and **POSIX**.
+
+# MMKV for Android
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of Android to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 50K in binary size**: MMKV adds about 50K per architecture on App size, and much less when zipped (apk).
+
+
+## Getting Started
+
+### Installation Via Maven
+Add the following lines to `build.gradle` on your app module:
+
+```gradle
+dependencies {
+ implementation 'com.tencent:mmkv-static:1.2.1'
+ // replace "1.2.1" with any available version
+}
+```
+
+For other installation options, see [Android Setup](https://github.com/Tencent/MMKV/wiki/android_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.
+Setup MMKV on App startup, say your `Application` class, add these lines:
+
+```Java
+public void onCreate() {
+ super.onCreate();
+
+ String rootDir = MMKV.initialize(this);
+ System.out.println("mmkv root: " + rootDir);
+ //……
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```Java
+import com.tencent.mmkv.MMKV;
+
+MMKV kv = MMKV.defaultMMKV();
+
+kv.encode("bool", true);
+boolean bValue = kv.decodeBool("bool");
+
+kv.encode("int", Integer.MIN_VALUE);
+int iValue = kv.decodeInt("int");
+
+kv.encode("string", "Hello from mmkv");
+String str = kv.decodeString("string");
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [Android Tutorial](https://github.com/Tencent/MMKV/wiki/android_tutorial).
+
+## Performance
+Writing random `int` for 1000 times, we get this chart:
+![](https://github.com/Tencent/MMKV/wiki/assets/profile_android_mini.png)
+For more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/android_benchmark).
+
+# MMKV for iOS/macOS
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of iOS/macOS to achieve best performance.
+
+* **Easy-to-use**. You can use MMKV as you go, no configurations needed. All changes are saved immediately, no `synchronize` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **Less than 30K in binary size**: MMKV adds less than 30K per architecture on App size, and much less when zipped (ipa).
+
+## Getting Started
+
+### Installation Via CocoaPods:
+ 1. Install [CocoaPods](https://guides.CocoaPods.org/using/getting-started.html);
+ 2. Open terminal, `cd` to your project directory, run `pod repo update` to make CocoaPods aware of the latest available MMKV versions;
+ 3. Edit your Podfile, add `pod 'MMKV'` to your app target;
+ 4. Run `pod install`;
+ 5. Open the `.xcworkspace` file generated by CocoaPods;
+ 6. Add `#import ` to your source file and we are done.
+
+For other installation options, see [iOS/macOS Setup](https://github.com/Tencent/MMKV/wiki/iOS_setup).
+
+### Quick Tutorial
+You can use MMKV as you go, no configurations needed. All changes are saved immediately, no `synchronize` calls needed.
+Setup MMKV on App startup, in your `-[MyApp application: didFinishLaunchingWithOptions:]`, add these lines:
+
+```objective-c
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // init MMKV in the main thread
+ [MMKV initializeMMKV:nil];
+
+ //...
+ return YES;
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```objective-c
+MMKV *mmkv = [MMKV defaultMMKV];
+
+[mmkv setBool:YES forKey:@"bool"];
+BOOL bValue = [mmkv getBoolForKey:@"bool"];
+
+[mmkv setInt32:-1024 forKey:@"int32"];
+int32_t iValue = [mmkv getInt32ForKey:@"int32"];
+
+[mmkv setString:@"hello, mmkv" forKey:@"string"];
+NSString *str = [mmkv getStringForKey:@"string"];
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found [here](https://github.com/Tencent/MMKV/wiki/iOS_tutorial).
+
+## Performance
+Writing random `int` for 10000 times, we get this chart:
+![](https://github.com/Tencent/MMKV/wiki/assets/profile_mini.png)
+For more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/iOS_benchmark).
+
+
+# MMKV for Win32
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of Windows to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 10K in binary size**: MMKV adds about 10K on application size, and much less when zipped.
+
+
+## Getting Started
+
+### Installation Via Source
+1. Getting source code from git repository:
+
+ ```
+ git clone https://github.com/Tencent/MMKV.git
+ ```
+
+2. Add `Win32/MMKV/MMKV.vcxproj` to your solution;
+3. Add `MMKV` project to your project's dependencies;
+4. Add `$(OutDir)include` to your project's `C/C++` -> `General` -> `Additional Include Directories`;
+5. Add `$(OutDir)` to your project's `Linker` -> `General` -> `Additional Library Directories`;
+6. Add `MMKV.lib` to your project's `Linker` -> `Input` -> `Additional Dependencies`;
+7. Add `#include ` to your source file and we are done.
+
+
+note:
+
+1. MMKV is compiled with `MT/MTd` runtime by default. If your project uses `MD/MDd`, you should change MMKV's setting to match your project's (`C/C++` -> `Code Generation` -> `Runtime Library`), or vise versa.
+2. MMKV is developed with Visual Studio 2017, change the `Platform Toolset` if you use a different version of Visual Studio.
+
+For other installation options, see [Win32 Setup](https://github.com/Tencent/MMKV/wiki/windows_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.
+Setup MMKV on App startup, say in your `main()`, add these lines:
+
+```C++
+#include
+
+int main() {
+ std::wstring rootDir = getYourAppDocumentDir();
+ MMKV::initializeMMKV(rootDir);
+ //...
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```C++
+auto mmkv = MMKV::defaultMMKV();
+
+mmkv->set(true, "bool");
+std::cout << "bool = " << mmkv->getBool("bool") << std::endl;
+
+mmkv->set(1024, "int32");
+std::cout << "int32 = " << mmkv->getInt32("int32") << std::endl;
+
+mmkv->set("Hello, MMKV for Win32", "string");
+std::string result;
+mmkv->getString("string", result);
+std::cout << "string = " << result << std::endl;
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [Win32 Tutorial](https://github.com/Tencent/MMKV/wiki/windows_tutorial).
+
+# MMKV for POSIX
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of POSIX to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 7K in binary size**: MMKV adds about 7K on application size, and much less when zipped.
+
+
+## Getting Started
+
+### Installation Via CMake
+1. Getting source code from git repository:
+
+ ```
+ git clone https://github.com/Tencent/MMKV.git
+ ```
+2. Edit your `CMakeLists.txt`, add those lines:
+
+ ```cmake
+ add_subdirectory(mmkv/POSIX/src mmkv)
+ target_link_libraries(MyApp
+ mmkv)
+ ```
+3. Add `#include "MMKV.h"` to your source file and we are done.
+
+For other installation options, see [POSIX Setup](https://github.com/Tencent/MMKV/wiki/posix_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.
+Setup MMKV on App startup, say in your `main()`, add these lines:
+
+```C++
+#include "MMKV.h"
+
+int main() {
+ std::string rootDir = getYourAppDocumentDir();
+ MMKV::initializeMMKV(rootDir);
+ //...
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```C++
+auto mmkv = MMKV::defaultMMKV();
+
+mmkv->set(true, "bool");
+std::cout << "bool = " << mmkv->getBool("bool") << std::endl;
+
+mmkv->set(1024, "int32");
+std::cout << "int32 = " << mmkv->getInt32("int32") << std::endl;
+
+mmkv->set("Hello, MMKV for Win32", "string");
+std::string result;
+mmkv->getString("string", result);
+std::cout << "string = " << result << std::endl;
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [POSIX Tutorial](https://github.com/Tencent/MMKV/wiki/posix_tutorial).
+
+## License
+MMKV is published under the BSD 3-Clause license. For details check out the [LICENSE.TXT](./LICENSE.TXT).
+
+## Change Log
+Check out the [CHANGELOG.md](./CHANGELOG.md) for details of change history.
+
+## Contributing
+
+If you are interested in contributing, check out the [CONTRIBUTING.md](./CONTRIBUTING.md), also join our [Tencent OpenSource Plan](https://opensource.tencent.com/contribution).
+
+To give clarity of what is expected of our members, MMKV has adopted the code of conduct defined by the Contributor Covenant, which is widely used. And we think it articulates our values well. For more, check out the [Code of Conduct](./CODE_OF_CONDUCT.md).
+
+## FAQ & Feedback
+Check out the [FAQ](https://github.com/Tencent/MMKV/wiki/FAQ) first. Should there be any questions, don't hesitate to create [issues](https://github.com/Tencent/MMKV/issues).
diff --git a/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/MMKV.h b/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/MMKV.h
new file mode 100644
index 00000000..cf5e2f59
--- /dev/null
+++ b/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/MMKV.h
@@ -0,0 +1,222 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "MMKVHandler.h"
+
+typedef NS_ENUM(NSUInteger, MMKVMode) {
+ MMKVSingleProcess = 0x1,
+ MMKVMultiProcess = 0x2,
+};
+
+@interface MMKV : NSObject
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir NS_SWIFT_NAME(initialize(rootDir:));
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:logLevel:));
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @param groupDir the root dir of multi-process MMKV, MMKV with MMKVMultiProcess mode will be stored in groupDir/mmkv
+/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:groupDir:logLevel:));
+
+/// a generic purpose instance (in MMKVSingleProcess mode)
++ (nullable instancetype)defaultMMKV;
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID NS_SWIFT_NAME(init(mmapID:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param mode MMKVMultiProcess for multi-process MMKV
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:mode:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(init(mmapID:cryptKey:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param mode MMKVMultiProcess for multi-process MMKV
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:cryptKey:mode:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param relativePath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID relativePath:(nullable NSString *)relativePath NS_SWIFT_NAME(init(mmapID:relativePath:)) __attribute__((deprecated("use +mmkvWithID:rootPath: instead")));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:rootPath:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param relativePath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey relativePath:(nullable NSString *)relativePath NS_SWIFT_NAME(init(mmapID:cryptKey:relativePath:)) __attribute__((deprecated("use +mmkvWithID:cryptKey:rootPath: instead")));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:cryptKey:rootPath:));
+
+// you can call this on applicationWillTerminate, it's totally fine if you don't call
++ (void)onAppTerminate;
+
++ (NSString *)mmkvBasePath;
+
+// if you want to change the base path, do it BEFORE getting any MMKV instance
+// otherwise the behavior is undefined
++ (void)setMMKVBasePath:(NSString *)basePath __attribute__((deprecated("use +initializeMMKV: instead", "+initializeMMKV:")));
+
+// transform plain text into encrypted text, or vice versa by passing newKey = nil
+// you can change existing crypt key with different key
+- (BOOL)reKey:(nullable NSData *)newKey NS_SWIFT_NAME(reset(cryptKey:));
+- (nullable NSData *)cryptKey;
+
+// just reset cryptKey (will not encrypt or decrypt anything)
+// usually you should call this method after other process reKey() the multi-process mmkv
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(checkReSet(cryptKey:));
+
+- (BOOL)setObject:(nullable NSObject *)object forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setBool:(BOOL)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setFloat:(float)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setDouble:(double)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setString:(NSString *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setData:(NSData *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (nullable id)getObjectOfClass:(Class)cls forKey:(NSString *)key NS_SWIFT_NAME(object(of:forKey:));
+
+- (BOOL)getBoolForKey:(NSString *)key __attribute__((swift_name("bool(forKey:)")));
+- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue __attribute__((swift_name("bool(forKey:defaultValue:)")));
+
+- (int32_t)getInt32ForKey:(NSString *)key NS_SWIFT_NAME(int32(forKey:));
+- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue NS_SWIFT_NAME(int32(forKey:defaultValue:));
+
+- (uint32_t)getUInt32ForKey:(NSString *)key NS_SWIFT_NAME(uint32(forKey:));
+- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue NS_SWIFT_NAME(uint32(forKey:defaultValue:));
+
+- (int64_t)getInt64ForKey:(NSString *)key NS_SWIFT_NAME(int64(forKey:));
+- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue NS_SWIFT_NAME(int64(forKey:defaultValue:));
+
+- (uint64_t)getUInt64ForKey:(NSString *)key NS_SWIFT_NAME(uint64(forKey:));
+- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue NS_SWIFT_NAME(uint64(forKey:defaultValue:));
+
+- (float)getFloatForKey:(NSString *)key NS_SWIFT_NAME(float(forKey:));
+- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue NS_SWIFT_NAME(float(forKey:defaultValue:));
+
+- (double)getDoubleForKey:(NSString *)key NS_SWIFT_NAME(double(forKey:));
+- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue NS_SWIFT_NAME(double(forKey:defaultValue:));
+
+- (nullable NSString *)getStringForKey:(NSString *)key NS_SWIFT_NAME(string(forKey:));
+- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue NS_SWIFT_NAME(string(forKey:defaultValue:));
+
+- (nullable NSDate *)getDateForKey:(NSString *)key NS_SWIFT_NAME(date(forKey:));
+- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue NS_SWIFT_NAME(date(forKey:defaultValue:));
+
+- (nullable NSData *)getDataForKey:(NSString *)key NS_SWIFT_NAME(data(forKey:));
+- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue NS_SWIFT_NAME(data(forKey:defaultValue:));
+
+// return the actual size consumption of the key's value
+// Note: might be a little bigger than value's length
+- (size_t)getValueSizeForKey:(NSString *)key NS_SWIFT_NAME(valueSize(forKey:));
+
+// return size written into buffer
+// return -1 on any error
+- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer NS_SWIFT_NAME(writeValue(forKey:buffer:));
+
+- (BOOL)containsKey:(NSString *)key NS_SWIFT_NAME(contains(key:));
+
+- (size_t)count;
+
+- (size_t)totalSize;
+
+- (size_t)actualSize;
+
+- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block;
+- (NSArray *)allKeys;
+
+- (void)removeValueForKey:(NSString *)key NS_SWIFT_NAME(removeValue(forKey:));
+
+- (void)removeValuesForKeys:(NSArray *)arrKeys NS_SWIFT_NAME(removeValues(forKeys:));
+
+- (void)clearAll;
+
+// MMKV's size won't reduce after deleting key-values
+// call this method after lots of deleting if you care about disk usage
+// note that `clearAll` has the similar effect of `trim`
+- (void)trim;
+
+// call this method if the instance is no longer needed in the near future
+// any subsequent call to the instance is undefined behavior
+- (void)close;
+
+// call this method if you are facing memory-warning
+// any subsequent call to the instance will load all key-values from file again
+- (void)clearMemoryCache;
+
+// you don't need to call this, really, I mean it
+// unless you worry about running out of battery
+- (void)sync;
+- (void)async;
+
+// check if content changed by other process
+- (void)checkContentChanged;
+
++ (void)registerHandler:(id)handler;
++ (void)unregiserHandler;
+
+// MMKVLogInfo by default
+// MMKVLogNone to disable all logging
++ (void)setLogLevel:(MMKVLogLevel)logLevel __attribute__((deprecated("use +initializeMMKV:logLevel: instead", "initializeMMKV:nil logLevel")));
+
+// Migrate NSUserDefault data to MMKV
+// return imported count of key-values
+- (uint32_t)migrateFromUserDefaults:(NSUserDefaults *)userDaults NS_SWIFT_NAME(migrateFrom(userDefaults:));
+
+// for CrashProtected Only
++ (BOOL)isFileValid:(NSString *)mmapID NS_SWIFT_NAME(isFileValid(for:));
++ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(isFileValid(for:rootPath:));
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/MMKVHandler.h b/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/MMKVHandler.h
new file mode 100644
index 00000000..80fd57c6
--- /dev/null
+++ b/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/MMKVHandler.h
@@ -0,0 +1,60 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKVHandler_h
+#define MMKVHandler_h
+#import
+
+typedef NS_ENUM(NSUInteger, MMKVRecoverStrategic) {
+ MMKVOnErrorDiscard = 0,
+ MMKVOnErrorRecover,
+};
+
+typedef NS_ENUM(NSUInteger, MMKVLogLevel) {
+ MMKVLogDebug = 0, // not available for release/product build
+ MMKVLogInfo = 1, // default level
+ MMKVLogWarning,
+ MMKVLogError,
+ MMKVLogNone, // special level used to disable all log messages
+};
+
+// callback is called on the operating thread of the MMKV instance
+@protocol MMKVHandler
+@optional
+
+// by default MMKV will discard all datas on crc32-check failure
+// return `MMKVOnErrorRecover` to recover any data on the file
+- (MMKVRecoverStrategic)onMMKVCRCCheckFail:(NSString *)mmapID;
+
+// by default MMKV will discard all datas on file length mismatch
+// return `MMKVOnErrorRecover` to recover any data on the file
+- (MMKVRecoverStrategic)onMMKVFileLengthError:(NSString *)mmapID;
+
+// by default MMKV will print log using NSLog
+// implement this method to redirect MMKV's log
+- (void)mmkvLogWithLevel:(MMKVLogLevel)level file:(const char *)file line:(int)line func:(const char *)funcname message:(NSString *)message;
+
+// called when content is changed by other process
+// doesn't guarantee real-time notification
+- (void)onMMKVContentChange:(NSString *)mmapID;
+
+@end
+
+#endif /* MMKVHandler_h */
diff --git a/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/libMMKV.mm b/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/libMMKV.mm
new file mode 100644
index 00000000..0abce763
--- /dev/null
+++ b/ios/Pods/MMKVAppExtension/iOS/MMKV/MMKV/libMMKV.mm
@@ -0,0 +1,699 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2020 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "MMKV.h"
+#import
+#import
+#import
+#import
+#import
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+#import
+#endif
+
+using namespace std;
+
+static NSMutableDictionary *g_instanceDic = nil;
+static mmkv::ThreadLock *g_lock;
+static id g_callbackHandler = nil;
+static bool g_isLogRedirecting = false;
+static NSString *g_basePath = nil;
+static NSString *g_groupPath = nil;
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+static BOOL g_isRunningInAppExtension = NO;
+#endif
+
+static void LogHandler(mmkv::MMKVLogLevel level, const char *file, int line, const char *function, NSString *message);
+static mmkv::MMKVRecoverStrategic ErrorHandler(const string &mmapID, mmkv::MMKVErrorType errorType);
+static void ContentChangeHandler(const string &mmapID);
+
+@implementation MMKV {
+ NSString *m_mmapID;
+ NSString *m_mmapKey;
+ mmkv::MMKV *m_mmkv;
+}
+
+#pragma mark - init
+
+// protect from some old code that don't call +initializeMMKV:
++ (void)initialize {
+ if (self == MMKV.class) {
+ g_instanceDic = [[NSMutableDictionary alloc] init];
+ g_lock = new mmkv::ThreadLock();
+ g_lock->initialize();
+
+ mmkv::MMKV::minimalInit([self mmkvBasePath].UTF8String);
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+ // just in case someone forget to set the MMKV_IOS_EXTENSION macro
+ if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) {
+ g_isRunningInAppExtension = YES;
+ }
+ if (!g_isRunningInAppExtension) {
+ auto appState = [UIApplication sharedApplication].applicationState;
+ auto isInBackground = (appState == UIApplicationStateBackground);
+ mmkv::MMKV::setIsInBackground(isInBackground);
+ MMKVInfo("appState:%ld", (long) appState);
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
+ }
+#endif
+ }
+}
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir {
+ return [MMKV initializeMMKV:rootDir logLevel:MMKVLogInfo];
+}
+
+static BOOL g_hasCalledInitializeMMKV = NO;
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel {
+ if (g_hasCalledInitializeMMKV) {
+ MMKVWarning("already called +initializeMMKV before, ignore this request");
+ return [self mmkvBasePath];
+ }
+ g_hasCalledInitializeMMKV = YES;
+
+ g_basePath = (rootDir != nil) ? rootDir : [self mmkvBasePath];
+ mmkv::MMKV::initializeMMKV(g_basePath.UTF8String, (mmkv::MMKVLogLevel) logLevel);
+
+ return [self mmkvBasePath];
+}
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel {
+ auto ret = [MMKV initializeMMKV:rootDir logLevel:logLevel];
+
+ g_groupPath = [groupDir stringByAppendingPathComponent:@"mmkv"];
+ MMKVInfo("groupDir: %@", g_groupPath);
+
+ return ret;
+}
+
+// a generic purpose instance
++ (instancetype)defaultMMKV {
+ return [MMKV mmkvWithID:(@"" DEFAULT_MMAP_ID) cryptKey:nil rootPath:nil mode:MMKVSingleProcess];
+}
+
+// any unique ID (com.tencent.xin.pay, etc)
++ (instancetype)mmkvWithID:(NSString *)mmapID {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:nil mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode {
+ auto rootPath = (mode == MMKVSingleProcess) ? nil : g_groupPath;
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:rootPath mode:mode];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:nil mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode {
+ auto rootPath = (mode == MMKVSingleProcess) ? nil : g_groupPath;
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:rootPath mode:mode];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:rootPath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID relativePath:(nullable NSString *)relativePath {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:relativePath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:rootPath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey relativePath:(nullable NSString *)relativePath {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:relativePath mode:MMKVSingleProcess];
+}
+
+// relatePath and MMKVMultiProcess mode can't be set at the same time, so we hide this method from public
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode {
+ if (!g_hasCalledInitializeMMKV) {
+ MMKVError("MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods");
+ }
+ if (mmapID.length <= 0) {
+ return nil;
+ }
+
+ SCOPED_LOCK(g_lock);
+
+ if (mode == MMKVMultiProcess) {
+ if (!rootPath) {
+ rootPath = g_groupPath;
+ }
+ if (!rootPath) {
+ MMKVError("Getting a multi-process MMKV [%@] without setting groupDir makes no sense", mmapID);
+ MMKV_ASSERT(0);
+ }
+ }
+ NSString *kvKey = [MMKV mmapKeyWithMMapID:mmapID rootPath:rootPath];
+ MMKV *kv = [g_instanceDic objectForKey:kvKey];
+ if (kv == nil) {
+ kv = [[MMKV alloc] initWithMMapID:mmapID cryptKey:cryptKey rootPath:rootPath mode:mode];
+ if (!kv->m_mmkv) {
+ return nil;
+ }
+ kv->m_mmapKey = kvKey;
+ [g_instanceDic setObject:kv forKey:kvKey];
+ }
+ return kv;
+}
+
+- (instancetype)initWithMMapID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(NSString *)rootPath mode:(MMKVMode)mode {
+ if (self = [super init]) {
+ string pathTmp;
+ if (rootPath.length > 0) {
+ pathTmp = rootPath.UTF8String;
+ }
+ string cryptKeyTmp;
+ if (cryptKey.length > 0) {
+ cryptKeyTmp = string((char *) cryptKey.bytes, cryptKey.length);
+ }
+ string *rootPathPtr = pathTmp.empty() ? nullptr : &pathTmp;
+ string *cryptKeyPtr = cryptKeyTmp.empty() ? nullptr : &cryptKeyTmp;
+ m_mmkv = mmkv::MMKV::mmkvWithID(mmapID.UTF8String, (mmkv::MMKVMode) mode, cryptKeyPtr, rootPathPtr);
+ if (!m_mmkv) {
+ return self;
+ }
+ m_mmapID = [NSString stringWithUTF8String:m_mmkv->mmapID().c_str()];
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+ if (!g_isRunningInAppExtension) {
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onMemoryWarning)
+ name:UIApplicationDidReceiveMemoryWarningNotification
+ object:nil];
+ }
+#endif
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self clearMemoryCache];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+#pragma mark - Application state
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+- (void)onMemoryWarning {
+ MMKVInfo("cleaning on memory warning %@", m_mmapID);
+
+ [self clearMemoryCache];
+}
+
++ (void)didEnterBackground {
+ mmkv::MMKV::setIsInBackground(true);
+ MMKVInfo("isInBackground:%d", true);
+}
+
++ (void)didBecomeActive {
+ mmkv::MMKV::setIsInBackground(false);
+ MMKVInfo("isInBackground:%d", false);
+}
+#endif
+
+- (void)clearAll {
+ m_mmkv->clearAll();
+}
+
+- (void)clearMemoryCache {
+ if (m_mmkv) {
+ m_mmkv->clearMemoryCache();
+ }
+}
+
+- (void)close {
+ SCOPED_LOCK(g_lock);
+ MMKVInfo("closing %@", m_mmapID);
+
+ m_mmkv->close();
+ m_mmkv = nullptr;
+
+ [g_instanceDic removeObjectForKey:m_mmapKey];
+}
+
+- (void)trim {
+ m_mmkv->trim();
+}
+
+#pragma mark - encryption & decryption
+
+#ifndef MMKV_DISABLE_CRYPT
+
+- (nullable NSData *)cryptKey {
+ auto str = m_mmkv->cryptKey();
+ if (str.length() > 0) {
+ return [NSData dataWithBytes:str.data() length:str.length()];
+ }
+ return nil;
+}
+
+- (BOOL)reKey:(nullable NSData *)newKey {
+ string key;
+ if (newKey.length > 0) {
+ key = string((char *) newKey.bytes, newKey.length);
+ }
+ return m_mmkv->reKey(key);
+}
+
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {
+ if (cryptKey.length > 0) {
+ string key = string((char *) cryptKey.bytes, cryptKey.length);
+ m_mmkv->checkReSetCryptKey(&key);
+ } else {
+ m_mmkv->checkReSetCryptKey(nullptr);
+ }
+}
+
+#else
+
+- (nullable NSData *)cryptKey {
+ return nil;
+}
+
+- (BOOL)reKey:(nullable NSData *)newKey {
+ return NO;
+}
+
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+#pragma mark - set & get
+
+- (BOOL)setObject:(nullable NSObject *)object forKey:(NSString *)key {
+ return m_mmkv->set(object, key);
+}
+
+- (BOOL)setBool:(BOOL)value forKey:(NSString *)key {
+ return m_mmkv->set((bool) value, key);
+}
+
+- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setFloat:(float)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setDouble:(double)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setString:(NSString *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (BOOL)setData:(NSData *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (id)getObjectOfClass:(Class)cls forKey:(NSString *)key {
+ return m_mmkv->getObject(key, cls);
+}
+
+- (BOOL)getBoolForKey:(NSString *)key {
+ return [self getBoolForKey:key defaultValue:FALSE];
+}
+- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue {
+ return m_mmkv->getBool(key, defaultValue);
+}
+
+- (int32_t)getInt32ForKey:(NSString *)key {
+ return [self getInt32ForKey:key defaultValue:0];
+}
+- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue {
+ return m_mmkv->getInt32(key, defaultValue);
+}
+
+- (uint32_t)getUInt32ForKey:(NSString *)key {
+ return [self getUInt32ForKey:key defaultValue:0];
+}
+- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue {
+ return m_mmkv->getUInt32(key, defaultValue);
+}
+
+- (int64_t)getInt64ForKey:(NSString *)key {
+ return [self getInt64ForKey:key defaultValue:0];
+}
+- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue {
+ return m_mmkv->getInt64(key, defaultValue);
+}
+
+- (uint64_t)getUInt64ForKey:(NSString *)key {
+ return [self getUInt64ForKey:key defaultValue:0];
+}
+- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue {
+ return m_mmkv->getUInt64(key, defaultValue);
+}
+
+- (float)getFloatForKey:(NSString *)key {
+ return [self getFloatForKey:key defaultValue:0];
+}
+- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue {
+ return m_mmkv->getFloat(key, defaultValue);
+}
+
+- (double)getDoubleForKey:(NSString *)key {
+ return [self getDoubleForKey:key defaultValue:0];
+}
+- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue {
+ return m_mmkv->getDouble(key, defaultValue);
+}
+
+- (nullable NSString *)getStringForKey:(NSString *)key {
+ return [self getStringForKey:key defaultValue:nil];
+}
+- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSString *valueString = [self getObjectOfClass:NSString.class forKey:key];
+ if (!valueString) {
+ valueString = defaultValue;
+ }
+ return valueString;
+}
+
+- (nullable NSDate *)getDateForKey:(NSString *)key {
+ return [self getDateForKey:key defaultValue:nil];
+}
+- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSDate *valueDate = [self getObjectOfClass:NSDate.class forKey:key];
+ if (!valueDate) {
+ valueDate = defaultValue;
+ }
+ return valueDate;
+}
+
+- (nullable NSData *)getDataForKey:(NSString *)key {
+ return [self getDataForKey:key defaultValue:nil];
+}
+- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSData *valueData = [self getObjectOfClass:NSData.class forKey:key];
+ if (!valueData) {
+ valueData = defaultValue;
+ }
+ return valueData;
+}
+
+- (size_t)getValueSizeForKey:(NSString *)key {
+ return m_mmkv->getValueSize(key, false);
+}
+
+- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer {
+ return m_mmkv->writeValueToBuffer(key, buffer.mutableBytes, static_cast(buffer.length));
+}
+
+#pragma mark - enumerate
+
+- (BOOL)containsKey:(NSString *)key {
+ return m_mmkv->containsKey(key);
+}
+
+- (size_t)count {
+ return m_mmkv->count();
+}
+
+- (size_t)totalSize {
+ return m_mmkv->totalSize();
+}
+
+- (size_t)actualSize {
+ return m_mmkv->actualSize();
+}
+
+- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block {
+ m_mmkv->enumerateKeys(block);
+}
+
+- (NSArray *)allKeys {
+ return m_mmkv->allKeys();
+}
+
+- (void)removeValueForKey:(NSString *)key {
+ m_mmkv->removeValueForKey(key);
+}
+
+- (void)removeValuesForKeys:(NSArray *)arrKeys {
+ m_mmkv->removeValuesForKeys(arrKeys);
+}
+
+#pragma mark - Boring stuff
+
+- (void)sync {
+ m_mmkv->sync(mmkv::MMKV_SYNC);
+}
+
+- (void)async {
+ m_mmkv->sync(mmkv::MMKV_ASYNC);
+}
+
+- (void)checkContentChanged {
+ m_mmkv->checkContentChanged();
+}
+
++ (void)onAppTerminate {
+ SCOPED_LOCK(g_lock);
+
+ [g_instanceDic removeAllObjects];
+
+ mmkv::MMKV::onExit();
+}
+
++ (NSString *)mmkvBasePath {
+ if (g_basePath.length > 0) {
+ return g_basePath;
+ }
+
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentPath = (NSString *) [paths firstObject];
+ if ([documentPath length] > 0) {
+ g_basePath = [documentPath stringByAppendingPathComponent:@"mmkv"];
+ return g_basePath;
+ } else {
+ return @"";
+ }
+}
+
++ (void)setMMKVBasePath:(NSString *)basePath {
+ if (basePath.length > 0) {
+ g_basePath = basePath;
+ [MMKV initializeMMKV:basePath];
+
+ // still warn about it
+ g_hasCalledInitializeMMKV = NO;
+
+ MMKVInfo("set MMKV base path to: %@", g_basePath);
+ }
+}
+
+static NSString *md5(NSString *value) {
+ uint8_t md[MD5_DIGEST_LENGTH] = {};
+ char tmp[3] = {}, buf[33] = {};
+ auto data = [value dataUsingEncoding:NSUTF8StringEncoding];
+ openssl::MD5((uint8_t *) data.bytes, data.length, md);
+ for (auto ch : md) {
+ snprintf(tmp, sizeof(tmp), "%2.2x", ch);
+ strcat(buf, tmp);
+ }
+ return [NSString stringWithCString:buf encoding:NSASCIIStringEncoding];
+}
+
++ (NSString *)mmapKeyWithMMapID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {
+ NSString *string = nil;
+ if ([rootPath length] > 0 && [rootPath isEqualToString:[MMKV mmkvBasePath]] == NO) {
+ string = md5([rootPath stringByAppendingPathComponent:mmapID]);
+ } else {
+ string = mmapID;
+ }
+ MMKVDebug("mmapKey: %@", string);
+ return string;
+}
+
++ (BOOL)isFileValid:(NSString *)mmapID {
+ return [self isFileValid:mmapID rootPath:nil];
+}
+
++ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path {
+ if (mmapID.length > 0) {
+ if (path.length > 0) {
+ string rootPath(path.UTF8String);
+ return mmkv::MMKV::isFileValid(mmapID.UTF8String, &rootPath);
+ } else {
+ return mmkv::MMKV::isFileValid(mmapID.UTF8String, nullptr);
+ }
+ }
+ return NO;
+}
+
++ (void)registerHandler:(id)handler {
+ SCOPED_LOCK(g_lock);
+ g_callbackHandler = handler;
+
+ if ([g_callbackHandler respondsToSelector:@selector(mmkvLogWithLevel:file:line:func:message:)]) {
+ g_isLogRedirecting = true;
+ mmkv::MMKV::registerLogHandler(LogHandler);
+ }
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVCRCCheckFail:)] ||
+ [g_callbackHandler respondsToSelector:@selector(onMMKVFileLengthError:)]) {
+ mmkv::MMKV::registerErrorHandler(ErrorHandler);
+ }
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentChange:)]) {
+ mmkv::MMKV::registerContentChangeHandler(ContentChangeHandler);
+ }
+}
+
++ (void)unregiserHandler {
+ SCOPED_LOCK(g_lock);
+
+ g_isLogRedirecting = false;
+ g_callbackHandler = nil;
+
+ mmkv::MMKV::unRegisterLogHandler();
+ mmkv::MMKV::unRegisterErrorHandler();
+ mmkv::MMKV::unRegisterContentChangeHandler();
+}
+
++ (void)setLogLevel:(MMKVLogLevel)logLevel {
+ mmkv::MMKV::setLogLevel((mmkv::MMKVLogLevel) logLevel);
+}
+
+- (uint32_t)migrateFromUserDefaults:(NSUserDefaults *)userDaults {
+ NSDictionary *dic = [userDaults dictionaryRepresentation];
+ if (dic.count <= 0) {
+ MMKVInfo("migrate data fail, userDaults is nil or empty");
+ return 0;
+ }
+ __block uint32_t count = 0;
+ [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
+ if ([key isKindOfClass:[NSString class]]) {
+ NSString *stringKey = key;
+ if ([MMKV tranlateData:obj key:stringKey kv:self]) {
+ count++;
+ }
+ } else {
+ MMKVWarning("unknown type of key:%@", key);
+ }
+ }];
+ return count;
+}
+
++ (BOOL)tranlateData:(id)obj key:(NSString *)key kv:(MMKV *)kv {
+ if ([obj isKindOfClass:[NSString class]]) {
+ return [kv setString:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSData class]]) {
+ return [kv setData:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSDate class]]) {
+ return [kv setDate:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSNumber class]]) {
+ NSNumber *num = obj;
+ CFNumberType numberType = CFNumberGetType((CFNumberRef) obj);
+ switch (numberType) {
+ case kCFNumberCharType:
+ case kCFNumberSInt8Type:
+ case kCFNumberSInt16Type:
+ case kCFNumberSInt32Type:
+ case kCFNumberIntType:
+ case kCFNumberShortType:
+ return [kv setInt32:num.intValue forKey:key];
+ case kCFNumberSInt64Type:
+ case kCFNumberLongType:
+ case kCFNumberNSIntegerType:
+ case kCFNumberLongLongType:
+ return [kv setInt64:num.longLongValue forKey:key];
+ case kCFNumberFloat32Type:
+ return [kv setFloat:num.floatValue forKey:key];
+ case kCFNumberFloat64Type:
+ case kCFNumberDoubleType:
+ return [kv setDouble:num.doubleValue forKey:key];
+ default:
+ MMKVWarning("unknown number type:%ld, key:%@", (long) numberType, key);
+ return NO;
+ }
+ } else if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
+ return [kv setObject:obj forKey:key];
+ } else {
+ MMKVWarning("unknown type of key:%@", key);
+ }
+ return NO;
+}
+
+@end
+
+#pragma makr - callbacks
+
+static void LogHandler(mmkv::MMKVLogLevel level, const char *file, int line, const char *function, NSString *message) {
+ [g_callbackHandler mmkvLogWithLevel:(MMKVLogLevel) level file:file line:line func:function message:message];
+}
+
+static mmkv::MMKVRecoverStrategic ErrorHandler(const string &mmapID, mmkv::MMKVErrorType errorType) {
+ if (errorType == mmkv::MMKVCRCCheckFail) {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVCRCCheckFail:)]) {
+ auto ret = [g_callbackHandler onMMKVCRCCheckFail:[NSString stringWithUTF8String:mmapID.c_str()]];
+ return (mmkv::MMKVRecoverStrategic) ret;
+ }
+ } else {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVFileLengthError:)]) {
+ auto ret = [g_callbackHandler onMMKVFileLengthError:[NSString stringWithUTF8String:mmapID.c_str()]];
+ return (mmkv::MMKVRecoverStrategic) ret;
+ }
+ }
+ return mmkv::OnErrorDiscard;
+}
+
+static void ContentChangeHandler(const string &mmapID) {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentChange:)]) {
+ [g_callbackHandler onMMKVContentChange:[NSString stringWithUTF8String:mmapID.c_str()]];
+ }
+}
diff --git a/ios/Pods/MMKVCore/Core/CodedInputData.cpp b/ios/Pods/MMKVCore/Core/CodedInputData.cpp
new file mode 100644
index 00000000..9f8c161e
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputData.cpp
@@ -0,0 +1,228 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CodedInputData.h"
+#include "PBUtility.h"
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+
+namespace mmkv {
+
+CodedInputData::CodedInputData(const void *oData, size_t length)
+ : m_ptr((uint8_t *) oData), m_size(length), m_position(0) {
+ MMKV_ASSERT(m_ptr);
+}
+
+void CodedInputData::seek(size_t addedSize) {
+ if (m_position + addedSize > m_size) {
+ throw out_of_range("OutOfSpace");
+ }
+ m_position += addedSize;
+}
+
+double CodedInputData::readDouble() {
+ return Int64ToFloat64(this->readRawLittleEndian64());
+}
+
+float CodedInputData::readFloat() {
+ return Int32ToFloat32(this->readRawLittleEndian32());
+}
+
+int64_t CodedInputData::readInt64() {
+ int32_t shift = 0;
+ int64_t result = 0;
+ while (shift < 64) {
+ int8_t b = this->readRawByte();
+ result |= (int64_t)(b & 0x7f) << shift;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ shift += 7;
+ }
+ throw invalid_argument("InvalidProtocolBuffer malformedInt64");
+}
+
+uint64_t CodedInputData::readUInt64() {
+ return static_cast(readInt64());
+}
+
+int32_t CodedInputData::readInt32() {
+ return this->readRawVarint32();
+}
+
+uint32_t CodedInputData::readUInt32() {
+ return static_cast(readRawVarint32());
+}
+
+bool CodedInputData::readBool() {
+ return this->readRawVarint32() != 0;
+}
+
+#ifndef MMKV_APPLE
+
+string CodedInputData::readString() {
+ int32_t size = readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ string result((char *) (m_ptr + m_position), s_size);
+ m_position += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+string CodedInputData::readString(KeyValueHolder &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ kvHolder.keySize = static_cast(s_size);
+
+ auto ptr = m_ptr + m_position;
+ string result((char *) (m_ptr + m_position), s_size);
+ m_position += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+#endif
+
+MMBuffer CodedInputData::readData() {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ MMBuffer data(((int8_t *) m_ptr) + m_position, s_size);
+ m_position += s_size;
+ return data;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+void CodedInputData::readData(KeyValueHolder &kvHolder) {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ kvHolder.computedKVSize = static_cast(m_position - kvHolder.offset);
+ kvHolder.valueSize = static_cast(s_size);
+
+ m_position += s_size;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+int32_t CodedInputData::readRawVarint32() {
+ int8_t tmp = this->readRawByte();
+ if (tmp >= 0) {
+ return tmp;
+ }
+ int32_t result = tmp & 0x7f;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 7;
+ } else {
+ result |= (tmp & 0x7f) << 7;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 14;
+ } else {
+ result |= (tmp & 0x7f) << 14;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 21;
+ } else {
+ result |= (tmp & 0x7f) << 21;
+ result |= (tmp = this->readRawByte()) << 28;
+ if (tmp < 0) {
+ // discard upper 32 bits
+ for (int i = 0; i < 5; i++) {
+ if (this->readRawByte() >= 0) {
+ return result;
+ }
+ }
+ throw invalid_argument("InvalidProtocolBuffer malformed varint32");
+ }
+ }
+ }
+ }
+ return result;
+}
+
+int32_t CodedInputData::readRawLittleEndian32() {
+ int8_t b1 = this->readRawByte();
+ int8_t b2 = this->readRawByte();
+ int8_t b3 = this->readRawByte();
+ int8_t b4 = this->readRawByte();
+ return (((int32_t) b1 & 0xff)) | (((int32_t) b2 & 0xff) << 8) | (((int32_t) b3 & 0xff) << 16) |
+ (((int32_t) b4 & 0xff) << 24);
+}
+
+int64_t CodedInputData::readRawLittleEndian64() {
+ int8_t b1 = this->readRawByte();
+ int8_t b2 = this->readRawByte();
+ int8_t b3 = this->readRawByte();
+ int8_t b4 = this->readRawByte();
+ int8_t b5 = this->readRawByte();
+ int8_t b6 = this->readRawByte();
+ int8_t b7 = this->readRawByte();
+ int8_t b8 = this->readRawByte();
+ return (((int64_t) b1 & 0xff)) | (((int64_t) b2 & 0xff) << 8) | (((int64_t) b3 & 0xff) << 16) |
+ (((int64_t) b4 & 0xff) << 24) | (((int64_t) b5 & 0xff) << 32) | (((int64_t) b6 & 0xff) << 40) |
+ (((int64_t) b7 & 0xff) << 48) | (((int64_t) b8 & 0xff) << 56);
+}
+
+int8_t CodedInputData::readRawByte() {
+ if (m_position == m_size) {
+ auto msg = "reach end, m_position: " + to_string(m_position) + ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ auto *bytes = (int8_t *) m_ptr;
+ return bytes[m_position++];
+}
+
+#ifdef MMKV_APPLE
+#endif // MMKV_APPLE
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/CodedInputData.h b/ios/Pods/MMKVCore/Core/CodedInputData.h
new file mode 100644
index 00000000..b0987567
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputData.h
@@ -0,0 +1,83 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_CODEDINPUTDATA_H
+#define MMKV_CODEDINPUTDATA_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include
+
+namespace mmkv {
+
+class CodedInputData {
+ uint8_t *const m_ptr;
+ size_t m_size;
+ size_t m_position;
+
+ int8_t readRawByte();
+
+ int32_t readRawVarint32();
+
+ int32_t readRawLittleEndian32();
+
+ int64_t readRawLittleEndian64();
+
+public:
+ CodedInputData(const void *oData, size_t length);
+
+ bool isAtEnd() const { return m_position == m_size; };
+
+ void seek(size_t addedSize);
+
+ bool readBool();
+
+ double readDouble();
+
+ float readFloat();
+
+ int64_t readInt64();
+
+ uint64_t readUInt64();
+
+ int32_t readInt32();
+
+ uint32_t readUInt32();
+
+ MMBuffer readData();
+ void readData(KeyValueHolder &kvHolder);
+
+#ifndef MMKV_APPLE
+ std::string readString();
+ std::string readString(KeyValueHolder &kvHolder);
+#else
+ NSString *readString();
+ NSString *readString(KeyValueHolder &kvHolder);
+ NSData *readNSData();
+#endif
+};
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_CODEDINPUTDATA_H
diff --git a/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.cpp b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.cpp
new file mode 100644
index 00000000..71a83e07
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.cpp
@@ -0,0 +1,278 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "CodedInputDataCrypt.h"
+#include "PBUtility.h"
+#include
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+#ifndef MMKV_DISABLE_CRYPT
+
+using namespace std;
+
+namespace mmkv {
+
+CodedInputDataCrypt::CodedInputDataCrypt(const void *oData, size_t length, AESCrypt &crypt)
+ : m_ptr((uint8_t *) oData), m_size(length), m_position(0), m_decryptPosition(0), m_decrypter(crypt) {
+ m_decryptBufferSize = AES_KEY_LEN * 2;
+ m_decryptBufferPosition = static_cast(crypt.m_number);
+ m_decryptBufferDiscardPosition = m_decryptBufferPosition;
+ m_decryptBufferDecryptLength = m_decryptBufferPosition;
+
+ m_decryptBuffer = (uint8_t *) malloc(m_decryptBufferSize);
+ if (!m_decryptBuffer) {
+ throw runtime_error(strerror(errno));
+ }
+}
+
+CodedInputDataCrypt::~CodedInputDataCrypt() {
+ if (m_decryptBuffer) {
+ free(m_decryptBuffer);
+ }
+}
+
+void CodedInputDataCrypt::seek(size_t addedSize) {
+ m_position += addedSize;
+ m_decryptPosition += addedSize;
+
+ if (m_position > m_size) {
+ throw out_of_range("OutOfSpace");
+ }
+ assert(m_position % AES_KEY_LEN == m_decrypter.m_number);
+}
+
+void CodedInputDataCrypt::consumeBytes(size_t length, bool discardPreData) {
+ if (discardPreData) {
+ m_decryptBufferDiscardPosition = m_decryptBufferPosition;
+ }
+ auto decryptedBytesLeft = m_decryptBufferDecryptLength - m_decryptBufferPosition;
+ if (decryptedBytesLeft >= length) {
+ return;
+ }
+ length -= decryptedBytesLeft;
+
+ // if there's some data left inside m_decrypter.m_vector, use them first
+ // it will be faster when always decrypt with (n * AES_KEY_LEN) bytes
+ if (m_decrypter.m_number != 0) {
+ auto alignDecrypter = AES_KEY_LEN - m_decrypter.m_number;
+ // make sure no data left inside m_decrypter.m_vector after decrypt
+ if (length < alignDecrypter) {
+ length = alignDecrypter;
+ } else {
+ length -= alignDecrypter;
+ length = ((length + AES_KEY_LEN - 1) / AES_KEY_LEN) * AES_KEY_LEN;
+ length += alignDecrypter;
+ }
+ } else {
+ length = ((length + AES_KEY_LEN - 1) / AES_KEY_LEN) * AES_KEY_LEN;
+ }
+ auto bytesLeftInSrc = m_size - m_decryptPosition;
+ length = min(bytesLeftInSrc, length);
+
+ auto bytesLeftInBuffer = m_decryptBufferSize - m_decryptBufferDecryptLength;
+ // try move some space
+ if (bytesLeftInBuffer < length && m_decryptBufferDiscardPosition > 0) {
+ auto posToMove = (m_decryptBufferDiscardPosition / AES_KEY_LEN) * AES_KEY_LEN;
+ if (posToMove) {
+ auto sizeToMove = m_decryptBufferDecryptLength - posToMove;
+ memmove(m_decryptBuffer, m_decryptBuffer + posToMove, sizeToMove);
+ m_decryptBufferPosition -= posToMove;
+ m_decryptBufferDecryptLength -= posToMove;
+ m_decryptBufferDiscardPosition = 0;
+ bytesLeftInBuffer = m_decryptBufferSize - m_decryptBufferDecryptLength;
+ }
+ }
+ // still no enough sapce, try realloc()
+ if (bytesLeftInBuffer < length) {
+ auto newSize = m_decryptBufferSize + length;
+ auto newBuffer = realloc(m_decryptBuffer, newSize);
+ if (!newBuffer) {
+ throw runtime_error(strerror(errno));
+ }
+ m_decryptBuffer = (uint8_t *) newBuffer;
+ m_decryptBufferSize = newSize;
+ }
+ m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer + m_decryptBufferDecryptLength, length);
+ m_decryptPosition += length;
+ m_decryptBufferDecryptLength += length;
+ assert(m_decryptPosition == m_size || m_decrypter.m_number == 0);
+}
+
+void CodedInputDataCrypt::skipBytes(size_t length) {
+ m_position += length;
+
+ auto decryptedBytesLeft = m_decryptBufferDecryptLength - m_decryptBufferPosition;
+ if (decryptedBytesLeft >= length) {
+ m_decryptBufferPosition += length;
+ return;
+ }
+ length -= decryptedBytesLeft;
+ // if this happens, we need optimization like the alignDecrypter above
+ assert(m_decrypter.m_number == 0);
+
+ size_t alignSize = ((length + AES_KEY_LEN - 1) / AES_KEY_LEN) * AES_KEY_LEN;
+ auto bytesLeftInSrc = m_size - m_decryptPosition;
+ auto size = min(alignSize, bytesLeftInSrc);
+ decryptedBytesLeft = size - length;
+ for (size_t index = 0, round = size / AES_KEY_LEN; index < round; index++) {
+ m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer, AES_KEY_LEN);
+ m_decryptPosition += AES_KEY_LEN;
+ size -= AES_KEY_LEN;
+ }
+ if (size) {
+ m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer, size);
+ m_decryptPosition += size;
+ m_decryptBufferPosition = size - decryptedBytesLeft;
+ m_decryptBufferDecryptLength = size;
+ } else {
+ m_decryptBufferPosition = AES_KEY_LEN - decryptedBytesLeft;
+ m_decryptBufferDecryptLength = AES_KEY_LEN;
+ }
+ assert(m_decryptBufferPosition <= m_decryptBufferDecryptLength);
+ assert(m_decryptPosition - m_decryptBufferDecryptLength + m_decryptBufferPosition == m_position);
+}
+
+inline void CodedInputDataCrypt::statusBeforeDecrypt(size_t rollbackSize, AESCryptStatus &status) {
+ rollbackSize += m_decryptBufferDecryptLength - m_decryptBufferPosition;
+ m_decrypter.statusBeforeDecrypt(m_ptr + m_decryptPosition, m_decryptBuffer + m_decryptBufferDecryptLength,
+ rollbackSize, status);
+}
+
+int8_t CodedInputDataCrypt::readRawByte() {
+ assert(m_position <= m_decryptPosition);
+ if (m_position == m_size) {
+ auto msg = "reach end, m_position: " + to_string(m_position) + ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ m_position++;
+
+ assert(m_decryptBufferPosition < m_decryptBufferSize);
+ auto *bytes = (int8_t *) m_decryptBuffer;
+ return bytes[m_decryptBufferPosition++];
+}
+
+int32_t CodedInputDataCrypt::readRawVarint32(bool discardPreData) {
+ consumeBytes(10, discardPreData);
+
+ int8_t tmp = this->readRawByte();
+ if (tmp >= 0) {
+ return tmp;
+ }
+ int32_t result = tmp & 0x7f;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 7;
+ } else {
+ result |= (tmp & 0x7f) << 7;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 14;
+ } else {
+ result |= (tmp & 0x7f) << 14;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 21;
+ } else {
+ result |= (tmp & 0x7f) << 21;
+ result |= (tmp = this->readRawByte()) << 28;
+ if (tmp < 0) {
+ // discard upper 32 bits
+ for (int i = 0; i < 5; i++) {
+ if (this->readRawByte() >= 0) {
+ return result;
+ }
+ }
+ throw invalid_argument("InvalidProtocolBuffer malformed varint32");
+ }
+ }
+ }
+ }
+ return result;
+}
+
+int32_t CodedInputDataCrypt::readInt32() {
+ return this->readRawVarint32();
+}
+
+# ifndef MMKV_APPLE
+
+string CodedInputDataCrypt::readString(KeyValueHolderCrypt &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32(true);
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ consumeBytes(s_size);
+
+ kvHolder.keySize = static_cast(s_size);
+
+ string result((char *) (m_decryptBuffer + m_decryptBufferPosition), s_size);
+ m_position += s_size;
+ m_decryptBufferPosition += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+# endif
+
+void CodedInputDataCrypt::readData(KeyValueHolderCrypt &kvHolder) {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(s_size)) {
+ kvHolder.type = KeyValueHolderType_Offset;
+ kvHolder.valueSize = static_cast(s_size);
+ kvHolder.pbKeyValueSize =
+ static_cast(pbRawVarint32Size(kvHolder.valueSize) + pbRawVarint32Size(kvHolder.keySize));
+
+ size_t rollbackSize = kvHolder.pbKeyValueSize + kvHolder.keySize;
+ statusBeforeDecrypt(rollbackSize, kvHolder.cryptStatus);
+
+ skipBytes(s_size);
+ } else {
+ consumeBytes(s_size);
+
+ kvHolder.type = KeyValueHolderType_Direct;
+ kvHolder = KeyValueHolderCrypt(m_decryptBuffer + m_decryptBufferPosition, s_size);
+ m_decryptBufferPosition += s_size;
+ m_position += s_size;
+ }
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_DISABLE_CRYPT
diff --git a/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.h b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.h
new file mode 100644
index 00000000..fac32e62
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.h
@@ -0,0 +1,87 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef CodedInputDataCrypt_h
+#define CodedInputDataCrypt_h
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include "aes/AESCrypt.h"
+#include
+
+#ifdef MMKV_DISABLE_CRYPT
+
+namespace mmkv {
+class CodedInputDataCrypt;
+}
+
+#else
+
+namespace mmkv {
+
+class CodedInputDataCrypt {
+ uint8_t *const m_ptr;
+ size_t m_size;
+ size_t m_position;
+ size_t m_decryptPosition; // position of text that has beed decrypted
+
+ AESCrypt &m_decrypter;
+ uint8_t *m_decryptBuffer; // internal decrypt buffer, grows by (n * AES_KEY_LEN) bytes
+ size_t m_decryptBufferSize;
+ size_t m_decryptBufferPosition; // reader position in the buffer, synced with m_position
+ size_t m_decryptBufferDecryptLength; // length of the buffer that has been used
+ size_t m_decryptBufferDiscardPosition; // recycle position, any data before that can be discarded
+
+ void consumeBytes(size_t length, bool discardPreData = false);
+ void skipBytes(size_t length);
+ void statusBeforeDecrypt(size_t rollbackSize, AESCryptStatus &status);
+
+ int8_t readRawByte();
+
+ int32_t readRawVarint32(bool discardPreData = false);
+
+public:
+ CodedInputDataCrypt(const void *oData, size_t length, AESCrypt &crypt);
+
+ ~CodedInputDataCrypt();
+
+ bool isAtEnd() { return m_position == m_size; };
+
+ void seek(size_t addedSize);
+
+ int32_t readInt32();
+
+ void readData(KeyValueHolderCrypt &kvHolder);
+
+#ifndef MMKV_APPLE
+ std::string readString(KeyValueHolderCrypt &kvHolder);
+#else
+ NSString *readString(KeyValueHolderCrypt &kvHolder);
+#endif
+};
+
+} // namespace mmkv
+
+#endif // MMKV_DISABLE_CRYPT
+#endif // __cplusplus
+#endif /* CodedInputDataCrypt_h */
diff --git a/ios/Pods/MMKVCore/Core/CodedInputDataCrypt_OSX.cpp b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt_OSX.cpp
new file mode 100644
index 00000000..5d19203a
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt_OSX.cpp
@@ -0,0 +1,62 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "CodedInputDataCrypt.h"
+
+#if defined(MMKV_APPLE) && !defined(MMKV_DISABLE_CRYPT)
+
+# include "PBUtility.h"
+# include
+
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+
+using namespace std;
+
+namespace mmkv {
+
+NSString *CodedInputDataCrypt::readString(KeyValueHolderCrypt &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32(true);
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ consumeBytes(s_size);
+
+ kvHolder.keySize = static_cast(s_size);
+
+ auto ptr = m_decryptBuffer + m_decryptBufferPosition;
+ NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];
+ m_position += s_size;
+ m_decryptBufferPosition += s_size;
+ return [result autorelease];
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_APPLE && !MMKV_DISABLE_CRYPT
diff --git a/ios/Pods/MMKVCore/Core/CodedInputData_OSX.cpp b/ios/Pods/MMKVCore/Core/CodedInputData_OSX.cpp
new file mode 100644
index 00000000..41bd58ae
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputData_OSX.cpp
@@ -0,0 +1,92 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CodedInputData.h"
+
+#ifdef MMKV_APPLE
+
+# include "PBUtility.h"
+# include
+
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+
+using namespace std;
+
+namespace mmkv {
+
+NSString *CodedInputData::readString() {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ auto ptr = m_ptr + m_position;
+ NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];
+ m_position += s_size;
+ return [result autorelease];
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+NSString *CodedInputData::readString(KeyValueHolder &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ kvHolder.keySize = static_cast(s_size);
+
+ auto ptr = m_ptr + m_position;
+ NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];
+ m_position += s_size;
+ return [result autorelease];
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+NSData *CodedInputData::readNSData() {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ NSData *result = [NSData dataWithBytes:(m_ptr + m_position) length:s_size];
+ m_position += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_APPLE
diff --git a/ios/Pods/MMKVCore/Core/CodedOutputData.cpp b/ios/Pods/MMKVCore/Core/CodedOutputData.cpp
new file mode 100644
index 00000000..96d37dee
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedOutputData.cpp
@@ -0,0 +1,174 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CodedOutputData.h"
+#include "PBUtility.h"
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+
+namespace mmkv {
+
+CodedOutputData::CodedOutputData(void *ptr, size_t len) : m_ptr((uint8_t *) ptr), m_size(len), m_position(0) {
+ MMKV_ASSERT(m_ptr);
+}
+
+uint8_t *CodedOutputData::curWritePointer() {
+ return m_ptr + m_position;
+}
+
+void CodedOutputData::writeDouble(double value) {
+ this->writeRawLittleEndian64(Float64ToInt64(value));
+}
+
+void CodedOutputData::writeFloat(float value) {
+ this->writeRawLittleEndian32(Float32ToInt32(value));
+}
+
+void CodedOutputData::writeInt64(int64_t value) {
+ this->writeRawVarint64(value);
+}
+
+void CodedOutputData::writeUInt64(uint64_t value) {
+ writeRawVarint64(static_cast(value));
+}
+
+void CodedOutputData::writeInt32(int32_t value) {
+ if (value >= 0) {
+ this->writeRawVarint32(value);
+ } else {
+ this->writeRawVarint64(value);
+ }
+}
+
+void CodedOutputData::writeUInt32(uint32_t value) {
+ writeRawVarint32(static_cast(value));
+}
+
+void CodedOutputData::writeBool(bool value) {
+ this->writeRawByte(static_cast(value ? 1 : 0));
+}
+
+void CodedOutputData::writeData(const MMBuffer &value) {
+ this->writeRawVarint32((int32_t) value.length());
+ this->writeRawData(value);
+}
+
+#ifndef MMKV_APPLE
+
+void CodedOutputData::writeString(const string &value) {
+ size_t numberOfBytes = value.size();
+ this->writeRawVarint32((int32_t) numberOfBytes);
+ if (m_position + numberOfBytes > m_size) {
+ auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " + to_string(numberOfBytes) +
+ ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ memcpy(m_ptr + m_position, ((uint8_t *) value.data()), numberOfBytes);
+ m_position += numberOfBytes;
+}
+
+#endif // MMKV_APPLE
+
+size_t CodedOutputData::spaceLeft() {
+ if (m_size <= m_position) {
+ return 0;
+ }
+ return m_size - m_position;
+}
+
+void CodedOutputData::seek(size_t addedSize) {
+ m_position += addedSize;
+
+ if (m_position > m_size) {
+ throw out_of_range("OutOfSpace");
+ }
+}
+
+void CodedOutputData::writeRawByte(uint8_t value) {
+ if (m_position == m_size) {
+ throw out_of_range("m_position: " + to_string(m_position) + " m_size: " + to_string(m_size));
+ return;
+ }
+
+ m_ptr[m_position++] = value;
+}
+
+void CodedOutputData::writeRawData(const MMBuffer &data) {
+ size_t numberOfBytes = data.length();
+ if (m_position + numberOfBytes > m_size) {
+ auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " + to_string(numberOfBytes) +
+ ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ memcpy(m_ptr + m_position, data.getPtr(), numberOfBytes);
+ m_position += numberOfBytes;
+}
+
+void CodedOutputData::writeRawVarint32(int32_t value) {
+ while (true) {
+ if ((value & ~0x7f) == 0) {
+ this->writeRawByte(static_cast(value));
+ return;
+ } else {
+ this->writeRawByte(static_cast((value & 0x7F) | 0x80));
+ value = logicalRightShift32(value, 7);
+ }
+ }
+}
+
+void CodedOutputData::writeRawVarint64(int64_t value) {
+ while (true) {
+ if ((value & ~0x7f) == 0) {
+ this->writeRawByte(static_cast(value));
+ return;
+ } else {
+ this->writeRawByte(static_cast((value & 0x7f) | 0x80));
+ value = logicalRightShift64(value, 7);
+ }
+ }
+}
+
+void CodedOutputData::writeRawLittleEndian32(int32_t value) {
+ this->writeRawByte(static_cast((value) &0xff));
+ this->writeRawByte(static_cast((value >> 8) & 0xff));
+ this->writeRawByte(static_cast((value >> 16) & 0xff));
+ this->writeRawByte(static_cast((value >> 24) & 0xff));
+}
+
+void CodedOutputData::writeRawLittleEndian64(int64_t value) {
+ this->writeRawByte(static_cast((value) &0xff));
+ this->writeRawByte(static_cast((value >> 8) & 0xff));
+ this->writeRawByte(static_cast((value >> 16) & 0xff));
+ this->writeRawByte(static_cast((value >> 24) & 0xff));
+ this->writeRawByte(static_cast((value >> 32) & 0xff));
+ this->writeRawByte(static_cast((value >> 40) & 0xff));
+ this->writeRawByte(static_cast((value >> 48) & 0xff));
+ this->writeRawByte(static_cast((value >> 56) & 0xff));
+}
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/CodedOutputData.h b/ios/Pods/MMKVCore/Core/CodedOutputData.h
new file mode 100644
index 00000000..be452bfe
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedOutputData.h
@@ -0,0 +1,82 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_CODEDOUTPUTDATA_H
+#define MMKV_CODEDOUTPUTDATA_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "MMBuffer.h"
+#include
+
+namespace mmkv {
+
+class CodedOutputData {
+ uint8_t *const m_ptr;
+ size_t m_size;
+ size_t m_position;
+
+public:
+ CodedOutputData(void *ptr, size_t len);
+
+ size_t spaceLeft();
+
+ uint8_t *curWritePointer();
+
+ void seek(size_t addedSize);
+
+ void writeRawByte(uint8_t value);
+
+ void writeRawLittleEndian32(int32_t value);
+
+ void writeRawLittleEndian64(int64_t value);
+
+ void writeRawVarint32(int32_t value);
+
+ void writeRawVarint64(int64_t value);
+
+ void writeRawData(const MMBuffer &data);
+
+ void writeDouble(double value);
+
+ void writeFloat(float value);
+
+ void writeInt64(int64_t value);
+
+ void writeUInt64(uint64_t value);
+
+ void writeInt32(int32_t value);
+
+ void writeUInt32(uint32_t value);
+
+ void writeBool(bool value);
+
+ void writeData(const MMBuffer &value);
+
+#ifndef MMKV_APPLE
+ void writeString(const std::string &value);
+#endif
+};
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_CODEDOUTPUTDATA_H
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock.cpp b/ios/Pods/MMKVCore/Core/InterProcessLock.cpp
new file mode 100644
index 00000000..7db7228f
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock.cpp
@@ -0,0 +1,181 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InterProcessLock.h"
+#include "MMKVLog.h"
+
+#ifndef MMKV_WIN32
+# include
+#endif
+
+namespace mmkv {
+
+bool FileLock::lock(LockType lockType) {
+ return doLock(lockType, true);
+}
+
+bool FileLock::try_lock(LockType lockType) {
+ return doLock(lockType, false);
+}
+
+bool FileLock::doLock(LockType lockType, bool wait) {
+ if (!isFileLockValid()) {
+ return false;
+ }
+ bool unLockFirstIfNeeded = false;
+
+ if (lockType == SharedLockType) {
+ // don't want shared-lock to break any existing locks
+ if (m_sharedLockCount > 0 || m_exclusiveLockCount > 0) {
+ m_sharedLockCount++;
+ return true;
+ }
+ } else {
+ // don't want exclusive-lock to break existing exclusive-locks
+ if (m_exclusiveLockCount > 0) {
+ m_exclusiveLockCount++;
+ return true;
+ }
+ // prevent deadlock
+ if (m_sharedLockCount > 0) {
+ unLockFirstIfNeeded = true;
+ }
+ }
+
+ auto ret = platformLock(lockType, wait, unLockFirstIfNeeded);
+ if (ret) {
+ if (lockType == SharedLockType) {
+ m_sharedLockCount++;
+ } else {
+ m_exclusiveLockCount++;
+ }
+ }
+ return ret;
+}
+
+#ifndef MMKV_WIN32
+
+static int32_t LockType2FlockType(LockType lockType) {
+ switch (lockType) {
+ case SharedLockType:
+ return LOCK_SH;
+ case ExclusiveLockType:
+ return LOCK_EX;
+ }
+ return LOCK_EX;
+}
+
+bool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded) {
+# ifdef MMKV_ANDROID
+ if (m_isAshmem) {
+ return ashmemLock(lockType, wait, unLockFirstIfNeeded);
+ }
+# endif
+ auto realLockType = LockType2FlockType(lockType);
+ auto cmd = wait ? realLockType : (realLockType | LOCK_NB);
+ if (unLockFirstIfNeeded) {
+ // try lock
+ auto ret = flock(m_fd, realLockType | LOCK_NB);
+ if (ret == 0) {
+ return true;
+ }
+ // let's be gentleman: unlock my shared-lock to prevent deadlock
+ ret = flock(m_fd, LOCK_UN);
+ if (ret != 0) {
+ MMKVError("fail to try unlock first fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ }
+
+ auto ret = flock(m_fd, cmd);
+ if (ret != 0) {
+ MMKVError("fail to lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ // try recover my shared-lock
+ if (unLockFirstIfNeeded) {
+ ret = flock(m_fd, LockType2FlockType(SharedLockType));
+ if (ret != 0) {
+ // let's hope this never happen
+ MMKVError("fail to recover shared-lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool FileLock::platformUnLock(bool unlockToSharedLock) {
+# ifdef MMKV_ANDROID
+ if (m_isAshmem) {
+ return ashmemUnLock(unlockToSharedLock);
+ }
+# endif
+ int cmd = unlockToSharedLock ? LOCK_SH : LOCK_UN;
+ auto ret = flock(m_fd, cmd);
+ if (ret != 0) {
+ MMKVError("fail to unlock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ return false;
+ } else {
+ return true;
+ }
+}
+
+#endif // MMKV_WIN32
+
+bool FileLock::unlock(LockType lockType) {
+ if (!isFileLockValid()) {
+ return false;
+ }
+ bool unlockToSharedLock = false;
+
+ if (lockType == SharedLockType) {
+ if (m_sharedLockCount == 0) {
+ return false;
+ }
+ // don't want shared-lock to break any existing locks
+ if (m_sharedLockCount > 1 || m_exclusiveLockCount > 0) {
+ m_sharedLockCount--;
+ return true;
+ }
+ } else {
+ if (m_exclusiveLockCount == 0) {
+ return false;
+ }
+ if (m_exclusiveLockCount > 1) {
+ m_exclusiveLockCount--;
+ return true;
+ }
+ // restore shared-lock when all exclusive-locks are done
+ if (m_sharedLockCount > 0) {
+ unlockToSharedLock = true;
+ }
+ }
+
+ auto ret = platformUnLock(unlockToSharedLock);
+ if (ret) {
+ if (lockType == SharedLockType) {
+ m_sharedLockCount--;
+ } else {
+ m_exclusiveLockCount--;
+ }
+ }
+ return ret;
+}
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock.h b/ios/Pods/MMKVCore/Core/InterProcessLock.h
new file mode 100644
index 00000000..2da539b0
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock.h
@@ -0,0 +1,119 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_INTERPROCESSLOCK_H
+#define MMKV_INTERPROCESSLOCK_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include
+
+namespace mmkv {
+
+enum LockType {
+ SharedLockType,
+ ExclusiveLockType,
+};
+
+// a recursive POSIX file-lock wrapper
+// handles lock upgrade & downgrade correctly
+class FileLock {
+ MMKVFileHandle_t m_fd;
+ size_t m_sharedLockCount;
+ size_t m_exclusiveLockCount;
+
+ bool doLock(LockType lockType, bool wait);
+ bool platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded);
+ bool platformUnLock(bool unLockFirstIfNeeded);
+
+#ifndef MMKV_WIN32
+ bool isFileLockValid() { return m_fd >= 0; }
+# ifdef MMKV_ANDROID
+ const bool m_isAshmem;
+ struct flock m_lockInfo;
+ bool ashmemLock(LockType lockType, bool wait, bool unLockFirstIfNeeded);
+ bool ashmemUnLock(bool unLockFirstIfNeeded);
+# endif
+
+#else // defined(MMKV_WIN32)
+ OVERLAPPED m_overLapped;
+
+ bool isFileLockValid() { return m_fd != INVALID_HANDLE_VALUE; }
+#endif // MMKV_WIN32
+
+public:
+#ifndef MMKV_WIN32
+# ifndef MMKV_ANDROID
+ explicit FileLock(MMKVFileHandle_t fd) : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0) {}
+# else
+ explicit FileLock(MMKVFileHandle_t fd, bool isAshmem = false);
+# endif // MMKV_ANDROID
+#else // defined(MMKV_WIN32)
+ explicit FileLock(MMKVFileHandle_t fd) : m_fd(fd), m_overLapped{}, m_sharedLockCount(0), m_exclusiveLockCount(0) {}
+#endif // MMKV_WIN32
+
+ bool lock(LockType lockType);
+
+ bool try_lock(LockType lockType);
+
+ bool unlock(LockType lockType);
+
+ // just forbid it for possibly misuse
+ explicit FileLock(const FileLock &other) = delete;
+ FileLock &operator=(const FileLock &other) = delete;
+};
+
+class InterProcessLock {
+ FileLock *m_fileLock;
+ LockType m_lockType;
+
+public:
+ InterProcessLock(FileLock *fileLock, LockType lockType)
+ : m_fileLock(fileLock), m_lockType(lockType), m_enable(true) {
+ MMKV_ASSERT(m_fileLock);
+ }
+
+ bool m_enable;
+
+ void lock() {
+ if (m_enable) {
+ m_fileLock->lock(m_lockType);
+ }
+ }
+
+ bool try_lock() {
+ if (m_enable) {
+ return m_fileLock->try_lock(m_lockType);
+ }
+ return false;
+ }
+
+ void unlock() {
+ if (m_enable) {
+ m_fileLock->unlock(m_lockType);
+ }
+ }
+};
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_INTERPROCESSLOCK_H
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock_Android.cpp b/ios/Pods/MMKVCore/Core/InterProcessLock_Android.cpp
new file mode 100644
index 00000000..5a06103f
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock_Android.cpp
@@ -0,0 +1,98 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InterProcessLock.h"
+
+#ifdef MMKV_ANDROID
+# include "MMKVLog.h"
+# include
+# include
+
+namespace mmkv {
+
+FileLock::FileLock(MMKVFileHandle_t fd, bool isAshmem)
+ : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0), m_isAshmem(isAshmem) {
+ m_lockInfo.l_type = F_WRLCK;
+ m_lockInfo.l_start = 0;
+ m_lockInfo.l_whence = SEEK_SET;
+ m_lockInfo.l_len = 0;
+ m_lockInfo.l_pid = 0;
+}
+
+static short LockType2FlockType(LockType lockType) {
+ switch (lockType) {
+ case SharedLockType:
+ return F_RDLCK;
+ case ExclusiveLockType:
+ return F_WRLCK;
+ }
+}
+
+bool FileLock::ashmemLock(LockType lockType, bool wait, bool unLockFirstIfNeeded) {
+ m_lockInfo.l_type = LockType2FlockType(lockType);
+ if (unLockFirstIfNeeded) {
+ // try lock
+ auto ret = fcntl(m_fd, F_SETLK, &m_lockInfo);
+ if (ret == 0) {
+ return true;
+ }
+ // lets be gentleman: unlock my shared-lock to prevent deadlock
+ auto type = m_lockInfo.l_type;
+ m_lockInfo.l_type = F_UNLCK;
+ ret = fcntl(m_fd, F_SETLK, &m_lockInfo);
+ if (ret != 0) {
+ MMKVError("fail to try unlock first fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ m_lockInfo.l_type = type;
+ }
+
+ int cmd = wait ? F_SETLKW : F_SETLK;
+ auto ret = fcntl(m_fd, cmd, &m_lockInfo);
+ if (ret != 0) {
+ MMKVError("fail to lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ // try recover my shared-lock
+ if (unLockFirstIfNeeded) {
+ m_lockInfo.l_type = LockType2FlockType(SharedLockType);
+ ret = fcntl(m_fd, cmd, &m_lockInfo);
+ if (ret != 0) {
+ // let's hope this never happen
+ MMKVError("fail to recover shared-lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool FileLock::ashmemUnLock(bool unlockToSharedLock) {
+ m_lockInfo.l_type = static_cast(unlockToSharedLock ? F_RDLCK : F_UNLCK);
+ auto ret = fcntl(m_fd, F_SETLK, &m_lockInfo);
+ if (ret != 0) {
+ MMKVError("fail to unlock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ return false;
+ } else {
+ return true;
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_ANDROID
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock_Win32.cpp b/ios/Pods/MMKVCore/Core/InterProcessLock_Win32.cpp
new file mode 100644
index 00000000..fe2c619b
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock_Win32.cpp
@@ -0,0 +1,98 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InterProcessLock.h"
+
+#ifdef MMKV_WIN32
+# include "MMKVLog.h"
+
+namespace mmkv {
+
+static DWORD LockType2Flag(LockType lockType) {
+ DWORD flag = 0;
+ switch (lockType) {
+ case SharedLockType:
+ flag = 0;
+ break;
+ case ExclusiveLockType:
+ flag = LOCKFILE_EXCLUSIVE_LOCK;
+ break;
+ }
+ return flag;
+}
+
+bool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded) {
+ auto realLockType = LockType2Flag(lockType);
+ auto flag = wait ? realLockType : (realLockType | LOCKFILE_FAIL_IMMEDIATELY);
+ if (unLockFirstIfNeeded) {
+ /* try exclusive-lock above shared-lock will always fail in Win32
+ auto ret = LockFileEx(m_fd, realLockType | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &m_overLapped);
+ if (ret) {
+ return true;
+ }*/
+ // let's be gentleman: unlock my shared-lock to prevent deadlock
+ auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ MMKVError("fail to try unlock first fd=%p, error:%d", m_fd, GetLastError());
+ }
+ }
+
+ auto ret = LockFileEx(m_fd, flag, 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ MMKVError("fail to lock fd=%p, error:%d", m_fd, GetLastError());
+ // try recover my shared-lock
+ if (unLockFirstIfNeeded) {
+ ret = LockFileEx(m_fd, LockType2Flag(SharedLockType), 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ // let's hope this never happen
+ MMKVError("fail to recover shared-lock fd=%p, error:%d", m_fd, GetLastError());
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool FileLock::platformUnLock(bool unlockToSharedLock) {
+ /* quote from MSDN:
+ * If the same range is locked with an exclusive and a shared lock,
+ * two unlock operations are necessary to unlock the region;
+ * the first unlock operation unlocks the exclusive lock,
+ * the second unlock operation unlocks the shared lock.
+ */
+ if (unlockToSharedLock) {
+ auto flag = LockType2Flag(SharedLockType);
+ if (!LockFileEx(m_fd, flag, 0, 1, 0, &m_overLapped)) {
+ MMKVError("fail to roll back to shared-lock, error:%d", GetLastError());
+ }
+ }
+ auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ MMKVError("fail to unlock fd=%p, error:%d", m_fd, GetLastError());
+ return false;
+ } else {
+ return true;
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_WIN32
diff --git a/ios/Pods/MMKVCore/Core/KeyValueHolder.cpp b/ios/Pods/MMKVCore/Core/KeyValueHolder.cpp
new file mode 100644
index 00000000..9694348c
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/KeyValueHolder.cpp
@@ -0,0 +1,216 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2020 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyValueHolder.h"
+#include "PBUtility.h"
+#include "aes/AESCrypt.h"
+#include
+#include
+#include
+
+namespace mmkv {
+
+KeyValueHolder::KeyValueHolder(uint32_t keyLength, uint32_t valueLength, uint32_t off)
+ : keySize(static_cast(keyLength)), valueSize(valueLength), offset(off) {
+ computedKVSize = keySize + static_cast(pbRawVarint32Size(keySize));
+ computedKVSize += static_cast(pbRawVarint32Size(valueSize));
+}
+
+MMBuffer KeyValueHolder::toMMBuffer(const void *basePtr) const {
+ auto realPtr = (uint8_t *) basePtr + offset;
+ realPtr += computedKVSize;
+ return MMBuffer(realPtr, valueSize, MMBufferNoCopy);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(const void *src, size_t length) {
+ if (length <= SmallBufferSize()) {
+ type = KeyValueHolderType_Direct;
+ paddedSize = static_cast(length);
+ memcpy(paddedValue, src, length);
+ } else {
+ type = KeyValueHolderType_Memory;
+ memSize = static_cast(length);
+ memPtr = malloc(length);
+ if (!memPtr) {
+ throw std::runtime_error(strerror(errno));
+ }
+ memcpy(memPtr, src, memSize);
+ }
+}
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(MMBuffer &&data) {
+ if (data.type == MMBuffer::MMBufferType_Small) {
+ static_assert(SmallBufferSize() >= MMBuffer::SmallBufferSize(), "KeyValueHolderCrypt can't hold MMBuffer");
+
+ type = KeyValueHolderType_Direct;
+ paddedSize = static_cast(data.length());
+ memcpy(paddedValue, data.getPtr(), data.length());
+ } else {
+# ifdef MMKV_APPLE
+ assert(data.m_data == nil);
+# endif
+ type = KeyValueHolderType_Memory;
+ memSize = static_cast(data.length());
+ memPtr = data.getPtr();
+
+ data.detach();
+ }
+}
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(uint32_t keyLength, uint32_t valueLength, uint32_t off)
+ : type(KeyValueHolderType_Offset), keySize(static_cast(keyLength)), valueSize(valueLength), offset(off) {
+
+ pbKeyValueSize = static_cast(pbRawVarint32Size(keySize) + pbRawVarint32Size(valueSize));
+}
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(KeyValueHolderCrypt &&other) noexcept {
+ this->move(std::move(other));
+}
+
+KeyValueHolderCrypt &KeyValueHolderCrypt::operator=(KeyValueHolderCrypt &&other) noexcept {
+ if (type == KeyValueHolderType_Memory && memPtr) {
+ free(memPtr);
+ }
+ this->move(std::move(other));
+ return *this;
+}
+
+void KeyValueHolderCrypt::move(KeyValueHolderCrypt &&other) noexcept {
+ if (other.type == KeyValueHolderType_Direct || other.type == KeyValueHolderType_Offset) {
+ memcpy(this, &other, sizeof(other));
+ } else if (other.type == KeyValueHolderType_Memory) {
+ type = KeyValueHolderType_Memory;
+ memSize = other.memSize;
+ memPtr = other.memPtr;
+ other.memPtr = nullptr;
+ }
+}
+
+KeyValueHolderCrypt::~KeyValueHolderCrypt() {
+ if (type == KeyValueHolderType_Memory && memPtr) {
+ free(memPtr);
+ }
+}
+
+// get decrypt data with [position, -1)
+static MMBuffer decryptBuffer(AESCrypt &crypter, const MMBuffer &inputBuffer, size_t position) {
+ static uint8_t smallBuffer[16];
+ auto basePtr = (uint8_t *) inputBuffer.getPtr();
+ auto ptr = basePtr;
+ for (size_t index = sizeof(smallBuffer); index < position; index += sizeof(smallBuffer)) {
+ crypter.decrypt(ptr, smallBuffer, sizeof(smallBuffer));
+ ptr += sizeof(smallBuffer);
+ }
+ if (ptr < basePtr + position) {
+ crypter.decrypt(ptr, smallBuffer, static_cast(basePtr + position - ptr));
+ ptr = basePtr + position;
+ }
+ size_t length = inputBuffer.length() - position;
+ MMBuffer tmp(length);
+
+ auto input = ptr;
+ auto output = tmp.getPtr();
+ crypter.decrypt(input, output, length);
+
+ return tmp;
+}
+
+MMBuffer KeyValueHolderCrypt::toMMBuffer(const void *basePtr, const AESCrypt *crypter) const {
+ if (type == KeyValueHolderType_Direct) {
+ return MMBuffer((void *) paddedValue, paddedSize, MMBufferNoCopy);
+ } else if (type == KeyValueHolderType_Memory) {
+ return MMBuffer(memPtr, memSize, MMBufferNoCopy);
+ } else {
+ auto realPtr = (uint8_t *) basePtr + offset;
+ auto position = static_cast(pbKeyValueSize + keySize);
+ auto realSize = position + valueSize;
+ auto kvBuffer = MMBuffer(realPtr, realSize, MMBufferNoCopy);
+ auto decrypter = crypter->cloneWithStatus(cryptStatus);
+ return decryptBuffer(decrypter, kvBuffer, position);
+ }
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+} // namespace mmkv
+
+#if !defined(MMKV_DISABLE_CRYPT) && !defined(NDEBUG)
+# include "CodedInputData.h"
+# include "CodedOutputData.h"
+# include "MMKVLog.h"
+# include
+
+using namespace std;
+
+namespace mmkv {
+
+void KeyValueHolderCrypt::testAESToMMBuffer() {
+ const uint8_t plainText[] = "Hello, OpenSSL-mmkv::KeyValueHolderCrypt::testAESToMMBuffer() with AES CFB 128.";
+ constexpr size_t textLength = sizeof(plainText) - 1;
+
+ const uint8_t key[] = "TheAESKey";
+ constexpr size_t keyLength = sizeof(key) - 1;
+
+ uint8_t iv[AES_KEY_LEN];
+ srand((unsigned) time(nullptr));
+ for (uint32_t i = 0; i < AES_KEY_LEN; i++) {
+ iv[i] = (uint8_t) rand();
+ }
+ AESCrypt crypt1(key, keyLength, iv, sizeof(iv));
+
+ auto encryptText = new uint8_t[DEFAULT_MMAP_SIZE];
+ memset(encryptText, 0, DEFAULT_MMAP_SIZE);
+ CodedOutputData output(encryptText, DEFAULT_MMAP_SIZE);
+ output.writeData(MMBuffer((void *) key, keyLength, MMBufferNoCopy));
+ auto lengthOfValue = textLength + pbRawVarint32Size((uint32_t) textLength);
+ output.writeRawVarint32((int32_t) lengthOfValue);
+ output.writeData(MMBuffer((void *) plainText, textLength, MMBufferNoCopy));
+ crypt1.encrypt(encryptText, encryptText, (size_t)(output.curWritePointer() - encryptText));
+
+ AESCrypt decrypt(key, keyLength, iv, sizeof(iv));
+ uint8_t smallBuffer[32];
+ decrypt.decrypt(encryptText, smallBuffer, 5);
+ auto keySize = CodedInputData(smallBuffer, 5).readUInt32();
+ auto sizeOfKeySize = pbRawVarint32Size(keySize);
+ auto position = sizeOfKeySize;
+ decrypt.decrypt(encryptText + 5, smallBuffer + 5, static_cast(sizeOfKeySize + keySize - 5));
+ position += keySize;
+ decrypt.decrypt(encryptText + position, smallBuffer + position, 5);
+ auto valueSize = CodedInputData(smallBuffer + position, 5).readUInt32();
+ // auto sizeOfValueSize = pbRawVarint32Size(valueSize);
+ KeyValueHolderCrypt kvHolder(keySize, valueSize, 0);
+ auto rollbackSize = position + 5;
+ decrypt.statusBeforeDecrypt(encryptText + rollbackSize, smallBuffer + rollbackSize, rollbackSize,
+ kvHolder.cryptStatus);
+ auto value = kvHolder.toMMBuffer(encryptText, &decrypt);
+# ifdef MMKV_APPLE
+ MMKVInfo("testAESToMMBuffer: %@", CodedInputData((char *) value.getPtr(), value.length()).readString());
+# else
+ MMKVInfo("testAESToMMBuffer: %s", CodedInputData((char *) value.getPtr(), value.length()).readString().c_str());
+# endif
+ MMKVInfo("MMBuffer::SmallBufferSize() = %u, KeyValueHolderCrypt::SmallBufferSize() = %u",
+ MMBuffer::SmallBufferSize(), KeyValueHolderCrypt::SmallBufferSize());
+}
+
+} // namespace mmkv
+
+#endif
diff --git a/ios/Pods/MMKVCore/Core/KeyValueHolder.h b/ios/Pods/MMKVCore/Core/KeyValueHolder.h
new file mode 100644
index 00000000..355ee890
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/KeyValueHolder.h
@@ -0,0 +1,116 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2020 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef KeyValueHolder_hpp
+#define KeyValueHolder_hpp
+#ifdef __cplusplus
+
+#include "MMBuffer.h"
+#include "aes/AESCrypt.h"
+
+namespace mmkv {
+
+#pragma pack(push, 1)
+
+struct KeyValueHolder {
+ uint16_t computedKVSize; // internal use only
+ uint16_t keySize;
+ uint32_t valueSize;
+ uint32_t offset;
+
+ KeyValueHolder() = default;
+ KeyValueHolder(uint32_t keyLength, uint32_t valueLength, uint32_t offset);
+
+ MMBuffer toMMBuffer(const void *basePtr) const;
+};
+
+#ifndef MMKV_DISABLE_CRYPT
+
+enum KeyValueHolderType : uint8_t {
+ KeyValueHolderType_Direct, // store value directly
+ KeyValueHolderType_Memory, // store value in the heap memory
+ KeyValueHolderType_Offset, // store value by offset
+};
+
+// kv holder for encrypted mmkv
+struct KeyValueHolderCrypt {
+ KeyValueHolderType type = KeyValueHolderType_Direct;
+
+ union {
+ // store value by offset
+ struct {
+ uint8_t pbKeyValueSize; // size needed to encode keySize & valueSize
+ uint16_t keySize;
+ uint32_t valueSize;
+ uint32_t offset;
+ AESCryptStatus cryptStatus;
+ };
+ // store value directly
+ struct {
+ uint8_t paddedSize;
+ uint8_t paddedValue[1];
+ };
+ // store value in the heap memory
+ struct {
+ uint32_t memSize;
+ void *memPtr;
+ };
+ };
+
+ static constexpr size_t SmallBufferSize() {
+ return sizeof(KeyValueHolderCrypt) - offsetof(KeyValueHolderCrypt, paddedValue);
+ }
+
+ static bool isValueStoredAsOffset(size_t valueSize) { return valueSize >= 256; }
+
+ KeyValueHolderCrypt() = default;
+ KeyValueHolderCrypt(const void *valuePtr, size_t valueLength);
+ explicit KeyValueHolderCrypt(MMBuffer &&data);
+ KeyValueHolderCrypt(uint32_t keyLength, uint32_t valueLength, uint32_t offset);
+
+ KeyValueHolderCrypt(KeyValueHolderCrypt &&other) noexcept;
+ KeyValueHolderCrypt &operator=(KeyValueHolderCrypt &&other) noexcept;
+ void move(KeyValueHolderCrypt &&other) noexcept;
+
+ ~KeyValueHolderCrypt();
+
+ MMBuffer toMMBuffer(const void *basePtr, const AESCrypt *crypter) const;
+
+ std::tuple toTuple() {
+ return std::make_tuple(offset, pbKeyValueSize + keySize + valueSize, &cryptStatus);
+ }
+
+ // those are expensive, just forbid it for possibly misuse
+ explicit KeyValueHolderCrypt(const KeyValueHolderCrypt &other) = delete;
+ KeyValueHolderCrypt &operator=(const KeyValueHolderCrypt &other) = delete;
+
+#ifndef NDEBUG
+ static void testAESToMMBuffer();
+#endif
+};
+
+#endif // MMKV_DISABLE_CRYPT
+
+#pragma pack(pop)
+
+} // namespace mmkv
+
+#endif
+#endif /* KeyValueHolder_hpp */
diff --git a/ios/Pods/MMKVCore/Core/MMBuffer.cpp b/ios/Pods/MMKVCore/Core/MMBuffer.cpp
new file mode 100644
index 00000000..6986959d
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMBuffer.cpp
@@ -0,0 +1,184 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MMBuffer.h"
+#include
+#include
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif
+
+using namespace std;
+
+namespace mmkv {
+
+MMBuffer::MMBuffer(size_t length) {
+ if (length > SmallBufferSize()) {
+ type = MMBufferType_Normal;
+ isNoCopy = MMBufferCopy;
+ size = length;
+ ptr = malloc(size);
+ if (!ptr) {
+ throw std::runtime_error(strerror(errno));
+ }
+#ifdef MMKV_APPLE
+ m_data = nil;
+#endif
+ } else {
+ type = MMBufferType_Small;
+ paddedSize = static_cast(length);
+ }
+}
+
+MMBuffer::MMBuffer(void *source, size_t length, MMBufferCopyFlag flag) : isNoCopy(flag) {
+ if (isNoCopy == MMBufferCopy) {
+ if (length > SmallBufferSize()) {
+ type = MMBufferType_Normal;
+ size = length;
+ ptr = malloc(size);
+ if (!ptr) {
+ throw std::runtime_error(strerror(errno));
+ }
+ memcpy(ptr, source, size);
+#ifdef MMKV_APPLE
+ m_data = nil;
+#endif
+ } else {
+ type = MMBufferType_Small;
+ paddedSize = static_cast(length);
+ memcpy(paddedBuffer, source, length);
+ }
+ } else {
+ type = MMBufferType_Normal;
+ size = length;
+ ptr = source;
+#ifdef MMKV_APPLE
+ m_data = nil;
+#endif
+ }
+}
+
+#ifdef MMKV_APPLE
+MMBuffer::MMBuffer(NSData *data, MMBufferCopyFlag flag)
+ : type(MMBufferType_Normal), ptr((void *) data.bytes), size(data.length), isNoCopy(flag) {
+ if (isNoCopy == MMBufferCopy) {
+ m_data = [data retain];
+ } else {
+ m_data = data;
+ }
+}
+#endif
+
+MMBuffer::MMBuffer(MMBuffer &&other) noexcept : type(other.type) {
+ if (type == MMBufferType_Normal) {
+ size = other.size;
+ ptr = other.ptr;
+ isNoCopy = other.isNoCopy;
+#ifdef MMKV_APPLE
+ m_data = other.m_data;
+#endif
+ other.detach();
+ } else {
+ paddedSize = other.paddedSize;
+ memcpy(paddedBuffer, other.paddedBuffer, paddedSize);
+ }
+}
+
+MMBuffer &MMBuffer::operator=(MMBuffer &&other) noexcept {
+ if (type == MMBufferType_Normal) {
+ if (other.type == MMBufferType_Normal) {
+ std::swap(isNoCopy, other.isNoCopy);
+ std::swap(size, other.size);
+ std::swap(ptr, other.ptr);
+#ifdef MMKV_APPLE
+ std::swap(m_data, other.m_data);
+#endif
+ } else {
+ type = MMBufferType_Small;
+ if (isNoCopy == MMBufferCopy) {
+#ifdef MMKV_APPLE
+ if (m_data) {
+ [m_data release];
+ } else if (ptr) {
+ free(ptr);
+ }
+#else
+ if (ptr) {
+ free(ptr);
+ }
+#endif
+ }
+ paddedSize = other.paddedSize;
+ memcpy(paddedBuffer, other.paddedBuffer, paddedSize);
+ }
+ } else {
+ if (other.type == MMBufferType_Normal) {
+ type = MMBufferType_Normal;
+ isNoCopy = other.isNoCopy;
+ size = other.size;
+ ptr = other.ptr;
+#ifdef MMKV_APPLE
+ m_data = other.m_data;
+#endif
+ other.detach();
+ } else {
+ uint8_t tmp[SmallBufferSize()];
+ memcpy(tmp, other.paddedBuffer, other.paddedSize);
+ memcpy(other.paddedBuffer, paddedBuffer, paddedSize);
+ memcpy(paddedBuffer, tmp, other.paddedSize);
+ std::swap(paddedSize, other.paddedSize);
+ }
+ }
+
+ return *this;
+}
+
+MMBuffer::~MMBuffer() {
+ if (type == MMBufferType_Small) {
+ return;
+ }
+
+#ifdef MMKV_APPLE
+ if (m_data) {
+ if (isNoCopy == MMBufferCopy) {
+ [m_data release];
+ }
+ return;
+ }
+#endif
+
+ if (isNoCopy == MMBufferCopy && ptr) {
+ free(ptr);
+ }
+}
+
+void MMBuffer::detach() {
+ // type = MMBufferType_Small;
+ // paddedSize = 0;
+ auto memsetPtr = (size_t *) &type;
+ *memsetPtr = 0;
+}
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/MMBuffer.h b/ios/Pods/MMKVCore/Core/MMBuffer.h
new file mode 100644
index 00000000..c3268f2e
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMBuffer.h
@@ -0,0 +1,104 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_MMBUFFER_H
+#define MMKV_MMBUFFER_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include
+#include
+
+namespace mmkv {
+
+enum MMBufferCopyFlag : bool {
+ MMBufferCopy = false,
+ MMBufferNoCopy = true,
+};
+
+#pragma pack(push, 1)
+
+#ifndef MMKV_DISABLE_CRYPT
+struct KeyValueHolderCrypt;
+#endif
+
+class MMBuffer {
+ enum MMBufferType : uint8_t {
+ MMBufferType_Small, // store small buffer in stack memory
+ MMBufferType_Normal, // store in heap memory
+ };
+ MMBufferType type;
+
+ union {
+ struct {
+ MMBufferCopyFlag isNoCopy;
+ size_t size;
+ void *ptr;
+#ifdef MMKV_APPLE
+ NSData *m_data;
+#endif
+ };
+ struct {
+ uint8_t paddedSize;
+ // make at least 10 bytes to hold all primitive types (negative int32, int64, double etc) on 32 bit device
+ // on 64 bit device it's guaranteed larger than 10 bytes
+ uint8_t paddedBuffer[10];
+ };
+ };
+
+ static constexpr size_t SmallBufferSize() {
+ return sizeof(MMBuffer) - offsetof(MMBuffer, paddedBuffer);
+ }
+
+public:
+ explicit MMBuffer(size_t length = 0);
+ MMBuffer(void *source, size_t length, MMBufferCopyFlag flag = MMBufferCopy);
+#ifdef MMKV_APPLE
+ explicit MMBuffer(NSData *data, MMBufferCopyFlag flag = MMBufferCopy);
+#endif
+
+ MMBuffer(MMBuffer &&other) noexcept;
+ MMBuffer &operator=(MMBuffer &&other) noexcept;
+
+ ~MMBuffer();
+
+ void *getPtr() const { return (type == MMBufferType_Small) ? (void *) paddedBuffer : ptr; }
+
+ size_t length() const { return (type == MMBufferType_Small) ? paddedSize : size; }
+
+ // transfer ownership to others
+ void detach();
+
+ // those are expensive, just forbid it for possibly misuse
+ explicit MMBuffer(const MMBuffer &other) = delete;
+ MMBuffer &operator=(const MMBuffer &other) = delete;
+
+#ifndef MMKV_DISABLE_CRYPT
+ friend KeyValueHolderCrypt;
+#endif
+};
+
+#pragma pack(pop)
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_MMBUFFER_H
diff --git a/ios/Pods/MMKVCore/Core/MMKV.cpp b/ios/Pods/MMKVCore/Core/MMKV.cpp
new file mode 100644
index 00000000..febcf724
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV.cpp
@@ -0,0 +1,958 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CodedInputData.h"
+#include "CodedOutputData.h"
+#include "InterProcessLock.h"
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include "MMKVLog.h"
+#include "MMKVMetaInfo.hpp"
+#include "MMKV_IO.h"
+#include "MemoryFile.h"
+#include "MiniPBCoder.h"
+#include "PBUtility.h"
+#include "ScopedLock.hpp"
+#include "ThreadLock.h"
+#include "aes/AESCrypt.h"
+#include "aes/openssl/openssl_aes.h"
+#include "aes/openssl/openssl_md5.h"
+#include "crc32/Checksum.h"
+#include
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+using namespace mmkv;
+
+unordered_map *g_instanceDic;
+ThreadLock *g_instanceLock;
+MMKVPath_t g_rootDir;
+static mmkv::ErrorHandler g_errorHandler;
+size_t mmkv::DEFAULT_MMAP_SIZE;
+
+#ifndef MMKV_WIN32
+constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = "specialCharacter";
+#else
+constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = L"specialCharacter";
+#endif
+constexpr uint32_t Fixed32Size = pbFixed32Size();
+
+MMKV_NAMESPACE_BEGIN
+
+#ifndef MMKV_ANDROID
+MMKV::MMKV(const std::string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath)
+ : m_mmapID(mmapID)
+ , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
+ , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
+ , m_dic(nullptr)
+ , m_dicCrypt(nullptr)
+ , m_file(new MemoryFile(m_path))
+ , m_metaFile(new MemoryFile(m_crcPath))
+ , m_metaInfo(new MMKVMetaInfo())
+ , m_crypter(nullptr)
+ , m_lock(new ThreadLock())
+ , m_fileLock(new FileLock(m_metaFile->getFd()))
+ , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
+ , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
+ , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0) {
+ m_actualSize = 0;
+ m_output = nullptr;
+
+# ifndef MMKV_DISABLE_CRYPT
+ if (cryptKey && cryptKey->length() > 0) {
+ m_dicCrypt = new MMKVMapCrypt();
+ m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
+ } else {
+ m_dic = new MMKVMap();
+ }
+# else
+ m_dic = new MMKVMap();
+# endif
+
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ m_crcDigest = 0;
+
+ m_lock->initialize();
+ m_sharedProcessLock->m_enable = m_isInterProcess;
+ m_exclusiveProcessLock->m_enable = m_isInterProcess;
+
+ // sensitive zone
+ {
+ SCOPED_LOCK(m_sharedProcessLock);
+ loadFromFile();
+ }
+}
+#endif
+
+MMKV::~MMKV() {
+ clearMemoryCache();
+
+ delete m_dic;
+#ifndef MMKV_DISABLE_CRYPT
+ delete m_dicCrypt;
+ delete m_crypter;
+#endif
+ delete m_file;
+ delete m_metaFile;
+ delete m_metaInfo;
+ delete m_lock;
+ delete m_fileLock;
+ delete m_sharedProcessLock;
+ delete m_exclusiveProcessLock;
+}
+
+MMKV *MMKV::defaultMMKV(MMKVMode mode, string *cryptKey) {
+#ifndef MMKV_ANDROID
+ return mmkvWithID(DEFAULT_MMAP_ID, mode, cryptKey);
+#else
+ return mmkvWithID(DEFAULT_MMAP_ID, DEFAULT_MMAP_SIZE, mode, cryptKey);
+#endif
+}
+
+void initialize() {
+ g_instanceDic = new unordered_map;
+ g_instanceLock = new ThreadLock();
+ g_instanceLock->initialize();
+
+ mmkv::DEFAULT_MMAP_SIZE = mmkv::getPageSize();
+ MMKVInfo("version %s page size:%d", MMKV_VERSION, DEFAULT_MMAP_SIZE);
+#if !defined(NDEBUG) && !defined(MMKV_DISABLE_CRYPT)
+ AESCrypt::testAESCrypt();
+ KeyValueHolderCrypt::testAESToMMBuffer();
+#endif
+}
+
+ThreadOnceToken_t once_control = ThreadOnceUninitialized;
+
+void MMKV::initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel) {
+ g_currentLogLevel = logLevel;
+
+ ThreadLock::ThreadOnce(&once_control, initialize);
+
+ g_rootDir = rootDir;
+ mkPath(g_rootDir);
+
+ MMKVInfo("root dir: " MMKV_PATH_FORMAT, g_rootDir.c_str());
+}
+
+#ifndef MMKV_ANDROID
+MMKV *MMKV::mmkvWithID(const string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath) {
+
+ if (mmapID.empty()) {
+ return nullptr;
+ }
+ SCOPED_LOCK(g_instanceLock);
+
+ auto mmapKey = mmapedKVKey(mmapID, rootPath);
+ auto itr = g_instanceDic->find(mmapKey);
+ if (itr != g_instanceDic->end()) {
+ MMKV *kv = itr->second;
+ return kv;
+ }
+
+ if (rootPath) {
+ MMKVPath_t specialPath = (*rootPath) + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
+ if (!isFileExist(specialPath)) {
+ mkPath(specialPath);
+ }
+ MMKVInfo("prepare to load %s (id %s) from rootPath %s", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());
+ }
+
+ auto kv = new MMKV(mmapID, mode, cryptKey, rootPath);
+ kv->m_mmapKey = mmapKey;
+ (*g_instanceDic)[mmapKey] = kv;
+ return kv;
+}
+#endif
+
+void MMKV::onExit() {
+ SCOPED_LOCK(g_instanceLock);
+
+ for (auto &pair : *g_instanceDic) {
+ MMKV *kv = pair.second;
+ kv->sync();
+ kv->clearMemoryCache();
+ delete kv;
+ pair.second = nullptr;
+ }
+
+ delete g_instanceDic;
+ g_instanceDic = nullptr;
+}
+
+const string &MMKV::mmapID() {
+ return m_mmapID;
+}
+
+mmkv::ContentChangeHandler g_contentChangeHandler = nullptr;
+
+void MMKV::notifyContentChanged() {
+ if (g_contentChangeHandler) {
+ g_contentChangeHandler(m_mmapID);
+ }
+}
+
+void MMKV::checkContentChanged() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+}
+
+void MMKV::registerContentChangeHandler(mmkv::ContentChangeHandler handler) {
+ g_contentChangeHandler = handler;
+}
+
+void MMKV::unRegisterContentChangeHandler() {
+ g_contentChangeHandler = nullptr;
+}
+
+void MMKV::clearMemoryCache() {
+ MMKVInfo("clearMemoryCache [%s]", m_mmapID.c_str());
+ SCOPED_LOCK(m_lock);
+ if (m_needLoadFromFile) {
+ return;
+ }
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ clearDictionary(m_dic);
+#ifndef MMKV_DISABLE_CRYPT
+ clearDictionary(m_dicCrypt);
+ if (m_crypter) {
+ if (m_metaInfo->m_version >= MMKVVersionRandomIV) {
+ m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));
+ } else {
+ m_crypter->resetIV();
+ }
+ }
+#endif
+
+ delete m_output;
+ m_output = nullptr;
+
+ m_file->clearMemoryCache();
+ m_actualSize = 0;
+ m_metaInfo->m_crcDigest = 0;
+}
+
+void MMKV::close() {
+ MMKVInfo("close [%s]", m_mmapID.c_str());
+ SCOPED_LOCK(g_instanceLock);
+ m_lock->lock();
+
+#ifndef MMKV_ANDROID
+ auto itr = g_instanceDic->find(m_mmapKey);
+#else
+ auto itr = g_instanceDic->find(m_mmapID);
+#endif
+ if (itr != g_instanceDic->end()) {
+ g_instanceDic->erase(itr);
+ }
+ delete this;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+string MMKV::cryptKey() {
+ SCOPED_LOCK(m_lock);
+
+ if (m_crypter) {
+ char key[AES_KEY_LEN];
+ m_crypter->getKey(key);
+ return string(key, strnlen(key, AES_KEY_LEN));
+ }
+ return "";
+}
+
+void MMKV::checkReSetCryptKey(const string *cryptKey) {
+ SCOPED_LOCK(m_lock);
+
+ if (m_crypter) {
+ if (cryptKey && cryptKey->length() > 0) {
+ string oldKey = this->cryptKey();
+ if (oldKey != *cryptKey) {
+ MMKVInfo("setting new aes key");
+ delete m_crypter;
+ auto ptr = cryptKey->data();
+ m_crypter = new AESCrypt(ptr, cryptKey->length());
+
+ checkLoadData();
+ } else {
+ // nothing to do
+ }
+ } else {
+ MMKVInfo("reset aes key");
+ delete m_crypter;
+ m_crypter = nullptr;
+
+ checkLoadData();
+ }
+ } else {
+ if (cryptKey && cryptKey->length() > 0) {
+ MMKVInfo("setting new aes key");
+ auto ptr = cryptKey->data();
+ m_crypter = new AESCrypt(ptr, cryptKey->length());
+
+ checkLoadData();
+ } else {
+ // nothing to do
+ }
+ }
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+bool MMKV::isFileValid() {
+ return m_file->isFileValid();
+}
+
+// crc
+
+// assuming m_file is valid
+bool MMKV::checkFileCRCValid(size_t actualSize, uint32_t crcDigest) {
+ auto ptr = (uint8_t *) m_file->getMemory();
+ if (ptr) {
+ m_crcDigest = (uint32_t) CRC32(0, (const uint8_t *) ptr + Fixed32Size, (uint32_t) actualSize);
+
+ if (m_crcDigest == crcDigest) {
+ return true;
+ }
+ MMKVError("check crc [%s] fail, crc32:%u, m_crcDigest:%u", m_mmapID.c_str(), crcDigest, m_crcDigest);
+ }
+ return false;
+}
+
+void MMKV::recaculateCRCDigestWithIV(const void *iv) {
+ auto ptr = (const uint8_t *) m_file->getMemory();
+ if (ptr) {
+ m_crcDigest = 0;
+ m_crcDigest = (uint32_t) CRC32(0, ptr + Fixed32Size, (uint32_t) m_actualSize);
+ writeActualSize(m_actualSize, m_crcDigest, iv, IncreaseSequence);
+ }
+}
+
+void MMKV::updateCRCDigest(const uint8_t *ptr, size_t length) {
+ if (ptr == nullptr) {
+ return;
+ }
+ m_crcDigest = (uint32_t) CRC32(m_crcDigest, ptr, (uint32_t) length);
+
+ writeActualSize(m_actualSize, m_crcDigest, nullptr, KeepSequence);
+}
+
+// set & get
+
+bool MMKV::set(bool value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbBoolSize();
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeBool(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(int32_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbInt32Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeInt32(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(uint32_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbUInt32Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeUInt32(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(int64_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbInt64Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeInt64(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(uint64_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbUInt64Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeUInt64(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(float value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbFloatSize();
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeFloat(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(double value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbDoubleSize();
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeDouble(value);
+
+ return setDataForKey(move(data), key);
+}
+
+#ifndef MMKV_APPLE
+
+bool MMKV::set(const char *value, MMKVKey_t key) {
+ if (!value) {
+ removeValueForKey(key);
+ return true;
+ }
+ return setDataForKey(MMBuffer((void *) value, strlen(value), MMBufferNoCopy), key, true);
+}
+
+bool MMKV::set(const string &value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ return setDataForKey(MMBuffer((void *) value.data(), value.length(), MMBufferNoCopy), key, true);
+}
+
+bool MMKV::set(const MMBuffer &value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ // delay write the size needed for encoding value
+ // avoid memory copying
+ return setDataForKey(MMBuffer(value.getPtr(), value.length(), MMBufferNoCopy), key, true);
+}
+
+bool MMKV::set(const vector &v, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ auto data = MiniPBCoder::encodeDataWithObject(v);
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::getString(MMKVKey_t key, string &result) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ result = input.readString();
+ return true;
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return false;
+}
+
+MMBuffer MMKV::getBytes(MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return MMBuffer();
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readData();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return MMBuffer();
+}
+
+bool MMKV::getVector(MMKVKey_t key, vector &result) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ result = MiniPBCoder::decodeVector(data);
+ return true;
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return false;
+}
+
+#endif // MMKV_APPLE
+
+bool MMKV::getBool(MMKVKey_t key, bool defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readBool();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+int32_t MMKV::getInt32(MMKVKey_t key, int32_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readInt32();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+uint32_t MMKV::getUInt32(MMKVKey_t key, uint32_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readUInt32();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+int64_t MMKV::getInt64(MMKVKey_t key, int64_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readInt64();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+uint64_t MMKV::getUInt64(MMKVKey_t key, uint64_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readUInt64();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+float MMKV::getFloat(MMKVKey_t key, float defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readFloat();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+double MMKV::getDouble(MMKVKey_t key, double defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readDouble();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+size_t MMKV::getValueSize(MMKVKey_t key, bool actualSize) {
+ if (isKeyEmpty(key)) {
+ return 0;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (actualSize) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ auto length = input.readInt32();
+ if (length >= 0) {
+ auto s_length = static_cast(length);
+ if (pbRawVarint32Size(length) + s_length == data.length()) {
+ return s_length;
+ }
+ }
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return data.length();
+}
+
+int32_t MMKV::writeValueToBuffer(MMKVKey_t key, void *ptr, int32_t size) {
+ if (isKeyEmpty(key) || size < 0) {
+ return -1;
+ }
+ auto s_size = static_cast(size);
+
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ auto length = input.readInt32();
+ auto offset = pbRawVarint32Size(length);
+ if (length >= 0) {
+ auto s_length = static_cast(length);
+ if (offset + s_length == data.length()) {
+ if (s_length <= s_size) {
+ memcpy(ptr, (uint8_t *) data.getPtr() + offset, s_length);
+ return length;
+ }
+ } else {
+ if (data.length() <= s_size) {
+ memcpy(ptr, data.getPtr(), data.length());
+ return static_cast(data.length());
+ }
+ }
+ }
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ return -1;
+}
+
+// enumerate
+
+bool MMKV::containsKey(MMKVKey_t key) {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ if (m_crypter) {
+ return m_dicCrypt->find(key) != m_dicCrypt->end();
+ } else {
+ return m_dic->find(key) != m_dic->end();
+ }
+}
+
+size_t MMKV::count() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+ if (m_crypter) {
+ return m_dicCrypt->size();
+ } else {
+ return m_dic->size();
+ }
+}
+
+size_t MMKV::totalSize() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+ return m_file->getFileSize();
+}
+
+size_t MMKV::actualSize() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+ return m_actualSize;
+}
+
+void MMKV::removeValueForKey(MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return;
+ }
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+ removeDataForKey(key);
+}
+
+#ifndef MMKV_APPLE
+
+vector MMKV::allKeys() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ vector keys;
+ if (m_crypter) {
+ for (const auto &itr : *m_dicCrypt) {
+ keys.push_back(itr.first);
+ }
+ } else {
+ for (const auto &itr : *m_dic) {
+ keys.push_back(itr.first);
+ }
+ }
+ return keys;
+}
+
+void MMKV::removeValuesForKeys(const vector &arrKeys) {
+ if (arrKeys.empty()) {
+ return;
+ }
+ if (arrKeys.size() == 1) {
+ return removeValueForKey(arrKeys[0]);
+ }
+
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+ size_t deleteCount = 0;
+ if (m_crypter) {
+ for (const auto &key : arrKeys) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ m_dicCrypt->erase(itr);
+ deleteCount++;
+ }
+ }
+ } else {
+ for (const auto &key : arrKeys) {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ m_dic->erase(itr);
+ deleteCount++;
+ }
+ }
+ }
+ if (deleteCount > 0) {
+ m_hasFullWriteback = false;
+
+ fullWriteback();
+ }
+}
+
+#endif // MMKV_APPLE
+
+// file
+
+void MMKV::sync(SyncFlag flag) {
+ SCOPED_LOCK(m_lock);
+ if (m_needLoadFromFile || !isFileValid()) {
+ return;
+ }
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ m_file->msync(flag);
+ m_metaFile->msync(flag);
+}
+
+void MMKV::lock() {
+ m_exclusiveProcessLock->lock();
+}
+void MMKV::unlock() {
+ m_exclusiveProcessLock->unlock();
+}
+bool MMKV::try_lock() {
+ return m_exclusiveProcessLock->try_lock();
+}
+
+void MMKV::registerErrorHandler(ErrorHandler handler) {
+ SCOPED_LOCK(g_instanceLock);
+ g_errorHandler = handler;
+}
+
+void MMKV::unRegisterErrorHandler() {
+ SCOPED_LOCK(g_instanceLock);
+ g_errorHandler = nullptr;
+}
+
+void MMKV::registerLogHandler(LogHandler handler) {
+ SCOPED_LOCK(g_instanceLock);
+ g_logHandler = handler;
+}
+
+void MMKV::unRegisterLogHandler() {
+ SCOPED_LOCK(g_instanceLock);
+ g_logHandler = nullptr;
+}
+
+void MMKV::setLogLevel(MMKVLogLevel level) {
+ SCOPED_LOCK(g_instanceLock);
+ g_currentLogLevel = level;
+}
+
+static void mkSpecialCharacterFileDirectory() {
+ MMKVPath_t path = g_rootDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
+ mkPath(path);
+}
+
+template
+static string md5(const basic_string &value) {
+ uint8_t md[MD5_DIGEST_LENGTH] = {};
+ char tmp[3] = {}, buf[33] = {};
+ openssl::MD5((const uint8_t *) value.c_str(), value.size() * (sizeof(T) / sizeof(uint8_t)), md);
+ for (auto ch : md) {
+ snprintf(tmp, sizeof(tmp), "%2.2x", ch);
+ strcat(buf, tmp);
+ }
+ return string(buf);
+}
+
+static MMKVPath_t encodeFilePath(const string &mmapID) {
+ const char *specialCharacters = "\\/:*?\"<>|";
+ string encodedID;
+ bool hasSpecialCharacter = false;
+ for (auto ch : mmapID) {
+ if (strchr(specialCharacters, ch) != nullptr) {
+ encodedID = md5(mmapID);
+ hasSpecialCharacter = true;
+ break;
+ }
+ }
+ if (hasSpecialCharacter) {
+ static ThreadOnceToken_t once_control = ThreadOnceUninitialized;
+ ThreadLock::ThreadOnce(&once_control, mkSpecialCharacterFileDirectory);
+ return MMKVPath_t(SPECIAL_CHARACTER_DIRECTORY_NAME) + MMKV_PATH_SLASH + string2MMKVPath_t(encodedID);
+ } else {
+ return string2MMKVPath_t(mmapID);
+ }
+}
+
+string mmapedKVKey(const string &mmapID, MMKVPath_t *rootPath) {
+ if (rootPath && g_rootDir != (*rootPath)) {
+ return md5(*rootPath + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID));
+ }
+ return mmapID;
+}
+
+MMKVPath_t mappedKVPathWithID(const string &mmapID, MMKVMode mode, MMKVPath_t *rootPath) {
+#ifndef MMKV_ANDROID
+ if (rootPath) {
+#else
+ if (mode & MMKV_ASHMEM) {
+ return ashmemMMKVPathWithID(encodeFilePath(mmapID));
+ } else if (rootPath) {
+#endif
+ return *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID);
+ }
+ return g_rootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID);
+}
+
+#ifndef MMKV_WIN32
+constexpr auto CRC_SUFFIX = ".crc";
+#else
+constexpr auto CRC_SUFFIX = L".crc";
+#endif
+
+MMKVPath_t crcPathWithID(const string &mmapID, MMKVMode mode, MMKVPath_t *rootPath) {
+#ifndef MMKV_ANDROID
+ if (rootPath) {
+#else
+ if (mode & MMKV_ASHMEM) {
+ return ashmemMMKVPathWithID(encodeFilePath(mmapID)) + CRC_SUFFIX;
+ } else if (rootPath) {
+#endif
+ return *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID) + CRC_SUFFIX;
+ }
+ return g_rootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID) + CRC_SUFFIX;
+}
+
+MMKVRecoverStrategic onMMKVCRCCheckFail(const string &mmapID) {
+ if (g_errorHandler) {
+ return g_errorHandler(mmapID, MMKVErrorType::MMKVCRCCheckFail);
+ }
+ return OnErrorDiscard;
+}
+
+MMKVRecoverStrategic onMMKVFileLengthError(const string &mmapID) {
+ if (g_errorHandler) {
+ return g_errorHandler(mmapID, MMKVErrorType::MMKVFileLength);
+ }
+ return OnErrorDiscard;
+}
+
+MMKV_NAMESPACE_END
diff --git a/ios/Pods/MMKVCore/Core/MMKV.h b/ios/Pods/MMKVCore/Core/MMKV.h
new file mode 100644
index 00000000..fb505744
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV.h
@@ -0,0 +1,349 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_MMKV_H
+#define MMKV_MMKV_H
+#ifdef __cplusplus
+
+#include "MMBuffer.h"
+#include
+
+namespace mmkv {
+class CodedOutputData;
+class MemoryFile;
+class AESCrypt;
+struct MMKVMetaInfo;
+class FileLock;
+class InterProcessLock;
+class ThreadLock;
+} // namespace mmkv
+
+MMKV_NAMESPACE_BEGIN
+
+enum MMKVMode : uint32_t {
+ MMKV_SINGLE_PROCESS = 0x1,
+ MMKV_MULTI_PROCESS = 0x2,
+#ifdef MMKV_ANDROID
+ CONTEXT_MODE_MULTI_PROCESS = 0x4, // in case someone mistakenly pass Context.MODE_MULTI_PROCESS
+ MMKV_ASHMEM = 0x8,
+#endif
+};
+
+class MMKV {
+#ifndef MMKV_ANDROID
+ std::string m_mmapKey;
+ MMKV(const std::string &mmapID, MMKVMode mode, std::string *cryptKey, MMKVPath_t *rootPath);
+#else // defined(MMKV_ANDROID)
+ MMKV(const std::string &mmapID, int size, MMKVMode mode, std::string *cryptKey, MMKVPath_t *rootPath);
+
+ MMKV(const std::string &mmapID, int ashmemFD, int ashmemMetaFd, std::string *cryptKey = nullptr);
+#endif
+
+ ~MMKV();
+
+ std::string m_mmapID;
+ MMKVPath_t m_path;
+ MMKVPath_t m_crcPath;
+ mmkv::MMKVMap *m_dic;
+ mmkv::MMKVMapCrypt *m_dicCrypt;
+
+ mmkv::MemoryFile *m_file;
+ size_t m_actualSize;
+ mmkv::CodedOutputData *m_output;
+
+ bool m_needLoadFromFile;
+ bool m_hasFullWriteback;
+
+ uint32_t m_crcDigest;
+ mmkv::MemoryFile *m_metaFile;
+ mmkv::MMKVMetaInfo *m_metaInfo;
+
+ mmkv::AESCrypt *m_crypter;
+
+ mmkv::ThreadLock *m_lock;
+ mmkv::FileLock *m_fileLock;
+ mmkv::InterProcessLock *m_sharedProcessLock;
+ mmkv::InterProcessLock *m_exclusiveProcessLock;
+
+#ifdef MMKV_APPLE
+ using MMKVKey_t = NSString *__unsafe_unretained;
+ static bool isKeyEmpty(MMKVKey_t key) { return key.length <= 0; }
+#else
+ using MMKVKey_t = const std::string &;
+ static bool isKeyEmpty(MMKVKey_t key) { return key.empty(); }
+#endif
+
+ void loadFromFile();
+
+ void partialLoadFromFile();
+
+ void checkDataValid(bool &loadFromFile, bool &needFullWriteback);
+
+ void checkLoadData();
+
+ bool isFileValid();
+
+ bool checkFileCRCValid(size_t actualSize, uint32_t crcDigest);
+
+ void recaculateCRCDigestWithIV(const void *iv);
+
+ void updateCRCDigest(const uint8_t *ptr, size_t length);
+
+ size_t readActualSize();
+
+ void oldStyleWriteActualSize(size_t actualSize);
+
+ bool writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool increaseSequence);
+
+ bool ensureMemorySize(size_t newSize);
+
+ bool fullWriteback(mmkv::AESCrypt *newCrypter = nullptr);
+
+ bool doFullWriteBack(std::pair preparedData, mmkv::AESCrypt *newCrypter);
+
+ mmkv::MMBuffer getDataForKey(MMKVKey_t key);
+
+ // isDataHolder: avoid memory copying
+ bool setDataForKey(mmkv::MMBuffer &&data, MMKVKey_t key, bool isDataHolder = false);
+
+ bool removeDataForKey(MMKVKey_t key);
+
+ using KVHolderRet_t = std::pair;
+ // isDataHolder: avoid memory copying
+ KVHolderRet_t doAppendDataWithKey(const mmkv::MMBuffer &data, const mmkv::MMBuffer &key, bool isDataHolder, uint32_t keyLength);
+ KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data, MMKVKey_t key, bool isDataHolder = false);
+ KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data, const mmkv::KeyValueHolder &kvHolder, bool isDataHolder = false);
+#ifdef MMKV_APPLE
+ KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data,
+ MMKVKey_t key,
+ const mmkv::KeyValueHolderCrypt &kvHolder,
+ bool isDataHolder = false);
+#endif
+
+ void notifyContentChanged();
+
+#if defined(MMKV_ANDROID) && !defined(MMKV_DISABLE_CRYPT)
+ void checkReSetCryptKey(int fd, int metaFD, std::string *cryptKey);
+#endif
+
+public:
+ // call this before getting any MMKV instance
+ static void initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel = MMKVLogInfo);
+
+#ifdef MMKV_APPLE
+ // protect from some old code that don't call initializeMMKV()
+ static void minimalInit(MMKVPath_t defaultRootDir);
+#endif
+
+ // a generic purpose instance
+ static MMKV *defaultMMKV(MMKVMode mode = MMKV_SINGLE_PROCESS, std::string *cryptKey = nullptr);
+
+#ifndef MMKV_ANDROID
+
+ // mmapID: any unique ID (com.tencent.xin.pay, etc)
+ // if you want a per-user mmkv, you could merge user-id within mmapID
+ // cryptKey: 16 bytes at most
+ static MMKV *mmkvWithID(const std::string &mmapID,
+ MMKVMode mode = MMKV_SINGLE_PROCESS,
+ std::string *cryptKey = nullptr,
+ MMKVPath_t *rootPath = nullptr);
+
+#else // defined(MMKV_ANDROID)
+
+ // mmapID: any unique ID (com.tencent.xin.pay, etc)
+ // if you want a per-user mmkv, you could merge user-id within mmapID
+ // cryptKey: 16 bytes at most
+ static MMKV *mmkvWithID(const std::string &mmapID,
+ int size = mmkv::DEFAULT_MMAP_SIZE,
+ MMKVMode mode = MMKV_SINGLE_PROCESS,
+ std::string *cryptKey = nullptr,
+ MMKVPath_t *rootPath = nullptr);
+
+ static MMKV *mmkvWithAshmemFD(const std::string &mmapID, int fd, int metaFD, std::string *cryptKey = nullptr);
+
+ int ashmemFD();
+
+ int ashmemMetaFD();
+
+#endif // MMKV_ANDROID
+
+ // you can call this on application termination, it's totally fine if you don't call
+ static void onExit();
+
+ const std::string &mmapID();
+
+ const bool m_isInterProcess;
+
+#ifndef MMKV_DISABLE_CRYPT
+ std::string cryptKey();
+
+ // transform plain text into encrypted text, or vice versa with empty cryptKey
+ // you can change existing crypt key with different cryptKey
+ bool reKey(const std::string &cryptKey);
+
+ // just reset cryptKey (will not encrypt or decrypt anything)
+ // usually you should call this method after other process reKey() the multi-process mmkv
+ void checkReSetCryptKey(const std::string *cryptKey);
+#endif
+
+ bool set(bool value, MMKVKey_t key);
+
+ bool set(int32_t value, MMKVKey_t key);
+
+ bool set(uint32_t value, MMKVKey_t key);
+
+ bool set(int64_t value, MMKVKey_t key);
+
+ bool set(uint64_t value, MMKVKey_t key);
+
+ bool set(float value, MMKVKey_t key);
+
+ bool set(double value, MMKVKey_t key);
+
+ // avoid unexpected type conversion (pointer to bool, etc)
+ template
+ bool set(T value, MMKVKey_t key) = delete;
+
+#ifdef MMKV_APPLE
+ bool set(NSObject *__unsafe_unretained obj, MMKVKey_t key);
+
+ NSObject *getObject(MMKVKey_t key, Class cls);
+#else // !defined(MMKV_APPLE)
+ bool set(const char *value, MMKVKey_t key);
+
+ bool set(const std::string &value, MMKVKey_t key);
+
+ bool set(const mmkv::MMBuffer &value, MMKVKey_t key);
+
+ bool set(const std::vector &vector, MMKVKey_t key);
+
+ bool getString(MMKVKey_t key, std::string &result);
+
+ mmkv::MMBuffer getBytes(MMKVKey_t key);
+
+ bool getVector(MMKVKey_t key, std::vector &result);
+#endif // MMKV_APPLE
+
+ bool getBool(MMKVKey_t key, bool defaultValue = false);
+
+ int32_t getInt32(MMKVKey_t key, int32_t defaultValue = 0);
+
+ uint32_t getUInt32(MMKVKey_t key, uint32_t defaultValue = 0);
+
+ int64_t getInt64(MMKVKey_t key, int64_t defaultValue = 0);
+
+ uint64_t getUInt64(MMKVKey_t key, uint64_t defaultValue = 0);
+
+ float getFloat(MMKVKey_t key, float defaultValue = 0);
+
+ double getDouble(MMKVKey_t key, double defaultValue = 0);
+
+ // return the actual size consumption of the key's value
+ // pass actualSize = true to get value's length
+ size_t getValueSize(MMKVKey_t key, bool actualSize);
+
+ // return size written into buffer
+ // return -1 on any error
+ int32_t writeValueToBuffer(MMKVKey_t key, void *ptr, int32_t size);
+
+ bool containsKey(MMKVKey_t key);
+
+ size_t count();
+
+ size_t totalSize();
+
+ size_t actualSize();
+
+#ifdef MMKV_APPLE
+ NSArray *allKeys();
+
+ void removeValuesForKeys(NSArray *arrKeys);
+
+ typedef void (^EnumerateBlock)(NSString *key, BOOL *stop);
+ void enumerateKeys(EnumerateBlock block);
+
+# ifdef MMKV_IOS
+ static void setIsInBackground(bool isInBackground);
+# endif
+#else // !defined(MMKV_APPLE)
+ std::vector allKeys();
+
+ void removeValuesForKeys(const std::vector &arrKeys);
+#endif // MMKV_APPLE
+
+ void removeValueForKey(MMKVKey_t key);
+
+ void clearAll();
+
+ // MMKV's size won't reduce after deleting key-values
+ // call this method after lots of deleting if you care about disk usage
+ // note that `clearAll` has the similar effect of `trim`
+ void trim();
+
+ // call this method if the instance is no longer needed in the near future
+ // any subsequent call to the instance is undefined behavior
+ void close();
+
+ // call this method if you are facing memory-warning
+ // any subsequent call to the instance will load all key-values from file again
+ void clearMemoryCache();
+
+ // you don't need to call this, really, I mean it
+ // unless you worry about running out of battery
+ void sync(SyncFlag flag = MMKV_SYNC);
+
+ // get exclusive access
+ void lock();
+ void unlock();
+ bool try_lock();
+
+ // check if content changed by other process
+ void checkContentChanged();
+
+ // called when content is changed by other process
+ // doesn't guarantee real-time notification
+ static void registerContentChangeHandler(mmkv::ContentChangeHandler handler);
+ static void unRegisterContentChangeHandler();
+
+ // by default MMKV will discard all datas on failure
+ // return `OnErrorRecover` to recover any data from file
+ static void registerErrorHandler(mmkv::ErrorHandler handler);
+ static void unRegisterErrorHandler();
+
+ // MMKVLogInfo by default
+ // pass MMKVLogNone to disable all logging
+ static void setLogLevel(MMKVLogLevel level);
+
+ // by default MMKV will print log to the console
+ // implement this method to redirect MMKV's log
+ static void registerLogHandler(mmkv::LogHandler handler);
+ static void unRegisterLogHandler();
+
+ static bool isFileValid(const std::string &mmapID, MMKVPath_t *relatePath = nullptr);
+
+ // just forbid it for possibly misuse
+ explicit MMKV(const MMKV &other) = delete;
+ MMKV &operator=(const MMKV &other) = delete;
+};
+
+MMKV_NAMESPACE_END
+
+#endif
+#endif // MMKV_MMKV_H
diff --git a/ios/Pods/MMKVCore/Core/MMKVLog.cpp b/ios/Pods/MMKVCore/Core/MMKVLog.cpp
new file mode 100644
index 00000000..5747958a
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVLog.cpp
@@ -0,0 +1,128 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MMKVLog.h"
+
+MMKV_NAMESPACE_BEGIN
+
+#ifndef NDEBUG
+MMKVLogLevel g_currentLogLevel = MMKVLogDebug;
+#else
+MMKVLogLevel g_currentLogLevel = MMKVLogInfo;
+#endif
+
+mmkv::LogHandler g_logHandler;
+
+MMKV_NAMESPACE_END
+
+#ifdef ENABLE_MMKV_LOG
+# include
+# include
+
+using namespace mmkv;
+
+const char *_getFileName(const char *path) {
+ const char *ptr = strrchr(path, '/');
+ if (!ptr) {
+ ptr = strrchr(path, '\\');
+ }
+ if (ptr) {
+ return ptr + 1;
+ } else {
+ return path;
+ }
+}
+
+# ifndef MMKV_ANDROID
+
+static const char *MMKVLogLevelDesc(MMKVLogLevel level) {
+ switch (level) {
+ case MMKVLogDebug:
+ return "D";
+ case MMKVLogInfo:
+ return "I";
+ case MMKVLogWarning:
+ return "W";
+ case MMKVLogError:
+ return "E";
+ default:
+ return "N";
+ }
+}
+
+# ifdef MMKV_APPLE
+
+void _MMKVLogWithLevel(MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...) {
+ if (level >= g_currentLogLevel) {
+ NSString *nsFormat = [NSString stringWithUTF8String:format];
+ va_list argList;
+ va_start(argList, format);
+ NSString *message = [[NSString alloc] initWithFormat:nsFormat arguments:argList];
+ va_end(argList);
+
+ auto filename = _getFileName(file);
+
+ if (g_logHandler) {
+ g_logHandler(level, filename, line, func, message);
+ } else {
+ NSLog(@"[%s] <%s:%d::%s> %@", MMKVLogLevelDesc(level), filename, line, func, message);
+ }
+ }
+}
+
+# else
+
+void _MMKVLogWithLevel(MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...) {
+ if (level >= g_currentLogLevel) {
+ std::string message;
+ char buffer[16];
+
+ va_list args;
+ va_start(args, format);
+ auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ if (length < 0) { // something wrong
+ message = {};
+ } else if (length < sizeof(buffer)) {
+ message = std::string(buffer, static_cast(length));
+ } else {
+ message.resize(static_cast(length), '\0');
+ va_start(args, format);
+ std::vsnprintf(const_cast(message.data()), static_cast(length) + 1, format, args);
+ va_end(args);
+ }
+
+ auto filename = _getFileName(file);
+
+ if (g_logHandler) {
+ g_logHandler(level, filename, line, func, message);
+ } else {
+ printf("[%s] <%s:%d::%s> %s\n", MMKVLogLevelDesc(level), filename, line, func, message.c_str());
+ //fflush(stdout);
+ }
+ }
+}
+
+# endif // MMKV_APPLE
+
+# endif // MMKV_ANDROID
+
+#endif // ENABLE_MMKV_LOG
diff --git a/ios/Pods/MMKVCore/Core/MMKVLog.h b/ios/Pods/MMKVCore/Core/MMKVLog.h
new file mode 100644
index 00000000..f55f0ee9
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVLog.h
@@ -0,0 +1,75 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_MMKVLOG_H
+#define MMKV_MMKVLOG_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include
+#include
+#include
+
+void _MMKVLogWithLevel(
+ MMKV_NAMESPACE_PREFIX::MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...);
+
+MMKV_NAMESPACE_BEGIN
+
+extern MMKVLogLevel g_currentLogLevel;
+extern mmkv::LogHandler g_logHandler;
+
+// enable logging
+#define ENABLE_MMKV_LOG
+
+#ifdef ENABLE_MMKV_LOG
+
+# define MMKVError(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogError, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+# define MMKVWarning(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogWarning, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+# define MMKVInfo(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogInfo, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+
+# ifndef NDEBUG
+# define MMKVDebug(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogDebug, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+# else
+# define MMKVDebug(format, ...) \
+ {}
+# endif
+
+#else
+
+# define MMKVError(format, ...) \
+ {}
+# define MMKVWarning(format, ...) \
+ {}
+# define MMKVInfo(format, ...) \
+ {}
+# define MMKVDebug(format, ...) \
+ {}
+
+#endif
+
+MMKV_NAMESPACE_END
+
+#endif
+#endif //MMKV_MMKVLOG_H
diff --git a/ios/Pods/MMKVCore/Core/MMKVLog_Android.cpp b/ios/Pods/MMKVCore/Core/MMKVLog_Android.cpp
new file mode 100644
index 00000000..ee2435f9
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVLog_Android.cpp
@@ -0,0 +1,83 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MMKVLog.h"
+
+#ifdef ENABLE_MMKV_LOG
+# ifdef MMKV_ANDROID
+# include
+# include
+# include
+
+using namespace std;
+
+constexpr auto APP_NAME = "MMKV";
+
+extern const char *_getFileName(const char *path);
+
+static android_LogPriority MMKVLogLevelDesc(MMKVLogLevel level) {
+ switch (level) {
+ case MMKVLogDebug:
+ return ANDROID_LOG_DEBUG;
+ case MMKVLogInfo:
+ return ANDROID_LOG_INFO;
+ case MMKVLogWarning:
+ return ANDROID_LOG_WARN;
+ case MMKVLogError:
+ return ANDROID_LOG_ERROR;
+ default:
+ return ANDROID_LOG_UNKNOWN;
+ }
+}
+
+void _MMKVLogWithLevel(MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...) {
+ if (level >= g_currentLogLevel) {
+ string message;
+ char buffer[16];
+
+ va_list args;
+ va_start(args, format);
+ auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ if (length < 0) { // something wrong
+ message = {};
+ } else if (length < sizeof(buffer)) {
+ message = string(buffer, static_cast(length));
+ } else {
+ message.resize(static_cast(length), '\0');
+ va_start(args, format);
+ std::vsnprintf(const_cast(message.data()), static_cast(length) + 1, format, args);
+ va_end(args);
+ }
+
+ auto filename = _getFileName(file);
+
+ if (g_logHandler) {
+ g_logHandler(level, filename, line, func, message);
+ } else {
+ auto desc = MMKVLogLevelDesc(level);
+ __android_log_print(desc, APP_NAME, "<%s:%d::%s> %s", filename, line, func, message.c_str());
+ }
+ }
+}
+# endif // MMKV_ANDROID
+
+#endif // ENABLE_MMKV_LOG
diff --git a/ios/Pods/MMKVCore/Core/MMKVMetaInfo.hpp b/ios/Pods/MMKVCore/Core/MMKVMetaInfo.hpp
new file mode 100644
index 00000000..2aad778b
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVMetaInfo.hpp
@@ -0,0 +1,81 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_MMKVMETAINFO_H
+#define MMKV_MMKVMETAINFO_H
+#ifdef __cplusplus
+
+#include "aes/AESCrypt.h"
+#include
+#include
+
+namespace mmkv {
+
+enum MMKVVersion : uint32_t {
+ MMKVVersionDefault = 0,
+
+ // record full write back count
+ MMKVVersionSequence = 1,
+
+ // store random iv for encryption
+ MMKVVersionRandomIV = 2,
+
+ // store actual size together with crc checksum, try to reduce file corruption
+ MMKVVersionActualSize = 3,
+};
+
+struct MMKVMetaInfo {
+ uint32_t m_crcDigest = 0;
+ uint32_t m_version = MMKVVersionSequence;
+ uint32_t m_sequence = 0; // full write-back count
+ uint8_t m_vector[AES_KEY_LEN] = {};
+ uint32_t m_actualSize = 0;
+
+ // confirmed info: it's been synced to file
+ struct {
+ uint32_t lastActualSize = 0;
+ uint32_t lastCRCDigest = 0;
+ uint32_t _reserved[16] = {};
+ } m_lastConfirmedMetaInfo;
+
+ void write(void *ptr) const {
+ MMKV_ASSERT(ptr);
+ memcpy(ptr, this, sizeof(MMKVMetaInfo));
+ }
+
+ void writeCRCAndActualSizeOnly(void *ptr) const {
+ MMKV_ASSERT(ptr);
+ auto other = (MMKVMetaInfo *) ptr;
+ other->m_crcDigest = m_crcDigest;
+ other->m_actualSize = m_actualSize;
+ }
+
+ void read(const void *ptr) {
+ MMKV_ASSERT(ptr);
+ memcpy(this, ptr, sizeof(MMKVMetaInfo));
+ }
+};
+
+static_assert(sizeof(MMKVMetaInfo) <= (4 * 1024), "MMKVMetaInfo lager than one pagesize");
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_MMKVMETAINFO_H
diff --git a/ios/Pods/MMKVCore/Core/MMKVPredef.h b/ios/Pods/MMKVCore/Core/MMKVPredef.h
new file mode 100755
index 00000000..dad0a1fb
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVPredef.h
@@ -0,0 +1,198 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_SRC_MMKVPREDEF_H
+#define MMKV_SRC_MMKVPREDEF_H
+
+// disable encryption & decryption to reduce some code
+//#define MMKV_DISABLE_CRYPT
+
+// using POSIX implementation
+//#define FORCE_POSIX
+
+#ifdef __cplusplus
+
+#include
+#include
+#include
+
+constexpr auto MMKV_VERSION = "v1.2.1";
+
+#ifdef __ANDROID__
+# define MMKV_ANDROID
+#elif __APPLE__
+# ifdef FORCE_POSIX
+# define MMKV_POSIX
+# else
+# define MMKV_APPLE
+# ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
+# define MMKV_IOS
+# elif __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__
+# define MMKV_WATCH
+# else
+# define MMKV_MAC
+# endif
+# endif // FORCE_POSIX
+#elif __linux__ || __unix__
+# define MMKV_POSIX
+# if __linux__
+# define MMKV_LINUX
+# endif
+#elif _WIN32
+# define MMKV_WIN32
+#endif
+
+#ifdef MMKV_WIN32
+# if !defined(_WIN32_WINNT)
+# define _WIN32_WINNT _WIN32_WINNT_WINXP
+# endif
+
+# include
+// Exclude rarely-used stuff from Windows headers
+# define WIN32_LEAN_AND_MEAN
+// Windows Header Files
+# include
+
+constexpr auto MMKV_PATH_SLASH = L"\\";
+# define MMKV_PATH_FORMAT "%ws"
+using MMKVFileHandle_t = HANDLE;
+using MMKVPath_t = std::wstring;
+extern MMKVPath_t string2MMKVPath_t(const std::string &str);
+
+# ifndef MMKV_EMBED_ZLIB
+# define MMKV_EMBED_ZLIB 1
+# endif
+
+#else // MMKV_WIN32
+
+constexpr auto MMKV_PATH_SLASH = "/";
+# define MMKV_PATH_FORMAT "%s"
+using MMKVFileHandle_t = int;
+using MMKVPath_t = std::string;
+# define string2MMKVPath_t(str) (str)
+
+# ifndef MMKV_EMBED_ZLIB
+# define MMKV_EMBED_ZLIB 0
+# endif
+
+#endif // MMKV_WIN32
+
+#ifdef MMKV_APPLE
+# import
+# define MMKV_NAMESPACE_BEGIN namespace mmkv {
+# define MMKV_NAMESPACE_END }
+# define MMKV_NAMESPACE_PREFIX mmkv
+using MMKVLog_t = NSString *;
+#else
+# define MMKV_NAMESPACE_BEGIN
+# define MMKV_NAMESPACE_END
+# define MMKV_NAMESPACE_PREFIX
+using MMKVLog_t = const std::string &;
+#endif // MMKV_APPLE
+
+MMKV_NAMESPACE_BEGIN
+
+enum MMKVLogLevel : int {
+ MMKVLogDebug = 0, // not available for release/product build
+ MMKVLogInfo = 1, // default level
+ MMKVLogWarning,
+ MMKVLogError,
+ MMKVLogNone, // special level used to disable all log messages
+};
+
+enum MMKVRecoverStrategic : int {
+ OnErrorDiscard = 0,
+ OnErrorRecover,
+};
+
+enum MMKVErrorType : int {
+ MMKVCRCCheckFail = 0,
+ MMKVFileLength,
+};
+
+enum SyncFlag : bool { MMKV_SYNC = true, MMKV_ASYNC = false };
+
+MMKV_NAMESPACE_END
+
+namespace mmkv {
+
+typedef void (*LogHandler)(MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message);
+
+// by default MMKV will discard all datas on failure
+// return `OnErrorRecover` to recover any data from file
+typedef MMKVRecoverStrategic (*ErrorHandler)(const std::string &mmapID, MMKVErrorType errorType);
+
+// called when content is changed by other process
+// doesn't guarantee real-time notification
+typedef void (*ContentChangeHandler)(const std::string &mmapID);
+
+extern size_t DEFAULT_MMAP_SIZE;
+#define DEFAULT_MMAP_ID "mmkv.default"
+
+class MMBuffer;
+struct KeyValueHolder;
+
+#ifdef MMKV_DISABLE_CRYPT
+using KeyValueHolderCrypt = KeyValueHolder;
+#else
+struct KeyValueHolderCrypt;
+#endif
+
+#ifdef MMKV_APPLE
+struct KeyHasher {
+ size_t operator()(NSString *key) const { return key.hash; }
+};
+
+struct KeyEqualer {
+ bool operator()(NSString *left, NSString *right) const {
+ if (left == right) {
+ return true;
+ }
+ return ([left isEqualToString:right] == YES);
+ }
+};
+
+using MMKVVector = std::vector>;
+using MMKVMap = std::unordered_map;
+using MMKVMapCrypt = std::unordered_map;
+#else
+using MMKVVector = std::vector>;
+using MMKVMap = std::unordered_map;
+using MMKVMapCrypt = std::unordered_map;
+#endif // MMKV_APPLE
+
+template
+void unused(const T &) {}
+
+constexpr size_t AES_KEY_LEN = 16;
+constexpr size_t AES_KEY_BITSET_LEN = 128;
+
+} // namespace mmkv
+
+#ifndef NDEBUG
+# include
+# define MMKV_ASSERT(var) assert(var)
+#else
+# define MMKV_ASSERT(var) mmkv::unused(var)
+#endif
+
+#endif //cplus-plus
+
+#endif //MMKV_SRC_MMKVPREDEF_H
diff --git a/ios/Pods/MMKVCore/Core/MMKV_Android.cpp b/ios/Pods/MMKVCore/Core/MMKV_Android.cpp
new file mode 100644
index 00000000..b57089fb
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_Android.cpp
@@ -0,0 +1,202 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MMKV.h"
+
+#ifdef MMKV_ANDROID
+
+# include "InterProcessLock.h"
+# include "KeyValueHolder.h"
+# include "MMKVLog.h"
+# include "MMKVMetaInfo.hpp"
+# include "MemoryFile.h"
+# include "ScopedLock.hpp"
+# include "ThreadLock.h"
+# include
+
+using namespace std;
+using namespace mmkv;
+
+extern unordered_map *g_instanceDic;
+extern ThreadLock *g_instanceLock;
+
+extern string mmapedKVKey(const string &mmapID, string *rootPath);
+extern string mappedKVPathWithID(const string &mmapID, MMKVMode mode, string *rootPath);
+extern string crcPathWithID(const string &mmapID, MMKVMode mode, string *rootPath);
+
+MMKV::MMKV(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath)
+ : m_mmapID(mmapedKVKey(mmapID, rootPath)) // historically Android mistakenly use mmapKey as mmapID
+ , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
+ , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
+ , m_dic(nullptr)
+ , m_dicCrypt(nullptr)
+ , m_file(new MemoryFile(m_path, size, (mode & MMKV_ASHMEM) ? MMFILE_TYPE_ASHMEM : MMFILE_TYPE_FILE))
+ , m_metaFile(new MemoryFile(m_crcPath, DEFAULT_MMAP_SIZE, m_file->m_fileType))
+ , m_metaInfo(new MMKVMetaInfo())
+ , m_crypter(nullptr)
+ , m_lock(new ThreadLock())
+ , m_fileLock(new FileLock(m_metaFile->getFd(), (mode & MMKV_ASHMEM)))
+ , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
+ , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
+ , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0 || (mode & CONTEXT_MODE_MULTI_PROCESS) != 0) {
+ m_actualSize = 0;
+ m_output = nullptr;
+
+# ifndef MMKV_DISABLE_CRYPT
+ if (cryptKey && cryptKey->length() > 0) {
+ m_dicCrypt = new MMKVMapCrypt();
+ m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
+ } else
+# endif
+ {
+ m_dic = new MMKVMap();
+ }
+
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ m_crcDigest = 0;
+
+ m_sharedProcessLock->m_enable = m_isInterProcess;
+ m_exclusiveProcessLock->m_enable = m_isInterProcess;
+
+ // sensitive zone
+ {
+ SCOPED_LOCK(m_sharedProcessLock);
+ loadFromFile();
+ }
+}
+
+MMKV::MMKV(const string &mmapID, int ashmemFD, int ashmemMetaFD, string *cryptKey)
+ : m_mmapID(mmapID)
+ , m_path(mappedKVPathWithID(m_mmapID, MMKV_ASHMEM, nullptr))
+ , m_crcPath(crcPathWithID(m_mmapID, MMKV_ASHMEM, nullptr))
+ , m_dic(nullptr)
+ , m_dicCrypt(nullptr)
+ , m_file(new MemoryFile(ashmemFD))
+ , m_metaFile(new MemoryFile(ashmemMetaFD))
+ , m_metaInfo(new MMKVMetaInfo())
+ , m_crypter(nullptr)
+ , m_lock(new ThreadLock())
+ , m_fileLock(new FileLock(m_metaFile->getFd(), true))
+ , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
+ , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
+ , m_isInterProcess(true) {
+
+ m_actualSize = 0;
+ m_output = nullptr;
+
+# ifndef MMKV_DISABLE_CRYPT
+ if (cryptKey && cryptKey->length() > 0) {
+ m_dicCrypt = new MMKVMapCrypt();
+ m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
+ } else
+# endif
+ {
+ m_dic = new MMKVMap();
+ }
+
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ m_crcDigest = 0;
+
+ m_sharedProcessLock->m_enable = m_isInterProcess;
+ m_exclusiveProcessLock->m_enable = m_isInterProcess;
+
+ // sensitive zone
+ {
+ SCOPED_LOCK(m_sharedProcessLock);
+ loadFromFile();
+ }
+}
+
+MMKV *MMKV::mmkvWithID(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath) {
+
+ if (mmapID.empty()) {
+ return nullptr;
+ }
+ SCOPED_LOCK(g_instanceLock);
+
+ auto mmapKey = mmapedKVKey(mmapID, rootPath);
+ auto itr = g_instanceDic->find(mmapKey);
+ if (itr != g_instanceDic->end()) {
+ MMKV *kv = itr->second;
+ return kv;
+ }
+ if (rootPath) {
+ if (!isFileExist(*rootPath)) {
+ if (!mkPath(*rootPath)) {
+ return nullptr;
+ }
+ }
+ MMKVInfo("prepare to load %s (id %s) from rootPath %s", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());
+ }
+ auto kv = new MMKV(mmapID, size, mode, cryptKey, rootPath);
+ (*g_instanceDic)[mmapKey] = kv;
+ return kv;
+}
+
+MMKV *MMKV::mmkvWithAshmemFD(const string &mmapID, int fd, int metaFD, string *cryptKey) {
+
+ if (fd < 0) {
+ return nullptr;
+ }
+ SCOPED_LOCK(g_instanceLock);
+
+ auto itr = g_instanceDic->find(mmapID);
+ if (itr != g_instanceDic->end()) {
+ MMKV *kv = itr->second;
+# ifndef MMKV_DISABLE_CRYPT
+ kv->checkReSetCryptKey(fd, metaFD, cryptKey);
+# endif
+ return kv;
+ }
+ auto kv = new MMKV(mmapID, fd, metaFD, cryptKey);
+ (*g_instanceDic)[mmapID] = kv;
+ return kv;
+}
+
+int MMKV::ashmemFD() {
+ return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_file->getFd() : -1;
+}
+
+int MMKV::ashmemMetaFD() {
+ return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_metaFile->getFd() : -1;
+}
+
+# ifndef MMKV_DISABLE_CRYPT
+void MMKV::checkReSetCryptKey(int fd, int metaFD, string *cryptKey) {
+ SCOPED_LOCK(m_lock);
+
+ checkReSetCryptKey(cryptKey);
+
+ if (m_file->m_fileType & MMFILE_TYPE_ASHMEM) {
+ if (m_file->getFd() != fd) {
+ ::close(fd);
+ }
+ if (m_metaFile->getFd() != metaFD) {
+ ::close(metaFD);
+ }
+ }
+}
+# endif // MMKV_DISABLE_CRYPT
+
+#endif // MMKV_ANDROID
diff --git a/ios/Pods/MMKVCore/Core/MMKV_IO.cpp b/ios/Pods/MMKVCore/Core/MMKV_IO.cpp
new file mode 100644
index 00000000..7cf9d58d
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_IO.cpp
@@ -0,0 +1,1114 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "MMKV_IO.h"
+#include "CodedInputData.h"
+#include "CodedOutputData.h"
+#include "InterProcessLock.h"
+#include "MMBuffer.h"
+#include "MMKVLog.h"
+#include "MMKVMetaInfo.hpp"
+#include "MemoryFile.h"
+#include "MiniPBCoder.h"
+#include "PBUtility.h"
+#include "ScopedLock.hpp"
+#include "ThreadLock.h"
+#include "aes/AESCrypt.h"
+#include "aes/openssl/openssl_aes.h"
+#include "aes/openssl/openssl_md5.h"
+#include "crc32/Checksum.h"
+#include
+#include
+
+#ifdef MMKV_IOS
+# include "MMKV_OSX.h"
+#endif
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+using namespace mmkv;
+using KVHolderRet_t = std::pair;
+
+constexpr uint32_t Fixed32Size = pbFixed32Size();
+
+MMKV_NAMESPACE_BEGIN
+
+void MMKV::loadFromFile() {
+ if (m_metaFile->isFileValid()) {
+ m_metaInfo->read(m_metaFile->getMemory());
+ }
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ if (m_metaInfo->m_version >= MMKVVersionRandomIV) {
+ m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));
+ }
+ }
+#endif
+ if (!m_file->isFileValid()) {
+ m_file->reloadFromFile();
+ }
+ if (!m_file->isFileValid()) {
+ MMKVError("file [%s] not valid", m_path.c_str());
+ } else {
+ // error checking
+ bool loadFromFile = false, needFullWriteback = false;
+ checkDataValid(loadFromFile, needFullWriteback);
+ MMKVInfo("loading [%s] with %zu actual size, file size %zu, InterProcess %d, meta info "
+ "version:%u",
+ m_mmapID.c_str(), m_actualSize, m_file->getFileSize(), m_isInterProcess, m_metaInfo->m_version);
+ auto ptr = (uint8_t *) m_file->getMemory();
+ // loading
+ if (loadFromFile && m_actualSize > 0) {
+ MMKVInfo("loading [%s] with crc %u sequence %u version %u", m_mmapID.c_str(), m_metaInfo->m_crcDigest,
+ m_metaInfo->m_sequence, m_metaInfo->m_version);
+ MMBuffer inputBuffer(ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);
+ if (m_crypter) {
+ clearDictionary(m_dicCrypt);
+ } else {
+ clearDictionary(m_dic);
+ }
+ if (needFullWriteback) {
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ MiniPBCoder::greedyDecodeMap(*m_dicCrypt, inputBuffer, m_crypter);
+ } else
+#endif
+ {
+ MiniPBCoder::greedyDecodeMap(*m_dic, inputBuffer);
+ }
+ } else {
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ MiniPBCoder::decodeMap(*m_dicCrypt, inputBuffer, m_crypter);
+ } else
+#endif
+ {
+ MiniPBCoder::decodeMap(*m_dic, inputBuffer);
+ }
+ }
+ m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
+ m_output->seek(m_actualSize);
+ if (needFullWriteback) {
+ fullWriteback();
+ }
+ } else {
+ // file not valid or empty, discard everything
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
+ if (m_actualSize > 0) {
+ writeActualSize(0, 0, nullptr, IncreaseSequence);
+ sync(MMKV_SYNC);
+ } else {
+ writeActualSize(0, 0, nullptr, KeepSequence);
+ }
+ }
+ if (m_crypter) {
+ MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dicCrypt->size());
+ } else {
+ MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dic->size());
+ }
+ }
+
+ m_needLoadFromFile = false;
+}
+
+// read from last m_position
+void MMKV::partialLoadFromFile() {
+ m_metaInfo->read(m_metaFile->getMemory());
+
+ size_t oldActualSize = m_actualSize;
+ m_actualSize = readActualSize();
+ auto fileSize = m_file->getFileSize();
+ MMKVDebug("loading [%s] with file size %zu, oldActualSize %zu, newActualSize %zu", m_mmapID.c_str(), fileSize,
+ oldActualSize, m_actualSize);
+
+ if (m_actualSize > 0) {
+ if (m_actualSize < fileSize && m_actualSize + Fixed32Size <= fileSize) {
+ if (m_actualSize > oldActualSize) {
+ auto position = oldActualSize;
+ size_t addedSize = m_actualSize - position;
+ auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;
+ // incremental update crc digest
+ m_crcDigest = (uint32_t) CRC32(m_crcDigest, basePtr + position, addedSize);
+ if (m_crcDigest == m_metaInfo->m_crcDigest) {
+ MMBuffer inputBuffer(basePtr, m_actualSize, MMBufferNoCopy);
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ MiniPBCoder::greedyDecodeMap(*m_dicCrypt, inputBuffer, m_crypter, position);
+ } else
+#endif
+ {
+ MiniPBCoder::greedyDecodeMap(*m_dic, inputBuffer, position);
+ }
+ m_output->seek(addedSize);
+ m_hasFullWriteback = false;
+
+ if (m_crypter) {
+ MMKVDebug("partial loaded [%s] with %zu values", m_mmapID.c_str(), m_dicCrypt->size());
+ } else {
+ MMKVDebug("partial loaded [%s] with %zu values", m_mmapID.c_str(), m_dic->size());
+ }
+ return;
+ } else {
+ MMKVError("m_crcDigest[%u] != m_metaInfo->m_crcDigest[%u]", m_crcDigest, m_metaInfo->m_crcDigest);
+ }
+ }
+ }
+ }
+ // something is wrong, do a full load
+ clearMemoryCache();
+ loadFromFile();
+}
+
+void MMKV::checkDataValid(bool &loadFromFile, bool &needFullWriteback) {
+ // try auto recover from last confirmed location
+ auto fileSize = m_file->getFileSize();
+ auto checkLastConfirmedInfo = [&] {
+ if (m_metaInfo->m_version >= MMKVVersionActualSize) {
+ // downgrade & upgrade support
+ uint32_t oldStyleActualSize = 0;
+ memcpy(&oldStyleActualSize, m_file->getMemory(), Fixed32Size);
+ if (oldStyleActualSize != m_actualSize) {
+ MMKVWarning("oldStyleActualSize %u not equal to meta actual size %lu", oldStyleActualSize,
+ m_actualSize);
+ if (oldStyleActualSize < fileSize && (oldStyleActualSize + Fixed32Size) <= fileSize) {
+ if (checkFileCRCValid(oldStyleActualSize, m_metaInfo->m_crcDigest)) {
+ MMKVInfo("looks like [%s] been downgrade & upgrade again", m_mmapID.c_str());
+ loadFromFile = true;
+ writeActualSize(oldStyleActualSize, m_metaInfo->m_crcDigest, nullptr, KeepSequence);
+ return;
+ }
+ } else {
+ MMKVWarning("oldStyleActualSize %u greater than file size %lu", oldStyleActualSize, fileSize);
+ }
+ }
+
+ auto lastActualSize = m_metaInfo->m_lastConfirmedMetaInfo.lastActualSize;
+ if (lastActualSize < fileSize && (lastActualSize + Fixed32Size) <= fileSize) {
+ auto lastCRCDigest = m_metaInfo->m_lastConfirmedMetaInfo.lastCRCDigest;
+ if (checkFileCRCValid(lastActualSize, lastCRCDigest)) {
+ loadFromFile = true;
+ writeActualSize(lastActualSize, lastCRCDigest, nullptr, KeepSequence);
+ } else {
+ MMKVError("check [%s] error: lastActualSize %u, lastActualCRC %u", m_mmapID.c_str(), lastActualSize,
+ lastCRCDigest);
+ }
+ } else {
+ MMKVError("check [%s] error: lastActualSize %u, file size is %u", m_mmapID.c_str(), lastActualSize,
+ fileSize);
+ }
+ }
+ };
+
+ m_actualSize = readActualSize();
+
+ if (m_actualSize < fileSize && (m_actualSize + Fixed32Size) <= fileSize) {
+ if (checkFileCRCValid(m_actualSize, m_metaInfo->m_crcDigest)) {
+ loadFromFile = true;
+ } else {
+ checkLastConfirmedInfo();
+
+ if (!loadFromFile) {
+ auto strategic = onMMKVCRCCheckFail(m_mmapID);
+ if (strategic == OnErrorRecover) {
+ loadFromFile = true;
+ needFullWriteback = true;
+ }
+ MMKVInfo("recover strategic for [%s] is %d", m_mmapID.c_str(), strategic);
+ }
+ }
+ } else {
+ MMKVError("check [%s] error: %zu size in total, file size is %zu", m_mmapID.c_str(), m_actualSize, fileSize);
+
+ checkLastConfirmedInfo();
+
+ if (!loadFromFile) {
+ auto strategic = onMMKVFileLengthError(m_mmapID);
+ if (strategic == OnErrorRecover) {
+ // make sure we don't over read the file
+ m_actualSize = fileSize - Fixed32Size;
+ loadFromFile = true;
+ needFullWriteback = true;
+ }
+ MMKVInfo("recover strategic for [%s] is %d", m_mmapID.c_str(), strategic);
+ }
+ }
+}
+
+void MMKV::checkLoadData() {
+ if (m_needLoadFromFile) {
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ m_needLoadFromFile = false;
+ loadFromFile();
+ return;
+ }
+ if (!m_isInterProcess) {
+ return;
+ }
+
+ if (!m_metaFile->isFileValid()) {
+ return;
+ }
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ MMKVMetaInfo metaInfo;
+ metaInfo.read(m_metaFile->getMemory());
+ if (m_metaInfo->m_sequence != metaInfo.m_sequence) {
+ MMKVInfo("[%s] oldSeq %u, newSeq %u", m_mmapID.c_str(), m_metaInfo->m_sequence, metaInfo.m_sequence);
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ clearMemoryCache();
+ loadFromFile();
+ notifyContentChanged();
+ } else if (m_metaInfo->m_crcDigest != metaInfo.m_crcDigest) {
+ MMKVDebug("[%s] oldCrc %u, newCrc %u, new actualSize %u", m_mmapID.c_str(), m_metaInfo->m_crcDigest,
+ metaInfo.m_crcDigest, metaInfo.m_actualSize);
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ size_t fileSize = m_file->getActualFileSize();
+ if (m_file->getFileSize() != fileSize) {
+ MMKVInfo("file size has changed [%s] from %zu to %zu", m_mmapID.c_str(), m_file->getFileSize(), fileSize);
+ clearMemoryCache();
+ loadFromFile();
+ } else {
+ partialLoadFromFile();
+ }
+ notifyContentChanged();
+ }
+}
+
+constexpr uint32_t ItemSizeHolder = 0x00ffffff;
+constexpr uint32_t ItemSizeHolderSize = 4;
+
+static pair prepareEncode(const mmkv::MMKVMap &dic) {
+ // make some room for placeholder
+ size_t totalSize = ItemSizeHolderSize;
+ for (auto &itr : dic) {
+ auto &kvHolder = itr.second;
+ totalSize += kvHolder.computedKVSize + kvHolder.valueSize;
+ }
+ return make_pair(MMBuffer(), totalSize);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+static pair prepareEncode(const mmkv::MMKVMapCrypt &dic) {
+ MMKVVector vec;
+ size_t totalSize = 0;
+ // make some room for placeholder
+ uint32_t smallestOffet = 5 + 1; // 5 is the largest size needed to encode varint32
+ for (auto &itr : dic) {
+ auto &kvHolder = itr.second;
+ if (kvHolder.type == KeyValueHolderType_Offset) {
+ totalSize += kvHolder.pbKeyValueSize + kvHolder.keySize + kvHolder.valueSize;
+ smallestOffet = min(smallestOffet, kvHolder.offset);
+ } else {
+ vec.emplace_back(itr.first, kvHolder.toMMBuffer(nullptr, nullptr));
+ }
+ }
+ if (smallestOffet > 5) {
+ smallestOffet = ItemSizeHolderSize;
+ }
+ totalSize += smallestOffet;
+ if (vec.empty()) {
+ return make_pair(MMBuffer(), totalSize);
+ }
+ auto buffer = MiniPBCoder::encodeDataWithObject(vec);
+ // skip the pb size of buffer
+ auto sizeOfMap = CodedInputData(buffer.getPtr(), buffer.length()).readUInt32();
+ totalSize += sizeOfMap;
+ return make_pair(move(buffer), totalSize);
+}
+#endif
+
+// since we use append mode, when -[setData: forKey:] many times, space may not be enough
+// try a full rewrite to make space
+bool MMKV::ensureMemorySize(size_t newSize) {
+ if (!isFileValid()) {
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
+ return false;
+ }
+
+ if (newSize >= m_output->spaceLeft() || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {
+ // try a full rewrite to make space
+ auto fileSize = m_file->getFileSize();
+ auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);
+ auto sizeOfDic = preparedData.second;
+ size_t lenNeeded = sizeOfDic + Fixed32Size + newSize;
+ size_t dicCount = m_crypter ? m_dicCrypt->size() : m_dic->size();
+ size_t avgItemSize = lenNeeded / std::max(1, dicCount);
+ size_t futureUsage = avgItemSize * std::max(8, (dicCount + 1) / 2);
+ // 1. no space for a full rewrite, double it
+ // 2. or space is not large enough for future usage, double it to avoid frequently full rewrite
+ if (lenNeeded >= fileSize || (lenNeeded + futureUsage) >= fileSize) {
+ size_t oldSize = fileSize;
+ do {
+ fileSize *= 2;
+ } while (lenNeeded + futureUsage >= fileSize);
+ MMKVInfo("extending [%s] file size from %zu to %zu, incoming size:%zu, future usage:%zu", m_mmapID.c_str(),
+ oldSize, fileSize, newSize, futureUsage);
+
+ // if we can't extend size, rollback to old state
+ if (!m_file->truncate(fileSize)) {
+ return false;
+ }
+
+ // check if we fail to make more space
+ if (!isFileValid()) {
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
+ return false;
+ }
+ }
+ return doFullWriteBack(move(preparedData), nullptr);
+ }
+ return true;
+}
+
+size_t MMKV::readActualSize() {
+ MMKV_ASSERT(m_file->getMemory());
+ MMKV_ASSERT(m_metaFile->isFileValid());
+
+ uint32_t actualSize = 0;
+ memcpy(&actualSize, m_file->getMemory(), Fixed32Size);
+
+ if (m_metaInfo->m_version >= MMKVVersionActualSize) {
+ if (m_metaInfo->m_actualSize != actualSize) {
+ MMKVWarning("[%s] actual size %u, meta actual size %u", m_mmapID.c_str(), actualSize,
+ m_metaInfo->m_actualSize);
+ }
+ return m_metaInfo->m_actualSize;
+ } else {
+ return actualSize;
+ }
+}
+
+void MMKV::oldStyleWriteActualSize(size_t actualSize) {
+ MMKV_ASSERT(m_file->getMemory());
+
+ m_actualSize = actualSize;
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(m_file->getMemory(), Fixed32Size);
+ if (!ret.first) {
+ return;
+ }
+#endif
+ memcpy(m_file->getMemory(), &actualSize, Fixed32Size);
+}
+
+bool MMKV::writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool increaseSequence) {
+ // backward compatibility
+ oldStyleWriteActualSize(size);
+
+ if (!m_metaFile->isFileValid()) {
+ return false;
+ }
+
+ bool needsFullWrite = false;
+ m_actualSize = size;
+ m_metaInfo->m_actualSize = static_cast(size);
+ m_crcDigest = crcDigest;
+ m_metaInfo->m_crcDigest = crcDigest;
+ if (m_metaInfo->m_version < MMKVVersionSequence) {
+ m_metaInfo->m_version = MMKVVersionSequence;
+ needsFullWrite = true;
+ }
+#ifndef MMKV_DISABLE_CRYPT
+ if (unlikely(iv)) {
+ memcpy(m_metaInfo->m_vector, iv, sizeof(m_metaInfo->m_vector));
+ if (m_metaInfo->m_version < MMKVVersionRandomIV) {
+ m_metaInfo->m_version = MMKVVersionRandomIV;
+ }
+ needsFullWrite = true;
+ }
+#endif
+ if (unlikely(increaseSequence)) {
+ m_metaInfo->m_sequence++;
+ m_metaInfo->m_lastConfirmedMetaInfo.lastActualSize = static_cast(size);
+ m_metaInfo->m_lastConfirmedMetaInfo.lastCRCDigest = crcDigest;
+ if (m_metaInfo->m_version < MMKVVersionActualSize) {
+ m_metaInfo->m_version = MMKVVersionActualSize;
+ }
+ needsFullWrite = true;
+ }
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(m_metaFile->getMemory(), sizeof(MMKVMetaInfo));
+ if (!ret.first) {
+ return false;
+ }
+#endif
+ if (unlikely(needsFullWrite)) {
+ m_metaInfo->write(m_metaFile->getMemory());
+ } else {
+ m_metaInfo->writeCRCAndActualSizeOnly(m_metaFile->getMemory());
+ }
+ return true;
+}
+
+MMBuffer MMKV::getDataForKey(MMKVKey_t key) {
+ checkLoadData();
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
+ return itr->second.toMMBuffer(basePtr, m_crypter);
+ }
+ } else
+#endif
+ {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
+ return itr->second.toMMBuffer(basePtr);
+ }
+ }
+ MMBuffer nan;
+ return nan;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+// for Apple watch simulator
+# if defined(TARGET_OS_SIMULATOR) && defined(TARGET_CPU_X86)
+static AESCryptStatus t_status;
+# else
+thread_local AESCryptStatus t_status;
+# endif
+#endif // MMKV_DISABLE_CRYPT
+
+bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
+ if ((!isDataHolder && data.length() == 0) || isKeyEmpty(key)) {
+ return false;
+ }
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ if (isDataHolder) {
+ auto sizeNeededForData = pbRawVarint32Size((uint32_t) data.length()) + data.length();
+ if (!KeyValueHolderCrypt::isValueStoredAsOffset(sizeNeededForData)) {
+ data = MiniPBCoder::encodeDataWithObject(data);
+ isDataHolder = false;
+ }
+ }
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+# ifdef MMKV_APPLE
+ auto ret = appendDataWithKey(data, key, itr->second, isDataHolder);
+# else
+ auto ret = appendDataWithKey(data, key, isDataHolder);
+# endif
+ if (!ret.first) {
+ return false;
+ }
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {
+ KeyValueHolderCrypt kvHolder(ret.second.keySize, ret.second.valueSize, ret.second.offset);
+ memcpy(&kvHolder.cryptStatus, &t_status, sizeof(t_status));
+ itr->second = move(kvHolder);
+ } else {
+ itr->second = KeyValueHolderCrypt(move(data));
+ }
+ } else {
+ auto ret = appendDataWithKey(data, key, isDataHolder);
+ if (!ret.first) {
+ return false;
+ }
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {
+ auto r = m_dicCrypt->emplace(
+ key, KeyValueHolderCrypt(ret.second.keySize, ret.second.valueSize, ret.second.offset));
+ if (r.second) {
+ memcpy(&(r.first->second.cryptStatus), &t_status, sizeof(t_status));
+ }
+ } else {
+ m_dicCrypt->emplace(key, KeyValueHolderCrypt(move(data)));
+ }
+ }
+ } else
+#endif // MMKV_DISABLE_CRYPT
+ {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ auto ret = appendDataWithKey(data, itr->second, isDataHolder);
+ if (!ret.first) {
+ return false;
+ }
+ itr->second = std::move(ret.second);
+ } else {
+ auto ret = appendDataWithKey(data, key, isDataHolder);
+ if (!ret.first) {
+ return false;
+ }
+ m_dic->emplace(key, std::move(ret.second));
+ }
+ }
+ m_hasFullWriteback = false;
+#ifdef MMKV_APPLE
+ [key retain];
+#endif
+ return true;
+}
+
+bool MMKV::removeDataForKey(MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ m_hasFullWriteback = false;
+ static MMBuffer nan;
+# ifdef MMKV_APPLE
+ auto ret = appendDataWithKey(nan, key, itr->second);
+# else
+ auto ret = appendDataWithKey(nan, key);
+# endif
+ if (ret.first) {
+# ifdef MMKV_APPLE
+ [itr->first release];
+# endif
+ m_dicCrypt->erase(itr);
+ }
+ return ret.first;
+ }
+ } else
+#endif // MMKV_DISABLE_CRYPT
+ {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ m_hasFullWriteback = false;
+ static MMBuffer nan;
+ auto ret = appendDataWithKey(nan, itr->second);
+ if (ret.first) {
+#ifdef MMKV_APPLE
+ [itr->first release];
+#endif
+ m_dic->erase(itr);
+ }
+ return ret.first;
+ }
+ }
+
+ return false;
+}
+
+KVHolderRet_t
+MMKV::doAppendDataWithKey(const MMBuffer &data, const MMBuffer &keyData, bool isDataHolder, uint32_t originKeyLength) {
+ auto isKeyEncoded = (originKeyLength < keyData.length());
+ auto keyLength = static_cast(keyData.length());
+ auto valueLength = static_cast(data.length());
+ if (isDataHolder) {
+ valueLength += pbRawVarint32Size(valueLength);
+ }
+ // size needed to encode the key
+ size_t size = isKeyEncoded ? keyLength : (keyLength + pbRawVarint32Size(keyLength));
+ // size needed to encode the value
+ size += valueLength + pbRawVarint32Size(valueLength);
+
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ bool hasEnoughSize = ensureMemorySize(size);
+ if (!hasEnoughSize || !isFileValid()) {
+ return make_pair(false, KeyValueHolder());
+ }
+
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(m_output->curWritePointer(), size);
+ if (!ret.first) {
+ return make_pair(false, KeyValueHolder());
+ }
+#endif
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(valueLength)) {
+ m_crypter->getCurStatus(t_status);
+ }
+ }
+#endif
+ try {
+ if (isKeyEncoded) {
+ m_output->writeRawData(keyData);
+ } else {
+ m_output->writeData(keyData);
+ }
+ if (isDataHolder) {
+ m_output->writeRawVarint32((int32_t) valueLength);
+ }
+ m_output->writeData(data); // note: write size of data
+ } catch (std::exception &e) {
+ MMKVError("%s", e.what());
+ return make_pair(false, KeyValueHolder());
+ }
+
+ auto offset = static_cast(m_actualSize);
+ auto ptr = (uint8_t *) m_file->getMemory() + Fixed32Size + m_actualSize;
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ m_crypter->encrypt(ptr, ptr, size);
+ }
+#endif
+ m_actualSize += size;
+ updateCRCDigest(ptr, size);
+
+ return make_pair(true, KeyValueHolder(originKeyLength, valueLength, offset));
+}
+
+KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, bool isDataHolder) {
+#ifdef MMKV_APPLE
+ auto oData = [key dataUsingEncoding:NSUTF8StringEncoding];
+ auto keyData = MMBuffer(oData, MMBufferNoCopy);
+#else
+ auto keyData = MMBuffer((void *) key.data(), key.size(), MMBufferNoCopy);
+#endif
+ return doAppendDataWithKey(data, keyData, isDataHolder, static_cast(keyData.length()));
+}
+
+KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, const KeyValueHolder &kvHolder, bool isDataHolder) {
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ uint32_t keyLength = kvHolder.keySize;
+ // size needed to encode the key
+ size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);
+
+ // ensureMemorySize() might change kvHolder.offset, so have to do it early
+ {
+ auto valueLength = static_cast(data.length());
+ if (isDataHolder) {
+ valueLength += pbRawVarint32Size(valueLength);
+ }
+ auto size = rawKeySize + valueLength + pbRawVarint32Size(valueLength);
+ bool hasEnoughSize = ensureMemorySize(size);
+ if (!hasEnoughSize) {
+ return make_pair(false, KeyValueHolder());
+ }
+ }
+ auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;
+ MMBuffer keyData(basePtr + kvHolder.offset, rawKeySize, MMBufferNoCopy);
+
+ return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);
+}
+
+bool MMKV::fullWriteback(AESCrypt *newCrypter) {
+ if (m_hasFullWriteback) {
+ return true;
+ }
+ if (m_needLoadFromFile) {
+ return true;
+ }
+ if (!isFileValid()) {
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
+ return false;
+ }
+
+ if (m_crypter ? m_dicCrypt->empty() : m_dic->empty()) {
+ clearAll();
+ return true;
+ }
+
+ auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);
+ auto sizeOfDic = preparedData.second;
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ if (sizeOfDic > 0) {
+ auto fileSize = m_file->getFileSize();
+ if (sizeOfDic + Fixed32Size <= fileSize) {
+ return doFullWriteBack(move(preparedData), newCrypter);
+ } else {
+ assert(0);
+ assert(newCrypter == nullptr);
+ // ensureMemorySize will extend file & full rewrite, no need to write back again
+ return ensureMemorySize(sizeOfDic + Fixed32Size - fileSize);
+ }
+ }
+ return false;
+}
+
+// we don't need to really serialize the dictionary, just reuse what's already in the file
+static void
+memmoveDictionary(MMKVMap &dic, CodedOutputData *output, uint8_t *ptr, AESCrypt *encrypter, size_t totalSize) {
+ auto originOutputPtr = output->curWritePointer();
+ // make space to hold the fake size of dictionary's serialization result
+ auto writePtr = originOutputPtr + ItemSizeHolderSize;
+ // reuse what's already in the file
+ if (!dic.empty()) {
+ // sort by offset
+ vector vec;
+ vec.reserve(dic.size());
+ for (auto &itr : dic) {
+ vec.push_back(&itr.second);
+ }
+ sort(vec.begin(), vec.end(), [](const auto &left, const auto &right) { return left->offset < right->offset; });
+
+ // merge nearby items to make memmove quicker
+ vector> dataSections; // pair(offset, size)
+ dataSections.emplace_back(vec.front()->offset, vec.front()->computedKVSize + vec.front()->valueSize);
+ for (size_t index = 1, total = vec.size(); index < total; index++) {
+ auto kvHolder = vec[index];
+ auto &lastSection = dataSections.back();
+ if (kvHolder->offset == lastSection.first + lastSection.second) {
+ lastSection.second += kvHolder->computedKVSize + kvHolder->valueSize;
+ } else {
+ dataSections.emplace_back(kvHolder->offset, kvHolder->computedKVSize + kvHolder->valueSize);
+ }
+ }
+ // do the move
+ auto basePtr = ptr + Fixed32Size;
+ for (auto §ion : dataSections) {
+ // memmove() should handle this well: src == dst
+ memmove(writePtr, basePtr + section.first, section.second);
+ writePtr += section.second;
+ }
+ // update offset
+ if (!encrypter) {
+ auto offset = ItemSizeHolderSize;
+ for (auto kvHolder : vec) {
+ kvHolder->offset = offset;
+ offset += kvHolder->computedKVSize + kvHolder->valueSize;
+ }
+ }
+ }
+ // hold the fake size of dictionary's serialization result
+ output->writeRawVarint32(ItemSizeHolder);
+ auto writtenSize = static_cast(writePtr - originOutputPtr);
+#ifndef MMKV_DISABLE_CRYPT
+ if (encrypter) {
+ encrypter->encrypt(originOutputPtr, originOutputPtr, writtenSize);
+ }
+#endif
+ assert(writtenSize == totalSize);
+ output->seek(writtenSize - ItemSizeHolderSize);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+static void memmoveDictionary(MMKVMapCrypt &dic,
+ CodedOutputData *output,
+ uint8_t *ptr,
+ AESCrypt *decrypter,
+ AESCrypt *encrypter,
+ pair &preparedData) {
+ // reuse what's already in the file
+ vector vec;
+ if (!dic.empty()) {
+ // sort by offset
+ vec.reserve(dic.size());
+ for (auto &itr : dic) {
+ if (itr.second.type == KeyValueHolderType_Offset) {
+ vec.push_back(&itr.second);
+ }
+ }
+ sort(vec.begin(), vec.end(), [](auto left, auto right) { return left->offset < right->offset; });
+ }
+ auto sizeHolder = ItemSizeHolder, sizeHolderSize = ItemSizeHolderSize;
+ if (!vec.empty()) {
+ auto smallestOffset = vec.front()->offset;
+ if (smallestOffset != ItemSizeHolderSize && smallestOffset <= 5) {
+ sizeHolderSize = smallestOffset;
+ assert(sizeHolderSize != 0);
+ static const uint32_t ItemSizeHolders[] = {0, 0x0f, 0xff, 0xffff, 0xffffff, 0xffffffff};
+ sizeHolder = ItemSizeHolders[sizeHolderSize];
+ }
+ }
+ output->writeRawVarint32(static_cast(sizeHolder));
+ auto writePtr = output->curWritePointer();
+ if (encrypter) {
+ encrypter->encrypt(writePtr - sizeHolderSize, writePtr - sizeHolderSize, sizeHolderSize);
+ }
+ if (!vec.empty()) {
+ // merge nearby items to make memmove quicker
+ vector> dataSections; // pair(offset, size)
+ dataSections.push_back(vec.front()->toTuple());
+ for (size_t index = 1, total = vec.size(); index < total; index++) {
+ auto kvHolder = vec[index];
+ auto &lastSection = dataSections.back();
+ if (kvHolder->offset == get<0>(lastSection) + get<1>(lastSection)) {
+ get<1>(lastSection) += kvHolder->pbKeyValueSize + kvHolder->keySize + kvHolder->valueSize;
+ } else {
+ dataSections.push_back(kvHolder->toTuple());
+ }
+ }
+ // do the move
+ auto basePtr = ptr + Fixed32Size;
+ for (auto §ion : dataSections) {
+ auto crypter = decrypter->cloneWithStatus(*get<2>(section));
+ crypter.decrypt(basePtr + get<0>(section), writePtr, get<1>(section));
+ writePtr += get<1>(section);
+ }
+ // update offset & AESCryptStatus
+ if (encrypter) {
+ auto offset = sizeHolderSize;
+ for (auto kvHolder : vec) {
+ kvHolder->offset = offset;
+ auto size = kvHolder->pbKeyValueSize + kvHolder->keySize + kvHolder->valueSize;
+ encrypter->getCurStatus(kvHolder->cryptStatus);
+ encrypter->encrypt(basePtr + offset, basePtr + offset, size);
+ offset += size;
+ }
+ }
+ }
+ auto &data = preparedData.first;
+ if (data.length() > 0) {
+ auto dataSize = CodedInputData(data.getPtr(), data.length()).readUInt32();
+ if (dataSize > 0) {
+ auto dataPtr = (uint8_t *) data.getPtr() + pbRawVarint32Size(dataSize);
+ if (encrypter) {
+ encrypter->encrypt(dataPtr, writePtr, dataSize);
+ } else {
+ memcpy(writePtr, dataPtr, dataSize);
+ }
+ writePtr += dataSize;
+ }
+ }
+ auto writtenSize = static_cast(writePtr - output->curWritePointer());
+ assert(writtenSize + sizeHolderSize == preparedData.second);
+ output->seek(writtenSize);
+}
+
+# define InvalidCryptPtr ((AESCrypt *) (void *) (1))
+
+#endif // MMKV_DISABLE_CRYPT
+
+bool MMKV::doFullWriteBack(pair preparedData, AESCrypt *newCrypter) {
+ auto ptr = (uint8_t *) m_file->getMemory();
+ auto totalSize = preparedData.second;
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(ptr + Fixed32Size, totalSize);
+ if (!ret.first) {
+ return false;
+ }
+#endif
+
+#ifndef MMKV_DISABLE_CRYPT
+ uint8_t newIV[AES_KEY_LEN];
+ auto decrypter = m_crypter;
+ auto encrypter = (newCrypter == InvalidCryptPtr) ? nullptr : (newCrypter ? newCrypter : m_crypter);
+ if (encrypter) {
+ AESCrypt::fillRandomIV(newIV);
+ encrypter->resetIV(newIV, sizeof(newIV));
+ }
+#endif
+
+ delete m_output;
+ m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ memmoveDictionary(*m_dicCrypt, m_output, ptr, decrypter, encrypter, preparedData);
+ } else {
+#else
+ {
+ auto encrypter = m_crypter;
+#endif
+ memmoveDictionary(*m_dic, m_output, ptr, encrypter, totalSize);
+ }
+
+ m_actualSize = totalSize;
+#ifndef MMKV_DISABLE_CRYPT
+ if (encrypter) {
+ recaculateCRCDigestWithIV(newIV);
+ } else
+#endif
+ {
+ recaculateCRCDigestWithIV(nullptr);
+ }
+ m_hasFullWriteback = true;
+ // make sure lastConfirmedMetaInfo is saved
+ sync(MMKV_SYNC);
+ return true;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+bool MMKV::reKey(const string &cryptKey) {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ bool ret = false;
+ if (m_crypter) {
+ if (cryptKey.length() > 0) {
+ string oldKey = this->cryptKey();
+ if (cryptKey == oldKey) {
+ return true;
+ } else {
+ // change encryption key
+ MMKVInfo("reKey with new aes key");
+ auto newCrypt = new AESCrypt(cryptKey.data(), cryptKey.length());
+ ret = fullWriteback(newCrypt);
+ if (ret) {
+ delete m_crypter;
+ m_crypter = newCrypt;
+ }
+ }
+ } else {
+ // decryption to plain text
+ MMKVInfo("reKey to no aes key");
+ ret = fullWriteback(InvalidCryptPtr);
+ if (ret) {
+ delete m_crypter;
+ m_crypter = nullptr;
+ if (!m_dic) {
+ m_dic = new MMKVMap();
+ }
+ }
+ }
+ } else {
+ if (cryptKey.length() > 0) {
+ // transform plain text to encrypted text
+ MMKVInfo("reKey to a aes key");
+ auto newCrypt = new AESCrypt(cryptKey.data(), cryptKey.length());
+ ret = fullWriteback(newCrypt);
+ if (ret) {
+ m_crypter = newCrypt;
+ if (!m_dicCrypt) {
+ m_dicCrypt = new MMKVMapCrypt();
+ }
+ }
+ } else {
+ return true;
+ }
+ }
+ // m_dic or m_dicCrypt is not valid after reKey
+ if (ret) {
+ clearMemoryCache();
+ }
+ return ret;
+}
+#endif
+
+void MMKV::trim() {
+ SCOPED_LOCK(m_lock);
+ MMKVInfo("prepare to trim %s", m_mmapID.c_str());
+
+ checkLoadData();
+
+ if (m_actualSize == 0) {
+ clearAll();
+ return;
+ } else if (m_file->getFileSize() <= DEFAULT_MMAP_SIZE) {
+ return;
+ }
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ fullWriteback();
+ auto oldSize = m_file->getFileSize();
+ auto fileSize = oldSize;
+ while (fileSize > (m_actualSize + Fixed32Size) * 2) {
+ fileSize /= 2;
+ }
+ fileSize = std::max(fileSize, DEFAULT_MMAP_SIZE);
+ if (oldSize == fileSize) {
+ MMKVInfo("there's no need to trim %s with size %zu, actualSize %zu", m_mmapID.c_str(), fileSize, m_actualSize);
+ return;
+ }
+
+ MMKVInfo("trimming %s from %zu to %zu, actualSize %zu", m_mmapID.c_str(), oldSize, fileSize, m_actualSize);
+
+ if (!m_file->truncate(fileSize)) {
+ return;
+ }
+ fileSize = m_file->getFileSize();
+ auto ptr = (uint8_t *) m_file->getMemory();
+ delete m_output;
+ m_output = new CodedOutputData(ptr + pbFixed32Size(), fileSize - Fixed32Size);
+ m_output->seek(m_actualSize);
+
+ MMKVInfo("finish trim %s from %zu to %zu", m_mmapID.c_str(), oldSize, fileSize);
+}
+
+void MMKV::clearAll() {
+ MMKVInfo("cleaning all key-values from [%s]", m_mmapID.c_str());
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ if (m_needLoadFromFile) {
+ m_file->reloadFromFile();
+ }
+
+ if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
+ MMKVInfo("nothing to clear for [%s]", m_mmapID.c_str());
+ return;
+ }
+ m_file->truncate(DEFAULT_MMAP_SIZE);
+
+#ifndef MMKV_DISABLE_CRYPT
+ uint8_t newIV[AES_KEY_LEN];
+ AESCrypt::fillRandomIV(newIV);
+ if (m_crypter) {
+ m_crypter->resetIV(newIV, sizeof(newIV));
+ }
+ writeActualSize(0, 0, newIV, IncreaseSequence);
+#else
+ writeActualSize(0, 0, nullptr, IncreaseSequence);
+#endif
+ m_metaFile->msync(MMKV_SYNC);
+
+ clearMemoryCache();
+ loadFromFile();
+}
+
+bool MMKV::isFileValid(const string &mmapID, MMKVPath_t *relatePath) {
+ MMKVPath_t kvPath = mappedKVPathWithID(mmapID, MMKV_SINGLE_PROCESS, relatePath);
+ if (!isFileExist(kvPath)) {
+ return true;
+ }
+
+ MMKVPath_t crcPath = crcPathWithID(mmapID, MMKV_SINGLE_PROCESS, relatePath);
+ if (!isFileExist(crcPath)) {
+ return false;
+ }
+
+ uint32_t crcFile = 0;
+ MMBuffer *data = readWholeFile(crcPath);
+ if (data) {
+ if (data->getPtr()) {
+ MMKVMetaInfo metaInfo;
+ metaInfo.read(data->getPtr());
+ crcFile = metaInfo.m_crcDigest;
+ }
+ delete data;
+ } else {
+ return false;
+ }
+
+ uint32_t crcDigest = 0;
+ MMBuffer *fileData = readWholeFile(kvPath);
+ if (fileData) {
+ if (fileData->getPtr() && (fileData->length() >= Fixed32Size)) {
+ uint32_t actualSize = 0;
+ memcpy(&actualSize, fileData->getPtr(), Fixed32Size);
+ if (actualSize > (fileData->length() - Fixed32Size)) {
+ delete fileData;
+ return false;
+ }
+
+ crcDigest = (uint32_t) CRC32(0, (const uint8_t *) fileData->getPtr() + Fixed32Size, (uint32_t) actualSize);
+ }
+ delete fileData;
+ return crcFile == crcDigest;
+ } else {
+ return false;
+ }
+}
+
+MMKV_NAMESPACE_END
diff --git a/ios/Pods/MMKVCore/Core/MMKV_IO.h b/ios/Pods/MMKVCore/Core/MMKV_IO.h
new file mode 100644
index 00000000..4b2d723d
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_IO.h
@@ -0,0 +1,57 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef MMKV_IO_h
+#define MMKV_IO_h
+#ifdef __cplusplus
+
+#include "MMKV.h"
+
+MMKV_NAMESPACE_BEGIN
+
+std::string mmapedKVKey(const std::string &mmapID, MMKVPath_t *rootPath = nullptr);
+MMKVPath_t mappedKVPathWithID(const std::string &mmapID, MMKVMode mode, MMKVPath_t *rootPath);
+MMKVPath_t crcPathWithID(const std::string &mmapID, MMKVMode mode, MMKVPath_t *rootPath);
+
+MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID);
+MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID);
+
+template
+void clearDictionary(T *dic) {
+ if (!dic) {
+ return;
+ }
+#ifdef MMKV_APPLE
+ for (auto &pair : *dic) {
+ [pair.first release];
+ }
+#endif
+ dic->clear();
+}
+
+enum : bool {
+ KeepSequence = false,
+ IncreaseSequence = true,
+};
+
+MMKV_NAMESPACE_END
+
+#endif
+#endif /* MMKV_IO_h */
diff --git a/ios/Pods/MMKVCore/Core/MMKV_OSX.cpp b/ios/Pods/MMKVCore/Core/MMKV_OSX.cpp
new file mode 100644
index 00000000..a1db0e3c
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_OSX.cpp
@@ -0,0 +1,333 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MMKVPredef.h"
+
+#ifdef MMKV_APPLE
+
+# include "CodedInputData.h"
+# include "CodedOutputData.h"
+# include "InterProcessLock.h"
+# include "MMKV.h"
+# include "MemoryFile.h"
+# include "MiniPBCoder.h"
+# include "PBUtility.h"
+# include "ScopedLock.hpp"
+# include "ThreadLock.h"
+# include "aes/AESCrypt.h"
+# include
+
+# ifdef MMKV_IOS
+# include "MMKV_OSX.h"
+# include
+# endif
+
+# ifdef __aarch64__
+# include "Checksum.h"
+# endif
+
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+
+using namespace std;
+using namespace mmkv;
+
+extern ThreadLock *g_instanceLock;
+extern MMKVPath_t g_rootDir;
+
+enum { UnKnown = 0, PowerMac = 1, Mac, iPhone, iPod, iPad, AppleTV, AppleWatch };
+static void GetAppleMachineInfo(int &device, int &version);
+
+MMKV_NAMESPACE_BEGIN
+
+# ifdef MMKV_IOS
+MLockPtr::MLockPtr(void *ptr, size_t size) : m_lockDownSize(0), m_lockedPtr(nullptr) {
+ if (!ptr || size == 0) {
+ return;
+ }
+ // calc ptr to mlock()
+ auto writePtr = (size_t) ptr;
+ auto lockPtr = (writePtr / DEFAULT_MMAP_SIZE) * DEFAULT_MMAP_SIZE;
+ auto lockDownSize = writePtr - lockPtr + size;
+ if (mlock((void *) lockPtr, lockDownSize) == 0) {
+ m_lockedPtr = (uint8_t *) lockPtr;
+ m_lockDownSize = lockDownSize;
+ } else {
+ MMKVError("fail to mlock [%p], %s", m_lockedPtr, strerror(errno));
+ // just fail on this condition, otherwise app will crash anyway
+ }
+}
+
+MLockPtr::MLockPtr(MLockPtr &&other) : m_lockDownSize(other.m_lockDownSize), m_lockedPtr(other.m_lockedPtr) {
+ other.m_lockedPtr = nullptr;
+}
+
+MLockPtr::~MLockPtr() {
+ if (m_lockedPtr) {
+ munlock(m_lockedPtr, m_lockDownSize);
+ }
+}
+
+# endif
+
+extern ThreadOnceToken_t once_control;
+extern void initialize();
+
+void MMKV::minimalInit(MMKVPath_t defaultRootDir) {
+ ThreadLock::ThreadOnce(&once_control, initialize);
+
+ // crc32 instruction requires A10 chip, aka iPhone 7 or iPad 6th generation
+ int device = 0, version = 0;
+ GetAppleMachineInfo(device, version);
+# ifdef MMKV_USE_ARMV8_CRC32
+ if ((device == iPhone && version >= 9) || (device == iPad && version >= 7)) {
+ CRC32 = mmkv::armv8_crc32;
+ }
+# endif
+ MMKVInfo("Apple Device:%d, version:%d", device, version);
+
+ g_rootDir = defaultRootDir;
+ mkPath(g_rootDir);
+
+ MMKVInfo("default root dir: " MMKV_PATH_FORMAT, g_rootDir.c_str());
+}
+
+# ifdef MMKV_IOS
+
+static bool g_isInBackground = false;
+
+void MMKV::setIsInBackground(bool isInBackground) {
+ SCOPED_LOCK(g_instanceLock);
+
+ g_isInBackground = isInBackground;
+ MMKVInfo("g_isInBackground:%d", g_isInBackground);
+}
+
+pair guardForBackgroundWriting(void *ptr, size_t size) {
+ if (g_isInBackground) {
+ MLockPtr mlockPtr(ptr, size);
+ return make_pair(mlockPtr.isLocked(), move(mlockPtr));
+ } else {
+ return make_pair(true, MLockPtr(nullptr, 0));
+ }
+}
+
+# endif // MMKV_IOS
+
+bool MMKV::set(NSObject *__unsafe_unretained obj, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ if (!obj) {
+ removeValueForKey(key);
+ return true;
+ }
+
+ NSData *tmpData = nil;
+ if ([obj isKindOfClass:NSString.class]) {
+ auto str = (NSString *) obj;
+ tmpData = [str dataUsingEncoding:NSUTF8StringEncoding];
+ } else if ([obj isKindOfClass:NSData.class]) {
+ tmpData = (NSData *) obj;
+ }
+ if (tmpData) {
+ // delay write the size needed for encoding tmpData
+ // avoid memory copying
+ return setDataForKey(MMBuffer(tmpData, MMBufferNoCopy), key, true);
+ } else if ([obj isKindOfClass:NSDate.class]) {
+ NSDate *oDate = (NSDate *) obj;
+ double time = oDate.timeIntervalSince1970;
+ return set(time, key);
+ } else {
+ /*if ([object conformsToProtocol:@protocol(NSCoding)])*/ {
+ auto tmp = [NSKeyedArchiver archivedDataWithRootObject:obj];
+ if (tmp.length > 0) {
+ return setDataForKey(MMBuffer(tmp, MMBufferNoCopy), key);
+ }
+ }
+ }
+ return false;
+}
+
+NSObject *MMKV::getObject(MMKVKey_t key, Class cls) {
+ if (isKeyEmpty(key) || !cls) {
+ return nil;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ if (MiniPBCoder::isCompatibleClass(cls)) {
+ try {
+ auto result = MiniPBCoder::decodeObject(data, cls);
+ return result;
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ if ([cls conformsToProtocol:@protocol(NSCoding)]) {
+ auto tmp = [NSData dataWithBytesNoCopy:data.getPtr() length:data.length() freeWhenDone:NO];
+ return [NSKeyedUnarchiver unarchiveObjectWithData:tmp];
+ }
+ }
+ }
+ return nil;
+}
+
+# ifndef MMKV_DISABLE_CRYPT
+
+constexpr uint32_t Fixed32Size = pbFixed32Size();
+
+pair
+MMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, const KeyValueHolderCrypt &kvHolder, bool isDataHolder) {
+ if (kvHolder.type != KeyValueHolderType_Offset) {
+ return appendDataWithKey(data, key, isDataHolder);
+ }
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ uint32_t keyLength = kvHolder.keySize;
+ // size needed to encode the key
+ size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);
+
+ auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;
+ MMBuffer keyData(rawKeySize);
+ AESCrypt decrypter = m_crypter->cloneWithStatus(kvHolder.cryptStatus);
+ decrypter.decrypt(basePtr + kvHolder.offset, keyData.getPtr(), rawKeySize);
+
+ return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);
+}
+# endif
+
+NSArray *MMKV::allKeys() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ NSMutableArray *keys = [NSMutableArray array];
+ if (m_crypter) {
+ for (const auto &pair : *m_dicCrypt) {
+ [keys addObject:pair.first];
+ }
+ } else {
+ for (const auto &pair : *m_dic) {
+ [keys addObject:pair.first];
+ }
+ }
+ return keys;
+}
+
+void MMKV::removeValuesForKeys(NSArray *arrKeys) {
+ if (arrKeys.count == 0) {
+ return;
+ }
+ if (arrKeys.count == 1) {
+ return removeValueForKey(arrKeys[0]);
+ }
+
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+ size_t deleteCount = 0;
+ if (m_crypter) {
+ for (NSString *key in arrKeys) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ [itr->first release];
+ m_dicCrypt->erase(itr);
+ deleteCount++;
+ }
+ }
+ } else {
+ for (NSString *key in arrKeys) {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ [itr->first release];
+ m_dic->erase(itr);
+ deleteCount++;
+ }
+ }
+ }
+ if (deleteCount > 0) {
+ m_hasFullWriteback = false;
+
+ fullWriteback();
+ }
+}
+
+void MMKV::enumerateKeys(EnumerateBlock block) {
+ if (block == nil) {
+ return;
+ }
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ MMKVInfo("enumerate [%s] begin", m_mmapID.c_str());
+ if (m_crypter) {
+ for (const auto &pair : *m_dicCrypt) {
+ BOOL stop = NO;
+ block(pair.first, &stop);
+ if (stop) {
+ break;
+ }
+ }
+ } else {
+ for (const auto &pair : *m_dic) {
+ BOOL stop = NO;
+ block(pair.first, &stop);
+ if (stop) {
+ break;
+ }
+ }
+ }
+ MMKVInfo("enumerate [%s] finish", m_mmapID.c_str());
+}
+
+MMKV_NAMESPACE_END
+
+static void GetAppleMachineInfo(int &device, int &version) {
+ device = UnKnown;
+ version = 0;
+
+ struct utsname systemInfo = {};
+ uname(&systemInfo);
+
+ std::string machine(systemInfo.machine);
+ if (machine.find("PowerMac") != std::string::npos || machine.find("Power Macintosh") != std::string::npos) {
+ device = PowerMac;
+ } else if (machine.find("Mac") != std::string::npos || machine.find("Macintosh") != std::string::npos) {
+ device = Mac;
+ } else if (machine.find("iPhone") != std::string::npos) {
+ device = iPhone;
+ } else if (machine.find("iPod") != std::string::npos) {
+ device = iPod;
+ } else if (machine.find("iPad") != std::string::npos) {
+ device = iPad;
+ } else if (machine.find("AppleTV") != std::string::npos) {
+ device = AppleTV;
+ } else if (machine.find("AppleWatch") != std::string::npos) {
+ device = AppleWatch;
+ }
+ auto pos = machine.find_first_of("0123456789");
+ if (pos != std::string::npos) {
+ version = std::atoi(machine.substr(pos).c_str());
+ }
+}
+
+#endif // MMKV_APPLE
diff --git a/ios/Pods/MMKVCore/Core/MMKV_OSX.h b/ios/Pods/MMKVCore/Core/MMKV_OSX.h
new file mode 100644
index 00000000..ae1915f6
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_OSX.h
@@ -0,0 +1,51 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include "MMKVPredef.h"
+
+#if defined(MMKV_IOS) && defined(__cplusplus)
+
+MMKV_NAMESPACE_BEGIN
+
+class MLockPtr {
+ size_t m_lockDownSize;
+ uint8_t *m_lockedPtr;
+
+public:
+ MLockPtr(void *ptr, size_t size);
+ MLockPtr(MLockPtr &&other);
+
+ ~MLockPtr();
+
+ bool isLocked() const {
+ return (m_lockedPtr != nullptr);
+ }
+
+ // just forbid it for possibly misuse
+ explicit MLockPtr(const MLockPtr &other) = delete;
+ MLockPtr &operator=(const MLockPtr &other) = delete;
+};
+
+std::pair guardForBackgroundWriting(void *ptr, size_t size);
+
+MMKV_NAMESPACE_END
+
+#endif
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile.cpp b/ios/Pods/MMKVCore/Core/MemoryFile.cpp
new file mode 100644
index 00000000..8247b166
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile.cpp
@@ -0,0 +1,304 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MemoryFile.h"
+
+#ifndef MMKV_WIN32
+
+# include "InterProcessLock.h"
+# include "MMBuffer.h"
+# include "MMKVLog.h"
+# include "ScopedLock.hpp"
+# include
+# include
+# include
+# include
+# include
+
+using namespace std;
+
+namespace mmkv {
+
+static bool getFileSize(int fd, size_t &size);
+
+# ifdef MMKV_ANDROID
+extern size_t ASharedMemory_getSize(int fd);
+# else
+MemoryFile::MemoryFile(const MMKVPath_t &path) : m_name(path), m_fd(-1), m_ptr(nullptr), m_size(0) {
+ reloadFromFile();
+}
+# endif // MMKV_ANDROID
+
+# ifdef MMKV_IOS
+void tryResetFileProtection(const string &path);
+# endif
+
+bool MemoryFile::truncate(size_t size) {
+ if (m_fd < 0) {
+ return false;
+ }
+ if (size == m_size) {
+ return true;
+ }
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
+ if (size > m_size) {
+ MMKVError("ashmem %s reach size limit:%zu, consider configure with larger size", m_name.c_str(), m_size);
+ } else {
+ MMKVInfo("no way to trim ashmem %s from %zu to smaller size %zu", m_name.c_str(), m_size, size);
+ }
+ return false;
+ }
+# endif // MMKV_ANDROID
+
+ auto oldSize = m_size;
+ m_size = size;
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ }
+
+ if (::ftruncate(m_fd, static_cast(m_size)) != 0) {
+ MMKVError("fail to truncate [%s] to size %zu, %s", m_name.c_str(), m_size, strerror(errno));
+ m_size = oldSize;
+ return false;
+ }
+ if (m_size > oldSize) {
+ if (!zeroFillFile(m_fd, oldSize, m_size - oldSize)) {
+ MMKVError("fail to zeroFile [%s] to size %zu, %s", m_name.c_str(), m_size, strerror(errno));
+ m_size = oldSize;
+ return false;
+ }
+ }
+
+ if (m_ptr) {
+ if (munmap(m_ptr, oldSize) != 0) {
+ MMKVError("fail to munmap [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ }
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ return ret;
+}
+
+bool MemoryFile::msync(SyncFlag syncFlag) {
+ if (m_ptr) {
+ auto ret = ::msync(m_ptr, m_size, syncFlag ? MMKV_SYNC : MMKV_ASYNC);
+ if (ret == 0) {
+ return true;
+ }
+ MMKVError("fail to msync [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ return false;
+}
+
+bool MemoryFile::mmap() {
+ m_ptr = (char *) ::mmap(m_ptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
+ if (m_ptr == MAP_FAILED) {
+ MMKVError("fail to mmap [%s], %s", m_name.c_str(), strerror(errno));
+ m_ptr = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+void MemoryFile::reloadFromFile() {
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
+ return;
+ }
+# endif
+ if (isFileValid()) {
+ MMKVWarning("calling reloadFromFile while the cache [%s] is still valid", m_name.c_str());
+ MMKV_ASSERT(0);
+ clearMemoryCache();
+ }
+
+ m_fd = open(m_name.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU);
+ if (m_fd < 0) {
+ MMKVError("fail to open:%s, %s", m_name.c_str(), strerror(errno));
+ } else {
+ FileLock fileLock(m_fd);
+ InterProcessLock lock(&fileLock, ExclusiveLockType);
+ SCOPED_LOCK(&lock);
+
+ mmkv::getFileSize(m_fd, m_size);
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ truncate(roundSize);
+ } else {
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+# ifdef MMKV_IOS
+ tryResetFileProtection(m_name);
+# endif
+ }
+}
+
+void MemoryFile::doCleanMemoryCache(bool forceClean) {
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM && !forceClean) {
+ return;
+ }
+# endif
+ if (m_ptr && m_ptr != MAP_FAILED) {
+ if (munmap(m_ptr, m_size) != 0) {
+ MMKVError("fail to munmap [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ }
+ m_ptr = nullptr;
+
+ if (m_fd >= 0) {
+ if (::close(m_fd) != 0) {
+ MMKVError("fail to close [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ }
+ m_fd = -1;
+ m_size = 0;
+}
+
+size_t MemoryFile::getActualFileSize() {
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
+ return ASharedMemory_getSize(m_fd);
+ }
+# endif
+ size_t size = 0;
+ mmkv::getFileSize(m_fd, size);
+ return size;
+}
+
+bool isFileExist(const string &nsFilePath) {
+ if (nsFilePath.empty()) {
+ return false;
+ }
+
+ struct stat temp = {};
+ return lstat(nsFilePath.c_str(), &temp) == 0;
+}
+
+extern bool mkPath(const MMKVPath_t &str) {
+ char *path = strdup(str.c_str());
+
+ struct stat sb = {};
+ bool done = false;
+ char *slash = path;
+
+ while (!done) {
+ slash += strspn(slash, "/");
+ slash += strcspn(slash, "/");
+
+ done = (*slash == '\0');
+ *slash = '\0';
+
+ if (stat(path, &sb) != 0) {
+ if (errno != ENOENT || mkdir(path, 0777) != 0) {
+ MMKVWarning("%s : %s", path, strerror(errno));
+ free(path);
+ return false;
+ }
+ } else if (!S_ISDIR(sb.st_mode)) {
+ MMKVWarning("%s: %s", path, strerror(ENOTDIR));
+ free(path);
+ return false;
+ }
+
+ *slash = '/';
+ }
+ free(path);
+
+ return true;
+}
+
+MMBuffer *readWholeFile(const MMKVPath_t &path) {
+ MMBuffer *buffer = nullptr;
+ int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ auto fileLength = lseek(fd, 0, SEEK_END);
+ if (fileLength > 0) {
+ buffer = new MMBuffer(static_cast(fileLength));
+ lseek(fd, 0, SEEK_SET);
+ auto readSize = read(fd, buffer->getPtr(), static_cast(fileLength));
+ if (readSize != -1) {
+ //fileSize = readSize;
+ } else {
+ MMKVWarning("fail to read %s: %s", path.c_str(), strerror(errno));
+
+ delete buffer;
+ buffer = nullptr;
+ }
+ }
+ close(fd);
+ } else {
+ MMKVWarning("fail to open %s: %s", path.c_str(), strerror(errno));
+ }
+ return buffer;
+}
+
+bool zeroFillFile(int fd, size_t startPos, size_t size) {
+ if (fd < 0) {
+ return false;
+ }
+
+ if (lseek(fd, static_cast(startPos), SEEK_SET) < 0) {
+ MMKVError("fail to lseek fd[%d], error:%s", fd, strerror(errno));
+ return false;
+ }
+
+ static const char zeros[4096] = {};
+ while (size >= sizeof(zeros)) {
+ if (write(fd, zeros, sizeof(zeros)) < 0) {
+ MMKVError("fail to write fd[%d], error:%s", fd, strerror(errno));
+ return false;
+ }
+ size -= sizeof(zeros);
+ }
+ if (size > 0) {
+ if (write(fd, zeros, size) < 0) {
+ MMKVError("fail to write fd[%d], error:%s", fd, strerror(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool getFileSize(int fd, size_t &size) {
+ struct stat st = {};
+ if (fstat(fd, &st) != -1) {
+ size = (size_t) st.st_size;
+ return true;
+ }
+ return false;
+}
+
+size_t getPageSize() {
+ return static_cast(getpagesize());
+}
+
+} // namespace mmkv
+
+#endif // MMKV_WIN32
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile.h b/ios/Pods/MMKVCore/Core/MemoryFile.h
new file mode 100644
index 00000000..a308deab
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile.h
@@ -0,0 +1,106 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_MAMERYFILE_H
+#define MMKV_MAMERYFILE_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#ifdef MMKV_ANDROID
+MMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID);
+
+namespace mmkv {
+extern int g_android_api;
+
+enum FileType : bool { MMFILE_TYPE_FILE = false, MMFILE_TYPE_ASHMEM = true };
+} // namespace mmkv
+#endif // MMKV_ANDROID
+
+namespace mmkv {
+
+class MemoryFile {
+ MMKVPath_t m_name;
+ MMKVFileHandle_t m_fd;
+#ifdef MMKV_WIN32
+ HANDLE m_fileMapping;
+#endif
+ void *m_ptr;
+ size_t m_size;
+
+ bool mmap();
+
+ void doCleanMemoryCache(bool forceClean);
+
+public:
+#ifndef MMKV_ANDROID
+ explicit MemoryFile(const MMKVPath_t &path);
+#else
+ MemoryFile(const MMKVPath_t &path, size_t size, FileType fileType);
+ explicit MemoryFile(MMKVFileHandle_t ashmemFD);
+
+ const FileType m_fileType;
+#endif // MMKV_ANDROID
+
+ ~MemoryFile() { doCleanMemoryCache(true); }
+
+ size_t getFileSize() const { return m_size; }
+
+ // get the actual file size on disk
+ size_t getActualFileSize();
+
+ void *getMemory() { return m_ptr; }
+
+ const MMKVPath_t &getName() { return m_name; }
+
+ MMKVFileHandle_t getFd() { return m_fd; }
+
+ // the newly expanded file content will be zeroed
+ bool truncate(size_t size);
+
+ bool msync(SyncFlag syncFlag);
+
+ // call this if clearMemoryCache() has been called
+ void reloadFromFile();
+
+ void clearMemoryCache() { doCleanMemoryCache(false); }
+
+#ifndef MMKV_WIN32
+ bool isFileValid() { return m_fd >= 0 && m_size > 0 && m_ptr; }
+#else
+ bool isFileValid() { return m_fd != INVALID_HANDLE_VALUE && m_size > 0 && m_fileMapping && m_ptr; }
+#endif
+
+ // just forbid it for possibly misuse
+ explicit MemoryFile(const MemoryFile &other) = delete;
+ MemoryFile &operator=(const MemoryFile &other) = delete;
+};
+
+class MMBuffer;
+
+extern bool mkPath(const MMKVPath_t &path);
+extern bool isFileExist(const MMKVPath_t &nsFilePath);
+extern MMBuffer *readWholeFile(const MMKVPath_t &path);
+extern bool zeroFillFile(MMKVFileHandle_t fd, size_t startPos, size_t size);
+extern size_t getPageSize();
+} // namespace mmkv
+
+#endif
+#endif //MMKV_MAMERYFILE_H
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile_Android.cpp b/ios/Pods/MMKVCore/Core/MemoryFile_Android.cpp
new file mode 100644
index 00000000..c005151d
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile_Android.cpp
@@ -0,0 +1,192 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MemoryFile.h"
+
+#ifdef MMKV_ANDROID
+
+# include "MMBuffer.h"
+# include "MMKVLog.h"
+# include
+# include
+# include
+# include
+# include
+
+using namespace std;
+
+constexpr char ASHMEM_NAME_DEF[] = "/dev/ashmem";
+
+namespace mmkv {
+
+// for Android Q limiting ashmem access
+extern int ASharedMemory_create(const char *name, size_t size);
+extern size_t ASharedMemory_getSize(int fd);
+extern string ASharedMemory_getName(int fd);
+
+MemoryFile::MemoryFile(const string &path, size_t size, FileType fileType)
+ : m_name(path), m_fd(-1), m_ptr(nullptr), m_size(0), m_fileType(fileType) {
+ if (m_fileType == MMFILE_TYPE_FILE) {
+ reloadFromFile();
+ } else {
+ // round up to (n * pagesize)
+ if (size < DEFAULT_MMAP_SIZE || (size % DEFAULT_MMAP_SIZE != 0)) {
+ size = ((size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ }
+ auto filename = m_name.c_str();
+ auto ptr = strstr(filename, ASHMEM_NAME_DEF);
+ if (ptr && ptr[sizeof(ASHMEM_NAME_DEF) - 1] == '/') {
+ filename = ptr + sizeof(ASHMEM_NAME_DEF);
+ }
+ m_fd = ASharedMemory_create(filename, size);
+ if (m_fd >= 0) {
+ m_size = size;
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+ }
+}
+
+MemoryFile::MemoryFile(int ashmemFD)
+ : m_name(""), m_fd(ashmemFD), m_ptr(nullptr), m_size(0), m_fileType(MMFILE_TYPE_ASHMEM) {
+ if (m_fd < 0) {
+ MMKVError("fd %d invalid", m_fd);
+ } else {
+ m_name = ASharedMemory_getName(m_fd);
+ m_size = ASharedMemory_getSize(m_fd);
+ MMKVInfo("ashmem name:%s, size:%zu", m_name.c_str(), m_size);
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+}
+
+} // namespace mmkv
+
+# pragma mark - ashmem
+# include
+# include
+
+namespace mmkv {
+
+constexpr auto ASHMEM_NAME_LEN = 256;
+constexpr auto __ASHMEMIOC = 0x77;
+# define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
+# define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
+# define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
+# define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
+
+int g_android_api = __ANDROID_API_L__;
+
+void *loadLibrary() {
+ auto name = "libandroid.so";
+ static auto handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL);
+ if (handle == RTLD_DEFAULT) {
+ MMKVError("unable to load library %s", name);
+ }
+ return handle;
+}
+
+typedef int (*AShmem_create_t)(const char *name, size_t size);
+
+int ASharedMemory_create(const char *name, size_t size) {
+ int fd = -1;
+ if (g_android_api >= __ANDROID_API_O__) {
+ static auto handle = loadLibrary();
+ static AShmem_create_t funcPtr =
+ (handle != nullptr) ? reinterpret_cast(dlsym(handle, "ASharedMemory_create")) : nullptr;
+ if (funcPtr) {
+ fd = funcPtr(name, size);
+ if (fd < 0) {
+ MMKVError("fail to ASharedMemory_create %s with size %zu, errno:%s", name, size, strerror(errno));
+ }
+ } else {
+ MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
+ }
+ }
+ if (fd < 0) {
+ fd = open(ASHMEM_NAME_DEF, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ MMKVError("fail to open ashmem:%s, %s", name, strerror(errno));
+ } else {
+ if (ioctl(fd, ASHMEM_SET_NAME, name) != 0) {
+ MMKVError("fail to set ashmem name:%s, %s", name, strerror(errno));
+ } else if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
+ MMKVError("fail to set ashmem:%s, size %zu, %s", name, size, strerror(errno));
+ }
+ }
+ }
+ return fd;
+}
+
+typedef size_t (*AShmem_getSize_t)(int fd);
+
+size_t ASharedMemory_getSize(int fd) {
+ size_t size = 0;
+ if (g_android_api >= __ANDROID_API_O__) {
+ static auto handle = loadLibrary();
+ static AShmem_getSize_t funcPtr =
+ (handle != nullptr) ? reinterpret_cast(dlsym(handle, "ASharedMemory_getSize")) : nullptr;
+ if (funcPtr) {
+ size = funcPtr(fd);
+ if (size == 0) {
+ MMKVError("fail to ASharedMemory_getSize:%d, %s", fd, strerror(errno));
+ }
+ } else {
+ MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
+ }
+ }
+ if (size == 0) {
+ int tmp = ioctl(fd, ASHMEM_GET_SIZE, nullptr);
+ if (tmp < 0) {
+ MMKVError("fail to get ashmem size:%d, %s", fd, strerror(errno));
+ } else {
+ size = static_cast(tmp);
+ }
+ }
+ return size;
+}
+
+string ASharedMemory_getName(int fd) {
+ // Android Q doesn't have ASharedMemory_getName()
+ // I've make a request to Google, https://issuetracker.google.com/issues/130741665
+ // There's nothing we can do before it's supported officially by Google
+ if (g_android_api >= 29) {
+ return "";
+ }
+
+ char name[ASHMEM_NAME_LEN] = {0};
+ if (ioctl(fd, ASHMEM_GET_NAME, name) != 0) {
+ MMKVError("fail to get ashmem name:%d, %s", fd, strerror(errno));
+ return "";
+ }
+ return string(name);
+}
+
+} // namespace mmkv
+
+MMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID) {
+ return MMKVPath_t(ASHMEM_NAME_DEF) + MMKV_PATH_SLASH + mmapID;
+}
+
+#endif // MMKV_ANDROID
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile_OSX.cpp b/ios/Pods/MMKVCore/Core/MemoryFile_OSX.cpp
new file mode 100644
index 00000000..3235dc7f
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile_OSX.cpp
@@ -0,0 +1,52 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MemoryFile.h"
+
+#ifdef MMKV_IOS
+
+# include "MMKVLog.h"
+
+using namespace std;
+
+namespace mmkv {
+
+void tryResetFileProtection(const string &path) {
+ @autoreleasepool {
+ NSString *nsPath = [NSString stringWithUTF8String:path.c_str()];
+ NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:nsPath error:nullptr];
+ NSString *protection = [attr valueForKey:NSFileProtectionKey];
+ MMKVInfo("protection on [%@] is %@", nsPath, protection);
+ if ([protection isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication] == NO) {
+ NSMutableDictionary *newAttr = [NSMutableDictionary dictionaryWithDictionary:attr];
+ [newAttr setObject:NSFileProtectionCompleteUntilFirstUserAuthentication forKey:NSFileProtectionKey];
+ NSError *err = nil;
+ [[NSFileManager defaultManager] setAttributes:newAttr ofItemAtPath:nsPath error:&err];
+ if (err != nil) {
+ MMKVError("fail to set attribute %@ on [%@]: %@", NSFileProtectionCompleteUntilFirstUserAuthentication,
+ nsPath, err);
+ }
+ }
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_IOS
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile_Win32.cpp b/ios/Pods/MMKVCore/Core/MemoryFile_Win32.cpp
new file mode 100644
index 00000000..29f0597b
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile_Win32.cpp
@@ -0,0 +1,315 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MemoryFile.h"
+
+#ifdef MMKV_WIN32
+
+# include "InterProcessLock.h"
+# include "MMBuffer.h"
+# include "MMKVLog.h"
+# include "ScopedLock.hpp"
+# include "ThreadLock.h"
+
+using namespace std;
+
+namespace mmkv {
+
+static bool getFileSize(MMKVFileHandle_t fd, size_t &size);
+static bool ftruncate(MMKVFileHandle_t file, size_t size);
+
+MemoryFile::MemoryFile(const MMKVPath_t &path)
+ : m_name(path), m_fd(INVALID_HANDLE_VALUE), m_fileMapping(nullptr), m_ptr(nullptr), m_size(0) {
+ reloadFromFile();
+}
+
+bool MemoryFile::truncate(size_t size) {
+ if (m_fd < 0) {
+ return false;
+ }
+ if (size == m_size) {
+ return true;
+ }
+
+ auto oldSize = m_size;
+ m_size = size;
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ }
+
+ if (!ftruncate(m_fd, m_size)) {
+ MMKVError("fail to truncate [%ws] to size %zu", m_name.c_str(), m_size);
+ m_size = oldSize;
+ return false;
+ }
+ if (m_size > oldSize) {
+ if (!zeroFillFile(m_fd, oldSize, m_size - oldSize)) {
+ MMKVError("fail to zeroFile [%ws] to size %zu", m_name.c_str(), m_size);
+ m_size = oldSize;
+ return false;
+ }
+ }
+
+ if (m_ptr) {
+ if (!UnmapViewOfFile(m_ptr)) {
+ MMKVError("fail to munmap [%ws], %d", m_name.c_str(), GetLastError());
+ }
+ m_ptr = nullptr;
+ }
+ if (m_fileMapping) {
+ CloseHandle(m_fileMapping);
+ m_fileMapping = nullptr;
+ }
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ return ret;
+}
+
+bool MemoryFile::msync(SyncFlag syncFlag) {
+ if (m_ptr) {
+ if (FlushViewOfFile(m_ptr, m_size)) {
+ if (syncFlag == MMKV_SYNC) {
+ if (!FlushFileBuffers(m_fd)) {
+ MMKVError("fail to FlushFileBuffers [%ws]:%d", m_name.c_str(), GetLastError());
+ return false;
+ }
+ }
+ return true;
+ }
+ MMKVError("fail to FlushViewOfFile [%ws]:%d", m_name.c_str(), GetLastError());
+ return false;
+ }
+ return false;
+}
+
+bool MemoryFile::mmap() {
+ m_fileMapping = CreateFileMapping(m_fd, nullptr, PAGE_READWRITE, 0, 0, nullptr);
+ if (!m_fileMapping) {
+ MMKVError("fail to CreateFileMapping [%ws], %d", m_name.c_str(), GetLastError());
+ return false;
+ } else {
+ m_ptr = (char *) MapViewOfFile(m_fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if (!m_ptr) {
+ MMKVError("fail to mmap [%ws], %d", m_name.c_str(), GetLastError());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void MemoryFile::reloadFromFile() {
+ if (isFileValid()) {
+ MMKVWarning("calling reloadFromFile while the cache [%ws] is still valid", m_name.c_str());
+ assert(0);
+ clearMemoryCache();
+ }
+
+ m_fd =
+ CreateFile(m_name.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (m_fd == INVALID_HANDLE_VALUE) {
+ MMKVError("fail to open:%ws, %d", m_name.c_str(), GetLastError());
+ } else {
+ FileLock fileLock(m_fd);
+ InterProcessLock lock(&fileLock, ExclusiveLockType);
+ SCOPED_LOCK(&lock);
+
+ mmkv::getFileSize(m_fd, m_size);
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ truncate(roundSize);
+ } else {
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+ }
+}
+
+void MemoryFile::doCleanMemoryCache(bool forceClean) {
+ if (m_ptr) {
+ UnmapViewOfFile(m_ptr);
+ m_ptr = nullptr;
+ }
+ if (m_fileMapping) {
+ CloseHandle(m_fileMapping);
+ m_fileMapping = nullptr;
+ }
+ if (m_fd != INVALID_HANDLE_VALUE) {
+ CloseHandle(m_fd);
+ m_fd = INVALID_HANDLE_VALUE;
+ }
+}
+
+size_t MemoryFile::getActualFileSize() {
+ size_t size = 0;
+ mmkv::getFileSize(m_fd, size);
+ return size;
+}
+
+size_t getPageSize() {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ return system_info.dwPageSize;
+}
+
+bool isFileExist(const MMKVPath_t &nsFilePath) {
+ if (nsFilePath.empty()) {
+ return false;
+ }
+ auto attribute = GetFileAttributes(nsFilePath.c_str());
+ return (attribute != INVALID_FILE_ATTRIBUTES);
+}
+
+bool mkPath(const MMKVPath_t &str) {
+ wchar_t *path = _wcsdup(str.c_str());
+
+ bool done = false;
+ wchar_t *slash = path;
+
+ while (!done) {
+ slash += wcsspn(slash, L"\\");
+ slash += wcscspn(slash, L"\\");
+
+ done = (*slash == L'\0');
+ *slash = L'\0';
+
+ auto attribute = GetFileAttributes(path);
+ if (attribute == INVALID_FILE_ATTRIBUTES) {
+ if (!CreateDirectory(path, nullptr)) {
+ MMKVError("fail to create dir:%ws, %d", str.c_str(), GetLastError());
+ free(path);
+ return false;
+ }
+ } else if (!(attribute & FILE_ATTRIBUTE_DIRECTORY)) {
+ MMKVError("%ws attribute:%d not a directry", str.c_str(), attribute);
+ free(path);
+ return false;
+ }
+
+ *slash = L'\\';
+ }
+ free(path);
+ return true;
+}
+
+MMBuffer *readWholeFile(const MMKVPath_t &nsFilePath) {
+ MMBuffer *buffer = nullptr;
+ auto fd = CreateFile(nsFilePath.c_str(), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (fd != INVALID_HANDLE_VALUE) {
+ size_t fileLength = 0;
+ getFileSize(fd, fileLength);
+ if (fileLength > 0) {
+ buffer = new MMBuffer(static_cast(fileLength));
+ SetFilePointer(fd, 0, 0, FILE_BEGIN);
+ DWORD readSize = 0;
+ if (ReadFile(fd, buffer->getPtr(), fileLength, &readSize, nullptr)) {
+ //fileSize = readSize;
+ } else {
+ MMKVWarning("fail to read %ws: %d", nsFilePath.c_str(), GetLastError());
+ delete buffer;
+ buffer = nullptr;
+ }
+ }
+ CloseHandle(fd);
+ } else {
+ MMKVWarning("fail to open %ws: %d", nsFilePath.c_str(), GetLastError());
+ }
+ return buffer;
+}
+
+bool zeroFillFile(MMKVFileHandle_t file, size_t startPos, size_t size) {
+ if (file == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+ if (size == 0) {
+ return true;
+ }
+
+ LARGE_INTEGER position;
+ position.QuadPart = startPos;
+ if (!SetFilePointerEx(file, position, nullptr, FILE_BEGIN)) {
+ MMKVError("fail to lseek fd[%p], error:%d", file, GetLastError());
+ return false;
+ }
+
+ static const char zeros[4096] = {0};
+ while (size >= sizeof(zeros)) {
+ DWORD bytesWritten = 0;
+ if (!WriteFile(file, zeros, sizeof(zeros), &bytesWritten, nullptr)) {
+ MMKVError("fail to write fd[%p], error:%d", file, GetLastError());
+ return false;
+ }
+ size -= bytesWritten;
+ }
+ if (size > 0) {
+ DWORD bytesWritten = 0;
+ if (!WriteFile(file, zeros, size, &bytesWritten, nullptr)) {
+ MMKVError("fail to write fd[%p], error:%d", file, GetLastError());
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool ftruncate(MMKVFileHandle_t file, size_t size) {
+ LARGE_INTEGER large;
+ large.QuadPart = size;
+ if (SetFilePointerEx(file, large, 0, FILE_BEGIN)) {
+ if (SetEndOfFile(file)) {
+ return true;
+ }
+ MMKVError("fail to SetEndOfFile:%d", GetLastError());
+ return false;
+ } else {
+ MMKVError("fail to SetFilePointer:%d", GetLastError());
+ return false;
+ }
+}
+
+static bool getFileSize(MMKVFileHandle_t fd, size_t &size) {
+ LARGE_INTEGER filesize = {0};
+ if (GetFileSizeEx(fd, &filesize)) {
+ size = static_cast(filesize.QuadPart);
+ return true;
+ }
+ return false;
+}
+
+} // namespace mmkv
+
+std::wstring string2MMKVPath_t(const std::string &str) {
+ auto length = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
+ auto buffer = new wchar_t[length];
+ MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buffer, length);
+ wstring result(buffer);
+ delete[] buffer;
+ return result;
+}
+
+#endif // MMKV_WIN32
diff --git a/ios/Pods/MMKVCore/Core/MiniPBCoder.cpp b/ios/Pods/MMKVCore/Core/MiniPBCoder.cpp
new file mode 100644
index 00000000..8c88523e
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MiniPBCoder.cpp
@@ -0,0 +1,366 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MiniPBCoder.h"
+#include "CodedInputData.h"
+#include "CodedInputDataCrypt.h"
+#include "CodedOutputData.h"
+#include "PBEncodeItem.hpp"
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+
+namespace mmkv {
+
+MiniPBCoder::MiniPBCoder() : m_encodeItems(new std::vector()) {
+}
+
+MiniPBCoder::MiniPBCoder(const MMBuffer *inputBuffer, AESCrypt *crypter) : MiniPBCoder() {
+ m_inputBuffer = inputBuffer;
+#ifndef MMKV_DISABLE_CRYPT
+ if (crypter) {
+ m_inputDataDecrpt = new CodedInputDataCrypt(m_inputBuffer->getPtr(), m_inputBuffer->length(), *crypter);
+ } else {
+ m_inputData = new CodedInputData(m_inputBuffer->getPtr(), m_inputBuffer->length());
+ }
+#else
+ m_inputData = new CodedInputData(m_inputBuffer->getPtr(), m_inputBuffer->length());
+#endif // MMKV_DISABLE_CRYPT
+}
+
+MiniPBCoder::~MiniPBCoder() {
+ delete m_inputData;
+#ifndef MMKV_DISABLE_CRYPT
+ delete m_inputDataDecrpt;
+#endif
+ delete m_outputBuffer;
+ delete m_outputData;
+ delete m_encodeItems;
+}
+
+// encode
+
+// write object using prepared m_encodeItems[]
+void MiniPBCoder::writeRootObject() {
+ for (size_t index = 0, total = m_encodeItems->size(); index < total; index++) {
+ PBEncodeItem *encodeItem = &(*m_encodeItems)[index];
+ switch (encodeItem->type) {
+ case PBEncodeItemType_Data: {
+ m_outputData->writeData(*(encodeItem->value.bufferValue));
+ break;
+ }
+ case PBEncodeItemType_Container: {
+ m_outputData->writeUInt32(encodeItem->valueSize);
+ break;
+ }
+#ifndef MMKV_APPLE
+ case PBEncodeItemType_String: {
+ m_outputData->writeString(*(encodeItem->value.strValue));
+ break;
+ }
+#else
+ case PBEncodeItemType_NSString: {
+ m_outputData->writeUInt32(encodeItem->valueSize);
+ if (encodeItem->valueSize > 0 && encodeItem->value.tmpObjectValue != nullptr) {
+ auto obj = (__bridge NSData *) encodeItem->value.tmpObjectValue;
+ MMBuffer buffer(obj, MMBufferNoCopy);
+ m_outputData->writeRawData(buffer);
+ }
+ break;
+ }
+ case PBEncodeItemType_NSData: {
+ m_outputData->writeUInt32(encodeItem->valueSize);
+ if (encodeItem->valueSize > 0 && encodeItem->value.objectValue != nullptr) {
+ auto obj = (__bridge NSData *) encodeItem->value.objectValue;
+ MMBuffer buffer(obj, MMBufferNoCopy);
+ m_outputData->writeRawData(buffer);
+ }
+ break;
+ }
+ case PBEncodeItemType_NSDate: {
+ NSDate *oDate = (__bridge NSDate *) encodeItem->value.objectValue;
+ m_outputData->writeDouble(oDate.timeIntervalSince1970);
+ break;
+ }
+#endif // MMKV_APPLE
+ case PBEncodeItemType_None: {
+ MMKVError("%d", encodeItem->type);
+ break;
+ }
+ }
+ }
+}
+
+size_t MiniPBCoder::prepareObjectForEncode(const MMBuffer &buffer) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_Data;
+ encodeItem->value.bufferValue = &buffer;
+ encodeItem->valueSize = static_cast(buffer.length());
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+size_t MiniPBCoder::prepareObjectForEncode(const MMKVVector &vec) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_Container;
+ encodeItem->value.bufferValue = nullptr;
+
+ for (const auto &itr : vec) {
+ const auto &key = itr.first;
+ const auto &value = itr.second;
+# ifdef MMKV_APPLE
+ if (key.length <= 0) {
+# else
+ if (key.length() <= 0) {
+# endif
+ continue;
+ }
+
+ size_t keyIndex = prepareObjectForEncode(key);
+ if (keyIndex < m_encodeItems->size()) {
+ size_t valueIndex = prepareObjectForEncode(value);
+ if (valueIndex < m_encodeItems->size()) {
+ (*m_encodeItems)[index].valueSize += (*m_encodeItems)[keyIndex].compiledSize;
+ (*m_encodeItems)[index].valueSize += (*m_encodeItems)[valueIndex].compiledSize;
+ } else {
+ m_encodeItems->pop_back(); // pop key
+ }
+ }
+ }
+
+ encodeItem = &(*m_encodeItems)[index];
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+MMBuffer MiniPBCoder::writePreparedItems(size_t index) {
+ PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr;
+ if (oItem && oItem->compiledSize > 0) {
+ m_outputBuffer = new MMBuffer(oItem->compiledSize);
+ m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length());
+
+ writeRootObject();
+ }
+
+ return std::move(*m_outputBuffer);
+}
+
+MMBuffer MiniPBCoder::encodeDataWithObject(const MMBuffer &obj) {
+ try {
+ auto valueSize = static_cast(obj.length());
+ auto compiledSize = pbRawVarint32Size(valueSize) + valueSize;
+ MMBuffer result(compiledSize);
+ CodedOutputData output(result.getPtr(), result.length());
+ output.writeData(obj);
+ return result;
+ } catch (const std::exception &exception) {
+ MMKVError("%s", exception.what());
+ return MMBuffer();
+ }
+}
+
+#ifndef MMKV_APPLE
+
+size_t MiniPBCoder::prepareObjectForEncode(const string &str) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_String;
+ encodeItem->value.strValue = &str;
+ encodeItem->valueSize = static_cast(str.size());
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+size_t MiniPBCoder::prepareObjectForEncode(const vector &v) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_Container;
+ encodeItem->value.bufferValue = nullptr;
+
+ for (const auto &str : v) {
+ size_t itemIndex = prepareObjectForEncode(str);
+ if (itemIndex < m_encodeItems->size()) {
+ (*m_encodeItems)[index].valueSize += (*m_encodeItems)[itemIndex].compiledSize;
+ }
+ }
+
+ encodeItem = &(*m_encodeItems)[index];
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+vector MiniPBCoder::decodeOneVector() {
+ vector v;
+
+ m_inputData->readInt32();
+
+ while (!m_inputData->isAtEnd()) {
+ auto value = m_inputData->readString();
+ v.push_back(move(value));
+ }
+
+ return v;
+}
+
+void MiniPBCoder::decodeOneMap(MMKVMap &dic, size_t position, bool greedy) {
+ auto block = [position, this](MMKVMap &dictionary) {
+ if (position) {
+ m_inputData->seek(position);
+ } else {
+ m_inputData->readInt32();
+ }
+ while (!m_inputData->isAtEnd()) {
+ KeyValueHolder kvHolder;
+ const auto &key = m_inputData->readString(kvHolder);
+ if (key.length() > 0) {
+ m_inputData->readData(kvHolder);
+ if (kvHolder.valueSize > 0) {
+ dictionary[key] = move(kvHolder);
+ } else {
+ auto itr = dictionary.find(key);
+ if (itr != dictionary.end()) {
+ dictionary.erase(itr);
+ }
+ }
+ }
+ }
+ };
+
+ if (greedy) {
+ try {
+ block(dic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ try {
+ MMKVMap tmpDic;
+ block(tmpDic);
+ dic.swap(tmpDic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+}
+
+# ifndef MMKV_DISABLE_CRYPT
+
+void MiniPBCoder::decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy) {
+ auto block = [position, this](MMKVMapCrypt &dictionary) {
+ if (position) {
+ m_inputDataDecrpt->seek(position);
+ } else {
+ m_inputDataDecrpt->readInt32();
+ }
+ while (!m_inputDataDecrpt->isAtEnd()) {
+ KeyValueHolderCrypt kvHolder;
+ const auto &key = m_inputDataDecrpt->readString(kvHolder);
+ if (key.length() > 0) {
+ m_inputDataDecrpt->readData(kvHolder);
+ if (kvHolder.valueSize > 0) {
+ dictionary[key] = move(kvHolder);
+ } else {
+ auto itr = dictionary.find(key);
+ if (itr != dictionary.end()) {
+ dictionary.erase(itr);
+ }
+ }
+ }
+ }
+ };
+
+ if (greedy) {
+ try {
+ block(dic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ try {
+ MMKVMapCrypt tmpDic;
+ block(tmpDic);
+ dic.swap(tmpDic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+}
+
+# endif // MMKV_DISABLE_CRYPT
+
+vector MiniPBCoder::decodeVector(const MMBuffer &oData) {
+ MiniPBCoder oCoder(&oData);
+ return oCoder.decodeOneVector();
+}
+
+#endif // MMKV_APPLE
+
+void MiniPBCoder::decodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position) {
+ MiniPBCoder oCoder(&oData);
+ oCoder.decodeOneMap(dic, position, false);
+}
+
+void MiniPBCoder::greedyDecodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position) {
+ MiniPBCoder oCoder(&oData);
+ oCoder.decodeOneMap(dic, position, true);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+void MiniPBCoder::decodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position) {
+ MiniPBCoder oCoder(&oData, crypter);
+ oCoder.decodeOneMap(dic, position, false);
+}
+
+void MiniPBCoder::greedyDecodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position) {
+ MiniPBCoder oCoder(&oData, crypter);
+ oCoder.decodeOneMap(dic, position, true);
+}
+
+#endif
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/MiniPBCoder.h b/ios/Pods/MMKVCore/Core/MiniPBCoder.h
new file mode 100644
index 00000000..15895687
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MiniPBCoder.h
@@ -0,0 +1,129 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMKV_MINIPBCODER_H
+#define MMKV_MINIPBCODER_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include "MMBuffer.h"
+#include "MMKVLog.h"
+#include "PBUtility.h"
+#include
+
+namespace mmkv {
+
+class CodedInputData;
+class CodedOutputData;
+class AESCrypt;
+class CodedInputDataCrypt;
+struct PBEncodeItem;
+
+class MiniPBCoder {
+ const MMBuffer *m_inputBuffer = nullptr;
+ CodedInputData *m_inputData = nullptr;
+ CodedInputDataCrypt *m_inputDataDecrpt = nullptr;
+
+ MMBuffer *m_outputBuffer = nullptr;
+ CodedOutputData *m_outputData = nullptr;
+ std::vector *m_encodeItems = nullptr;
+
+ MiniPBCoder();
+ explicit MiniPBCoder(const MMBuffer *inputBuffer, AESCrypt *crypter = nullptr);
+ ~MiniPBCoder();
+
+ void writeRootObject();
+
+ size_t prepareObjectForEncode(const MMKVVector &vec);
+ size_t prepareObjectForEncode(const MMBuffer &buffer);
+
+ template
+ MMBuffer getEncodeData(const T &obj) {
+ size_t index = prepareObjectForEncode(obj);
+ return writePreparedItems(index);
+ }
+
+ MMBuffer writePreparedItems(size_t index);
+
+ void decodeOneMap(MMKVMap &dic, size_t position, bool greedy);
+#ifndef MMKV_DISABLE_CRYPT
+ void decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy);
+#endif
+
+#ifndef MMKV_APPLE
+ size_t prepareObjectForEncode(const std::string &str);
+ size_t prepareObjectForEncode(const std::vector