//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.Xml { using System; using System.Collections; using System.Globalization; using System.IO; using System.Runtime; using System.Runtime.Serialization; using System.Security; using System.Text; class XmlBufferReader { XmlDictionaryReader reader; Stream stream; byte[] streamBuffer; byte[] buffer; int offsetMin; int offsetMax; IXmlDictionary dictionary; XmlBinaryReaderSession session; byte[] guid; int offset; const int maxBytesPerChar = 3; char[] chars; int windowOffset; int windowOffsetMax; ValueHandle listValue; static byte[] emptyByteArray = new byte[0]; static XmlBufferReader empty = new XmlBufferReader(emptyByteArray); public XmlBufferReader(XmlDictionaryReader reader) { this.reader = reader; } public XmlBufferReader(byte[] buffer) { this.reader = null; this.buffer = buffer; } static public XmlBufferReader Empty { get { return empty; } } public byte[] Buffer { get { return buffer; } } public bool IsStreamed { get { return stream != null; } } public void SetBuffer(Stream stream, IXmlDictionary dictionary, XmlBinaryReaderSession session) { if (streamBuffer == null) { streamBuffer = new byte[128]; } SetBuffer(stream, streamBuffer, 0, 0, dictionary, session); this.windowOffset = 0; this.windowOffsetMax = streamBuffer.Length; } public void SetBuffer(byte[] buffer, int offset, int count, IXmlDictionary dictionary, XmlBinaryReaderSession session) { SetBuffer(null, buffer, offset, count, dictionary, session); } void SetBuffer(Stream stream, byte[] buffer, int offset, int count, IXmlDictionary dictionary, XmlBinaryReaderSession session) { this.stream = stream; this.buffer = buffer; this.offsetMin = offset; this.offset = offset; this.offsetMax = offset + count; this.dictionary = dictionary; this.session = session; } public void Close() { if (streamBuffer != null && streamBuffer.Length > 4096) { streamBuffer = null; } if (stream != null) { stream.Close(); this.stream = null; } this.buffer = emptyByteArray; this.offset = 0; this.offsetMax = 0; this.windowOffset = 0; this.windowOffsetMax = 0; this.dictionary = null; this.session = null; } public bool EndOfFile { get { return offset == offsetMax && !TryEnsureByte(); } } public byte GetByte() { int offset = this.offset; if (offset < offsetMax) return buffer[offset]; else return GetByteHard(); } public void SkipByte() { Advance(1); } byte GetByteHard() { EnsureByte(); return buffer[offset]; } public byte[] GetBuffer(int count, out int offset) { offset = this.offset; if (offset <= this.offsetMax - count) return buffer; return GetBufferHard(count, out offset); } public byte[] GetBuffer(int count, out int offset, out int offsetMax) { offset = this.offset; if (offset <= this.offsetMax - count) { offsetMax = this.offset + count; } else { TryEnsureBytes(Math.Min(count, windowOffsetMax - offset)); offsetMax = this.offsetMax; } return buffer; } public byte[] GetBuffer(out int offset, out int offsetMax) { offset = this.offset; offsetMax = this.offsetMax; return buffer; } byte[] GetBufferHard(int count, out int offset) { offset = this.offset; EnsureBytes(count); return buffer; } void EnsureByte() { if (!TryEnsureByte()) XmlExceptionHelper.ThrowUnexpectedEndOfFile(reader); } bool TryEnsureByte() { if (stream == null) return false; if (offsetMax >= windowOffsetMax) XmlExceptionHelper.ThrowMaxBytesPerReadExceeded(reader, windowOffsetMax - windowOffset); if (offsetMax >= buffer.Length) return TryEnsureBytes(1); int b = stream.ReadByte(); if (b == -1) return false; buffer[offsetMax++] = (byte)b; return true; } void EnsureBytes(int count) { if (!TryEnsureBytes(count)) XmlExceptionHelper.ThrowUnexpectedEndOfFile(reader); } bool TryEnsureBytes(int count) { if (stream == null) return false; if (offset > int.MaxValue - count) XmlExceptionHelper.ThrowMaxBytesPerReadExceeded(reader, windowOffsetMax - windowOffset); int newOffsetMax = offset + count; if (newOffsetMax < offsetMax) return true; if (newOffsetMax > windowOffsetMax) XmlExceptionHelper.ThrowMaxBytesPerReadExceeded(reader, windowOffsetMax - windowOffset); if (newOffsetMax > buffer.Length) { byte[] newBuffer = new byte[Math.Max(newOffsetMax, buffer.Length * 2)]; System.Buffer.BlockCopy(this.buffer, 0, newBuffer, 0, offsetMax); buffer = newBuffer; streamBuffer = newBuffer; } int needed = newOffsetMax - offsetMax; while (needed > 0) { int actual = stream.Read(buffer, offsetMax, needed); if (actual == 0) return false; offsetMax += actual; needed -= actual; } return true; } #if NO void ReadByte(byte b) { if (BufferReader.GetByte() != b) XmlExceptionHelper.ThrowTokenExpected(this, ((char)b).ToString(), (char)BufferReader.GetByte()); } #endif public void Advance(int count) { Fx.Assert(this.offset + count <= offsetMax, ""); this.offset += count; } public void InsertBytes(byte[] buffer, int offset, int count) { Fx.Assert(stream != null, ""); if (offsetMax > buffer.Length - count) { byte[] newBuffer = new byte[offsetMax + count]; System.Buffer.BlockCopy(this.buffer, 0, newBuffer, 0, this.offsetMax); this.buffer = newBuffer; this.streamBuffer = newBuffer; } System.Buffer.BlockCopy(this.buffer, this.offset, this.buffer, this.offset + count, this.offsetMax - this.offset); offsetMax += count; System.Buffer.BlockCopy(buffer, offset, this.buffer, this.offset, count); } public void SetWindow(int windowOffset, int windowLength) { // [0...elementOffset-1][elementOffset..offset][offset..offsetMax-1][offsetMax..buffer.Length] // ^--Elements, Attributes in scope // ^-- The node just consumed // ^--Data buffered, not consumed // ^--Unused space if (windowOffset > int.MaxValue - windowLength) windowLength = int.MaxValue - windowOffset; if (offset != windowOffset) { System.Buffer.BlockCopy(buffer, offset, buffer, windowOffset, offsetMax - offset); offsetMax = windowOffset + (offsetMax - offset); offset = windowOffset; } this.windowOffset = windowOffset; this.windowOffsetMax = Math.Max(windowOffset + windowLength, offsetMax); } public int Offset { get { return offset; } set { Fx.Assert(value >= offsetMin && value <= offsetMax, ""); this.offset = value; } } public int ReadBytes(int count) { Fx.Assert(count >= 0, ""); int offset = this.offset; if (offset > offsetMax - count) EnsureBytes(count); this.offset += count; return offset; } public int ReadMultiByteUInt31() { int i = GetByte(); Advance(1); if ((i & 0x80) == 0) return i; i &= 0x7F; int j = GetByte(); Advance(1); i |= ((j & 0x7F) << 7); if ((j & 0x80) == 0) return i; int k = GetByte(); Advance(1); i |= ((k & 0x7F) << 14); if ((k & 0x80) == 0) return i; int l = GetByte(); Advance(1); i |= ((l & 0x7F) << 21); if ((l & 0x80) == 0) return i; int m = GetByte(); Advance(1); i |= (m << 28); if ((m & 0xF8) != 0) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); return i; } public int ReadUInt8() { byte b = GetByte(); Advance(1); return b; } public int ReadInt8() { return (sbyte)ReadUInt8(); } public int ReadUInt16() { int offset; byte[] buffer = GetBuffer(2, out offset); int i = buffer[offset + 0] + (buffer[offset + 1] << 8); Advance(2); return i; } public int ReadInt16() { return (Int16)ReadUInt16(); } public int ReadInt32() { int offset; byte[] buffer = GetBuffer(4, out offset); byte b1 = buffer[offset + 0]; byte b2 = buffer[offset + 1]; byte b3 = buffer[offset + 2]; byte b4 = buffer[offset + 3]; Advance(4); return (((((b4 << 8) + b3) << 8) + b2) << 8) + b1; } public int ReadUInt31() { int i = ReadInt32(); if (i < 0) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); return i; } public long ReadInt64() { Int64 lo = (UInt32)ReadInt32(); Int64 hi = (UInt32)ReadInt32(); return (hi << 32) + lo; } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.", Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")] [SecuritySafeCritical] unsafe public float ReadSingle() { int offset; byte[] buffer = GetBuffer(ValueHandleLength.Single, out offset); float value; byte* pb = (byte*)&value; Fx.Assert(sizeof(float) == 4, ""); pb[0] = buffer[offset + 0]; pb[1] = buffer[offset + 1]; pb[2] = buffer[offset + 2]; pb[3] = buffer[offset + 3]; Advance(ValueHandleLength.Single); return value; } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.", Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")] [SecuritySafeCritical] unsafe public double ReadDouble() { int offset; byte[] buffer = GetBuffer(ValueHandleLength.Double, out offset); double value; byte* pb = (byte*)&value; Fx.Assert(sizeof(double) == 8, ""); pb[0] = buffer[offset + 0]; pb[1] = buffer[offset + 1]; pb[2] = buffer[offset + 2]; pb[3] = buffer[offset + 3]; pb[4] = buffer[offset + 4]; pb[5] = buffer[offset + 5]; pb[6] = buffer[offset + 6]; pb[7] = buffer[offset + 7]; Advance(ValueHandleLength.Double); return value; } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.", Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")] [SecuritySafeCritical] unsafe public decimal ReadDecimal() { const int SignMask = unchecked((int)0x80000000); const int ScaleMask = 0x00FF0000; int offset; byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out offset); byte b1 = buffer[offset + 0]; byte b2 = buffer[offset + 1]; byte b3 = buffer[offset + 2]; byte b4 = buffer[offset + 3]; int flags = (((((b4 << 8) + b3) << 8) + b2) << 8) + b1; //this logic mirrors the code in Decimal(int []) ctor. if ((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16)) { decimal value; byte* pb = (byte*)&value; for (int i = 0; i < sizeof(decimal); i++) pb[i] = buffer[offset + i]; Advance(ValueHandleLength.Decimal); return value; } else { XmlExceptionHelper.ThrowInvalidBinaryFormat(this.reader); } //compiler doesn't know that XmlExceptionHelper.ThrowInvalidBinaryFormat always throws, //so we have to have a return statement here even though we shouldn't hit this code path... Fx.Assert("A decimal value should have been returned or an exception should have been thrown."); return default(decimal); } public UniqueId ReadUniqueId() { int offset; byte[] buffer = GetBuffer(ValueHandleLength.UniqueId, out offset); UniqueId uniqueId = new UniqueId(buffer, offset); Advance(ValueHandleLength.UniqueId); return uniqueId; } public DateTime ReadDateTime() { long value = 0; try { value = ReadInt64(); return DateTime.FromBinary(value); } catch (ArgumentException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value.ToString(CultureInfo.InvariantCulture), "DateTime", exception)); } catch (FormatException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value.ToString(CultureInfo.InvariantCulture), "DateTime", exception)); } catch (OverflowException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value.ToString(CultureInfo.InvariantCulture), "DateTime", exception)); } } public TimeSpan ReadTimeSpan() { long value = 0; try { value = ReadInt64(); return TimeSpan.FromTicks(value); } catch (ArgumentException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value.ToString(CultureInfo.InvariantCulture), "TimeSpan", exception)); } catch (FormatException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value.ToString(CultureInfo.InvariantCulture), "TimeSpan", exception)); } catch (OverflowException exception) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value.ToString(CultureInfo.InvariantCulture), "TimeSpan", exception)); } } public Guid ReadGuid() { int offset; byte[] buffer = GetBuffer(ValueHandleLength.Guid, out offset); Guid guid = GetGuid(offset); Advance(ValueHandleLength.Guid); return guid; } public string ReadUTF8String(int length) { int offset; byte[] buffer = GetBuffer(length, out offset); char[] chars = GetCharBuffer(length); int charCount = GetChars(offset, length, chars); string value = new string(chars, 0, charCount); Advance(length); return value; } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")] [SecurityCritical] unsafe public void UnsafeReadArray(byte* dst, byte* dstMax) { UnsafeReadArray(dst, (int)(dstMax - dst)); } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")] [SecurityCritical] unsafe void UnsafeReadArray(byte* dst, int length) { if (stream != null) { const int chunk = 256; while (length >= chunk) { byte[] _buffer = GetBuffer(chunk, out offset); for (int i = 0; i < chunk; i++) { *dst++ = _buffer[offset + i]; } Advance(chunk); length -= chunk; } } if (length > 0) { byte[] buffer = GetBuffer(length, out offset); fixed (byte* _src = &buffer[offset]) { byte* src = _src; byte* dstMax = dst + length; while (dst < dstMax) { *dst = *src; dst++; src++; } } Advance(length); } } char[] GetCharBuffer(int count) { if (count > 1024) return new char[count]; if (chars == null || chars.Length < count) chars = new char[count]; return chars; } int GetChars(int offset, int length, char[] chars) { byte[] buffer = this.buffer; for (int i = 0; i < length; i++) { byte b = buffer[offset + i]; if (b >= 0x80) return i + XmlConverter.ToChars(buffer, offset + i, length - i, chars, i); chars[i] = (char)b; } return length; } int GetChars(int offset, int length, char[] chars, int charOffset) { byte[] buffer = this.buffer; for (int i = 0; i < length; i++) { byte b = buffer[offset + i]; if (b >= 0x80) return i + XmlConverter.ToChars(buffer, offset + i, length - i, chars, charOffset + i); chars[charOffset + i] = (char)b; } return length; } public string GetString(int offset, int length) { char[] chars = GetCharBuffer(length); int charCount = GetChars(offset, length, chars); return new string(chars, 0, charCount); } public string GetUnicodeString(int offset, int length) { return XmlConverter.ToStringUnicode(buffer, offset, length); } public string GetString(int offset, int length, XmlNameTable nameTable) { char[] chars = GetCharBuffer(length); int charCount = GetChars(offset, length, chars); return nameTable.Add(chars, 0, charCount); } public int GetEscapedChars(int offset, int length, char[] chars) { byte[] buffer = this.buffer; int charCount = 0; int textOffset = offset; int offsetMax = offset + length; while (true) { while (offset < offsetMax && IsAttrChar(buffer[offset])) offset++; charCount += GetChars(textOffset, offset - textOffset, chars, charCount); if (offset == offsetMax) break; textOffset = offset; if (buffer[offset] == '&') { while (offset < offsetMax && buffer[offset] != ';') offset++; offset++; int ch = GetCharEntity(textOffset, offset - textOffset); textOffset = offset; if (ch > char.MaxValue) { SurrogateChar surrogate = new SurrogateChar(ch); chars[charCount++] = surrogate.HighChar; chars[charCount++] = surrogate.LowChar; } else { chars[charCount++] = (char)ch; } } else if (buffer[offset] == '\n' || buffer[offset] == '\t') { chars[charCount++] = ' '; offset++; textOffset = offset; } else // '\r' { chars[charCount++] = ' '; offset++; if (offset < offsetMax && buffer[offset] == '\n') offset++; textOffset = offset; } } return charCount; } bool IsAttrChar(int ch) { switch (ch) { case '&': case '\r': case '\t': case '\n': return false; default: return true; } } public string GetEscapedString(int offset, int length) { char[] chars = GetCharBuffer(length); int charCount = GetEscapedChars(offset, length, chars); return new string(chars, 0, charCount); } public string GetEscapedString(int offset, int length, XmlNameTable nameTable) { char[] chars = GetCharBuffer(length); int charCount = GetEscapedChars(offset, length, chars); return nameTable.Add(chars, 0, charCount); } int GetLessThanCharEntity(int offset, int length) { byte[] buffer = this.buffer; if (length != 4 || buffer[offset + 1] != (byte)'l' || buffer[offset + 2] != (byte)'t') { XmlExceptionHelper.ThrowInvalidCharRef(reader); } return (int)'<'; } int GetGreaterThanCharEntity(int offset, int length) { byte[] buffer = this.buffer; if (length != 4 || buffer[offset + 1] != (byte)'g' || buffer[offset + 2] != (byte)'t') { XmlExceptionHelper.ThrowInvalidCharRef(reader); } return (int)'>'; } int GetQuoteCharEntity(int offset, int length) { byte[] buffer = this.buffer; if (length != 6 || buffer[offset + 1] != (byte)'q' || buffer[offset + 2] != (byte)'u' || buffer[offset + 3] != (byte)'o' || buffer[offset + 4] != (byte)'t') { XmlExceptionHelper.ThrowInvalidCharRef(reader); } return (int)'"'; } int GetAmpersandCharEntity(int offset, int length) { byte[] buffer = this.buffer; if (length != 5 || buffer[offset + 1] != (byte)'a' || buffer[offset + 2] != (byte)'m' || buffer[offset + 3] != (byte)'p') { XmlExceptionHelper.ThrowInvalidCharRef(reader); } return (int)'&'; } int GetApostropheCharEntity(int offset, int length) { byte[] buffer = this.buffer; if (length != 6 || buffer[offset + 1] != (byte)'a' || buffer[offset + 2] != (byte)'p' || buffer[offset + 3] != (byte)'o' || buffer[offset + 4] != (byte)'s') { XmlExceptionHelper.ThrowInvalidCharRef(reader); } return (int)'\''; } int GetDecimalCharEntity(int offset, int length) { byte[] buffer = this.buffer; Fx.Assert(buffer[offset + 0] == '&', ""); Fx.Assert(buffer[offset + 1] == '#', ""); Fx.Assert(buffer[offset + length - 1] == ';', ""); int value = 0; for (int i = 2; i < length - 1; i++) { byte ch = buffer[offset + i]; if (ch < (byte)'0' || ch > (byte)'9') XmlExceptionHelper.ThrowInvalidCharRef(reader); value = value * 10 + (ch - '0'); if (value > SurrogateChar.MaxValue) XmlExceptionHelper.ThrowInvalidCharRef(reader); } return value; } int GetHexCharEntity(int offset, int length) { byte[] buffer = this.buffer; Fx.Assert(buffer[offset + 0] == '&', ""); Fx.Assert(buffer[offset + 1] == '#', ""); Fx.Assert(buffer[offset + 2] == 'x', ""); Fx.Assert(buffer[offset + length - 1] == ';', ""); int value = 0; for (int i = 3; i < length - 1; i++) { byte ch = buffer[offset + i]; int digit = 0; if (ch >= '0' && ch <= '9') digit = (ch - '0'); else if (ch >= 'a' && ch <= 'f') digit = 10 + (ch - 'a'); else if (ch >= 'A' && ch <= 'F') digit = 10 + (ch - 'A'); else XmlExceptionHelper.ThrowInvalidCharRef(reader); Fx.Assert(digit >= 0 && digit < 16, ""); value = value * 16 + digit; if (value > SurrogateChar.MaxValue) XmlExceptionHelper.ThrowInvalidCharRef(reader); } return value; } public int GetCharEntity(int offset, int length) { if (length < 3) XmlExceptionHelper.ThrowInvalidCharRef(reader); byte[] buffer = this.buffer; Fx.Assert(buffer[offset] == '&', ""); Fx.Assert(buffer[offset + length - 1] == ';', ""); switch (buffer[offset + 1]) { case (byte)'l': return GetLessThanCharEntity(offset, length); case (byte)'g': return GetGreaterThanCharEntity(offset, length); case (byte)'a': if (buffer[offset + 2] == (byte)'m') return GetAmpersandCharEntity(offset, length); else return GetApostropheCharEntity(offset, length); case (byte)'q': return GetQuoteCharEntity(offset, length); case (byte)'#': if (buffer[offset + 2] == (byte)'x') return GetHexCharEntity(offset, length); else return GetDecimalCharEntity(offset, length); default: XmlExceptionHelper.ThrowInvalidCharRef(reader); return 0; } } public bool IsWhitespaceKey(int key) { string s = GetDictionaryString(key).Value; for (int i = 0; i < s.Length; i++) { if (!XmlConverter.IsWhitespace(s[i])) return false; } return true; } public bool IsWhitespaceUTF8(int offset, int length) { byte[] buffer = this.buffer; for (int i = 0; i < length; i++) { if (!XmlConverter.IsWhitespace((char)buffer[offset + i])) return false; } return true; } public bool IsWhitespaceUnicode(int offset, int length) { byte[] buffer = this.buffer; for (int i = 0; i < length; i += sizeof(char)) { char ch = (char)GetInt16(offset + i); if (!XmlConverter.IsWhitespace(ch)) return false; } return true; } public bool Equals2(int key1, int key2, XmlBufferReader bufferReader2) { // If the keys aren't from the same dictionary, they still might be the same if (key1 == key2) return true; else return GetDictionaryString(key1).Value == bufferReader2.GetDictionaryString(key2).Value; } public bool Equals2(int key1, XmlDictionaryString xmlString2) { if ((key1 & 1) == 0 && xmlString2.Dictionary == dictionary) return xmlString2.Key == (key1 >> 1); else return GetDictionaryString(key1).Value == xmlString2.Value; } public bool Equals2(int offset1, int length1, byte[] buffer2) { int length2 = buffer2.Length; if (length1 != length2) return false; byte[] buffer1 = this.buffer; for (int i = 0; i < length1; i++) { if (buffer1[offset1 + i] != buffer2[i]) return false; } return true; } public bool Equals2(int offset1, int length1, XmlBufferReader bufferReader2, int offset2, int length2) { if (length1 != length2) return false; byte[] buffer1 = this.buffer; byte[] buffer2 = bufferReader2.buffer; for (int i = 0; i < length1; i++) { if (buffer1[offset1 + i] != buffer2[offset2 + i]) return false; } return true; } public bool Equals2(int offset1, int length1, int offset2, int length2) { if (length1 != length2) return false; if (offset1 == offset2) return true; byte[] buffer = this.buffer; for (int i = 0; i < length1; i++) { if (buffer[offset1 + i] != buffer[offset2 + i]) return false; } return true; } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.", Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")] [SecuritySafeCritical] unsafe public bool Equals2(int offset1, int length1, string s2) { int byteLength = length1; int charLength = s2.Length; // N unicode chars will be represented in at least N bytes, but // no more than N * 3 bytes. If the byte count falls outside of this // range, then the strings cannot be equal. if (byteLength < charLength || byteLength > charLength * maxBytesPerChar) return false; byte[] buffer = this.buffer; if (length1 < 8) { int length = Math.Min(byteLength, charLength); int offset = offset1; for (int i = 0; i < length; i++) { byte b = buffer[offset + i]; if (b >= 0x80) return XmlConverter.ToString(buffer, offset1, length1) == s2; if (s2[i] != (char)b) return false; } return byteLength == charLength; } else { int length = Math.Min(byteLength, charLength); fixed (byte* _pb = &buffer[offset1]) { byte* pb = _pb; byte* pbMax = pb + length; fixed (char* _pch = s2) { char* pch = _pch; // Try to do the fast comparison in ascii space int t = 0; while (pb < pbMax && *pb < 0x80) { t = *pb - (byte)(*pch); // The code generated is better if we break out then return if (t != 0) break; pb++; pch++; } if (t != 0) return false; if (pb == pbMax) return (byteLength == charLength); } } return XmlConverter.ToString(buffer, offset1, length1) == s2; } } public int Compare(int offset1, int length1, int offset2, int length2) { byte[] buffer = this.buffer; int length = Math.Min(length1, length2); for (int i = 0; i < length; i++) { int s = buffer[offset1 + i] - buffer[offset2 + i]; if (s != 0) return s; } return length1 - length2; } public byte GetByte(int offset) { return buffer[offset]; } public int GetInt8(int offset) { return (sbyte)GetByte(offset); } public int GetInt16(int offset) { byte[] buffer = this.buffer; return (Int16)(buffer[offset] + (buffer[offset + 1] << 8)); } public int GetInt32(int offset) { byte[] buffer = this.buffer; byte b1 = buffer[offset + 0]; byte b2 = buffer[offset + 1]; byte b3 = buffer[offset + 2]; byte b4 = buffer[offset + 3]; return (((((b4 << 8) + b3) << 8) + b2) << 8) + b1; } public long GetInt64(int offset) { byte[] buffer = this.buffer; byte b1, b2, b3, b4; b1 = buffer[offset + 0]; b2 = buffer[offset + 1]; b3 = buffer[offset + 2]; b4 = buffer[offset + 3]; Int64 lo = (UInt32)(((((b4 << 8) + b3) << 8) + b2) << 8) + b1; b1 = buffer[offset + 4]; b2 = buffer[offset + 5]; b3 = buffer[offset + 6]; b4 = buffer[offset + 7]; Int64 hi = (UInt32)(((((b4 << 8) + b3) << 8) + b2) << 8) + b1; return (hi << 32) + lo; } public ulong GetUInt64(int offset) { return (ulong)GetInt64(offset); } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.", Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")] [SecuritySafeCritical] unsafe public float GetSingle(int offset) { byte[] buffer = this.buffer; float value; byte* pb = (byte*)&value; Fx.Assert(sizeof(float) == 4, ""); pb[0] = buffer[offset + 0]; pb[1] = buffer[offset + 1]; pb[2] = buffer[offset + 2]; pb[3] = buffer[offset + 3]; return value; } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.", Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")] [SecuritySafeCritical] unsafe public double GetDouble(int offset) { byte[] buffer = this.buffer; double value; byte* pb = (byte*)&value; Fx.Assert(sizeof(double) == 8, ""); pb[0] = buffer[offset + 0]; pb[1] = buffer[offset + 1]; pb[2] = buffer[offset + 2]; pb[3] = buffer[offset + 3]; pb[4] = buffer[offset + 4]; pb[5] = buffer[offset + 5]; pb[6] = buffer[offset + 6]; pb[7] = buffer[offset + 7]; return value; } [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.", Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")] [SecuritySafeCritical] public unsafe decimal GetDecimal(int offset) { const int SignMask = unchecked((int)0x80000000); const int ScaleMask = 0x00FF0000; byte[] buffer = this.buffer; byte b1 = buffer[offset + 0]; byte b2 = buffer[offset + 1]; byte b3 = buffer[offset + 2]; byte b4 = buffer[offset + 3]; int flags = (((((b4 << 8) + b3) << 8) + b2) << 8) + b1; //this logic mirrors the code in Decimal(int []) ctor. if ((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16)) { decimal value; byte* pb = (byte*)&value; for (int i = 0; i < sizeof(decimal); i++) pb[i] = buffer[offset + i]; return value; } else { XmlExceptionHelper.ThrowInvalidBinaryFormat(this.reader); } //compiler doesn't know that XmlExceptionHelper.ThrowInvalidBinaryFormat always throws, //so we have to have a return statement here even though we shouldn't hit this code path... Fx.Assert("A decimal value should have been returned or an exception should have been thrown."); return default(decimal); } public UniqueId GetUniqueId(int offset) { return new UniqueId(this.buffer, offset); } public Guid GetGuid(int offset) { if (guid == null) guid = new byte[16]; System.Buffer.BlockCopy(buffer, offset, guid, 0, guid.Length); return new Guid(guid); } public void GetBase64(int srcOffset, byte[] buffer, int dstOffset, int count) { System.Buffer.BlockCopy(this.buffer, srcOffset, buffer, dstOffset, count); } public XmlBinaryNodeType GetNodeType() { return (XmlBinaryNodeType)GetByte(); } public void SkipNodeType() { SkipByte(); } public object[] GetList(int offset, int count) { int bufferOffset = this.Offset; this.Offset = offset; try { object[] objects = new object[count]; for (int i = 0; i < count; i++) { XmlBinaryNodeType nodeType = GetNodeType(); SkipNodeType(); Fx.Assert(nodeType != XmlBinaryNodeType.StartListText, ""); ReadValue(nodeType, listValue); objects[i] = listValue.ToObject(); } return objects; } finally { this.Offset = bufferOffset; } } public XmlDictionaryString GetDictionaryString(int key) { IXmlDictionary keyDictionary; if ((key & 1) != 0) { keyDictionary = session; } else { keyDictionary = dictionary; } XmlDictionaryString s; if (!keyDictionary.TryLookup(key >> 1, out s)) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); return s; } public int ReadDictionaryKey() { int key = ReadMultiByteUInt31(); if ((key & 1) != 0) { if (session == null) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); int sessionKey = (key >> 1); XmlDictionaryString xmlString; if (!session.TryLookup(sessionKey, out xmlString)) { if (sessionKey < XmlDictionaryString.MinKey || sessionKey > XmlDictionaryString.MaxKey) XmlExceptionHelper.ThrowXmlDictionaryStringIDOutOfRange(this.reader); XmlExceptionHelper.ThrowXmlDictionaryStringIDUndefinedSession(this.reader, sessionKey); } } else { if (dictionary == null) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); int staticKey = (key >> 1); XmlDictionaryString xmlString; if (!dictionary.TryLookup(staticKey, out xmlString)) { if (staticKey < XmlDictionaryString.MinKey || staticKey > XmlDictionaryString.MaxKey) XmlExceptionHelper.ThrowXmlDictionaryStringIDOutOfRange(this.reader); XmlExceptionHelper.ThrowXmlDictionaryStringIDUndefinedStatic(this.reader, staticKey); } } return key; } public void ReadValue(XmlBinaryNodeType nodeType, ValueHandle value) { switch (nodeType) { case XmlBinaryNodeType.EmptyText: value.SetValue(ValueHandleType.Empty); break; case XmlBinaryNodeType.ZeroText: value.SetValue(ValueHandleType.Zero); break; case XmlBinaryNodeType.OneText: value.SetValue(ValueHandleType.One); break; case XmlBinaryNodeType.TrueText: value.SetValue(ValueHandleType.True); break; case XmlBinaryNodeType.FalseText: value.SetValue(ValueHandleType.False); break; case XmlBinaryNodeType.BoolText: value.SetValue(ReadUInt8() != 0 ? ValueHandleType.True : ValueHandleType.False); break; case XmlBinaryNodeType.Chars8Text: ReadValue(value, ValueHandleType.UTF8, ReadUInt8()); break; case XmlBinaryNodeType.Chars16Text: ReadValue(value, ValueHandleType.UTF8, ReadUInt16()); break; case XmlBinaryNodeType.Chars32Text: ReadValue(value, ValueHandleType.UTF8, ReadUInt31()); break; case XmlBinaryNodeType.UnicodeChars8Text: ReadUnicodeValue(value, ReadUInt8()); break; case XmlBinaryNodeType.UnicodeChars16Text: ReadUnicodeValue(value, ReadUInt16()); break; case XmlBinaryNodeType.UnicodeChars32Text: ReadUnicodeValue(value, ReadUInt31()); break; case XmlBinaryNodeType.Bytes8Text: ReadValue(value, ValueHandleType.Base64, ReadUInt8()); break; case XmlBinaryNodeType.Bytes16Text: ReadValue(value, ValueHandleType.Base64, ReadUInt16()); break; case XmlBinaryNodeType.Bytes32Text: ReadValue(value, ValueHandleType.Base64, ReadUInt31()); break; case XmlBinaryNodeType.DictionaryText: value.SetDictionaryValue(ReadDictionaryKey()); break; case XmlBinaryNodeType.UniqueIdText: ReadValue(value, ValueHandleType.UniqueId, ValueHandleLength.UniqueId); break; case XmlBinaryNodeType.GuidText: ReadValue(value, ValueHandleType.Guid, ValueHandleLength.Guid); break; case XmlBinaryNodeType.DecimalText: ReadValue(value, ValueHandleType.Decimal, ValueHandleLength.Decimal); break; case XmlBinaryNodeType.Int8Text: ReadValue(value, ValueHandleType.Int8, ValueHandleLength.Int8); break; case XmlBinaryNodeType.Int16Text: ReadValue(value, ValueHandleType.Int16, ValueHandleLength.Int16); break; case XmlBinaryNodeType.Int32Text: ReadValue(value, ValueHandleType.Int32, ValueHandleLength.Int32); break; case XmlBinaryNodeType.Int64Text: ReadValue(value, ValueHandleType.Int64, ValueHandleLength.Int64); break; case XmlBinaryNodeType.UInt64Text: ReadValue(value, ValueHandleType.UInt64, ValueHandleLength.UInt64); break; case XmlBinaryNodeType.FloatText: ReadValue(value, ValueHandleType.Single, ValueHandleLength.Single); break; case XmlBinaryNodeType.DoubleText: ReadValue(value, ValueHandleType.Double, ValueHandleLength.Double); break; case XmlBinaryNodeType.TimeSpanText: ReadValue(value, ValueHandleType.TimeSpan, ValueHandleLength.TimeSpan); break; case XmlBinaryNodeType.DateTimeText: ReadValue(value, ValueHandleType.DateTime, ValueHandleLength.DateTime); break; case XmlBinaryNodeType.StartListText: ReadList(value); break; case XmlBinaryNodeType.QNameDictionaryText: ReadQName(value); break; default: XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); break; } } void ReadValue(ValueHandle value, ValueHandleType type, int length) { int offset = ReadBytes(length); value.SetValue(type, offset, length); } void ReadUnicodeValue(ValueHandle value, int length) { if ((length & 1) != 0) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); ReadValue(value, ValueHandleType.Unicode, length); } void ReadList(ValueHandle value) { if (listValue == null) { listValue = new ValueHandle(this); } int count = 0; int offset = this.Offset; while (true) { XmlBinaryNodeType nodeType = GetNodeType(); SkipNodeType(); if (nodeType == XmlBinaryNodeType.StartListText) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); if (nodeType == XmlBinaryNodeType.EndListText) break; ReadValue(nodeType, listValue); count++; } value.SetValue(ValueHandleType.List, offset, count); } public void ReadQName(ValueHandle value) { int prefix = ReadUInt8(); if (prefix >= 26) XmlExceptionHelper.ThrowInvalidBinaryFormat(reader); int key = ReadDictionaryKey(); value.SetQNameValue(prefix, key); } public int[] GetRows() { if (buffer == null) { return new int[1] { 0 }; } ArrayList list = new ArrayList(); list.Add(offsetMin); for (int i = offsetMin; i < offsetMax; i++) { if (buffer[i] == (byte)13 || buffer[i] == (byte)10) { if (i + 1 < offsetMax && buffer[i + 1] == (byte)10) i++; list.Add(i + 1); } } return (int[])list.ToArray(typeof(int)); } } }