Skip to content

NaturalStringComparer mis-orders numeric chunks of 10+ digits due to Int32 overflow fallback #41

Closed

Description

@matt-edmondson

What's wrong

In Sorting/NaturalStringComparer.cs, Compare, numeric chunks are compared like this:

if (int.TryParse(xMatch.Value, out int xNum) && int.TryParse(yMatch.Value, out int yNum))
{
    int numComparison = xNum.CompareTo(yNum);
    ...
}
else // Otherwise, compare them as strings
{
    int stringComparison = string.Compare(xMatch.Value, yMatch.Value, StringComparison.Ordinal);
    ...
}

Any digit run whose value exceeds Int32.MaxValue (2147483647 — i.e. 10+ digit chunks) fails int.TryParse, so the && short-circuits and the code falls into the ordinal-string-comparison branch, even though both chunks are purely numeric.

Why it matters / concrete failure scenario

Compare("file9999999999", "file10000000000") compares a 10-digit chunk against an 11-digit chunk. Numerically the first is smaller, but with both int.TryParse calls failing, the code falls back to string.Compare("9999999999", "10000000000", StringComparison.Ordinal), which compares '9' vs '1' and returns the wrong sign — the comparer reports the larger number as "less than" the smaller one. This silently corrupts sort order for any filenames/IDs with large embedded numbers (large IDs, timestamps, sequence numbers past ~2.1 billion).

The existing test Compare_LargeNumbers_HandledCorrectly only checks "file1000000" vs "file9999999" (both well within Int32 range), so this overflow path is untested.

Suggested fix

Don't parse to int. Either:

  • strip leading zeros and compare numeric chunks first by length, then by ordinal digit string (the standard trick for arbitrary-precision numeric strings), or
  • parse with long/System.Numerics.BigInteger as a quick mitigation (raises the threshold but is still bounded).

Add a test case with 10+ digit chunks crossing the Int32.MaxValue boundary.

Acceptance criteria

  • NaturalStringComparer.Compare orders numeric chunks correctly for values exceeding Int32.MaxValue (e.g. "file9999999999" sorts before "file10000000000").
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions