diff --git a/Android/MMKV/build.gradle b/Android/MMKV/build.gradle index 49ea817e..bf05db96 100644 --- a/Android/MMKV/build.gradle +++ b/Android/MMKV/build.gradle @@ -8,7 +8,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.1' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.1' classpath 'digital.wup:android-maven-publish:3.6.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" diff --git a/Android/MMKV/gradle.properties b/Android/MMKV/gradle.properties index d9401d11..c1abcd37 100644 --- a/Android/MMKV/gradle.properties +++ b/Android/MMKV/gradle.properties @@ -6,12 +6,14 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME_PREFIX=1.0.19 +VERSION_NAME_PREFIX=1.0.20 #VERSION_NAME_SUFFIX=-SNAPSHOT VERSION_NAME_SUFFIX= \ No newline at end of file diff --git a/Android/MMKV/gradle/android-publish.gradle b/Android/MMKV/gradle/android-publish.gradle index 9d4955fd..fa9641a3 100644 --- a/Android/MMKV/gradle/android-publish.gradle +++ b/Android/MMKV/gradle/android-publish.gradle @@ -151,9 +151,9 @@ android.libraryVariants.all { variant -> } // require gradle 4.x + - if (GradleVersion.current() < GradleVersion.version('4.0')) { + /*if (GradleVersion.current() < GradleVersion.version('4.0')) { throw new GradleException('android-publish.gradle need Gradle 4.0 or newer') - } + }*/ // For bintrayUpload def publicationName = "component${variant.name.capitalize()}" diff --git a/Android/MMKV/gradle/wrapper/gradle-wrapper.properties b/Android/MMKV/gradle/wrapper/gradle-wrapper.properties index 0b54a20f..9abe49d9 100644 --- a/Android/MMKV/gradle/wrapper/gradle-wrapper.properties +++ b/Android/MMKV/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 04 16:56:29 CST 2018 +#Wed May 08 16:12:37 CST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/Android/MMKV/mmkv/build.gradle b/Android/MMKV/mmkv/build.gradle index a392a6ab..43d6b427 100644 --- a/Android/MMKV/mmkv/build.gradle +++ b/Android/MMKV/mmkv/build.gradle @@ -4,7 +4,7 @@ android { // defaultPublishConfig "StaticCppRelease" defaultPublishConfig "SharedCppRelease" defaultConfig { - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags "-std=c++1z -fno-exceptions" @@ -19,7 +19,7 @@ android { minifyEnabled false externalNativeBuild { cmake { - cppFlags "-fvisibility=hidden", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables" + cppFlags "-fvisibility=hidden", "-funwind-tables", "-fasynchronous-unwind-tables" } } consumerProguardFiles 'proguard-rules.pro' @@ -61,8 +61,8 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:support-annotations:28.0.0' + implementation 'androidx.annotation:annotation:1.0.2' testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTest.java b/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTest.java index 61edeafc..7acc0ce4 100644 --- a/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTest.java +++ b/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTest.java @@ -23,7 +23,7 @@ import android.content.Context; import android.content.Intent; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import org.junit.AfterClass; import org.junit.BeforeClass; diff --git a/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTestService.java b/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTestService.java index 300c8940..c210e5ff 100644 --- a/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTestService.java +++ b/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTestService.java @@ -24,7 +24,7 @@ import android.content.Intent; import android.os.IBinder; import android.os.Process; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; public class MMKVTestService extends Service { public static final String SharedMMKVID = "SharedMMKVID"; diff --git a/Android/MMKV/mmkv/src/main/cpp/CodedOutputData.cpp b/Android/MMKV/mmkv/src/main/cpp/CodedOutputData.cpp index 8e608d1f..9eed953a 100644 --- a/Android/MMKV/mmkv/src/main/cpp/CodedOutputData.cpp +++ b/Android/MMKV/mmkv/src/main/cpp/CodedOutputData.cpp @@ -60,6 +60,11 @@ void CodedOutputData::writeBool(bool value) { void CodedOutputData::writeString(const string &value) { size_t numberOfBytes = value.size(); + if (m_position + numberOfBytes > m_size) { + MMKVError("m_position: %d, numberOfBytes: %zd, m_size: %zd", m_position, numberOfBytes, + m_size); + return; + } this->writeRawVarint32((int32_t) numberOfBytes); memcpy(m_ptr + m_position, ((uint8_t *) value.data()), numberOfBytes); m_position += numberOfBytes; @@ -93,6 +98,11 @@ void CodedOutputData::writeRawByte(uint8_t value) { void CodedOutputData::writeRawData(const MMBuffer &data) { size_t numberOfBytes = data.length(); + if (m_position + numberOfBytes > m_size) { + MMKVError("m_position: %d, numberOfBytes: %zd, m_size: %zd", m_position, numberOfBytes, + m_size); + return; + } memcpy(m_ptr + m_position, data.getPtr(), numberOfBytes); m_position += numberOfBytes; } diff --git a/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.cpp b/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.cpp index 44dce4a5..163e150b 100644 --- a/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.cpp +++ b/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.cpp @@ -20,26 +20,20 @@ #include "InterProcessLock.h" #include "MMKVLog.h" +#include #include -static short LockType2FlockType(LockType lockType) { +static int LockType2FlockType(LockType lockType) { switch (lockType) { case SharedLockType: - return F_RDLCK; + return LOCK_SH; case ExclusiveLockType: - return F_WRLCK; + return LOCK_EX; } + return LOCK_EX; } -FileLock::FileLock(int fd) : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0) { - 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; -} - -bool FileLock::doLock(LockType lockType, int cmd) { +bool FileLock::doLock(LockType lockType, bool wait) { if (!isFileLockValid()) { return false; } @@ -63,25 +57,23 @@ bool FileLock::doLock(LockType lockType, int cmd) { } } - m_lockInfo.l_type = LockType2FlockType(lockType); + int realLockType = LockType2FlockType(lockType); + int cmd = wait ? realLockType : (realLockType | LOCK_NB); if (unLockFirstIfNeeded) { // try lock - auto ret = fcntl(m_fd, F_SETLK, &m_lockInfo); + auto ret = flock(m_fd, realLockType | LOCK_NB); 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); + 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)); } - m_lockInfo.l_type = type; } - auto ret = fcntl(m_fd, cmd, &m_lockInfo); + auto ret = flock(m_fd, cmd); if (ret != 0) { MMKVError("fail to lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno)); return false; @@ -91,11 +83,11 @@ bool FileLock::doLock(LockType lockType, int cmd) { } bool FileLock::lock(LockType lockType) { - return doLock(lockType, F_SETLKW); + return doLock(lockType, true); } bool FileLock::try_lock(LockType lockType) { - return doLock(lockType, F_SETLK); + return doLock(lockType, false); } bool FileLock::unlock(LockType lockType) { @@ -127,8 +119,8 @@ bool FileLock::unlock(LockType lockType) { } } - m_lockInfo.l_type = static_cast(unlockToSharedLock ? F_RDLCK : F_UNLCK); - auto ret = fcntl(m_fd, F_SETLK, &m_lockInfo); + 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; diff --git a/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.h b/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.h index 5427b80d..1650e71d 100644 --- a/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.h +++ b/Android/MMKV/mmkv/src/main/cpp/InterProcessLock.h @@ -33,11 +33,10 @@ enum LockType { // handles lock upgrade & downgrade correctly class FileLock { int m_fd; - struct flock m_lockInfo; size_t m_sharedLockCount; size_t m_exclusiveLockCount; - bool doLock(LockType lockType, int cmd); + bool doLock(LockType lockType, bool wait); bool isFileLockValid() { return m_fd >= 0; } @@ -47,7 +46,7 @@ class FileLock { FileLock &operator=(const FileLock &other) = delete; public: - FileLock(int fd); + FileLock(int fd) : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0) {} bool lock(LockType lockType); diff --git a/Android/MMKV/mmkv/src/main/cpp/MMKV.cpp b/Android/MMKV/mmkv/src/main/cpp/MMKV.cpp index 9aa330db..a7342b50 100644 --- a/Android/MMKV/mmkv/src/main/cpp/MMKV.cpp +++ b/Android/MMKV/mmkv/src/main/cpp/MMKV.cpp @@ -334,14 +334,14 @@ void MMKV::loadFromFile() { if (checkFileCRCValid()) { loadFromFile = true; } else { - auto strategic = onMMKVCRCCheckFail(m_mmapID); + auto strategic = mmkv::onMMKVCRCCheckFail(m_mmapID); if (strategic == OnErrorRecover) { loadFromFile = true; needFullWriteback = true; } } } else { - auto strategic = onMMKVFileLengthError(m_mmapID); + auto strategic = mmkv::onMMKVFileLengthError(m_mmapID); if (strategic == OnErrorRecover) { writeAcutalSize(m_size - Fixed32Size); loadFromFile = true; @@ -633,7 +633,7 @@ void MMKV::trim() { fullWriteback(); auto oldSize = m_size; - while (m_size > (m_actualSize * 2)) { + while (m_size > (m_actualSize + Fixed32Size) * 2) { m_size /= 2; } if (oldSize == m_size) { @@ -642,7 +642,8 @@ void MMKV::trim() { return; } - MMKVInfo("trimming %s from %zu to %zu", m_mmapID.c_str(), oldSize, m_size); + MMKVInfo("trimming %s from %zu to %zu, actualSize %zu", m_mmapID.c_str(), oldSize, m_size, + m_actualSize); if (ftruncate(m_fd, m_size) != 0) { MMKVError("fail to truncate [%s] to size %zu, %s", m_mmapID.c_str(), m_size, @@ -676,7 +677,7 @@ bool MMKV::ensureMemorySize(size_t newSize) { if (newSize >= m_output->spaceLeft()) { // try a full rewrite to make space static const int offset = pbFixed32Size(0); - MMBuffer data = MiniPBCoder::encodeDataWithObject(m_dic); + MMBuffer data = m_dic.empty() ? MMBuffer(0) : MiniPBCoder::encodeDataWithObject(m_dic); size_t lenNeeded = data.length() + offset + newSize; if (m_isAshmem) { if (lenNeeded > m_size) { @@ -771,16 +772,12 @@ bool MMKV::setDataForKey(MMBuffer &&data, const std::string &key) { SCOPEDLOCK(m_exclusiveProcessLock); checkLoadData(); - // m_dic[key] = std::move(data); - auto itr = m_dic.find(key); - if (itr == m_dic.end()) { - itr = m_dic.emplace(key, std::move(data)).first; - } else { - itr->second = std::move(data); + auto ret = appendDataWithKey(data, key); + if (ret) { + m_dic[key] = std::move(data); + m_hasFullWriteback = false; } - m_hasFullWriteback = false; - - return appendDataWithKey(itr->second, key); + return ret; } bool MMKV::removeDataForKey(const std::string &key) { @@ -798,6 +795,8 @@ bool MMKV::removeDataForKey(const std::string &key) { return false; } +constexpr uint32_t ItemSizeHolder = 0x00ffffff, ItemSizeHolderSize = 4; + bool MMKV::appendDataWithKey(const MMBuffer &data, const std::string &key) { size_t keyLength = key.length(); // size needed to encode the key @@ -805,6 +804,12 @@ bool MMKV::appendDataWithKey(const MMBuffer &data, const std::string &key) { // size needed to encode the value size += data.length() + pbRawVarint32Size((int32_t) data.length()); + bool isFirstWrite = false; + if (m_actualSize == 0) { + isFirstWrite = true; + size += ItemSizeHolderSize; // size needed to encode the ItemSizeHolder + } + SCOPEDLOCK(m_exclusiveProcessLock); bool hasEnoughSize = ensureMemorySize(size); @@ -812,33 +817,24 @@ bool MMKV::appendDataWithKey(const MMBuffer &data, const std::string &key) { if (!hasEnoughSize || !isFileValid()) { return false; } - if (m_actualSize == 0) { - auto allData = MiniPBCoder::encodeDataWithObject(m_dic); - if (allData.length() > 0) { - if (m_crypter) { - m_crypter->reset(); - auto ptr = (unsigned char *) allData.getPtr(); - m_crypter->encrypt(ptr, ptr, allData.length()); - } - writeAcutalSize(allData.length()); - m_output->writeRawData(allData); // note: don't write size of data - recaculateCRCDigest(); - return true; - } - return false; - } else { - writeAcutalSize(m_actualSize + size); - m_output->writeString(key); - m_output->writeData(data); // note: write size of data + // write holder of size of m_dic + // which will be ignore while decoding + // yet it's required for decoding + if (isFirstWrite) { + m_output->writeInt32(ItemSizeHolder); + } - auto ptr = (uint8_t *) m_ptr + Fixed32Size + m_actualSize - size; - if (m_crypter) { - m_crypter->encrypt(ptr, ptr, size); - } - updateCRCDigest(ptr, size, KeepSequence); + writeAcutalSize(m_actualSize + size); + m_output->writeString(key); + m_output->writeData(data); // note: write size of data - return true; + auto ptr = (uint8_t *) m_ptr + Fixed32Size + m_actualSize - size; + if (m_crypter) { + m_crypter->encrypt(ptr, ptr, size); } + updateCRCDigest(ptr, size, KeepSequence); + + return true; } bool MMKV::fullWriteback() { diff --git a/Android/MMKV/mmkv/src/main/cpp/MMKVLog.cpp b/Android/MMKV/mmkv/src/main/cpp/MMKVLog.cpp index 084238bb..eca48c54 100644 --- a/Android/MMKV/mmkv/src/main/cpp/MMKVLog.cpp +++ b/Android/MMKV/mmkv/src/main/cpp/MMKVLog.cpp @@ -75,7 +75,7 @@ void _MMKVLogWithLevel( } if (g_isLogRedirecting) { - mmkvLog((int) level, file, line, func, message); + mmkv::mmkvLog((int) level, file, line, func, message); } else { __android_log_print(MMKVLogLevelDesc(level), APPNAME, "<%s:%d::%s> %s", file, line, func, message.c_str()); diff --git a/Android/MMKV/mmkv/src/main/cpp/native-bridge.cpp b/Android/MMKV/mmkv/src/main/cpp/native-bridge.cpp index 3cfbf1a3..483900dd 100644 --- a/Android/MMKV/mmkv/src/main/cpp/native-bridge.cpp +++ b/Android/MMKV/mmkv/src/main/cpp/native-bridge.cpp @@ -36,6 +36,8 @@ static jmethodID g_mmkvLogID = nullptr; static JavaVM *g_currentJVM = nullptr; int g_android_api = __ANDROID_API_L__; +static int registerNativeMethods(JNIEnv *env, jclass cls); + extern "C" JNIEXPORT JNICALL jint JNI_OnLoad(JavaVM *vm, void *reserved) { g_currentJVM = vm; JNIEnv *env; @@ -43,7 +45,6 @@ extern "C" JNIEXPORT JNICALL jint JNI_OnLoad(JavaVM *vm, void *reserved) { return -1; } - // Get jclass with env->FindClass. if (g_cls) { env->DeleteGlobalRef(g_cls); } @@ -54,10 +55,21 @@ extern "C" JNIEXPORT JNICALL jint JNI_OnLoad(JavaVM *vm, void *reserved) { return -2; } g_cls = reinterpret_cast(env->NewGlobalRef(instance)); + if (!g_cls) { + MMKVError("fail to create global reference for %s", clsName); + return -3; + } + int ret = registerNativeMethods(env, g_cls); + if (ret != 0) { + MMKVError("fail to register native methods for class %s, ret = %d", clsName, ret); + return -4; + } g_fileID = env->GetFieldID(g_cls, "nativeHandle", "J"); - if (!g_cls || !g_fileID) { + if (!g_fileID) { MMKVError("fail to locate fileID"); + return -5; } + g_callbackOnCRCFailID = env->GetStaticMethodID(g_cls, "onMMKVCRCCheckFail", "(Ljava/lang/String;)I"); if (!g_callbackOnCRCFailID) { @@ -91,8 +103,12 @@ extern "C" JNIEXPORT JNICALL jint JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_VERSION_1_6; } -extern "C" JNIEXPORT JNICALL void -Java_com_tencent_mmkv_MMKV_jniInitialize(JNIEnv *env, jobject obj, jstring rootDir) { +//#define MMKV_JNI extern "C" JNIEXPORT JNICALL +#define MMKV_JNI static + +namespace mmkv { + +MMKV_JNI void jniInitialize(JNIEnv *env, jobject obj, jstring rootDir) { if (!rootDir) { return; } @@ -103,7 +119,7 @@ Java_com_tencent_mmkv_MMKV_jniInitialize(JNIEnv *env, jobject obj, jstring rootD } } -extern "C" JNIEXPORT JNICALL void Java_com_tencent_mmkv_MMKV_onExit(JNIEnv *env, jobject obj) { +MMKV_JNI void onExit(JNIEnv *env, jobject obj) { MMKV::onExit(); } @@ -208,8 +224,8 @@ void mmkvLog(int level, } } -extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithID( - JNIEnv *env, jobject obj, jstring mmapID, jint mode, jstring cryptKey, jstring relativePath) { +MMKV_JNI jlong getMMKVWithID( + JNIEnv *env, jobject, jstring mmapID, jint mode, jstring cryptKey, jstring relativePath) { MMKV *kv = nullptr; if (!mmapID) { return (jlong) kv; @@ -241,7 +257,7 @@ extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithID( return (jlong) kv; } -extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithIDAndSize( +MMKV_JNI jlong getMMKVWithIDAndSize( JNIEnv *env, jobject obj, jstring mmapID, jint size, jint mode, jstring cryptKey) { MMKV *kv = nullptr; if (!mmapID || size < 0) { @@ -261,10 +277,7 @@ extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithIDAndSi return (jlong) kv; } -extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getDefaultMMKV(JNIEnv *env, - jobject obj, - jint mode, - jstring cryptKey) { +MMKV_JNI jlong getDefaultMMKV(JNIEnv *env, jobject obj, jint mode, jstring cryptKey) { MMKV *kv = nullptr; if (cryptKey) { @@ -280,7 +293,7 @@ extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getDefaultMMKV(JNI return (jlong) kv; } -extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithAshmemFD( +MMKV_JNI jlong getMMKVWithAshmemFD( JNIEnv *env, jobject obj, jstring mmapID, jint fd, jint metaFD, jstring cryptKey) { MMKV *kv = nullptr; if (!mmapID || fd < 0 || metaFD < 0) { @@ -301,8 +314,7 @@ extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithAshmemF return (jlong) kv; } -extern "C" JNIEXPORT JNICALL jstring Java_com_tencent_mmkv_MMKV_mmapID(JNIEnv *env, - jobject instance) { +MMKV_JNI jstring mmapID(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { return string2jstring(env, kv->mmapID()); @@ -310,8 +322,7 @@ extern "C" JNIEXPORT JNICALL jstring Java_com_tencent_mmkv_MMKV_mmapID(JNIEnv *e return nullptr; } -extern "C" JNIEXPORT JNICALL jint Java_com_tencent_mmkv_MMKV_ashmemFD(JNIEnv *env, - jobject instance) { +MMKV_JNI jint ashmemFD(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { return kv->ashmemFD(); @@ -319,8 +330,7 @@ extern "C" JNIEXPORT JNICALL jint Java_com_tencent_mmkv_MMKV_ashmemFD(JNIEnv *en return -1; } -extern "C" JNIEXPORT JNICALL jint Java_com_tencent_mmkv_MMKV_ashmemMetaFD(JNIEnv *env, - jobject instance) { +MMKV_JNI jint ashmemMetaFD(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { return kv->ashmemMetaFD(); @@ -328,8 +338,7 @@ extern "C" JNIEXPORT JNICALL jint Java_com_tencent_mmkv_MMKV_ashmemMetaFD(JNIEnv return -1; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeBool( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jboolean value) { +MMKV_JNI jboolean encodeBool(JNIEnv *env, jobject, jlong handle, jstring oKey, jboolean value) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -338,8 +347,8 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeBool( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_decodeBool( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jboolean defaultValue) { +MMKV_JNI jboolean +decodeBool(JNIEnv *env, jobject, jlong handle, jstring oKey, jboolean defaultValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -348,8 +357,7 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_decodeBool( return defaultValue; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeInt( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jint value) { +MMKV_JNI jboolean encodeInt(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jint value) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -358,8 +366,7 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeInt( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jint Java_com_tencent_mmkv_MMKV_decodeInt( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jint defaultValue) { +MMKV_JNI jint decodeInt(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jint defaultValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -368,8 +375,7 @@ extern "C" JNIEXPORT JNICALL jint Java_com_tencent_mmkv_MMKV_decodeInt( return defaultValue; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeLong( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jlong value) { +MMKV_JNI jboolean encodeLong(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jlong value) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -378,8 +384,8 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeLong( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_decodeLong( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jlong defaultValue) { +MMKV_JNI jlong +decodeLong(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jlong defaultValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -388,8 +394,7 @@ extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_decodeLong( return defaultValue; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeFloat( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jfloat value) { +MMKV_JNI jboolean encodeFloat(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jfloat value) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -398,8 +403,7 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeFloat( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jfloat Java_com_tencent_mmkv_MMKV_decodeFloat( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jfloat defaultValue) { +MMKV_JNI jfloat decodeFloat(JNIEnv *env, jobject, jlong handle, jstring oKey, jfloat defaultValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -408,8 +412,8 @@ extern "C" JNIEXPORT JNICALL jfloat Java_com_tencent_mmkv_MMKV_decodeFloat( return defaultValue; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeDouble( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jdouble value) { +MMKV_JNI jboolean +encodeDouble(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jdouble value) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -418,8 +422,8 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeDouble( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jdouble Java_com_tencent_mmkv_MMKV_decodeDouble( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jdouble defaultValue) { +MMKV_JNI jdouble +decodeDouble(JNIEnv *env, jobject, jlong handle, jstring oKey, jdouble defaultValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -428,8 +432,7 @@ extern "C" JNIEXPORT JNICALL jdouble Java_com_tencent_mmkv_MMKV_decodeDouble( return defaultValue; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeString( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jstring oValue) { +MMKV_JNI jboolean encodeString(JNIEnv *env, jobject, jlong handle, jstring oKey, jstring oValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -444,8 +447,8 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeString( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jstring Java_com_tencent_mmkv_MMKV_decodeString( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jstring oDefaultValue) { +MMKV_JNI jstring +decodeString(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jstring oDefaultValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -458,8 +461,7 @@ extern "C" JNIEXPORT JNICALL jstring Java_com_tencent_mmkv_MMKV_decodeString( return oDefaultValue; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeBytes( - JNIEnv *env, jobject obj, jlong handle, jstring oKey, jbyteArray oValue) { +MMKV_JNI jboolean encodeBytes(JNIEnv *env, jobject, jlong handle, jstring oKey, jbyteArray oValue) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -484,10 +486,7 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeBytes( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jbyteArray Java_com_tencent_mmkv_MMKV_decodeBytes(JNIEnv *env, - jobject obj, - jlong handle, - jstring oKey) { +MMKV_JNI jbyteArray decodeBytes(JNIEnv *env, jobject obj, jlong handle, jstring oKey) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -501,8 +500,7 @@ extern "C" JNIEXPORT JNICALL jbyteArray Java_com_tencent_mmkv_MMKV_decodeBytes(J return nullptr; } -extern "C" JNIEXPORT JNICALL jobjectArray Java_com_tencent_mmkv_MMKV_allKeys(JNIEnv *env, - jobject instance) { +MMKV_JNI jobjectArray allKeys(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { vector keys = kv->allKeys(); @@ -511,10 +509,7 @@ extern "C" JNIEXPORT JNICALL jobjectArray Java_com_tencent_mmkv_MMKV_allKeys(JNI return nullptr; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_containsKey(JNIEnv *env, - jobject instance, - jlong handle, - jstring oKey) { +MMKV_JNI jboolean containsKey(JNIEnv *env, jobject instance, jlong handle, jstring oKey) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -523,9 +518,7 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_containsKey(JNI return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_count(JNIEnv *env, - jobject instance, - jlong handle) { +MMKV_JNI jlong count(JNIEnv *env, jobject instance, jlong handle) { MMKV *kv = reinterpret_cast(handle); if (kv) { jlong size = kv->count(); @@ -534,9 +527,7 @@ extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_count(JNIEnv *env, return 0; } -extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_totalSize(JNIEnv *env, - jobject instance, - jlong handle) { +MMKV_JNI jlong totalSize(JNIEnv *env, jobject instance, jlong handle) { MMKV *kv = reinterpret_cast(handle); if (kv) { jlong size = kv->totalSize(); @@ -545,10 +536,7 @@ extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_totalSize(JNIEnv * return 0; } -extern "C" JNIEXPORT JNICALL void Java_com_tencent_mmkv_MMKV_removeValueForKey(JNIEnv *env, - jobject instance, - jlong handle, - jstring oKey) { +MMKV_JNI void removeValueForKey(JNIEnv *env, jobject instance, jlong handle, jstring oKey) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -556,8 +544,7 @@ extern "C" JNIEXPORT JNICALL void Java_com_tencent_mmkv_MMKV_removeValueForKey(J } } -extern "C" JNIEXPORT JNICALL void Java_com_tencent_mmkv_MMKV_removeValuesForKeys( - JNIEnv *env, jobject instance, jobjectArray arrKeys) { +MMKV_JNI void removeValuesForKeys(JNIEnv *env, jobject instance, jobjectArray arrKeys) { MMKV *kv = getMMKV(env, instance); if (kv && arrKeys) { vector keys = jarray2vector(env, arrKeys); @@ -567,25 +554,21 @@ extern "C" JNIEXPORT JNICALL void Java_com_tencent_mmkv_MMKV_removeValuesForKeys } } -extern "C" JNIEXPORT JNICALL void Java_com_tencent_mmkv_MMKV_clearAll(JNIEnv *env, - jobject instance) { +MMKV_JNI void clearAll(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { kv->clearAll(); } } -extern "C" JNIEXPORT JNICALL void -Java_com_tencent_mmkv_MMKV_sync(JNIEnv *env, jobject instance, jboolean sync) { +MMKV_JNI void sync(JNIEnv *env, jobject instance, jboolean sync) { MMKV *kv = getMMKV(env, instance); if (kv) { kv->sync((bool) sync); } } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_isFileValid(JNIEnv *env, - jclass type, - jstring oMmapID) { +MMKV_JNI jboolean isFileValid(JNIEnv *env, jclass type, jstring oMmapID) { if (oMmapID) { string mmapID = jstring2string(env, oMmapID); return (jboolean) MMKV::isFileValid(mmapID); @@ -593,8 +576,7 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_isFileValid(JNI return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeSet( - JNIEnv *env, jobject instance, jlong handle, jstring oKey, jobjectArray arrStr) { +MMKV_JNI jboolean encodeSet(JNIEnv *env, jobject, jlong handle, jstring oKey, jobjectArray arrStr) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -609,8 +591,7 @@ extern "C" JNIEXPORT JNICALL jboolean Java_com_tencent_mmkv_MMKV_encodeSet( return (jboolean) false; } -extern "C" JNIEXPORT JNICALL jobjectArray Java_com_tencent_mmkv_MMKV_decodeStringSet( - JNIEnv *env, jobject instance, jlong handle, jstring oKey) { +MMKV_JNI jobjectArray decodeStringSet(JNIEnv *env, jobject, jlong handle, jstring oKey) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -623,30 +604,28 @@ extern "C" JNIEXPORT JNICALL jobjectArray Java_com_tencent_mmkv_MMKV_decodeStrin return nullptr; } -extern "C" JNIEXPORT JNICALL void Java_com_tencent_mmkv_MMKV_clearMemoryCache(JNIEnv *env, - jobject instance) { +MMKV_JNI void clearMemoryCache(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { kv->clearMemoryState(); } } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_lock(JNIEnv *env, jobject instance) { +MMKV_JNI void lock(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { kv->lock(); } } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_unlock(JNIEnv *env, jobject instance) { +MMKV_JNI void unlock(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { kv->unlock(); } } -extern "C" JNIEXPORT jboolean JNICALL Java_com_tencent_mmkv_MMKV_tryLock(JNIEnv *env, - jobject instance) { +MMKV_JNI jboolean tryLock(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { return (jboolean) kv->try_lock(); @@ -654,12 +633,11 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_tencent_mmkv_MMKV_tryLock(JNIEnv return jboolean(false); } -extern "C" JNIEXPORT jint JNICALL Java_com_tencent_mmkv_MMKV_pageSize(JNIEnv *env, jclass type) { +MMKV_JNI jint pageSize(JNIEnv *env, jclass type) { return DEFAULT_MMAP_SIZE; } -extern "C" JNIEXPORT jstring JNICALL Java_com_tencent_mmkv_MMKV_cryptKey(JNIEnv *env, - jobject instance) { +MMKV_JNI jstring cryptKey(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { string cryptKey = kv->cryptKey(); @@ -670,9 +648,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_com_tencent_mmkv_MMKV_cryptKey(JNIEnv return nullptr; } -extern "C" JNIEXPORT jboolean JNICALL Java_com_tencent_mmkv_MMKV_reKey(JNIEnv *env, - jobject instance, - jstring cryptKey) { +MMKV_JNI jboolean reKey(JNIEnv *env, jobject instance, jstring cryptKey) { MMKV *kv = getMMKV(env, instance); if (kv) { string newKey; @@ -684,9 +660,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_tencent_mmkv_MMKV_reKey(JNIEnv *e return (jboolean) false; } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_checkReSetCryptKey(JNIEnv *env, - jobject instance, - jstring cryptKey) { +MMKV_JNI void checkReSetCryptKey(JNIEnv *env, jobject instance, jstring cryptKey) { MMKV *kv = getMMKV(env, instance); if (kv) { string newKey; @@ -702,14 +676,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_checkReSetCryptKey( } } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_trim(JNIEnv *env, jobject instance) { +MMKV_JNI void trim(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { kv->trim(); } } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_close(JNIEnv *env, jobject instance) { +MMKV_JNI void close(JNIEnv *env, jobject instance) { MMKV *kv = getMMKV(env, instance); if (kv) { kv->close(); @@ -717,8 +691,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_close(JNIEnv *env, } } -extern "C" JNIEXPORT jint JNICALL Java_com_tencent_mmkv_MMKV_valueSize( - JNIEnv *env, jobject instance, jlong handle, jstring oKey, jboolean actualSize) { +MMKV_JNI jint valueSize(JNIEnv *env, jobject, jlong handle, jstring oKey, jboolean actualSize) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { string key = jstring2string(env, oKey); @@ -727,21 +700,15 @@ extern "C" JNIEXPORT jint JNICALL Java_com_tencent_mmkv_MMKV_valueSize( return 0; } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_setLogLevel(JNIEnv *env, - jclass type, - jint level) { +MMKV_JNI void setLogLevel(JNIEnv *env, jclass type, jint level) { g_currentLogLevel = (MMKVLogLevel) level; } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_setLogReDirecting(JNIEnv *env, - jclass type, - jboolean enable) { +MMKV_JNI void setLogReDirecting(JNIEnv *env, jclass type, jboolean enable) { g_isLogRedirecting = (enable == JNI_TRUE); } -extern "C" JNIEXPORT jlong JNICALL Java_com_tencent_mmkv_MMKV_createNB(JNIEnv *env, - jobject instance, - jint size) { +MMKV_JNI jlong createNB(JNIEnv *env, jobject instance, jint size) { auto ptr = malloc(static_cast(size)); if (!ptr) { MMKVError("fail to create NativeBuffer:%s", strerror(errno)); @@ -750,14 +717,11 @@ extern "C" JNIEXPORT jlong JNICALL Java_com_tencent_mmkv_MMKV_createNB(JNIEnv *e return reinterpret_cast(ptr); } -extern "C" JNIEXPORT void JNICALL Java_com_tencent_mmkv_MMKV_destroyNB(JNIEnv *env, - jobject instance, - jlong pointer, - jint size) { +MMKV_JNI void destroyNB(JNIEnv *env, jobject instance, jlong pointer, jint size) { free(reinterpret_cast(pointer)); } -extern "C" JNIEXPORT jint JNICALL Java_com_tencent_mmkv_MMKV_writeValueToNB( +MMKV_JNI jint writeValueToNB( JNIEnv *env, jobject instance, jlong handle, jstring oKey, jlong pointer, jint size) { MMKV *kv = reinterpret_cast(handle); if (kv && oKey) { @@ -766,3 +730,66 @@ extern "C" JNIEXPORT jint JNICALL Java_com_tencent_mmkv_MMKV_writeValueToNB( } return -1; } + +} // namespace mmkv + +static JNINativeMethod g_methods[] = { + {"onExit", "()V", (void *) mmkv::onExit}, + {"cryptKey", "()Ljava/lang/String;", (void *) mmkv::cryptKey}, + {"reKey", "(Ljava/lang/String;)Z", (void *) mmkv::reKey}, + {"checkReSetCryptKey", "(Ljava/lang/String;)V", (void *) mmkv::checkReSetCryptKey}, + {"pageSize", "()I", (void *) mmkv::pageSize}, + {"mmapID", "()Ljava/lang/String;", (void *) mmkv::mmapID}, + {"lock", "()V", (void *) mmkv::lock}, + {"unlock", "()V", (void *) mmkv::unlock}, + {"tryLock", "()Z", (void *) mmkv::tryLock}, + {"allKeys", "()[Ljava/lang/String;", (void *) mmkv::allKeys}, + {"removeValuesForKeys", "([Ljava/lang/String;)V", (void *) mmkv::removeValuesForKeys}, + {"clearAll", "()V", (void *) mmkv::clearAll}, + {"trim", "()V", (void *) mmkv::trim}, + {"close", "()V", (void *) mmkv::close}, + {"clearMemoryCache", "()V", (void *) mmkv::clearMemoryCache}, + {"sync", "(Z)V", (void *) mmkv::sync}, + {"isFileValid", "(Ljava/lang/String;)Z", (void *) mmkv::isFileValid}, + {"ashmemFD", "()I", (void *) mmkv::ashmemFD}, + {"ashmemMetaFD", "()I", (void *) mmkv::ashmemMetaFD}, + {"jniInitialize", "(Ljava/lang/String;)V", (void *) mmkv::jniInitialize}, + {"getMMKVWithID", "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)J", + (void *) mmkv::getMMKVWithID}, + {"getMMKVWithIDAndSize", "(Ljava/lang/String;IILjava/lang/String;)J", + (void *) mmkv::getMMKVWithIDAndSize}, + {"getDefaultMMKV", "(ILjava/lang/String;)J", (void *) mmkv::getDefaultMMKV}, + {"getMMKVWithAshmemFD", "(Ljava/lang/String;IILjava/lang/String;)J", + (void *) mmkv::getMMKVWithAshmemFD}, + {"encodeBool", "(JLjava/lang/String;Z)Z", (void *) mmkv::encodeBool}, + {"decodeBool", "(JLjava/lang/String;Z)Z", (void *) mmkv::decodeBool}, + {"encodeInt", "(JLjava/lang/String;I)Z", (void *) mmkv::encodeInt}, + {"decodeInt", "(JLjava/lang/String;I)I", (void *) mmkv::decodeInt}, + {"encodeLong", "(JLjava/lang/String;J)Z", (void *) mmkv::encodeLong}, + {"decodeLong", "(JLjava/lang/String;J)J", (void *) mmkv::decodeLong}, + {"encodeFloat", "(JLjava/lang/String;F)Z", (void *) mmkv::encodeFloat}, + {"decodeFloat", "(JLjava/lang/String;F)F", (void *) mmkv::decodeFloat}, + {"encodeDouble", "(JLjava/lang/String;D)Z", (void *) mmkv::encodeDouble}, + {"decodeDouble", "(JLjava/lang/String;D)D", (void *) mmkv::decodeDouble}, + {"encodeString", "(JLjava/lang/String;Ljava/lang/String;)Z", (void *) mmkv::encodeString}, + {"decodeString", "(JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void *) mmkv::decodeString}, + {"encodeSet", "(JLjava/lang/String;[Ljava/lang/String;)Z", (void *) mmkv::encodeSet}, + {"decodeStringSet", "(JLjava/lang/String;)[Ljava/lang/String;", (void *) mmkv::decodeStringSet}, + {"encodeBytes", "(JLjava/lang/String;[B)Z", (void *) mmkv::encodeBytes}, + {"decodeBytes", "(JLjava/lang/String;)[B", (void *) mmkv::decodeBytes}, + {"containsKey", "(JLjava/lang/String;)Z", (void *) mmkv::containsKey}, + {"count", "(J)J", (void *) mmkv::count}, + {"totalSize", "(J)J", (void *) mmkv::totalSize}, + {"removeValueForKey", "(JLjava/lang/String;)V", (void *) mmkv::removeValueForKey}, + {"valueSize", "(JLjava/lang/String;Z)I", (void *) mmkv::valueSize}, + {"setLogLevel", "(I)V", (void *) mmkv::setLogLevel}, + {"setLogReDirecting", "(Z)V", (void *) mmkv::setLogReDirecting}, + {"createNB", "(I)J", (void *) mmkv::createNB}, + {"destroyNB", "(JI)V", (void *) mmkv::destroyNB}, + {"writeValueToNB", "(JLjava/lang/String;JI)I", (void *) mmkv::writeValueToNB}, +}; + +static int registerNativeMethods(JNIEnv *env, jclass cls) { + return env->RegisterNatives(cls, g_methods, sizeof(g_methods) / sizeof(g_methods[0])); +} diff --git a/Android/MMKV/mmkv/src/main/cpp/native-bridge.h b/Android/MMKV/mmkv/src/main/cpp/native-bridge.h index 0b26111f..117b5e61 100644 --- a/Android/MMKV/mmkv/src/main/cpp/native-bridge.h +++ b/Android/MMKV/mmkv/src/main/cpp/native-bridge.h @@ -28,6 +28,8 @@ enum MMKVRecoverStrategic : int { OnErrorRecover, }; +namespace mmkv { + MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID); MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID); @@ -37,6 +39,7 @@ void mmkvLog(int level, int line, const std::string &function, const std::string &message); +} // namespace mmkv extern int g_android_api; diff --git a/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKV.java b/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKV.java index 46bfe6db..1bc793a0 100644 --- a/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKV.java +++ b/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKV.java @@ -27,7 +27,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.Log; import java.lang.reflect.Field; diff --git a/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVContentProvider.java b/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVContentProvider.java index 6f46bdd0..8ccc9515 100644 --- a/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVContentProvider.java +++ b/Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVContentProvider.java @@ -31,8 +31,8 @@ import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.util.Log; public class MMKVContentProvider extends ContentProvider { diff --git a/Android/MMKV/mmkvdemo/build.gradle b/Android/MMKV/mmkvdemo/build.gradle index 6381eca4..8733b102 100644 --- a/Android/MMKV/mmkvdemo/build.gradle +++ b/Android/MMKV/mmkvdemo/build.gradle @@ -20,7 +20,7 @@ android { versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -63,13 +63,13 @@ repositories { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') // implementation project(':mmkv') - implementation 'com.tencent:mmkv:1.0.19' -// implementation 'com.tencent:mmkv-static:1.0.19' - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.tencent:mmkv:1.0.20' +// implementation 'com.tencent:mmkv-static:1.0.20' + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.getkeepsafe.relinker:relinker:1.3.1' } diff --git a/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/BenchMarkBaseService.java b/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/BenchMarkBaseService.java index 3f6412d8..e1fd9f93 100644 --- a/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/BenchMarkBaseService.java +++ b/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/BenchMarkBaseService.java @@ -24,7 +24,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.IBinder; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.Log; import java.util.Random; diff --git a/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MainActivity.java b/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MainActivity.java index 41862727..fe67eac1 100644 --- a/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MainActivity.java +++ b/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MainActivity.java @@ -23,7 +23,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.SystemClock; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; @@ -54,8 +54,8 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); // set root dir - // String rootDir = "mmkv root: " + MMKV.initialize(this); - String dir = getFilesDir().getAbsolutePath() + "/mmkv_2"; + //String rootDir = MMKV.initialize(this); + String dir = getFilesDir().getAbsolutePath() + "/mmkv"; String rootDir = MMKV.initialize(dir, new MMKV.LibLoader() { @Override public void loadLibrary(String libName) { @@ -85,6 +85,7 @@ public void onClick(View v) { baseline.sqliteBaselineTest(); //testInterProcessReKey(); + //testInterProcessLockPhase2(); } }); @@ -133,7 +134,9 @@ public void onClick(View v) { KotlinUsecaseKt.kotlinFunctionalTest(); //testInterProcessLogic(); - //estImportSharedPreferences(); + //testImportSharedPreferences(); + //testInterProcessLockPhase1(); + //testCornerSize(); } @Override @@ -402,6 +405,35 @@ public void run() { } } + private void testInterProcessLockPhase1() { + MMKV mmkv1 = MMKV.mmkvWithID(MyService.LOCK_PHASE_1, MMKV.MULTI_PROCESS_MODE); + mmkv1.lock(); + Log.d("locked in main", MyService.LOCK_PHASE_1); + + Intent intent = new Intent(this, MyService.class); + intent.putExtra(BenchMarkBaseService.CMD_ID, MyService.CMD_LOCK); + startService(intent); + } + private void testInterProcessLockPhase2() { + MMKV mmkv2 = MMKV.mmkvWithID(MyService.LOCK_PHASE_2, MMKV.MULTI_PROCESS_MODE); + mmkv2.lock(); + Log.d("locked in main", MyService.LOCK_PHASE_2); + } + + private void testCornerSize() { + MMKV mmkv = MMKV.mmkvWithID("cornerSize", MMKV.MULTI_PROCESS_MODE, "aes"); + mmkv.clearAll(); + int size = MMKV.pageSize() - 2; + size -= 4; + String key = "key"; + int keySize = 3 + 1; + size -= keySize; + int valueSize = 3; + size -= valueSize; + byte[] value = new byte[size]; + mmkv.encode(key, value); + } + @Override public MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID) { return MMKVRecoverStrategic.OnErrorRecover; diff --git a/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MyService.java b/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MyService.java index e1e9763b..1db67667 100644 --- a/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MyService.java +++ b/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MyService.java @@ -29,6 +29,10 @@ public class MyService extends BenchMarkBaseService { private static final String CALLER = "MyService"; public static final String CMD_REMOVE = "cmd_remove"; + public static final String CMD_LOCK = "cmd_lock"; + public static final String LOCK_PHASE_1 = "lock_phase_1"; + public static final String LOCK_PHASE_2 = "lock_phase_2"; + @Override public void onCreate() { super.onCreate(); @@ -59,6 +63,8 @@ public int onStartCommand(Intent intent, int flags, int startId) { super.prepareAshmemMMKVByCP(); } else if (cmd.equals(CMD_REMOVE)) { testRemove(); + } else if (cmd.equals(CMD_LOCK)) { + testLock(); } } } @@ -70,4 +76,22 @@ private void testRemove() { Log.d("mmkv in child", "" + mmkv.decodeInt(CMD_ID)); mmkv.remove(CMD_ID); } + + private void testLock() { + // get the lock immediately + MMKV mmkv2 = MMKV.mmkvWithID(LOCK_PHASE_2, MMKV.MULTI_PROCESS_MODE); + mmkv2.lock(); + Log.d("locked in child", LOCK_PHASE_2); + + Runnable waiter = new Runnable() { + @Override + public void run() { + MMKV mmkv1 = MMKV.mmkvWithID(LOCK_PHASE_1, MMKV.MULTI_PROCESS_MODE); + mmkv1.lock(); + Log.d("locked in child", LOCK_PHASE_1); + } + }; + // wait infinitely + new Thread(waiter).start(); + } } diff --git a/Android/MMKV/mmkvdemo/src/main/res/layout/activity_main.xml b/Android/MMKV/mmkvdemo/src/main/res/layout/activity_main.xml index 33581c34..d442f882 100644 --- a/Android/MMKV/mmkvdemo/src/main/res/layout/activity_main.xml +++ b/Android/MMKV/mmkvdemo/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dc80b185..ebe0f9a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # MMKV Change Log +## v1.0.20 / 2019-06-05 +### iOS / macOS +What's new + +* Fix a bug that MMKV might crash while storing key-value with specific length. +* Fix a bug that `-[MMKV trim]` might not work properly. + +### Android +What's new + +* Migrate to AndroidX library. +* Fix a bug that MMKV might crash while storing key-value with specific length. +* Fix a bug that `trim()` might not work properly. +* Using `RegisterNatives()` to simpify native method naming. + +### Win32 +* Fix a bug that MMKV might crash while storing key-value with specific length. +* Fix a bug that `trim()` might not work properly. +* Fix a bug that `clearAll()` might not work properly. + ## v1.0.19 / 2019-04-22 ### iOS / macOS What's new diff --git a/MMKV.podspec b/MMKV.podspec index 5f01cd92..dc63ea14 100644 --- a/MMKV.podspec +++ b/MMKV.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MMKV" - s.version = "1.0.19" + s.version = "1.0.20" s.summary = "MMKV is a cross-platform key-value storage framework developed by WeChat." s.description = <<-DESC diff --git a/README.md b/README.md index f53a76f6..818b53be 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![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.0.19-brightgreen.svg)](https://github.com/Tencent/MMKV/releases) +[![Release Version](https://img.shields.io/badge/release-1.0.20-brightgreen.svg)](https://github.com/Tencent/MMKV/releases) [![Platform](https://img.shields.io/badge/Platform-%20iOS%20%7C%20Android-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home) 中文版本请参看[这里](./readme_cn.md) @@ -75,8 +75,8 @@ Add the following lines to `build.gradle` on your app module: ```gradle dependencies { - implementation 'com.tencent:mmkv:1.0.19' - // replace "1.0.19" with any available version + implementation 'com.tencent:mmkv:1.0.20' + // replace "1.0.20" with any available version } ``` diff --git a/Win32/MMKV/MMKV.cpp b/Win32/MMKV/MMKV.cpp index a8a0f25f..b92ec64e 100644 --- a/Win32/MMKV/MMKV.cpp +++ b/Win32/MMKV/MMKV.cpp @@ -382,6 +382,19 @@ void MMKV::clearAll() { if (m_size != DEFAULT_MMAP_SIZE) { MMKVInfo("truncating [%s] from %zu to %zd", m_mmapID.c_str(), m_size, DEFAULT_MMAP_SIZE); + if (m_ptr) { + if (!UnmapViewOfFile(m_ptr)) { + MMKVError("fail to munmap [%s], %d", m_mmapID.c_str(), GetLastError()); + } + m_ptr = nullptr; + } + + if (m_fileMapping) { + if (!CloseHandle(m_fileMapping)) { + MMKVError("fail to CloseHandle [%s], %d", m_mmapID.c_str(), GetLastError()); + } + m_fileMapping = nullptr; + } if (!ftruncate(m_fd, DEFAULT_MMAP_SIZE)) { MMKVError("fail to truncate [%s] to size %zd", m_mmapID.c_str(), DEFAULT_MMAP_SIZE); } @@ -464,7 +477,7 @@ void MMKV::trim() { fullWriteback(); auto oldSize = m_size; - while (m_size > (m_actualSize * 2)) { + while (m_size > (m_actualSize + Fixed32Size) * 2) { m_size /= 2; } if (oldSize == m_size) { @@ -473,19 +486,21 @@ void MMKV::trim() { return; } - MMKVInfo("trimming %s from %zu to %zu", m_mmapID.c_str(), oldSize, m_size); + MMKVInfo("trimming %s from %zu to %zu, acutal size %zu", m_mmapID.c_str(), oldSize, m_size, + m_actualSize); - if (!ftruncate(m_fd, m_size)) { - MMKVError("fail to truncate [%s] to size %zu", m_mmapID.c_str(), m_size); - m_size = oldSize; - return; - } if (!UnmapViewOfFile(m_ptr)) { MMKVError("fail to munmap [%s], %d", m_mmapID.c_str(), GetLastError()); } m_ptr = nullptr; CloseHandle(m_fileMapping); + + if (!ftruncate(m_fd, m_size)) { + MMKVError("fail to truncate [%s] to size %zu", m_mmapID.c_str(), m_size); + m_size = oldSize; + } + m_fileMapping = CreateFileMapping(m_fd, nullptr, PAGE_READWRITE, 0, 0, nullptr); if (!m_fileMapping) { MMKVError("fail to CreateFileMapping [%s], %d", m_mmapID.c_str(), GetLastError()); @@ -495,7 +510,6 @@ void MMKV::trim() { MMKVError("fail to mmap [%s], %d", m_mmapID.c_str(), GetLastError()); } } - delete m_output; m_output = new CodedOutputData(m_ptr + pbFixed32Size(0), m_size - pbFixed32Size(0)); m_output->seek(m_actualSize); @@ -514,7 +528,7 @@ bool MMKV::ensureMemorySize(size_t newSize) { if (newSize >= m_output->spaceLeft()) { // try a full rewrite to make space static const int offset = pbFixed32Size(0); - MMBuffer data = MiniPBCoder::encodeDataWithObject(m_dic); + MMBuffer data = m_dic.empty() ? MMBuffer(0) : MiniPBCoder::encodeDataWithObject(m_dic); size_t lenNeeded = data.length() + offset + newSize; size_t avgItemSize = lenNeeded / std::max(1, m_dic.size()); size_t futureUsage = avgItemSize * std::max(8, (m_dic.size() + 1) / 2); @@ -605,16 +619,12 @@ bool MMKV::setDataForKey(MMBuffer &&data, const std::string &key) { SCOPEDLOCK(m_exclusiveProcessLock); checkLoadData(); - // m_dic[key] = std::move(data); - auto itr = m_dic.find(key); - if (itr == m_dic.end()) { - itr = m_dic.emplace(key, std::move(data)).first; - } else { - itr->second = std::move(data); + auto ret = appendDataWithKey(data, key); + if (ret) { + m_dic[key] = std::move(data); + m_hasFullWriteback = false; } - m_hasFullWriteback = false; - - return appendDataWithKey(itr->second, key); + return ret; } bool MMKV::removeDataForKey(const std::string &key) { @@ -632,6 +642,8 @@ bool MMKV::removeDataForKey(const std::string &key) { return false; } +constexpr uint32_t ItemSizeHolder = 0x00ffffff, ItemSizeHolderSize = 4; + bool MMKV::appendDataWithKey(const MMBuffer &data, const std::string &key) { size_t keyLength = key.length(); // size needed to encode the key @@ -639,6 +651,12 @@ bool MMKV::appendDataWithKey(const MMBuffer &data, const std::string &key) { // size needed to encode the value size += data.length() + pbRawVarint32Size((int32_t) data.length()); + bool isFirstWrite = false; + if (m_actualSize == 0) { + isFirstWrite = true; + size += ItemSizeHolderSize; // size needed to encode the ItemSizeHolder + } + SCOPEDLOCK(m_exclusiveProcessLock); bool hasEnoughSize = ensureMemorySize(size); @@ -646,33 +664,24 @@ bool MMKV::appendDataWithKey(const MMBuffer &data, const std::string &key) { if (!hasEnoughSize || !isFileValid()) { return false; } - if (m_actualSize == 0) { - auto allData = MiniPBCoder::encodeDataWithObject(m_dic); - if (allData.length() > 0) { - if (m_crypter) { - m_crypter->reset(); - auto ptr = (unsigned char *) allData.getPtr(); - m_crypter->encrypt(ptr, ptr, allData.length()); - } - writeAcutalSize(allData.length()); - m_output->writeRawData(allData); // note: don't write size of data - recaculateCRCDigest(); - return true; - } - return false; - } else { - writeAcutalSize(m_actualSize + size); - m_output->writeString(key); - m_output->writeData(data); // note: write size of data + // write holder of size of m_dic + // which will be ignore while decoding + // yet it's required for decoding + if (isFirstWrite) { + m_output->writeInt32(ItemSizeHolder); + } - auto ptr = (uint8_t *) m_ptr + Fixed32Size + m_actualSize - size; - if (m_crypter) { - m_crypter->encrypt(ptr, ptr, size); - } - updateCRCDigest(ptr, size, KeepSequence); + writeAcutalSize(m_actualSize + size); + m_output->writeString(key); + m_output->writeData(data); // note: write size of data - return true; + auto ptr = (uint8_t *) m_ptr + Fixed32Size + m_actualSize - size; + if (m_crypter) { + m_crypter->encrypt(ptr, ptr, size); } + updateCRCDigest(ptr, size, KeepSequence); + + return true; } bool MMKV::fullWriteback() { diff --git a/Win32/Win32Demo/Win32Demo.cpp b/Win32/Win32Demo/Win32Demo.cpp index 88770c28..08fc4616 100644 --- a/Win32/Win32Demo/Win32Demo.cpp +++ b/Win32/Win32Demo/Win32Demo.cpp @@ -165,6 +165,27 @@ void processTest() { cout << "total count of process_test: " << mmkv->count() << endl; } +size_t getpagesize(void) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + return system_info.dwPageSize; +} + +void cornetSizeTest() { + auto mmkv = MMKV::mmkvWithID("cornerSize", MMKV_MULTI_PROCESS, &string("aes")); + mmkv->clearAll(); + auto size = getpagesize() - 2; + size -= 4; + string key = "key"; + auto keySize = 3 + 1; + size -= keySize; + auto valueSize = 3; + size -= valueSize; + mmkv::MMBuffer value(size); + mmkv->set(value, key); + mmkv->trim(); +} + static void LogHandler(MMKVLogLevel level, const std::string &file, int line, @@ -207,6 +228,7 @@ int main() { arrStringKeys.push_back("string-" + to_string(index)); } + //cornetSizeTest(); //brutleTest(); threadTest(); processTest(); diff --git a/iOS/MMKV/MMKV.xcodeproj/project.pbxproj b/iOS/MMKV/MMKV.xcodeproj/project.pbxproj index c8dcd4fd..0694f80b 100644 --- a/iOS/MMKV/MMKV.xcodeproj/project.pbxproj +++ b/iOS/MMKV/MMKV.xcodeproj/project.pbxproj @@ -554,6 +554,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; + MARKETING_VERSION = 1.0.20; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit, @@ -593,6 +594,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; + MARKETING_VERSION = 1.0.20; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit, diff --git a/iOS/MMKV/MMKV/MMKV.mm b/iOS/MMKV/MMKV/MMKV.mm index d9be8156..05f592c8 100644 --- a/iOS/MMKV/MMKV/MMKV.mm +++ b/iOS/MMKV/MMKV/MMKV.mm @@ -455,7 +455,8 @@ - (void)trim { [self fullWriteBack]; auto oldSize = m_size; - while (m_size > (m_actualSize * 2)) { + constexpr auto offset = pbFixed32Size(0); + while (m_size > (m_actualSize + offset) * 2) { m_size /= 2; } if (oldSize == m_size) { @@ -463,7 +464,7 @@ - (void)trim { return; } - MMKVInfo(@"trimming %@ from %zu to %zu", m_mmapID, oldSize, m_size); + MMKVInfo(@"trimming %@ from %zu to %zu, actualSize %zu", m_mmapID, oldSize, m_size, m_actualSize); if (ftruncate(m_fd, m_size) != 0) { MMKVError(@"fail to truncate [%@] to size %zu, %s", m_mmapID, m_size, strerror(errno)); @@ -531,7 +532,7 @@ - (BOOL)ensureMemorySize:(size_t)newSize { if (newSize >= m_output->spaceLeft()) { // try a full rewrite to make space static const int offset = pbFixed32Size(0); - NSData *data = [MiniPBCoder encodeDataWithObject:m_dic]; + NSData *data = m_dic.count > 0 ? [MiniPBCoder encodeDataWithObject:m_dic] : nil; size_t lenNeeded = data.length + offset + newSize; size_t avgItemSize = lenNeeded / std::max(1, m_dic.count); size_t futureUsage = avgItemSize * std::max(8, m_dic.count / 2); @@ -634,61 +635,62 @@ - (BOOL)setRawData:(NSData *)data forKey:(NSString *)key { } CScopedLock lock(m_lock); - [m_dic setObject:data forKey:key]; - m_hasFullWriteBack = NO; - - return [self appendData:data forKey:key]; + auto ret = [self appendData:data forKey:key]; + if (ret) { + [m_dic setObject:data forKey:key]; + m_hasFullWriteBack = NO; + } + return ret; } +constexpr uint32_t ItemSizeHolder = 0x00ffffff, ItemSizeHolderSize = 4; + - (BOOL)appendData:(NSData *)data forKey:(NSString *)key { size_t keyLength = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - size_t size = keyLength + pbRawVarint32Size((int32_t) keyLength); // size needed to encode the key - size += data.length + pbRawVarint32Size((int32_t) data.length); // size needed to encode the value + auto kvSize = keyLength + pbRawVarint32Size((int32_t) keyLength); // size needed to encode the key + kvSize += data.length + pbRawVarint32Size((int32_t) data.length); // size needed to encode the value + auto size = kvSize; + + bool isFirstWrite = false; + if (m_actualSize == 0) { + isFirstWrite = true; + size += ItemSizeHolderSize; // size needed to encode the ItemSizeHolder + } BOOL hasEnoughSize = [self ensureMemorySize:size]; if (hasEnoughSize == NO || [self isFileValid] == NO) { return NO; } - if (m_actualSize == 0) { - NSData *allData = [MiniPBCoder encodeDataWithObject:m_dic]; - if (allData.length > 0) { - if (m_cryptor) { - m_cryptor->reset(); - auto ptr = (unsigned char *) allData.bytes; - m_cryptor->encrypt(ptr, ptr, allData.length); - } - BOOL ret = [self writeActualSize:allData.length]; - if (ret) { - ret = [self protectFromBackgroundWriting:m_actualSize - writeBlock:^(MiniCodedOutputData *output) { - output->writeRawData(allData); // note: don't write size of data - }]; - if (ret) { - [self recaculateCRCDigest]; - } - } - return ret; + // write holder of size of m_dic + // which will be ignore while decoding + // yet it's required for decoding + if (isFirstWrite) { + auto ret = [self protectFromBackgroundWriting:ItemSizeHolderSize + writeBlock:^(MiniCodedOutputData *output) { + output->writeInt32(ItemSizeHolder); + }]; + if (!ret) { + return NO; } - return NO; - } else { - BOOL ret = [self writeActualSize:m_actualSize + size]; + } + + BOOL ret = [self writeActualSize:m_actualSize + size]; + if (ret) { + ret = [self protectFromBackgroundWriting:kvSize + writeBlock:^(MiniCodedOutputData *output) { + output->writeString(key); + output->writeData(data); // note: write size of data + }]; if (ret) { static const int offset = pbFixed32Size(0); - ret = [self protectFromBackgroundWriting:size - writeBlock:^(MiniCodedOutputData *output) { - output->writeString(key); - output->writeData(data); // note: write size of data - }]; - if (ret) { - auto ptr = (uint8_t *) m_ptr + offset + m_actualSize - size; - if (m_cryptor) { - m_cryptor->encrypt(ptr, ptr, size); - } - [self updateCRCDigest:ptr withSize:size]; + auto ptr = (uint8_t *) m_ptr + offset + m_actualSize - size; + if (m_cryptor) { + m_cryptor->encrypt(ptr, ptr, size); } + [self updateCRCDigest:ptr withSize:size]; } - return ret; } + return ret; } - (NSData *)getRawDataForKey:(NSString *)key { diff --git a/iOS/MMKV/MMKV/Resources/Info.plist b/iOS/MMKV/MMKV/Resources/Info.plist index 9b3d5e39..fcd36391 100644 --- a/iOS/MMKV/MMKV/Resources/Info.plist +++ b/iOS/MMKV/MMKV/Resources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0.19 + $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/iOS/MMKVDemo/MMKVDemo/ViewController.mm b/iOS/MMKVDemo/MMKVDemo/ViewController.mm index 46387f28..5b3b9930 100644 --- a/iOS/MMKVDemo/MMKVDemo/ViewController.mm +++ b/iOS/MMKVDemo/MMKVDemo/ViewController.mm @@ -56,6 +56,7 @@ - (void)viewDidLoad { [self funcionalTest]; //[self testReKey]; //[self testImportFromUserDefault]; + //[self testCornerSize]; DemoSwiftUsage *swiftUsageDemo = [[DemoSwiftUsage alloc] init]; [swiftUsageDemo testSwiftFunctionality]; @@ -126,6 +127,20 @@ - (void)funcionalTest { [mmkv close]; } +- (void)testCornerSize { + auto mmkv = [MMKV mmkvWithID:@"test/cornerSize" cryptKey:[@"crypt" dataUsingEncoding:NSUTF8StringEncoding]]; + [mmkv clearAll]; + auto size = getpagesize() - 2; + size -= 4; + NSString *key = @"key"; + auto keySize = 3 + 1; + size -= keySize; + auto valueSize = 3; + size -= valueSize; + NSData *value = [NSMutableData dataWithLength:size]; + [mmkv setObject:value forKey:key]; +} + - (void)testMMKV:(NSString *)mmapID withCryptKey:(NSData *)cryptKey decodeOnly:(BOOL)decodeOnly { MMKV *mmkv = [MMKV mmkvWithID:mmapID cryptKey:cryptKey]; diff --git a/readme_cn.md b/readme_cn.md index 0c01a9bd..9b1b80b5 100644 --- a/readme_cn.md +++ b/readme_cn.md @@ -59,8 +59,8 @@ NSString *str = [mmkv getStringForKey:@"string"]; ```gradle dependencies { - implementation 'com.tencent:mmkv:1.0.19' - // replace "1.0.19" with any available version + implementation 'com.tencent:mmkv:1.0.20' + // replace "1.0.20" with any available version } ```