Răsfoiți Sursa

2004-12-17 Atsushi Enomoto <[email protected]>

	* System.Xml.dll.sources : added XmlReaderBinarySupport.cs.

	* XmlReaderBinarySupport.cs : added support class for base64/binhex
	  reading that mostly automates those reader support.
	* XmlReader.cs : added CanReadBinaryContent, CanReadValueChunk,
	  ReadContentAsBase64(), ReadElementContentAsBase64(),
	  ReadContentAsBinHex(), ReadElementContentAsBinHex() and
	  ReadValueChunk().
	* XmlTextReader.cs : Now ReadBase64() and ReadBinHex() implementations
	  are moved to XmlReaderBinarySupport. Added CanReadBinaryContent and
	  CanReadValueChunk overrides. Call Binary.Reset() to enable them.
	* XmlTextReader2.cs : added CanReadBinaryContent, CanReadValueChunk.
	  Added ReadContentAsBase64(), ReadElementContentAsBase64(),
	  ReadContentAsBinHex(), ReadElementContentAsBinHex() (just because
	  they are overriden in MS.NET).
	* XmlNodeReader.cs : added CanReadBinaryContent and CanReadValueChunk.
	  Call Binary.Reset() to enable them.

	* XPathNavigatorReader.cs : added CanReadBinaryContent and
	  CanReadValueChunk. Call Binary.Reset() to enable them.

	* SubtreeXmlReader.cs, XmlFilterReader.cs :
	  added CanReadBinaryContent and CanReadValueChunk.
	  Call Binary.Reset() to enable them.


svn path=/trunk/mcs/; revision=37892
Atsushi Eno 21 ani în urmă
părinte
comite
503e5b950a

+ 4 - 0
mcs/class/System.XML/ChangeLog

@@ -1,3 +1,7 @@
+2004-12-17  Atsushi Enomoto  <[email protected]>
+
+	* System.Xml.dll.sources : added XmlReaderBinarySupport.cs.
+
 2004-12-14  Atsushi Enomoto  <[email protected]>
 
 	* System.Xml.dll.sources : added XmlSchemaValidatingReader.

+ 5 - 0
mcs/class/System.XML/Mono.Xml.XPath/ChangeLog

@@ -1,3 +1,8 @@
+2004-12-17  Atsushi Enomoto <[email protected]>
+
+	* XPathNavigatorReader.cs : added CanReadBinaryContent and
+	  CanReadValueChunk. Call Binary.Reset() to enable them.
+
 2004-12-16  Atsushi Enomoto <[email protected]>
 
 	* XPathNavigatorReader.cs : added SchemaInfo.

+ 16 - 0
mcs/class/System.XML/Mono.Xml.XPath/XPathNavigatorReader.cs

@@ -69,6 +69,17 @@ namespace Mono.Xml.XPath
 		bool nextIsEOF;
 
 		#region Properties
