public Archetype *GetOrCreateArchetype(ComponentTypeInArchetype *types, int count,
                                               EntityGroupManager groupManager)
        {
            var type = GetExistingArchetype(types, count);

            if (type != null)
            {
                return(type);
            }

            AssertArchetypeComponents(types, count);

            // This is a new archetype, allocate it and add it to the hash map
            type             = (Archetype *)m_ArchetypeChunkAllocator.Allocate(sizeof(Archetype), 8);
            type->TypesCount = count;
            type->Types      =
                (ComponentTypeInArchetype *)m_ArchetypeChunkAllocator.Construct(
                    sizeof(ComponentTypeInArchetype) * count, 4, types);
            type->EntityCount = 0;
            type->ChunkCount  = 0;

            type->NumSharedComponents   = 0;
            type->SharedComponentOffset = null;

            var disabledTypeIndex = TypeManager.GetTypeIndex <Disabled>();

            type->Disabled = false;
            for (var i = 0; i < count; ++i)
            {
                if (TypeManager.GetComponentType(types[i].TypeIndex).Category == TypeManager.TypeCategory.ISharedComponentData)
                {
                    ++type->NumSharedComponents;
                }
                if (types[i].TypeIndex == disabledTypeIndex)
                {
                    type->Disabled = true;
                }
            }

            // Compute how many IComponentData types store Entities and need to be patched.
            // Types can have more than one entity, which means that this count is not necessarily
            // the same as the type count.
            int scalarEntityPatchCount = 0;
            int bufferEntityPatchCount = 0;

            for (var i = 0; i < count; ++i)
            {
                var ct            = TypeManager.GetComponentType(types[i].TypeIndex);
                var entityOffsets = ct.EntityOffsets;
                if (entityOffsets == null)
                {
                    continue;
                }

                if (ct.BufferCapacity >= 0)
                {
                    bufferEntityPatchCount += entityOffsets.Length;
                }
                else
                {
                    scalarEntityPatchCount += entityOffsets.Length;
                }
            }

            var chunkDataSize = Chunk.GetChunkBufferSize(type->TypesCount, type->NumSharedComponents);

            // FIXME: proper alignment
            type->Offsets                = (int *)m_ArchetypeChunkAllocator.Allocate(sizeof(int) * count, 4);
            type->SizeOfs                = (int *)m_ArchetypeChunkAllocator.Allocate(sizeof(int) * count, 4);
            type->TypeMemoryOrder        = (int *)m_ArchetypeChunkAllocator.Allocate(sizeof(int) * count, 4);
            type->ScalarEntityPatches    = (EntityRemapUtility.EntityPatchInfo *)m_ArchetypeChunkAllocator.Allocate(sizeof(EntityRemapUtility.EntityPatchInfo) * scalarEntityPatchCount, 4);
            type->ScalarEntityPatchCount = scalarEntityPatchCount;
            type->BufferEntityPatches    = (EntityRemapUtility.BufferEntityPatchInfo *)m_ArchetypeChunkAllocator.Allocate(sizeof(EntityRemapUtility.BufferEntityPatchInfo) * bufferEntityPatchCount, 4);
            type->BufferEntityPatchCount = bufferEntityPatchCount;

            var bytesPerInstance = 0;

            for (var i = 0; i < count; ++i)
            {
                var cType  = TypeManager.GetComponentType(types[i].TypeIndex);
                var sizeOf = cType.SizeInChunk; // Note that this includes internal capacity and header overhead for buffers.
                type->SizeOfs[i] = sizeOf;

                bytesPerInstance += sizeOf;
            }

            type->ChunkCapacity = chunkDataSize / bytesPerInstance;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (bytesPerInstance > chunkDataSize)
            {
                throw new ArgumentException(
                          $"Entity archetype component data is too large. The maximum component data is {chunkDataSize} but the component data is {bytesPerInstance}");
            }

            Assert.IsTrue(Chunk.kMaximumEntitiesPerChunk >= type->ChunkCapacity);
#endif

            // For serialization a stable ordering of the components in the
            // chunk is desired. The type index is not stable, since it depends
            // on the order in which types are added to the TypeManager.
            // A permutation of the types ordered by a TypeManager-generated
            // memory ordering is used instead.
            var memoryOrderings = new NativeArray <UInt64>(count, Allocator.Temp);
            for (int i = 0; i < count; ++i)
            {
                memoryOrderings[i] = TypeManager.GetComponentType(types[i].TypeIndex).MemoryOrdering;
            }
            for (int i = 0; i < count; ++i)
            {
                int index = i;
                while (index > 1 && memoryOrderings[i] < memoryOrderings[type->TypeMemoryOrder[index - 1]])
                {
                    type->TypeMemoryOrder[index] = type->TypeMemoryOrder[index - 1];
                    --index;
                }
                type->TypeMemoryOrder[index] = i;
            }
            memoryOrderings.Dispose();

            var usedBytes = 0;
            for (var i = 0; i < count; ++i)
            {
                var index  = type->TypeMemoryOrder[i];
                var sizeOf = type->SizeOfs[index];

                type->Offsets[index] = usedBytes;

                usedBytes += sizeOf * type->ChunkCapacity;
            }

            type->NumManagedArrays   = 0;
            type->ManagedArrayOffset = null;

            for (var i = 0; i < count; ++i)
            {
                if (TypeManager.GetComponentType(types[i].TypeIndex).Category == TypeManager.TypeCategory.Class)
                {
                    ++type->NumManagedArrays;
                }
            }

            if (type->NumManagedArrays > 0)
            {
                type->ManagedArrayOffset = (int *)m_ArchetypeChunkAllocator.Allocate(sizeof(int) * count, 4);
                var mi = 0;
                for (var i = 0; i < count; ++i)
                {
                    var cType = TypeManager.GetComponentType(types[i].TypeIndex);
                    if (cType.Category == TypeManager.TypeCategory.Class)
                    {
                        type->ManagedArrayOffset[i] = mi++;
                    }
                    else
                    {
                        type->ManagedArrayOffset[i] = -1;
                    }
                }
            }

            if (type->NumSharedComponents > 0)
            {
                type->SharedComponentOffset = (int *)m_ArchetypeChunkAllocator.Allocate(sizeof(int) * count, 4);
                var mi = 0;
                for (var i = 0; i < count; ++i)
                {
                    var cType = TypeManager.GetComponentType(types[i].TypeIndex);
                    if (cType.Category == TypeManager.TypeCategory.ISharedComponentData)
                    {
                        type->SharedComponentOffset[i] = mi++;
                    }
                    else
                    {
                        type->SharedComponentOffset[i] = -1;
                    }
                }
            }

            // Fill in arrays of scalar and buffer entity patches
            var scalarPatchInfo = type->ScalarEntityPatches;
            var bufferPatchInfo = type->BufferEntityPatches;
            for (var i = 0; i != count; i++)
            {
                var ct      = TypeManager.GetComponentType(types[i].TypeIndex);
                var offsets = ct.EntityOffsets;
                if (ct.BufferCapacity >= 0)
                {
                    bufferPatchInfo = EntityRemapUtility.AppendBufferEntityPatches(bufferPatchInfo, offsets, type->Offsets[i], type->SizeOfs[i], ct.ElementSize);
                }
                else
                {
                    scalarPatchInfo = EntityRemapUtility.AppendEntityPatches(scalarPatchInfo, offsets, type->Offsets[i], type->SizeOfs[i]);
                }
            }
            type->ScalarEntityPatchCount = scalarEntityPatchCount;
            type->BufferEntityPatchCount = bufferEntityPatchCount;

            // Update the list of all created archetypes
            type->PrevArchetype = m_LastArchetype;
            m_LastArchetype     = type;

            UnsafeLinkedListNode.InitializeList(&type->ChunkList);
            UnsafeLinkedListNode.InitializeList(&type->ChunkListWithEmptySlots);
            type->FreeChunksBySharedComponents.Init(8);

            m_TypeLookup.Add(GetHash(types, count), (IntPtr)type);

            type->SystemStateCleanupComplete = ArchetypeSystemStateCleanupComplete(type);
            type->SystemStateCleanupNeeded   = ArchetypeSystemStateCleanupNeeded(type);

            groupManager.OnArchetypeAdded(type);

            return(type);
        }