Checkpoint

This commit is contained in:
2020-05-01 05:08:04 -04:00
parent abba062f4f
commit 1a394ddb31
20 changed files with 239 additions and 102 deletions

View File

@@ -7,4 +7,8 @@
<RootNamespace>Foodsoft.Alpm</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
</ItemGroup>
</Project>

View File

@@ -8,8 +8,8 @@ namespace Foodsoft.Alpm
{
public AlpmException() { }
internal AlpmException(SafeAlpmHandle handle) : base(alpm.alpm_strerror(alpm.alpm_errno(handle))) { }
public AlpmException(Error errno) : base(alpm.alpm_strerror(errno)) { }
public AlpmException(Error errno, Exception inner) : base(alpm.alpm_strerror(errno), inner) { }
public AlpmException(ErrorCode errno) : base(alpm.alpm_strerror(errno)) { }
public AlpmException(ErrorCode errno, Exception inner) : base(alpm.alpm_strerror(errno), inner) { }
protected AlpmException(SerializationInfo info,
StreamingContext context) : base(info, context) { }

View File

@@ -6,7 +6,7 @@ namespace Foodsoft.Alpm
public readonly struct Backup : IEquatable<Backup>
{
public string Name { get; }
public string Hash { get; }
public string? Hash { get; }
public bool Equals(Backup other)
{
@@ -24,17 +24,17 @@ namespace Foodsoft.Alpm
}
[StructLayout(LayoutKind.Sequential)]
private readonly unsafe struct NativeBackup
private readonly struct NativeBackup
{
internal readonly sbyte* name;
internal readonly sbyte* hash;
internal readonly IntPtr name;
internal readonly IntPtr hash;
}
internal unsafe Backup(IntPtr ptr)
{
var native = (NativeBackup*) ptr;
Name = new string(native->name);
Hash = new string(native->hash);
Name = Marshal.PtrToStringUTF8(native->name)!;
Hash = Marshal.PtrToStringUTF8(native->hash)!;
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Foodsoft.Alpm
var ret = alpm.alpm_pkg_checkmd5sum(Handle);
if (ret == 0) return true;
var errno = alpm.alpm_errno(Handle.SafeAlpmHandle);
return errno == Error.PkgInvalid ? false : throw new AlpmException(errno);
return errno == ErrorCode.PkgInvalid ? false : throw new AlpmException(errno);
}
public void SetInstallReason(InstallReason reason)

View File

@@ -42,7 +42,7 @@ namespace Foodsoft.Alpm
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var s in value)
if (alpm.alpm_list_append_strdup(ref listPtr, s) == IntPtr.Zero)
throw new AlpmException(Error.Memory);
throw new AlpmException(ErrorCode.Memory);
success = true;
}
@@ -63,8 +63,8 @@ namespace Foodsoft.Alpm
public SigLevel SigLevel => alpm.alpm_db_get_siglevel(_handle);
public Error Valid =>
alpm.alpm_db_get_valid(_handle) != 0 ? alpm.alpm_errno(_handle.SafeAlpmHandle) : Error.OK;
public ErrorCode Valid =>
alpm.alpm_db_get_valid(_handle) != 0 ? alpm.alpm_errno(_handle.SafeAlpmHandle) : ErrorCode.OK;
public CachePackageList PackageCache
{
@@ -118,7 +118,7 @@ namespace Foodsoft.Alpm
if (pkgPtr == IntPtr.Zero)
{
var err = alpm.alpm_errno(_handle.SafeAlpmHandle);
if (err == Error.PkgNotFound)
if (err == ErrorCode.PkgNotFound)
return null;
throw new AlpmException(err);
}
@@ -134,7 +134,7 @@ namespace Foodsoft.Alpm
if (result == IntPtr.Zero)
{
var errNo = alpm.alpm_errno(_handle.SafeAlpmHandle);
if (errNo != Error.OK)
if (errNo != ErrorCode.OK)
throw new AlpmException(errNo);
}

View File

@@ -5,22 +5,12 @@ namespace Foodsoft.Alpm
{
public class Depend : IEquatable<Depend>
{
public enum ModType
{
Any = 1,
Equal,
GreaterThanOrEqual,
LessThanOrEqual,
GreaterThan,
LessThan
}
internal unsafe Depend(IntPtr ptr)
{
var native = (NativeDepend*) ptr;
Name = new string(native->name);
Version = new string(native->version);
Description = new string(native->description);
Name = Marshal.PtrToStringUTF8(native->name)!;
Version = Marshal.PtrToStringUTF8(native->version)!;
Description = Marshal.PtrToStringUTF8(native->description)!;
NameHash = native->name_hash;
Mod = native->mod;
}
@@ -48,13 +38,23 @@ namespace Foodsoft.Alpm
}
[StructLayout(LayoutKind.Sequential)]
private readonly unsafe struct NativeDepend
private readonly struct NativeDepend
{
internal readonly sbyte* name;
internal readonly sbyte* version;
internal readonly sbyte* description;
internal readonly IntPtr name;
internal readonly IntPtr version;
internal readonly IntPtr description;
internal readonly ulong name_hash;
internal readonly ModType mod;
}
}
public enum ModType
{
Any = 1,
Equal,
GreaterThanOrEqual,
LessThanOrEqual,
GreaterThan,
LessThan
}
}

