コード例 #1
0
        public bool Generate <TContainer, TValue>(FieldInfo fieldInfo, ReflectedPropertyBag <TContainer> propertyBag)
        {
            if (!typeof(TContainer).IsValueType)
            {
                return(false);
            }

            if (!fieldInfo.FieldType.IsValueType)
            {
                return(false);
            }

            if (!UnsafeUtility.IsBlittable(fieldInfo.FieldType) && fieldInfo.FieldType != typeof(char))
            {
                return(false);
            }

            var propertyType = typeof(UnmanagedProperty <,>).MakeGenericType(typeof(TContainer), fieldInfo.FieldType);
            var property     = Activator.CreateInstance(propertyType,
                                                        fieldInfo.Name,
                                                        UnsafeUtility.GetFieldOffset(fieldInfo),
                                                        null != fieldInfo.GetCustomAttribute <ReadOnlyAttribute>(),
                                                        new PropertyAttributeCollection(fieldInfo.GetCustomAttributes().ToArray()));

            propertyBag.AddProperty <IProperty <TContainer, TValue>, TValue>((IProperty <TContainer, TValue>)property);
            return(true);
        }
コード例 #2
0
ファイル: TypeDecomposer.cs プロジェクト: NeroWeNeed/reactics
 public FieldData(int offset, FieldInfo info)
 {
     this.offset      = offset + UnsafeUtility.GetFieldOffset(info);
     length           = UnsafeUtility.SizeOf(info.FieldType);
     type             = info.FieldType;
     isAssetReference = info.GetCustomAttribute <AssetReferenceAttribute>() != null;
 }
コード例 #3
0
        static bool TryGetOffsetOfField(Type rootType, PropertyPath propertyPath, int startIndex, out int offset)
        {
            offset = 0;

            var currentType = rootType;

            for (var i = startIndex; i < propertyPath.PartsCount; i++)
            {
                if (!currentType.IsValueType)
                {
                    return(false);
                }

                var part = propertyPath[i];

                if (part.IsIndex || part.IsKey)
                {
                    throw new ArgumentException("TinyAnimation does not support array indexers or dictionary keys for bindings.");
                }

                var f = currentType.GetField(propertyPath[i].Name, BindingFlags.Instance | BindingFlags.Public);

                if (f == null)
                {
                    return(false);
                }

                offset     += UnsafeUtility.GetFieldOffset(f);
                currentType = f.FieldType;
            }

            return(true);
        }
コード例 #4
0
 public InjectionData(FieldInfo field, Type genericType, bool isReadOnly)
 {
     this.IndexInComponentGroup = -1;
     this.FieldOffset           = UnsafeUtility.GetFieldOffset(field);
     Unity.Entities.ComponentType.AccessMode accessModeType = isReadOnly ? Unity.Entities.ComponentType.AccessMode.ReadOnly : Unity.Entities.ComponentType.AccessMode.ReadWrite;
     this.ComponentType = new Unity.Entities.ComponentType(genericType, accessModeType);
     this.IsReadOnly    = isReadOnly;
 }
コード例 #5
0
        public int GetOffsetOfField(string fieldName)
        {
            var currentBase = GetCurrentOffset();
            var fieldInfo   = m_StructTypes[m_StructTypes.Count - 1].Type.GetField(fieldName);

            currentBase += UnsafeUtility.GetFieldOffset(fieldInfo);
            return(currentBase);
        }
コード例 #6
0
        private static void Init()
        {
            var type  = typeof(ComponentTypeHandle <>);
            var field = type.GetField("m_Safety", BindingFlags.NonPublic |
                                      BindingFlags.Instance |
                                      BindingFlags.GetField);

            s_ArchetypeComponentSafetyVarPos = UnsafeUtility.GetFieldOffset(field);
        }
コード例 #7
0
    static ListUtilities()
    {
        var type        = typeof(List <>);
        var itemsMember = type.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic);
        var sizeMember  = type.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic);

        ItemsFieldOffset = UnsafeUtility.GetFieldOffset(itemsMember);
        SizeFieldOffset  = UnsafeUtility.GetFieldOffset(sizeMember);
    }
