| File: System\Xml\Core\ReadContentAsBinaryHelperAsync.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.Diagnostics; using System.Threading.Tasks; namespace System.Xml { internal sealed partial class ReadContentAsBinaryHelper { // Internal methods internal async Task<int> ReadContentAsBase64Async(byte[] buffer, int index, int count) // only ever awaited, so no need to separate out argument handling { ArgumentNullException.ThrowIfNull(buffer); if (index < 0 || (uint)count > buffer.Length - index) { throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count)); } switch (_state) { case State.None: if (!_reader.CanReadContentAs()) { throw _reader.CreateReadContentAsException(nameof(ReadContentAsBase64)); } if (!await InitAsync().ConfigureAwait(false)) { return 0; } break; case State.InReadContent: // if we have a correct decoder, go read if (_decoder == _base64Decoder) { // read more binary data return await ReadContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } break; case State.InReadElementContent: throw new InvalidOperationException(SR.Xml_MixingBinaryContentMethods); default: Debug.Fail($"Unexpected state {_state}"); return 0; } Debug.Assert(_state == State.InReadContent); // setup base64 decoder InitBase64Decoder(); // read more binary data return await ReadContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } internal async Task<int> ReadContentAsBinHexAsync(byte[] buffer, int index, int count) // only ever awaited, so no need to separate out argument handling { ArgumentNullException.ThrowIfNull(buffer); if (index < 0 || (uint)count > buffer.Length - index) { throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count)); } switch (_state) { case State.None: if (!_reader.CanReadContentAs()) { throw _reader.CreateReadContentAsException(nameof(ReadContentAsBinHex)); } if (!await InitAsync().ConfigureAwait(false)) { return 0; } break; case State.InReadContent: // if we have a correct decoder, go read if (_decoder == _binHexDecoder) { // read more binary data return await ReadContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } break; case State.InReadElementContent: throw new InvalidOperationException(SR.Xml_MixingBinaryContentMethods); default: Debug.Fail($"Unexpected state {_state}"); return 0; } Debug.Assert(_state == State.InReadContent); // setup binhex decoder InitBinHexDecoder(); // read more binary data return await ReadContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } internal async Task<int> ReadElementContentAsBase64Async(byte[] buffer, int index, int count) // only ever awaited, so no need to separate out argument handling { ArgumentNullException.ThrowIfNull(buffer); if (index < 0 || (uint)count > buffer.Length - index) { throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count)); } switch (_state) { case State.None: if (_reader.NodeType != XmlNodeType.Element) { throw _reader.CreateReadElementContentAsException(nameof(ReadElementContentAsBase64)); } if (!await InitOnElementAsync().ConfigureAwait(false)) { return 0; } break; case State.InReadContent: throw new InvalidOperationException(SR.Xml_MixingBinaryContentMethods); case State.InReadElementContent: // if we have a correct decoder, go read if (_decoder == _base64Decoder) { // read more binary data return await ReadElementContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } break; default: Debug.Fail($"Unexpected state {_state}"); return 0; } Debug.Assert(_state == State.InReadElementContent); // setup base64 decoder InitBase64Decoder(); // read more binary data return await ReadElementContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } internal async Task<int> ReadElementContentAsBinHexAsync(byte[] buffer, int index, int count) // only ever awaited, so no need to separate out argument handling { ArgumentNullException.ThrowIfNull(buffer); if (index < 0 || (uint)count > buffer.Length - index) { throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count)); } switch (_state) { case State.None: if (_reader.NodeType != XmlNodeType.Element) { throw _reader.CreateReadElementContentAsException(nameof(ReadElementContentAsBinHex)); } if (!await InitOnElementAsync().ConfigureAwait(false)) { return 0; } break; case State.InReadContent: throw new InvalidOperationException(SR.Xml_MixingBinaryContentMethods); case State.InReadElementContent: // if we have a correct decoder, go read if (_decoder == _binHexDecoder) { // read more binary data return await ReadElementContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } break; default: Debug.Fail($"Unexpected state {_state}"); return 0; } Debug.Assert(_state == State.InReadElementContent); // setup binhex decoder InitBinHexDecoder(); // read more binary data return await ReadElementContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); } internal async Task FinishAsync() { if (_state != State.None) { while (await MoveToNextContentNodeAsync(true).ConfigureAwait(false)) ; if (_state == State.InReadElementContent) { if (_reader.NodeType != XmlNodeType.EndElement) { throw new XmlException(SR.Xml_InvalidNodeType, _reader.NodeType.ToString(), _reader as IXmlLineInfo); } // move off the EndElement await _reader.ReadAsync().ConfigureAwait(false); } } Reset(); } // Private methods private async Task<bool> InitAsync() { // make sure we are on a content node if (!await MoveToNextContentNodeAsync(false).ConfigureAwait(false)) { return false; } _state = State.InReadContent; _isEnd = false; return true; } private async Task<bool> InitOnElementAsync() { Debug.Assert(_reader.NodeType == XmlNodeType.Element); bool isEmpty = _reader.IsEmptyElement; // move to content or off the empty element await _reader.ReadAsync().ConfigureAwait(false); if (isEmpty) { return false; } // make sure we are on a content node if (!await MoveToNextContentNodeAsync(false).ConfigureAwait(false)) { if (_reader.NodeType != XmlNodeType.EndElement) { throw new XmlException(SR.Xml_InvalidNodeType, _reader.NodeType.ToString(), _reader as IXmlLineInfo); } // move off end element await _reader.ReadAsync().ConfigureAwait(false); return false; } _state = State.InReadElementContent; _isEnd = false; return true; } private async Task<int> ReadContentAsBinaryAsync(byte[] buffer, int index, int count) { Debug.Assert(_decoder != null); if (_isEnd) { Reset(); return 0; } _decoder.SetNextOutputBuffer(buffer, index, count); while (true) { // use streaming ReadValueChunk if the reader supports it if (_canReadValueChunk) { while (true) { if (_valueOffset < _valueChunkLength) { int decodedCharsCount = _decoder.Decode(_valueChunk!, _valueOffset, _valueChunkLength - _valueOffset); _valueOffset += decodedCharsCount; } if (_decoder.IsFull) { return _decoder.DecodedCount; } Debug.Assert(_valueOffset == _valueChunkLength); if ((_valueChunkLength = await _reader.ReadValueChunkAsync(_valueChunk!, 0, ChunkSize).ConfigureAwait(false)) == 0) { break; } _valueOffset = 0; } } else { // read what is reader.Value string value = await _reader.GetValueAsync().ConfigureAwait(false); int decodedCharsCount = _decoder.Decode(value, _valueOffset, value.Length - _valueOffset); _valueOffset += decodedCharsCount; if (_decoder.IsFull) { return _decoder.DecodedCount; } } _valueOffset = 0; // move to next textual node in the element content; throw on sub elements if (!await MoveToNextContentNodeAsync(true).ConfigureAwait(false)) { _isEnd = true; return _decoder.DecodedCount; } } } private async Task<int> ReadElementContentAsBinaryAsync(byte[] buffer, int index, int count) { if (count == 0) { return 0; } // read binary int decoded = await ReadContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false); if (decoded > 0) { return decoded; } // if 0 bytes returned check if we are on a closing EndElement, throw exception if not if (_reader.NodeType != XmlNodeType.EndElement) { throw new XmlException(SR.Xml_InvalidNodeType, _reader.NodeType.ToString(), _reader as IXmlLineInfo); } // move off the EndElement await _reader.ReadAsync().ConfigureAwait(false); _state = State.None; return 0; } private async Task<bool> MoveToNextContentNodeAsync(bool moveIfOnContentNode) { do { switch (_reader.NodeType) { case XmlNodeType.Attribute: return !moveIfOnContentNode; case XmlNodeType.Text: case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: case XmlNodeType.CDATA: if (!moveIfOnContentNode) { return true; } break; case XmlNodeType.ProcessingInstruction: case XmlNodeType.Comment: case XmlNodeType.EndEntity: // skip comments, pis and end entity nodes break; case XmlNodeType.EntityReference: if (_reader.CanResolveEntity) { _reader.ResolveEntity(); break; } goto default; default: return false; } moveIfOnContentNode = false; } while (await _reader.ReadAsync().ConfigureAwait(false)); return false; } } }