public static void TryFind_Char_Ordinal(ustring source, char searchTerm, Range?expectedForwardMatch, Range?expectedBackwardMatch)
        {
            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
            Utf8Span searchSpan = boundedSpan.Span;

            source = null; // to avoid accidentally using this for the remainder of the test

            // First, search forward

            bool wasFound = searchSpan.TryFind(searchTerm, out Range actualForwardMatch);

            Assert.Equal(expectedForwardMatch.HasValue, wasFound);

            if (wasFound)
            {
                AssertRangesEqual(searchSpan.Length, expectedForwardMatch.Value, actualForwardMatch);
            }

            // Also check Contains / StartsWith / SplitOn

            Assert.Equal(wasFound, searchSpan.Contains(searchTerm));
            Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm));

            (var before, var after) = searchSpan.SplitOn(searchTerm);
            if (wasFound)
            {
                Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality
Exemplo n.º 2
0
        public static void Normalize(string utf16Source, string utf16Expected, NormalizationForm normalizationForm)
        {
            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(utf16Source);
            Utf8Span utf8Source = boundedSpan.Span;

            // Quick IsNormalized tests

            Assert.Equal(utf16Source == utf16Expected, utf8Source.IsNormalized(normalizationForm));

            // Normalize and return new Utf8String instances

            ustring utf8Normalized = utf8Source.Normalize(normalizationForm);

            Assert.True(ustring.AreEquivalent(utf8Normalized, utf16Expected));

            // Normalize to byte arrays which are too small, expect -1 (failure)

            Assert.Equal(-1, utf8Source.Normalize(new byte[utf8Normalized.GetByteLength() - 1], normalizationForm));

            // Normalize to byte arrays which are the correct length, expect success,
            // then compare buffer contents for ordinal equality.

            foreach (int bufferLength in new int[] { utf8Normalized.GetByteLength() /* just right */, utf8Normalized.GetByteLength() + 1 /* with extra room */ })
            {
                byte[] dest = new byte[bufferLength];
                Assert.Equal(utf8Normalized.GetByteLength(), utf8Source.Normalize(dest, normalizationForm));
                Utf8Span normalizedSpan = Utf8Span.UnsafeCreateWithoutValidation(dest[..utf8Normalized.GetByteLength()]);
        public static void RunesProperty_FromData()
        {
            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234");
            Utf8Span span = boundedSpan.Span;

            var runesEnumerator = span.Runes.GetEnumerator();

            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x0012), runesEnumerator.Current);
            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x0123), runesEnumerator.Current);
            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x1234), runesEnumerator.Current);
            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x101234), runesEnumerator.Current);
            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x0012), runesEnumerator.Current);
            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x0123), runesEnumerator.Current);
            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x1234), runesEnumerator.Current);
            Assert.True(runesEnumerator.MoveNext());
            Assert.Equal(new Rune(0x101234), runesEnumerator.Current);
            Assert.False(runesEnumerator.MoveNext());
        }
        public static void CharsProperty_FromData()
        {
            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234");
            Utf8Span span = boundedSpan.Span;

            var charsEnumerator = span.Chars.GetEnumerator();

            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\U00000012', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\U00000123', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\U00001234', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\uDBC4', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\uDE34', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\U00000012', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\U00000123', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\U00001234', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\uDBC4', charsEnumerator.Current);
            Assert.True(charsEnumerator.MoveNext());
            Assert.Equal('\uDE34', charsEnumerator.Current);
            Assert.False(charsEnumerator.MoveNext());
        }
