Documentation
¶
Overview ¶
Package mmkv is a cgo-free, read-only decoder for MMKV files. It parses the on-disk format directly in Go so the read path never crosses the cgo boundary. Writes are out of scope — keep using the official cgo library for those. See doc/DESIGN.md for the format spec and boundaries.
Scope: plaintext or AES-encrypted (WithEncryption), optional key expiration, read-only, single-writer/multi-reader.
Freshness, like MMKV C++: every read is transparently up to date. The reader mmaps ".crc" and, on each read, cheaply compares the writer's change canary (crcDigest, actualSize, sequence); when it changed it reloads the snapshot under a shared flock — interlocking with an MMKV writer opened MMKV_MULTI_PROCESS. No manual refresh, near-zero cost when nothing changed. POSIX-only (matches MMKV's flock + mmap).
Index ¶
- Constants
- Variables
- func BackupOne(rootDir, mmapID, dstDir string) error
- func CheckExist(rootDir, mmapID string) bool
- func IsFileValid(rootDir, mmapID string) bool
- func RemoveStorage(rootDir, mmapID string) error
- func RestoreOneFromDirectory(rootDir, mmapID, srcDir string) error
- type Decryptor
- type MMKV
- func (m *MMKV) ActualSize() int
- func (m *MMKV) AllKeys() []string
- func (m *MMKV) Async() error
- func (m *MMKV) ClearAll() error
- func (m *MMKV) ClearMemoryCache()
- func (m *MMKV) Close() error
- func (m *MMKV) Contains(key string) bool
- func (m *MMKV) Count() int
- func (m *MMKV) DisableAutoKeyExpire() error
- func (m *MMKV) DisableCompareBeforeSet()
- func (m *MMKV) EnableAutoKeyExpire(seconds uint32) error
- func (m *MMKV) EnableCompareBeforeSet()
- func (m *MMKV) Err() error
- func (m *MMKV) GetBool(key string) (bool, bool)
- func (m *MMKV) GetBytes(key string) ([]byte, bool)
- func (m *MMKV) GetBytesCopy(key string) ([]byte, bool)
- func (m *MMKV) GetFloat32(key string) (float32, bool)
- func (m *MMKV) GetFloat64(key string) (float64, bool)
- func (m *MMKV) GetInt32(key string) (int32, bool)
- func (m *MMKV) GetInt64(key string) (int64, bool)
- func (m *MMKV) GetString(key string) (string, bool)
- func (m *MMKV) GetStringCopy(key string) (string, bool)
- func (m *MMKV) GetStringSlice(key string) ([]string, bool)
- func (m *MMKV) GetUInt32(key string) (uint32, bool)
- func (m *MMKV) GetUInt64(key string) (uint64, bool)
- func (m *MMKV) GetValueSize(key string) int
- func (m *MMKV) ImportFrom(src *MMKV) (int, error)
- func (m *MMKV) IsCompareBeforeSetEnabled() bool
- func (m *MMKV) IsEncryptionEnabled() bool
- func (m *MMKV) IsExpirationEnabled() bool
- func (m *MMKV) Lock()
- func (m *MMKV) MmapID() string
- func (m *MMKV) ReKey(newKey []byte) error
- func (m *MMKV) RemoveValueForKey(key string) error
- func (m *MMKV) RemoveValuesForKeys(keys []string) error
- func (m *MMKV) RootDir() string
- func (m *MMKV) SetBool(key string, v bool) error
- func (m *MMKV) SetBytes(key string, v []byte) error
- func (m *MMKV) SetFloat32(key string, v float32) error
- func (m *MMKV) SetFloat64(key string, v float64) error
- func (m *MMKV) SetInt32(key string, v int32) error
- func (m *MMKV) SetInt64(key string, v int64) error
- func (m *MMKV) SetString(key, v string) error
- func (m *MMKV) SetStringSlice(key string, v []string) error
- func (m *MMKV) SetUInt32(key string, v uint32) error
- func (m *MMKV) SetUInt64(key string, v uint64) error
- func (m *MMKV) Sync() error
- func (m *MMKV) TotalSize() int
- func (m *MMKV) Trim() error
- func (m *MMKV) TryLock() bool
- func (m *MMKV) Unlock()
- func (m *MMKV) WriteValueToBuffer(key string, buf []byte) int
- type MMKVOption
- type NameSpace
- func (ns NameSpace) BackupOne(mmapID, dstDir string) error
- func (ns NameSpace) MMKVWithID(mmapID string, opts ...MMKVOption) (*MMKV, error)
- func (ns NameSpace) Open(mmapID string, opts ...Option) (*Reader, error)
- func (ns NameSpace) RestoreOne(mmapID, srcDir string) error
- func (ns NameSpace) RootDir() string
- type Option
- type Reader
- func (r *Reader) Close() error
- func (r *Reader) Contains(key string) bool
- func (r *Reader) Err() error
- func (r *Reader) GetBool(key string) (bool, bool)
- func (r *Reader) GetBytes(key string) ([]byte, bool)
- func (r *Reader) GetBytesCopy(key string) ([]byte, bool)
- func (r *Reader) GetFloat32(key string) (float32, bool)
- func (r *Reader) GetFloat64(key string) (float64, bool)
- func (r *Reader) GetInt32(key string) (int32, bool)
- func (r *Reader) GetInt64(key string) (int64, bool)
- func (r *Reader) GetString(key string) (string, bool)
- func (r *Reader) GetStringCopy(key string) (string, bool)
- func (r *Reader) GetUInt32(key string) (uint32, bool)
- func (r *Reader) GetUInt64(key string) (uint64, bool)
- func (r *Reader) Keys() []string
- type Writer
- func (w *Writer) Flush() error
- func (w *Writer) SetBool(key string, v bool) *Writer
- func (w *Writer) SetBytes(key string, v []byte) *Writer
- func (w *Writer) SetFloat32(key string, v float32) *Writer
- func (w *Writer) SetFloat64(key string, v float64) *Writer
- func (w *Writer) SetInt32(key string, v int32) *Writer
- func (w *Writer) SetInt64(key string, v int64) *Writer
- func (w *Writer) SetString(key, v string) *Writer
- func (w *Writer) SetStringSlice(key string, v []string) *Writer
- func (w *Writer) SetUInt32(key string, v uint32) *Writer
- func (w *Writer) SetUInt64(key string, v uint64) *Writer
Constants ¶
const ExpireNever uint32 = 0
ExpireNever, passed to EnableAutoKeyExpire, enables expiration without a common duration: keys never expire unless given a per-set duration (matches MMKV).
Variables ¶
var ( // ErrUnsupportedVersion guards against silent breakage on a future MMKV // on-disk format. Fall back to the cgo library when you see this. ErrUnsupportedVersion = errors.New("mmkv: unsupported MMKV file version") // ErrCRCMismatch means the data region failed its CRC32 check — corrupt, // torn write, or (commonly) an encrypted file read without (the right) key. ErrCRCMismatch = errors.New("mmkv: CRC mismatch") )
var ErrClosed = errors.New("mmkv: instance closed")
ErrClosed is returned by operations on a closed MMKV.
var ErrReadOnly = errors.New("mmkv: instance is read-only")
ErrReadOnly is returned by mutating operations on a read-only instance.
Functions ¶
func BackupOne ¶
BackupOne copies one MMKV instance (data file + ".crc") from rootDir to dstDir, producing a consistent, independently-readable snapshot. It holds a shared flock on ".crc" during the copy, so it is safe to run while an MMKV writer (opened MMKV_MULTI_PROCESS) is active — the writer's exclusive lock and this shared lock interlock. POSIX-only (matches MMKV's flock).
Backup is encryption-agnostic: it copies raw bytes, so encrypted instances back up fine without a key. Restore is a write operation and is intentionally out of scope — use the official cgo library's RestoreOneFromDirectory from the writer process.
func CheckExist ¶
CheckExist reports whether both the data file and its ".crc" exist.
func IsFileValid ¶
IsFileValid reports whether the instance on disk is structurally valid: the meta parses to a supported version and the data region passes its CRC. Like MMKV's isFileValid, this is a structural check over the raw region (the CRC is over the ciphertext when encrypted), so it needs no key.
func RemoveStorage ¶
RemoveStorage closes any cached instance for the file and unlinks both the data file and its ".crc" (matching MMKV's removeStorage: close, then delete).
func RestoreOneFromDirectory ¶
RestoreOneFromDirectory restores one instance (data + ".crc") from srcDir into rootDir, overwriting in place. It holds an exclusive flock on the destination ".crc" for the whole copy (interlocking with multi-process instances and other processes) and writes with O_TRUNC, which preserves the inode so a live mmap stays valid — then it flags any cached instance to remap+reload on its next call. The pure-Go counterpart to BackupOne.
Coordination: a SINGLE-process instance takes no flock, so don't restore over one that is being concurrently accessed; restore over multi-process instances or while the file is idle (matches MMKV's restore contract).
Types ¶
type Decryptor ¶
Decryptor turns the on-disk (encrypted) data region into plaintext wire bytes. iv is the per-file IV from the meta file (.crc m_vector, 16 bytes) — it can change when the writer does a full write-back, so it is passed on every reload. Inject a custom one via WithDecryptor, or use WithEncryption for standard MMKV AES (AES-CFB-128; AES-128 or AES-256 chosen by key length).
type MMKV ¶
type MMKV struct {
// contains filtered or unexported fields
}
MMKV is a cgo-free, read+write MMKV instance: it reads and writes the official on-disk format and uses the same flock protocol, so it interoperates with the C++ library over the same files across processes. Open it with MMKVWithID.
Concurrency mirrors MMKV C++: every operation takes the instance thread lock (serializing goroutines) and, in multi-process mode, a cross-process flock on the ".crc" file (shared for reads, exclusive for writes); single-process mode skips the flock, keeping reads cheap. One instance is shared per file per process (see MMKVWithID).
View lifetime: GetBytes/GetString return zero-copy views into the live mapping that are valid only until the next call on this instance (a write or a cross-process reload can remap the data). Use the Copy variants to retain. POSIX-only (Linux + macOS).
func MMKVWithID ¶
func MMKVWithID(rootDir, mmapID string, opts ...MMKVOption) (*MMKV, error)
MMKVWithID opens (or returns the already-open) read+write instance for <rootDir>/<encodeFilePath(mmapID)> (+ ".crc"). One instance is cached per file per process — concurrent callers get the same *MMKV, which is required for correctness (two instances would each flock the same fd without mutual exclusion and diverge in memory). POSIX-only.
func (*MMKV) ActualSize ¶
func (*MMKV) ClearAll ¶
ClearAll resets the instance to empty, truncating the data file back to the default size and bumping the sequence so other processes reload.
func (*MMKV) ClearMemoryCache ¶
func (m *MMKV) ClearMemoryCache()
ClearMemoryCache drops the in-memory dictionary so the next access reloads from disk (matches MMKV's clearMemoryCache). It does not change the files.
func (*MMKV) Close ¶
Close unmaps the files and drops the instance from the registry. Outstanding GetBytes/GetString views become invalid.
func (*MMKV) DisableAutoKeyExpire ¶
DisableAutoKeyExpire turns off expiration: it drops already-expired keys, strips the trailing timestamp from the rest, clears the meta flag, and rewrites.
func (*MMKV) DisableCompareBeforeSet ¶
func (m *MMKV) DisableCompareBeforeSet()
DisableCompareBeforeSet turns off compare-before-set.
func (*MMKV) EnableAutoKeyExpire ¶
EnableAutoKeyExpire turns on key expiration with a default per-set duration in seconds (0 = ExpireNever, i.e. keys expire only with a per-set duration). It persists the flag in the meta and, on first enable, appends a never-expire timestamp to every existing value via a full write-back — so the on-disk format matches MMKV's enableAutoKeyExpire.
func (*MMKV) EnableCompareBeforeSet ¶
func (m *MMKV) EnableCompareBeforeSet()
EnableCompareBeforeSet makes a Set a no-op when the stored value already equals the new one — avoiding write amplification for unchanged values. It is an in-memory, per-instance setting (not persisted) and is mutually exclusive with key expiration: it has no effect while expiration is enabled, and enabling expiration turns it off (matching MMKV).
func (*MMKV) GetBytes ¶
GetBytes returns a zero-copy view; valid only until the next call on this instance. Use GetBytesCopy to retain.
func (*MMKV) GetBytesCopy ¶
GetBytesCopy returns an independent copy of the value.
func (*MMKV) GetString ¶
GetString returns a zero-copy string view; valid only until the next call on this instance, and the bytes must not be mutated. Use GetStringCopy to retain.
func (*MMKV) GetStringCopy ¶
GetStringCopy returns the value as an independent string.
func (*MMKV) GetStringSlice ¶
GetStringSlice returns the value as a []string (MMKV's vector<string>). The strings are copies, so the result outlives the next call.
func (*MMKV) GetValueSize ¶
GetValueSize returns the number of stored value bytes for key (the encoded value, expire timestamp stripped), or -1 if absent.
func (*MMKV) ImportFrom ¶
ImportFrom copies every live key from src into this instance (applying this instance's expiration/compareBeforeSet policy), returning the number of keys imported. src is snapshotted under its own lock first, then written here, so two instances importing from each other can't deadlock.
func (*MMKV) IsCompareBeforeSetEnabled ¶
IsCompareBeforeSetEnabled reports whether compare-before-set is on.
func (*MMKV) IsEncryptionEnabled ¶
IsEncryptionEnabled reports whether the instance is AES-encrypted.
func (*MMKV) IsExpirationEnabled ¶
IsExpirationEnabled reports whether auto key expiration is on.
func (*MMKV) Lock ¶
func (m *MMKV) Lock()
Lock / Unlock / TryLock expose MMKV's public cross-process exclusive lock for atomic read-modify-write across processes (matching MMKV::lock/unlock/try_lock).
Only the cross-process flock is held across the user's critical section; the thread mutex is taken just to manipulate the lock counters and released immediately, so ordinary Get/Set calls between Lock and Unlock do NOT deadlock (they re-enter the already-held exclusive flock by reference count). In single-process mode (no WithMultiProcess) these are no-ops, exactly like MMKV.
Usage:
m.Lock()
defer m.Unlock()
v, _ := m.GetInt32("counter")
m.SetInt32("counter", v+1)
func (*MMKV) ReKey ¶
ReKey changes the encryption key, re-encrypting the whole file via a full write-back (with a fresh IV). It also converts between plaintext and encrypted: pass a non-empty key to encrypt (or change the key), or an empty key to decrypt to plaintext. The new key takes effect only if the rewrite succeeds. AES width follows the key length (>16 bytes ⇒ AES-256).
func (*MMKV) RemoveValueForKey ¶
RemoveValueForKey deletes key. It appends an empty-value tombstone when it fits (durable + visible cross-process), else compacts via a full write-back.
func (*MMKV) RemoveValuesForKeys ¶
RemoveValuesForKeys deletes several keys, compacting once.
func (*MMKV) SetStringSlice ¶
SetStringSlice stores a []string (MMKV's vector<string>).
func (*MMKV) TotalSize ¶
TotalSize returns the data file size (bytes). ActualSize returns the bytes of the live KV region.
func (*MMKV) Trim ¶
Trim compacts the store (a full write-back) and then shrinks the data file toward ~2x the live size, floored at one page — matching MMKV's trim().
type MMKVOption ¶
type MMKVOption func(*mmkvConfig)
MMKVOption configures an MMKV instance at open time.
func WithContentChangedHandler ¶
func WithContentChangedHandler(fn func()) MMKVOption
WithContentChangedHandler registers a callback invoked after the instance reloads because another process changed the file (multi-process mode). It runs while the instance lock is held, so do not call back into this instance from it; keep it short.
func WithCryptKey ¶
func WithCryptKey(key []byte) MMKVOption
WithCryptKey opens an AES-encrypted instance. The AES width follows MMKV: a key longer than 16 bytes selects AES-256, otherwise AES-128 (truncated/zero- padded). Pass the same key the file was written with; open the same file with consistent options across the process (the instance is cached by path).
func WithMultiProcess ¶
func WithMultiProcess() MMKVOption
WithMultiProcess opens the instance for cross-process use: every read takes a shared flock and every write an exclusive flock on the ".crc" file, interlocking with other processes (Go or C++). The writer side of any process sharing the file MUST also be multi-process. Without it, flock is skipped and reads stay fast (single-process, like MMKV's default).
func WithReadOnly ¶
func WithReadOnly() MMKVOption
WithReadOnly opens the instance read-only: the files are mapped O_RDONLY and every mutating call returns ErrReadOnly. The file must already exist. This is the single-type way to read a store you must not (or cannot) write.
func WithRecoverOnError ¶
func WithRecoverOnError() MMKVOption
WithRecoverOnError salvages as much as possible (greedy decode) when the data region fails its CRC and the last-confirmed snapshot can't be restored either, instead of discarding to empty (MMKV's OnErrorRecover). A writable open then repairs the store immediately with a full write-back; a salvage during a cross-process reload is repaired by the next write. Read-only instances just serve the salvaged view.
type NameSpace ¶
type NameSpace struct {
// contains filtered or unexported fields
}
NameSpace reads MMKV instances stored under a custom root directory — MMKV's "namespace". A namespace is nothing more than a different rootDir; this type is sugar so callers don't repeat the path. Instances created by the cgo side via GetNameSpace(rootDir).MMKVWithID(id) live at <rootDir>/<encodeFilePath(id)>.
func OpenNameSpace ¶
OpenNameSpace returns a NameSpace rooted at rootDir.
func (NameSpace) MMKVWithID ¶
func (ns NameSpace) MMKVWithID(mmapID string, opts ...MMKVOption) (*MMKV, error)
MMKVWithID opens a read+write instance within the namespace (custom root dir).
func (NameSpace) RestoreOne ¶
RestoreOne restores an instance within the namespace from srcDir.
type Option ¶
type Option func(*Reader)
Option configures a Reader.
func WithDecryptor ¶
WithDecryptor supplies a custom decryptor for encrypted files.
func WithEncryption ¶
WithEncryption decrypts files written with an MMKV cryptKey. The AES width follows MMKV: a key longer than 16 bytes selects AES-256, otherwise AES-128; the key is truncated or zero-padded to 16/32 bytes. Pass the SAME key the writer used.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader is a cgo-free read-only view of one MMKV instance.
func Open ¶
Open loads <rootDir>/<encodeFilePath(mmapID)> (+ ".crc") and keeps the reader transparently fresh (check-on-read; see package doc). POSIX-only.
func (*Reader) Close ¶
Close releases buffers, the mmap and the fd. Value slices from GetBytes/GetString become invalid afterwards.
func (*Reader) Err ¶
Err returns the last error from a best-effort reload, if any. Reads keep serving the last good snapshot when a reload fails.
func (*Reader) GetBytes ¶
GetBytes returns the value as a view into the reader's buffer (no copy); valid until the next reload/Close. Do not mutate it. Use GetBytesCopy for an independent slice (recommended in live mode).
func (*Reader) GetBytesCopy ¶
GetBytesCopy returns an independent copy of the value.
func (*Reader) GetString ¶
GetString returns the value as a zero-copy string view over the reader's buffer; valid until the next reload/Close, and the underlying bytes must not be mutated (e.g. via a GetBytes alias). The view aliases heap memory and is kept alive by the GC while held. Use GetStringCopy for an independent string (recommended for multi-goroutine live use).
func (*Reader) GetStringCopy ¶
GetStringCopy returns the value as an independent string (a copy).
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer builds a plaintext MMKV instance from scratch and flushes it in a single full write-back — the same operation MMKV performs on its first write (empty dict → expandAndWriteBack → doFullWriteBack). It produces a file that the official C++ library and the read-only Reader both load: version=4 (Flag), sequence=1, no encryption, no expiration.
This is the Phase-A write seam. The incremental, lock-coordinated live MMKV type (append, transparent reload, multi-process) builds on the same encode/meta/memfile/flock foundation. A Writer is not safe for concurrent use and does not coordinate with other processes; use it to author or replace a file, not to co-write a live one. POSIX-only.
func (*Writer) Flush ¶
Flush writes the data file and its ".crc" meta to disk via a full write-back, then msyncs both (data first, then meta — the order MMKV relies on for crash consistency).