// Copyright (c) Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; using FlaxEngine.Interop; namespace FlaxEngine { /// /// Class with helper functions. /// public static partial class Utils { /// /// Copies data from one memory location to another using an unmanaged memory pointers. /// /// Uses low-level platform impl. /// The source location. /// The destination location. /// The length (amount of bytes to copy). [Obsolete("Use MemoryCopy with long length and source/destination parameters swapped instead")] public static void MemoryCopy(IntPtr source, IntPtr destination, int length) { // [Deprecated on 30.05.2021, expires on 30.05.2022] MemoryCopy(destination, source, (ulong)length); } /// /// Copies data from one memory location to another using an unmanaged memory pointers. /// /// Uses low-level platform impl. /// The source location. /// The destination location. /// The length (amount of bytes to copy). [LibraryImport("FlaxEngine", EntryPoint = "PlatformInternal_MemoryCopy")] public static partial void MemoryCopy(IntPtr destination, IntPtr source, ulong length); /// /// Clears the memory region with zeros. /// /// Uses low-level platform impl. /// Destination memory address /// Size of the memory to clear in bytes [LibraryImport("FlaxEngine", EntryPoint = "PlatformInternal_MemoryClear")] public static partial void MemoryClear(IntPtr dst, ulong size); /// /// Compares two blocks of the memory. /// /// Uses low-level platform impl. /// The first buffer address. /// The second buffer address. /// Size of the memory to compare in bytes. [LibraryImport("FlaxEngine", EntryPoint = "PlatformInternal_MemoryCompare")] public static partial int MemoryCompare(IntPtr buf1, IntPtr buf2, ulong size); /// /// Rounds the floating point value up to 1 decimal place. /// /// The value. /// The rounded result. public static float RoundTo1DecimalPlace(float value) { return (float)Math.Round(value * 10) / 10; } /// /// Rounds the floating point value up to 2 decimal places. /// /// The value. /// The rounded result. public static float RoundTo2DecimalPlaces(float value) { return (float)Math.Round(value * 100) / 100; } /// /// Rounds the floating point value up to 3 decimal places. /// /// The value. /// The rounded result. public static float RoundTo3DecimalPlaces(float value) { return (float)Math.Round(value * 1000) / 1000; } /// /// Gets the empty array of the given type (shared one). /// /// The type. /// The empty array object. public static T[] GetEmptyArray() { #if USE_NETCORE return Array.Empty(); #else return Enumerable.Empty() as T[]; #endif } /// /// Determines whether two arrays are equal by comparing the elements by using the default equality comparer for their type. /// /// The type of the elements of the input sequences. /// The first array. /// The second array. /// true if the two source sequences are of equal length and their corresponding elements are equal according to the default equality comparer for their type; otherwise, false. public static bool ArraysEqual(T[] a1, T[] a2) { if (ReferenceEquals(a1, a2)) return true; if (a1 == null || a2 == null) return false; if (a1.Length != a2.Length) return false; var comparer = EqualityComparer.Default; for (int i = 0; i < a1.Length; i++) { if (!comparer.Equals(a1[i], a2[i])) return false; } return true; } /// /// Determines whether two arrays are equal by comparing the elements by using the default equality comparer for their type. /// /// The type of the elements of the input sequences. /// The first array. /// The second array. /// true if the two source sequences are of equal length and their corresponding elements are equal according to the default equality comparer for their type; otherwise, false. public static bool ArraysEqual(T[] a1, IReadOnlyList a2) { if (a1 == null || a2 == null) return false; if (a1.Length != a2.Count) return false; var comparer = EqualityComparer.Default; for (int i = 0; i < a1.Length; i++) { if (!comparer.Equals(a1[i], a2[i])) return false; } return true; } /// /// Determines whether two arrays are equal by comparing the elements by using the default equality comparer for their type. /// /// The type of the elements of the input sequences. /// The first array. /// The second array. /// true if the two source sequences are of equal length and their corresponding elements are equal according to the default equality comparer for their type; otherwise, false. public static bool ArraysEqual(List a1, List a2) { if (ReferenceEquals(a1, a2)) return true; if (a1 == null || a2 == null) return false; if (a1.Count != a2.Count) return false; var comparer = EqualityComparer.Default; for (int i = 0; i < a1.Count; i++) { if (!comparer.Equals(a1[i], a2[i])) return false; } return true; } /// /// Gets all currently loaded assemblies in the runtime. /// /// List of assemblies public static Assembly[] GetAssemblies() { #if USE_NETCORE return AssemblyLoadContext.Default.Assemblies.Concat(NativeInterop.scriptingAssemblyLoadContext.Assemblies).ToArray(); #else return AppDomain.CurrentDomain.GetAssemblies(); #endif } /// /// Gets the assembly with the given name. /// /// The name. /// The assembly or null if not found. public static Assembly GetAssemblyByName(string name) { return GetAssemblyByName(name, GetAssemblies()); } /// /// Gets the assembly with the given name. /// /// The name. /// The assemblies collection to search for. /// The assembly or null if not found. public static Assembly GetAssemblyByName(string name, Assembly[] assemblies) { Assembly result = null; for (int i = 0; i < assemblies.Length; i++) { var assemblyName = assemblies[i].GetName(); if (assemblyName.Name == name) { result = assemblies[i]; break; } } return result; } /// /// Gets the location of the assembly. /// /// The assembly. /// Path in the filesystem public static string GetAssemblyLocation(Assembly assembly) { #if USE_NETCORE var location = assembly.Location; if (!string.IsNullOrEmpty(location)) return location; if (Interop.NativeInterop.AssemblyLocations.TryGetValue(assembly.FullName, out location)) return location; return null; #else return assembly.Location; #endif } #if USE_MONO internal static T[] ExtractArrayFromList(List list) { return list != null ? (T[])Internal_ExtractArrayFromList(list) : null; } #else private class ExtractArrayFromListContext { public static FieldInfo itemsField; } internal static T[] ExtractArrayFromList(List list) { if (list == null) return null; if (ExtractArrayFromListContext.itemsField == null) { Type listType = typeof(List); ExtractArrayFromListContext.itemsField = listType.GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance); } return (T[])ExtractArrayFromListContext.itemsField.GetValue(list); // boxing is slower; } #endif internal static Float2[] ConvertCollection(Vector2[] v) { // [Deprecated on 26.05.2022, expires on 26.05.2024] Float2[] result = null; if (v != null && v.Length != 0) { result = new Float2[v.Length]; for (int i = 0; i < v.Length; i++) result[i] = v[i]; } return result; } internal static List ConvertCollection(List v) { // [Deprecated on 26.05.2022, expires on 26.05.2024] List result = null; if (v != null && v.Count != 0) { result = new List(); result.Capacity = v.Count; for (int i = 0; i < v.Count; i++) result.Add(v[i]); } return result; } internal static Float3[] ConvertCollection(Vector3[] v) { // [Deprecated on 26.05.2022, expires on 26.05.2024] Float3[] result = null; if (v != null && v.Length != 0) { result = new Float3[v.Length]; for (int i = 0; i < v.Length; i++) result[i] = v[i]; } return result; } internal static List ConvertCollection(List v) { // [Deprecated on 26.05.2022, expires on 26.05.2024] List result = null; if (v != null && v.Count != 0) { result = new List(); result.Capacity = v.Count; for (int i = 0; i < v.Count; i++) result.Add(v[i]); } return result; } internal static Float4[] ConvertCollection(Vector4[] v) { // [Deprecated on 26.05.2022, expires on 26.05.2024] Float4[] result = null; if (v != null && v.Length != 0) { result = new Float4[v.Length]; for (int i = 0; i < v.Length; i++) result[i] = v[i]; } return result; } internal static List ConvertCollection(List v) { // [Deprecated on 26.05.2022, expires on 26.05.2024] List result = null; if (v != null && v.Count != 0) { result = new List(); result.Capacity = v.Count; for (int i = 0; i < v.Count; i++) result.Add(v[i]); } return result; } #if USE_NETCORE #else [LibraryImport("FlaxEngine", EntryPoint = "UtilsInternal_ExtractArrayFromList")] [return: MarshalUsing(typeof(FlaxEngine.SystemArrayMarshaller))] internal static partial Array Internal_ExtractArrayFromList([MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))] object list); #endif /// /// Reads the color from the binary stream. /// /// The stream. /// The value. public static Color32 ReadColor32(this BinaryReader stream) { return new Color32(stream.ReadByte(), stream.ReadByte(), stream.ReadByte(), stream.ReadByte()); } /// /// Reads the color from the binary stream. /// /// The stream. /// The value. public static Color ReadColor(this BinaryReader stream) { return new Color(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Vector2 from the binary stream. /// /// The stream. /// The value. public static Vector2 ReadVector2(this BinaryReader stream) { return new Vector2(stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Vector3 from the binary stream. /// /// The stream. /// The value. public static Vector3 ReadVector3(this BinaryReader stream) { return new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Vector4 from the binary stream. /// /// The stream. /// The value. public static Vector4 ReadVector4(this BinaryReader stream) { return new Vector4(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Vector2 from the binary stream. /// /// The stream. /// True to explicitly use 64-bit precision for serialized data, otherwise will use 32-bit precision. /// The value. public static Vector2 ReadVector2(this BinaryReader stream, bool useDouble) { if (useDouble) return new Vector2(stream.ReadDouble(), stream.ReadDouble()); return new Vector2(stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Vector3 from the binary stream. /// /// The stream. /// True to explicitly use 64-bit precision for serialized data, otherwise will use 32-bit precision. /// The value. public static Vector3 ReadVector3(this BinaryReader stream, bool useDouble) { if (useDouble) return new Vector3(stream.ReadDouble(), stream.ReadDouble(), stream.ReadDouble()); return new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Vector4 from the binary stream. /// /// The stream. /// True to explicitly use 64-bit precision for serialized data, otherwise will use 32-bit precision. /// The value. public static Vector4 ReadVector4(this BinaryReader stream, bool useDouble) { if (useDouble) return new Vector4(stream.ReadDouble(), stream.ReadDouble(), stream.ReadDouble(), stream.ReadDouble()); return new Vector4(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Float2 from the binary stream. /// /// The stream. /// The value. public static Float2 ReadFloat2(this BinaryReader stream) { return new Float2(stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Float3 from the binary stream. /// /// The stream. /// The value. public static Float3 ReadFloat3(this BinaryReader stream) { return new Float3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Float4 from the binary stream. /// /// The stream. /// The value. public static Float4 ReadFloat4(this BinaryReader stream) { return new Float4(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the Double2 from the binary stream. /// /// The stream. /// The value. public static Double2 ReadDouble2(this BinaryReader stream) { return new Double2(stream.ReadDouble(), stream.ReadDouble()); } /// /// Reads the Double3 from the binary stream. /// /// The stream. /// The value. public static Double3 ReadDouble3(this BinaryReader stream) { return new Double3(stream.ReadDouble(), stream.ReadDouble(), stream.ReadDouble()); } /// /// Reads the Double4 from the binary stream. /// /// The stream. /// The value. public static Double4 ReadDouble4(this BinaryReader stream) { return new Double4(stream.ReadDouble(), stream.ReadDouble(), stream.ReadDouble(), stream.ReadDouble()); } /// /// Reads the Int2 from the binary stream. /// /// The stream. /// The value. public static Int2 ReadInt2(this BinaryReader stream) { return new Int2(stream.ReadInt32(), stream.ReadInt32()); } /// /// Reads the Int3 from the binary stream. /// /// The stream. /// The value. public static Int3 ReadInt3(this BinaryReader stream) { return new Int3(stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32()); } /// /// Reads the Int4 from the binary stream. /// /// The stream. /// The value. public static Int4 ReadInt4(this BinaryReader stream) { return new Int4(stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32()); } /// /// Reads the Quaternion from the binary stream. /// /// The stream. /// The value. public static Quaternion ReadQuaternion(this BinaryReader stream) { return new Quaternion(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } /// /// Reads the BoundingBox from the binary stream. /// /// The stream. /// The value. public static BoundingBox ReadBoundingBox(this BinaryReader stream) { return new BoundingBox(stream.ReadVector3(), stream.ReadVector3()); } /// /// Reads the BoundingSphere from the binary stream. /// /// The stream. /// The value. public static BoundingSphere ReadBoundingSphere(this BinaryReader stream) { return new BoundingSphere(stream.ReadVector3(), stream.ReadSingle()); } /// /// Reads the Ray from the binary stream. /// /// The stream. /// The value. public static Ray ReadRay(this BinaryReader stream) { return new Ray(stream.ReadVector3(), stream.ReadVector3()); } /// /// Reads the Transform from the binary stream. /// /// The stream. /// The value. public static Transform ReadTransform(this BinaryReader stream) { return new Transform(stream.ReadVector3(), stream.ReadQuaternion(), stream.ReadVector3()); } /// /// Reads the Matrix from the binary stream. /// /// The stream. /// The value. public static Matrix ReadMatrix(this BinaryReader stream) { return new Matrix(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } internal static byte[] ReadJsonBytes(this BinaryReader stream) { // ReadStream::ReadJson var engineBuild = stream.ReadInt32(); var size = stream.ReadInt32(); return stream.ReadBytes(size); } /// /// Deserializes object from Json by reading it as a raw data (ver+length+bytes). /// /// Reads version number, data length and actual data bytes from the stream. /// The stream. /// The object to deserialize. public static void ReadJson(this BinaryReader stream, ISerializable obj) { // ReadStream::ReadJson var engineBuild = stream.ReadInt32(); var size = stream.ReadInt32(); if (obj != null) { var data = stream.ReadBytes(size); Json.JsonSerializer.LoadFromBytes(obj, data, engineBuild); } else stream.BaseStream.Seek(size, SeekOrigin.Current); } /// /// Writes the color to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Color32 value) { stream.Write(value.R); stream.Write(value.G); stream.Write(value.B); stream.Write(value.A); } /// /// Writes the color to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Color value) { stream.Write(value.R); stream.Write(value.G); stream.Write(value.B); stream.Write(value.A); } /// /// Writes the Vector2 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Vector2 value) { stream.Write((float)value.X); stream.Write((float)value.Y); } /// /// Writes the Vector3 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Vector3 value) { stream.Write((float)value.X); stream.Write((float)value.Y); stream.Write((float)value.Z); } /// /// Writes the Vector4 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Vector4 value) { stream.Write((float)value.X); stream.Write((float)value.Y); stream.Write((float)value.Z); stream.Write((float)value.W); } /// /// Writes the Vector2 to the binary stream. /// /// The stream. /// The value to write. /// True to explicitly use 64-bit precision for serialized data, otherwise will use 32-bit precision. public static void Write(this BinaryWriter stream, Vector2 value, bool useDouble) { if (useDouble) { stream.Write((double)value.X); stream.Write((double)value.Y); } else { stream.Write((float)value.X); stream.Write((float)value.Y); } } /// /// Writes the Vector3 to the binary stream. /// /// The stream. /// The value to write. /// True to explicitly use 64-bit precision for serialized data, otherwise will use 32-bit precision. public static void Write(this BinaryWriter stream, Vector3 value, bool useDouble) { if (useDouble) { stream.Write((double)value.X); stream.Write((double)value.Y); stream.Write((double)value.Z); } else { stream.Write((float)value.X); stream.Write((float)value.Y); stream.Write((float)value.Z); } } /// /// Writes the Vector4 to the binary stream. /// /// The stream. /// The value to write. /// True to explicitly use 64-bit precision for serialized data, otherwise will use 32-bit precision. public static void Write(this BinaryWriter stream, Vector4 value, bool useDouble) { if (useDouble) { stream.Write((double)value.X); stream.Write((double)value.Y); stream.Write((double)value.Z); stream.Write((double)value.W); } else { stream.Write((float)value.X); stream.Write((float)value.Y); stream.Write((float)value.Z); stream.Write((float)value.W); } } /// /// Writes the Vector2 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Float2 value) { stream.Write(value.X); stream.Write(value.Y); } /// /// Writes the Vector3 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Float3 value) { stream.Write(value.X); stream.Write(value.Y); stream.Write(value.Z); } /// /// Writes the Vector4 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Float4 value) { stream.Write(value.X); stream.Write(value.Y); stream.Write(value.Z); stream.Write(value.W); } /// /// Writes the Vector2 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Double2 value) { stream.Write(value.X); stream.Write(value.Y); } /// /// Writes the Vector3 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Double3 value) { stream.Write(value.X); stream.Write(value.Y); stream.Write(value.Z); } /// /// Writes the Vector4 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Double4 value) { stream.Write(value.X); stream.Write(value.Y); stream.Write(value.Z); stream.Write(value.W); } /// /// Writes the Int2 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Int2 value) { stream.Write(value.X); stream.Write(value.Y); } /// /// Writes the Int3 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Int3 value) { stream.Write(value.X); stream.Write(value.Y); stream.Write(value.Z); } /// /// Writes the Int4 to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Int4 value) { stream.Write(value.X); stream.Write(value.Y); stream.Write(value.Z); stream.Write(value.W); } /// /// Writes the Quaternion to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Quaternion value) { stream.Write(value.X); stream.Write(value.Y); stream.Write(value.Z); stream.Write(value.W); } /// /// Writes the BoundingBox to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, BoundingBox value) { stream.Write(value.Minimum); stream.Write(value.Maximum); } /// /// Writes the BoundingSphere to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, BoundingSphere value) { stream.Write(value.Center); stream.Write(value.Radius); } /// /// Writes the Transform to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Transform value) { stream.Write(value.Translation); stream.Write(value.Orientation); stream.Write(value.Scale); } /// /// Writes the Rectangle to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Rectangle value) { stream.Write(value.Location); stream.Write(value.Size); } /// /// Writes the Ray to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Ray value) { stream.Write(value.Position); stream.Write(value.Direction); } /// /// Writes the Matrix to the binary stream. /// /// The stream. /// The value to write. public static void Write(this BinaryWriter stream, Matrix value) { stream.Write(value.M11); stream.Write(value.M12); stream.Write(value.M13); stream.Write(value.M14); stream.Write(value.M21); stream.Write(value.M22); stream.Write(value.M23); stream.Write(value.M24); stream.Write(value.M31); stream.Write(value.M32); stream.Write(value.M33); stream.Write(value.M34); stream.Write(value.M41); stream.Write(value.M42); stream.Write(value.M43); stream.Write(value.M44); } internal static void WriteJsonBytes(this BinaryWriter stream, byte[] bytes) { // WriteStream::WriteJson stream.Write(Globals.EngineBuildNumber); if (bytes != null) { stream.Write(bytes.Length); stream.Write(bytes); } else stream.Write(0); } /// /// Serializes object to Json and writes it as a raw data (ver+length+bytes). /// /// The stream. /// Writes version number, data length and actual data bytes to the stream. /// The object to serialize. public static void WriteJson(this BinaryWriter stream, ISerializable obj) { // WriteStream::WriteJson stream.Write(Globals.EngineBuildNumber); if (obj != null) { var bytes = Json.JsonSerializer.SaveToBytes(obj); stream.Write(bytes.Length); stream.Write(bytes); } else stream.Write(0); } /// /// Initializes the structure (value type) by inflating it with values from (recursive). /// /// The object to initialize. /// The structure type. public static void InitStructure(object obj, Type type) { var fields = type.GetFields(BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public); for (var i = 0; i < fields.Length; i++) { var field = fields[i]; var fieldType = field.FieldType; var attr = field.GetCustomAttribute(); if (attr != null) { var value = attr.Value; if (value != null && value.GetType() != fieldType) value = Convert.ChangeType(value, fieldType); field.SetValue(obj, value); } else if (fieldType.IsValueType) { var fieldValue = Activator.CreateInstance(fieldType); InitStructure(fieldValue, fieldType); field.SetValue(obj, fieldValue); } } } /// /// Gets the array of method parameter types. /// /// The method to get it's parameters. /// Method parameters array. public static Type[] GetParameterTypes(this MethodBase method) { Type[] parameterTypes; var parameters = method.GetParameters(); if (parameters.Length != 0) { parameterTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) parameterTypes[i] = parameters[i].ParameterType; } else parameterTypes = Array.Empty(); return parameterTypes; } /// /// A category of number values used for formatting and input fields. /// public enum ValueCategory { /// /// Nothing. /// None, /// /// Distance (eg. meters). /// Distance, /// /// Area (eg. m^2). /// Area, /// /// Volume (eg. m^3). /// Volume, /// /// Mass (eg. kilograms). /// Mass, /// /// Angle (eg. degrees). /// Angle, /// /// Speed (distance / time). /// Speed, /// /// Acceleration (distance^2 / time). /// Acceleration, /// /// Time (eg. seconds). /// Time, /// /// Force (mass * distance / time^2). /// Force, /// /// Torque (mass * distance^2 / time^2). /// Torque, } } }