Compare commits
No commits in common. "41a75bd2993992c23a0c8b48df8dad23517d6c82" and "a40e4e5b9ecaff3228bae55c013bd0b970d0f4bd" have entirely different histories.
41a75bd299
...
a40e4e5b9e
119
callback.go
Normal file
119
callback.go
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/*
|
||||
#include "golang-bridge.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// the callback type of Logger
|
||||
type LogHandler func(level int, file string, line int, function string, message string)
|
||||
|
||||
var gLogHandler LogHandler
|
||||
|
||||
// register a log callback
|
||||
func RegisterLogHandler(logHandler LogHandler) {
|
||||
gLogHandler = logHandler
|
||||
|
||||
C.setWantsLogRedirect(C.bool(true))
|
||||
}
|
||||
|
||||
// unregister a log callback
|
||||
func UnRegisterLogHandler() {
|
||||
gLogHandler = nil
|
||||
|
||||
C.setWantsLogRedirect(C.bool(false))
|
||||
}
|
||||
|
||||
//export myLogHandler
|
||||
func myLogHandler(level int, file string, line int, function string, message string) {
|
||||
if gLogHandler != nil {
|
||||
gLogHandler(level, file, line, function, message)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
OnErrorDiscard = iota // When there's an error, MMKV will discard everything by default.
|
||||
OnErrorRecover // When there's an error, MMKV will try to recover as much data as possible.
|
||||
)
|
||||
|
||||
const (
|
||||
MMKVCRCCheckFail = iota
|
||||
MMKVFileLength
|
||||
)
|
||||
|
||||
// the callback type of error handler
|
||||
// error is either MMKVCRCCheckFail or MMKVFileLength
|
||||
// return OnErrorDiscard (default) or OnErrorRecover
|
||||
type ErrorHandler func(mmapID string, error int) int
|
||||
|
||||
var gErrorHandler ErrorHandler
|
||||
|
||||
// register a error callback
|
||||
func RegisterErrorHandler(errorHandler ErrorHandler) {
|
||||
gErrorHandler = errorHandler
|
||||
|
||||
C.setWantsErrorHandle(C.bool(true))
|
||||
}
|
||||
|
||||
// unregister a error callback
|
||||
func UnRegisterErrorHandler() {
|
||||
gErrorHandler = nil
|
||||
|
||||
C.setWantsErrorHandle(C.bool(false))
|
||||
}
|
||||
|
||||
//export myErrorHandler
|
||||
func myErrorHandler(mmapID string, error int) int {
|
||||
if gErrorHandler != nil {
|
||||
return gErrorHandler(mmapID, error)
|
||||
}
|
||||
return OnErrorDiscard
|
||||
}
|
||||
|
||||
// the type of content change handler
|
||||
type ContentChangeHandler func(mmapID string)
|
||||
|
||||
var gContentChangeHandler ContentChangeHandler
|
||||
|
||||
// register a content change callback
|
||||
func RegisterContentChangeHandler(contentChangeHandler ContentChangeHandler) {
|
||||
gContentChangeHandler = contentChangeHandler
|
||||
|
||||
C.setWantsContentChangeHandle(C.bool(true))
|
||||
}
|
||||
|
||||
// unregister a content change callback
|
||||
func UnRegisterContentChangeHandler() {
|
||||
gContentChangeHandler = nil
|
||||
|
||||
C.setWantsContentChangeHandle(C.bool(false))
|
||||
}
|
||||
|
||||
//export myContentChangeHandler
|
||||
func myContentChangeHandler(mmapID string) {
|
||||
if gContentChangeHandler != nil {
|
||||
gContentChangeHandler(mmapID)
|
||||
}
|
||||
}
|
15
go.mod
15
go.mod
@ -1,16 +1,3 @@
|
||||
module unlock-music.dev/mmkv
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
golang.org/x/exp v0.0.0-20221204150635-6dcec336b2bb
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
go 1.15
|
||||
|
27
go.sum
27
go.sum
@ -1,27 +0,0 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
golang.org/x/exp v0.0.0-20221204150635-6dcec336b2bb h1:QIsP/NmClBICkqnJ4rSIhnrGiGR7Yv9ZORGGnmmLTPk=
|
||||
golang.org/x/exp v0.0.0-20221204150635-6dcec336b2bb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
542
golang-bridge.cpp
Normal file
542
golang-bridge.cpp
Normal file
@ -0,0 +1,542 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CGO
|
||||
|
||||
# include "MMKVPredef.h"
|
||||
|
||||
# include "MMKV.h"
|
||||
# include "golang-bridge.h"
|
||||
# include <stdint.h>
|
||||
# include <string>
|
||||
# include <cstring>
|
||||
|
||||
using namespace mmkv;
|
||||
using namespace std;
|
||||
|
||||
# define MMKV_EXPORT extern "C" __attribute__((visibility("default"))) __attribute__((used))
|
||||
|
||||
MMKV_EXPORT void mmkvInitialize(GoStringWrap rootDir, int32_t logLevel) {
|
||||
if (!rootDir.ptr) {
|
||||
return;
|
||||
}
|
||||
MMKV::initializeMMKV(string(rootDir.ptr, rootDir.length), (MMKVLogLevel) logLevel);
|
||||
}
|
||||
|
||||
MMKV_EXPORT void onExit() {
|
||||
MMKV::onExit();
|
||||
}
|
||||
|
||||
MMKV_EXPORT void *getMMKVWithID(GoStringWrap mmapID, int32_t mode, GoStringWrap cryptKey, GoStringWrap rootPath) {
|
||||
MMKV *kv = nullptr;
|
||||
if (!mmapID.ptr) {
|
||||
return kv;
|
||||
}
|
||||
auto str = string(mmapID.ptr, mmapID.length);
|
||||
|
||||
bool done = false;
|
||||
if (cryptKey.ptr) {
|
||||
auto crypt = string(cryptKey.ptr, cryptKey.length);
|
||||
if (crypt.length() > 0) {
|
||||
if (rootPath.ptr) {
|
||||
auto path = string(rootPath.ptr, rootPath.length);
|
||||
kv = MMKV::mmkvWithID(str, (MMKVMode) mode, &crypt, &path);
|
||||
} else {
|
||||
kv = MMKV::mmkvWithID(str, (MMKVMode) mode, &crypt, nullptr);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
if (rootPath.ptr) {
|
||||
auto path = string(rootPath.ptr, rootPath.length);
|
||||
kv = MMKV::mmkvWithID(str, (MMKVMode) mode, nullptr, &path);
|
||||
} else {
|
||||
kv = MMKV::mmkvWithID(str, (MMKVMode) mode, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return kv;
|
||||
}
|
||||
|
||||
MMKV_EXPORT void *getDefaultMMKV(int32_t mode, GoStringWrap cryptKey) {
|
||||
MMKV *kv = nullptr;
|
||||
|
||||
if (cryptKey.ptr) {
|
||||
auto crypt = string(cryptKey.ptr, cryptKey.length);
|
||||
if (crypt.length() > 0) {
|
||||
kv = MMKV::defaultMMKV((MMKVMode) mode, &crypt);
|
||||
}
|
||||
}
|
||||
if (!kv) {
|
||||
kv = MMKV::defaultMMKV((MMKVMode) mode, nullptr);
|
||||
}
|
||||
|
||||
return kv;
|
||||
}
|
||||
|
||||
MMKV_EXPORT const char *mmapID(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
return kv->mmapID().c_str();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeBool(void *handle, GoStringWrap oKey, bool value) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->set((bool) value, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool decodeBool(void *handle, GoStringWrap oKey, bool defaultValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->getBool(key, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeInt32(void *handle, GoStringWrap oKey, int32_t value) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->set((int32_t) value, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT int32_t decodeInt32(void *handle, GoStringWrap oKey, int32_t defaultValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->getInt32(key, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeUInt32(void *handle, GoStringWrap oKey, uint32_t value) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->set(value, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT uint32_t decodeUInt32(void *handle, GoStringWrap oKey, uint32_t defaultValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->getUInt32(key, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeInt64(void *handle, GoStringWrap oKey, int64_t value) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->set((int64_t) value, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT int64_t decodeInt64(void *handle, GoStringWrap oKey, int64_t defaultValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->getInt64(key, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeUInt64(void *handle, GoStringWrap oKey, uint64_t value) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->set(value, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT uint64_t decodeUInt64(void *handle, GoStringWrap oKey, uint64_t defaultValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->getUInt64(key, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeFloat(void *handle, GoStringWrap oKey, float value) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->set((float) value, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT float decodeFloat(void *handle, GoStringWrap oKey, float defaultValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->getFloat(key, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeDouble(void *handle, GoStringWrap oKey, double value) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->set((double) value, key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT double decodeDouble(void *handle, GoStringWrap oKey, double defaultValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->getDouble(key, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool encodeBytes(void *handle, GoStringWrap oKey, GoStringWrap oValue) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
if (oValue.ptr) {
|
||||
auto value = MMBuffer((void *) oValue.ptr, oValue.length, MMBufferNoCopy);
|
||||
return kv->set(value, key);
|
||||
} else {
|
||||
kv->removeValueForKey(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT void *decodeBytes(void *handle, GoStringWrap oKey, uint64_t *lengthPtr) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
auto value = kv->getBytes(key);
|
||||
if (value.length() > 0) {
|
||||
if (value.isStoredOnStack()) {
|
||||
auto result = malloc(value.length());
|
||||
if (result) {
|
||||
memcpy(result, value.getPtr(), value.length());
|
||||
*lengthPtr = value.length();
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
void *result = value.getPtr();
|
||||
*lengthPtr = value.length();
|
||||
value.detach();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
# ifndef MMKV_DISABLE_CRYPT
|
||||
|
||||
MMKV_EXPORT bool reKey(void *handle, GoStringWrap oKey) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
if (oKey.ptr && oKey.length > 0) {
|
||||
string key(oKey.ptr, oKey.length);
|
||||
return kv->reKey(key);
|
||||
} else {
|
||||
return kv->reKey(string());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT void *cryptKey(void *handle, uint32_t *lengthPtr) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && lengthPtr) {
|
||||
auto cryptKey = kv->cryptKey();
|
||||
if (cryptKey.length() > 0) {
|
||||
auto ptr = malloc(cryptKey.length());
|
||||
if (ptr) {
|
||||
memcpy(ptr, cryptKey.data(), cryptKey.length());
|
||||
*lengthPtr = cryptKey.length();
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MMKV_EXPORT void checkReSetCryptKey(void *handle, GoStringWrap oKey) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
if (oKey.ptr && oKey.length > 0) {
|
||||
string key(oKey.ptr, oKey.length);
|
||||
kv->checkReSetCryptKey(&key);
|
||||
} else {
|
||||
kv->checkReSetCryptKey(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# endif // MMKV_DISABLE_CRYPT
|
||||
|
||||
MMKV_EXPORT GoStringWrap *allKeys(void *handle, uint64_t *lengthPtr) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
auto keys = kv->allKeys();
|
||||
if (!keys.empty()) {
|
||||
auto keyArray = (GoStringWrap *) calloc(keys.size(), sizeof(GoStringWrap));
|
||||
if (!keyArray) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < keys.size(); index++) {
|
||||
auto &key = keys[index];
|
||||
auto &stringWrap = keyArray[index];
|
||||
stringWrap.length = static_cast<uint32_t>(key.length());
|
||||
stringWrap.ptr = (char *) malloc(key.length());
|
||||
if (stringWrap.ptr) {
|
||||
memcpy((void *) stringWrap.ptr, key.data(), key.length());
|
||||
}
|
||||
}
|
||||
*lengthPtr = keys.size();
|
||||
return keyArray;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool containsKey(void *handle, GoStringWrap oKey) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
return kv->containsKey(key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MMKV_EXPORT uint64_t count(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
return kv->count();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
MMKV_EXPORT uint64_t totalSize(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
return kv->totalSize();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
MMKV_EXPORT uint64_t actualSize(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
return kv->actualSize();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
MMKV_EXPORT void removeValueForKey(void *handle, GoStringWrap oKey) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && oKey.ptr) {
|
||||
auto key = string(oKey.ptr, oKey.length);
|
||||
kv->removeValueForKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
MMKV_EXPORT void removeValuesForKeys(void *handle, GoStringWrap *keyArray, uint64_t count) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv && keyArray && count > 0) {
|
||||
vector<string> arrKeys;
|
||||
arrKeys.reserve(count);
|
||||
for (uint64_t index = 0; index < count; index++) {
|
||||
auto &stringWrap = keyArray[index];
|
||||
if (stringWrap.ptr && stringWrap.length > 0) {
|
||||
arrKeys.emplace_back(stringWrap.ptr, stringWrap.length);
|
||||
}
|
||||
}
|
||||
if (!arrKeys.empty()) {
|
||||
kv->removeValuesForKeys(arrKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MMKV_EXPORT void clearAll(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
kv->clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
MMKV_EXPORT void mmkvSync(void *handle, bool sync) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
kv->sync((SyncFlag) sync);
|
||||
}
|
||||
}
|
||||
|
||||
MMKV_EXPORT void clearMemoryCache(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
kv->clearMemoryCache();
|
||||
}
|
||||
}
|
||||
|
||||
MMKV_EXPORT int32_t pageSize() {
|
||||
return static_cast<int32_t>(DEFAULT_MMAP_SIZE);
|
||||
}
|
||||
|
||||
MMKV_EXPORT const char *version() {
|
||||
return MMKV_VERSION;
|
||||
}
|
||||
|
||||
MMKV_EXPORT void trim(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
kv->trim();
|
||||
}
|
||||
}
|
||||
|
||||
MMKV_EXPORT void mmkvClose(void *handle) {
|
||||
MMKV *kv = static_cast<MMKV *>(handle);
|
||||
if (kv) {
|
||||
kv->close();
|
||||
}
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool backupOneToDirectory(GoStringWrap_t mmapID, GoStringWrap_t dstDir, GoStringWrap_t srcDir) {
|
||||
if (!mmapID.ptr || !dstDir.ptr) {
|
||||
return false;
|
||||
}
|
||||
auto id = string(mmapID.ptr, mmapID.length);
|
||||
auto dst = string(dstDir.ptr, dstDir.length);
|
||||
if (srcDir.ptr) {
|
||||
auto src = string(srcDir.ptr, srcDir.length);
|
||||
return MMKV::backupOneToDirectory(id, dst, &src);
|
||||
}
|
||||
return MMKV::backupOneToDirectory(id, dst, nullptr);
|
||||
}
|
||||
|
||||
MMKV_EXPORT bool restoreOneFromDirectory(GoStringWrap_t mmapID, GoStringWrap_t srcDir, GoStringWrap_t dstDir) {
|
||||
if (!mmapID.ptr || !srcDir.ptr) {
|
||||
return false;
|
||||
}
|
||||
auto id = string(mmapID.ptr, mmapID.length);
|
||||
auto src = string(srcDir.ptr, srcDir.length);
|
||||
if (dstDir.ptr) {
|
||||
auto dst = string(dstDir.ptr, dstDir.length);
|
||||
return MMKV::restoreOneFromDirectory(id, src, &dst);
|
||||
}
|
||||
return MMKV::restoreOneFromDirectory(id, src, nullptr);
|
||||
}
|
||||
|
||||
MMKV_EXPORT uint64_t backupAllToDirectory(GoStringWrap_t dstDir, GoStringWrap_t srcDir) {
|
||||
if (!dstDir.ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto dst = string(dstDir.ptr, dstDir.length);
|
||||
if (srcDir.ptr) {
|
||||
auto src = string(srcDir.ptr, srcDir.length);
|
||||
return MMKV::backupAllToDirectory(dst, &src);
|
||||
}
|
||||
return MMKV::backupAllToDirectory(dst, nullptr);
|
||||
}
|
||||
|
||||
MMKV_EXPORT uint64_t restoreAllFromDirectory(GoStringWrap_t srcDir, GoStringWrap_t dstDir) {
|
||||
if (!srcDir.ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto src = string(srcDir.ptr, srcDir.length);
|
||||
if (dstDir.ptr) {
|
||||
auto dst = string(dstDir.ptr, dstDir.length);
|
||||
return MMKV::restoreAllFromDirectory(src, &dst);
|
||||
}
|
||||
return MMKV::restoreAllFromDirectory(src, nullptr);
|
||||
}
|
||||
|
||||
extern "C" void myLogHandler(int64_t level, GoStringWrap file, int64_t line, GoStringWrap function, GoStringWrap message);
|
||||
|
||||
void cLogHandler(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) {
|
||||
GoStringWrap oFile { file, static_cast<int64_t>(strlen(file)) };
|
||||
GoStringWrap oFunction { function, static_cast<int64_t>(strlen(function)) };
|
||||
GoStringWrap oMessage { message.data(), static_cast<int64_t>(message.length()) };
|
||||
|
||||
myLogHandler(level, oFile, line, oFunction, oMessage);
|
||||
}
|
||||
|
||||
void setWantsLogRedirect(bool redirect) {
|
||||
if (redirect) {
|
||||
MMKV::registerLogHandler(&cLogHandler);
|
||||
} else {
|
||||
MMKV::unRegisterLogHandler();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int64_t myErrorHandler(GoStringWrap mmapID, int64_t error);
|
||||
|
||||
static MMKVRecoverStrategic cErrorHandler(const std::string &mmapID, MMKVErrorType errorType) {
|
||||
GoStringWrap oID { mmapID.data(), static_cast<int64_t>(mmapID.length()) };
|
||||
|
||||
return static_cast<MMKVRecoverStrategic>(myErrorHandler(oID, static_cast<int64_t>(errorType)));
|
||||
}
|
||||
|
||||
void setWantsErrorHandle(bool errorHandle) {
|
||||
if (errorHandle) {
|
||||
MMKV::registerErrorHandler(&cErrorHandler);
|
||||
} else {
|
||||
MMKV::unRegisterErrorHandler();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void myContentChangeHandler(GoStringWrap mmapID);
|
||||
|
||||
static void cContentChangeHandler(const std::string &mmapID) {
|
||||
GoStringWrap oID { mmapID.data(), static_cast<int64_t>(mmapID.length()) };
|
||||
|
||||
myContentChangeHandler(oID);
|
||||
}
|
||||
|
||||
void setWantsContentChangeHandle(bool errorHandle) {
|
||||
if (errorHandle) {
|
||||
MMKV::registerContentChangeHandler(&cContentChangeHandler);
|
||||
} else {
|
||||
MMKV::unRegisterContentChangeHandler();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CGO
|
106
golang-bridge.h
Normal file
106
golang-bridge.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <cstdint>
|
||||
extern "C" {
|
||||
#else
|
||||
# include <stdbool.h>
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
struct GoStringWrap {
|
||||
const char *ptr;
|
||||
int64_t length;
|
||||
};
|
||||
typedef struct GoStringWrap GoStringWrap_t;
|
||||
|
||||
struct GoSliceWrap {
|
||||
void *array;
|
||||
int64_t length;
|
||||
int64_t capacity;
|
||||
};
|
||||
typedef struct GoSliceWrap GoSliceWrap_t;
|
||||
|
||||
void mmkvInitialize(GoStringWrap_t rootDir, int32_t logLevel);
|
||||
void onExit();
|
||||
|
||||
void *getMMKVWithID(GoStringWrap_t mmapID, int32_t mode, GoStringWrap_t cryptKey, GoStringWrap_t rootPath);
|
||||
void *getDefaultMMKV(int32_t mode, GoStringWrap_t cryptKey);
|
||||
const char *mmapID(void *handle);
|
||||
|
||||
bool encodeBool(void *handle, GoStringWrap_t oKey, bool value);
|
||||
bool decodeBool(void *handle, GoStringWrap_t oKey, bool defaultValue);
|
||||
|
||||
bool encodeInt32(void *handle, GoStringWrap_t oKey, int32_t value);
|
||||
int32_t decodeInt32(void *handle, GoStringWrap_t oKey, int32_t defaultValue);
|
||||
|
||||
bool encodeUInt32(void *handle, GoStringWrap_t oKey, uint32_t value);
|
||||
uint32_t decodeUInt32(void *handle, GoStringWrap_t oKey, uint32_t defaultValue);
|
||||
|
||||
bool encodeInt64(void *handle, GoStringWrap_t oKey, int64_t value);
|
||||
int64_t decodeInt64(void *handle, GoStringWrap_t oKey, int64_t defaultValue);
|
||||
|
||||
bool encodeUInt64(void *handle, GoStringWrap_t oKey, uint64_t value);
|
||||
uint64_t decodeUInt64(void *handle, GoStringWrap_t oKey, uint64_t defaultValue);
|
||||
|
||||
bool encodeFloat(void *handle, GoStringWrap_t oKey, float value);
|
||||
float decodeFloat(void *handle, GoStringWrap_t oKey, float defaultValue);
|
||||
|
||||
bool encodeDouble(void *handle, GoStringWrap_t oKey, double value);
|
||||
double decodeDouble(void *handle, GoStringWrap_t oKey, double defaultValue);
|
||||
|
||||
bool encodeBytes(void *handle, GoStringWrap_t oKey, GoStringWrap_t oValue);
|
||||
void *decodeBytes(void *handle, GoStringWrap_t oKey, uint64_t *lengthPtr);
|
||||
|
||||
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);
|
||||
bool containsKey(void *handle, GoStringWrap_t oKey);
|
||||
uint64_t count(void *handle);
|
||||
uint64_t totalSize(void *handle);
|
||||
uint64_t actualSize(void *handle);
|
||||
|
||||
void removeValueForKey(void *handle, GoStringWrap_t oKey);
|
||||
void removeValuesForKeys(void *handle, GoStringWrap_t *keyArray, uint64_t count);
|
||||
void clearAll(void *handle);
|
||||
|
||||
void mmkvSync(void *handle, bool sync);
|
||||
void clearMemoryCache(void *handle);
|
||||
void trim(void *handle);
|
||||
void mmkvClose(void *handle);
|
||||
|
||||
bool backupOneToDirectory(GoStringWrap_t mmapID, GoStringWrap_t dstDir, GoStringWrap_t srcDir);
|
||||
bool restoreOneFromDirectory(GoStringWrap_t mmapID, GoStringWrap_t srcDir, GoStringWrap_t dstDir);
|
||||
uint64_t backupAllToDirectory(GoStringWrap_t dstDir, GoStringWrap_t srcDir);
|
||||
uint64_t restoreAllFromDirectory(GoStringWrap_t srcDir, GoStringWrap_t dstDir);
|
||||
|
||||
int32_t pageSize();
|
||||
const char *version();
|
||||
|
||||
void setWantsLogRedirect(bool redirect);
|
||||
void setWantsErrorHandle(bool errorHandle);
|
||||
void setWantsContentChangeHandle(bool contentChange);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
15
interface.go
15
interface.go
@ -1,15 +0,0 @@
|
||||
package mmkv
|
||||
|
||||
type Manager interface {
|
||||
// OpenVault opens a vault with the given id.
|
||||
// If the vault does not exist, it will be created.
|
||||
// If id is empty, DefaultVaultID will be used.
|
||||
OpenVault(id string) (Vault, error)
|
||||
}
|
||||
|
||||
type Vault interface {
|
||||
Keys() []string
|
||||
GetRaw(key string) ([]byte, bool)
|
||||
GetBytes(key string) ([]byte, error)
|
||||
GetString(key string) (string, error)
|
||||
}
|
BIN
lib/libcore.a
Normal file
BIN
lib/libcore.a
Normal file
Binary file not shown.
BIN
lib/libmmkv.a
Normal file
BIN
lib/libmmkv.a
Normal file
Binary file not shown.
78
manager.go
78
manager.go
@ -1,78 +0,0 @@
|
||||
package mmkv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultVaultID = "mmkv.default"
|
||||
)
|
||||
|
||||
type manager struct {
|
||||
dir string
|
||||
vaults map[string]Vault
|
||||
}
|
||||
|
||||
// NewManager creates a new MMKV Manager.
|
||||
func NewManager(dir string) (Manager, error) {
|
||||
// check dir exists
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to stat dir: %w", err)
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil, fmt.Errorf("not a directory")
|
||||
}
|
||||
|
||||
return &manager{
|
||||
dir: dir,
|
||||
vaults: make(map[string]Vault),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *manager) OpenVault(id string) (Vault, error) {
|
||||
if id == "" {
|
||||
id = DefaultVaultID
|
||||
}
|
||||
|
||||
if v, ok := m.vaults[id]; ok {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
vault, err := m.openVault(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open vault: %w", err)
|
||||
}
|
||||
m.vaults[id] = vault
|
||||
|
||||
return vault, nil
|
||||
}
|
||||
|
||||
func (m *manager) openVault(id string) (Vault, error) {
|
||||
metaFile, err := os.Open(path.Join(m.dir, id+".crc"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open metadata file: %w", err)
|
||||
}
|
||||
defer metaFile.Close()
|
||||
|
||||
vaultFile, err := os.Open(path.Join(m.dir, id))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open vault file: %w", err)
|
||||
}
|
||||
defer vaultFile.Close()
|
||||
|
||||
meta, err := loadMetadata(metaFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load metadata: %w", err)
|
||||
}
|
||||
|
||||
v, err := loadVault(vaultFile, meta)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load vault: %w", err)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package mmkv
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewManager(t *testing.T) {
|
||||
mgr, err := NewManager("./testdata")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, mgr)
|
||||
|
||||
vault, err := mgr.OpenVault("")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, vault)
|
||||
}
|
56
metadata.go
56
metadata.go
@ -1,56 +0,0 @@
|
||||
package mmkv
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
// added in version 0
|
||||
crc32 uint32
|
||||
|
||||
// added in version 1
|
||||
version uint32
|
||||
sequence uint32 // full write back count
|
||||
|
||||
// added in version 2
|
||||
aesVector []byte // random iv for encryption, aes.BlockSize (16 bytes)
|
||||
|
||||
// added in version 3, try to reduce file corruption
|
||||
actualSize uint32
|
||||
lastActualSize uint32
|
||||
lastCRC32 uint32
|
||||
|
||||
//_reversed []byte // 64 bytes
|
||||
}
|
||||
|
||||
func loadMetadata(rd io.Reader) (*metadata, error) {
|
||||
buf := make([]byte, 0x68)
|
||||
_, err := io.ReadFull(rd, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read metadata: %w", err)
|
||||
}
|
||||
|
||||
m := &metadata{}
|
||||
|
||||
m.crc32 = binary.LittleEndian.Uint32(buf[0:4])
|
||||
m.version = binary.LittleEndian.Uint32(buf[4:8])
|
||||
|
||||
if m.version >= 1 {
|
||||
m.sequence = binary.LittleEndian.Uint32(buf[8:12])
|
||||
}
|
||||
|
||||
if m.version >= 2 {
|
||||
m.aesVector = buf[12:28]
|
||||
}
|
||||
|
||||
if m.version >= 3 {
|
||||
m.actualSize = binary.LittleEndian.Uint32(buf[28:32])
|
||||
m.lastActualSize = binary.LittleEndian.Uint32(buf[32:36])
|
||||
m.lastCRC32 = binary.LittleEndian.Uint32(buf[36:40])
|
||||
}
|
||||
|
||||
//m._reversed = buf[40:104]
|
||||
return m, nil
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package mmkv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_loadMetadata(t *testing.T) {
|
||||
file, err := os.Open("./testdata/mmkv.default.crc")
|
||||
require.NoError(t, err)
|
||||
|
||||
meta, err := loadMetadata(file)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, uint32(3), meta.version)
|
||||
assert.Equal(t, uint32(1), meta.sequence)
|
||||
|
||||
assert.Equal(t, uint32(28), meta.actualSize)
|
||||
assert.Equal(t, uint32(197326043), meta.crc32)
|
||||
|
||||
assert.Equal(t, uint32(4), meta.lastActualSize)
|
||||
assert.Equal(t, uint32(1285129681), meta.lastCRC32)
|
||||
|
||||
assert.Equal(t, bytes.Repeat([]byte{0x00}, 16), meta.aesVector)
|
||||
}
|
538
mmkv.go
Normal file
538
mmkv.go
Normal file
@ -0,0 +1,538 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
33
mmkv_test.go
Normal file
33
mmkv_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package mmkv
|
||||
|
||||
import (
|
||||
"testing"
|
||||
// "regexp"
|
||||
)
|
||||
|
||||
func TestVersionEmpty(t *testing.T) {
|
||||
msg := Version()
|
||||
if msg == "" {
|
||||
t.Fatalf("Version() == \"\"")
|
||||
}
|
||||
}
|
BIN
testdata/mmkv.default
vendored
BIN
testdata/mmkv.default
vendored
Binary file not shown.
BIN
testdata/mmkv.default.crc
vendored
BIN
testdata/mmkv.default.crc
vendored
Binary file not shown.
95
vault.go
95
vault.go
@ -1,95 +0,0 @@
|
||||
package mmkv
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/exp/maps"
|
||||
"google.golang.org/protobuf/encoding/protowire"
|
||||
)
|
||||
|
||||
type vault map[string][]byte
|
||||
|
||||
func (v vault) Keys() []string {
|
||||
return maps.Keys(v)
|
||||
}
|
||||
|
||||
func (v vault) GetRaw(key string) ([]byte, bool) {
|
||||
val, ok := v[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (v vault) GetBytes(key string) ([]byte, error) {
|
||||
raw, ok := v[key]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("key not found: %s", key)
|
||||
}
|
||||
|
||||
val, n := protowire.ConsumeBytes(raw)
|
||||
if n < 0 {
|
||||
return nil, fmt.Errorf("invalid protobuf bytes")
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (v vault) GetString(key string) (string, error) {
|
||||
val, err := v.GetBytes(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(val), nil
|
||||
}
|
||||
|
||||
// metadata is optional. but if it exists, validate with it.
|
||||
func loadVault(src io.Reader, m *metadata) (Vault, error) {
|
||||
fileSizeBuf := make([]byte, 4)
|
||||
_, err := io.ReadFull(src, fileSizeBuf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file size: %w", err)
|
||||
}
|
||||
size := binary.LittleEndian.Uint32(fileSizeBuf)
|
||||
|
||||
if m != nil && size != m.actualSize {
|
||||
return nil, fmt.Errorf("metadata and vault payload size mismatch")
|
||||
}
|
||||
|
||||
buf := make([]byte, size)
|
||||
_, err = io.ReadFull(src, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
if m != nil && m.crc32 != crc32.ChecksumIEEE(buf) {
|
||||
return nil, fmt.Errorf("metadata and vault payload crc32 mismatch")
|
||||
}
|
||||
|
||||
v := make(vault)
|
||||
|
||||
// mmkv is not really protobuf compatible,
|
||||
// type of key & value (the first 4 bytes) is incorrect.
|
||||
// so skip the first 4 bytes & manually parse the rest.
|
||||
rd := proto.NewBuffer(buf[4:])
|
||||
|
||||
for {
|
||||
if len(rd.Unread()) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
key, err := rd.DecodeStringBytes()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode key: %w", err)
|
||||
}
|
||||
val, err := rd.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode value: %w", err)
|
||||
}
|
||||
v[key] = val
|
||||
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package mmkv
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_loadVault(t *testing.T) {
|
||||
file, err := os.Open("./testdata/mmkv.default")
|
||||
require.NoError(t, err)
|
||||
|
||||
v, err := loadVault(file, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(v.Keys()))
|
||||
|
||||
val, err := v.GetString("world")
|
||||
assert.Equal(t, "hello", val)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = v.GetBytes("foo")
|
||||
assert.Error(t, err)
|
||||
}
|
Loading…
Reference in New Issue
Block a user