Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Xml / ValueHandle.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Xml
6 {
7     using System.Runtime;
8     using System.Runtime.Serialization;
9     using System.Text;
10
11     enum ValueHandleConstStringType
12     {
13         String = 0,
14         Number = 1,
15         Array = 2,
16         Object = 3,
17         Boolean = 4,
18         Null = 5,
19     }
20
21     static class ValueHandleLength
22     {
23         public const int Int8 = 1;
24         public const int Int16 = 2;
25         public const int Int32 = 4;
26         public const int Int64 = 8;
27         public const int UInt64 = 8;
28         public const int Single = 4;
29         public const int Double = 8;
30         public const int Decimal = 16;
31         public const int DateTime = 8;
32         public const int TimeSpan = 8;
33         public const int Guid = 16;
34         public const int UniqueId = 16;
35     }
36
37     enum ValueHandleType
38     {
39         Empty,
40         True,
41         False,
42         Zero,
43         One,
44         Int8,
45         Int16,
46         Int32,
47         Int64,
48         UInt64,
49         Single,
50         Double,
51         Decimal,
52         DateTime,
53         TimeSpan,
54         Guid,
55         UniqueId,
56         UTF8,
57         EscapedUTF8,
58         Base64,
59         Dictionary,
60         List,
61         Char,
62         Unicode,
63         QName,
64         ConstString
65     }
66
67     class ValueHandle
68     {
69         XmlBufferReader bufferReader;
70         ValueHandleType type;
71         int offset;
72         int length;
73         static Base64Encoding base64Encoding;
74
75
76         static string[] constStrings = {
77                                         "string",
78                                         "number",
79                                         "array",
80                                         "object",
81                                         "boolean",
82                                         "null",
83                                        };
84
85         public ValueHandle(XmlBufferReader bufferReader)
86         {
87             this.bufferReader = bufferReader;
88             this.type = ValueHandleType.Empty;
89         }
90
91         static Base64Encoding Base64Encoding
92         {
93             get
94             {
95                 if (base64Encoding == null)
96                     base64Encoding = new Base64Encoding();
97                 return base64Encoding;
98             }
99         }
100
101         public void SetConstantValue(ValueHandleConstStringType constStringType)
102         {
103             type = ValueHandleType.ConstString;
104             offset = (int)constStringType;
105         }
106
107         public void SetValue(ValueHandleType type)
108         {
109             this.type = type;
110         }
111
112         public void SetDictionaryValue(int key)
113         {
114             SetValue(ValueHandleType.Dictionary, key, 0);
115         }
116
117         public void SetCharValue(int ch)
118         {
119             SetValue(ValueHandleType.Char, ch, 0);
120         }
121
122         public void SetQNameValue(int prefix, int key)
123         {
124             SetValue(ValueHandleType.QName, key, prefix);
125         }
126
127         public void SetValue(ValueHandleType type, int offset, int length)
128         {
129             this.type = type;
130             this.offset = offset;
131             this.length = length;
132         }
133
134         public bool IsWhitespace()
135         {
136             switch (this.type)
137             {
138                 case ValueHandleType.UTF8:
139                     return bufferReader.IsWhitespaceUTF8(this.offset, this.length);
140
141                 case ValueHandleType.Dictionary:
142                     return bufferReader.IsWhitespaceKey(this.offset);
143
144                 case ValueHandleType.Char:
145                     int ch = GetChar();
146                     if (ch > char.MaxValue)
147                         return false;
148                     return XmlConverter.IsWhitespace((char)ch);
149
150                 case ValueHandleType.EscapedUTF8:
151                     return bufferReader.IsWhitespaceUTF8(this.offset, this.length);
152
153                 case ValueHandleType.Unicode:
154                     return bufferReader.IsWhitespaceUnicode(this.offset, this.length);
155
156                 case ValueHandleType.True:
157                 case ValueHandleType.False:
158                 case ValueHandleType.Zero:
159                 case ValueHandleType.One:
160                     return false;
161
162                 case ValueHandleType.ConstString:
163                     return constStrings[offset].Length == 0;
164
165                 default:
166                     return this.length == 0;
167             }
168         }
169
170         public Type ToType()
171         {
172             switch (type)
173             {
174                 case ValueHandleType.False:
175                 case ValueHandleType.True:
176                     return typeof(bool);
177                 case ValueHandleType.Zero:
178                 case ValueHandleType.One:
179                 case ValueHandleType.Int8:
180                 case ValueHandleType.Int16:
181                 case ValueHandleType.Int32:
182                     return typeof(int);
183                 case ValueHandleType.Int64:
184                     return typeof(long);
185                 case ValueHandleType.UInt64:
186                     return typeof(ulong);
187                 case ValueHandleType.Single:
188                     return typeof(float);
189                 case ValueHandleType.Double:
190                     return typeof(double);
191                 case ValueHandleType.Decimal:
192                     return typeof(decimal);
193                 case ValueHandleType.DateTime:
194                     return typeof(DateTime);
195                 case ValueHandleType.Empty:
196                 case ValueHandleType.UTF8:
197                 case ValueHandleType.Unicode:
198                 case ValueHandleType.EscapedUTF8:
199                 case ValueHandleType.Dictionary:
200                 case ValueHandleType.Char:
201                 case ValueHandleType.QName:
202                 case ValueHandleType.ConstString:
203                     return typeof(string);
204                 case ValueHandleType.Base64:
205                     return typeof(byte[]);
206                 case ValueHandleType.List:
207                     return typeof(object[]);
208                 case ValueHandleType.UniqueId:
209                     return typeof(UniqueId);
210                 case ValueHandleType.Guid:
211                     return typeof(Guid);
212                 case ValueHandleType.TimeSpan:
213                     return typeof(TimeSpan);
214                 default:
215                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
216             }
217         }
218
219         public Boolean ToBoolean()
220         {
221             ValueHandleType type = this.type;
222             if (type == ValueHandleType.False)
223                 return false;
224             if (type == ValueHandleType.True)
225                 return true;
226             if (type == ValueHandleType.UTF8)
227                 return XmlConverter.ToBoolean(bufferReader.Buffer, offset, length);
228             if (type == ValueHandleType.Int8)
229             {
230                 int value = GetInt8();
231                 if (value == 0)
232                     return false;
233                 if (value == 1)
234                     return true;
235             }
236             return XmlConverter.ToBoolean(GetString());
237         }
238
239         public int ToInt()
240         {
241             ValueHandleType type = this.type;
242             if (type == ValueHandleType.Zero)
243                 return 0;
244             if (type == ValueHandleType.One)
245                 return 1;
246             if (type == ValueHandleType.Int8)
247                 return GetInt8();
248             if (type == ValueHandleType.Int16)
249                 return GetInt16();
250             if (type == ValueHandleType.Int32)
251                 return GetInt32();
252             if (type == ValueHandleType.Int64)
253             {
254                 long value = GetInt64();
255                 if (value >= int.MinValue && value <= int.MaxValue)
256                 {
257                     return (int)value;
258                 }
259             }
260             if (type == ValueHandleType.UInt64)
261             {
262                 ulong value = GetUInt64();
263                 if (value <= int.MaxValue)
264                 {
265                     return (int)value;
266                 }
267             }
268             if (type == ValueHandleType.UTF8)
269                 return XmlConverter.ToInt32(bufferReader.Buffer, offset, length);
270             return XmlConverter.ToInt32(GetString());
271         }
272
273         public long ToLong()
274         {
275             ValueHandleType type = this.type;
276             if (type == ValueHandleType.Zero)
277                 return 0;
278             if (type == ValueHandleType.One)
279                 return 1;
280             if (type == ValueHandleType.Int8)
281                 return GetInt8();
282             if (type == ValueHandleType.Int16)
283                 return GetInt16();
284             if (type == ValueHandleType.Int32)
285                 return GetInt32();
286             if (type == ValueHandleType.Int64)
287                 return GetInt64();
288             if (type == ValueHandleType.UInt64)
289             {
290                 ulong value = GetUInt64();
291                 if (value <= long.MaxValue)
292                 {
293                     return (long)value;
294                 }
295             }
296             if (type == ValueHandleType.UTF8)
297             {
298                 return XmlConverter.ToInt64(bufferReader.Buffer, offset, length);
299             }
300             return XmlConverter.ToInt64(GetString());
301         }
302
303         public ulong ToULong()
304         {
305             ValueHandleType type = this.type;
306             if (type == ValueHandleType.Zero)
307                 return 0;
308             if (type == ValueHandleType.One)
309                 return 1;
310             if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
311             {
312                 long value = ToLong();
313                 if (value >= 0)
314                     return (ulong)value;
315             }
316             if (type == ValueHandleType.UInt64)
317                 return GetUInt64();
318             if (type == ValueHandleType.UTF8)
319                 return XmlConverter.ToUInt64(bufferReader.Buffer, offset, length);
320             return XmlConverter.ToUInt64(GetString());
321         }
322
323         public Single ToSingle()
324         {
325             ValueHandleType type = this.type;
326             if (type == ValueHandleType.Single)
327                 return GetSingle();
328             if (type == ValueHandleType.Double)
329             {
330                 double value = GetDouble();
331                 if ((value >= Single.MinValue && value <= Single.MaxValue) || double.IsInfinity(value) || double.IsNaN(value))
332                     return (Single)value;
333             }
334             if (type == ValueHandleType.Zero)
335                 return 0;
336             if (type == ValueHandleType.One)
337                 return 1;
338             if (type == ValueHandleType.Int8)
339                 return GetInt8();
340             if (type == ValueHandleType.Int16)
341                 return GetInt16();
342             if (type == ValueHandleType.UTF8)
343                 return XmlConverter.ToSingle(bufferReader.Buffer, offset, length);
344             return XmlConverter.ToSingle(GetString());
345         }
346
347         public Double ToDouble()
348         {
349             ValueHandleType type = this.type;
350             if (type == ValueHandleType.Double)
351                 return GetDouble();
352             if (type == ValueHandleType.Single)
353                 return GetSingle();
354             if (type == ValueHandleType.Zero)
355                 return 0;
356             if (type == ValueHandleType.One)
357                 return 1;
358             if (type == ValueHandleType.Int8)
359                 return GetInt8();
360             if (type == ValueHandleType.Int16)
361                 return GetInt16();
362             if (type == ValueHandleType.Int32)
363                 return GetInt32();
364             if (type == ValueHandleType.UTF8)
365                 return XmlConverter.ToDouble(bufferReader.Buffer, offset, length);
366             return XmlConverter.ToDouble(GetString());
367         }
368
369         public Decimal ToDecimal()
370         {
371             ValueHandleType type = this.type;
372             if (type == ValueHandleType.Decimal)
373                 return GetDecimal();
374             if (type == ValueHandleType.Zero)
375                 return 0;
376             if (type == ValueHandleType.One)
377                 return 1;
378             if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
379                 return ToLong();
380             if (type == ValueHandleType.UInt64)
381                 return GetUInt64();
382             if (type == ValueHandleType.UTF8)
383                 return XmlConverter.ToDecimal(bufferReader.Buffer, offset, length);
384             return XmlConverter.ToDecimal(GetString());
385         }
386
387         public DateTime ToDateTime()
388         {
389             if (type == ValueHandleType.DateTime)
390             {
391                 return XmlConverter.ToDateTime(GetInt64());
392             }
393             if (type == ValueHandleType.UTF8)
394             {
395                 return XmlConverter.ToDateTime(bufferReader.Buffer, offset, length);
396             }
397             return XmlConverter.ToDateTime(GetString());
398         }
399
400         public UniqueId ToUniqueId()
401         {
402             if (type == ValueHandleType.UniqueId)
403                 return GetUniqueId();
404             if (type == ValueHandleType.UTF8)
405                 return XmlConverter.ToUniqueId(bufferReader.Buffer, offset, length);
406             return XmlConverter.ToUniqueId(GetString());
407         }
408
409         public TimeSpan ToTimeSpan()
410         {
411             if (type == ValueHandleType.TimeSpan)
412                 return new TimeSpan(GetInt64());
413             if (type == ValueHandleType.UTF8)
414                 return XmlConverter.ToTimeSpan(bufferReader.Buffer, offset, length);
415             return XmlConverter.ToTimeSpan(GetString());
416         }
417
418         public Guid ToGuid()
419         {
420             if (type == ValueHandleType.Guid)
421                 return GetGuid();
422             if (type == ValueHandleType.UTF8)
423                 return XmlConverter.ToGuid(bufferReader.Buffer, offset, length);
424             return XmlConverter.ToGuid(GetString());
425         }
426
427         public override string ToString()
428         {
429             return GetString();
430         }
431
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, this.offset, this.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
479         public string GetString()
480         {
481             ValueHandleType type = this.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.UTF8:
514                     return GetCharsText();
515                 case ValueHandleType.Unicode:
516                     return GetUnicodeCharsText();
517                 case ValueHandleType.EscapedUTF8:
518                     return GetEscapedCharsText();
519                 case ValueHandleType.Char:
520                     return GetCharText();
521                 case ValueHandleType.Dictionary:
522                     return GetDictionaryString().Value;
523                 case ValueHandleType.Base64:
524                     return Base64Encoding.GetString(ToByteArray());
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 constStrings[offset];
537                 default:
538                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
539             }
540         }
541
542         // ASSUMPTION ([....]): all chars in str will be ASCII
543         public bool Equals2(string str, bool checkLower)
544         {
545             if (this.type != ValueHandleType.UTF8)
546                 return GetString() == str;
547
548             if (this.length != str.Length)
549                 return false;
550
551             byte[] buffer = bufferReader.Buffer;
552             for (int i = 0; i < this.length; ++i)
553             {
554                 Fx.Assert(str[i] < 128, "");
555                 byte ch = buffer[i + this.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
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
618         public object[] ToList()
619         {
620             return bufferReader.GetList(offset, length);
621         }
622
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
671         public bool TryReadBase64(byte[] buffer, int offset, int count, out int actual)
672         {
673             if (type == ValueHandleType.Base64)
674             {
675                 actual = Math.Min(this.length, count);
676                 GetBase64(buffer, offset, actual);
677                 this.offset += actual;
678                 this.length -= actual;
679                 return true;
680             }
681             if (type == ValueHandleType.UTF8 && count >= 3 && (this.length % 4) == 0)
682             {
683                 try
684                 {
685                     int charCount = Math.Min(count / 3 * 4, this.length);
686                     actual = Base64Encoding.GetBytes(bufferReader.Buffer, this.offset, charCount, buffer, offset);
687                     this.offset += charCount;
688                     this.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
700         public bool TryReadChars(char[] chars, int offset, int count, out int actual)
701         {
702             Fx.Assert(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 = this.offset;
717             int byteCount = this.length;
718             bool insufficientSpaceInCharsArray = false; 
719
720             while (true)
721             {
722                 while (charCount > 0 && byteCount > 0)
723                 {
724                     // fast path for codepoints U+0000 - U+007F
725                     byte b = bytes[byteOffset];
726                     if (b >= 0x80)
727                         break;
728                     chars[charOffset] = (char)b;
729                     byteOffset++;
730                     byteCount--;
731                     charOffset++;
732                     charCount--;
733                 }
734
735                 if (charCount == 0 || byteCount == 0 || insufficientSpaceInCharsArray)
736                     break;
737
738                 int actualByteCount;
739                 int actualCharCount;
740
741                 UTF8Encoding encoding = new UTF8Encoding(false, true);
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've 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                                 Fx.Assert(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             this.offset = byteOffset;
812             this.length = byteCount;
813
814             actual = (count - charCount);
815             return true;
816         }
817
818         bool TryReadUnicodeChars(char[] chars, int offset, int count, out int actual)
819         {
820             int charCount = Math.Min(count, this.length / sizeof(char));
821             for (int i = 0; i < charCount; i++)
822             {
823                 chars[offset + i] = (char)bufferReader.GetInt16(this.offset + i * sizeof(char));
824             }
825             this.offset += charCount * sizeof(char);
826             this.length -= charCount * sizeof(char);
827             actual = charCount;
828             return true;
829         }
830
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
845         public bool TryGetByteArrayLength(out int length)
846         {
847             if (type == ValueHandleType.Base64)
848             {
849                 length = this.length;
850                 return true;
851             }
852             length = 0;
853             return false;
854         }
855
856         string GetCharsText()
857         {
858             Fx.Assert(type == ValueHandleType.UTF8, "");
859             if (length == 1 && bufferReader.GetByte(offset) == '1')
860                 return "1";
861             return bufferReader.GetString(offset, length);
862         }
863
864         string GetUnicodeCharsText()
865         {
866             Fx.Assert(type == ValueHandleType.Unicode, "");
867             return bufferReader.GetUnicodeString(offset, length);
868         }
869
870         string GetEscapedCharsText()
871         {
872             Fx.Assert(type == ValueHandleType.EscapedUTF8, "");
873             return bufferReader.GetEscapedString(offset, length);
874         }
875
876         string GetCharText()
877         {
878             int ch = GetChar();
879             if (ch > char.MaxValue)
880             {
881                 SurrogateChar surrogate = new SurrogateChar(ch);
882                 char[] chars = new char[2];
883                 chars[0] = surrogate.HighChar;
884                 chars[1] = surrogate.LowChar;
885                 return new string(chars, 0, 2);
886             }
887             else
888             {
889                 return ((char)ch).ToString();
890             }
891         }
892
893         int GetChar()
894         {
895             Fx.Assert(type == ValueHandleType.Char, "");
896             return offset;
897         }
898
899         int GetInt8()
900         {
901             Fx.Assert(type == ValueHandleType.Int8, "");
902             return bufferReader.GetInt8(offset);
903         }
904
905         int GetInt16()
906         {
907             Fx.Assert(type == ValueHandleType.Int16, "");
908             return bufferReader.GetInt16(offset);
909         }
910
911         int GetInt32()
912         {
913             Fx.Assert(type == ValueHandleType.Int32, "");
914             return bufferReader.GetInt32(offset);
915         }
916
917         long GetInt64()
918         {
919             Fx.Assert(type == ValueHandleType.Int64 || type == ValueHandleType.TimeSpan || type == ValueHandleType.DateTime, "");
920             return bufferReader.GetInt64(offset);
921         }
922
923         ulong GetUInt64()
924         {
925             Fx.Assert(type == ValueHandleType.UInt64, "");
926             return bufferReader.GetUInt64(offset);
927         }
928
929         float GetSingle()
930         {
931             Fx.Assert(type == ValueHandleType.Single, "");
932             return bufferReader.GetSingle(offset);
933         }
934
935         double GetDouble()
936         {
937             Fx.Assert(type == ValueHandleType.Double, "");
938             return bufferReader.GetDouble(offset);
939         }
940
941         decimal GetDecimal()
942         {
943             Fx.Assert(type == ValueHandleType.Decimal, "");
944             return bufferReader.GetDecimal(offset);
945         }
946
947         UniqueId GetUniqueId()
948         {
949             Fx.Assert(type == ValueHandleType.UniqueId, "");
950             return bufferReader.GetUniqueId(offset);
951         }
952
953         Guid GetGuid()
954         {
955             Fx.Assert(type == ValueHandleType.Guid, "");
956             return bufferReader.GetGuid(offset);
957         }
958
959         void GetBase64(byte[] buffer, int offset, int count)
960         {
961             Fx.Assert(type == ValueHandleType.Base64, "");
962             bufferReader.GetBase64(this.offset, buffer, offset, count);
963         }
964
965         XmlDictionaryString GetDictionaryString()
966         {
967             Fx.Assert(type == ValueHandleType.Dictionary, "");
968             return bufferReader.GetDictionaryString(offset);
969         }
970
971         string GetQNameDictionaryText()
972         {
973             Fx.Assert(type == ValueHandleType.QName, "");
974             return string.Concat(PrefixHandle.GetString(PrefixHandle.GetAlphaPrefix(length)), ":", bufferReader.GetDictionaryString(offset));
975         }
976     }
977 }