Exemplo n.º 5
0
        public static void Equals_NonOrdinal(string str1, string str2, StringComparison comparison, string culture, bool shouldCompareAsEqual)
        {
            Func <string, string, string, string, string, int> action = (str1, str2, comparison, culture, shouldCompareAsEqual) =>
            {
                using (new ThreadCultureChange(culture))
                {
                    using BoundedUtf8Span boundedSpan1 = new BoundedUtf8Span(str1);
                    using BoundedUtf8Span boundedSpan2 = new BoundedUtf8Span(str2);

                    Utf8Span span1 = boundedSpan1.Span;
                    Utf8Span span2 = boundedSpan2.Span;

                    StringComparison comparisonType = Enum.Parse <StringComparison>(comparison);
                    bool             expected       = bool.Parse(shouldCompareAsEqual);

                    Assert.Equal(expected, span1.Equals(span2, comparisonType));
                    Assert.Equal(expected, span2.Equals(span1, comparisonType));
                    Assert.Equal(expected, Utf8Span.Equals(span1, span2, comparisonType));
                    Assert.Equal(expected, Utf8Span.Equals(span2, span1, comparisonType));
                }

                return(RemoteExecutor.SuccessExitCode);
            };

            if (culture != null && PlatformDetection.IsUap) // need to apply a culture to the current thread
            {
                RemoteExecutor.Invoke(action, str1, str2, comparison.ToString(), culture, shouldCompareAsEqual.ToString()).Dispose();
            }
            else
            {
                action(str1, str2, comparison.ToString(), culture, shouldCompareAsEqual.ToString());
            }
        }
Exemplo n.º 6
0
        private static void SplitTest_Common(ustring source, Utf8SpanSplitDelegate splitAction, Range[] expectedRanges)
        {
            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
            Utf8Span span = boundedSpan.Span;
            int      totalSpanLengthInBytes = span.Bytes.Length;

            source = null; // to avoid inadvertently using this for the remainder of the method

            // First, run the split with default options and make sure the ranges are equivalent

            List <Range> actualRanges = new List <Range>();

            foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.None))
            {
                actualRanges.Add(GetRangeOfSubspan(span, slice));
            }

            Assert.Equal(expectedRanges, actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));

            // Next, run the split with empty entries removed

            actualRanges = new List <Range>();
            foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.RemoveEmptyEntries))
            {
                actualRanges.Add(GetRangeOfSubspan(span, slice));
            }

            Assert.Equal(expectedRanges.Where(range => !range.IsEmpty(totalSpanLengthInBytes)), actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));

            // Next, run the split with results trimmed (but allowing empty results)

            expectedRanges = (Range[])expectedRanges.Clone(); // clone the array since we're about to mutate it
            for (int i = 0; i < expectedRanges.Length; i++)
            {
                expectedRanges[i] = GetRangeOfSubspan(span, span[expectedRanges[i]].Trim());
            }

            actualRanges = new List <Range>();
            foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.TrimEntries))
            {
                actualRanges.Add(GetRangeOfSubspan(span, slice));
            }

            Assert.Equal(expectedRanges, actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));

            // Finally, run the split both trimmed and with empty entries removed

            actualRanges = new List <Range>();
            foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.TrimEntries | Utf8StringSplitOptions.RemoveEmptyEntries))
            {
                actualRanges.Add(GetRangeOfSubspan(span, slice));
            }

            Assert.Equal(expectedRanges.Where(range => !range.IsEmpty(totalSpanLengthInBytes)), actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));
        }
            static void RunTest(string testData, string expected, CultureInfo culture)
            {
                using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(testData);
                Utf8Span inputSpan = boundedSpan.Span;

                // First try the allocating APIs

                ustring expectedUtf8 = u8(expected) ?? ustring.Empty;
                ustring actualUtf8;

                if (culture is null)
                {
                    actualUtf8 = inputSpan.ToLowerInvariant();
                }
                else
                {
                    actualUtf8 = inputSpan.ToLower(culture);
                }

                Assert.Equal(expectedUtf8, actualUtf8);

                // Next, try the non-allocating APIs with too small a buffer

                if (expectedUtf8.Length > 0)
                {
                    byte[] bufferTooSmall = new byte[expectedUtf8.Length - 1];

                    if (culture is null)
                    {
                        Assert.Equal(-1, inputSpan.ToLowerInvariant(bufferTooSmall));
                    }
                    else
                    {
                        Assert.Equal(-1, inputSpan.ToLower(bufferTooSmall, culture));
                    }
                }

                // Then the non-allocating APIs with a properly sized buffer

                foreach (int bufferSize in new[] { expectedUtf8.Length, expectedUtf8.Length + 1 })
                {
                    byte[] buffer = new byte[expectedUtf8.Length];

                    if (culture is null)
                    {
                        Assert.Equal(expectedUtf8.Length, inputSpan.ToLowerInvariant(buffer));
                    }
                    else
                    {
                        Assert.Equal(expectedUtf8.Length, inputSpan.ToLower(buffer, culture));
                    }

                    Assert.True(expectedUtf8.AsBytes().SequenceEqual(buffer));
                }
            }
