Viewing is open to everyone. Downloading and starring need a GitHub sign-in.
Sign in with GitHub
Native.cs
v0.1.0-build.85
Latest
Commit ede38f5
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | using System; |
| 2 | using System.Text; |
| 3 | |
| 4 | namespace OpenCKey.Interop |
| 5 | { |
| 6 | /// <summary>Small helpers shared by the wrappers: ABI check, status checks, C strings.</summary> |
| 7 | internal static unsafe class Native |
| 8 | { |
| 9 | private static readonly object Gate = new object(); |
| 10 | private static volatile bool _abiChecked; |
| 11 | |
| 12 | /// <summary> |
| 13 | /// Verifies, once, that the loaded native library speaks the ABI these |
| 14 | /// struct mirrors were written for. A mismatched library would read |
| 15 | /// the structs at the wrong offsets, so it is refused outright. |
| 16 | /// </summary> |
| 17 | internal static void EnsureAbi() |
| 18 | { |
| 19 | if (_abiChecked) return; |
| 20 | lock (Gate) |
| 21 | { |
| 22 | if (_abiChecked) return; |
| 23 | uint abi; |
| 24 | try |
| 25 | { |
| 26 | abi = NativeMethods.ckey_get_abi_version(); |
| 27 | } |
| 28 | catch (DllNotFoundException e) |
| 29 | { |
| 30 | throw new CKeyException(CKeyStatus.InternalError, |
| 31 | "The native ckey library was not found. The CKey package ships it for win-x64, linux-x64, " + |
| 32 | "osx-x64 and osx-arm64; for other platforms build sdk/dotnet/native with CMake. " + e.Message); |
| 33 | } |
| 34 | if (abi != NativeConstants.AbiVersion) |
| 35 | { |
| 36 | throw new CKeyException(CKeyStatus.InternalError, |
| 37 | $"The native ckey library has ABI version {abi}; this CKey assembly requires {NativeConstants.AbiVersion}."); |
| 38 | } |
| 39 | _abiChecked = true; |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | internal static void Check(int status, string what) |
| 44 | { |
| 45 | if (status != 0) throw new CKeyException((CKeyStatus)status, $"{what} failed: {(CKeyStatus)status}."); |
| 46 | } |
| 47 | |
| 48 | internal static void Check(int status, string what, ref NativeDiagnostics diagnostics) |
| 49 | { |
| 50 | if (status == 0) return; |
| 51 | string message = Message(ref diagnostics); |
| 52 | throw new CKeyException((CKeyStatus)status, |
| 53 | string.IsNullOrEmpty(message) ? $"{what} failed: {(CKeyStatus)status}." : $"{what} failed: {message}"); |
| 54 | } |
| 55 | |
| 56 | internal static string Message(ref NativeDiagnostics diagnostics) |
| 57 | { |
| 58 | fixed (byte* m = diagnostics.Message) return FromUtf8Z(m, NativeConstants.DiagnosticsMessageBytes) ?? string.Empty; |
| 59 | } |
| 60 | |
| 61 | /// <summary>Reads a NUL-terminated UTF-8 string of at most max bytes; null for a null pointer.</summary> |
| 62 | internal static string? FromUtf8Z(byte* p, int max = int.MaxValue) |
| 63 | { |
| 64 | if (p == null) return null; |
| 65 | int n = 0; |
| 66 | while (n < max && p[n] != 0) n++; |
| 67 | return n == 0 ? string.Empty : Encoding.UTF8.GetString(p, n); |
| 68 | } |
| 69 | |
| 70 | /// <summary>UTF-8 bytes with a terminating NUL; null for null.</summary> |
| 71 | internal static byte[]? ToUtf8Z(string? s) |
| 72 | { |
| 73 | if (s == null) return null; |
| 74 | int n = Encoding.UTF8.GetByteCount(s); |
| 75 | var bytes = new byte[n + 1]; |
| 76 | Encoding.UTF8.GetBytes(s, 0, s.Length, bytes, 0); |
| 77 | return bytes; |
| 78 | } |
| 79 | |
| 80 | internal static int ToInt32Length(nuint length, string what) |
| 81 | { |
| 82 | if (length > int.MaxValue) throw new CKeyException(CKeyStatus.InsufficientBuffer, $"{what} needs more than 2 GiB."); |
| 83 | return (int)length; |
| 84 | } |
| 85 | } |
| 86 | } |