View File

@@ -80,7 +80,7 @@ namespace Foodsoft.Alpm
Encoding.UTF8.GetBytes(pSrc, s.Length, pDest, poolBuffer.Length - start);
pDest[nbWritten] = 0;
if (alpm.alpm_list_append(ref listPtr, (IntPtr) pDest) == IntPtr.Zero)
throw new AlpmException(Error.Memory);
throw new AlpmException(ErrorCode.Memory);
start += nbWritten + 1;
}

View File

@@ -1,8 +1,6 @@
using System;
namespace Foodsoft.Alpm
{
public enum Error
public enum ErrorCode
{
OK = 0,
Memory,
@@ -80,28 +78,4 @@ namespace Foodsoft.Alpm
/*MissingCompileTimeFeatures*/
MissingCapabilitySignatures
}
[Flags]
public enum LogLevel
{
Error = 1,
Warning = (1 << 1),
Debug = (1 << 2),
Function = (1 << 3)
}
[Flags]
public enum SigLevel
{
Package = (1 << 0),
PackageOptional = (1 << 1),
PackageMarginalOK = (1 << 2),
PackageUnknownOK = (1 << 3),
Database = (1 << 10),
DatabaseOptional = (1 << 11),
DatabaseMarginalOK = (1 << 12),
DatabaseUnknownOK = (1 << 13),
UseDefault = (1 << 31)
}
}

View File

@@ -28,7 +28,7 @@ namespace Foodsoft.Alpm
refs[pkgPtr] = pkg;
if (alpm.alpm_list_append(ref listPtr, handle.DangerousGetHandle()) == IntPtr.Zero)
throw new AlpmException(Error.Memory);
throw new AlpmException(ErrorCode.Memory);
}
var foundPtr = alpm.alpm_find_satisfier(listPtr, depString);

View File

