1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 #pragma warning disable 1634 // Stops compiler from warning about unknown warnings (for Presharp)
7 namespace System.Runtime.Serialization.Json
10 using System.ServiceModel;
13 using System.Security;
15 // This wrapper does not support seek.
16 // Supports: UTF-8, Unicode, BigEndianUnicode
17 // ASSUMPTION ([....]): This class will only be used for EITHER reading OR writing. It can be done, it would just mean more buffers.
18 class JsonEncodingStreamWrapper : Stream
20 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
21 + " data from being modified or leaked to other components in appdomain.")]
22 static readonly UnicodeEncoding SafeBEUTF16 = new UnicodeEncoding(true, false, false);
24 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
25 + " data from being modified or leaked to other components in appdomain.")]
26 static readonly UnicodeEncoding SafeUTF16 = new UnicodeEncoding(false, false, false);
28 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
29 + " data from being modified or leaked to other components in appdomain.")]
30 static readonly UTF8Encoding SafeUTF8 = new UTF8Encoding(false, false);
32 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
33 + " data from being modified or leaked to other components in appdomain.")]
34 static readonly UnicodeEncoding ValidatingBEUTF16 = new UnicodeEncoding(true, false, true);
36 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
37 + " data from being modified or leaked to other components in appdomain.")]
38 static readonly UnicodeEncoding ValidatingUTF16 = new UnicodeEncoding(false, false, true);
40 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
41 + " data from being modified or leaked to other components in appdomain.")]
42 static readonly UTF8Encoding ValidatingUTF8 = new UTF8Encoding(false, true);
43 const int BufferLength = 128;
45 byte[] byteBuffer = new byte[1];
54 SupportedEncoding encodingCode;
59 public JsonEncodingStreamWrapper(Stream stream, Encoding encoding, bool isReader)
61 this.isReading = isReader;
64 InitForReading(stream, encoding);
70 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
73 InitForWriting(stream, encoding);
77 enum SupportedEncoding
85 // This stream wrapper does not support duplex
86 public override bool CanRead
95 return this.stream.CanRead;
99 // The encoding conversion and buffering breaks seeking.
100 public override bool CanSeek
102 get { return false; }
105 // Delegate properties
106 public override bool CanTimeout
108 get { return this.stream.CanTimeout; }
111 // This stream wrapper does not support duplex
112 public override bool CanWrite
121 return this.stream.CanWrite;
125 public override long Length
127 get { return this.stream.Length; }
131 // The encoding conversion and buffering breaks seeking.
132 public override long Position
136 #pragma warning suppress 56503 // The contract for non seekable stream is to throw exception
137 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
139 set { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); }
142 public override int ReadTimeout
144 get { return this.stream.ReadTimeout; }
145 set { this.stream.ReadTimeout = value; }
148 public override int WriteTimeout
150 get { return this.stream.WriteTimeout; }
151 set { this.stream.WriteTimeout = value; }
154 public static ArraySegment<byte> ProcessBuffer(byte[] buffer, int offset, int count, Encoding encoding)
158 SupportedEncoding expectedEnc = GetSupportedEncoding(encoding);
159 SupportedEncoding dataEnc;
162 dataEnc = SupportedEncoding.UTF8;
166 dataEnc = ReadEncoding(buffer[offset], buffer[offset + 1]);
168 if ((expectedEnc != SupportedEncoding.None) && (expectedEnc != dataEnc))
170 ThrowExpectedEncodingMismatch(expectedEnc, dataEnc);
174 if (dataEnc == SupportedEncoding.UTF8)
176 return new ArraySegment<byte>(buffer, offset, count);
181 new ArraySegment<byte>(ValidatingUTF8.GetBytes(GetEncoding(dataEnc).GetChars(buffer, offset, count)));
183 catch (DecoderFallbackException e)
185 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
186 new XmlException(SR.GetString(SR.JsonInvalidBytes), e));
190 public override void Close()
197 public override void Flush()
202 public override int Read(byte[] buffer, int offset, int count)
208 if (encodingCode == SupportedEncoding.UTF8)
210 return this.stream.Read(buffer, offset, count);
213 // No more bytes than can be turned into characters
215 byteCount = this.stream.Read(bytes, byteCount, (chars.Length - 1) * 2);
217 // Check for end of stream
223 // Fix up incomplete chars
227 int charCount = this.encoding.GetChars(bytes, 0, byteCount, chars, 0);
228 byteCount = Encoding.UTF8.GetBytes(chars, 0, charCount, bytes, 0);
232 if (byteCount < count)
236 Buffer.BlockCopy(bytes, byteOffset, buffer, offset, count);
241 catch (DecoderFallbackException ex)
243 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
244 new XmlException(SR.GetString(SR.JsonInvalidBytes), ex));
248 public override int ReadByte()
250 if (byteCount == 0 && encodingCode == SupportedEncoding.UTF8)
252 return this.stream.ReadByte();
254 if (Read(byteBuffer, 0, 1) == 0)
258 return byteBuffer[0];
261 public override long Seek(long offset, SeekOrigin origin)
263 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
267 public override void SetLength(long value)
269 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
272 public override void Write(byte[] buffer, int offset, int count)
274 // Optimize UTF-8 case
275 if (encodingCode == SupportedEncoding.UTF8)
277 this.stream.Write(buffer, offset, count);
283 int size = chars.Length < count ? chars.Length : count;
284 int charCount = dec.GetChars(buffer, offset, size, chars, 0, false);
285 byteCount = enc.GetBytes(chars, 0, charCount, bytes, 0, false);
286 this.stream.Write(bytes, 0, byteCount);
292 public override void WriteByte(byte b)
294 if (encodingCode == SupportedEncoding.UTF8)
296 this.stream.WriteByte(b);
300 Write(byteBuffer, 0, 1);
303 static Encoding GetEncoding(SupportedEncoding e)
307 case SupportedEncoding.UTF8:
308 return ValidatingUTF8;
310 case SupportedEncoding.UTF16LE:
311 return ValidatingUTF16;
313 case SupportedEncoding.UTF16BE:
314 return ValidatingBEUTF16;
317 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
318 new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
322 static string GetEncodingName(SupportedEncoding enc)
326 case SupportedEncoding.UTF8:
329 case SupportedEncoding.UTF16LE:
332 case SupportedEncoding.UTF16BE:
336 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
337 new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
341 static SupportedEncoding GetSupportedEncoding(Encoding encoding)
343 if (encoding == null)
345 return SupportedEncoding.None;
347 if (encoding.WebName == ValidatingUTF8.WebName)
349 return SupportedEncoding.UTF8;
351 else if (encoding.WebName == ValidatingUTF16.WebName)
353 return SupportedEncoding.UTF16LE;
355 else if (encoding.WebName == ValidatingBEUTF16.WebName)
357 return SupportedEncoding.UTF16BE;
361 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
362 new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
366 static SupportedEncoding ReadEncoding(byte b1, byte b2)
368 if (b1 == 0x00 && b2 != 0x00)
370 return SupportedEncoding.UTF16BE;
372 else if (b1 != 0x00 && b2 == 0x00)
374 // 857 It's possible to misdetect UTF-32LE as UTF-16LE, but that's OK.
375 return SupportedEncoding.UTF16LE;
377 else if (b1 == 0x00 && b2 == 0x00)
379 // UTF-32BE not supported
380 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonInvalidBytes)));
384 return SupportedEncoding.UTF8;
388 static void ThrowExpectedEncodingMismatch(SupportedEncoding expEnc, SupportedEncoding actualEnc)
390 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonExpectedEncoding, GetEncodingName(expEnc), GetEncodingName(actualEnc))));
393 void CleanupCharBreak()
395 int max = byteOffset + byteCount;
397 // Read on 2 byte boundaries
398 if ((byteCount % 2) != 0)
400 int b = this.stream.ReadByte();
403 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
404 new XmlException(SR.GetString(SR.JsonUnexpectedEndOfFile)));
407 bytes[max++] = (byte)b;
411 // Don't cut off a surrogate character
413 if (encodingCode == SupportedEncoding.UTF16LE)
415 w = bytes[max - 2] + (bytes[max - 1] << 8);
419 w = bytes[max - 1] + (bytes[max - 2] << 8);
421 if ((w & 0xDC00) != 0xDC00 && w >= 0xD800 && w <= 0xDBFF) // First 16-bit number of surrogate pair
423 int b1 = this.stream.ReadByte();
424 int b2 = this.stream.ReadByte();
427 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
428 new XmlException(SR.GetString(SR.JsonUnexpectedEndOfFile)));
430 bytes[max++] = (byte)b1;
431 bytes[max++] = (byte)b2;
441 chars = new char[BufferLength];
445 void EnsureByteBuffer()
452 bytes = new byte[BufferLength * 4];
457 void FillBuffer(int count)
462 int read = stream.Read(bytes, byteOffset + byteCount, count);
473 void InitForReading(Stream inputStream, Encoding expectedEncoding)
477 this.stream = new BufferedStream(inputStream);
479 SupportedEncoding expectedEnc = GetSupportedEncoding(expectedEncoding);
480 SupportedEncoding dataEnc = ReadEncoding();
481 if ((expectedEnc != SupportedEncoding.None) && (expectedEnc != dataEnc))
483 ThrowExpectedEncodingMismatch(expectedEnc, dataEnc);
486 // Fastpath: UTF-8 (do nothing)
487 if (dataEnc != SupportedEncoding.UTF8)
491 FillBuffer((BufferLength - 1) * 2);
492 this.encodingCode = dataEnc;
493 this.encoding = GetEncoding(dataEnc);
495 int count = this.encoding.GetChars(bytes, byteOffset, byteCount, chars, 0);
497 byteCount = ValidatingUTF8.GetBytes(chars, 0, count, bytes, 0);
500 catch (DecoderFallbackException ex)
502 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
503 new XmlException(SR.GetString(SR.JsonInvalidBytes), ex));
507 void InitForWriting(Stream outputStream, Encoding writeEncoding)
509 this.encoding = writeEncoding;
510 this.stream = new BufferedStream(outputStream);
512 // Set the encoding code
513 this.encodingCode = GetSupportedEncoding(writeEncoding);
515 if (this.encodingCode != SupportedEncoding.UTF8)
518 dec = ValidatingUTF8.GetDecoder();
519 enc = this.encoding.GetEncoder();
523 SupportedEncoding ReadEncoding()
525 int b1 = this.stream.ReadByte();
526 int b2 = this.stream.ReadByte();
534 e = SupportedEncoding.UTF8;
539 e = SupportedEncoding.UTF8;
545 e = ReadEncoding((byte)b1, (byte)b2);