qamp/Qrakhen.Qamp.Core/Values/Value.cs

210 lines
5.9 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
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;
// HEY. IDEA.
// have custom structs for types. this way we can handle many more thing and also more clean no????
public interface IPrimitive
{
public static abstract string Name { get; }
public static abstract int SizeOf { get; }
public ulong Raw { get; }
public string Print();
}
public readonly record struct Signed(long Value)// : IPrimitive
{
}
public interface IValue<T>
{
T Data { get; }
}
public interface ITypedValue
{
T ValueType { get; }
}
public interface IDynamicValue : IValue<ulong>
{
}
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public readonly struct Value :
IDynamicValue,
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;
public ulong Data => Unsigned;
[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 (Type == T.Void)
return $"void";
if (!includeType)
return $"{AsDynamic(false)}";
string type = Type.ToString();
if (Type.HasFlag(T.Pointer))
type = $"{Ptr.Value?.Type.ToString()}@{Ptr.Address}" ?? "ptr:null";
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;