@@ -1,15 +1,19 @@
using System;
using System.Collections.Generic;
using Foodsoft.Alpm.Interop;
namespace Foodsoft.Alpm
{
public sealed class Handle : IDisposable
{
private readonly object _eventLock = new object();
private readonly SafeAlpmHandle _handle;
public Handle(string root, string dbpath)
private EventHandler<LogEventArgs> _logEvent;
public Handle(string root, string dbPath)
{
_handle = alpm.alpm_initialize(root, dbpath, out var err);
_handle = alpm.alpm_initialize(root, dbPath, out var err);
if (_handle.IsInvalid) throw new AlpmException(err);
}
@@ -22,6 +26,8 @@ namespace Foodsoft.Alpm
set => Detail.SetStringCollection(value, _handle, s => alpm.alpm_option_add_cachedir(_handle, s));
}
public LogLevel LogLevel { get; set; }
public void Dispose()
{
_handle.Dispose();
@@ -55,5 +61,42 @@ namespace Foodsoft.Alpm
{
return alpm.alpm_pkg_should_ignore(_handle, pkg.Handle) == 0;
}
public event EventHandler<LogEventArgs> Log
{
add
{
void LogCallback(LogLevel level, IntPtr format, IntPtr args)
{
if (level > LogLevel) return;
_logEvent?.Invoke(this,
new LogEventArgs {Level = level, Message = CFormatter.Format(format, args)});
}
lock (_eventLock)
{
var wasEmpty = _logEvent == null;
_logEvent += value;
if (wasEmpty)
alpm.alpm_option_set_logcb(_handle, LogCallback);
}
}
remove
{
lock (_eventLock)
{
// ReSharper disable once DelegateSubtraction
_logEvent -= value;
if (_logEvent == null)
alpm.alpm_option_set_logcb(_handle, null);
}
}
}
public struct LogEventArgs
{
public LogLevel Level;
public string Message;
}
}
}

View File

@@ -0,0 +1,88 @@
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);
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace Foodsoft.Alpm.Marshalling
{
@@ -31,8 +33,7 @@ namespace Foodsoft.Alpm.Marshalling
return Marshal.PtrToStringUTF8(pNativeData)!;
}
// ReSharper disable once UnusedMember.Local
// ReSharper disable once UnusedParameter.Local
[UsedImplicitly]
private static ICustomMarshaler GetInstance(string cookie)
{
return _instance;

View File

@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace Foodsoft.Alpm.Marshalling
{
@@ -7,8 +8,7 @@ namespace Foodsoft.Alpm.Marshalling
{
private static readonly UTF8Return _instance = default;
// ReSharper disable once UnusedMember.Local
// ReSharper disable once UnusedParameter.Local
[UsedImplicitly]
private static ICustomMarshaler GetInstance(string cookie)
{
return _instance;

13
Alpm/LogLevel.cs Normal file
View File

@@ -0,0 +1,13 @@
using System;
namespace Foodsoft.Alpm
{
[Flags]
public enum LogLevel
{
Error = 1,
Warning = (1 << 1),
Debug = (1 << 2),
Function = (1 << 3)
}
}

View File

@@ -5,10 +5,7 @@ namespace Foodsoft.Alpm
{
public abstract class Package : IPackageData
{
internal Package(SafePackageHandle handle)
{
Handle = handle;
}
internal Package(SafePackageHandle handle) => Handle = handle;
internal SafePackageHandle Handle { get; }
@@ -57,24 +54,15 @@ namespace Foodsoft.Alpm
public virtual Database? DB => null;
public void Dispose()
{
Handle.Dispose();
}
public void Dispose() => Handle.Dispose();
public IEnumerable<Package> ComputeRequiredBy()
{
throw new NotImplementedException();
}
public IEnumerable<Package> ComputeRequiredBy() => throw new NotImplementedException();
public IEnumerable<Package> ComputeOptionalFor()
{
throw new NotImplementedException();
}
public static int VersionCompare(string v1, string v2)
{
return alpm.alpm_pkg_vercmp(v1, v2);
}
public static int VersionCompare(string v1, string v2) => alpm.alpm_pkg_vercmp(v1, v2);
}
}

View File

@@ -4,7 +4,7 @@ using System.Runtime.ConstrainedExecution;
namespace Foodsoft.Alpm
{
internal class SafeFilePackageHandle : SafePackageHandle
internal sealed class SafeFilePackageHandle : SafePackageHandle
{
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[PrePrepareMethod]

19
Alpm/SigLevel.cs Normal file
View File

@@ -0,0 +1,19 @@
using System;
namespace Foodsoft.Alpm
{
[Flags]
public enum SigLevel
{
Package = (1 << 0),
PackageOptional = (1 << 1),
PackageMarginalOK = (1 << 2),
PackageUnknownOK = (1 << 3),
Database = (1 << 10),
DatabaseOptional = (1 << 11),
DatabaseMarginalOK = (1 << 12),
DatabaseUnknownOK = (1 << 13),
UseDefault = (1 << 31)
}
}

View File

@@ -16,7 +16,6 @@ namespace Foodsoft.Alpm
// ReSharper disable once InconsistentNaming
internal static class alpm
{
// ReSharper disable once InconsistentNaming
public delegate void alpm_fn_free(IntPtr ptr);
[DllImport(nameof(alpm))]
@@ -145,6 +144,12 @@ namespace Foodsoft.Alpm
[DllImport(nameof(alpm))]
public static extern int alpm_pkg_free(IntPtr ptr);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LogFuncCallBack(LogLevel level, IntPtr format, IntPtr argsAddress);
[DllImport(nameof(alpm))]
public static extern IntPtr alpm_option_set_logcb(SafeAlpmHandle handle, LogFuncCallBack? cb);
[DllImport(nameof(alpm))]
public static extern IntPtr alpm_option_get_cachedirs(SafeAlpmHandle handle);
@@ -159,11 +164,11 @@ namespace Foodsoft.Alpm
string cachedir);
[DllImport(nameof(alpm))]
public static extern Error alpm_errno(SafeAlpmHandle handle);
public static extern ErrorCode alpm_errno(SafeAlpmHandle handle);
[DllImport(nameof(alpm))]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Return))]
public static extern string alpm_strerror(Error err);
public static extern string alpm_strerror(ErrorCode err);
[DllImport(nameof(alpm))]
public static extern IntPtr alpm_get_localdb(SafeAlpmHandle handle);
@@ -218,7 +223,7 @@ namespace Foodsoft.Alpm
public static extern SafeAlpmHandle alpm_initialize(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8In))]
string root, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8In))]
string dbpath, out Error err);
string dbpath, out ErrorCode err);
[DllImport(nameof(alpm))]
public static extern int alpm_release(IntPtr handle);

