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