Append encrypted updates so existing compaction handles IV rotation and sync, avoiding a synchronous flush on every update.
Add regression tests. Profiled on iPhone.
Close the cached instance before acquiring the deletion flock, matching the instance-mutex-before-file-lock order used by writers. Retain the independent CRC descriptor and cross-process exclusion during deletion.
Document instance lifetime requirements and add deterministic regression coverage for writer ordering, cached and uncached removal, and cross-process exclusion. The test passes under ASan and UBSan and detects the original lock inversion.
importFrom() passes the destination no-copy views of the source's values
(KeyValueHolderCrypt::toMMBuffer() / KeyValueHolder::toMMBuffer()). An
encrypted destination keeps a value that is not stored as an offset with
KeyValueHolderCrypt(MMBuffer &&), which adopted the buffer's pointer without
checking that the buffer owns it, and freed it later on remove, overwrite or
clearAll(): a free of memory inside the source's map node or mmap, or a
double free of the source's heap buffer.
Copy a no-copy buffer instead of adopting it. Add a unit test importing plain
and encrypted stores into AES-128 and AES-256 destinations.
A first set after reopening can encode without the expiration suffix and still report success, leaving an unreadable value. Move the existing write locks and load check ahead of expiry-dependent encoding in each setter path, without adding lock acquisitions or scalar buffers. Route encoded vectors through the existing expiration helper so public templates do not need lock internals.
Keep a minimal set-succeeds/read-fails regression separate from the setter-overload coverage. Both explicitly avoid warming the lazy instance before set.
Read the expiration flag before the read-only metadata return. Filter expired entries in memory for count/allKeys instead of using fullWriteback, which refuses read-only stores. Test each API as the first operation on a fresh instance, for plain and encrypted stores, and verify filtering leaves stored entries intact.
A reopened instance starts with expiration disabled in memory. Load metadata before selecting the raw or expiration-aware read path; otherwise the first getter can return an expired value. Move the existing load check rather than adding another one.
Keys longer than 65531 bytes cause keySize and computedKVSize (both uint16_t)
to wrap around, silently corrupting the internal offset so that subsequent reads
return garbage. Reject keys exceeding the safe threshold (UINT16_MAX - 4) at
all write and parse entry points.
Provide a language-agnostic C99 bridge using standard C types (const char*,
void*, int64_t, etc.) so MMKV Core can be consumed from C-only environments
- Core/cbridge/MMKVBridge.h: pure C header with mmkv_ prefixed API
- Core/cbridge/MMKVBridge.cpp: implementation wrapping MMKV C++ API
- POSIX/demo/demo_c.c: pure C demo exercising all API surface
- CMakeLists.txt updates for core library and demo_c target
Replace 4 separate static function pointers (LogHandler, ErrorHandler,
ContentChangeHandler, ContentLoadedHandler) with a single MMKVHandler
abstract class in Core C++. Update all 6 platform wrappers (iOS, Android,
OHOS, Python, Golang, Flutter) to use adapter classes bridging to the new
unified handler. Add onMMKVContentLoadSuccessfully callback to Android,
OHOS, Flutter, Python and Golang where it was previously missing.