Browse Source

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.
HP van Braam 10 months ago
parent
commit
240f510fa7

+ 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()) {
 	if (!p_custom_features.is_empty()) {
 		// Store how many properties are saved, add one for custom features, which must always go first.
 		// 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);
 		String key = CoreStringName(_custom_features);
 		file->store_pascal_string(key);
 		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 = encode_variant(p_custom_features, buff.ptrw(), len, false);
 		ERR_FAIL_COND_V(err != OK, err);
 		ERR_FAIL_COND_V(err != OK, err);
-		file->store_32(len);
+		file->store_32(uint32_t(len));
 		file->store_buffer(buff.ptr(), buff.size());
 		file->store_buffer(buff.ptr(), buff.size());
 
 
 	} else {
 	} else {
 		// Store how many properties are saved.
 		// 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) {
 	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 = encode_variant(value, buff.ptrw(), len, true);
 			ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant.");
 			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());
 			file->store_buffer(buff.ptr(), buff.size());
 		}
 		}
 	}
 	}

+ 2 - 4
core/config/project_settings.h

@@ -47,10 +47,8 @@ public:
 	typedef HashMap<String, Variant> CustomMap;
 	typedef HashMap<String, Variant> CustomMap;
 	static const String PROJECT_DATA_DIR_NAME_SUFFIX;
 	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
 #ifdef TOOLS_ENABLED
 	const static PackedStringArray get_required_features();
 	const static PackedStringArray get_required_features();

+ 1 - 1
core/crypto/aes_context.h

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

+ 1 - 1
core/crypto/hashing_context.h

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

+ 4 - 0
core/input/input_enums.h

@@ -31,6 +31,8 @@
 #ifndef INPUT_ENUMS_H
 #ifndef INPUT_ENUMS_H
 #define INPUT_ENUMS_H
 #define INPUT_ENUMS_H
 
 
