commit a40e4e5b9ecaff3228bae55c013bd0b970d0f4bd Author: Unlock Music Dev Date: Sun Dec 4 22:42:29 2022 +0800 init repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..8d7d1be --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Go MMKV + +initial generated by https://github.com/tencent/mmkv \ No newline at end of file diff --git a/callback.go b/callback.go new file mode 100644 index 0000000..a2dbf51 --- /dev/null +++ b/callback.go @@ -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 +*/ +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) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3f7caeb --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module unlock-music.dev/mmkv + +go 1.15 diff --git a/golang-bridge.cpp b/golang-bridge.cpp new file mode 100644 index 0000000..f687025 --- /dev/null +++ b/golang-bridge.cpp @@ -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 +# include +# include + +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(handle); + if (kv) { + return kv->mmapID().c_str(); + } + return nullptr; +} + +MMKV_EXPORT bool encodeBool(void *handle, GoStringWrap oKey, bool value) { + MMKV *kv = static_cast(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(handle); + if (kv) { + return kv->count(); + } + return 0; +} + +MMKV_EXPORT uint64_t totalSize(void *handle) { + MMKV *kv = static_cast(handle); + if (kv) { + return kv->totalSize(); + } + return 0; +} + +MMKV_EXPORT uint64_t actualSize(void *handle) { + MMKV *kv = static_cast(handle); + if (kv) { + return kv->actualSize(); + } + return 0; +} + +MMKV_EXPORT void removeValueForKey(void *handle, GoStringWrap oKey) { + MMKV *kv = static_cast(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(handle); + if (kv && keyArray && count > 0) { + vector 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(handle); + if (kv) { + kv->clearAll(); + } +} + +MMKV_EXPORT void mmkvSync(void *handle, bool sync) { + MMKV *kv = static_cast(handle); + if (kv) { + kv->sync((SyncFlag) sync); + } +} + +MMKV_EXPORT void clearMemoryCache(void *handle) { + MMKV *kv = static_cast(handle); + if (kv) { + kv->clearMemoryCache(); + } +} + +MMKV_EXPORT int32_t pageSize() { + return static_cast(DEFAULT_MMAP_SIZE); +} + +MMKV_EXPORT const char *version() { + return MMKV_VERSION; +} + +MMKV_EXPORT void trim(void *handle) { + MMKV *kv = static_cast(handle); + if (kv) { + kv->trim(); + } +} + +MMKV_EXPORT void mmkvClose(void *handle) { + MMKV *kv = static_cast(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(strlen(file)) }; + GoStringWrap oFunction { function, static_cast(strlen(function)) }; + GoStringWrap oMessage { message.data(), static_cast(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(mmapID.length()) }; + + return static_cast(myErrorHandler(oID, static_cast(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(mmapID.length()) }; + + myContentChangeHandler(oID); +} + +void setWantsContentChangeHandle(bool errorHandle) { + if (errorHandle) { + MMKV::registerContentChangeHandler(&cContentChangeHandler); + } else { + MMKV::unRegisterContentChangeHandler(); + } +} + +#endif // CGO diff --git a/golang-bridge.h b/golang-bridge.h new file mode 100644 index 0000000..1599bcc --- /dev/null +++ b/golang-bridge.h @@ -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 +extern "C" { +#else +# include +# include +#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 diff --git a/lib/libcore.a b/lib/libcore.a new file mode 100644 index 0000000..05f4411 Binary files /dev/null and b/lib/libcore.a differ diff --git a/lib/libmmkv.a b/lib/libmmkv.a new file mode 100644 index 0000000..3b5ac90 Binary files /dev/null and b/lib/libmmkv.a differ diff --git a/mmkv.go b/mmkv.go new file mode 100644 index 0000000..d9b5089 --- /dev/null +++ b/mmkv.go @@ -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 + +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 +} diff --git a/mmkv_test.go b/mmkv_test.go new file mode 100644 index 0000000..b84e097 --- /dev/null +++ b/mmkv_test.go @@ -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() == \"\"") + } +}