diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs index 07765d45883b7..aa3046d42f8c7 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.InteropServices; using System.Runtime.Serialization; namespace System.Xml @@ -1250,10 +1251,7 @@ private unsafe int ReadArray(bool[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - fixed (bool* items = &array[offset]) - { - BufferReader.UnsafeReadArray((byte*)items, (byte*)&items[actual]); - } + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); SkipArrayElements(actual); return actual; } @@ -1276,10 +1274,7 @@ private unsafe int ReadArray(short[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - fixed (short* items = &array[offset]) - { - BufferReader.UnsafeReadArray((byte*)items, (byte*)&items[actual]); - } + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); SkipArrayElements(actual); return actual; } @@ -1302,10 +1297,7 @@ private unsafe int ReadArray(int[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - fixed (int* items = &array[offset]) - { - BufferReader.UnsafeReadArray((byte*)items, (byte*)&items[actual]); - } + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); SkipArrayElements(actual); return actual; } @@ -1328,10 +1320,7 @@ private unsafe int ReadArray(long[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - fixed (long* items = &array[offset]) - { - BufferReader.UnsafeReadArray((byte*)items, (byte*)&items[actual]); - } + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); SkipArrayElements(actual); return actual; } @@ -1354,10 +1343,7 @@ private unsafe int ReadArray(float[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - fixed (float* items = &array[offset]) - { - BufferReader.UnsafeReadArray((byte*)items, (byte*)&items[actual]); - } + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); SkipArrayElements(actual); return actual; } @@ -1380,10 +1366,7 @@ private unsafe int ReadArray(double[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - fixed (double* items = &array[offset]) - { - BufferReader.UnsafeReadArray((byte*)items, (byte*)&items[actual]); - } + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); SkipArrayElements(actual); return actual; } @@ -1406,10 +1389,7 @@ private unsafe int ReadArray(decimal[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - fixed (decimal* items = &array[offset]) - { - BufferReader.UnsafeReadArray((byte*)items, (byte*)&items[actual]); - } + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); SkipArrayElements(actual); return actual; } @@ -1433,9 +1413,11 @@ private int ReadArray(DateTime[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - for (int i = 0; i < actual; i++) + // Try to read in whole array, but don't fail if not possible + BufferReader.GetBuffer(actual * ValueHandleLength.DateTime, out _, out _); + foreach (ref DateTime item in array.AsSpan(offset, actual)) { - array[offset + i] = BufferReader.ReadDateTime(); + item = BufferReader.ReadDateTime(); } SkipArrayElements(actual); return actual; @@ -1460,9 +1442,18 @@ private int ReadArray(Guid[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - for (int i = 0; i < actual; i++) + if (BitConverter.IsLittleEndian) { - array[offset + i] = BufferReader.ReadGuid(); + BufferReader.ReadRawArrayBytes(array.AsSpan(offset, actual)); + } + else + { + // Try to read in whole array, but don't fail if not possible + BufferReader.GetBuffer(actual * ValueHandleLength.Guid, out _, out _); + foreach (ref Guid item in array.AsSpan(offset, actual)) + { + item = BufferReader.ReadGuid(); + } } SkipArrayElements(actual); return actual; @@ -1487,9 +1478,11 @@ private int ReadArray(TimeSpan[] array, int offset, int count) { CheckArray(array, offset, count); int actual = Math.Min(count, _arrayCount); - for (int i = 0; i < actual; i++) + // Try to read in whole array, but don't fail if not possible + BufferReader.GetBuffer(actual * ValueHandleLength.TimeSpan, out _, out _); + foreach (ref TimeSpan item in array.AsSpan(offset, actual)) { - array[offset + i] = BufferReader.ReadTimeSpan(); + item = BufferReader.ReadTimeSpan(); } SkipArrayElements(actual); return actual; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs index 2ad5b6cdcabc9..f193d1815cd59 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs @@ -12,6 +12,7 @@ using System.Runtime.Serialization; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Runtime.CompilerServices; using System.Buffers.Binary; namespace System.Xml @@ -26,7 +27,6 @@ internal sealed class XmlBufferReader private int _offsetMax; private IXmlDictionary? _dictionary; private XmlBinaryReaderSession? _session; - private byte[]? _guid; private int _offset; private const int maxBytesPerChar = 3; private char[]? _chars; @@ -350,35 +350,16 @@ public int ReadUInt8() } public int ReadInt8() - { - return (sbyte)ReadUInt8(); - } + => (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; - } + => BitConverter.IsLittleEndian ? ReadRawBytes() : BinaryPrimitives.ReverseEndianness(ReadRawBytes()); public int ReadInt16() - { - return (short)ReadUInt16(); - } + => (short)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; - } + => BitConverter.IsLittleEndian ? ReadRawBytes() : BinaryPrimitives.ReverseEndianness(ReadRawBytes()); public int ReadUInt31() { @@ -389,59 +370,35 @@ public int ReadUInt31() } public long ReadInt64() - { - long lo = (uint)ReadInt32(); - long hi = (uint)ReadInt32(); - return (hi << 32) + lo; - } + => BitConverter.IsLittleEndian ? ReadRawBytes() : BinaryPrimitives.ReverseEndianness(ReadRawBytes()); - public unsafe float ReadSingle() - { - int offset; - byte[] buffer = GetBuffer(ValueHandleLength.Single, out offset); - float value; - byte* pb = (byte*)&value; - DiagnosticUtility.DebugAssert(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; - } + public float ReadSingle() + => ReadRawBytes(); - public unsafe double ReadDouble() - { - int offset; - byte[] buffer = GetBuffer(ValueHandleLength.Double, out offset); - double value; - byte* pb = (byte*)&value; - DiagnosticUtility.DebugAssert(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; - } + public double ReadDouble() + => ReadRawBytes(); public decimal ReadDecimal() { - byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out int offset); - ReadOnlySpan bytes = buffer.AsSpan(offset, sizeof(decimal)); - ReadOnlySpan span = stackalloc int[4] + if (BitConverter.IsLittleEndian) { - BinaryPrimitives.ReadInt32LittleEndian(bytes), - BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4)), - BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8)), - BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12)) - }; - - return new decimal(span); + return ReadRawBytes(); + } + else + { + byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out int offset); + ReadOnlySpan bytes = buffer.AsSpan(offset, sizeof(decimal)); + ReadOnlySpan span = stackalloc int[4] + { + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8, 4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12, 4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4, 4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(0, 4)) + }; + + Advance(ValueHandleLength.Decimal); + return new decimal(span); + } } public UniqueId ReadUniqueId() @@ -517,43 +474,21 @@ public string ReadUTF8String(int length) return value; } - public unsafe void UnsafeReadArray(byte* dst, byte* dstMax) + public void ReadRawArrayBytes(Span dst) + where T : unmanaged { - UnsafeReadArray(dst, (int)(dstMax - dst)); + ReadRawArrayBytes(MemoryMarshal.AsBytes(dst)); } - private unsafe void UnsafeReadArray(byte* dst, int length) + public void ReadRawArrayBytes(Span dst) { - if (_stream != null) + if (dst.Length > 0) { - 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; - } - } + GetBuffer(dst.Length, out _offset) + .AsSpan(_offset, dst.Length) + .CopyTo(dst); - 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); + Advance(dst.Length); } } @@ -1003,99 +938,63 @@ public int GetInt8(int offset) return (sbyte)GetByte(offset); } - public int GetInt16(int offset) + private T ReadRawBytes() where T : unmanaged { - byte[] buffer = _buffer; - return (short)(buffer[offset] + (buffer[offset + 1] << 8)); + ReadOnlySpan buffer = GetBuffer(Unsafe.SizeOf(), out int offset) + .AsSpan(offset, Unsafe.SizeOf()); + T value = MemoryMarshal.Read(buffer); + + Advance(Unsafe.SizeOf()); + return value; } + private T ReadRawBytes(int offset) where T : unmanaged + => MemoryMarshal.Read(_buffer.AsSpan(offset, Unsafe.SizeOf())); + + public int GetInt16(int offset) + => BitConverter.IsLittleEndian ? ReadRawBytes(offset) : BinaryPrimitives.ReverseEndianness(ReadRawBytes(offset)); + public int GetInt32(int offset) - { - byte[] buffer = _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; - } + => BitConverter.IsLittleEndian ? ReadRawBytes(offset) : BinaryPrimitives.ReverseEndianness(ReadRawBytes(offset)); public long GetInt64(int offset) - { - byte[] buffer = _buffer; - byte b1, b2, b3, b4; - b1 = buffer[offset + 0]; - b2 = buffer[offset + 1]; - b3 = buffer[offset + 2]; - b4 = buffer[offset + 3]; - long lo = (uint)(((((b4 << 8) + b3) << 8) + b2) << 8) + b1; - b1 = buffer[offset + 4]; - b2 = buffer[offset + 5]; - b3 = buffer[offset + 6]; - b4 = buffer[offset + 7]; - long hi = (uint)(((((b4 << 8) + b3) << 8) + b2) << 8) + b1; - return (hi << 32) + lo; - } + => BitConverter.IsLittleEndian ? ReadRawBytes(offset) : BinaryPrimitives.ReverseEndianness(ReadRawBytes(offset)); public ulong GetUInt64(int offset) - { - return (ulong)GetInt64(offset); - } + => (ulong)GetInt64(offset); - public unsafe float GetSingle(int offset) - { - byte[] buffer = _buffer; - float value; - byte* pb = (byte*)&value; - DiagnosticUtility.DebugAssert(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; - } + public float GetSingle(int offset) + => ReadRawBytes(offset); - public unsafe double GetDouble(int offset) - { - byte[] buffer = _buffer; - double value; - byte* pb = (byte*)&value; - DiagnosticUtility.DebugAssert(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; - } + public double GetDouble(int offset) + => ReadRawBytes(offset); public decimal GetDecimal(int offset) { - ReadOnlySpan bytes = _buffer.AsSpan(offset, sizeof(decimal)); - ReadOnlySpan span = stackalloc int[4] + if (BitConverter.IsLittleEndian) { - BinaryPrimitives.ReadInt32LittleEndian(bytes), - BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4)), - BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8)), - BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12)) - }; + return ReadRawBytes(offset); + } + else + { + ReadOnlySpan bytes = _buffer.AsSpan(offset, sizeof(decimal)); + ReadOnlySpan span = stackalloc int[4] + { + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(8, 4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(12, 4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(4, 4)), + BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(0, 4)) + }; - return new decimal(span); + return new decimal(span); + } } public UniqueId GetUniqueId(int offset) - { - return new UniqueId(_buffer, offset); - } + => new UniqueId(_buffer, offset); public Guid GetGuid(int offset) - { - _guid ??= new byte[16]; - System.Buffer.BlockCopy(_buffer, offset, _guid, 0, _guid.Length); - return new Guid(_guid); - } + => new Guid(_buffer.AsSpan(offset, ValueHandleLength.Guid)); public void GetBase64(int srcOffset, byte[] buffer, int dstOffset, int count) { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj index f72418e8eebfb..bdc0d2c86c22c 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj @@ -44,7 +44,8 @@ Link="SerializationTestTypes\SampleTypes.cs" /> - + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj index ce77b694a8833..1628ec4ae6ebd 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj @@ -47,6 +47,7 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Xml/XmlBinaryNodeType.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Xml/XmlBinaryNodeType.cs new file mode 100644 index 0000000000000..bc26002661e35 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Xml/XmlBinaryNodeType.cs @@ -0,0 +1,200 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Xml +{ + // This file is a decompiled "snapshot" based on actual file in System.Private.DataContractSerialization + // All members are assigned constants so that accidental changes to original will not be reflected here + internal enum XmlBinaryNodeType : byte + { + EndElement = 1, + Comment = 2, + Array = 3, + MinAttribute = 4, + ShortAttribute = 4, + Attribute = 5, + ShortDictionaryAttribute = 6, + DictionaryAttribute = 7, + ShortXmlnsAttribute = 8, + XmlnsAttribute = 9, + ShortDictionaryXmlnsAttribute = 10, + DictionaryXmlnsAttribute = 11, + PrefixDictionaryAttributeA = 12, + PrefixDictionaryAttributeB = 13, + PrefixDictionaryAttributeC = 14, + PrefixDictionaryAttributeD = 15, + PrefixDictionaryAttributeE = 16, + PrefixDictionaryAttributeF = 17, + PrefixDictionaryAttributeG = 18, + PrefixDictionaryAttributeH = 19, + PrefixDictionaryAttributeI = 20, + PrefixDictionaryAttributeJ = 21, + PrefixDictionaryAttributeK = 22, + PrefixDictionaryAttributeL = 23, + PrefixDictionaryAttributeM = 24, + PrefixDictionaryAttributeN = 25, + PrefixDictionaryAttributeO = 26, + PrefixDictionaryAttributeP = 27, + PrefixDictionaryAttributeQ = 28, + PrefixDictionaryAttributeR = 29, + PrefixDictionaryAttributeS = 30, + PrefixDictionaryAttributeT = 31, + PrefixDictionaryAttributeU = 32, + PrefixDictionaryAttributeV = 33, + PrefixDictionaryAttributeW = 34, + PrefixDictionaryAttributeX = 35, + PrefixDictionaryAttributeY = 36, + PrefixDictionaryAttributeZ = 37, + PrefixAttributeA = 38, + PrefixAttributeB = 39, + PrefixAttributeC = 40, + PrefixAttributeD = 41, + PrefixAttributeE = 42, + PrefixAttributeF = 43, + PrefixAttributeG = 44, + PrefixAttributeH = 45, + PrefixAttributeI = 46, + PrefixAttributeJ = 47, + PrefixAttributeK = 48, + PrefixAttributeL = 49, + PrefixAttributeM = 50, + PrefixAttributeN = 51, + PrefixAttributeO = 52, + PrefixAttributeP = 53, + PrefixAttributeQ = 54, + PrefixAttributeR = 55, + PrefixAttributeS = 56, + PrefixAttributeT = 57, + PrefixAttributeU = 58, + PrefixAttributeV = 59, + PrefixAttributeW = 60, + PrefixAttributeX = 61, + PrefixAttributeY = 62, + MaxAttribute = 63, + PrefixAttributeZ = 63, + MinElement = 64, + ShortElement = 64, + Element = 65, + ShortDictionaryElement = 66, + DictionaryElement = 67, + PrefixDictionaryElementA = 68, + PrefixDictionaryElementB = 69, + PrefixDictionaryElementC = 70, + PrefixDictionaryElementD = 71, + PrefixDictionaryElementE = 72, + PrefixDictionaryElementF = 73, + PrefixDictionaryElementG = 74, + PrefixDictionaryElementH = 75, + PrefixDictionaryElementI = 76, + PrefixDictionaryElementJ = 77, + PrefixDictionaryElementK = 78, + PrefixDictionaryElementL = 79, + PrefixDictionaryElementM = 80, + PrefixDictionaryElementN = 81, + PrefixDictionaryElementO = 82, + PrefixDictionaryElementP = 83, + PrefixDictionaryElementQ = 84, + PrefixDictionaryElementR = 85, + PrefixDictionaryElementS = 86, + PrefixDictionaryElementT = 87, + PrefixDictionaryElementU = 88, + PrefixDictionaryElementV = 89, + PrefixDictionaryElementW = 90, + PrefixDictionaryElementX = 91, + PrefixDictionaryElementY = 92, + PrefixDictionaryElementZ = 93, + PrefixElementA = 94, + PrefixElementB = 95, + PrefixElementC = 96, + PrefixElementD = 97, + PrefixElementE = 98, + PrefixElementF = 99, + PrefixElementG = 100, + PrefixElementH = 101, + PrefixElementI = 102, + PrefixElementJ = 103, + PrefixElementK = 104, + PrefixElementL = 105, + PrefixElementM = 106, + PrefixElementN = 107, + PrefixElementO = 108, + PrefixElementP = 109, + PrefixElementQ = 110, + PrefixElementR = 111, + PrefixElementS = 112, + PrefixElementT = 113, + PrefixElementU = 114, + PrefixElementV = 115, + PrefixElementW = 116, + PrefixElementX = 117, + PrefixElementY = 118, + MaxElement = 119, + PrefixElementZ = 119, + MinText = 128, + ZeroText = 128, + ZeroTextWithEndElement = 129, + OneText = 130, + OneTextWithEndElement = 131, + FalseText = 132, + FalseTextWithEndElement = 133, + TrueText = 134, + TrueTextWithEndElement = 135, + Int8Text = 136, + Int8TextWithEndElement = 137, + Int16Text = 138, + Int16TextWithEndElement = 139, + Int32Text = 140, + Int32TextWithEndElement = 141, + Int64Text = 142, + Int64TextWithEndElement = 143, + FloatText = 144, + FloatTextWithEndElement = 145, + DoubleText = 146, + DoubleTextWithEndElement = 147, + DecimalText = 148, + DecimalTextWithEndElement = 149, + DateTimeText = 150, + DateTimeTextWithEndElement = 151, + Chars8Text = 152, + Chars8TextWithEndElement = 153, + Chars16Text = 154, + Chars16TextWithEndElement = 155, + Chars32Text = 156, + Chars32TextWithEndElement = 157, + Bytes8Text = 158, + Bytes8TextWithEndElement = 159, + Bytes16Text = 160, + Bytes16TextWithEndElement = 161, + Bytes32Text = 162, + Bytes32TextWithEndElement = 163, + StartListText = 164, + StartListTextWithEndElement = 165, + EndListText = 166, + EndListTextWithEndElement = 167, + EmptyText = 168, + EmptyTextWithEndElement = 169, + DictionaryText = 170, + DictionaryTextWithEndElement = 171, + UniqueIdText = 172, + UniqueIdTextWithEndElement = 173, + TimeSpanText = 174, + TimeSpanTextWithEndElement = 175, + GuidText = 176, + GuidTextWithEndElement = 177, + UInt64Text = 178, + UInt64TextWithEndElement = 179, + BoolText = 180, + BoolTextWithEndElement = 181, + UnicodeChars8Text = 182, + UnicodeChars8TextWithEndElement = 183, + UnicodeChars16Text = 184, + UnicodeChars16TextWithEndElement = 185, + UnicodeChars32Text = 186, + UnicodeChars32TextWithEndElement = 187, + QNameDictionaryText = 188, + MaxText = 189, + QNameDictionaryTextWithEndElement = 189 + } +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs index cd34fceb0fb4f..e15f9fc2df544 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Xml; using Xunit; @@ -161,6 +163,105 @@ public static void ReadStringTest() Assert.Equal(value, s); } } + + [Fact] + public static void BinaryXml_ReadPrimitiveTypes() + { + float f = 1.23456788f; + ReadOnlySpan floatBytes = new byte[] { 0x52, 0x06, 0x9e, 0x3f }; + Guid guid = new Guid(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + + AssertReadContentFromBinary(long.MaxValue, XmlBinaryNodeType.Int64TextWithEndElement, new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }); + + AssertReadContentFromBinary((byte)0x78, XmlBinaryNodeType.Int8Text, new byte[] { 0x78 }); + AssertReadContentFromBinary((short)0x1234, XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0x12 }); + AssertReadContentFromBinary(unchecked((short)0xf234), XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0xf2 }); + AssertReadContentFromBinary((int)0x12345678, XmlBinaryNodeType.Int32Text, new byte[] { 0x78, 0x56, 0x34, 0x12 }); + AssertReadContentFromBinary((long)0x0102030412345678, XmlBinaryNodeType.Int64Text, new byte[] { 0x78, 0x56, 0x34, 0x12, 04, 03, 02, 01 }); + + // Integer values should be represented using smalles possible type + AssertReadContentFromBinary((long)0, XmlBinaryNodeType.ZeroText, ReadOnlySpan.Empty); + AssertReadContentFromBinary((long)1, XmlBinaryNodeType.OneText, ReadOnlySpan.Empty); + AssertReadContentFromBinary((int)0x00000078, XmlBinaryNodeType.Int8Text, new byte[] { 0x78 }); + AssertReadContentFromBinary(unchecked((int)0xfffffff0), XmlBinaryNodeType.Int8Text, new byte[] { 0xf0 }); + AssertReadContentFromBinary((int)0x00001234, XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0x12 }); + AssertReadContentFromBinary(unchecked((int)0xfffff234), XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0xf2 }); + AssertReadContentFromBinary((long)0x12345678, XmlBinaryNodeType.Int32Text, new byte[] { 0x78, 0x56, 0x34, 0x12 }); + AssertReadContentFromBinary(unchecked((long)0xfffffffff2345678), XmlBinaryNodeType.Int32Text, new byte[] { 0x78, 0x56, 0x34, 0xf2 }); + + AssertReadContentFromBinary(f, XmlBinaryNodeType.FloatText, floatBytes); + AssertReadContentFromBinary(8.20788039913184E-304, XmlBinaryNodeType.DoubleText, new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 }); + AssertReadContentFromBinary(guid, XmlBinaryNodeType.GuidText, guid.ToByteArray()); + AssertReadContentFromBinary(new TimeSpan(0x0807060504030201), XmlBinaryNodeType.TimeSpanText, new byte[] { 01, 02, 03, 04, 05, 06, 07, 08 }); + AssertReadContentFromBinary(new decimal(0x20212223, 0x10111213, 0x01020304, true, scale: 0x1b), XmlBinaryNodeType.DecimalText + , new byte[] { 0x0, 0x0, 0x1b, 0x80, 0x4, 0x3, 0x2, 0x1, 0x23, 0x22, 0x21, 0x20, 0x13, 0x12, 0x11, 0x10 }); + + // Double can be represented as float or inte as long as no detail is lost + AssertReadContentFromBinary((double)0x0100, XmlBinaryNodeType.Int16Text, new byte[] { 0x00, 0x01 }); + AssertReadContentFromBinary((double)f, XmlBinaryNodeType.FloatText, floatBytes); + } + + [Fact] + public static void BinaryXml_Array_RoundTrip() + { + int[] ints = new int[] { -1, 0x01020304, 0x11223344, -1 }; + TimeSpan[] timespans = new[] { TimeSpan.FromTicks(0x0102030405060708), TimeSpan.FromTicks(0x1011121314151617) }; + // Write more than 4 kb in a single call to ensure we hit path for reading (and writing happens on 512b) large arrays + long[] longs = Enumerable.Range(0x01020304, 513).Select(i => (long)i | (long)(~i << 32)).ToArray(); + Guid[] guids = new[] { + new Guid(new ReadOnlySpan(new byte[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 })), + new Guid(new ReadOnlySpan(new byte[] {10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160 })) + }; + + using var ms = new MemoryStream(); + using var writer = XmlDictionaryWriter.CreateBinaryWriter(ms); + writer.WriteStartElement("root"); + writer.WriteArray(null, "ints", null, ints, 1, 2); + writer.WriteArray(null, "timespans", null, timespans, 0, timespans.Length); + writer.WriteArray(null, "longs", null, longs, 0, longs.Length); + writer.WriteArray(null, "guids", null, guids, 0, guids.Length); + writer.WriteEndElement(); + writer.WriteEndDocument(); + writer.Flush(); + ms.Seek(0, SeekOrigin.Begin); + + int[] actualInts = new int[] { -1, -1, -1, -1 }; + + using var reader = XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max); + reader.ReadStartElement("root"); + int intsRead = reader.ReadArray("ints", string.Empty, actualInts, 1, 3); + TimeSpan[] actualTimeSpans = reader.ReadTimeSpanArray("timespans", string.Empty); + long[] actualLongs = reader.ReadInt64Array("longs", string.Empty); + Guid[] actualGuids = reader.ReadGuidArray("guids", string.Empty); + reader.ReadEndElement(); + + Assert.Equal(XmlNodeType.None, reader.NodeType); // Should be at end + + Assert.Equal(2, intsRead); + AssertExtensions.SequenceEqual(ints, actualInts); + AssertExtensions.SequenceEqual(actualLongs, longs); + AssertExtensions.SequenceEqual(actualTimeSpans, timespans); + AssertExtensions.SequenceEqual(actualGuids, guids); + } + + private static void AssertReadContentFromBinary(T expected, XmlBinaryNodeType nodeType, ReadOnlySpan bytes) + { + ReadOnlySpan documentStart = new byte[] { 0x40, 0x1, 0x61 }; // start node "a" + MemoryStream ms = new MemoryStream(documentStart.Length + 1 + bytes.Length); + ms.Write(documentStart); + ms.WriteByte((byte)(nodeType | XmlBinaryNodeType.EndElement)); // With EndElement + ms.Write(bytes); + ms.Seek(0, SeekOrigin.Begin); + XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max); + reader.ReadStartElement("a"); + T result = (T)reader.ReadContentAs(typeof(T), null); + reader.ReadEndElement(); + + Assert.True(ms.Position == ms.Length, "whole buffer should have been consumed"); + Assert.True(XmlNodeType.None == reader.NodeType, "XmlDictionaryReader should be at end of document"); + Assert.Equal(expected, result); + } + private static Stream GenerateStreamFromString(string s) { var stream = new MemoryStream();