Refactor UserDefault, use file mapping & plain binary encode/decode for all platform

This commit is contained in:
halx99 2020-02-04 22:28:54 +08:00
parent 8cb437601f
commit b6cf5871cd
6 changed files with 191 additions and 886 deletions

View File

@ -1,243 +0,0 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "base/CCUserDefault.h"
#include "platform/CCPlatformConfig.h"
#include "base/ccUtils.h"
#include "platform/CCCommon.h"
#include "base/base64.h"
#include "platform/CCFileUtils.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
static const std::string helperClassName = "org.cocos2dx.lib.Cocos2dxHelper";
using namespace std;
NS_CC_BEGIN
/**
* implements of UserDefault
*/
UserDefault* UserDefault::_userDefault = nullptr;
string UserDefault::_filePath = string("");
bool UserDefault::_isFilePathInitialized = false;
UserDefault::~UserDefault()
{
}
UserDefault::UserDefault()
{
}
void UserDefault::destroyInstance()
{
CC_SAFE_DELETE(_userDefault);
}
bool UserDefault::getBoolForKey(const char* pKey)
{
return getBoolForKey(pKey, false);
}
bool UserDefault::getBoolForKey(const char* pKey, bool defaultValue)
{
return JniHelper::callStaticBooleanMethod(helperClassName, "getBoolForKey", pKey, defaultValue);
}
int UserDefault::getIntegerForKey(const char* pKey)
{
return getIntegerForKey(pKey, 0);
}
int UserDefault::getIntegerForKey(const char* pKey, int defaultValue)
{
return JniHelper::callStaticIntMethod(helperClassName, "getIntegerForKey", pKey, defaultValue);
}
float UserDefault::getFloatForKey(const char* pKey)
{
return getFloatForKey(pKey, 0.0f);
}
float UserDefault::getFloatForKey(const char* pKey, float defaultValue)
{
return JniHelper::callStaticFloatMethod(helperClassName, "getFloatForKey", pKey, defaultValue);
}
double UserDefault::getDoubleForKey(const char* pKey)
{
return getDoubleForKey(pKey, 0.0);
}
double UserDefault::getDoubleForKey(const char* pKey, double defaultValue)
{
return JniHelper::callStaticDoubleMethod(helperClassName, "getDoubleForKey", pKey, defaultValue);
}
std::string UserDefault::getStringForKey(const char* pKey)
{
return getStringForKey(pKey, "");
}
string UserDefault::getStringForKey(const char* pKey, const std::string & defaultValue)
{
return JniHelper::callStaticStringMethod(helperClassName, "getStringForKey", pKey, defaultValue);
}
Data UserDefault::getDataForKey(const char* pKey)
{
return getDataForKey(pKey, Data::Null);
}
Data UserDefault::getDataForKey(const char* pKey, const Data& defaultValue)
{
char * encodedDefaultData = NULL;
unsigned int encodedDefaultDataLen = !defaultValue.isNull() ? base64Encode(defaultValue.getBytes(), defaultValue.getSize(), &encodedDefaultData) : 0;
string encodedStr = JniHelper::callStaticStringMethod(helperClassName, "getStringForKey", pKey, (const char*)encodedDefaultData);
if (encodedDefaultData)
free(encodedDefaultData);
CCLOG("ENCODED STRING: --%s--%d", encodedStr.c_str(), (int)encodedStr.length());
unsigned char * decodedData = NULL;
int decodedDataLen = base64Decode((unsigned char*)encodedStr.c_str(), (unsigned int)encodedStr.length(), &decodedData);
CCLOG("DECODED DATA: %s %d", decodedData, decodedDataLen);
if (decodedData && decodedDataLen) {
Data ret;
ret.fastSet(decodedData, decodedDataLen);
return ret;
}
return defaultValue;
}
void UserDefault::setBoolForKey(const char* pKey, bool value)
{
JniHelper::callStaticVoidMethod(helperClassName, "setBoolForKey", pKey, value);
}
void UserDefault::setIntegerForKey(const char* pKey, int value)
{
JniHelper::callStaticVoidMethod(helperClassName, "setIntegerForKey", pKey, value);
}
void UserDefault::setFloatForKey(const char* pKey, float value)
{
JniHelper::callStaticVoidMethod(helperClassName, "setFloatForKey", pKey, value);
}
void UserDefault::setDoubleForKey(const char* pKey, double value)
{
JniHelper::callStaticVoidMethod(helperClassName, "setDoubleForKey", pKey, value);
}
void UserDefault::setStringForKey(const char* pKey, const std::string& value)
{
JniHelper::callStaticVoidMethod(helperClassName, "setStringForKey", pKey, value);
}
void UserDefault::setDataForKey(const char* pKey, const Data& value)
{
CCLOG("SET DATA FOR KEY: --%s--%d", value.getBytes(), (int)(value.getSize()));
char * encodedData = nullptr;
unsigned int encodedDataLen = base64Encode(value.getBytes(), value.getSize(), &encodedData);
CCLOG("SET DATA ENCODED: --%s", encodedData);
JniHelper::callStaticVoidMethod(helperClassName, "setStringForKey", pKey, (const char*)encodedData);
if (encodedData)
free(encodedData);
}
UserDefault* UserDefault::getInstance()
{
if (! _userDefault)
{
_userDefault = new (std::nothrow) UserDefault();
}
return _userDefault;
}
bool UserDefault::isXMLFileExist()
{
return FileUtils::getInstance()->isFileExist(_filePath);
}
void UserDefault::initXMLFilePath()
{
//~ #ifdef KEEP_COMPATABILITY
//~ if (! _isFilePathInitialized)
//~ {
//~ // UserDefault.xml is stored in /data/data/<package-path>/ before v2.1.2
//~ std::string packageName = JniHelper::callStaticStringMethod(helperClassName, "getCocos2dxPackageName");
//~ _filePath += "/data/data/" + packageName + "/" + XML_FILE_NAME;
//~ _isFilePathInitialized = true;
//~ }
//~ #endif
}
// create new xml file
bool UserDefault::createXMLFile()
{
return false;
}
const string& UserDefault::getXMLFilePath()
{
return _filePath;
}
void UserDefault::flush()
{
}
void UserDefault::deleteValueForKey(const char* key)
{
// check the params
if (!key)
{
CCLOG("the key is invalid");
}
JniHelper::callStaticVoidMethod(helperClassName, "deleteValueForKey", key);
flush();
}
NS_CC_END
#endif // (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

