Files
DotNetAlpm/Alpm/Interop/CFormatter.cs
2020-05-01 05:08:04 -04:00

88 lines
3.2 KiB
C#

using System;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Text;
namespace Foodsoft.Alpm.Interop
{
internal static class CFormatter
{
private static readonly int vaListCopyNWords;
private static readonly Vsnprintf? vsnprintfFunc;
static CFormatter()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD))
{
vaListCopyNWords = RuntimeInformation.ProcessArchitecture switch
{
Architecture.X86 => 1,
Architecture.X64 => 3,
Architecture.Arm => 1,
Architecture.Arm64 => 4,
_ => vaListCopyNWords
};
vsnprintfFunc = vsnprintfLibc;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
vaListCopyNWords = 1;
vsnprintfFunc = vsnprintfUCrt;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
vaListCopyNWords = RuntimeInformation.ProcessArchitecture switch
{
Architecture.X86 => 1,
Architecture.X64 => 3,
Architecture.Arm => 1,
Architecture.Arm64 => 1,
_ => vaListCopyNWords
};
vsnprintfFunc = vsnprintfLibc;
}
}
internal static unsafe string Format(IntPtr format, IntPtr vaList)
{
const int bufSize = 256;
if (vsnprintfFunc == null || vaListCopyNWords <= 0)
return "[Formatter unsupported for platform]";
var vaListBuf = (void**) vaList;
var vaListCopy = stackalloc void*[vaListCopyNWords];
for (var i = 0; i < vaListCopyNWords; i++) vaListCopy[i] = vaListBuf[i];
var strBuf = stackalloc byte[bufSize];
var strBufPtr = (IntPtr) strBuf;
var nbNeed = vsnprintfFunc(strBufPtr, (UIntPtr) bufSize, format, vaList);
if (nbNeed < bufSize)
return Marshal.PtrToStringUTF8(strBufPtr, nbNeed);
var pool = ArrayPool<byte>.Shared;
var buffer = pool.Rent(nbNeed + 1);
fixed (byte* pb = buffer)
{
nbNeed = vsnprintfFunc((IntPtr) pb, (UIntPtr) buffer.Length, format, (IntPtr) vaListCopy);
}
var result = Encoding.UTF8.GetString(buffer, 0, nbNeed);
pool.Return(buffer);
return result;
}
private delegate int Vsnprintf(IntPtr buffer, UIntPtr size, IntPtr format, IntPtr args);
[DllImport("libc", EntryPoint = "vsnprintf", CallingConvention = CallingConvention.Cdecl)]
internal static extern int vsnprintfLibc(IntPtr buffer, UIntPtr size, IntPtr format, IntPtr args);
[DllImport("ucrt", EntryPoint = "vsnprintf", CallingConvention = CallingConvention.Cdecl)]
internal static extern int vsnprintfUCrt(IntPtr buffer, UIntPtr size, IntPtr format, IntPtr args);
}
}