1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Runtime.Serialization;
6 using System.Diagnostics;
7 using System.Globalization;
8 using System.Text;
9 
10 
11 namespace System.Xml
12 {
13     internal enum ValueHandleConstStringType
14     {
15         String = 0,
16         Number = 1,
17         Array = 2,
18         Object = 3,
19         Boolean = 4,
20         Null = 5,
21     }
22 
23     internal static class ValueHandleLength
24     {
25         public const int Int8 = 1;
26         public const int Int16 = 2;
27         public const int Int32 = 4;
28         public const int Int64 = 8;
29         public const int UInt64 = 8;
30         public const int Single = 4;
31         public const int Double = 8;
32         public const int Decimal = 16;
33         public const int DateTime = 8;
34         public const int TimeSpan = 8;
35         public const int Guid = 16;
36         public const int UniqueId = 16;
37     }
38 
39     internal enum ValueHandleType
40     {
41         Empty,
42         True,
43         False,
44         Zero,
45         One,
46         Int8,
47         Int16,
48         Int32,
49         Int64,
50         UInt64,
51         Single,
52         Double,
53         Decimal,
54         DateTime,
55         TimeSpan,
56         Guid,
57         UniqueId,
58         UTF8,
59         EscapedUTF8,
60         Base64,
61         Dictionary,
62         List,
63         Char,
64         Unicode,
65         QName,
66         ConstString
67     }
68 
69     internal class ValueHandle
70     {
71         private XmlBufferReader _bufferReader;
72         private ValueHandleType _type;
73         private int _offset;
74         private int _length;
75         private static Base64Encoding s_base64Encoding;
76         private static string[] s_constStrings = {
77                                         "string",
78                                         "number",
79                                         "array",
80                                         "object",
81                                         "boolean",
82                                         "null",
83                                        };
84 
ValueHandle(XmlBufferReader bufferReader)85         public ValueHandle(XmlBufferReader bufferReader)
86         {
87             _bufferReader = bufferReader;
88             _type = ValueHandleType.Empty;
89         }
90 
91         private static Base64Encoding Base64Encoding
92         {
93             get
94             {
95                 if (s_base64Encoding == null)
96                     s_base64Encoding = new Base64Encoding();
97                 return s_base64Encoding;
98             }
99         }
SetConstantValue(ValueHandleConstStringType constStringType)100         public void SetConstantValue(ValueHandleConstStringType constStringType)
101         {
102             _type = ValueHandleType.ConstString;
103             _offset = (int)constStringType;
104         }
105 
SetValue(ValueHandleType type)106         public void SetValue(ValueHandleType type)
107         {
108             _type = type;
109         }
110 
SetDictionaryValue(int key)111         public void SetDictionaryValue(int key)
112         {
113             SetValue(ValueHandleType.Dictionary, key, 0);
114         }
115 
SetCharValue(int ch)116         public void SetCharValue(int ch)
117         {
118             SetValue(ValueHandleType.Char, ch, 0);
119         }
120 
SetQNameValue(int prefix, int key)121         public void SetQNameValue(int prefix, int key)
122         {
123             SetValue(ValueHandleType.QName, key, prefix);
124         }
SetValue(ValueHandleType type, int offset, int length)125         public void SetValue(ValueHandleType type, int offset, int length)
126         {
127             _type = type;
128             _offset = offset;
129             _length = length;
130         }
131 
IsWhitespace()132         public bool IsWhitespace()
133         {
134             switch (_type)
135             {
136                 case ValueHandleType.UTF8:
137                     return _bufferReader.IsWhitespaceUTF8(_offset, _length);
138 
139                 case ValueHandleType.Dictionary:
140                     return _bufferReader.IsWhitespaceKey(_offset);
141 
142                 case ValueHandleType.Char:
143                     int ch = GetChar();
144                     if (ch > char.MaxValue)
145                         return false;
146                     return XmlConverter.IsWhitespace((char)ch);
147 
148                 case ValueHandleType.EscapedUTF8:
149                     return _bufferReader.IsWhitespaceUTF8(_offset, _length);
150 
151                 case ValueHandleType.Unicode:
152                     return _bufferReader.IsWhitespaceUnicode(_offset, _length);
153 
154                 case ValueHandleType.True:
155                 case ValueHandleType.False:
156                 case ValueHandleType.Zero:
157                 case ValueHandleType.One:
158                     return false;
159 
160                 case ValueHandleType.ConstString:
161                     return s_constStrings[_offset].Length == 0;
162 
163                 default:
164                     return _length == 0;
165             }
166         }
167 
ToType()168         public Type ToType()
169         {
170             switch (_type)
171             {
172                 case ValueHandleType.False:
173                 case ValueHandleType.True:
174                     return typeof(bool);
175                 case ValueHandleType.Zero:
176                 case ValueHandleType.One:
177                 case ValueHandleType.Int8:
178                 case ValueHandleType.Int16:
179                 case ValueHandleType.Int32:
180                     return typeof(int);
181                 case ValueHandleType.Int64:
182                     return typeof(long);
183                 case ValueHandleType.UInt64:
184                     return typeof(ulong);
185                 case ValueHandleType.Single:
186                     return typeof(float);
187                 case ValueHandleType.Double:
188                     return typeof(double);
189                 case ValueHandleType.Decimal:
190                     return typeof(decimal);
191                 case ValueHandleType.DateTime:
192                     return typeof(DateTime);
193                 case ValueHandleType.Empty:
194                 case ValueHandleType.UTF8:
195                 case ValueHandleType.Unicode:
196                 case ValueHandleType.EscapedUTF8:
197                 case ValueHandleType.Dictionary:
198                 case ValueHandleType.Char:
199                 case ValueHandleType.QName:
200                 case ValueHandleType.ConstString:
201                     return typeof(string);
202                 case ValueHandleType.Base64:
203                     return typeof(byte[]);
204                 case ValueHandleType.List:
205                     return typeof(object[]);
206                 case ValueHandleType.UniqueId:
207                     return typeof(UniqueId);
208                 case ValueHandleType.Guid:
209                     return typeof(Guid);
210                 case ValueHandleType.TimeSpan:
211                     return typeof(TimeSpan);
212                 default:
213                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
214             }
215         }
216 
ToBoolean()217         public Boolean ToBoolean()
218         {
219             ValueHandleType type = _type;
220             if (type == ValueHandleType.False)
221                 return false;
222             if (type == ValueHandleType.True)
223                 return true;
224             if (type == ValueHandleType.UTF8)
225                 return XmlConverter.ToBoolean(_bufferReader.Buffer, _offset, _length);
226             if (type == ValueHandleType.Int8)
227             {
228                 int value = GetInt8();
229                 if (value == 0)
230                     return false;
231                 if (value == 1)
232                     return true;
233             }
234             return XmlConverter.ToBoolean(GetString());
235         }
236 
ToInt()237         public int ToInt()
238         {
239             ValueHandleType type = _type;
240             if (type == ValueHandleType.Zero)
241                 return 0;
242             if (type == ValueHandleType.One)
243                 return 1;
244             if (type == ValueHandleType.Int8)
245                 return GetInt8();
246             if (type == ValueHandleType.Int16)
247                 return GetInt16();
248             if (type == ValueHandleType.Int32)
249                 return GetInt32();
250             if (type == ValueHandleType.Int64)
251             {
252                 long value = GetInt64();
253                 if (value >= int.MinValue && value <= int.MaxValue)
254                 {
255                     return (int)value;
256                 }
257             }
258             if (type == ValueHandleType.UInt64)
259             {
260                 ulong value = GetUInt64();
261                 if (value <= int.MaxValue)
262                 {
263                     return (int)value;
264                 }
265             }
266             if (type == ValueHandleType.UTF8)
267                 return XmlConverter.ToInt32(_bufferReader.Buffer, _offset, _length);
268             return XmlConverter.ToInt32(GetString());
269         }
270 
ToLong()271         public long ToLong()
272         {
273             ValueHandleType type = _type;
274             if (type == ValueHandleType.Zero)
275                 return 0;
276             if (type == ValueHandleType.One)
277                 return 1;
278             if (type == ValueHandleType.Int8)
279                 return GetInt8();
280             if (type == ValueHandleType.Int16)
281                 return GetInt16();
282             if (type == ValueHandleType.Int32)
283                 return GetInt32();
284             if (type == ValueHandleType.Int64)
285                 return GetInt64();
286             if (type == ValueHandleType.UInt64)
287             {
288                 ulong value = GetUInt64();
289                 if (value <= long.MaxValue)
290                 {
291                     return (long)value;
292                 }
293             }
294             if (type == ValueHandleType.UTF8)
295             {
296                 return XmlConverter.ToInt64(_bufferReader.Buffer, _offset, _length);
297             }
298             return XmlConverter.ToInt64(GetString());
299         }
300 
ToULong()301         public ulong ToULong()
302         {
303             ValueHandleType type = _type;
304             if (type == ValueHandleType.Zero)
305                 return 0;
306             if (type == ValueHandleType.One)
307                 return 1;
308             if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
309             {
310                 long value = ToLong();
311                 if (value >= 0)
312                     return (ulong)value;
313             }
314             if (type == ValueHandleType.UInt64)
315                 return GetUInt64();
316             if (type == ValueHandleType.UTF8)
317                 return XmlConverter.ToUInt64(_bufferReader.Buffer, _offset, _length);
318             return XmlConverter.ToUInt64(GetString());
319         }
320 
ToSingle()321         public Single ToSingle()
322         {
323             ValueHandleType type = _type;
324             if (type == ValueHandleType.Single)
325                 return GetSingle();
326             if (type == ValueHandleType.Double)
327             {
328                 double value = GetDouble();
329 
330                 if ((value >= Single.MinValue && value <= Single.MaxValue) || !double.IsFinite(value))
331                 {
332                     return (Single)value;
333                 }
334             }
335             if (type == ValueHandleType.Zero)
336                 return 0;
337             if (type == ValueHandleType.One)
338                 return 1;
339             if (type == ValueHandleType.Int8)
340                 return GetInt8();
341             if (type == ValueHandleType.Int16)
342                 return GetInt16();
343             if (type == ValueHandleType.UTF8)
344                 return XmlConverter.ToSingle(_bufferReader.Buffer, _offset, _length);
345             return XmlConverter.ToSingle(GetString());
346         }
347 
ToDouble()348         public Double ToDouble()
349         {
350             ValueHandleType type = _type;
351             if (type == ValueHandleType.Double)
352                 return GetDouble();
353             if (type == ValueHandleType.Single)
354                 return GetSingle();
355             if (type == ValueHandleType.Zero)
356                 return 0;
357             if (type == ValueHandleType.One)
358                 return 1;
359             if (type == ValueHandleType.Int8)
360                 return GetInt8();
361             if (type == ValueHandleType.Int16)
362                 return GetInt16();
363             if (type == ValueHandleType.Int32)
364                 return GetInt32();
365             if (type == ValueHandleType.UTF8)
366                 return XmlConverter.ToDouble(_bufferReader.Buffer, _offset, _length);
367             return XmlConverter.ToDouble(GetString());
368         }
369 
ToDecimal()370         public Decimal ToDecimal()
371         {
372             ValueHandleType type = _type;
373             if (type == ValueHandleType.Decimal)
374                 return GetDecimal();
375             if (type == ValueHandleType.Zero)
376                 return 0;
377             if (type == ValueHandleType.One)
378                 return 1;
379             if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
380                 return ToLong();
381             if (type == ValueHandleType.UInt64)
382                 return GetUInt64();
383             if (type == ValueHandleType.UTF8)
384                 return XmlConverter.ToDecimal(_bufferReader.Buffer, _offset, _length);
385             return XmlConverter.ToDecimal(GetString());
386         }
387 
ToDateTime()388         public DateTime ToDateTime()
389         {
390             if (_type == ValueHandleType.DateTime)
391             {
392                 return XmlConverter.ToDateTime(GetInt64());
393             }
394             if (_type == ValueHandleType.UTF8)
395             {
396                 return XmlConverter.ToDateTime(_bufferReader.Buffer, _offset, _length);
397             }
398             return XmlConverter.ToDateTime(GetString());
399         }
400 
ToUniqueId()401         public UniqueId ToUniqueId()
402         {
403             if (_type == ValueHandleType.UniqueId)
404                 return GetUniqueId();
405             if (_type == ValueHandleType.UTF8)
406                 return XmlConverter.ToUniqueId(_bufferReader.Buffer, _offset, _length);
407             return XmlConverter.ToUniqueId(GetString());
408         }
409 
ToTimeSpan()410         public TimeSpan ToTimeSpan()
411         {
412             if (_type == ValueHandleType.TimeSpan)
413                 return new TimeSpan(GetInt64());
414             if (_type == ValueHandleType.UTF8)
415                 return XmlConverter.ToTimeSpan(_bufferReader.Buffer, _offset, _length);
416             return XmlConverter.ToTimeSpan(GetString());
417         }
418 
ToGuid()419         public Guid ToGuid()
420         {
421             if (_type == ValueHandleType.Guid)
422                 return GetGuid();
423             if (_type == ValueHandleType.UTF8)
424                 return XmlConverter.ToGuid(_bufferReader.Buffer, _offset, _length);
425             return XmlConverter.ToGuid(GetString());
426         }
ToString()427         public override string ToString()
428         {
429             return GetString();
430         }
431 
ToByteArray()432         public byte[] ToByteArray()
433         {
434             if (_type == ValueHandleType.Base64)
435             {
436                 byte[] buffer = new byte[_length];
437                 GetBase64(buffer, 0, _length);
438                 return buffer;
439             }
440             if (_type == ValueHandleType.UTF8 && (_length % 4) == 0)
441             {
442                 try
443                 {
444                     int expectedLength = _length / 4 * 3;
445                     if (_length > 0)
446                     {
447                         if (_bufferReader.Buffer[_offset + _length - 1] == '=')
448                         {
449                             expectedLength--;
450                             if (_bufferReader.Buffer[_offset + _length - 2] == '=')
451                                 expectedLength--;
452                         }
453                     }
454                     byte[] buffer = new byte[expectedLength];
455                     int actualLength = Base64Encoding.GetBytes(_bufferReader.Buffer, _offset, _length, buffer, 0);
456                     if (actualLength != buffer.Length)
457                     {
458                         byte[] newBuffer = new byte[actualLength];
459                         Buffer.BlockCopy(buffer, 0, newBuffer, 0, actualLength);
460                         buffer = newBuffer;
461                     }
462                     return buffer;
463                 }
464                 catch (FormatException)
465                 {
466                     // Something unhappy with the characters, fall back to the hard way
467                 }
468             }
469             try
470             {
471                 return Base64Encoding.GetBytes(XmlConverter.StripWhitespace(GetString()));
472             }
473             catch (FormatException exception)
474             {
475                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(exception.Message, exception.InnerException));
476             }
477         }
478 
GetString()479         public string GetString()
480         {
481             ValueHandleType type = _type;
482             if (type == ValueHandleType.UTF8)
483                 return GetCharsText();
484 
485             switch (type)
486             {
487                 case ValueHandleType.False:
488                     return "false";
489                 case ValueHandleType.True:
490                     return "true";
491                 case ValueHandleType.Zero:
492                     return "0";
493                 case ValueHandleType.One:
494                     return "1";
495                 case ValueHandleType.Int8:
496                 case ValueHandleType.Int16:
497                 case ValueHandleType.Int32:
498                     return XmlConverter.ToString(ToInt());
499                 case ValueHandleType.Int64:
500                     return XmlConverter.ToString(GetInt64());
501                 case ValueHandleType.UInt64:
502                     return XmlConverter.ToString(GetUInt64());
503                 case ValueHandleType.Single:
504                     return XmlConverter.ToString(GetSingle());
505                 case ValueHandleType.Double:
506                     return XmlConverter.ToString(GetDouble());
507                 case ValueHandleType.Decimal:
508                     return XmlConverter.ToString(GetDecimal());
509                 case ValueHandleType.DateTime:
510                     return XmlConverter.ToString(ToDateTime());
511                 case ValueHandleType.Empty:
512                     return string.Empty;
513                 case ValueHandleType.Unicode:
514                     return GetUnicodeCharsText();
515                 case ValueHandleType.EscapedUTF8:
516                     return GetEscapedCharsText();
517                 case ValueHandleType.Char:
518                     return GetCharText();
519                 case ValueHandleType.Dictionary:
520                     return GetDictionaryString().Value;
521                 case ValueHandleType.Base64:
522                     byte[] bytes = ToByteArray();
523                     DiagnosticUtility.DebugAssert(bytes != null, "");
524                     return Base64Encoding.GetString(bytes, 0, bytes.Length);
525                 case ValueHandleType.List:
526                     return XmlConverter.ToString(ToList());
527                 case ValueHandleType.UniqueId:
528                     return XmlConverter.ToString(ToUniqueId());
529                 case ValueHandleType.Guid:
530                     return XmlConverter.ToString(ToGuid());
531                 case ValueHandleType.TimeSpan:
532                     return XmlConverter.ToString(ToTimeSpan());
533                 case ValueHandleType.QName:
534                     return GetQNameDictionaryText();
535                 case ValueHandleType.ConstString:
536                     return s_constStrings[_offset];
537                 default:
538                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
539             }
540         }
541 
542         // ASSUMPTION (Microsoft): all chars in str will be ASCII
Equals2(string str, bool checkLower)543         public bool Equals2(string str, bool checkLower)
544         {
545             if (_type != ValueHandleType.UTF8)
546                 return GetString() == str;
547 
548             if (_length != str.Length)
549                 return false;
550 
551             byte[] buffer = _bufferReader.Buffer;
552             for (int i = 0; i < _length; ++i)
553             {
554                 DiagnosticUtility.DebugAssert(str[i] < 128, "");
555                 byte ch = buffer[i + _offset];
556                 if (ch == str[i])
557                     continue;
558 
559                 if (checkLower && char.ToLowerInvariant((char)ch) == str[i])
560                     continue;
561 
562                 return false;
563             }
564 
565             return true;
566         }
567 
Sign(XmlSigningNodeWriter writer)568         public void Sign(XmlSigningNodeWriter writer)
569         {
570             switch (_type)
571             {
572                 case ValueHandleType.Int8:
573                 case ValueHandleType.Int16:
574                 case ValueHandleType.Int32:
575                     writer.WriteInt32Text(ToInt());
576                     break;
577                 case ValueHandleType.Int64:
578                     writer.WriteInt64Text(GetInt64());
579                     break;
580                 case ValueHandleType.UInt64:
581                     writer.WriteUInt64Text(GetUInt64());
582                     break;
583                 case ValueHandleType.Single:
584                     writer.WriteFloatText(GetSingle());
585                     break;
586                 case ValueHandleType.Double:
587                     writer.WriteDoubleText(GetDouble());
588                     break;
589                 case ValueHandleType.Decimal:
590                     writer.WriteDecimalText(GetDecimal());
591                     break;
592                 case ValueHandleType.DateTime:
593                     writer.WriteDateTimeText(ToDateTime());
594                     break;
595                 case ValueHandleType.Empty:
596                     break;
597                 case ValueHandleType.UTF8:
598                     writer.WriteEscapedText(_bufferReader.Buffer, _offset, _length);
599                     break;
600                 case ValueHandleType.Base64:
601                     writer.WriteBase64Text(_bufferReader.Buffer, 0, _bufferReader.Buffer, _offset, _length);
602                     break;
603                 case ValueHandleType.UniqueId:
604                     writer.WriteUniqueIdText(ToUniqueId());
605                     break;
606                 case ValueHandleType.Guid:
607                     writer.WriteGuidText(ToGuid());
608                     break;
609                 case ValueHandleType.TimeSpan:
610                     writer.WriteTimeSpanText(ToTimeSpan());
611                     break;
612                 default:
613                     writer.WriteEscapedText(GetString());
614                     break;
615             }
616         }
617 
ToList()618         public object[] ToList()
619         {
620             return _bufferReader.GetList(_offset, _length);
621         }
622 
ToObject()623         public object ToObject()
624         {
625             switch (_type)
626             {
627                 case ValueHandleType.False:
628                 case ValueHandleType.True:
629                     return ToBoolean();
630                 case ValueHandleType.Zero:
631                 case ValueHandleType.One:
632                 case ValueHandleType.Int8:
633                 case ValueHandleType.Int16:
634                 case ValueHandleType.Int32:
635                     return ToInt();
636                 case ValueHandleType.Int64:
637                     return ToLong();
638                 case ValueHandleType.UInt64:
639                     return GetUInt64();
640                 case ValueHandleType.Single:
641                     return ToSingle();
642                 case ValueHandleType.Double:
643                     return ToDouble();
644                 case ValueHandleType.Decimal:
645                     return ToDecimal();
646                 case ValueHandleType.DateTime:
647                     return ToDateTime();
648                 case ValueHandleType.Empty:
649                 case ValueHandleType.UTF8:
650                 case ValueHandleType.Unicode:
651                 case ValueHandleType.EscapedUTF8:
652                 case ValueHandleType.Dictionary:
653                 case ValueHandleType.Char:
654                 case ValueHandleType.ConstString:
655                     return ToString();
656                 case ValueHandleType.Base64:
657                     return ToByteArray();
658                 case ValueHandleType.List:
659                     return ToList();
660                 case ValueHandleType.UniqueId:
661                     return ToUniqueId();
662                 case ValueHandleType.Guid:
663                     return ToGuid();
664                 case ValueHandleType.TimeSpan:
665                     return ToTimeSpan();
666                 default:
667                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
668             }
669         }
670 
TryReadBase64(byte[] buffer, int offset, int count, out int actual)671         public bool TryReadBase64(byte[] buffer, int offset, int count, out int actual)
672         {
673             if (_type == ValueHandleType.Base64)
674             {
675                 actual = Math.Min(_length, count);
676                 GetBase64(buffer, offset, actual);
677                 _offset += actual;
678                 _length -= actual;
679                 return true;
680             }
681             if (_type == ValueHandleType.UTF8 && count >= 3 && (_length % 4) == 0)
682             {
683                 try
684                 {
685                     int charCount = Math.Min(count / 3 * 4, _length);
686                     actual = Base64Encoding.GetBytes(_bufferReader.Buffer, _offset, charCount, buffer, offset);
687                     _offset += charCount;
688                     _length -= charCount;
689                     return true;
690                 }
691                 catch (FormatException)
692                 {
693                     // Something unhappy with the characters, fall back to the hard way
694                 }
695             }
696             actual = 0;
697             return false;
698         }
699 
TryReadChars(char[] chars, int offset, int count, out int actual)700         public bool TryReadChars(char[] chars, int offset, int count, out int actual)
701         {
702             DiagnosticUtility.DebugAssert(offset + count <= chars.Length, string.Format("offset '{0}' + count '{1}' MUST BE <= chars.Length '{2}'", offset, count, chars.Length));
703 
704             if (_type == ValueHandleType.Unicode)
705                 return TryReadUnicodeChars(chars, offset, count, out actual);
706 
707             if (_type != ValueHandleType.UTF8)
708             {
709                 actual = 0;
710                 return false;
711             }
712 
713             int charOffset = offset;
714             int charCount = count;
715             byte[] bytes = _bufferReader.Buffer;
716             int byteOffset = _offset;
717             int byteCount = _length;
718             bool insufficientSpaceInCharsArray = false;
719 
720             var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
721             while (true)
722             {
723                 while (charCount > 0 && byteCount > 0)
724                 {
725                     // fast path for codepoints U+0000 - U+007F
726                     byte b = bytes[byteOffset];
727                     if (b >= 0x80)
728                         break;
729                     chars[charOffset] = (char)b;
730                     byteOffset++;
731                     byteCount--;
732                     charOffset++;
733                     charCount--;
734                 }
735 
736                 if (charCount == 0 || byteCount == 0 || insufficientSpaceInCharsArray)
737                     break;
738 
739                 int actualByteCount;
740                 int actualCharCount;
741 
742                 try
743                 {
744                     // If we're asking for more than are possibly available, or more than are truly available then we can return the entire thing
745                     if (charCount >= encoding.GetMaxCharCount(byteCount) || charCount >= encoding.GetCharCount(bytes, byteOffset, byteCount))
746                     {
747                         actualCharCount = encoding.GetChars(bytes, byteOffset, byteCount, chars, charOffset);
748                         actualByteCount = byteCount;
749                     }
750                     else
751                     {
752                         Decoder decoder = encoding.GetDecoder();
753 
754                         // Since x bytes can never generate more than x characters this is a safe estimate as to what will fit
755                         actualByteCount = Math.Min(charCount, byteCount);
756 
757                         // We use a decoder so we don't error if we fall across a character boundary
758                         actualCharCount = decoder.GetChars(bytes, byteOffset, actualByteCount, chars, charOffset);
759 
760                         // We might have gotten zero characters though if < 4 bytes were requested because
761                         // codepoints from U+0000 - U+FFFF can be up to 3 bytes in UTF-8, and represented as ONE char
762                         // codepoints from U+10000 - U+10FFFF (last Unicode codepoint representable in UTF-8) are represented by up to 4 bytes in UTF-8
763                         //                                    and represented as TWO chars (high+low surrogate)
764                         // (e.g. 1 char requested, 1 char in the buffer represented in 3 bytes)
765                         while (actualCharCount == 0)
766                         {
767                             // Note the by the time we arrive here, if actualByteCount == 3, the next decoder.GetChars() call will read the 4th byte
768                             // if we don't bail out since the while loop will advance actualByteCount only after reading the byte.
769                             if (actualByteCount >= 3 && charCount < 2)
770                             {
771                                 // If we reach here, it means that we're:
772                                 // - trying to decode more than 3 bytes and,
773                                 // - there is only one char left of charCount where we're stuffing decoded characters.
774                                 // In this case, we need to back off since decoding > 3 bytes in UTF-8 means that we will get 2 16-bit chars
775                                 // (a high surrogate and a low surrogate) - the Decoder will attempt to provide both at once
776                                 // and an ArgumentException will be thrown complaining that there's not enough space in the output char array.
777 
778                                 // actualByteCount = 0 when the while loop is broken out of; decoder goes out of scope so its state no longer matters
779 
780                                 insufficientSpaceInCharsArray = true;
781                                 break;
782                             }
783                             else
784                             {
785                                 DiagnosticUtility.DebugAssert(byteOffset + actualByteCount < bytes.Length,
786                                     string.Format("byteOffset {0} + actualByteCount {1} MUST BE < bytes.Length {2}", byteOffset, actualByteCount, bytes.Length));
787 
788                                 // Request a few more bytes to get at least one character
789                                 actualCharCount = decoder.GetChars(bytes, byteOffset + actualByteCount, 1, chars, charOffset);
790                                 actualByteCount++;
791                             }
792                         }
793 
794                         // Now that we actually retrieved some characters, figure out how many bytes it actually was
795                         actualByteCount = encoding.GetByteCount(chars, charOffset, actualCharCount);
796                     }
797                 }
798                 catch (FormatException exception)
799                 {
800                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateEncodingException(bytes, byteOffset, byteCount, exception));
801                 }
802 
803                 // Advance
804                 byteOffset += actualByteCount;
805                 byteCount -= actualByteCount;
806 
807                 charOffset += actualCharCount;
808                 charCount -= actualCharCount;
809             }
810 
811             _offset = byteOffset;
812             _length = byteCount;
813 
814             actual = (count - charCount);
815             return true;
816         }
817 
TryReadUnicodeChars(char[] chars, int offset, int count, out int actual)818         private bool TryReadUnicodeChars(char[] chars, int offset, int count, out int actual)
819         {
820             int charCount = Math.Min(count, _length / sizeof(char));
821             for (int i = 0; i < charCount; i++)
822             {
823                 chars[offset + i] = (char)_bufferReader.GetInt16(_offset + i * sizeof(char));
824             }
825             _offset += charCount * sizeof(char);
826             _length -= charCount * sizeof(char);
827             actual = charCount;
828             return true;
829         }
830 
TryGetDictionaryString(out XmlDictionaryString value)831         public bool TryGetDictionaryString(out XmlDictionaryString value)
832         {
833             if (_type == ValueHandleType.Dictionary)
834             {
835                 value = GetDictionaryString();
836                 return true;
837             }
838             else
839             {
840                 value = null;
841                 return false;
842             }
843         }
844 
TryGetByteArrayLength(out int length)845         public bool TryGetByteArrayLength(out int length)
846         {
847             if (_type == ValueHandleType.Base64)
848             {
849                 length = _length;
850                 return true;
851             }
852             length = 0;
853             return false;
854         }
GetCharsText()855         private string GetCharsText()
856         {
857             DiagnosticUtility.DebugAssert(_type == ValueHandleType.UTF8, "");
858             if (_length == 1 && _bufferReader.GetByte(_offset) == '1')
859                 return "1";
860             return _bufferReader.GetString(_offset, _length);
861         }
862 
GetUnicodeCharsText()863         private string GetUnicodeCharsText()
864         {
865             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Unicode, "");
866             return _bufferReader.GetUnicodeString(_offset, _length);
867         }
868 
GetEscapedCharsText()869         private string GetEscapedCharsText()
870         {
871             DiagnosticUtility.DebugAssert(_type == ValueHandleType.EscapedUTF8, "");
872             return _bufferReader.GetEscapedString(_offset, _length);
873         }
GetCharText()874         private string GetCharText()
875         {
876             int ch = GetChar();
877             if (ch > char.MaxValue)
878             {
879                 SurrogateChar surrogate = new SurrogateChar(ch);
880                 char[] chars = new char[2];
881                 chars[0] = surrogate.HighChar;
882                 chars[1] = surrogate.LowChar;
883                 return new string(chars, 0, 2);
884             }
885             else
886             {
887                 return ((char)ch).ToString();
888             }
889         }
890 
GetChar()891         private int GetChar()
892         {
893             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Char, "");
894             return _offset;
895         }
896 
GetInt8()897         private int GetInt8()
898         {
899             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Int8, "");
900             return _bufferReader.GetInt8(_offset);
901         }
902 
GetInt16()903         private int GetInt16()
904         {
905             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Int16, "");
906             return _bufferReader.GetInt16(_offset);
907         }
908 
GetInt32()909         private int GetInt32()
910         {
911             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Int32, "");
912             return _bufferReader.GetInt32(_offset);
913         }
914 
GetInt64()915         private long GetInt64()
916         {
917             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Int64 || _type == ValueHandleType.TimeSpan || _type == ValueHandleType.DateTime, "");
918             return _bufferReader.GetInt64(_offset);
919         }
920 
GetUInt64()921         private ulong GetUInt64()
922         {
923             DiagnosticUtility.DebugAssert(_type == ValueHandleType.UInt64, "");
924             return _bufferReader.GetUInt64(_offset);
925         }
926 
GetSingle()927         private float GetSingle()
928         {
929             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Single, "");
930             return _bufferReader.GetSingle(_offset);
931         }
932 
GetDouble()933         private double GetDouble()
934         {
935             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Double, "");
936             return _bufferReader.GetDouble(_offset);
937         }
938 
GetDecimal()939         private decimal GetDecimal()
940         {
941             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Decimal, "");
942             return _bufferReader.GetDecimal(_offset);
943         }
944 
GetUniqueId()945         private UniqueId GetUniqueId()
946         {
947             DiagnosticUtility.DebugAssert(_type == ValueHandleType.UniqueId, "");
948             return _bufferReader.GetUniqueId(_offset);
949         }
950 
GetGuid()951         private Guid GetGuid()
952         {
953             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Guid, "");
954             return _bufferReader.GetGuid(_offset);
955         }
956 
GetBase64(byte[] buffer, int offset, int count)957         private void GetBase64(byte[] buffer, int offset, int count)
958         {
959             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Base64, "");
960             _bufferReader.GetBase64(_offset, buffer, offset, count);
961         }
962 
GetDictionaryString()963         private XmlDictionaryString GetDictionaryString()
964         {
965             DiagnosticUtility.DebugAssert(_type == ValueHandleType.Dictionary, "");
966             return _bufferReader.GetDictionaryString(_offset);
967         }
968 
GetQNameDictionaryText()969         private string GetQNameDictionaryText()
970         {
971             DiagnosticUtility.DebugAssert(_type == ValueHandleType.QName, "");
972             return string.Concat(PrefixHandle.GetString(PrefixHandle.GetAlphaPrefix(_length)), ":", _bufferReader.GetDictionaryString(_offset));
973         }
974     }
975 }
976