|
|
@@ -43,8 +43,9 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
#>
|
|
|
<#=t#> value;
|
|
|
|
|
|
-#if USE_AWAIT_USING
|
|
|
- await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
+ try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
|
|
|
{
|
|
|
if (!await e.MoveNextAsync())
|
|
|
{
|
|
|
@@ -104,80 +105,13 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
}
|
|
|
<#
|
|
|
}
|
|
|
-#>
|
|
|
- }
|
|
|
- }
|
|
|
-#else
|
|
|
- var e = _source.GetAsyncEnumerator(_cancellationToken);
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- if (!await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- return value;
|
|
|
- }
|
|
|
-
|
|
|
- value = e.Current;
|
|
|
- }
|
|
|
-
|
|
|
-<#
|
|
|
- }
|
|
|
-#>
|
|
|
- while (await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
-<#
|
|
|
- }
|
|
|
#>
|
|
|
}
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
- await e.DisposeAsync().ConfigureAwait(false);
|
|
|
+ await e.DisposeAsync();
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|
|
|
@@ -187,8 +121,9 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
#>
|
|
|
<#=t#> value = null;
|
|
|
|
|
|
-#if USE_AWAIT_USING
|
|
|
- await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
+ try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
|
|
|
{
|
|
|
// 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.
|
|
|
@@ -317,163 +252,6 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
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
|
|
|
- {
|
|
|
- // 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- var cur = e.Current;
|
|
|
- var x = cur.GetValueOrDefault();
|
|
|
-
|
|
|
- if (x <#=comparison#> valueVal)
|
|
|
- {
|
|
|
- valueVal = x;
|
|
|
- value = cur;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- while (await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- return value;
|
|
|
- }
|
|
|
-
|
|
|
- var cur = e.Current;
|
|
|
-
|
|
|
- if (cur.HasValue)
|
|
|
- {
|
|
|
- valueVal = (value = cur).GetValueOrDefault();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-<#
|
|
|
- }
|
|
|
-#>
|
|
|
- while (await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- var cur = e.Current;
|
|
|
- var x = cur.GetValueOrDefault();
|
|
|
-
|
|
|
<#
|
|
|
if (shortCircuit)
|
|
|
{
|
|
|
@@ -501,9 +279,8 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
- await e.DisposeAsync().ConfigureAwait(false);
|
|
|
+ await e.DisposeAsync();
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|
|
|
@@ -545,8 +322,9 @@ foreach (var overload in new[] {
|
|
|
#>
|
|
|
<#=t#> value;
|
|
|
|
|
|
-#if USE_AWAIT_USING
|
|
|
- await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
+ try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
|
|
|
{
|
|
|
if (!await e.MoveNextAsync())
|
|
|
{
|
|
|
@@ -606,80 +384,13 @@ foreach (var overload in new[] {
|
|
|
}
|
|
|
<#
|
|
|
}
|
|
|
-#>
|
|
|
- }
|
|
|
- }
|
|
|
-#else
|
|
|
- var e = _source.GetAsyncEnumerator(_cancellationToken);
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- if (!await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- return value;
|
|
|
- }
|
|
|
-
|
|
|
- value = <#=overload.invoke#>;
|
|
|
- }
|
|
|
-
|
|
|
-<#
|
|
|
- }
|
|
|
-#>
|
|
|
- while (await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
-<#
|
|
|
- }
|
|
|
#>
|
|
|
}
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
- await e.DisposeAsync().ConfigureAwait(false);
|
|
|
+ await e.DisposeAsync();
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|
|
|
@@ -689,8 +400,9 @@ foreach (var overload in new[] {
|
|
|
#>
|
|
|
<#=t#> value = null;
|
|
|
|
|
|
-#if USE_AWAIT_USING
|
|
|
- await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
+ try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
|
|
|
{
|
|
|
// 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.
|
|
|
@@ -819,163 +531,6 @@ foreach (var overload in new[] {
|
|
|
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
|
|
|
- {
|
|
|
- // 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- var cur = <#=overload.invoke#>;
|
|
|
- var x = cur.GetValueOrDefault();
|
|
|
-
|
|
|
- if (x <#=comparison#> valueVal)
|
|
|
- {
|
|
|
- valueVal = x;
|
|
|
- value = cur;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- while (await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- 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().ConfigureAwait(false))
|
|
|
- {
|
|
|
- return value;
|
|
|
- }
|
|
|
-
|
|
|
- var cur = <#=overload.invoke#>;
|
|
|
-
|
|
|
- if (cur.HasValue)
|
|
|
- {
|
|
|
- valueVal = (value = cur).GetValueOrDefault();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-<#
|
|
|
- }
|
|
|
-#>
|
|
|
- while (await e.MoveNextAsync().ConfigureAwait(false))
|
|
|
- {
|
|
|
- var cur = <#=overload.invoke#>;
|
|
|
- var x = cur.GetValueOrDefault();
|
|
|
-
|
|
|
<#
|
|
|
if (shortCircuit)
|
|
|
{
|
|
|
@@ -1003,9 +558,8 @@ foreach (var overload in new[] {
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
- await e.DisposeAsync().ConfigureAwait(false);
|
|
|
+ await e.DisposeAsync();
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|