1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
9 using System
.Runtime
.Serialization
;
11 // This wrapper does not support seek.
12 // Constructors consume/emit byte order mark.
13 // Supports: UTF-8, Unicode, BigEndianUnicode
14 // ASSUMPTION (Microsoft): This class will only be used for EITHER reading OR writing. It can be done, it would just mean more buffers.
15 // ASSUMPTION (Microsoft): The byte buffer is large enough to hold the declaration
16 // ASSUMPTION (Microsoft): The buffer manipulation methods (FillBuffer/Compare/etc.) will only be used to parse the declaration
17 // during construction.
18 class EncodingStreamWrapper
: Stream
20 enum SupportedEncoding { UTF8, UTF16LE, UTF16BE, None }
21 static readonly UTF8Encoding SafeUTF8
= new UTF8Encoding(false, false);
22 static readonly UnicodeEncoding SafeUTF16
= new UnicodeEncoding(false, false, false);
23 static readonly UnicodeEncoding SafeBEUTF16
= new UnicodeEncoding(true, false, false);
24 static readonly UTF8Encoding ValidatingUTF8
= new UTF8Encoding(false, true);
25 static readonly UnicodeEncoding ValidatingUTF16
= new UnicodeEncoding(false, false, true);
26 static readonly UnicodeEncoding ValidatingBEUTF16
= new UnicodeEncoding(true, false, true);
27 const int BufferLength
= 128;
29 // UTF-8 is fastpath, so that's how these are stored
30 // Compare methods adapt to unicodes.
31 static readonly byte[] encodingAttr
= new byte[] { (byte)'e', (byte)'n', (byte)'c', (byte)'o', (byte)'d', (byte)'i', (byte)'n', (byte)'g' }
;
32 static readonly byte[] encodingUTF8
= new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'8' }
;
33 static readonly byte[] encodingUnicode
= new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'1', (byte)'6' }
;
34 static readonly byte[] encodingUnicodeLE
= new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'1', (byte)'6', (byte)'l', (byte)'e' }
;
35 static readonly byte[] encodingUnicodeBE
= new byte[] { (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'1', (byte)'6', (byte)'b', (byte)'e' }
;
37 SupportedEncoding encodingCode
;
49 byte[] byteBuffer
= new byte[1];
51 // Reading constructor
52 public EncodingStreamWrapper(Stream stream
, Encoding encoding
)
56 this.isReading
= true;
57 this.stream
= new BufferedStream(stream
);
59 // Decode the expected encoding
60 SupportedEncoding expectedEnc
= GetSupportedEncoding(encoding
);
62 // Get the byte order mark so we can determine the encoding
63 // May want to try to delay allocating everything until we know the BOM
64 SupportedEncoding declEnc
= ReadBOMEncoding(encoding
== null);
66 // Check that the expected encoding matches the decl encoding.
67 if (expectedEnc
!= SupportedEncoding
.None
&& expectedEnc
!= declEnc
)
68 ThrowExpectedEncodingMismatch(expectedEnc
, declEnc
);
70 // Fastpath: UTF-8 BOM
71 if (declEnc
== SupportedEncoding
.UTF8
)
73 // Fastpath: UTF-8 BOM, No declaration
75 if (bytes
[byteOffset
+ 1] != '?' || bytes
[byteOffset
] != '<')
80 FillBuffer(BufferLength
);
81 CheckUTF8DeclarationEncoding(bytes
, byteOffset
, byteCount
, declEnc
, expectedEnc
);
87 FillBuffer((BufferLength
- 1) * 2);
88 SetReadDocumentEncoding(declEnc
);
90 int count
= this.encoding
.GetChars(bytes
, byteOffset
, byteCount
, chars
, 0);
92 byteCount
= ValidatingUTF8
.GetBytes(chars
, 0, count
, bytes
, 0);
94 // Check for declaration
95 if (bytes
[1] == '?' && bytes
[0] == '<')
97 CheckUTF8DeclarationEncoding(bytes
, 0, byteCount
, declEnc
, expectedEnc
);
101 // Declaration required if no out-of-band encoding
102 if (expectedEnc
== SupportedEncoding
.None
)
103 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlDeclarationRequired
)));
107 catch (DecoderFallbackException ex
)
109 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlInvalidBytes
), ex
));
113 void SetReadDocumentEncoding(SupportedEncoding e
)
116 this.encodingCode
= e
;
117 this.encoding
= GetEncoding(e
);
120 static Encoding
GetEncoding(SupportedEncoding e
)
124 case SupportedEncoding
.UTF8
:
125 return ValidatingUTF8
;
127 case SupportedEncoding
.UTF16LE
:
128 return ValidatingUTF16
;
130 case SupportedEncoding
.UTF16BE
:
131 return ValidatingBEUTF16
;
134 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlEncodingNotSupported
)));
138 static Encoding
GetSafeEncoding(SupportedEncoding e
)
142 case SupportedEncoding
.UTF8
:
145 case SupportedEncoding
.UTF16LE
:
148 case SupportedEncoding
.UTF16BE
:
152 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlEncodingNotSupported
)));
156 static string GetEncodingName(SupportedEncoding enc
)
160 case SupportedEncoding
.UTF8
:
163 case SupportedEncoding
.UTF16LE
:
166 case SupportedEncoding
.UTF16BE
:
170 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlEncodingNotSupported
)));
174 static SupportedEncoding
GetSupportedEncoding(Encoding encoding
)
176 if (encoding
== null)
177 return SupportedEncoding
.None
;
178 else if (encoding
.WebName
== ValidatingUTF8
.WebName
)
179 return SupportedEncoding
.UTF8
;
180 else if (encoding
.WebName
== ValidatingUTF16
.WebName
)
181 return SupportedEncoding
.UTF16LE
;
182 else if (encoding
.WebName
== ValidatingBEUTF16
.WebName
)
183 return SupportedEncoding
.UTF16BE
;
185 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlEncodingNotSupported
)));
188 // Writing constructor
189 public EncodingStreamWrapper(Stream stream
, Encoding encoding
, bool emitBOM
)
191 this.isReading
= false;
192 this.encoding
= encoding
;
193 this.stream
= new BufferedStream(stream
);
195 // Set the encoding code
196 this.encodingCode
= GetSupportedEncoding(encoding
);
198 if (encodingCode
!= SupportedEncoding
.UTF8
)
201 dec
= ValidatingUTF8
.GetDecoder();
202 enc
= this.encoding
.GetEncoder();
207 byte[] bom
= this.encoding
.GetPreamble();
209 this.stream
.Write(bom
, 0, bom
.Length
);
214 SupportedEncoding
ReadBOMEncoding(bool notOutOfBand
)
216 int b1
= this.stream
.ReadByte();
217 int b2
= this.stream
.ReadByte();
218 int b3
= this.stream
.ReadByte();
219 int b4
= this.stream
.ReadByte();
221 // Premature end of stream
223 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.UnexpectedEndOfFile
)));
226 SupportedEncoding e
= ReadBOMEncoding((byte)b1
, (byte)b2
, (byte)b3
, (byte)b4
, notOutOfBand
, out preserve
);
247 byteCount
= preserve
;
252 static SupportedEncoding
ReadBOMEncoding(byte b1
, byte b2
, byte b3
, byte b4
, bool notOutOfBand
, out int preserve
)
254 SupportedEncoding e
= SupportedEncoding
.UTF8
; // Default
257 if (b1
== '<' && b2
!= 0x00) // UTF-8, no BOM
259 e
= SupportedEncoding
.UTF8
;
262 else if (b1
== 0xFF && b2
== 0xFE) // UTF-16 little endian
264 e
= SupportedEncoding
.UTF16LE
;
267 else if (b1
== 0xFE && b2
== 0xFF) // UTF-16 big endian
269 e
= SupportedEncoding
.UTF16BE
;
272 else if (b1
== 0x00 && b2
== '<') // UTF-16 big endian, no BOM
274 e
= SupportedEncoding
.UTF16BE
;
276 if (notOutOfBand
&& (b3
!= 0x00 || b4
!= '?'))
277 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlDeclMissing
)));
280 else if (b1
== '<' && b2
== 0x00) // UTF-16 little endian, no BOM
282 e
= SupportedEncoding
.UTF16LE
;
284 if (notOutOfBand
&& (b3
!= '?' || b4
!= 0x00))
285 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlDeclMissing
)));
288 else if (b1
== 0xEF && b2
== 0xBB) // UTF8 with BOM
291 if (notOutOfBand
&& b3
!= 0xBF)
292 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlBadBOM
)));
303 void FillBuffer(int count
)
308 int read
= stream
.Read(bytes
, byteOffset
+ byteCount
, count
);
321 chars
= new char[BufferLength
];
324 void EnsureByteBuffer()
329 bytes
= new byte[BufferLength
* 4];
334 static void CheckUTF8DeclarationEncoding(byte[] buffer
, int offset
, int count
, SupportedEncoding e
, SupportedEncoding expectedEnc
)
338 int max
= offset
+ Math
.Min(count
, BufferLength
);
340 // Encoding should be second "=", abort at first "?"
343 for (i
= offset
+ 2; i
< max
; i
++) // Skip the "<?" so we don't get caught by the first "?"
347 if (buffer
[i
] == quot
)
354 if (buffer
[i
] == (byte)'\'' || buffer
[i
] == (byte)'"')
358 else if (buffer
[i
] == (byte)'=')
367 else if (buffer
[i
] == (byte)'?') // Not legal character in a decl before second "="
376 if (e
!= SupportedEncoding
.UTF8
&& expectedEnc
== SupportedEncoding
.None
)
377 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlDeclarationRequired
)));
381 if (encEq
< 28) // Earliest second "=" can appear
382 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlMalformedDecl
)));
384 // Back off whitespace
385 for (i
= encEq
- 1; IsWhitespace(buffer
[i
]); i
--);
387 // Check for encoding attribute
388 if (!Compare(encodingAttr
, buffer
, i
- encodingAttr
.Length
+ 1))
390 if (e
!= SupportedEncoding
.UTF8
&& expectedEnc
== SupportedEncoding
.None
)
391 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlDeclarationRequired
)));
395 // Move ahead of whitespace
396 for (i
= encEq
+ 1; i
< max
&& IsWhitespace(buffer
[i
]); i
++);
399 if (buffer
[i
] != '\'' && buffer
[i
] != '"')
400 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlMalformedDecl
)));
404 for (i
= q
+ 1; buffer
[i
] != quot
&& i
< max
; ++i
);
406 if (buffer
[i
] != quot
)
407 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlMalformedDecl
)));
409 int encStart
= q
+ 1;
410 int encCount
= i
- encStart
;
412 // lookup the encoding
413 SupportedEncoding declEnc
= e
;
414 if (encCount
== encodingUTF8
.Length
&& CompareCaseInsensitive(encodingUTF8
, buffer
, encStart
))
416 declEnc
= SupportedEncoding
.UTF8
;
418 else if (encCount
== encodingUnicodeLE
.Length
&& CompareCaseInsensitive(encodingUnicodeLE
, buffer
, encStart
))
420 declEnc
= SupportedEncoding
.UTF16LE
;
422 else if (encCount
== encodingUnicodeBE
.Length
&& CompareCaseInsensitive(encodingUnicodeBE
, buffer
, encStart
))
424 declEnc
= SupportedEncoding
.UTF16BE
;
426 else if (encCount
== encodingUnicode
.Length
&& CompareCaseInsensitive(encodingUnicode
, buffer
, encStart
))
428 if (e
== SupportedEncoding
.UTF8
)
429 ThrowEncodingMismatch(SafeUTF8
.GetString(buffer
, encStart
, encCount
), SafeUTF8
.GetString(encodingUTF8
, 0, encodingUTF8
.Length
));
433 ThrowEncodingMismatch(SafeUTF8
.GetString(buffer
, encStart
, encCount
), e
);
437 ThrowEncodingMismatch(SafeUTF8
.GetString(buffer
, encStart
, encCount
), e
);
440 static bool CompareCaseInsensitive(byte[] key
, byte[] buffer
, int offset
)
442 for (int i
= 0; i
< key
.Length
; i
++)
444 if (key
[i
] == buffer
[offset
+ i
])
447 if (key
[i
] != Char
.ToLower((char)buffer
[offset
+ i
], System
.Globalization
.CultureInfo
.InvariantCulture
))
453 static bool Compare(byte[] key
, byte[] buffer
, int offset
)
455 for (int i
= 0; i
< key
.Length
; i
++)
457 if (key
[i
] != buffer
[offset
+ i
])
463 static bool IsWhitespace(byte ch
)
465 return ch
== (byte)' ' || ch
== (byte)'\n' || ch
== (byte)'\t' || ch
== (byte)'\r';
468 internal static ArraySegment
<byte> ProcessBuffer(byte[] buffer
, int offset
, int count
, Encoding encoding
)
471 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.UnexpectedEndOfFile
)));
476 ArraySegment
<byte> seg
;
478 SupportedEncoding expectedEnc
= GetSupportedEncoding(encoding
);
479 SupportedEncoding declEnc
= ReadBOMEncoding(buffer
[offset
], buffer
[offset
+ 1], buffer
[offset
+ 2], buffer
[offset
+ 3], encoding
== null, out preserve
);
480 if (expectedEnc
!= SupportedEncoding
.None
&& expectedEnc
!= declEnc
)
481 ThrowExpectedEncodingMismatch(expectedEnc
, declEnc
);
483 offset
+= 4 - preserve
;
484 count
-= 4 - preserve
;
490 if (declEnc
== SupportedEncoding
.UTF8
)
492 // Fastpath: No declaration
493 if (buffer
[offset
+ 1] != '?' || buffer
[offset
] != '<')
495 seg
= new ArraySegment
<byte>(buffer
, offset
, count
);
499 CheckUTF8DeclarationEncoding(buffer
, offset
, count
, declEnc
, expectedEnc
);
500 seg
= new ArraySegment
<byte>(buffer
, offset
, count
);
505 localEnc
= GetSafeEncoding(declEnc
);
506 int inputCount
= Math
.Min(count
, BufferLength
* 2);
507 chars
= new char[localEnc
.GetMaxCharCount(inputCount
)];
508 int ccount
= localEnc
.GetChars(buffer
, offset
, inputCount
, chars
, 0);
509 bytes
= new byte[ValidatingUTF8
.GetMaxByteCount(ccount
)];
510 int bcount
= ValidatingUTF8
.GetBytes(chars
, 0, ccount
, bytes
, 0);
512 // Check for declaration
513 if (bytes
[1] == '?' && bytes
[0] == '<')
515 CheckUTF8DeclarationEncoding(bytes
, 0, bcount
, declEnc
, expectedEnc
);
519 // Declaration required if no out-of-band encoding
520 if (expectedEnc
== SupportedEncoding
.None
)
521 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlDeclarationRequired
)));
524 seg
= new ArraySegment
<byte>(ValidatingUTF8
.GetBytes(GetEncoding(declEnc
).GetChars(buffer
, offset
, count
)));
527 catch (DecoderFallbackException e
)
529 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlInvalidBytes
), e
));
533 static void ThrowExpectedEncodingMismatch(SupportedEncoding expEnc
, SupportedEncoding actualEnc
)
535 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlExpectedEncoding
, GetEncodingName(expEnc
), GetEncodingName(actualEnc
))));
538 static void ThrowEncodingMismatch(string declEnc
, SupportedEncoding enc
)
540 ThrowEncodingMismatch(declEnc
, GetEncodingName(enc
));
543 static void ThrowEncodingMismatch(string declEnc
, string docEnc
)
545 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlEncodingMismatch
, declEnc
, docEnc
)));
548 // This stream wrapper does not support duplex
549 public override bool CanRead
556 return this.stream
.CanRead
;
560 // The encoding conversion and buffering breaks seeking.
561 public override bool CanSeek
569 // This stream wrapper does not support duplex
570 public override bool CanWrite
577 return this.stream
.CanWrite
;
582 // The encoding conversion and buffering breaks seeking.
583 public override long Position
587 #pragma warning suppress 56503 // The contract for non seekable stream is to throw exception
588 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new NotSupportedException());
592 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new NotSupportedException());
596 public override void Close()
603 public override void Flush()
608 public override int ReadByte()
610 if (byteCount
== 0 && encodingCode
== SupportedEncoding
.UTF8
)
611 return this.stream
.ReadByte();
612 if (Read(byteBuffer
, 0, 1) == 0)
614 return byteBuffer
[0];
617 public override int Read(byte[] buffer
, int offset
, int count
)
623 if (encodingCode
== SupportedEncoding
.UTF8
)
624 return this.stream
.Read(buffer
, offset
, count
);
626 // No more bytes than can be turned into characters
628 byteCount
= this.stream
.Read(bytes
, byteCount
, (chars
.Length
- 1) * 2);
630 // Check for end of stream
634 // Fix up incomplete chars
638 int charCount
= this.encoding
.GetChars(bytes
, 0, byteCount
, chars
, 0);
639 byteCount
= Encoding
.UTF8
.GetBytes(chars
, 0, charCount
, bytes
, 0);
643 if (byteCount
< count
)
645 Buffer
.BlockCopy(bytes
, byteOffset
, buffer
, offset
, count
);
650 catch (DecoderFallbackException ex
)
652 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.XmlInvalidBytes
), ex
));
656 void CleanupCharBreak()
658 int max
= byteOffset
+ byteCount
;
660 // Read on 2 byte boundaries
661 if ((byteCount
% 2) != 0)
663 int b
= this.stream
.ReadByte();
665 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.UnexpectedEndOfFile
)));
667 bytes
[max
++] = (byte)b
;
671 // Don't cut off a surrogate character
673 if (encodingCode
== SupportedEncoding
.UTF16LE
)
675 w
= bytes
[max
- 2] + (bytes
[max
- 1] << 8);
679 w
= bytes
[max
- 1] + (bytes
[max
- 2] << 8);
681 if ((w
& 0xDC00) != 0xDC00 && w
>= 0xD800 && w
<= 0xDBFF) // First 16-bit number of surrogate pair
683 int b1
= this.stream
.ReadByte();
684 int b2
= this.stream
.ReadByte();
686 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new XmlException(SR
.GetString(SR
.UnexpectedEndOfFile
)));
687 bytes
[max
++] = (byte)b1
;
688 bytes
[max
++] = (byte)b2
;
693 public override long Seek(long offset
, SeekOrigin origin
)
695 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new NotSupportedException());
698 public override void WriteByte(byte b
)
700 if (encodingCode
== SupportedEncoding
.UTF8
)
702 this.stream
.WriteByte(b
);
706 Write(byteBuffer
, 0, 1);
709 public override void Write(byte[] buffer
, int offset
, int count
)
711 // Optimize UTF-8 case
712 if (encodingCode
== SupportedEncoding
.UTF8
)
714 this.stream
.Write(buffer
, offset
, count
);
720 int size
= chars
.Length
< count
? chars
.Length
: count
;
721 int charCount
= dec
.GetChars(buffer
, offset
, size
, chars
, 0, false);
722 byteCount
= enc
.GetBytes(chars
, 0, charCount
, bytes
, 0, false);
723 this.stream
.Write(bytes
, 0, byteCount
);
729 // Delegate properties
730 public override bool CanTimeout { get { return this.stream.CanTimeout; }
}
731 public override long Length { get { return this.stream.Length; }
}
732 public override int ReadTimeout
734 get { return this.stream.ReadTimeout; }
735 set { this.stream.ReadTimeout = value; }
737 public override int WriteTimeout
739 get { return this.stream.WriteTimeout; }
740 set { this.stream.WriteTimeout = value; }
744 public override void SetLength(long value)
746 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new NotSupportedException());
750 // Add format exceptions
751 // Do we need to modify the stream position/Seek to account for the buffer?
752 // ASSUMPTION (Microsoft): This class will only be used for EITHER reading OR writing.
754 class UTF16Stream
: Stream
756 const int BufferLength
= 128;
763 byte[] trailBytes
= new byte[4];
766 public UTF16Stream(Stream stream
, bool bigEndian
)
768 this.stream
= stream
;
769 this.bigEndian
= bigEndian
;
770 this.streamBuffer
= byte[BufferLength
];
773 public override void Close()
780 public override void Flush()
785 public override int Read(byte[] buffer
, int offset
, int count
)
789 // Read what we can if we aren't sure we have enough for a single character
790 if (this.streamMax
< 4)
791 this.streamMax
+= this.stream
.Read(this.streamBuffer
, streamOffset
, streamBuffer
.Length
- this.streamMax
);
793 int totalWritten
= 0;
794 while (streamOffset
< streamMax
&& count
> 0)
799 read
= ReadUTF16Char(out ch
, streamBuffer
, streamOffset
, streamBuffer
.Length
- streamMax
);
803 int written
= WriteUTF8Char(ch
, buffer
, offset
, count
);
807 totalWritten
+= written
;
808 streamOffset
+= read
;
813 // Shift down the leftover data
814 if (this.streamOffset
> 0 && this.streamOffset
< this.streamMax
)
816 Buffer
.BlockCopy(this.streamBuffer
, this.streamOffset
, this.streamBuffer
, 0, this.streamMax
- this.streamOffset
);
817 this.streamMax
-= this.streamOffset
;
818 this.streamOffset
= 0;
824 int ReadUTF8Char(out int ch
, byte[] buffer
, int offset
, int count
)
827 if (buffer
[offset
] < 0x80)
833 int mask
= buffer
[offset
] & 0xF0;
840 b1
= buffer
[offset
+ 0];
841 b2
= buffer
[offset
+ 1];
843 ch
= ((b1
& 0x1F) << 6) + (b2
& 0x3F);
847 else if (mask
== 0xE0)
852 b1
= buffer
[offset
+ 0];
853 b2
= buffer
[offset
+ 1];
854 b3
= buffer
[offset
+ 2];
856 ch
= ((((b1
& 0x0F) << 6) + (b2
& 0x3F)) << 6) + (b3
& 0x3F);
860 else if (mask
== 0xF0)
865 b1
= buffer
[offset
+ 0];
866 b2
= buffer
[offset
+ 1];
867 b3
= buffer
[offset
+ 2];
868 b4
= buffer
[offset
+ 3];
870 ch
= ((((((b1
& 0x0F) << 6) + (b2
& 0x3F)) << 6) + (b3
& 0x3F)) << 6) + (b4
& 0x3F);
879 int ReadUTF16Char(out int ch
, byte[] buffer
, int offset
, int count
)
886 int w1
= ReadEndian(buffer
, offset
);
888 if (w1
< 0xD800 || w1
> 0xDFFF)
897 int w2
= ReadEndian(buffer
, offset
+ 2);
899 ch
= ((w1
& 0x03FF) << 10) + (w2
& 0x03FF);
903 int ReadEndian(byte[] buffer
, int offset
)
907 return (buffer
[offset
+ 0] << 8) + buffer
[offset
+ 1];
911 return (buffer
[offset
+ 1] << 8) + buffer
[offset
+ 0];
915 int WriteUTF8Char(int ch
, byte[] buffer
, int offset
, int count
)
919 buffer
[offset
] = (byte)ch
;
927 buffer
[offset
+ 1] = 0x80 | (ch
& 0x3F);
929 buffer
[offset
+ 0] = 0xC0 | ch
;
932 else if (ch
< 0x10000)
937 buffer
[offset
+ 2] = 0x80 | (ch
& 0x3F);
939 buffer
[offset
+ 1] = 0x80 | (ch
& 0x3F);
941 buffer
[offset
+ 0] = 0xE0 | ch
;
944 else if (ch
<= 0x110000)
948 buffer
[offset
+ 3] = 0x80 | (ch
& 0x3F);
950 buffer
[offset
+ 2] = 0x80 | (ch
& 0x3F);
952 buffer
[offset
+ 1] = 0x80 | (ch
& 0x3F);
954 buffer
[offset
+ 0] = 0xF0 | ch
;
962 int WriteUTF16Char(int ch
, byte[] buffer
, int offset
, int count
)
969 WriteEndian(ch
, buffer
, offset
);
977 int w2
= 0xDC00 | (ch
& 0x03FF);
978 int w1
= 0xD800 | ch
>> 10;
979 WriteEndian(w1
, buffer
, offset
);
980 WriteEndian(w2
, buffer
, offset
+ 2);
984 void WriteEndian(int ch
, byte[] buffer
, int offset
)
988 buffer
[offset
+ 1] = (byte)ch
;
989 buffer
[offset
+ 0] = ch
>> 8;
993 buffer
[offset
+ 0] = (byte)ch
;
994 buffer
[offset
+ 1] = ch
>> 8;
998 public override void Write(byte[] buffer
, int offset
, int count
)
1002 // Write the trail bytes
1005 int free
= 4-trailCount
;
1006 int total
= (count
< free
? count
: free
) + trialCount
;
1007 Buffer
.BlockCopy(buffer
, offset
, trailBytes
, trailCount
, total
);
1010 int r
= ReadUTF8Char(out c
, trailBuffer
, 0, total
);
1011 if (r
== 0 && count
< free
)
1017 int diff
= r
- trailCount
;
1020 streamOffset
= WriteUTF16Char(c
, streamBuffer
, 0, streamBuffer
.Length
- streamOffset
);
1025 if (streamBuffer
.Length
- streamOffset
< 4)
1027 this.stream
.Write(streamBuffer
, 0, streamOffset
);
1032 int read
= ReadUTF8Char(out ch
, buffer
, offset
, count
);
1036 int written
= WriteUTF16Char(ch
, streamBuffer
, streamOffset
, streamBuffer
.Length
- streamOffset
);
1040 streamOffset
+= written
;
1045 if (streamOffset
> 0)
1047 this.stream
.Write(streamBuffer
, 0, streamOffset
);
1051 // Save trailing bytes
1054 Buffer
.BlockCopy(buffer
, offset
, trailBytes
, 0, count
);
1059 // Delegate properties
1060 public override bool CanRead { get { return this.stream.CanRead; }
}
1061 public override bool CanSeek { get { return this.stream.CanSeek; }
}
1062 public override bool CanTimeout { get { return this.stream.CanTimeout; }
}
1063 public override bool CanWrite { get { return this.stream.CanWrite; }
}
1064 public override long Length { get { return this.stream.Length; }
}
1065 public override long Position
1067 get { return this.stream.Position; }
1068 set { this.stream.Position = value; }
1070 public override int ReadTimeout
1072 get { return this.stream.ReadTimeout; }
1073 set { this.stream.ReadTimeout = value; }
1075 public override int WriteTimeout
1077 get { return this.stream.WriteTimeout; }
1078 set { this.stream.WriteTimeout = value; }
1082 public override long Seek(long offset
, SeekOrigin origin
)
1084 return this.stream
.Seek(offset
, origin
);
1087 public override void SetLength(long value)
1089 this.stream
.SetLength(value);