mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-01 10:58:29 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
238 lines
5.3 KiB
C#
238 lines
5.3 KiB
C#
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
|
|
namespace Sandbox;
|
|
|
|
internal unsafe static partial class Interop
|
|
{
|
|
private static Logger log = Logging.GetLogger();
|
|
|
|
[SkipHotload] static List<PassBackString> FrameAllocatedStrings = new();
|
|
|
|
public static int Free()
|
|
{
|
|
int i = 0;
|
|
|
|
foreach ( var entry in FrameAllocatedStrings )
|
|
{
|
|
entry.Free();
|
|
i++;
|
|
}
|
|
|
|
FrameAllocatedStrings.Clear();
|
|
|
|
return i;
|
|
}
|
|
|
|
const int maxNativeString = 1024 * 1024 * 64; // a 64mb string sounds sensible!
|
|
|
|
/// <summary>
|
|
/// Convert a native utf pointer to a string
|
|
/// </summary>
|
|
public static string GetString( IntPtr pointer )
|
|
{
|
|
if ( pointer == IntPtr.Zero )
|
|
return null;
|
|
|
|
int length = GetUtf8Length( (byte*)pointer, maxNativeString );
|
|
|
|
if ( length < 0 )
|
|
{
|
|
Log.Warning( "Really long, or really invalid string detected" );
|
|
return null;
|
|
}
|
|
|
|
return GetString( pointer, length );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a native utf pointer to a string
|
|
/// </summary>
|
|
public static string GetString( IntPtr pointer, int byteLen )
|
|
{
|
|
if ( pointer == IntPtr.Zero || byteLen < 0 )
|
|
return null;
|
|
|
|
if ( byteLen == 0 )
|
|
return string.Empty;
|
|
|
|
return Encoding.UTF8.GetString( (byte*)pointer, byteLen );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the length of a null-terminated UTF-8 string using AVX2 (fallback to scalar if unavailable)
|
|
/// </summary>
|
|
[MethodImpl( MethodImplOptions.AggressiveInlining )]
|
|
private static int GetUtf8Length( byte* ptr, int maxLen )
|
|
{
|
|
byte* start = ptr;
|
|
int length = 0;
|
|
|
|
while ( length < maxLen && ptr[length] != 0 )
|
|
length++;
|
|
|
|
return length >= maxLen ? -1 : length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a native utf pointer to a string
|
|
/// </summary>
|
|
public static string GetWString( IntPtr pointer )
|
|
{
|
|
if ( pointer == IntPtr.Zero )
|
|
return null;
|
|
|
|
return Marshal.PtrToStringUni( pointer );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a native utf pointer to a string
|
|
/// </summary>
|
|
public static string GetWString( IntPtr pointer, int byteLen )
|
|
{
|
|
if ( pointer == IntPtr.Zero || byteLen <= 0 )
|
|
return null;
|
|
|
|
return Marshal.PtrToStringUni( pointer, byteLen );
|
|
}
|
|
|
|
public unsafe ref struct InteropString
|
|
{
|
|
public IntPtr Pointer;
|
|
|
|
public InteropString( string str )
|
|
{
|
|
if ( str is null )
|
|
return;
|
|
|
|
uint nb = (uint)Encoding.UTF8.GetByteCount( str );
|
|
byte* mem = (byte*)NativeMemory.Alloc( nb + 1 );
|
|
|
|
fixed ( char* src = str )
|
|
{
|
|
Encoding.UTF8.GetBytes( src, str.Length, mem, (int)nb );
|
|
}
|
|
|
|
mem[nb] = 0;
|
|
Pointer = (IntPtr)mem;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
NativeMemory.Free( (void*)Pointer );
|
|
Pointer = default;
|
|
}
|
|
}
|
|
|
|
public unsafe ref struct InteropWString
|
|
{
|
|
public IntPtr Pointer;
|
|
|
|
public InteropWString( string str )
|
|
{
|
|
if ( str is null )
|
|
return;
|
|
|
|
uint nb = (uint)Encoding.Unicode.GetByteCount( str );
|
|
byte* mem = (byte*)NativeMemory.Alloc( nb + 2 );
|
|
|
|
fixed ( char* src = str )
|
|
{
|
|
Encoding.Unicode.GetBytes( src, str.Length, mem, (int)nb );
|
|
}
|
|
|
|
*(ushort*)(mem + nb) = 0;
|
|
Pointer = (IntPtr)mem;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
NativeMemory.Free( (void*)Pointer );
|
|
Pointer = default;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the binding system to log an exception when calling a binding
|
|
/// </summary>
|
|
public static void BindingException( string ClassName, string MethodName, Exception e )
|
|
{
|
|
try
|
|
{
|
|
log.Error( e, e.Message );
|
|
}
|
|
catch ( Exception e2 )
|
|
{
|
|
System.Diagnostics.Debug.WriteLine( "Exception thrown when logging exception: {0}", e2 );
|
|
System.Diagnostics.Debug.WriteLine( "Original exception: {0}", e );
|
|
}
|
|
}
|
|
|
|
internal static void NativeAssemblyLoadFailed( string libraryName )
|
|
{
|
|
string errorMessage = $"Failed to load native library '{libraryName}'. Error Code: {Marshal.GetLastWin32Error()}/{Marshal.GetLastSystemError()}";
|
|
|
|
throw new NativeAssemblyLoadException( errorMessage, Marshal.GetLastWin32Error() );
|
|
}
|
|
|
|
/// <summary>
|
|
/// used to pass a string back to native
|
|
/// </summary>
|
|
public unsafe struct PassBackString
|
|
{
|
|
public IntPtr Pointer;
|
|
|
|
public PassBackString( string str )
|
|
{
|
|
if ( str is null )
|
|
return;
|
|
|
|
uint nb = (uint)Encoding.UTF8.GetByteCount( str );
|
|
byte* mem = (byte*)NativeMemory.Alloc( nb + 1 );
|
|
|
|
fixed ( char* src = str )
|
|
{
|
|
Encoding.UTF8.GetBytes( src, str.Length, mem, (int)nb );
|
|
}
|
|
|
|
mem[nb] = 0;
|
|
Pointer = (IntPtr)mem;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
NativeMemory.Free( (void*)Pointer );
|
|
Pointer = default;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is called when native calls a managed function and it returns a string. In this case
|
|
/// we can't free the string immediately, so we store it in a list and free it at the end of the frame.
|
|
/// This has potential to crash, if we free the string before the thread uses it but this would be super
|
|
/// rare and the other option is to never return strings like this.
|
|
/// </summary>
|
|
internal static IntPtr GetTemporaryStringPointerForNative( string str )
|
|
{
|
|
var f = new PassBackString( str );
|
|
FrameAllocatedStrings.Add( f );
|
|
return f.Pointer;
|
|
}
|
|
}
|
|
|
|
internal class NativeAssemblyLoadException : Exception
|
|
{
|
|
public int ErrorCode { get; }
|
|
|
|
public NativeAssemblyLoadException( string message, int errorCode ) : base( message )
|
|
{
|
|
ErrorCode = errorCode;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"{base.ToString()}, ErrorCode: {ErrorCode}";
|
|
}
|
|
}
|