diff --git a/.gitignore b/.gitignore index 017250fa..791ce8e5 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ cmake-build-release/ build/ dist/ *.egg-info/ +.cmake #CLion .idea diff --git a/Android/MMKV/gradle.properties b/Android/MMKV/gradle.properties index 5f00fb09..e743e231 100644 --- a/Android/MMKV/gradle.properties +++ b/Android/MMKV/gradle.properties @@ -14,7 +14,7 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME_PREFIX=1.3.0 +VERSION_NAME_PREFIX=1.3.1 #VERSION_NAME_SUFFIX=-SNAPSHOT VERSION_NAME_SUFFIX= diff --git a/Android/MMKV/mmkv/src/main/cpp/flutter-bridge.cpp b/Android/MMKV/mmkv/src/main/cpp/flutter-bridge.cpp index 5d6e73a8..93df6d32 100644 --- a/Android/MMKV/mmkv/src/main/cpp/flutter-bridge.cpp +++ b/Android/MMKV/mmkv/src/main/cpp/flutter-bridge.cpp @@ -347,10 +347,10 @@ MMKV_EXPORT int32_t writeValueToNB(void *handle, char *oKey, void *pointer, uint return -1; } -MMKV_EXPORT uint64_t allKeys(void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr) { +MMKV_EXPORT uint64_t allKeys(void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr, bool filterExpire) { MMKV *kv = static_cast(handle); if (kv) { - auto keys = kv->allKeys(); + auto keys = kv->allKeys(filterExpire); if (!keys.empty()) { auto keyArray = (char **) malloc(keys.size() * sizeof(void *)); auto sizeArray = (uint32_t *) malloc(keys.size() * sizeof(uint32_t *)); @@ -385,10 +385,10 @@ MMKV_EXPORT bool containsKey(void *handle, char *oKey) { return false; } -MMKV_EXPORT uint64_t count(void *handle) { +MMKV_EXPORT uint64_t count(void *handle, bool filterExpire) { MMKV *kv = static_cast(handle); if (kv) { - return kv->count(); + return kv->count(filterExpire); } return 0; } diff --git a/Android/MMKV/mmkvdemo/build.gradle b/Android/MMKV/mmkvdemo/build.gradle index 98ab97f9..4d8f1d61 100644 --- a/Android/MMKV/mmkvdemo/build.gradle +++ b/Android/MMKV/mmkvdemo/build.gradle @@ -76,14 +76,14 @@ repositories { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation project(':mmkv') -// implementation 'com.tencent:mmkv:1.3.0' -// implementation 'com.tencent:mmkv-static:1.3.0' // this is identical to 'com.tencent:mmkv' -// implementation 'com.tencent:mmkv-shared:1.3.0' +// implementation 'com.tencent:mmkv:1.3.1' +// implementation 'com.tencent:mmkv-static:1.3.1' // this is identical to 'com.tencent:mmkv' +// implementation 'com.tencent:mmkv-shared:1.3.1' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'com.getkeepsafe.relinker:relinker:1.4.3' } diff --git a/CHANGELOG.md b/CHANGELOG.md index 525ac784..52d6a338 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # MMKV Change Log +## v1.3.1 / 2023-8-11 +This is a hotfix version. It's **highly recommended** that v1.2.16 & v1.3.0 users upgrade as soon as possible. + +### Changes for All platforms +* Fix a critical bug that might cause multi-process MMKV corrupt. This bug was introduced in v1.2.16. +* Add the ability to filter expired keys on `count()` & `allKeys()` methods when auto key expiration is turn on. +* Reduce the `msync()` call on newly created MMKV instances. + +### iOS & macOS +* Fix a bug that NSKeyedArchive object might fail to decode when auto key expiration is turn on. + ## v1.3.0 / 2023-06-14 ### Changes for All platforms * Add auto key expiration feature. Note that this is a breaking change, once upgrade to auto expiration, the MMKV file is not valid for older versions of MMKV (v1.2.16 and below) to correctly operate. diff --git a/Core/MMKV.cpp b/Core/MMKV.cpp index f22e2ac7..1b99f9c2 100755 --- a/Core/MMKV.cpp +++ b/Core/MMKV.cpp @@ -307,7 +307,8 @@ void MMKV::clearMemoryCache() { m_output = nullptr; m_file->clearMemoryCache(); - m_metaFile->clearMemoryCache(); + // inter-process lock rely on MetaFile's fd, never close it + // m_metaFile->clearMemoryCache(); m_actualSize = 0; m_metaInfo->m_crcDigest = 0; } @@ -1363,7 +1364,17 @@ bool MMKV::restoreOneFromDirectory(const string &mmapKey, const MMKVPath_t &srcP auto ret = copyFileContent(srcPath, kv->m_file->getFd()); if (ret) { auto srcCRCPath = srcPath + CRC_SUFFIX; - ret = copyFileContent(srcCRCPath, kv->m_metaFile->getFd()); + // ret = copyFileContent(srcCRCPath, kv->m_metaFile->getFd()); +#ifndef MMKV_ANDROID + MemoryFile srcCRCFile(srcCRCPath); +#else + MemoryFile srcCRCFile(srcCRCPath, DEFAULT_MMAP_SIZE, MMFILE_TYPE_FILE); +#endif + if (srcCRCFile.isFileValid()) { + memcpy(kv->m_metaFile->getMemory(), srcCRCFile.getMemory(), sizeof(MMKVMetaInfo)); + } else { + ret = false; + } } // reload data after restore diff --git a/Core/MMKVPredef.h b/Core/MMKVPredef.h index 66b1b25a..2debf618 100755 --- a/Core/MMKVPredef.h +++ b/Core/MMKVPredef.h @@ -34,7 +34,7 @@ #include #include -constexpr auto MMKV_VERSION = "v1.3.0"; +constexpr auto MMKV_VERSION = "v1.3.1"; #ifdef DEBUG # define MMKV_DEBUG diff --git a/Core/MMKV_IO.cpp b/Core/MMKV_IO.cpp index 12debfe0..8da0719a 100755 --- a/Core/MMKV_IO.cpp +++ b/Core/MMKV_IO.cpp @@ -1536,7 +1536,9 @@ size_t MMKV::filterExpiredKeys() { } } } - MMKVInfo("deleted %zu expired keys inside [%s]", count, m_mmapID.c_str()); + if (count != 0) { + MMKVInfo("deleted %zu expired keys inside [%s]", count, m_mmapID.c_str()); + } return count; } diff --git a/Core/MemoryFile.cpp b/Core/MemoryFile.cpp index e43e0d11..fba85ec6 100644 --- a/Core/MemoryFile.cpp +++ b/Core/MemoryFile.cpp @@ -201,7 +201,7 @@ void MemoryFile::reloadFromFile(size_t expectedCapacity) { if (isFileValid()) { MMKVWarning("calling reloadFromFile while the cache [%s] is still valid", m_diskFile.m_path.c_str()); MMKV_ASSERT(0); - clearMemoryCache(); + doCleanMemoryCache(false); } if (!m_diskFile.open()) { @@ -275,15 +275,19 @@ extern bool mkPath(const MMKVPath_t &str) { if (stat(path, &sb) != 0) { if (errno != ENOENT || mkdir(path, 0777) != 0) { MMKVWarning("%s : %s", path, strerror(errno)); - free(path); - return false; + // there's report that some Android devices might not have access permission on parent dir + if (done) { + free(path); + return false; + } + goto LContinue; } } else if (!S_ISDIR(sb.st_mode)) { MMKVWarning("%s: %s", path, strerror(ENOTDIR)); free(path); return false; } - +LContinue: *slash = '/'; } free(path); diff --git a/Core/core.vcxproj b/Core/core.vcxproj index 84bde3f4..a80e096a 100755 --- a/Core/core.vcxproj +++ b/Core/core.vcxproj @@ -65,7 +65,7 @@ {32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5} - 10.0.17763.0 + 10.0 Win32Proj Win32 core @@ -75,12 +75,12 @@ StaticLibrary Unicode - v140 + v143 StaticLibrary Unicode - v140 + v143 diff --git a/MMKV.podspec b/MMKV.podspec index 935d0348..4eb008b8 100644 --- a/MMKV.podspec +++ b/MMKV.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MMKV" - s.version = "1.3.0" + s.version = "1.3.1" s.summary = "MMKV is a cross-platform key-value storage framework developed by WeChat." s.description = <<-DESC @@ -31,7 +31,7 @@ Pod::Spec.new do |s| "CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF" => "NO", } - s.dependency 'MMKVCore', '~> 1.3.0' + s.dependency 'MMKVCore', '~> 1.3.1' end diff --git a/MMKVAppExtension.podspec b/MMKVAppExtension.podspec index e03b5261..6ba42a00 100644 --- a/MMKVAppExtension.podspec +++ b/MMKVAppExtension.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MMKVAppExtension" - s.version = "1.3.0" + s.version = "1.3.1" s.summary = "MMKV is a cross-platform key-value storage framework developed by WeChat." s.module_name = "MMKVAppExtension" @@ -31,7 +31,7 @@ Pod::Spec.new do |s| "GCC_PREPROCESSOR_DEFINITIONS" => "MMKV_IOS_EXTENSION", } - s.dependency 'MMKVCore', '~> 1.3.0' + s.dependency 'MMKVCore', '~> 1.3.1' end diff --git a/MMKVCore.podspec b/MMKVCore.podspec index dccb1184..1c8c84da 100644 --- a/MMKVCore.podspec +++ b/MMKVCore.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MMKVCore" - s.version = "1.3.0" + s.version = "1.3.1" s.summary = "MMKVCore for MMKV. MMKV is a cross-platform key-value storage framework developed by WeChat." s.description = <<-DESC diff --git a/MMKVWatchExtension.podspec b/MMKVWatchExtension.podspec index 5797c873..db76e0d1 100644 --- a/MMKVWatchExtension.podspec +++ b/MMKVWatchExtension.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MMKVWatchExtension" - s.version = "1.3.0" + s.version = "1.3.1" s.summary = "MMKV is a cross-platform key-value storage framework developed by WeChat." s.module_name = "MMKVWatchExtension" @@ -31,7 +31,7 @@ Pod::Spec.new do |s| "GCC_PREPROCESSOR_DEFINITIONS" => "MMKV_IOS_EXTENSION", } - s.dependency 'MMKVCore', '~> 1.3.0' + s.dependency 'MMKVCore', '~> 1.3.1' end diff --git a/POSIX/Python/demo.py b/POSIX/Python/demo.py index ac9ee996..f848e3aa 100644 --- a/POSIX/Python/demo.py +++ b/POSIX/Python/demo.py @@ -139,6 +139,8 @@ def test_auto_expire(): time.sleep(2) print("contains never_expire_key_1:", "never_expire_key_1" in kv) print("contains auto_expire_key_1:", "auto_expire_key_1" in kv) + print("count filter expire key:", kv.count(True)) + print("all non expire keys:", kv.keys(True)) def logger(log_level, file, line, function, message): diff --git a/POSIX/Python/libmmkv_python.cpp b/POSIX/Python/libmmkv_python.cpp index 0b6ae110..478c9895 100644 --- a/POSIX/Python/libmmkv_python.cpp +++ b/POSIX/Python/libmmkv_python.cpp @@ -224,9 +224,9 @@ PYBIND11_MODULE(mmkv, m) { "decode a bytes value", py::arg("key"), py::arg("defaultValue") = py::bytes()); clsMMKV.def("__contains__", &MMKV::containsKey, py::arg("key")); - clsMMKV.def("keys", &MMKV::allKeys); + clsMMKV.def("keys", &MMKV::allKeys, py::arg("filterExpire") = false); - clsMMKV.def("count", &MMKV::count); + clsMMKV.def("count", &MMKV::count, py::arg("filterExpire") = false); clsMMKV.def("totalSize", &MMKV::totalSize); clsMMKV.def("actualSize", &MMKV::actualSize); diff --git a/POSIX/demo/demo.cpp b/POSIX/demo/demo.cpp index 04244226..e61a1cf5 100644 --- a/POSIX/demo/demo.cpp +++ b/POSIX/demo/demo.cpp @@ -36,7 +36,7 @@ using namespace std; using namespace mmkv; -string to_string(vector &&arr, const char* sp = ", ") { +string to_string(const vector &arr, const char* sp = ", ") { string str; for (const auto &element : arr) { str += element; @@ -363,6 +363,11 @@ void testAutoExpiration() { sleep(2); assert(mmkv->containsKey("never_expire_key_1") == true); assert(mmkv->containsKey("auto_expire_key_1") == false); + + auto count = mmkv->count(true); + cout << "count all non expire keys: " << count << endl; + auto allKeys = mmkv->allKeys(true); + cout << "all non expire keys: " << ::to_string(allKeys) << endl; } void testExpectedCapacity() { diff --git a/POSIX/golang/golang-bridge.cpp b/POSIX/golang/golang-bridge.cpp index 2c782836..5017ae85 100644 --- a/POSIX/golang/golang-bridge.cpp +++ b/POSIX/golang/golang-bridge.cpp @@ -392,10 +392,10 @@ MMKV_EXPORT void checkReSetCryptKey(void *handle, GoStringWrap oKey) { # endif // MMKV_DISABLE_CRYPT -MMKV_EXPORT GoStringWrap *allKeys(void *handle, uint64_t *lengthPtr) { +MMKV_EXPORT GoStringWrap *allKeys(void *handle, uint64_t *lengthPtr, bool filterExpire) { MMKV *kv = static_cast(handle); if (kv) { - auto keys = kv->allKeys(); + auto keys = kv->allKeys(filterExpire); if (!keys.empty()) { auto keyArray = (GoStringWrap *) calloc(keys.size(), sizeof(GoStringWrap)); if (!keyArray) { @@ -427,10 +427,10 @@ MMKV_EXPORT bool containsKey(void *handle, GoStringWrap oKey) { return false; } -MMKV_EXPORT uint64_t count(void *handle) { +MMKV_EXPORT uint64_t count(void *handle, bool filterExpire) { MMKV *kv = static_cast(handle); if (kv) { - return kv->count(); + return kv->count(filterExpire); } return 0; } diff --git a/POSIX/golang/golang-bridge.h b/POSIX/golang/golang-bridge.h index 53f9c82c..aab113a9 100644 --- a/POSIX/golang/golang-bridge.h +++ b/POSIX/golang/golang-bridge.h @@ -83,9 +83,9 @@ bool reKey(void *handle, GoStringWrap_t oKey); void *cryptKey(void *handle, uint32_t *lengthPtr); void checkReSetCryptKey(void *handle, GoStringWrap_t oKey); -GoStringWrap_t *allKeys(void *handle, uint64_t *lengthPtr); +GoStringWrap_t *allKeys(void *handle, uint64_t *lengthPtr, bool filterExpire); bool containsKey(void *handle, GoStringWrap_t oKey); -uint64_t count(void *handle); +uint64_t count(void *handle, bool filterExpire); uint64_t totalSize(void *handle); uint64_t actualSize(void *handle); diff --git a/POSIX/golang/mmkv.go b/POSIX/golang/mmkv.go index 3c8d1623..67c840df 100644 --- a/POSIX/golang/mmkv.go +++ b/POSIX/golang/mmkv.go @@ -76,37 +76,37 @@ const ( MMKV_Expire_Year = 365 * 30 * 24 * 60 * 60 ) -// a wrapper of native C memory, efficient for simple usage +// MMBuffer a wrapper of native C memory, efficient for simple usage // must call MMBuffer.Destroy() after no longer usage type MMBuffer struct { ptr uintptr length int } -// the address of underlying memory +// Pointer the address of underlying memory func (buffer MMBuffer) Pointer() uintptr { return buffer.ptr } -// the size of underlying memory +// Length the size of underlying memory func (buffer MMBuffer) Length() int { return buffer.length } -// get byte slice view of underlying memory +// ByteSliceView get byte slice view of underlying memory // the slice is valid as long as MMBuffer.Destroy() not called func (buffer MMBuffer) ByteSliceView() []byte { bytes := (*[1 << 30]byte)(unsafe.Pointer(buffer.ptr))[0:buffer.length:buffer.length] return bytes } -// get string view of underlying memory +// StringView get string view of underlying memory // the string is valid as long as MMBuffer.Destroy() not called func (buffer MMBuffer) StringView() string { return *((*string)(unsafe.Pointer(&buffer))) } -// must call Destroy() after no longer usage +// Destroy must call Destroy() after no longer usage func (buffer MMBuffer) Destroy() { C.free(unsafe.Pointer(buffer.ptr)) } @@ -147,46 +147,48 @@ type MMKV interface { GetFloat64(key string) float64 GetFloat64WithDefault(key string, defaultValue float64) float64 - // string value should be utf-8 encoded + // SetString string value should be utf-8 encoded SetString(value string, key string) bool SetStringExpire(value string, key string, expireDuration uint32) bool GetString(key string) string - // get C memory directly (without memcpy), much more efferent for large value + // GetStringBuffer get C memory directly (without memcpy), much more efferent for large value GetStringBuffer(key string) MMBuffer SetBytes(value []byte, key string) bool SetBytesExpire(value []byte, key string, expireDuration uint32) bool GetBytes(key string) []byte - // get C memory directly (without memcpy), much more efferent for large value + // GetBytesBuffer get C memory directly (without memcpy), much more efferent for large value GetBytesBuffer(key string) MMBuffer RemoveKey(key string) RemoveKeys(keys []string) - // clear all key-values + // ClearAll clear all key-values ClearAll() - // return count of keys + // Count return count of keys Count() uint64 + // CountNonExpiredKeys same as Count() except that it filters expired keys + CountNonExpiredKeys() uint64 AllKeys() []string + // AllNonExpireKeys same as AllKeys() except that it filters expired keys + AllNonExpireKeys() []string + Contains(key string) bool - // total size of the file + // TotalSize total size of the file TotalSize() uint64 - // actual used size of the file + // ActualSize actual used size of the file ActualSize() uint64 - // the mmapID of the instance + // MMAP_ID the mmapID of the instance MMAP_ID() string - /* Synchronize memory to file. You don't need to call this, really, I mean it. Unless you worry about running out of battery. - * Pass true to perform synchronous write. - * Pass false to perform asynchronous write, return immediately. - */ + // Sync Synchronize memory to file. You don't need to call this, really, I mean it. Unless you worry about running out of battery. Sync(sync bool) - // Clear all caches (on memory warning). + // ClearMemoryCache Clear all caches (on memory warning). ClearMemoryCache() /* Trim the file size to minimal. @@ -200,21 +202,21 @@ type MMKV interface { // Any subsequent call to the instance is undefined behavior. Close() - /* Change encryption key for the MMKV instance. + /* ReKey Change encryption key for the MMKV instance. * The cryptKey is 16 bytes limited. * You can transfer a plain-text MMKV into encrypted by setting an non-null, non-empty cryptKey. * Or vice versa by passing cryptKey with null. See also checkReSetCryptKey(). */ ReKey(newKey string) bool - // Just reset the cryptKey (will not encrypt or decrypt anything). + // CheckReSetCryptKey Just reset the cryptKey (will not encrypt or decrypt anything). // Usually you should call this method after other process reKey() the multi-process mmkv. CheckReSetCryptKey(key string) // See also reKey(). CryptKey() string - // passing MMKV_Expire_Never (0) means never expire + // EnableAutoKeyExpire passing MMKV_Expire_Never (0) means never expire EnableAutoKeyExpire(expireDurationInSecond uint32) bool DisableAutoKeyExpire() bool @@ -222,7 +224,7 @@ type MMKV interface { type ctorMMKV uintptr -// return the version of MMKV +// Version return the version of MMKV func Version() string { version := C.version() goStr := C.GoString(version) @@ -254,29 +256,29 @@ func InitializeMMKVWithLogLevelAndHandler(rootDir string, logLevel int, logHandl C.mmkvInitialize(C.wrapGoString(rootDir), C.int32_t(logLevel), C.bool(true)) } -// Call before App exists, it's just fine not calling it on most case (except when the device shutdown suddenly). +// OnExit Call before App exists, it's just fine not calling it on most case (except when the device shutdown suddenly). func OnExit() { C.onExit() } -// return the page size of memory +// PageSize return the page size of memory func PageSize() int32 { return int32(C.pageSize()) } -// a generic purpose instance in single-process mode. +// DefaultMMKV a generic purpose instance in single-process mode. func DefaultMMKV() MMKV { mmkv := ctorMMKV(C.getDefaultMMKV(MMKV_SINGLE_PROCESS, C.GoStringWrapNil())) return MMKV(mmkv) } -// a generic purpose instance in single-process or multi-process mode. +// DefaultMMKVWithMode a generic purpose instance in single-process or multi-process mode. func DefaultMMKVWithMode(mode int) MMKV { mmkv := ctorMMKV(C.getDefaultMMKV(C.int(mode), C.GoStringWrapNil())) return MMKV(mmkv) } -// an encrypted generic purpose instance in single-process or multi-process mode. +// DefaultMMKVWithModeAndCryptKey an encrypted generic purpose instance in single-process or multi-process mode. func DefaultMMKVWithModeAndCryptKey(mode int, cryptKey string) MMKV { mmkv := ctorMMKV(C.getDefaultMMKV(MMKV_SINGLE_PROCESS, C.wrapGoString(cryptKey))) return MMKV(mmkv) @@ -304,28 +306,28 @@ func MMKVWithIDAndMode(mmapID string, mode int) MMKV { return MMKV(mmkv) } -// an encrypted instance with specific location ${MMKV Root}/mmapID, in single-process or multi-process mode. +// MMKVWithIDAndModeAndCryptKey an encrypted instance with specific location ${MMKV Root}/mmapID, in single-process or multi-process mode. func MMKVWithIDAndModeAndCryptKey(mmapID string, mode int, cryptKey string) MMKV { cStrNull := C.GoStringWrapNil() mmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), C.int(mode), C.wrapGoString(cryptKey), cStrNull, 0)) return MMKV(mmkv) } -// backup one MMKV instance (from the root dir of MMKV) to dstDir +// BackupOneToDirectory backup one MMKV instance (from the root dir of MMKV) to dstDir func BackupOneToDirectory(mmapID string, dstDir string) bool { cStrNull := C.GoStringWrapNil() ret := C.backupOneToDirectory(C.wrapGoString(mmapID), C.wrapGoString(dstDir), cStrNull) return bool(ret) } -// restore one MMKV instance from srcDir (to the root dir of MMKV) +// RestoreOneFromDirectory restore one MMKV instance from srcDir (to the root dir of MMKV) func RestoreOneFromDirectory(mmapID string, srcDir string) bool { cStrNull := C.GoStringWrapNil() ret := C.restoreOneFromDirectory(C.wrapGoString(mmapID), C.wrapGoString(srcDir), cStrNull) return bool(ret) } -// backup all MMKV instance (from the root dir of MMKV) to dstDir +// BackupAllToDirectory backup all MMKV instance (from the root dir of MMKV) to dstDir // return count of MMKV successfully backup-ed func BackupAllToDirectory(dstDir string) uint64 { cStrNull := C.GoStringWrapNil() @@ -333,7 +335,7 @@ func BackupAllToDirectory(dstDir string) uint64 { return uint64(ret) } -// restore all MMKV instance from srcDir (to the root dir of MMKV) +// RestoreAllFromDirectory restore all MMKV instance from srcDir (to the root dir of MMKV) // return count of MMKV successfully restored func RestoreAllFromDirectory(srcDir string) uint64 { cStrNull := C.GoStringWrapNil() @@ -541,12 +543,16 @@ func (kv ctorMMKV) RemoveKeys(keys []string) { } func (kv ctorMMKV) Count() uint64 { - return uint64(C.count(unsafe.Pointer(kv))) + return uint64(C.count(unsafe.Pointer(kv), C.bool(false))) } -func (kv ctorMMKV) AllKeys() []string { +func (kv ctorMMKV) CountNonExpiredKeys() uint64 { + return uint64(C.count(unsafe.Pointer(kv), C.bool(true))) +} + +func (kv ctorMMKV) allKeys(filterExpire bool) []string { count := uint64(0) - keyArray := C.allKeys(unsafe.Pointer(kv), (*C.uint64_t)(&count)) + keyArray := C.allKeys(unsafe.Pointer(kv), (*C.uint64_t)(&count), C.bool(filterExpire)) if keyArray == nil || count == 0 { return []string{} } @@ -566,6 +572,14 @@ func (kv ctorMMKV) AllKeys() []string { return result } +func (kv ctorMMKV) AllKeys() []string { + return kv.allKeys(false) +} + +func (kv ctorMMKV) AllNonExpireKeys() []string { + return kv.allKeys(true) +} + func (kv ctorMMKV) Contains(key string) bool { ret := C.containsKey(unsafe.Pointer(kv), C.wrapGoString(key)) return bool(ret) diff --git a/POSIX/golang/test/main.go b/POSIX/golang/test/main.go index f1e10837..9ae7c086 100644 --- a/POSIX/golang/test/main.go +++ b/POSIX/golang/test/main.go @@ -255,6 +255,8 @@ func testAutoExpire() { time.Sleep(2 * time.Second) fmt.Println("contains never_expire_key_1:", kv.Contains("never_expire_key_1")) fmt.Println("contains auto_expire_key_1:", kv.Contains("auto_expire_key_1")) + fmt.Println("count non expire keys", kv.CountNonExpiredKeys()) + fmt.Println("all non expire keys", kv.AllNonExpireKeys()) } func logHandler(level int, file string, line int, function string, message string) { diff --git a/README.md b/README.md index 07421a81..82b2c434 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.3.0-brightgreen.svg)](https://github.com/Tencent/MMKV/releases) +[![Release Version](https://img.shields.io/badge/release-1.3.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) @@ -28,8 +28,8 @@ Add the following lines to `build.gradle` on your app module: ```gradle dependencies { - implementation 'com.tencent:mmkv:1.3.0' - // replace "1.3.0" with any available version + implementation 'com.tencent:mmkv:1.3.1' + // replace "1.3.1" with any available version } ``` diff --git a/README_CN.md b/README_CN.md index 311452b6..7b289f63 100644 --- a/README_CN.md +++ b/README_CN.md @@ -22,8 +22,8 @@ MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列 ```gradle dependencies { - implementation 'com.tencent:mmkv:1.3.0' - // replace "1.3.0" with any available version + implementation 'com.tencent:mmkv:1.3.1' + // replace "1.3.1" with any available version } ``` 更多安装指引参考 [Android Setup](https://github.com/Tencent/MMKV/wiki/android_setup_cn)。 diff --git a/Win32/Win32Demo/Win32Demo.cpp b/Win32/Win32Demo/Win32Demo.cpp index 1ebc8228..e746f196 100755 --- a/Win32/Win32Demo/Win32Demo.cpp +++ b/Win32/Win32Demo/Win32Demo.cpp @@ -41,7 +41,7 @@ wstring getAppDataRoaming(const wstring &company, const wstring &appName) { } } -string to_string(vector &&arr, const char *sp = ", ") { +string to_string(const vector &arr, const char *sp = ", ") { string str; for (const auto &element : arr) { str += element; @@ -313,6 +313,11 @@ void testAutoExpire() { Sleep(2 * 1000); assert(mmkv->containsKey("never_expire_key_1") == true); assert(mmkv->containsKey("auto_expire_key_1") == false); + + auto count = mmkv->count(true); + cout << "count all non expire keys: " << count << endl; + auto allKeys = mmkv->allKeys(true); + cout << "all non expire keys: " << ::to_string(allKeys) << endl; } void testExpectedCapacity() { diff --git a/Win32/Win32Demo/Win32Demo.vcxproj b/Win32/Win32Demo/Win32Demo.vcxproj index aaee4acf..e351597a 100644 --- a/Win32/Win32Demo/Win32Demo.vcxproj +++ b/Win32/Win32Demo/Win32Demo.vcxproj @@ -23,19 +23,19 @@ {532303C4-38D9-45D3-B925-18056C66CD01} Win32Proj Win32Demo - 10.0.17763.0 + 10.0 Application true - v140 + v143 Unicode Application false - v140 + v143 true Unicode diff --git a/Win32/Win32DemoProcess/Win32DemoProcess.vcxproj b/Win32/Win32DemoProcess/Win32DemoProcess.vcxproj index 7a8ac718..2a1518c5 100644 --- a/Win32/Win32DemoProcess/Win32DemoProcess.vcxproj +++ b/Win32/Win32DemoProcess/Win32DemoProcess.vcxproj @@ -23,19 +23,19 @@ {86D6C605-81F3-407B-A766-690EDF4CD346} Win32Proj Win32DemoProcess - 10.0.17763.0 + 10.0 Application true - v140 + v143 Unicode Application false - v140 + v143 true Unicode diff --git a/flutter/CHANGELOG.md b/flutter/CHANGELOG.md index ad061ea7..a26dcaff 100644 --- a/flutter/CHANGELOG.md +++ b/flutter/CHANGELOG.md @@ -1,4 +1,9 @@ # MMKV for Flutter Change Log +## v1.3.1 / 2023-8-11 +This is a hotfix version. It's **highly recommended** that v1.2.16 & v1.3.0 users upgrade as soon as possible. +* Fix a critical bug that might cause multi-process MMKV corrupt. This bug was introduced in v1.2.16. +* Add the ability to filter expired keys on `count()` & `allKeys()` methods when auto key expiration is turn on. +* Reduce the `msync()` call on newly created MMKV instances. ## v1.3.0 / 2023-06-14 * Add auto key expiration feature. Note that this is a breaking change, once upgrade to auto expiration, the MMKV file is not valid for older versions of MMKV (v1.2.16 and below) to correctly operate. diff --git a/flutter/README.md b/flutter/README.md index c714df9a..ee3c0601 100644 --- a/flutter/README.md +++ b/flutter/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.3.0-brightgreen.svg)](https://github.com/Tencent/MMKV/releases) +[![Release Version](https://img.shields.io/badge/release-1.3.1-brightgreen.svg)](https://github.com/Tencent/MMKV/releases) [![Platform](https://img.shields.io/badge/Platform-%20Android%20%7C%20iOS-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home) MMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's currently available on **Android** and **iOS**. @@ -26,11 +26,11 @@ Add the following lines to `pubspec.yaml` on your app module. Then run `flutter ```yaml dependencies: - mmkv: ">=1.3.0" + mmkv: ">=1.3.1" ... ``` -If you already include MMKV native lib in your App, you need to upgrade to version newer than v1.3.0. +If you already include MMKV native lib in your App, you need to upgrade to version newer than v1.3.1. #### iOS To avoid conflict of the native lib name 'libMMKV.so' on iOS, we need to **change the plugin name 'mmkv' to 'mmkvflutter'**. @@ -244,8 +244,8 @@ Due to the current limitation of Flutter runtime, we can't redirect log on the F var mmkv = MMKV('testCustomDir', rootDir: dir.path + '/mmkv_3'); ``` **Note:** It's recommended to store MMKV files **inside** your App's sandbox path. **DO NOT** store them on external storage(aka SD card). If you have to do it, you should follow Android's [scoped storage](https://developer.android.com/preview/privacy/storage) enforcement. - -### Additional docs + +### Additional docs For additional documents, checkout the [wiki](https://github.com/Tencent/MMKV/wiki/flutter_setup). ## License diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index bd5d911a..d92e687f 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.4.1' } } @@ -16,7 +16,7 @@ rootProject.allprojects { repositories { google() mavenCentral() -// mavenLocal() + mavenLocal() } } @@ -33,6 +33,6 @@ android { } dependencies { - implementation 'com.tencent:mmkv:1.3.0' + implementation 'com.tencent:mmkv:1.3.1' } } diff --git a/flutter/android/gradle/wrapper/gradle-wrapper.properties b/flutter/android/gradle/wrapper/gradle-wrapper.properties index 8c63dd8d..ceccc3a8 100644 --- a/flutter/android/gradle/wrapper/gradle-wrapper.properties +++ b/flutter/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip diff --git a/flutter/example/android/app/build.gradle b/flutter/example/android/app/build.gradle index 500f4a74..ebc7b229 100644 --- a/flutter/example/android/app/build.gradle +++ b/flutter/example/android/app/build.gradle @@ -48,11 +48,23 @@ android { } dependencies { - implementation('com.tencent:mmkv:1.2.8') + implementation('com.tencent:mmkv:1.3.1') + + constraints { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") { + because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib") + } + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") { + because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib") + } + } modules { - module("com.tencent:mmkv") { - replacedBy("com.tencent:mmkv-static", "Using mmkv-static for flutter") + module("com.tencent:mmkv-static") { + replacedBy("com.tencent:mmkv", "Using mmkv for flutter") + } + module("com.tencent:mmkv-shared") { + replacedBy("com.tencent:mmkv", "Using mmkv for flutter") } } } diff --git a/flutter/example/android/build.gradle b/flutter/example/android/build.gradle index 328175bb..951e6bad 100644 --- a/flutter/example/android/build.gradle +++ b/flutter/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.4.1' } } @@ -24,6 +24,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/flutter/example/android/gradle/wrapper/gradle-wrapper.properties index 939efa29..cfe88f69 100644 --- a/flutter/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip diff --git a/flutter/example/ios/Podfile.lock b/flutter/example/ios/Podfile.lock index d121167e..4f141515 100644 --- a/flutter/example/ios/Podfile.lock +++ b/flutter/example/ios/Podfile.lock @@ -1,18 +1,19 @@ PODS: - Flutter (1.0.0) - - MMKV (1.3.0): - - MMKVCore (~> 1.3.0) - - MMKVCore (1.3.0) - - mmkvflutter (1.3.0): + - MMKV (1.3.1): + - MMKVCore (~> 1.3.1) + - MMKVCore (1.3.1) + - mmkvflutter (1.3.1): - Flutter - - MMKV (>= 1.3.0) - - path_provider_ios (0.0.1): + - MMKV (>= 1.3.1) + - path_provider_foundation (0.0.1): - Flutter + - FlutterMacOS DEPENDENCIES: - Flutter (from `Flutter`) - mmkvflutter (from `.symlinks/plugins/mmkvflutter/ios`) - - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) SPEC REPOS: trunk: @@ -24,15 +25,15 @@ EXTERNAL SOURCES: :path: Flutter mmkvflutter: :path: ".symlinks/plugins/mmkvflutter/ios" - path_provider_ios: - :path: ".symlinks/plugins/path_provider_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - MMKV: 9c6c3fa4ddd849f28c7b9a5c9d23aab84f14ee35 - MMKVCore: 9bb7440b170181ac5b81f542ac258103542e693d - mmkvflutter: 1c56409a659f49e4d55ad826662200502f5c8029 - path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + MMKV: 5a07930c70c70b86cd87761a42c8f3836fb681d7 + MMKVCore: e50135dbd33235b6ab390635991bab437ab873c0 + mmkvflutter: a321a78260e9c370a4c79b02c35bfb58c0e18592 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 PODFILE CHECKSUM: 751e522a1d35e86af4f588d982f4a515941611c5 diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index eac852e2..31b9ba80 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -332,5 +332,8 @@ class _MyAppState extends State { print("auto_expire_key_5: ${mmkv.containsKey("auto_expire_key_5")}"); print("auto_expire_key_6: ${mmkv.containsKey("auto_expire_key_6")}"); print("never_expire_key_1: ${mmkv.containsKey("never_expire_key_1")}"); + + print("count non-expired keys: ${mmkv.countNonExpiredKeys}"); + print("all non-expired keys: ${mmkv.allNonExpiredKeys}"); } } diff --git a/flutter/example/pubspec.lock b/flutter/example/pubspec.lock index 7d03a7ea..002de614 100644 --- a/flutter/example/pubspec.lock +++ b/flutter/example/pubspec.lock @@ -61,18 +61,10 @@ packages: dependency: transitive description: name: ffi - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.2" - file: - dependency: transitive - description: - name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" - source: hosted - version: "6.1.4" + version: "2.1.0" flutter: dependency: "direct main" description: flutter @@ -121,7 +113,7 @@ packages: path: ".." relative: true source: path - version: "1.3.0" + version: "1.3.1" path: dependency: transitive description: @@ -134,50 +126,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: "909b84830485dbcd0308edf6f7368bc8fd76afa26a270420f34cabea2a6467a0" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.0" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.1.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.0" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.0" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.0" platform: dependency: transitive description: @@ -190,18 +182,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.4" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" + version: "2.1.5" sky_engine: dependency: transitive description: flutter @@ -267,18 +251,18 @@ packages: dependency: transitive description: name: win32 - sha256: "7dacfda1edcca378031db9905ad7d7bd56b29fd1a90b0908b71a52a12c41e36b" + sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0 url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.0.6" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: f0c26453a2d47aa4c2570c6a033246a3fc62da2fe23c7ffdd0a7495086dc0247 url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.2" sdks: dart: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" diff --git a/flutter/ios/Classes/flutter-bridge.mm b/flutter/ios/Classes/flutter-bridge.mm index d1b7fe65..e96f2100 100644 --- a/flutter/ios/Classes/flutter-bridge.mm +++ b/flutter/ios/Classes/flutter-bridge.mm @@ -300,10 +300,10 @@ MMKV_EXPORT int32_t MMKV_FUNC(writeValueToNB)(const void *handle, char *oKey, vo return -1; } -MMKV_EXPORT uint64_t MMKV_FUNC(allKeys)(const void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr) { +MMKV_EXPORT uint64_t MMKV_FUNC(allKeys)(const void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr, bool filterExpire) { MMKV *kv = (__bridge MMKV *) handle; if (kv) { - auto keys = [kv allKeys]; + auto keys = filterExpire ? [kv allNonExpiredKeys] : [kv allKeys]; if (keys.count > 0) { auto keyArray = (char **) malloc(keys.count * sizeof(void *)); auto sizeArray = (uint32_t *) malloc(keys.count * sizeof(uint32_t *)); @@ -336,10 +336,10 @@ MMKV_EXPORT bool MMKV_FUNC(containsKey)(const void *handle, char *oKey) { return false; } -MMKV_EXPORT uint64_t MMKV_FUNC(count)(const void *handle) { +MMKV_EXPORT uint64_t MMKV_FUNC(count)(const void *handle, bool filterExpire) { MMKV *kv = (__bridge MMKV *) handle; if (kv) { - return [kv count]; + return filterExpire ? [kv countNonExpiredKeys] : [kv count]; } return 0; } diff --git a/flutter/ios/mmkvflutter.podspec b/flutter/ios/mmkvflutter.podspec index c7adc08d..c0fbc99d 100644 --- a/flutter/ios/mmkvflutter.podspec +++ b/flutter/ios/mmkvflutter.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |s| s.name = 'mmkvflutter' - s.version = '1.3.0' + s.version = '1.3.1' s.summary = 'MMKV is a cross-platform key-value storage framework developed by WeChat.' s.description = <<-DESC The MMKV, for Flutter. @@ -19,7 +19,7 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.dependency 'MMKV', '>= 1.3.0' + s.dependency 'MMKV', '>= 1.3.1' s.platform = :ios, '11.0' # Flutter.framework does not contain a i386 slice. diff --git a/flutter/lib/mmkv.dart b/flutter/lib/mmkv.dart index 24ebd3a0..6976adb9 100644 --- a/flutter/lib/mmkv.dart +++ b/flutter/lib/mmkv.dart @@ -22,7 +22,6 @@ import "dart:async"; import "dart:convert"; import "dart:ffi"; // For FFI import "dart:io"; // For Platform.isX -import "dart:typed_data"; import "package:ffi/ffi.dart"; import "package:flutter/cupertino.dart"; @@ -491,11 +490,20 @@ class MMKV { /// Get all the keys (_unsorted_). List get allKeys { + return _allKeysImp(false); + } + + /// Get all non-expired keys (_unsorted_). Note that this call has costs. + List get allNonExpiredKeys { + return _allKeysImp(true); + } + + List _allKeysImp(bool filterExpire) { final keyArrayPtr = calloc>>(); final sizeArrayPtr = calloc>(); final List keys = []; - final count = _allKeys(_handle, keyArrayPtr, sizeArrayPtr); + final count = _allKeys(_handle, keyArrayPtr, sizeArrayPtr, _bool2Int(filterExpire)); if (count > 0) { final keyArray = keyArrayPtr[0]; final sizeArray = sizeArrayPtr[0]; @@ -528,7 +536,12 @@ class MMKV { } int get count { - return _count(_handle); + return _count(_handle, _bool2Int(false)); + } + + /// Get non-expired keys. Note that this call has costs. + int get countNonExpiredKeys { + return _count(_handle, _bool2Int(true)); } /// Get the file size. See also [actualSize]. @@ -960,12 +973,12 @@ final int Function(Pointer, Pointer, Pointer, int) .asFunction(); final int Function(Pointer, Pointer>>, - Pointer>) _allKeys = + Pointer>, int) _allKeys = _nativeLib .lookup< NativeFunction< Uint64 Function(Pointer, Pointer>>, - Pointer>)>>(_nativeFuncName("allKeys")) + Pointer>, Int8)>>(_nativeFuncName("allKeys")) .asFunction(); final int Function(Pointer, Pointer) _containsKey = _nativeLib @@ -973,8 +986,8 @@ final int Function(Pointer, Pointer) _containsKey = _nativeLib _nativeFuncName("containsKey")) .asFunction(); -final int Function(Pointer) _count = _nativeLib - .lookup)>>( +final int Function(Pointer, int) _count = _nativeLib + .lookup, Int8)>>( _nativeFuncName("count")) .asFunction(); diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 6c200257..1520925e 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -101,18 +101,18 @@ packages: dependency: "direct main" description: name: ffi - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.0" file: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" flutter: dependency: "direct main" description: flutter @@ -122,10 +122,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_test: dependency: "direct dev" description: flutter @@ -255,50 +255,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: "909b84830485dbcd0308edf6f7368bc8fd76afa26a270420f34cabea2a6467a0" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.0" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.1.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.0" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.0" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.0" platform: dependency: transitive description: @@ -311,10 +311,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" pool: dependency: transitive description: @@ -323,14 +323,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" pub_semver: dependency: transitive description: @@ -476,10 +468,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.9.0" watcher: dependency: transitive description: @@ -508,18 +500,18 @@ packages: dependency: transitive description: name: win32 - sha256: "7dacfda1edcca378031db9905ad7d7bd56b29fd1a90b0908b71a52a12c41e36b" + sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0 url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.0.6" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: f0c26453a2d47aa4c2570c6a033246a3fc62da2fe23c7ffdd0a7495086dc0247 url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.2" yaml: dependency: transitive description: diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index fa7518e5..7c3882c5 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -1,11 +1,11 @@ name: mmkv description: An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX. -version: 1.3.0 +version: 1.3.1 homepage: https://github.com/Tencent/mmkv environment: - sdk: '>=2.12.0 <3.0.0' - flutter: ">=3.0.0 <4.0.0" + sdk: '>=2.15.0 <4.0.0' + flutter: ">=3.0.0" dependencies: flutter: diff --git a/iOS/MMKV/MMKV.xcodeproj/project.pbxproj b/iOS/MMKV/MMKV.xcodeproj/project.pbxproj index 49e4dc8d..65695731 100644 --- a/iOS/MMKV/MMKV.xcodeproj/project.pbxproj +++ b/iOS/MMKV/MMKV.xcodeproj/project.pbxproj @@ -485,7 +485,7 @@ INFOPLIST_FILE = "MMKV copy-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.3.0; + MARKETING_VERSION = 1.3.1; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit, @@ -527,7 +527,7 @@ INFOPLIST_FILE = "MMKV copy-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.3.0; + MARKETING_VERSION = 1.3.1; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit, @@ -727,7 +727,7 @@ INFOPLIST_FILE = "$(SRCROOT)/MMKV/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.3.0; + MARKETING_VERSION = 1.3.1; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit, @@ -764,7 +764,7 @@ INFOPLIST_FILE = "$(SRCROOT)/MMKV/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.3.0; + MARKETING_VERSION = 1.3.1; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit, @@ -807,7 +807,7 @@ INFOPLIST_FILE = "MMKVAppExtension copy-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.3.0; + MARKETING_VERSION = 1.3.1; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit, @@ -851,7 +851,7 @@ INFOPLIST_FILE = "MMKVAppExtension copy-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.3.0; + MARKETING_VERSION = 1.3.1; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( "-framework", UIKit,