124 lines
4.0 KiB
C#
124 lines
4.0 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.InteropServices;
|
|
using Qrakhen.Qamp.Core.Values.Objects;
|
|
using String = Qrakhen.Qamp.Core.Values.Objects.String;
|
|
using T = Qrakhen.Qamp.Core.Values.ValueType;
|
|
|
|
namespace Qrakhen.Qamp.Core.Values;
|
|
|
|
public interface IValue
|
|
{
|
|
ValueType ValueType { get; }
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
|
public readonly struct Value : IValue, IDebug<string>
|
|
{
|
|
public static readonly Value Void = new Value();
|
|
|
|
[FieldOffset(0x00)] [Serialized] public readonly ulong Unsigned;
|
|
[FieldOffset(0x00)] [Serialized] public readonly long Signed;
|
|
[FieldOffset(0x00)] [Serialized] public readonly bool Bool;
|
|
[FieldOffset(0x00)] [Serialized] public readonly char Char;
|
|
[FieldOffset(0x00)] [Serialized] public readonly double Decimal;
|
|
[FieldOffset(0x00)] [Serialized] public readonly Address Address;
|
|
[FieldOffset(0x00)] [Serialized] public readonly Ptr Ptr;
|
|
[FieldOffset(0x00)] [Serialized] public readonly Ref Ref;
|
|
|
|
[FieldOffset(0x08)] [Serialized] public readonly T Type;
|
|
|
|
[FieldOffset(0x0c)] [Serialized] public readonly uint Meta; // like lengths of arrays or lists
|
|
|
|
public T ValueType => Type;
|
|
|
|
private Value(T type) => Type = type;
|
|
|
|
public Value() : this(T.Void) { }
|
|
public Value(ulong unsigned) : this(T.Unsigned) => Unsigned = unsigned;
|
|
public Value(long signed) : this(T.Signed) => Signed = signed;
|
|
public Value(char character) : this(T.Char) => Char = character;
|
|
public Value(bool boolean) : this(T.Bool) => Bool = boolean;
|
|
public Value(double @decimal) : this(T.Decimal) => Decimal = @decimal;
|
|
public Value(Address address) : this(T.Address) => Address = address;
|
|
public Value(Ptr ptr) : this(ptr.Value?.Type ?? T.Pointer) => Ptr = ptr;
|
|
public Value(Ref @ref) : this(T.Reference) => Ref = @ref;
|
|
|
|
public dynamic? Dynamic => AsDynamic();
|
|
|
|
public bool Is(T type, bool exact = true)
|
|
{
|
|
return exact ? (Type & type) == type : (Type & type) > 0;
|
|
}
|
|
|
|
private unsafe dynamic? AsDynamic(bool throwWhenNull = true)
|
|
{
|
|
if (Type.HasFlag(T.Object))
|
|
return Ptr.Value;
|
|
|
|
return Type switch {
|
|
T.Void => throwWhenNull ? throw new NullReferenceException() : null,
|
|
T.Unsigned => Unsigned,
|
|
T.Signed => Signed,
|
|
T.Decimal => Decimal,
|
|
T.Bool => Bool,
|
|
T.Address => Address,
|
|
T.Reference => (Value)(*Ref.Value),
|
|
_ => throwWhenNull ? throw new NullReferenceException() : null,
|
|
};
|
|
}
|
|
|
|
public static Value FromAny(object? any = null)
|
|
{
|
|
if (any == null)
|
|
return new Value();
|
|
|
|
Type type = any.GetType();
|
|
|
|
return new Value();
|
|
}
|
|
|
|
public override bool Equals([NotNullWhen(true)] object? obj)
|
|
{
|
|
return obj switch {
|
|
Value value when Type == value.Type => Unsigned == value.Unsigned,
|
|
Value value => Dynamic?.Equals(value.Dynamic),
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
public override int GetHashCode() => Unsigned.GetHashCode();
|
|
|
|
public override unsafe string ToString()
|
|
{
|
|
string type = Type.ToString();
|
|
if (Type.HasFlag(T.Pointer))
|
|
type = Ptr.Value?.Type.ToString() ?? "NullPtr";
|
|
|
|
return $"[{type}, {AsDynamic(false) ?? "null"}]";
|
|
}
|
|
|
|
public string Debug(DebugLevel level = DebugLevel.None)
|
|
{
|
|
if (level < DebugLevel.Strong)
|
|
return Debugger.GetContextString(this);
|
|
|
|
string str = $"{Debugger.GetContextString(this)}\n";
|
|
|
|
string[] lines =
|
|
[
|
|
$"{nameof(Meta)}: {Meta}",
|
|
$"{nameof(Unsigned)}: {Unsigned}",
|
|
$"{nameof(Signed)}: {Signed}",
|
|
$"{nameof(Bool)}: {Bool}",
|
|
$"{nameof(Char)}: {Char}",
|
|
$"{nameof(Decimal)}: {Decimal}",
|
|
$"{nameof(Address)}: {Address}",
|
|
$"{nameof(Ref)}: {Ref}",
|
|
$"{nameof(Ptr)}: {Ptr}"
|
|
];
|
|
|
|
return str + string.Join("\n - ", lines);
|
|
}
|
|
}
|
|
|
|
public class SerializedAttribute : Attribute; |