+#include "core/error/error_macros.h"
+
 enum class HatDir {
 enum class HatDir {
 	UP = 0,
 	UP = 0,
 	RIGHT = 1,
 	RIGHT = 1,
@@ -131,6 +133,8 @@ enum class MouseButtonMask {
 };
 };
 
 
 inline MouseButtonMask mouse_button_to_mask(MouseButton button) {
 inline MouseButtonMask mouse_button_to_mask(MouseButton button) {
+	ERR_FAIL_COND_V(button == MouseButton::NONE, MouseButtonMask::NONE);
+
 	return MouseButtonMask(1 << ((int)button - 1));
 	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 zstd_window_log_size;
 	static int gzip_chunk;
 	static int gzip_chunk;
 
 
-	enum Mode {
+	enum Mode : int32_t {
 		MODE_FASTLZ,
 		MODE_FASTLZ,
 		MODE_DEFLATE,
 		MODE_DEFLATE,
 		MODE_ZSTD,
 		MODE_ZSTD,

+ 1 - 1
core/io/dir_access.h

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

+ 5 - 5
core/io/file_access.cpp

@@ -383,7 +383,7 @@ double FileAccess::get_double() const {
 String FileAccess::get_token() const {
 String FileAccess::get_token() const {
 	CharString token;
 	CharString token;
 
 
-	char32_t c = get_8();
+	uint8_t c = get_8();
 
 
 	while (!eof_reached()) {
 	while (!eof_reached()) {
 		if (c <= ' ') {
 		if (c <= ' ') {
@@ -391,7 +391,7 @@ String FileAccess::get_token() const {
 				break;
 				break;
 			}
 			}
 		} else {
 		} else {
-			token += c;
+			token += char(c);
 		}
 		}
 		c = get_8();
 		c = get_8();
 	}
 	}
@@ -448,14 +448,14 @@ public:
 String FileAccess::get_line() const {
 String FileAccess::get_line() const {
 	CharBuffer line;
 	CharBuffer line;
 
 
-	char32_t c = get_8();
+	uint8_t c = get_8();
 
 
 	while (!eof_reached()) {
 	while (!eof_reached()) {
 		if (c == '\n' || c == '\0') {
 		if (c == '\n' || c == '\0') {
 			line.push_back(0);
 			line.push_back(0);
 			return String::utf8(line.get_data());
 			return String::utf8(line.get_data());
 		} else if (c != '\r') {
 		} else if (c != '\r') {
-			line.push_back(c);
+			line.push_back(char(c));
 		}
 		}
 
 
 		c = get_8();
 		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 = encode_variant(p_var, &w[0], len, p_full_objects);
 	ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
 	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) {
 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);
 	GDCLASS(FileAccess, RefCounted);
 
 
 public:
 public:
-	enum AccessType {
+	enum AccessType : int32_t {
 		ACCESS_RESOURCES,
 		ACCESS_RESOURCES,
 		ACCESS_USERDATA,
 		ACCESS_USERDATA,
 		ACCESS_FILESYSTEM,
 		ACCESS_FILESYSTEM,
@@ -54,14 +54,14 @@ public:
 		ACCESS_MAX
 		ACCESS_MAX
 	};
 	};
 
 
-	enum ModeFlags {
+	enum ModeFlags : int32_t {
 		READ = 1,
 		READ = 1,
 		WRITE = 2,
 		WRITE = 2,
 		READ_WRITE = 3,
 		READ_WRITE = 3,
 		WRITE_READ = 7,
 		WRITE_READ = 7,
 	};
 	};
 
 
-	enum UnixPermissionFlags {
+	enum UnixPermissionFlags : int32_t {
 		UNIX_EXECUTE_OTHER = 0x001,
 		UNIX_EXECUTE_OTHER = 0x001,
 		UNIX_WRITE_OTHER = 0x002,
 		UNIX_WRITE_OTHER = 0x002,
 		UNIX_READ_OTHER = 0x004,
 		UNIX_READ_OTHER = 0x004,
@@ -76,7 +76,7 @@ public:
 		UNIX_SET_USER_ID = 0x800,
 		UNIX_SET_USER_ID = 0x800,
 	};
 	};
 
 
-	enum CompressionMode {
+	enum CompressionMode : int32_t {
 		COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
 		COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
 		COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
 		COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
 		COMPRESSION_ZSTD = Compression::MODE_ZSTD,
 		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_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //write header 4
 		f->store_32(cmode); //write compression mode 4
 		f->store_32(cmode); //write compression mode 4
 		f->store_32(block_size); //write block size 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;
 		uint32_t bc = (write_max / block_size) + 1;
 
 
 		for (uint32_t i = 0; i < bc; i++) {
 		for (uint32_t i = 0; i < bc; i++) {
@@ -147,7 +147,7 @@ void FileAccessCompressed::_close() {
 
 
 		f->seek(16); //ok write block sizes
 		f->seek(16); //ok write block sizes
 		for (uint32_t i = 0; i < bc; i++) {
 		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->seek_end();
 		f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //magic at the end too
 		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();
 		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;
 	write_pos += p_length;
 	return true;
 	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 {
 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.");
 	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);
 	uint64_t to_copy = MIN(p_length, get_length() - pos);
+
 	memcpy(p_dst, data.ptr() + pos, to_copy);
 	memcpy(p_dst, data.ptr() + pos, to_copy);
 	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) {
 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_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()) {
 	if (pos + p_length >= get_length()) {
 		ERR_FAIL_COND_V(data.resize(pos + p_length) != OK, false);
 		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);
 	memcpy(data.ptrw() + pos, p_src, p_length);
 	pos += p_length;
 	pos += p_length;
+
 	return true;
 	return true;
 }
 }
 
 

+ 1 - 1
core/io/file_access_encrypted.h

@@ -37,7 +37,7 @@
 
 
 class FileAccessEncrypted : public FileAccess {
 class FileAccessEncrypted : public FileAccess {
 public:
 public:
-	enum Mode {
+	enum Mode : int32_t {
 		MODE_READ,
 		MODE_READ,
 		MODE_WRITE_AES256,
 		MODE_WRITE_AES256,
 		MODE_MAX
 		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 {
 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);
 	ERR_FAIL_NULL_V(data, -1);
 
 
 	uint64_t left = length - pos;
 	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) {
 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 left = length - pos;
 	uint64_t write = MIN(p_length, left);
 	uint64_t write = MIN(p_length, left);

+ 1 - 1
core/io/image.h

@@ -70,7 +70,7 @@ public:
 		MAX_PIXELS = 268435456 // 16384 ^ 2
 		MAX_PIXELS = 268435456 // 16384 ^ 2
 	};
 	};
 
 
-	enum Format {
+	enum Format : int32_t {
 		FORMAT_L8, // Luminance
 		FORMAT_L8, // Luminance
 		FORMAT_LA8, // Luminance-Alpha
 		FORMAT_LA8, // Luminance-Alpha
 		FORMAT_R8,
 		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);
 			return itos(p_var);
 		case Variant::FLOAT: {
 		case Variant::FLOAT: {
 			double num = p_var;
 			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_INT32_ARRAY:
 		case Variant::PACKED_INT64_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: {
 		case Variant::INT: {
 			if (header & HEADER_DATA_FLAG_64) {
 			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
 				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;
 				r_variant = val;
 				if (r_len) {
 				if (r_len) {
 					(*r_len) += 8;
 					(*r_len) += 8;
@@ -225,7 +225,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 			} else {
 			} else {
 				ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
 				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;
 				r_variant = val;
 				if (r_len) {
 				if (r_len) {
 					(*r_len) += 4;
 					(*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) {
 			if (header & HEADER_DATA_FLAG_64) {
 				// 64 bits.
 				// 64 bits.
 				if (buf) {
 				if (buf) {
-					encode_uint64(p_variant.operator int64_t(), buf);
+					encode_uint64(p_variant.operator uint64_t(), buf);
 				}
 				}
 
 
 				r_len += 8;
 				r_len += 8;
 			} else {
 			} else {
 				if (buf) {
 				if (buf) {
-					encode_uint32(p_variant.operator int32_t(), buf);
+					encode_uint32(p_variant.operator uint32_t(), buf);
 				}
 				}
 
 
 				r_len += 4;
 				r_len += 4;

+ 2 - 2
core/io/net_socket.h

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

+ 3 - 3
core/io/pck_packer.cpp

@@ -182,7 +182,7 @@ Error PCKPacker::flush(bool p_verbose) {
 	}
 	}
 
 
 	// write the index
 	// write the index
-	file->store_32(files.size());
+	file->store_32(uint32_t(files.size()));
 
 
 	Ref<FileAccessEncrypted> fae;
 	Ref<FileAccessEncrypted> fae;
 	Ref<FileAccess> fhead = file;
 	Ref<FileAccess> fhead = file;
@@ -201,7 +201,7 @@ Error PCKPacker::flush(bool p_verbose) {
 		int string_len = files[i].path.utf8().length();
 		int string_len = files[i].path.utf8().length();
 		int pad = _get_pad(4, string_len);
 		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);
 		fhead->store_buffer((const uint8_t *)files[i].path.utf8().get_data(), string_len);
 		for (int j = 0; j < pad; j++) {
 		for (int j = 0; j < pad; j++) {
 			fhead->store_8(0);
 			fhead->store_8(0);
@@ -231,7 +231,7 @@ Error PCKPacker::flush(bool p_verbose) {
 		file->store_8(0);
 		file->store_8(0);
 	}
 	}
 
 
-	int64_t file_base = file->get_position();
+	uint64_t file_base = file->get_position();
 	file->seek(file_base_ofs);
 	file->seek(file_base_ofs);
 	file->store_64(file_base); // update files base
 	file->store_64(file_base); // update files base
 	file->seek(file_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();
 	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.");
 	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");
 	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) {
 static void save_ustring(Ref<FileAccess> f, const String &p_string) {
 	CharString utf8 = p_string.utf8();
 	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);
 	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;
 	f->real_is_double = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_REAL_T_IS_DOUBLE) != 0;
 
 
 	if (using_uids) {
 	if (using_uids) {
-		uid = f->get_64();
+		uid = ResourceUID::ID(f->get_64());
 	} else {
 	} else {
 		f->get_64(); // skip over uid field
 		f->get_64(); // skip over uid field
 		uid = ResourceUID::INVALID_ID;
 		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.type = get_unicode_string();
 		er.path = get_unicode_string();
 		er.path = get_unicode_string();
 		if (using_uids) {
 		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 (!p_keep_uuid_paths && er.uid != ResourceUID::INVALID_ID) {
 				if (ResourceUID::get_singleton()->has_id(er.uid)) {
 				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.
 					// 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) {
 		if (using_uids) {
 			ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path);
 			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;
 			int64_t val = p_property;
 			if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) {
 			if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) {
 				f->store_32(VARIANT_INT64);
 				f->store_32(VARIANT_INT64);
-				f->store_64(val);
+				f->store_64(uint64_t(val));
 
 
 			} else {
 			} else {
 				f->store_32(VARIANT_INT);
 				f->store_32(VARIANT_INT);
-				f->store_32(int32_t(p_property));
+				f->store_32(uint32_t(p_property));
 			}
 			}
 
 
 		} break;
 		} break;
@@ -1645,8 +1645,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 		case Variant::VECTOR2I: {
 		case Variant::VECTOR2I: {
 			f->store_32(VARIANT_VECTOR2I);
 			f->store_32(VARIANT_VECTOR2I);
 			Vector2i val = p_property;
 			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;
 		} break;
 		case Variant::RECT2: {
 		case Variant::RECT2: {
@@ -1661,10 +1661,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 		case Variant::RECT2I: {
 		case Variant::RECT2I: {
 			f->store_32(VARIANT_RECT2I);
 			f->store_32(VARIANT_RECT2I);
 			Rect2i val = p_property;
 			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;
 		} break;
 		case Variant::VECTOR3: {
 		case Variant::VECTOR3: {
@@ -1678,9 +1678,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 		case Variant::VECTOR3I: {
 		case Variant::VECTOR3I: {
 			f->store_32(VARIANT_VECTOR3I);
 			f->store_32(VARIANT_VECTOR3I);
 			Vector3i val = p_property;
 			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;
 		} break;
 		case Variant::VECTOR4: {
 		case Variant::VECTOR4: {
@@ -1695,10 +1695,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 		case Variant::VECTOR4I: {
 		case Variant::VECTOR4I: {
 			f->store_32(VARIANT_VECTOR4I);
 			f->store_32(VARIANT_VECTOR4I);
 			Vector4i val = p_property;
 			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;
 		} break;
 		case Variant::PLANE: {
 		case Variant::PLANE: {
@@ -1821,14 +1821,14 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			f->store_16(snc);
 			f->store_16(snc);
 			for (int i = 0; i < np.get_name_count(); i++) {
 			for (int i = 0; i < np.get_name_count(); i++) {
 				if (string_map.has(np.get_name(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 {
 				} else {
 					save_unicode_string(f, np.get_name(i), true);
 					save_unicode_string(f, np.get_name(i), true);
 				}
 				}
 			}
 			}
 			for (int i = 0; i < np.get_subname_count(); i++) {
 			for (int i = 0; i < np.get_subname_count(); i++) {
 				if (string_map.has(np.get_subname(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 {
 				} else {
 					save_unicode_string(f, np.get_subname(i), true);
 					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);
 			f->store_32(VARIANT_RID);
 			WARN_PRINT("Can't save RIDs.");
 			WARN_PRINT("Can't save RIDs.");
 			RID val = p_property;
 			RID val = p_property;
-			f->store_32(val.get_id());
+			f->store_32(uint32_t(val.get_id()));
 		} break;
 		} break;
 		case Variant::OBJECT: {
 		case Variant::OBJECT: {
 			f->store_32(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()) {
 			if (!res->is_built_in()) {
 				f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX);
 				f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX);
-				f->store_32(external_resources[res]);
+				f->store_32(uint32_t(external_resources[res]));
 			} else {
 			} else {
 				if (!resource_map.has(res)) {
 				if (!resource_map.has(res)) {
 					f->store_32(OBJECT_EMPTY);
 					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(OBJECT_INTERNAL_RESOURCE);
-				f->store_32(resource_map[res]);
+				f->store_32(uint32_t(resource_map[res]));
 				//internal resource
 				//internal resource
 			}
 			}
 
 
@@ -1900,7 +1900,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			f->store_32(VARIANT_PACKED_BYTE_ARRAY);
 			f->store_32(VARIANT_PACKED_BYTE_ARRAY);
 			Vector<uint8_t> arr = p_property;
 			Vector<uint8_t> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const uint8_t *r = arr.ptr();
 			const uint8_t *r = arr.ptr();
 			f->store_buffer(r, len);
 			f->store_buffer(r, len);
 			_pad_buffer(f, 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);
 			f->store_32(VARIANT_PACKED_INT32_ARRAY);
 			Vector<int32_t> arr = p_property;
 			Vector<int32_t> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const int32_t *r = arr.ptr();
 			const int32_t *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
-				f->store_32(r[i]);
+				f->store_32(uint32_t(r[i]));
 			}
 			}
 
 
 		} break;
 		} break;
@@ -1921,10 +1921,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			f->store_32(VARIANT_PACKED_INT64_ARRAY);
 			f->store_32(VARIANT_PACKED_INT64_ARRAY);
 			Vector<int64_t> arr = p_property;
 			Vector<int64_t> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const int64_t *r = arr.ptr();
 			const int64_t *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
-				f->store_64(r[i]);
+				f->store_64(uint64_t(r[i]));
 			}
 			}
 
 
 		} break;
 		} break;
@@ -1932,7 +1932,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			f->store_32(VARIANT_PACKED_FLOAT32_ARRAY);
 			f->store_32(VARIANT_PACKED_FLOAT32_ARRAY);
 			Vector<float> arr = p_property;
 			Vector<float> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const float *r = arr.ptr();
 			const float *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 				f->store_float(r[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);
 			f->store_32(VARIANT_PACKED_FLOAT64_ARRAY);
 			Vector<double> arr = p_property;
 			Vector<double> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const double *r = arr.ptr();
 			const double *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 				f->store_double(r[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);
 			f->store_32(VARIANT_PACKED_STRING_ARRAY);
 			Vector<String> arr = p_property;
 			Vector<String> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const String *r = arr.ptr();
 			const String *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 				save_unicode_string(f, r[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);
 			f->store_32(VARIANT_PACKED_VECTOR2_ARRAY);
 			Vector<Vector2> arr = p_property;
 			Vector<Vector2> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const Vector2 *r = arr.ptr();
 			const Vector2 *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 				f->store_real(r[i].x);
 				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);
 			f->store_32(VARIANT_PACKED_VECTOR3_ARRAY);
 			Vector<Vector3> arr = p_property;
 			Vector<Vector3> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const Vector3 *r = arr.ptr();
 			const Vector3 *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 				f->store_real(r[i].x);
 				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);
 			f->store_32(VARIANT_PACKED_COLOR_ARRAY);
 			Vector<Color> arr = p_property;
 			Vector<Color> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const Color *r = arr.ptr();
 			const Color *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 				f->store_float(r[i].r);
 				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);
 			f->store_32(VARIANT_PACKED_VECTOR4_ARRAY);
 			Vector<Vector4> arr = p_property;
 			Vector<Vector4> arr = p_property;
 			int len = arr.size();
 			int len = arr.size();
-			f->store_32(len);
+			f->store_32(uint32_t(len));
 			const Vector4 *r = arr.ptr();
 			const Vector4 *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 				f->store_real(r[i].x);
 				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) {
 void ResourceFormatSaverBinaryInstance::save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len) {
 	CharString utf8 = p_string.utf8();
 	CharString utf8 = p_string.utf8();
 	if (p_bit_on_len) {
 	if (p_bit_on_len) {
-		p_f->store_32((utf8.length() + 1) | 0x80000000);
+		p_f->store_32(uint32_t((utf8.length() + 1) | 0x80000000));
 	} else {
 	} 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);
 	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);
 		f->store_32(format_flags);
 	}
 	}
 	ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true);
 	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()) {
 	if (!script_class.is_empty()) {
 		save_unicode_string(f, script_class);
 		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++) {
 	for (int i = 0; i < strings.size(); i++) {
 		save_unicode_string(f, strings[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;
 		res_path = relative_paths ? local_path.path_to_file(res_path) : res_path;
 		save_unicode_string(f, res_path);
 		save_unicode_string(f, res_path);
 		ResourceUID::ID ruid = ResourceSaver::get_resource_id_for_path(save_order[i]->get_path(), false);
 		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
 	// 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;
 	Vector<uint64_t> ofs_pos;
 	HashSet<String> used_unique_ids;
 	HashSet<String> used_unique_ids;
 
 
@@ -2362,10 +2362,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
 	for (const ResourceData &rd : resources) {
 	for (const ResourceData &rd : resources) {
 		ofs_table.push_back(f->get_position());
 		ofs_table.push_back(f->get_position());
 		save_unicode_string(f, rd.type);
 		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) {
 		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);
 			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
 	f->get_64(); // Skip previous UID
 
 
 	fw->store_32(flags);
 	fw->store_32(flags);
-	fw->store_64(p_uid);
+	fw->store_64(uint64_t(p_uid));
 
 
 	if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
 	if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
 		save_ustring(fw, get_ustring(f));
 		save_ustring(fw, get_ustring(f));

+ 1 - 1
core/io/resource_importer.h

@@ -46,7 +46,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
 		String importer;
 		String importer;
 		String group_file;
 		String group_file;
 		Variant metadata;
 		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;
 	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

@@ -178,7 +178,7 @@ Error ResourceUID::save_to_cache() {
 	cache_entries = 0;
 	cache_entries = 0;
 
 
 	for (KeyValue<ID, Cache> &E : unique_ids) {
 	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();
 		uint32_t s = E.value.cs.length();
 		f->store_32(s);
 		f->store_32(s);
 		f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);
 		f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);
@@ -241,7 +241,7 @@ Error ResourceUID::update_cache() {
 				}
 				}
 				f->seek_end();
 				f->seek_end();
 			}
 			}
-			f->store_64(E.key);
+			f->store_64(uint64_t(E.key));
 			uint32_t s = E.value.cs.length();
 			uint32_t s = E.value.cs.length();
 			f->store_32(s);
 			f->store_32(s);
 			f->store_buffer((const uint8_t *)E.value.cs.ptr(), 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)
 	GDCLASS(ResourceUID, Object)
 public:
 public:
 	typedef int64_t ID;
 	typedef int64_t ID;
-	enum {
-		INVALID_ID = -1
-	};
+	constexpr const static ID INVALID_ID = -1;
 
 
 	static String get_cache_file();
 	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) {
 void StreamPeer::put_double(double p_val) {
 	uint8_t buf[8];
 	uint8_t buf[8];
+
 	encode_double(p_val, buf);
 	encode_double(p_val, buf);
 	if (big_endian) {
 	if (big_endian) {
 		uint64_t *p64 = (uint64_t *)buf;
 		uint64_t *p64 = (uint64_t *)buf;
 		*p64 = BSWAP64(*p64);
 		*p64 = BSWAP64(*p64);
 	}
 	}
+
 	put_data(buf, 8);
 	put_data(buf, 8);
 }
 }
 
 
@@ -243,75 +245,87 @@ uint8_t StreamPeer::get_u8() {
 int8_t StreamPeer::get_8() {
 int8_t StreamPeer::get_8() {
 	uint8_t buf[1] = {};
 	uint8_t buf[1] = {};
 	get_data(buf, 1);
 	get_data(buf, 1);
-	return buf[0];
+	return int8_t(buf[0]);
 }
 }
 
 
 uint16_t StreamPeer::get_u16() {
 uint16_t StreamPeer::get_u16() {
 	uint8_t buf[2];
 	uint8_t buf[2];
 	get_data(buf, 2);
 	get_data(buf, 2);
+
 	uint16_t r = decode_uint16(buf);
 	uint16_t r = decode_uint16(buf);
 	if (big_endian) {
 	if (big_endian) {
 		r = BSWAP16(r);
 		r = BSWAP16(r);
 	}
 	}
+
 	return r;
 	return r;
 }
 }
 
 
 int16_t StreamPeer::get_16() {
 int16_t StreamPeer::get_16() {
 	uint8_t buf[2];
 	uint8_t buf[2];
 	get_data(buf, 2);
 	get_data(buf, 2);
+
 	uint16_t r = decode_uint16(buf);
 	uint16_t r = decode_uint16(buf);
 	if (big_endian) {
 	if (big_endian) {
 		r = BSWAP16(r);
 		r = BSWAP16(r);
 	}
 	}
-	return r;
+
+	return int16_t(r);
 }
 }
 
 
 uint32_t StreamPeer::get_u32() {
 uint32_t StreamPeer::get_u32() {
 	uint8_t buf[4];
 	uint8_t buf[4];
 	get_data(buf, 4);
 	get_data(buf, 4);
+
 	uint32_t r = decode_uint32(buf);
 	uint32_t r = decode_uint32(buf);
 	if (big_endian) {
 	if (big_endian) {
 		r = BSWAP32(r);
 		r = BSWAP32(r);
 	}
 	}
+
 	return r;
 	return r;
 }
 }
 
 
 int32_t StreamPeer::get_32() {
 int32_t StreamPeer::get_32() {
 	uint8_t buf[4];
 	uint8_t buf[4];
 	get_data(buf, 4);
 	get_data(buf, 4);
+
 	uint32_t r = decode_uint32(buf);
 	uint32_t r = decode_uint32(buf);
 	if (big_endian) {
 	if (big_endian) {
 		r = BSWAP32(r);
 		r = BSWAP32(r);
 	}
 	}
-	return r;
+
+	return int32_t(r);
 }
 }
 
 
 uint64_t StreamPeer::get_u64() {
 uint64_t StreamPeer::get_u64() {
 	uint8_t buf[8];
 	uint8_t buf[8];
 	get_data(buf, 8);
 	get_data(buf, 8);
+
 	uint64_t r = decode_uint64(buf);
 	uint64_t r = decode_uint64(buf);
 	if (big_endian) {
 	if (big_endian) {
 		r = BSWAP64(r);
 		r = BSWAP64(r);
 	}
 	}
+
 	return r;
 	return r;
 }
 }
 
 
 int64_t StreamPeer::get_64() {
 int64_t StreamPeer::get_64() {
 	uint8_t buf[8];
 	uint8_t buf[8];
 	get_data(buf, 8);
 	get_data(buf, 8);
+
 	uint64_t r = decode_uint64(buf);
 	uint64_t r = decode_uint64(buf);
 	if (big_endian) {
 	if (big_endian) {
 		r = BSWAP64(r);
 		r = BSWAP64(r);
 	}
 	}
-	return r;
+
+	return int64_t(r);
 }
 }
 
 
 float StreamPeer::get_half() {
 float StreamPeer::get_half() {
 	uint8_t buf[2];
 	uint8_t buf[2];
 	get_data(buf, 2);
 	get_data(buf, 2);
 
 
-	uint16_t *p16 = (uint16_t *)buf;
 	if (big_endian) {
 	if (big_endian) {
+		uint16_t *p16 = (uint16_t *)buf;
 		*p16 = BSWAP16(*p16);
 		*p16 = BSWAP16(*p16);
 	}
 	}
 
 
@@ -344,7 +358,7 @@ double StreamPeer::get_double() {
 
 
 String StreamPeer::get_string(int p_bytes) {
 String StreamPeer::get_string(int p_bytes) {
 	if (p_bytes < 0) {
 	if (p_bytes < 0) {
-		p_bytes = get_u32();
+		p_bytes = get_32();
 	}
 	}
 	ERR_FAIL_COND_V(p_bytes < 0, String());
 	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) {
 String StreamPeer::get_utf8_string(int p_bytes) {
 	if (p_bytes < 0) {
 	if (p_bytes < 0) {
-		p_bytes = get_u32();
+		p_bytes = get_32();
 	}
 	}
 	ERR_FAIL_COND_V(p_bytes < 0, String());
 	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) {
 Error StreamPeerBuffer::put_data(const uint8_t *p_data, int p_bytes) {
-	if (p_bytes <= 0) {
+	if (p_bytes <= 0 || !p_data) {
 		return OK;
 		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) {
 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()) {
 	if (pointer + p_bytes > data.size()) {
 		r_received = data.size() - pointer;
 		r_received = data.size() - pointer;
 		if (r_received <= 0) {
 		if (r_received <= 0) {

+ 1 - 1
core/math/bvh_pair.inc

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

+ 1 - 3
core/math/bvh_tree.h

@@ -109,9 +109,7 @@ struct BVHHandle {
 template <typename T>
 template <typename T>
 class BVH_IterativeInfo {
 class BVH_IterativeInfo {
 public:
 public:
-	enum {
-		ALLOCA_STACK_SIZE = 128
-	};
+	constexpr static const size_t ALLOCA_STACK_SIZE = 128;
 
 
 	int32_t depth = 1;
 	int32_t depth = 1;
 	int32_t threshold = ALLOCA_STACK_SIZE - 2;
 	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.
 		// of the mantissa, rounding the truncated bits.
 		union {
 		union {
 			float f;
 			float f;
-			int32_t i;
+			uint32_t i;
 		} R, G, B, E;
 		} R, G, B, E;
 
 
 		E.f = MaxChannel;
 		E.f = MaxChannel;
@@ -168,7 +168,7 @@ struct [[nodiscard]] Color {
 		// Combine the fields. RGB floats have unwanted data in the upper 9
 		// 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
 		// bits. Only red needs to mask them off because green and blue shift
 		// it out to the left.
 		// 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 {
 	_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) {
 	if (p_from == p_to) {
 		return p_from;
 		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) {
 			for (const StringName &F : snames) {
 				hash = hash_murmur3_one_64(F.hash(), hash);
 				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_valid() const { return id != 0; }
 	_ALWAYS_INLINE_ bool is_null() const { return id == 0; }
 	_ALWAYS_INLINE_ bool is_null() const { return id == 0; }
 	_ALWAYS_INLINE_ operator uint64_t() const { return id; }
 	_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; }
 	_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);
 	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);
 	free_aligned_static(p_memory);
 	return ret;
 	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>
 template <int SHORT_BUFFER_SIZE>
 StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::reserve(int p_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;
 		return *this;
 	}
 	}
 
 

+ 57 - 72
core/string/ustring.cpp

@@ -42,10 +42,6 @@
 #include "core/variant/variant.h"
 #include "core/variant/variant.h"
 #include "core/version_generated.gen.h"
 #include "core/version_generated.gen.h"
 
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <cstdint>
-
 #ifdef _MSC_VER
 #ifdef _MSC_VER
 #define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
 #define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
 #endif
 #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) {
 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_num == (double)(int64_t)p_num) {
 		if (p_trailing) {
 		if (p_trailing) {
 			return num_int64((int64_t)p_num) + ".0";
 			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);
 			return num_int64((int64_t)p_num);
 		}
 		}
 	}
 	}
+
 	int decimals = 14;
 	int decimals = 14;
 	// We want to align the digits to the above sane default, so we only need
 	// 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.
 	// 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) {
 	if (abs_num > 10) {
 		decimals -= (int)floor(log10(abs_num));
 		decimals -= (int)floor(log10(abs_num));
 	}
 	}
+
 	return num(p_num, decimals);
 	return num(p_num, decimals);
 }
 }
 
 
 String String::num_real(float p_num, bool p_trailing) {
 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_num == (float)(int64_t)p_num) {
 		if (p_trailing) {
 		if (p_trailing) {
 			return num_int64((int64_t)p_num) + ".0";
 			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) {
 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];
 	char buf[256];
@@ -1947,7 +1945,7 @@ CharString String::ascii(bool p_allow_extended) const {
 	for (int i = 0; i < size(); i++) {
 	for (int i = 0; i < size(); i++) {
 		char32_t c = this_ptr[i];
 		char32_t c = this_ptr[i];
 		if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) {
 		if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) {
-			cs_ptrw[i] = c;
+			cs_ptrw[i] = char(c);
 		} else {
 		} else {
 			print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as ASCII/Latin-1", (uint32_t)c));
 			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.
 			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;
 	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++) {
 	for (int i = 0; i < to; i++) {
-		char32_t c = operator[](i);
+		C c = p_in[i];
 		if (is_digit(c)) {
 		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 *= 10;
 			integer += c - '0';
 			integer += c - '0';
+			++digits;
 
 
 		} else if (integer == 0 && c == '-') {
 		} 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) {
 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) {
 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 {
 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;
 		return p_this;
 	}
 	}
 
 
-	const int key_length = p_key.length();
+	const size_t key_length = p_key.length();
 
 
 	int search_from = 0;
 	int search_from = 0;
 	int result = 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) {
 	while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
 		found.push_back(result);
 		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;
 		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 with_length = p_with.length();
 	const int old_length = p_this.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();
 	char32_t *new_ptrw = new_string.ptrw();
 	const char32_t *old_ptr = p_this.ptr();
 	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) {
 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()) {
 	if (key_length == 0 || p_this.is_empty()) {
 		return p_this;
 		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) {
 	while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
 		found.push_back(result);
 		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;
 		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 with_length = with_string.length();
 	const int old_length = p_this.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();
 	char32_t *new_ptrw = new_string.ptrw();
 	const char32_t *old_ptr = p_this.ptr();
 	const char32_t *old_ptr = p_this.ptr();
@@ -4639,8 +4623,9 @@ bool String::is_valid_string() const {
 String String::uri_encode() const {
 String String::uri_encode() const {
 	const CharString temp = utf8();
 	const CharString temp = utf8();
 	String res;
 	String res;
+
 	for (int i = 0; i < temp.length(); ++i) {
 	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)) {
 		if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) {
 			res += ord;
 			res += ord;
 		} else {
 		} else {

+ 3 - 1
core/templates/command_queue_mt.h

@@ -339,7 +339,9 @@ class CommandQueueMT {
 	template <typename T>
 	template <typename T>
 	T *allocate() {
 	T *allocate() {
 		// alloc size is size+T+safeguard
 		// 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();
 		uint64_t size = command_mem.size();
 		command_mem.resize(size + alloc_size + 8);
 		command_mem.resize(size + alloc_size + 8);
 		*(uint64_t *)&command_mem[size] = alloc_size;
 		*(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 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 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 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 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(); }
 	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 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 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 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 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 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) {
 	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);
 		return hash_fmix32(h);
 	}
 	}
 	static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) {
 	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);
 		return hash_fmix32(h);
 	}
 	}
 	static _FORCE_INLINE_ uint32_t hash(const Vector4i &p_vec) {
 	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);
 		return hash_fmix32(h);
 	}
 	}
 	static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
 	static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
@@ -379,10 +379,10 @@ struct HashMapHasherDefault {
 		return hash_fmix32(h);
 		return hash_fmix32(h);
 	}
 	}
 	static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
 	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);
 		return hash_fmix32(h);
 	}
 	}
 	static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) {
 	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;
 		Vector<T> ret;
 		ret.resize(size());
 		ret.resize(size());
 		T *w = ret.ptrw();
 		T *w = ret.ptrw();
-		memcpy(w, data, sizeof(T) * count);
+		if (w) {
+			memcpy(w, data, sizeof(T) * count);
+		}
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -305,7 +307,9 @@ public:
 		Vector<uint8_t> ret;
 		Vector<uint8_t> ret;
 		ret.resize(count * sizeof(T));
 		ret.resize(count * sizeof(T));
 		uint8_t *w = ret.ptrw();
 		uint8_t *w = ret.ptrw();
-		memcpy(w, data, sizeof(T) * count);
+		if (w) {
+			memcpy(w, data, sizeof(T) * count);
+		}
 		return ret;
 		return ret;
 	}
 	}
 
 

+ 5 - 2
core/templates/vector.h

@@ -156,8 +156,11 @@ public:
 		if (is_empty()) {
 		if (is_empty()) {
 			return ret;
 			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;
 		return ret;
 	}
 	}
 
 

+ 29 - 29
core/variant/variant.cpp

@@ -1494,11 +1494,11 @@ Variant::operator int64_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return int64_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return int64_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return int64_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -1512,11 +1512,11 @@ Variant::operator int32_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return int32_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return int32_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return int32_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -1530,11 +1530,11 @@ Variant::operator int16_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return int16_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return int16_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return int16_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -1548,11 +1548,11 @@ Variant::operator int8_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return int8_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return int8_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return int8_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -1566,11 +1566,11 @@ Variant::operator uint64_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return uint64_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return uint64_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return uint64_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -1584,11 +1584,11 @@ Variant::operator uint32_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return uint32_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return uint32_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return uint32_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -1602,11 +1602,11 @@ Variant::operator uint16_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return uint16_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return uint16_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return uint16_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -1620,11 +1620,11 @@ Variant::operator uint8_t() const {
 		case BOOL:
 		case BOOL:
 			return _data._bool ? 1 : 0;
 			return _data._bool ? 1 : 0;
 		case INT:
 		case INT:
-			return _data._int;
+			return uint8_t(_data._int);
 		case FLOAT:
 		case FLOAT:
-			return _data._float;
+			return uint8_t(_data._float);
 		case STRING:
 		case STRING:
-			return operator String().to_int();
+			return uint8_t(operator String().to_int());
 		default: {
 		default: {
 			return 0;
 			return 0;
 		}
 		}
@@ -2484,22 +2484,22 @@ Variant::Variant(int8_t p_int8) :
 
 
 Variant::Variant(uint64_t p_uint64) :
 Variant::Variant(uint64_t p_uint64) :
 		type(INT) {
 		type(INT) {
-	_data._int = p_uint64;
+	_data._int = int64_t(p_uint64);
 }
 }
 
 
 Variant::Variant(uint32_t p_uint32) :
 Variant::Variant(uint32_t p_uint32) :
 		type(INT) {
 		type(INT) {
-	_data._int = p_uint32;
+	_data._int = int64_t(p_uint32);
 }
 }
 
 
 Variant::Variant(uint16_t p_uint16) :
 Variant::Variant(uint16_t p_uint16) :
 		type(INT) {
 		type(INT) {
-	_data._int = p_uint16;
+	_data._int = int64_t(p_uint16);
 }
 }
 
 
 Variant::Variant(uint8_t p_uint8) :
 Variant::Variant(uint8_t p_uint8) :
 		type(INT) {
 		type(INT) {
-	_data._int = p_uint8;
+	_data._int = int64_t(p_uint8);
 }
 }
 
 
 Variant::Variant(float p_float) :
 Variant::Variant(float p_float) :
@@ -2514,7 +2514,7 @@ Variant::Variant(double p_double) :
 
 
 Variant::Variant(const ObjectID &p_id) :
 Variant::Variant(const ObjectID &p_id) :
 		type(INT) {
 		type(INT) {
-	_data._int = p_id;
+	_data._int = int64_t(p_id);
 }
 }
 
 
 Variant::Variant(const StringName &p_string) :
 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) {
 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)
 	// The buffer is assumed to include at least one character (for null terminator)
 	ERR_FAIL_COND_V(!p_num_chars, 0);
 	ERR_FAIL_COND_V(!p_num_chars, 0);
+	ERR_FAIL_NULL_V(p_buffer, 0);
 
 
 	int available = MAX(s.length() - pos, 0);
 	int available = MAX(s.length() - pos, 0);
 	if (available >= (int)p_num_chars) {
 	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
 		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
 } // namespace TestJSON
 
 
 #endif // TEST_JSON_H
 #endif // TEST_JSON_H