public unsafe void ValidateMatchesCache(int foundCacheIndex, ref EntityQueryBuilder builder, int *delegateTypeIndices, int delegateTypeCount)
        {
            if (!builder.ShallowEquals(ref m_CacheCheckQueryBuilders[foundCacheIndex]))
            {
                goto bad;
            }

            var cached = m_CacheCheckDelegateTypeIndices[foundCacheIndex];

            if (cached.Length != delegateTypeCount)
            {
                goto bad;
            }

            for (var i = 0; i < delegateTypeCount; ++i)
            {
                if (cached[i] != delegateTypeIndices[i])
                {
                    goto bad;
                }
            }

            return;

bad:
            throw new InvalidOperationException("Type signature does not match cached");
        }
        // this is a specialized function intended only for validation that builders are hashing and getting cached
        // correctly without unexpected collisions. "Equals" is hard to truly validate because the type may not
        // fully be constructed yet due to ForEach not getting called yet.
        internal bool ShallowEquals(ref EntityQueryBuilder other)
        {
            #if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (!ReferenceEquals(m_System, other.m_System))
            {
                throw new InvalidOperationException($"Suspicious comparison of {nameof(EntityQueryBuilder)}s with different {nameof(ComponentSystem)}s");
            }
            #endif

            return
                (m_Any.Equals(ref other.m_Any) &&
                 m_None.Equals(ref other.m_None) &&
                 m_All.Equals(ref other.m_All) &&
                 ReferenceEquals(m_Query, other.m_Query));
        }
        public unsafe int CreateCachedQuery(uint hash, EntityQuery query
            #if ENABLE_UNITY_COLLECTIONS_CHECKS
                                            , ref EntityQueryBuilder builder, int *delegateTypeIndices, int delegateTypeCount
            #endif
                                            )
        {
            #if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            #endif

            var index = 0;

            // find open slot or create one

            for (;;)
            {
                if (index == m_CacheHashes.Length)
                {
                    var newSize = m_CacheHashes.Length + Math.Max(m_CacheHashes.Length / 2, 1);

                    UnityEngine.Debug.LogError(
                        $"{nameof(EntityQueryCache)} is too small to hold the current number of queries this {nameof(ComponentSystem)} is running. The cache automatically expanded to {newSize}, " +
                        $"but this may cause a GC. Set cache size at init time via {nameof(ComponentSystem.InitEntityQueryCache)}() to a large enough number to ensure no allocations are required at run time.");

                    Array.Resize(ref m_CacheHashes, newSize);
                    Array.Resize(ref m_CachedEntityQueries, newSize);

                    #if ENABLE_UNITY_COLLECTIONS_CHECKS
                    Array.Resize(ref m_CacheCheckQueryBuilders, newSize);
                    Array.Resize(ref m_CacheCheckDelegateTypeIndices, newSize);
                    #endif
                    break;
                }

                if (m_CachedEntityQueries[index] == null)
                {
                    break;
                }

                #if ENABLE_UNITY_COLLECTIONS_CHECKS
                if (m_CacheHashes[index] == hash)
                {
                    throw new InvalidOperationException($"Unexpected {nameof(CreateCachedQuery)} with hash {hash} that already exists in cache at slot {index}");
                }
                #endif

                ++index;
            }

            // store in cache

            m_CacheHashes[index]         = hash;
            m_CachedEntityQueries[index] = query;

            #if ENABLE_UNITY_COLLECTIONS_CHECKS
            m_CacheCheckQueryBuilders[index] = builder;
            var checkDelegateTypeIndices = new int[delegateTypeCount];
            for (var i = 0; i < delegateTypeCount; ++i)
            {
                checkDelegateTypeIndices[i] = delegateTypeIndices[i];
            }
            m_CacheCheckDelegateTypeIndices[index] = checkDelegateTypeIndices;
            #endif

            return(index);
        }