Repository: dotnet/runtime
File: src/libraries/System.Private.CoreLib/src/System/Array.cs (C#)

Code before:
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
            }

            for (int i = 0; i < array.Length; i++)
            {
                if (match(array[i]))
                {
                    return array[i];
                }
            }
            return default;
        }

        public static T[] FindAll<T>(T[] array, Predicate<T> match)
        {
            if (array == null)
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);

            if (match == null)
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);

            if (array.Length == 0)
                return array;

            ArrayPool<T>? sharedArrayPool = null;
            T[]? matchArray = null; // only rent/allocate if needed

            uint matchesFound = 0;
            for (int i = 0; i < array.Length; i++)
            {
                if (match(array[i]))
                {
                    // Just assume the rest is matches, it is cheaper than to always allocate an empty List,
                    // that might need to grow the internal array & copy the values, and is expensive to do, both copy & allocations+GC.
                    // Large arrays are rented, cheaper than to grow a List multiple times & no GC, only release
                    matchArray ??= GetMatchArray(array.Length - i);
                    matchArray[matchesFound++] = array[i];
                }
            }

            if (matchArray == null)
                return EmptyArray<T>.Value;

            if (matchArray.Length == matchesFound && sharedArrayPool == null)
                return matchArray; // If we have matched all allocated and not rented the array, we can just return it, brilliant suggestion from lechu445

            // If all were matches, we could have returned the existing array,
            // but the old code always made a new array, so it would be a breaking change
            T[] result = new T[matchesFound];
            Buffer.Memmove(ref MemoryMarshal.GetArrayDataReference(result), ref MemoryMarshal.GetArrayDataReference(matchArray), matchesFound);
            sharedArrayPool?.Return(matchArray);

Review comment (suggestion):
This is not the correct pattern to allocate optional arrays.

Instead, use a `ValueListBuilder` with capacity equals original array.

Code after:
            }

            if (match == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
            }

            for (int i = 0; i < array.Length; i++)
            {
                if (match(array[i]))
                {
                    return array[i];
                }
            }
            return default;
        }

        public static T[] FindAll<T>(T[] array, Predicate<T> match)
        {
            if (array == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
            }

            if (match == null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
            }

            InlineArray4<T> stackAllocatedMatches = default;
            Span<T> span = stackAllocatedMatches;
            int foundCount = 0;
            T[]? values = null;

            foreach (T value in array)
            {
                if (match(value))
                {
                    if (foundCount >= span.Length)
                    {
                        values = new T[Math.Min((uint)span.Length * 2, (uint)array.Length)];
                        span.CopyTo(values);
                        span = values;
                    }

                    span[foundCount++] = value;
                }
            }

            return values?.Length == foundCount ? values : span[..foundCount].ToArray();
        }