| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- //
- // 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;
- }
- }
- }
|