1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
8 using System.Runtime.Serialization;
11 enum ValueHandleConstStringType
21 static class ValueHandleLength
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;
69 XmlBufferReader bufferReader;
73 static Base64Encoding base64Encoding;
76 static string[] constStrings = {
85 public ValueHandle(XmlBufferReader bufferReader)
87 this.bufferReader = bufferReader;
88 this.type = ValueHandleType.Empty;
91 static Base64Encoding Base64Encoding
95 if (base64Encoding == null)
96 base64Encoding = new Base64Encoding();
97 return base64Encoding;
101 public void SetConstantValue(ValueHandleConstStringType constStringType)
103 type = ValueHandleType.ConstString;
104 offset = (int)constStringType;
107 public void SetValue(ValueHandleType type)
112 public void SetDictionaryValue(int key)
114 SetValue(ValueHandleType.Dictionary, key, 0);
117 public void SetCharValue(int ch)
119 SetValue(ValueHandleType.Char, ch, 0);
122 public void SetQNameValue(int prefix, int key)
124 SetValue(ValueHandleType.QName, key, prefix);
127 public void SetValue(ValueHandleType type, int offset, int length)
130 this.offset = offset;
131 this.length = length;
134 public bool IsWhitespace()
138 case ValueHandleType.UTF8:
139 return bufferReader.IsWhitespaceUTF8(this.offset, this.length);
141 case ValueHandleType.Dictionary:
142 return bufferReader.IsWhitespaceKey(this.offset);
144 case ValueHandleType.Char:
146 if (ch > char.MaxValue)
148 return XmlConverter.IsWhitespace((char)ch);
150 case ValueHandleType.EscapedUTF8:
151 return bufferReader.IsWhitespaceUTF8(this.offset, this.length);
153 case ValueHandleType.Unicode:
154 return bufferReader.IsWhitespaceUnicode(this.offset, this.length);
156 case ValueHandleType.True:
157 case ValueHandleType.False:
158 case ValueHandleType.Zero:
159 case ValueHandleType.One:
162 case ValueHandleType.ConstString:
163 return constStrings[offset].Length == 0;
166 return this.length == 0;
174 case ValueHandleType.False:
175 case ValueHandleType.True:
177 case ValueHandleType.Zero:
178 case ValueHandleType.One:
179 case ValueHandleType.Int8:
180 case ValueHandleType.Int16:
181 case ValueHandleType.Int32:
183 case ValueHandleType.Int64:
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:
212 case ValueHandleType.TimeSpan:
213 return typeof(TimeSpan);
215 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
219 public Boolean ToBoolean()
221 ValueHandleType type = this.type;
222 if (type == ValueHandleType.False)
224 if (type == ValueHandleType.True)
226 if (type == ValueHandleType.UTF8)
227 return XmlConverter.ToBoolean(bufferReader.Buffer, offset, length);
228 if (type == ValueHandleType.Int8)
230 int value = GetInt8();
236 return XmlConverter.ToBoolean(GetString());
241 ValueHandleType type = this.type;
242 if (type == ValueHandleType.Zero)
244 if (type == ValueHandleType.One)
246 if (type == ValueHandleType.Int8)
248 if (type == ValueHandleType.Int16)
250 if (type == ValueHandleType.Int32)
252 if (type == ValueHandleType.Int64)
254 long value = GetInt64();
255 if (value >= int.MinValue && value <= int.MaxValue)
260 if (type == ValueHandleType.UInt64)
262 ulong value = GetUInt64();
263 if (value <= int.MaxValue)
268 if (type == ValueHandleType.UTF8)
269 return XmlConverter.ToInt32(bufferReader.Buffer, offset, length);
270 return XmlConverter.ToInt32(GetString());
275 ValueHandleType type = this.type;
276 if (type == ValueHandleType.Zero)
278 if (type == ValueHandleType.One)
280 if (type == ValueHandleType.Int8)
282 if (type == ValueHandleType.Int16)
284 if (type == ValueHandleType.Int32)
286 if (type == ValueHandleType.Int64)
288 if (type == ValueHandleType.UInt64)
290 ulong value = GetUInt64();
291 if (value <= long.MaxValue)
296 if (type == ValueHandleType.UTF8)
298 return XmlConverter.ToInt64(bufferReader.Buffer, offset, length);
300 return XmlConverter.ToInt64(GetString());
303 public ulong ToULong()
305 ValueHandleType type = this.type;
306 if (type == ValueHandleType.Zero)
308 if (type == ValueHandleType.One)
310 if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
312 long value = ToLong();
316 if (type == ValueHandleType.UInt64)
318 if (type == ValueHandleType.UTF8)
319 return XmlConverter.ToUInt64(bufferReader.Buffer, offset, length);
320 return XmlConverter.ToUInt64(GetString());
323 public Single ToSingle()
325 ValueHandleType type = this.type;
326 if (type == ValueHandleType.Single)
328 if (type == ValueHandleType.Double)
330 double value = GetDouble();
331 if ((value >= Single.MinValue && value <= Single.MaxValue) || double.IsInfinity(value) || double.IsNaN(value))
332 return (Single)value;
334 if (type == ValueHandleType.Zero)
336 if (type == ValueHandleType.One)
338 if (type == ValueHandleType.Int8)
340 if (type == ValueHandleType.Int16)
342 if (type == ValueHandleType.UTF8)
343 return XmlConverter.ToSingle(bufferReader.Buffer, offset, length);
344 return XmlConverter.ToSingle(GetString());
347 public Double ToDouble()
349 ValueHandleType type = this.type;
350 if (type == ValueHandleType.Double)
352 if (type == ValueHandleType.Single)
354 if (type == ValueHandleType.Zero)
356 if (type == ValueHandleType.One)
358 if (type == ValueHandleType.Int8)
360 if (type == ValueHandleType.Int16)
362 if (type == ValueHandleType.Int32)
364 if (type == ValueHandleType.UTF8)
365 return XmlConverter.ToDouble(bufferReader.Buffer, offset, length);
366 return XmlConverter.ToDouble(GetString());
369 public Decimal ToDecimal()
371 ValueHandleType type = this.type;
372 if (type == ValueHandleType.Decimal)
374 if (type == ValueHandleType.Zero)
376 if (type == ValueHandleType.One)
378 if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
380 if (type == ValueHandleType.UInt64)
382 if (type == ValueHandleType.UTF8)
383 return XmlConverter.ToDecimal(bufferReader.Buffer, offset, length);
384 return XmlConverter.ToDecimal(GetString());
387 public DateTime ToDateTime()
389 if (type == ValueHandleType.DateTime)
391 return XmlConverter.ToDateTime(GetInt64());
393 if (type == ValueHandleType.UTF8)
395 return XmlConverter.ToDateTime(bufferReader.Buffer, offset, length);
397 return XmlConverter.ToDateTime(GetString());
400 public UniqueId ToUniqueId()
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());
409 public TimeSpan ToTimeSpan()
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());
420 if (type == ValueHandleType.Guid)
422 if (type == ValueHandleType.UTF8)
423 return XmlConverter.ToGuid(bufferReader.Buffer, offset, length);
424 return XmlConverter.ToGuid(GetString());
427 public override string ToString()
432 public byte[] ToByteArray()
434 if (type == ValueHandleType.Base64)
436 byte[] buffer = new byte[length];
437 GetBase64(buffer, 0, length);
440 if (type == ValueHandleType.UTF8 && (length % 4) == 0)
444 int expectedLength = length / 4 * 3;
447 if (bufferReader.Buffer[offset + length - 1] == '=')
450 if (bufferReader.Buffer[offset + length - 2] == '=')
454 byte[] buffer = new byte[expectedLength];
455 int actualLength = Base64Encoding.GetBytes(bufferReader.Buffer, this.offset, this.length, buffer, 0);
456 if (actualLength != buffer.Length)
458 byte[] newBuffer = new byte[actualLength];
459 Buffer.BlockCopy(buffer, 0, newBuffer, 0, actualLength);
464 catch (FormatException)
466 // Something unhappy with the characters, fall back to the hard way
471 return Base64Encoding.GetBytes(XmlConverter.StripWhitespace(GetString()));
473 catch (FormatException exception)
475 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(exception.Message, exception.InnerException));
479 public string GetString()
481 ValueHandleType type = this.type;
482 if (type == ValueHandleType.UTF8)
483 return GetCharsText();
487 case ValueHandleType.False:
489 case ValueHandleType.True:
491 case ValueHandleType.Zero:
493 case ValueHandleType.One:
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:
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];
538 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
542 // ASSUMPTION (Microsoft): all chars in str will be ASCII
543 public bool Equals2(string str, bool checkLower)
545 if (this.type != ValueHandleType.UTF8)
546 return GetString() == str;
548 if (this.length != str.Length)
551 byte[] buffer = bufferReader.Buffer;
552 for (int i = 0; i < this.length; ++i)
554 Fx.Assert(str[i] < 128, "");
555 byte ch = buffer[i + this.offset];
559 if (checkLower && char.ToLowerInvariant((char)ch) == str[i])
568 public void Sign(XmlSigningNodeWriter writer)
572 case ValueHandleType.Int8:
573 case ValueHandleType.Int16:
574 case ValueHandleType.Int32:
575 writer.WriteInt32Text(ToInt());
577 case ValueHandleType.Int64:
578 writer.WriteInt64Text(GetInt64());
580 case ValueHandleType.UInt64:
581 writer.WriteUInt64Text(GetUInt64());
583 case ValueHandleType.Single:
584 writer.WriteFloatText(GetSingle());
586 case ValueHandleType.Double:
587 writer.WriteDoubleText(GetDouble());
589 case ValueHandleType.Decimal:
590 writer.WriteDecimalText(GetDecimal());
592 case ValueHandleType.DateTime:
593 writer.WriteDateTimeText(ToDateTime());
595 case ValueHandleType.Empty:
597 case ValueHandleType.UTF8:
598 writer.WriteEscapedText(bufferReader.Buffer, offset, length);
600 case ValueHandleType.Base64:
601 writer.WriteBase64Text(bufferReader.Buffer, 0, bufferReader.Buffer, offset, length);
603 case ValueHandleType.UniqueId:
604 writer.WriteUniqueIdText(ToUniqueId());
606 case ValueHandleType.Guid:
607 writer.WriteGuidText(ToGuid());
609 case ValueHandleType.TimeSpan:
610 writer.WriteTimeSpanText(ToTimeSpan());
613 writer.WriteEscapedText(GetString());
618 public object[] ToList()
620 return bufferReader.GetList(offset, length);
623 public object ToObject()
627 case ValueHandleType.False:
628 case ValueHandleType.True:
630 case ValueHandleType.Zero:
631 case ValueHandleType.One:
632 case ValueHandleType.Int8:
633 case ValueHandleType.Int16:
634 case ValueHandleType.Int32:
636 case ValueHandleType.Int64:
638 case ValueHandleType.UInt64:
640 case ValueHandleType.Single:
642 case ValueHandleType.Double:
644 case ValueHandleType.Decimal:
646 case ValueHandleType.DateTime:
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:
656 case ValueHandleType.Base64:
657 return ToByteArray();
658 case ValueHandleType.List:
660 case ValueHandleType.UniqueId:
662 case ValueHandleType.Guid:
664 case ValueHandleType.TimeSpan:
667 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
671 public bool TryReadBase64(byte[] buffer, int offset, int count, out int actual)
673 if (type == ValueHandleType.Base64)
675 actual = Math.Min(this.length, count);
676 GetBase64(buffer, offset, actual);
677 this.offset += actual;
678 this.length -= actual;
681 if (type == ValueHandleType.UTF8 && count >= 3 && (this.length % 4) == 0)
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;
691 catch (FormatException)
693 // Something unhappy with the characters, fall back to the hard way
700 public bool TryReadChars(char[] chars, int offset, int count, out int actual)
702 Fx.Assert(offset + count <= chars.Length, string.Format("offset '{0}' + count '{1}' MUST BE <= chars.Length '{2}'", offset, count, chars.Length));
704 if (type == ValueHandleType.Unicode)
705 return TryReadUnicodeChars(chars, offset, count, out actual);
707 if (type != ValueHandleType.UTF8)
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;
722 while (charCount > 0 && byteCount > 0)
724 // fast path for codepoints U+0000 - U+007F
725 byte b = bytes[byteOffset];
728 chars[charOffset] = (char)b;
735 if (charCount == 0 || byteCount == 0 || insufficientSpaceInCharsArray)
741 UTF8Encoding encoding = new UTF8Encoding(false, true);
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))
747 actualCharCount = encoding.GetChars(bytes, byteOffset, byteCount, chars, charOffset);
748 actualByteCount = byteCount;
752 Decoder decoder = encoding.GetDecoder();
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);
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);
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)
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)
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.
778 // actualByteCount = 0 when the while loop is broken out of; decoder goes out of scope so its state no longer matters
780 insufficientSpaceInCharsArray = true;
785 Fx.Assert(byteOffset + actualByteCount < bytes.Length,
786 string.Format("byteOffset {0} + actualByteCount {1} MUST BE < bytes.Length {2}", byteOffset, actualByteCount, bytes.Length));
788 // Request a few more bytes to get at least one character
789 actualCharCount = decoder.GetChars(bytes, byteOffset + actualByteCount, 1, chars, charOffset);
794 // Now that we actually retrieved some characters, figure out how many bytes it actually was
795 actualByteCount = encoding.GetByteCount(chars, charOffset, actualCharCount);
798 catch (FormatException exception)
800 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateEncodingException(bytes, byteOffset, byteCount, exception));
804 byteOffset += actualByteCount;
805 byteCount -= actualByteCount;
807 charOffset += actualCharCount;
808 charCount -= actualCharCount;
811 this.offset = byteOffset;
812 this.length = byteCount;
814 actual = (count - charCount);
818 bool TryReadUnicodeChars(char[] chars, int offset, int count, out int actual)
820 int charCount = Math.Min(count, this.length / sizeof(char));
821 for (int i = 0; i < charCount; i++)
823 chars[offset + i] = (char)bufferReader.GetInt16(this.offset + i * sizeof(char));
825 this.offset += charCount * sizeof(char);
826 this.length -= charCount * sizeof(char);
831 public bool TryGetDictionaryString(out XmlDictionaryString value)
833 if (type == ValueHandleType.Dictionary)
835 value = GetDictionaryString();
845 public bool TryGetByteArrayLength(out int length)
847 if (type == ValueHandleType.Base64)
849 length = this.length;
856 string GetCharsText()
858 Fx.Assert(type == ValueHandleType.UTF8, "");
859 if (length == 1 && bufferReader.GetByte(offset) == '1')
861 return bufferReader.GetString(offset, length);
864 string GetUnicodeCharsText()
866 Fx.Assert(type == ValueHandleType.Unicode, "");
867 return bufferReader.GetUnicodeString(offset, length);
870 string GetEscapedCharsText()
872 Fx.Assert(type == ValueHandleType.EscapedUTF8, "");
873 return bufferReader.GetEscapedString(offset, length);
879 if (ch > char.MaxValue)
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);
889 return ((char)ch).ToString();
895 Fx.Assert(type == ValueHandleType.Char, "");
901 Fx.Assert(type == ValueHandleType.Int8, "");
902 return bufferReader.GetInt8(offset);
907 Fx.Assert(type == ValueHandleType.Int16, "");
908 return bufferReader.GetInt16(offset);
913 Fx.Assert(type == ValueHandleType.Int32, "");
914 return bufferReader.GetInt32(offset);
919 Fx.Assert(type == ValueHandleType.Int64 || type == ValueHandleType.TimeSpan || type == ValueHandleType.DateTime, "");
920 return bufferReader.GetInt64(offset);
925 Fx.Assert(type == ValueHandleType.UInt64, "");
926 return bufferReader.GetUInt64(offset);
931 Fx.Assert(type == ValueHandleType.Single, "");
932 return bufferReader.GetSingle(offset);
937 Fx.Assert(type == ValueHandleType.Double, "");
938 return bufferReader.GetDouble(offset);
943 Fx.Assert(type == ValueHandleType.Decimal, "");
944 return bufferReader.GetDecimal(offset);
947 UniqueId GetUniqueId()
949 Fx.Assert(type == ValueHandleType.UniqueId, "");
950 return bufferReader.GetUniqueId(offset);
955 Fx.Assert(type == ValueHandleType.Guid, "");
956 return bufferReader.GetGuid(offset);
959 void GetBase64(byte[] buffer, int offset, int count)
961 Fx.Assert(type == ValueHandleType.Base64, "");
962 bufferReader.GetBase64(this.offset, buffer, offset, count);
965 XmlDictionaryString GetDictionaryString()
967 Fx.Assert(type == ValueHandleType.Dictionary, "");
968 return bufferReader.GetDictionaryString(offset);
971 string GetQNameDictionaryText()
973 Fx.Assert(type == ValueHandleType.QName, "");
974 return string.Concat(PrefixHandle.GetString(PrefixHandle.GetAlphaPrefix(length)), ":", bufferReader.GetDictionaryString(offset));