1
0
Fork 0

Core ubsan fixes

This fixes UBSAN errors reported by running our testsuite, importing the
TPS demo, and running the TPS demo. I have tried, wherever possible, to
fix issues related to reported issues but not directly reported by UBSAN
because thse code paths just happened to not have been exercised in
these cases.

These fixes apply only to errors reported, and caused by, core/

The following things have been changed:

* Make sure there are no implicit sign changing casts in core.
* Explicitly type enums that are part of a public API such that users of
  the API cannot pass in wrongly-sized values leading to potential stack
  corruption.
* Ensure that memcpy is never called with invalid or null pointers as
  this is undefined behavior, and when the engine is built with
  optimizations turned on leads to memory corruption and hard to debug
  crashes.
* Replace enum values only used as static values with constexpr static
  const values instead. This has no runtime overhead. This makes it so
  that the size of the enums is explicit.
* Make sure that nan and inf is handled consistently in String.
* Implement a _to_int template to ensure that all of the paths use the
  same algorhithm, and correct the negative integer case.
* Changed the way the json serializer precision work, and added tests to
  verify the new behavior. The behavior doesn't quite match master in
  particulary for negative doubles as the original code tried to cast -inf
  to an int. This then led to negative doubles losing all but one of
  their decimal points when serializing. Behavior in GDScript remains
  unchanged.
This commit is contained in:
HP van Braam 2024-12-14 02:17:09 +01:00
parent b9437c3938
commit 240f510fa7
40 changed files with 373 additions and 248 deletions

View File

@ -898,7 +898,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
if (!p_custom_features.is_empty()) {
// Store how many properties are saved, add one for custom features, which must always go first.
file->store_32(count + 1);
file->store_32(uint32_t(count + 1));
String key = CoreStringName(_custom_features);
file->store_pascal_string(key);
@ -911,12 +911,12 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
err = encode_variant(p_custom_features, buff.ptrw(), len, false);
ERR_FAIL_COND_V(err != OK, err);
file->store_32(len);
file->store_32(uint32_t(len));
file->store_buffer(buff.ptr(), buff.size());
} else {
// Store how many properties are saved.
file->store_32(count);
file->store_32(uint32_t(count));
}
for (const KeyValue<String, List<String>> &E : p_props) {
@ -943,7 +943,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
err = encode_variant(value, buff.ptrw(), len, true);
ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant.");
file->store_32(len);
file->store_32(uint32_t(len));
file->store_buffer(buff.ptr(), buff.size());
}
}

View File

@ -47,10 +47,8 @@ public:
typedef HashMap<String, Variant> CustomMap;
static const String PROJECT_DATA_DIR_NAME_SUFFIX;
enum {
// Properties that are not for built in values begin from this value, so builtin ones are displayed first.
NO_BUILTIN_ORDER_BASE = 1 << 16
};
// Properties that are not for built in values begin from this value, so builtin ones are displayed first.
constexpr static const int32_t NO_BUILTIN_ORDER_BASE = 1 << 16;
#ifdef TOOLS_ENABLED
const static PackedStringArray get_required_features();

View File

