#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span <char> destination) #pragma warning restore IDE0060 { UnicodeDebug.AssertIsValidScalar(scalarValue); // For inputs 0x0000..0x10FFFF, log2 will return 0..20. // (It counts the number of bits following the highest set bit.) // // We divide by 4 to get the number of nibbles (this rounds down), // then +1 to account for rounding effects. This also accounts for // that when log2 results in an exact multiple of 4, no rounding has // taken place, but we need to include a char for the preceding '0x1'. // Finally, we +4 to account for the "&#x" prefix and the ";" suffix, // then -1 to get the index of the last legal location we want to write to. // >> +1 +4 -1 = +4 int idxOfSemicolon = (int)((uint)BitOperations.Log2(scalarValue) / 4) + 4; Debug.Assert(4 <= idxOfSemicolon && idxOfSemicolon <= 9, "Expected '�'..''."); if (!SpanUtility.IsValidIndex(destination, idxOfSemicolon)) { goto OutOfSpaceInner; } destination[idxOfSemicolon] = ';'; // It's more efficient to write 4 chars at a time instead of 1 char. // The '0' at the end will be overwritten. if (!SpanUtility.TryWriteChars(destination, '&', '#', 'x', '0')) { Debug.Fail("We should've had enough room to write 4 chars."); } destination = destination.Slice(3, idxOfSemicolon - 3); for (int i = destination.Length - 1; SpanUtility.IsValidIndex(destination, i); i--) { char asUpperHex = HexConverter.ToCharUpper((int)scalarValue); destination[i] = asUpperHex; scalarValue >>= 4; // write a nibble - not a byte - at a time } return(destination.Length + 4); OutOfSpaceInner: return(-1); }
private bool TryEncodeUnicodeScalarUtf8(uint unicodeScalar, Span <char> utf16ScratchBuffer, Span <byte> utf8Destination, out int bytesWritten) { if (!TryEncodeUnicodeScalar(unicodeScalar, utf16ScratchBuffer, out int charsWritten)) { // We really don't expect any encoder to exceed 24 escaped chars per input scalar. // If this happens, throw an exception and we can figure out if we want to support it // in the future. ThrowArgumentException_MaxOutputCharsPerInputChar(); } // Transcode chars -> bytes one at a time. utf16ScratchBuffer = utf16ScratchBuffer.Slice(0, charsWritten); int dstIdx = 0; while (!utf16ScratchBuffer.IsEmpty) { if (Rune.DecodeFromUtf16(utf16ScratchBuffer, out Rune nextScalarValue, out int scalarUtf16CodeUnitCount) != OperationStatus.Done) { // Wrote bad UTF-16 data, we cannot transcode to UTF-8. ThrowArgumentException_MaxOutputCharsPerInputChar(); } uint utf8lsb = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)nextScalarValue.Value); do { if (SpanUtility.IsValidIndex(utf8Destination, dstIdx)) { utf8Destination[dstIdx++] = (byte)utf8lsb; } else { bytesWritten = 0; // ran out of space in the destination return(false); } } while ((utf8lsb >>= 8) != 0); utf16ScratchBuffer = utf16ScratchBuffer.Slice(scalarUtf16CodeUnitCount); } bytesWritten = dstIdx; return(true); }
internal override int EncodeUtf8(Rune value, Span <byte> destination) { if (value.Value == '<') { if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'l', (byte)'t', (byte)';')) { goto OutOfSpace; } return(4); } else if (value.Value == '>') { if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'g', (byte)'t', (byte)';')) { goto OutOfSpace; } return(4); } else if (value.Value == '&') { if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'a', (byte)'m', (byte)'p', (byte)';')) { goto OutOfSpace; } return(5); } else if (value.Value == '\"') { if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'q', (byte)'u', (byte)'o', (byte)'t', (byte)';')) { goto OutOfSpace; } return(6); } else { return(TryEncodeScalarAsHex(this, (uint)value.Value, destination)); } OutOfSpace: return(-1);
internal override int EncodeUtf16(Rune value, Span <char> destination) { if (value.Value == '<') { if (!SpanUtility.TryWriteChars(destination, '&', 'l', 't', ';')) { goto OutOfSpace; } return(4); } else if (value.Value == '>') { if (!SpanUtility.TryWriteChars(destination, '&', 'g', 't', ';')) { goto OutOfSpace; } return(4); } else if (value.Value == '&') { if (!SpanUtility.TryWriteChars(destination, '&', 'a', 'm', 'p', ';')) { goto OutOfSpace; } return(5); } else if (value.Value == '\"') { if (!SpanUtility.TryWriteChars(destination, '&', 'q', 'u', 'o', 't', ';')) { goto OutOfSpace; } return(6); } else { return(TryEncodeScalarAsHex(this, (uint)value.Value, destination)); } OutOfSpace: return(-1);
#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span <byte> destination) #pragma warning restore IDE0060 { UnicodeDebug.AssertIsValidScalar(scalarValue); // See comments in the UTF-16 equivalent method later in this file. int idxOfSemicolon = (int)((uint)BitOperations.Log2(scalarValue) / 4) + 4; Debug.Assert(4 <= idxOfSemicolon && idxOfSemicolon <= 9, "Expected '�'..''."); if (!SpanUtility.IsValidIndex(destination, idxOfSemicolon)) { goto OutOfSpaceInner; } destination[idxOfSemicolon] = (byte)';'; if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'#', (byte)'x', (byte)'0')) { Debug.Fail("We should've had enough room to write 4 bytes."); } destination = destination.Slice(3, idxOfSemicolon - 3); for (int i = destination.Length - 1; SpanUtility.IsValidIndex(destination, i); i--) { char asUpperHex = HexConverter.ToCharUpper((int)scalarValue); destination[i] = (byte)asUpperHex; scalarValue >>= 4; // write a nibble - not a byte - at a time } return(destination.Length + 4); OutOfSpaceInner: return(-1); }