View File

@ -1,254 +0,0 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "platform/CCPlatformConfig.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
#import <Foundation/Foundation.h>
#include <string>
#import "base/CCUserDefault.h"
#import "platform/CCPlatformConfig.h"
#import "platform/CCPlatformMacros.h"
#import "base/base64.h"
#import "platform/CCFileUtils.h"
using namespace std;
NS_CC_BEGIN
/**
* implements of UserDefault
*/
UserDefault* UserDefault::_userDefault = nullptr;
string UserDefault::_filePath = string("");
bool UserDefault::_isFilePathInitialized = false;
UserDefault::~UserDefault()
{
}
UserDefault::UserDefault()
{
}
bool UserDefault::getBoolForKey(const char* pKey)
{
return getBoolForKey(pKey, false);
}
bool UserDefault::getBoolForKey(const char* pKey, bool defaultValue)
{
bool ret = defaultValue;
NSNumber *value = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithUTF8String:pKey]];
if (value)
{
ret = [value boolValue];
}
return ret;
}
int UserDefault::getIntegerForKey(const char* pKey)
{
return getIntegerForKey(pKey, 0);
}
int UserDefault::getIntegerForKey(const char* pKey, int defaultValue)
{
int ret = defaultValue;
NSNumber *value = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithUTF8String:pKey]];
if (value)
{
ret = [value intValue];
}
return ret;
}
float UserDefault::getFloatForKey(const char* pKey)
{
return getFloatForKey(pKey, 0);
}
float UserDefault::getFloatForKey(const char* pKey, float defaultValue)
{
float ret = defaultValue;
NSNumber *value = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithUTF8String:pKey]];
if (value)
{
ret = [value floatValue];
}
return ret;
}
double UserDefault::getDoubleForKey(const char* pKey)
{
return getDoubleForKey(pKey, 0);
}
double UserDefault::getDoubleForKey(const char* pKey, double defaultValue)
{
double ret = defaultValue;
NSNumber *value = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithUTF8String:pKey]];
if (value)
{
ret = [value doubleValue];
}
return ret;
}
std::string UserDefault::getStringForKey(const char* pKey)
{
return getStringForKey(pKey, "");
}
string UserDefault::getStringForKey(const char* pKey, const std::string & defaultValue)
{
NSString *str = [[NSUserDefaults standardUserDefaults] stringForKey:[NSString stringWithUTF8String:pKey]];
if (! str)
{
return defaultValue;
}
else
{
return [str UTF8String];
}
}
Data UserDefault::getDataForKey(const char* pKey)
{
return getDataForKey(pKey, Data::Null);
}
Data UserDefault::getDataForKey(const char* pKey, const Data& defaultValue)
{
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:[NSString stringWithUTF8String:pKey]];
if (! data)
{
return defaultValue;
}
else
{
Data ret;
ret.copy((unsigned char*)data.bytes, data.length);
return ret;
}
}
void UserDefault::setBoolForKey(const char* pKey, bool value)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:value] forKey:[NSString stringWithUTF8String:pKey]];
}
void UserDefault::setIntegerForKey(const char* pKey, int value)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:value] forKey:[NSString stringWithUTF8String:pKey]];
}
void UserDefault::setFloatForKey(const char* pKey, float value)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithFloat:value] forKey:[NSString stringWithUTF8String:pKey]];
}
void UserDefault::setDoubleForKey(const char* pKey, double value)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithDouble:value] forKey:[NSString stringWithUTF8String:pKey]];
}
void UserDefault::setStringForKey(const char* pKey, const std::string & value)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:value.c_str()] forKey:[NSString stringWithUTF8String:pKey]];
}
void UserDefault::setDataForKey(const char* pKey, const Data& value) {
[[NSUserDefaults standardUserDefaults] setObject:[NSData dataWithBytes: value.getBytes() length: value.getSize()] forKey:[NSString stringWithUTF8String:pKey]];
}
UserDefault* UserDefault::getInstance()
{
if (! _userDefault)
{
_userDefault = new (std::nothrow) UserDefault();
}
return _userDefault;
}
void UserDefault::destroyInstance()
{
CC_SAFE_DELETE(_userDefault);
}
bool UserDefault::isXMLFileExist()
{
return FileUtils::getInstance()->isFileExist(_filePath);
}
void UserDefault::initXMLFilePath()
{
}
// create new xml file
bool UserDefault::createXMLFile()
{
return false;
}
const string& UserDefault::getXMLFilePath()
{
return _filePath;
}
void UserDefault::flush()
{
[[NSUserDefaults standardUserDefaults] synchronize];
}
void UserDefault::deleteValueForKey(const char* key)
{
// check the params
if (!key)
{
CCLOG("the key is invalid");
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[NSString stringWithUTF8String:key]];
flush();
}
NS_CC_END
#endif // (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

