What's wrong
NominalWordWrap (Extensions/StringExtensions.cs:238-253) validates its widths like this:
if (wrapWidth <= 0) throw new ArgumentOutOfRangeException(...);
if (nominalGlyphWidth <= 0) throw new ArgumentOutOfRangeException(...);
...
int maxCharsPerLine = Math.Max(1, (int)Math.Floor(wrapWidth / nominalGlyphWidth));
Every comparison with NaN is false, so a NaN width gets past both checks. (int)Math.Floor(NaN) is 0, and Math.Max(1, 0) turns that into a limit of 1 character per line. The XML docs say ArgumentOutOfRangeException is thrown when either width "is not greater than zero", and NaN is not greater than zero.
Repro (.NET 10, HEAD 0386c21)
"hello world foo".NominalWordWrap(float.NaN, 1f) returns 13 lines: h, e, l, l, o, w, o, r, l, d, f, o, o.
"hello world foo".NominalWordWrap(10f, float.NaN) gives the same result.
Why it matters
NaN widths are common in UI code, for example a zero-size window divided by zero or an unmeasured font. When one arrives, the text silently turns into a vertical column of single characters instead of failing where the bad value came in.
The same cast has a second problem, found by tracing but not run here. The library also targets net5–net8 and netstandard2.0, where float→int conversion does not saturate. On those targets, wrapWidth = float.PositiveInfinity, or any ratio above int.MaxValue, casts to int.MinValue, Math.Max clamps that to 1, and the text again wraps one character per line. .NET 9+ saturates, so on those runtimes it does not happen.
Suggested fix
if (!(wrapWidth > 0)) throw new ArgumentOutOfRangeException(nameof(wrapWidth), wrapWidth, "...");
if (!(nominalGlyphWidth > 0)) throw new ArgumentOutOfRangeException(nameof(nominalGlyphWidth), nominalGlyphWidth, "...");
double ratio = Math.Floor((double)wrapWidth / nominalGlyphWidth);
int maxCharsPerLine = ratio >= int.MaxValue ? int.MaxValue : Math.Max(1, (int)ratio);
Acceptance criteria
- Either argument being
float.NaN throws ArgumentOutOfRangeException naming that parameter.
wrapWidth = float.PositiveInfinity (or a huge ratio) puts each input line on a single output line, on every target framework.
- Tests cover both cases.
What's wrong
NominalWordWrap(Extensions/StringExtensions.cs:238-253) validates its widths like this:Every comparison with
NaNis false, so a NaN width gets past both checks.(int)Math.Floor(NaN)is0, andMath.Max(1, 0)turns that into a limit of 1 character per line. The XML docs sayArgumentOutOfRangeExceptionis thrown when either width "is not greater than zero", and NaN is not greater than zero.Repro (.NET 10, HEAD
0386c21)"hello world foo".NominalWordWrap(float.NaN, 1f)returns 13 lines:h,e,l,l,o,w,o,r,l,d,f,o,o."hello world foo".NominalWordWrap(10f, float.NaN)gives the same result.Why it matters
NaN widths are common in UI code, for example a zero-size window divided by zero or an unmeasured font. When one arrives, the text silently turns into a vertical column of single characters instead of failing where the bad value came in.
The same cast has a second problem, found by tracing but not run here. The library also targets net5–net8 and netstandard2.0, where float→int conversion does not saturate. On those targets,
wrapWidth = float.PositiveInfinity, or any ratio aboveint.MaxValue, casts toint.MinValue,Math.Maxclamps that to 1, and the text again wraps one character per line. .NET 9+ saturates, so on those runtimes it does not happen.Suggested fix
Acceptance criteria
float.NaNthrowsArgumentOutOfRangeExceptionnaming that parameter.wrapWidth = float.PositiveInfinity(or a huge ratio) puts each input line on a single output line, on every target framework.