// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
//
// Copyright (c) 2010-2012 SharpDX - Alexandre Mutel
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma warning disable SA1405 // Debug.Assert must provide message text
using att;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
namespace lib
{
///
/// Utility class.
///
public static class Util
{
/*
#if XENKO_PLATFORM_UWP
public static unsafe void CopyMemory(IntPtr dest, IntPtr src, int sizeInBytesToCopy)
{
Interop.memcpy((void*)dest, (void*)src, sizeInBytesToCopy);
}
#else
#if XENKO_PLATFORM_WINDOWS_DESKTOP
private const string MemcpyDll = "msvcrt.dll";
#elif XENKO_PLATFORM_ANDROID
private const string MemcpyDll = "libc.so";
#elif XENKO_PLATFORM_UNIX
// We do not specifiy the .so extension as libc.so on Linux
// is actually not a .so files but a script. Using just libc
// will automatically find the corresponding .so.
private const string MemcpyDll = "libc";
#elif XENKO_PLATFORM_IOS
private const string MemcpyDll = ObjCRuntime.Constants.SystemLibrary;
#else
# error Unsupported platform
#endif
[DllImport(MemcpyDll, EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
#if !XENKO_RUNTIME_CORECLR
[SuppressUnmanagedCodeSecurity]
#endif
private static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, ulong sizeInBytesToCopy);
///
/// Copy memory.
///
/// The destination memory location
/// The source memory location.
/// The count.
public static void CopyMemory(IntPtr dest, IntPtr src, int sizeInBytesToCopy)
{
CopyMemory(dest, src, (ulong)sizeInBytesToCopy);
}
#endif
*/
public static void checkAndAddDirectory( string path )
{
if( !Directory.Exists( path ) )
{
log.info( $"Creating directory {path}" );
Directory.CreateDirectory( path );
}
else
{
log.debug( $"{path} already exists." );
}
}
///
/// Compares two block of memory.
///
/// The pointer to compare from.
/// The pointer to compare against.
/// The size in bytes to compare.
/// True if the buffers are equivalent, false otherwise.
public static unsafe bool CompareMemory( IntPtr from, IntPtr against, int sizeToCompare )
{
var pSrc = (byte*)from;
var pDst = (byte*)against;
// Compare 8 bytes.
var numberOf = sizeToCompare >> 3;
while( numberOf > 0 )
{
if( *(long*)pSrc != *(long*)pDst )
return false;
pSrc += 8;
pDst += 8;
numberOf--;
}
// Compare remaining bytes.
numberOf = sizeToCompare & 7;
while( numberOf > 0 )
{
if( *pSrc != *pDst )
return false;
pSrc++;
pDst++;
numberOf--;
}
return true;
}
///
/// Clears the memory.
///
/// The dest.
/// The value.
/// The size in bytes to clear.
public static void ClearMemory( IntPtr dest, byte value, int sizeInBytesToClear )
{
unsafe
{
Interop.memset( (void*)dest, value, sizeInBytesToClear );
}
}
///
/// Return the sizeof a struct from a CLR. Equivalent to sizeof operator but works on generics too.
///
/// a struct to evaluate
/// sizeof this struct
public static int SizeOf() where T : struct
{
return Interop.SizeOf();
}
///
/// Return the sizeof an array of struct. Equivalent to sizeof operator but works on generics too.
///
/// a struct
/// The array of struct to evaluate.
/// sizeof in bytes of this array of struct
public static int SizeOf( T[] array ) where T : struct
{
return array == null ? 0 : array.Length * Interop.SizeOf();
}
///
/// Pins the specified source and call an action with the pinned pointer.
///
/// The type of the structure to pin
/// The source.
/// The pin action to perform on the pinned pointer.
public static void Pin( ref T source, Action pinAction ) where T : struct
{
unsafe
{
pinAction( (IntPtr)Interop.Fixed( ref source ) );
}
}
///
/// Pins the specified source and call an action with the pinned pointer.
///
/// The type of the structure to pin
/// The source array.
/// The pin action to perform on the pinned pointer.
public static void Pin( T[] source, [NotNull] Action pinAction ) where T : struct
{
unsafe
{
pinAction( source == null ? IntPtr.Zero : (IntPtr)Interop.Fixed( source ) );
}
}
///
/// Covnerts a structured array to an equivalent byte array.
///
/// The source.
/// The byte array.
public static byte[] ToByteArray( T[] source ) where T : struct
{
if( source == null )
return null;
var buffer = new byte[SizeOf() * source.Length];
if( source.Length == 0 )
return buffer;
unsafe
{
fixed( void* pBuffer = buffer )
Interop.Write( pBuffer, source, 0, source.Length );
}
return buffer;
}
///
/// Reads the specified T data from a memory location.
///
/// Type of a data to read
/// Memory location to read from.
/// The data read from the memory location
public static T Read( IntPtr source ) where T : struct
{
unsafe
{
return Interop.ReadInline( (void*)source );
}
}
///
/// Reads the specified T data from a memory location.
///
/// Type of a data to read
/// Memory location to read from.
/// The data write to.
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static void Read( IntPtr source, ref T data ) where T : struct
{
unsafe
{
Interop.CopyInline( ref data, (void*)source );
}
}
///
/// Reads the specified T data from a memory location.
///
/// Type of a data to read
/// Memory location to read from.
/// The data write to.
public static void ReadOut( IntPtr source, out T data ) where T : struct
{
unsafe
{
Interop.CopyInlineOut( out data, (void*)source );
}
}
///
/// Reads the specified T data from a memory location.
///
/// Type of a data to read
/// Memory location to read from.
/// The data write to.
/// source pointer + sizeof(T)
public static IntPtr ReadAndPosition( IntPtr source, ref T data ) where T : struct
{
unsafe
{
return (IntPtr)Interop.Read( (void*)source, ref data );
}
}
///
/// Reads the specified array T[] data from a memory location.
///
/// Type of a data to read
/// Memory location to read from.
/// The data write to.
/// The offset in the array to write to.
/// The number of T element to read from the memory location
/// source pointer + sizeof(T) * count
public static IntPtr Read( IntPtr source, T[] data, int offset, int count ) where T : struct
{
unsafe
{
return (IntPtr)Interop.Read( (void*)source, data, offset, count );
}
}
///
/// Writes the specified T data to a memory location.
///
/// Type of a data to write
/// Memory location to write to.
/// The data to write.
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static void Write( IntPtr destination, ref T data ) where T : struct
{
unsafe
{
Interop.CopyInline( (void*)destination, ref data );
}
}
///
/// Writes the specified T data to a memory location.
///
/// Type of a data to write
/// Memory location to write to.
/// The data to write.
/// destination pointer + sizeof(T)
public static IntPtr WriteAndPosition( IntPtr destination, ref T data ) where T : struct
{
unsafe
{
return (IntPtr)Interop.Write( (void*)destination, ref data );
}
}
///
/// Writes the specified array T[] data to a memory location.
///
/// Type of a data to write
/// Memory location to write to.
/// The array of T data to write.
/// The offset in the array to read from.
/// The number of T element to write to the memory location
public static void Write( byte[] destination, T[] data, int offset, int count ) where T : struct
{
unsafe
{
fixed( void* pDest = destination )
{
Write( (IntPtr)pDest, data, offset, count );
}
}
}
///
/// Writes the specified array T[] data to a memory location.
///
/// Type of a data to write
/// Memory location to write to.
/// The array of T data to write.
/// The offset in the array to read from.
/// The number of T element to write to the memory location
/// destination pointer + sizeof(T) * count
public static IntPtr Write( IntPtr destination, T[] data, int offset, int count ) where T : struct
{
unsafe
{
return (IntPtr)Interop.Write( (void*)destination, data, offset, count );
}
}
///
/// Allocate an aligned memory buffer.
///
/// Size of the buffer to allocate.
/// Alignment, a positive value which is a power of 2. 16 bytes by default.
/// A pointer to a buffer aligned.
///
/// To free this buffer, call
///
public static unsafe IntPtr AllocateMemory( int sizeInBytes, int align = 16 )
{
var mask = align - 1;
if( ( align & mask ) != 0 )
{
throw new ArgumentException( "Alignment is not power of 2", nameof( align ) );
}
var memPtr = Marshal.AllocHGlobal(sizeInBytes + mask + sizeof(void*));
var ptr = (byte*)((ulong)(memPtr.ToInt32() + sizeof(void*) + mask) & ~(ulong)mask);
( (IntPtr*)ptr )[-1] = memPtr;
return new IntPtr( ptr );
}
///
/// Allocate an aligned memory buffer and clear it with a specified value (0 by defaault).
///
/// Size of the buffer to allocate.
/// Default value used to clear the buffer.
/// Alignment, 16 bytes by default.
/// A pointer to a buffer aligned.
///
/// To free this buffer, call
///
public static IntPtr AllocateClearedMemory( int sizeInBytes, byte clearValue = 0, int align = 16 )
{
var ptr = AllocateMemory(sizeInBytes, align);
ClearMemory( ptr, clearValue, sizeInBytes );
return ptr;
}
///
/// Determines whether the specified memory pointer is aligned in memory.
///
/// The memory pointer.
/// The align.
/// true if the specified memory pointer is aligned in memory; otherwise, false.
public static bool IsMemoryAligned( IntPtr memoryPtr, int align = 16 )
{
return ( memoryPtr.ToInt64() & ( align - 1 ) ) == 0;
}
///
/// Allocate an aligned memory buffer.
///
///
/// The buffer must have been allocated with
///
public static unsafe void FreeMemory( IntPtr alignedBuffer )
{
Marshal.FreeHGlobal( ( (IntPtr*)alignedBuffer )[-1] );
}
///
/// If non-null, disposes the specified object and set it to null, otherwise do nothing.
///
/// The disposable.
public static void Dispose( ref T disposable ) where T : class, IDisposable
{
if( disposable != null )
{
disposable.Dispose();
disposable = null;
}
}
///
/// String helper join method to display an array of object as a single string.
///
/// The separator.
/// The array.
/// a string with array elements serparated by the seperator
[NotNull]
public static string Join( string separator, T[] array )
{
var text = new StringBuilder();
if( array != null )
{
for( var i = 0; i < array.Length; i++ )
{
if( i > 0 )
text.Append( separator );
text.Append( array[i] );
}
}
return text.ToString();
}
///
/// String helper join method to display an enumrable of object as a single string.
///
/// The separator.
/// The enumerable.
/// a string with array elements serparated by the seperator
[NotNull]
public static string Join( string separator, [NotNull] IEnumerable elements )
{
var elementList = new List();
foreach( var element in elements )
elementList.Add( element.ToString() );
var text = new StringBuilder();
for( var i = 0; i < elementList.Count; i++ )
{
var element = elementList[i];
if( i > 0 )
text.Append( separator );
text.Append( element );
}
return text.ToString();
}
///
/// String helper join method to display an enumrable of object as a single string.
///
/// The separator.
/// The enumerable.
/// a string with array elements serparated by the seperator
[NotNull]
public static string Join( string separator, [NotNull] IEnumerator elements )
{
var elementList = new List();
while( elements.MoveNext() )
elementList.Add( elements.Current.ToString() );
var text = new StringBuilder();
for( var i = 0; i < elementList.Count; i++ )
{
var element = elementList[i];
if( i > 0 )
text.Append( separator );
text.Append( element );
}
return text.ToString();
}
///
/// Read stream to a byte[] buffer
///
/// input stream
/// a byte[] buffer
[NotNull]
public static byte[] ReadStream( [NotNull] Stream stream )
{
var readLength = 0;
return ReadStream( stream, ref readLength );
}
///
/// Read stream to a byte[] buffer
///
/// input stream
/// length to read
/// a byte[] buffer
[NotNull]
public static byte[] ReadStream( [NotNull] Stream stream, ref int readLength )
{
System.Diagnostics.Debug.Assert( stream != null );
System.Diagnostics.Debug.Assert( stream.CanRead );
var num = readLength;
System.Diagnostics.Debug.Assert( num <= ( stream.Length - stream.Position ) );
if( num == 0 )
readLength = (int)( stream.Length - stream.Position );
num = readLength;
System.Diagnostics.Debug.Assert( num >= 0 );
if( num == 0 )
return new byte[0];
var buffer = new byte[num];
var bytesRead = 0;
if( num > 0 )
{
do
{
bytesRead += stream.Read( buffer, bytesRead, readLength - bytesRead );
} while( bytesRead < readLength );
}
return buffer;
}
///
/// Computes a hashcode for a dictionary.
///
/// Hashcode for the list.
public static int GetHashCode( IDictionary dict )
{
if( dict == null )
return 0;
var hashCode = 0;
foreach( DictionaryEntry keyValue in dict )
{
hashCode = ( hashCode * 397 ) ^ keyValue.Key.GetHashCode();
hashCode = ( hashCode * 397 ) ^ ( keyValue.Value?.GetHashCode() ?? 0 );
}
return hashCode;
}
///
/// Computes a hashcode for an enumeration
///
/// An enumerator.
/// Hashcode for the list.
public static int GetHashCode( IEnumerable it )
{
if( it == null )
return 0;
var hashCode = 0;
foreach( var current in it )
{
hashCode = ( hashCode * 397 ) ^ ( current?.GetHashCode() ?? 0 );
}
return hashCode;
}
///
/// Computes a hashcode for an enumeration
///
/// An enumerator.
/// Hashcode for the list.
public static int GetHashCode( IEnumerator it )
{
if( it == null )
return 0;
var hashCode = 0;
while( it.MoveNext() )
{
var current = it.Current;
hashCode = ( hashCode * 397 ) ^ ( current?.GetHashCode() ?? 0 );
}
return hashCode;
}
///
/// Compares two collection, element by elements.
///
/// A "from" enumerator.
/// A "to" enumerator.
/// True if lists are identical. False otherwise.
public static bool Compare( IEnumerable left, IEnumerable right )
{
if( ReferenceEquals( left, right ) )
return true;
if( ReferenceEquals( left, null ) || ReferenceEquals( right, null ) )
return false;
return Compare( left.GetEnumerator(), right.GetEnumerator() );
}
///
/// Compares two collection, element by elements.
///
/// A "from" enumerator.
/// A "to" enumerator.
/// True if lists are identical. False otherwise.
public static bool Compare( IEnumerator leftIt, IEnumerator rightIt )
{
if( ReferenceEquals( leftIt, rightIt ) )
return true;
if( ReferenceEquals( leftIt, null ) || ReferenceEquals( rightIt, null ) )
return false;
bool hasLeftNext;
bool hasRightNext;
while( true )
{
hasLeftNext = leftIt.MoveNext();
hasRightNext = rightIt.MoveNext();
if( !hasLeftNext || !hasRightNext )
break;
if( !Equals( leftIt.Current, rightIt.Current ) )
return false;
}
// If there is any left element
if( hasLeftNext != hasRightNext )
return false;
return true;
}
///
/// Compares two collection, element by elements.
///
/// The collection to compare from.
/// The colllection to compare to.
/// True if lists are identical (but no necessarely of the same time). False otherwise.
public static bool Compare( IDictionary first, IDictionary second )
{
if( ReferenceEquals( first, second ) )
return true;
if( ReferenceEquals( first, null ) || ReferenceEquals( second, null ) )
return false;
if( first.Count != second.Count )
return false;
var comparer = EqualityComparer.Default;
foreach( var keyValue in first )
{
TValue secondValue;
if( !second.TryGetValue( keyValue.Key, out secondValue ) )
return false;
if( !comparer.Equals( keyValue.Value, secondValue ) )
return false;
}
// Check that all keys in second are in first
return second.Keys.All( first.ContainsKey );
}
public static bool Compare( T[] left, T[] right )
{
if( ReferenceEquals( left, right ) )
return true;
if( ReferenceEquals( left, null ) || ReferenceEquals( right, null ) )
return false;
if( left.Length != right.Length )
return false;
var comparer = EqualityComparer.Default;
for( var i = 0; i < left.Length; ++i )
{
if( !comparer.Equals( left[i], right[i] ) )
return false;
}
return true;
}
///
/// Compares two collection, element by elements.
///
/// The collection to compare from.
/// The colllection to compare to.
/// True if lists are identical (but no necessarely of the same time). False otherwise.
public static bool Compare( ICollection left, ICollection right )
{
if( ReferenceEquals( left, right ) )
return true;
if( ReferenceEquals( left, null ) || ReferenceEquals( right, null ) )
return false;
if( left.Count != right.Count )
return false;
var count = 0;
var leftIt = left.GetEnumerator();
var rightIt = right.GetEnumerator();
var comparer = EqualityComparer.Default;
while( leftIt.MoveNext() && rightIt.MoveNext() )
{
if( !comparer.Equals( leftIt.Current, rightIt.Current ) )
return false;
count++;
}
// Just double check to make sure that the iterator actually returns
// the exact number of elements
if( count != left.Count )
return false;
return true;
}
///
/// Swaps the value between two references.
///
/// Type of a data to swap.
/// The left value.
/// The right value.
public static void Swap( ref T left, ref T right )
{
var temp = left;
left = right;
right = temp;
}
///
/// Suspends current thread for a .
///
/// The duration of sleep.
public static void Sleep( TimeSpan sleepTime )
{
var ms = (long)sleepTime.TotalMilliseconds;
if( ms < 0 || ms > int.MaxValue )
{
throw new ArgumentOutOfRangeException( nameof( sleepTime ), "Sleep time must be a duration less than '2^31 - 1' milliseconds." );
}
// MH PORTED NativeInvoke.Sleep((int)ms);
Thread.Sleep( (int)ms );
}
///
/// Suspends current thread for a .
///
/// The duration of sleep in milliseconds.
public static void Sleep( int sleepTimeInMillis )
{
// MH PORTED NativeInvoke.Sleep(sleepTimeInMillis);
Thread.Sleep( sleepTimeInMillis );
}
///
/// Writes the specified T data to a memory location.
///
/// Type of a data to write
/// Memory location to write to.
/// The data to write.
internal static void UnsafeWrite( IntPtr destination, ref T data )
{
unsafe
{
Interop.CopyInline( (void*)destination, ref data );
}
}
///
/// Reads the specified T data from a memory location.
///
/// Type of a data to read
/// Memory location to read from.
/// The data write to.
internal static void UnsafeReadOut( IntPtr source, out T data )
{
unsafe
{
Interop.CopyInlineOut( out data, (void*)source );
}
}
///
/// Return the sizeof a struct from a CLR. Equivalent to sizeof operator but works on generics too.
///
/// a struct to evaluate
/// sizeof this struct
internal static int UnsafeSizeOf()
{
return Interop.SizeOf();
}
///
/// Linq assisted full tree iteration and collection in a single line.
/// Warning, could be slow.
///
/// The type to iterate.
/// The root item
/// The function to retrieve a child
public static IEnumerable IterateTree( T root, Func> childrenF )
{
var q = new List { root };
while( q.Any() )
{
var c = q[0];
q.RemoveAt( 0 );
q.AddRange( childrenF( c ) ?? Enumerable.Empty() );
yield return c;
}
}
///
/// Converts a raw time to a .
///
/// The delta.
/// The .
public static TimeSpan ConvertRawToTimestamp( long delta )
{
return new TimeSpan( delta == 0 ? 0 : ( delta * TimeSpan.TicksPerSecond ) / Stopwatch.Frequency );
}
}
}