瀏覽代碼

Merge pull request #100386 from hpvb/core-ubsan

Core: Fix UBSAN reports
Rémi Verschelde 8 月之前
父節點
當前提交
851d8e49e8

+ 4 - 4
core/config/project_settings.cpp

@@ -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());
 		}
 	}

+ 2 - 4
core/config/project_settings.h

@@ -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();

+ 1 - 1
core/crypto/aes_context.h

@@ -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,

+ 1 - 1
core/crypto/hashing_context.h

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

+ 4 - 0
core/input/input_enums.h

@@ -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));
 }
 

+ 1 - 1
core/io/compression.h

@@ -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,

+ 1 - 1
core/io/dir_access.h

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

+ 5 - 5
core/io/file_access.cpp

@@ -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) {

+ 4 - 4
core/io/file_access.h

@@ -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,

+ 5 - 3
core/io/file_access_compressed.cpp

@@ -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;

+ 14 - 2
core/io/file_access_encrypted.cpp

@@ -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;
 }
 

+ 1 - 1
core/io/file_access_encrypted.h

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

+ 10 - 2
core/io/file_access_memory.cpp

@@ -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);

+ 1 - 1
core/io/image.h

@@ -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,

+ 11 - 7
core/io/json.cpp

@@ -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:

+ 4 - 4
core/io/marshalls.cpp

@@ -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;

+ 2 - 2
core/io/net_socket.h

@@ -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,

+ 3 - 3
core/io/pck_packer.cpp

@@ -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);

+ 1 - 1
core/io/remote_filesystem_client.cpp

@@ -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");
 

+ 45 - 45
core/io/resource_format_binary.cpp

@@ -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));

+ 1 - 1
core/io/resource_importer.h

@@ -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;

+ 2 - 2
core/io/resource_uid.cpp

@@ -200,7 +200,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);
@@ -263,7 +263,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);

+ 1 - 3
core/io/resource_uid.h

@@ -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();
 

+ 27 - 8
core/io/stream_peer.cpp

@@ -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) {

+ 1 - 1
core/math/bvh_pair.inc

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

+ 1 - 3
core/math/bvh_tree.h

@@ -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;

+ 2 - 2
core/math/color.h

@@ -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 {

+ 1 - 1
core/math/random_pcg.cpp

@@ -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);
 }

+ 1 - 1
core/object/class_db.cpp

@@ -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);
 			}
 		}
 

+ 1 - 1
core/object/object_id.h

@@ -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; }

+ 3 - 1
core/os/memory.cpp

@@ -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;
 }

+ 1 - 1
core/string/string_buffer.h

@@ -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;
 	}
 

+ 57 - 72
core/string/ustring.cpp

@@ -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,30 +2485,50 @@ int64_t String::bin_to_int() const {
 	return binary * sign;
 }
 
-int64_t String::to_int() const {
-	if (length() == 0) {
-		return 0;
-	}
-
-	int to = (find_char('.') >= 0) ? find_char('.') : length();
-
-	int64_t integer = 0;
-	int64_t sign = 1;
+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++) {
-		char32_t c = operator[](i);
+		C c = p_in[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."));
+			// 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 == '-') {
-			sign = -sign;
+			positive = !positive;
 		}
 	}
 
-	return integer * sign;
+	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;
+	}
+
+	int to = (find_char('.') >= 0) ? find_char('.') : length();
+
+	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 {

+ 3 - 1
core/templates/command_queue_mt.h

@@ -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;

+ 22 - 22
core/templates/hashfuncs.h

@@ -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) {

+ 6 - 2
core/templates/local_vector.h

@@ -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;
 	}
 

+ 5 - 2
core/templates/vector.h

@@ -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;
 	}
 

+ 29 - 29
core/variant/variant.cpp

@@ -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) :

+ 1 - 0
core/variant/variant_parser.cpp

@@ -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) {

+ 85 - 0
tests/core/io/test_json.h

@@ -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