コード例 #8
0
 public FieldOffsetInfo(int currentOffset, FieldInfo parent, FieldInfo fieldInfo)
 {
     this.root   = parent == null;
     fullName    = root ? fieldInfo.Name : $"{parent.Name}.{fieldInfo.Name}";
     name        = fieldInfo.Name;
     this.type   = fieldInfo.FieldType;
     this.offset = currentOffset + UnsafeUtility.GetFieldOffset(fieldInfo);
     length      = UnsafeUtility.SizeOf(fieldInfo.FieldType);
 }
コード例 #9
0
        public InjectionData(FieldInfo field, Type genericType, bool isReadOnly)
        {
            IndexInComponentGroup = -1;
            FieldOffset           = UnsafeUtility.GetFieldOffset(field);

            var accessMode = isReadOnly ? ComponentType.AccessMode.ReadOnly : ComponentType.AccessMode.ReadWrite;

            ComponentType = new ComponentType(genericType, accessMode);
            IsReadOnly    = isReadOnly;
        }
コード例 #10
0
ファイル: DynamicBuilders.cs プロジェクト: quabug/EntitiesBT
        public void Build(BlobBuilder builder, IntPtr dataPtr)
        {
            var fields = Type.GetType(BlobDataType).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            for (var i = 0; i < Builders.Length; i++)
            {
                var offset = UnsafeUtility.GetFieldOffset(fields[i]);
                Builders[i].Build(builder, dataPtr + offset);
            }
        }
コード例 #11
0
#pragma warning restore 649

            static KernelLayout Calculate()
            {
                var          type = typeof(Layout <TUserKernel, TKernelData, TKernelPortDefinition>);
                KernelLayout ret;

                ret.Combined     = SimpleType.Create <Layout <TUserKernel, TKernelData, TKernelPortDefinition> >();
                ret.DataOffset   = UnsafeUtility.GetFieldOffset(type.GetField("Data"));
                ret.KernelOffset = UnsafeUtility.GetFieldOffset(type.GetField("Kernel"));

                return(ret);
            }
コード例 #12
0
        public static int GetFieldOffset(FieldInfo field)
        {
#if UNITY_COLLECTIONS
            return(UnsafeUtility.GetFieldOffset(field));
#else
            int GetFieldOffset(RuntimeFieldHandle h) =>
            Marshal.ReadInt32(h.Value + (4 + IntPtr.Size)) & 0xFFFFFF;

            return(GetFieldOffset(field.FieldHandle));
#endif
        }
コード例 #13
0
ファイル: Builders.cs プロジェクト: quabug/EntitiesBT
        public override unsafe void Build(BlobBuilder builder, ref T data)
        {
            var dataPtr = new IntPtr(UnsafeUtility.AddressOf(ref data));
            var fields  = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            for (var i = 0; i < Builders.Length; i++)
            {
                var offset = UnsafeUtility.GetFieldOffset(fields[i]);
                Builders[i].Build(builder, dataPtr + offset);
            }
        }
コード例 #14
0
        private unsafe InjectComponentGroupData(ComponentSystemBase system, FieldInfo groupField,
                                                InjectionData[] componentDataInjections, InjectionData[] bufferArrayInjections,
                                                InjectionData[] sharedComponentInjections,
                                                FieldInfo entityArrayInjection, FieldInfo indexFromEntityInjection, InjectionContext injectionContext,
                                                FieldInfo lengthInjection, FieldInfo componentGroupIndexField, ComponentType[] componentRequirements)
        {
            m_EntityGroup = system.GetComponentGroupInternal(componentRequirements);

            m_ComponentGroupIndex = Array.IndexOf(system.ComponentGroups, m_EntityGroup);

            m_ComponentDataInjections   = componentDataInjections;
            m_BufferArrayInjections     = bufferArrayInjections;
            m_SharedComponentInjections = sharedComponentInjections;
            m_InjectionContext          = injectionContext;

            PatchGetIndexInComponentGroup(m_ComponentDataInjections);
            PatchGetIndexInComponentGroup(m_BufferArrayInjections);
            PatchGetIndexInComponentGroup(m_SharedComponentInjections);

            injectionContext.PrepareEntries(m_EntityGroup);

            if (entityArrayInjection != null)
            {
                m_EntityArrayOffset = UnsafeUtility.GetFieldOffset(entityArrayInjection);
            }
            else
            {
                m_EntityArrayOffset = -1;
            }

            if (lengthInjection != null)
            {
                m_LengthOffset = UnsafeUtility.GetFieldOffset(lengthInjection);
            }
            else
            {
                m_LengthOffset = -1;
            }

            m_GroupFieldOffset = UnsafeUtility.GetFieldOffset(groupField);

            if (componentGroupIndexField != null)
            {
                ulong gchandle;
                var   pinnedSystemPtr = (byte *)UnsafeUtility.PinGCObjectAndGetAddress(system, out gchandle);
                var   groupIndexPtr   = pinnedSystemPtr + m_GroupFieldOffset + UnsafeUtility.GetFieldOffset(componentGroupIndexField);

                int groupIndex = m_ComponentGroupIndex;
                UnsafeUtility.CopyStructureToPtr(ref groupIndex, groupIndexPtr);

                UnsafeUtility.ReleaseGCObject(gchandle);
            }
        }
