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.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); } }