+
+#if NET_2_0
+		public override bool CanReadBinaryContent {
+			get { return true; }
+		}
+
+		public override bool CanReadValueChunk {
+			get { return true; }
+		}
+#endif
+
 		public override XmlNodeType NodeType 
 		{
 			get {
@@ -447,6 +458,11 @@ namespace Mono.Xml.XPath
 
 		public override bool Read ()
 		{
+#if NET_2_0
+			if (Binary != null)
+				Binary.Reset ();
+#endif
+
 			switch (ReadState) {
 			case ReadState.EndOfFile:
 			case ReadState.Closed:

+ 6 - 0
mcs/class/System.XML/Mono.Xml/ChangeLog

@@ -1,3 +1,9 @@
+2004-12-17  Atsushi Enomoto <[email protected]>
+
+	* SubtreeXmlReader.cs, XmlFilterReader.cs :
+	  added CanReadBinaryContent and CanReadValueChunk.
+	  Call Binary.Reset() to enable them.
+
 2004-12-16  Atsushi Enomoto <[email protected]>
 
 	* SubtreeXmlReader.cs, XmlFilterReader.cs :

+ 10 - 0
mcs/class/System.XML/Mono.Xml/SubtreeXmlReader.cs

@@ -60,6 +60,16 @@ namespace Mono.Xml
 			get { return initial ? 0 : Reader.AttributeCount; }
 		}
 
+#if NET_2_0
+		public override bool CanReadBinaryContent {
+			get { return Reader.CanReadBinaryContent; }
+		}
+
+		public override bool CanReadValueChunk {
+			get { return Reader.CanReadValueChunk; }
+		}
+#endif
+
 		public override int Depth {
 			get { return Reader.Depth - startDepth; }
 		}

+ 11 - 0
mcs/class/System.XML/Mono.Xml/XmlFilterReader.cs

@@ -51,6 +51,17 @@ namespace Mono.Xml
 		}
 
 		#region Properties
+
+#if NET_2_0
+		public override bool CanReadBinaryContent {
+			get { return reader.CanReadBinaryContent; }
+		}
+
+		public override bool CanReadValueChunk {
+			get { return reader.CanReadValueChunk; }
+		}
+#endif
+
 		// This is the only one non-overriden property.
 		public XmlReader Reader {
 			get { return reader; }

+ 1 - 0
mcs/class/System.XML/System.Xml.dll.sources

@@ -131,6 +131,7 @@ System.Xml/XmlProcessingInstruction.cs
 System.Xml/XmlQualifiedName.cs
 System.Xml/XmlQueryDialect.cs
 System.Xml/XmlReader.cs
+System.Xml/XmlReaderBinarySupport.cs
 System.Xml/XmlReaderSettings.cs
 System.Xml/XmlResolver.cs
 System.Xml/XmlSecureResolver.cs

+ 18 - 0
mcs/class/System.XML/System.Xml/ChangeLog

@@ -1,3 +1,21 @@
+2004-12-17  Atsushi Enomoto <[email protected]>
+
+	* XmlReaderBinarySupport.cs : added support class for base64/binhex
+	  reading that mostly automates those reader support.
+	* XmlReader.cs : added CanReadBinaryContent, CanReadValueChunk,
+	  ReadContentAsBase64(), ReadElementContentAsBase64(),
+	  ReadContentAsBinHex(), ReadElementContentAsBinHex() and
+	  ReadValueChunk().
+	* XmlTextReader.cs : Now ReadBase64() and ReadBinHex() implementations
+	  are moved to XmlReaderBinarySupport. Added CanReadBinaryContent and
+	  CanReadValueChunk overrides. Call Binary.Reset() to enable them.
+	* XmlTextReader2.cs : added CanReadBinaryContent, CanReadValueChunk.
+	  Added ReadContentAsBase64(), ReadElementContentAsBase64(),
+	  ReadContentAsBinHex(), ReadElementContentAsBinHex() (just because
+	  they are overriden in MS.NET).
+	* XmlNodeReader.cs : added CanReadBinaryContent and CanReadValueChunk.
+	  Call Binary.Reset() to enable them.
+
 2004-12-16  Atsushi Enomoto <[email protected]>
 
 	* XmlConvert.cs :

+ 23 - 0
mcs/class/System.XML/System.Xml/XmlNodeReader.cs

@@ -123,6 +123,24 @@ namespace System.Xml
 			}
 		}
 
+#if NET_2_0
+		public override bool CanReadBinaryContent {
+			get { return true; }
+		}
+
+		public override bool CanReadValueChunk {
+			get { return true; }
+		}
+#else
+		internal override bool CanReadBinaryContent {
+			get { return true; }
+		}
+
+		internal override bool CanReadValueChunk {
+			get { return true; }
+		}
+#endif
+
 		public override bool CanResolveEntity {
 			get {
 				return true;
@@ -851,6 +869,11 @@ namespace System.Xml
 			if (EOF)
 				return false;
 
+#if NET_2_0
+			if (Binary != null)
+				Binary.Reset ();
+#endif
+
 			this.CheckAndResetEntityReaderOnMoveToAttribute ();
 			if (entityReader != null) {
 				// Read finalizes entity reader.

+ 96 - 0
mcs/class/System.XML/System.Xml/XmlReader.cs

@@ -48,6 +48,7 @@ namespace System.Xml
 #endif
 	{
 		private StringBuilder readStringBuffer;
+		private XmlReaderBinarySupport binary;
 #if NET_2_0
 		private XmlReaderSettings settings;
 #endif
@@ -66,6 +67,45 @@ namespace System.Xml
 
 		public abstract string BaseURI { get; }
 
+		internal XmlReaderBinarySupport Binary {
+			get { return binary; }
+		}
+
+		internal XmlReaderBinarySupport.CharGetter BinaryCharGetter {
+			get { return binary != null ? binary.Getter : null; }
+			set {
+				if (binary == null)
+					binary = new XmlReaderBinarySupport (this);
+				binary.Getter = value;
+			}
+		}
+
+#if NET_2_0
+		// To enable it internally in sys.xml, just insert these
+		// two lines into Read():
+		//
+		//	#if NET_2_0
+		//	if (Binary != null)
+		//		Binary.Reset ();
+		//	#endif
+		//
+		public virtual bool CanReadBinaryContent {
+			get { return false; }
+		}
+
+		public virtual bool CanReadValueChunk {
+			get { return false; }
+		}
+#else
+		internal virtual bool CanReadBinaryContent {
+			get { return false; }
+		}
+
+		internal virtual bool CanReadValueChunk {
+			get { return false; }
+		}
+#endif
+
 		public virtual bool CanResolveEntity
 		{
 			get	{ return false; }
@@ -959,8 +999,64 @@ namespace System.Xml
 		{
 			return ReadContentString ();
 		}
+
+		public virtual int ReadContentAsBase64 (
+			byte [] buffer, int offset, int length)
+		{
+			CheckSupport ();
+			return binary.ReadContentAsBase64 (
+				buffer, offset, length);
+		}
+
+		public virtual int ReadContentAsBinHex (
+			byte [] buffer, int offset, int length)
+		{
+			CheckSupport ();
+			return binary.ReadContentAsBinHex (
+				buffer, offset, length);
+		}
+
+		public virtual int ReadElementContentAsBase64 (
+			byte [] buffer, int offset, int length)
+		{
+			CheckSupport ();
+			return binary.ReadElementContentAsBase64 (
+				buffer, offset, length);
+		}
+
+		public virtual int ReadElementContentAsBinHex (
+			byte [] buffer, int offset, int length)
+		{
+			CheckSupport ();
+			return binary.ReadElementContentAsBase64 (
+				buffer, offset, length);
+		}
 #endif
 
+#if NET_2_0
+		public virtual int ReadValueChunk (
+			char [] buffer, int offset, int length)
+#else
+		internal virtual int ReadValueChunk (
+			char [] buffer, int offset, int length)
+#endif
+		{
+			if (!CanReadValueChunk)
+				throw new NotSupportedException ();
+			if (binary == null)
+				binary = new XmlReaderBinarySupport (this);
+			return binary.ReadValueChunk (buffer, offset, length);
+		}
+
+		private void CheckSupport ()
+		{
+			// Default implementation expects both.
+			if (!CanReadBinaryContent || !CanReadValueChunk)
+				throw new NotSupportedException ();
+			if (binary == null)
+				binary = new XmlReaderBinarySupport (this);
+		}
+
 		public abstract void ResolveEntity ();
 
 		public virtual void Skip ()

+ 394 - 0
mcs/class/System.XML/System.Xml/XmlReaderBinarySupport.cs

@@ -0,0 +1,394 @@
+//
+// System.Xml.XmlReaderBinarySupport.cs
+//
+// Author:
+//   Atsushi Enomoto  ([email protected])
+//
+// (C)2004 Novell Inc,
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Text;
+
+namespace System.Xml
+{
+	internal class XmlReaderBinarySupport
+	{
+		public delegate int CharGetter (
+			char [] buffer, int offset, int length);
+
+		public enum CommandState {
+			None,
+			ReadElementContentAsBase64,
+			ReadContentAsBase64,
+			ReadElementContentAsBinHex,
+			ReadContentAsBinHex
+		}
+
+		public XmlReaderBinarySupport (XmlReader reader)
+		{
+			this.reader = reader;
+			Reset ();
+		}
+
+		XmlReader reader;
+		CharGetter getter;
+		byte [] base64Cache = new byte [3];
+		int base64CacheStartsAt;
+		CommandState state;
+		StringBuilder textCache;
+		bool hasCache;
+		bool dontReset;
+
+		public CharGetter Getter {
+			get { return getter; }
+			set { getter = value; }
+		}
+
+		public void Reset ()
+		{
+			if (!dontReset) {
+				dontReset = true;
+				if (hasCache) {
+					reader.Read ();
+					switch (state) {
+					case CommandState.ReadElementContentAsBase64:
+					case CommandState.ReadElementContentAsBinHex:
+						reader.Read ();
+						break;
+					}
+				}
+				base64CacheStartsAt = -1;
+				state = CommandState.None;
+				hasCache = false;
+				dontReset = false;
+			}
+		}
+
+		InvalidOperationException StateError (CommandState action)
+		{
+			return new InvalidOperationException (
+				String.Format ("Invalid attempt to read binary content by {0}, while once binary reading was started by {1}", action, state));
+		}
+
+		private void CheckState (bool element, CommandState action)
+		{
+			if (state == CommandState.None) {
+				if (textCache == null)
+					textCache = new StringBuilder ();
+				else
+					textCache.Length = 0;
+				if (action == CommandState.None)
+					return; // for ReadValueChunk()
+				if (reader.ReadState != ReadState.Interactive)
+					return;
+				switch (reader.NodeType) {
+				case XmlNodeType.Text:
+				case XmlNodeType.CDATA:
+				case XmlNodeType.SignificantWhitespace:
+				case XmlNodeType.Whitespace:
+					if (!element) {
+						state = action;
+						return;
+					}
+					break;
+				case XmlNodeType.Element:
+					if (element) {
+						if (!reader.IsEmptyElement)
+							reader.Read ();
+						state = action;
+						return;
+					}
+					break;
+				}
+				throw new XmlException ((element ? 
+					"Reader is not positioned on an element."
+					: "Reader is not positioned on a text node."));
+			}
+			if (state == action)
+				return;
+			throw StateError (action);
+		}
+
+		public int ReadElementContentAsBase64 (
+			byte [] buffer, int offset, int length)
+		{
+			CheckState (true, CommandState.ReadElementContentAsBase64);
+			return ReadBase64 (buffer, offset, length);
+		}
+
+		public int ReadContentAsBase64 (
+			byte [] buffer, int offset, int length)
+		{
+			CheckState (false, CommandState.ReadContentAsBase64);
+			return ReadBase64 (buffer, offset, length);
+		}
+
+		public int ReadElementContentAsBinHex (
+			byte [] buffer, int offset, int length)
+		{
+			CheckState (true, CommandState.ReadElementContentAsBinHex);
+			return ReadBinHex (buffer, offset, length);
+		}
+
+		public int ReadContentAsBinHex (
+			byte [] buffer, int offset, int length)
+		{
+			CheckState (false, CommandState.ReadContentAsBinHex);
+			return ReadBinHex (buffer, offset, length);
+		}
+
+		public int ReadBase64 (byte [] buffer, int offset, int length)
+		{
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
+			else if (length < 0)
+				throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
+			else if (buffer.Length < offset + length)
+				throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
+
+			if (reader.IsEmptyElement)
+				return 0;
+			if (length == 0)	// It does not raise an error.
+				return 0;
+
+			int bufIndex = offset;
+			int bufLast = offset + length;
+
+			if (base64CacheStartsAt >= 0) {
+				for (int i = base64CacheStartsAt; i < 3; i++) {
+					buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
+					if (bufIndex == bufLast)
+						return bufLast - offset;
+				}
+			}
+
+			for (int i = 0; i < 3; i++)
+				base64Cache [i] = 0;
+			base64CacheStartsAt = -1;
+
+			int max = (int) System.Math.Ceiling (4.0 / 3 * length);
+			int additional = max % 4;
+			if (additional > 0)
+				max += 4 - additional;
+			char [] chars = new char [max];
+			int charsLength = getter != null ?
+				getter (chars, 0, max) :
+				ReadValueChunk (chars, 0, max);
+
+			byte b = 0;
+			byte work = 0;
+			for (int i = 0; i < charsLength - 3; i++) {
+				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
+					break;
+				b = (byte) (GetBase64Byte (chars [i]) << 2);
+				if (bufIndex < bufLast)
+					buffer [bufIndex] = b;
+				else {
+					if (base64CacheStartsAt < 0)
+						base64CacheStartsAt = 0;
+					base64Cache [0] = b;
+				}
+				// charsLength mod 4 might not equals to 0.
+				if (++i == charsLength)
+					break;
+				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i))  == charsLength)
+					break;
+				b = GetBase64Byte (chars [i]);
+				work = (byte) (b >> 4);
+				if (bufIndex < bufLast) {
+					buffer [bufIndex] += work;
+					bufIndex++;
+				}
+				else
+					base64Cache [0] += work;
+
+				work = (byte) ((b & 0xf) << 4);
+				if (bufIndex < bufLast) {
+					buffer [bufIndex] = work;
+				}
+				else {
+					if (base64CacheStartsAt < 0)
+						base64CacheStartsAt = 1;
+					base64Cache [1] = work;
+				}
+
+				if (++i == charsLength)
+					break;
+				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
+					break;
+				b = GetBase64Byte (chars [i]);
+				work = (byte) (b >> 2);
+				if (bufIndex < bufLast) {
+					buffer [bufIndex] += work;
+					bufIndex++;
+				}
+				else
+					base64Cache [1] += work;
+
+				work = (byte) ((b & 3) << 6);
+				if (bufIndex < bufLast)
+					buffer [bufIndex] = work;
+				else {
+					if (base64CacheStartsAt < 0)
+						base64CacheStartsAt = 2;
+					base64Cache [2] = work;
+				}
+				if (++i == charsLength)
+					break;
+				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
+					break;
+				work = GetBase64Byte (chars [i]);
+				if (bufIndex < bufLast) {
+					buffer [bufIndex] += work;
+					bufIndex++;
+				}
+				else
+					base64Cache [2] += work;
+			}
+			int ret = System.Math.Min (bufLast - offset, bufIndex - offset);
+			if (ret < length && charsLength > 0)
+				return ret + ReadBase64 (buffer, offset + ret, length - ret);
+			else
+				return ret;
+		}
+
+		// Since ReadBase64() is processed for every 4 chars, it does
+		// not handle '=' here.
+		private byte GetBase64Byte (char ch)
+		{
+			switch (ch) {
+			case '+':
+				return 62;
+			case '/':
+				return 63;
+			default:
+				if (ch >= 'A' && ch <= 'Z')
+					return (byte) (ch - 'A');
+				else if (ch >= 'a' && ch <= 'z')
+					return (byte) (ch - 'a' + 26);
+				else if (ch >= '0' && ch <= '9')
+					return (byte) (ch - '0' + 52);
+				else
+					throw new XmlException ("Invalid Base64 character was found.");
+			}
+		}
+
+		private int SkipIgnorableBase64Chars (char [] chars, int charsLength, int i)
+		{
+			while (chars [i] == '=' || XmlChar.IsWhitespace (chars [i]))
+				if (charsLength == ++i)
+					break;
+			return i;
+		}
+
+		public int ReadBinHex (byte [] buffer, int offset, int length)
+		{
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
+			else if (length < 0)
+				throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
+			else if (buffer.Length < offset + length)
+				throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
+
+			if (length == 0)
+				return 0;
+
+			char [] chars = new char [length * 2];
+			int charsLength = getter != null ?
+				getter (chars, 0, length * 2) :
+				ReadValueChunk (chars, 0, length * 2);
+			return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
+		}
+
+		public int ReadValueChunk (
+			char [] buffer, int offset, int length)
+		{
+			CommandState backup = state;
+			if (state == CommandState.None)
+				CheckState (false, CommandState.None);
+
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
+			else if (length < 0)
+				throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
+			else if (buffer.Length < offset + length)
+				throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
+
+			if (length == 0)
+				return 0;
+
+			if (!hasCache) {
+				if (reader.IsEmptyElement)
+					return 0;
+			}
+
+			bool consumeToEnd = false;
+			while (!consumeToEnd && textCache.Length < length) {
+				switch (reader.NodeType) {
+				case XmlNodeType.Text:
+				case XmlNodeType.CDATA:
+				case XmlNodeType.SignificantWhitespace:
+				case XmlNodeType.Whitespace:
+					if (hasCache) {
+						switch (reader.NodeType) {
+						case XmlNodeType.Text:
+						case XmlNodeType.CDATA:
+						case XmlNodeType.SignificantWhitespace:
+						case XmlNodeType.Whitespace:
+							Read ();
+							break;
+						default:
+							consumeToEnd = true;
+							break;
+						}
+					}
+					textCache.Append (reader.Value);
+					hasCache = true;
+					break;
+				}
+			}
+			state = backup;
+			int min = textCache.Length;
+			if (min > length)
+				min = length;
+			string str = textCache.ToString (0, min);
+			textCache.Remove (0, str.Length);
+			str.CopyTo (0, buffer, offset, str.Length);
+			if (min < length)
+				return min + ReadValueChunk (buffer, offset + min, length - min);
+			else
+				return min;
+		}
+
+		private bool Read ()
+		{
+			dontReset = true;
+			bool b = reader.Read ();
+			dontReset = false;
+			return b;
+		}
+	}
+}

+ 44 - 159
mcs/class/System.XML/System.Xml/XmlTextReader.cs

@@ -156,6 +156,24 @@ namespace System.Xml
 			get { return parserContext.BaseURI; }
 		}
 
+#if NET_2_0
+		public override bool CanReadBinaryContent {
+			get { return true; }
+		}
+
+		public override bool CanReadValueChunk {
+			get { return true; }
+		}
+#else
+		internal override bool CanReadBinaryContent {
+			get { return true; }
+		}
+
+		internal override bool CanReadValueChunk {
+			get { return true; }
+		}
+#endif
+
 		internal bool CharacterChecking {
 			get { return checkCharacters && normalization; }
 			set { checkCharacters = value; }
@@ -511,6 +529,9 @@ namespace System.Xml
 				return true;
 			}
 
+			if (Binary != null)
+				Binary.Reset ();
+
 			bool more = false;
 			readState = ReadState.Interactive;
 			currentLinkedNodeLineNumber = line;
@@ -533,8 +554,6 @@ namespace System.Xml
 				return ReadUntilEndTag ();
 			}
 
-			base64CacheStartsAt = -1;
-
 			more = ReadContent ();
 
 			if (!more && startNodeType == XmlNodeType.Document && currentState != XmlNodeType.EndElement)
@@ -565,122 +584,27 @@ namespace System.Xml
 				return false;
 		}
 
