2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.XML / System.Xml / XmlInputStream.cs
index 4035b66c132044d372916982fcef478c70467316..e60951cd38e85961d6e16e68ad55e2230725ce9e 100644 (file)
@@ -7,15 +7,36 @@
 //
 //     (C)2003 Atsushi Enomoto
 //
+
+//
+// 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.IO;
 using System.Text;
 using System.Xml;
 
-namespace Mono.Xml.Native
+namespace System.Xml
 {
        #region XmlStreamReader
-       public class XmlStreamReader : StreamReader
+       internal class XmlStreamReader : StreamReader
        {
                XmlInputStream input;
 
@@ -26,33 +47,7 @@ namespace Mono.Xml.Native
                }
 
                public XmlStreamReader (Stream input)
-                       : this (new XmlInputStream (input, true))
-               {
-               }
-
-               public XmlStreamReader (Stream input, bool docent)
-                       : this (new XmlInputStream (input, docent))
-               {
-               }
-
-//             public XmlStreamReader (string url)
-//                     : this (url, true)
-//             {
-//             }
-//
-//             public XmlStreamReader (string url, bool docent)
-//                     : this (new XmlInputStream (url, docent, null, null))
-//             {
-//             }
-
-               public XmlStreamReader (string url, XmlResolver resolver, string baseURI)
-                       : this (url, true, resolver, baseURI)
-               {
-               }
-
-               public XmlStreamReader (string url, bool docent, XmlResolver resolver,
-                       string baseURI)
-                       : this (new XmlInputStream (url, docent, resolver, baseURI))
+                       : this (new XmlInputStream (input))
                {
                }
 
@@ -68,6 +63,7 @@ namespace Mono.Xml.Native
                                Close ();
                        }
                }
+
        }
        #endregion
 