View File

@ -2,6 +2,7 @@
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
Copyright (c) 2017-2020 c4games.com.
http://www.cocos2d-x.org
@ -23,6 +24,16 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#if defined(_WIN32)
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <errno.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include "base/CCUserDefault.h"
#include "platform/CCCommon.h"
#include "platform/CCFileUtils.h"
@ -30,104 +41,49 @@ THE SOFTWARE.
#include "base/base64.h"
#include "base/ccUtils.h"
#if (CC_TARGET_PLATFORM != CC_PLATFORM_IOS && CC_TARGET_PLATFORM != CC_PLATFORM_MAC && CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID)
#include "yasio/ibstream.hpp"
#include "yasio/obstream.hpp"
#include "yasio/detail/sz.hpp"
// root name of xml
#define USERDEFAULT_ROOT_NAME "userDefaultRoot"
#if defined(_WIN32)
#define O_READ_FLAGS O_BINARY | O_RDONLY, S_IREAD
#define O_WRITE_FLAGS O_CREAT | O_RDWR | O_BINARY, S_IWRITE | S_IREAD
#define O_APPEND_FLAGS O_APPEND | O_CREAT | O_RDWR | O_BINARY, S_IWRITE | S_IREAD
#define posix_open ::_open
#define posix_close ::_close
#define posix_lseek ::_lseek
#define posix_read ::_read
#define posix_write ::_write
#define posix_fd2fh(fd) reinterpret_cast<HANDLE>(_get_osfhandle(fd))
#define posix_fsetsize(fd, size) ::_chsize(fd, size)
#else
#define O_READ_FLAGS O_RDONLY, S_IRUSR
#define O_WRITE_FLAGS O_CREAT | O_RDWR, S_IRWXU
#define O_APPEND_FLAGS O_APPEND | O_CREAT | O_RDWR, S_IRWXU
#define posix_open ::open
#define posix_close ::close
#define posix_lseek ::lseek
#define posix_read ::read
#define posix_write ::write
#define posix_fd2fh(fd) (fd)
#define posix_fsetsize(fd, size) ::ftruncate(fd, size), ::lseek(fd, 0, SEEK_SET)
#endif
#define XML_FILE_NAME "UserDefault.xml"
using namespace std;
#define USER_DEFAULT_FILENAME "UserDefault.bin"
NS_CC_BEGIN
/**
* define the functions here because we don't want to
* export xmlNodePtr and other types in "CCUserDefault.h"
*/
static pugi::xml_node getXMLNodeForKey(const char* pKey, pugi::xml_node& rootNode, pugi::xml_document& doc)
{
pugi::xml_node curNode;
// check the key value
if (! pKey)
{
return pugi::xml_node{};
}
do
{
std::string xmlBuffer = FileUtils::getInstance()->getStringFromFile(UserDefault::getInstance()->getXMLFilePath());
if (!xmlBuffer.empty())
{
doc.load_buffer_inplace(&xmlBuffer.front(), xmlBuffer.size());
// get root node
rootNode = doc.document_element();
}
if (!rootNode)
{
// create root element
auto rootEle = doc.append_child(USERDEFAULT_ROOT_NAME);
if (!rootEle)
break;
rootNode = rootEle;
}
curNode = rootNode.child(pKey);
} while (0);
return curNode;
}
static void setValueForKey(const char* pKey, const char* pValue)
{
pugi::xml_node rootNode;
pugi::xml_document doc;
pugi::xml_node node;
// check the params
if (! pKey || ! pValue)
{
return;
}
// find the node
node = getXMLNodeForKey(pKey, rootNode, doc);
// if node exist, change the content
if (node)
{
node.set_value(pValue);
}
else
{
if (rootNode)
{
node = rootNode.append_child(pKey);
node.set_value(pValue);
}
}
// save file and free doc
if (doc)
{
doc.save_file(UserDefault::getInstance()->getXMLFilePath().c_str(), " ");
}
}
/**
* implements of UserDefault
*/
UserDefault* UserDefault::_userDefault = nullptr;
string UserDefault::_filePath = string("");
bool UserDefault::_isFilePathInitialized = false;
UserDefault::~UserDefault()
{
_rwmmap.reset();
if (_fd != -1)
posix_close(_fd);
}
UserDefault::UserDefault()
@ -141,25 +97,11 @@ bool UserDefault::getBoolForKey(const char* pKey)
bool UserDefault::getBoolForKey(const char* pKey, bool defaultValue)
{
const char* value = nullptr;
pugi::xml_node rootNode;
pugi::xml_document doc;
pugi::xml_node node;
node = getXMLNodeForKey(pKey, rootNode, doc);
// find the node
if (node)
{
value = (const char*)node.text().as_string();
}
auto it = this->_values.find(pKey);
if (it != this->_values.end())
return it->second == "1";
bool ret = defaultValue;
if (value)
{
ret = (! strcmp(value, "true"));
}
return ret;
return defaultValue;
}
int UserDefault::getIntegerForKey(const char* pKey)
@ -169,25 +111,11 @@ int UserDefault::getIntegerForKey(const char* pKey)
int UserDefault::getIntegerForKey(const char* pKey, int defaultValue)
{
const char* value = nullptr;
pugi::xml_node rootNode;
pugi::xml_document doc;
pugi::xml_node node;
node = getXMLNodeForKey(pKey, rootNode, doc);
// find the node
if (node)
{
value = (const char*)(const char*)node.text().as_string();
}
auto it = this->_values.find(pKey);
if (it != this->_values.end())
return atoi(it->second.c_str());
int ret = defaultValue;
if (value)
{
ret = atoi(value);
}
return ret;
return defaultValue;
}
float UserDefault::getFloatForKey(const char* pKey)
@ -209,25 +137,11 @@ double UserDefault::getDoubleForKey(const char* pKey)
double UserDefault::getDoubleForKey(const char* pKey, double defaultValue)
{
const char* value = nullptr;
pugi::xml_node rootNode;
pugi::xml_document doc;
pugi::xml_node node;
node = getXMLNodeForKey(pKey, rootNode, doc);
// find the node
if (node)
{
value = (const char*)(const char*)node.text().as_string();
}
auto it = this->_values.find(pKey);
if (it != this->_values.end())
return utils::atof(it->second.c_str());
double ret = defaultValue;
if (value)
{
ret = utils::atof(value);
}
return ret;
return defaultValue;
}
std::string UserDefault::getStringForKey(const char* pKey)
@ -235,79 +149,19 @@ std::string UserDefault::getStringForKey(const char* pKey)
return getStringForKey(pKey, "");
}
string UserDefault::getStringForKey(const char* pKey, const std::string & defaultValue)
std::string UserDefault::getStringForKey(const char* pKey, const std::string & defaultValue)
{
const char* value = nullptr;
pugi::xml_node rootNode;
pugi::xml_document doc;
pugi::xml_node node;
node = getXMLNodeForKey(pKey, rootNode, doc);
// find the node
if (node)
{
value = (const char*)(const char*)node.text().as_string();
}
auto it = this->_values.find(pKey);
if (it != this->_values.end())
return it->second;
string ret = defaultValue;
if (value)
{
ret = string(value);
}
return ret;
return defaultValue;
}
Data UserDefault::getDataForKey(const char* pKey)
{
return getDataForKey(pKey, Data::Null);
}
Data UserDefault::getDataForKey(const char* pKey, const Data& defaultValue)
{
const char* encodedData = nullptr;
pugi::xml_node rootNode;
pugi::xml_document doc;
pugi::xml_node node;
node = getXMLNodeForKey(pKey, rootNode, doc);
// find the node
if (node)
{
encodedData = (const char*)(const char*)node.text().as_string();
}
Data ret;
if (encodedData)
{
unsigned char * decodedData = nullptr;
int decodedDataLen = base64Decode((unsigned char*)encodedData, (unsigned int)strlen(encodedData), &decodedData);
if (decodedData) {
ret.fastSet(decodedData, decodedDataLen);
}
}
else
{
ret = defaultValue;
}
return ret;
}
void UserDefault::setBoolForKey(const char* pKey, bool value)
{
// save bool value as string
if (true == value)
{
setStringForKey(pKey, "true");
}
else
{
setStringForKey(pKey, "false");
}
setStringForKey(pKey, value ? "true" : "false");
}
void UserDefault::setIntegerForKey(const char* pKey, int value)
@ -323,7 +177,7 @@ void UserDefault::setIntegerForKey(const char* pKey, int value)
memset(tmp, 0, 50);
sprintf(tmp, "%d", value);
setValueForKey(pKey, tmp);
setStringForKey(pKey, tmp);
}
void UserDefault::setFloatForKey(const char* pKey, float value)
@ -344,7 +198,7 @@ void UserDefault::setDoubleForKey(const char* pKey, double value)
memset(tmp, 0, 50);
sprintf(tmp, "%f", value);
setValueForKey(pKey, tmp);
setStringForKey(pKey, tmp);
}
void UserDefault::setStringForKey(const char* pKey, const std::string & value)
@ -355,40 +209,22 @@ void UserDefault::setStringForKey(const char* pKey, const std::string & value)
return;
}
setValueForKey(pKey, value.c_str());
}
auto it = this->_values.find(pKey);
void UserDefault::setDataForKey(const char* pKey, const Data& value) {
// check key
if (! pKey)
{
return;
}
if (it != this->_values.end())
it->second = value;
else
this->_values.emplace(pKey, value);
char *encodedData = nullptr;
base64Encode(value.getBytes(), static_cast<unsigned int>(value.getSize()), &encodedData);
setValueForKey(pKey, encodedData);
if (encodedData)
free(encodedData);
flush();
}
UserDefault* UserDefault::getInstance()
{
if (!_userDefault)
{
initXMLFilePath();
// only create xml file one time
// the file exists after the program exit
if ((!isXMLFileExist()) && (!createXMLFile()))
{
return nullptr;
}
_userDefault = new (std::nothrow) UserDefault();
_userDefault->init();
}
return _userDefault;
@ -407,76 +243,71 @@ void UserDefault::setDelegate(UserDefault *delegate)
_userDefault = delegate;
}
bool UserDefault::isXMLFileExist()
void UserDefault::init()
{
return FileUtils::getInstance()->isFileExist(_filePath);
}
void UserDefault::initXMLFilePath()
{
if (! _isFilePathInitialized)
if (! _initialized)
{
_filePath += FileUtils::getInstance()->getWritablePath() + XML_FILE_NAME;
_isFilePathInitialized = true;
_filePath = FileUtils::getInstance()->getWritablePath() + USER_DEFAULT_FILENAME;
// construct file mapping
_fd = posix_open(_filePath.c_str(), O_WRITE_FLAGS);
if (_fd == -1) {
log("[Warnning] UserDefault::init open storage file '%s' failed!", _filePath.c_str());
return;
}
int filesize = posix_lseek(_fd, 0, SEEK_END);
posix_lseek(_fd, 0, SEEK_SET);
if (filesize < _curMapSize) {
posix_fsetsize(_fd, _curMapSize);
}
/// load to memory _values
_rwmmap = std::make_shared<mio::mmap_sink>(posix_fd2fh(_fd), 0, _curMapSize);
if (_rwmmap->is_mapped()) { // no error
yasio::ibstream_view ibs(_rwmmap->data(), _rwmmap->length());
if (ibs.length() > 0) {
// count
// key/value --> string/string
int count = ibs.read_i7();
for (auto i = 0; i < count; ++i) {
auto key = ibs.read_v();
auto value = ibs.read_v();
this->_values.emplace(key, value);
}
}
}
_initialized = true;
}
}
// create new xml file
bool UserDefault::createXMLFile()
{
bool bRet = false;
pugi::xml_document doc;
doc.load_string(R"(<?xml version="1.0" encoding="UTF-8" ?>)"
"<" USERDEFAULT_ROOT_NAME " />", pugi::parse_full);
bRet = doc.save_file(_filePath.c_str(), " ");
return bRet;
}
const string& UserDefault::getXMLFilePath()
{
return _filePath;
}
void UserDefault::flush()
{
// TODO: Win32 store to UserDefault.plain file?
yasio::obstream obs;
obs.write_i7(this->_values.size());
for (auto& item : this->_values) {
obs.write_v(item.first);
obs.write_v(item.second);
}
if (obs.length() > _curMapSize) {
_rwmmap->unmap();
std::error_code error;
_curMapSize <<= 1; // X2
_rwmmap->map(posix_fd2fh(_fd), 0, _curMapSize, error);
}
if (_rwmmap->is_mapped())
memcpy(_rwmmap->data(), obs.data(), obs.length());
}
void UserDefault::deleteValueForKey(const char* key)
{
pugi::xml_node rootNode;
pugi::xml_document doc;
pugi::xml_node node;
// check the params
if (!key)
{
CCLOG("the key is invalid");
return;
}
// find the node
node = getXMLNodeForKey(key, rootNode, doc);
// if node not exist, don't need to delete
if (!node)
{
return;
}
// save file and free doc
if (doc)
{
rootNode.remove_child(node);
doc.save_file(UserDefault::getInstance()->getXMLFilePath().c_str(), " ");
}
flush();
if(this->_values.erase(key) > 0)
flush();
}
NS_CC_END
#endif // (CC_TARGET_PLATFORM != CC_PLATFORM_IOS && CC_PLATFORM != CC_PLATFORM_ANDROID)

View File

@ -2,6 +2,7 @@
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
Copyright (c) 2017-2020 c4games.com.
http://www.cocos2d-x.org
@ -28,7 +29,9 @@ THE SOFTWARE.
#include "platform/CCPlatformMacros.h"
#include <string>
#include "base/CCData.h"
#include <unordered_map>
#include "mio/mio.hpp"
/**
* @addtogroup base
@ -139,23 +142,6 @@ public:
*/
virtual std::string getStringForKey(const char* key, const std::string & defaultValue);
/**
* Get Data value by key, if the key doesn't exist, will return an empty Data.
* @param key The key to get value.
* @return Data value of the key.
* @js NA
*/
Data getDataForKey(const char* key);
/**
* Get Data value by key, if the key doesn't exist, will return an empty Data.
* @param key The key to get value.
* @param defaultValue The default value to return if the key doesn't exist.
* @return Data value of the key.
* @js NA
*/
virtual Data getDataForKey(const char* key, const Data& defaultValue);
// set value methods
/**
@ -193,13 +179,7 @@ public:
* @js NA
*/
virtual void setStringForKey(const char* key, const std::string & value);
/**
* Set Data value by key.
* @param key The key to set.
* @param value A Data value to set to the key.
* @js NA
*/
virtual void setDataForKey(const char* key, const Data& value);
/**
* You should invoke this function to save values set by setXXXForKey().
* @js NA
@ -235,28 +215,21 @@ public:
*/
static void setDelegate(UserDefault *delegate);
/** All supported platforms other iOS & Android use xml file to save values. This function is return the file path of the xml path.
* @js NA
*/
static const std::string& getXMLFilePath();
/** All supported platforms other iOS & Android use xml file to save values. This function checks whether the xml file exists or not.
* @return True if the xml file exists, false if not.
* @js NA
*/
static bool isXMLFileExist();
protected:
UserDefault();
virtual ~UserDefault();
void init();
private:
static bool createXMLFile();
static void initXMLFilePath();
std::unordered_map<std::string, std::string> _values;
static UserDefault* _userDefault;
static std::string _filePath;
static bool _isFilePathInitialized;
std::string _filePath;
int _fd = -1; // the file handle for data persistence
std::shared_ptr<mio::mmap_sink> _rwmmap;
int _curMapSize = 4096; // init mapsize is 4K
bool _initialized = false;
};

