539 lines
16 KiB
Go
539 lines
16 KiB
Go
/*
|
|
* Tencent is pleased to support the open source community by making
|
|
* MMKV available.
|
|
*
|
|
* Copyright (C) 2020 THL A29 Limited, a Tencent company.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the BSD 3-Clause License (the "License"); you may not use
|
|
* this file except in compliance with the License. You may obtain a copy of
|
|
* the License at
|
|
*
|
|
* https://opensource.org/licenses/BSD-3-Clause
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// MMKV is a cross-platform key-value storage framework developed by WeChat.
|
|
package mmkv
|
|
|
|
// #cgo CXXFLAGS: -D CGO -D FORCE_POSIX -I ${SRCDIR}/../../Core -std=c++17
|
|
// #cgo LDFLAGS: -L./lib -lmmkv -lcore -lz -lpthread
|
|
/*
|
|
#include "golang-bridge.h"
|
|
#include <stdlib.h>
|
|
|
|
typedef void* voidptr_t;
|
|
|
|
static GoStringWrap_t wrapGoString(_GoString_ str) {
|
|
GoStringWrap_t wrap = { _GoStringPtr(str), _GoStringLen(str) };
|
|
return wrap;
|
|
}
|
|
|
|
static GoStringWrap_t GoStringWrapNil() {
|
|
GoStringWrap_t result = { 0, 0 };
|
|
return result;
|
|
}
|
|
|
|
static GoStringWrap_t wrapGoByteSlice(const void *ptr, size_t len) {
|
|
GoStringWrap_t wrap = { ptr, len };
|
|
return wrap;
|
|
}
|
|
|
|
static void freeStringArray(GoStringWrap_t *a, size_t size) {
|
|
for (size_t i = 0; i < size; i++) {
|
|
free((void*) a[i].ptr);
|
|
}
|
|
}
|
|
*/
|
|
import "C"
|
|
import "unsafe"
|
|
|
|
const (
|
|
MMKVLogDebug = iota // not available for release/product build
|
|
MMKVLogInfo // default level
|
|
MMKVLogWarning
|
|
MMKVLogError
|
|
MMKVLogNone // special level used to disable all log messages
|
|
)
|
|
|
|
const (
|
|
MMKV_SINGLE_PROCESS = 1 << iota
|
|
MMKV_MULTI_PROCESS
|
|
)
|
|
|
|
// 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
|
|
func (buffer MMBuffer) Pointer() uintptr {
|
|
return buffer.ptr
|
|
}
|
|
|
|
// the size of underlying memory
|
|
func (buffer MMBuffer) Length() int {
|
|
return buffer.length
|
|
}
|
|
|
|
// 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
|
|
// 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
|
|
func (buffer MMBuffer) Destroy() {
|
|
C.free(unsafe.Pointer(buffer.ptr))
|
|
}
|
|
|
|
type MMKV interface {
|
|
SetBool(value bool, key string) bool
|
|
GetBool(key string) bool
|
|
GetBoolWithDefault(key string, defaultValue bool) bool
|
|
|
|
SetInt32(value int32, key string) bool
|
|
GetInt32(key string) int32
|
|
GetInt32WithDefault(key string, defaultValue int32) int32
|
|
|
|
SetInt64(value int64, key string) bool
|
|
GetInt64(key string) int64
|
|
GetInt64WithDefault(key string, defaultValue int64) int64
|
|
|
|
SetUInt32(value uint32, key string) bool
|
|
GetUInt32(key string) uint32
|
|
GetUInt32WithDefault(key string, defaultValue uint32) uint32
|
|
|
|
SetUInt64(value uint64, key string) bool
|
|
GetUInt64(key string) uint64
|
|
GetUInt64WithDefault(key string, defaultValue uint64) uint64
|
|
|
|
SetFloat32(value float32, key string) bool
|
|
GetFloat32(key string) float32
|
|
GetFloat32WithDefault(key string, defaultValue float32) float32
|
|
|
|
SetFloat64(value float64, key string) bool
|
|
GetFloat64(key string) float64
|
|
GetFloat64WithDefault(key string, defaultValue float64) float64
|
|
|
|
// string value should be utf-8 encoded
|
|
SetString(value string, key string) bool
|
|
GetString(key string) string
|
|
// get C memory directly (without memcpy), much more efferent for large value
|
|
GetStringBuffer(key string) MMBuffer
|
|
|
|
SetBytes(value []byte, key string) bool
|
|
GetBytes(key string) []byte
|
|
// 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()
|
|
|
|
// return count of keys
|
|
Count() uint64
|
|
|
|
AllKeys() []string
|
|
Contains(key string) bool
|
|
|
|
// total size of the file
|
|
TotalSize() uint64
|
|
|
|
// actual used size of the file
|
|
ActualSize() uint64
|
|
|
|
// 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(sync bool)
|
|
// Clear all caches (on memory warning).
|
|
ClearMemoryCache()
|
|
|
|
/* Trim the file size to minimal.
|
|
* MMKV's size won't reduce after deleting key-values.
|
|
* Call this method after lots of deleting if you care about disk usage.
|
|
* Note that clearAll() has the similar effect.
|
|
*/
|
|
Trim()
|
|
|
|
// Close the instance when it's no longer needed in the near future.
|
|
// Any subsequent call to the instance is undefined behavior.
|
|
Close()
|
|
|
|
/* 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).
|
|
// Usually you should call this method after other process reKey() the multi-process mmkv.
|
|
CheckReSetCryptKey(key string)
|
|
|
|
// See also reKey().
|
|
CryptKey() string
|
|
}
|
|
|
|
type ctorMMKV uintptr
|
|
|
|
// return the version of MMKV
|
|
func Version() string {
|
|
version := C.version()
|
|
goStr := C.GoString(version)
|
|
return goStr
|
|
}
|
|
|
|
/* MMKV must be initialized before any usage.
|
|
* Generally speaking you should do this inside main():
|
|
func main() {
|
|
mmkv.InitializeMMKV("/path/to/my/working/dir")
|
|
// other logic
|
|
}
|
|
*/
|
|
func InitializeMMKV(rootDir string) {
|
|
C.mmkvInitialize(C.wrapGoString(rootDir), MMKVLogInfo)
|
|
}
|
|
|
|
// Same as the function InitializeMMKV() above, except that you can customize MMKV's log level by passing logLevel.
|
|
// You can even turnoff logging by passing MMKVLogNone, which we don't recommend doing.
|
|
func InitializeMMKVWithLogLevel(rootDir string, logLevel int) {
|
|
C.mmkvInitialize(C.wrapGoString(rootDir), C.int32_t(logLevel))
|
|
}
|
|
|
|
// 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
|
|
func PageSize() int32 {
|
|
return int32(C.pageSize())
|
|
}
|
|
|
|
// 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.
|
|
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.
|
|
func DefaultMMKVWithModeAndCryptKey(mode int, cryptKey string) MMKV {
|
|
mmkv := ctorMMKV(C.getDefaultMMKV(MMKV_SINGLE_PROCESS, C.wrapGoString(cryptKey)))
|
|
return MMKV(mmkv)
|
|
}
|
|
|
|
// an instance with specific location ${MMKV Root}/mmapID, in single-process mode.
|
|
func MMKVWithID(mmapID string) MMKV {
|
|
cStrNull := C.GoStringWrapNil()
|
|
mmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), MMKV_SINGLE_PROCESS, cStrNull, cStrNull))
|
|
return MMKV(mmkv)
|
|
}
|
|
|
|
// an instance with specific location ${MMKV Root}/mmapID, in single-process or multi-process mode.
|
|
func MMKVWithIDAndMode(mmapID string, mode int) MMKV {
|
|
cStrNull := C.GoStringWrapNil()
|
|
mmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), C.int(mode), cStrNull, cStrNull))
|
|
return MMKV(mmkv)
|
|
}
|
|
|
|
// 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))
|
|
return MMKV(mmkv)
|
|
}
|
|
|
|
// 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)
|
|
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
|
|
// return count of MMKV successfully backup-ed
|
|
func BackupAllToDirectory(dstDir string) uint64 {
|
|
cStrNull := C.GoStringWrapNil()
|
|
ret := C.backupAllToDirectory(C.wrapGoString(dstDir), cStrNull)
|
|
return uint64(ret)
|
|
}
|
|
|
|
// 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()
|
|
ret := C.restoreAllFromDirectory(C.wrapGoString(srcDir), cStrNull)
|
|
return uint64(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetBool(value bool, key string) bool {
|
|
ret := C.encodeBool(unsafe.Pointer(kv), C.wrapGoString(key), C.bool(value))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetBool(key string) bool {
|
|
return kv.GetBoolWithDefault(key, false)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetBoolWithDefault(key string, defaultValue bool) bool {
|
|
value := C.decodeBool(unsafe.Pointer(kv), C.wrapGoString(key), C.bool(defaultValue))
|
|
return bool(value)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetInt32(value int32, key string) bool {
|
|
ret := C.encodeInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.int32_t(value))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetInt32(key string) int32 {
|
|
return kv.GetInt32WithDefault(key, 0)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetInt32WithDefault(key string, defaultValue int32) int32 {
|
|
value := C.decodeInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.int32_t(defaultValue))
|
|
return int32(value)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetUInt32(value uint32, key string) bool {
|
|
ret := C.encodeUInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.uint32_t(value))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetUInt32(key string) uint32 {
|
|
return kv.GetUInt32WithDefault(key, 0)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetUInt32WithDefault(key string, defaultValue uint32) uint32 {
|
|
value := C.decodeUInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.uint32_t(defaultValue))
|
|
return uint32(value)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetInt64(value int64, key string) bool {
|
|
ret := C.encodeInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.int64_t(value))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetInt64(key string) int64 {
|
|
return kv.GetInt64WithDefault(key, 0)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetInt64WithDefault(key string, defaultValue int64) int64 {
|
|
value := C.decodeInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.int64_t(defaultValue))
|
|
return int64(value)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetUInt64(value uint64, key string) bool {
|
|
ret := C.encodeUInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.uint64_t(value))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetUInt64(key string) uint64 {
|
|
return kv.GetUInt64WithDefault(key, 0)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetUInt64WithDefault(key string, defaultValue uint64) uint64 {
|
|
value := C.decodeUInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.uint64_t(defaultValue))
|
|
return uint64(value)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetFloat32(value float32, key string) bool {
|
|
ret := C.encodeFloat(unsafe.Pointer(kv), C.wrapGoString(key), C.float(value))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetFloat32(key string) float32 {
|
|
return kv.GetFloat32WithDefault(key, 0)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetFloat32WithDefault(key string, defaultValue float32) float32 {
|
|
value := C.decodeFloat(unsafe.Pointer(kv), C.wrapGoString(key), C.float(defaultValue))
|
|
return float32(value)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetFloat64(value float64, key string) bool {
|
|
ret := C.encodeDouble(unsafe.Pointer(kv), C.wrapGoString(key), C.double(value))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetFloat64(key string) float64 {
|
|
return kv.GetFloat64WithDefault(key, 0)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetFloat64WithDefault(key string, defaultValue float64) float64 {
|
|
value := C.decodeDouble(unsafe.Pointer(kv), C.wrapGoString(key), C.double(defaultValue))
|
|
return float64(value)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetString(value string, key string) bool {
|
|
cValue := C.wrapGoString(value)
|
|
ret := C.encodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), cValue)
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetString(key string) string {
|
|
var length uint64
|
|
|
|
cValue := C.decodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), (*C.uint64_t)(&length))
|
|
value := C.GoStringN((*C.char)(cValue), C.int(length))
|
|
|
|
C.free(unsafe.Pointer(cValue))
|
|
return value
|
|
}
|
|
|
|
func (kv ctorMMKV) GetStringBuffer(key string) MMBuffer {
|
|
return kv.GetBytesBuffer(key)
|
|
}
|
|
|
|
func (kv ctorMMKV) SetBytes(value []byte, key string) bool {
|
|
cValue := C.wrapGoByteSlice(unsafe.Pointer(&value[0]), C.size_t(len(value)))
|
|
ret := C.encodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), cValue)
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) GetBytes(key string) []byte {
|
|
var length uint64
|
|
|
|
cValue := C.decodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), (*C.uint64_t)(&length))
|
|
value := C.GoBytes(unsafe.Pointer(cValue), C.int(length))
|
|
|
|
C.free(unsafe.Pointer(cValue))
|
|
return value
|
|
}
|
|
|
|
func (kv ctorMMKV) GetBytesBuffer(key string) MMBuffer {
|
|
var length uint64
|
|
|
|
cValue := C.decodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), (*C.uint64_t)(&length))
|
|
value := MMBuffer{uintptr(cValue), int(length)}
|
|
|
|
return value
|
|
}
|
|
|
|
func (kv ctorMMKV) RemoveKey(key string) {
|
|
C.removeValueForKey(unsafe.Pointer(kv), C.wrapGoString(key))
|
|
}
|
|
|
|
func (kv ctorMMKV) RemoveKeys(keys []string) {
|
|
keyArray := (*C.struct_GoStringWrap)(unsafe.Pointer(&keys[0]))
|
|
C.removeValuesForKeys(unsafe.Pointer(kv), keyArray, C.uint64_t(len(keys)))
|
|
}
|
|
|
|
func (kv ctorMMKV) Count() uint64 {
|
|
return uint64(C.count(unsafe.Pointer(kv)))
|
|
}
|
|
|
|
func (kv ctorMMKV) AllKeys() []string {
|
|
count := uint64(0)
|
|
keyArray := C.allKeys(unsafe.Pointer(kv), (*C.uint64_t)(&count))
|
|
if keyArray == nil || count == 0 {
|
|
return []string{}
|
|
}
|
|
// turn C array into go slice with offset(0), length(count) & capacity(count)
|
|
keys := (*[1 << 30]C.struct_GoStringWrap)(unsafe.Pointer(keyArray))[0:count:count]
|
|
|
|
// Actually the keys IS a go string slice, but we need to COPY the elements for the caller to use.
|
|
// Too bad go doesn't has destructors, hence we can't simply TRANSFER ownership of C memory.
|
|
result := make([]string, count)
|
|
for index := uint64(0); index < count; index++ {
|
|
key := keys[index]
|
|
result[index] = C.GoStringN(key.ptr, C.int(key.length))
|
|
}
|
|
|
|
C.freeStringArray(keyArray, C.size_t(count))
|
|
C.free(unsafe.Pointer(keyArray))
|
|
return result
|
|
}
|
|
|
|
func (kv ctorMMKV) Contains(key string) bool {
|
|
ret := C.containsKey(unsafe.Pointer(kv), C.wrapGoString(key))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) ClearAll() {
|
|
C.clearAll(unsafe.Pointer(kv))
|
|
}
|
|
|
|
func (kv ctorMMKV) TotalSize() uint64 {
|
|
return uint64(C.totalSize(unsafe.Pointer(kv)))
|
|
}
|
|
|
|
func (kv ctorMMKV) ActualSize() uint64 {
|
|
return uint64(C.actualSize(unsafe.Pointer(kv)))
|
|
}
|
|
|
|
func (kv ctorMMKV) MMAP_ID() string {
|
|
cStr := C.mmapID(unsafe.Pointer(kv))
|
|
return C.GoString(cStr)
|
|
}
|
|
|
|
func (kv ctorMMKV) Sync(sync bool) {
|
|
C.mmkvSync(unsafe.Pointer(kv), C.bool(sync))
|
|
}
|
|
|
|
func (kv ctorMMKV) ClearMemoryCache() {
|
|
C.clearMemoryCache(unsafe.Pointer(kv))
|
|
}
|
|
|
|
func (kv ctorMMKV) Trim() {
|
|
C.trim(unsafe.Pointer(kv))
|
|
}
|
|
|
|
func (kv ctorMMKV) Close() {
|
|
C.mmkvClose(unsafe.Pointer(kv))
|
|
}
|
|
|
|
func (kv ctorMMKV) ReKey(newKey string) bool {
|
|
ret := C.reKey(unsafe.Pointer(kv), C.wrapGoString(newKey))
|
|
return bool(ret)
|
|
}
|
|
|
|
func (kv ctorMMKV) CheckReSetCryptKey(key string) {
|
|
C.checkReSetCryptKey(unsafe.Pointer(kv), C.wrapGoString(key))
|
|
}
|
|
|
|
func (kv ctorMMKV) CryptKey() string {
|
|
var cLen C.uint32_t
|
|
cStr := C.cryptKey(unsafe.Pointer(kv), &cLen)
|
|
if cStr == nil || cLen == 0 {
|
|
return ""
|
|
}
|
|
result := C.GoStringN((*C.char)(cStr), C.int(cLen))
|
|
C.free(unsafe.Pointer(cStr))
|
|
return result
|
|
}
|