@@ -75,178 +71,141 @@ namespace Mono.Xml.Native
        {
                Encoding enc;
                Stream stream;
-               byte[] buffer = new byte[256];
+               byte[] buffer;
                int bufLength;
                int bufPos;
-               bool isDocumentEntity;  // allow omitting "version" or not.
 
                static XmlException encodingException = new XmlException ("invalid encoding specification.");
-/*
-               public XmlInputStream (string url)
-                       : this (url, true)
-               {
-               }
-*/
-               public XmlInputStream (string url, bool docent, XmlResolver resolver, string baseURI)
-               {
-                       this.isDocumentEntity = docent;
-                       // Use XmlResolver to resolve external entity.
-#if true // #if REMOVE_IT_AFTER_URI_IMPLEMENTED
-                       if (resolver == null)
-                               resolver = new XmlUrlResolver ();
-                       Uri uri = resolver.ResolveUri (
-                               baseURI == null || baseURI == String.Empty ?
-                                null : new Uri (baseURI), url);
-                       Stream s = resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
-#else
-                       Stream s = new FileStream (url, FileMode.Open, FileAccess.Read);
-#endif
-                       Initialize (s);
-               }
 
                public XmlInputStream (Stream stream)
-                       : this (stream, true)
-               {
-               }
-
-               public XmlInputStream (Stream stream, bool docent)
                {
-                       this.isDocumentEntity = docent;
                        Initialize (stream);
                }
 
                private void Initialize (Stream stream)
                {
-                       // FIXME: seems too waste...
-                       MemoryStream ms = new MemoryStream ();
+                       buffer = new byte [64];
                        this.stream = stream;
-                       int c = stream.ReadByte ();
+                       enc = Encoding.UTF8; // Default to UTF8 if we can't guess it
+                       bufLength = stream.Read (buffer, 0, buffer.Length);
+                       if (bufLength == -1 || bufLength == 0) {
+                               return;
+                       }
+
+                       int c = ReadByteSpecial ();
                        switch (c) {
                        case 0xFF:
-                               c = stream.ReadByte ();
+                               c = ReadByteSpecial ();
                                if (c == 0xFE) {
                                        // BOM-ed little endian utf-16
                                        enc = Encoding.Unicode;
                                } else {
                                        // It doesn't start from "<?xml" then its encoding is utf-8
-                                       enc = Encoding.UTF8;
-                                       ms.WriteByte ((byte)0xFF);
-                                       ms.WriteByte ((byte)c);
+                                       bufPos = 0;
                                }
                                break;
                        case 0xFE:
-                               c = stream.ReadByte ();
+                               c = ReadByteSpecial ();
                                if (c == 0xFF) {
                                        // BOM-ed big endian utf-16
                                        enc = Encoding.BigEndianUnicode;
                                        return;
                                } else {
                                        // It doesn't start from "<?xml" then its encoding is utf-8
-                                       enc = Encoding.UTF8;
-                                       ms.WriteByte ((byte)0xFE);
-                                       ms.WriteByte ((byte)c);
+                                       bufPos = 0;
                                }
                                break;
                        case 0xEF:
-                               enc = Encoding.UTF8;
-                               c = ReadByte ();
+                               c = ReadByteSpecial ();
                                if (c == 0xBB) {
-                                       c = ReadByte ();
+                                       c = ReadByteSpecial ();
                                        if (c != 0xBF) {
-                                               ms.WriteByte ((byte)0xEF);
-                                               ms.WriteByte ((byte)0xBB);
-                                               ms.WriteByte ((byte)c);
+                                               bufPos = 0;
                                        }
                                } else {
-                                       ms.WriteByte ((byte)0xEF);
+                                       buffer [--bufPos] = 0xEF;
                                }
                                break;
                        case '<':
                                // try to get encoding name from XMLDecl.
-                               ms.WriteByte ((byte)'<');
-                               int size = stream.Read (buffer, 1, 4);
-                               ms.Write (buffer, 1, 4);
-                               if (Encoding.ASCII.GetString (buffer, 1, 4) == "?xml") {
-                                       int loop = 0;
-                                       c = SkipWhitespace (ms);
+                               if (bufLength >= 5 && Encoding.ASCII.GetString (buffer, 1, 4) == "?xml") {
+                                       bufPos += 4;
+                                       c = SkipWhitespace ();
 
                                        // version. It is optional here.
-                                       if (c != 'v') {
-                                               // FIXME: temporarily comment out here.
-//                                             if (isDocumentEntity)
-//                                                     throw new XmlException ("invalid xml declaration.");
-                                       } else {
-                                               ms.WriteByte ((byte)'v');
-                                               while (loop++ >= 0 && c >= 0) {
-                                                       c = stream.ReadByte ();
-                                                       ms.WriteByte ((byte)c);
+                                       if (c == 'v') {
+                                               while (c >= 0) {
+                                                       c = ReadByteSpecial ();
                                                        if (c == '0') { // 0 of 1.0
-                                                               ms.WriteByte ((byte)stream.ReadByte ());
+                                                               ReadByteSpecial ();
                                                                break;
                                                        }
                                                }
-                                               c = SkipWhitespace (ms);
+                                               c = SkipWhitespace ();
                                        }
 
                                        if (c == 'e') {
-                                               ms.WriteByte ((byte)'e');
-                                               size = stream.Read (buffer, 0, 7);
-                                               ms.Write (buffer, 0, 7);
-                                               if (Encoding.ASCII.GetString(buffer, 0, 7) == "ncoding") {
-                                                       c = this.SkipWhitespace(ms);
+                                               int remaining = bufLength - bufPos;
+                                               if (remaining >= 7 && Encoding.ASCII.GetString(buffer, bufPos, 7) == "ncoding") {
+                                                       bufPos += 7;
+                                                       c = SkipWhitespace();
                                                        if (c != '=')
                                                                throw encodingException;
-                                                       ms.WriteByte ((byte)'=');
-                                                       c = this.SkipWhitespace (ms);
+                                                       c = SkipWhitespace ();
                                                        int quoteChar = c;
-                                                       ms.WriteByte ((byte)c);
-                                                       int start = (int)ms.Position;
-                                                       while (loop++ >= 0) {
-                                                               c = stream.ReadByte ();
+                                                       StringBuilder sb = new StringBuilder ();
+                                                       while (true) {
+                                                               c = ReadByteSpecial ();
                                                                if (c == quoteChar)
                                                                        break;
                                                                else if (c < 0)
                                                                        throw encodingException;
-                                                               ms.WriteByte ((byte)c);
+
+                                                               sb.Append ((char) c);
                                                        }
-                                                       string encodingName = Encoding.UTF8.GetString (ms.GetBuffer (), start, (int)ms.Position - start);
+                                                       string encodingName = sb.ToString ();
                                                        if (!XmlChar.IsValidIANAEncoding (encodingName))
                                                                throw encodingException;
-                                                       ms.WriteByte ((byte)quoteChar);
                                                        enc = Encoding.GetEncoding (encodingName);
                                                }
-                                               else
-                                                       ms.Write (buffer, 0, size);
                                        }
-                                       else
-                                               ms.WriteByte ((byte)c);
                                }
-                               buffer = ms.ToArray ();
-                               bufLength = buffer.Length;
                                bufPos = 0;
                                break;
                        default:
-                               buffer [0] = (byte)c;
-                               bufLength = 1;
-                               enc = Encoding.UTF8;
+                               bufPos = 0;
                                break;
                        }
                }
 
+               // Just like readbyte, but grows the buffer too.
+               int ReadByteSpecial ()
+               {
+                       if (bufLength > bufPos)
+                               return buffer [bufPos++];
+
+                       byte [] newbuf = new byte [buffer.Length * 2];
+                       Buffer.BlockCopy (buffer, 0, newbuf, 0, bufLength);
+                       int nbytes = stream.Read (newbuf, bufLength, buffer.Length);
+                       if (nbytes == -1 || nbytes == 0)
+                               return -1;
+                               
+                       bufLength += nbytes;
+                       buffer = newbuf;
+                       return buffer [bufPos++];
+               }
+
                // skips whitespace and returns misc char that was read from stream
-               private int SkipWhitespace (MemoryStream ms)    // ms may be null
+               private int SkipWhitespace ()
                {
-                       int loop = 0;
                        int c;
-                       while (loop++ >= 0) { // defends infinite loop (expecting overflow)
-                               c = stream.ReadByte ();
-                               switch (c) {
+                       while (true) {
+                               c = ReadByteSpecial ();
+                               switch ((char) c) {
                                case '\r': goto case ' ';
                                case '\n': goto case ' ';
                                case '\t': goto case ' ';
                                case ' ':
-                                       if (ms != null)
-                                               ms.WriteByte ((byte)c);
                                        continue;
                                default:
                                        return c;
@@ -261,11 +220,17 @@ namespace Mono.Xml.Native
 
                #region Public Overrides
                public override bool CanRead {
-                       get { return stream.CanRead; }
+                       get {
+                               if (bufLength > bufPos)
+                                       return true;
+                               else
+                                       return stream.CanRead; 
+                       }
                }
 
+               // FIXME: It should support base stream's CanSeek.
                public override bool CanSeek {
-                       get { return false; } //stream.CanSeek; }
+                       get { return false; } // stream.CanSeek; }
                }
 
                public override bool CanWrite {
@@ -280,7 +245,7 @@ namespace Mono.Xml.Native
 
                public override long Position {
                        get {
-                               return stream.Position + bufLength;
+                               return stream.Position - bufLength + bufPos;
                        }
                        set {
                                if(value < bufLength)
@@ -304,13 +269,13 @@ namespace Mono.Xml.Native
                {
                        int ret;
                        if (count <= bufLength - bufPos)        {       // all from buffer
-                               Array.Copy (this.buffer, bufPos, buffer, offset, count);
+                               Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, count);
                                bufPos += count;
                                ret = count;
                        } else {
                                int bufRest = bufLength - bufPos;
                                if (bufLength > bufPos) {
-                                       Array.Copy (this.buffer, bufPos, buffer, offset, bufRest);
+                                       Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, bufRest);
                                        bufPos += bufRest;
                                }
                                ret = bufRest +