View File

@ -1,13 +1,11 @@
if(APPLE)
set(COCOS_BASE_SPECIFIC_SRC
base/CCUserDefault-apple.mm
base/CCController-apple.mm
)
elseif(ANDROID)
set(COCOS_BASE_SPECIFIC_SRC
base/CCUserDefault-android.cpp
base/CCController-android.cpp
)
elseif(LINUX OR WINDOWS)
@ -134,5 +132,4 @@ set(COCOS_BASE_SRC
base/pvr.cpp
base/s3tc.cpp
${COCOS_BASE_SPECIFIC_SRC}
)

View File

@ -1,5 +1,6 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
Copyright (c) 2020 c4games.com.
http://www.cocos2d-x.org
@ -58,50 +59,50 @@ UserDefaultTest::UserDefaultTest()
doTest();
}
template<typename T>
void logData(const char* key)
{
Data data = UserDefault::getInstance()->getDataForKey(key);
T* buffer = (T*) data.getBytes();
ssize_t length = data.getSize() / sizeof(T);
//template<typename T>
//void logData(const char* key)
//{
// Data data = UserDefault::getInstance()->getDataForKey(key);
// T* buffer = (T*) data.getBytes();
// ssize_t length = data.getSize() / sizeof(T);
//
// std::ostringstream ss;
// ss << setprecision(2) << std::fixed;
// for (int i = 0; i < length; i++)
// {
// ss << buffer[i] << " ";
// }
//
// CCLOG("%s is %s", key, ss.str().c_str());
//}
std::ostringstream ss;
ss << setprecision(2) << std::fixed;
for (int i = 0; i < length; i++)
{
ss << buffer[i] << " ";
}
CCLOG("%s is %s", key, ss.str().c_str());
}
template<typename T>
void setData(const char* key)
{
Data data;
vector<T> v;
for (int i = 0; i <= 5; i++)
{
v.push_back(static_cast<T>(i));
}
data.copy((unsigned char*) v.data(), v.size() * sizeof(T));
UserDefault::getInstance()->setDataForKey(key, data);
}
template<typename T>
void setData2(const char* key)
{
Data data;
vector<T> v;
for (int i = 5; i >= 0; i--)
{
v.push_back(static_cast<T>(i));
}
data.copy((unsigned char*) v.data(), v.size() * sizeof(T));
UserDefault::getInstance()->setDataForKey(key, data);
}
//template<typename T>
//void setData(const char* key)
//{
// Data data;
// vector<T> v;
//
// for (int i = 0; i <= 5; i++)
// {
// v.push_back(static_cast<T>(i));
// }
// data.copy((unsigned char*) v.data(), v.size() * sizeof(T));
// UserDefault::getInstance()->setDataForKey(key, data);
//}
//
//template<typename T>
//void setData2(const char* key)
//{
// Data data;
// vector<T> v;
//
// for (int i = 5; i >= 0; i--)
// {
// v.push_back(static_cast<T>(i));
// }
// data.copy((unsigned char*) v.data(), v.size() * sizeof(T));
// UserDefault::getInstance()->setDataForKey(key, data);
//}
void UserDefaultTest::doTest()
{
@ -116,15 +117,15 @@ void UserDefaultTest::doTest()
UserDefault::getInstance()->setBoolForKey("bool", true);
// test saving of Data buffers
setData<int>("int_data");
setData<float>("float_data");
setData<double>("double_data");
// setData<int>("int_data");
// setData<float>("float_data");
// setData<double>("double_data");
printValue();
logData<int>("int_data");
logData<float>("float_data");
logData<double>("double_data");
// logData<int>("int_data");
// logData<float>("float_data");
// logData<double>("double_data");
//CCUserDefault::getInstance()->flush();
@ -138,18 +139,18 @@ void UserDefaultTest::doTest()
UserDefault::getInstance()->setDoubleForKey("double", 2.6);
UserDefault::getInstance()->setBoolForKey("bool", false);
setData2<int>("int_data");
setData2<float>("float_data");
setData2<double>("double_data");
// setData2<int>("int_data");
// setData2<float>("float_data");
// setData2<double>("double_data");
UserDefault::getInstance()->flush();
// print value
printValue();
logData<int>("int_data");
logData<float>("float_data");
logData<double>("double_data");
// logData<int>("int_data");
// logData<float>("float_data");
// logData<double>("double_data");
this->_label->setString(this->_label->getString() + "\n" + "********************** after delete value ***********************");