using System.Diagnostics; namespace System { struct Variant { enum ObjectType { UnownedObject, OwnedObject, NullObject } enum StructFlag { InternalValue, OwnedPtr, ExternalPtr } int mStructType; // 0 = unowned object, 1 = owned object, 2 = null value (mData is type), otherwise is struct type (|0 is internal, |1 is owned ptr, |2 is external ptr) int mData; // This is either an Object reference, struct data, or a pointer to struct data public bool OwnsMemory { get { if (mStructType <= 2) return mStructType == 1; return (mStructType & 1) != 0; } } public bool IsObject { get { if (mStructType <= 2) return true; if ((mStructType & 3) == (int)StructFlag.ExternalPtr) return VariantType.IsObject; return false; } } public bool IsValueType { get { if (mStructType <= 2) return false; if ((mStructType & 3) == (int)StructFlag.ExternalPtr) return VariantType.IsValueType; return mStructType != 0; } } public Type VariantType { get { if (mStructType == 2) { return (Type)Internal.UnsafeCastToObject((void*)mData); } if (mStructType <= 1) { return Internal.UnsafeCastToObject((void*)mData).GetType(); } return (Type)Internal.UnsafeCastToObject((void*)(mStructType & ~3)); } } public bool HasValue { get { return (mStructType != 0) || (mData != 0); } } public void* DataPtr { get mut { if (mStructType <= 3) { if (mStructType == 2) return null; Object obj = Internal.UnsafeCastToObject((void*)mData); return (uint8*)Internal.UnsafeCastToPtr(obj) + obj.GetType().[Friend]mMemberDataOffset; } if ((mStructType & 3) == (int)StructFlag.InternalValue) return (void*)&mData; else return (void*)mData; } } protected override void GCMarkMembers() { if ((mStructType == (int)ObjectType.UnownedObject) || (mStructType == (int)ObjectType.OwnedObject)) { var obj = Internal.UnsafeCastToObject((void*)mData); GC.Mark(obj); } } public void Dispose() mut { if (mStructType == (int)ObjectType.OwnedObject) { delete Internal.UnsafeCastToObject((void*)mData); } else if (OwnsMemory) { delete (void*)mData; } mStructType = 0; mData = 0; } public static Variant Create(T val, bool owns = false) where T : class { Variant variant; if (val == null) { variant.mStructType = (int)ObjectType.NullObject; variant.mData = (int)Internal.UnsafeCastToPtr(typeof(T)); } else { variant.mStructType = (int)(owns ? (int)ObjectType.OwnedObject : (int)ObjectType.UnownedObject); variant.mData = (int)Internal.UnsafeCastToPtr(val); } return variant; } public static Variant Create(T val) where T : struct { Variant variant; Type type = typeof(T); if (sizeof(T) <= sizeof(int)) { variant.mStructType = (int)Internal.UnsafeCastToPtr(type); variant.mData = 0; *(T*)&variant.mData = val; } else { variant.mStructType = (int)Internal.UnsafeCastToPtr(type) | 1; T* newVal = (T*)new uint8[sizeof(T)]*; *newVal = val; variant.mData = (int)(void*)newVal; } return variant; } public static Variant Create(T val) where T : struct* { Variant variant; Type type = typeof(T); if (type.Size <= sizeof(int)) { variant.mStructType = (int)Internal.UnsafeCastToPtr(type); variant.mData = 0; *(T*)&variant.mData = val; } else { variant.mStructType = (int)Internal.UnsafeCastToPtr(type) | 1; T* newVal = (T*)new uint8[sizeof(T)]*; *newVal = val; variant.mData = (int)(void*)newVal; } return variant; } public static Variant Create(ref T val) where T : struct { Variant variant; Type type = typeof(T); variant.mStructType = (int)Internal.UnsafeCastToPtr(type) | 2; variant.mData = 0; variant.mData = (int)(void*)&val; return variant; } public static Variant CreateOwned(T val) where T : struct { Variant variant; Type type = typeof(T); variant.mStructType = (int)Internal.UnsafeCastToPtr(type) | 1; T* newVal = (T*)new uint8[sizeof(T)]*; *newVal = val; variant.mData = (int)(void*)newVal; return variant; } public void EnsureReference() mut { if ((mStructType <= 2) && (mStructType & 3 == (int)StructFlag.InternalValue)) return; var val = mData; mStructType |= (int)StructFlag.OwnedPtr; int* newVal = (int*)new uint8[sizeof(int)]*; *newVal = val; mData = (int)(void*)newVal; } public static Variant Create(Type type, void* val) { Variant variant; Debug.Assert(!type.IsObject); if (type.Size <= sizeof(int)) { variant.mStructType = (int)Internal.UnsafeCastToPtr(type); variant.mData = 0; Internal.MemCpy(&variant.mData, val, type.[Friend]mSize); } else { variant.mStructType = (int)Internal.UnsafeCastToPtr(type) | 1; void* data = new uint8[type.[Friend]mSize]*; Internal.MemCpy(data, val, type.[Friend]mSize); variant.mData = (int)data; } return variant; } public static Variant CreateReference(Type type, void* val) { Variant variant; variant.mStructType = (int)Internal.UnsafeCastToPtr(type) | 2; variant.mData = (int)val; return variant; } public static void* Alloc(Type type, out Variant variant) { variant = .(); if (type.IsObject) { return &variant.mData; } else { if (type.Size <= sizeof(int)) { variant.mStructType = (int)Internal.UnsafeCastToPtr(type); variant.mData = 0; return &variant.mData; } else { variant.mStructType = (int)Internal.UnsafeCastToPtr(type) | 1; void* data = new uint8[type.[Friend]mSize]*; variant.mData = (int)data; return data; } } } public T Get() where T : class { Debug.Assert(IsObject); if (mStructType == 2) return (T)null; Type type = typeof(T); T obj; if (mStructType >= 3) obj = (T)Internal.UnsafeCastToObject(*(void**)(void*)mData); else obj = (T)Internal.UnsafeCastToObject((void*)mData); Debug.Assert(obj.GetType().IsSubtypeOf(type)); return obj; } public T Get() where T : struct { Debug.Assert(!IsObject); //var type = VariantType; //Debug.Assert((typeof(T) == type) || (typeof(T) == type.GetUnderlyingType())); if ((mStructType & 3) == (int)StructFlag.InternalValue) { int data = mData; return *(T*)&data; } else return *(T*)(void*)mData; } public T Get() where T : struct* { Debug.Assert(!IsObject); //var type = VariantType; //Debug.Assert((typeof(T) == type) || (typeof(T) == type.GetUnderlyingType())); if ((mStructType & 3) == (int)StructFlag.InternalValue) { int data = mData; return *(T*)&data; } else return *(T*)(void*)mData; } public Result GetBoxed() { if (IsObject) return .Err; var type = VariantType; var boxedType = type.BoxedType; if (boxedType == null) return .Err; var self = this; var object = Try!(boxedType.CreateObject()); Internal.MemCpy((uint8*)Internal.UnsafeCastToPtr(object) + boxedType.[Friend]mMemberDataOffset, self.DataPtr, type.Size); return object; } /*public void Get(ref T val) { if (VariantType != typeof(T)) return; val = Get(); }*/ public void CopyValueData(void* dest) { if (IsObject) { if (mStructType == 2) *((Object*)dest) = null; else *((Object*)dest) = Internal.UnsafeCastToObject((void*)mData); return; } var type = VariantType; if ((mStructType & 3) == (int)StructFlag.InternalValue) { int data = mData; Internal.MemCpy(dest, &data, type.Size); } else { Internal.MemCpy(dest, (void*)mData, type.Size); } } public void* GetValueData() mut { Debug.Assert(!IsObject); if ((mStructType & 3) == (int)StructFlag.InternalValue) { return (void*)&mData; } else { return (void*)mData; } } [Commutable] public static bool operator==(Variant v1, Variant v2) { if (v1.IsObject) { if (!v2.IsObject) return false; if ((v1.mStructType == 2) != (v2.mStructType == 2)) return false; // If one is null but the other isn't return v1.mData == v2.mData; } if (v1.mStructType & 3 != v2.mStructType & 3) return false; if ((v1.mStructType & 3 == 0) && (v2.mStructType & 3 == 0)) return v1.mData == v2.mData; var v1; var v2; let type = v1.VariantType; let ptr1 = v1.DataPtr; let ptr2 = v2.DataPtr; return Internal.MemCmp(ptr1, ptr2, type.[Friend]mSize) == 0; } public static mixin Equals(var v1, var v2) { v1.Get() == v2.Get() } public static Variant CreateFromVariant(Variant varFrom) { Variant varTo = varFrom; if (varTo.mStructType == 1) varTo.mStructType = 0; if (varTo.mStructType > 2) { varTo.mStructType &= ~3; let type = (Type)Internal.UnsafeCastToObject((void*)(varFrom.mStructType & ~3)); if (type.[Friend]mSize > sizeof(int)) { varTo.mStructType |= (int)StructFlag.OwnedPtr; void* data = new uint8[type.[Friend]mSize]*; Internal.MemCpy(data, (void*)varFrom.mData, type.[Friend]mSize); varTo.mData = (int)data; } } return varTo; } public static Variant CreateFromVariantRef(ref Variant varFrom) { Variant varTo = varFrom; if (varTo.mStructType == 1) varTo.mStructType = 0; if (varTo.mStructType > 2) { varTo.mStructType &= ~3; varTo.mStructType |= (int)StructFlag.ExternalPtr; varTo.mData = (int)varFrom.DataPtr; } return varTo; } public static Result CreateFromBoxed(Object objectFrom) { if (objectFrom == null) return default; Variant variant = ?; Type objType = objectFrom.[Friend]RawGetType(); if (objType.IsBoxed) { void* srcDataPtr = (uint8*)Internal.UnsafeCastToPtr(objectFrom) + objType.[Friend]mMemberDataOffset; var underlying = objType.UnderlyingType; variant.mStructType = (int)Internal.UnsafeCastToPtr(underlying); if (underlying.Size <= sizeof(int)) { variant.mData = 0; *(int*)&variant.mData = *(int*)srcDataPtr; } else { variant.mStructType |= (int)StructFlag.OwnedPtr; void* data = new uint8[underlying.[Friend]mSize]*; Internal.MemCpy(data, srcDataPtr, underlying.[Friend]mSize); variant.mData = (int)data; } } else { variant.mStructType = 0; variant.mData = (int)Internal.UnsafeCastToPtr(objectFrom); } return variant; } } }