-		private int SkipIgnorableBase64Chars (char [] chars, int charsLength, int i)
-		{
-			while (chars [i] == '=' || XmlChar.IsWhitespace (chars [i]))
-				if (charsLength == ++i)
-					break;
-			return i;
-		}
-
 		public int ReadBase64 (byte [] buffer, int offset, int length)
 		{
-			if (offset < 0)
-				throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
-			else if (length < 0)
-				throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
-			else if (buffer.Length < offset + length)
-				throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
-
-			if (length == 0)	// It does not raise an error.
-				return 0;
-
-			int bufIndex = offset;
-			int bufLast = offset + length;
-
-			if (base64CacheStartsAt >= 0) {
-				for (int i = base64CacheStartsAt; i < 3; i++) {
-					buffer [bufIndex++] = base64Cache [base64CacheStartsAt++];
-					if (bufIndex == bufLast)
-						return bufLast - offset;
-				}
+			BinaryCharGetter = binaryCharGetter;
+			try {
+				return Binary.ReadBase64 (buffer, offset, length);
+			} finally {
+				BinaryCharGetter = null;
 			}
+		}
 
-			for (int i = 0; i < 3; i++)
-				base64Cache [i] = 0;
-			base64CacheStartsAt = -1;
-
-			int max = (int) System.Math.Ceiling (4.0 / 3 * length);
-			int additional = max % 4;
-			if (additional > 0)
-				max += 4 - additional;
-			char [] chars = new char [max];
-			int charsLength = ReadChars (chars, 0, max);
-
-			byte b = 0;
-			byte work = 0;
-			for (int i = 0; i < charsLength - 3; i++) {
-				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
-					break;
-				b = (byte) (GetBase64Byte (chars [i]) << 2);
-				if (bufIndex < bufLast)
-					buffer [bufIndex] = b;
-				else {
-					if (base64CacheStartsAt < 0)
-						base64CacheStartsAt = 0;
-					base64Cache [0] = b;
-				}
-				// charsLength mod 4 might not equals to 0.
-				if (++i == charsLength)
-					break;
-				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i))  == charsLength)
-					break;
-				b = GetBase64Byte (chars [i]);
-				work = (byte) (b >> 4);
-				if (bufIndex < bufLast) {
-					buffer [bufIndex] += work;
-					bufIndex++;
-				}
-				else
-					base64Cache [0] += work;
-
-				work = (byte) ((b & 0xf) << 4);
-				if (bufIndex < bufLast) {
-					buffer [bufIndex] = work;
-				}
-				else {
-					if (base64CacheStartsAt < 0)
-						base64CacheStartsAt = 1;
-					base64Cache [1] = work;
-				}
-
-				if (++i == charsLength)
-					break;
-				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
-					break;
-				b = GetBase64Byte (chars [i]);
-				work = (byte) (b >> 2);
-				if (bufIndex < bufLast) {
-					buffer [bufIndex] += work;
-					bufIndex++;
-				}
-				else
-					base64Cache [1] += work;
-
-				work = (byte) ((b & 3) << 6);
-				if (bufIndex < bufLast)
-					buffer [bufIndex] = work;
-				else {
-					if (base64CacheStartsAt < 0)
-						base64CacheStartsAt = 2;
-					base64Cache [2] = work;
-				}
-				if (++i == charsLength)
-					break;
-				if ((i = SkipIgnorableBase64Chars (chars, charsLength, i)) == charsLength)
-					break;
-				work = GetBase64Byte (chars [i]);
-				if (bufIndex < bufLast) {
-					buffer [bufIndex] += work;
-					bufIndex++;
-				}
-				else
-					base64Cache [2] += work;
+		public int ReadBinHex (byte [] buffer, int offset, int length)
+		{
+			BinaryCharGetter = binaryCharGetter;
+			try {
+				return Binary.ReadBinHex (buffer, offset, length);
+			} finally {
+				BinaryCharGetter = null;
 			}
-			return System.Math.Min (bufLast - offset, bufIndex - offset);
 		}
 
