浏览代码

Merge pull request #106008 from beicause/cs-byte-array-compress

C#: Expose byte array compress and decompress
Thaddeus Crews 4 月之前
父节点
当前提交
66d20a24b4

+ 48 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs

@@ -682,5 +682,53 @@ namespace Godot
         {
             return Marshaling.ConvertManagedTypeToVariantType(type, out bool _);
         }
+
+        /// <summary>
+        ///	Returns a new byte array with the data compressed.
+        /// </summary>
+        /// <param name="instance">The byte array to compress.</param>
+        /// <param name="compressionMode">The compression mode, one of <see cref="FileAccess.CompressionMode"/></param>
+        /// <returns>The compressed byte array.</returns>
+        public static byte[] Compress(this byte[] instance, FileAccess.CompressionMode compressionMode = 0)
+        {
+            using godot_packed_byte_array src = Marshaling.ConvertSystemArrayToNativePackedByteArray(instance);
+            NativeFuncs.godotsharp_packed_byte_array_compress(src, (int)compressionMode, out var ret);
+            using (ret)
+                return Marshaling.ConvertNativePackedByteArrayToSystemArray(ret);
+        }
+
+        /// <summary>
+        /// Returns a new byte array with the data decompressed.
+        /// <para>Note: Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate compression mode lacks a checksum or header.</para>
+        /// </summary>
+        /// <param name="instance">The byte array to decompress.</param>
+        /// <param name="bufferSize">The size of the uncompressed data.</param>
+        /// <param name="compressionMode">The compression mode, one of <see cref="FileAccess.CompressionMode"/></param>
+        /// <returns>The decompressed byte array.</returns>
+        public static byte[] Decompress(this byte[] instance, long bufferSize, FileAccess.CompressionMode compressionMode = 0)
+        {
+            using godot_packed_byte_array src = Marshaling.ConvertSystemArrayToNativePackedByteArray(instance);
+            NativeFuncs.godotsharp_packed_byte_array_decompress(src, bufferSize, (int)compressionMode, out var ret);
+            using (ret)
+                return Marshaling.ConvertNativePackedByteArrayToSystemArray(ret);
+        }
+
+        /// <summary>
+        ///	Returns a new byte array with the data decompressed. <b>This method only accepts brotli, gzip, and deflate compression modes</b>.
+        /// <para>This method is potentially slower than <see cref="Decompress"/>, as it may have to re-allocate its output buffer multiple times while decompressing, whereas <see cref="Decompress"/> knows it's output buffer size from the beginning.</para>
+        /// <para>GZIP has a maximal compression ratio of 1032:1, meaning it's very possible for a small compressed payload to decompress to a potentially very large output. To guard against this, you may provide a maximum size this function is allowed to allocate in bytes via [param max_output_size]. Passing -1 will allow for unbounded output. If any positive value is passed, and the decompression exceeds that amount in bytes, then an error will be returned.</para>
+        /// <para>Note: Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate compression mode lacks a checksum or header.</para>
+        /// </summary>
+        /// <param name="instance">The byte array to decompress.</param>
+        /// <param name="maxOutputSize">The maximum size this function is allowed to allocate in bytes.</param>
+        /// <param name="compressionMode">The compression mode, one of <see cref="FileAccess.CompressionMode"/></param>
+        /// <returns>The decompressed byte array.</returns>
+        public static byte[] DecompressDynamic(this byte[] instance, long maxOutputSize, FileAccess.CompressionMode compressionMode = 0)
+        {
+            using godot_packed_byte_array src = Marshaling.ConvertSystemArrayToNativePackedByteArray(instance);
+            NativeFuncs.godotsharp_packed_byte_array_decompress_dynamic(src, maxOutputSize, (int)compressionMode, out var ret);
+            using (ret)
+                return Marshaling.ConvertNativePackedByteArrayToSystemArray(ret);
+        }
     }
 }

+ 6 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs

@@ -433,6 +433,12 @@ namespace Godot.NativeInterop
 
         public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str);
 
+        public static partial void godotsharp_packed_byte_array_compress(scoped in godot_packed_byte_array p_src, int p_mode, out godot_packed_byte_array r_dst);
+
+        public static partial void godotsharp_packed_byte_array_decompress(scoped in godot_packed_byte_array p_src, long p_buffer_size, int p_mode, out godot_packed_byte_array r_dst);
+
+        public static partial void godotsharp_packed_byte_array_decompress_dynamic(scoped in godot_packed_byte_array p_src, long p_buffer_size, int p_mode, out godot_packed_byte_array r_dst);
+
         // Dictionary
 
         public static partial godot_bool godotsharp_dictionary_try_get_value(scoped ref godot_dictionary p_self,

+ 44 - 0
modules/mono/glue/runtime_interop.cpp

@@ -1168,6 +1168,47 @@ void godotsharp_array_to_string(const Array *p_self, String *r_str) {
 	*r_str = Variant(*p_self).operator String();
 }
 
+void godotsharp_packed_byte_array_compress(const PackedByteArray *p_src, int p_mode, PackedByteArray *r_dst) {
+	if (p_src->size() > 0) {
+		Compression::Mode mode = (Compression::Mode)(p_mode);
+		r_dst->resize(Compression::get_max_compressed_buffer_size(p_src->size(), mode));
+		int result = Compression::compress(r_dst->ptrw(), p_src->ptr(), p_src->size(), mode);
+
+		result = result >= 0 ? result : 0;
+		r_dst->resize(result);
+	}
+}
+
+void godotsharp_packed_byte_array_decompress(const PackedByteArray *p_src, int64_t p_buffer_size, int p_mode, PackedByteArray *r_dst) {
+	int64_t buffer_size = p_buffer_size;
+	Compression::Mode mode = (Compression::Mode)(p_mode);
+
+	if (buffer_size <= 0) {
+		ERR_FAIL_MSG("Decompression buffer size must be greater than zero.");
+	}
+	if (p_src->size() == 0) {
+		ERR_FAIL_MSG("Compressed buffer size must be greater than zero.");
+	}
+
+	r_dst->resize(buffer_size);
+	int result = Compression::decompress(r_dst->ptrw(), buffer_size, p_src->ptr(), p_src->size(), mode);
+
+	result = result >= 0 ? result : 0;
+	r_dst->resize(result);
+}
+
+void godotsharp_packed_byte_array_decompress_dynamic(const PackedByteArray *p_src, int64_t p_max_output_size, int p_mode, PackedByteArray *r_dst) {
+	int64_t max_output_size = p_max_output_size;
+	Compression::Mode mode = (Compression::Mode)(p_mode);
+
+	int result = Compression::decompress_dynamic(r_dst, max_output_size, p_src->ptr(), p_src->size(), mode);
+
+	if (result != OK) {
+		r_dst->clear();
+		ERR_FAIL_MSG("Decompression failed.");
+	}
+}
+
 // Dictionary
 
 bool godotsharp_dictionary_try_get_value(const Dictionary *p_self, const Variant *p_key, Variant *r_value) {
@@ -1683,6 +1724,9 @@ static const void *unmanaged_callbacks[]{
 	(void *)godotsharp_array_slice,
 	(void *)godotsharp_array_sort,
 	(void *)godotsharp_array_to_string,
+	(void *)godotsharp_packed_byte_array_compress,
+	(void *)godotsharp_packed_byte_array_decompress,
+	(void *)godotsharp_packed_byte_array_decompress_dynamic,
 	(void *)godotsharp_dictionary_try_get_value,
 	(void *)godotsharp_dictionary_set_value,
 	(void *)godotsharp_dictionary_keys,