コード例 #15
0
 public override InjectionContext.Entry CreateInjectionInfoFor(FieldInfo field, bool isReadOnly)
 {
     return(new InjectionContext.Entry
     {
         Hook = this,
         FieldInfo = field,
         IsReadOnly = isReadOnly,
         AccessMode = isReadOnly ? ComponentType.AccessMode.ReadOnly : ComponentType.AccessMode.ReadWrite,
         IndexInComponentGroup = -1,
         FieldOffset = UnsafeUtility.GetFieldOffset(field),
         ComponentRequirements = new[] { typeof(Transform) }
     });
 }
コード例 #16
0
        public unsafe void Buffer_AndBufferDescription_HaveSameLayout()
        {
            var typed   = typeof(Buffer <byte>);
            var untyped = typeof(BufferDescription);

            Assert.AreEqual(UnsafeUtility.SizeOf(typed), UnsafeUtility.SizeOf(untyped));

            var fields = typed.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);

            Assert.AreEqual(1, fields.Length);
            Assert.AreEqual(fields[0].FieldType, untyped);
            Assert.Zero(UnsafeUtility.GetFieldOffset(fields[0]));
        }
コード例 #17
0
        public unsafe void TypeInfo_BlobAssetReferenceOffsets_AreSortedAndCorrect()
        {
            var typeInfo = TypeManager.GetTypeInfo <EcsTestDataBlobAssetRef2>();

            Assert.AreEqual(2, typeInfo.BlobAssetRefOffsetCount);
            var offsets = TypeManager.GetBlobAssetRefOffsets(typeInfo);
            int offsetA = offsets[0].Offset;
            int offsetB = offsets[1].Offset;

            Assert.Less(offsetA, offsetB, "BlobAssetOffsets offsets are assumed to be sorted.");
            Assert.AreEqual(UnsafeUtility.GetFieldOffset(typeof(EcsTestDataBlobAssetRef2).GetField(nameof(EcsTestDataBlobAssetRef2.value))), offsetA);
            Assert.AreEqual(UnsafeUtility.GetFieldOffset(typeof(EcsTestDataBlobAssetRef2).GetField(nameof(EcsTestDataBlobAssetRef2.value2))), offsetB);
        }
コード例 #18
0
            public static Field Create(FieldInfo info, Type type, string name, bool selfNodeOnly)
            {
                var offset =
                    UnsafeUtility.GetFieldOffset(info);

                return(new Field
                {
                    info = info,
                    name = name,
                    type = type,
                    offset = offset,
                    selfNodeOnly = selfNodeOnly
                });
            }
コード例 #19
0
        public unsafe void TypeInfo_EntityReferenceOffsets_AreSortedAndCorrect()
        {
            var typeInfo = TypeManager.GetTypeInfo <EcsTestDataEntity2>();

            Assert.IsTrue(TypeManager.HasEntityReferences(TypeManager.GetTypeIndex <EcsTestDataEntity2>()));
            Assert.AreEqual(2, typeInfo.EntityOffsetCount);
            var offsets = TypeManager.GetEntityOffsets(typeInfo);
            int offsetA = offsets[0].Offset;
            int offsetB = offsets[1].Offset;

            Assert.Less(offsetA, offsetB, "Entity offsets are assumed to be sorted.");
            Assert.AreEqual(UnsafeUtility.GetFieldOffset(typeof(EcsTestDataEntity2).GetField(nameof(EcsTestDataEntity2.value1))), offsetA);
            Assert.AreEqual(UnsafeUtility.GetFieldOffset(typeof(EcsTestDataEntity2).GetField(nameof(EcsTestDataEntity2.value2))), offsetB);
        }