-		public int ReadBinHex (byte [] buffer, int offset, int length)
+		public int ReadChars (char [] buffer, int offset, int length)
 		{
 			if (offset < 0)
 				throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
@@ -689,16 +613,14 @@ namespace System.Xml
 			else if (buffer.Length < offset + length)
 				throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
 
-			if (length == 0)
+			if (IsEmptyElement) {
+				Read ();
 				return 0;
+			}
 
-			char [] chars = new char [length * 2];
-			int charsLength = ReadChars (chars, 0, length * 2);
-			return XmlConvert.FromBinHexString (chars, offset, charsLength, buffer);
-		}
+			if (NodeType != XmlNodeType.Element)
+				return 0;
 
-		public int ReadChars (char [] buffer, int offset, int length)
-		{
 			return ReadCharsInternal (buffer, offset, length);
 		}
 
@@ -854,8 +776,6 @@ namespace System.Xml
 					}
 
 					valueCache = tmpBuilder.ToString ();
-//					if (cachedNormalization)
-//						NormalizeSpaces ();
 					return valueCache;
 				}
 
@@ -958,8 +878,7 @@ namespace System.Xml
 
 		// For ReadChars()/ReadBase64()/ReadBinHex()
 		private bool shouldSkipUntilEndTag;
-		private byte [] base64Cache = new byte [3];
-		private int base64CacheStartsAt;
+		XmlReaderBinarySupport.CharGetter binaryCharGetter;
 
 		// These values are never re-initialized.
 		private bool namespaces = true;
