| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093 |
- //-----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace System.Xml
- {
- using System;
- using System.IO;
- using System.Text;
- using System.Runtime.Serialization;
- // This wrapper does not support seek.
- // Constructors consume/emit byte order mark.
- // Supports: UTF-8, Unicode, BigEndianUnicode
- // ASSUMPTION ([....]): This class will only be used for EITHER reading OR writing. It can be done, it would just mean more buffers.
- // ASSUMPTION ([....]): The byte buffer is large enough to hold the declaration
- // ASSUMPTION ([....]): The buffer manipulation methods (FillBuffer/Compare/etc.) will only be used to parse the declaration
- // during construction.
- class EncodingStreamWrapper : Stream
- {
- enum SupportedEncoding { UTF8, UTF16LE, UTF16BE, None }
- static readonly UTF8Encoding SafeUTF8 = new UTF8Encoding(false, false);
- static readonly UnicodeEncoding SafeUTF16 = new UnicodeEncoding(false, false, false);
- static readonly UnicodeEncoding SafeBEUTF16 = new UnicodeEncoding(true, false, false);
- static readonly UTF8Encoding ValidatingUTF8 = new UTF8Encoding(false, true);
- static readonly UnicodeEncoding ValidatingUTF16 = new UnicodeEncoding(false, false, true);
- static readonly UnicodeEncoding ValidatingBEUTF16 = new UnicodeEncoding(true, false, true);
- const int BufferLength = 128;
- // UTF-8 is fastpath, so that's how these are stored
- // Compare methods adapt to unicodes.
- static readonly byte[] encodingAttr = new byte[] { (byte)'e', (byte)'n', (byte)'c', (byte)'o', (byte)'d', (byte)'i', (byte)'n', (byte)'g' };
- static readonly byte[] encodingUTF8 = new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'8' };
- static readonly byte[] encodingUnicode = new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'1', (byte)'6' };
- static readonly byte[] encodingUnicodeLE = new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'1', (byte)'6', (byte)'l', (byte)'e' };
- static readonly byte[] encodingUnicodeBE = new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'1', (byte)'6', (byte)'b', (byte)'e' };
- SupportedEncoding encodingCode;
- Encoding encoding;
- Encoder enc;
- Decoder dec;
- bool isReading;
- Stream stream;
- char[] chars;
- byte[] bytes;
- int byteOffset;
- int byteCount;
- byte[] byteBuffer = new byte[1];
- // Reading constructor
- public EncodingStreamWrapper(Stream stream, Encoding encoding)
- {
- try
- {
- this.isReading = true;
- this.stream = new BufferedStream(stream);
- // Decode the expected encoding
- SupportedEncoding expectedEnc = GetSupportedEncoding(encoding);
- // Get the byte order mark so we can determine the encoding
- // May want to try to delay allocating everything until we know the BOM
- SupportedEncoding declEnc = ReadBOMEncoding(encoding == null);
- // Check that the expected encoding matches the decl encoding.
- if (expectedEnc != SupportedEncoding.None && expectedEnc != declEnc)
- ThrowExpectedEncodingMismatch(expectedEnc, declEnc);
- // Fastpath: UTF-8 BOM
- if (declEnc == SupportedEncoding.UTF8)
- {
- // Fastpath: UTF-8 BOM, No declaration
- FillBuffer(2);
- if (bytes[byteOffset + 1] != '?' || bytes[byteOffset] != '<')
- {
- return;
- }
- FillBuffer(BufferLength);
- CheckUTF8DeclarationEncoding(bytes, byteOffset, byteCount, declEnc, expectedEnc);
- }
- else
- {
- // Convert to UTF-8
- EnsureBuffers();
- FillBuffer((BufferLength - 1) * 2);
- SetReadDocumentEncoding(declEnc);
- CleanupCharBreak();
- int count = this.encoding.GetChars(bytes, byteOffset, byteCount, chars, 0);
- byteOffset = 0;
- byteCount = ValidatingUTF8.GetBytes(chars, 0, count, bytes, 0);
- // Check for declaration
- if (bytes[1] == '?' && bytes[0] == '<')
- {
- CheckUTF8DeclarationEncoding(bytes, 0, byteCount, declEnc, expectedEnc);
- }
- else
- {
- // Declaration required if no out-of-band encoding
- if (expectedEnc == SupportedEncoding.None)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlDeclarationRequired)));
- }
- }
- }
- catch (DecoderFallbackException ex)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidBytes), ex));
- }
- }
- void SetReadDocumentEncoding(SupportedEncoding e)
- {
- EnsureBuffers();
- this.encodingCode = e;
- this.encoding = GetEncoding(e);
- }
- static Encoding GetEncoding(SupportedEncoding e)
- {
- switch (e)
- {
- case SupportedEncoding.UTF8:
- return ValidatingUTF8;
- case SupportedEncoding.UTF16LE:
- return ValidatingUTF16;
- case SupportedEncoding.UTF16BE:
- return ValidatingBEUTF16;
- default:
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlEncodingNotSupported)));
- }
- }
- static Encoding GetSafeEncoding(SupportedEncoding e)
- {
- switch (e)
- {
- case SupportedEncoding.UTF8:
- return SafeUTF8;
- case SupportedEncoding.UTF16LE:
- return SafeUTF16;
- case SupportedEncoding.UTF16BE:
- return SafeBEUTF16;
- default:
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlEncodingNotSupported)));
- }
- }
- static string GetEncodingName(SupportedEncoding enc)
- {
- switch (enc)
- {
- case SupportedEncoding.UTF8:
- return "utf-8";
- case SupportedEncoding.UTF16LE:
- return "utf-16LE";
- case SupportedEncoding.UTF16BE:
- return "utf-16BE";
- default:
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlEncodingNotSupported)));
- }
- }
- static SupportedEncoding GetSupportedEncoding(Encoding encoding)
- {
- if (encoding == null)
- return SupportedEncoding.None;
- else if (encoding.WebName == ValidatingUTF8.WebName)
- return SupportedEncoding.UTF8;
- else if (encoding.WebName == ValidatingUTF16.WebName)
- return SupportedEncoding.UTF16LE;
- else if (encoding.WebName == ValidatingBEUTF16.WebName)
- return SupportedEncoding.UTF16BE;
- else
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlEncodingNotSupported)));
- }
- // Writing constructor
- public EncodingStreamWrapper(Stream stream, Encoding encoding, bool emitBOM)
- {
- this.isReading = false;
- this.encoding = encoding;
- this.stream = new BufferedStream(stream);
- // Set the encoding code
- this.encodingCode = GetSupportedEncoding(encoding);
- if (encodingCode != SupportedEncoding.UTF8)
- {
- EnsureBuffers();
- dec = ValidatingUTF8.GetDecoder();
- enc = this.encoding.GetEncoder();
- // Emit BOM
- if (emitBOM)
- {
- byte[] bom = this.encoding.GetPreamble();
- if (bom.Length > 0)
- this.stream.Write(bom, 0, bom.Length);
- }
- }
- }
- SupportedEncoding ReadBOMEncoding(bool notOutOfBand)
- {
- int b1 = this.stream.ReadByte();
- int b2 = this.stream.ReadByte();
- int b3 = this.stream.ReadByte();
- int b4 = this.stream.ReadByte();
- // Premature end of stream
- if (b4 == -1)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.UnexpectedEndOfFile)));
- int preserve;
- SupportedEncoding e = ReadBOMEncoding((byte)b1, (byte)b2, (byte)b3, (byte)b4, notOutOfBand, out preserve);
- EnsureByteBuffer();
- switch (preserve)
- {
- case 1:
- bytes[0] = (byte)b4;
- break;
- case 2:
- bytes[0] = (byte)b3;
- bytes[1] = (byte)b4;
- break;
- case 4:
- bytes[0] = (byte)b1;
- bytes[1] = (byte)b2;
- bytes[2] = (byte)b3;
- bytes[3] = (byte)b4;
- break;
- }
- byteCount = preserve;
- return e;
- }
- static SupportedEncoding ReadBOMEncoding(byte b1, byte b2, byte b3, byte b4, bool notOutOfBand, out int preserve)
- {
- SupportedEncoding e = SupportedEncoding.UTF8; // Default
- preserve = 0;
- if (b1 == '<' && b2 != 0x00) // UTF-8, no BOM
- {
- e = SupportedEncoding.UTF8;
- preserve = 4;
- }
- else if (b1 == 0xFF && b2 == 0xFE) // UTF-16 little endian
- {
- e = SupportedEncoding.UTF16LE;
- preserve = 2;
- }
- else if (b1 == 0xFE && b2 == 0xFF) // UTF-16 big endian
- {
- e = SupportedEncoding.UTF16BE;
- preserve = 2;
- }
- else if (b1 == 0x00 && b2 == '<') // UTF-16 big endian, no BOM
- {
- e = SupportedEncoding.UTF16BE;
- if (notOutOfBand && (b3 != 0x00 || b4 != '?'))
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlDeclMissing)));
- preserve = 4;
- }
- else if (b1 == '<' && b2 == 0x00) // UTF-16 little endian, no BOM
- {
- e = SupportedEncoding.UTF16LE;
- if (notOutOfBand && (b3 != '?' || b4 != 0x00))
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlDeclMissing)));
- preserve = 4;
- }
- else if (b1 == 0xEF && b2 == 0xBB) // UTF8 with BOM
- {
- // Encoding error
- if (notOutOfBand && b3 != 0xBF)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlBadBOM)));
- preserve = 1;
- }
- else // Assume UTF8
- {
- preserve = 4;
- }
- return e;
- }
- void FillBuffer(int count)
- {
- count -= byteCount;
- while (count > 0)
- {
- int read = stream.Read(bytes, byteOffset + byteCount, count);
- if (read == 0)
- break;
- byteCount += read;
- count -= read;
- }
- }
- void EnsureBuffers()
- {
- EnsureByteBuffer();
- if (chars == null)
- chars = new char[BufferLength];
- }
- void EnsureByteBuffer()
- {
- if (bytes != null)
- return;
- bytes = new byte[BufferLength * 4];
- byteOffset = 0;
- byteCount = 0;
- }
- static void CheckUTF8DeclarationEncoding(byte[] buffer, int offset, int count, SupportedEncoding e, SupportedEncoding expectedEnc)
- {
- byte quot = 0;
- int encEq = -1;
- int max = offset + Math.Min(count, BufferLength);
- // Encoding should be second "=", abort at first "?"
- int i = 0;
- int eq = 0;
- for (i = offset + 2; i < max; i++) // Skip the "<?" so we don't get caught by the first "?"
- {
- if (quot != 0)
- {
- if (buffer[i] == quot)
- {
- quot = 0;
- }
- continue;
- }
- if (buffer[i] == (byte)'\'' || buffer[i] == (byte)'"')
- {
- quot = buffer[i];
- }
- else if (buffer[i] == (byte)'=')
- {
- if (eq == 1)
- {
- encEq = i;
- break;
- }
- eq++;
- }
- else if (buffer[i] == (byte)'?') // Not legal character in a decl before second "="
- {
- break;
- }
- }
- // No encoding found
- if (encEq == -1)
- {
- if (e != SupportedEncoding.UTF8 && expectedEnc == SupportedEncoding.None)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlDeclarationRequired)));
- return;
- }
- if (encEq < 28) // Earliest second "=" can appear
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlMalformedDecl)));
- // Back off whitespace
- for (i = encEq - 1; IsWhitespace(buffer[i]); i--);
- // Check for encoding attribute
- if (!Compare(encodingAttr, buffer, i - encodingAttr.Length + 1))
- {
- if (e != SupportedEncoding.UTF8 && expectedEnc == SupportedEncoding.None)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlDeclarationRequired)));
- return;
- }
- // Move ahead of whitespace
- for (i = encEq + 1; i < max && IsWhitespace(buffer[i]); i++);
- // Find the quotes
- if (buffer[i] != '\'' && buffer[i] != '"')
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlMalformedDecl)));
- quot = buffer[i];
- int q = i;
- for (i = q + 1; buffer[i] != quot && i < max; ++i);
- if (buffer[i] != quot)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlMalformedDecl)));
- int encStart = q + 1;
- int encCount = i - encStart;
- // lookup the encoding
- SupportedEncoding declEnc = e;
- if (encCount == encodingUTF8.Length && CompareCaseInsensitive(encodingUTF8, buffer, encStart))
- {
- declEnc = SupportedEncoding.UTF8;
- }
- else if (encCount == encodingUnicodeLE.Length && CompareCaseInsensitive(encodingUnicodeLE, buffer, encStart))
- {
- declEnc = SupportedEncoding.UTF16LE;
- }
- else if (encCount == encodingUnicodeBE.Length && CompareCaseInsensitive(encodingUnicodeBE, buffer, encStart))
- {
- declEnc = SupportedEncoding.UTF16BE;
- }
- else if (encCount == encodingUnicode.Length && CompareCaseInsensitive(encodingUnicode, buffer, encStart))
- {
- if (e == SupportedEncoding.UTF8)
- ThrowEncodingMismatch(SafeUTF8.GetString(buffer, encStart, encCount), SafeUTF8.GetString(encodingUTF8, 0, encodingUTF8.Length));
- }
- else
- {
- ThrowEncodingMismatch(SafeUTF8.GetString(buffer, encStart, encCount), e);
- }
- if (e != declEnc)
- ThrowEncodingMismatch(SafeUTF8.GetString(buffer, encStart, encCount), e);
- }
- static bool CompareCaseInsensitive(byte[] key, byte[] buffer, int offset)
- {
- for (int i = 0; i < key.Length; i++)
- {
- if (key[i] == buffer[offset + i])
- continue;
- if (key[i] != Char.ToLower((char)buffer[offset + i], System.Globalization.CultureInfo.InvariantCulture))
- return false;
- }
- return true;
- }
- static bool Compare(byte[] key, byte[] buffer, int offset)
- {
- for (int i = 0; i < key.Length; i++)
- {
- if (key[i] != buffer[offset + i])
- return false;
- }
- return true;
- }
- static bool IsWhitespace(byte ch)
- {
- return ch == (byte)' ' || ch == (byte)'\n' || ch == (byte)'\t' || ch == (byte)'\r';
- }
- internal static ArraySegment<byte> ProcessBuffer(byte[] buffer, int offset, int count, Encoding encoding)
- {
- if (count < 4)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.UnexpectedEndOfFile)));
- try
- {
- int preserve;
- ArraySegment<byte> seg;
- SupportedEncoding expectedEnc = GetSupportedEncoding(encoding);
- SupportedEncoding declEnc = ReadBOMEncoding(buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3], encoding == null, out preserve);
- if (expectedEnc != SupportedEncoding.None && expectedEnc != declEnc)
- ThrowExpectedEncodingMismatch(expectedEnc, declEnc);
- offset += 4 - preserve;
- count -= 4 - preserve;
- // Fastpath: UTF-8
- char[] chars;
- byte[] bytes;
- Encoding localEnc;
- if (declEnc == SupportedEncoding.UTF8)
- {
- // Fastpath: No declaration
- if (buffer[offset + 1] != '?' || buffer[offset] != '<')
- {
- seg = new ArraySegment<byte>(buffer, offset, count);
- return seg;
- }
- CheckUTF8DeclarationEncoding(buffer, offset, count, declEnc, expectedEnc);
- seg = new ArraySegment<byte>(buffer, offset, count);
- return seg;
- }
- // Convert to UTF-8
- localEnc = GetSafeEncoding(declEnc);
- int inputCount = Math.Min(count, BufferLength * 2);
- chars = new char[localEnc.GetMaxCharCount(inputCount)];
- int ccount = localEnc.GetChars(buffer, offset, inputCount, chars, 0);
- bytes = new byte[ValidatingUTF8.GetMaxByteCount(ccount)];
- int bcount = ValidatingUTF8.GetBytes(chars, 0, ccount, bytes, 0);
- // Check for declaration
- if (bytes[1] == '?' && bytes[0] == '<')
- {
- CheckUTF8DeclarationEncoding(bytes, 0, bcount, declEnc, expectedEnc);
- }
- else
- {
- // Declaration required if no out-of-band encoding
- if (expectedEnc == SupportedEncoding.None)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlDeclarationRequired)));
- }
- seg = new ArraySegment<byte>(ValidatingUTF8.GetBytes(GetEncoding(declEnc).GetChars(buffer, offset, count)));
- return seg;
- }
- catch (DecoderFallbackException e)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidBytes), e));
- }
- }
- static void ThrowExpectedEncodingMismatch(SupportedEncoding expEnc, SupportedEncoding actualEnc)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlExpectedEncoding, GetEncodingName(expEnc), GetEncodingName(actualEnc))));
- }
- static void ThrowEncodingMismatch(string declEnc, SupportedEncoding enc)
- {
- ThrowEncodingMismatch(declEnc, GetEncodingName(enc));
- }
- static void ThrowEncodingMismatch(string declEnc, string docEnc)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlEncodingMismatch, declEnc, docEnc)));
- }
- // This stream wrapper does not support duplex
- public override bool CanRead
- {
- get
- {
- if (!isReading)
- return false;
- return this.stream.CanRead;
- }
- }
- // The encoding conversion and buffering breaks seeking.
- public override bool CanSeek
- {
- get
- {
- return false;
- }
- }
- // This stream wrapper does not support duplex
- public override bool CanWrite
- {
- get
- {
- if (isReading)
- return false;
- return this.stream.CanWrite;
- }
- }
- // The encoding conversion and buffering breaks seeking.
- public override long Position
- {
- get
- {
- #pragma warning suppress 56503 // The contract for non seekable stream is to throw exception
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
- }
- set
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
- }
- }
- public override void Close()
- {
- Flush();
- base.Close();
- this.stream.Close();
- }
- public override void Flush()
- {
- this.stream.Flush();
- }
- public override int ReadByte()
- {
- if (byteCount == 0 && encodingCode == SupportedEncoding.UTF8)
- return this.stream.ReadByte();
- if (Read(byteBuffer, 0, 1) == 0)
- return -1;
- return byteBuffer[0];
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- try
- {
- if (byteCount == 0)
- {
- if (encodingCode == SupportedEncoding.UTF8)
- return this.stream.Read(buffer, offset, count);
- // No more bytes than can be turned into characters
- byteOffset = 0;
- byteCount = this.stream.Read(bytes, byteCount, (chars.Length - 1) * 2);
- // Check for end of stream
- if (byteCount == 0)
- return 0;
- // Fix up incomplete chars
- CleanupCharBreak();
- // Change encoding
- int charCount = this.encoding.GetChars(bytes, 0, byteCount, chars, 0);
- byteCount = Encoding.UTF8.GetBytes(chars, 0, charCount, bytes, 0);
- }
- // Give them bytes
- if (byteCount < count)
- count = byteCount;
- Buffer.BlockCopy(bytes, byteOffset, buffer, offset, count);
- byteOffset += count;
- byteCount -= count;
- return count;
- }
- catch (DecoderFallbackException ex)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidBytes), ex));
- }
- }
- void CleanupCharBreak()
- {
- int max = byteOffset + byteCount;
- // Read on 2 byte boundaries
- if ((byteCount % 2) != 0)
- {
- int b = this.stream.ReadByte();
- if (b < 0)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.UnexpectedEndOfFile)));
- bytes[max++] = (byte)b;
- byteCount++;
- }
- // Don't cut off a surrogate character
- int w;
- if (encodingCode == SupportedEncoding.UTF16LE)
- {
- w = bytes[max - 2] + (bytes[max - 1] << 8);
- }
- else
- {
- w = bytes[max - 1] + (bytes[max - 2] << 8);
- }
- if ((w & 0xDC00) != 0xDC00 && w >= 0xD800 && w <= 0xDBFF) // First 16-bit number of surrogate pair
- {
- int b1 = this.stream.ReadByte();
- int b2 = this.stream.ReadByte();
- if (b2 < 0)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.UnexpectedEndOfFile)));
- bytes[max++] = (byte)b1;
- bytes[max++] = (byte)b2;
- byteCount += 2;
- }
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
- }
- public override void WriteByte(byte b)
- {
- if (encodingCode == SupportedEncoding.UTF8)
- {
- this.stream.WriteByte(b);
- return;
- }
- byteBuffer[0] = b;
- Write(byteBuffer, 0, 1);
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- // Optimize UTF-8 case
- if (encodingCode == SupportedEncoding.UTF8)
- {
- this.stream.Write(buffer, offset, count);
- return;
- }
- while (count > 0)
- {
- int size = chars.Length < count ? chars.Length : count;
- int charCount = dec.GetChars(buffer, offset, size, chars, 0, false);
- byteCount = enc.GetBytes(chars, 0, charCount, bytes, 0, false);
- this.stream.Write(bytes, 0, byteCount);
- offset += size;
- count -= size;
- }
- }
- // Delegate properties
- public override bool CanTimeout { get { return this.stream.CanTimeout; } }
- public override long Length { get { return this.stream.Length; } }
- public override int ReadTimeout
- {
- get { return this.stream.ReadTimeout; }
- set { this.stream.ReadTimeout = value; }
- }
- public override int WriteTimeout
- {
- get { return this.stream.WriteTimeout; }
- set { this.stream.WriteTimeout = value; }
- }
- // Delegate methods
- public override void SetLength(long value)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
- }
- }
- // Add format exceptions
- // Do we need to modify the stream position/Seek to account for the buffer?
- // ASSUMPTION ([....]): This class will only be used for EITHER reading OR writing.
- #if NO
- class UTF16Stream : Stream
- {
- const int BufferLength = 128;
-
- Stream stream;
- bool bigEndian;
- byte[] streamBuffer;
- int streamOffset;
- int streamMax;
- byte[] trailBytes = new byte[4];
- int trailCount;
-
- public UTF16Stream(Stream stream, bool bigEndian)
- {
- this.stream = stream;
- this.bigEndian = bigEndian;
- this.streamBuffer = byte[BufferLength];
- }
- public override void Close()
- {
- Flush();
- base.Close();
- this.stream.Close();
- }
- public override void Flush()
- {
- this.stream.Flush();
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- // Validate args
-
- // Read what we can if we aren't sure we have enough for a single character
- if (this.streamMax < 4)
- this.streamMax += this.stream.Read(this.streamBuffer, streamOffset, streamBuffer.Length - this.streamMax);
- int totalWritten = 0;
- while (streamOffset < streamMax && count > 0)
- {
- int ch;
- int read;
-
- read = ReadUTF16Char(out ch, streamBuffer, streamOffset, streamBuffer.Length - streamMax);
- if (read == 0)
- break;
- int written = WriteUTF8Char(ch, buffer, offset, count);
- if (written == 0)
- break;
-
- totalWritten += written;
- streamOffset += read;
- offset += written;
- count -= written;
- }
-
- // Shift down the leftover data
- if (this.streamOffset > 0 && this.streamOffset < this.streamMax)
- {
- Buffer.BlockCopy(this.streamBuffer, this.streamOffset, this.streamBuffer, 0, this.streamMax - this.streamOffset);
- this.streamMax -= this.streamOffset;
- this.streamOffset = 0;
- }
- return totalWritten;
- }
- int ReadUTF8Char(out int ch, byte[] buffer, int offset, int count)
- {
- ch = -1;
- if (buffer[offset] < 0x80)
- {
- ch = buffer[offset];
- return 1;
- }
-
- int mask = buffer[offset] & 0xF0;
- byte b1, b2, b3, b4;
- if (mask == 0xC0)
- {
- if (count < 2)
- return 0;
-
- b1 = buffer[offset + 0];
- b2 = buffer[offset + 1];
-
- ch = ((b1 & 0x1F) << 6) + (b2 & 0x3F);
-
- return 2;
- }
- else if (mask == 0xE0)
- {
- if (count < 3)
- return 0;
-
- b1 = buffer[offset + 0];
- b2 = buffer[offset + 1];
- b3 = buffer[offset + 2];
- ch = ((((b1 & 0x0F) << 6) + (b2 & 0x3F)) << 6) + (b3 & 0x3F);
-
- return 3;
- }
- else if (mask == 0xF0)
- {
- if (count < 4)
- return 0;
-
- b1 = buffer[offset + 0];
- b2 = buffer[offset + 1];
- b3 = buffer[offset + 2];
- b4 = buffer[offset + 3];
-
- ch = ((((((b1 & 0x0F) << 6) + (b2 & 0x3F)) << 6) + (b3 & 0x3F)) << 6) + (b4 & 0x3F);
-
- return 4;
- }
-
- // Invalid
- return 0;
- }
-
- int ReadUTF16Char(out int ch, byte[] buffer, int offset, int count)
- {
- ch = -1;
-
- if (count < 2)
- return 0;
-
- int w1 = ReadEndian(buffer, offset);
-
- if (w1 < 0xD800 || w1 > 0xDFFF)
- {
- ch = w1;
- return 2;
- }
-
- if (count < 4)
- return 0;
-
- int w2 = ReadEndian(buffer, offset + 2);
-
- ch = ((w1 & 0x03FF) << 10) + (w2 & 0x03FF);
- return 4;
- }
- int ReadEndian(byte[] buffer, int offset)
- {
- if (bigEndian)
- {
- return (buffer[offset + 0] << 8) + buffer[offset + 1];
- }
- else
- {
- return (buffer[offset + 1] << 8) + buffer[offset + 0];
- }
- }
- int WriteUTF8Char(int ch, byte[] buffer, int offset, int count)
- {
- if (ch < 0x80)
- {
- buffer[offset] = (byte)ch;
- return 1;
- }
- else if (ch < 0x800)
- {
- if (count < 2)
- return 0;
- buffer[offset + 1] = 0x80 | (ch & 0x3F);
- ch >>= 6;
- buffer[offset + 0] = 0xC0 | ch;
- return 2
- }
- else if (ch < 0x10000)
- {
- if (count < 3)
- return 0;
-
- buffer[offset + 2] = 0x80 | (ch & 0x3F);
- ch >>= 6;
- buffer[offset + 1] = 0x80 | (ch & 0x3F);
- ch >>= 6;
- buffer[offset + 0] = 0xE0 | ch;
- return 3;
- }
- else if (ch <= 0x110000)
- {
- if (count < 4)
- return 0;
- buffer[offset + 3] = 0x80 | (ch & 0x3F);
- ch >>= 6;
- buffer[offset + 2] = 0x80 | (ch & 0x3F);
- ch >>= 6;
- buffer[offset + 1] = 0x80 | (ch & 0x3F);
- ch >>= 6;
- buffer[offset + 0] = 0xF0 | ch;
- return 4;
- }
-
- // Invalid?
- return 0;
- }
- int WriteUTF16Char(int ch, byte[] buffer, int offset, int count)
- {
- if (ch < 0x10000)
- {
- if (count < 2)
- return 0;
- WriteEndian(ch, buffer, offset);
- return 2;
- }
- if (count < 4)
- return 0;
-
- ch -= 0x10000;
- int w2 = 0xDC00 | (ch & 0x03FF);
- int w1 = 0xD800 | ch >> 10;
- WriteEndian(w1, buffer, offset);
- WriteEndian(w2, buffer, offset + 2);
- return 4;
- }
- void WriteEndian(int ch, byte[] buffer, int offset)
- {
- if (bigEndian)
- {
- buffer[offset + 1] = (byte)ch;
- buffer[offset + 0] = ch >> 8;
- }
- else
- {
- buffer[offset + 0] = (byte)ch;
- buffer[offset + 1] = ch >> 8;
- }
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- // Validate args
-
- // Write the trail bytes
- if (trailCount > 0)
- {
- int free = 4-trailCount;
- int total = (count < free ? count : free) + trialCount;
- Buffer.BlockCopy(buffer, offset, trailBytes, trailCount, total);
-
- int c;
- int r = ReadUTF8Char(out c, trailBuffer, 0, total);
- if (r == 0 && count < free)
- {
- trailCount = total;
- return;
- }
- int diff = r - trailCount;
- offset += diff;
- count -= diff;
- streamOffset = WriteUTF16Char(c, streamBuffer, 0, streamBuffer.Length - streamOffset);
- }
-
- while (count > 0)
- {
- if (streamBuffer.Length - streamOffset < 4)
- {
- this.stream.Write(streamBuffer, 0, streamOffset);
- streamOffset = 0;
- }
- int ch;
- int read = ReadUTF8Char(out ch, buffer, offset, count);
- if (read == 0)
- break;
- int written = WriteUTF16Char(ch, streamBuffer, streamOffset, streamBuffer.Length - streamOffset);
- if (written == 0)
- break;
-
- streamOffset += written;
- offset += read;
- count -= read;
- }
-
- if (streamOffset > 0)
- {
- this.stream.Write(streamBuffer, 0, streamOffset);
- streamOffset = 0;
- }
-
- // Save trailing bytes
- if (count > 0)
- {
- Buffer.BlockCopy(buffer, offset, trailBytes, 0, count);
- trailCount = count;
- }
- }
- // Delegate properties
- public override bool CanRead { get { return this.stream.CanRead; } }
- public override bool CanSeek { get { return this.stream.CanSeek; } }
- public override bool CanTimeout { get { return this.stream.CanTimeout; } }
- public override bool CanWrite { get { return this.stream.CanWrite; } }
- public override long Length { get { return this.stream.Length; } }
- public override long Position
- {
- get { return this.stream.Position; }
- set { this.stream.Position = value; }
- }
- public override int ReadTimeout
- {
- get { return this.stream.ReadTimeout; }
- set { this.stream.ReadTimeout = value; }
- }
- public override int WriteTimeout
- {
- get { return this.stream.WriteTimeout; }
- set { this.stream.WriteTimeout = value; }
- }
-
- // Delegate methods
- public override long Seek(long offset, SeekOrigin origin)
- {
- return this.stream.Seek(offset, origin);
- }
-
- public override void SetLength(long value)
- {
- this.stream.SetLength(value);
- }
- }
- #endif
- }
|