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
11 using System.ServiceModel;
15 using System.Security;
17 // This wrapper does not support seek.
18 // Supports: UTF-8, Unicode, BigEndianUnicode
19 // ASSUMPTION (Microsoft): This class will only be used for EITHER reading OR writing. It can be done, it would just mean more buffers.
20 class JsonEncodingStreamWrapper : Stream
22 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
23 + " data from being modified or leaked to other components in appdomain.")]
24 static readonly UnicodeEncoding SafeBEUTF16 = new UnicodeEncoding(true, false, false);
26 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
27 + " data from being modified or leaked to other components in appdomain.")]
28 static readonly UnicodeEncoding SafeUTF16 = new UnicodeEncoding(false, false, false);
30 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
31 + " data from being modified or leaked to other components in appdomain.")]
32 static readonly UTF8Encoding SafeUTF8 = new UTF8Encoding(false, false);
34 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
35 + " data from being modified or leaked to other components in appdomain.")]
36 static readonly UnicodeEncoding ValidatingBEUTF16 = new UnicodeEncoding(true, false, true);
38 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
39 + " data from being modified or leaked to other components in appdomain.")]
40 static readonly UnicodeEncoding ValidatingUTF16 = new UnicodeEncoding(false, false, true);
42 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
43 + " data from being modified or leaked to other components in appdomain.")]
44 static readonly UTF8Encoding ValidatingUTF8 = new UTF8Encoding(false, true);
45 const int BufferLength = 128;
47 byte[] byteBuffer = new byte[1];
56 SupportedEncoding encodingCode;
61 public JsonEncodingStreamWrapper(Stream stream, Encoding encoding, bool isReader)
63 this.isReading = isReader;
66 InitForReading(stream, encoding);
72 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
75 InitForWriting(stream, encoding);
79 enum SupportedEncoding
87 // This stream wrapper does not support duplex
88 public override bool CanRead
97 return this.stream.CanRead;
101 // The encoding conversion and buffering breaks seeking.
102 public override bool CanSeek
104 get { return false; }
107 // Delegate properties
108 public override bool CanTimeout
110 get { return this.stream.CanTimeout; }
113 // This stream wrapper does not support duplex
114 public override bool CanWrite
123 return this.stream.CanWrite;
127 public override long Length
129 get { return this.stream.Length; }
133 // The encoding conversion and buffering breaks seeking.
134 public override long Position
138 #pragma warning suppress 56503 // The contract for non seekable stream is to throw exception
139 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
141 set { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); }
144 public override int ReadTimeout
146 get { return this.stream.ReadTimeout; }
147 set { this.stream.ReadTimeout = value; }
150 public override int WriteTimeout
152 get { return this.stream.WriteTimeout; }
153 set { this.stream.WriteTimeout = value; }
156 public static ArraySegment<byte> ProcessBuffer(byte[] buffer, int offset, int count, Encoding encoding)
160 SupportedEncoding expectedEnc = GetSupportedEncoding(encoding);
161 SupportedEncoding dataEnc;
164 dataEnc = SupportedEncoding.UTF8;
168 dataEnc = ReadEncoding(buffer[offset], buffer[offset + 1]);
170 if ((expectedEnc != SupportedEncoding.None) && (expectedEnc != dataEnc))
172 ThrowExpectedEncodingMismatch(expectedEnc, dataEnc);
176 if (dataEnc == SupportedEncoding.UTF8)
178 return new ArraySegment<byte>(buffer, offset, count);
183 new ArraySegment<byte>(ValidatingUTF8.GetBytes(GetEncoding(dataEnc).GetChars(buffer, offset, count)));
185 catch (DecoderFallbackException e)
187 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
188 new XmlException(SR.GetString(SR.JsonInvalidBytes), e));
192 public override void Close()
199 public override void Flush()
204 public override int Read(byte[] buffer, int offset, int count)
210 if (encodingCode == SupportedEncoding.UTF8)
212 return this.stream.Read(buffer, offset, count);
215 // No more bytes than can be turned into characters
217 byteCount = this.stream.Read(bytes, byteCount, (chars.Length - 1) * 2);
219 // Check for end of stream
225 // Fix up incomplete chars
229 int charCount = this.encoding.GetChars(bytes, 0, byteCount, chars, 0);
230 byteCount = Encoding.UTF8.GetBytes(chars, 0, charCount, bytes, 0);
234 if (byteCount < count)
238 Buffer.BlockCopy(bytes, byteOffset, buffer, offset, count);
243 catch (DecoderFallbackException ex)
245 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
246 new XmlException(SR.GetString(SR.JsonInvalidBytes), ex));
250 public override int ReadByte()
252 if (byteCount == 0 && encodingCode == SupportedEncoding.UTF8)
254 return this.stream.ReadByte();
256 if (Read(byteBuffer, 0, 1) == 0)
260 return byteBuffer[0];
263 public override long Seek(long offset, SeekOrigin origin)
265 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
269 public override void SetLength(long value)
271 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
274 public override void Write(byte[] buffer, int offset, int count)
276 // Optimize UTF-8 case
277 if (encodingCode == SupportedEncoding.UTF8)
279 this.stream.Write(buffer, offset, count);
285 int size = chars.Length < count ? chars.Length : count;
286 int charCount = dec.GetChars(buffer, offset, size, chars, 0, false);
287 byteCount = enc.GetBytes(chars, 0, charCount, bytes, 0, false);
288 this.stream.Write(bytes, 0, byteCount);
294 public override void WriteByte(byte b)
296 if (encodingCode == SupportedEncoding.UTF8)
298 this.stream.WriteByte(b);
302 Write(byteBuffer, 0, 1);
305 static Encoding GetEncoding(SupportedEncoding e)
309 case SupportedEncoding.UTF8:
310 return ValidatingUTF8;
312 case SupportedEncoding.UTF16LE:
313 return ValidatingUTF16;
315 case SupportedEncoding.UTF16BE:
316 return ValidatingBEUTF16;
319 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
320 new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
324 static string GetEncodingName(SupportedEncoding enc)
328 case SupportedEncoding.UTF8:
331 case SupportedEncoding.UTF16LE:
334 case SupportedEncoding.UTF16BE:
338 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
339 new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
343 static SupportedEncoding GetSupportedEncoding(Encoding encoding)
345 if (encoding == null)
347 return SupportedEncoding.None;
349 if (encoding.WebName == ValidatingUTF8.WebName)
351 return SupportedEncoding.UTF8;
353 else if (encoding.WebName == ValidatingUTF16.WebName)
355 return SupportedEncoding.UTF16LE;
357 else if (encoding.WebName == ValidatingBEUTF16.WebName)
359 return SupportedEncoding.UTF16BE;
363 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
364 new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
368 static SupportedEncoding ReadEncoding(byte b1, byte b2)
370 if (b1 == 0x00 && b2 != 0x00)
372 return SupportedEncoding.UTF16BE;
374 else if (b1 != 0x00 && b2 == 0x00)
376 // 857 It's possible to misdetect UTF-32LE as UTF-16LE, but that's OK.
377 return SupportedEncoding.UTF16LE;
379 else if (b1 == 0x00 && b2 == 0x00)
381 // UTF-32BE not supported
382 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonInvalidBytes)));
386 return SupportedEncoding.UTF8;
390 static void ThrowExpectedEncodingMismatch(SupportedEncoding expEnc, SupportedEncoding actualEnc)
392 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonExpectedEncoding, GetEncodingName(expEnc), GetEncodingName(actualEnc))));
395 void CleanupCharBreak()
397 int max = byteOffset + byteCount;
399 // Read on 2 byte boundaries
400 if ((byteCount % 2) != 0)
402 int b = this.stream.ReadByte();
405 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
406 new XmlException(SR.GetString(SR.JsonUnexpectedEndOfFile)));
409 bytes[max++] = (byte)b;
413 // Don't cut off a surrogate character
415 if (encodingCode == SupportedEncoding.UTF16LE)
417 w = bytes[max - 2] + (bytes[max - 1] << 8);
421 w = bytes[max - 1] + (bytes[max - 2] << 8);
423 if ((w & 0xDC00) != 0xDC00 && w >= 0xD800 && w <= 0xDBFF) // First 16-bit number of surrogate pair
425 int b1 = this.stream.ReadByte();
426 int b2 = this.stream.ReadByte();
429 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
430 new XmlException(SR.GetString(SR.JsonUnexpectedEndOfFile)));
432 bytes[max++] = (byte)b1;
433 bytes[max++] = (byte)b2;
443 chars = new char[BufferLength];
447 void EnsureByteBuffer()
454 bytes = new byte[BufferLength * 4];
459 void FillBuffer(int count)
464 int read = stream.Read(bytes, byteOffset + byteCount, count);
475 void InitForReading(Stream inputStream, Encoding expectedEncoding)
479 this.stream = new BufferedStream(inputStream);
481 SupportedEncoding expectedEnc = GetSupportedEncoding(expectedEncoding);
482 SupportedEncoding dataEnc = ReadEncoding();
483 if ((expectedEnc != SupportedEncoding.None) && (expectedEnc != dataEnc))
485 ThrowExpectedEncodingMismatch(expectedEnc, dataEnc);
488 // Fastpath: UTF-8 (do nothing)
489 if (dataEnc != SupportedEncoding.UTF8)
493 FillBuffer((BufferLength - 1) * 2);
494 this.encodingCode = dataEnc;
495 this.encoding = GetEncoding(dataEnc);
497 int count = this.encoding.GetChars(bytes, byteOffset, byteCount, chars, 0);
499 byteCount = ValidatingUTF8.GetBytes(chars, 0, count, bytes, 0);
502 catch (DecoderFallbackException ex)
504 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
505 new XmlException(SR.GetString(SR.JsonInvalidBytes), ex));
509 void InitForWriting(Stream outputStream, Encoding writeEncoding)
511 this.encoding = writeEncoding;
512 this.stream = new BufferedStream(outputStream);
514 // Set the encoding code
515 this.encodingCode = GetSupportedEncoding(writeEncoding);
517 if (this.encodingCode != SupportedEncoding.UTF8)
520 dec = ValidatingUTF8.GetDecoder();
521 enc = this.encoding.GetEncoder();
525 SupportedEncoding ReadEncoding()
527 int b1 = this.stream.ReadByte();
528 int b2 = this.stream.ReadByte();
536 e = SupportedEncoding.UTF8;
541 e = SupportedEncoding.UTF8;
547 e = ReadEncoding((byte)b1, (byte)b2);