1 2 using System.Diagnostics; 3 4 using System.Threading.Tasks; 5 6 namespace System.Xml 7 { 8 internal partial class ReadContentAsBinaryHelper { 9 10 // Internal methods 11 ReadContentAsBase64Async( byte[] buffer, int index, int count )12 internal async Task< int > ReadContentAsBase64Async( byte[] buffer, int index, int count ) { 13 // check arguments 14 if ( buffer == null ) { 15 throw new ArgumentNullException( "buffer" ); 16 } 17 if ( count < 0 ) { 18 throw new ArgumentOutOfRangeException( "count" ); 19 } 20 if ( index < 0 ) { 21 throw new ArgumentOutOfRangeException( "index" ); 22 } 23 if ( buffer.Length - index < count ) { 24 throw new ArgumentOutOfRangeException( "count" ); 25 } 26 27 switch ( state ) { 28 case State.None: 29 if ( !reader.CanReadContentAs() ) { 30 throw reader.CreateReadContentAsException( "ReadContentAsBase64" ); 31 } 32 if ( !await InitAsync().ConfigureAwait(false) ) { 33 return 0; 34 } 35 break; 36 case State.InReadContent: 37 // if we have a correct decoder, go read 38 if ( decoder == base64Decoder ) { 39 // read more binary data 40 return await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 41 } 42 break; 43 case State.InReadElementContent: 44 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) ); 45 default: 46 Debug.Assert( false ); 47 return 0; 48 } 49 50 Debug.Assert( state == State.InReadContent ); 51 52 // setup base64 decoder 53 InitBase64Decoder(); 54 55 // read more binary data 56 return await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 57 } 58 ReadContentAsBinHexAsync( byte[] buffer, int index, int count )59 internal async Task< int > ReadContentAsBinHexAsync( byte[] buffer, int index, int count ) { 60 // check arguments 61 if ( buffer == null ) { 62 throw new ArgumentNullException( "buffer" ); 63 } 64 if ( count < 0 ) { 65 throw new ArgumentOutOfRangeException( "count" ); 66 } 67 if ( index < 0 ) { 68 throw new ArgumentOutOfRangeException( "index" ); 69 } 70 if ( buffer.Length - index < count ) { 71 throw new ArgumentOutOfRangeException( "count" ); 72 } 73 74 switch ( state ) { 75 case State.None: 76 if ( !reader.CanReadContentAs() ) { 77 throw reader.CreateReadContentAsException( "ReadContentAsBinHex" ); 78 } 79 if ( !await InitAsync().ConfigureAwait(false) ) { 80 return 0; 81 } 82 break; 83 case State.InReadContent: 84 // if we have a correct decoder, go read 85 if ( decoder == binHexDecoder ) { 86 // read more binary data 87 return await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 88 } 89 break; 90 case State.InReadElementContent: 91 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) ); 92 default: 93 Debug.Assert( false ); 94 return 0; 95 } 96 97 Debug.Assert( state == State.InReadContent ); 98 99 // setup binhex decoder 100 InitBinHexDecoder(); 101 102 // read more binary data 103 return await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 104 } 105 ReadElementContentAsBase64Async( byte[] buffer, int index, int count )106 internal async Task< int > ReadElementContentAsBase64Async( byte[] buffer, int index, int count ) { 107 // check arguments 108 if ( buffer == null ) { 109 throw new ArgumentNullException( "buffer" ); 110 } 111 if ( count < 0 ) { 112 throw new ArgumentOutOfRangeException( "count" ); 113 } 114 if ( index < 0 ) { 115 throw new ArgumentOutOfRangeException( "index" ); 116 } 117 if ( buffer.Length - index < count ) { 118 throw new ArgumentOutOfRangeException( "count" ); 119 } 120 121 switch ( state ) { 122 case State.None: 123 if ( reader.NodeType != XmlNodeType.Element ) { 124 throw reader.CreateReadElementContentAsException( "ReadElementContentAsBase64" ); 125 } 126 if ( !await InitOnElementAsync().ConfigureAwait(false) ) { 127 return 0; 128 } 129 break; 130 case State.InReadContent: 131 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) ); 132 case State.InReadElementContent: 133 // if we have a correct decoder, go read 134 if ( decoder == base64Decoder ) { 135 // read more binary data 136 return await ReadElementContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 137 } 138 break; 139 default: 140 Debug.Assert( false ); 141 return 0; 142 } 143 144 Debug.Assert( state == State.InReadElementContent ); 145 146 // setup base64 decoder 147 InitBase64Decoder(); 148 149 // read more binary data 150 return await ReadElementContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 151 } 152 ReadElementContentAsBinHexAsync( byte[] buffer, int index, int count )153 internal async Task< int > ReadElementContentAsBinHexAsync( byte[] buffer, int index, int count ) { 154 // check arguments 155 if ( buffer == null ) { 156 throw new ArgumentNullException( "buffer" ); 157 } 158 if ( count < 0 ) { 159 throw new ArgumentOutOfRangeException( "count" ); 160 } 161 if ( index < 0 ) { 162 throw new ArgumentOutOfRangeException( "index" ); 163 } 164 if ( buffer.Length - index < count ) { 165 throw new ArgumentOutOfRangeException( "count" ); 166 } 167 168 switch ( state ) { 169 case State.None: 170 if ( reader.NodeType != XmlNodeType.Element ) { 171 throw reader.CreateReadElementContentAsException( "ReadElementContentAsBinHex" ); 172 } 173 if ( !await InitOnElementAsync().ConfigureAwait(false) ) { 174 return 0; 175 } 176 break; 177 case State.InReadContent: 178 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) ); 179 case State.InReadElementContent: 180 // if we have a correct decoder, go read 181 if ( decoder == binHexDecoder ) { 182 // read more binary data 183 return await ReadElementContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 184 } 185 break; 186 default: 187 Debug.Assert( false ); 188 return 0; 189 } 190 191 Debug.Assert( state == State.InReadElementContent ); 192 193 // setup binhex decoder 194 InitBinHexDecoder(); 195 196 // read more binary data 197 return await ReadElementContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 198 } 199 FinishAsync()200 internal async Task FinishAsync() { 201 if ( state != State.None ) { 202 while ( await MoveToNextContentNodeAsync( true ).ConfigureAwait(false) ) 203 ; 204 if ( state == State.InReadElementContent ) { 205 if ( reader.NodeType != XmlNodeType.EndElement ) { 206 throw new XmlException( Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo ); 207 } 208 // move off the EndElement 209 await reader.ReadAsync().ConfigureAwait(false); 210 } 211 } 212 Reset(); 213 } 214 215 // Private methods InitAsync()216 private async Task< bool > InitAsync() { 217 // make sure we are on a content node 218 if ( !await MoveToNextContentNodeAsync( false ).ConfigureAwait(false) ) { 219 return false; 220 } 221 222 state = State.InReadContent; 223 isEnd = false; 224 return true; 225 } 226 InitOnElementAsync()227 private async Task< bool > InitOnElementAsync() { 228 Debug.Assert( reader.NodeType == XmlNodeType.Element ); 229 bool isEmpty = reader.IsEmptyElement; 230 231 // move to content or off the empty element 232 await reader.ReadAsync().ConfigureAwait(false); 233 if ( isEmpty ) { 234 return false; 235 } 236 237 // make sure we are on a content node 238 if ( !await MoveToNextContentNodeAsync( false ).ConfigureAwait(false) ) { 239 if ( reader.NodeType != XmlNodeType.EndElement ) { 240 throw new XmlException( Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo ); 241 } 242 // move off end element 243 await reader.ReadAsync().ConfigureAwait(false); 244 return false; 245 } 246 state = State.InReadElementContent; 247 isEnd = false; 248 return true; 249 } 250 ReadContentAsBinaryAsync( byte[] buffer, int index, int count )251 private async Task< int > ReadContentAsBinaryAsync( byte[] buffer, int index, int count ) { 252 Debug.Assert( decoder != null ); 253 254 if ( isEnd ) { 255 Reset(); 256 return 0; 257 } 258 decoder.SetNextOutputBuffer( buffer, index, count ); 259 260 for (;;) { 261 // use streaming ReadValueChunk if the reader supports it 262 if ( canReadValueChunk ) { 263 for (;;) { 264 if ( valueOffset < valueChunkLength ) { 265 int decodedCharsCount = decoder.Decode( valueChunk, valueOffset, valueChunkLength - valueOffset ); 266 valueOffset += decodedCharsCount; 267 } 268 if ( decoder.IsFull ) { 269 return decoder.DecodedCount; 270 } 271 Debug.Assert( valueOffset == valueChunkLength ); 272 if ( ( valueChunkLength = await reader.ReadValueChunkAsync( valueChunk, 0, ChunkSize ).ConfigureAwait(false) ) == 0 ) { 273 break; 274 } 275 valueOffset = 0; 276 } 277 } 278 else { 279 // read what is reader.Value 280 string value = await reader.GetValueAsync().ConfigureAwait(false); 281 int decodedCharsCount = decoder.Decode( value, valueOffset, value.Length - valueOffset ); 282 valueOffset += decodedCharsCount; 283 284 if ( decoder.IsFull ) { 285 return decoder.DecodedCount; 286 } 287 } 288 289 valueOffset = 0; 290 291 // move to next textual node in the element content; throw on sub elements 292 if ( !await MoveToNextContentNodeAsync( true ).ConfigureAwait(false) ) { 293 isEnd = true; 294 return decoder.DecodedCount; 295 } 296 } 297 } 298 ReadElementContentAsBinaryAsync( byte[] buffer, int index, int count )299 private async Task< int > ReadElementContentAsBinaryAsync( byte[] buffer, int index, int count ) { 300 if ( count == 0 ) { 301 return 0; 302 } 303 // read binary 304 int decoded = await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false); 305 if ( decoded > 0 ) { 306 return decoded; 307 } 308 309 // if 0 bytes returned check if we are on a closing EndElement, throw exception if not 310 if ( reader.NodeType != XmlNodeType.EndElement ) { 311 throw new XmlException( Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo ); 312 } 313 314 // move off the EndElement 315 await reader.ReadAsync().ConfigureAwait(false); 316 state = State.None; 317 return 0; 318 } 319 MoveToNextContentNodeAsync( bool moveIfOnContentNode )320 async Task< bool > MoveToNextContentNodeAsync( bool moveIfOnContentNode ) { 321 do { 322 switch ( reader.NodeType ) { 323 case XmlNodeType.Attribute: 324 return !moveIfOnContentNode; 325 case XmlNodeType.Text: 326 case XmlNodeType.Whitespace: 327 case XmlNodeType.SignificantWhitespace: 328 case XmlNodeType.CDATA: 329 if ( !moveIfOnContentNode ) { 330 return true; 331 } 332 break; 333 case XmlNodeType.ProcessingInstruction: 334 case XmlNodeType.Comment: 335 case XmlNodeType.EndEntity: 336 // skip comments, pis and end entity nodes 337 break; 338 case XmlNodeType.EntityReference: 339 if ( reader.CanResolveEntity ) { 340 reader.ResolveEntity(); 341 break; 342 } 343 goto default; 344 default: 345 return false; 346 } 347 moveIfOnContentNode = false; 348 } while ( await reader.ReadAsync().ConfigureAwait(false) ); 349 return false; 350 } 351 } 352 } 353