2 //------------------------------------------------------------------------------
3 // <copyright file="ReadContentAsBinaryHelper.cs" company="Microsoft">
4 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <owner current="true" primary="true">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 using System.Diagnostics;
13 internal partial class ReadContentAsBinaryHelper {
28 bool canReadValueChunk;
32 IncrementalReadDecoder decoder;
33 Base64Decoder base64Decoder;
34 BinHexDecoder binHexDecoder;
37 const int ChunkSize = 256;
40 internal ReadContentAsBinaryHelper( XmlReader reader ) {
42 this.canReadValueChunk = reader.CanReadValueChunk;
44 if ( canReadValueChunk ) {
45 valueChunk = new char[ChunkSize];
50 internal static ReadContentAsBinaryHelper CreateOrReset( ReadContentAsBinaryHelper helper, XmlReader reader ) {
51 if ( helper == null ) {
52 return new ReadContentAsBinaryHelper( reader );
62 internal int ReadContentAsBase64( byte[] buffer, int index, int count ) {
64 if ( buffer == null ) {
65 throw new ArgumentNullException( "buffer" );
68 throw new ArgumentOutOfRangeException( "count" );
71 throw new ArgumentOutOfRangeException( "index" );
73 if ( buffer.Length - index < count ) {
74 throw new ArgumentOutOfRangeException( "count" );
79 if ( !reader.CanReadContentAs() ) {
80 throw reader.CreateReadContentAsException( "ReadContentAsBase64" );
86 case State.InReadContent:
87 // if we have a correct decoder, go read
88 if ( decoder == base64Decoder ) {
89 // read more binary data
90 return ReadContentAsBinary( buffer, index, count );
93 case State.InReadElementContent:
94 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
96 Debug.Assert( false );
100 Debug.Assert( state == State.InReadContent );
102 // setup base64 decoder
105 // read more binary data
106 return ReadContentAsBinary( buffer, index, count );
109 internal int ReadContentAsBinHex( byte[] buffer, int index, int count ) {
111 if ( buffer == null ) {
112 throw new ArgumentNullException( "buffer" );
115 throw new ArgumentOutOfRangeException( "count" );
118 throw new ArgumentOutOfRangeException( "index" );
120 if ( buffer.Length - index < count ) {
121 throw new ArgumentOutOfRangeException( "count" );
126 if ( !reader.CanReadContentAs() ) {
127 throw reader.CreateReadContentAsException( "ReadContentAsBinHex" );
133 case State.InReadContent:
134 // if we have a correct decoder, go read
135 if ( decoder == binHexDecoder ) {
136 // read more binary data
137 return ReadContentAsBinary( buffer, index, count );
140 case State.InReadElementContent:
141 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
143 Debug.Assert( false );
147 Debug.Assert( state == State.InReadContent );
149 // setup binhex decoder
152 // read more binary data
153 return ReadContentAsBinary( buffer, index, count );
156 internal int ReadElementContentAsBase64( byte[] buffer, int index, int count ) {
158 if ( buffer == null ) {
159 throw new ArgumentNullException( "buffer" );
162 throw new ArgumentOutOfRangeException( "count" );
165 throw new ArgumentOutOfRangeException( "index" );
167 if ( buffer.Length - index < count ) {
168 throw new ArgumentOutOfRangeException( "count" );
173 if ( reader.NodeType != XmlNodeType.Element ) {
174 throw reader.CreateReadElementContentAsException( "ReadElementContentAsBase64" );
176 if ( !InitOnElement() ) {
180 case State.InReadContent:
181 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
182 case State.InReadElementContent:
183 // if we have a correct decoder, go read
184 if ( decoder == base64Decoder ) {
185 // read more binary data
186 return ReadElementContentAsBinary( buffer, index, count );
190 Debug.Assert( false );
194 Debug.Assert( state == State.InReadElementContent );
196 // setup base64 decoder
199 // read more binary data
200 return ReadElementContentAsBinary( buffer, index, count );
203 internal int ReadElementContentAsBinHex( byte[] buffer, int index, int count ) {
205 if ( buffer == null ) {
206 throw new ArgumentNullException( "buffer" );
209 throw new ArgumentOutOfRangeException( "count" );
212 throw new ArgumentOutOfRangeException( "index" );
214 if ( buffer.Length - index < count ) {
215 throw new ArgumentOutOfRangeException( "count" );
220 if ( reader.NodeType != XmlNodeType.Element ) {
221 throw reader.CreateReadElementContentAsException( "ReadElementContentAsBinHex" );
223 if ( !InitOnElement() ) {
227 case State.InReadContent:
228 throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
229 case State.InReadElementContent:
230 // if we have a correct decoder, go read
231 if ( decoder == binHexDecoder ) {
232 // read more binary data
233 return ReadElementContentAsBinary( buffer, index, count );
237 Debug.Assert( false );
241 Debug.Assert( state == State.InReadElementContent );
243 // setup binhex decoder
246 // read more binary data
247 return ReadElementContentAsBinary( buffer, index, count );
250 internal void Finish() {
251 if ( state != State.None ) {
252 while ( MoveToNextContentNode( true ) )
254 if ( state == State.InReadElementContent ) {
255 if ( reader.NodeType != XmlNodeType.EndElement ) {
256 throw new XmlException( Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo );
258 // move off the EndElement
265 internal void Reset() {
272 private bool Init() {
273 // make sure we are on a content node
274 if ( !MoveToNextContentNode( false ) ) {
278 state = State.InReadContent;
283 private bool InitOnElement() {
284 Debug.Assert( reader.NodeType == XmlNodeType.Element );
285 bool isEmpty = reader.IsEmptyElement;
287 // move to content or off the empty element
293 // make sure we are on a content node
294 if ( !MoveToNextContentNode( false ) ) {
295 if ( reader.NodeType != XmlNodeType.EndElement ) {
296 throw new XmlException( Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo );
298 // move off end element
302 state = State.InReadElementContent;
307 private void InitBase64Decoder() {
308 if ( base64Decoder == null ) {
309 base64Decoder = new Base64Decoder();
312 base64Decoder.Reset();
314 decoder = base64Decoder;
317 private void InitBinHexDecoder() {
318 if ( binHexDecoder == null ) {
319 binHexDecoder = new BinHexDecoder();
322 binHexDecoder.Reset();
324 decoder = binHexDecoder;
327 private int ReadContentAsBinary( byte[] buffer, int index, int count ) {
328 Debug.Assert( decoder != null );
334 decoder.SetNextOutputBuffer( buffer, index, count );
337 // use streaming ReadValueChunk if the reader supports it
338 if ( canReadValueChunk ) {
340 if ( valueOffset < valueChunkLength ) {
341 int decodedCharsCount = decoder.Decode( valueChunk, valueOffset, valueChunkLength - valueOffset );
342 valueOffset += decodedCharsCount;
344 if ( decoder.IsFull ) {
345 return decoder.DecodedCount;
347 Debug.Assert( valueOffset == valueChunkLength );
348 if ( ( valueChunkLength = reader.ReadValueChunk( valueChunk, 0, ChunkSize ) ) == 0 ) {
355 // read what is reader.Value
356 string value = reader.Value;
357 int decodedCharsCount = decoder.Decode( value, valueOffset, value.Length - valueOffset );
358 valueOffset += decodedCharsCount;
360 if ( decoder.IsFull ) {
361 return decoder.DecodedCount;
367 // move to next textual node in the element content; throw on sub elements
368 if ( !MoveToNextContentNode( true ) ) {
370 return decoder.DecodedCount;
375 private int ReadElementContentAsBinary( byte[] buffer, int index, int count ) {
380 int decoded = ReadContentAsBinary( buffer, index, count );
385 // if 0 bytes returned check if we are on a closing EndElement, throw exception if not
386 if ( reader.NodeType != XmlNodeType.EndElement ) {
387 throw new XmlException( Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo );
390 // move off the EndElement
396 bool MoveToNextContentNode( bool moveIfOnContentNode ) {
398 switch ( reader.NodeType ) {
399 case XmlNodeType.Attribute:
400 return !moveIfOnContentNode;
401 case XmlNodeType.Text:
402 case XmlNodeType.Whitespace:
403 case XmlNodeType.SignificantWhitespace:
404 case XmlNodeType.CDATA:
405 if ( !moveIfOnContentNode ) {
409 case XmlNodeType.ProcessingInstruction:
410 case XmlNodeType.Comment:
411 case XmlNodeType.EndEntity:
412 // skip comments, pis and end entity nodes
414 case XmlNodeType.EntityReference:
415 if ( reader.CanResolveEntity ) {
416 reader.ResolveEntity();
423 moveIfOnContentNode = false;
424 } while ( reader.Read() );