public void EncodeIntoBuffer_SurrogatePairs()
        {
            // Arange
            TextEncoder encoder = new ConfigurableScalarTextEncoder(_ => false);

            const string X    = "\U00000058"; // LATIN CAPITAL LETTER X (ascii)
            const string Pair = "\U0001033A"; // GOTHIC LETTER KUSMA (surrogate pair)

            const string eX    = "[0058]";
            const string ePair = "[1033A]";

            // Act & assert
            Assert.Equal("", encoder.Encode(""));

            Assert.Equal(eX, encoder.Encode(X));                               // no iteration, block
            Assert.Equal(eX + eX, encoder.Encode(X + X));                      // two iterations, no block
            Assert.Equal(eX + eX + eX, encoder.Encode(X + X + X));             // two iterations, block

            Assert.Equal(ePair, encoder.Encode(Pair));                         // one iteration, no block
            Assert.Equal(ePair + ePair, encoder.Encode(Pair + Pair));          // two iterations, no block

            Assert.Equal(eX + ePair, encoder.Encode(X + Pair));                // two iterations, no block
            Assert.Equal(ePair + eX, encoder.Encode(Pair + X));                // one iteration, block

            Assert.Equal(eX + ePair + eX, encoder.Encode(X + Pair + X));       // two iterations, block, even length
            Assert.Equal(ePair + eX + ePair, encoder.Encode(Pair + X + Pair)); // three iterations, no block, odd length
        }
        public void EncodeUtf16_OperationStatus_SurrogateHandlingEdgeCases(char[] input, int destBufferSize, bool isFinalBlock, string expectedOutput, int expectedCharsConsumed, OperationStatus expectedResult)
        {
            // Arrange

            var encoder = new ConfigurableScalarTextEncoder(_ => true); // allow all well-formed scalars

            using BoundedMemory <char> boundedInput  = BoundedMemory.AllocateFromExistingData(input);
            using BoundedMemory <char> boundedOutput = BoundedMemory.Allocate <char>(destBufferSize);

            // Act

            OperationStatus actualResult = encoder.Encode(boundedInput.Span, boundedOutput.Span, out int actualCharsConsumed, out int actualCharsWritten, isFinalBlock);

            // Assert

            Assert.Equal(expectedResult, actualResult);
            Assert.Equal(expectedCharsConsumed, actualCharsConsumed);
            Assert.Equal(expectedOutput, boundedOutput.Span.Slice(0, actualCharsWritten).ToString());
        }
        public void EncodeUtf16_OperationStatus_AlphaNumericOnly(string input, int destBufferSize, string expectedOutput, int expectedCharsConsumed, OperationStatus expectedResult)
        {
            // Arrange

            var encoder = new ConfigurableScalarTextEncoder(scalar => UnicodeUtility.IsInRangeInclusive((uint)scalar | 0x20, 'a', 'z')); // allow only [A-Za-z] unescaped

            using BoundedMemory <char> boundedInput  = BoundedMemory.AllocateFromExistingData <char>(input.AsSpan());
            using BoundedMemory <char> boundedOutput = BoundedMemory.Allocate <char>(destBufferSize);

            // Act

            OperationStatus actualResult = encoder.Encode(boundedInput.Span, boundedOutput.Span, out int actualCharsConsumed, out int actualCharsWritten);

            // Assert

            Assert.Equal(expectedResult, actualResult);
            Assert.Equal(expectedCharsConsumed, actualCharsConsumed);
            Assert.Equal(expectedOutput, boundedOutput.Span.Slice(0, actualCharsWritten).ToString());
        }