no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

56
cocos/base/Agent.h Normal file
View File

@@ -0,0 +1,56 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <memory>
#include "Macros.h"
namespace cc {
template <typename Actor>
class CC_DLL Agent : public Actor {
public:
Agent() noexcept = delete;
explicit Agent(Actor *const actor) noexcept
: Actor(), _actor(actor) {}
~Agent() override = default;
Agent(Agent const &) = delete;
Agent(Agent &&) = delete;
Agent &operator=(Agent const &) = delete;
Agent &operator=(Agent &&) = delete;
inline Actor *getActor() const noexcept { return _actor; }
protected:
Actor *_actor{nullptr};
};
} // namespace cc

181
cocos/base/Assertf.h Normal file
View File

@@ -0,0 +1,181 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <assert.h> // NOLINT
#include <sstream>
#include <utility>
#if CC_DEBUG && !NDEBUG
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
#define CC_ASSERT_FORMAT(cond, fmt, ...) \
do { \
if (!(cond)) { \
char message[256] = {0}; \
wchar_t messageTmp[256] = {0}; \
snprintf(message, sizeof(message), "CC_ASSERT(%s) failed. " fmt "\n%s", #cond, ##__VA_ARGS__, __FUNCTION__); \
std::copy(message, message + strlen(message), messageTmp); \
_wassert(messageTmp, _CRT_WIDE(__FILE__), (unsigned)__LINE__); \
} \
} while (false)
#define CC_ABORTF(fmt, ...) \
do { \
char message[256] = {0}; \
wchar_t messageTmp[256] = {0}; \
snprintf(message, sizeof(message), fmt, ##__VA_ARGS__); \
std::copy(message, message + strlen(message), messageTmp); \
_wassert(messageTmp, _CRT_WIDE(__FILE__), (unsigned)__LINE__); \
} while (false)
#elif (CC_PLATFORM == CC_PLATFORM_ANDROID)
#define CC_ASSERT_FORMAT(cond, fmt, ...) \
do { \
if (__builtin_expect(!(cond), 0)) { \
char message[256] = {0}; \
snprintf(message, sizeof(message), "CC_ASSERT(%s) failed. " fmt, #cond, ##__VA_ARGS__); \
__assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, message); \
} \
} while (false)
#define CC_ABORTF(fmt, ...) \
do { \
char message[256] = {0}; \
snprintf(message, sizeof(message), fmt, ##__VA_ARGS__); \
__assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, message); \
} while (false)
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
#define CC_ASSERT_FORMAT(cond, fmt, ...) \
do { \
if (__builtin_expect(!(cond), 0)) { \
char message[256] = {0}; \
snprintf(message, sizeof(message), "CC_ASSERT(%s) failed. " fmt, #cond, ##__VA_ARGS__); \
__assert(message, __FILE__, __LINE__); \
} \
} while (false)
#define CC_ABORTF(fmt, ...) \
do { \
char message[256] = {0}; \
snprintf(message, sizeof(message), fmt, ##__VA_ARGS__); \
__assert(message, __FILE__, __LINE__); \
} while (false)
#else
#define CC_ASSERT_FORMAT(cond, ...) assert(cond)
#define CC_ABORTF(fmt, ...) abort()
#endif
// NOLINTNEXTLINE
#define _CC_ASSERT_(cond) assert(cond)
// NOLINTNEXTLINE
#define _CC_ASSERTF_CMP(expr1, op, expr2, fmt, ...) \
do { \
CC_ASSERT_FORMAT((expr1)op(expr2), fmt, ##__VA_ARGS__); \
} while (false)
// NOLINTNEXTLINE
#define _CC_ASSERT_CMP(expr1, op, expr2) \
do { \
_CC_ASSERT_((expr1)op(expr2)); \
} while (false)
// CC_ASSERTF variants
#define CC_ASSERTF_EQ(expr1, expr2, fmt, ...) _CC_ASSERTF_CMP(expr1, ==, expr2, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_NE(expr1, expr2, fmt, ...) _CC_ASSERTF_CMP(expr1, !=, expr2, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_LE(expr1, expr2, fmt, ...) _CC_ASSERTF_CMP(expr1, <=, expr2, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_GE(expr1, expr2, fmt, ...) _CC_ASSERTF_CMP(expr1, >=, expr2, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_LT(expr1, expr2, fmt, ...) _CC_ASSERTF_CMP(expr1, <, expr2, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_GT(expr1, expr2, fmt, ...) _CC_ASSERTF_CMP(expr1, >, expr2, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_TRUE(expr1, fmt, ...) _CC_ASSERTF_CMP(expr1, ==, true, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_FALSE(expr1, fmt, ...) _CC_ASSERTF_CMP(expr1, ==, false, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_NULL(expr1, fmt, ...) _CC_ASSERTF_CMP(expr1, ==, nullptr, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_NOT_NULL(expr1, fmt, ...) _CC_ASSERTF_CMP(expr1, !=, nullptr, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_ZERO(expr1, fmt, ...) _CC_ASSERTF_CMP(expr1, ==, 0, fmt, ##__VA_ARGS__)
#define CC_ASSERTF_NONZERO(expr1, fmt, ...) _CC_ASSERTF_CMP(expr1, !=, 0, fmt, ##__VA_ARGS__)
// CC_ASSERT variants
#define CC_ASSERT_EQ(expr1, expr2) _CC_ASSERT_CMP(expr1, ==, expr2)
#define CC_ASSERT_NE(expr1, expr2) _CC_ASSERT_CMP(expr1, !=, expr2)
#define CC_ASSERT_LE(expr1, expr2) _CC_ASSERT_CMP(expr1, <=, expr2)
#define CC_ASSERT_GE(expr1, expr2) _CC_ASSERT_CMP(expr1, >=, expr2)
#define CC_ASSERT_LT(expr1, expr2) _CC_ASSERT_CMP(expr1, <, expr2)
#define CC_ASSERT_GT(expr1, expr2) _CC_ASSERT_CMP(expr1, >, expr2)
#define CC_ASSERT_TRUE(expr1) _CC_ASSERT_CMP(expr1, ==, true)
#define CC_ASSERT_FALSE(expr1) _CC_ASSERT_CMP(expr1, ==, false)
#define CC_ASSERT_NULL(expr1) _CC_ASSERT_CMP(expr1, ==, nullptr)
#define CC_ASSERT_NOT_NULL(expr1) _CC_ASSERT_CMP(expr1, !=, nullptr)
#define CC_ASSERT_ZERO(expr1) _CC_ASSERT_CMP(expr1, ==, 0)
#define CC_ASSERT_NONZERO(expr1) _CC_ASSERT_CMP(expr1, !=, 0)
/**
* @brief printf like assert
*
* CC_ASSERTF(1==2, "value %d should not be equal to %d", 1, 2);
* CC_ASSERTF_EQ(1, 3/3, "n equals to n");
* CC_ASSERTF_NE(1, s, "not initial value");
*
*/
#define CC_ASSERTF(cond, fmt, ...) CC_ASSERT_FORMAT(cond, fmt, ##__VA_ARGS__)
/**
* CC_ABORT call abort() in debug mode.
*
* printf like abort: CC_ABORTF
*
* CC_ABORTF("Dead Code")
* CC_ABORTF("Invalidate state code %d", statusCode);
* CC_ABORTF("Should crash in debug mode")
*/
#define CC_ABORT() abort()
#define CC_ASSERT(cond) _CC_ASSERT_(cond)
#else
#define CC_ASSERTF(...) ((void)0)
#define CC_ASSERTF_EQ(expr1, expr2, ...) ((void)0)
#define CC_ASSERTF_NE(expr1, expr2, ...) ((void)0)
#define CC_ASSERTF_LE(expr1, expr2, ...) ((void)0)
#define CC_ASSERTF_GE(expr1, expr2, ...) ((void)0)
#define CC_ASSERTF_LT(expr1, expr2, ...) ((void)0)
#define CC_ASSERTF_GT(expr1, expr2, ...) ((void)0)
#define CC_ASSERTF_TRUE(expr1, ...) ((void)0)
#define CC_ASSERTF_FALSE(expr1, ...) ((void)0)
#define CC_ASSERTF_NULL(expr1, ...) ((void)0)
#define CC_ASSERTF_NOT_NULL(expr1, ...) ((void)0)
#define CC_ASSERTF_ZERO(expr1, ...) ((void)0)
#define CC_ASSERTF_NONZERO(expr1, ...) ((void)0)
#define CC_ASSERT(...) ((void)0)
#define CC_ASSERT_EQ(expr1, expr2, ...) ((void)0)
#define CC_ASSERT_NE(expr1, expr2, ...) ((void)0)
#define CC_ASSERT_LE(expr1, expr2, ...) ((void)0)
#define CC_ASSERT_GE(expr1, expr2, ...) ((void)0)
#define CC_ASSERT_LT(expr1, expr2, ...) ((void)0)
#define CC_ASSERT_GT(expr1, expr2, ...) ((void)0)
#define CC_ASSERT_TRUE(expr1, ...) ((void)0)
#define CC_ASSERT_FALSE(expr1, ...) ((void)0)
#define CC_ASSERT_NULL(expr1, ...) ((void)0)
#define CC_ASSERT_NOT_NULL(expr1, ...) ((void)0)
#define CC_ASSERT_ZERO(expr1, ...) ((void)0)
#define CC_ASSERT_NONZERO(expr1, ...) ((void)0)
#define CC_ABORTF(...) ((void)0) // ignored in release builds
#define CC_ABORT() ((void)0) // ignored in release builds
#endif

View File

@@ -0,0 +1,46 @@
/****************************************************************************
Copyright (c) 2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "BinaryArchive.h"
#include "base/Assertf.h"
namespace cc {
bool BinaryInputArchive::load(char *data, uint32_t size) {
CC_ASSERT(!!_stream);
return _stream.rdbuf()->sgetn(data, size) == size;
}
void BinaryInputArchive::move(uint32_t length) {
CC_ASSERT(!!_stream);
_stream.ignore(length);
}
void BinaryOutputArchive::save(const char *data, uint32_t size) {
CC_ASSERT(!!_stream);
auto len = _stream.rdbuf()->sputn(data, size);
CC_ASSERT(len == size);
}
} // namespace cc

View File

@@ -0,0 +1,93 @@
/****************************************************************************
Copyright (c) 2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <iostream>
namespace cc {
/**
* Binary input stream archive.
*/
class BinaryInputArchive {
public:
explicit BinaryInputArchive(std::istream &stream) : _stream(stream) {}
~BinaryInputArchive() = default;
/**
* Read data from stream.
* @param data Pointer to data address to read.
* @param size Length of the data.
*/
bool load(char *data, uint32_t size);
/**
* Read arithmetic data from stream.
* @param val Data to read.
*/
template <typename T, typename = std::enable_if<std::is_arithmetic_v<T>>>
bool load(T &val) {
return load(reinterpret_cast<char *>(std::addressof(val)), sizeof(T));
}
/**
* Skip data length
* @param length Skip length
*/
void move(uint32_t length);
private:
std::istream &_stream;
};
/**
* Binary output stream archive.
*/
class BinaryOutputArchive {
public:
explicit BinaryOutputArchive(std::ostream &stream) : _stream(stream) {}
~BinaryOutputArchive() = default;
/**
* Write data to stream.
* @param data Pointer to data address to write.
* @param size Length of the data.
*/
void save(const char *data, uint32_t size);
/**
* Write arithmetic data to stream.
* @param val Data to write.
*/
template <typename T, typename = std::enable_if<std::is_arithmetic_v<T>>>
void save(const T &v) {
save(reinterpret_cast<const char *>(std::addressof(v)), sizeof(T));
}
private:
std::ostream &_stream;
};
} // namespace cc

77
cocos/base/Compressed.cpp Normal file
View File

@@ -0,0 +1,77 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 <cstring>
#include "base/Compressed.h"
#include "base/Utils.h"
// compressed file header
static const uint32_t COMPRESSED_HEADER_LENGTH = 4;
static const uint32_t COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH = 4;
static const uint32_t COMPRESSED_MIPMAP_DATA_SIZE_LENGTH = 4;
static const uint32_t COMPRESSED_MAGIC = 0x50494d43;
bool compressedIsValid(const unsigned char* pFile) {
const uint32_t magic = static_cast<uint32_t>(pFile[0]) +
static_cast<uint32_t>(pFile[1]) * 256 +
static_cast<uint32_t>(pFile[2]) * 65536 +
static_cast<uint32_t>(pFile[3]) * 16777216;
return magic == COMPRESSED_MAGIC;
}
uint32_t getChunkNumbers(const unsigned char* pFile) {
return static_cast<uint32_t>(pFile[COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + 0]) +
static_cast<uint32_t>(pFile[COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + 1]) * 256 +
static_cast<uint32_t>(pFile[COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + 2]) * 65536 +
static_cast<uint32_t>(pFile[COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + 3]) * 16777216;
}
uint32_t getChunkSizes(const unsigned char* pFile, uint32_t level) {
const uint32_t byteOffset = COMPRESSED_HEADER_LENGTH + COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + COMPRESSED_MIPMAP_DATA_SIZE_LENGTH * level;
return (pFile[byteOffset + 0]) +
(pFile[byteOffset + 1]) * 256 +
(pFile[byteOffset + 2]) * 65536 +
(pFile[byteOffset + 3]) * 16777216;
}
unsigned char* getChunk(const unsigned char* pFile, uint32_t level) {
unsigned char* dstData = nullptr;
const auto chunkCount = getChunkNumbers(pFile);
const auto compressedFileHeaderLength = COMPRESSED_HEADER_LENGTH + COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + COMPRESSED_MIPMAP_DATA_SIZE_LENGTH * chunkCount;
uint32_t byteOffset = compressedFileHeaderLength;
for (uint32_t i = 0; i < chunkCount; ++i) {
const auto chunkSize = getChunkSizes(pFile, i);
if (i == level) {
dstData = static_cast<unsigned char*>(malloc(chunkSize * sizeof(unsigned char)));
memcpy(dstData, pFile + byteOffset, chunkSize);
break;
}
byteOffset += chunkSize;
}
return dstData;
}

38
cocos/base/Compressed.h Normal file
View File

@@ -0,0 +1,38 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstdint>
// Check if a compressed file header is correctly formatted
bool compressedIsValid(const unsigned char *pFile);
// Read the chunks count from compressed header
uint32_t getChunkNumbers(const unsigned char *pFile);
// Read the chunk size from compressed header
uint32_t getChunkSizes(const unsigned char *pFile, uint32_t level);
// Read the chunk from compressed header
unsigned char *getChunk(const unsigned char *pFile, uint32_t level);

69
cocos/base/Config.h Normal file
View File

@@ -0,0 +1,69 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2011 Zynga Inc.
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
/** Support PNG or not. If your application don't use png format picture, you can undefine this macro to save package size.
*/
#ifndef CC_USE_PNG
#define CC_USE_PNG 1
#endif // CC_USE_PNG
/** Support JPEG or not. If your application don't use jpeg format picture, you can undefine this macro to save package size.
*/
#ifndef CC_USE_JPEG
#define CC_USE_JPEG 1
#endif // CC_USE_JPEG
/** Support webp or not. If your application don't use webp format picture, you can undefine this macro to save package size.
*/
#ifndef CC_USE_WEBP
#define CC_USE_WEBP 1
#endif // CC_USE_WEBP
/** Support EditBox
*/
#ifndef CC_USE_EDITBOX
#define CC_USE_EDITBOX 1
#endif
#ifndef CC_FILEUTILS_APPLE_ENABLE_OBJC
#define CC_FILEUTILS_APPLE_ENABLE_OBJC 1
#endif
#ifndef CC_ENABLE_CACHE_JSB_FUNC_RESULT
#define CC_ENABLE_CACHE_JSB_FUNC_RESULT 1
#endif
#ifndef USE_MEMORY_LEAK_DETECTOR
#define USE_MEMORY_LEAK_DETECTOR 0
#endif
#ifndef CC_USE_PROFILER
#define CC_USE_PROFILER 0
#endif

130
cocos/base/Data.cpp Normal file
View File

@@ -0,0 +1,130 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/Data.h"
#include <cstring>
#include "base/Log.h"
namespace cc {
const Data Data::NULL_DATA;
Data::Data() = default;
Data::Data(Data &&other) noexcept {
// CC_LOG_INFO("In the move constructor of Data.");
move(other);
}
Data::Data(const Data &other) {
// CC_LOG_INFO("In the copy constructor of Data.");
copy(other._bytes, other._size);
}
Data::~Data() {
// CC_LOG_INFO("deallocing Data: %p", this);
clear();
}
Data &Data::operator=(const Data &other) {
// CC_LOG_INFO("In the copy assignment of Data.");
if (this != &other) {
copy(other._bytes, other._size);
}
return *this;
}
Data &Data::operator=(Data &&other) noexcept {
// CC_LOG_INFO("In the move assignment of Data.");
move(other);
return *this;
}
void Data::move(Data &other) {
clear();
_bytes = other._bytes;
_size = other._size;
other._bytes = nullptr;
other._size = 0;
}
bool Data::isNull() const {
return (_bytes == nullptr || _size == 0);
}
uint8_t *Data::getBytes() const {
return _bytes;
}
uint32_t Data::getSize() const {
return _size;
}
void Data::copy(const unsigned char *bytes, uint32_t size) {
clear();
if (size > 0) {
_size = size;
_bytes = static_cast<unsigned char *>(malloc(sizeof(unsigned char) * _size));
memcpy(_bytes, bytes, _size);
}
}
void Data::fastSet(unsigned char *bytes, uint32_t size) {
free(_bytes);
_bytes = bytes;
_size = size;
}
void Data::resize(uint32_t size) {
CC_ASSERT(size);
if (_size == size) {
return;
}
_size = size;
_bytes = static_cast<unsigned char *>(realloc(_bytes, sizeof(unsigned char) * _size));
}
void Data::clear() {
free(_bytes);
_bytes = nullptr;
_size = 0;
}
uint8_t *Data::takeBuffer(uint32_t *size) {
auto *buffer = getBytes();
if (size) {
*size = getSize();
}
_bytes = nullptr;
_size = 0;
return buffer;
}
} // namespace cc

145
cocos/base/Data.h Normal file
View File

@@ -0,0 +1,145 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
namespace cc {
class CC_DLL Data {
friend class Properties;
public:
/**
* This parameter is defined for convenient reference if a null Data object is needed.
*/
static const Data NULL_DATA;
/**
* Constructor of Data.
*/
Data();
/**
* Copy constructor of Data.
*/
Data(const Data &other);
/**
* Copy constructor of Data.
*/
Data(Data &&other) noexcept;
/**
* Destructor of Data.
*/
~Data();
/**
* Overloads of operator=.
*/
Data &operator=(const Data &other);
/**
* Overloads of operator=.
*/
Data &operator=(Data &&other) noexcept;
/**
* Gets internal bytes of Data. It will return the pointer directly used in Data, so don't delete it.
*
* @return Pointer of bytes used internal in Data.
*/
uint8_t *getBytes() const;
/**
* Gets the size of the bytes.
*
* @return The size of bytes of Data.
*/
uint32_t getSize() const;
/** Copies the buffer pointer and its size.
* @note This method will copy the whole buffer.
* Developer should free the pointer after invoking this method.
* @see Data::fastSet
*/
void copy(const unsigned char *bytes, uint32_t size);
/** Fast set the buffer pointer and its size. Please use it carefully.
* @param bytes The buffer pointer, note that it have to be allocated by 'malloc' or 'calloc',
* since in the destructor of Data, the buffer will be deleted by 'free'.
* @note 1. This method will move the ownship of 'bytes'pointer to Data,
* 2. The pointer should not be used outside after it was passed to this method.
* @see Data::copy
*/
void fastSet(unsigned char *bytes, uint32_t size);
void resize(uint32_t size);
/**
* Clears data, free buffer and reset data size.
*/
void clear();
/**
* Check whether the data is null.
*
* @return True if the Data is null, false if not.
*/
bool isNull() const;
/**
* Get the internal buffer of data and set data to empty state.
*
* The ownership of the buffer removed from the data object.
* That is the user have to free the returned buffer.
* The data object is set to empty state, that is internal buffer is set to nullptr
* and size is set to zero.
* Usage:
* @code
* Data d;
* // ...
* uint32_t size;
* uint8_t* buffer = d.takeBuffer(&size);
* // use buffer and size
* free(buffer);
* @endcode
*
* @param size Will fill with the data buffer size in bytes, if you do not care buffer size, pass nullptr.
* @return the internal data buffer, free it after use.
*/
unsigned char *takeBuffer(uint32_t *size = nullptr);
private:
void move(Data &other); //NOLINT
uint8_t *_bytes{nullptr};
uint32_t _size{0};
};
} // namespace cc

View File

@@ -0,0 +1,62 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/DeferredReleasePool.h"
#include "base/Log.h"
namespace cc {
ccstd::vector<RefCounted *> DeferredReleasePool::managedObjectArray{};
void DeferredReleasePool::add(RefCounted *object) {
DeferredReleasePool::managedObjectArray.push_back(object);
}
void DeferredReleasePool::clear() {
for (const auto &obj : DeferredReleasePool::managedObjectArray) {
obj->release();
}
DeferredReleasePool::managedObjectArray.clear();
}
bool DeferredReleasePool::contains(RefCounted *object) {
for (const auto &obj : DeferredReleasePool::managedObjectArray) { // NOLINT(readability-use-anyofallof) // remove after using C++20
if (obj == object) {
return true;
}
}
return false;
}
void DeferredReleasePool::dump() {
CC_LOG_DEBUG("number of managed object %ul\n", DeferredReleasePool::managedObjectArray.size());
CC_LOG_DEBUG("%20s%20s%20s", "Object pointer", "Object id", "reference count");
for (const auto &obj : DeferredReleasePool::managedObjectArray) {
CC_UNUSED_PARAM(obj);
CC_LOG_DEBUG("%20p%20u\n", obj, obj->getRefCount());
}
}
} // namespace cc

View File

@@ -0,0 +1,69 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/RefCounted.h"
#include "base/std/container/string.h"
#include "base/std/container/vector.h"
namespace cc {
class CC_DLL DeferredReleasePool {
public:
static void add(RefCounted *object);
static void clear();
/**
* Checks whether the autorelease pool contains the specified object.
*
* @param object The object to be checked.
* @return True if the autorelease pool contains the object, false if not
*/
static bool contains(RefCounted *object);
/**
* Dump the objects that are put into the autorelease pool. It is used for debugging.
*
* The result will look like:
* Object pointer address object id reference count
*/
static void dump();
private:
/**
* The underlying array of object managed by the pool.
*
* Although Array retains the object once when an object is added, proper
* Ref::release() is called outside the array to make sure that the pool
* does not affect the managed object's reference count. So an object can
* be destructed properly by calling Ref::release() even if the object
* is in the pool.
*/
static ccstd::vector<RefCounted *> managedObjectArray;
};
} // namespace cc

View File

@@ -0,0 +1,97 @@
/****************************************************************************
Copyright (c) 2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <type_traits>
/* This macro is used for checking whether a member function exists in a class in compiling time.
Usage:
If we have a class called `MyClass` as follows:
```c++
class MyClass {
public:
int myMethod(bool a, float b) { return 100; }
};
```
Test code:
```c++
#include "base/HasMemberFunction.h"
CC_DEFINE_HAS_MEMBER_FUNC(myMethod)
template <typename T>
void myTest(T* arg0) {
if constexpr (has_myMethod<T, int(bool, float)>::value) {
// <1>: DO SOMETHING if T owns `myMethod` function.
// ...
} else {
// <2>: DO SOMETHING if T doesn't own `myMethod` function.
// ...
}
}
static int myTestEntry() {
MyClass a;
myTest(&a); // --> Go to <1>
int b;
myTest(&b; // --> Go to <2>
}
```
*/
#define CC_DEFINE_HAS_MEMBER_FUNC(memFunc) \
template <typename, typename T> \
struct has_##memFunc { \
static_assert( \
std::integral_constant<T, false>::value, \
"Second template parameter needs to be of function type."); \
}; \
\
template <typename C, typename Ret, typename... Args> \
struct has_##memFunc<C, Ret(Args...)> { \
private: \
template <typename T> \
static constexpr auto check(T*) \
-> typename std::is_same< \
decltype(std::declval<T>().memFunc(std::declval<Args>()...)), \
Ret>::type; \
\
template <typename> \
static constexpr std::false_type check(...); \
typedef decltype(check<C>(0)) type; \
\
public: \
static constexpr bool value = type::value; \
};
namespace cc {
CC_DEFINE_HAS_MEMBER_FUNC(setScriptObject)
CC_DEFINE_HAS_MEMBER_FUNC(getScriptObject)
} // namespace cc

89
cocos/base/IndexHandle.h Normal file
View File

@@ -0,0 +1,89 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstdint>
#include <limits>
#include <type_traits>
namespace cc {
template <typename Index, typename Enable = std::enable_if_t<std::is_integral<Index>::value>>
class IndexHandle {
public:
struct Hasher {
inline std::size_t operator()(IndexHandle const &s) const noexcept { return s._index; }
};
using IndexType = Index;
IndexHandle() noexcept = default;
explicit IndexHandle(IndexType index) noexcept : _index(index) {}
inline bool isValid() const noexcept;
inline void clear() noexcept;
inline bool operator<(IndexHandle const &rhs) const noexcept;
inline bool operator==(IndexHandle const &rhs) const noexcept;
inline bool operator!=(IndexHandle const &rhs) const noexcept;
inline operator IndexType() const noexcept; // NOLINT(google-explicit-constructor) we need this implicitly
static IndexType constexpr UNINITIALIZED{std::numeric_limits<IndexType>::max()};
private:
IndexType _index{UNINITIALIZED};
};
template <typename Index, typename Enable>
bool IndexHandle<Index, Enable>::isValid() const noexcept {
return _index != UNINITIALIZED;
}
template <typename Index, typename Enable>
void IndexHandle<Index, Enable>::clear() noexcept {
_index = UNINITIALIZED;
}
template <typename Index, typename Enable>
bool IndexHandle<Index, Enable>::operator<(IndexHandle const &rhs) const noexcept {
return _index < rhs._index;
}
template <typename Index, typename Enable>
bool IndexHandle<Index, Enable>::operator==(IndexHandle const &rhs) const noexcept {
return (_index == rhs._index);
}
template <typename Index, typename Enable>
bool IndexHandle<Index, Enable>::operator!=(IndexHandle const &rhs) const noexcept {
return !operator==(rhs);
}
template <typename Index, typename Enable>
IndexHandle<Index, Enable>::operator IndexType() const noexcept {
return _index;
}
} // namespace cc

91
cocos/base/Locked.h Normal file
View File

@@ -0,0 +1,91 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <memory>
namespace cc {
template <class T, class LK>
class UniqueLockedRef {
public:
UniqueLockedRef(T *t, LK *mtx) : _data(t), _mtx(mtx) {
_mtx->lock();
}
UniqueLockedRef(UniqueLockedRef &&t) noexcept {
_data = t._data;
_mtx = t._mtx;
t._data = nullptr;
t._mtx = nullptr;
}
UniqueLockedRef &operator=(UniqueLockedRef &&t) noexcept {
_data = t._data;
_mtx = t._mtx;
t._data = nullptr;
t._mtx = nullptr;
return *this;
}
UniqueLockedRef(const T &t) = delete;
UniqueLockedRef &operator=(const UniqueLockedRef &) = delete;
~UniqueLockedRef() {
if (_mtx) {
_mtx->unlock();
}
}
T *operator->() {
return _data;
}
T &operator*() {
return *_data;
}
private:
T *_data{};
LK *_mtx{};
};
template <class T, class LK>
class Locked {
public:
Locked() = default;
Locked(const Locked &) = delete;
Locked(Locked &&) = delete;
Locked &operator=(const Locked &) = delete;
Locked &operator=(Locked &&) = delete;
UniqueLockedRef<T, LK> lock() {
UniqueLockedRef<T, LK> ref(&_data, &_mutex);
return std::move(ref);
}
private:
LK _mutex{};
T _data{};
};
} // namespace cc

239
cocos/base/Log.cpp Normal file
View File

@@ -0,0 +1,239 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "Log.h"
#include <cstdarg>
#include <ctime>
#include "base/std/container/string.h"
#include "base/std/container/vector.h"
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#define COLOR_FATAL FOREGROUND_INTENSITY | FOREGROUND_RED
#define COLOR_ERROR FOREGROUND_RED
#define COLOR_WARN 6
#define COLOR_INFO FOREGROUND_GREEN | FOREGROUND_BLUE
#define COLOR_DEBUG 7
#define COLOR_NORMAL 8
#define SET_CONSOLE_TEXT_COLOR(color) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color)
#elif (CC_PLATFORM == CC_PLATFORM_ANDROID)
#include <android/log.h>
#elif CC_PLATFORM == CC_PLATFORM_OHOS
#include <hilog/log.h>
#elif (CC_PLATFORM == CC_PLATFORM_OPENHARMONY)
#include <hilog/log.h>
#endif
namespace cc {
#define LOG_USE_TIMESTAMP
#if (CC_DEBUG == 1)
LogLevel Log::slogLevel = LogLevel::LEVEL_DEBUG;
#else
LogLevel Log::slogLevel = LogLevel::INFO;
#endif
FILE *Log::slogFile = nullptr;
const ccstd::vector<ccstd::string> LOG_LEVEL_DESCS{"FATAL", "ERROR", "WARN", "INFO", "DEBUG"};
void Log::setLogFile(const ccstd::string &filename) {
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
if (slogFile) {
fclose(slogFile);
}
slogFile = fopen(filename.c_str(), "w");
if (slogFile) {
ccstd::string msg;
msg += "------------------------------------------------------\n";
struct tm *tm_time;
time_t ct_time;
time(&ct_time);
tm_time = localtime(&ct_time);
char dateBuffer[256] = {0};
snprintf(dateBuffer, sizeof(dateBuffer), "LOG DATE: %04d-%02d-%02d %02d:%02d:%02d\n",
tm_time->tm_year + 1900,
tm_time->tm_mon + 1,
tm_time->tm_mday,
tm_time->tm_hour,
tm_time->tm_min,
tm_time->tm_sec);
msg += dateBuffer;
msg += "------------------------------------------------------\n";
fputs(msg.c_str(), slogFile);
fflush(slogFile);
}
#endif
}
void Log::close() {
if (slogFile) {
fclose(slogFile);
slogFile = nullptr;
}
}
void Log::logMessage(LogType type, LogLevel level, const char *formats, ...) {
char buff[4096];
char *p = buff;
char *last = buff + sizeof(buff) - 3;
#if defined(LOG_USE_TIMESTAMP)
struct tm *tmTime;
time_t ctTime;
time(&ctTime);
tmTime = localtime(&ctTime);
p += sprintf(p, "%02d:%02d:%02d ", tmTime->tm_hour, tmTime->tm_min, tmTime->tm_sec);
#endif
p += sprintf(p, "[%s]: ", LOG_LEVEL_DESCS[static_cast<int>(level)].c_str());
va_list args;
va_start(args, formats);
// p += StringUtil::vprintf(p, last, formats, args);
std::ptrdiff_t count = (last - p);
int ret = vsnprintf(p, count, formats, args);
if (ret >= count - 1) {
p += (count - 1);
} else if (ret >= 0) {
p += ret;
}
va_end(args);
*p++ = '\n';
*p = 0;
if (slogFile) {
fputs(buff, slogFile);
fflush(slogFile);
}
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
WCHAR wszBuf[4096] = {0};
MultiByteToWideChar(CP_UTF8, 0, buff, -1, wszBuf, sizeof(wszBuf));
WORD color;
switch (level) {
case LogLevel::LEVEL_DEBUG: color = COLOR_DEBUG; break;
case LogLevel::INFO: color = COLOR_INFO; break;
case LogLevel::WARN: color = COLOR_WARN; break;
case LogLevel::ERR: color = COLOR_ERROR; break;
case LogLevel::FATAL: color = COLOR_FATAL; break;
default: color = COLOR_INFO;
}
SET_CONSOLE_TEXT_COLOR(color);
wprintf(L"%s", wszBuf);
SET_CONSOLE_TEXT_COLOR(COLOR_NORMAL);
OutputDebugStringW(wszBuf);
#elif (CC_PLATFORM == CC_PLATFORM_ANDROID)
android_LogPriority priority;
switch (level) {
case LogLevel::LEVEL_DEBUG:
priority = ANDROID_LOG_DEBUG;
break;
case LogLevel::INFO:
priority = ANDROID_LOG_INFO;
break;
case LogLevel::WARN:
priority = ANDROID_LOG_WARN;
break;
case LogLevel::ERR:
priority = ANDROID_LOG_ERROR;
break;
case LogLevel::FATAL:
priority = ANDROID_LOG_FATAL;
break;
default:
priority = ANDROID_LOG_INFO;
}
__android_log_write(priority, (type == LogType::KERNEL ? "Cocos" : "CocosScript"), buff);
#elif (CC_PLATFORM == CC_PLATFORM_OHOS)
const char *typeStr = (type == LogType::KERNEL ? "Cocos %{public}s" : "CocosScript %{public}s");
switch (level) {
case LogLevel::LEVEL_DEBUG:
HILOG_DEBUG(LOG_APP, typeStr, (const char *)buff);
break;
case LogLevel::INFO:
HILOG_INFO(LOG_APP, typeStr, buff);
break;
case LogLevel::WARN:
HILOG_WARN(LOG_APP, typeStr, buff);
break;
case LogLevel::ERR:
HILOG_ERROR(LOG_APP, typeStr, buff);
break;
case LogLevel::FATAL:
HILOG_FATAL(LOG_APP, typeStr, buff);
break;
default:
HILOG_DEBUG(LOG_APP, typeStr, buff);
}
#elif (CC_PLATFORM == CC_PLATFORM_OPENHARMONY)
::LogLevel ohosLoglevel = ::LogLevel::LOG_DEBUG;
switch (level) {
case LogLevel::LEVEL_DEBUG:
ohosLoglevel = ::LogLevel::LOG_DEBUG;
break;
case LogLevel::INFO:
ohosLoglevel = ::LogLevel::LOG_INFO;
break;
case LogLevel::WARN:
ohosLoglevel = ::LogLevel::LOG_WARN;
break;
case LogLevel::ERR:
ohosLoglevel = ::LogLevel::LOG_ERROR;
break;
case LogLevel::FATAL:
ohosLoglevel = ::LogLevel::LOG_FATAL;
break;
default:
ohosLoglevel = ::LogLevel::LOG_INFO;
break;
}
OH_LOG_Print(LOG_APP, ohosLoglevel, LOG_DOMAIN, "HMG_LOG", "%{public}s", buff);
#else
fputs(buff, stdout);
#endif
#if CC_REMOTE_LOG
logRemote(buff);
#endif
}
} // namespace cc

79
cocos/base/Log.h Normal file
View File

@@ -0,0 +1,79 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "Macros.h"
#include "base/std/container/string.h"
namespace cc {
enum class LogType {
KERNEL,
SCRIPT,
COUNT,
};
enum class LogLevel {
FATAL,
ERR,
WARN,
INFO,
LEVEL_DEBUG, // DEBUG is a macro on windows, so use LEVEL_DEBUG instead.
COUNT,
};
class CC_DLL Log {
public:
static LogLevel slogLevel; // for read only
static inline void setLogLevel(LogLevel level) { slogLevel = level; }
static inline FILE *getLogFile() { return slogFile; }
static void setLogFile(const ccstd::string &filename);
static void close();
static void logMessage(LogType type, LogLevel level, const char *formats, ...);
private:
static void logRemote(const char *msg);
static FILE *slogFile;
};
} // namespace cc
#define CC_LOG_DEBUG(formats, ...) \
if (cc::Log::slogLevel >= cc::LogLevel::LEVEL_DEBUG) cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::LEVEL_DEBUG, formats, ##__VA_ARGS__)
#define CC_LOG_INFO(formats, ...) \
if (cc::Log::slogLevel >= cc::LogLevel::INFO) cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::INFO, formats, ##__VA_ARGS__)
#define CC_LOG_WARNING(formats, ...) \
if (cc::Log::slogLevel >= cc::LogLevel::WARN) cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::WARN, formats, ##__VA_ARGS__)
#define DO_CC_LOG_ERROR(formats, ...) \
if (cc::Log::slogLevel >= cc::LogLevel::ERR) cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::ERR, formats, ##__VA_ARGS__)
#define CC_LOG_FATAL(formats, ...) \
if (cc::Log::slogLevel >= cc::LogLevel::FATAL) cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::FATAL, formats, ##__VA_ARGS__)
#define CC_LOG_ERROR(formats, ...) \
do { \
DO_CC_LOG_ERROR("[ERROR] file %s: line %d ", __FILE__, __LINE__); \
DO_CC_LOG_ERROR(formats, ##__VA_ARGS__); \
} while (0)

336
cocos/base/LogRemote.cpp Normal file
View File

@@ -0,0 +1,336 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/Log.h"
#if CC_REMOTE_LOG
#include <chrono>
#include <sstream>
#include <string_view>
#include "platform/FileUtils.h"
#include "rapidjson/document.h"
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <winsock2.h>
#include <cstdio>
#pragma comment(lib, "ws2_32.lib")
#else
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#endif
namespace {
// #define AUTO_TEST_CONFIG_FILE "auto-test-config.json" // v1
#define AUTO_TEST_CONFIG_FILE "testConfig.json" // v2
#define OLD_ATC_KEY_CONFIG "ServerConfig"
#define OLD_ATC_KEY_IP "IP"
#define OLD_ATC_KEY_PORT "PORT"
#define OLD_ATC_KEY_PLANID "planId"
#define OLD_ATC_KEY_FLAGID "flagId"
#define ATC_KEY_CONFIG "localServer"
#define ATC_KEY_IP "ip"
#define ATC_KEY_PORT "port"
#define ATC_KEY_JOBID "jobId"
#define ATC_KEY_PLATFORMS "platforms"
#define ATC_KEY_PLATFORM_INDEX "platformIndex"
enum class UdpLogClientState {
UNINITIALIZED,
INITIALIZED,
CONFIGURED,
OK,
DONE, // FAILED
};
uint64_t logId = 0;
/**
* Parse auto-test-config.json to get ServerConfig.IP & ServerConfig.PORT
* Logs will be formated with 5 fields
* 1. testId
* 2. clientId
* 3. bootId, the boot timestamp
* 4. sequence number of the message
* 5. log content
*
* These parts are joined with '\n'.
*
* The log text is sent to the server via UDP. Due to the nature of the UDP protocol,
* there may be packet loss/missequencing issues.
*/
class UdpLogClient {
public:
UdpLogClient() {
init();
tryParseConfig();
}
~UdpLogClient() {
deinit();
}
void sendFullLog(const std::string_view &msg) {
if (_status == UdpLogClientState::DONE) {
return;
}
std::stringstream ss;
ss << _testID << std::endl
<< _clientID << std::endl
<< _bootID << std::endl
<< ++logId << std::endl
<< msg;
sendLog(ss.str());
}
private:
void init() {
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
WSAData wsa;
if (WSAStartup(MAKEWORD(1, 2), &wsa) != 0) {
printf("WSAStartup failed, code: %d\n", WSAGetLastError());
_status = UdpLogClientState::DONE;
return;
}
#endif
_status = UdpLogClientState::INITIALIZED;
}
void deinit() {
if (_status == UdpLogClientState::OK) {
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
closesocket(_sock);
WSACleanup();
#else
close(_sock);
#endif
}
}
void tryParseConfig() {
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
if (_status != UdpLogClientState::INITIALIZED) {
return;
}
#endif
auto *fu = cc::FileUtils::getInstance();
if (!fu) {
// engine is not ready, retry later
return;
}
if (!fu->isFileExist(AUTO_TEST_CONFIG_FILE)) {
_status = UdpLogClientState::DONE;
return;
}
auto content = fu->getStringFromFile(AUTO_TEST_CONFIG_FILE);
rapidjson::Document doc;
doc.Parse(content.c_str(), content.length());
if (doc.HasParseError()) {
auto code = doc.GetParseError();
_status = UdpLogClientState::DONE;
return;
}
if (doc.HasMember(OLD_ATC_KEY_CONFIG)) {
// parse clientID & testID
if (doc.HasMember(OLD_ATC_KEY_FLAGID)) {
_clientID = doc[OLD_ATC_KEY_FLAGID].GetString();
} else {
_clientID = "flagId is not set!";
}
if (doc.HasMember(OLD_ATC_KEY_PLANID)) {
_testID = doc[OLD_ATC_KEY_PLANID].GetString();
} else {
_testID = "planId is not set!";
}
// parse ip & port
rapidjson::Value &cfg = doc[OLD_ATC_KEY_CONFIG];
if (!cfg.HasMember(OLD_ATC_KEY_IP) || !cfg.HasMember(OLD_ATC_KEY_PORT)) {
_status = UdpLogClientState::DONE;
return;
}
const char *remoteIp = cfg[OLD_ATC_KEY_IP].GetString();
// The `PORT` property is used by other service and the next port is for log collection.
int remotePort = cfg[OLD_ATC_KEY_PORT].GetInt() + 1;
setServerAddr(remoteIp, remotePort);
} else if (doc.HasMember(ATC_KEY_CONFIG)) {
if (doc.HasMember(ATC_KEY_JOBID)) {
_testID = doc[ATC_KEY_JOBID].GetString();
} else {
_testID = "jobId is not set!";
}
if (doc.HasMember(ATC_KEY_PLATFORMS)) {
rapidjson::Value &platforms = doc[ATC_KEY_PLATFORMS];
if (!platforms.IsArray() || platforms.Size() < 1) {
_clientID = "array platforms is empty";
} else {
rapidjson::Value &plt = platforms[0];
if (!plt.HasMember(ATC_KEY_PLATFORM_INDEX)) {
_clientID = "platforms[0] not key platformIndex";
} else {
rapidjson::Value &index = plt[ATC_KEY_PLATFORM_INDEX];
_clientID = index.IsInt() ? std::to_string(index.GetInt()) : index.GetString();
}
}
} else {
_clientID = "platforms not set!";
}
// parse ip & port
rapidjson::Value &cfg = doc[ATC_KEY_CONFIG];
if (!cfg.HasMember(ATC_KEY_IP) || !cfg.HasMember(ATC_KEY_PORT)) {
_status = UdpLogClientState::DONE;
return;
}
const char *remoteIp = cfg[ATC_KEY_IP].GetString();
// The `PORT` property is used by other service and the next port is for log collection.
int remotePort = cfg[ATC_KEY_PORT].GetInt() + 1;
setServerAddr(remoteIp, remotePort);
} else {
_status = UdpLogClientState::DONE;
return;
}
_bootID = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
_status = UdpLogClientState::CONFIGURED;
}
void setServerAddr(const std::string_view &addr, int port) {
memset(&_serverAddr, 0, sizeof(_serverAddr));
_serverAddr.sin_family = AF_INET;
_serverAddr.sin_addr.s_addr = inet_addr(addr.data());
_serverAddr.sin_port = htons(port);
}
static auto getErrorCode() {
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
return WSAGetLastError();
#else
return errno;
#endif
}
void ensureInitSocket() {
if (_status == UdpLogClientState::INITIALIZED) {
tryParseConfig();
}
if (_status != UdpLogClientState::CONFIGURED) {
return;
}
memset(&_localAddr, 0, sizeof(_localAddr));
_localAddr.sin_family = AF_INET;
_localAddr.sin_addr.s_addr = INADDR_ANY;
_localAddr.sin_port = 0; // bind random port
if ((_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
auto errorCode = getErrorCode();
_status = UdpLogClientState::DONE;
printf("create socket failed, code: %d\n", errorCode);
return;
}
if (bind(_sock, reinterpret_cast<sockaddr *>(&_localAddr), sizeof(_localAddr))) {
_status = UdpLogClientState::DONE;
auto errorCode = getErrorCode();
printf("bind socket failed, code: %d\n", errorCode);
return;
}
if (connect(_sock, reinterpret_cast<sockaddr *>(&_serverAddr), sizeof(_serverAddr))) {
auto errorCode = getErrorCode();
printf("connect socket failed, code: %d\n", errorCode);
_status = UdpLogClientState::DONE;
return;
}
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
u_long mode = 1;
if (ioctlsocket(_sock, FIONBIO, &mode)) {
auto errorCode = getErrorCode();
printf("set nonblock failed, code: %d\n", errorCode);
// continue
}
#else
int flags = fcntl(_sock, F_GETFL);
if (fcntl(_sock, F_SETFL, flags | O_NONBLOCK)) {
auto errorCode = getErrorCode();
printf("set nonblock failed, code: %d\n", errorCode);
// continue
}
#endif
_status = UdpLogClientState::OK;
}
void sendLog(const std::string_view &msg) {
ensureInitSocket();
if (_status == UdpLogClientState::OK) {
send(_sock, msg.data(), msg.size(), 0);
}
}
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
SOCKET _sock{INVALID_SOCKET};
#else
int _sock{-1};
#endif
sockaddr_in _serverAddr;
sockaddr_in _localAddr;
UdpLogClientState _status{UdpLogClientState::UNINITIALIZED};
std::string _testID;
std::string _clientID;
uint64_t _bootID{0};
};
void sendLogThroughUDP(const std::string_view &msg) {
static UdpLogClient remote;
remote.sendFullLog(msg);
}
} // namespace
#endif
namespace cc {
void Log::logRemote(const char *msg) {
#if CC_REMOTE_LOG
sendLogThroughUDP(msg);
#endif
}
} // namespace cc

411
cocos/base/Macros.h Normal file
View File

@@ -0,0 +1,411 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2011 Zynga Inc.
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstdint> // To include uint8_t, uint16_t and so on.
#include "Assertf.h"
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
#if defined(CC_STATIC)
#define CC_DLL
#else
#if defined(_USRDLL)
#define CC_DLL __declspec(dllexport)
#else /* use a DLL library */
#define CC_DLL __declspec(dllimport)
#endif
#endif
#else
#define CC_DLL
#endif
/** @def CC_DEGREES_TO_RADIANS
converts degrees to radians
*/
#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__)*0.01745329252f) // PI / 180
/** @def CC_RADIANS_TO_DEGREES
converts radians to degrees
*/
#define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__)*57.29577951f) // PI * 180
#ifndef FLT_EPSILON
#define FLT_EPSILON 1.192092896e-07F
#endif // FLT_EPSILON
/**
Helper macros which converts 4-byte little/big endian
integral number to the machine native number representation
It should work same as apples CFSwapInt32LittleToHost(..)
*/
/// when define returns true it means that our architecture uses big endian
#define CC_HOST_IS_BIG_ENDIAN (bool)(*(unsigned short *)"\0\xff" < 0x100)
#define CC_SWAP32(i) ((i & 0x000000ff) << 24 | (i & 0x0000ff00) << 8 | (i & 0x00ff0000) >> 8 | (i & 0xff000000) >> 24)
#define CC_SWAP16(i) ((i & 0x00ff) << 8 | (i & 0xff00) >> 8)
#define CC_SWAP_INT32_LITTLE_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? CC_SWAP32(i) : (i))
#define CC_SWAP_INT16_LITTLE_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? CC_SWAP16(i) : (i))
#define CC_SWAP_INT32_BIG_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? (i) : CC_SWAP32(i))
#define CC_SWAP_INT16_BIG_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? (i) : CC_SWAP16(i))
// new callbacks based on C++11
#define CC_CALLBACK_0(__selector__, __target__, ...) std::bind(&__selector__, __target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__, __target__, ...) std::bind(&__selector__, __target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__, __target__, ...) std::bind(&__selector__, __target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__, __target__, ...) std::bind(&__selector__, __target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
// Generic macros
#define CC_BREAK_IF(cond) \
if (cond) break
/** @def CC_DEPRECATED_ATTRIBUTE
* Only certain compilers support __attribute__((deprecated)).
*/
#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
#define CC_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
#elif _MSC_VER >= 1400 // vs 2005 or higher
#define CC_DEPRECATED_ATTRIBUTE __declspec(deprecated)
#else
#define CC_DEPRECATED_ATTRIBUTE
#endif
/** @def CC_DEPRECATED(...)
* Macro to mark things deprecated as of a particular version
* can be used with arbitrary parameters which are thrown away.
* e.g. CC_DEPRECATED(4.0) or CC_DEPRECATED(4.0, "not going to need this anymore") etc.
*/
#define CC_DEPRECATED(...) CC_DEPRECATED_ATTRIBUTE
#ifdef __GNUC__
#define CC_UNUSED __attribute__((unused))
#else
#define CC_UNUSED
#endif
#define CC_UNUSED_PARAM(unusedparam) (void)unusedparam
/** @def CC_FORMAT_PRINTF(formatPos, argPos)
* Only certain compiler support __attribute__((format))
*
* @param formatPos 1-based position of format string argument.
* @param argPos 1-based position of first format-dependent argument.
*/
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos)))
#elif defined(__has_attribute)
#if __has_attribute(format)
#define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos)))
#else
#define CC_FORMAT_PRINTF(formatPos, argPos)
#endif // __has_attribute(format)
#else
#define CC_FORMAT_PRINTF(formatPos, argPos)
#endif
// Initial compiler-related stuff to set.
#define CC_COMPILER_MSVC 1
#define CC_COMPILER_CLANG 2
#define CC_COMPILER_GNUC 3
// CPU Architecture
#define CC_CPU_UNKNOWN 0
#define CC_CPU_X86 1
#define CC_CPU_PPC 2
#define CC_CPU_ARM 3
#define CC_CPU_MIPS 4
// 32-bits or 64-bits CPU
#define CC_CPU_ARCH_32 1
#define CC_CPU_ARCH_64 2
// Mode
#define CC_MODE_DEBUG 1
#define CC_MODE_RELEASE 2
// Compiler type and version recognition
#if defined(_MSC_VER)
#define CC_COMPILER CC_COMPILER_MSVC
#elif defined(__clang__)
#define CC_COMPILER CC_COMPILER_CLANG
#elif defined(__GNUC__)
#define CC_COMPILER CC_COMPILER_GNUC
#else
#error "Unknown compiler. Abort!"
#endif
#if INTPTR_MAX == INT32_MAX
#define CC_CPU_ARCH CC_CPU_ARCH_32
#else
#define CC_CPU_ARCH CC_CPU_ARCH_64
#endif
#if defined(__arm64__) || defined(__aarch64__)
#define CC_ARCH_ARM64 1
#else
#define CC_ARCH_ARM64 0
#endif
// CC_HAS_ARM64_FP16 set to 1 if the architecture provides an IEEE compliant ARM fp16 type
#if CC_ARCH_ARM64
#ifndef CC_HAS_ARM64_FP16
#if defined(__ARM_FP16_FORMAT_IEEE)
#define CC_HAS_ARM64_FP16 1
#else
#define CC_HAS_ARM64_FP16 0
#endif
#endif
#endif
// CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC set to 1 if the architecture supports Neon vector intrinsics for fp16.
#if CC_ARCH_ARM64
#ifndef CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC
#if defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)
#define CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC 1
#else
#define CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC 0
#endif
#endif
#endif
// CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC set to 1 if the architecture supports Neon scalar intrinsics for fp16.
#if CC_ARCH_ARM64
#ifndef CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC
#if defined(__ARM_FEATURE_FP16_SCALAR_ARITHMETIC)
#define CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC 1
#else
#define CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC 0
#endif
#endif
#endif
// Disable MSVC warning
#if (CC_COMPILER == CC_COMPILER_MSVC)
#pragma warning(disable : 4251 4275 4819)
#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE
#endif
#ifndef _SCL_SECURE_NO_DEPRECATE
#define _SCL_SECURE_NO_DEPRECATE
#endif
#endif
#define CC_CACHELINE_SIZE 64
#if (CC_COMPILER == CC_COMPILER_MSVC)
// MSVC ENABLE/DISABLE WARNING DEFINITION
#define CC_DISABLE_WARNINGS() \
__pragma(warning(push, 0))
#define CC_ENABLE_WARNINGS() \
__pragma(warning(pop))
#elif (CC_COMPILER == CC_COMPILER_GNUC)
// GCC ENABLE/DISABLE WARNING DEFINITION
#define CC_DISABLE_WARNINGS() \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wall\"") \
_Pragma("clang diagnostic ignored \"-Wextra\"") \
_Pragma("clang diagnostic ignored \"-Wtautological-compare\"")
#define CC_ENABLE_WARNINGS() \
_Pragma("GCC diagnostic pop")
#elif (CC_COMPILER == CC_COMPILER_CLANG)
// CLANG ENABLE/DISABLE WARNING DEFINITION
#define CC_DISABLE_WARNINGS() \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wall\"") \
_Pragma("clang diagnostic ignored \"-Wextra\"") \
_Pragma("clang diagnostic ignored \"-Wtautological-compare\"")
#define CC_ENABLE_WARNINGS() \
_Pragma("clang diagnostic pop")
#endif
#define CC_DISALLOW_ASSIGN(TypeName) \
TypeName &operator=(const TypeName &) = delete; \
TypeName &operator=(TypeName &&) = delete;
#define CC_DISALLOW_COPY_MOVE_ASSIGN(TypeName) \
TypeName(const TypeName &) = delete; \
TypeName(TypeName &&) = delete; \
CC_DISALLOW_ASSIGN(TypeName)
#if (CC_COMPILER == CC_COMPILER_MSVC)
#define CC_ALIGN(N) __declspec(align(N))
#define CC_CACHE_ALIGN __declspec(align(CC_CACHELINE_SIZE))
#define CC_PACKED_ALIGN(N) __declspec(align(N))
#define CC_ALIGNED_DECL(type, var, alignment) __declspec(align(alignment)) type var
#define CC_READ_COMPILER_BARRIER() _ReadBarrier()
#define CC_WRITE_COMPILER_BARRIER() _WriteBarrier()
#define CC_COMPILER_BARRIER() _ReadWriteBarrier()
#define CC_READ_MEMORY_BARRIER() MemoryBarrier()
#define CC_WRITE_MEMORY_BARRIER() MemoryBarrier()
#define CC_MEMORY_BARRIER() MemoryBarrier()
#define CC_CPU_READ_MEMORY_BARRIER() \
do { \
__asm { lfence} \
} while (0)
#define CC_CPU_WRITE_MEMORY_BARRIER() \
do { \
__asm { sfence} \
} while (0)
#define CC_CPU_MEMORY_BARRIER() \
do { \
__asm { mfence} \
} while (0)
#elif (CC_COMPILER == CC_COMPILER_GNUC) || (CC_COMPILER == CC_COMPILER_CLANG)
#define CC_ALIGN(N) __attribute__((__aligned__((N))))
#define CC_CACHE_ALIGN __attribute__((__aligned__((CC_CACHELINE_SIZE))))
#define CC_PACKED_ALIGN(N) __attribute__((packed, aligned(N)))
#define CC_ALIGNED_DECL(type, var, alignment) type var __attribute__((__aligned__(alignment)))
#define CC_READ_COMPILER_BARRIER() \
do { \
__asm__ __volatile__("" \
: \
: \
: "memory"); \
} while (0)
#define CC_WRITE_COMPILER_BARRIER() \
do { \
__asm__ __volatile__("" \
: \
: \
: "memory"); \
} while (0)
#define CC_COMPILER_BARRIER() \
do { \
__asm__ __volatile__("" \
: \
: \
: "memory"); \
} while (0)
#define CC_READ_MEMORY_BARRIER() \
do { \
__sync_synchronize(); \
} while (0)
#define CC_WRITE_MEMORY_BARRIER() \
do { \
__sync_synchronize(); \
} while (0)
#define CC_MEMORY_BARRIER() \
do { \
__sync_synchronize(); \
} while (0)
#define CC_CPU_READ_MEMORY_BARRIER() \
do { \
__asm__ __volatile__("lfence" \
: \
: \
: "memory"); \
} while (0)
#define CC_CPU_WRITE_MEMORY_BARRIER() \
do { \
__asm__ __volatile__("sfence" \
: \
: \
: "memory"); \
} while (0)
#define CC_CPU_MEMORY_BARRIER() \
do { \
__asm__ __volatile__("mfence" \
: \
: \
: "memory"); \
} while (0)
#else
#error "Unsupported compiler!"
#endif
/* Stack-alignment
If macro __CC_SIMD_ALIGN_STACK defined, means there requests
special code to ensure stack align to a 16-bytes boundary.
Note:
This macro can only guarantee callee stack pointer (esp) align
to a 16-bytes boundary, but not that for frame pointer (ebp).
Because most compiler might use frame pointer to access to stack
variables, so you need to wrap those alignment required functions
with extra function call.
*/
#if defined(__INTEL_COMPILER)
// For intel's compiler, simply calling alloca seems to do the right
// thing. The size of the allocated block seems to be irrelevant.
#define CC_SIMD_ALIGN_STACK() _alloca(16)
#define CC_SIMD_ALIGN_ATTRIBUTE
#elif (CC_CPU == CC_CPU_X86) && (CC_COMPILER == CC_COMPILER_GNUC || CC_COMPILER == CC_COMPILER_CLANG) && (CC_CPU_ARCH != CC_CPU_ARCH_64)
// mark functions with GCC attribute to force stack alignment to 16 bytes
#define CC_SIMD_ALIGN_ATTRIBUTE __attribute__((force_align_arg_pointer))
#elif (CC_COMPILER == CC_COMPILER_MSVC)
// Fortunately, MSVC will align the stack automatically
#define CC_SIMD_ALIGN_ATTRIBUTE
#else
#define CC_SIMD_ALIGN_ATTRIBUTE
#endif
// mode
#if (defined(_DEBUG) || defined(DEBUG)) && (CC_PLATFORM == CC_PLATFORM_WINDOWS)
#define CC_MODE CC_MODE_DEBUG
#else
#define CC_MODE CC_MODE_RELEASE
#endif
#define CC_TOSTR(s) #s
#if defined(__GNUC__) && __GNUC__ >= 4
#define CC_PREDICT_TRUE(x) __builtin_expect(!!(x), 1)
#define CC_PREDICT_FALSE(x) __builtin_expect(!!(x), 0)
#else
#define CC_PREDICT_TRUE(x) (x)
#define CC_PREDICT_FALSE(x) (x)
#endif
#if defined(_MSC_VER)
#define CC_FORCE_INLINE __forceinline
#elif defined(__GNUC__) || defined(__clang__)
#define CC_FORCE_INLINE inline __attribute__((always_inline))
#else
#if defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
#define CC_FORCE_INLINE static inline
#elif
#define CC_FORCE_INLINE inline
#endif
#endif

35
cocos/base/Object.h Executable file
View File

@@ -0,0 +1,35 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
namespace cc {
class Object {
public:
Object() = default;
virtual ~Object() = default;
};
} // namespace cc

226
cocos/base/Ptr.h Normal file
View File

@@ -0,0 +1,226 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
// Originally the class is from WebRTC.
// https://chromium.googlesource.com/external/webrtc/+/master/api/scoped_refptr.h
//
// A smart pointer class for reference counted objects. Use this class instead
// of calling addRef and Release manually on a reference counted object to
// avoid common memory leaks caused by forgetting to Release an object
// reference. Sample usage:
//
// class MyFoo : public RefCounted<MyFoo> {
// ...
// };
//
// void some_function() {
// IntrusivePtr<MyFoo> foo = ccnew MyFoo();
// foo->Method(param);
// // `foo` is released when this function returns
// }
//
// void some_other_function() {
// IntrusivePtr<MyFoo> foo = ccnew MyFoo();
// ...
// foo = nullptr; // explicitly releases `foo`
// ...
// if (foo)
// foo->Method(param);
// }
//
// The above examples show how IntrusivePtr<T> acts like a pointer to T.
// Given two IntrusivePtr<T> classes, it is also possible to exchange
// references between the two objects, like so:
//
// {
// IntrusivePtr<MyFoo> a = ccnew MyFoo();
// IntrusivePtr<MyFoo> b;
//
// b.swap(a);
// // now, `b` references the MyFoo object, and `a` references null.
// }
//
// To make both `a` and `b` in the above example reference the same MyFoo
// object, simply use the assignment operator:
//
// {
// IntrusivePtr<MyFoo> a = ccnew MyFoo();
// IntrusivePtr<MyFoo> b;
//
// b = a;
// // now, `a` and `b` each own a reference to the same MyFoo object.
// }
//
#pragma once
#include <utility>
#include "cocos/base/std/hash/hash_fwd.hpp"
namespace cc {
template <class T>
class IntrusivePtr {
public:
using element_type = T;
IntrusivePtr() {
}
IntrusivePtr(T *p) : _ptr(p) { // NOLINT
if (_ptr) {
_ptr->addRef();
}
}
IntrusivePtr(const IntrusivePtr<T> &r) : _ptr(r._ptr) {
if (_ptr) {
_ptr->addRef();
}
}
template <typename U>
IntrusivePtr(const IntrusivePtr<U> &r) : _ptr(r.get()) { // NOLINT
if (_ptr) {
_ptr->addRef();
}
}
// Move constructors.
IntrusivePtr(IntrusivePtr<T> &&r) noexcept : _ptr(r.release()) {
}
template <typename U>
IntrusivePtr(IntrusivePtr<U> &&r) noexcept : _ptr(r.release()) { // NOLINT
}
~IntrusivePtr() {
if (_ptr) {
_ptr->release();
}
}
T *get() const { return _ptr; }
operator T *() const { return _ptr; } // NOLINT
T &operator*() const { return *_ptr; }
T *operator->() const { return _ptr; }
// As reference count is 1 after creating a RefCounted object, so do not have to
// invoke p->addRef();
IntrusivePtr<T> &operator=(T *p) {
reset(p);
return *this;
}
IntrusivePtr<T> &operator=(const IntrusivePtr<T> &r) { // NOLINT
return *this = r._ptr; // NOLINT
}
template <typename U>
IntrusivePtr<T> &operator=(const IntrusivePtr<U> &r) {
return *this = r.get(); // NOLINT
}
IntrusivePtr<T> &operator=(IntrusivePtr<T> &&r) noexcept {
IntrusivePtr<T>(std::move(r)).swap(*this);
return *this;
}
template <typename U>
IntrusivePtr<T> &operator=(IntrusivePtr<U> &&r) noexcept {
IntrusivePtr<T>(std::move(r)).swap(*this);
return *this;
}
bool operator==(std::nullptr_t) {
return _ptr == nullptr;
}
bool operator==(T *r) {
return _ptr == r;
}
bool operator!=(std::nullptr_t) {
return _ptr != nullptr;
}
bool operator!=(T *r) {
return _ptr != r;
}
void reset() noexcept {
if (_ptr) {
_ptr->release();
}
_ptr = nullptr;
}
void reset(T *p) {
// AddRef first so that self assignment should work
if (p) {
p->addRef();
}
if (_ptr) {
_ptr->release();
}
_ptr = p;
}
void swap(T **pp) noexcept {
T *p = _ptr;
_ptr = *pp;
*pp = p;
}
void swap(IntrusivePtr<T> &r) noexcept { swap(&r._ptr); }
private:
// Returns the (possibly null) raw pointer, and makes the scoped_refptr hold a
// null pointer, all without touching the reference count of the underlying
// pointed-to object. The object is still reference counted, and the caller of
// release() is now the proud owner of one reference, so it is responsible for
// calling Release() once on the object when no longer using it.
T *release() {
T *retVal = _ptr;
_ptr = nullptr;
return retVal;
}
protected:
T *_ptr{nullptr};
};
} // namespace cc
namespace ccstd {
template <class T>
struct hash<cc::IntrusivePtr<T>> {
hash_t operator()(const cc::IntrusivePtr<T> &val) const noexcept {
return hash<T *>{}(val.get());
}
};
} // namespace ccstd

122
cocos/base/Random.h Normal file
View File

@@ -0,0 +1,122 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstdlib>
#include <random>
#include "base/Macros.h"
namespace cc {
/**
* @class RandomHelper
* @brief A helper class for creating random number.
*/
class CC_DLL RandomHelper {
public:
template <typename T>
static inline T randomReal(T min, T max) {
std::uniform_real_distribution<T> dist(min, max);
auto &mt = RandomHelper::getEngine();
return dist(mt);
}
template <typename T>
static inline T randomInt(T min, T max) {
std::uniform_int_distribution<T> dist(min, max);
auto &mt = RandomHelper::getEngine();
return dist(mt);
}
private:
static inline std::mt19937 &getEngine() {
static std::random_device seedGen;
static std::mt19937 engine(seedGen());
return engine;
}
};
/**
* Returns a random value between `min` and `max`.
*/
template <typename T>
inline T random(T min, T max) {
return RandomHelper::randomInt<T>(min, max);
}
template <>
inline float random(float min, float max) {
return RandomHelper::randomReal(min, max);
}
template <>
inline long double random(long double min, long double max) {
return RandomHelper::randomReal(min, max);
}
template <>
inline double random(double min, double max) {
return RandomHelper::randomReal(min, max);
}
/**
* Returns a random int between 0 and RAND_MAX.
*/
inline int random() {
return cc::random(0, static_cast<int>(RAND_MAX));
};
/**
* Returns a random float between -1 and 1.
* It can be seeded using std::srand(seed);
*/
inline float randMinus1_1() {
// IDEA: using the new c++11 random engine generator
// without a proper way to set a seed is not useful.
// Resorting to the old random method since it can
// be seeded using std::srand()
return ((std::rand() / (float)RAND_MAX) * 2) - 1;
// return cc::random(-1.f, 1.f);
};
/**
* Returns a random float between 0 and 1.
* It can be seeded using std::srand(seed);
*/
inline float rand0_1() {
// IDEA: using the new c++11 random engine generator
// without a proper way to set a seed is not useful.
// Resorting to the old random method since it can
// be seeded using std::srand()
return std::rand() / (float)RAND_MAX;
// return cc::random(0.f, 1.f);
};
} // namespace cc

109
cocos/base/RefCounted.cpp Normal file
View File

@@ -0,0 +1,109 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/RefCounted.h"
#if CC_REF_LEAK_DETECTION
#include <algorithm> // std::find
#include "base/Log.h"
#include "base/std/container/list.h"
#endif
namespace cc {
#if CC_REF_LEAK_DETECTION
static void trackRef(RefCounted *ref);
static void untrackRef(RefCounted *ref);
#endif
RefCounted::RefCounted() { // NOLINT(modernize-use-equals-default)
#if CC_REF_LEAK_DETECTION
trackRef(this);
#endif
}
RefCounted::~RefCounted() { // NOLINT(modernize-use-equals-default)
#if CC_REF_LEAK_DETECTION
untrackRef(this);
#endif
}
void RefCounted::addRef() {
++_referenceCount;
}
void RefCounted::release() {
CC_ASSERT_GT(_referenceCount, 0);
--_referenceCount;
if (_referenceCount == 0) {
delete this;
}
}
unsigned int RefCounted::getRefCount() const {
return _referenceCount;
}
#if CC_REF_LEAK_DETECTION
static ccstd::list<RefCounted *> __refAllocationList;
void RefCounted::printLeaks() {
// Dump Ref object memory leaks
if (__refAllocationList.empty()) {
CC_LOG_INFO("[memory] All Ref objects successfully cleaned up (no leaks detected).\n");
} else {
CC_LOG_INFO("[memory] WARNING: %d Ref objects still active in memory.\n", (int)__refAllocationList.size());
for (const auto &ref : __refAllocationList) {
CC_ASSERT(ref);
const char *type = typeid(*ref).name();
CC_LOG_INFO("[memory] LEAK: Ref object '%s' still active with reference count %d.\n", (type ? type : ""), ref->getRefCount());
}
}
}
static void trackRef(RefCounted *ref) {
CC_ASSERT(ref);
// Create memory allocation record.
__refAllocationList.push_back(ref);
}
static void untrackRef(RefCounted *ref) {
auto iter = std::find(__refAllocationList.begin(), __refAllocationList.end(), ref);
if (iter == __refAllocationList.end()) {
CC_LOG_INFO("[memory] CORRUPTION: Attempting to free (%s) with invalid ref tracking record.\n", typeid(*ref).name());
return;
}
__refAllocationList.erase(iter);
}
#endif // #if CC_REF_LEAK_DETECTION
} // namespace cc

105
cocos/base/RefCounted.h Normal file
View File

@@ -0,0 +1,105 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#define CC_REF_LEAK_DETECTION 0
namespace cc {
class RefCounted;
/**
* Interface that defines how to clone an Ref.
*/
class CC_DLL Clonable {
public:
/** Returns a copy of the Ref. */
virtual Clonable *clone() const = 0;
virtual ~Clonable() = default;
};
/**
* Ref is used for reference count management. If a class inherits from Ref,
* then it is easy to be shared in different places.
*/
class CC_DLL RefCounted {
public:
virtual ~RefCounted();
/**
* Retains the ownership.
*
* This increases the Ref's reference count.
*
* @see release, autorelease
*/
void addRef();
/**
* Releases the ownership immediately.
*
* This decrements the Ref's reference count.
*
* If the reference count reaches 0 after the decrement, this Ref is
* destructed.
*
* @see retain, autorelease
*/
void release();
/**
* Returns the Ref's current reference count.
*
* @returns The Ref's reference count.
*/
unsigned int getRefCount() const;
protected:
/**
* Constructor
*
* The Ref's reference count is 1 after construction.
*/
RefCounted();
/// count of references
unsigned int _referenceCount{0};
// Memory leak diagnostic data (only included when CC_REF_LEAK_DETECTION is defined and its value isn't zero)
#if CC_REF_LEAK_DETECTION
public:
static void printLeaks();
#endif
};
using SCHEDULE_CB = void (RefCounted::*)(float);
#define CC_SCHEDULE_CALLBACK(cb) static_cast<cc::SCHEDULE_CB>(&cb)
} // namespace cc

309
cocos/base/RefMap.h Normal file
View File

@@ -0,0 +1,309 @@
/****************************************************************************
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Log.h"
#include "base/Random.h"
#include "base/RefCounted.h"
#include "base/std/container/unordered_map.h"
#include "base/std/container/vector.h"
namespace cc {
/**
* Similar to ccstd::unordered_map, but it will manage reference count automatically internally.
* Which means it will invoke RefCounted::addRef() when adding an element, and invoke RefCounted::release() when removing an element.
* @warning The element should be `RefCounted` or its sub-class.
*/
template <class K, class V>
class RefMap {
public:
// ------------------------------------------
// Iterators
// ------------------------------------------
/** Iterator, can be used to loop the Map. */
using iterator = typename ccstd::unordered_map<K, V>::iterator;
/** Const iterator, can be used to loop the Map. */
using const_iterator = typename ccstd::unordered_map<K, V>::const_iterator;
/** Return iterator to beginning. */
iterator begin() { return _data.begin(); }
/** Return const_iterator to beginning. */
const_iterator begin() const { return _data.begin(); }
/** Return iterator to end.*/
iterator end() { return _data.end(); }
/** Return const_iterator to end.*/
const_iterator end() const { return _data.end(); }
/** Return const_iterator to beginning.*/
const_iterator cbegin() const { return _data.cbegin(); }
/** Return const_iterator to end.*/
const_iterator cend() const { return _data.cend(); }
/** Default constructor */
RefMap<K, V>() {
static_assert(std::is_convertible<V, RefCounted *>::value, "Invalid Type for cc::Map<K, V>!");
}
/** Constructor with capacity. */
explicit RefMap<K, V>(uint32_t capacity) {
static_assert(std::is_convertible<V, RefCounted *>::value, "Invalid Type for cc::Map<K, V>!");
_data.reserve(capacity);
}
/** Copy constructor. */
RefMap<K, V>(const RefMap<K, V> &other) {
static_assert(std::is_convertible<V, RefCounted *>::value, "Invalid Type for cc::Map<K, V>!");
_data = other._data;
addRefForAllObjects();
}
/** Move constructor. */
RefMap<K, V>(RefMap<K, V> &&other) noexcept {
static_assert(std::is_convertible<V, RefCounted *>::value, "Invalid Type for cc::Map<K, V>!");
_data = std::move(other._data);
}
/**
* Destructor.
* It will release all objects in map.
*/
~RefMap<K, V>() {
clear();
}
/** Sets capacity of the map. */
void reserve(uint32_t capacity) {
_data.reserve(capacity);
}
/** Returns the number of buckets in the Map container. */
uint32_t bucketCount() const {
return static_cast<uint32_t>(_data.bucket_count());
}
/** Returns the number of elements in bucket n. */
uint32_t bucketSize(uint32_t n) const {
return static_cast<uint32_t>(_data.bucket_size(n));
}
/** Returns the bucket number where the element with key k is located. */
uint32_t bucket(const K &k) const {
return _data.bucket(k);
}
/** The number of elements in the map. */
uint32_t size() const {
return static_cast<uint32_t>(_data.size());
}
/**
* Returns a bool value indicating whether the map container is empty, i.e. whether its size is 0.
* @note This function does not modify the content of the container in any way.
* To clear the content of an array object, member function unordered_map::clear exists.
*/
bool empty() const {
return _data.empty();
}
/** Returns all keys in the map. */
ccstd::vector<K> keys() const {
ccstd::vector<K> keys;
if (!_data.empty()) {
keys.reserve(_data.size());
for (const auto &element : _data) {
keys.push_back(element.first);
}
}
return keys;
}
/** Returns all keys that matches the object. */
ccstd::vector<K> keys(V object) const {
ccstd::vector<K> keys;
if (!_data.empty()) {
keys.reserve(_data.size() / 10);
for (const auto &element : _data) {
if (element.second == object) {
keys.push_back(element.first);
}
}
}
keys.shrink_to_fit();
return keys;
}
/**
* Returns a reference to the mapped value of the element with key k in the map.
*
* @note If key does not match the key of any element in the container, the function return nullptr.
* @param key Key value of the element whose mapped value is accessed.
* Member type K is the keys for the elements in the container. defined in Map<K, V> as an alias of its first template parameter (Key).
*/
V at(const K &key) {
auto iter = _data.find(key);
if (iter != _data.end()) {
return iter->second;
}
return nullptr;
}
/**
* Searches the container for an element with 'key' as key and returns an iterator to it if found,
* otherwise it returns an iterator to Map<K, V>::end (the element past the end of the container).
*
* @param key Key to be searched for.
* Member type 'K' is the type of the keys for the elements in the container,
* defined in Map<K, V> as an alias of its first template parameter (Key).
*/
const_iterator find(const K &key) const {
return _data.find(key);
}
iterator find(const K &key) {
return _data.find(key);
}
/**
* Inserts new elements in the map.
*
* @note If the container has already contained the key, this function will erase the old pair(key, object) and insert the new pair.
* @param key The key to be inserted.
* @param object The object to be inserted.
*/
void insert(const K &key, V object) {
CC_ASSERT_NOT_NULL(object);
object->addRef();
erase(key);
_data.insert(std::make_pair(key, object));
}
/**
* Removes an element with an iterator from the Map<K, V> container.
*
* @param position Iterator pointing to a single element to be removed from the Map<K, V>.
* Member type const_iterator is a forward iterator type.
*/
iterator erase(const_iterator position) {
CC_ASSERT(position != _data.cend());
position->second->release();
return _data.erase(position);
}
/**
* Removes an element with an iterator from the Map<K, V> container.
*
* @param k Key of the element to be erased.
* Member type 'K' is the type of the keys for the elements in the container,
* defined in Map<K, V> as an alias of its first template parameter (Key).
*/
size_t erase(const K &k) {
auto iter = _data.find(k);
if (iter != _data.end()) {
iter->second->release();
_data.erase(iter);
return 1;
}
return 0;
}
/**
* Removes some elements with a vector which contains keys in the map.
*
* @param keys Keys of elements to be erased.
*/
void erase(const ccstd::vector<K> &keys) {
for (const auto &key : keys) {
this->erase(key);
}
}
/**
* All the elements in the Map<K,V> container are dropped:
* their reference count will be decreased, and they are removed from the container,
* leaving it with a size of 0.
*/
void clear() {
for (const auto &element : _data) {
element.second->release();
}
_data.clear();
}
/**
* Gets a random object in the map.
* @return Returns the random object if the map isn't empty, otherwise it returns nullptr.
*/
V getRandomObject() const {
if (!_data.empty()) {
auto randIdx = RandomHelper::randomInt<int>(0, static_cast<int>(_data.size()) - 1);
const_iterator randIter = _data.begin();
std::advance(randIter, randIdx);
return randIter->second;
}
return nullptr;
}
/** Copy assignment operator. */
RefMap<K, V> &operator=(const RefMap<K, V> &other) {
if (this != &other) {
clear();
_data = other._data;
addRefForAllObjects();
}
return *this;
}
/** Move assignment operator. */
RefMap<K, V> &operator=(RefMap<K, V> &&other) noexcept {
if (this != &other) {
clear();
_data = std::move(other._data);
}
return *this;
}
private:
/** Retains all the objects in the map */
void addRefForAllObjects() {
for (const auto &element : _data) {
element.second->addRef();
}
}
ccstd::unordered_map<K, V> _data;
};
} // namespace cc

523
cocos/base/RefVector.h Normal file
View File

@@ -0,0 +1,523 @@
/****************************************************************************
Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <sys/types.h>
#include <algorithm> // for std::find
#include <cstdint>
#include <functional>
#include "base/Log.h"
#include "base/Random.h"
#include "base/RefCounted.h"
#include "base/memory/Memory.h"
#include "base/std/container/vector.h"
namespace cc {
/*
* Similar to ccstd::vector, but it will manage reference count automatically internally.
* Which means it will invoke RefCounted::addRef() when adding an element, and invoke RefCounted::release() when removing an element.
* @warn The element should be `RefCounted` or its sub-class.
*/
template <class T>
class RefVector {
public:
// ------------------------------------------
// Iterators
// ------------------------------------------
/** Iterator, can be used to loop the Vector. */
using iterator = typename ccstd::vector<T>::iterator;
/** Const iterator, can be used to loop the Vector. */
using const_iterator = typename ccstd::vector<T>::const_iterator;
/** Reversed iterator, can be used to loop the Vector in reverse sequence. */
using reverse_iterator = typename ccstd::vector<T>::reverse_iterator;
/** Reversed iterator, can be used to loop the Vector in reverse sequence. */
using const_reverse_iterator = typename ccstd::vector<T>::const_reverse_iterator;
/** Returns an iterator pointing the first element of the Vector. */
iterator begin() { return _data.begin(); }
/** Returns an iterator pointing the first element of the Vector. */
const_iterator begin() const { return _data.begin(); }
/**
* Returns an iterator referring to the `past-the-end` element in the Vector container.
* The past-the-end element is the theoretical element that would follow the last element in the Vector.
* It does not point to any element, and thus shall not be dereferenced.
*/
iterator end() { return _data.end(); }
/**
* Returns iterator referring to the `past-the-end` element in the Vector container.
* The past-the-end element is the theoretical element that would follow the last element in the Vector.
* It does not point to any element, and thus shall not be dereferenced.
*/
const_iterator end() const { return _data.end(); }
/** Returns a const_iterator pointing the first element of the Vector. */
const_iterator cbegin() const { return _data.cbegin(); }
/** Returns a const_iterator pointing the `past-the-end` element of the Vector. */
const_iterator cend() const { return _data.cend(); }
/** Returns a reverse iterator pointing to the last element of the Vector. */
reverse_iterator rbegin() { return _data.rbegin(); }
/** Returns a reverse iterator pointing to the last element of the Vector. */
const_reverse_iterator rbegin() const { return _data.rbegin(); }
/** Returns a reverse iterator pointing to the theoretical element preceding the
* first element of the vector (which is considered its reverse end).
*/
reverse_iterator rend() { return _data.rend(); }
/** Returns a reverse iterator pointing to the theoretical element preceding the
* first element of the vector (which is considered its reverse end).
*/
const_reverse_iterator rend() const { return _data.rend(); }
/** Returns a const_reverse_iterator pointing to the last element in the container (i.e., its reverse beginning). */
const_reverse_iterator crbegin() const { return _data.crbegin(); }
/** Returns a const_reverse_iterator pointing to the theoretical element preceding the first element in
* the container (which is considered its reverse end).
*/
const_reverse_iterator crend() const { return _data.crend(); }
/** Constructor. */
RefVector<T>()
: _data() {
static_assert(std::is_convertible<T, RefCounted *>::value, "Invalid Type for cc::Vector<T>!");
}
/**
* Constructor with a capacity.
* @param capacity Capacity of the Vector.
*/
explicit RefVector<T>(uint32_t capacity)
: _data() {
static_assert(std::is_convertible<T, RefCounted *>::value, "Invalid Type for cc::Vector<T>!");
// CC_LOG_INFO("In the default constructor with capacity of Vector.");
reserve(capacity);
}
/** Constructor with initializer list. */
RefVector<T>(std::initializer_list<T> list) {
for (auto &element : list) {
pushBack(element);
}
}
/** Destructor. */
~RefVector<T>() {
// CC_LOG_INFO("In the destructor of Vector.");
clear();
}
/** Copy constructor. */
RefVector<T>(const RefVector<T> &other) {
static_assert(std::is_convertible<T, RefCounted *>::value, "Invalid Type for cc::Vector<T>!");
// CC_LOG_INFO("In the copy constructor!");
_data = other._data;
addRefForAllObjects();
}
/** Copy constructor. */
explicit RefVector<T>(const ccstd::vector<T> &other) {
static_assert(std::is_convertible<T, RefCounted *>::value, "Invalid Type for cc::Vector<T>!");
// CC_LOG_INFO("In the copy constructor!");
_data = other;
addRefForAllObjects();
}
/** Constructor with std::move semantic. */
RefVector<T>(RefVector<T> &&other) noexcept {
static_assert(std::is_convertible<T, RefCounted *>::value, "Invalid Type for cc::Vector<T>!");
// CC_LOG_INFO("In the move constructor of Vector!");
_data = std::move(other._data);
}
/** Constructor with std::move semantic. */
explicit RefVector<T>(ccstd::vector<T> &&other) noexcept {
static_assert(std::is_convertible<T, RefCounted *>::value, "Invalid Type for cc::Vector<T>!");
// CC_LOG_INFO("In the move constructor of Vector!");
_data = std::move(other);
// NOTE: The reference count of T in ccstd::vector may be 0 so we need to retain all elements in ccstd::vector.
addRefForAllObjects();
}
/** Copy assignment operator. */
RefVector<T> &operator=(const RefVector<T> &other) {
if (this != &other) {
// CC_LOG_INFO("In the copy assignment operator!");
clear();
_data = other._data;
addRefForAllObjects();
}
return *this;
}
RefVector<T> &operator=(const ccstd::vector<T> &other) {
static_assert(std::is_convertible<T, RefCounted *>::value, "Invalid Type for cc::Vector<T>!");
if (&_data != &other) {
// CC_LOG_INFO("In the copy assign operator!");
clear();
_data = other;
addRefForAllObjects();
}
return *this;
}
/** Move assignment operator with std::move semantic. */
RefVector<T> &operator=(RefVector<T> &&other) noexcept {
if (this != &other) {
// CC_LOG_INFO("In the move assignment operator!");
clear();
_data = std::move(other._data);
}
return *this;
}
RefVector<T> &operator=(ccstd::vector<T> &&other) noexcept {
if (&_data != &other) {
// CC_LOG_INFO("In the move assignment operator!");
clear();
_data = std::move(other);
// NOTE: The reference count of T in ccstd::vector may be 0 so we need to retain all elements in ccstd::vector.
addRefForAllObjects();
}
return *this;
}
RefVector<T> &operator=(std::initializer_list<T> list) {
clear();
for (auto &element : list) {
pushBack(element);
}
return *this;
}
// Can not return reference, or it can use like this
// refVector[i] = val;
// Then, reference count will be wrong. In order to correct reference count, should:
// - dec refVector[i] reference count
// - add `val` reference count.
// It is hard to use, so delete it.
T &operator[](uint32_t idx) = delete;
// As non const version is disabled, disable const version too.
const T &operator[](uint32_t idx) const = delete;
/**
* Requests that the vector capacity be at least enough to contain n elements.
* @param capacity Minimum capacity requested of the Vector.
*/
void reserve(uint32_t n) {
_data.reserve(n);
}
/** @brief Returns the size of the storage space currently allocated for the Vector, expressed in terms of elements.
* @note This capacity is not necessarily equal to the Vector size.
* It can be equal or greater, with the extra space allowing to accommodate for growth without the need to reallocate on each insertion.
* @return The size of the currently allocated storage capacity in the Vector, measured in terms of the number elements it can hold.
*/
uint32_t capacity() const {
return _data.capacity();
}
/** @brief Returns the number of elements in the Vector.
* @note This is the number of actual objects held in the Vector, which is not necessarily equal to its storage capacity.
* @return The number of elements in the Vector.
*/
uint32_t size() const {
return static_cast<uint32_t>(_data.size());
}
/** @brief Returns whether the Vector is empty (i.e. whether its size is 0).
* @note This function does not modify the container in any way. To clear the content of a vector, see Vector<T>::clear.
*/
bool empty() const {
return _data.empty();
}
/** Returns the maximum number of elements that the Vector can hold. */
uint32_t maxSize() const {
return _data.max_size();
}
/** Returns index of a certain object, return UINT_MAX if doesn't contain the object */
uint32_t getIndex(T object) const {
auto iter = std::find(_data.begin(), _data.end(), object);
if (iter != _data.end()) {
return iter - _data.begin();
}
return -1;
}
/** @brief Find the object in the Vector.
* @param object The object to find.
* @return Returns an iterator which refers to the element that its value is equals to object.
* Returns Vector::end() if not found.
*/
const_iterator find(T object) const {
return std::find(_data.begin(), _data.end(), object);
}
/** @brief Find the object in the Vector.
* @param object The object to find.
* @return Returns an iterator which refers to the element that its value is equals to object.
* Returns Vector::end() if not found.
*/
iterator find(T object) {
return std::find(_data.begin(), _data.end(), object);
}
/** Returns the element at position 'index' in the Vector. */
const T &at(uint32_t index) const {
CC_ASSERT(index < size());
return _data[index];
}
T &at(uint32_t index) {
CC_ASSERT(index < size());
return _data[index];
}
/** Returns the first element in the Vector. */
T front() const {
return _data.front();
}
/** Returns the last element of the Vector. */
T back() const {
return _data.back();
}
/** Returns a random element of the Vector. */
T getRandomObject() const {
if (!_data.empty()) {
auto randIdx = RandomHelper::randomInt<int>(0, static_cast<int>(_data.size()) - 1);
return *(_data.begin() + randIdx);
}
return nullptr;
}
/**
* Checks whether an object is in the container.
* @param object The object to be checked.
* @return True if the object is in the container, false if not.
*/
bool contains(T object) const {
return (std::find(_data.begin(), _data.end(), object) != _data.end());
}
/**
* Checks whether two vectors are equal.
* @param other The vector to be compared.
* @return True if two vectors are equal, false if not.
*/
bool equals(const RefVector<T> &other) const {
uint32_t s = this->size();
if (s != other.size()) {
return false;
}
for (uint32_t i = 0; i < s; i++) {
if (this->at(i) != other.at(i)) {
return false;
}
}
return true;
}
// Adds objects
/** Adds a new element at the end of the Vector. */
void pushBack(T object) {
CC_ASSERT_NOT_NULL(object);
_data.push_back(object);
object->addRef();
}
/** Push all elements of an existing Vector to the end of current Vector. */
void pushBack(const RefVector<T> &other) {
for (const auto &obj : other) {
_data.push_back(obj);
obj->addRef();
}
}
/**
* Insert an object at certain index.
* @param index The index to be inserted at.
* @param object The object to be inserted.
*/
void insert(uint32_t index, T object) {
CC_ASSERT(index <= size());
CC_ASSERT_NOT_NULL(object);
_data.insert((std::begin(_data) + index), object);
object->addRef();
}
// Removes Objects
/** Removes the last element in the Vector. */
void popBack() {
CC_ASSERT(!_data.empty());
auto last = _data.back();
_data.pop_back();
last->release();
}
/** Remove a certain object in Vector.
* @param object The object to be removed.
* @param removeAll Whether to remove all elements with the same value.
* If its value is 'false', it will just erase the first occurrence.
*/
void eraseObject(T object, bool removeAll = false) {
CC_ASSERT_NOT_NULL(object);
if (removeAll) {
for (auto iter = _data.begin(); iter != _data.end();) {
if ((*iter) == object) {
iter = _data.erase(iter);
object->release();
} else {
++iter;
}
}
} else {
auto iter = std::find(_data.begin(), _data.end(), object);
if (iter != _data.end()) {
_data.erase(iter);
object->release();
}
}
}
/** @brief Removes from the vector with an iterator.
* @param position Iterator pointing to a single element to be removed from the Vector.
* @return An iterator pointing to the new location of the element that followed the last element erased by the function call.
* This is the container end if the operation erased the last element in the sequence.
*/
iterator erase(iterator position) {
CC_ASSERT(position >= _data.begin() && position < _data.end());
(*position)->release();
return _data.erase(position);
}
/** @brief Removes from the Vector with a range of elements ( [first, last) ).
* @param first The beginning of the range.
* @param last The end of the range, the 'last' will not be removed, it's only for indicating the end of range.
* @return An iterator pointing to the new location of the element that followed the last element erased by the function call.
* This is the container end if the operation erased the last element in the sequence.
*/
iterator erase(iterator first, iterator last) {
for (auto iter = first; iter != last; ++iter) {
(*iter)->release();
}
return _data.erase(first, last);
}
/** @brief Removes from the Vector by index.
* @param index The index of the element to be removed from the Vector.
* @return An iterator pointing to the successor of Vector[index].
*/
iterator erase(uint32_t index) {
CC_ASSERT(!_data.empty() && index < size());
auto it = std::next(begin(), index);
(*it)->release();
return _data.erase(it);
}
/** @brief Removes all elements from the Vector (which are destroyed), leaving the container with a size of 0.
* @note All the elements in the Vector will be released (reference count will be decreased).
*/
void clear() {
for (auto it = std::begin(_data); it != std::end(_data); ++it) {
auto *ptr = *it;
if (ptr) {
ptr->release();
}
}
_data.clear();
}
// Rearranging Content
/** Swap the values object1 and object2. */
void swap(T object1, T object2) {
uint32_t idx1 = getIndex(object1);
uint32_t idx2 = getIndex(object2);
std::swap(_data[idx1], _data[idx2]);
}
/** Swap two elements by indexes. */
void swap(uint32_t index1, uint32_t index2) {
CC_ASSERT(index1 < size() && index2 < size());
std::swap(_data[index1], _data[index2]);
}
/** Replace value at index with given object. */
void replace(uint32_t index, T object) {
CC_ASSERT(index < size());
CC_ASSERT_NOT_NULL(object);
CC_SAFE_RELEASE(_data[index]);
_data[index] = object;
CC_SAFE_ADD_REF(object);
}
/** Reverses the Vector. */
void reverse() {
std::reverse(std::begin(_data), std::end(_data));
}
/** Requests the container to reduce its capacity to fit its size. */
void shrinkToFit() {
_data.shrink_to_fit();
}
const ccstd::vector<T> &get() const {
return _data;
}
void resize(uint32_t size) {
_data.resize(size);
}
protected:
/** Retains all the objects in the vector */
void addRefForAllObjects() {
for (const auto &obj : _data) {
obj->addRef();
}
}
ccstd::vector<T> _data;
};
} // namespace cc

398
cocos/base/Scheduler.cpp Normal file
View File

@@ -0,0 +1,398 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2011 Zynga Inc.
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/Scheduler.h"
#include <algorithm>
#include <climits>
#include "base/Log.h"
#include "base/Macros.h"
#include "base/memory/Memory.h"
namespace {
constexpr unsigned CC_REPEAT_FOREVER{UINT_MAX - 1};
constexpr int MAX_FUNC_TO_PERFORM{30};
constexpr int INITIAL_TIMER_COUND{10};
} // namespace
namespace cc {
// implementation Timer
void Timer::setupTimerWithInterval(float seconds, unsigned int repeat, float delay) {
_elapsed = -1;
_interval = seconds;
_delay = delay;
_useDelay = _delay > 0.0F;
_repeat = repeat;
_runForever = _repeat == CC_REPEAT_FOREVER;
}
void Timer::update(float dt) {
if (_elapsed == -1) {
_elapsed = 0;
_timesExecuted = 0;
return;
}
// accumulate elapsed time
_elapsed += dt;
// deal with delay
if (_useDelay) {
if (_elapsed < _delay) {
return;
}
trigger(_delay);
_elapsed = _elapsed - _delay;
_timesExecuted += 1;
_useDelay = false;
// after delay, the rest time should compare with interval
if (!_runForever && _timesExecuted > _repeat) { //unschedule timer
cancel();
return;
}
}
// if _interval == 0, should trigger once every frame
float interval = (_interval > 0) ? _interval : _elapsed;
while (_elapsed >= interval) {
trigger(interval);
_elapsed -= interval;
_timesExecuted += 1;
if (!_runForever && _timesExecuted > _repeat) {
cancel();
break;
}
if (_elapsed <= 0.F) {
break;
}
if (_scheduler->isCurrentTargetSalvaged()) {
break;
}
}
}
// TimerTargetCallback
bool TimerTargetCallback::initWithCallback(Scheduler *scheduler, const ccSchedulerFunc &callback, void *target, const ccstd::string &key, float seconds, unsigned int repeat, float delay) {
_scheduler = scheduler;
_target = target;
_callback = callback;
_key = key;
setupTimerWithInterval(seconds, repeat, delay);
return true;
}
void TimerTargetCallback::trigger(float dt) {
if (_callback) {
_callback(dt);
}
}
void TimerTargetCallback::cancel() {
_scheduler->unschedule(_key, _target);
}
// implementation of Scheduler
Scheduler::Scheduler() {
// I don't expect to have more than 30 functions to all per frame
_functionsToPerform.reserve(MAX_FUNC_TO_PERFORM);
}
Scheduler::~Scheduler() {
unscheduleAll();
}
void Scheduler::removeHashElement(HashTimerEntry *element) {
if (element) {
for (auto &timer : element->timers) {
timer->release();
}
element->timers.clear();
_hashForTimers.erase(element->target);
delete element;
}
}
void Scheduler::schedule(const ccSchedulerFunc &callback, void *target, float interval, bool paused, const ccstd::string &key) {
this->schedule(callback, target, interval, CC_REPEAT_FOREVER, 0.0F, paused, key);
}
void Scheduler::schedule(const ccSchedulerFunc &callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const ccstd::string &key) {
CC_ASSERT(target);
CC_ASSERT(!key.empty());
auto iter = _hashForTimers.find(target);
HashTimerEntry *element = nullptr;
if (iter == _hashForTimers.end()) {
element = ccnew HashTimerEntry();
element->target = target;
_hashForTimers[target] = element;
// Is this the 1st element ? Then set the pause level to all the selectors of this target
element->paused = paused;
} else {
element = iter->second;
CC_ASSERT(element->paused == paused);
}
if (element->timers.empty()) {
element->timers.reserve(INITIAL_TIMER_COUND);
} else {
for (auto &e : element->timers) {
auto *timer = dynamic_cast<TimerTargetCallback *>(e);
if (key == timer->getKey()) {
CC_LOG_DEBUG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
timer->setInterval(interval);
return;
}
}
}
auto *timer = ccnew TimerTargetCallback();
timer->addRef();
timer->initWithCallback(this, callback, target, key, interval, repeat, delay);
element->timers.emplace_back(timer);
}
void Scheduler::unschedule(const ccstd::string &key, void *target) {
// explicit handle nil arguments when removing an object
if (target == nullptr || key.empty()) {
return;
}
auto iter = _hashForTimers.find(target);
if (iter != _hashForTimers.end()) {
HashTimerEntry *element = iter->second;
int i = 0;
auto &timers = element->timers;
for (auto *t : timers) {
auto *timer = dynamic_cast<TimerTargetCallback *>(t);
if (timer && key == timer->getKey()) {
if (timer == element->currentTimer && (!element->currentTimerSalvaged)) {
element->currentTimer->addRef();
element->currentTimerSalvaged = true;
}
timers.erase(timers.begin() + i);
timer->release();
// update timerIndex in case we are in tick:, looping over the actions
if (element->timerIndex >= i) {
element->timerIndex--;
}
if (timers.empty()) {
if (_currentTarget == element) {
_currentTargetSalvaged = true;
} else {
removeHashElement(element);
}
}
return;
}
++i;
}
}
}
bool Scheduler::isScheduled(const ccstd::string &key, void *target) {
CC_ASSERT(!key.empty());
CC_ASSERT(target);
auto iter = _hashForTimers.find(target);
if (iter == _hashForTimers.end()) {
return false;
}
HashTimerEntry *element = iter->second;
if (element->timers.empty()) {
return false;
}
const auto &timers = element->timers;
return std::any_of(timers.begin(), timers.end(), [&key](Timer *t) {
auto *timer = dynamic_cast<TimerTargetCallback *>(t);
return (timer && key == timer->getKey());
});
}
void Scheduler::unscheduleAll() {
for (auto iter = _hashForTimers.begin(); iter != _hashForTimers.end();) {
unscheduleAllForTarget(iter++->first);
}
}
void Scheduler::unscheduleAllForTarget(void *target) {
// explicit nullptr handling
if (target == nullptr) {
return;
}
// Custom Selectors
auto iter = _hashForTimers.find(target);
if (iter != _hashForTimers.end()) {
HashTimerEntry *element = iter->second;
auto &timers = element->timers;
if (std::find(timers.begin(), timers.end(), element->currentTimer) != timers.end() &&
(!element->currentTimerSalvaged)) {
element->currentTimer->addRef();
element->currentTimerSalvaged = true;
}
for (auto *t : timers) {
t->release();
}
timers.clear();
if (_currentTarget == element) {
_currentTargetSalvaged = true;
} else {
removeHashElement(element);
}
}
}
void Scheduler::resumeTarget(void *target) {
CC_ASSERT_NOT_NULL(target);
// custom selectors
auto iter = _hashForTimers.find(target);
if (iter != _hashForTimers.end()) {
iter->second->paused = false;
}
}
void Scheduler::pauseTarget(void *target) {
CC_ASSERT_NOT_NULL(target);
// custom selectors
auto iter = _hashForTimers.find(target);
if (iter != _hashForTimers.end()) {
iter->second->paused = true;
}
}
bool Scheduler::isTargetPaused(void *target) {
CC_ASSERT_NOT_NULL(target);
// Custom selectors
auto iter = _hashForTimers.find(target);
if (iter != _hashForTimers.end()) {
return iter->second->paused;
}
return false; // should never get here
}
void Scheduler::performFunctionInCocosThread(const std::function<void()> &function) {
_performMutex.lock();
_functionsToPerform.push_back(function);
_performMutex.unlock();
}
void Scheduler::removeAllFunctionsToBePerformedInCocosThread() {
std::unique_lock<std::mutex> lock(_performMutex);
_functionsToPerform.clear();
}
// main loop
void Scheduler::update(float dt) {
_updateHashLocked = true;
// Iterate over all the custom selectors
HashTimerEntry *elt = nullptr;
for (auto iter = _hashForTimers.begin(); iter != _hashForTimers.end();) {
elt = iter->second;
_currentTarget = elt;
_currentTargetSalvaged = false;
if (!_currentTarget->paused) {
// The 'timers' array may change while inside this loop
for (elt->timerIndex = 0; elt->timerIndex < static_cast<int>(elt->timers.size()); ++(elt->timerIndex)) {
elt->currentTimer = elt->timers.at(elt->timerIndex);
elt->currentTimerSalvaged = false;
elt->currentTimer->update(dt);
if (elt->currentTimerSalvaged) {
// The currentTimer told the remove itself. To prevent the timer from
// accidentally deallocating itself before finishing its step, we retained
// it. Now that step is done, it's safe to release it.
elt->currentTimer->release();
}
elt->currentTimer = nullptr;
}
}
// only delete currentTarget if no actions were scheduled during the cycle (issue #481)
if (_currentTargetSalvaged && _currentTarget->timers.empty()) {
++iter;
removeHashElement(_currentTarget);
if (iter != _hashForTimers.end()) {
++iter;
}
} else {
++iter;
}
}
_updateHashLocked = false;
_currentTarget = nullptr;
//
// Functions allocated from another thread
//
// Testing size is faster than locking / unlocking.
// And almost never there will be functions scheduled to be called.
if (!_functionsToPerform.empty()) {
_performMutex.lock();
// fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
auto temp = _functionsToPerform;
_functionsToPerform.clear();
_performMutex.unlock();
for (const auto &function : temp) {
function();
}
}
}
} // namespace cc

305
cocos/base/Scheduler.h Normal file
View File

@@ -0,0 +1,305 @@
/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2011 Zynga Inc.
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <functional>
#include <mutex>
#include "base/RefCounted.h"
#include "base/std/container/set.h"
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "base/std/container/vector.h"
namespace cc {
class Scheduler;
using ccSchedulerFunc = std::function<void(float)>;
/**
* @cond
*/
class CC_DLL Timer : public RefCounted {
public:
/** get interval in seconds */
inline float getInterval() const { return _interval; };
/** set interval in seconds */
inline void setInterval(float interval) { _interval = interval; };
void setupTimerWithInterval(float seconds, unsigned int repeat, float delay);
virtual void trigger(float dt) = 0;
virtual void cancel() = 0;
/** triggers the timer */
void update(float dt);
protected:
Timer() = default;
Scheduler *_scheduler = nullptr;
float _elapsed = 0.F;
bool _runForever = false;
bool _useDelay = false;
unsigned int _timesExecuted = 0;
unsigned int _repeat = 0; //0 = once, 1 is 2 x executed
float _delay = 0.F;
float _interval = 0.F;
};
class CC_DLL TimerTargetCallback final : public Timer {
public:
TimerTargetCallback() = default;
// Initializes a timer with a target, a lambda and an interval in seconds, repeat in number of times to repeat, delay in seconds.
bool initWithCallback(Scheduler *scheduler, const ccSchedulerFunc &callback, void *target, const ccstd::string &key, float seconds, unsigned int repeat, float delay);
inline const ccSchedulerFunc &getCallback() const { return _callback; };
inline const ccstd::string &getKey() const { return _key; };
void trigger(float dt) override;
void cancel() override;
private:
void *_target = nullptr;
ccSchedulerFunc _callback = nullptr;
ccstd::string _key;
};
/**
* @endcond
*/
/**
* @addtogroup base
* @{
*/
struct _listEntry;
struct _hashSelectorEntry;
struct _hashUpdateEntry;
/** @brief Scheduler is responsible for triggering the scheduled callbacks.
You should not use system timer for your game logic. Instead, use this class.
There are 2 different types of callbacks (selectors):
- update selector: the 'update' selector will be called every frame. You can customize the priority.
- custom selector: A custom selector will be called every frame, or with a custom interval of time
The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'.
*/
class CC_DLL Scheduler final {
public:
/**
* Constructor
*
* @js ctor
*/
Scheduler();
/**
* Destructor
*
* @js NA
* @lua NA
*/
~Scheduler();
/** 'update' the scheduler.
* You should NEVER call this method, unless you know what you are doing.
* @lua NA
*/
void update(float dt);
/////////////////////////////////////
// schedule
/** The scheduled method will be called every 'interval' seconds.
If paused is true, then it won't be called until it is resumed.
If 'interval' is 0, it will be called every frame, but if so, it's recommended to use 'scheduleUpdate' instead.
If the 'callback' is already scheduled, then only the interval parameter will be updated without re-scheduling it again.
repeat let the action be repeated repeat + 1 times, use CC_REPEAT_FOREVER to let the action run continuously
delay is the amount of time the action will wait before it'll start.
@param callback The callback function.
@param target The target of the callback function.
@param interval The interval to schedule the callback. If the value is 0, then the callback will be scheduled every frame.
@param repeat repeat+1 times to schedule the callback.
@param delay Schedule call back after `delay` seconds. If the value is not 0, the first schedule will happen after `delay` seconds.
But it will only affect first schedule. After first schedule, the delay time is determined by `interval`.
@param paused Whether or not to pause the schedule.
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
@since v3.0
*/
void schedule(const ccSchedulerFunc &callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const ccstd::string &key);
/** The scheduled method will be called every 'interval' seconds for ever.
@param callback The callback function.
@param target The target of the callback function.
@param interval The interval to schedule the callback. If the value is 0, then the callback will be scheduled every frame.
@param paused Whether or not to pause the schedule.
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
@since v3.0
*/
void schedule(const ccSchedulerFunc &callback, void *target, float interval, bool paused, const ccstd::string &key);
/////////////////////////////////////
// unschedule
/** Unschedules a callback for a key and a given target.
If you want to unschedule the 'callbackPerFrame', use unscheduleUpdate.
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
@param target The target to be unscheduled.
@since v3.0
*/
void unschedule(const ccstd::string &key, void *target);
/** Unschedules all selectors for a given target.
This also includes the "update" selector.
@param target The target to be unscheduled.
@since v0.99.3
@lua NA
*/
void unscheduleAllForTarget(void *target);
/** Unschedules all selectors from all targets.
You should NEVER call this method, unless you know what you are doing.
@since v0.99.3
*/
void unscheduleAll();
/** Unschedules all selectors from all targets with a minimum priority.
You should only call this with `PRIORITY_NON_SYSTEM_MIN` or higher.
@param minPriority The minimum priority of selector to be unscheduled. Which means, all selectors which
priority is higher than minPriority will be unscheduled.
@since v2.0.0
*/
void unscheduleAllWithMinPriority(int minPriority);
/////////////////////////////////////
// isScheduled
/** Checks whether a callback associated with 'key' and 'target' is scheduled.
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
@param target The target of the callback.
@return True if the specified callback is invoked, false if not.
@since v3.0.0
*/
bool isScheduled(const ccstd::string &key, void *target);
/////////////////////////////////////
/** Pauses the target.
All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.
If the target is not present, nothing happens.
@param target The target to be paused.
@since v0.99.3
*/
void pauseTarget(void *target);
/** Resumes the target.
The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.
If the target is not present, nothing happens.
@param target The target to be resumed.
@since v0.99.3
*/
void resumeTarget(void *target);
/** Returns whether or not the target is paused.
* @param target The target to be checked.
* @return True if the target is paused, false if not.
* @since v1.0.0
* @lua NA
*/
bool isTargetPaused(void *target);
/** Pause all selectors from all targets with a minimum priority.
You should only call this with PRIORITY_NON_SYSTEM_MIN or higher.
@param minPriority The minimum priority of selector to be paused. Which means, all selectors which
priority is higher than minPriority will be paused.
@since v2.0.0
*/
ccstd::set<void *> pauseAllTargetsWithMinPriority(int minPriority);
/** Calls a function on the cocos2d thread. Useful when you need to call a cocos2d function from another thread.
This function is thread safe.
@param function The function to be run in cocos2d thread.
@since v3.0
@js NA
*/
void performFunctionInCocosThread(const std::function<void()> &function);
/**
* Remove all pending functions queued to be performed with Scheduler::performFunctionInCocosThread
* Functions unscheduled in this manner will not be executed
* This function is thread safe
* @since v3.14
* @js NA
*/
void removeAllFunctionsToBePerformedInCocosThread();
bool isCurrentTargetSalvaged() const { return _currentTargetSalvaged; };
private:
// Hash Element used for "selectors with interval"
struct HashTimerEntry {
ccstd::vector<Timer *> timers;
void *target;
int timerIndex;
Timer *currentTimer;
bool currentTimerSalvaged;
bool paused;
};
void removeHashElement(struct HashTimerEntry *element);
void removeUpdateFromHash(struct _listEntry *entry);
// update specific
// Used for "selectors with interval"
ccstd::unordered_map<void *, HashTimerEntry *> _hashForTimers;
struct HashTimerEntry *_currentTarget = nullptr;
bool _currentTargetSalvaged = false;
// If true unschedule will not remove anything from a hash. Elements will only be marked for deletion.
bool _updateHashLocked = false;
// Used for "perform Function"
ccstd::vector<std::function<void()>> _functionsToPerform;
std::mutex _performMutex;
};
// end of base group
/** @} */
} // namespace cc

View File

@@ -0,0 +1,34 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "StringHandle.h"
namespace cc {
StringHandle::StringHandle(IndexType handle, const char *str) noexcept
: IndexHandle(handle),
_str(str) {
}
} // namespace cc

41
cocos/base/StringHandle.h Normal file
View File

@@ -0,0 +1,41 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "IndexHandle.h"
namespace cc {
class StringHandle final : public IndexHandle<uint32_t> {
public:
StringHandle() noexcept = default;
explicit StringHandle(IndexType handle, const char *str) noexcept;
inline char const *str() const noexcept { return _str; }
private:
char const *_str{nullptr};
};
} // namespace cc

144
cocos/base/StringPool.h Normal file
View File

@@ -0,0 +1,144 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstring>
#include "StringHandle.h"
#include "base/Macros.h"
#include "base/memory/Memory.h"
#include "base/std/container/unordered_map.h"
#include "base/std/container/vector.h"
#include "base/std/hash/hash.h"
#include "threading/ReadWriteLock.h"
namespace cc {
template <bool ThreadSafe>
class StringPool final {
private:
class StringHasher final {
public:
ccstd::hash_t operator()(const char *str) const noexcept {
return ccstd::hash_range(str, str + strlen(str));
}
};
class StringEqual final {
public:
bool operator()(const char *c1, const char *c2) const noexcept {
return strcmp(c1, c2) == 0;
}
};
public:
StringPool() = default;
~StringPool();
StringPool(const StringPool &) = delete;
StringPool(StringPool &&) = delete;
StringPool &operator=(const StringPool &) = delete;
StringPool &operator=(StringPool &&) = delete;
StringHandle stringToHandle(const char *str) noexcept;
char const *handleToString(const StringHandle &handle) const noexcept;
StringHandle find(const char *str) const noexcept;
private:
StringHandle doStringToHandle(const char *str) noexcept;
char const *doHandleToString(const StringHandle &handle) const noexcept;
StringHandle doFind(const char *str) const noexcept;
ccstd::unordered_map<char const *, StringHandle, StringHasher, StringEqual> _stringToHandles{};
ccstd::vector<char const *> _handleToStrings{};
mutable ReadWriteLock _readWriteLock{};
};
using ThreadSafeStringPool = StringPool<true>;
template <bool ThreadSafe>
StringPool<ThreadSafe>::~StringPool() {
for (char const *strCache : _handleToStrings) {
delete[] strCache;
}
}
template <bool ThreadSafe>
inline StringHandle StringPool<ThreadSafe>::stringToHandle(const char *str) noexcept {
if (ThreadSafe) {
return _readWriteLock.lockWrite([this, str]() {
return doStringToHandle(str);
});
}
return doStringToHandle(str);
}
template <bool ThreadSafe>
inline char const *StringPool<ThreadSafe>::handleToString(const StringHandle &handle) const noexcept {
if (ThreadSafe) {
return _readWriteLock.lockRead([this, handle]() {
return doHandleToString(handle);
});
}
return doHandleToString(handle);
}
template <bool ThreadSafe>
StringHandle StringPool<ThreadSafe>::find(const char *str) const noexcept {
if (ThreadSafe) {
return _readWriteLock.lockRead([this, str]() {
return doFind(str);
});
}
return doFind(str);
}
template <bool ThreadSafe>
inline StringHandle StringPool<ThreadSafe>::doStringToHandle(const char *str) noexcept {
const auto it = _stringToHandles.find(str);
if (it == _stringToHandles.end()) {
size_t const strLength = strlen(str) + 1;
char *const strCache = ccnew char[strLength];
strcpy(strCache, str);
StringHandle name(static_cast<StringHandle::IndexType>(_handleToStrings.size()), strCache);
_handleToStrings.emplace_back(strCache);
_stringToHandles.emplace(strCache, name);
return name;
}
return it->second;
}
template <bool ThreadSafe>
inline const char *StringPool<ThreadSafe>::doHandleToString(const StringHandle &handle) const noexcept {
CC_ASSERT(handle < _handleToStrings.size());
return _handleToStrings[handle];
}
template <bool ThreadSafe>
StringHandle StringPool<ThreadSafe>::doFind(char const *str) const noexcept {
auto const it = _stringToHandles.find(str);
return it == _stringToHandles.end() ? StringHandle{} : it->second;
}
} // namespace cc

182
cocos/base/StringUtil.cpp Normal file
View File

@@ -0,0 +1,182 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "StringUtil.h"
#include <algorithm>
#include <cctype>
#include <cstdarg>
#ifndef CC_WGPU_WASM
#include "base/ZipUtils.h"
#endif
#include "base/base64.h"
#include "base/std/container/string.h"
#include "memory/Memory.h"
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#endif
namespace cc {
#if defined(_WIN32)
int StringUtil::vprintf(char *buf, const char *last, const char *fmt, va_list args) {
if (last <= buf) return 0;
int count = (int)(last - buf);
int ret = _vsnprintf_s(buf, count, _TRUNCATE, fmt, args);
if (ret < 0) {
if (errno == 0) {
return count - 1;
} else {
return 0;
}
} else {
return ret;
}
}
#else
int StringUtil::vprintf(char *buf, const char *last, const char *fmt, va_list args) {
if (last <= buf) {
return 0;
}
auto count = static_cast<int>(last - buf);
int ret = vsnprintf(buf, count, fmt, args);
if (ret >= count - 1) {
return count - 1;
}
if (ret < 0) {
return 0;
}
return ret;
}
#endif
int StringUtil::printf(char *buf, const char *last, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int ret = vprintf(buf, last, fmt, args);
va_end(args);
return ret;
}
ccstd::string StringUtil::format(const char *fmt, ...) {
char sz[4096];
va_list args;
va_start(args, fmt);
vprintf(sz, sz + sizeof(sz) - 1, fmt, args);
va_end(args);
return sz;
}
ccstd::vector<ccstd::string> StringUtil::split(const ccstd::string &str, const ccstd::string &delims, uint32_t maxSplits) {
ccstd::vector<ccstd::string> strs;
if (str.empty()) {
return strs;
}
// Pre-allocate some space for performance
strs.reserve(maxSplits ? maxSplits + 1 : 16); // 16 is guessed capacity for most case
uint32_t numSplits{0};
// Use STL methods
size_t start{0};
size_t pos{0};
do {
pos = str.find_first_of(delims, start);
if (pos == start) {
// Do nothing
start = pos + 1;
} else if (pos == ccstd::string::npos || (maxSplits && numSplits == maxSplits)) {
// Copy the rest of the string
strs.push_back(str.substr(start));
break;
} else {
// Copy up to delimiter
strs.push_back(str.substr(start, pos - start));
start = pos + 1;
}
// parse up to next real data
start = str.find_first_not_of(delims, start);
++numSplits;
} while (pos != ccstd::string::npos);
return strs;
}
ccstd::string &StringUtil::replace(ccstd::string &str, const ccstd::string &findStr, const ccstd::string &replaceStr) {
size_t startPos = str.find(findStr);
if (startPos == ccstd::string::npos) {
return str;
}
str.replace(startPos, findStr.length(), replaceStr);
return str;
}
ccstd::string &StringUtil::replaceAll(ccstd::string &str, const ccstd::string &findStr, const ccstd::string &replaceStr) {
if (findStr.empty()) {
return str;
}
size_t startPos = 0;
while ((startPos = str.find(findStr, startPos)) != ccstd::string::npos) {
str.replace(startPos, findStr.length(), replaceStr);
startPos += replaceStr.length();
}
return str;
}
ccstd::string &StringUtil::tolower(ccstd::string &str) {
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c) { return std::tolower(c); });
return str;
}
ccstd::string &StringUtil::toupper(ccstd::string &str) {
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c) { return std::toupper(c); });
return str;
}
ccstd::string GzipedString::value() const { // NOLINT(readability-convert-member-functions-to-static)
#ifndef CC_WGPU_WASM
uint8_t *outGzip{nullptr};
uint8_t *outBase64{nullptr};
auto *input = reinterpret_cast<unsigned char *>(const_cast<char *>(_str.c_str()));
auto lenOfBase64 = base64Decode(input, static_cast<unsigned int>(_str.size()), &outBase64);
auto lenofUnzip = ZipUtils::inflateMemory(outBase64, static_cast<uint32_t>(lenOfBase64), &outGzip);
ccstd::string ret(outGzip, outGzip + lenofUnzip);
free(outGzip);
free(outBase64);
return ret;
#else
return "";
#endif
}
} // namespace cc

78
cocos/base/StringUtil.h Normal file
View File

@@ -0,0 +1,78 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstdarg>
#include <cstddef>
#include "Macros.h"
#include "base/std/container/string.h"
#include "base/std/container/vector.h"
namespace cc {
class CC_DLL StringUtil {
public:
static int vprintf(char *buf, const char *last, const char *fmt, va_list args);
static int printf(char *buf, const char *last, const char *fmt, ...);
static ccstd::string format(const char *fmt, ...);
static ccstd::vector<ccstd::string> split(const ccstd::string &str, const ccstd::string &delims, uint32_t maxSplits = 0);
static ccstd::string &replace(ccstd::string &str, const ccstd::string &findStr, const ccstd::string &replaceStr);
static ccstd::string &replaceAll(ccstd::string &str, const ccstd::string &findStr, const ccstd::string &replaceStr);
static ccstd::string &tolower(ccstd::string &str);
static ccstd::string &toupper(ccstd::string &str);
};
/**
* Store compressed text which compressed with gzip & base64
* fetch plain-text with `value()`.
*/
class CC_DLL GzipedString {
public:
explicit GzipedString(ccstd::string &&dat) : _str(dat) {}
explicit GzipedString(const char *dat) : _str(dat) {}
GzipedString(const GzipedString &o) = default;
GzipedString &operator=(const GzipedString &d) = default;
GzipedString(GzipedString &&o) noexcept {
_str = std::move(o._str);
}
GzipedString &operator=(ccstd::string &&d) {
_str = std::move(d);
return *this;
}
GzipedString &operator=(GzipedString &&d) noexcept {
_str = std::move(d._str);
return *this;
}
/**
* return text decompress with base64decode | un-gzip
*/
ccstd::string value() const;
private:
ccstd::string _str{};
};
} // namespace cc

View File

@@ -0,0 +1,47 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
namespace cc {
/* overloaded is used in ccstd::visit a variant value. For example:
ccstd::variant<ccstd::monostate, int, bool, float> value;
ccstd::visit(cc::overloaded{
[](auto& v) {
// Do something with v
},
[](ccstd::monostate&) {} // Do nothing if value isn't initialized
}, value);
*/
// https://stackoverflow.com/questions/69915380/what-does-templateclass-ts-struct-overloaded-ts-using-tsoperator
// https://en.cppreference.com/w/cpp/language/class_template_argument_deduction
template <class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
} // namespace cc

380
cocos/base/ThreadPool.cpp Normal file
View File

@@ -0,0 +1,380 @@
/****************************************************************************
Copyright (c) 2016-2017 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/ThreadPool.h"
#include <chrono>
#include <memory>
#include "base/memory/Memory.h"
#include "platform/StdC.h"
#ifdef __ANDROID__
#include <android/log.h>
#define LOG_TAG "ThreadPool"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#else
#define LOGD(...) printf(__VA_ARGS__)
#endif
#define TIME_MINUS(now, prev) (std::chrono::duration_cast<std::chrono::milliseconds>((now) - (prev)).count() / 1000.0f)
namespace cc {
#define DEFAULT_THREAD_POOL_MIN_NUM (4)
#define DEFAULT_THREAD_POOL_MAX_NUM (20)
#define DEFAULT_SHRINK_INTERVAL (5)
#define DEFAULT_SHRINK_STEP (2)
#define DEFAULT_STRETCH_STEP (2)
LegacyThreadPool *LegacyThreadPool::_instance = nullptr;
LegacyThreadPool *LegacyThreadPool::getDefaultThreadPool() {
if (LegacyThreadPool::_instance == nullptr) {
LegacyThreadPool::_instance = newCachedThreadPool(DEFAULT_THREAD_POOL_MIN_NUM,
DEFAULT_THREAD_POOL_MAX_NUM,
DEFAULT_SHRINK_INTERVAL, DEFAULT_SHRINK_STEP,
DEFAULT_STRETCH_STEP);
}
return LegacyThreadPool::_instance;
}
void LegacyThreadPool::destroyDefaultThreadPool() {
delete LegacyThreadPool::_instance;
LegacyThreadPool::_instance = nullptr;
}
LegacyThreadPool *LegacyThreadPool::newCachedThreadPool(int minThreadNum, int maxThreadNum, int shrinkInterval,
int shrinkStep, int stretchStep) {
auto *pool = ccnew LegacyThreadPool(minThreadNum, maxThreadNum);
if (pool != nullptr) {
pool->setFixedSize(false);
pool->setShrinkInterval(shrinkInterval);
pool->setShrinkStep(shrinkStep);
pool->setStretchStep(stretchStep);
}
return pool;
}
LegacyThreadPool *LegacyThreadPool::newFixedThreadPool(int threadNum) {
auto *pool = ccnew LegacyThreadPool(threadNum, threadNum);
if (pool != nullptr) {
pool->setFixedSize(true);
}
return pool;
}
LegacyThreadPool *LegacyThreadPool::newSingleThreadPool() {
auto *pool = ccnew LegacyThreadPool(1, 1);
if (pool != nullptr) {
pool->setFixedSize(true);
}
return pool;
}
LegacyThreadPool::LegacyThreadPool(int minNum, int maxNum)
: _minThreadNum(minNum),
_maxThreadNum(maxNum) {
init();
}
// the destructor waits for all the functions in the queue to be finished
LegacyThreadPool::~LegacyThreadPool() {
stop();
}
// number of idle threads
int LegacyThreadPool::getIdleThreadNum() const {
auto *thiz = const_cast<LegacyThreadPool *>(this);
std::lock_guard<std::mutex> lk(thiz->_idleThreadNumMutex);
return _idleThreadNum;
}
void LegacyThreadPool::init() {
_lastShrinkTime = std::chrono::high_resolution_clock::now();
_maxThreadNum = std::max(_minThreadNum, _maxThreadNum);
_threads.resize(_maxThreadNum);
_abortFlags.resize(_maxThreadNum);
_idleFlags.resize(_maxThreadNum);
_initedFlags.resize(_maxThreadNum);
for (int i = 0; i < _maxThreadNum; ++i) {
_idleFlags[i] = std::make_shared<std::atomic<bool>>(false);
if (i < _minThreadNum) {
_abortFlags[i] = std::make_shared<std::atomic<bool>>(false);
setThread(i);
_initedFlags[i] = std::make_shared<std::atomic<bool>>(true);
++_initedThreadNum;
} else {
_abortFlags[i] = std::make_shared<std::atomic<bool>>(true);
_initedFlags[i] = std::make_shared<std::atomic<bool>>(false);
}
}
}
bool LegacyThreadPool::tryShrinkPool() {
LOGD("shrink pool, _idleThreadNum = %d \n", getIdleThreadNum());
auto before = std::chrono::high_resolution_clock::now();
ccstd::vector<int> threadIDsToJoin;
int maxThreadNumToJoin = std::min(_initedThreadNum - _minThreadNum, _shrinkStep);
for (int i = 0; i < _maxThreadNum; ++i) {
if ((int)threadIDsToJoin.size() >= maxThreadNumToJoin) {
break;
}
if (*_idleFlags[i]) {
*_abortFlags[i] = true;
threadIDsToJoin.push_back(i);
}
}
{
// stop the detached threads that were waiting
std::unique_lock<std::mutex> lock(_mutex);
_cv.notify_all();
}
for (const auto &threadID : threadIDsToJoin) { // wait for the computing threads to finish
if (_threads[threadID]->joinable()) {
_threads[threadID]->join();
}
_threads[threadID].reset();
*_initedFlags[threadID] = false;
--_initedThreadNum;
}
auto after = std::chrono::high_resolution_clock::now();
float seconds = TIME_MINUS(after, before);
LOGD("shrink %d threads, waste: %f seconds\n", (int)threadIDsToJoin.size(), seconds);
return (_initedThreadNum <= _minThreadNum);
}
void LegacyThreadPool::stretchPool(int count) {
auto before = std::chrono::high_resolution_clock::now();
int oldThreadCount = _initedThreadNum;
int newThreadCount = 0;
for (int i = 0; i < _maxThreadNum; ++i) {
if (!*_initedFlags[i]) {
*_abortFlags[i] = false;
setThread(i);
*_initedFlags[i] = true;
++_initedThreadNum;
if (++newThreadCount >= count) {
break;
}
}
}
if (newThreadCount > 0) {
auto after = std::chrono::high_resolution_clock::now();
float seconds = TIME_MINUS(after, before);
LOGD("stretch pool from %d to %d, waste %f seconds\n", oldThreadCount, _initedThreadNum,
seconds);
}
}
void LegacyThreadPool::pushTask(const std::function<void(int)> &runnable,
TaskType type /* = DEFAULT*/) {
if (!_isFixedSize) {
_idleThreadNumMutex.lock();
int idleNum = _idleThreadNum;
_idleThreadNumMutex.unlock();
if (idleNum > _minThreadNum) {
if (_taskQueue.empty()) {
auto now = std::chrono::high_resolution_clock::now();
float seconds = TIME_MINUS(now, _lastShrinkTime);
if (seconds > _shrinkInterval) {
tryShrinkPool();
_lastShrinkTime = now;
}
}
} else if (idleNum == 0) {
stretchPool(_stretchStep);
}
}
auto callback = ccnew std::function<void(int)>([runnable](int tid) {
runnable(tid);
});
Task task;
task.type = type;
task.callback = callback;
_taskQueue.push(task);
{
std::unique_lock<std::mutex> lock(_mutex);
_cv.notify_one();
}
}
void LegacyThreadPool::stopAllTasks() {
Task task;
while (_taskQueue.pop(task)) {
delete task.callback; // empty the queue
}
}
void LegacyThreadPool::stopTasksByType(TaskType type) {
Task task;
ccstd::vector<Task> notStopTasks;
notStopTasks.reserve(_taskQueue.size());
while (_taskQueue.pop(task)) {
if (task.type == type) { // Delete the task from queue
delete task.callback;
} else { // If task type isn't match, push it into a vector, then insert to task queue again
notStopTasks.push_back(task);
}
}
if (!notStopTasks.empty()) {
for (const auto &t : notStopTasks) {
_taskQueue.push(t);
}
}
}
void LegacyThreadPool::joinThread(int tid) {
if (tid < 0 || tid >= (int)_threads.size()) {
LOGD("Invalid thread id %d\n", tid);
return;
}
// wait for the computing threads to finish
if (*_initedFlags[tid] && _threads[tid]->joinable()) {
_threads[tid]->join();
*_initedFlags[tid] = false;
--_initedThreadNum;
}
}
int LegacyThreadPool::getTaskNum() const {
return (int)_taskQueue.size();
}
void LegacyThreadPool::setFixedSize(bool isFixedSize) {
_isFixedSize = isFixedSize;
}
void LegacyThreadPool::setShrinkInterval(int seconds) {
if (seconds >= 0) {
_shrinkInterval = static_cast<float>(seconds);
}
}
void LegacyThreadPool::setShrinkStep(int step) {
if (step > 0) {
_shrinkStep = step;
}
}
void LegacyThreadPool::setStretchStep(int step) {
if (step > 0) {
_stretchStep = step;
}
}
void LegacyThreadPool::stop() {
if (_isDone || _isStop) {
return;
}
_isDone = true; // give the waiting threads a command to finish
{
std::unique_lock<std::mutex> lock(_mutex);
_cv.notify_all(); // stop all waiting threads
}
for (int i = 0, n = static_cast<int>(_threads.size()); i < n; ++i) {
joinThread(i);
}
// if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads
// therefore delete them here
stopAllTasks();
_threads.clear();
_abortFlags.clear();
}
void LegacyThreadPool::setThread(int tid) {
std::shared_ptr<std::atomic<bool>> abortPtr(
_abortFlags[tid]); // a copy of the shared ptr to the flag
auto f = [this, tid, abortPtr /* a copy of the shared ptr to the abort */]() {
std::atomic<bool> &abort = *abortPtr;
Task task;
bool isPop = _taskQueue.pop(task);
while (true) {
while (isPop) { // if there is anything in the queue
std::unique_ptr<std::function<void(int)>> func(
task.callback); // at return, delete the function even if an exception occurred
(*task.callback)(tid);
if (abort) {
return; // the thread is wanted to stop, return even if the queue is not empty yet
}
isPop = _taskQueue.pop(task);
}
// the queue is empty here, wait for the next command
std::unique_lock<std::mutex> lock(_mutex);
_idleThreadNumMutex.lock();
++_idleThreadNum;
_idleThreadNumMutex.unlock();
*_idleFlags[tid] = true;
_cv.wait(lock, [this, &task, &isPop, &abort]() {
isPop = _taskQueue.pop(task);
return isPop || _isDone || abort;
});
*_idleFlags[tid] = false;
_idleThreadNumMutex.lock();
--_idleThreadNum;
_idleThreadNumMutex.unlock();
if (!isPop) {
return; // if the queue is empty and isDone == true or *flag then return
}
}
};
_threads[tid].reset(
ccnew std::thread(f)); // compiler may not support std::make_unique()
}
} // namespace cc

216
cocos/base/ThreadPool.h Normal file
View File

@@ -0,0 +1,216 @@
/****************************************************************************
Copyright (c) 2016-2017 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include "base/Utils.h"
#include "base/std/container/queue.h"
namespace cc {
class CC_DLL LegacyThreadPool {
public:
enum class TaskType {
DEFAULT = 0,
NETWORK,
IO,
AUDIO,
USER = 1000
};
/*
* Gets the default thread pool which is a cached thread pool with default parameters.
*/
static LegacyThreadPool *getDefaultThreadPool();
/*
* Destroys the default thread pool
*/
static void destroyDefaultThreadPool();
/*
* Creates a cached thread pool
* @note The return value has to be delete while it doesn't needed
*/
static LegacyThreadPool *newCachedThreadPool(int minThreadNum, int maxThreadNum, int shrinkInterval,
int shrinkStep, int stretchStep);
/*
* Creates a thread pool with fixed thread count
* @note The return value has to be delete while it doesn't needed
*/
static LegacyThreadPool *newFixedThreadPool(int threadNum);
/*
* Creates a thread pool with only one thread in the pool, it could be used to execute multiply tasks serially in just one thread.
* @note The return value has to be delete while it doesn't needed
*/
static LegacyThreadPool *newSingleThreadPool();
// the destructor waits for all the functions in the queue to be finished
~LegacyThreadPool();
/* Pushs a task to thread pool
* @param runnable The callback of the task executed in sub thread
* @param type The task type, it's TASK_TYPE_DEFAULT if this argument isn't assigned
* @note This function has to be invoked in cocos thread
*/
void pushTask(const std::function<void(int /*threadId*/)> &runnable, TaskType type = TaskType::DEFAULT);
// Stops all tasks, it will remove all tasks in queue
void stopAllTasks();
// Stops some tasks by type
void stopTasksByType(TaskType type);
// Gets the minimum thread numbers
inline int getMinThreadNum() const { return _minThreadNum; };
// Gets the maximum thread numbers
inline int getMaxThreadNum() const { return _maxThreadNum; };
// Gets the number of idle threads
int getIdleThreadNum() const;
// Gets the number of initialized threads
inline int getInitedThreadNum() const { return _initedThreadNum; };
// Gets the task number
int getTaskNum() const;
/*
* Trys to shrink pool
* @note This method is only available for cached thread pool
*/
bool tryShrinkPool();
private:
LegacyThreadPool(int minNum, int maxNum);
LegacyThreadPool(const LegacyThreadPool &);
LegacyThreadPool(LegacyThreadPool &&) noexcept;
LegacyThreadPool &operator=(const LegacyThreadPool &);
LegacyThreadPool &operator=(LegacyThreadPool &&) noexcept;
void init();
void stop();
void setThread(int tid);
void joinThread(int tid);
void setFixedSize(bool isFixedSize);
void setShrinkInterval(int seconds);
void setShrinkStep(int step);
void setStretchStep(int step);
void stretchPool(int count);
ccstd::vector<std::unique_ptr<std::thread>> _threads;
ccstd::vector<std::shared_ptr<std::atomic<bool>>> _abortFlags;
ccstd::vector<std::shared_ptr<std::atomic<bool>>> _idleFlags;
ccstd::vector<std::shared_ptr<std::atomic<bool>>> _initedFlags;
template <typename T>
class ThreadSafeQueue {
public:
bool push(T const &value) {
std::unique_lock<std::mutex> lock(this->mutex);
this->q.push(value);
return true;
}
// deletes the retrieved element, do not use for non integral types
bool pop(T &v) {
std::unique_lock<std::mutex> lock(this->mutex);
if (this->q.empty())
return false;
v = this->q.front();
this->q.pop();
return true;
}
bool empty() const {
auto thiz = const_cast<ThreadSafeQueue *>(this);
std::unique_lock<std::mutex> lock(thiz->mutex);
return this->q.empty();
}
size_t size() const {
auto thiz = const_cast<ThreadSafeQueue *>(this);
std::unique_lock<std::mutex> lock(thiz->mutex);
return this->q.size();
}
private:
ccstd::queue<T> q;
std::mutex mutex;
};
struct Task {
TaskType type;
std::function<void(int)> *callback;
};
static LegacyThreadPool *_instance;
ThreadSafeQueue<Task> _taskQueue;
std::atomic<bool> _isDone{false};
std::atomic<bool> _isStop{false};
//IDEA: std::atomic<int> isn't supported by ndk-r10e while compiling with `armeabi` arch.
// So using a mutex here instead.
int _idleThreadNum{0}; // how many threads are waiting
std::mutex _idleThreadNumMutex;
std::mutex _mutex;
std::condition_variable _cv;
int _minThreadNum{0};
int _maxThreadNum{0};
int _initedThreadNum{0};
std::chrono::time_point<std::chrono::high_resolution_clock> _lastShrinkTime;
float _shrinkInterval{5};
int _shrinkStep{2};
int _stretchStep{2};
bool _isFixedSize{false};
};
} // namespace cc

71
cocos/base/Timer.cpp Normal file
View File

@@ -0,0 +1,71 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "Timer.h"
namespace cc {
namespace utils {
Timer::Timer(bool doReset) {
if (doReset) {
reset();
}
}
void Timer::reset() {
_startTime = Clock::now();
}
int64_t Timer::getMicroseconds() const {
auto currentTime = Clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(currentTime - _startTime).count();
if (duration < 0) {
duration = 0;
}
return duration;
}
int64_t Timer::getMilliseconds() const {
auto currentTime = Clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - _startTime).count();
if (duration < 0) {
duration = 0;
}
return duration;
}
float Timer::getSeconds(bool highPrecision) const {
if (highPrecision) {
int64_t micro = getMicroseconds();
return static_cast<float>(micro) / 1000000.0F;
}
int64_t milli = getMilliseconds();
return static_cast<float>(milli) / 1000.0F;
}
} // namespace utils
} // namespace cc

51
cocos/base/Timer.h Normal file
View File

@@ -0,0 +1,51 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <chrono>
#include <cstdint>
#include "base/Macros.h"
namespace cc {
namespace utils {
class CC_DLL Timer {
public:
using Clock = std::chrono::high_resolution_clock;
using TimePoint = Clock::time_point;
explicit Timer(bool doReset = true);
void reset();
int64_t getMicroseconds() const;
int64_t getMilliseconds() const;
float getSeconds(bool highPrecision = false) const;
private:
TimePoint _startTime;
};
} // namespace utils
} // namespace cc

79
cocos/base/TypeDef.h Normal file
View File

@@ -0,0 +1,79 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstdint>
#include "base/Macros.h"
#include "base/std/container/unordered_map.h"
using uint = std::uint32_t;
using ushort = std::uint16_t;
#if (CC_PLATFORM != CC_PLATFORM_LINUX && CC_PLATFORM != CC_PLATFORM_QNX && CC_PLATFORM != CC_PLATFORM_EMSCRIPTEN && CC_PLATFORM != CC_PLATFORM_OPENHARMONY) // linux and openharmony has typedef ulong
using ulong = std::uint32_t;
#endif
using FlagBits = std::uint32_t;
using index_t = int32_t;
#define CC_INVALID_INDEX (-1)
#define CC_ENUM_CONVERSION_OPERATOR(T) \
inline std::underlying_type<T>::type toNumber(const T v) { return static_cast<std::underlying_type<T>::type>(v); }
#define CC_ENUM_BITWISE_OPERATORS(T) \
constexpr bool operator!(const T v) { return !static_cast<std::underlying_type<T>::type>(v); } \
constexpr T operator~(const T v) { return static_cast<T>(~static_cast<std::underlying_type<T>::type>(v)); } \
constexpr bool operator||(const T lhs, const T rhs) { return (static_cast<std::underlying_type<T>::type>(lhs) || static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr bool operator&&(const T lhs, const T rhs) { return (static_cast<std::underlying_type<T>::type>(lhs) && static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr T operator|(const T lhs, const T rhs) { return static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) | static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr T operator&(const T lhs, const T rhs) { return static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) & static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr T operator^(const T lhs, const T rhs) { return static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) ^ static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr T operator+(const T lhs, const T rhs) { return static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) + static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr T operator+(const T lhs, bool rhs) { return static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) + rhs); } \
constexpr void operator|=(T &lhs, const T rhs) { lhs = static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) | static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr void operator&=(T &lhs, const T rhs) { lhs = static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) & static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr void operator^=(T &lhs, const T rhs) { lhs = static_cast<T>(static_cast<std::underlying_type<T>::type>(lhs) ^ static_cast<std::underlying_type<T>::type>(rhs)); } \
constexpr bool hasFlag(const T flags, const T flagToTest) { \
using ValueType = std::underlying_type<T>::type; \
CC_ASSERT((static_cast<ValueType>(flagToTest) & (static_cast<ValueType>(flagToTest) - 1)) == 0); \
return (static_cast<ValueType>(flags) & static_cast<ValueType>(flagToTest)) != 0; \
} \
constexpr bool hasAnyFlags(const T flags, const T flagsToTest) { \
using ValueType = std::underlying_type<T>::type; \
return (static_cast<ValueType>(flags) & static_cast<ValueType>(flagsToTest)) != 0; \
} \
constexpr bool hasAllFlags(const T flags, const T flagsToTest) { \
using ValueType = std::underlying_type<T>::type; \
return (static_cast<ValueType>(flags) & static_cast<ValueType>(flagsToTest)) == static_cast<ValueType>(flagsToTest); \
} \
constexpr T addFlags(T &flags, const T flagsToAdd) { \
flags |= flagsToAdd; \
return flags; \
} \
constexpr T removeFlags(T &flags, const T flagsToRemove) { \
flags &= ~flagsToRemove; \
return flags; \
} \
CC_ENUM_CONVERSION_OPERATOR(T)

347
cocos/base/UTF8.cpp Normal file
View File

@@ -0,0 +1,347 @@
/****************************************************************************
Copyright (c) 2014 cocos2d-x.org
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/UTF8.h"
#include <cstdarg>
#include <cstdlib>
#include "ConvertUTF/ConvertUTF.h"
#include "base/Log.h"
namespace cc {
namespace StringUtils { //NOLINT
ccstd::string format(const char *format, ...) {
#define CC_MAX_STRING_LENGTH (1024 * 100)
ccstd::string ret;
va_list ap;
va_start(ap, format);
char *buf = static_cast<char *>(malloc(CC_MAX_STRING_LENGTH));
if (buf != nullptr) {
vsnprintf(buf, CC_MAX_STRING_LENGTH, format, ap);
ret = buf;
free(buf);
}
va_end(ap);
return ret;
}
/*
* @str: the string to search through.
* @c: the character to not look for.
*
* Return value: the index of the last character that is not c.
* */
unsigned int getIndexOfLastNotChar16(const ccstd::vector<char16_t> &str, char16_t c) {
int len = static_cast<int>(str.size());
int i = len - 1;
for (; i >= 0; --i) {
if (str[i] != c) {
return i;
}
}
return i;
}
/*
* @str: the string to trim
* @index: the index to start trimming from.
*
* Trims str st str=[0, index) after the operation.
*
* Return value: the trimmed string.
* */
static void trimUTF16VectorFromIndex(ccstd::vector<char16_t> &str, int index) { //NOLINT
int size = static_cast<int>(str.size());
if (index >= size || index < 0) {
return;
}
str.erase(str.begin() + index, str.begin() + size);
}
/*
* @ch is the unicode character whitespace?
*
* Reference: http://en.wikipedia.org/wiki/Whitespace_character#Unicode
*
* Return value: weather the character is a whitespace character.
* */
bool isUnicodeSpace(char16_t ch) {
return (ch >= 0x0009 && ch <= 0x000D) || ch == 0x0020 || ch == 0x0085 || ch == 0x00A0 || ch == 0x1680 || (ch >= 0x2000 && ch <= 0x200A) || ch == 0x2028 || ch == 0x2029 || ch == 0x202F || ch == 0x205F || ch == 0x3000;
}
bool isCJKUnicode(char16_t ch) {
return (ch >= 0x4E00 && ch <= 0x9FBF) // CJK Unified Ideographs
|| (ch >= 0x2E80 && ch <= 0x2FDF) // CJK Radicals Supplement & Kangxi Radicals
|| (ch >= 0x2FF0 && ch <= 0x30FF) // Ideographic Description Characters, CJK Symbols and Punctuation & Japanese
|| (ch >= 0x3100 && ch <= 0x31BF) // Korean
|| (ch >= 0xAC00 && ch <= 0xD7AF) // Hangul Syllables
|| (ch >= 0xF900 && ch <= 0xFAFF) // CJK Compatibility Ideographs
|| (ch >= 0xFE30 && ch <= 0xFE4F) // CJK Compatibility Forms
|| (ch >= 0x31C0 && ch <= 0x4DFF); // Other extensions
}
void trimUTF16Vector(ccstd::vector<char16_t> &str) {
int len = static_cast<int>(str.size());
if (len <= 0) {
return;
}
int lastIndex = len - 1;
// Only start trimming if the last character is whitespace..
if (isUnicodeSpace(str[lastIndex])) {
for (int i = lastIndex - 1; i >= 0; --i) {
if (isUnicodeSpace(str[i])) {
lastIndex = i;
}
else {
break;
}
}
trimUTF16VectorFromIndex(str, lastIndex);
}
}
template <typename T>
struct ConvertTrait {
using ArgType = T;
};
template <>
struct ConvertTrait<char> {
using ArgType = UTF8;
};
template <>
struct ConvertTrait<char16_t> {
using ArgType = UTF16;
};
template <>
struct ConvertTrait<char32_t> {
using ArgType = UTF32;
};
template <typename From, typename To, typename FromTrait = ConvertTrait<From>, typename ToTrait = ConvertTrait<To>>
bool utfConvert(
const std::basic_string<From> &from, std::basic_string<To> &to,
ConversionResult (*cvtfunc)(const typename FromTrait::ArgType **, const typename FromTrait::ArgType *,
typename ToTrait::ArgType **, typename ToTrait::ArgType *,
ConversionFlags)) {
static_assert(sizeof(From) == sizeof(typename FromTrait::ArgType), "Error size mismatched");
static_assert(sizeof(To) == sizeof(typename ToTrait::ArgType), "Error size mismatched");
if (from.empty()) {
to.clear();
return true;
}
// See: http://unicode.org/faq/utf_bom.html#gen6
constexpr int mostBytesPerCharacter = 4;
const size_t maxNumberOfChars = from.length(); // all UTFs at most one element represents one character.
const size_t numberOfOut = maxNumberOfChars * mostBytesPerCharacter / sizeof(To);
std::basic_string<To> working(numberOfOut, 0);
auto inbeg = reinterpret_cast<const typename FromTrait::ArgType *>(&from[0]);
auto inend = inbeg + from.length();
auto outbeg = reinterpret_cast<typename ToTrait::ArgType *>(&working[0]);
auto outend = outbeg + working.length();
auto r = cvtfunc(&inbeg, inend, &outbeg, outend, strictConversion);
if (r != conversionOK) {
return false;
}
working.resize(reinterpret_cast<To *>(outbeg) - &working[0]);
to = std::move(working);
return true;
};
CC_DLL void UTF8LooseFix(const ccstd::string &in, ccstd::string &out) { //NOLINT
const auto *p = reinterpret_cast<const UTF8 *>(in.c_str());
const auto *end = reinterpret_cast<const UTF8 *>(in.c_str() + in.size());
unsigned ucharLen = 0;
while (p < end) {
ucharLen = getNumBytesForUTF8(*p);
if (isLegalUTF8Sequence(p, p + ucharLen)) {
if (p + ucharLen < end) {
out.append(p, p + ucharLen);
}
p += ucharLen;
} else {
p += 1; //skip bad char
}
}
}
bool UTF8ToUTF16(const ccstd::string &utf8, std::u16string &outUtf16) { //NOLINT
return utfConvert(utf8, outUtf16, ConvertUTF8toUTF16);
}
bool UTF8ToUTF32(const ccstd::string &utf8, std::u32string &outUtf32) { //NOLINT
return utfConvert(utf8, outUtf32, ConvertUTF8toUTF32);
}
bool UTF16ToUTF8(const std::u16string &utf16, ccstd::string &outUtf8) { //NOLINT
return utfConvert(utf16, outUtf8, ConvertUTF16toUTF8);
}
bool UTF16ToUTF32(const std::u16string &utf16, std::u32string &outUtf32) { //NOLINT
return utfConvert(utf16, outUtf32, ConvertUTF16toUTF32);
}
bool UTF32ToUTF8(const std::u32string &utf32, ccstd::string &outUtf8) { //NOLINT
return utfConvert(utf32, outUtf8, ConvertUTF32toUTF8);
}
bool UTF32ToUTF16(const std::u32string &utf32, std::u16string &outUtf16) { //NOLINT
return utfConvert(utf32, outUtf16, ConvertUTF32toUTF16);
}
#if (CC_PLATFORM == CC_PLATFORM_ANDROID || CC_PLATFORM == CC_PLATFORM_OHOS)
ccstd::string getStringUTFCharsJNI(JNIEnv *env, jstring srcjStr, bool *ret) {
ccstd::string utf8Str;
auto *unicodeChar = static_cast<const uint16_t *>(env->GetStringChars(srcjStr, nullptr));
size_t unicodeCharLength = env->GetStringLength(srcjStr);
const std::u16string unicodeStr(reinterpret_cast<const char16_t *>(unicodeChar), unicodeCharLength);
bool flag = UTF16ToUTF8(unicodeStr, utf8Str);
if (ret) {
*ret = flag;
}
if (!flag) {
utf8Str = "";
}
env->ReleaseStringChars(srcjStr, unicodeChar);
return utf8Str;
}
jstring newStringUTFJNI(JNIEnv *env, const ccstd::string &utf8Str, bool *ret) {
std::u16string utf16Str;
bool flag = cc::StringUtils::UTF8ToUTF16(utf8Str, utf16Str);
if (ret) {
*ret = flag;
}
if (!flag) {
utf16Str.clear();
}
jstring stringText = env->NewString(reinterpret_cast<const jchar *>(utf16Str.data()), utf16Str.length());
return stringText;
}
#endif
ccstd::vector<char16_t> getChar16VectorFromUTF16String(const std::u16string &utf16) {
return ccstd::vector<char16_t>(utf16.begin(), utf16.end());
}
long getCharacterCountInUTF8String(const ccstd::string &utf8) { //NOLINT
return getUTF8StringLength(reinterpret_cast<const UTF8 *>(utf8.c_str()));
}
StringUTF8::StringUTF8(const ccstd::string &newStr) {
replace(newStr);
}
std::size_t StringUTF8::length() const {
return _str.size();
}
void StringUTF8::replace(const ccstd::string &newStr) {
_str.clear();
if (!newStr.empty()) {
const auto *sequenceUtf8 = reinterpret_cast<const UTF8 *>(newStr.c_str());
int lengthString = getUTF8StringLength(sequenceUtf8);
if (lengthString == 0) {
CC_LOG_DEBUG("Bad utf-8 set string: %s", newStr.c_str());
return;
}
while (*sequenceUtf8) {
std::size_t lengthChar = getNumBytesForUTF8(*sequenceUtf8);
CharUTF8 charUTF8;
charUTF8._char.append(reinterpret_cast<const char *>(sequenceUtf8), lengthChar);
sequenceUtf8 += lengthChar;
_str.push_back(charUTF8);
}
}
}
ccstd::string StringUTF8::getAsCharSequence() const {
ccstd::string charSequence;
for (auto &charUtf8 : _str) {
charSequence.append(charUtf8._char);
}
return charSequence;
}
bool StringUTF8::deleteChar(std::size_t pos) {
if (pos < _str.size()) {
_str.erase(_str.begin() + pos);
return true;
}
return false;
}
bool StringUTF8::insert(std::size_t pos, const ccstd::string &insertStr) {
StringUTF8 utf8(insertStr);
return insert(pos, utf8);
}
bool StringUTF8::insert(std::size_t pos, const StringUTF8 &insertStr) {
if (pos <= _str.size()) {
_str.insert(_str.begin() + pos, insertStr._str.begin(), insertStr._str.end());
return true;
}
return false;
}
} // namespace StringUtils
} // namespace cc

216
cocos/base/UTF8.h Normal file
View File

@@ -0,0 +1,216 @@
/****************************************************************************
Copyright (c) 2014 cocos2d-x.org
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <sstream>
#include "base/Macros.h"
#include "base/std/container/string.h"
#include "base/std/container/vector.h"
#if (CC_PLATFORM == CC_PLATFORM_ANDROID || CC_PLATFORM == CC_PLATFORM_OHOS)
#include "platform/java/jni/JniHelper.h"
#endif
namespace cc {
namespace StringUtils { //NOLINT
template <typename T>
ccstd::string toString(T arg) {
std::stringstream ss;
ss << arg;
return ss.str();
}
ccstd::string CC_DLL format(const char *format, ...) CC_FORMAT_PRINTF(1, 2);
/**
* @brief Converts from UTF8 string to UTF16 string.
*
* This function resizes \p outUtf16 to required size and
* fill its contents with result UTF16 string if conversion success.
* If conversion fails it guarantees not to change \p outUtf16.
*
* @param inUtf8 The source UTF8 string to be converted from.
* @param outUtf16 The output string to hold the result UTF16s.
* @return True if succeed, otherwise false.
* @note Please check the return value before using \p outUtf16
* e.g.
* @code
* std::u16string utf16;
* bool ret = StringUtils::UTF8ToUTF16("你好hello", utf16);
* if (ret) {
* do_some_thing_with_utf16(utf16);
* }
* @endcode
*/
CC_DLL bool UTF8ToUTF16(const ccstd::string &inUtf8, std::u16string &outUtf16); //NOLINT
/**
* @brief Same as \a UTF8ToUTF16 but converts form UTF8 to UTF32.
*
* @see UTF8ToUTF16
*/
CC_DLL bool UTF8ToUTF32(const ccstd::string &inUtf8, std::u32string &outUtf32); //NOLINT
/**
* @brief Same as \a UTF8ToUTF16 but converts form UTF16 to UTF8.
*
* @see UTF8ToUTF16
*/
CC_DLL bool UTF16ToUTF8(const std::u16string &inUtf16, ccstd::string &outUtf8); //NOLINT
/**
* @brief Same as \a UTF8ToUTF16 but converts form UTF16 to UTF32.
*
* @see UTF8ToUTF16
*/
CC_DLL bool UTF16ToUTF32(const std::u16string &inUtf16, std::u32string &outUtf32); //NOLINT
/**
* @brief Same as \a UTF8ToUTF16 but converts form UTF32 to UTF8.
*
* @see UTF8ToUTF16
*/
CC_DLL bool UTF32ToUTF8(const std::u32string &inUtf32, ccstd::string &outUtf8); //NOLINT
/**
* @brief Same as \a UTF8ToUTF16 but converts form UTF32 to UTF16.
*
* @see UTF8ToUTF16
*/
CC_DLL bool UTF32ToUTF16(const std::u32string &inUtf32, std::u16string &outUtf16); //NOLINT
/**
* @brief Skip some bad char code.
*/
CC_DLL void UTF8LooseFix(const ccstd::string &in, ccstd::string &out); //NOLINT
#if (CC_PLATFORM == CC_PLATFORM_ANDROID || CC_PLATFORM == CC_PLATFORM_OHOS)
/**
* @brief convert jstring to utf8 ccstd::string, same function with env->getStringUTFChars.
* because getStringUTFChars can not pass special emoticon
* @param env The JNI Env
* @param srcjStr The jstring which want to convert
* @param ret True if the conversion succeeds and the ret pointer isn't null
* @returns the result of utf8 string
*/
CC_DLL ccstd::string getStringUTFCharsJNI(JNIEnv *env, jstring srcjStr, bool *ret = nullptr);
/**
* @brief create a jstring with utf8 ccstd::string, same function with env->newStringUTF
* because newStringUTF can not convert special emoticon
* @param env The JNI Env
* @param srcjStr The ccstd::string which want to convert
* @param ret True if the conversion succeeds and the ret pointer isn't null
* @returns the result of jstring,the jstring need to DeleteLocalRef(jstring);
*/
CC_DLL jstring newStringUTFJNI(JNIEnv *env, const ccstd::string &utf8Str, bool *ret = nullptr);
#endif
/**
* @brief Trims the unicode spaces at the end of char16_t vector.
*/
CC_DLL void trimUTF16Vector(ccstd::vector<char16_t> &str); //NOLINT
/**
* @brief Whether the character is a whitespace character.
* @param ch The unicode character.
* @returns Whether the character is a white space character.
*
* @see http://en.wikipedia.org/wiki/Whitespace_character#Unicode
*
*/
CC_DLL bool isUnicodeSpace(char16_t ch);
/**
* @brief Whether the character is a Chinese/Japanese/Korean character.
* @param ch The unicode character.
* @returns Whether the character is a Chinese character.
*
* @see http://www.searchtb.com/2012/04/chinese_encode.html
* @see http://tieba.baidu.com/p/748765987
*
*/
CC_DLL bool isCJKUnicode(char16_t ch);
/**
* @brief Returns the length of the string in characters.
* @param utf8 An UTF-8 encoded string.
* @returns The length of the string in characters.
*/
CC_DLL long getCharacterCountInUTF8String(const ccstd::string &utf8); //NOLINT
/**
* @brief Gets the index of the last character that is not equal to the character given.
* @param str The string to be searched.
* @param c The character to be searched for.
* @returns The index of the last character that is not \p c.
*/
CC_DLL unsigned int getIndexOfLastNotChar16(const ccstd::vector<char16_t> &str, char16_t c);
/**
* @brief Gets char16_t vector from a given utf16 string.
*/
CC_DLL ccstd::vector<char16_t> getChar16VectorFromUTF16String(const std::u16string &utf16);
/**
* Utf8 sequence
* Store all utf8 chars as ccstd::string
* Build from ccstd::string
*/
class CC_DLL StringUTF8 {
public:
struct CharUTF8 {
ccstd::string _char;
bool isAnsi() { return _char.size() == 1; }
};
using CharUTF8Store = ccstd::vector<CharUTF8>;
StringUTF8() = default;
explicit StringUTF8(const ccstd::string &newStr);
~StringUTF8() = default;
std::size_t length() const;
void replace(const ccstd::string &newStr);
ccstd::string getAsCharSequence() const;
bool deleteChar(std::size_t pos);
bool insert(std::size_t pos, const ccstd::string &insertStr);
bool insert(std::size_t pos, const StringUTF8 &insertStr);
CharUTF8Store &getString() { return _str; }
private:
CharUTF8Store _str;
};
} // namespace StringUtils
} // namespace cc

96
cocos/base/Utils.cpp Normal file
View File

@@ -0,0 +1,96 @@
/****************************************************************************
Copyright (c) 2010 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/Utils.h"
#if CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
#include <sys/time.h>
#endif
#include <cmath>
#include <cstdlib>
#include <cstring>
#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED
#include "boost/stacktrace.hpp"
#include "base/base64.h"
#include "base/memory/MemoryHook.h"
#include "platform/FileUtils.h"
namespace cc {
namespace utils {
#define MAX_ITOA_BUFFER_SIZE 256
double atof(const char *str) {
if (str == nullptr) {
return 0.0;
}
char buf[MAX_ITOA_BUFFER_SIZE];
strncpy(buf, str, MAX_ITOA_BUFFER_SIZE);
// strip string, only remain 7 numbers after '.'
char *dot = strchr(buf, '.');
if (dot != nullptr && dot - buf + 8 < MAX_ITOA_BUFFER_SIZE) {
dot[8] = '\0';
}
return ::atof(buf);
}
uint32_t nextPOT(uint32_t x) {
x = x - 1;
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
return x + 1;
}
// painfully slow to execute, use with caution
ccstd::string getStacktrace(uint32_t skip, uint32_t maxDepth) {
return boost::stacktrace::to_string(boost::stacktrace::stacktrace(skip, maxDepth));
}
} // namespace utils
#if USE_MEMORY_LEAK_DETECTOR
// Make sure GMemoryHook to be initialized first.
#if (CC_COMPILER == CC_COMPILER_MSVC)
#pragma warning(push)
#pragma warning(disable : 4073)
#pragma init_seg(lib)
MemoryHook GMemoryHook;
#pragma warning(pop)
#elif (CC_COMPILER == CC_COMPILER_GNUC || CC_COMPILER == CC_COMPILER_CLANG)
MemoryHook GMemoryHook __attribute__((init_priority(101)));
#endif
#endif
} // namespace cc

391
cocos/base/Utils.h Normal file
View File

@@ -0,0 +1,391 @@
/****************************************************************************
Copyright (c) 2010 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <algorithm>
#include <bitset>
#include <cerrno>
#include <climits>
#include <limits>
#include "base/Macros.h"
#include "base/TypeDef.h"
#include "base/std/container/string.h"
#include "base/std/container/vector.h"
/** @file ccUtils.h
Misc free functions
*/
namespace cc {
namespace utils {
CC_DLL ccstd::string getStacktrace(uint32_t skip = 0, uint32_t maxDepth = UINT32_MAX);
/**
* Returns the Next Power of Two value.
* Examples:
* - If "value" is 15, it will return 16.
* - If "value" is 16, it will return 16.
* - If "value" is 17, it will return 32.
* @param value The value to get next power of two.
* @return Returns the next power of two value.
* @since v0.99.5
*/
CC_DLL uint32_t nextPOT(uint32_t x);
/**
* Same to ::atof, but strip the string, remain 7 numbers after '.' before call atof.
* Why we need this? Because in android c++_static, atof ( and std::atof ) is unsupported for numbers have long decimal part and contain
* several numbers can approximate to 1 ( like 90.099998474121094 ), it will return inf. This function is used to fix this bug.
* @param str The string be to converted to double.
* @return Returns converted value of a string.
*/
CC_DLL double atof(const char *str);
#pragma warning(disable : 4146)
template <typename T, typename = typename std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value>>
inline T getLowestBit(T mask) {
return mask & (-mask);
}
#pragma warning(default : 4146)
template <typename T, typename = typename std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value>>
inline T clearLowestBit(T mask) {
return mask & (mask - 1);
}
// v must be power of 2
inline uint32_t getBitPosition(uint32_t v) {
if (!v) return 0;
uint32_t c = 32;
if (v & 0x0000FFFF) c -= 16;
if (v & 0x00FF00FF) c -= 8;
if (v & 0x0F0F0F0F) c -= 4;
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
return c;
}
// v must be power of 2
inline uint64_t getBitPosition(uint64_t v) {
if (!v) return 0;
uint64_t c = 64;
if (v & 0x00000000FFFFFFFFLL) c -= 32;
if (v & 0x0000FFFF0000FFFFLL) c -= 16;
if (v & 0x00FF00FF00FF00FFLL) c -= 8;
if (v & 0x0F0F0F0F0F0F0F0FLL) c -= 4;
if (v & 0x3333333333333333LL) c -= 2;
if (v & 0x5555555555555555LL) c -= 1;
return c;
}
template <typename T, typename = typename std::enable_if_t<std::is_integral<T>::value>>
inline size_t popcount(T mask) {
return std::bitset<sizeof(T)>(mask).count();
}
template <typename T, typename = typename std::enable_if_t<std::is_integral<T>::value>>
inline T alignTo(T size, T alignment) {
return ((size - 1) / alignment + 1) * alignment;
}
template <uint size, uint alignment>
constexpr uint ALIGN_TO = ((size - 1) / alignment + 1) * alignment;
template <class T>
inline uint toUint(T value) {
static_assert(std::is_arithmetic<T>::value, "T must be numeric");
CC_ASSERT(static_cast<uintmax_t>(value) <= static_cast<uintmax_t>(std::numeric_limits<uint>::max()));
return static_cast<uint>(value);
}
template <typename Map>
Map &mergeToMap(Map &outMap, const Map &inMap) {
for (const auto &e : inMap) {
outMap.emplace(e.first, e.second);
}
return outMap;
}
namespace numext {
template <typename Tgt, typename Src>
CC_FORCE_INLINE Tgt bit_cast(const Src &src) { // NOLINT(readability-identifier-naming)
// The behaviour of memcpy is not specified for non-trivially copyable types
static_assert(std::is_trivially_copyable<Src>::value, "THIS_TYPE_IS_NOT_SUPPORTED");
static_assert(std::is_trivially_copyable<Tgt>::value && std::is_default_constructible<Tgt>::value,
"THIS_TYPE_IS_NOT_SUPPORTED");
static_assert(sizeof(Src) == sizeof(Tgt), "THIS_TYPE_IS_NOT_SUPPORTED");
Tgt tgt;
// Load src into registers first. This allows the memcpy to be elided by CUDA.
const Src staged = src;
memcpy(&tgt, &staged, sizeof(Tgt));
return tgt;
}
} // namespace numext
// Following the Arm ACLE arm_neon.h should also include arm_fp16.h but not all
// compilers seem to follow this. We therefore include it explicitly.
// See also: https://bugs.llvm.org/show_bug.cgi?id=47955
#if defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC)
#include <arm_fp16.h>
#endif
// Code from https://gitlab.com/libeigen/eigen/-/blob/master/Eigen/src/Core/arch/Default/Half.h#L586
struct HalfRaw {
constexpr HalfRaw() : x(0) {}
#if defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC)
explicit HalfRaw(uint16_t raw) : x(numext::bit_cast<__fp16>(raw)) {
}
__fp16 x;
#else
explicit constexpr HalfRaw(uint16_t raw) : x(raw) {}
uint16_t x; // NOLINT(modernize-use-default-member-init)
#endif
};
// Conversion routines, including fallbacks for the host or older CUDA.
// Note that newer Intel CPUs (Haswell or newer) have vectorized versions of
// these in hardware. If we need more performance on older/other CPUs, they are
// also possible to vectorize directly.
CC_FORCE_INLINE HalfRaw rawUint16ToHalf(uint16_t x) {
// We cannot simply do a "return HalfRaw(x)" here, because HalfRaw is union type
// in the hip_fp16 header file, and that will trigger a compile error
// On the other hand, having anything but a return statement also triggers a compile error
// because this is constexpr function.
// Fortunately, since we need to disable EIGEN_CONSTEXPR for GPU anyway, we can get out
// of this catch22 by having separate bodies for GPU / non GPU
#if defined(CC_HAS_GPU_FP16)
HalfRaw h;
h.x = x;
return h;
#else
return HalfRaw(x);
#endif
}
CC_FORCE_INLINE uint16_t rawHalfAsUint16(const HalfRaw &h) {
// HIP/CUDA/Default have a member 'x' of type uint16_t.
// For ARM64 native half, the member 'x' is of type __fp16, so we need to bit-cast.
// For SYCL, cl::sycl::half is _Float16, so cast directly.
#if defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC)
return numext::bit_cast<uint16_t>(h.x);
#else
return h.x;
#endif
}
union float32_bits {
unsigned int u;
float f;
};
CC_FORCE_INLINE HalfRaw floatToHalf(float ff) {
#if defined(CC_HAS_FP16_C)
HalfRaw h;
#ifdef _MSC_VER
// MSVC does not have scalar instructions.
h.x = _mm_extract_epi16(_mm_cvtps_ph(_mm_set_ss(ff), 0), 0);
#else
h.x = _cvtss_sh(ff, 0);
#endif
return h;
#elif defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC)
HalfRaw h;
h.x = static_cast<__fp16>(ff);
return h;
#else
float32_bits f;
f.f = ff;
const float32_bits f32infty = {255 << 23};
const float32_bits f16max = {(127 + 16) << 23};
const float32_bits denorm_magic = {((127 - 15) + (23 - 10) + 1) << 23}; // NOLINT(readability-identifier-naming)
unsigned int sign_mask = 0x80000000U; // NOLINT
HalfRaw o;
o.x = static_cast<uint16_t>(0x0U);
unsigned int sign = f.u & sign_mask;
f.u ^= sign;
// NOTE all the integer compares in this function can be safely
// compiled into signed compares since all operands are below
// 0x80000000. Important if you want fast straight SSE2 code
// (since there's no unsigned PCMPGTD).
if (f.u >= f16max.u) { // result is Inf or NaN (all exponent bits set)
o.x = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
} else { // (De)normalized number or zero
if (f.u < (113 << 23)) { // resulting FP16 is subnormal or zero
// use a magic value to align our 10 mantissa bits at the bottom of
// the float. as long as FP addition is round-to-nearest-even this
// just works.
f.f += denorm_magic.f;
// and one integer subtract of the bias later, we have our final float!
o.x = static_cast<uint16_t>(f.u - denorm_magic.u);
} else {
unsigned int mant_odd = (f.u >> 13) & 1; // NOLINT(readability-identifier-naming) // resulting mantissa is odd
// update exponent, rounding bias part 1
// Equivalent to `f.u += ((unsigned int)(15 - 127) << 23) + 0xfff`, but
// without arithmetic overflow.
f.u += 0xc8000fffU;
// rounding bias part 2
f.u += mant_odd;
// take the bits!
o.x = static_cast<uint16_t>(f.u >> 13);
}
}
o.x |= static_cast<uint16_t>(sign >> 16);
return o;
#endif
}
CC_FORCE_INLINE float halfToFloat(HalfRaw h) {
#if defined(CC_HAS_FP16_C)
#ifdef _MSC_VER
// MSVC does not have scalar instructions.
return _mm_cvtss_f32(_mm_cvtph_ps(_mm_set1_epi16(h.x)));
#else
return _cvtsh_ss(h.x);
#endif
#elif defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC)
return static_cast<float>(h.x);
#else
const float32_bits magic = {113 << 23};
const unsigned int shifted_exp = 0x7c00 << 13; // NOLINT(readability-identifier-naming) // exponent mask after shift
float32_bits o;
o.u = (h.x & 0x7fff) << 13; // exponent/mantissa bits
unsigned int exp = shifted_exp & o.u; // just the exponent
o.u += (127 - 15) << 23; // exponent adjust
// handle exponent special cases
if (exp == shifted_exp) { // Inf/NaN?
o.u += (128 - 16) << 23; // extra exp adjust
} else if (exp == 0) { // Zero/Denormal?
o.u += 1 << 23; // extra exp adjust
o.f -= magic.f; // renormalize
}
o.u |= (h.x & 0x8000) << 16; // sign bit
return o.f;
#endif
}
namespace array {
/**
* @zh
* 移除首个指定的数组元素。判定元素相等时相当于于使用了 `Array.prototype.indexOf`。
* @en
* Removes the first occurrence of a specific object from the array.
* Decision of the equality of elements is similar to `Array.prototype.indexOf`.
* @param array 数组。
* @param value 待移除元素。
*/
template <typename T>
bool remove(ccstd::vector<T> &array, T value) {
auto iter = std::find(array.begin(), array.end(), value);
if (iter != array.end()) {
array.erase(iter);
return true;
}
return false;
}
/**
* @zh
* 移除指定索引的数组元素。
* @en
* Removes the array item at the specified index.
* @param array 数组。
* @param index 待移除元素的索引。
*/
template <typename T>
bool removeAt(ccstd::vector<T> &array, int32_t index) {
if (index >= 0 && index < static_cast<int32_t>(array.size())) {
array.erase(array.begin() + index);
return true;
}
return false;
}
/**
* @zh
* 移除指定索引的数组元素。
* 此函数十分高效,但会改变数组的元素次序。
* @en
* Removes the array item at the specified index.
* It's faster but the order of the array will be changed.
* @param array 数组。
* @param index 待移除元素的索引。
*/
template <typename T>
bool fastRemoveAt(ccstd::vector<T> &array, int32_t index) {
const auto length = static_cast<int32_t>(array.size());
if (index < 0 || index >= length) {
return false;
}
array[index] = array[length - 1];
array.resize(length - 1);
return true;
}
/**
* @zh
* 移除首个指定的数组元素。判定元素相等时相当于于使用了 `Array.prototype.indexOf`。
* 此函数十分高效,但会改变数组的元素次序。
* @en
* Removes the first occurrence of a specific object from the array.
* Decision of the equality of elements is similar to `Array.prototype.indexOf`.
* It's faster but the order of the array will be changed.
* @param array 数组。
* @param value 待移除元素。
*/
template <typename T>
bool fastRemove(ccstd::vector<T> &array, T value) {
auto iter = std::find(array.begin(), array.end(), value);
if (iter != array.end()) {
*iter = array[array.size() - 1];
array.resize(array.size() - 1);
return true;
}
return false;
}
} // namespace array
} // namespace utils
} // namespace cc

829
cocos/base/Value.cpp Normal file
View File

@@ -0,0 +1,829 @@
/****************************************************************************
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/Value.h"
#include <memory.h>
#include <algorithm>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <sstream>
#include "base/Utils.h"
#include "base/memory/Memory.h"
namespace cc {
const ValueVector VALUE_VECTOR_NULL;
const ValueMap VALUE_MAP_NULL = {};
const ValueMapIntKey VALUE_MAP_INT_KEY_NULL = {};
const Value Value::VALUE_NULL;
Value::Value()
: _type(Type::NONE) {
memset(&_field, 0, sizeof(_field));
}
Value::Value(unsigned char v)
: _type(Type::BYTE) {
_field.byteVal = v;
}
Value::Value(int v)
: _type(Type::INTEGER) {
_field.intVal = v;
}
Value::Value(unsigned int v)
: _type(Type::UNSIGNED) {
_field.unsignedVal = v;
}
Value::Value(float v)
: _type(Type::FLOAT) {
_field.floatVal = v;
}
Value::Value(double v)
: _type(Type::DOUBLE) {
_field.doubleVal = v;
}
Value::Value(bool v)
: _type(Type::BOOLEAN) {
_field.boolVal = v;
}
Value::Value(const char *v)
: _type(Type::STRING) {
_field.strVal = ccnew ccstd::string();
if (v) {
*_field.strVal = v;
}
}
Value::Value(const ccstd::string &v)
: _type(Type::STRING) {
_field.strVal = ccnew ccstd::string();
*_field.strVal = v;
}
Value::Value(const ValueVector &v)
: _type(Type::VECTOR) {
_field.vectorVal = ccnew ValueVector();
*_field.vectorVal = v;
}
Value::Value(ValueVector &&v)
: _type(Type::VECTOR) {
_field.vectorVal = ccnew ValueVector();
*_field.vectorVal = std::move(v);
}
Value::Value(const ValueMap &v)
: _type(Type::MAP) {
_field.mapVal = ccnew ValueMap();
*_field.mapVal = v;
}
Value::Value(ValueMap &&v)
: _type(Type::MAP) {
_field.mapVal = ccnew ValueMap();
*_field.mapVal = std::move(v);
}
Value::Value(const ValueMapIntKey &v)
: _type(Type::INT_KEY_MAP) {
_field.intKeyMapVal = ccnew ValueMapIntKey();
*_field.intKeyMapVal = v;
}
Value::Value(ValueMapIntKey &&v)
: _type(Type::INT_KEY_MAP) {
_field.intKeyMapVal = ccnew ValueMapIntKey();
*_field.intKeyMapVal = std::move(v);
}
Value::Value(const Value &other) //NOLINT(misc-no-recursion)
: _type(Type::NONE) {
*this = other;
}
Value::Value(Value &&other) noexcept
: _type(Type::NONE) {
*this = std::move(other);
}
Value::~Value() {
clear();
}
Value &Value::operator=(const Value &other) { //NOLINT(misc-no-recursion)
if (this != &other) {
reset(other._type);
switch (other._type) {
case Type::BYTE:
_field.byteVal = other._field.byteVal;
break;
case Type::INTEGER:
_field.intVal = other._field.intVal;
break;
case Type::UNSIGNED:
_field.unsignedVal = other._field.unsignedVal;
break;
case Type::FLOAT:
_field.floatVal = other._field.floatVal;
break;
case Type::DOUBLE:
_field.doubleVal = other._field.doubleVal;
break;
case Type::BOOLEAN:
_field.boolVal = other._field.boolVal;
break;
case Type::STRING:
if (_field.strVal == nullptr) {
_field.strVal = ccnew ccstd::string();
}
*_field.strVal = *other._field.strVal;
break;
case Type::VECTOR:
if (_field.vectorVal == nullptr) {
_field.vectorVal = ccnew ValueVector();
}
*_field.vectorVal = *other._field.vectorVal;
break;
case Type::MAP:
if (_field.mapVal == nullptr) {
_field.mapVal = ccnew ValueMap();
}
*_field.mapVal = *other._field.mapVal;
break;
case Type::INT_KEY_MAP:
if (_field.intKeyMapVal == nullptr) {
_field.intKeyMapVal = ccnew ValueMapIntKey();
}
*_field.intKeyMapVal = *other._field.intKeyMapVal;
break;
default:
break;
}
}
return *this;
}
Value &Value::operator=(Value &&other) noexcept {
if (this != &other) {
clear();
switch (other._type) {
case Type::BYTE:
_field.byteVal = other._field.byteVal;
break;
case Type::INTEGER:
_field.intVal = other._field.intVal;
break;
case Type::UNSIGNED:
_field.unsignedVal = other._field.unsignedVal;
break;
case Type::FLOAT:
_field.floatVal = other._field.floatVal;
break;
case Type::DOUBLE:
_field.doubleVal = other._field.doubleVal;
break;
case Type::BOOLEAN:
_field.boolVal = other._field.boolVal;
break;
case Type::STRING:
_field.strVal = other._field.strVal;
break;
case Type::VECTOR:
_field.vectorVal = other._field.vectorVal;
break;
case Type::MAP:
_field.mapVal = other._field.mapVal;
break;
case Type::INT_KEY_MAP:
_field.intKeyMapVal = other._field.intKeyMapVal;
break;
default:
break;
}
_type = other._type;
memset(&other._field, 0, sizeof(other._field));
other._type = Type::NONE;
}
return *this;
}
Value &Value::operator=(unsigned char v) {
reset(Type::BYTE);
_field.byteVal = v;
return *this;
}
Value &Value::operator=(int v) {
reset(Type::INTEGER);
_field.intVal = v;
return *this;
}
Value &Value::operator=(unsigned int v) {
reset(Type::UNSIGNED);
_field.unsignedVal = v;
return *this;
}
Value &Value::operator=(float v) {
reset(Type::FLOAT);
_field.floatVal = v;
return *this;
}
Value &Value::operator=(double v) {
reset(Type::DOUBLE);
_field.doubleVal = v;
return *this;
}
Value &Value::operator=(bool v) {
reset(Type::BOOLEAN);
_field.boolVal = v;
return *this;
}
Value &Value::operator=(const char *v) {
reset(Type::STRING);
*_field.strVal = v ? v : "";
return *this;
}
Value &Value::operator=(const ccstd::string &v) {
reset(Type::STRING);
*_field.strVal = v;
return *this;
}
Value &Value::operator=(const ValueVector &v) {
reset(Type::VECTOR);
*_field.vectorVal = v;
return *this;
}
Value &Value::operator=(ValueVector &&v) {
reset(Type::VECTOR);
*_field.vectorVal = std::move(v);
return *this;
}
Value &Value::operator=(const ValueMap &v) {
reset(Type::MAP);
*_field.mapVal = v;
return *this;
}
Value &Value::operator=(ValueMap &&v) {
reset(Type::MAP);
*_field.mapVal = std::move(v);
return *this;
}
Value &Value::operator=(const ValueMapIntKey &v) {
reset(Type::INT_KEY_MAP);
*_field.intKeyMapVal = v;
return *this;
}
Value &Value::operator=(ValueMapIntKey &&v) {
reset(Type::INT_KEY_MAP);
*_field.intKeyMapVal = std::move(v);
return *this;
}
bool Value::operator!=(const Value &v) {
return !(*this == v);
}
bool Value::operator!=(const Value &v) const { //NOLINT(misc-no-recursion)
return !(*this == v);
}
bool Value::operator==(const Value &v) {
const auto &t = *this;
return t == v;
}
bool Value::operator==(const Value &v) const { //NOLINT(misc-no-recursion)
if (this == &v) {
return true;
}
if (v._type != this->_type) {
return false;
}
if (this->isNull()) {
return true;
}
switch (_type) {
case Type::BYTE: return v._field.byteVal == this->_field.byteVal;
case Type::INTEGER: return v._field.intVal == this->_field.intVal;
case Type::UNSIGNED: return v._field.unsignedVal == this->_field.unsignedVal;
case Type::BOOLEAN: return v._field.boolVal == this->_field.boolVal;
case Type::STRING: return *v._field.strVal == *this->_field.strVal;
case Type::FLOAT: return std::abs(v._field.floatVal - this->_field.floatVal) <= FLT_EPSILON;
case Type::DOUBLE: return std::abs(v._field.doubleVal - this->_field.doubleVal) <= DBL_EPSILON;
case Type::VECTOR: {
const auto &v1 = *(this->_field.vectorVal);
const auto &v2 = *(v._field.vectorVal);
const auto size = v1.size();
if (size == v2.size()) {
for (size_t i = 0; i < size; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
return true;
}
return false;
}
case Type::MAP: {
const auto &map1 = *(this->_field.mapVal);
const auto &map2 = *(v._field.mapVal);
for (const auto &kvp : map1) { //NOLINT
auto it = map2.find(kvp.first);
if (it == map2.end() || it->second != kvp.second) {
return false;
}
}
return true;
}
case Type::INT_KEY_MAP: {
const auto &map1 = *(this->_field.intKeyMapVal);
const auto &map2 = *(v._field.intKeyMapVal);
for (const auto &kvp : map1) { //NOLINT
auto it = map2.find(kvp.first);
if (it == map2.end() || it->second != kvp.second) {
return false;
}
}
return true;
}
default:
break;
};
return false;
}
/// Convert value to a specified type
unsigned char Value::asByte() const {
CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP);
if (_type == Type::BYTE) {
return _field.byteVal;
}
if (_type == Type::INTEGER) {
return static_cast<unsigned char>(_field.intVal);
}
if (_type == Type::UNSIGNED) {
return static_cast<unsigned char>(_field.unsignedVal);
}
if (_type == Type::STRING) {
return static_cast<unsigned char>(atoi(_field.strVal->c_str()));
}
if (_type == Type::FLOAT) {
return static_cast<unsigned char>(_field.floatVal);
}
if (_type == Type::DOUBLE) {
return static_cast<unsigned char>(_field.doubleVal);
}
if (_type == Type::BOOLEAN) {
return _field.boolVal ? 1 : 0;
}
return 0;
}
int Value::asInt() const {
CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP);
if (_type == Type::INTEGER) {
return _field.intVal;
}
if (_type == Type::UNSIGNED) {
CC_ASSERT(_field.unsignedVal < INT_MAX);
return static_cast<int>(_field.unsignedVal);
}
if (_type == Type::BYTE) {
return _field.byteVal;
}
if (_type == Type::STRING) {
return atoi(_field.strVal->c_str());
}
if (_type == Type::FLOAT) {
return static_cast<int>(_field.floatVal);
}
if (_type == Type::DOUBLE) {
return static_cast<int>(_field.doubleVal);
}
if (_type == Type::BOOLEAN) {
return _field.boolVal ? 1 : 0;
}
return 0;
}
unsigned int Value::asUnsignedInt() const {
CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP);
if (_type == Type::UNSIGNED) {
return _field.unsignedVal;
}
if (_type == Type::INTEGER) {
CC_ASSERT(_field.intVal >= 0);
return static_cast<unsigned int>(_field.intVal);
}
if (_type == Type::BYTE) {
return static_cast<unsigned int>(_field.byteVal);
}
if (_type == Type::STRING) {
// NOTE: strtoul is required (need to augment on unsupported platforms)
return static_cast<unsigned int>(strtoul(_field.strVal->c_str(), nullptr, 10));
}
if (_type == Type::FLOAT) {
return static_cast<unsigned int>(_field.floatVal);
}
if (_type == Type::DOUBLE) {
return static_cast<unsigned int>(_field.doubleVal);
}
if (_type == Type::BOOLEAN) {
return _field.boolVal ? 1U : 0U;
}
return 0U;
}
float Value::asFloat() const {
CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP);
if (_type == Type::FLOAT) {
return _field.floatVal;
}
if (_type == Type::BYTE) {
return static_cast<float>(_field.byteVal);
}
if (_type == Type::STRING) {
return static_cast<float>(utils::atof(_field.strVal->c_str()));
}
if (_type == Type::INTEGER) {
return static_cast<float>(_field.intVal);
}
if (_type == Type::UNSIGNED) {
return static_cast<float>(_field.unsignedVal);
}
if (_type == Type::DOUBLE) {
return static_cast<float>(_field.doubleVal);
}
if (_type == Type::BOOLEAN) {
return _field.boolVal ? 1.0F : 0.0F;
}
return 0.0F;
}
double Value::asDouble() const {
CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP);
if (_type == Type::DOUBLE) {
return _field.doubleVal;
}
if (_type == Type::BYTE) {
return static_cast<double>(_field.byteVal);
}
if (_type == Type::STRING) {
return static_cast<double>(utils::atof(_field.strVal->c_str()));
}
if (_type == Type::INTEGER) {
return static_cast<double>(_field.intVal);
}
if (_type == Type::UNSIGNED) {
return static_cast<double>(_field.unsignedVal);
}
if (_type == Type::FLOAT) {
return static_cast<double>(_field.floatVal);
}
if (_type == Type::BOOLEAN) {
return _field.boolVal ? 1.0 : 0.0;
}
return 0.0;
}
bool Value::asBool() const {
CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP);
if (_type == Type::BOOLEAN) {
return _field.boolVal;
}
if (_type == Type::BYTE) {
return _field.byteVal != 0;
}
if (_type == Type::STRING) {
return *_field.strVal != "0" || *_field.strVal != "false";
}
if (_type == Type::INTEGER) {
return _field.intVal != 0;
}
if (_type == Type::UNSIGNED) {
return _field.unsignedVal != 0;
}
if (_type == Type::FLOAT) {
return _field.floatVal != 0.0F;
}
if (_type == Type::DOUBLE) {
return _field.doubleVal != 0.0;
}
return false;
}
ccstd::string Value::asString() const {
CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP);
if (_type == Type::STRING) {
return *_field.strVal;
}
std::stringstream ret;
switch (_type) {
case Type::BYTE:
ret << _field.byteVal;
break;
case Type::INTEGER:
ret << _field.intVal;
break;
case Type::UNSIGNED:
ret << _field.unsignedVal;
break;
case Type::FLOAT:
ret << std::fixed << std::setprecision(7) << _field.floatVal;
break;
case Type::DOUBLE:
ret << std::fixed << std::setprecision(16) << _field.doubleVal;
break;
case Type::BOOLEAN:
ret << (_field.boolVal ? "true" : "false");
break;
default:
break;
}
return ret.str();
}
ValueVector &Value::asValueVector() {
CC_ASSERT_EQ(_type, Type::VECTOR);
return *_field.vectorVal;
}
const ValueVector &Value::asValueVector() const {
CC_ASSERT_EQ(_type, Type::VECTOR);
return *_field.vectorVal;
}
ValueMap &Value::asValueMap() {
CC_ASSERT_EQ(_type, Type::MAP);
return *_field.mapVal;
}
const ValueMap &Value::asValueMap() const {
CC_ASSERT_EQ(_type, Type::MAP);
return *_field.mapVal;
}
ValueMapIntKey &Value::asIntKeyMap() {
CC_ASSERT_EQ(_type, Type::INT_KEY_MAP);
return *_field.intKeyMapVal;
}
const ValueMapIntKey &Value::asIntKeyMap() const {
CC_ASSERT_EQ(_type, Type::INT_KEY_MAP);
return *_field.intKeyMapVal;
}
static ccstd::string getTabs(int depth) {
ccstd::string tabWidth;
for (int i = 0; i < depth; ++i) {
tabWidth += "\t";
}
return tabWidth;
}
static ccstd::string visit(const Value &v, int depth);
static ccstd::string visitVector(const ValueVector &v, int depth) { //NOLINT[misc-no-recursion]
std::stringstream ret;
if (depth > 0) {
ret << "\n";
}
ret << getTabs(depth) << "[\n";
int i = 0;
for (const auto &child : v) {
ret << getTabs(depth + 1) << i << ": " << visit(child, depth + 1);
++i;
}
ret << getTabs(depth) << "]\n";
return ret.str();
}
template <class T>
static ccstd::string visitMap(const T &v, int depth) { //NOLINT[misc-no-recursion]
std::stringstream ret;
if (depth > 0) {
ret << "\n";
}
ret << getTabs(depth) << "{\n";
for (auto iter = v.begin(); iter != v.end(); ++iter) {
ret << getTabs(depth + 1) << iter->first << ": ";
ret << visit(iter->second, depth + 1);
}
ret << getTabs(depth) << "}\n";
return ret.str();
}
static ccstd::string visit(const Value &v, int depth) { //NOLINT[misc-no-recursion]
std::stringstream ret;
switch (v.getType()) {
case Value::Type::NONE:
case Value::Type::BYTE:
case Value::Type::INTEGER:
case Value::Type::UNSIGNED:
case Value::Type::FLOAT:
case Value::Type::DOUBLE:
case Value::Type::BOOLEAN:
case Value::Type::STRING:
ret << v.asString() << "\n";
break;
case Value::Type::VECTOR:
ret << visitVector(v.asValueVector(), depth);
break;
case Value::Type::MAP:
ret << visitMap(v.asValueMap(), depth);
break;
case Value::Type::INT_KEY_MAP:
ret << visitMap(v.asIntKeyMap(), depth);
break;
default:
CC_ABORT();
break;
}
return ret.str();
}
ccstd::string Value::getDescription() const {
ccstd::string ret("\n");
ret += visit(*this, 0);
return ret;
}
void Value::clear() {
// Free memory the old value allocated
switch (_type) {
case Type::BYTE:
_field.byteVal = 0;
break;
case Type::INTEGER:
_field.intVal = 0;
break;
case Type::UNSIGNED:
_field.unsignedVal = 0U;
break;
case Type::FLOAT:
_field.floatVal = 0.0F;
break;
case Type::DOUBLE:
_field.doubleVal = 0.0;
break;
case Type::BOOLEAN:
_field.boolVal = false;
break;
case Type::STRING:
CC_SAFE_DELETE(_field.strVal);
break;
case Type::VECTOR:
CC_SAFE_DELETE(_field.vectorVal);
break;
case Type::MAP:
CC_SAFE_DELETE(_field.mapVal);
break;
case Type::INT_KEY_MAP:
CC_SAFE_DELETE(_field.intKeyMapVal);
break;
default:
break;
}
_type = Type::NONE;
}
void Value::reset(Type type) {
if (_type == type) {
return;
}
clear();
// Allocate memory for the new value
switch (type) {
case Type::STRING:
_field.strVal = ccnew ccstd::string();
break;
case Type::VECTOR:
_field.vectorVal = ccnew ValueVector();
break;
case Type::MAP:
_field.mapVal = ccnew ValueMap();
break;
case Type::INT_KEY_MAP:
_field.intKeyMapVal = ccnew ValueMapIntKey();
break;
default:
break;
}
_type = type;
}
} // namespace cc

238
cocos/base/Value.h Normal file
View File

@@ -0,0 +1,238 @@
/****************************************************************************
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "base/std/container/vector.h"
namespace cc {
class Value;
using ValueVector = ccstd::vector<Value>;
using ValueMap = ccstd::unordered_map<ccstd::string, Value>;
using ValueMapIntKey = ccstd::unordered_map<int, Value>;
CC_DLL extern const ValueVector VALUE_VECTOR_NULL;
CC_DLL extern const ValueMap VALUE_MAP_NULL;
CC_DLL extern const ValueMapIntKey VALUE_MAP_INT_KEY_NULL;
/*
* This class is provide as a wrapper of basic types, such as int and bool.
*/
class CC_DLL Value {
public:
/** A predefined Value that has not value. */
static const Value VALUE_NULL;
/** Default constructor. */
Value();
/** Create a Value by an unsigned char value. */
explicit Value(unsigned char v);
/** Create a Value by an integer value. */
explicit Value(int v);
/** Create a Value by an unsigned value. */
explicit Value(unsigned int v);
/** Create a Value by a float value. */
explicit Value(float v);
/** Create a Value by a double value. */
explicit Value(double v);
/** Create a Value by a bool value. */
explicit Value(bool v);
/** Create a Value by a char pointer. It will copy the chars internally. */
explicit Value(const char *v);
/** Create a Value by a string. */
explicit Value(const ccstd::string &v);
/** Create a Value by a ValueVector object. */
explicit Value(const ValueVector &v);
/** Create a Value by a ValueVector object. It will use std::move internally. */
explicit Value(ValueVector &&v);
/** Create a Value by a ValueMap object. */
explicit Value(const ValueMap &v);
/** Create a Value by a ValueMap object. It will use std::move internally. */
explicit Value(ValueMap &&v);
/** Create a Value by a ValueMapIntKey object. */
explicit Value(const ValueMapIntKey &v);
/** Create a Value by a ValueMapIntKey object. It will use std::move internally. */
explicit Value(ValueMapIntKey &&v);
/** Create a Value by another Value object. */
Value(const Value &other);
/** Create a Value by a Value object. It will use std::move internally. */
Value(Value &&other) noexcept;
/** Destructor. */
~Value();
/** Assignment operator, assign from Value to Value. */
Value &operator=(const Value &other);
/** Assignment operator, assign from Value to Value. It will use std::move internally. */
Value &operator=(Value &&other) noexcept;
/** Assignment operator, assign from unsigned char to Value. */
Value &operator=(unsigned char v);
/** Assignment operator, assign from integer to Value. */
Value &operator=(int v);
/** Assignment operator, assign from integer to Value. */
Value &operator=(unsigned int v);
/** Assignment operator, assign from float to Value. */
Value &operator=(float v);
/** Assignment operator, assign from double to Value. */
Value &operator=(double v);
/** Assignment operator, assign from bool to Value. */
Value &operator=(bool v);
/** Assignment operator, assign from char* to Value. */
Value &operator=(const char *v);
/** Assignment operator, assign from string to Value. */
Value &operator=(const ccstd::string &v);
/** Assignment operator, assign from ValueVector to Value. */
Value &operator=(const ValueVector &v);
/** Assignment operator, assign from ValueVector to Value. */
Value &operator=(ValueVector &&v);
/** Assignment operator, assign from ValueMap to Value. */
Value &operator=(const ValueMap &v);
/** Assignment operator, assign from ValueMap to Value. It will use std::move internally. */
Value &operator=(ValueMap &&v);
/** Assignment operator, assign from ValueMapIntKey to Value. */
Value &operator=(const ValueMapIntKey &v);
/** Assignment operator, assign from ValueMapIntKey to Value. It will use std::move internally. */
Value &operator=(ValueMapIntKey &&v);
/** != operator overloading */
bool operator!=(const Value &v);
/** != operator overloading */
bool operator!=(const Value &v) const;
/** == operator overloading */
bool operator==(const Value &v);
/** == operator overloading */
bool operator==(const Value &v) const;
/** Gets as a byte value. Will convert to unsigned char if possible, or will trigger assert error. */
unsigned char asByte() const;
/** Gets as an integer value. Will convert to integer if possible, or will trigger assert error. */
int asInt() const;
/** Gets as an unsigned value. Will convert to unsigned if possible, or will trigger assert error. */
unsigned int asUnsignedInt() const;
/** Gets as a float value. Will convert to float if possible, or will trigger assert error. */
float asFloat() const;
/** Gets as a double value. Will convert to double if possible, or will trigger assert error. */
double asDouble() const;
/** Gets as a bool value. Will convert to bool if possible, or will trigger assert error. */
bool asBool() const;
/** Gets as a string value. Will convert to string if possible, or will trigger assert error. */
ccstd::string asString() const;
/** Gets as a ValueVector reference. Will convert to ValueVector if possible, or will trigger assert error. */
ValueVector &asValueVector();
/** Gets as a const ValueVector reference. Will convert to ValueVector if possible, or will trigger assert error. */
const ValueVector &asValueVector() const;
/** Gets as a ValueMap reference. Will convert to ValueMap if possible, or will trigger assert error. */
ValueMap &asValueMap();
/** Gets as a const ValueMap reference. Will convert to ValueMap if possible, or will trigger assert error. */
const ValueMap &asValueMap() const;
/** Gets as a ValueMapIntKey reference. Will convert to ValueMapIntKey if possible, or will trigger assert error. */
ValueMapIntKey &asIntKeyMap();
/** Gets as a const ValueMapIntKey reference. Will convert to ValueMapIntKey if possible, or will trigger assert error. */
const ValueMapIntKey &asIntKeyMap() const;
/**
* Checks if the Value is null.
* @return True if the Value is null, false if not.
*/
inline bool isNull() const { return _type == Type::NONE; }
/** Value type wrapped by Value. */
enum class Type {
/// no value is wrapped, an empty Value
NONE = 0,
/// wrap byte
BYTE,
/// wrap integer
INTEGER,
/// wrap unsigned
UNSIGNED,
/// wrap float
FLOAT,
/// wrap double
DOUBLE,
/// wrap bool
BOOLEAN,
/// wrap string
STRING,
/// wrap vector
VECTOR,
/// wrap ValueMap
MAP,
/// wrap ValueMapIntKey
INT_KEY_MAP
};
/** Gets the value type. */
inline Type getType() const { return _type; }
/** Gets the description of the class. */
ccstd::string getDescription() const;
private:
void clear();
void reset(Type type);
union {
unsigned char byteVal;
int intVal;
unsigned int unsignedVal;
float floatVal;
double doubleVal;
bool boolVal;
ccstd::string *strVal;
ValueVector *vectorVal;
ValueMap *mapVal;
ValueMapIntKey *intKeyMapVal;
} _field;
Type _type;
};
} // namespace cc

657
cocos/base/ZipUtils.cpp Normal file
View File

@@ -0,0 +1,657 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
// IDEA: hack, must be included before ziputils
#include "base/ZipUtils.h"
#include <mutex>
#ifdef MINIZIP_FROM_SYSTEM
#include <minizip/unzip.h>
#else // from our embedded sources
#include "unzip/unzip.h"
#endif
#include <zlib.h>
#include <cstdlib>
#include <memory>
#include "base/Data.h"
#include "base/Locked.h"
#include "base/Log.h"
#include "base/memory/Memory.h"
#include "platform/FileUtils.h"
#include "unzip/ioapi_mem.h"
// minizip 1.2.0 is same with other platforms
#ifndef unzGoToFirstFile64
#define unzGoToFirstFile64(A, B, C, D) unzGoToFirstFile2(A, B, C, D, NULL, 0, NULL, 0) // NOLINT(readability-identifier-naming)
#define unzGoToNextFile64(A, B, C, D) unzGoToNextFile2(A, B, C, D, NULL, 0, NULL, 0) // NOLINT(readability-identifier-naming)#endif
#endif
namespace cc {
unsigned int ZipUtils::encryptedPvrKeyParts[4] = {0, 0, 0, 0};
unsigned int ZipUtils::encryptionKey[1024];
bool ZipUtils::encryptionKeyIsValid = false;
// --------------------- ZipUtils ---------------------
inline void ZipUtils::decodeEncodedPvr(unsigned int *data, uint32_t len) {
const int enclen = 1024;
const int securelen = 512;
const int distance = 64;
// check if key was set
// make sure to call caw_setkey_part() for all 4 key parts
CC_ASSERT(ZipUtils::encryptedPvrKeyParts[0] != 0); // CCZ file is encrypted but key part 0 is not set. Call ZipUtils::setPvrEncryptionKeyPart(...)?
CC_ASSERT(ZipUtils::encryptedPvrKeyParts[1] != 0); // CCZ file is encrypted but key part 1 is not set. Call ZipUtils::setPvrEncryptionKeyPart(...)?
CC_ASSERT(ZipUtils::encryptedPvrKeyParts[2] != 0); // CCZ file is encrypted but key part 2 is not set. Call ZipUtils::setPvrEncryptionKeyPart(...)?
CC_ASSERT(ZipUtils::encryptedPvrKeyParts[3] != 0); // CCZ file is encrypted but key part 3 is not set. Call ZipUtils::setPvrEncryptionKeyPart(...)?
// create long key
if (!ZipUtils::encryptionKeyIsValid) {
unsigned int y{0};
unsigned int p{0};
unsigned int e{0};
unsigned int rounds = 6;
unsigned int sum = 0;
unsigned int z = ZipUtils::encryptionKey[enclen - 1];
do {
#define DELTA 0x9e3779b9
#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (ZipUtils::encryptedPvrKeyParts[(p & 3) ^ e] ^ z)))
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < enclen - 1; p++) {
y = ZipUtils::encryptionKey[p + 1];
z = ZipUtils::encryptionKey[p] += MX;
}
y = ZipUtils::encryptionKey[0];
z = ZipUtils::encryptionKey[enclen - 1] += MX;
} while (--rounds);
ZipUtils::encryptionKeyIsValid = true;
}
int b = 0;
int i = 0;
// encrypt first part completely
for (; i < len && i < securelen; i++) {
data[i] ^= ZipUtils::encryptionKey[b++];
if (b >= enclen) {
b = 0;
}
}
// encrypt second section partially
for (; i < len; i += distance) {
data[i] ^= ZipUtils::encryptionKey[b++];
if (b >= enclen) {
b = 0;
}
}
}
inline unsigned int ZipUtils::checksumPvr(const unsigned int *data, uint32_t len) {
unsigned int cs = 0;
const int cslen = 128;
len = (len < cslen) ? len : cslen;
for (int i = 0; i < len; i++) {
cs = cs ^ data[i];
}
return cs;
}
// memory in iPhone is precious
// Should buffer factor be 1.5 instead of 2 ?
#define BUFFER_INC_FACTOR (2)
int ZipUtils::inflateMemoryWithHint(unsigned char *in, uint32_t inLength, unsigned char **out, uint32_t *outLength, uint32_t outLengthHint) {
/* ret value */
int err = Z_OK;
uint32_t bufferSize = outLengthHint;
*out = static_cast<unsigned char *>(malloc(bufferSize));
z_stream descompressionStream; /* decompression stream */
descompressionStream.zalloc = static_cast<alloc_func>(nullptr);
descompressionStream.zfree = static_cast<free_func>(nullptr);
descompressionStream.opaque = static_cast<voidpf>(nullptr);
descompressionStream.next_in = in;
descompressionStream.avail_in = inLength;
descompressionStream.next_out = *out;
descompressionStream.avail_out = bufferSize;
/* window size to hold 256k */
if ((err = inflateInit2(&descompressionStream, 15 + 32)) != Z_OK) {
return err;
}
for (;;) {
err = inflate(&descompressionStream, Z_NO_FLUSH);
if (err == Z_STREAM_END) {
break;
}
switch (err) {
case Z_NEED_DICT:
err = Z_DATA_ERROR;
case Z_DATA_ERROR:
case Z_MEM_ERROR:
inflateEnd(&descompressionStream);
return err;
}
// not enough memory ?
if (err != Z_STREAM_END) {
*out = static_cast<unsigned char *>(realloc(*out, bufferSize * BUFFER_INC_FACTOR));
/* not enough memory, ouch */
if (!*out) {
CC_LOG_DEBUG("ZipUtils: realloc failed");
inflateEnd(&descompressionStream);
return Z_MEM_ERROR;
}
descompressionStream.next_out = *out + bufferSize;
descompressionStream.avail_out = static_cast<unsigned int>(bufferSize);
bufferSize *= BUFFER_INC_FACTOR;
}
}
*outLength = bufferSize - descompressionStream.avail_out;
err = inflateEnd(&descompressionStream);
return err;
}
uint32_t ZipUtils::inflateMemoryWithHint(unsigned char *in, uint32_t inLength, unsigned char **out, uint32_t outLengthHint) {
uint32_t outLength = 0;
int err = inflateMemoryWithHint(in, inLength, out, &outLength, outLengthHint);
if (err != Z_OK || *out == nullptr) {
if (err == Z_MEM_ERROR) {
CC_LOG_DEBUG("ZipUtils: Out of memory while decompressing map data!");
} else if (err == Z_VERSION_ERROR) {
CC_LOG_DEBUG("ZipUtils: Incompatible zlib version!");
} else if (err == Z_DATA_ERROR) {
CC_LOG_DEBUG("ZipUtils: Incorrect zlib compressed data!");
} else {
CC_LOG_DEBUG("ZipUtils: Unknown error while decompressing map data!");
}
if (*out) {
free(*out);
*out = nullptr;
}
outLength = 0;
}
return outLength;
}
uint32_t ZipUtils::inflateMemory(unsigned char *in, uint32_t inLength, unsigned char **out) {
// 256k for hint
return inflateMemoryWithHint(in, inLength, out, 256 * 1024);
}
int ZipUtils::inflateGZipFile(const char *path, unsigned char **out) {
int len;
int offset = 0;
CC_ASSERT(out);
CC_ASSERT(&*out);
gzFile inFile = gzopen(FileUtils::getInstance()->getSuitableFOpen(path).c_str(), "rb");
if (inFile == nullptr) {
CC_LOG_DEBUG("ZipUtils: error open gzip file: %s", path);
return -1;
}
/* 512k initial decompress buffer */
unsigned int bufferSize = 512 * 1024;
unsigned int totalBufferSize = bufferSize;
*out = static_cast<unsigned char *>(malloc(bufferSize));
if (!out) {
CC_LOG_DEBUG("ZipUtils: out of memory");
return -1;
}
for (;;) {
len = gzread(inFile, *out + offset, bufferSize);
if (len < 0) {
CC_LOG_DEBUG("ZipUtils: error in gzread");
free(*out);
*out = nullptr;
return -1;
}
if (len == 0) {
break;
}
offset += len;
// finish reading the file
if (static_cast<unsigned int>(len) < bufferSize) {
break;
}
bufferSize *= BUFFER_INC_FACTOR;
totalBufferSize += bufferSize;
auto *tmp = static_cast<unsigned char *>(realloc(*out, totalBufferSize));
if (!tmp) {
CC_LOG_DEBUG("ZipUtils: out of memory");
free(*out);
*out = nullptr;
return -1;
}
*out = tmp;
}
if (gzclose(inFile) != Z_OK) {
CC_LOG_DEBUG("ZipUtils: gzclose failed");
}
return offset;
}
bool ZipUtils::isCCZFile(const char *path) {
// load file into memory
Data compressedData = FileUtils::getInstance()->getDataFromFile(path);
if (compressedData.isNull()) {
CC_LOG_DEBUG("ZipUtils: loading file failed");
return false;
}
return isCCZBuffer(compressedData.getBytes(), compressedData.getSize());
}
bool ZipUtils::isCCZBuffer(const unsigned char *buffer, uint32_t len) {
if (len < sizeof(struct CCZHeader)) {
return false;
}
const auto *header = reinterpret_cast<const struct CCZHeader *>(buffer);
return header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && (header->sig[3] == '!' || header->sig[3] == 'p');
}
bool ZipUtils::isGZipFile(const char *path) {
// load file into memory
Data compressedData = FileUtils::getInstance()->getDataFromFile(path);
if (compressedData.isNull()) {
CC_LOG_DEBUG("ZipUtils: loading file failed");
return false;
}
return isGZipBuffer(compressedData.getBytes(), compressedData.getSize());
}
bool ZipUtils::isGZipBuffer(const unsigned char *buffer, uint32_t len) {
if (len < 2) {
return false;
}
return buffer[0] == 0x1F && buffer[1] == 0x8B;
}
int ZipUtils::inflateCCZBuffer(const unsigned char *buffer, uint32_t bufferLen, unsigned char **out) {
const auto *header = reinterpret_cast<const struct CCZHeader *>(buffer);
// verify header
if (header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && header->sig[3] == '!') {
// verify header version
unsigned int version = CC_SWAP_INT16_BIG_TO_HOST(header->version);
if (version > 2) {
CC_LOG_DEBUG("Unsupported CCZ header format");
return -1;
}
// verify compression format
if (CC_SWAP_INT16_BIG_TO_HOST(header->compression_type) != CCZ_COMPRESSION_ZLIB) {
CC_LOG_DEBUG("CCZ Unsupported compression method");
return -1;
}
} else if (header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && header->sig[3] == 'p') {
// encrypted ccz file
header = reinterpret_cast<const struct CCZHeader *>(buffer);
// verify header version
unsigned int version = CC_SWAP_INT16_BIG_TO_HOST(header->version);
if (version > 0) {
CC_LOG_DEBUG("Unsupported CCZ header format");
return -1;
}
// verify compression format
if (CC_SWAP_INT16_BIG_TO_HOST(header->compression_type) != CCZ_COMPRESSION_ZLIB) {
CC_LOG_DEBUG("CCZ Unsupported compression method");
return -1;
}
#if CC_DEBUG > 0
// decrypt
auto *ints = reinterpret_cast<unsigned int *>(const_cast<unsigned char *>(buffer) + 12);
uint32_t enclen = (bufferLen - 12) / 4;
decodeEncodedPvr(ints, enclen);
// verify checksum in debug mode
unsigned int calculated = checksumPvr(ints, enclen);
unsigned int required = CC_SWAP_INT32_BIG_TO_HOST(header->reserved);
if (calculated != required) {
CC_LOG_DEBUG("Can't decrypt image file. Is the decryption key valid?");
return -1;
}
#endif
} else {
CC_LOG_DEBUG("Invalid CCZ file");
return -1;
}
unsigned int len = CC_SWAP_INT32_BIG_TO_HOST(header->len);
*out = static_cast<unsigned char *>(malloc(len));
if (!*out) {
CC_LOG_DEBUG("CCZ: Failed to allocate memory for texture");
return -1;
}
uLongf destlen = len;
const auto *source = reinterpret_cast<const Bytef *>(buffer + sizeof(*header));
int ret = uncompress(*out, &destlen, source, static_cast<uLong>(bufferLen - sizeof(*header)));
if (ret != Z_OK) {
CC_LOG_DEBUG("CCZ: Failed to uncompress data");
free(*out);
*out = nullptr;
return -1;
}
return static_cast<int>(len);
}
int ZipUtils::inflateCCZFile(const char *path, unsigned char **out) {
CC_ASSERT(out);
// load file into memory
Data compressedData = FileUtils::getInstance()->getDataFromFile(path);
if (compressedData.isNull()) {
CC_LOG_DEBUG("Error loading CCZ compressed file");
return -1;
}
return inflateCCZBuffer(compressedData.getBytes(), compressedData.getSize(), out);
}
void ZipUtils::setPvrEncryptionKeyPart(int index, unsigned int value) {
CC_ASSERT_GE(index, 0);
CC_ASSERT_LE(index, 3);
if (ZipUtils::encryptedPvrKeyParts[index] != value) {
ZipUtils::encryptedPvrKeyParts[index] = value;
ZipUtils::encryptionKeyIsValid = false;
}
}
void ZipUtils::setPvrEncryptionKey(unsigned int keyPart1, unsigned int keyPart2, unsigned int keyPart3, unsigned int keyPart4) {
setPvrEncryptionKeyPart(0, keyPart1);
setPvrEncryptionKeyPart(1, keyPart2);
setPvrEncryptionKeyPart(2, keyPart3);
setPvrEncryptionKeyPart(3, keyPart4);
}
// --------------------- ZipFile ---------------------
// from unzip.cpp
#define UNZ_MAXFILENAMEINZIP 256
static const ccstd::string EMPTY_FILE_NAME;
struct ZipEntryInfo {
unz_file_pos pos;
uLong uncompressed_size;
};
class ZipFilePrivate {
public:
Locked<unzFile, std::recursive_mutex> zipFile;
std::unique_ptr<ourmemory_s> memfs;
// ccstd::unordered_map is faster if available on the platform
using FileListContainer = ccstd::unordered_map<ccstd::string, struct ZipEntryInfo>;
FileListContainer fileList;
};
ZipFile *ZipFile::createWithBuffer(const void *buffer, uint32_t size) {
auto *zip = ccnew ZipFile();
if (zip && zip->initWithBuffer(buffer, size)) {
return zip;
}
delete zip;
return nullptr;
}
ZipFile::ZipFile()
: _data(ccnew ZipFilePrivate) {
auto zipFile = _data->zipFile.lock();
*zipFile = nullptr;
}
ZipFile::ZipFile(const ccstd::string &zipFile, const ccstd::string &filter)
: _data(ccnew ZipFilePrivate) {
auto zipFileL = _data->zipFile.lock();
*zipFileL = unzOpen(FileUtils::getInstance()->getSuitableFOpen(zipFile).c_str());
setFilter(filter);
}
ZipFile::~ZipFile() {
if (_data) {
auto zipFile = _data->zipFile.lock();
if (*zipFile) {
unzClose(*zipFile);
}
}
CC_SAFE_DELETE(_data);
}
bool ZipFile::setFilter(const ccstd::string &filter) {
bool ret = false;
do {
CC_BREAK_IF(!_data);
auto zipFile = _data->zipFile.lock();
CC_BREAK_IF(!(*zipFile));
// clear existing file list
_data->fileList.clear();
// UNZ_MAXFILENAMEINZIP + 1 - it is done so in unzLocateFile
char szCurrentFileName[UNZ_MAXFILENAMEINZIP + 1];
unz_file_info64 fileInfo;
// go through all files and store position information about the required files
int err = unzGoToFirstFile64(*zipFile, &fileInfo,
szCurrentFileName, sizeof(szCurrentFileName) - 1);
while (err == UNZ_OK) {
unz_file_pos posInfo;
int posErr = unzGetFilePos(*zipFile, &posInfo);
if (posErr == UNZ_OK) {
ccstd::string currentFileName = szCurrentFileName;
// cache info about filtered files only (like 'assets/')
if (filter.empty() || currentFileName.substr(0, filter.length()) == filter) {
ZipEntryInfo entry;
entry.pos = posInfo;
entry.uncompressed_size = static_cast<uLong>(fileInfo.uncompressed_size);
_data->fileList[currentFileName] = entry;
}
}
// next file - also get the information about it
err = unzGoToNextFile64(*zipFile, &fileInfo,
szCurrentFileName, sizeof(szCurrentFileName) - 1);
}
ret = true;
} while (false);
return ret;
}
bool ZipFile::fileExists(const ccstd::string &fileName) const {
bool ret = false;
do {
CC_BREAK_IF(!_data);
ret = _data->fileList.find(fileName) != _data->fileList.end();
} while (false);
return ret;
}
unsigned char *ZipFile::getFileData(const ccstd::string &fileName, uint32_t *size) {
unsigned char *buffer = nullptr;
if (size) {
*size = 0;
}
auto zipFile = _data->zipFile.lock();
do {
CC_BREAK_IF(!(*zipFile));
CC_BREAK_IF(fileName.empty());
auto it = _data->fileList.find(fileName);
CC_BREAK_IF(it == _data->fileList.end());
ZipEntryInfo fileInfo = it->second;
int nRet = unzGoToFilePos(*zipFile, &fileInfo.pos);
CC_BREAK_IF(UNZ_OK != nRet);
nRet = unzOpenCurrentFile(*zipFile);
CC_BREAK_IF(UNZ_OK != nRet);
buffer = static_cast<unsigned char *>(malloc(fileInfo.uncompressed_size));
int CC_UNUSED nSize = unzReadCurrentFile(*zipFile, buffer, static_cast<unsigned int>(fileInfo.uncompressed_size));
CC_ASSERT(nSize == 0 || nSize == (int)fileInfo.uncompressed_size);
if (size) {
*size = static_cast<uint32_t>(fileInfo.uncompressed_size);
}
unzCloseCurrentFile(*zipFile);
} while (false);
return buffer;
}
bool ZipFile::getFileData(const ccstd::string &fileName, ResizableBuffer *buffer) {
bool res = false;
do {
auto zipFile = _data->zipFile.lock();
CC_BREAK_IF(!(*zipFile));
CC_BREAK_IF(fileName.empty());
auto it = _data->fileList.find(fileName);
CC_BREAK_IF(it == _data->fileList.end());
ZipEntryInfo fileInfo = it->second;
int nRet = unzGoToFilePos(*zipFile, &fileInfo.pos);
CC_BREAK_IF(UNZ_OK != nRet);
nRet = unzOpenCurrentFile(*zipFile);
CC_BREAK_IF(UNZ_OK != nRet);
buffer->resize(fileInfo.uncompressed_size);
int CC_UNUSED nSize = unzReadCurrentFile(*zipFile, buffer->buffer(), static_cast<unsigned int>(fileInfo.uncompressed_size));
CC_ASSERT(nSize == 0 || nSize == (int)fileInfo.uncompressed_size);
unzCloseCurrentFile(*zipFile);
res = true;
} while (false);
return res;
}
ccstd::string ZipFile::getFirstFilename() {
auto zipFile = _data->zipFile.lock();
if (unzGoToFirstFile(*zipFile) != UNZ_OK) return EMPTY_FILE_NAME;
ccstd::string path;
unz_file_info info;
getCurrentFileInfo(&path, &info);
return path;
}
ccstd::string ZipFile::getNextFilename() {
auto zipFile = _data->zipFile.lock();
if (unzGoToNextFile(*zipFile) != UNZ_OK) return EMPTY_FILE_NAME;
ccstd::string path;
unz_file_info info;
getCurrentFileInfo(&path, &info);
return path;
}
int ZipFile::getCurrentFileInfo(ccstd::string *filename, unz_file_info *info) {
char path[FILENAME_MAX + 1];
auto zipFile = _data->zipFile.lock();
int ret = unzGetCurrentFileInfo(*zipFile, info, path, sizeof(path), nullptr, 0, nullptr, 0);
if (ret != UNZ_OK) {
*filename = EMPTY_FILE_NAME;
} else {
filename->assign(path);
}
return ret;
}
bool ZipFile::initWithBuffer(const void *buffer, uint32_t size) {
if (!buffer || size == 0) return false;
auto zipFile = _data->zipFile.lock();
zlib_filefunc_def memoryFile = {nullptr};
std::unique_ptr<ourmemory_t> memfs(ccnew ourmemory_t{static_cast<char *>(const_cast<void *>(buffer)), static_cast<uint32_t>(size), 0, 0, 0});
if (!memfs) return false;
fill_memory_filefunc(&memoryFile, memfs.get());
*zipFile = unzOpen2(nullptr, &memoryFile);
if (!(*zipFile)) return false;
setFilter(EMPTY_FILE_NAME);
return true;
}
} // namespace cc

276
cocos/base/ZipUtils.h Normal file
View File

@@ -0,0 +1,276 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#include "base/std/container/string.h"
#include "platform/FileUtils.h"
#if (CC_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/FileUtils-android.h"
#endif
namespace cc {
#ifndef _unz64_H
using unz_file_info = struct unz_file_info_s;
#endif
struct CCZHeader {
unsigned char sig[4]; /** Signature. Should be 'CCZ!' 4 bytes. */
uint16_t compression_type; /** Should be 0. */
uint16_t version; /** Should be 2 (although version type==1 is also supported). */
unsigned int reserved; /** Reserved for users. */
unsigned int len; /** Size of the uncompressed file. */
};
enum {
CCZ_COMPRESSION_ZLIB, /** zlib format. */
CCZ_COMPRESSION_BZIP2, /** bzip2 format (not supported yet). */
CCZ_COMPRESSION_GZIP, /** gzip format (not supported yet). */
CCZ_COMPRESSION_NONE, /** plain (not supported yet). */
};
class CC_DLL ZipUtils {
public:
/**
* Inflates either zlib or gzip deflated memory. The inflated memory is expected to be freed by the caller.
*
* It will allocate 256k for the destination buffer. If it is not enough it will multiply the previous buffer size per 2, until there is enough memory.
*
* @return The length of the deflated buffer.
* @since v0.8.1
*/
static uint32_t inflateMemory(unsigned char *in, uint32_t inLength, unsigned char **out);
/**
* Inflates either zlib or gzip deflated memory. The inflated memory is expected to be freed by the caller.
*
* @param outLengthHint It is assumed to be the needed room to allocate the inflated buffer.
*
* @return The length of the deflated buffer.
* @since v1.0.0
*/
static uint32_t inflateMemoryWithHint(unsigned char *in, uint32_t inLength, unsigned char **out, uint32_t outLengthHint);
/**
* Inflates a GZip file into memory.
*
* @return The length of the deflated buffer.
* @since v0.99.5
*/
static int inflateGZipFile(const char *path, unsigned char **out);
/**
* Test a file is a GZip format file or not.
*
* @return True is a GZip format file. false is not.
* @since v3.0
*/
static bool isGZipFile(const char *path);
/**
* Test the buffer is GZip format or not.
*
* @return True is GZip format. false is not.
* @since v3.0
*/
static bool isGZipBuffer(const unsigned char *buffer, uint32_t len);
/**
* Inflates a CCZ file into memory.
*
* @return The length of the deflated buffer.
* @since v0.99.5
*/
static int inflateCCZFile(const char *path, unsigned char **out);
/**
* Inflates a buffer with CCZ format into memory.
*
* @return The length of the deflated buffer.
* @since v3.0
*/
static int inflateCCZBuffer(const unsigned char *buffer, uint32_t len, unsigned char **out);
/**
* Test a file is a CCZ format file or not.
*
* @return True is a CCZ format file. false is not.
* @since v3.0
*/
static bool isCCZFile(const char *path);
/**
* Test the buffer is CCZ format or not.
*
* @return True is CCZ format. false is not.
* @since v3.0
*/
static bool isCCZBuffer(const unsigned char *buffer, uint32_t len);
/**
* Sets the pvr.ccz encryption key parts separately for added security.
*
* Example: If the key used to encrypt the pvr.ccz file is
* 0xaaaaaaaabbbbbbbbccccccccdddddddd you will call this function 4
* different times, preferably from 4 different source files, as follows
*
* ZipUtils::setPvrEncryptionKeyPart(0, 0xaaaaaaaa);
* ZipUtils::setPvrEncryptionKeyPart(1, 0xbbbbbbbb);
* ZipUtils::setPvrEncryptionKeyPart(2, 0xcccccccc);
* ZipUtils::setPvrEncryptionKeyPart(3, 0xdddddddd);
*
* Splitting the key into 4 parts and calling the function from 4 different source
* files increases the difficulty to reverse engineer the encryption key.
* Be aware that encryption is *never* 100% secure and the key code
* can be cracked by knowledgable persons.
*
* IMPORTANT: Be sure to call setPvrEncryptionKey or
* setPvrEncryptionKeyPart with all of the key parts *before* loading
* the sprite sheet or decryption will fail and the sprite sheet
* will fail to load.
*
* @param index Part of the key [0..3].
* @param value Value of the key part.
*/
static void setPvrEncryptionKeyPart(int index, unsigned int value);
/**
* Sets the pvr.ccz encryption key.
*
* Example: If the key used to encrypt the pvr.ccz file is
* 0xaaaaaaaabbbbbbbbccccccccdddddddd you will call this function with
* the key split into 4 parts as follows
*
* ZipUtils::setPvrEncryptionKey(0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd);
*
* Note that using this function makes it easier to reverse engineer and discover
* the complete key because the key parts are present in one function call.
*
* IMPORTANT: Be sure to call setPvrEncryptionKey or setPvrEncryptionKeyPart
* with all of the key parts *before* loading the spritesheet or decryption
* will fail and the sprite sheet will fail to load.
*
* @param keyPart1 The key value part 1.
* @param keyPart2 The key value part 2.
* @param keyPart3 The key value part 3.
* @param keyPart4 The key value part 4.
*/
static void setPvrEncryptionKey(unsigned int keyPart1, unsigned int keyPart2, unsigned int keyPart3, unsigned int keyPart4);
private:
static int inflateMemoryWithHint(unsigned char *in, uint32_t inLength, unsigned char **out, uint32_t *outLength, uint32_t outLengthHint);
static inline void decodeEncodedPvr(unsigned int *data, uint32_t len);
static inline unsigned int checksumPvr(const unsigned int *data, uint32_t len);
static unsigned int encryptedPvrKeyParts[4];
static unsigned int encryptionKey[1024];
static bool encryptionKeyIsValid;
};
// forward declaration
class ZipFilePrivate;
struct unz_file_info_s;
/**
* Zip file - reader helper class.
*
* It will cache the file list of a particular zip file with positions inside an archive,
* so it would be much faster to read some particular files or to check their existence.
*
* @since v2.0.5
*/
class CC_DLL ZipFile {
public:
/**
* Constructor, open zip file and store file list.
*
* @param zipFile Zip file name
* @param filter The first part of file names, which should be accessible.
* For example, "@assets/". Other files will be missed.
*
* @since v2.0.5
*/
explicit ZipFile(const ccstd::string &zipFile, const ccstd::string &filter = ccstd::string());
virtual ~ZipFile();
/**
* Regenerate accessible file list based on a new filter string.
*
* @param filter New filter string (first part of files names)
* @return true whenever zip file is open successfully and it is possible to locate
* at least the first file, false otherwise
*
* @since v2.0.5
*/
bool setFilter(const ccstd::string &filter);
/**
* Check does a file exists or not in zip file
*
* @param fileName File to be checked on existence
* @return true whenever file exists, false otherwise
*
* @since v2.0.5
*/
bool fileExists(const ccstd::string &fileName) const;
/**
* Get resource file data from a zip file.
* @param fileName File name
* @param[out] pSize If the file read operation succeeds, it will be the data size, otherwise 0.
* @return Upon success, a pointer to the data is returned, otherwise nullptr.
* @warning Recall: you are responsible for calling free() on any Non-nullptr pointer returned.
*
* @since v2.0.5
*/
unsigned char *getFileData(const ccstd::string &fileName, uint32_t *size);
/**
* Get resource file data from a zip file.
* @param fileName File name
* @param[out] buffer If the file read operation succeeds, if will contain the file data.
* @return True if successful.
*/
bool getFileData(const ccstd::string &fileName, ResizableBuffer *buffer);
ccstd::string getFirstFilename();
ccstd::string getNextFilename();
static ZipFile *createWithBuffer(const void *buffer, uint32_t size);
private:
/* Only used internal for createWithBuffer() */
ZipFile();
bool initWithBuffer(const void *buffer, uint32_t size);
int getCurrentFileInfo(ccstd::string *filename, unz_file_info *info);
/** Internal data like zip file pointer / file list array and so on */
ZipFilePrivate *_data{nullptr};
};
} // end of namespace cc

60
cocos/base/astc.cpp Normal file
View File

@@ -0,0 +1,60 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/astc.h"
#include "platform/Image.h"
static const unsigned int MAGIC = 0x5CA1AB13;
static const astc_byte ASTC_HEADER_SIZE_X_BEGIN = 7;
static const astc_byte ASTC_HEADER_SIZE_Y_BEGIN = 10;
bool astcIsValid(const astc_byte *pHeader) {
uint32_t magicval = static_cast<uint32_t>(pHeader[0]) +
static_cast<uint32_t>(pHeader[1]) * 256 +
static_cast<uint32_t>(pHeader[2]) * 65536 +
static_cast<uint32_t>(pHeader[3]) * 16777216;
if (magicval != MAGIC) {
return false;
}
int xdim = pHeader[ASTC_HEADER_MAGIC];
int ydim = pHeader[ASTC_HEADER_MAGIC + 1];
int zdim = pHeader[ASTC_HEADER_MAGIC + 2];
return !((xdim < 3 || xdim > 6 || ydim < 3 || ydim > 6 || zdim < 3 || zdim > 6) &&
(xdim < 4 || xdim == 7 || xdim == 9 || xdim == 11 || xdim > 12 ||
ydim < 4 || ydim == 7 || ydim == 9 || ydim == 11 || ydim > 12 || zdim != 1));
}
int astcGetWidth(const astc_byte *pHeader) {
int xsize = pHeader[ASTC_HEADER_SIZE_X_BEGIN] + (pHeader[ASTC_HEADER_SIZE_X_BEGIN + 1] * 256) + (pHeader[ASTC_HEADER_SIZE_X_BEGIN + 2] * 65536);
return xsize;
}
int astcGetHeight(const astc_byte *pHeader) {
int ysize = pHeader[ASTC_HEADER_SIZE_Y_BEGIN] + (pHeader[ASTC_HEADER_SIZE_Y_BEGIN + 1] * 256) + (pHeader[ASTC_HEADER_SIZE_Y_BEGIN + 2] * 65536);
return ysize;
}

50
cocos/base/astc.h Normal file
View File

@@ -0,0 +1,50 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 <cstdint>
#pragma once
using astc_byte = unsigned char;
using astc_uint32 = unsigned int;
// Size of a ASTC header
#define ASTC_HEADER_SIZE 16
#define ASTC_HEADER_MAGIC 4
// Check if a ASTC header is correctly formatted
bool astcIsValid(const astc_byte *pHeader);
// Read the image width from a ASTC header
int astcGetWidth(const astc_byte *pHeader);
// Read the image height from a ASTC header
int astcGetHeight(const astc_byte *pHeader);

179
cocos/base/base64.cpp Normal file
View File

@@ -0,0 +1,179 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/base64.h"
#include <cstdio>
#include <cstdlib>
#include "base/std/container/string.h"
#include "base/std/container/vector.h"
namespace cc {
ccstd::string alphabet{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};
int doBase64Decode(const unsigned char *input, unsigned int inputLen, unsigned char *output, unsigned int *outputLen) {
static ccstd::vector<char> inalphabet(256);
static ccstd::vector<char> decoder(256);
int i;
int bits;
int c = 0;
int charCount;
int errors = 0;
unsigned int inputIdx = 0;
unsigned int outputIdx = 0;
int length = static_cast<int>(alphabet.length());
for (i = length - 1; i >= 0; i--) {
inalphabet[alphabet[i]] = 1;
decoder[alphabet[i]] = i;
}
charCount = 0;
bits = 0;
for (inputIdx = 0; inputIdx < inputLen; inputIdx++) {
c = input[inputIdx];
if (c == '=') {
break;
}
if (c > 255 || !inalphabet[c]) {
continue;
}
bits += decoder[c];
++charCount;
if (charCount == 4) {
output[outputIdx++] = (bits >> 16);
output[outputIdx++] = ((bits >> 8) & 0xff);
output[outputIdx++] = (bits & 0xff);
bits = 0;
charCount = 0;
} else {
bits <<= 6;
}
}
if (c == '=') {
switch (charCount) {
case 1:
#if (CC_PLATFORM != CC_PLATFORM_BADA)
fprintf(stderr, "base64Decode: encoding incomplete: at least 2 bits missing");
#endif
++errors;
break;
case 2:
output[outputIdx++] = (bits >> 10);
break;
case 3:
output[outputIdx++] = (bits >> 16);
output[outputIdx++] = ((bits >> 8) & 0xff);
break;
}
} else if (inputIdx < inputLen) {
if (charCount) {
#if (CC_PLATFORM != CC_PLATFORM_BADA)
fprintf(stderr, "base64 encoding incomplete: at least %d bits truncated",
((4 - charCount) * 6));
#endif
++errors;
}
}
*outputLen = outputIdx;
return errors;
}
void doBase64Encode(const unsigned char *input, unsigned int inputLen, char *output) {
unsigned int charCount;
unsigned int bits;
unsigned int inputIdx = 0;
unsigned int outputIdx = 0;
charCount = 0;
bits = 0;
for (inputIdx = 0; inputIdx < inputLen; inputIdx++) {
bits |= input[inputIdx];
charCount++;
if (charCount == 3) {
output[outputIdx++] = alphabet[(bits >> 18) & 0x3f];
output[outputIdx++] = alphabet[(bits >> 12) & 0x3f];
output[outputIdx++] = alphabet[(bits >> 6) & 0x3f];
output[outputIdx++] = alphabet[bits & 0x3f];
bits = 0;
charCount = 0;
} else {
bits <<= 8;
}
}
if (charCount) {
if (charCount == 1) {
bits <<= 8;
}
output[outputIdx++] = alphabet[(bits >> 18) & 0x3f];
output[outputIdx++] = alphabet[(bits >> 12) & 0x3f];
if (charCount > 1) {
output[outputIdx++] = alphabet[(bits >> 6) & 0x3f];
} else {
output[outputIdx++] = '=';
}
output[outputIdx++] = '=';
}
output[outputIdx++] = 0;
}
int base64Decode(const unsigned char *in, unsigned int inLength, unsigned char **out) {
unsigned int outLength = 0;
//should be enough to store 6-bit buffers in 8-bit buffers
*out = static_cast<unsigned char *>(malloc(inLength / 4 * 3 + 1));
if (*out) {
int ret = doBase64Decode(in, inLength, *out, &outLength);
if (ret > 0) {
free(*out);
*out = nullptr;
outLength = 0;
}
}
return static_cast<int>(outLength);
}
int base64Encode(const unsigned char *in, unsigned int inLength, char **out) {
unsigned int outLength = (inLength + 2) / 3 * 4;
//should be enough to store 8-bit buffers in 6-bit buffers
*out = static_cast<char *>(malloc(outLength + 1));
if (*out) {
doBase64Encode(in, inLength, *out);
}
return static_cast<int>(outLength);
}
} // namespace cc

58
cocos/base/base64.h Normal file
View File

@@ -0,0 +1,58 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#ifdef __cplusplus
extern "C" {
#endif
namespace cc {
/**
* Decodes a 64base encoded memory. The decoded memory is
* expected to be freed by the caller by calling `free()`
*
* @returns the length of the out buffer
*/
int CC_DLL base64Decode(const unsigned char *in, unsigned int inLength, unsigned char **out);
/**
* Encodes bytes into a 64base encoded memory with terminating '\0' character.
* The encoded memory is expected to be freed by the caller by calling `free()`
*
* @returns the length of the out buffer
*
*/
int CC_DLL base64Encode(const unsigned char *in, unsigned int inLength, char **out);
} // namespace cc
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,387 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
// (c) Dean McNamee <dean@gmail.com>, 2012.
// C++ port by Mapbox, Konstantin Käfer <mail@kkaefer.com>, 2014-2017.
//
// https://github.com/deanm/css-color-parser-js
// https://github.com/kkaefer/css-color-parser-cpp
//
// 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 "csscolorparser.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <sstream>
#include "base/std/container/vector.h"
namespace CSSColorParser {
// http://www.w3.org/TR/css3-color/
struct NamedColor {
const char *name;
Color color;
};
const ccstd::vector<NamedColor> NAMED_COLORS = {
{"transparent", {0, 0, 0, 0}},
{"aliceblue", {240, 248, 255, 1}},
{"antiquewhite", {250, 235, 215, 1}},
{"aqua", {0, 255, 255, 1}},
{"aquamarine", {127, 255, 212, 1}},
{"azure", {240, 255, 255, 1}},
{"beige", {245, 245, 220, 1}},
{"bisque", {255, 228, 196, 1}},
{"black", {0, 0, 0, 1}},
{"blanchedalmond", {255, 235, 205, 1}},
{"blue", {0, 0, 255, 1}},
{"blueviolet", {138, 43, 226, 1}},
{"brown", {165, 42, 42, 1}},
{"burlywood", {222, 184, 135, 1}},
{"cadetblue", {95, 158, 160, 1}},
{"chartreuse", {127, 255, 0, 1}},
{"chocolate", {210, 105, 30, 1}},
{"coral", {255, 127, 80, 1}},
{"cornflowerblue", {100, 149, 237, 1}},
{"cornsilk", {255, 248, 220, 1}},
{"crimson", {220, 20, 60, 1}},
{"cyan", {0, 255, 255, 1}},
{"darkblue", {0, 0, 139, 1}},
{"darkcyan", {0, 139, 139, 1}},
{"darkgoldenrod", {184, 134, 11, 1}},
{"darkgray", {169, 169, 169, 1}},
{"darkgreen", {0, 100, 0, 1}},
{"darkgrey", {169, 169, 169, 1}},
{"darkkhaki", {189, 183, 107, 1}},
{"darkmagenta", {139, 0, 139, 1}},
{"darkolivegreen", {85, 107, 47, 1}},
{"darkorange", {255, 140, 0, 1}},
{"darkorchid", {153, 50, 204, 1}},
{"darkred", {139, 0, 0, 1}},
{"darksalmon", {233, 150, 122, 1}},
{"darkseagreen", {143, 188, 143, 1}},
{"darkslateblue", {72, 61, 139, 1}},
{"darkslategray", {47, 79, 79, 1}},
{"darkslategrey", {47, 79, 79, 1}},
{"darkturquoise", {0, 206, 209, 1}},
{"darkviolet", {148, 0, 211, 1}},
{"deeppink", {255, 20, 147, 1}},
{"deepskyblue", {0, 191, 255, 1}},
{"dimgray", {105, 105, 105, 1}},
{"dimgrey", {105, 105, 105, 1}},
{"dodgerblue", {30, 144, 255, 1}},
{"firebrick", {178, 34, 34, 1}},
{"floralwhite", {255, 250, 240, 1}},
{"forestgreen", {34, 139, 34, 1}},
{"fuchsia", {255, 0, 255, 1}},
{"gainsboro", {220, 220, 220, 1}},
{"ghostwhite", {248, 248, 255, 1}},
{"gold", {255, 215, 0, 1}},
{"goldenrod", {218, 165, 32, 1}},
{"gray", {128, 128, 128, 1}},
{"green", {0, 128, 0, 1}},
{"greenyellow", {173, 255, 47, 1}},
{"grey", {128, 128, 128, 1}},
{"honeydew", {240, 255, 240, 1}},
{"hotpink", {255, 105, 180, 1}},
{"indianred", {205, 92, 92, 1}},
{"indigo", {75, 0, 130, 1}},
{"ivory", {255, 255, 240, 1}},
{"khaki", {240, 230, 140, 1}},
{"lavender", {230, 230, 250, 1}},
{"lavenderblush", {255, 240, 245, 1}},
{"lawngreen", {124, 252, 0, 1}},
{"lemonchiffon", {255, 250, 205, 1}},
{"lightblue", {173, 216, 230, 1}},
{"lightcoral", {240, 128, 128, 1}},
{"lightcyan", {224, 255, 255, 1}},
{"lightgoldenrodyellow", {250, 250, 210, 1}},
{"lightgray", {211, 211, 211, 1}},
{"lightgreen", {144, 238, 144, 1}},
{"lightgrey", {211, 211, 211, 1}},
{"lightpink", {255, 182, 193, 1}},
{"lightsalmon", {255, 160, 122, 1}},
{"lightseagreen", {32, 178, 170, 1}},
{"lightskyblue", {135, 206, 250, 1}},
{"lightslategray", {119, 136, 153, 1}},
{"lightslategrey", {119, 136, 153, 1}},
{"lightsteelblue", {176, 196, 222, 1}},
{"lightyellow", {255, 255, 224, 1}},
{"lime", {0, 255, 0, 1}},
{"limegreen", {50, 205, 50, 1}},
{"linen", {250, 240, 230, 1}},
{"magenta", {255, 0, 255, 1}},
{"maroon", {128, 0, 0, 1}},
{"mediumaquamarine", {102, 205, 170, 1}},
{"mediumblue", {0, 0, 205, 1}},
{"mediumorchid", {186, 85, 211, 1}},
{"mediumpurple", {147, 112, 219, 1}},
{"mediumseagreen", {60, 179, 113, 1}},
{"mediumslateblue", {123, 104, 238, 1}},
{"mediumspringgreen", {0, 250, 154, 1}},
{"mediumturquoise", {72, 209, 204, 1}},
{"mediumvioletred", {199, 21, 133, 1}},
{"midnightblue", {25, 25, 112, 1}},
{"mintcream", {245, 255, 250, 1}},
{"mistyrose", {255, 228, 225, 1}},
{"moccasin", {255, 228, 181, 1}},
{"navajowhite", {255, 222, 173, 1}},
{"navy", {0, 0, 128, 1}},
{"oldlace", {253, 245, 230, 1}},
{"olive", {128, 128, 0, 1}},
{"olivedrab", {107, 142, 35, 1}},
{"orange", {255, 165, 0, 1}},
{"orangered", {255, 69, 0, 1}},
{"orchid", {218, 112, 214, 1}},
{"palegoldenrod", {238, 232, 170, 1}},
{"palegreen", {152, 251, 152, 1}},
{"paleturquoise", {175, 238, 238, 1}},
{"palevioletred", {219, 112, 147, 1}},
{"papayawhip", {255, 239, 213, 1}},
{"peachpuff", {255, 218, 185, 1}},
{"peru", {205, 133, 63, 1}},
{"pink", {255, 192, 203, 1}},
{"plum", {221, 160, 221, 1}},
{"powderblue", {176, 224, 230, 1}},
{"purple", {128, 0, 128, 1}},
{"red", {255, 0, 0, 1}},
{"rosybrown", {188, 143, 143, 1}},
{"royalblue", {65, 105, 225, 1}},
{"saddlebrown", {139, 69, 19, 1}},
{"salmon", {250, 128, 114, 1}},
{"sandybrown", {244, 164, 96, 1}},
{"seagreen", {46, 139, 87, 1}},
{"seashell", {255, 245, 238, 1}},
{"sienna", {160, 82, 45, 1}},
{"silver", {192, 192, 192, 1}},
{"skyblue", {135, 206, 235, 1}},
{"slateblue", {106, 90, 205, 1}},
{"slategray", {112, 128, 144, 1}},
{"slategrey", {112, 128, 144, 1}},
{"snow", {255, 250, 250, 1}},
{"springgreen", {0, 255, 127, 1}},
{"steelblue", {70, 130, 180, 1}},
{"tan", {210, 180, 140, 1}},
{"teal", {0, 128, 128, 1}},
{"thistle", {216, 191, 216, 1}},
{"tomato", {255, 99, 71, 1}},
{"turquoise", {64, 224, 208, 1}},
{"violet", {238, 130, 238, 1}},
{"wheat", {245, 222, 179, 1}},
{"white", {255, 255, 255, 1}},
{"whitesmoke", {245, 245, 245, 1}},
{"yellow", {255, 255, 0, 1}},
{"yellowgreen", {154, 205, 50, 1}}};
template <typename T>
uint8_t clampCssByte(T i) { // Clamp to integer 0 .. 255.
i = static_cast<T>(::round(i)); // Seems to be what Chrome does (vs truncation).
return static_cast<uint8_t>(i < 0 ? 0 : i > 255 ? 255
: i);
}
template <typename T>
float clampCssFloat(T f) { // Clamp to float 0.0 .. 1.0.
return f < 0 ? 0 : f > 1 ? 1
: float(f);
}
float parseFloat(const ccstd::string &str) {
return strtof(str.c_str(), nullptr);
}
int64_t parseInt(const ccstd::string &str, uint8_t base = 10) {
return strtoll(str.c_str(), nullptr, base);
}
uint8_t parseCssInt(const ccstd::string &str) { // int or percentage.
if (str.length() && str.back() == '%') {
return clampCssByte(parseFloat(str) / 100.0f * 255.0f);
}
return clampCssByte(parseInt(str));
}
float parseCssFloat(const ccstd::string &str) { // float or percentage.
if (str.length() && str.back() == '%') {
return clampCssFloat(parseFloat(str) / 100.0f);
}
return clampCssFloat(parseFloat(str));
}
float cssHueToRgb(float m1, float m2, float h) {
if (h < 0.0f) {
h += 1.0f;
} else if (h > 1.0f) {
h -= 1.0f;
}
if (h * 6.0f < 1.0f) {
return m1 + (m2 - m1) * h * 6.0f;
}
if (h * 2.0f < 1.0f) {
return m2;
}
if (h * 3.0f < 2.0f) {
return m1 + (m2 - m1) * (2.0f / 3.0f - h) * 6.0f;
}
return m1;
}
ccstd::vector<ccstd::string> split(const ccstd::string &s, char delim) {
ccstd::vector<ccstd::string> elems;
std::stringstream ss(s);
ccstd::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
Color parse(const ccstd::string &cssStr) {
ccstd::string str = cssStr;
// Remove all whitespace, not compliant, but should just be more accepting.
str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
// Convert to lowercase.
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
for (const auto &namedColor : NAMED_COLORS) {
if (str == namedColor.name) {
return namedColor.color;
}
}
// #abc and #abc123 syntax.
if (str.length() && str.front() == '#') {
if (str.length() == 4) {
int64_t iv =
parseInt(str.substr(1), 16); // REFINE(deanm): Stricter parsing.
if (!(iv >= 0 && iv <= 0xfff)) {
return {};
}
return Color(
static_cast<uint8_t>(((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8)),
static_cast<uint8_t>((iv & 0xf0) | ((iv & 0xf0) >> 4)),
static_cast<uint8_t>((iv & 0xf) | ((iv & 0xf) << 4)), 1);
}
if (str.length() == 7) {
int64_t iv = parseInt(str.substr(1), 16); // REFINE(deanm): Stricter parsing.
if (!(iv >= 0 && iv <= 0xffffff)) {
return {}; // Covers NaN.
}
return Color(static_cast<uint8_t>((iv & 0xff0000) >> 16),
static_cast<uint8_t>((iv & 0xff00) >> 8),
static_cast<uint8_t>(iv & 0xff), 1);
}
return Color();
}
size_t op = str.find_first_of('(');
size_t ep = str.find_first_of(')');
if (op != ccstd::string::npos && ep + 1 == str.length()) {
const ccstd::string fname = str.substr(0, op);
const ccstd::vector<ccstd::string> params =
split(str.substr(op + 1, ep - (op + 1)), ',');
float alpha = 1.0f;
if (fname == "rgba" || fname == "rgb") {
if (fname == "rgba") {
if (params.size() != 4) {
return {};
}
alpha = parseCssFloat(params.back());
} else {
if (params.size() != 3) {
return {};
}
}
return Color(parseCssInt(params[0]), parseCssInt(params[1]),
parseCssInt(params[2]), alpha);
}
if (fname == "hsla" || fname == "hsl") {
if (fname == "hsla") {
if (params.size() != 4) {
return {};
}
alpha = parseCssFloat(params.back());
} else {
if (params.size() != 3) {
return {};
}
}
float h = parseFloat(params[0]) / 360.0f;
while (h < 0.0f) {
++h;
}
while (h > 1.0f) {
--h;
}
// NOTE(deanm): According to the CSS spec s/l should only be
// percentages, but we don't bother and let float or percentage.
float s = parseCssFloat(params[1]);
float l = parseCssFloat(params[2]);
float m2 = l <= 0.5f ? l * (s + 1.0f) : l + s - l * s;
float m1 = l * 2.0f - m2;
return Color(
clampCssByte(cssHueToRgb(m1, m2, h + 1.0f / 3.0f) * 255.0f),
clampCssByte(cssHueToRgb(m1, m2, h) * 255.0f),
clampCssByte(cssHueToRgb(m1, m2, h - 1.0f / 3.0f) * 255.0f),
alpha);
}
}
return {};
}
} // namespace CSSColorParser

View File

@@ -0,0 +1,76 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
// (c) Dean McNamee <dean@gmail.com>, 2012.
// C++ port by Mapbox, Konstantin Käfer <mail@kkaefer.com>, 2014-2017.
//
// https://github.com/deanm/css-color-parser-js
// https://github.com/kkaefer/css-color-parser-cpp
//
// 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.
#pragma once
#include <cmath>
#include "base/std/container/string.h"
namespace CSSColorParser {
struct Color {
inline Color() = default;
inline Color(unsigned char red, unsigned char green, unsigned char blue, float alpha)
: r(red), g(green), b(blue), a(alpha > 1 ? 1 : alpha < 0 ? 0
: alpha) {
}
unsigned char r = 0, g = 0, b = 0;
float a = 1.0f;
};
inline bool operator==(const Color &lhs, const Color &rhs) {
return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b && ::fabs(lhs.a - rhs.a) < 0.0001f;
}
inline bool operator!=(const Color &lhs, const Color &rhs) {
return !(lhs == rhs);
}
Color parse(const ccstd::string &cssStr);
} // namespace CSSColorParser

83
cocos/base/etc1.cpp Normal file
View File

@@ -0,0 +1,83 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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.
#include "base/etc1.h"
#include <string.h>
static const char kMagic[] = {'P', 'K', 'M', ' ', '1', '0'};
static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
static etc1_uint32 readBEUint16(const etc1_byte *pIn) {
return (pIn[0] << 8) | pIn[1];
}
// Check if a PKM header is correctly formatted.
etc1_bool etc1_pkm_is_valid(const etc1_byte *pHeader) {
if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
return false;
}
etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
return format == ETC1_RGB_NO_MIPMAPS &&
encodedWidth >= width && encodedWidth - width < 4 &&
encodedHeight >= height && encodedHeight - height < 4;
}
// Read the image width from a PKM header
etc1_uint32 etc1_pkm_get_width(const etc1_byte *pHeader) {
return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
}
// Read the image height from a PKM header
etc1_uint32 etc1_pkm_get_height(const etc1_byte *pHeader) {
return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
}

78
cocos/base/etc1.h Normal file
View File

@@ -0,0 +1,78 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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 __etc1_h__
#define __etc1_h__
/// @cond DO_NOT_SHOW
#define ETC1_ENCODED_BLOCK_SIZE 8
#define ETC1_DECODED_BLOCK_SIZE 48
#ifndef ETC1_RGB8_OES
#define ETC1_RGB8_OES 0x8D64
#endif
typedef unsigned char etc1_byte;
typedef int etc1_bool;
typedef unsigned int etc1_uint32;
#ifdef __cplusplus
extern "C" {
#endif
// Size of a PKM header, in bytes.
#define ETC_PKM_HEADER_SIZE 16
// Check if a PKM header is correctly formatted.
etc1_bool etc1_pkm_is_valid(const etc1_byte *pHeader);
// Read the image width from a PKM header
etc1_uint32 etc1_pkm_get_width(const etc1_byte *pHeader);
// Read the image height from a PKM header
etc1_uint32 etc1_pkm_get_height(const etc1_byte *pHeader);
#ifdef __cplusplus
}
#endif
/// @endcond
#endif

73
cocos/base/etc2.cpp Normal file
View File

@@ -0,0 +1,73 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/etc2.h"
#include <stdint.h>
#include <string.h>
static const char kMagic[] = {'P', 'K', 'M', ' ', '2', '0'};
static const etc2_uint32 ETC2_PKM_FORMAT_OFFSET = 6;
static const etc2_uint32 ETC2_PKM_ENCODED_WIDTH_OFFSET = 8;
static const etc2_uint32 ETC2_PKM_ENCODED_HEIGHT_OFFSET = 10;
static const etc2_uint32 ETC2_PKM_WIDTH_OFFSET = 12;
static const etc2_uint32 ETC2_PKM_HEIGHT_OFFSET = 14;
static etc2_uint32 readBEUint16(const etc2_byte *pIn) {
return (pIn[0] << 8) | pIn[1];
}
// Check if a PKM header is correctly formatted.
etc2_bool etc2_pkm_is_valid(const etc2_byte *pHeader) {
if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
return false;
}
etc2_uint32 format = readBEUint16(pHeader + ETC2_PKM_FORMAT_OFFSET);
etc2_uint32 encodedWidth = readBEUint16(pHeader + ETC2_PKM_ENCODED_WIDTH_OFFSET);
etc2_uint32 encodedHeight = readBEUint16(pHeader + ETC2_PKM_ENCODED_HEIGHT_OFFSET);
etc2_uint32 width = readBEUint16(pHeader + ETC2_PKM_WIDTH_OFFSET);
etc2_uint32 height = readBEUint16(pHeader + ETC2_PKM_HEIGHT_OFFSET);
return (format == ETC2_RGB_NO_MIPMAPS || format == ETC2_RGBA_NO_MIPMAPS) &&
encodedWidth >= width && encodedWidth - width < 4 &&
encodedHeight >= height && encodedHeight - height < 4;
}
// Read the image width from a PKM header
etc2_uint32 etc2_pkm_get_width(const etc2_byte *pHeader) {
return readBEUint16(pHeader + ETC2_PKM_WIDTH_OFFSET);
}
// Read the image height from a PKM header
etc2_uint32 etc2_pkm_get_height(const etc2_byte *pHeader) {
return readBEUint16(pHeader + ETC2_PKM_HEIGHT_OFFSET);
}
etc2_uint32 etc2_pkm_get_format(const uint8_t *pHeader) {
return readBEUint16(pHeader + ETC2_PKM_FORMAT_OFFSET);
}

75
cocos/base/etc2.h Normal file
View File

@@ -0,0 +1,75 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#ifndef __etc2_h__
#define __etc2_h__
/// @cond DO_NOT_SHOW
typedef unsigned char etc2_byte;
typedef int etc2_bool;
typedef unsigned int etc2_uint32;
#ifndef GL_COMPRESSED_RGB8_ETC2
#define GL_COMPRESSED_RGB8_ETC2 0x9274
#endif
#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC
#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
#endif
#ifdef __cplusplus
extern "C" {
#endif
// Size of a PKM header, in bytes.
#define ETC2_PKM_HEADER_SIZE 16
#define ETC2_RGB_NO_MIPMAPS 1
#define ETC2_RGBA_NO_MIPMAPS 3
// Check if a PKM header is correctly formatted.
etc2_bool etc2_pkm_is_valid(const etc2_byte *pHeader);
// Read the image width from a PKM header
etc2_uint32 etc2_pkm_get_width(const etc2_byte *pHeader);
// Read the image height from a PKM header
etc2_uint32 etc2_pkm_get_height(const etc2_byte *pHeader);
// Read the image format from a PKM header
etc2_uint32 etc2_pkm_get_format(const etc2_byte *pHeader);
#ifdef __cplusplus
}
#endif
/// @endcond
#endif

View File

@@ -0,0 +1,51 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#if CC_USE_JOB_SYSTEM_TASKFLOW
#include "job-system-taskflow/TFJobGraph.h"
#include "job-system-taskflow/TFJobSystem.h"
namespace cc {
using JobToken = TFJobToken;
using JobGraph = TFJobGraph;
using JobSystem = TFJobSystem;
} // namespace cc
#elif CC_USE_JOB_SYSTEM_TBB
#include "job-system-tbb/TBBJobGraph.h"
#include "job-system-tbb/TBBJobSystem.h"
namespace cc {
using JobToken = TBBJobToken;
using JobGraph = TBBJobGraph;
using JobSystem = TBBJobSystem;
} // namespace cc
#else
#include "job-system-dummy/DummyJobGraph.h"
#include "job-system-dummy/DummyJobSystem.h"
namespace cc {
using JobToken = DummyJobToken;
using JobGraph = DummyJobGraph;
using JobSystem = DummyJobSystem;
} // namespace cc
#endif

View File

@@ -0,0 +1,147 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "DummyJobGraph.h"
#include "base/Macros.h"
#define DUMMY_GRAPH_NODE_CHUNK_SIZE 64
namespace cc {
namespace {
DummyGraphNode *freeList{nullptr};
ccstd::vector<DummyGraphNode *> allocatedChunks;
} // namespace
DummyGraphNode::~DummyGraphNode() {
delete _callback;
}
void DummyGraphNode::reset() {
_successors.clear();
_predecessors.clear();
delete _callback;
_callback = nullptr;
}
void DummyGraphNode::succeed(DummyGraphNode *other) {
CC_ASSERT_NE(this, other);
// Run after other
this->_predecessors.emplace(other);
other->_successors.emplace(this);
}
void DummyGraphNode::precede(DummyGraphNode *other) {
// Run before other
other->succeed(this);
}
void DummyGraphNode::allocChunk() {
CC_ASSERT_NULL(freeList);
freeList = ccnew DummyGraphNode[DUMMY_GRAPH_NODE_CHUNK_SIZE]();
allocatedChunks.emplace_back(freeList);
for (auto i = 0; i < DUMMY_GRAPH_NODE_CHUNK_SIZE - 1; i++) {
freeList[i]._next = &freeList[i + 1];
}
freeList[DUMMY_GRAPH_NODE_CHUNK_SIZE - 1]._next = nullptr;
}
DummyGraphNode *DummyGraphNode::alloc() {
if (freeList == nullptr) {
DummyGraphNode::allocChunk();
}
auto *p = freeList;
freeList = freeList->_next;
p->reset();
return p;
}
void DummyGraphNode::free(DummyGraphNode *node) {
node->_next = freeList;
freeList = node;
}
void DummyGraphNode::freeAll() {
for (auto *chunk : allocatedChunks) {
delete[] chunk;
}
allocatedChunks.clear();
}
DummyGraph::~DummyGraph() {
clear();
}
void DummyGraph::clear() {
for (auto *node : _nodes) {
DummyGraphNode::free(node);
}
_nodes.clear();
}
void DummyGraph::link(size_t precede, size_t after) {
_nodes[precede]->precede(_nodes[after]);
}
void DummyGraph::run() {
for (auto *node : _nodes) {
if (!excuted(node)) {
walk(node);
}
}
_generation++;
}
void DummyGraph::walk(DummyGraphNode *node) { //NOLINT(misc-no-recursion)
for (DummyGraphNode *n : node->_predecessors) {
if (!excuted(n)) {
walk(n);
}
}
if (!excuted(node)) {
node->_callback->execute();
node->_generation++;
}
for (DummyGraphNode *n : node->_successors) {
if (!excuted(n)) {
walk(n);
}
}
}
bool DummyGraph::excuted(DummyGraphNode *n) const {
return n->_generation != _generation;
}
void DummyJobGraph::makeEdge(uint32_t j1, uint32_t j2) {
_dummyGraph.link(j1, j2);
}
void DummyJobGraph::run() noexcept {
_dummyGraph.run();
_dummyGraph.clear();
}
} // namespace cc

View File

@@ -0,0 +1,162 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#include "base/memory/Memory.h"
#include "base/std/container/unordered_set.h"
#include "base/std/container/vector.h"
namespace cc {
class DummyJobSystem;
class DummyGraphNode;
class DummyGraph final {
public:
DummyGraph() = default;
DummyGraph(const DummyGraph &) = delete;
DummyGraph(DummyGraph &&) = delete;
DummyGraph &operator=(const DummyGraph &) = delete;
DummyGraph &operator=(DummyGraph &&) = delete;
~DummyGraph();
template <class Fn>
size_t addNode(Fn &&fn);
void run();
void link(size_t precede, size_t after);
void walk(DummyGraphNode *node);
void clear();
private:
bool excuted(DummyGraphNode *n) const;
int _generation{0};
ccstd::vector<DummyGraphNode *> _nodes;
};
class DummyGraphNodeTaskItf {
public:
virtual ~DummyGraphNodeTaskItf() = default;
virtual void execute() = 0;
};
template <class Fn>
class DummyGraphNodeTaskImpl final : public DummyGraphNodeTaskItf {
public:
explicit DummyGraphNodeTaskImpl(Fn &&t) noexcept;
DummyGraphNodeTaskImpl(const DummyGraphNodeTaskImpl &) = delete;
DummyGraphNodeTaskImpl(DummyGraphNodeTaskImpl &&) = delete;
DummyGraphNodeTaskImpl &operator=(const DummyGraphNodeTaskImpl &) = delete;
DummyGraphNodeTaskImpl &operator=(DummyGraphNodeTaskImpl &&) = delete;
~DummyGraphNodeTaskImpl() override = default;
inline void execute() override { _task(); }
private:
Fn _task;
};
class DummyGraphNode final {
public:
DummyGraphNode() = default;
DummyGraphNode(const DummyGraphNode &) = delete;
DummyGraphNode(DummyGraphNode &&) = delete;
DummyGraphNode &operator=(const DummyGraphNode &) = delete;
DummyGraphNode &operator=(DummyGraphNode &&) = delete;
~DummyGraphNode();
private:
static void allocChunk();
static DummyGraphNode *alloc();
static void free(DummyGraphNode *n);
static void freeAll();
void succeed(DummyGraphNode *other);
void precede(DummyGraphNode *other);
void reset();
DummyGraphNodeTaskItf *_callback{nullptr};
ccstd::unordered_set<DummyGraphNode *> _successors{};
ccstd::unordered_set<DummyGraphNode *> _predecessors{};
DummyGraphNode *_next{nullptr};
int _generation{0};
friend class DummyGraph;
};
template <class Fn>
DummyGraphNodeTaskImpl<Fn>::DummyGraphNodeTaskImpl(Fn &&t) noexcept : _task(t) {}
template <class Fn>
size_t DummyGraph::addNode(Fn &&fn) {
DummyGraphNode *n = DummyGraphNode::alloc();
n->_callback = ccnew DummyGraphNodeTaskImpl<Fn>(std::forward<Fn>(fn));
n->_generation = _generation;
_nodes.emplace_back(n);
return _nodes.size() - 1;
}
// exported declarations
class DummyJobGraph final {
public:
explicit DummyJobGraph(DummyJobSystem * /*system*/) noexcept {}
DummyJobGraph(const DummyJobGraph &) = delete;
DummyJobGraph(DummyJobGraph &&) = delete;
DummyJobGraph &operator=(const DummyJobGraph &) = delete;
DummyJobGraph &operator=(DummyJobGraph &&) = delete;
~DummyJobGraph() noexcept = default;
template <typename Function>
uint32_t createJob(Function &&func) noexcept;
template <typename Function>
uint32_t createForEachIndexJob(uint32_t begin, uint32_t end, uint32_t step, Function &&func) noexcept;
void makeEdge(uint32_t j1, uint32_t j2);
void run() noexcept;
inline void waitForAll() { run(); }
private:
DummyGraph _dummyGraph{};
};
template <typename Function>
uint32_t DummyJobGraph::createJob(Function &&func) noexcept {
return static_cast<uint32_t>(_dummyGraph.addNode(std::forward<Function>(func)));
}
template <typename Function>
uint32_t DummyJobGraph::createForEachIndexJob(uint32_t begin, uint32_t end, uint32_t step, Function &&func) noexcept {
return static_cast<uint32_t>(_dummyGraph.addNode([callable = std::forward<Function>(func), first = begin, last = end, step = step]() {
for (auto i = first; i < last; i += step) {
callable(i);
}
}));
}
} // namespace cc

View File

@@ -0,0 +1,32 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "DummyJobSystem.h"
#include "DummyJobGraph.h"
namespace cc {
DummyJobSystem *DummyJobSystem::instance = nullptr;
} // namespace cc

View File

@@ -0,0 +1,62 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#include "base/memory/Memory.h"
namespace cc {
using DummyJobToken = size_t;
class DummyJobGraph;
class DummyJobSystem final {
private:
static DummyJobSystem *instance;
public:
static DummyJobSystem *getInstance() {
if (!instance) {
instance = ccnew DummyJobSystem;
}
return instance;
}
static void destroyInstance() {
delete instance;
instance = nullptr;
}
DummyJobSystem() noexcept = default;
explicit DummyJobSystem(uint32_t /*threadCount*/) noexcept {}
inline uint32_t threadCount() const { return THREAD_COUNT; } //NOLINT
private:
static constexpr uint32_t THREAD_COUNT = 1U; //always one
};
} // namespace cc

View File

@@ -0,0 +1,40 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "TFJobGraph.h"
#include "TFJobSystem.h"
namespace cc {
void TFJobGraph::makeEdge(uint32_t j1, uint32_t j2) noexcept {
_tasks[j1].precede(_tasks[j2]);
}
void TFJobGraph::run() noexcept {
if (_pending) return;
_future = _executor->run(_flow);
_pending = true;
}
} // namespace cc

View File

@@ -0,0 +1,78 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "TFJobSystem.h"
#include "base/std/container/deque.h"
#include "taskflow/taskflow.hpp"
namespace cc {
using TFJobToken = void;
class TFJobGraph final {
public:
explicit TFJobGraph(TFJobSystem *system) noexcept : _executor(&system->_executor) {}
template <typename Function>
uint32_t createJob(Function &&func) noexcept;
template <typename Function>
uint32_t createForEachIndexJob(uint32_t begin, uint32_t end, uint32_t step, Function &&func) noexcept;
void makeEdge(uint32_t j1, uint32_t j2) noexcept;
void run() noexcept;
inline void waitForAll() {
if (_pending) {
_future.wait();
_pending = false;
}
}
private:
tf::Executor *_executor = nullptr;
tf::Taskflow _flow;
ccstd::deque<tf::Task> _tasks; // existing tasks cannot be invalidated
std::future<void> _future;
bool _pending = false;
};
template <typename Function>
uint32_t TFJobGraph::createJob(Function &&func) noexcept {
_tasks.emplace_back(_flow.emplace(func));
return static_cast<uint32_t>(_tasks.size() - 1u);
}
template <typename Function>
uint32_t TFJobGraph::createForEachIndexJob(uint32_t begin, uint32_t end, uint32_t step, Function &&func) noexcept {
_tasks.emplace_back(_flow.for_each_index(begin, end, step, func));
return static_cast<uint32_t>(_tasks.size() - 1u);
}
} // namespace cc

View File

@@ -0,0 +1,38 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "TFJobSystem.h"
#include "TFJobGraph.h"
#include "base/Log.h"
namespace cc {
TFJobSystem *TFJobSystem::_instance = nullptr;
TFJobSystem::TFJobSystem(uint32_t threadCount) noexcept
: _executor(threadCount) {
CC_LOG_INFO("Taskflow Job system initialized: %d worker threads", threadCount);
}
} // namespace cc

View File

@@ -0,0 +1,60 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <algorithm>
#include <thread>
#include "base/memory/Memory.h"
#include "taskflow/taskflow.hpp"
namespace cc {
class TFJobSystem final {
public:
static TFJobSystem *getInstance() {
if (!_instance) {
_instance = ccnew TFJobSystem;
}
return _instance;
}
static void destroyInstance() {
CC_SAFE_DELETE(_instance);
}
TFJobSystem() noexcept : TFJobSystem(std::max(2u, std::thread::hardware_concurrency() - 2u)) {}
explicit TFJobSystem(uint32_t threadCount) noexcept;
inline uint32_t threadCount() { return static_cast<uint32_t>(_executor.num_workers()); }
private:
friend class TFJobGraph;
static TFJobSystem *_instance;
tf::Executor _executor;
};
} // namespace cc

View File

@@ -0,0 +1,46 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "TBBJobGraph.h"
namespace cc {
void TBBJobGraph::makeEdge(uint32_t j1, uint32_t j2) noexcept {
if (j1 & PARALLEL_JOB_FLAG) {
j1 = _parallelJobs[j1 & PARALLEL_JOB_MASK].successor;
}
if (j2 & PARALLEL_JOB_FLAG) {
j2 = _parallelJobs[j2 & PARALLEL_JOB_MASK].predecessor;
}
tbb::flow::make_edge(_nodes[j1], _nodes[j2]);
}
void TBBJobGraph::run() noexcept {
_nodes.front().try_put(tbb::flow::continue_msg());
_pending = true;
}
} // namespace cc

View File

@@ -0,0 +1,107 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <tbb/flow_graph.h>
#include "base/std/container/deque.h"
#include "base/std/container/vector.h"
namespace cc {
using TBBJobToken = tbb::flow::continue_msg;
class TBBJobSystem;
class TBBJobGraph final {
public:
explicit TBBJobGraph(TBBJobSystem *system) noexcept {
_nodes.emplace_back(_graph, [](TBBJobToken t) {});
}
template <typename Function>
uint32_t createJob(Function &&func) noexcept;
template <typename Function>
uint32_t createForEachIndexJob(uint32_t begin, uint32_t end, uint32_t step, Function &&func) noexcept;
void makeEdge(uint32_t j1, uint32_t j2) noexcept;
void run() noexcept;
inline void waitForAll() {
if (_pending) {
_graph.wait_for_all();
_pending = false;
}
}
private:
static constexpr uint32_t PARALLEL_JOB_FLAG = 1u << 20;
static constexpr uint32_t PARALLEL_JOB_MASK = ~PARALLEL_JOB_FLAG;
tbb::flow::graph _graph;
using TBBJobNode = tbb::flow::continue_node<tbb::flow::continue_msg>;
ccstd::deque<TBBJobNode> _nodes; // existing nodes cannot be invalidated
struct TBBParallelJob {
uint32_t predecessor = 0u;
uint32_t successor = 0u;
};
ccstd::vector<TBBParallelJob> _parallelJobs;
bool _pending = false;
};
template <typename Function>
uint32_t TBBJobGraph::createJob(Function &&func) noexcept {
_nodes.emplace_back(_graph, func);
tbb::flow::make_edge(_nodes.front(), _nodes.back());
return static_cast<uint32_t>(_nodes.size() - 1u);
}
template <typename Function>
uint32_t TBBJobGraph::createForEachIndexJob(uint32_t begin, uint32_t end, uint32_t step, Function &&func) noexcept {
_nodes.emplace_back(_graph, [](TBBJobToken t) {});
auto predecessorIdx = static_cast<uint32_t>(_nodes.size() - 1u);
TBBJobNode &predecessor = _nodes.back();
tbb::flow::make_edge(_nodes.front(), predecessor);
_nodes.emplace_back(_graph, [](TBBJobToken t) {});
auto successorIdx = static_cast<uint32_t>(_nodes.size() - 1u);
TBBJobNode &successor = _nodes.back();
for (uint32_t i = begin; i < end; i += step) {
_nodes.emplace_back(_graph, [i, &func](TBBJobToken t) { func(i); });
tbb::flow::make_edge(predecessor, _nodes.back());
tbb::flow::make_edge(_nodes.back(), successor);
}
_parallelJobs.push_back({predecessorIdx, successorIdx});
return static_cast<uint32_t>((_parallelJobs.size() - 1u)) | PARALLEL_JOB_FLAG;
}
} // namespace cc

View File

@@ -0,0 +1,40 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/Log.h"
#include "TBBJobGraph.h"
#include "TBBJobSystem.h"
namespace cc {
TBBJobSystem *TBBJobSystem::_instance = nullptr;
TBBJobSystem::TBBJobSystem(uint32_t threadCount) noexcept
: _control(tbb::global_control::max_allowed_parallelism, threadCount),
_threadCount(threadCount) {
CC_LOG_INFO("TBB Job system initialized: %d worker threads", threadCount);
}
} // namespace cc

View File

@@ -0,0 +1,61 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <algorithm>
#include <thread>
#include "base/memory/Memory.h"
#include "tbb/global_control.h"
namespace cc {
class TBBJobGraph;
class TBBJobSystem final {
public:
static TBBJobSystem *getInstance() {
if (!_instance) {
_instance = ccnew TBBJobSystem;
}
return _instance;
}
static void destroyInstance() {
CC_SAFE_DELETE(_instance);
}
TBBJobSystem() noexcept : TBBJobSystem(std::max(2u, std::thread::hardware_concurrency() - 2u)) {}
explicit TBBJobSystem(uint32_t threadCount) noexcept;
inline uint32_t threadCount() { return _threadCount; }
private:
static TBBJobSystem *_instance;
tbb::global_control _control;
uint32_t _threadCount{0u};
};
} // namespace cc

View File

@@ -0,0 +1,300 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "CallStack.h"
#if USE_MEMORY_LEAK_DETECTOR
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#define __GNU_SOURCE
#include <cxxabi.h>
#include <dlfcn.h>
#include <pthread.h>
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
#include <execinfo.h>
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
#include <DbgHelp.h>
#include <Windows.h>
#pragma comment(lib, "dbghelp.lib")
#endif
#include <sstream>
namespace cc {
ccstd::string StackFrame::toString() {
static ccstd::string unknown("unknown");
#if CC_PLATFORM == CC_PLATFORM_ANDROID
std::stringstream stream;
stream << "\tmodule: " << (module.empty() ? unknown : module)
<< "\tfunction: " << (function.empty() ? unknown : function);
return stream.str();
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
std::stringstream stream;
stream << "\tfile: " << (file.empty() ? unknown : file);
return stream.str();
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
std::stringstream stream;
stream << "\tmodule: " << (module.empty() ? unknown : module)
<< "\tfile: " << (file.empty() ? unknown : file)
<< "\tfunction: " << (function.empty() ? unknown : function)
<< "\tline: " << line;
return stream.str();
#else
return unknown;
#endif
}
#if CC_PLATFORM == CC_PLATFORM_ANDROID
struct ThreadStack {
void *stack[MAX_STACK_FRAMES];
int current;
int overflow;
};
extern "C" {
static pthread_once_t s_once = PTHREAD_ONCE_INIT;
static pthread_key_t s_threadStackKey = 0;
static void __attribute__((no_instrument_function))
init_once(void) {
pthread_key_create(&s_threadStackKey, NULL);
}
static ThreadStack *__attribute__((no_instrument_function))
getThreadStack() {
ThreadStack *ptr = (ThreadStack *)pthread_getspecific(s_threadStackKey);
if (ptr) {
return ptr;
}
ptr = (ThreadStack *)calloc(1, sizeof(ThreadStack));
ptr->current = 0;
ptr->overflow = 0;
pthread_setspecific(s_threadStackKey, ptr);
return ptr;
}
void __attribute__((no_instrument_function))
__cyg_profile_func_enter(void *this_fn, void *call_site) {
pthread_once(&s_once, init_once);
ThreadStack *ptr = getThreadStack();
if (ptr->current < MAX_STACK_FRAMES) {
ptr->stack[ptr->current++] = this_fn;
ptr->overflow = 0;
} else {
ptr->overflow++;
}
}
void __attribute__((no_instrument_function))
__cyg_profile_func_exit(void *this_fn, void *call_site) {
pthread_once(&s_once, init_once);
ThreadStack *ptr = getThreadStack();
if (ptr->overflow == 0 && ptr->current > 0) {
ptr->current--;
}
if (ptr->overflow > 0) {
ptr->overflow--;
}
}
}
#endif
ccstd::string CallStack::basename(const ccstd::string &path) {
size_t found = path.find_last_of("/\\");
if (ccstd::string::npos != found) {
return path.substr(found + 1);
} else {
return path;
}
}
ccstd::vector<void *> CallStack::backtrace() {
#if CC_PLATFORM == CC_PLATFORM_ANDROID
ccstd::vector<void *> callstack;
callstack.reserve(MAX_STACK_FRAMES);
pthread_once(&s_once, init_once);
ThreadStack *ptr = getThreadStack();
for (int i = ptr->current - 1; i >= 0; i--) {
callstack.push_back(ptr->stack[i]);
}
return callstack;
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
ccstd::vector<void *> callstack;
callstack.reserve(MAX_STACK_FRAMES);
void *array[MAX_STACK_FRAMES];
int count = ::backtrace(array, MAX_STACK_FRAMES);
for (auto i = 0; i < count; i++) {
callstack.push_back(array[i]);
}
return callstack;
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
ccstd::vector<void *> callstack;
callstack.reserve(MAX_STACK_FRAMES);
void *array[MAX_STACK_FRAMES];
int count = CaptureStackBackTrace(0, MAX_STACK_FRAMES, array, NULL);
for (auto i = 0; i < count; i++) {
callstack.push_back(array[i]);
}
return callstack;
#else
return {};
#endif
}
ccstd::vector<StackFrame> CallStack::backtraceSymbols(const ccstd::vector<void *> &callstack) {
#if CC_PLATFORM == CC_PLATFORM_ANDROID
ccstd::vector<StackFrame> frames;
size_t size = callstack.size();
for (size_t i = 0; i < size; i++) {
Dl_info info;
StackFrame frame;
if (dladdr(callstack[i], &info)) {
if (info.dli_fname && strlen(info.dli_fname) > 0) {
frame.module = basename(info.dli_fname);
}
if (info.dli_sname && strlen(info.dli_sname) > 0) {
char *real_name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr);
if (real_name) {
frame.function = real_name;
free(real_name);
} else {
frame.function = info.dli_sname;
}
}
}
frames.push_back(std::move(frame));
}
return frames;
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
size_t size = callstack.size();
if (size == 0) {
return {};
}
ccstd::vector<StackFrame> frames;
char **strs = ::backtrace_symbols(&callstack[0], size);
for (size_t i = 0; i < size; i++) {
StackFrame frame;
frame.file = strs[i];
frames.push_back(std::move(frame));
}
return frames;
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
ccstd::vector<StackFrame> frames;
#if _WIN64
using PTR_DWORD = DWORD64;
#else
using PTR_DWORD = DWORD;
#endif
size_t size = callstack.size();
for (size_t i = 0; i < size; i++) {
StackFrame frame;
PTR_DWORD address = reinterpret_cast<PTR_DWORD>(callstack[i]);
char moduelName[MAX_PATH];
#if _WIN64
PTR_DWORD moduleBase = SymGetModuleBase64(_process, address);
#else
PTR_DWORD moduleBase = SymGetModuleBase(_process, address);
#endif
if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduelName, MAX_PATH)) {
frame.module = basename(moduelName);
}
DWORD64 offset = 0;
char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_LENGTH] = {0};
PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbolBuffer;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYMBOL_LENGTH - 1;
if (SymFromAddr(_process, address, &offset, symbol)) {
frame.function = symbol->Name;
}
IMAGEHLP_LINE line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
DWORD offset_ln = 0;
if (SymGetLineFromAddr(_process, address, &offset_ln, &line)) {
frame.file = line.FileName;
frame.line = line.LineNumber;
}
frames.push_back(std::move(frame));
}
return frames;
#else
return {};
#endif
}
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
void CallStack::initSym() {
_process = GetCurrentProcess();
if (SymInitialize(_process, nullptr, true) == false) {
CC_ABORT();
}
SymSetOptions(SYMOPT_LOAD_LINES);
}
void CallStack::cleanupSym() {
SymCleanup(_process);
}
HANDLE CallStack::_process = 0;
#endif
} // namespace cc
#endif

View File

@@ -0,0 +1,76 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "../Config.h"
#if USE_MEMORY_LEAK_DETECTOR
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
#include <Windows.h>
#endif
#include <cstdint>
#include "../Macros.h"
#include "base/std/container/string.h"
#include "base/std/container/vector.h"
namespace cc {
#define MAX_STACK_FRAMES 64
#define MAX_SYMBOL_LENGTH 255
/**
* A single frame of callstack.
*/
struct CC_DLL StackFrame {
ccstd::string module;
ccstd::string file;
ccstd::string function;
uint32_t line{0};
ccstd::string toString();
};
/**
* An utility class used to backtrace callstack.
*/
class CC_DLL CallStack {
public:
static ccstd::string basename(const ccstd::string &path);
static ccstd::vector<void *> backtrace();
static ccstd::vector<StackFrame> backtraceSymbols(const ccstd::vector<void *> &callstack);
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
static void initSym();
static void cleanupSym();
private:
static HANDLE _process;
#endif
};
} // namespace cc
#endif

131
cocos/base/memory/Memory.h Normal file
View File

@@ -0,0 +1,131 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#if (CC_PLATFORM == CC_PLATFORM_IOS)
#include <Availability.h>
#endif
#ifdef _MSC_VER
#include <malloc.h>
#else
#include <cstdlib>
#endif
#include <new> // std::nothrow
#include "base/Macros.h"
namespace cc {
class MemoryAllocDealloc final {
public:
inline static void *allocateBytesAligned(size_t alignment, size_t count) {
#ifdef _MSC_VER
void *ptr = _aligned_malloc(count, alignment);
#else
// alignment is not multiple of sizeof(void*)
CC_ASSERT_ZERO(alignment % sizeof(void *));
void *ptr = nullptr;
posix_memalign(&ptr, alignment, count);
#endif
return ptr;
}
inline static void deallocateBytesAligned(void *ptr) {
#ifdef _MSC_VER
_aligned_free(ptr);
#else
free(ptr);
#endif
}
};
} // namespace cc
#define ccnew new (std::nothrow) //NOLINT(readability-identifier-naming)
#define ccnew_placement(...) new (__VA_ARGS__) //NOLINT(readability-identifier-naming)
#define CC_SAFE_DELETE(ptr) \
if (ptr) { \
delete ptr; \
(ptr) = nullptr; \
}
#define CC_SAFE_DELETE_ARRAY(ptr) \
if (ptr) { \
delete[] ptr; \
(ptr) = nullptr; \
}
#define CC_MALLOC(bytes) malloc(bytes)
#define CC_MALLOC_ALIGN(bytes, align) ::cc::MemoryAllocDealloc::allocateBytesAligned(align, bytes)
#define CC_REALLOC(ptr, bytes) realloc(ptr, bytes)
#define CC_FREE(ptr) free((void *)ptr)
#define CC_FREE_ALIGN(ptr) ::cc::MemoryAllocDealloc::deallocateBytesAligned(ptr)
#define CC_SAFE_FREE(ptr) \
if (ptr) { \
CC_FREE(ptr); \
(ptr) = nullptr; \
}
#define CC_SAFE_DESTROY(ptr) \
if (ptr) { \
(ptr)->destroy(); \
}
#define CC_SAFE_DESTROY_AND_DELETE(ptr) \
if (ptr) { \
(ptr)->destroy(); \
delete ptr; \
(ptr) = nullptr; \
}
#define CC_SAFE_DESTROY_NULL(ptr) \
if (ptr) { \
(ptr)->destroy(); \
(ptr) = nullptr; \
}
#define CC_SAFE_RELEASE(p) \
if (p) { \
(p)->release(); \
}
#define CC_SAFE_RELEASE_NULL(p) \
if (p) { \
(p)->release(); \
(p) = nullptr; \
}
#define CC_SAFE_ADD_REF(p) \
if (p) { \
(p)->addRef(); \
}
#if ((CC_PLATFORM == CC_PLATFORM_IOS) && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_11_0)) || SWIGCOCOS
#define ALIGNAS(x)
#else
#define ALIGNAS(x) alignas(x)
#endif

View File

@@ -0,0 +1,338 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "MemoryHook.h"
#include "CallStack.h"
#if USE_MEMORY_LEAK_DETECTOR
#include <sstream>
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#define __GNU_SOURCE
#include <android/log.h>
#include <dlfcn.h>
static NewHookType g_new_hooker = nullptr;
static DeleteHookType g_delete_hooker = nullptr;
extern "C" {
void *malloc(size_t size) __attribute__((weak));
void free(void *ptr) __attribute__((weak));
// Use strong symbol to overwrite the weak one.
void *malloc(size_t size) {
static MallocType system_malloc = nullptr;
if (CC_PREDICT_FALSE(system_malloc == nullptr)) {
system_malloc = (MallocType)dlsym(RTLD_NEXT, "malloc");
}
void *ptr = system_malloc(size);
if (CC_PREDICT_TRUE(g_new_hooker != nullptr)) {
g_new_hooker(ptr, size);
}
return ptr;
}
void free(void *ptr) {
static FreeType system_free = nullptr;
if (CC_PREDICT_FALSE(system_free == nullptr)) {
system_free = (FreeType)dlsym(RTLD_NEXT, "free");
}
system_free(ptr);
if (CC_PREDICT_TRUE(g_delete_hooker != nullptr)) {
g_delete_hooker(ptr);
}
}
}
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
typedef void malloc_logger_t(uint32_t aType,
uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
uintptr_t aResult, uint32_t aNumHotFramesToSkip);
extern malloc_logger_t *malloc_logger;
static malloc_logger_t *g_system_malloc_logger = nullptr;
static NewHookType g_new_hooker = nullptr;
static DeleteHookType g_delete_hooker = nullptr;
static void
cc_malloc_logger(uint32_t aType,
uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
uintptr_t aResult, uint32_t aNumHotFramesToSkip) {
if (aResult != 0) {
size_t new_size = reinterpret_cast<size_t>(aArg3);
if (new_size != 0) {
// realloc
if (CC_PREDICT_TRUE(g_delete_hooker != nullptr)) {
const void *ptr = reinterpret_cast<const void *>(aArg2);
g_delete_hooker(ptr);
}
if (CC_PREDICT_TRUE(g_new_hooker != nullptr)) {
const void *new_ptr = reinterpret_cast<const void *>(aResult);
g_new_hooker(new_ptr, new_size);
}
} else {
// malloc/calloc/valloc
if (CC_PREDICT_TRUE(g_new_hooker != nullptr)) {
const void *ptr = reinterpret_cast<const void *>(aResult);
size_t size = reinterpret_cast<size_t>(aArg2);
g_new_hooker(ptr, size);
}
}
} else {
// free
if (CC_PREDICT_TRUE(g_delete_hooker != nullptr)) {
const void *ptr = reinterpret_cast<const void *>(aArg2);
g_delete_hooker(ptr);
}
}
}
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
#include <Windows.h>
extern "C" {
typedef void (*MallocHook_NewHook)(const void *ptr, size_t size);
typedef void (*MallocHook_DeleteHook)(const void *ptr);
int MallocHook_AddNewHook(MallocHook_NewHook hook);
int MallocHook_RemoveNewHook(MallocHook_NewHook hook);
int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook);
int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook);
}
#endif
namespace cc {
static void newHook(const void *ptr, size_t size) {
uint64_t address = reinterpret_cast<uint64_t>(ptr);
GMemoryHook.addRecord(address, size);
}
static void deleteHook(const void *ptr) {
uint64_t address = reinterpret_cast<uint64_t>(ptr);
GMemoryHook.removeRecord(address);
}
MemoryHook::MemoryHook() {
registerAll();
}
MemoryHook::~MemoryHook() {
unRegisterAll();
dumpMemoryLeak();
}
void MemoryHook::addRecord(uint64_t address, size_t size) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (_hooking) {
return;
}
_hooking = true;
// {} is necessary here to make variables being destroyed before _hooking = false
{
MemoryRecord record;
record.address = address;
record.size = size;
record.callstack = CallStack::backtrace();
_records.insert({address, record});
_totalSize += size;
}
_hooking = false;
}
void MemoryHook::removeRecord(uint64_t address) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (_hooking) {
return;
}
_hooking = true;
// {} is necessary here to make variables being destroyed before _hooking = false
{
auto iter = _records.find(address);
if (iter != _records.end()) {
_totalSize -= iter->second.size;
_records.erase(iter);
}
}
_hooking = false;
}
static bool isIgnored(const StackFrame &frame) {
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
static const ccstd::vector<ccstd::string> ignoreModules = {
"SDL2",
"EGL",
"GLESv2",
"opengl32",
"nvoglv64",
"sqlite3",
"libuv",
"SogouPy"};
static const ccstd::vector<ccstd::string> ignoreFunctions = {
"type_info::name"};
for (auto &module : ignoreModules) {
if (frame.module.find(module) != ccstd::string::npos) {
return true;
}
}
for (auto &function : ignoreFunctions) {
if (frame.function.find(function) != ccstd::string::npos) {
return true;
}
}
return false;
#else
return false;
#endif
}
void MemoryHook::dumpMemoryLeak() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
CallStack::initSym();
#endif
std::stringstream startStream;
startStream << std::endl;
startStream << "---------------------------------------------------------------------------------------------------------" << std::endl;
startStream << "--------------------------------------memory leak report start-------------------------------------------" << std::endl;
startStream << "---------------------------------------------------------------------------------------------------------" << std::endl;
log(startStream.str());
if (_records.size() == 0) {
std::stringstream stream;
stream << std::endl;
stream << "Congratulations! There is no memory leak at all." << std::endl;
log(stream.str());
}
uint32_t i = 0;
size_t skipSize = 0;
uint32_t skipCount = 0;
for (const auto &iter : _records) {
bool skip = false;
auto frames = CallStack::backtraceSymbols(iter.second.callstack);
for (auto &frame : frames) {
if (isIgnored(frame)) {
skip = true;
break;
}
}
if (skip) {
skipSize += iter.second.size;
skipCount++;
continue;
}
std::stringstream stream;
int k = 0;
stream << std::endl;
stream << "<" << ++i << ">:"
<< "leak " << iter.second.size << " bytes at 0x" << std::hex << iter.second.address << std::dec << std::endl;
stream << "\tcallstack:" << std::endl;
for (auto &frame : frames) {
stream << "\t[" << ++k << "]:" << frame.toString() << std::endl;
}
log(stream.str());
}
std::stringstream endStream;
endStream << std::endl
<< "Total leak count: " << _records.size() << " with " << _totalSize << " bytes, "
<< "Total skip count: " << skipCount << " with " << skipSize << " bytes" << std::endl;
endStream << "---------------------------------------------------------------------------------------------------------" << std::endl;
endStream << "--------------------------------------memory leak report end---------------------------------------------" << std::endl;
endStream << "---------------------------------------------------------------------------------------------------------" << std::endl;
log(endStream.str());
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
CallStack::cleanupSym();
#endif
}
void MemoryHook::log(const ccstd::string &msg) {
#if (CC_PLATFORM == CC_PLATFORM_ANDROID)
__android_log_write(ANDROID_LOG_WARN, "Cocos", msg.c_str());
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
fputs(msg.c_str(), stdout);
#elif (CC_PLATFORM == CC_PLATFORM_WINDOWS)
OutputDebugStringA(msg.c_str());
#endif
}
void MemoryHook::registerAll() {
#if CC_PLATFORM == CC_PLATFORM_ANDROID
g_new_hooker = newHook;
g_delete_hooker = deleteHook;
free(malloc(1)); // force to init system_malloc/system_free
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
g_system_malloc_logger = malloc_logger;
malloc_logger = cc_malloc_logger;
g_new_hooker = newHook;
g_delete_hooker = deleteHook;
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
MallocHook_AddNewHook(&newHook);
MallocHook_AddDeleteHook(&deleteHook);
#endif
}
void MemoryHook::unRegisterAll() {
#if CC_PLATFORM == CC_PLATFORM_ANDROID
g_new_hooker = nullptr;
g_delete_hooker = nullptr;
#elif CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS
malloc_logger = g_system_malloc_logger;
g_new_hooker = nullptr;
g_delete_hooker = nullptr;
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
MallocHook_RemoveNewHook(&newHook);
MallocHook_RemoveDeleteHook(&deleteHook);
#endif
}
} // namespace cc
#endif

View File

@@ -0,0 +1,93 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "../Config.h"
#if USE_MEMORY_LEAK_DETECTOR
#include <mutex>
#include "../Macros.h"
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "base/std/container/vector.h"
typedef void *(*MallocType)(size_t size);
typedef void (*FreeType)(void *ptr);
typedef void (*NewHookType)(const void *ptr, size_t size);
typedef void (*DeleteHookType)(const void *ptr);
namespace cc {
struct CC_DLL MemoryRecord {
uint64_t address{0};
size_t size{0};
ccstd::vector<void *> callstack;
};
class CC_DLL MemoryHook {
public:
MemoryHook();
~MemoryHook();
/**
* RecordMap's key is memory address.
*/
using RecordMap = ccstd::unordered_map<uint64_t, MemoryRecord>;
void addRecord(uint64_t address, size_t size);
void removeRecord(uint64_t address);
inline size_t getTotalSize() const { return _totalSize; }
private:
/**
* Dump all memory leaks to output window
*/
void dumpMemoryLeak();
static void log(const ccstd::string &msg);
/**
* Register all malloc hooks
*/
void registerAll();
/**
* Unregister all malloc hooks
*/
void unRegisterAll();
private:
std::recursive_mutex _mutex;
bool _hooking{false};
RecordMap _records;
size_t _totalSize{0U};
};
extern MemoryHook GMemoryHook;
} // namespace cc
#endif

79
cocos/base/std/any.h Normal file
View File

@@ -0,0 +1,79 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#ifdef USE_CXX_17
#include <any>
namespace ccstd {
using std::any;
using std::any_cast;
} // namespace ccstd
#else
#include "boost/any.hpp"
namespace ccstd {
class any : public boost::any { // NOLINT // use std style
public:
using boost::any::any;
inline bool has_value() const noexcept { // NOLINT // use std style
return !this->empty();
}
};
template <typename ValueType>
inline ValueType *any_cast(any *operand) noexcept { // NOLINT // use std style
return boost::any_cast<ValueType>(operand);
}
template <typename ValueType>
inline const ValueType *any_cast(const any *operand) noexcept { // NOLINT // use std style
return boost::any_cast<ValueType>(operand);
}
template <typename ValueType>
inline ValueType any_cast(any &operand) { // NOLINT // use std style
return boost::any_cast<ValueType>(operand);
}
template <typename ValueType>
inline ValueType any_cast(const any &operand) { // NOLINT // use std style
return boost::any_cast<ValueType>(operand);
}
template <typename ValueType>
inline ValueType any_cast(any &&operand) { // NOLINT // use std style
return boost::any_cast<ValueType>(operand);
}
} // namespace ccstd
#endif

View File

@@ -0,0 +1,31 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <array>
namespace ccstd {
using std::array;
} // namespace ccstd

View File

@@ -0,0 +1,37 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <deque>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::deque;
namespace pmr {
template <class T>
using deque = std::deque<T, boost::container::pmr::polymorphic_allocator<T>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,37 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <list>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::list;
namespace pmr {
template <class T>
using list = std::list<T, boost::container::pmr::polymorphic_allocator<T>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,38 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <map>
#include <utility>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::map;
namespace pmr {
template <class Key, class T, class Compare = std::less<Key>>
using map = std::map<Key, T, Compare, boost::container::pmr::polymorphic_allocator<std::pair<const Key, T>>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,37 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <queue>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::queue;
namespace pmr {
template <class T>
using queue = std::queue<T, boost::container::pmr::polymorphic_allocator<T>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,39 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
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.
****************************************************************************/
#pragma once
#include <set>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::set;
namespace pmr {
template <
class Key,
class Compare = std::less<Key>>
using set = std::set<Key, Compare, boost::container::pmr::polymorphic_allocator<Key>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,36 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <string>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::string;
namespace pmr {
using string = std::basic_string<char, std::char_traits<char>, boost::container::pmr::polymorphic_allocator<char>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,51 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <unordered_map>
#include <utility>
#include "base/std/hash/hash_fwd.hpp"
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::unordered_map;
using std::unordered_multimap;
namespace pmr {
template <
class Key,
class T,
class Hash = ccstd::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_map = std::unordered_map<Key, T, Hash, Pred, boost::container::pmr::polymorphic_allocator<std::pair<const Key, T>>>;
template <
class Key,
class T,
class Hash = ccstd::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred, boost::container::pmr::polymorphic_allocator<std::pair<const Key, T>>>;
} // namespace pmr
} // namespace ccstd

View File

@@ -0,0 +1,40 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
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.
****************************************************************************/
#pragma once
#include <unordered_set>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::unordered_set;
namespace pmr {
template <
class Key,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>>
using unordered_set = std::unordered_set<Key, Hash, KeyEqual, boost::container::pmr::polymorphic_allocator<Key>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,37 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <vector>
#include "boost/container/pmr/polymorphic_allocator.hpp"
namespace ccstd {
using std::vector;
namespace pmr {
template <class T>
using vector = std::vector<T, boost::container::pmr::polymorphic_allocator<T>>;
}
} // namespace ccstd

View File

@@ -0,0 +1,336 @@
// Copyright 2005-2009 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#if !defined(CCSTD_FUNCTIONAL_HASH_DETAIL_FLOAT_FUNCTIONS_HPP)
#define CCSTD_FUNCTIONAL_HASH_DETAIL_FLOAT_FUNCTIONS_HPP
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/config/no_tr1/cmath.hpp>
// Set BOOST_HASH_CONFORMANT_FLOATS to 1 for libraries known to have
// sufficiently good floating point support to not require any
// workarounds.
//
// When set to 0, the library tries to automatically
// use the best available implementation. This normally works well, but
// breaks when ambiguities are created by odd namespacing of the functions.
//
// Note that if this is set to 0, the library should still take full
// advantage of the platform's floating point support.
#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)
# define BOOST_HASH_CONFORMANT_FLOATS 0
#elif defined(__LIBCOMO__)
# define BOOST_HASH_CONFORMANT_FLOATS 0
#elif defined(__STD_RWCOMPILER_H__) || defined(_RWSTD_VER)
// Rogue Wave library:
# define BOOST_HASH_CONFORMANT_FLOATS 0
#elif defined(_LIBCPP_VERSION)
// libc++
# define BOOST_HASH_CONFORMANT_FLOATS 1
#elif defined(__GLIBCPP__) || defined(__GLIBCXX__)
// GNU libstdc++ 3
# if defined(__GNUC__) && __GNUC__ >= 4
# define BOOST_HASH_CONFORMANT_FLOATS 1
# else
# define BOOST_HASH_CONFORMANT_FLOATS 0
# endif
#elif defined(__STL_CONFIG_H)
// generic SGI STL
# define BOOST_HASH_CONFORMANT_FLOATS 0
#elif defined(__MSL_CPP__)
// MSL standard lib:
# define BOOST_HASH_CONFORMANT_FLOATS 0
#elif defined(__IBMCPP__)
// VACPP std lib (probably conformant for much earlier version).
# if __IBMCPP__ >= 1210
# define BOOST_HASH_CONFORMANT_FLOATS 1
# else
# define BOOST_HASH_CONFORMANT_FLOATS 0
# endif
#elif defined(MSIPL_COMPILE_H)
// Modena C++ standard library
# define BOOST_HASH_CONFORMANT_FLOATS 0
#elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER)
// Dinkumware Library (this has to appear after any possible replacement libraries):
# if _CPPLIB_VER >= 405
# define BOOST_HASH_CONFORMANT_FLOATS 1
# else
# define BOOST_HASH_CONFORMANT_FLOATS 0
# endif
#else
# define BOOST_HASH_CONFORMANT_FLOATS 0
#endif
#if BOOST_HASH_CONFORMANT_FLOATS
// The standard library is known to be compliant, so don't use the
// configuration mechanism.
namespace ccstd {
namespace hash_detail {
template <typename Float>
struct call_ldexp {
typedef Float float_type;
inline Float operator()(Float x, int y) const {
return std::ldexp(x, y);
}
};
template <typename Float>
struct call_frexp {
typedef Float float_type;
inline Float operator()(Float x, int* y) const {
return std::frexp(x, y);
}
};
template <typename Float>
struct select_hash_type
{
typedef Float type;
};
}
}
#else // BOOST_HASH_CONFORMANT_FLOATS == 0
// The C++ standard requires that the C float functions are overloarded
// for float, double and long double in the std namespace, but some of the older
// library implementations don't support this. On some that don't, the C99
// float functions (frexpf, frexpl, etc.) are available.
//
// The following tries to automatically detect which are available.
namespace ccstd {
namespace hash_detail {
// Returned by dummy versions of the float functions.
struct not_found {
// Implicitly convertible to float and long double in order to avoid
// a compile error when the dummy float functions are used.
inline operator float() const { return 0; }
inline operator long double() const { return 0; }
};
// A type for detecting the return type of functions.
template <typename T> struct is;
template <> struct is<float> { char x[10]; };
template <> struct is<double> { char x[20]; };
template <> struct is<long double> { char x[30]; };
template <> struct is<boost::hash_detail::not_found> { char x[40]; };
// Used to convert the return type of a function to a type for sizeof.
template <typename T> is<T> float_type(T);
// call_ldexp
//
// This will get specialized for float and long double
template <typename Float> struct call_ldexp
{
typedef double float_type;
inline double operator()(double a, int b) const
{
using namespace std;
return ldexp(a, b);
}
};
// call_frexp
//
// This will get specialized for float and long double
template <typename Float> struct call_frexp
{
typedef double float_type;
inline double operator()(double a, int* b) const
{
using namespace std;
return frexp(a, b);
}
};
}
}
// A namespace for dummy functions to detect when the actual function we want
// isn't available. ldexpl, ldexpf etc. might be added tby the macros below.
//
// AFAICT these have to be outside of the boost namespace, as if they're in
// the boost namespace they'll always be preferable to any other function
// (since the arguments are built in types, ADL can't be used).
namespace ccstd_hash_detect_float_functions {
template <class Float> boost::hash_detail::not_found ldexp(Float, int);
template <class Float> boost::hash_detail::not_found frexp(Float, int*);
}
// Macros for generating specializations of call_ldexp and call_frexp.
//
// check_cpp and check_c99 check if the C++ or C99 functions are available.
//
// Then the call_* functions select an appropriate implementation.
//
// I used c99_func in a few places just to get a unique name.
//
// Important: when using 'using namespace' at namespace level, include as
// little as possible in that namespace, as Visual C++ has an odd bug which
// can cause the namespace to be imported at the global level. This seems to
// happen mainly when there's a template in the same namesapce.
#define BOOST_HASH_CALL_FLOAT_FUNC(cpp_func, c99_func, type1, type2) \
namespace ccstd_hash_detect_float_functions { \
template <class Float> \
boost::hash_detail::not_found c99_func(Float, type2); \
} \
\
namespace ccstd { \
namespace hash_detail { \
namespace c99_func##_detect { \
using namespace std; \
using namespace ccstd_hash_detect_float_functions; \
\
struct check { \
static type1 x; \
static type2 y; \
BOOST_STATIC_CONSTANT(bool, cpp = \
sizeof(float_type(cpp_func(x,y))) \
== sizeof(is<type1>)); \
BOOST_STATIC_CONSTANT(bool, c99 = \
sizeof(float_type(c99_func(x,y))) \
== sizeof(is<type1>)); \
}; \
} \
\
template <bool x> \
struct call_c99_##c99_func : \
boost::hash_detail::call_##cpp_func<double> {}; \
\
template <> \
struct call_c99_##c99_func<true> { \
typedef type1 float_type; \
\
template <typename T> \
inline type1 operator()(type1 a, T b) const \
{ \
using namespace std; \
return c99_func(a, b); \
} \
}; \
\
template <bool x> \
struct call_cpp_##c99_func : \
call_c99_##c99_func< \
::boost::hash_detail::c99_func##_detect::check::c99 \
> {}; \
\
template <> \
struct call_cpp_##c99_func<true> { \
typedef type1 float_type; \
\
template <typename T> \
inline type1 operator()(type1 a, T b) const \
{ \
using namespace std; \
return cpp_func(a, b); \
} \
}; \
\
template <> \
struct call_##cpp_func<type1> : \
call_cpp_##c99_func< \
::boost::hash_detail::c99_func##_detect::check::cpp \
> {}; \
} \
}
#define BOOST_HASH_CALL_FLOAT_MACRO(cpp_func, c99_func, type1, type2) \
namespace ccstd { \
namespace hash_detail { \
\
template <> \
struct call_##cpp_func<type1> { \
typedef type1 float_type; \
inline type1 operator()(type1 x, type2 y) const { \
return c99_func(x, y); \
} \
}; \
} \
}
#if defined(ldexpf)
BOOST_HASH_CALL_FLOAT_MACRO(ldexp, ldexpf, float, int)
#else
BOOST_HASH_CALL_FLOAT_FUNC(ldexp, ldexpf, float, int)
#endif
#if defined(ldexpl)
BOOST_HASH_CALL_FLOAT_MACRO(ldexp, ldexpl, long double, int)
#else
BOOST_HASH_CALL_FLOAT_FUNC(ldexp, ldexpl, long double, int)
#endif
#if defined(frexpf)
BOOST_HASH_CALL_FLOAT_MACRO(frexp, frexpf, float, int*)
#else
BOOST_HASH_CALL_FLOAT_FUNC(frexp, frexpf, float, int*)
#endif
#if defined(frexpl)
BOOST_HASH_CALL_FLOAT_MACRO(frexp, frexpl, long double, int*)
#else
BOOST_HASH_CALL_FLOAT_FUNC(frexp, frexpl, long double, int*)
#endif
#undef BOOST_HASH_CALL_FLOAT_MACRO
#undef BOOST_HASH_CALL_FLOAT_FUNC
namespace ccstd
{
namespace hash_detail
{
template <typename Float1, typename Float2>
struct select_hash_type_impl {
typedef double type;
};
template <>
struct select_hash_type_impl<float, float> {
typedef float type;
};
template <>
struct select_hash_type_impl<long double, long double> {
typedef long double type;
};
// select_hash_type
//
// If there is support for a particular floating point type, use that
// otherwise use double (there's always support for double).
template <typename Float>
struct select_hash_type : select_hash_type_impl<
BOOST_DEDUCED_TYPENAME call_ldexp<Float>::float_type,
BOOST_DEDUCED_TYPENAME call_frexp<Float>::float_type
> {};
}
}
#endif // BOOST_HASH_CONFORMANT_FLOATS
#endif

View File

@@ -0,0 +1,271 @@
// Copyright 2005-2012 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#if !defined(CCSTD_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_HEADER)
#define CCSTD_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_HEADER
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include "base/std/hash/detail/float_functions.hpp"
#include "base/std/hash/detail/limits.hpp"
#include <boost/core/enable_if.hpp>
#include <boost/integer/static_log2.hpp>
#include <boost/cstdint.hpp>
#include <boost/assert.hpp>
#include <boost/limits.hpp>
#include <cstring>
#if defined(BOOST_MSVC)
#pragma warning(push)
#if BOOST_MSVC >= 1400
#pragma warning(disable:6294) // Ill-defined for-loop: initial condition does
// not satisfy test. Loop body not executed
#endif
#endif
// Can we use fpclassify?
// STLport
#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)
#define BOOST_HASH_USE_FPCLASSIFY 0
// GNU libstdc++ 3
#elif defined(__GLIBCPP__) || defined(__GLIBCXX__)
# if (defined(__USE_ISOC99) || defined(_GLIBCXX_USE_C99_MATH)) && \
!(defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
# define BOOST_HASH_USE_FPCLASSIFY 1
# else
# define BOOST_HASH_USE_FPCLASSIFY 0
# endif
// Everything else
#else
# define BOOST_HASH_USE_FPCLASSIFY 0
#endif
namespace ccstd
{
namespace hash_detail
{
inline void hash_float_combine(ccstd::hash_t& seed, ccstd::hash_t value)
{
seed ^= value + (seed<<6) + (seed>>2);
}
////////////////////////////////////////////////////////////////////////
// Binary hash function
//
// Only used for floats with known iec559 floats, and certain values in
// numeric_limits
inline ccstd::hash_t hash_binary(char* ptr, ccstd::hash_t length)
{
ccstd::hash_t seed = 0;
if (length >= sizeof(ccstd::hash_t)) {
std::memcpy(&seed, ptr, sizeof(ccstd::hash_t));
length -= sizeof(ccstd::hash_t);
ptr += sizeof(ccstd::hash_t);
while(length >= sizeof(ccstd::hash_t)) {
ccstd::hash_t buffer = 0;
std::memcpy(&buffer, ptr, sizeof(ccstd::hash_t));
hash_float_combine(seed, buffer);
length -= sizeof(ccstd::hash_t);
ptr += sizeof(ccstd::hash_t);
}
}
if (length > 0) {
ccstd::hash_t buffer = 0;
std::memcpy(&buffer, ptr, length);
hash_float_combine(seed, buffer);
}
return seed;
}
template <typename Float, unsigned digits, unsigned max_exponent>
struct enable_binary_hash
{
BOOST_STATIC_CONSTANT(bool, value =
std::numeric_limits<Float>::is_iec559 &&
std::numeric_limits<Float>::digits == digits &&
std::numeric_limits<Float>::radix == 2 &&
std::numeric_limits<Float>::max_exponent == max_exponent);
};
template <typename Float>
inline ccstd::hash_t float_hash_impl(Float v,
BOOST_DEDUCED_TYPENAME boost::enable_if_c<
enable_binary_hash<Float, 24, 128>::value,
ccstd::hash_t>::type)
{
return hash_binary((char*) &v, 4);
}
template <typename Float>
inline ccstd::hash_t float_hash_impl(Float v,
BOOST_DEDUCED_TYPENAME boost::enable_if_c<
enable_binary_hash<Float, 53, 1024>::value,
ccstd::hash_t>::type)
{
return hash_binary((char*) &v, 8);
}
template <typename Float>
inline ccstd::hash_t float_hash_impl(Float v,
BOOST_DEDUCED_TYPENAME boost::enable_if_c<
enable_binary_hash<Float, 64, 16384>::value,
ccstd::hash_t>::type)
{
return hash_binary((char*) &v, 10);
}
template <typename Float>
inline ccstd::hash_t float_hash_impl(Float v,
BOOST_DEDUCED_TYPENAME boost::enable_if_c<
enable_binary_hash<Float, 113, 16384>::value,
ccstd::hash_t>::type)
{
return hash_binary((char*) &v, 16);
}
////////////////////////////////////////////////////////////////////////
// Portable hash function
//
// Used as a fallback when the binary hash function isn't supported.
template <class T>
inline ccstd::hash_t float_hash_impl2(T v)
{
boost::hash_detail::call_frexp<T> frexp;
boost::hash_detail::call_ldexp<T> ldexp;
int exp = 0;
v = frexp(v, &exp);
// A postive value is easier to hash, so combine the
// sign with the exponent and use the absolute value.
if(v < 0) {
v = -v;
exp += limits<T>::max_exponent -
limits<T>::min_exponent;
}
v = ldexp(v, limits<ccstd::hash_t>::digits);
ccstd::hash_t seed = static_cast<ccstd::hash_t>(v);
v -= static_cast<T>(seed);
// ceiling(digits(T) * log2(radix(T))/ digits(size_t)) - 1;
ccstd::hash_t const length
= (limits<T>::digits *
boost::static_log2<limits<T>::radix>::value
+ limits<ccstd::hash_t>::digits - 1)
/ limits<ccstd::hash_t>::digits;
for(ccstd::hash_t i = 0; i != length; ++i)
{
v = ldexp(v, limits<ccstd::hash_t>::digits);
ccstd::hash_t part = static_cast<ccstd::hash_t>(v);
v -= static_cast<T>(part);
hash_float_combine(seed, part);
}
hash_float_combine(seed, static_cast<ccstd::hash_t>(exp));
return seed;
}
#if !defined(BOOST_HASH_DETAIL_TEST_WITHOUT_GENERIC)
template <class T>
inline ccstd::hash_t float_hash_impl(T v, ...)
{
typedef BOOST_DEDUCED_TYPENAME select_hash_type<T>::type type;
return float_hash_impl2(static_cast<type>(v));
}
#endif
}
}
#if BOOST_HASH_USE_FPCLASSIFY
#include <boost/config/no_tr1/cmath.hpp>
namespace ccstd
{
namespace hash_detail
{
template <class T>
inline ccstd::hash_t float_hash_value(T v)
{
#if defined(fpclassify)
switch (fpclassify(v))
#elif BOOST_HASH_CONFORMANT_FLOATS
switch (std::fpclassify(v))
#else
using namespace std;
switch (fpclassify(v))
#endif
{
case FP_ZERO:
return 0;
case FP_INFINITE:
return (ccstd::hash_t)(v > 0 ? -1 : -2);
case FP_NAN:
return (ccstd::hash_t)(-3);
case FP_NORMAL:
case FP_SUBNORMAL:
return float_hash_impl(v, 0);
default:
BOOST_ASSERT(0);
return 0;
}
}
}
}
#else // !BOOST_HASH_USE_FPCLASSIFY
namespace ccstd
{
namespace hash_detail
{
template <class T>
inline bool is_zero(T v)
{
#if !defined(__GNUC__) && !defined(__clang__)
return v == 0;
#else
// GCC's '-Wfloat-equal' will complain about comparing
// v to 0, but because it disables warnings for system
// headers it won't complain if you use std::equal_to to
// compare with 0. Resulting in this silliness:
return std::equal_to<T>()(v, 0);
#endif
}
template <class T>
inline ccstd::hash_t float_hash_value(T v)
{
return boost::hash_detail::is_zero(v) ? 0 : float_hash_impl(v, 0);
}
}
}
#endif // BOOST_HASH_USE_FPCLASSIFY
#undef BOOST_HASH_USE_FPCLASSIFY
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
#endif

View File

@@ -0,0 +1,62 @@
// Copyright 2005-2009 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// On some platforms std::limits gives incorrect values for long double.
// This tries to work around them.
#if !defined(CCSTD_FUNCTIONAL_HASH_DETAIL_LIMITS_HEADER)
#define CCSTD_FUNCTIONAL_HASH_DETAIL_LIMITS_HEADER
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/limits.hpp>
// On OpenBSD, numeric_limits is not reliable for long doubles, but
// the macros defined in <float.h> are and support long double when STLport
// doesn't.
#if defined(__OpenBSD__) || defined(_STLP_NO_LONG_DOUBLE)
#include <float.h>
#endif
namespace ccstd
{
namespace hash_detail
{
template <class T>
struct limits : std::numeric_limits<T> {};
#if defined(__OpenBSD__) || defined(_STLP_NO_LONG_DOUBLE)
template <>
struct limits<long double>
: std::numeric_limits<long double>
{
static long double epsilon() {
return LDBL_EPSILON;
}
static long double (max)() {
return LDBL_MAX;
}
static long double (min)() {
return LDBL_MIN;
}
BOOST_STATIC_CONSTANT(int, digits = LDBL_MANT_DIG);
BOOST_STATIC_CONSTANT(int, max_exponent = LDBL_MAX_EXP);
BOOST_STATIC_CONSTANT(int, min_exponent = LDBL_MIN_EXP);
#if defined(_STLP_NO_LONG_DOUBLE)
BOOST_STATIC_CONSTANT(int, radix = FLT_RADIX);
#endif
};
#endif // __OpenBSD__
}
}
#endif

View File

@@ -0,0 +1,361 @@
// Copyright 2005-2009 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Based on Peter Dimov's proposal
// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
// issue 6.18.
// This implements the extensions to the standard.
// It's undocumented, so you shouldn't use it....
#if !defined(CCSTD_FUNCTIONAL_HASH_EXTENSIONS_HPP)
#define CCSTD_FUNCTIONAL_HASH_EXTENSIONS_HPP
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include "base/std/hash/hash.h"
#include <boost/detail/container_fwd.hpp>
#include <boost/core/enable_if.hpp>
#include <boost/static_assert.hpp>
#if !defined(BOOST_NO_CXX11_HDR_ARRAY)
# include <array>
#endif
#if !defined(BOOST_NO_CXX11_HDR_TUPLE)
# include <tuple>
#endif
#include <memory>
#if defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)
#include <boost/type_traits/is_array.hpp>
#endif
namespace ccstd
{
template <class A, class B>
hash_t hash_value(std::pair<A, B> const&);
template <class T, class A>
hash_t hash_value(std::vector<T, A> const&);
template <class T, class A>
hash_t hash_value(std::list<T, A> const& v);
template <class T, class A>
hash_t hash_value(std::deque<T, A> const& v);
template <class K, class C, class A>
hash_t hash_value(std::set<K, C, A> const& v);
template <class K, class C, class A>
hash_t hash_value(std::multiset<K, C, A> const& v);
template <class K, class T, class C, class A>
hash_t hash_value(std::map<K, T, C, A> const& v);
template <class K, class T, class C, class A>
hash_t hash_value(std::multimap<K, T, C, A> const& v);
template <class T>
hash_t hash_value(std::complex<T> const&);
template <class A, class B>
hash_t hash_value(std::pair<A, B> const& v)
{
hash_t seed = 0;
ccstd::hash_combine(seed, v.first);
ccstd::hash_combine(seed, v.second);
return seed;
}
template <class T, class A>
hash_t hash_value(std::vector<T, A> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
template <class T, class A>
hash_t hash_value(std::list<T, A> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
template <class T, class A>
hash_t hash_value(std::deque<T, A> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
template <class K, class C, class A>
hash_t hash_value(std::set<K, C, A> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
template <class K, class C, class A>
hash_t hash_value(std::multiset<K, C, A> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
template <class K, class T, class C, class A>
hash_t hash_value(std::map<K, T, C, A> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
template <class K, class T, class C, class A>
hash_t hash_value(std::multimap<K, T, C, A> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
template <class T>
hash_t hash_value(std::complex<T> const& v)
{
ccstd::hash<T> hasher;
hash_t seed = hasher(v.imag());
seed ^= hasher(v.real()) + (seed<<6) + (seed>>2);
return seed;
}
#if !defined(BOOST_NO_CXX11_HDR_ARRAY)
template <class T, std::size_t N>
hash_t hash_value(std::array<T, N> const& v)
{
return ccstd::hash_range(v.begin(), v.end());
}
#endif
#if !defined(BOOST_NO_CXX11_HDR_TUPLE)
namespace hash_detail {
template <std::size_t I, typename T>
inline typename boost::enable_if_c<(I == std::tuple_size<T>::value),
void>::type
hash_combine_tuple(hash_t&, T const&)
{
}
template <std::size_t I, typename T>
inline typename boost::enable_if_c<(I < std::tuple_size<T>::value),
void>::type
hash_combine_tuple(hash_t& seed, T const& v)
{
ccstd::hash_combine(seed, std::get<I>(v));
ccstd::hash_detail::hash_combine_tuple<I + 1>(seed, v);
}
template <typename T>
inline hash_t hash_tuple(T const& v)
{
hash_t seed = 0;
ccstd::hash_detail::hash_combine_tuple<0>(seed, v);
return seed;
}
}
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <typename... T>
inline hash_t hash_value(std::tuple<T...> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
#else
inline hash_t hash_value(std::tuple<> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0>
inline hash_t hash_value(std::tuple<A0> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1>
inline hash_t hash_value(std::tuple<A0, A1> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2>
inline hash_t hash_value(std::tuple<A0, A1, A2> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2, typename A3>
inline hash_t hash_value(std::tuple<A0, A1, A2, A3> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2, typename A3, typename A4>
inline hash_t hash_value(std::tuple<A0, A1, A2, A3, A4> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
inline hash_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
inline hash_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
inline hash_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
inline hash_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
inline hash_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> const& v)
{
return ccstd::hash_detail::hash_tuple(v);
}
#endif
#endif
#if !defined(BOOST_NO_CXX11_SMART_PTR)
template <typename T>
inline hash_t hash_value(std::shared_ptr<T> const& x) {
return ccstd::hash_value(x.get());
}
template <typename T, typename Deleter>
inline hash_t hash_value(std::unique_ptr<T, Deleter> const& x) {
return ccstd::hash_value(x.get());
}
#endif
//
// call_hash_impl
//
// On compilers without function template ordering, this deals with arrays.
#if defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)
namespace hash_detail
{
template <bool IsArray>
struct call_hash_impl
{
template <class T>
struct inner
{
static hash_t call(T const& v)
{
using namespace ccstd;
return hash_value(v);
}
};
};
template <>
struct call_hash_impl<true>
{
template <class Array>
struct inner
{
static hash_t call(Array const& v)
{
const int size = sizeof(v) / sizeof(*v);
return ccstd::hash_range(v, v + size);
}
};
};
template <class T>
struct call_hash
: public call_hash_impl<ccstd::is_array<T>::value>
::BOOST_NESTED_TEMPLATE inner<T>
{
};
}
#endif // BOOST_NO_FUNCTION_TEMPLATE_ORDERING
//
// ccstd::hash
//
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
template <class T> struct hash
: ccstd::hash_detail::hash_base<T>
{
#if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)
hash_t operator()(T const& val) const
{
return hash_value(val);
}
#else
hash_t operator()(T const& val) const
{
return hash_detail::call_hash<T>::call(val);
}
#endif
};
#if BOOST_WORKAROUND(__DMC__, <= 0x848)
template <class T, unsigned int n> struct hash<T[n]>
: ccstd::hash_detail::hash_base<T[n]>
{
hash_t operator()(const T* val) const
{
return ccstd::hash_range(val, val+n);
}
};
#endif
#else // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
// On compilers without partial specialization, ccstd::hash<T>
// has already been declared to deal with pointers, so just
// need to supply the non-pointer version of hash_impl.
namespace hash_detail
{
template <bool IsPointer>
struct hash_impl;
template <>
struct hash_impl<false>
{
template <class T>
struct inner
: ccstd::hash_detail::hash_base<T>
{
#if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)
hash_t operator()(T const& val) const
{
return hash_value(val);
}
#else
hash_t operator()(T const& val) const
{
return hash_detail::call_hash<T>::call(val);
}
#endif
};
};
}
#endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
}
#endif

783
cocos/base/std/hash/hash.h Normal file
View File

@@ -0,0 +1,783 @@
// Copyright 2005-2014 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Based on Peter Dimov's proposal
// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
// issue 6.18.
//
// This also contains public domain code from MurmurHash. From the
// MurmurHash header:
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
#if !defined(CCSTD_FUNCTIONAL_HASH_HASH_HPP)
#define CCSTD_FUNCTIONAL_HASH_HASH_HPP
#include "boost/container_hash/hash.hpp"
#include "base/std/hash/hash_fwd.hpp"
#include <functional>
#include <iterator>
#include "base/std/hash/detail/hash_float.hpp"
#include <string>
#include <boost/limits.hpp>
#include <boost/type_traits/is_enum.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/core/enable_if.hpp>
#include <boost/cstdint.hpp>
#include <climits>
#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
#include <boost/type_traits/is_pointer.hpp>
#endif
#if !defined(BOOST_NO_CXX11_HDR_TYPEINDEX)
#include <typeindex>
#endif
#if !defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR)
#include <system_error>
#endif
#if defined(BOOST_MSVC)
#pragma warning(push)
#if BOOST_MSVC >= 1400
#pragma warning(disable:6295) // Ill-defined for-loop : 'unsigned int' values
// are always of range '0' to '4294967295'.
// Loop executes infinitely.
#endif
#endif
#if BOOST_WORKAROUND(__GNUC__, < 3) \
&& !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION)
#define CCSTD_HASH_CHAR_TRAITS string_char_traits
#else
#define CCSTD_HASH_CHAR_TRAITS char_traits
#endif
#if defined(_MSC_VER)
# define CCSTD_FUNCTIONAL_HASH_ROTL32(x, r) _rotl(x,r)
#else
# define CCSTD_FUNCTIONAL_HASH_ROTL32(x, r) (x << r) | (x >> (32 - r))
#endif
// Detect whether standard library has C++17 headers
#if !defined(CCSTD_HASH_CXX17)
# if defined(BOOST_MSVC)
# if defined(_HAS_CXX17) && _HAS_CXX17
# define CCSTD_HASH_CXX17 1
# endif
# elif defined(__cplusplus) && __cplusplus >= 201703
# define CCSTD_HASH_CXX17 1
# endif
#endif
#if !defined(CCSTD_HASH_CXX17)
# define CCSTD_HASH_CXX17 0
#endif
#if CCSTD_HASH_CXX17 && defined(__has_include)
# if !defined(CCSTD_HASH_HAS_STRING_VIEW) && __has_include(<string_view>)
# define CCSTD_HASH_HAS_STRING_VIEW 1
# endif
# if !defined(CCSTD_HASH_HAS_OPTIONAL) && __has_include(<optional>)
# define CCSTD_HASH_HAS_OPTIONAL 1
# endif
# if !defined(CCSTD_HASH_HAS_VARIANT) && __has_include(<variant>)
# define CCSTD_HASH_HAS_VARIANT 1
# endif
#endif
#if !defined(CCSTD_HASH_HAS_STRING_VIEW)
# define CCSTD_HASH_HAS_STRING_VIEW 0
#endif
#if !defined(CCSTD_HASH_HAS_OPTIONAL)
# define CCSTD_HASH_HAS_OPTIONAL 0
#endif
#if !defined(CCSTD_HASH_HAS_VARIANT)
# define CCSTD_HASH_HAS_VARIANT 0
#endif
#if CCSTD_HASH_HAS_STRING_VIEW
# include <string_view>
#endif
#if CCSTD_HASH_HAS_OPTIONAL
# include <optional>
#endif
#if CCSTD_HASH_HAS_VARIANT
# include <variant>
#endif
namespace ccstd
{
namespace hash_detail
{
#if defined(BOOST_NO_CXX98_FUNCTION_BASE)
template <typename T>
struct hash_base
{
typedef T argument_type;
typedef hash_t result_type;
};
#else
template <typename T>
struct hash_base : std::unary_function<T, hash_t> {};
#endif
struct enable_hash_value { typedef hash_t type; };
template <typename T> struct basic_numbers {};
template <typename T> struct long_numbers;
template <typename T> struct ulong_numbers;
template <typename T> struct float_numbers {};
template <> struct basic_numbers<bool> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<char> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<unsigned char> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<signed char> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<short> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<unsigned short> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<int> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<unsigned int> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<long> :
ccstd::hash_detail::enable_hash_value {};
template <> struct basic_numbers<unsigned long> :
ccstd::hash_detail::enable_hash_value {};
#if !defined(BOOST_NO_INTRINSIC_WCHAR_T)
template <> struct basic_numbers<wchar_t> :
ccstd::hash_detail::enable_hash_value {};
#endif
#if !defined(BOOST_NO_CXX11_CHAR16_T)
template <> struct basic_numbers<char16_t> :
ccstd::hash_detail::enable_hash_value {};
#endif
#if !defined(BOOST_NO_CXX11_CHAR32_T)
template <> struct basic_numbers<char32_t> :
ccstd::hash_detail::enable_hash_value {};
#endif
// long_numbers is defined like this to allow for separate
// specialization for long_long and int128_type, in case
// they conflict.
template <typename T> struct long_numbers2 {};
template <typename T> struct ulong_numbers2 {};
template <typename T> struct long_numbers : long_numbers2<T> {};
template <typename T> struct ulong_numbers : ulong_numbers2<T> {};
#if !defined(BOOST_NO_LONG_LONG)
template <> struct long_numbers<boost::long_long_type> :
ccstd::hash_detail::enable_hash_value {};
template <> struct ulong_numbers<boost::ulong_long_type> :
ccstd::hash_detail::enable_hash_value {};
#endif
#if defined(BOOST_HAS_INT128)
template <> struct long_numbers2<boost::int128_type> :
ccstd::hash_detail::enable_hash_value {};
template <> struct ulong_numbers2<boost::uint128_type> :
ccstd::hash_detail::enable_hash_value {};
#endif
template <> struct float_numbers<float> :
ccstd::hash_detail::enable_hash_value {};
template <> struct float_numbers<double> :
ccstd::hash_detail::enable_hash_value {};
template <> struct float_numbers<long double> :
ccstd::hash_detail::enable_hash_value {};
}
template <typename T>
typename ccstd::hash_detail::basic_numbers<T>::type hash_value(T);
template <typename T>
typename ccstd::hash_detail::long_numbers<T>::type hash_value(T);
template <typename T>
typename ccstd::hash_detail::ulong_numbers<T>::type hash_value(T);
template <typename T>
typename boost::enable_if<boost::is_enum<T>, hash_t>::type
hash_value(T);
#if !BOOST_WORKAROUND(__DMC__, <= 0x848)
template <class T> hash_t hash_value(T* const&);
#else
template <class T> hash_t hash_value(T*);
#endif
#if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)
template< class T, unsigned N >
hash_t hash_value(const T (&x)[N]);
template< class T, unsigned N >
hash_t hash_value(T (&x)[N]);
#endif
template <class Ch, class A>
hash_t hash_value(
std::basic_string<Ch, std::CCSTD_HASH_CHAR_TRAITS<Ch>, A> const&);
#if CCSTD_HASH_HAS_STRING_VIEW
template <class Ch>
hash_t hash_value(
std::basic_string_view<Ch, std::CCSTD_HASH_CHAR_TRAITS<Ch> > const&);
#endif
template <typename T>
typename ccstd::hash_detail::float_numbers<T>::type hash_value(T);
#if CCSTD_HASH_HAS_OPTIONAL
template <typename T>
hash_t hash_value(std::optional<T> const&);
#endif
#if CCSTD_HASH_HAS_VARIANT
hash_t hash_value(std::monostate);
template <typename... Types>
hash_t hash_value(std::variant<Types...> const&);
#endif
#if !defined(BOOST_NO_CXX11_HDR_TYPEINDEX)
hash_t hash_value(std::type_index);
#endif
#if !defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR)
hash_t hash_value(std::error_code const&);
hash_t hash_value(std::error_condition const&);
#endif
// Implementation
namespace hash_detail
{
template <class T>
inline hash_t hash_value_signed(T val)
{
const unsigned int size_t_bits = std::numeric_limits<hash_t>::digits;
// ceiling(std::numeric_limits<T>::digits / size_t_bits) - 1
const int length = (std::numeric_limits<T>::digits - 1)
/ static_cast<int>(size_t_bits);
hash_t seed = 0;
T positive = val < 0 ? -1 - val : val;
// Hopefully, this loop can be unrolled.
for(unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
{
seed ^= (hash_t) (positive >> i) + (seed<<6) + (seed>>2);
}
seed ^= (hash_t) val + (seed<<6) + (seed>>2);
return seed;
}
template <class T>
inline hash_t hash_value_unsigned(T val)
{
const unsigned int size_t_bits = std::numeric_limits<hash_t>::digits;
// ceiling(std::numeric_limits<T>::digits / size_t_bits) - 1
const int length = (std::numeric_limits<T>::digits - 1)
/ static_cast<int>(size_t_bits);
hash_t seed = 0;
// Hopefully, this loop can be unrolled.
for(unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
{
seed ^= (hash_t) (val >> i) + (seed<<6) + (seed>>2);
}
seed ^= (hash_t) val + (seed<<6) + (seed>>2);
return seed;
}
template<hash_t Bits> struct hash_combine_impl
{
template <typename SizeT>
inline static SizeT fn(SizeT seed, SizeT value)
{
seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2);
return seed;
}
};
template<> struct hash_combine_impl<32>
{
inline static std::uint32_t fn(std::uint32_t h1, std::uint32_t k1)
{
const std::uint32_t c1 = 0xcc9e2d51;
const std::uint32_t c2 = 0x1b873593;
k1 *= c1;
k1 = CCSTD_FUNCTIONAL_HASH_ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = CCSTD_FUNCTIONAL_HASH_ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
return h1;
}
};
template<> struct hash_combine_impl<64>
{
inline static std::uint64_t fn(std::uint64_t h, std::uint64_t k)
{
const std::uint64_t m = (std::uint64_t(0xc6a4a793) << 32) + 0x5bd1e995;
const int r = 47;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
// Completely arbitrary number, to prevent 0's
// from hashing to 0.
h += 0xe6546b64;
return h;
}
};
}
template <typename T>
typename ccstd::hash_detail::basic_numbers<T>::type hash_value(T v)
{
return static_cast<hash_t>(v);
}
template <typename T>
typename ccstd::hash_detail::long_numbers<T>::type hash_value(T v)
{
return hash_detail::hash_value_signed(v);
}
template <typename T>
typename ccstd::hash_detail::ulong_numbers<T>::type hash_value(T v)
{
return hash_detail::hash_value_unsigned(v);
}
template <typename T>
typename boost::enable_if<boost::is_enum<T>, hash_t>::type
hash_value(T v)
{
return static_cast<hash_t>(v);
}
// Implementation by Alberto Barbati and Dave Harris.
#if !BOOST_WORKAROUND(__DMC__, <= 0x848)
template <class T> hash_t hash_value(T* const& v)
#else
template <class T> hash_t hash_value(T* v)
#endif
{
#if defined(__VMS) && __INITIAL_POINTER_SIZE == 64
// for some reason ptrdiff_t on OpenVMS compiler with
// 64 bit is not 64 bit !!!
hash_t x = static_cast<hash_t>(
reinterpret_cast<long long int>(v));
#else
hash_t x = static_cast<hash_t>(
reinterpret_cast<std::ptrdiff_t>(v));
#endif
return x + (x >> 3);
}
#if defined(BOOST_MSVC)
#pragma warning(push)
#if BOOST_MSVC <= 1400
#pragma warning(disable:4267) // 'argument' : conversion from 'size_t' to
// 'unsigned int', possible loss of data
// A misguided attempt to detect 64-bit
// incompatability.
#endif
#endif
template <class T>
inline void hash_combine(hash_t& seed, T const& v)
{
ccstd::hash<T> hasher;
seed = ccstd::hash_detail::hash_combine_impl<sizeof(hash_t) * CHAR_BIT>::fn(seed, hasher(v));
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
template <class It>
inline hash_t hash_range(It first, It last)
{
hash_t seed = 0;
for(; first != last; ++first)
{
hash_combine<typename std::iterator_traits<It>::value_type>(seed, *first);
}
return seed;
}
template <class It>
inline void hash_range(hash_t& seed, It first, It last)
{
for(; first != last; ++first)
{
hash_combine<typename std::iterator_traits<It>::value_type>(seed, *first);
}
}
#if BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x551))
template <class T>
inline hash_t hash_range(T* first, T* last)
{
hash_t seed = 0;
for(; first != last; ++first)
{
ccstd::hash<T> hasher;
seed ^= hasher(*first) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
return seed;
}
template <class T>
inline void hash_range(hash_t& seed, T* first, T* last)
{
for(; first != last; ++first)
{
ccstd::hash<T> hasher;
seed ^= hasher(*first) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
}
#endif
#if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)
template< class T, unsigned N >
inline hash_t hash_value(const T (&x)[N])
{
return hash_range(x, x + N);
}
template< class T, unsigned N >
inline hash_t hash_value(T (&x)[N])
{
return hash_range(x, x + N);
}
#endif
template <class Ch, class A>
inline hash_t hash_value(
std::basic_string<Ch, std::CCSTD_HASH_CHAR_TRAITS<Ch>, A> const& v)
{
return hash_range(v.begin(), v.end());
}
#if CCSTD_HASH_HAS_STRING_VIEW
template <class Ch>
inline hash_t hash_value(
std::basic_string_view<Ch, std::CCSTD_HASH_CHAR_TRAITS<Ch> > const& v)
{
return hash_range(v.begin(), v.end());
}
#endif
template <typename T>
typename ccstd::hash_detail::float_numbers<T>::type hash_value(T v)
{
return ccstd::hash_detail::float_hash_value(v);
}
#if CCSTD_HASH_HAS_OPTIONAL
template <typename T>
inline hash_t hash_value(std::optional<T> const& v) {
if (!v) {
// Arbitray value for empty optional.
return 0x12345678;
} else {
ccstd::hash<T> hf;
return hf(*v);
}
}
#endif
#if CCSTD_HASH_HAS_VARIANT
inline hash_t hash_value(std::monostate) {
return 0x87654321;
}
template <typename... Types>
inline hash_t hash_value(std::variant<Types...> const& v) {
hash_t seed = 0;
hash_combine(seed, v.index());
std::visit([&seed](auto&& x) { hash_combine(seed, x); }, v);
return seed;
}
#endif
#if !defined(BOOST_NO_CXX11_HDR_TYPEINDEX)
inline hash_t hash_value(std::type_index v)
{
size_t hash = v.hash_code();
#ifndef INTPTR_MAX
#error "no INTPTR_MAX"
#endif
#ifndef INT64_MAX
#error "no INT64_MAX"
#endif
#if INTPTR_MAX == INT64_MAX
hash = (hash >> 32) ^ (hash & 0xFFFFFFFF);
#endif
return static_cast<hash_t>(hash);
}
#endif
#if !defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR)
inline hash_t hash_value(std::error_code const& v) {
hash_t seed = 0;
hash_combine(seed, v.value());
hash_combine(seed, &v.category());
return seed;
}
inline hash_t hash_value(std::error_condition const& v) {
hash_t seed = 0;
hash_combine(seed, v.value());
hash_combine(seed, &v.category());
return seed;
}
#endif
//
// ccstd::hash
//
// Define the specializations required by the standard. The general purpose
// ccstd::hash is defined later in extensions.hpp if
// CCSTD_HASH_NO_EXTENSIONS is not defined.
// CCSTD_HASH_SPECIALIZE - define a specialization for a type which is
// passed by copy.
//
// CCSTD_HASH_SPECIALIZE_REF - define a specialization for a type which is
// passed by const reference.
//
// These are undefined later.
#define CCSTD_HASH_SPECIALIZE(type) \
template <> struct hash<type> \
: public ccstd::hash_detail::hash_base<type> \
{ \
hash_t operator()(type v) const \
{ \
return ccstd::hash_value(v); \
} \
};
#define CCSTD_HASH_SPECIALIZE_REF(type) \
template <> struct hash<type> \
: public ccstd::hash_detail::hash_base<type> \
{ \
hash_t operator()(type const& v) const \
{ \
return ccstd::hash_value(v); \
} \
};
#define CCSTD_HASH_SPECIALIZE_TEMPLATE_REF(type) \
struct hash<type> \
: public ccstd::hash_detail::hash_base<type> \
{ \
hash_t operator()(type const& v) const \
{ \
return ccstd::hash_value(v); \
} \
};
CCSTD_HASH_SPECIALIZE(bool)
CCSTD_HASH_SPECIALIZE(char)
CCSTD_HASH_SPECIALIZE(signed char)
CCSTD_HASH_SPECIALIZE(unsigned char)
#if !defined(BOOST_NO_INTRINSIC_WCHAR_T)
CCSTD_HASH_SPECIALIZE(wchar_t)
#endif
#if !defined(BOOST_NO_CXX11_CHAR16_T)
CCSTD_HASH_SPECIALIZE(char16_t)
#endif
#if !defined(BOOST_NO_CXX11_CHAR32_T)
CCSTD_HASH_SPECIALIZE(char32_t)
#endif
CCSTD_HASH_SPECIALIZE(short)
CCSTD_HASH_SPECIALIZE(unsigned short)
CCSTD_HASH_SPECIALIZE(int)
CCSTD_HASH_SPECIALIZE(unsigned int)
CCSTD_HASH_SPECIALIZE(long)
CCSTD_HASH_SPECIALIZE(unsigned long)
CCSTD_HASH_SPECIALIZE(float)
CCSTD_HASH_SPECIALIZE(double)
CCSTD_HASH_SPECIALIZE(long double)
CCSTD_HASH_SPECIALIZE_REF(std::string)
#if !defined(BOOST_NO_STD_WSTRING) && !defined(BOOST_NO_INTRINSIC_WCHAR_T)
CCSTD_HASH_SPECIALIZE_REF(std::wstring)
#endif
#if !defined(BOOST_NO_CXX11_CHAR16_T)
CCSTD_HASH_SPECIALIZE_REF(std::basic_string<char16_t>)
#endif
#if !defined(BOOST_NO_CXX11_CHAR32_T)
CCSTD_HASH_SPECIALIZE_REF(std::basic_string<char32_t>)
#endif
#if CCSTD_HASH_HAS_STRING_VIEW
CCSTD_HASH_SPECIALIZE_REF(std::string_view)
# if !defined(BOOST_NO_STD_WSTRING) && !defined(BOOST_NO_INTRINSIC_WCHAR_T)
CCSTD_HASH_SPECIALIZE_REF(std::wstring_view)
# endif
# if !defined(BOOST_NO_CXX11_CHAR16_T)
CCSTD_HASH_SPECIALIZE_REF(std::basic_string_view<char16_t>)
# endif
# if !defined(BOOST_NO_CXX11_CHAR32_T)
CCSTD_HASH_SPECIALIZE_REF(std::basic_string_view<char32_t>)
# endif
#endif
#if !defined(BOOST_NO_LONG_LONG)
CCSTD_HASH_SPECIALIZE(boost::long_long_type)
CCSTD_HASH_SPECIALIZE(boost::ulong_long_type)
#endif
#if defined(BOOST_HAS_INT128)
CCSTD_HASH_SPECIALIZE(boost::int128_type)
CCSTD_HASH_SPECIALIZE(boost::uint128_type)
#endif
#if CCSTD_HASH_HAS_OPTIONAL
template <typename T>
CCSTD_HASH_SPECIALIZE_TEMPLATE_REF(std::optional<T>)
#endif
#if !defined(CCSTD_HASH_HAS_VARIANT)
template <typename... T>
CCSTD_HASH_SPECIALIZE_TEMPLATE_REF(std::variant<T...>)
CCSTD_HASH_SPECIALIZE(std::monostate)
#endif
#if !defined(BOOST_NO_CXX11_HDR_TYPEINDEX)
CCSTD_HASH_SPECIALIZE(std::type_index)
#endif
#undef CCSTD_HASH_SPECIALIZE
#undef CCSTD_HASH_SPECIALIZE_REF
#undef CCSTD_HASH_SPECIALIZE_TEMPLATE_REF
// Specializing ccstd::hash for pointers.
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
template <class T>
struct hash<T*>
: public ccstd::hash_detail::hash_base<T*>
{
hash_t operator()(T* v) const
{
#if !BOOST_WORKAROUND(__SUNPRO_CC, <= 0x590)
return ccstd::hash_value(v);
#else
hash_t x = static_cast<hash_t>(
reinterpret_cast<std::ptrdiff_t>(v));
return x + (x >> 3);
#endif
}
};
#else
// For compilers without partial specialization, we define a
// ccstd::hash for all remaining types. But hash_impl is only defined
// for pointers in 'extensions.hpp' - so when CCSTD_HASH_NO_EXTENSIONS
// is defined there will still be a compile error for types not supported
// in the standard.
namespace hash_detail
{
template <bool IsPointer>
struct hash_impl;
template <>
struct hash_impl<true>
{
template <class T>
struct inner
: public ccstd::hash_detail::hash_base<T>
{
hash_t operator()(T val) const
{
#if !BOOST_WORKAROUND(__SUNPRO_CC, <= 590)
return ccstd::hash_value(val);
#else
hash_t x = static_cast<hash_t>(
reinterpret_cast<std::ptrdiff_t>(val));
return x + (x >> 3);
#endif
}
};
};
}
template <class T> struct hash
: public ccstd::hash_detail::hash_impl<ccstd::is_pointer<T>::value>
::BOOST_NESTED_TEMPLATE inner<T>
{
};
#endif
}
#undef CCSTD_HASH_CHAR_TRAITS
#undef CCSTD_FUNCTIONAL_HASH_ROTL32
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
#endif // CCSTD_FUNCTIONAL_HASH_HASH_HPP
// Include this outside of the include guards in case the file is included
// twice - once with CCSTD_HASH_NO_EXTENSIONS defined, and then with it
// undefined.
#if !defined(CCSTD_HASH_NO_EXTENSIONS) \
&& !defined(CCSTD_FUNCTIONAL_HASH_EXTENSIONS_HPP)
#include "base/std/hash/extensions.hpp"
#endif

View File

@@ -0,0 +1,38 @@
// Copyright 2005-2009 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Based on Peter Dimov's proposal
// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
// issue 6.18.
#if !defined(CCSTD_FUNCTIONAL_HASH_FWD_HPP)
#define CCSTD_FUNCTIONAL_HASH_FWD_HPP
#include <boost/config/workaround.hpp>
#include <cstddef>
#include <cstdint>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
namespace ccstd
{
using hash_t = std::uint32_t;
template <class T> struct hash;
template <class T> void hash_combine(hash_t& seed, T const& v);
template <class It> hash_t hash_range(It, It);
template <class It> void hash_range(hash_t&, It, It);
#if BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x551))
template <class T> inline hash_t hash_range(T*, T*);
template <class T> inline void hash_range(hash_t&, T*, T*);
#endif
}
#endif

52
cocos/base/std/optional.h Normal file
View File

@@ -0,0 +1,52 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#ifdef USE_CXX_17
#include <optional>
namespace ccstd {
using std::nullopt;
using std::nullopt_t;
using std::optional;
}; // namespace ccstd
#else
#include "base/std/container/string.h"
#include "boost/none.hpp"
#include "boost/optional.hpp"
namespace ccstd {
using boost::optional;
using nullopt_t = boost::none_t;
const nullopt_t nullopt((boost::none_t::init_tag())); // NOLINT // use std style
}; // namespace ccstd
#endif

66
cocos/base/std/variant.h Normal file
View File

@@ -0,0 +1,66 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#ifdef USE_CXX_17
#include <variant>
namespace ccstd {
using std::get;
using std::get_if;
using std::holds_alternative;
using std::in_place_index;
using std::monostate;
using std::variant;
using std::variant_alternative;
using std::variant_alternative_t;
using std::variant_size;
using std::variant_size_v;
using std::visit;
}; // namespace ccstd
#else
#include "boost/variant2/variant.hpp"
namespace ccstd {
using boost::variant2::get;
using boost::variant2::get_if;
using boost::variant2::holds_alternative;
using boost::variant2::in_place_index;
using boost::variant2::monostate;
using boost::variant2::variant;
using boost::variant2::variant_alternative;
using boost::variant2::variant_alternative_t;
using boost::variant2::variant_size;
using boost::variant2::variant_size_v;
using boost::variant2::visit;
}; // namespace ccstd
#endif

View File

@@ -0,0 +1,46 @@
/****************************************************************************
Copyright (c) 2020-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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 "AutoReleasePool.h"
#import <Foundation/NSAutoreleasePool.h>
namespace cc {
AutoReleasePool::AutoReleasePool() {
_nsReleasePool = [[NSAutoreleasePool alloc] init];
}
AutoReleasePool::~AutoReleasePool() {
drain();
}
void AutoReleasePool::drain() {
if (_nsReleasePool) {
[static_cast<NSAutoreleasePool *>(_nsReleasePool) drain];
_nsReleasePool = nullptr;
}
}
} // namespace cc

View File

@@ -0,0 +1,35 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "AutoReleasePool.h"
namespace cc {
AutoReleasePool::AutoReleasePool() = default;
AutoReleasePool::~AutoReleasePool() = default;
void AutoReleasePool::drain() {
}
} // namespace cc

View File

@@ -0,0 +1,44 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
namespace cc {
class AutoReleasePool final {
public:
AutoReleasePool();
AutoReleasePool(const AutoReleasePool &) = delete;
AutoReleasePool(AutoReleasePool &&) = delete;
~AutoReleasePool();
AutoReleasePool &operator=(const AutoReleasePool &) = delete;
AutoReleasePool &operator=(AutoReleasePool &&) = delete;
void drain();
private:
void *_nsReleasePool{nullptr};
};
} // namespace cc

View File

@@ -0,0 +1,44 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "ConditionVariable.h"
namespace cc {
void ConditionVariable::wait() noexcept {
std::unique_lock<std::mutex> lock(_mutex);
_condVar.wait(lock);
}
void ConditionVariable::signal() noexcept {
std::lock_guard<std::mutex> lock(_mutex);
_condVar.notify_one();
}
void ConditionVariable::signalAll() noexcept {
std::lock_guard<std::mutex> lock(_mutex);
_condVar.notify_all();
}
} // namespace cc

View File

@@ -0,0 +1,54 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
namespace cc {
class ConditionVariable final {
public:
void wait() noexcept;
template <typename Function, typename... Args>
void wait(Function &&func, Args &&...args) noexcept;
void signal() noexcept;
void signalAll() noexcept;
private:
std::mutex _mutex;
std::condition_variable _condVar;
};
// DO NOT MANIPULATE ANY SYCHRONIZATION PRIMITIVES INSIDE THE CALLBACK
template <typename Function, typename... Args>
void ConditionVariable::wait(Function &&func, Args &&...args) noexcept {
std::unique_lock<std::mutex> lock(_mutex);
_condVar.wait(lock, std::bind(std::forward<Function>(func), std::forward<Args>(args)...));
}
} // namespace cc

View File

@@ -0,0 +1,46 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "ConditionVariable.h"
#include "Semaphore.h"
namespace cc {
template <typename T>
class Event final {
public:
inline void wait() noexcept { _syncObject.wait(); }
inline void signal() noexcept { _syncObject.signal(); };
inline void signalAll() noexcept { _syncObject.signalAll(); }
private:
T _syncObject{};
};
using EventCV = Event<ConditionVariable>;
using EventSem = Event<Semaphore>;
} // namespace cc

View File

@@ -0,0 +1,321 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "MessageQueue.h"
#include "AutoReleasePool.h"
#include "base/Utils.h"
namespace cc {
namespace {
uint32_t constexpr MEMORY_CHUNK_POOL_CAPACITY = 64;
uint32_t constexpr SWITCH_CHUNK_MEMORY_REQUIREMENT = sizeof(MemoryChunkSwitchMessage) + utils::ALIGN_TO<sizeof(DummyMessage), 16>;
} // namespace
MessageQueue::MemoryAllocator &MessageQueue::MemoryAllocator::getInstance() noexcept {
static MessageQueue::MemoryAllocator instance;
return instance;
}
uint8_t *MessageQueue::MemoryAllocator::request() noexcept {
uint8_t *newChunk = nullptr;
if (_chunkPool.try_dequeue(newChunk)) {
_chunkCount.fetch_sub(1, std::memory_order_acq_rel);
} else {
newChunk = memoryAllocateForMultiThread<uint8_t>(MEMORY_CHUNK_SIZE);
}
return newChunk;
}
void MessageQueue::MemoryAllocator::recycle(uint8_t *const chunk, bool const freeByUser) noexcept {
if (freeByUser) {
_chunkFreeQueue.enqueue(chunk);
} else {
free(chunk);
}
}
void MessageQueue::MemoryAllocator::freeByUser(MessageQueue *const mainMessageQueue) noexcept {
auto *queue = &_chunkFreeQueue;
ENQUEUE_MESSAGE_1(
mainMessageQueue, FreeChunksInFreeQueue,
queue, queue,
{
uint8_t *chunk = nullptr;
while (queue->try_dequeue(chunk)) {
MessageQueue::MemoryAllocator::getInstance().free(chunk);
}
});
mainMessageQueue->kick();
}
MessageQueue::MemoryAllocator::~MemoryAllocator() noexcept {
destroy();
}
void MessageQueue::MemoryAllocator::destroy() noexcept {
uint8_t *chunk = nullptr;
if (_chunkPool.try_dequeue(chunk)) {
::free(chunk);
_chunkCount.fetch_sub(1, std::memory_order_acq_rel);
}
}
void MessageQueue::MemoryAllocator::free(uint8_t *const chunk) noexcept {
if (_chunkCount.load(std::memory_order_acquire) >= MEMORY_CHUNK_POOL_CAPACITY) {
memoryFreeForMultiThread(chunk);
} else {
_chunkPool.enqueue(chunk);
_chunkCount.fetch_add(1, std::memory_order_acq_rel);
}
}
MessageQueue::MessageQueue() {
uint8_t *const chunk = MemoryAllocator::getInstance().request();
_writer.currentMemoryChunk = chunk;
_reader.currentMemoryChunk = chunk;
// sentinel node will not be executed
Message *const msg = allocate<DummyMessage>(1);
pushMessages();
pullMessages();
_reader.lastMessage = msg;
--_reader.newMessageCount;
}
void MessageQueue::kick() noexcept {
pushMessages();
std::lock_guard<std::mutex> lock(_mutex);
_condVar.notify_all();
}
void MessageQueue::kickAndWait() noexcept {
EventSem event;
EventSem *const pEvent = &event;
ENQUEUE_MESSAGE_1(this, WaitUntilFinish,
pEvent, pEvent,
{
pEvent->signal();
});
kick();
event.wait();
}
void MessageQueue::runConsumerThread() noexcept {
if (_immediateMode || _workerAttached) return;
_reader.terminateConsumerThread = false;
_reader.flushingFinished = false;
_consumerThread = ccnew std::thread(&MessageQueue::consumerThreadLoop, this);
_workerAttached = true;
}
void MessageQueue::terminateConsumerThread() noexcept {
if (_immediateMode || !_workerAttached) return;
EventSem event;
EventSem *const pEvent = &event;
ReaderContext *const pR = &_reader;
ccnew_placement(allocate<TerminateConsumerThreadMessage>(1)) TerminateConsumerThreadMessage(pEvent, pR);
kick();
event.wait();
if (_consumerThread != nullptr) {
if (_consumerThread->joinable()) {
_consumerThread->join();
}
}
CC_SAFE_DELETE(_consumerThread);
}
void MessageQueue::finishWriting() noexcept {
if (!_immediateMode) {
bool *const flushingFinished = &_reader.flushingFinished;
ENQUEUE_MESSAGE_1(this, finishWriting,
flushingFinished, flushingFinished,
{
*flushingFinished = true;
});
kick();
}
}
void MessageQueue::recycleMemoryChunk(uint8_t *const chunk) const noexcept {
MessageQueue::MemoryAllocator::getInstance().recycle(chunk, _freeChunksByUser);
}
void MessageQueue::freeChunksInFreeQueue(MessageQueue *const mainMessageQueue) noexcept {
MessageQueue::MemoryAllocator::getInstance().freeByUser(mainMessageQueue);
}
// NOLINTNEXTLINE(misc-no-recursion)
uint8_t *MessageQueue::allocateImpl(uint32_t allocatedSize, uint32_t const requestSize) noexcept {
uint32_t const alignedSize = align(requestSize, 16);
CC_ASSERT(alignedSize + SWITCH_CHUNK_MEMORY_REQUIREMENT <= MEMORY_CHUNK_SIZE);
uint32_t const newOffset = _writer.offset + alignedSize;
// newOffset contains the DummyMessage
if (newOffset + sizeof(MemoryChunkSwitchMessage) <= MEMORY_CHUNK_SIZE) {
uint8_t *const allocatedMemory = _writer.currentMemoryChunk + _writer.offset;
_writer.offset = newOffset;
return allocatedMemory;
}
uint8_t *const newChunk = MessageQueue::MemoryAllocator::getInstance().request();
auto *const switchMessage = reinterpret_cast<MemoryChunkSwitchMessage *>(_writer.currentMemoryChunk + _writer.offset);
ccnew_placement(switchMessage) MemoryChunkSwitchMessage(this, newChunk, _writer.currentMemoryChunk);
switchMessage->_next = reinterpret_cast<Message *>(newChunk); // point to start position
_writer.lastMessage = switchMessage;
++_writer.pendingMessageCount;
_writer.currentMemoryChunk = newChunk;
_writer.offset = 0;
DummyMessage *const head = allocate<DummyMessage>(1);
ccnew_placement(head) DummyMessage;
if (_immediateMode) {
pushMessages();
pullMessages();
CC_ASSERT_EQ(_reader.newMessageCount, 2);
executeMessages();
executeMessages();
}
return allocateImpl(allocatedSize, requestSize);
}
void MessageQueue::pushMessages() noexcept {
_writer.writtenMessageCount.fetch_add(_writer.pendingMessageCount, std::memory_order_acq_rel);
_writer.pendingMessageCount = 0;
}
void MessageQueue::pullMessages() noexcept {
uint32_t const writtenMessageCountNew = _writer.writtenMessageCount.load(std::memory_order_acquire);
_reader.newMessageCount += writtenMessageCountNew - _reader.writtenMessageCountSnap;
_reader.writtenMessageCountSnap = writtenMessageCountNew;
}
void MessageQueue::flushMessages() noexcept {
while (!_reader.flushingFinished) {
executeMessages();
}
_reader.flushingFinished = false;
}
void MessageQueue::executeMessages() noexcept {
Message *const msg = readMessage();
if (!msg) {
return;
}
msg->execute();
msg->~Message();
}
Message *MessageQueue::readMessage() noexcept {
while (!hasNewMessage()) { // if empty
std::unique_lock<std::mutex> lock(_mutex);
pullMessages(); // try pulling data from consumer
if (!hasNewMessage()) { // still empty
_condVar.wait(lock); // wait for the producer to wake me up
pullMessages(); // pulling again
}
}
Message *const msg = _reader.lastMessage->getNext();
_reader.lastMessage = msg;
--_reader.newMessageCount;
CC_ASSERT(msg);
return msg;
}
MessageQueue::~MessageQueue() {
recycleMemoryChunk(_writer.currentMemoryChunk);
}
void MessageQueue::consumerThreadLoop() noexcept {
while (!_reader.terminateConsumerThread) {
AutoReleasePool autoReleasePool;
flushMessages();
}
_workerAttached = false;
}
char const *DummyMessage::getName() const noexcept {
return "Dummy";
}
MemoryChunkSwitchMessage::MemoryChunkSwitchMessage(MessageQueue *const queue, uint8_t *const newChunk, uint8_t *const oldChunk) noexcept
: _messageQueue(queue),
_newChunk(newChunk),
_oldChunk(oldChunk) {
}
MemoryChunkSwitchMessage::~MemoryChunkSwitchMessage() {
_messageQueue->recycleMemoryChunk(_oldChunk);
}
void MemoryChunkSwitchMessage::execute() noexcept {
_messageQueue->_reader.currentMemoryChunk = _newChunk;
_messageQueue->pullMessages();
}
char const *MemoryChunkSwitchMessage::getName() const noexcept {
return "MemoryChunkSwitch";
}
TerminateConsumerThreadMessage::TerminateConsumerThreadMessage(EventSem *const pEvent, ReaderContext *const pR) noexcept
: _event(pEvent),
_reader(pR) {
}
void TerminateConsumerThreadMessage::execute() noexcept {
_reader->terminateConsumerThread = true;
_reader->flushingFinished = true;
_event->signal();
}
char const *TerminateConsumerThreadMessage::getName() const noexcept {
return "TerminateConsumerThread";
}
} // namespace cc

Some files were not shown because too many files have changed in this diff Show More