Very odd OutOfMemoryException issue with GetHashCode(string)
In .NET there's a class called StringComparer. It has some handy helpers, like the InvariantCultureIgnoreCase StringComparer. These classes also implement a method called GetHashCode(string), which produces the hashcode in the scope of the comparer, so if you're calling that method on the InvariantCultureIgnoreCase variant, you get the hashcode for that scope.
This is handy as hashcodes are important, for example to find duplicates. We recently ran into an issue with this, as passing a large string to this method caused it to throw an OutOfMemoryException, but ... there was plenty of memory left. What was even stranger was that the length of the string differs per appdomain and even machine!
So I wrote a little app, sourcecode is below. It fiddles with digits to find the maximum string length one can pass to GetHashCode before it throws this exception. Of course, this is of little use, but it illustrates the problem and is a good repro-case for Microsoft as well. The code below will crash with an OutOfMemoryException as it will test the found length by increasing it with 1. I'll post this to Connect (yes, I'm that naive, but perhaps this time they'll fix it). Tested on .NET 3.5 SP1 and XP sp3 as well as .NET 2.0 and XP sp3 (I'm pretty sure the error is in Win32, so it might be OS dependent even).
using System; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace OOMTester { public class Program { static void Main(string[] args) { int digitIndex = 0; char[] digits = new char[8]; StringComparer comparer = StringComparer.InvariantCultureIgnoreCase; while(digitIndex<digits.Length) { for(int i=9;i>=0;i--) { digits[digitIndex] = i.ToString()[0]; for(int j=digitIndex+1;j<digits.Length;j++) { digits[j] = '0'; } int length = Convert.ToInt32(new string(digits)); bool succeeded = false; try { int hashCode = comparer.GetHashCode(new string('X', length)); succeeded = true; } catch(OutOfMemoryException) { // failed. } catch(ArgumentException) { // out of range } if(succeeded) { digitIndex++; Console.WriteLine("Digit index increased: {0}. Full digits: {1}", digitIndex, new string(digits)); break; } } } Console.WriteLine("MaxLength: {0}", new string(digits)); int maxLength = Convert.ToInt32(new string(digits)); string xmlData = new string('X', maxLength); int hashcode = comparer.GetHashCode(xmlData); Console.WriteLine("Length: {0}. Hashcode: {1}", xmlData.Length, hashcode); maxLength++; xmlData = new string('X', maxLength); hashcode = comparer.GetHashCode(xmlData); Console.WriteLine("Length: {0}. Hashcode: {1}", xmlData.Length, hashcode); } } }
Update: Connect issue.