Преглед изворни кода

Use C# 8.0 in primitive Min and Max.

Bart De Smet пре 6 година
родитељ
комит
b138e06b0a

Разлика између датотеке није приказан због своје велике величине
+ 687 - 96
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.cs


+ 446 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.tt

@@ -43,6 +43,71 @@ foreach (var m in new[] { "Max", "Min" })
 #>
                 <#=t#> value;
 
+#if CSHARP8
+                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                {
+                    if (!await e.MoveNextAsync())
+                    {
+                        throw Error.NoElements();
+                    }
+
+                    value = e.Current;
+
+<#
+            if (isFloatingPoint && m == "Max")
+            {
+#>
+                    // NaN is ordered less than all other values. We need to do explicit checks
+                    // to ensure this, but once we've found a value that is not NaN we need no
+                    // longer worry about it, so first loop until such a value is found (or not,
+                    // as the case may be).
+
+                    while (<#=t#>.IsNaN(value))
+                    {
+                        if (!await e.MoveNextAsync())
+                        {
+                            return value;
+                        }
+
+                        value = e.Current;
+                    }
+
+<#
+            }
+#>
+                    while (await e.MoveNextAsync())
+                    {
+                        var x = e.Current;
+                        if (x <#=comparison#> value)
+                        {
+                            value = x;
+                        }
+<#
+            if (isFloatingPoint && m == "Min")
+            {
+#>
+                        else
+                        {
+                            // Normally NaN < anything is false, as is anything < NaN
+                            // However, this leads to some irksome outcomes in Min and Max.
+                            // If we use those semantics then Min(NaN, 5.0) is NaN, but
+                            // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
+                            // ordering where NaN is smaller than every value, including
+                            // negative infinity.
+                            // Not testing for NaN therefore isn't an option, but since we
+                            // can't find a smaller value, we can short-circuit.
+
+                            if (<#=t#>.IsNaN(x))
+                            {
+                                return x;
+                            }
+                        }
+<#
+            }
+#>
+                    }
+                }
+#else
                 var e = _source.GetAsyncEnumerator(_cancellationToken);
 
                 try
@@ -112,6 +177,7 @@ foreach (var m in new[] { "Max", "Min" })
                 {
                     await e.DisposeAsync().ConfigureAwait(false);
                 }
+#endif
 
                 return value;
 <#
@@ -121,6 +187,162 @@ foreach (var m in new[] { "Max", "Min" })
 #>
                 <#=t#> value = null;
 
+#if CSHARP8
+                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                {
+                    // Start off knowing that we've a non-null value (or exit here, knowing we don't)
+                    // so we don't have to keep testing for nullity.
+                    do
+                    {
+                        if (!await e.MoveNextAsync())
+                        {
+                            return value;
+                        }
+
+                        value = e.Current;
+                    }
+                    while (!value.HasValue);
+
+                    // Keep hold of the wrapped value, and do comparisons on that, rather than
+                    // using the lifted operation each time.
+                    var valueVal = value.GetValueOrDefault();
+
+<#
+            if (isInteger && m == "Max")
+            {
+#>
+                    if (valueVal >= 0)
+                    {
+                        // We can fast-path this case where we know HasValue will
+                        // never affect the outcome, without constantly checking
+                        // if we're in such a state. Similar fast-paths could
+                        // be done for other cases, but as all-positive or mostly-
+                        // positive integer values are quite common in real-world
+                        // uses, it's only been done for int? and long?.
+
+                        while (await e.MoveNextAsync())
+                        {
+                            var cur = e.Current;
+                            var x = cur.GetValueOrDefault();
+
+                            if (x <#=comparison#> valueVal)
+                            {
+                                valueVal = x;
+                                value = cur;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        while (await e.MoveNextAsync())
+                        {
+                            var cur = e.Current;
+                            var x = cur.GetValueOrDefault();
+
+                            // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                            // unless nulls either never happen or always happen.
+                            if (cur.HasValue & x <#=comparison#> valueVal)
+                            {
+                                valueVal = x;
+                                value = cur;
+                            }
+                        }
+                    }
+<#
+            }
+            else if (isFloatingPoint && m == "Min")
+            {
+#>
+                    while (await e.MoveNextAsync())
+                    {
+                        var cur = e.Current;
+                        if (cur.HasValue)
+                        {
+                            var x = cur.GetValueOrDefault();
+                            if (x <#=comparison#> valueVal)
+                            {
+                                valueVal = x;
+                                value = cur;
+                            }
+                            else
+                            {
+                                // Normally NaN < anything is false, as is anything < NaN
+                                // However, this leads to some irksome outcomes in Min and Max.
+                                // If we use those semantics then Min(NaN, 5.0) is NaN, but
+                                // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
+                                // ordering where NaN is smaller than every value, including
+                                // negative infinity.
+                                // Not testing for NaN therefore isn't an option, but since we
+                                // can't find a smaller value, we can short-circuit.
+
+                                if (<#=t.TrimEnd('?')#>.IsNaN(x))
+                                {
+                                    return cur;
+                                }
+                            }
+                        }
+                    }
+<#
+            }
+            else
+            {
+                if (isFloatingPoint && m == "Max")
+                {
+#>
+                    // NaN is ordered less than all other values. We need to do explicit checks
+                    // to ensure this, but once we've found a value that is not NaN we need no
+                    // longer worry about it, so first loop until such a value is found (or not,
+                    // as the case may be).
+
+                    while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
+                    {
+                        if (!await e.MoveNextAsync())
+                        {
+                            return value;
+                        }
+
+                        var cur = e.Current;
+
+                        if (cur.HasValue)
+                        {
+                            valueVal = (value = cur).GetValueOrDefault();
+                        }
+                    }
+
+<#
+                }
+#>
+                    while (await e.MoveNextAsync())
+                    {
+                        var cur = e.Current;
+                        var x = cur.GetValueOrDefault();
+
+<#
+                if (shortCircuit)
+                {
+#>
+                        if (cur.HasValue && x <#=comparison#> valueVal)
+<#
+                }
+                else
+                {
+#>
+                        // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                        // unless nulls either never happen or always happen.
+                        if (cur.HasValue & x <#=comparison#> valueVal)
+<#
+                }
+#>
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                    }
+<#
+            }
+#>
+                }
+#else
                 var e = _source.GetAsyncEnumerator(_cancellationToken);
 
                 try
@@ -281,6 +503,7 @@ foreach (var m in new[] { "Max", "Min" })
                 {
                     await e.DisposeAsync().ConfigureAwait(false);
                 }
+#endif
 
                 return value;
 <#
@@ -322,6 +545,71 @@ foreach (var overload in new[] {
 #>
                 <#=t#> value;
 
+#if CSHARP8
+                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                {
+                    if (!await e.MoveNextAsync())
+                    {
+                        throw Error.NoElements();
+                    }
+
+                    value = <#=overload.invoke#>;
+
+<#
+            if (isFloatingPoint && m == "Max")
+            {
+#>
+                    // NaN is ordered less than all other values. We need to do explicit checks
+                    // to ensure this, but once we've found a value that is not NaN we need no
+                    // longer worry about it, so first loop until such a value is found (or not,
+                    // as the case may be).
+
+                    while (<#=t#>.IsNaN(value))
+                    {
+                        if (!await e.MoveNextAsync())
+                        {
+                            return value;
+                        }
+
+                        value = <#=overload.invoke#>;
+                    }
+
+<#
+            }
+#>
+                    while (await e.MoveNextAsync())
+                    {
+                        var x = <#=overload.invoke#>;
+                        if (x <#=comparison#> value)
+                        {
+                            value = x;
+                        }
+<#
+            if (isFloatingPoint && m == "Min")
+            {
+#>
+                        else
+                        {
+                            // Normally NaN < anything is false, as is anything < NaN
+                            // However, this leads to some irksome outcomes in Min and Max.
+                            // If we use those semantics then Min(NaN, 5.0) is NaN, but
+                            // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
+                            // ordering where NaN is smaller than every value, including
+                            // negative infinity.
+                            // Not testing for NaN therefore isn't an option, but since we
+                            // can't find a smaller value, we can short-circuit.
+
+                            if (<#=t#>.IsNaN(x))
+                            {
+                                return x;
+                            }
+                        }
+<#
+            }
+#>
+                    }
+                }
+#else
                 var e = _source.GetAsyncEnumerator(_cancellationToken);
 
                 try
@@ -391,6 +679,7 @@ foreach (var overload in new[] {
                 {
                     await e.DisposeAsync().ConfigureAwait(false);
                 }
+#endif
 
                 return value;
 <#
@@ -400,6 +689,162 @@ foreach (var overload in new[] {
 #>
                 <#=t#> value = null;
 
+#if CSHARP8
+                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                {
+                    // Start off knowing that we've a non-null value (or exit here, knowing we don't)
+                    // so we don't have to keep testing for nullity.
+                    do
+                    {
+                        if (!await e.MoveNextAsync())
+                        {
+                            return value;
+                        }
+
+                        value = <#=overload.invoke#>;
+                    }
+                    while (!value.HasValue);
+
+                    // Keep hold of the wrapped value, and do comparisons on that, rather than
+                    // using the lifted operation each time.
+                    var valueVal = value.GetValueOrDefault();
+
+<#
+            if (isInteger && m == "Max")
+            {
+#>
+                    if (valueVal >= 0)
+                    {
+                        // We can fast-path this case where we know HasValue will
+                        // never affect the outcome, without constantly checking
+                        // if we're in such a state. Similar fast-paths could
+                        // be done for other cases, but as all-positive or mostly-
+                        // positive integer values are quite common in real-world
+                        // uses, it's only been done for int? and long?.
+
+                        while (await e.MoveNextAsync())
+                        {
+                            var cur = <#=overload.invoke#>;
+                            var x = cur.GetValueOrDefault();
+
+                            if (x <#=comparison#> valueVal)
+                            {
+                                valueVal = x;
+                                value = cur;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        while (await e.MoveNextAsync())
+                        {
+                            var cur = <#=overload.invoke#>;
+                            var x = cur.GetValueOrDefault();
+
+                            // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                            // unless nulls either never happen or always happen.
+                            if (cur.HasValue & x <#=comparison#> valueVal)
+                            {
+                                valueVal = x;
+                                value = cur;
+                            }
+                        }
+                    }
+<#
+            }
+            else if (isFloatingPoint && m == "Min")
+            {
+#>
+                    while (await e.MoveNextAsync())
+                    {
+                        var cur = <#=overload.invoke#>;
+                        if (cur.HasValue)
+                        {
+                            var x = cur.GetValueOrDefault();
+                            if (x <#=comparison#> valueVal)
+                            {
+                                valueVal = x;
+                                value = cur;
+                            }
+                            else
+                            {
+                                // Normally NaN < anything is false, as is anything < NaN
+                                // However, this leads to some irksome outcomes in Min and Max.
+                                // If we use those semantics then Min(NaN, 5.0) is NaN, but
+                                // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
+                                // ordering where NaN is smaller than every value, including
+                                // negative infinity.
+                                // Not testing for NaN therefore isn't an option, but since we
+                                // can't find a smaller value, we can short-circuit.
+
+                                if (<#=t.TrimEnd('?')#>.IsNaN(x))
+                                {
+                                    return cur;
+                                }
+                            }
+                        }
+                    }
+<#
+            }
+            else
+            {
+                if (isFloatingPoint && m == "Max")
+                {
+#>
+                    // NaN is ordered less than all other values. We need to do explicit checks
+                    // to ensure this, but once we've found a value that is not NaN we need no
+                    // longer worry about it, so first loop until such a value is found (or not,
+                    // as the case may be).
+
+                    while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
+                    {
+                        if (!await e.MoveNextAsync())
+                        {
+                            return value;
+                        }
+
+                        var cur = <#=overload.invoke#>;
+
+                        if (cur.HasValue)
+                        {
+                            valueVal = (value = cur).GetValueOrDefault();
+                        }
+                    }
+
+<#
+                }
+#>
+                    while (await e.MoveNextAsync())
+                    {
+                        var cur = <#=overload.invoke#>;
+                        var x = cur.GetValueOrDefault();
+
+<#
+                if (shortCircuit)
+                {
+#>
+                        if (cur.HasValue && x <#=comparison#> valueVal)
+<#
+                }
+                else
+                {
+#>
+                        // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                        // unless nulls either never happen or always happen.
+                        if (cur.HasValue & x <#=comparison#> valueVal)
+<#
+                }
+#>
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                    }
+<#
+            }
+#>
+                }
+#else
                 var e = _source.GetAsyncEnumerator(_cancellationToken);
 
                 try
@@ -560,6 +1005,7 @@ foreach (var overload in new[] {
                 {
                     await e.DisposeAsync().ConfigureAwait(false);
                 }
+#endif
 
                 return value;
 <#

Неке датотеке нису приказане због велике количине промена