@@ -1026,7 +945,7 @@ namespace System.Xml
 			currentState = XmlNodeType.None;
 
 			shouldSkipUntilEndTag = false;
-			base64CacheStartsAt = -1;
+			binaryCharGetter = new XmlReaderBinarySupport.CharGetter (ReadChars);
 
 			checkCharacters = true;
 #if NET_2_0
@@ -2717,45 +2636,9 @@ namespace System.Xml
 			return;
 		}
 
-		// Since ReadBase64() is processed for every 4 chars, it does
-		// not handle '=' here.
-		private byte GetBase64Byte (char ch)
-		{
-			switch (ch) {
-			case '+':
-				return 62;
-			case '/':
-				return 63;
-			default:
-				if (ch >= 'A' && ch <= 'Z')
-					return (byte) (ch - 'A');
-				else if (ch >= 'a' && ch <= 'z')
-					return (byte) (ch - 'a' + 26);
-				else if (ch >= '0' && ch <= '9')
-					return (byte) (ch - '0' + 52);
-				else
-					throw NotWFError ("Invalid Base64 character was found.");
-			}
-		}
-
 		// Returns -1 if it should throw an error.
 		private int ReadCharsInternal (char [] buffer, int offset, int length)
 		{
-			if (IsEmptyElement) {
-				Read ();
-				return 0;
-			}
-
-			if (offset < 0)
-				throw new ArgumentOutOfRangeException ("offset", offset, "Offset must be non-negative integer.");
-			else if (length < 0)
-				throw new ArgumentOutOfRangeException ("length", length, "Length must be non-negative integer.");
-			else if (buffer.Length < offset + length)
-				throw new ArgumentOutOfRangeException ("buffer length is smaller than the sum of offset and length.");
-
-			if (NodeType != XmlNodeType.Element)
-				return 0;
-
 			shouldSkipUntilEndTag = true;
 
 			int bufIndex = offset;
@@ -2796,6 +2679,8 @@ namespace System.Xml
 
 		private bool ReadUntilEndTag ()
 		{
+			if (Depth == 0)
+				currentState = XmlNodeType.EndElement;
 			int ch;
 			do {
 				ch = ReadChar ();

+ 45 - 0
mcs/class/System.XML/System.Xml/XmlTextReader2.cs

@@ -141,9 +141,18 @@ namespace System.Xml
 			get { return Current.BaseURI; }
 		}
 
+		public override bool CanReadBinaryContent {
+			get { return true; }
+		}
+
+		public override bool CanReadValueChunk {
+			get { return true; }
+		}
+
 		public override bool CanResolveEntity {
 			get { return true; }
 		}
+
 		public override int Depth {
 			get {
 				// On EndEntity, depth is the same as that 
@@ -636,6 +645,42 @@ namespace System.Xml
 				return source.ReadChars (buffer, offset, length);
 		}
 
+
+		[MonoTODO ("Check how expanded entity is handled here.")]
+		public override int ReadContentAsBase64 (byte [] buffer, int offset, int length)
+		{
+			if (entity != null)
+				return entity.ReadContentAsBase64 (buffer, offset, length);
+			else
+				return source.ReadContentAsBase64 (buffer, offset, length);
+		}
+
+		[MonoTODO ("Check how expanded entity is handled here.")]
+		public override int ReadContentAsBinHex (byte [] buffer, int offset, int length)
+		{
+			if (entity != null)
+				return entity.ReadContentAsBinHex (buffer, offset, length);
+			else
+				return source.ReadContentAsBinHex (buffer, offset, length);
+		}
+
+		[MonoTODO ("Check how expanded entity is handled here.")]
+		public override int ReadElementContentAsBase64 (byte [] buffer, int offset, int length)
+		{
+			if (entity != null)
+				return entity.ReadElementContentAsBase64 (buffer, offset, length);
+			else
+				return source.ReadElementContentAsBase64 (buffer, offset, length);
+		}
+
+		[MonoTODO ("Check how expanded entity is handled here.")]
+		public override int ReadElementContentAsBinHex (byte [] buffer, int offset, int length)
+		{
+			if (entity != null)
+				return entity.ReadElementContentAsBinHex (buffer, offset, length);
+			else
+				return source.ReadElementContentAsBinHex (buffer, offset, length);
+		}
 		#endregion
 	}
 }