コード例 #20
0
        public ArchetypeManager(SharedComponentDataManager sharedComponentManager)
        {
            m_SharedComponentManager = sharedComponentManager;
            m_TypeLookup             = new NativeMultiHashMap <uint, IntPtr>(256, Allocator.Persistent);

            m_EmptyChunkPool = (UnsafeLinkedListNode *)m_ArchetypeChunkAllocator.Allocate(sizeof(UnsafeLinkedListNode), UnsafeUtility.AlignOf <UnsafeLinkedListNode>());
            UnsafeLinkedListNode.InitializeList(m_EmptyChunkPool);

#if UNITY_ASSERTIONS
            // Buffer should be 16 byte aligned to ensure component data layout itself can gurantee being aligned
            var offset = UnsafeUtility.GetFieldOffset(typeof(Chunk).GetField("Buffer"));
            Assert.IsTrue(offset % 16 == 0, "Chunk buffer must be 16 byte aligned");
#endif
        }
コード例 #21
0
        InjectComponentGroupData(ComponentSystemBase system, FieldInfo groupField,
                                 InjectionData[] componentDataInjections, InjectionData[] fixedArrayInjections, InjectionData[] sharedComponentInjections,
                                 FieldInfo entityArrayInjection, FieldInfo indexFromEntityInjection, InjectionContext injectionContext,
                                 FieldInfo lengthInjection, ComponentType[] componentRequirements)
        {
            var requiredComponentTypes = componentRequirements;

            m_EntityGroup = system.GetComponentGroup(requiredComponentTypes);

            m_ComponentDataInjections   = componentDataInjections;
            m_FixedArrayInjections      = fixedArrayInjections;
            m_SharedComponentInjections = sharedComponentInjections;
            m_InjectionContext          = injectionContext;

            PatchGetIndexInComponentGroup(m_ComponentDataInjections);
            PatchGetIndexInComponentGroup(m_FixedArrayInjections);
            PatchGetIndexInComponentGroup(m_SharedComponentInjections);

            injectionContext.PrepareEntries(m_EntityGroup);

            if (entityArrayInjection != null)
            {
                m_EntityArrayOffset = UnsafeUtility.GetFieldOffset(entityArrayInjection);
            }
            else
            {
                m_EntityArrayOffset = -1;
            }

            if (indexFromEntityInjection != null)
            {
                m_IndexFromEntityOffset = UnsafeUtility.GetFieldOffset(indexFromEntityInjection);
            }
            else
            {
                m_IndexFromEntityOffset = -1;
            }

            if (lengthInjection != null)
            {
                m_LengthOffset = UnsafeUtility.GetFieldOffset(lengthInjection);
            }
            else
            {
                m_LengthOffset = -1;
            }

            m_GroupFieldOffset = UnsafeUtility.GetFieldOffset(groupField);
        }
コード例 #22
0
        public int GetCurrentOffset()
        {
            var offset = 0;

            var type = m_StructTypes[0].Type;

            for (var i = 1; i < m_StructTypes.Count; ++i)
            {
                var info      = m_StructTypes[i];
                var fieldInfo = type.GetField(info.Name);
                offset += UnsafeUtility.GetFieldOffset(fieldInfo);
                type    = info.Type;
            }
            return(offset);
        }
