diff --git a/MediaBrowser.MediaEncoding/Encoder/ApplePlatformHelper.cs b/MediaBrowser.MediaEncoding/Encoder/ApplePlatformHelper.cs index a8ff58b091..baea0df8cc 100644 --- a/MediaBrowser.MediaEncoding/Encoder/ApplePlatformHelper.cs +++ b/MediaBrowser.MediaEncoding/Encoder/ApplePlatformHelper.cs @@ -1,9 +1,11 @@ #pragma warning disable CA1031 using System; +using System.Buffers; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using System.Text; using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Encoder; @@ -12,43 +14,43 @@ namespace MediaBrowser.MediaEncoding.Encoder; /// Helper class for Apple platform specific operations. /// [SupportedOSPlatform("macos")] -public static class ApplePlatformHelper +public static partial class ApplePlatformHelper { private static readonly string[] _av1DecodeBlacklistedCpuClass = ["M1", "M2"]; - private static string GetSysctlValue(ReadOnlySpan name) + internal static string GetSysctlValue(string name) { - IntPtr length = IntPtr.Zero; + nuint length = 0; // Get length of the value - int osStatus = SysctlByName(name, IntPtr.Zero, ref length, IntPtr.Zero, 0); - - if (osStatus != 0) + int osStatus = sysctlbyname(name, Span.Empty, ref length, IntPtr.Zero, 0); + if (osStatus != 0 || length == 0) { - throw new NotSupportedException($"Failed to get sysctl value for {System.Text.Encoding.UTF8.GetString(name)} with error {osStatus}"); + throw new NotSupportedException($"Failed to get sysctl value for {name} with error {osStatus}"); } - IntPtr buffer = Marshal.AllocHGlobal(length.ToInt32()); + byte[] buffer = ArrayPool.Shared.Rent((int)length); try { - osStatus = SysctlByName(name, buffer, ref length, IntPtr.Zero, 0); + osStatus = sysctlbyname(name, buffer.AsSpan()[..(int)length], ref length, IntPtr.Zero, 0); if (osStatus != 0) { - throw new NotSupportedException($"Failed to get sysctl value for {System.Text.Encoding.UTF8.GetString(name)} with error {osStatus}"); + throw new NotSupportedException($"Failed to get sysctl value for {name} with error {osStatus}"); } - return Marshal.PtrToStringAnsi(buffer) ?? string.Empty; + if (length < 1) + { + return string.Empty; + } + + ReadOnlySpan data = buffer.AsSpan()[..(int)(length - 1)]; + return Encoding.UTF8.GetString(data); } finally { - Marshal.FreeHGlobal(buffer); + ArrayPool.Shared.Return(buffer); } } - private static int SysctlByName(ReadOnlySpan name, IntPtr oldp, ref IntPtr oldlenp, IntPtr newp, uint newlen) - { - return NativeMethods.SysctlByName(name.ToArray(), oldp, ref oldlenp, newp, newlen); - } - /// /// Check if the current system has hardware acceleration for AV1 decoding. /// @@ -63,7 +65,7 @@ public static class ApplePlatformHelper try { - string cpuBrandString = GetSysctlValue("machdep.cpu.brand_string"u8); + string cpuBrandString = GetSysctlValue("machdep.cpu.brand_string"); return !_av1DecodeBlacklistedCpuClass.Any(blacklistedCpuClass => cpuBrandString.Contains(blacklistedCpuClass, StringComparison.OrdinalIgnoreCase)); } catch (NotSupportedException e) @@ -78,10 +80,7 @@ public static class ApplePlatformHelper return false; } - private static class NativeMethods - { - [DllImport("libc", EntryPoint = "sysctlbyname", SetLastError = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - internal static extern int SysctlByName(byte[] name, IntPtr oldp, ref IntPtr oldlenp, IntPtr newp, uint newlen); - } + [LibraryImport("libc", EntryPoint = "sysctlbyname", SetLastError = true)] + [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] + internal static partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, Span oldp, ref nuint oldlenp, IntPtr newp, nuint newlen); } diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index fc11047a7f..288cd3e189 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -9,6 +9,7 @@ net10.0 false true + true diff --git a/tests/Jellyfin.MediaEncoding.Tests/Encoder/ApplePlatformHelperTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Encoder/ApplePlatformHelperTests.cs new file mode 100644 index 0000000000..9847acbb0a --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Encoder/ApplePlatformHelperTests.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.Versioning; +using MediaBrowser.MediaEncoding.Encoder; +using Xunit; + +namespace Jellyfin.MediaEncoding.Tests; + +[SupportedOSPlatform("macos")] +public class ApplePlatformHelperTests +{ + [Fact] + public void GetSysctlValue_CpuBrand_NotEmpty() + { + Assert.SkipUnless(OperatingSystem.IsMacOS(), "macOS-only test"); + + var value = ApplePlatformHelper.GetSysctlValue("machdep.cpu.brand_string"); + Assert.NotEmpty(value); + + // Make sure we don't include the null terminator + Assert.DoesNotContain("\0", value, StringComparison.Ordinal); + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index c06279af2d..6cadfacce8 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace Jellyfin.Server.Implementations.Tests.IO; -public class ManagedFileSystemTests +public partial class ManagedFileSystemTests { private readonly IFixture _fixture; private readonly ManagedFileSystem _sut; @@ -117,7 +117,7 @@ public class ManagedFileSystemTests } [SuppressMessage("Naming Rules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Have to")] - [DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)] + [LibraryImport("libc", SetLastError = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)] - private static extern int symlink(string target, string linkpath); + private static partial int symlink([MarshalAs(UnmanagedType.LPStr)] string target, [MarshalAs(UnmanagedType.LPStr)] string linkpath); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 958ffb8b6e..29de52a2ba 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -4,6 +4,7 @@ {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} Exe + true