170 lines
5.3 KiB
C#
170 lines
5.3 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.InteropServices;
|
|
using Qrakhen.Qamp.Core.Abstractions;
|
|
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
|
|
{
|
|
T ValueType { get; }
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
|
public readonly struct Value : IValue, ISerialize<Value>, IDebug<string>
|
|
{
|
|
public static readonly Value Void = new Value();
|
|
public static readonly Value True = new Value(true);
|
|
public static readonly Value False = new Value(false);
|
|
|
|
[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
|
|
|
|
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 Value(Obj obj) : this(Ptr.Create(obj)) { } // todo: very spicy, let's see if this doesn't fuck up the GC all too bad.
|
|
|
|
public dynamic? Dynamic => AsDynamic();
|
|
|
|
public T ValueType => Type;
|
|
public TypeInfo TypeInfo => TypeInfo.FromValue(this);
|
|
|
|
public bool IsNumber => Type <= T.Decimal;
|
|
public bool IsSigned => Is(T.Signed);
|
|
public bool IsUnsigned => Is(T.Unsigned);
|
|
public bool IsDecimal => Is(T.Decimal);
|
|
public bool IsBool => Is(T.Bool);
|
|
public bool IsString => Is(T.String);
|
|
public bool IsRef => Is(T.Reference, false);
|
|
public bool IsObj => Is(T.Object, false);
|
|
public bool IsExt => Is(T.Class) || Is(T.Instance);
|
|
public bool IsInstance => Is(T.Instance);
|
|
|
|
public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0;
|
|
|
|
public bool Is(T type, bool exact = true)
|
|
{
|
|
return exact ? (Type & type) == type : (Type & type) > 0;
|
|
}
|
|
|
|
private unsafe dynamic? AsDynamic(bool throwWhenNull = true)
|
|
{
|
|
if (IsString)
|
|
return Ptr.As<String>()!.Value;
|
|
|
|
if (IsObj)
|
|
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 void __gcMark()
|
|
{
|
|
if (IsObj && Ptr.Value != null) { }
|
|
}
|
|
|
|
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 string ToString() => ToString(false);
|
|
|
|
public string ToString(bool includeType)
|
|
{
|
|
if (!includeType)
|
|
return $"{AsDynamic(false)}";
|
|
|
|
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 byte[] Serialize()
|
|
{
|
|
List<byte> result = [.. ((ushort)Type).GetBytes() ];
|
|
byte[] bytes = Signed.GetBytes();
|
|
result.Add((byte)bytes.Length);
|
|
result.AddRange(bytes);
|
|
result.Add(0x7F);
|
|
return result.ToArray();
|
|
}
|
|
|
|
public static Value Deserialize(byte[] data)
|
|
{
|
|
return Void;
|
|
}
|
|
}
|
|
|
|
public class SerializedAttribute : Attribute; |