コード例 #23
0
        // Fills the int array with offsets of specified type's fields, starting from specified index and updating the index after each write into array.
        internal unsafe static void FillFieldOffsetsArray(Type typeContainingFields, List <KeyValuePair <string, bool> > fieldNamesAndAccess, int *fieldOffsets, ref int fieldIndex)
        {
            foreach (KeyValuePair <string, bool> fieldInfo in fieldNamesAndAccess)
            {
                if (fieldInfo.Value)
                {
                    fieldOffsets[fieldIndex] = UnsafeUtility.GetFieldOffset(typeContainingFields.GetField(fieldInfo.Key));
                }
                else
                {
                    fieldOffsets[fieldIndex] = UnsafeUtility.GetFieldOffset(typeContainingFields.GetField(fieldInfo.Key, BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
                }

                fieldIndex++;
            }
        }
コード例 #24
0
 private static void CollectInheritableFields(Type type, byte config, int offset, List <UISchema.InheritableField> fields)
 {
     if (type == typeof(UILength))
     {
         fields.Add(new UISchema.InheritableField
         {
             config = config, offset = offset, length = UnsafeUtility.SizeOf <UILength>()
         });
     }
     else
     {
         foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
         {
             CollectInheritableFields(field.FieldType, config, offset + UnsafeUtility.GetFieldOffset(field), fields);
         }
     }
 }
コード例 #25
0
        public override InjectionContext.Entry CreateInjectionInfoFor(FieldInfo field, bool isReadOnly)
        {
            var componentType = field.FieldType.GetGenericArguments()[0];
            var accessMode    = isReadOnly ? ComponentType.AccessMode.ReadOnly : ComponentType.AccessMode.ReadWrite;

            return(new InjectionContext.Entry
            {
                Hook = this,
                FieldInfo = field,
                IsReadOnly = false /* isReadOnly */,
                AccessMode = accessMode,
                IndexInComponentGroup = -1,
                FieldOffset = UnsafeUtility.GetFieldOffset(field),
                ComponentType = new ComponentType(componentType, accessMode),
                ComponentRequirements = componentType == typeof(Transform)
                    ? new[] { typeof(Transform), componentType }
                    : new [] { componentType }
            });
        }
コード例 #26
0
        private static void FindFields(List <FieldInfo> result, Type type, int parentOffset)
        {
            var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            foreach (var field in fields)
            {
                int offset = parentOffset + UnsafeUtility.GetFieldOffset(field);

                if (field.FieldType.IsPrimitive || field.FieldType.IsPointer)
                {
                    int sizeOf = -1;

                    if (field.FieldType.IsPointer)
                    {
                        sizeOf = UnsafeUtility.SizeOf <IntPtr>();
                    }
                    else
                    {
                        sizeOf = UnsafeUtility.SizeOf(field.FieldType);
                    }

                    if ((sizeOf & (sizeOf - 1)) != 0)
                    {
                        throw new ArgumentException($"Field {type}.{field} is of size {sizeOf} which is not a power of two");
                    }

                    if (sizeOf != ElementSize)
                    {
                        throw new ArgumentException($"Field {type}.{field} is of size {sizeOf}; currently only types of size {ElementSize} bytes are allowed");
                    }

                    result.Add(new FieldInfo {
                        Offset = offset                        /*, Size = sizeOf */
                    });
                }
                else
                {
                    FindFields(result, field.FieldType, offset);
                }
            }
        }
コード例 #27
0
        // Fills the specified array (given as pointer and length) with offsets of this struct's fields that need to be in sync with plugin struct fields.
        public unsafe static int FillFieldOffsetsArray(int startFrom, int *fieldOffsets, int fieldOffsetsLength)
        {
            const int myFieldCount = 3;

            if (startFrom + myFieldCount > fieldOffsetsLength)
            {
                throw new ArgumentException($"{nameof(fieldOffsets)} (length {fieldOffsetsLength}) is not big enough for all {myFieldCount} fields of this struct (starting from {startFrom})!");
            }

            int fieldIndex = startFrom;

            fieldOffsets[fieldIndex] = UnsafeUtility.GetFieldOffset(typeof(HpIntArray).GetField("Data"));
            fieldIndex++;
            fieldOffsets[fieldIndex] = UnsafeUtility.GetFieldOffset(typeof(HpIntArray).GetField("m_Size", BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
            fieldIndex++;
            fieldOffsets[fieldIndex] = UnsafeUtility.GetFieldOffset(typeof(HpIntArray).GetField("m_Flags", BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
            fieldIndex++;

            Assert.AreEqual(myFieldCount, fieldIndex - startFrom);
            return(fieldIndex);
        }
コード例 #28
0
ファイル: FastEquality.cs プロジェクト: vesper666/UnityECS
        private static void CreateLayoutRecurse(Type type, int baseOffset, List <Layout> layouts, ref int begin,
                                                ref int end)
        {
            var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            foreach (var field in fields)
            {
                var offset = baseOffset + UnsafeUtility.GetFieldOffset(field);

                if (field.FieldType.IsPrimitive || field.FieldType.IsPointer || field.FieldType.IsClass)
                {
                    var sizeOf = -1;
                    if (field.FieldType.IsPointer || field.FieldType.IsClass)
                    {
                        sizeOf = UnsafeUtility.SizeOf <PointerSize>();
                    }
                    else
                    {
                        sizeOf = UnsafeUtility.SizeOf(field.FieldType);
                    }

                    if (end != offset)
                    {
                        layouts.Add(new Layout {
                            offset = begin, count = end - begin, Aligned4 = false
                        });
                        begin = offset;
                        end   = offset + sizeOf;
                    }
                    else
                    {
                        end += sizeOf;
                    }
                }
                else
                {
                    CreateLayoutRecurse(field.FieldType, offset, layouts, ref begin, ref end);
                }
            }
        }
コード例 #29
0
        // There's more fields of in-place array here, but we don't care about them
#pragma warning restore CS0649, CS0169

        // Fills the specified array (given as pointer and length) with offsets of this struct's fields that need to be in sync with plugin struct fields.
        public unsafe static int FillFieldOffsetsArray(int startFrom, int *fieldOffsets, int fieldOffsetsLength)
        {
            // List of (field name, isPublic) pairs
            List <KeyValuePair <string, bool> > fieldNamesAndAccess = new List <KeyValuePair <string, bool> >()
            {
                new KeyValuePair <string, bool>("m_Allocator", false),
                new KeyValuePair <string, bool>("m_NumTotalElements", false),
                new KeyValuePair <string, bool>("m_PartiallyFreed", false),
                new KeyValuePair <string, bool>("m_IsLocked", false)
            };

            // We need to account for m_Data* separately
            int myFieldCount = fieldNamesAndAccess.Count + 1;

            if (startFrom + myFieldCount > fieldOffsetsLength)
            {
                throw new ArgumentException($"{nameof(fieldOffsets)} (length {fieldOffsetsLength}) is not big enough for all {myFieldCount} fields of this struct (starting from {startFrom})!");
            }

            int fieldIndex = startFrom;

            UnsafeEx.FillFieldOffsetsArray(typeof(HpBlockStream), fieldNamesAndAccess, fieldOffsets, ref fieldIndex);

            if (BuildPlatformUtil.is32Bit())
            {
                // 32bit
                fieldOffsets[fieldIndex] = UnsafeUtility.GetFieldOffset(typeof(HpBlockStream).GetField("m_Data32Bit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
            }
            else
            {
                // 64bit
                fieldOffsets[fieldIndex] = UnsafeUtility.GetFieldOffset(typeof(HpBlockStream).GetField("m_Data64Bit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
            }

            fieldIndex++;

            Assert.AreEqual(myFieldCount, fieldIndex - startFrom);
            return(fieldIndex);
        }
コード例 #30
0
ファイル: TypeDecomposer.cs プロジェクト: NeroWeNeed/reactics
        private void Decompose(FieldInfo fieldInfo, string prefix, int baseOffset, int offset, Dictionary <string, FieldData> data, char separator = '.')
        {
            string path = prefix;

            if (fieldInfo.GetCustomAttribute <EmbedAttribute>() == null)
            {
                path = string.IsNullOrEmpty(path) ? fieldInfo.Name : $"{path}{separator}{fieldInfo.Name}";
                if (data.ContainsKey(path))
                {
                    throw new Exception("Path already exists");
                }
                data[path] = new FieldData(baseOffset + offset, fieldInfo);
            }
            if (fieldInfo.FieldType != null && Type.GetTypeCode(fieldInfo.FieldType) == TypeCode.Object && fieldInfo.FieldType.GetCustomAttribute <TerminalAttribute>() == null && (!fieldInfo.FieldType.IsGenericType || !typeof(FunctionPointer <>).IsAssignableFrom(fieldInfo.FieldType.GetGenericTypeDefinition())))
            {
                foreach (var innerFieldInfo in fieldInfo.FieldType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
                {
                    if (innerFieldInfo.GetCustomAttribute <HideInDecompositionAttribute>() == null)
                    {
                        Decompose(innerFieldInfo, path, baseOffset, UnsafeUtility.GetFieldOffset(fieldInfo), data, separator);
                    }
                }
            }
        }