@ -38,7 +38,7 @@ class AESContext : public RefCounted {
GDCLASS(AESContext, RefCounted);
public:
enum Mode {
enum Mode : int32_t {
MODE_ECB_ENCRYPT,
MODE_ECB_DECRYPT,
MODE_CBC_ENCRYPT,

View File

@ -37,7 +37,7 @@ class HashingContext : public RefCounted {
GDCLASS(HashingContext, RefCounted);
public:
enum HashType {
enum HashType : int32_t {
HASH_MD5,
HASH_SHA1,
HASH_SHA256

View File

@ -31,6 +31,8 @@
#ifndef INPUT_ENUMS_H
#define INPUT_ENUMS_H
#include "core/error/error_macros.h"
enum class HatDir {
UP = 0,
RIGHT = 1,
@ -131,6 +133,8 @@ enum class MouseButtonMask {
};
inline MouseButtonMask mouse_button_to_mask(MouseButton button) {
ERR_FAIL_COND_V(button == MouseButton::NONE, MouseButtonMask::NONE);
return MouseButtonMask(1 << ((int)button - 1));
}

View File

@ -43,7 +43,7 @@ public:
static int zstd_window_log_size;
static int gzip_chunk;
enum Mode {
enum Mode : int32_t {
MODE_FASTLZ,
MODE_DEFLATE,
MODE_ZSTD,

View File

@ -40,7 +40,7 @@ class DirAccess : public RefCounted {
GDCLASS(DirAccess, RefCounted);
public:
enum AccessType {
enum AccessType : int32_t {
ACCESS_RESOURCES,
ACCESS_USERDATA,
ACCESS_FILESYSTEM,

View File

@ -383,7 +383,7 @@ double FileAccess::get_double() const {
String FileAccess::get_token() const {
CharString token;
char32_t c = get_8();
uint8_t c = get_8();
while (!eof_reached()) {
if (c <= ' ') {
@ -391,7 +391,7 @@ String FileAccess::get_token() const {
break;
}
} else {
token += c;
token += char(c);
}
c = get_8();
}
@ -448,14 +448,14 @@ public:
String FileAccess::get_line() const {
CharBuffer line;
char32_t c = get_8();
uint8_t c = get_8();
while (!eof_reached()) {
if (c == '\n' || c == '\0') {
line.push_back(0);
return String::utf8(line.get_data());
} else if (c != '\r') {
line.push_back(c);
line.push_back(char(c));
}
c = get_8();
@ -786,7 +786,7 @@ bool FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
err = encode_variant(p_var, &w[0], len, p_full_objects);
ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
return store_32(len) && store_buffer(buff);
return store_32(uint32_t(len)) && store_buffer(buff);
}
Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_error) {

View File

@ -46,7 +46,7 @@ class FileAccess : public RefCounted {
GDCLASS(FileAccess, RefCounted);
public:
enum AccessType {
enum AccessType : int32_t {
ACCESS_RESOURCES,
ACCESS_USERDATA,
ACCESS_FILESYSTEM,
@ -54,14 +54,14 @@ public:
ACCESS_MAX
};
enum ModeFlags {
enum ModeFlags : int32_t {
READ = 1,
WRITE = 2,
READ_WRITE = 3,
WRITE_READ = 7,
};
enum UnixPermissionFlags {
enum UnixPermissionFlags : int32_t {
UNIX_EXECUTE_OTHER = 0x001,
UNIX_WRITE_OTHER = 0x002,
UNIX_READ_OTHER = 0x004,
@ -76,7 +76,7 @@ public:
UNIX_SET_USER_ID = 0x800,
};
enum CompressionMode {
enum CompressionMode : int32_t {
COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
COMPRESSION_ZSTD = Compression::MODE_ZSTD,

View File

@ -125,7 +125,7 @@ void FileAccessCompressed::_close() {
f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //write header 4
f->store_32(cmode); //write compression mode 4
f->store_32(block_size); //write block size 4
f->store_32(write_max); //max amount of data written 4
f->store_32(uint32_t(write_max)); //max amount of data written 4
uint32_t bc = (write_max / block_size) + 1;
for (uint32_t i = 0; i < bc; i++) {
@ -147,7 +147,7 @@ void FileAccessCompressed::_close() {
f->seek(16); //ok write block sizes
for (uint32_t i = 0; i < bc; i++) {
f->store_32(block_sizes[i]);
f->store_32(uint32_t(block_sizes[i]));
}
f->seek_end();
f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //magic at the end too
@ -310,7 +310,9 @@ bool FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length)
write_ptr = buffer.ptrw();
}
memcpy(write_ptr + write_pos, p_src, p_length);
if (p_length) {
memcpy(write_ptr + write_pos, p_src, p_length);
}
write_pos += p_length;
return true;

View File

@ -210,10 +210,16 @@ bool FileAccessEncrypted::eof_reached() const {
}
uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode.");
if (!p_length) {
return 0;
}
ERR_FAIL_NULL_V(p_dst, -1);
uint64_t to_copy = MIN(p_length, get_length() - pos);
memcpy(p_dst, data.ptr() + pos, to_copy);
pos += to_copy;
@ -230,7 +236,12 @@ Error FileAccessEncrypted::get_error() const {
bool FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_V_MSG(!writing, false, "File has not been opened in write mode.");
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
if (!p_length) {
return true;
}
ERR_FAIL_NULL_V(p_src, false);
if (pos + p_length >= get_length()) {
ERR_FAIL_COND_V(data.resize(pos + p_length) != OK, false);
@ -238,6 +249,7 @@ bool FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length)
memcpy(data.ptrw() + pos, p_src, p_length);
pos += p_length;
return true;
}

View File

@ -37,7 +37,7 @@
class FileAccessEncrypted : public FileAccess {
public:
enum Mode {
enum Mode : int32_t {
MODE_READ,
MODE_WRITE_AES256,
MODE_MAX

View File

@ -123,7 +123,11 @@ bool FileAccessMemory::eof_reached() const {
}
uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
if (!p_length) {
return 0;
}
ERR_FAIL_NULL_V(p_dst, -1);
ERR_FAIL_NULL_V(data, -1);
uint64_t left = length - pos;
@ -148,7 +152,11 @@ void FileAccessMemory::flush() {
}
bool FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
if (!p_length) {
return true;
}
ERR_FAIL_NULL_V(p_src, false);
uint64_t left = length - pos;
uint64_t write = MIN(p_length, left);

View File

@ -70,7 +70,7 @@ public:
MAX_PIXELS = 268435456 // 16384 ^ 2
};
enum Format {
enum Format : int32_t {
FORMAT_L8, // Luminance
FORMAT_LA8, // Luminance-Alpha
FORMAT_R8,

View File

@ -71,14 +71,18 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
return itos(p_var);
case Variant::FLOAT: {
double num = p_var;
if (p_full_precision) {
// Store unreliable digits (17) instead of just reliable
// digits (14) so that the value can be decoded exactly.
return String::num(num, 17 - (int)floor(log10(num)));
} else {
// Store only reliable digits (14) by default.
return String::num(num, 14 - (int)floor(log10(num)));
// Only for exactly 0. If we have approximately 0 let the user decide how much
// precision they want.
if (num == double(0)) {
return String("0.0");
}
double magnitude = log10(Math::abs(num));
int total_digits = p_full_precision ? 17 : 14;
int precision = MAX(1, total_digits - (int)Math::floor(magnitude));
return String::num(num, precision);
}
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:

View File

@ -217,7 +217,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
case Variant::INT: {
if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
int64_t val = decode_uint64(buf);
int64_t val = int64_t(decode_uint64(buf));
r_variant = val;
if (r_len) {
(*r_len) += 8;
@ -225,7 +225,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} else {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t val = decode_uint32(buf);
int32_t val = int32_t(decode_uint32(buf));
r_variant = val;
if (r_len) {
(*r_len) += 4;
@ -1450,13 +1450,13 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
if (header & HEADER_DATA_FLAG_64) {
// 64 bits.
if (buf) {
encode_uint64(p_variant.operator int64_t(), buf);
encode_uint64(p_variant.operator uint64_t(), buf);
}
r_len += 8;
} else {
if (buf) {
encode_uint32(p_variant.operator int32_t(), buf);
encode_uint32(p_variant.operator uint32_t(), buf);
}
r_len += 4;

View File

@ -41,13 +41,13 @@ protected:
public:
static NetSocket *create();
enum PollType {
enum PollType : int32_t {
POLL_TYPE_IN,
POLL_TYPE_OUT,
POLL_TYPE_IN_OUT
};
enum Type {
enum Type : int32_t {
TYPE_NONE,
TYPE_TCP,
TYPE_UDP,

View File

@ -182,7 +182,7 @@ Error PCKPacker::flush(bool p_verbose) {
}
// write the index
file->store_32(files.size());
file->store_32(uint32_t(files.size()));
Ref<FileAccessEncrypted> fae;
Ref<FileAccess> fhead = file;
@ -201,7 +201,7 @@ Error PCKPacker::flush(bool p_verbose) {
int string_len = files[i].path.utf8().length();
int pad = _get_pad(4, string_len);
fhead->store_32(string_len + pad);
fhead->store_32(uint32_t(string_len + pad));
fhead->store_buffer((const uint8_t *)files[i].path.utf8().get_data(), string_len);
for (int j = 0; j < pad; j++) {
fhead->store_8(0);
@ -231,7 +231,7 @@ Error PCKPacker::flush(bool p_verbose) {
file->store_8(0);
}
int64_t file_base = file->get_position();
uint64_t file_base = file->get_position();
file->seek(file_base_ofs);
file->store_64(file_base); // update files base
file->seek(file_base);

View File

@ -237,7 +237,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int
tcp_client->poll();
ERR_FAIL_COND_V_MSG(tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED, ERR_CONNECTION_ERROR, "Remote filesystem server disconnected after sending header.");
uint32_t file_count = tcp_client->get_32();
uint32_t file_count = tcp_client->get_u32();
ERR_FAIL_COND_V_MSG(tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED, ERR_CONNECTION_ERROR, "Remote filesystem server disconnected while waiting for file list");

View File

@ -911,7 +911,7 @@ void ResourceLoaderBinary::set_translation_remapped(bool p_remapped) {
static void save_ustring(Ref<FileAccess> f, const String &p_string) {
CharString utf8 = p_string.utf8();
f->store_32(utf8.length() + 1);
f->store_32(uint32_t(utf8.length() + 1));
f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
}
@ -1051,7 +1051,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
f->real_is_double = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_REAL_T_IS_DOUBLE) != 0;
if (using_uids) {
uid = f->get_64();
uid = ResourceUID::ID(f->get_64());
} else {
f->get_64(); // skip over uid field
uid = ResourceUID::INVALID_ID;
@ -1084,7 +1084,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
er.type = get_unicode_string();
er.path = get_unicode_string();
if (using_uids) {
er.uid = f->get_64();
er.uid = ResourceUID::ID(f->get_64());
if (!p_keep_uuid_paths && er.uid != ResourceUID::INVALID_ID) {
if (ResourceUID::get_singleton()->has_id(er.uid)) {
// If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
@ -1477,7 +1477,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
if (using_uids) {
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path);
fw->store_64(uid);
fw->store_64(uint64_t(uid));
}
}
@ -1609,11 +1609,11 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
int64_t val = p_property;
if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) {
f->store_32(VARIANT_INT64);
f->store_64(val);
f->store_64(uint64_t(val));
} else {
f->store_32(VARIANT_INT);
f->store_32(int32_t(p_property));
f->store_32(uint32_t(p_property));
}
} break;
@ -1645,8 +1645,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
case Variant::VECTOR2I: {
f->store_32(VARIANT_VECTOR2I);
Vector2i val = p_property;
f->store_32(val.x);
f->store_32(val.y);
f->store_32(uint32_t(val.x));
f->store_32(uint32_t(val.y));
} break;
case Variant::RECT2: {
@ -1661,10 +1661,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
case Variant::RECT2I: {
f->store_32(VARIANT_RECT2I);
Rect2i val = p_property;
f->store_32(val.position.x);
f->store_32(val.position.y);
f->store_32(val.size.x);
f->store_32(val.size.y);
f->store_32(uint32_t(val.position.x));
f->store_32(uint32_t(val.position.y));
f->store_32(uint32_t(val.size.x));
f->store_32(uint32_t(val.size.y));
} break;
case Variant::VECTOR3: {
@ -1678,9 +1678,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
case Variant::VECTOR3I: {
f->store_32(VARIANT_VECTOR3I);
Vector3i val = p_property;
f->store_32(val.x);
f->store_32(val.y);
f->store_32(val.z);
f->store_32(uint32_t(val.x));
f->store_32(uint32_t(val.y));
f->store_32(uint32_t(val.z));
} break;
case Variant::VECTOR4: {
@ -1695,10 +1695,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
case Variant::VECTOR4I: {
f->store_32(VARIANT_VECTOR4I);
Vector4i val = p_property;
f->store_32(val.x);
f->store_32(val.y);
f->store_32(val.z);
f->store_32(val.w);
f->store_32(uint32_t(val.x));
f->store_32(uint32_t(val.y));
f->store_32(uint32_t(val.z));
f->store_32(uint32_t(val.w));
} break;
case Variant::PLANE: {
@ -1821,14 +1821,14 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_16(snc);
for (int i = 0; i < np.get_name_count(); i++) {
if (string_map.has(np.get_name(i))) {
f->store_32(string_map[np.get_name(i)]);
f->store_32(uint32_t(string_map[np.get_name(i)]));
} else {
save_unicode_string(f, np.get_name(i), true);
}
}
for (int i = 0; i < np.get_subname_count(); i++) {
if (string_map.has(np.get_subname(i))) {
f->store_32(string_map[np.get_subname(i)]);
f->store_32(uint32_t(string_map[np.get_subname(i)]));
} else {
save_unicode_string(f, np.get_subname(i), true);
}
@ -1839,7 +1839,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_RID);
WARN_PRINT("Can't save RIDs.");
RID val = p_property;
f->store_32(val.get_id());
f->store_32(uint32_t(val.get_id()));
} break;
case Variant::OBJECT: {
f->store_32(VARIANT_OBJECT);
@ -1851,7 +1851,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
if (!res->is_built_in()) {
f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX);
f->store_32(external_resources[res]);
f->store_32(uint32_t(external_resources[res]));
} else {
if (!resource_map.has(res)) {
f->store_32(OBJECT_EMPTY);
@ -1859,7 +1859,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
}
f->store_32(OBJECT_INTERNAL_RESOURCE);
f->store_32(resource_map[res]);
f->store_32(uint32_t(resource_map[res]));
//internal resource
}
@ -1900,7 +1900,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_BYTE_ARRAY);
Vector<uint8_t> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const uint8_t *r = arr.ptr();
f->store_buffer(r, len);
_pad_buffer(f, len);
@ -1910,10 +1910,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_INT32_ARRAY);
Vector<int32_t> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const int32_t *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_32(r[i]);
f->store_32(uint32_t(r[i]));
}
} break;
@ -1921,10 +1921,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_INT64_ARRAY);
Vector<int64_t> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const int64_t *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_64(r[i]);
f->store_64(uint64_t(r[i]));
}
} break;
@ -1932,7 +1932,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_FLOAT32_ARRAY);
Vector<float> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const float *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_float(r[i]);
@ -1943,7 +1943,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_FLOAT64_ARRAY);
Vector<double> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const double *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_double(r[i]);
@ -1954,7 +1954,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_STRING_ARRAY);
Vector<String> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const String *r = arr.ptr();
for (int i = 0; i < len; i++) {
save_unicode_string(f, r[i]);
@ -1965,7 +1965,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_VECTOR2_ARRAY);
Vector<Vector2> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const Vector2 *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_real(r[i].x);
@ -1977,7 +1977,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_VECTOR3_ARRAY);
Vector<Vector3> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const Vector3 *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_real(r[i].x);
@ -1990,7 +1990,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_COLOR_ARRAY);
Vector<Color> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const Color *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_float(r[i].r);
@ -2004,7 +2004,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_PACKED_VECTOR4_ARRAY);
Vector<Vector4> arr = p_property;
int len = arr.size();
f->store_32(len);
f->store_32(uint32_t(len));
const Vector4 *r = arr.ptr();
for (int i = 0; i < len; i++) {
f->store_real(r[i].x);
@ -2115,9 +2115,9 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
void ResourceFormatSaverBinaryInstance::save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len) {
CharString utf8 = p_string.utf8();
if (p_bit_on_len) {
p_f->store_32((utf8.length() + 1) | 0x80000000);
p_f->store_32(uint32_t((utf8.length() + 1) | 0x80000000));
} else {
p_f->store_32(utf8.length() + 1);
p_f->store_32(uint32_t(utf8.length() + 1));
}
p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
}
@ -2218,7 +2218,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
f->store_32(format_flags);
}
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true);
f->store_64(uid);
f->store_64(uint64_t(uid));
if (!script_class.is_empty()) {
save_unicode_string(f, script_class);
}
@ -2284,7 +2284,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
}
}
f->store_32(strings.size()); //string table size
f->store_32(uint32_t(strings.size())); //string table size
for (int i = 0; i < strings.size(); i++) {
save_unicode_string(f, strings[i]);
}
@ -2304,10 +2304,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
res_path = relative_paths ? local_path.path_to_file(res_path) : res_path;
save_unicode_string(f, res_path);
ResourceUID::ID ruid = ResourceSaver::get_resource_id_for_path(save_order[i]->get_path(), false);
f->store_64(ruid);
f->store_64(uint64_t(ruid));
}
// save internal resource table
f->store_32(saved_resources.size()); //amount of internal resources
f->store_32(uint32_t(saved_resources.size())); //amount of internal resources
Vector<uint64_t> ofs_pos;
HashSet<String> used_unique_ids;
@ -2362,10 +2362,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
for (const ResourceData &rd : resources) {
ofs_table.push_back(f->get_position());
save_unicode_string(f, rd.type);
f->store_32(rd.properties.size());
f->store_32(uint32_t(rd.properties.size()));
for (const Property &p : rd.properties) {
f->store_32(p.name_idx);
f->store_32(uint32_t(p.name_idx));
write_variant(f, p.value, resource_map, external_resources, string_map, p.pi);
}
}
@ -2473,7 +2473,7 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU
f->get_64(); // Skip previous UID
fw->store_32(flags);
fw->store_64(p_uid);
fw->store_64(uint64_t(p_uid));
if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
save_ustring(fw, get_ustring(f));

View File

@ -46,7 +46,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
String importer;
String group_file;
Variant metadata;
uint64_t uid = ResourceUID::INVALID_ID;
ResourceUID::ID uid = ResourceUID::INVALID_ID;
};
Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = nullptr) const;

View File

@ -178,7 +178,7 @@ Error ResourceUID::save_to_cache() {
cache_entries = 0;
for (KeyValue<ID, Cache> &E : unique_ids) {
f->store_64(E.key);
f->store_64(uint64_t(E.key));
uint32_t s = E.value.cs.length();
f->store_32(s);
f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);
@ -241,7 +241,7 @@ Error ResourceUID::update_cache() {
}
f->seek_end();
}
f->store_64(E.key);
f->store_64(uint64_t(E.key));
uint32_t s = E.value.cs.length();
f->store_32(s);
f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);

View File

@ -39,9 +39,7 @@ class ResourceUID : public Object {
GDCLASS(ResourceUID, Object)
public:
typedef int64_t ID;
enum {
INVALID_ID = -1
};
constexpr const static ID INVALID_ID = -1;
static String get_cache_file();

View File

@ -204,11 +204,13 @@ void StreamPeer::put_float(float p_val) {
void StreamPeer::put_double(double p_val) {
uint8_t buf[8];
encode_double(p_val, buf);
if (big_endian) {
uint64_t *p64 = (uint64_t *)buf;
*p64 = BSWAP64(*p64);
}
put_data(buf, 8);
}
@ -243,75 +245,87 @@ uint8_t StreamPeer::get_u8() {
int8_t StreamPeer::get_8() {
uint8_t buf[1] = {};
get_data(buf, 1);
return buf[0];
return int8_t(buf[0]);
}
uint16_t StreamPeer::get_u16() {
uint8_t buf[2];
get_data(buf, 2);
uint16_t r = decode_uint16(buf);
if (big_endian) {
r = BSWAP16(r);
}
return r;
}
int16_t StreamPeer::get_16() {
uint8_t buf[2];
get_data(buf, 2);
uint16_t r = decode_uint16(buf);
if (big_endian) {
r = BSWAP16(r);
}
return r;
return int16_t(r);
}
uint32_t StreamPeer::get_u32() {
uint8_t buf[4];
get_data(buf, 4);
uint32_t r = decode_uint32(buf);
if (big_endian) {
r = BSWAP32(r);
}
return r;
}
int32_t StreamPeer::get_32() {
uint8_t buf[4];
get_data(buf, 4);
uint32_t r = decode_uint32(buf);
if (big_endian) {
r = BSWAP32(r);
}
return r;
return int32_t(r);
}
uint64_t StreamPeer::get_u64() {
uint8_t buf[8];
get_data(buf, 8);
uint64_t r = decode_uint64(buf);
if (big_endian) {
r = BSWAP64(r);
}
return r;
}
int64_t StreamPeer::get_64() {
uint8_t buf[8];
get_data(buf, 8);
uint64_t r = decode_uint64(buf);
if (big_endian) {
r = BSWAP64(r);
}
return r;
return int64_t(r);
}
float StreamPeer::get_half() {
uint8_t buf[2];
get_data(buf, 2);
uint16_t *p16 = (uint16_t *)buf;
if (big_endian) {
uint16_t *p16 = (uint16_t *)buf;
*p16 = BSWAP16(*p16);
}
@ -344,7 +358,7 @@ double StreamPeer::get_double() {
String StreamPeer::get_string(int p_bytes) {
if (p_bytes < 0) {
p_bytes = get_u32();
p_bytes = get_32();
}
ERR_FAIL_COND_V(p_bytes < 0, String());
@ -359,7 +373,7 @@ String StreamPeer::get_string(int p_bytes) {
String StreamPeer::get_utf8_string(int p_bytes) {
if (p_bytes < 0) {
p_bytes = get_u32();
p_bytes = get_32();
}
ERR_FAIL_COND_V(p_bytes < 0, String());
@ -498,7 +512,7 @@ void StreamPeerBuffer::_bind_methods() {
}
Error StreamPeerBuffer::put_data(const uint8_t *p_data, int p_bytes) {
if (p_bytes <= 0) {
if (p_bytes <= 0 || !p_data) {
return OK;
}
@ -529,6 +543,11 @@ Error StreamPeerBuffer::get_data(uint8_t *p_buffer, int p_bytes) {
}
Error StreamPeerBuffer::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
if (!p_bytes) {
r_received = 0;
return OK;
}
if (pointer + p_bytes > data.size()) {
r_received = data.size() - pointer;
if (r_received <= 0) {

View File

@ -37,7 +37,7 @@ struct ItemPairs {
return n;
}
}
return -1;
return uint32_t(-1);
}
bool contains_pair_to(BVHHandle h) const {

View File

@ -109,9 +109,7 @@ struct BVHHandle {
template <typename T>
class BVH_IterativeInfo {
public:
enum {
ALLOCA_STACK_SIZE = 128
};
constexpr static const size_t ALLOCA_STACK_SIZE = 128;
int32_t depth = 1;
int32_t threshold = ALLOCA_STACK_SIZE - 2;

View File

@ -147,7 +147,7 @@ struct [[nodiscard]] Color {
// of the mantissa, rounding the truncated bits.
union {
float f;
int32_t i;
uint32_t i;
} R, G, B, E;
E.f = MaxChannel;
@ -168,7 +168,7 @@ struct [[nodiscard]] Color {
// Combine the fields. RGB floats have unwanted data in the upper 9
// bits. Only red needs to mask them off because green and blue shift
// it out to the left.
return E.i | (B.i << 18) | (G.i << 9) | (R.i & 511);
return E.i | (B.i << 18U) | (G.i << 9U) | (R.i & 511U);
}
_FORCE_INLINE_ Color blend(const Color &p_over) const {

View File

@ -80,5 +80,5 @@ int RandomPCG::random(int p_from, int p_to) {
if (p_from == p_to) {
return p_from;
}
return rand(abs(p_from - p_to) + 1) + MIN(p_from, p_to);
return int(rand(uint32_t(Math::abs(p_from - p_to)) + 1U)) + MIN(p_from, p_to);
}

View File

@ -454,7 +454,7 @@ uint32_t ClassDB::get_api_hash(APIType p_api) {
for (const StringName &F : snames) {
hash = hash_murmur3_one_64(F.hash(), hash);
hash = hash_murmur3_one_64(t->constant_map[F], hash);
hash = hash_murmur3_one_64(uint64_t(t->constant_map[F]), hash);
}
}

View File

@ -46,7 +46,7 @@ public:
_ALWAYS_INLINE_ bool is_valid() const { return id != 0; }
_ALWAYS_INLINE_ bool is_null() const { return id == 0; }
_ALWAYS_INLINE_ operator uint64_t() const { return id; }
_ALWAYS_INLINE_ operator int64_t() const { return id; }
_ALWAYS_INLINE_ operator int64_t() const { return (int64_t)id; }
_ALWAYS_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; }
_ALWAYS_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; }

View File

@ -87,7 +87,9 @@ void *Memory::realloc_aligned_static(void *p_memory, size_t p_bytes, size_t p_pr
}
void *ret = alloc_aligned_static(p_bytes, p_alignment);
memcpy(ret, p_memory, p_prev_bytes);
if (ret) {
memcpy(ret, p_memory, p_prev_bytes);
}
free_aligned_static(p_memory);
return ret;
}

View File

@ -118,7 +118,7 @@ StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const c
template <int SHORT_BUFFER_SIZE>
StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::reserve(int p_size) {
if (p_size < SHORT_BUFFER_SIZE || p_size < buffer.size()) {
if (p_size < SHORT_BUFFER_SIZE || p_size < buffer.size() || !p_size) {
return *this;
}

View File

@ -42,10 +42,6 @@
#include "core/variant/variant.h"
#include "core/version_generated.gen.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstdint>
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
#endif
@ -1804,6 +1800,10 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
}
String String::num_real(double p_num, bool p_trailing) {
if (Math::is_nan(p_num) || Math::is_inf(p_num)) {
return num(p_num, 0);
}
if (p_num == (double)(int64_t)p_num) {
if (p_trailing) {
return num_int64((int64_t)p_num) + ".0";
@ -1811,6 +1811,7 @@ String String::num_real(double p_num, bool p_trailing) {
return num_int64((int64_t)p_num);
}
}
int decimals = 14;
// We want to align the digits to the above sane default, so we only need
// to subtract log10 for numbers with a positive power of ten magnitude.
@ -1818,10 +1819,15 @@ String String::num_real(double p_num, bool p_trailing) {
if (abs_num > 10) {
decimals -= (int)floor(log10(abs_num));
}
return num(p_num, decimals);
}
String String::num_real(float p_num, bool p_trailing) {
if (Math::is_nan(p_num) || Math::is_inf(p_num)) {
return num(p_num, 0);
}
if (p_num == (float)(int64_t)p_num) {
if (p_trailing) {
return num_int64((int64_t)p_num) + ".0";
@ -1840,16 +1846,8 @@ String String::num_real(float p_num, bool p_trailing) {
}
String String::num_scientific(double p_num) {
if (Math::is_nan(p_num)) {
return "nan";
}
if (Math::is_inf(p_num)) {
if (signbit(p_num)) {
return "-inf";
} else {
return "inf";
}
if (Math::is_nan(p_num) || Math::is_inf(p_num)) {
return num(p_num, 0);
}
char buf[256];
@ -1947,7 +1945,7 @@ CharString String::ascii(bool p_allow_extended) const {
for (int i = 0; i < size(); i++) {
char32_t c = this_ptr[i];
if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) {
cs_ptrw[i] = c;
cs_ptrw[i] = char(c);
} else {
print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as ASCII/Latin-1", (uint32_t)c));
cs_ptrw[i] = 0x20; // ASCII doesn't have a replacement character like unicode, 0x1a is sometimes used but is kinda arcane.
@ -2487,6 +2485,42 @@ int64_t String::bin_to_int() const {
return binary * sign;
}
template <typename C, typename T>
_ALWAYS_INLINE_ int64_t _to_int(const T &p_in, int to) {
// Accumulate the total number in an unsigned integer as the range is:
// +9223372036854775807 to -9223372036854775808 and the smallest negative
// number does not fit inside an int64_t. So we accumulate the positive
// number in an unsigned, and then at the very end convert to its signed
// form.
uint64_t integer = 0;
uint8_t digits = 0;
bool positive = true;
for (int i = 0; i < to; i++) {
C c = p_in[i];
if (is_digit(c)) {
// No need to do expensive checks unless we're approaching INT64_MAX / INT64_MIN.
if (unlikely(digits > 18)) {
bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((positive && c > '7') || (!positive && c > '8')));
ERR_FAIL_COND_V_MSG(overflow, positive ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_in) + " as a 64-bit signed integer, since the value is " + (positive ? "too large." : "too small."));
}
integer *= 10;
integer += c - '0';
++digits;
} else if (integer == 0 && c == '-') {
positive = !positive;
}
}
if (positive) {
return int64_t(integer);
} else {
return int64_t(integer * uint64_t(-1));
}
}
int64_t String::to_int() const {
if (length() == 0) {
return 0;
@ -2494,23 +2528,7 @@ int64_t String::to_int() const {
int to = (find_char('.') >= 0) ? find_char('.') : length();
int64_t integer = 0;
int64_t sign = 1;
for (int i = 0; i < to; i++) {
char32_t c = operator[](i);
if (is_digit(c)) {
bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small."));
integer *= 10;
integer += c - '0';
} else if (integer == 0 && c == '-') {
sign = -sign;
}
}
return integer * sign;
return _to_int<char32_t>(*this, to);
}
int64_t String::to_int(const char *p_str, int p_len) {
@ -2523,25 +2541,7 @@ int64_t String::to_int(const char *p_str, int p_len) {
}
}
int64_t integer = 0;
int64_t sign = 1;
for (int i = 0; i < to; i++) {
char c = p_str[i];
if (is_digit(c)) {
bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small."));
integer *= 10;
integer += c - '0';
} else if (c == '-' && integer == 0) {
sign = -sign;
} else if (c != ' ') {
break;
}
}
return integer * sign;
return _to_int<char>(p_str, to);
}
int64_t String::to_int(const wchar_t *p_str, int p_len) {
@ -2554,25 +2554,7 @@ int64_t String::to_int(const wchar_t *p_str, int p_len) {
}
}
int64_t integer = 0;
int64_t sign = 1;
for (int i = 0; i < to; i++) {
wchar_t c = p_str[i];
if (is_digit(c)) {
bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small."));
integer *= 10;
integer += c - '0';
} else if (c == '-' && integer == 0) {
sign = -sign;
} else if (c != ' ') {
break;
}
}
return integer * sign;
return _to_int<wchar_t>(p_str, to);
}
bool String::is_numeric() const {
@ -3969,7 +3951,7 @@ static String _replace_common(const String &p_this, const String &p_key, const S
return p_this;
}
const int key_length = p_key.length();
const size_t key_length = p_key.length();
int search_from = 0;
int result = 0;
@ -3978,6 +3960,7 @@ static String _replace_common(const String &p_this, const String &p_key, const S
while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
found.push_back(result);
ERR_FAIL_COND_V_MSG((result + key_length) > INT32_MAX, p_this, "Key length too long");
search_from = result + key_length;
}
@ -3990,7 +3973,7 @@ static String _replace_common(const String &p_this, const String &p_key, const S
const int with_length = p_with.length();
const int old_length = p_this.length();
new_string.resize(old_length + found.size() * (with_length - key_length) + 1);
new_string.resize(old_length + int(found.size()) * (with_length - key_length) + 1);
char32_t *new_ptrw = new_string.ptrw();
const char32_t *old_ptr = p_this.ptr();
@ -4021,7 +4004,7 @@ static String _replace_common(const String &p_this, const String &p_key, const S
}
static String _replace_common(const String &p_this, char const *p_key, char const *p_with, bool p_case_insensitive) {
int key_length = strlen(p_key);
size_t key_length = strlen(p_key);
if (key_length == 0 || p_this.is_empty()) {
return p_this;
@ -4034,6 +4017,7 @@ static String _replace_common(const String &p_this, char const *p_key, char cons
while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
found.push_back(result);
ERR_FAIL_COND_V_MSG((result + key_length) > INT32_MAX, p_this, "Key length too long");
search_from = result + key_length;
}
@ -4048,7 +4032,7 @@ static String _replace_common(const String &p_this, char const *p_key, char cons
const int with_length = with_string.length();
const int old_length = p_this.length();
new_string.resize(old_length + found.size() * (with_length - key_length) + 1);
new_string.resize(old_length + int(found.size()) * (with_length - key_length) + 1);
char32_t *new_ptrw = new_string.ptrw();
const char32_t *old_ptr = p_this.ptr();
@ -4639,8 +4623,9 @@ bool String::is_valid_string() const {
String String::uri_encode() const {
const CharString temp = utf8();
String res;
for (int i = 0; i < temp.length(); ++i) {
uint8_t ord = temp[i];
uint8_t ord = uint8_t(temp[i]);
if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) {
res += ord;
} else {

View File

@ -339,7 +339,9 @@ class CommandQueueMT {
template <typename T>
T *allocate() {
// alloc size is size+T+safeguard
uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1));
static_assert(sizeof(T) < UINT32_MAX, "Type too large to fit in the command queue.");
uint32_t alloc_size = ((sizeof(T) + 8U - 1U) & ~(8U - 1U));
uint64_t size = command_mem.size();
command_mem.resize(size + alloc_size + 8);
*(uint64_t *)&command_mem[size] = alloc_size;

View File

@ -323,9 +323,9 @@ struct HashMapHasherDefault {
static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }
static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(p_wchar); }
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); }
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); }
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(uint32_t(p_wchar)); }
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.get_data()); }
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
@ -333,31 +333,31 @@ struct HashMapHasherDefault {
static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(p_int); }
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(uint64_t(p_int)); }
static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_murmur3_one_float(p_float); }
static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_murmur3_one_double(p_double); }
static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return hash_fmix32(p_int); }
static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(p_int); }
static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(p_int); }
static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(p_int); }
static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(p_int); }
static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(p_int); }
static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(uint32_t(p_int)); }
static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) {
uint32_t h = hash_murmur3_one_32(p_vec.x);
h = hash_murmur3_one_32(p_vec.y, h);
uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
return hash_fmix32(h);
}
static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) {
uint32_t h = hash_murmur3_one_32(p_vec.x);
h = hash_murmur3_one_32(p_vec.y, h);
h = hash_murmur3_one_32(p_vec.z, h);
uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
h = hash_murmur3_one_32(uint32_t(p_vec.z), h);
return hash_fmix32(h);
}
static _FORCE_INLINE_ uint32_t hash(const Vector4i &p_vec) {
uint32_t h = hash_murmur3_one_32(p_vec.x);
h = hash_murmur3_one_32(p_vec.y, h);
h = hash_murmur3_one_32(p_vec.z, h);
h = hash_murmur3_one_32(p_vec.w, h);
uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
h = hash_murmur3_one_32(uint32_t(p_vec.z), h);
h = hash_murmur3_one_32(uint32_t(p_vec.w), h);
return hash_fmix32(h);
}
static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
@ -379,10 +379,10 @@ struct HashMapHasherDefault {
return hash_fmix32(h);
}
static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
uint32_t h = hash_murmur3_one_32(p_rect.position.x);
h = hash_murmur3_one_32(p_rect.position.y, h);
h = hash_murmur3_one_32(p_rect.size.x, h);
h = hash_murmur3_one_32(p_rect.size.y, h);
uint32_t h = hash_murmur3_one_32(uint32_t(p_rect.position.x));
h = hash_murmur3_one_32(uint32_t(p_rect.position.y), h);
h = hash_murmur3_one_32(uint32_t(p_rect.size.x), h);
h = hash_murmur3_one_32(uint32_t(p_rect.size.y), h);
return hash_fmix32(h);
}
static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) {

View File

@ -297,7 +297,9 @@ public:
Vector<T> ret;
ret.resize(size());
T *w = ret.ptrw();
memcpy(w, data, sizeof(T) * count);
if (w) {
memcpy(w, data, sizeof(T) * count);
}
return ret;
}
@ -305,7 +307,9 @@ public:
Vector<uint8_t> ret;
ret.resize(count * sizeof(T));
uint8_t *w = ret.ptrw();
memcpy(w, data, sizeof(T) * count);
if (w) {
memcpy(w, data, sizeof(T) * count);
}
return ret;
}

View File

@ -156,8 +156,11 @@ public:
if (is_empty()) {
return ret;
}
ret.resize(size() * sizeof(T));
memcpy(ret.ptrw(), ptr(), sizeof(T) * size());
size_t alloc_size = size() * sizeof(T);
ret.resize(alloc_size);
if (alloc_size) {
memcpy(ret.ptrw(), ptr(), alloc_size);
}
return ret;
}

View File

@ -1494,11 +1494,11 @@ Variant::operator int64_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return int64_t(_data._int);
case FLOAT:
return _data._float;
return int64_t(_data._float);
case STRING:
return operator String().to_int();
return int64_t(operator String().to_int());
default: {
return 0;
}
@ -1512,11 +1512,11 @@ Variant::operator int32_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return int32_t(_data._int);
case FLOAT:
return _data._float;
return int32_t(_data._float);
case STRING:
return operator String().to_int();
return int32_t(operator String().to_int());
default: {
return 0;
}
@ -1530,11 +1530,11 @@ Variant::operator int16_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return int16_t(_data._int);
case FLOAT:
return _data._float;
return int16_t(_data._float);
case STRING:
return operator String().to_int();
return int16_t(operator String().to_int());
default: {
return 0;
}
@ -1548,11 +1548,11 @@ Variant::operator int8_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return int8_t(_data._int);
case FLOAT:
return _data._float;
return int8_t(_data._float);
case STRING:
return operator String().to_int();
return int8_t(operator String().to_int());
default: {
return 0;
}
@ -1566,11 +1566,11 @@ Variant::operator uint64_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return uint64_t(_data._int);
case FLOAT:
return _data._float;
return uint64_t(_data._float);
case STRING:
return operator String().to_int();
return uint64_t(operator String().to_int());
default: {
return 0;
}
@ -1584,11 +1584,11 @@ Variant::operator uint32_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return uint32_t(_data._int);
case FLOAT:
return _data._float;
return uint32_t(_data._float);
case STRING:
return operator String().to_int();
return uint32_t(operator String().to_int());
default: {
return 0;
}
@ -1602,11 +1602,11 @@ Variant::operator uint16_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return uint16_t(_data._int);
case FLOAT:
return _data._float;
return uint16_t(_data._float);
case STRING:
return operator String().to_int();
return uint16_t(operator String().to_int());
default: {
return 0;
}
@ -1620,11 +1620,11 @@ Variant::operator uint8_t() const {
case BOOL:
return _data._bool ? 1 : 0;
case INT:
return _data._int;
return uint8_t(_data._int);
case FLOAT:
return _data._float;
return uint8_t(_data._float);
case STRING:
return operator String().to_int();
return uint8_t(operator String().to_int());
default: {
return 0;
}
@ -2484,22 +2484,22 @@ Variant::Variant(int8_t p_int8) :
Variant::Variant(uint64_t p_uint64) :
type(INT) {
_data._int = p_uint64;
_data._int = int64_t(p_uint64);
}
Variant::Variant(uint32_t p_uint32) :
type(INT) {
_data._int = p_uint32;
_data._int = int64_t(p_uint32);
}
Variant::Variant(uint16_t p_uint16) :
type(INT) {
_data._int = p_uint16;
_data._int = int64_t(p_uint16);
}
Variant::Variant(uint8_t p_uint8) :
type(INT) {
_data._int = p_uint8;
_data._int = int64_t(p_uint8);
}
Variant::Variant(float p_float) :
@ -2514,7 +2514,7 @@ Variant::Variant(double p_double) :
Variant::Variant(const ObjectID &p_id) :
type(INT) {
_data._int = p_id;
_data._int = int64_t(p_id);
}
Variant::Variant(const StringName &p_string) :

View File

@ -99,6 +99,7 @@ bool VariantParser::StreamString::_is_eof() const {
uint32_t VariantParser::StreamString::_read_buffer(char32_t *p_buffer, uint32_t p_num_chars) {
// The buffer is assumed to include at least one character (for null terminator)
ERR_FAIL_COND_V(!p_num_chars, 0);
ERR_FAIL_NULL_V(p_buffer, 0);
int available = MAX(s.length() - pos, 0);
if (available >= (int)p_num_chars) {

View File

@ -232,6 +232,91 @@ TEST_CASE("[JSON] Parsing escape sequences") {
ERR_PRINT_ON
}
}
TEST_CASE("[JSON] Serialization") {
JSON json;
struct FpTestCase {
double number;
String json;
};
struct IntTestCase {
int64_t number;
String json;
};
struct UIntTestCase {
uint64_t number;
String json;
};
static FpTestCase fp_tests_default_precision[] = {
{ 0.0, "0.0" },
{ 1000.1234567890123456789, "1000.12345678901" },
{ -1000.1234567890123456789, "-1000.12345678901" },
{ DBL_MAX, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
{ DBL_MAX - 1, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
{ pow(2, 53), "9007199254740992.0" },
{ -pow(2, 53), "-9007199254740992.0" },
{ 0.00000000000000011, "0.00000000000000011" },
{ -0.00000000000000011, "-0.00000000000000011" },
{ 1.0 / 3.0, "0.333333333333333" },
{ 0.9999999999999999, "1.0" },
{ 1.0000000000000001, "1.0" },
};
static FpTestCase fp_tests_full_precision[] = {
{ 0.0, "0.0" },
{ 1000.1234567890123456789, "1000.12345678901238" },
{ -1000.1234567890123456789, "-1000.12345678901238" },
{ DBL_MAX, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
{ DBL_MAX - 1, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
{ pow(2, 53), "9007199254740992.0" },
{ -pow(2, 53), "-9007199254740992.0" },
{ 0.00000000000000011, "0.00000000000000011" },
{ -0.00000000000000011, "-0.00000000000000011" },
{ 1.0 / 3.0, "0.333333333333333315" },
{ 0.9999999999999999, "0.999999999999999889" },
{ 1.0000000000000001, "1.0" },
};
static IntTestCase int_tests[] = {
{ 0, "0" },
{ INT64_MAX, "9223372036854775807" },
{ INT64_MIN, "-9223372036854775808" },
};
SUBCASE("Floating point default precision") {
for (FpTestCase &test : fp_tests_default_precision) {
String json_value = json.stringify(test.number, "", true, false);
CHECK_MESSAGE(
json_value == test.json,
vformat("Serializing `%.20d` to JSON should return the expected value.", test.number));
}
}
SUBCASE("Floating point full precision") {
for (FpTestCase &test : fp_tests_full_precision) {
String json_value = json.stringify(test.number, "", true, true);
CHECK_MESSAGE(
json_value == test.json,
vformat("Serializing `%20f` to JSON should return the expected value.", test.number));
}
}
SUBCASE("Signed integer") {
for (IntTestCase &test : int_tests) {
String json_value = json.stringify(test.number, "", true, true);
CHECK_MESSAGE(
json_value == test.json,
vformat("Serializing `%d` to JSON should return the expected value.", test.number));
}
}
}
} // namespace TestJSON
#endif // TEST_JSON_H