private void AddStringsWithSameHashCode(int numberOfStrings)
        {
            string[] cachedStrings = new string[numberOfStrings];
            int[]    hashCodes     = new int[numberOfStrings];

            for (int i = 0; i < numberOfStrings; i++)
            {
                string strPart2 = "1" + String.Concat(Enumerable.Repeat("4428939786", i));
                hashCodes[i] = AddString("Random string ", strPart2, (string cachedString) =>
                {
                    _cache.GetDebugInfo().ShouldBe(new WeakStringCache.DebugInfo()
                    {
                        LiveStringCount      = 1,
                        CollectedStringCount = 0,
                    });
                    cachedStrings[i] = cachedString;
                });

                if (i > 0)
                {
                    // The strings have been carefully constructed to have the same hash code.
                    hashCodes[i].ShouldBe(hashCodes[i - 1]);
                }
            }

            // There are no cache hits when iterating over our strings again because the last one always wins and steals the slot.
            for (int i = 0; i < numberOfStrings; i++)
            {
                InternableString stringCopy            = new InternableString(new string(cachedStrings[i].ToCharArray()));
                string           cachedStringFromCache = _cache.GetOrCreateEntry(ref stringCopy, out bool cacheHit);
                cacheHit.ShouldBeFalse();
                cachedStringFromCache.ShouldNotBeSameAs(cachedStrings[i]);
            }
        }
        private int AddString(string strPart1, string strPart2, Action <string> callbackToRunWithTheStringAlive)
        {
            // Compose the string with SB so it doesn't get interned by the runtime.
            string           testString       = new StringBuilder(strPart1).Append(strPart2).ToString();
            InternableString testStringTarget = new InternableString(testString);

            int hashCode = testStringTarget.GetHashCode();

            string cachedString = _cache.GetOrCreateEntry(ref testStringTarget, out bool cacheHit);

            cacheHit.ShouldBeFalse();
            cachedString.ShouldBeSameAs(testString);

            callbackToRunWithTheStringAlive(cachedString);

            // Verify that the string is really in the cache and the cache returns the interned instance.
            string           testStringCopy       = new StringBuilder(strPart1).Append(strPart2).ToString();
            InternableString testStringCopyTarget = new InternableString(testStringCopy);

            cachedString = _cache.GetOrCreateEntry(ref testStringCopyTarget, out cacheHit);
            cacheHit.ShouldBeTrue();
            cachedString.ShouldBeSameAs(testString);

            // Trigger full GC and verify that nothing has changed since we're still keeping testString alive.
            GC.Collect();

            callbackToRunWithTheStringAlive(cachedString);

            testStringCopyTarget = new InternableString(testStringCopy);
            cachedString         = _cache.GetOrCreateEntry(ref testStringCopyTarget, out cacheHit);
            cacheHit.ShouldBeTrue();
            cachedString.ShouldBeSameAs(testString);

            return(hashCode);
        }
Example #3
0
        public void ReferenceEqualsReturnsExpectedValue()
        {
            string           str = "Test";
            InternableString internableString = new InternableString(str);

            internableString.ReferenceEquals(str).ShouldBeTrue();
            internableString = new InternableString(new string(str.ToCharArray()));
            internableString.ReferenceEquals(str).ShouldBeFalse();
        }
Example #4
0
        [InlineData("0123456789")] // even number of characters
        public void GetHashCodeIsStableRegardlessOfSpanLength(string testString)
        {
            int hashCode = new InternableString(testString).GetHashCode();

            // Chop the string into 2-3 parts and verify that the hash code is unchanged.
            for (int i = 0; i < testString.Length - 1; i++)
            {
                for (int j = i + 1; j < testString.Length; j++)
                {
                    SpanBasedStringBuilder stringBuilder = new SpanBasedStringBuilder();
                    stringBuilder.Append(testString.Substring(0, i));
                    stringBuilder.Append(testString.Substring(i, j - i));
                    stringBuilder.Append(testString.Substring(j));
                    InternableString internableString = new InternableString(stringBuilder);
                    internableString.GetHashCode().ShouldBe(hashCode);
                }
            }
        }
Example #5
0
        public void EqualsReturnsExpectedValue(InterningTestData.TestDatum datum)
        {
            InternableString internableString = new InternableString(MakeSpanBasedStringBuilder(datum));

            internableString.Equals(string.Empty).ShouldBe(internableString.Length == 0);

            string substr = datum.Fragments[0] ?? string.Empty;

            internableString.Equals(substr).ShouldBe(substr.Length == internableString.Length);

            if (datum.Fragments.Length > 1)
            {
                substr += datum.Fragments[1];
                internableString.Equals(substr).ShouldBe(substr.Length == internableString.Length);

                internableString.Equals(datum.ToString()).ShouldBeTrue();
            }

            internableString.Equals("Things").ShouldBeFalse();
        }