Exemplo n.º 8
0
        public static void Equals_NonOrdinal(string str1, string str2, StringComparison comparison, string culture, bool shouldCompareAsEqual)
        {
            using (new ThreadCultureChange(culture))
            {
                using BoundedUtf8Span boundedSpan1 = new BoundedUtf8Span(str1);
                using BoundedUtf8Span boundedSpan2 = new BoundedUtf8Span(str2);

                Utf8Span span1 = boundedSpan1.Span;
                Utf8Span span2 = boundedSpan2.Span;

                Assert.Equal(shouldCompareAsEqual, span1.Equals(span2, comparison));
                Assert.Equal(shouldCompareAsEqual, span2.Equals(span1, comparison));
                Assert.Equal(shouldCompareAsEqual, Utf8Span.Equals(span1, span2, comparison));
                Assert.Equal(shouldCompareAsEqual, Utf8Span.Equals(span2, span1, comparison));
            }
        }
Exemplo n.º 9
0
        public static void GetHashCode_WithComparison()
        {
            // Since hash code generation is randomized, it's possible (though unlikely) that
            // we might see unanticipated collisions. It's ok if this unit test fails once in
            // every few million runs, but if the unit test becomes truly flaky then that would
            // be indicative of a larger problem with hash code generation.
            //
            // These tests also make sure that the hash code is computed over the buffer rather
            // than over the reference.

            // Ordinal

            {
                using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ababaaAA");
                Utf8Span span = boundedSpan.Span;

                Assert.Equal(span[0..2].GetHashCode(StringComparison.Ordinal), span[2..4].GetHashCode(StringComparison.Ordinal));
Exemplo n.º 10
0
        public static void GetHashCode_Ordinal()
        {
            // Generate 17 all-null strings and make sure they have unique hash codes.
            // Assuming Marvin32 is a good PRF and has a full 32-bit output domain, we should
            // expect this test to fail only once every ~30 million runs due to the birthday paradox.
            // That should be good enough for inclusion as a unit test.

            HashSet <int> seenHashCodes = new HashSet <int>();

            for (int i = 0; i <= 16; i++)
            {
                using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(new byte[i]);
                Utf8Span span = boundedSpan.Span;

                Assert.True(seenHashCodes.Add(span.GetHashCode()), "This hash code was previously seen.");
            }
        }
Exemplo n.º 11
0
        public static void Split_Deconstruct()
        {
            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("a,b,c,d,e");
            Utf8Span span = boundedSpan.Span;

            // Note referential equality checks below (since we want to know exact slices
            // into the original buffer), not deep (textual) equality checks.

            {
                (Utf8Span a, Utf8Span b) = span.Split('x'); // not found
                Assert.True(a.Bytes == span.Bytes, "Expected referential equality of input.");
                Assert.True(b.Bytes == default);
            }

            {
                (Utf8Span a, Utf8Span b) = span.Split(',');
                Assert.True(a.Bytes == span.Bytes[..1]); // "a"
                Assert.True(b.Bytes == span.Bytes[2..]); // "b,c,d,e"
            }

            {
                (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e) = span.Split(',');
                Assert.True(a.Bytes == span.Bytes[0..1]); // "a"
                Assert.True(b.Bytes == span.Bytes[2..3]); // "b"
                Assert.True(c.Bytes == span.Bytes[4..5]); // "c"
                Assert.True(d.Bytes == span.Bytes[6..7]); // "d"
                Assert.True(e.Bytes == span.Bytes[8..9]); // "e"
            }

            {
                (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e, Utf8Span f, Utf8Span g, Utf8Span h) = span.Split(',');
                Assert.True(a.Bytes == span.Bytes[0..1]); // "a"
                Assert.True(b.Bytes == span.Bytes[2..3]); // "b"
                Assert.True(c.Bytes == span.Bytes[4..5]); // "c"
                Assert.True(d.Bytes == span.Bytes[6..7]); // "d"
                Assert.True(e.Bytes == span.Bytes[8..9]); // "e"
                Assert.True(f.Bytes == default);
                Assert.True(g.Bytes == default);
                Assert.True(h.Bytes == default);
            }
        }
Exemplo n.º 12
0
        public static void Trim(string input)
        {
            // Arrange

            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
            Utf8Span span = boundedSpan.Span;

            // Act

            Utf8Span trimmed = span.Trim();

            // Assert
            // Compute the trim manually and ensure it matches the trimmed span's characteristics.

            ReadOnlySpan <byte> utf8Bytes = span.Bytes;

            while (!utf8Bytes.IsEmpty)
            {
                OperationStatus status = Rune.DecodeFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed);
                Assert.Equal(OperationStatus.Done, status);

                if (!Rune.IsWhiteSpace(decodedRune))
                {
                    break;
                }

                utf8Bytes = utf8Bytes.Slice(bytesConsumed);
            }
            while (!utf8Bytes.IsEmpty)
            {
                OperationStatus status = Rune.DecodeLastFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed);
                Assert.Equal(OperationStatus.Done, status);

                if (!Rune.IsWhiteSpace(decodedRune))
                {
                    break;
                }

                utf8Bytes = utf8Bytes[..^ bytesConsumed];
Exemplo n.º 13
0
        public static void Split_Deconstruct_WithOptions()
        {
            using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("a, , b, c,, d, e");
            Utf8Span span = boundedSpan.Span;

            // Note referential equality checks below (since we want to know exact slices
            // into the original buffer), not deep (textual) equality checks.

            {
                (Utf8Span a, Utf8Span b) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries);
                Assert.True(a.Bytes == span.Bytes[..1]); // "a"
                Assert.True(b.Bytes == span.Bytes[2..]); // " , b, c,, d, e"
            }

            {
                (Utf8Span a, Utf8Span x, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries);
                Assert.True(a.Bytes == span.Bytes[0..1]);   // "a"
                Assert.True(x.Bytes == span.Bytes[2..3]);   // " "
                Assert.True(b.Bytes == span.Bytes[4..6]);   // " b"
                Assert.True(c.Bytes == span.Bytes[7..9]);   // " c"
                Assert.True(d.Bytes == span.Bytes[11..13]); // " d"
                Assert.True(e.Bytes == span.Bytes[14..]);   // " e"
            }

            {
                (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e, Utf8Span f, Utf8Span g, Utf8Span h) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries | Utf8StringSplitOptions.TrimEntries);
                Assert.True(a.Bytes == span.Bytes[0..1]);   // "a"
                Assert.True(b.Bytes == span.Bytes[5..6]);   // "b"
                Assert.True(c.Bytes == span.Bytes[8..9]);   // "c"
                Assert.True(d.Bytes == span.Bytes[12..13]); // "d"
                Assert.True(e.Bytes == span.Bytes[15..]);   // "e"
                Assert.True(f.Bytes == default);
                Assert.True(g.Bytes == default);
                Assert.True(h.Bytes == default);
            }
        }