| File: System\Xml\BinHexDecoder.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; namespace System.Xml { internal sealed class BinHexDecoder : IncrementalReadDecoder { // // Fields // private byte[]? _buffer; private int _startIndex; private int _curIndex; private int _endIndex; private bool _hasHalfByteCached; private byte _cachedHalfByte; // // IncrementalReadDecoder interface // internal override int DecodedCount { get { return _curIndex - _startIndex; } } internal override bool IsFull { get { return _curIndex == _endIndex; } } internal override int Decode(char[] chars, int startPos, int len) { ArgumentNullException.ThrowIfNull(chars); ArgumentOutOfRangeException.ThrowIfNegative(len); ArgumentOutOfRangeException.ThrowIfNegative(startPos); ArgumentOutOfRangeException.ThrowIfGreaterThan(len, chars.Length - startPos); if (len == 0) { return 0; } Decode(chars.AsSpan(startPos, len), _buffer.AsSpan(_curIndex, _endIndex - _curIndex), ref _hasHalfByteCached, ref _cachedHalfByte, out int charsDecoded, out int bytesDecoded); _curIndex += bytesDecoded; return charsDecoded; } internal override int Decode(string str, int startPos, int len) { ArgumentNullException.ThrowIfNull(str); ArgumentOutOfRangeException.ThrowIfNegative(len); ArgumentOutOfRangeException.ThrowIfNegative(startPos); ArgumentOutOfRangeException.ThrowIfGreaterThan(len, str.Length - startPos); if (len == 0) { return 0; } Decode(str.AsSpan(startPos, len), _buffer.AsSpan(_curIndex, _endIndex - _curIndex), ref _hasHalfByteCached, ref _cachedHalfByte, out int charsDecoded, out int bytesDecoded); _curIndex += bytesDecoded; return charsDecoded; } internal override void Reset() { _hasHalfByteCached = false; _cachedHalfByte = 0; } internal override void SetNextOutputBuffer(Array buffer, int index, int count) { Debug.Assert(buffer != null); Debug.Assert(count >= 0); Debug.Assert(index >= 0); Debug.Assert(buffer.Length - index >= count); Debug.Assert((buffer as byte[]) != null); _buffer = (byte[])buffer; _startIndex = index; _curIndex = index; _endIndex = index + count; } // // Static methods // public static byte[] Decode(ReadOnlySpan<char> chars, bool allowOddChars) { int len = chars.Length; if (len == 0) { return Array.Empty<byte>(); } byte[] bytes = new byte[(len + 1) / 2]; bool hasHalfByteCached = false; byte cachedHalfByte = 0; Decode(chars, bytes, ref hasHalfByteCached, ref cachedHalfByte, out _, out int bytesDecoded); if (hasHalfByteCached && !allowOddChars) { throw new XmlException(SR.Xml_InvalidBinHexValueOddCount, (string?)null); } if (bytesDecoded < bytes.Length) { Array.Resize(ref bytes, bytesDecoded); } return bytes; } // // Private methods // private static void Decode(ReadOnlySpan<char> chars, Span<byte> bytes, ref bool hasHalfByteCached, ref byte cachedHalfByte, out int charsDecoded, out int bytesDecoded) { int iByte = 0; int iChar = 0; for (; iChar < chars.Length; iChar++) { if ((uint)iByte >= (uint)bytes.Length) { break; // ran out of space in the destination buffer } byte halfByte; char ch = chars[iChar]; int val = HexConverter.FromChar(ch); if (val != 0xFF) { halfByte = (byte)val; } else if (XmlCharType.IsWhiteSpace(ch)) { continue; } else { throw new XmlException(SR.Xml_InvalidBinHexValue, ch.ToString()); } if (hasHalfByteCached) { bytes[iByte++] = (byte)((cachedHalfByte << 4) + halfByte); hasHalfByteCached = false; } else { cachedHalfByte = halfByte; hasHalfByteCached = true; } } bytesDecoded = iByte; charsDecoded = iChar; } } }