2 // System.Xml.XmlInputStream
3 // encoding-specification-wise XML input stream and reader
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
8 // (C)2003 Atsushi Enomoto
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Runtime.InteropServices;
38 #region XmlStreamReader
39 internal class XmlStreamReader : NonBlockingStreamReader
43 XmlStreamReader (XmlInputStream input)
44 : base (input, input.ActualEncoding != null ? input.ActualEncoding : XmlInputStream.StrictUTF8)
49 public XmlStreamReader (Stream input)
50 : this (new XmlInputStream (input))
54 public override void Close ()
59 protected override void Dispose (bool disposing)
61 base.Dispose (disposing);
70 #region NonBlockingStreamReader
71 // mostly copied from StreamReader.
72 internal class NonBlockingStreamReader : TextReader {
74 const int DefaultBufferSize = 1024;
75 const int DefaultFileBufferSize = 4096;
76 const int MinimumBufferSize = 128;
84 // The decoded buffer from the above input buffer
86 char [] decoded_buffer;
89 // Decoded bytes in decoded_buffer.
94 // Current position in the decoded_buffer
99 // The buffer size that we are using
109 public NonBlockingStreamReader(Stream stream, Encoding encoding)
111 int buffer_size = DefaultBufferSize;
112 base_stream = stream;
113 input_buffer = new byte [buffer_size];
114 this.buffer_size = buffer_size;
115 this.encoding = encoding;
116 decoder = encoding.GetDecoder ();
118 decoded_buffer = new char [encoding.GetMaxCharCount (buffer_size)];
123 public override void Close ()
128 protected override void Dispose (bool disposing)
130 if (disposing && base_stream != null)
131 base_stream.Close ();
134 decoded_buffer = null;
138 base.Dispose (disposing);
141 public void DiscardBufferedData ()
143 pos = decoded_count = 0;
147 // the buffer is empty, fill it again
148 private int ReadBuffer ()
153 // keep looping until the decoder gives us some chars
158 cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
163 mayBlock = (cbEncoded < buffer_size);
164 decoded_count += decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
166 } while (decoded_count == 0);
168 return decoded_count;
171 public override int Peek ()
173 if (base_stream == null)
174 throw new ObjectDisposedException ("StreamReader", "Cannot read from a closed StreamReader");
175 if (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
178 return decoded_buffer [pos];
181 public override int Read ()
183 if (base_stream == null)
184 throw new ObjectDisposedException ("StreamReader", "Cannot read from a closed StreamReader");
185 if (pos >= decoded_count && ReadBuffer () == 0)
188 return decoded_buffer [pos++];
191 public override int Read ([In, Out] char[] dest_buffer, int index, int count)
193 if (base_stream == null)
194 throw new ObjectDisposedException ("StreamReader", "Cannot read from a closed StreamReader");
195 if (dest_buffer == null)
196 throw new ArgumentNullException ("dest_buffer");
198 throw new ArgumentOutOfRangeException ("index", "< 0");
200 throw new ArgumentOutOfRangeException ("count", "< 0");
201 // re-ordered to avoid possible integer overflow
202 if (index > dest_buffer.Length - count)
203 throw new ArgumentException ("index + count > dest_buffer.Length");
208 if (pos >= decoded_count && ReadBuffer () == 0)
209 return chars_read > 0 ? chars_read : 0;
211 int cch = Math.Min (decoded_count - pos, count);
212 Array.Copy (decoded_buffer, pos, dest_buffer, index, cch);
221 public override string ReadLine()
223 if (base_stream == null)
224 throw new ObjectDisposedException ("StreamReader", "Cannot read from a closed StreamReader");
226 bool foundCR = false;
227 StringBuilder text = new StringBuilder ();
232 if (c == -1) { // end of stream
233 if (text.Length == 0)
242 if (c == '\n') { // newline
243 if ((text.Length > 0) && (text [text.Length - 1] == '\r'))
248 } else if (foundCR) {
258 text.Append ((char) c);
261 return text.ToString ();
264 public override string ReadToEnd()
266 if (base_stream == null)
267 throw new ObjectDisposedException ("StreamReader", "Cannot read from a closed StreamReader");
269 StringBuilder text = new StringBuilder ();
271 int size = decoded_buffer.Length;
272 char [] buffer = new char [size];
275 while ((len = Read (buffer, 0, size)) != 0)
276 text.Append (buffer, 0, len);
278 return text.ToString ();
283 class XmlInputStream : Stream
285 public static readonly Encoding StrictUTF8;
287 static XmlInputStream ()
289 StrictUTF8 = new UTF8Encoding (false, true);
298 static XmlException encodingException = new XmlException ("invalid encoding specification.");
300 public XmlInputStream (Stream stream)
305 private void Initialize (Stream stream)
307 buffer = new byte [64];
308 this.stream = stream;
309 enc = StrictUTF8; // Default to UTF8 if we can't guess it
310 bufLength = stream.Read (buffer, 0, buffer.Length);
311 if (bufLength == -1 || bufLength == 0) {
315 int c = ReadByteSpecial ();
318 c = ReadByteSpecial ();
320 // BOM-ed little endian utf-16
321 enc = Encoding.Unicode;
323 // It doesn't start from "<?xml" then its encoding is utf-8
328 c = ReadByteSpecial ();
330 // BOM-ed big endian utf-16
331 enc = Encoding.BigEndianUnicode;
334 // It doesn't start from "<?xml" then its encoding is utf-8
339 c = ReadByteSpecial ();
341 c = ReadByteSpecial ();
346 buffer [--bufPos] = 0xEF;
350 // try to get encoding name from XMLDecl.
351 if (bufLength >= 5 && Encoding.ASCII.GetString (buffer, 1, 4) == "?xml") {
353 c = SkipWhitespace ();
355 // version. It is optional here.
358 c = ReadByteSpecial ();
359 if (c == '0') { // 0 of 1.0
364 c = SkipWhitespace ();
368 int remaining = bufLength - bufPos;
369 if (remaining >= 7 && Encoding.ASCII.GetString(buffer, bufPos, 7) == "ncoding") {
371 c = SkipWhitespace();
373 throw encodingException;
374 c = SkipWhitespace ();
376 StringBuilder sb = new StringBuilder ();
378 c = ReadByteSpecial ();
382 throw encodingException;
384 sb.Append ((char) c);
386 string encodingName = sb.ToString ();
387 if (!XmlChar.IsValidIANAEncoding (encodingName))
388 throw encodingException;
389 enc = Encoding.GetEncoding (encodingName);
395 if (bufLength >= 10 && Encoding.Unicode.GetString (buffer, 2, 8) == "?xml")
396 enc = Encoding.Unicode;
407 // Just like readbyte, but grows the buffer too.
408 int ReadByteSpecial ()
410 if (bufLength > bufPos)
411 return buffer [bufPos++];
413 byte [] newbuf = new byte [buffer.Length * 2];
414 Buffer.BlockCopy (buffer, 0, newbuf, 0, bufLength);
415 int nbytes = stream.Read (newbuf, bufLength, buffer.Length);
416 if (nbytes == -1 || nbytes == 0)
421 return buffer [bufPos++];
424 // skips whitespace and returns misc char that was read from stream
425 private int SkipWhitespace ()
429 c = ReadByteSpecial ();
431 case '\r': goto case ' ';
432 case '\n': goto case ' ';
433 case '\t': goto case ' ';
440 throw new InvalidOperationException ();
443 public Encoding ActualEncoding {
447 #region Public Overrides
448 public override bool CanRead {
450 if (bufLength > bufPos)
453 return stream.CanRead;
457 // FIXME: It should support base stream's CanSeek.
458 public override bool CanSeek {
459 get { return false; } // stream.CanSeek; }
462 public override bool CanWrite {
463 get { return false; }
466 public override long Length {
468 return stream.Length;
472 public override long Position {
474 return stream.Position - bufLength + bufPos;
477 if(value < bufLength)
480 stream.Position = value - bufLength;
484 public override void Close ()
489 public override void Flush ()
494 public override int Read (byte[] buffer, int offset, int count)
497 if (count <= bufLength - bufPos) { // all from buffer
498 Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, count);
502 int bufRest = bufLength - bufPos;
503 if (bufLength > bufPos) {
504 Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, bufRest);
508 stream.Read (buffer, offset + bufRest, count - bufRest);
513 public override int ReadByte ()
515 if (bufLength > bufPos) {
516 return buffer [bufPos++];
518 return stream.ReadByte ();
521 public override long Seek (long offset, System.IO.SeekOrigin origin)
523 int bufRest = bufLength - bufPos;
524 if (origin == SeekOrigin.Current)
525 if (offset < bufRest)
526 return buffer [bufPos + offset];
528 return stream.Seek (offset - bufRest, origin);
530 return stream.Seek (offset, origin);
533 public override void SetLength (long value)
535 stream.SetLength (value);
538 public override void Write (byte[] buffer, int offset, int count)
540 throw new NotSupportedException ();