View File

@@ -2,11 +2,16 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=alpm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=depstring/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Foodsoft/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=libc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Marshaler/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=msvcrt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pacman/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=satisfier/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=strdup/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=syncdb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=syncdbs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=treename/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vercmp/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ucrt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vercmp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vprintf/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vsnprintf/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -1,27 +1,24 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Foodsoft.Alpm;
// ReSharper disable once UnusedType.Global
namespace Samples
{
[Example("installed")]
internal class Installed : IExample
{
private static string ModToStr(Depend.ModType modType)
private static string ModToStr(ModType modType)
{
return modType switch
{
Depend.ModType.Any => "",
Depend.ModType.Equal => "==",
Depend.ModType.GreaterThanOrEqual => ">=",
Depend.ModType.LessThanOrEqual => "<=",
Depend.ModType.GreaterThan => ">",
Depend.ModType.LessThan => "<",
ModType.Any => "",
ModType.Equal => "==",
ModType.GreaterThanOrEqual => ">=",
ModType.LessThanOrEqual => "<=",
ModType.GreaterThan => ">",
ModType.LessThan => "<",
_ => throw new ArgumentOutOfRangeException(nameof(modType), modType, null)
};
}
@@ -60,7 +57,7 @@ namespace Samples
//using var pkg = db.PackageCache.FindSatisfier("gcc=9.3.0-1");
var result = db.Search(new string[] {"gcc", "objc"});
var result = db.Search(new[] {"gcc", "objc"});
foreach (var pkg in result)
using (pkg)
{
@@ -82,7 +79,7 @@ namespace Samples
var examples = (from t in Assembly.GetExecutingAssembly().GetTypes()
let attribute = (Example?) t.GetCustomAttribute(typeof(Example))
where attribute != null
select new {Name = attribute.Name, Type = t}).ToImmutableDictionary((e) => e.Name);
select new {attribute.Name, Type = t}).ToImmutableDictionary((e) => e.Name);
if (args.Length < 1)
{
@@ -110,7 +107,7 @@ namespace Samples
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class Example : Attribute
{
public string Name { get; }