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 4505fb2d23612e..f81d5cfa08591e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs @@ -1346,14 +1346,14 @@ private unsafe int ReadArray(float[] array, int offset, int count) public override int ReadArray(string localName, string namespaceUri, float[] array, int offset, int count) { - if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.FloatTextWithEndElement)) + if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.FloatTextWithEndElement) && BitConverter.IsLittleEndian) return ReadArray(array, offset, count); return base.ReadArray(localName, namespaceUri, array, offset, count); } public override int ReadArray(XmlDictionaryString localName, XmlDictionaryString namespaceUri, float[] array, int offset, int count) { - if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.FloatTextWithEndElement)) + if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.FloatTextWithEndElement) && BitConverter.IsLittleEndian) return ReadArray(array, offset, count); return base.ReadArray(localName, namespaceUri, array, offset, count); } @@ -1372,14 +1372,14 @@ private unsafe int ReadArray(double[] array, int offset, int count) public override int ReadArray(string localName, string namespaceUri, double[] array, int offset, int count) { - if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DoubleTextWithEndElement)) + if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DoubleTextWithEndElement) && BitConverter.IsLittleEndian) return ReadArray(array, offset, count); return base.ReadArray(localName, namespaceUri, array, offset, count); } public override int ReadArray(XmlDictionaryString localName, XmlDictionaryString namespaceUri, double[] array, int offset, int count) { - if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DoubleTextWithEndElement)) + if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DoubleTextWithEndElement) && BitConverter.IsLittleEndian) return ReadArray(array, offset, count); return base.ReadArray(localName, namespaceUri, array, offset, count); } @@ -1398,14 +1398,14 @@ private unsafe int ReadArray(decimal[] array, int offset, int count) public override int ReadArray(string localName, string namespaceUri, decimal[] array, int offset, int count) { - if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DecimalTextWithEndElement)) + if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DecimalTextWithEndElement) && BitConverter.IsLittleEndian) return ReadArray(array, offset, count); return base.ReadArray(localName, namespaceUri, array, offset, count); } public override int ReadArray(XmlDictionaryString localName, XmlDictionaryString namespaceUri, decimal[] array, int offset, int count) { - if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DecimalTextWithEndElement)) + if (IsStartArray(localName, namespaceUri, XmlBinaryNodeType.DecimalTextWithEndElement) && BitConverter.IsLittleEndian) return ReadArray(array, offset, count); return base.ReadArray(localName, namespaceUri, array, offset, count); } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs index 53a9d45ffce18f..3255cb4dd06e6e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs @@ -12,6 +12,7 @@ using System.Runtime.Serialization; using System.Globalization; using System.Collections.Generic; +using System.Buffers.Binary; namespace System.Xml { @@ -757,12 +758,8 @@ public unsafe override void WriteFloatText(float f) { int offset; byte[] buffer = GetTextNodeBuffer(1 + sizeof(float), out offset); - byte* bytes = (byte*)&f; - buffer[offset + 0] = (byte)XmlBinaryNodeType.FloatText; - buffer[offset + 1] = bytes[0]; - buffer[offset + 2] = bytes[1]; - buffer[offset + 3] = bytes[2]; - buffer[offset + 4] = bytes[3]; + buffer[offset] = (byte)XmlBinaryNodeType.FloatText; + BinaryPrimitives.WriteSingleLittleEndian(buffer.AsSpan(offset + 1, sizeof(float)), f); Advance(1 + sizeof(float)); } } @@ -778,16 +775,8 @@ public unsafe override void WriteDoubleText(double d) { int offset; byte[] buffer = GetTextNodeBuffer(1 + sizeof(double), out offset); - byte* bytes = (byte*)&d; - buffer[offset + 0] = (byte)XmlBinaryNodeType.DoubleText; - buffer[offset + 1] = bytes[0]; - buffer[offset + 2] = bytes[1]; - buffer[offset + 3] = bytes[2]; - buffer[offset + 4] = bytes[3]; - buffer[offset + 5] = bytes[4]; - buffer[offset + 6] = bytes[5]; - buffer[offset + 7] = bytes[6]; - buffer[offset + 8] = bytes[7]; + buffer[offset] = (byte)XmlBinaryNodeType.DoubleText; + BinaryPrimitives.WriteDoubleLittleEndian(buffer.AsSpan(offset + 1, sizeof(double)), d); Advance(1 + sizeof(double)); } } @@ -798,9 +787,24 @@ public unsafe override void WriteDecimalText(decimal d) byte[] buffer = GetTextNodeBuffer(1 + sizeof(decimal), out offset); byte* bytes = (byte*)&d; buffer[offset++] = (byte)XmlBinaryNodeType.DecimalText; - for (int i = 0; i < sizeof(decimal); i++) + if (BitConverter.IsLittleEndian) { - buffer[offset++] = bytes[i]; + for (int i = 0; i < sizeof(decimal); i++) + { + buffer[offset++] = bytes[i]; + } + } + else + { + Span bits = stackalloc int[4]; + decimal.TryGetBits(d, bits, out int intsWritten); + Debug.Assert(intsWritten == 4); + + Span span = buffer.AsSpan(offset, sizeof(decimal)); + BinaryPrimitives.WriteInt32LittleEndian(span, bits[3]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(4), bits[2]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(8), bits[0]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(12), bits[1]); } Advance(1 + sizeof(decimal)); } @@ -890,15 +894,146 @@ private void WriteArrayInfo(XmlBinaryNodeType nodeType, int count) WriteMultiByteInt32(count); } - public unsafe void UnsafeWriteArray(XmlBinaryNodeType nodeType, int count, byte* array, byte* arrayMax) + public unsafe void UnsafeWriteBoolArray(bool[] array, int offset, int count) + { + WriteArrayInfo(XmlBinaryNodeType.BoolTextWithEndElement, count); + fixed (bool* items = &array[offset]) + { + base.UnsafeWriteBytes((byte*)items, count); + } + } + + public unsafe void UnsafeWriteInt16Array(short[] array, int offset, int count) + { + WriteArrayInfo(XmlBinaryNodeType.Int16TextWithEndElement, count); + if (BitConverter.IsLittleEndian) + { + fixed (short* items = &array[offset]) + { + base.UnsafeWriteBytes((byte*)items, sizeof(short) * count); + } + } + else + { + for (int i = 0; i < count; i++) + { + Span span = GetBuffer(sizeof(short), out int bufferOffset).AsSpan(bufferOffset, sizeof(short)); + BinaryPrimitives.WriteInt16LittleEndian(span, array[offset + i]); + Advance(sizeof(short)); + } + } + } + + public unsafe void UnsafeWriteInt32Array(int[] array, int offset, int count) + { + WriteArrayInfo(XmlBinaryNodeType.Int32TextWithEndElement, count); + if (BitConverter.IsLittleEndian) + { + fixed (int* items = &array[offset]) + { + base.UnsafeWriteBytes((byte*)items, sizeof(int) * count); + } + } + else + { + for (int i = 0; i < count; i++) + { + Span span = GetBuffer(sizeof(int), out int bufferOffset).AsSpan(bufferOffset, sizeof(int)); + BinaryPrimitives.WriteInt32LittleEndian(span, array[offset + i]); + Advance(sizeof(int)); + } + } + } + + public unsafe void UnsafeWriteInt64Array(long[] array, int offset, int count) { - WriteArrayInfo(nodeType, count); - UnsafeWriteArray(array, (int)(arrayMax - array)); + WriteArrayInfo(XmlBinaryNodeType.Int64TextWithEndElement, count); + if (BitConverter.IsLittleEndian) + { + fixed (long* items = &array[offset]) + { + base.UnsafeWriteBytes((byte*)items, sizeof(long) * count); + } + } + else + { + for (int i = 0; i < count; i++) + { + Span span = GetBuffer(sizeof(long), out int bufferOffset).AsSpan(bufferOffset, sizeof(long)); + BinaryPrimitives.WriteInt64LittleEndian(span, array[offset + i]); + Advance(sizeof(long)); + } + } } - private unsafe void UnsafeWriteArray(byte* array, int byteCount) + public unsafe void UnsafeWriteFloatArray(float[] array, int offset, int count) { - base.UnsafeWriteBytes(array, byteCount); + WriteArrayInfo(XmlBinaryNodeType.FloatTextWithEndElement, count); + if (BitConverter.IsLittleEndian) + { + fixed (float* items = &array[offset]) + { + base.UnsafeWriteBytes((byte*)items, sizeof(float) * count); + } + } + else + { + for (int i = 0; i < count; i++) + { + Span span = GetBuffer(sizeof(float), out int bufferOffset).AsSpan(bufferOffset, sizeof(float)); + BinaryPrimitives.WriteSingleLittleEndian(span, array[offset + i]); + Advance(sizeof(float)); + } + } + } + + public unsafe void UnsafeWriteDoubleArray(double[] array, int offset, int count) + { + WriteArrayInfo(XmlBinaryNodeType.DoubleTextWithEndElement, count); + if (BitConverter.IsLittleEndian) + { + fixed (double* items = &array[offset]) + { + base.UnsafeWriteBytes((byte*)items, sizeof(double) * count); + } + } + else + { + for (int i = 0; i < count; i++) + { + Span span = GetBuffer(sizeof(double), out int bufferOffset).AsSpan(bufferOffset, sizeof(double)); + BinaryPrimitives.WriteDoubleLittleEndian(span, array[offset + i]); + Advance(sizeof(double)); + } + } + } + + public unsafe void UnsafeWriteDecimalArray(decimal[] array, int offset, int count) + { + WriteArrayInfo(XmlBinaryNodeType.DecimalTextWithEndElement, count); + if (BitConverter.IsLittleEndian) + { + fixed (decimal* items = &array[offset]) + { + base.UnsafeWriteBytes((byte*)items, sizeof(decimal) * count); + } + } + else + { + Span bits = stackalloc int[4]; + for (int i = 0; i < count; i++) + { + decimal.TryGetBits(array[offset + i], bits, out int intsWritten); + Debug.Assert(intsWritten == 4); + + Span span = GetBuffer(16, out int bufferOffset).AsSpan(bufferOffset, 16); + BinaryPrimitives.WriteInt32LittleEndian(span, bits[3]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(4), bits[2]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(8), bits[0]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(12), bits[1]); + Advance(16); + } + } } public void WriteDateTimeArray(DateTime[] array, int offset, int count) @@ -1193,22 +1328,6 @@ private void WriteEndArray() EndArray(); } - private unsafe void UnsafeWriteArray(string? prefix, string localName, string? namespaceUri, - XmlBinaryNodeType nodeType, int count, byte* array, byte* arrayMax) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteArray(nodeType, count, array, arrayMax); - WriteEndArray(); - } - - private unsafe void UnsafeWriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, - XmlBinaryNodeType nodeType, int count, byte* array, byte* arrayMax) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteArray(nodeType, count, array, arrayMax); - WriteEndArray(); - } - private void CheckArray(Array array, int offset, int count) { if (array == null) @@ -1229,10 +1348,9 @@ public unsafe override void WriteArray(string? prefix, string localName, string? CheckArray(array, offset, count); if (count > 0) { - fixed (bool* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.BoolTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteBoolArray(array, offset, count); + WriteEndArray(); } } } @@ -1243,10 +1361,9 @@ public unsafe override void WriteArray(string? prefix, XmlDictionaryString local CheckArray(array, offset, count); if (count > 0) { - fixed (bool* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.BoolTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteBoolArray(array, offset, count); + WriteEndArray(); } } } @@ -1257,10 +1374,9 @@ public unsafe override void WriteArray(string? prefix, string localName, string? CheckArray(array, offset, count); if (count > 0) { - fixed (short* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.Int16TextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteInt16Array(array, offset, count); + WriteEndArray(); } } } @@ -1271,10 +1387,9 @@ public unsafe override void WriteArray(string? prefix, XmlDictionaryString local CheckArray(array, offset, count); if (count > 0) { - fixed (short* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.Int16TextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteInt16Array(array, offset, count); + WriteEndArray(); } } } @@ -1285,10 +1400,9 @@ public unsafe override void WriteArray(string? prefix, string localName, string? CheckArray(array, offset, count); if (count > 0) { - fixed (int* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.Int32TextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteInt32Array(array, offset, count); + WriteEndArray(); } } } @@ -1299,10 +1413,9 @@ public unsafe override void WriteArray(string? prefix, XmlDictionaryString local CheckArray(array, offset, count); if (count > 0) { - fixed (int* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.Int32TextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteInt32Array(array, offset, count); + WriteEndArray(); } } } @@ -1313,10 +1426,9 @@ public unsafe override void WriteArray(string? prefix, string localName, string? CheckArray(array, offset, count); if (count > 0) { - fixed (long* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.Int64TextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteInt64Array(array, offset, count); + WriteEndArray(); } } } @@ -1327,10 +1439,9 @@ public unsafe override void WriteArray(string? prefix, XmlDictionaryString local CheckArray(array, offset, count); if (count > 0) { - fixed (long* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.Int64TextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteInt64Array(array, offset, count); + WriteEndArray(); } } } @@ -1341,10 +1452,9 @@ public unsafe override void WriteArray(string? prefix, string localName, string? CheckArray(array, offset, count); if (count > 0) { - fixed (float* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.FloatTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteFloatArray(array, offset, count); + WriteEndArray(); } } } @@ -1355,10 +1465,9 @@ public unsafe override void WriteArray(string? prefix, XmlDictionaryString local CheckArray(array, offset, count); if (count > 0) { - fixed (float* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.FloatTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteFloatArray(array, offset, count); + WriteEndArray(); } } } @@ -1369,10 +1478,9 @@ public unsafe override void WriteArray(string? prefix, string localName, string? CheckArray(array, offset, count); if (count > 0) { - fixed (double* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.DoubleTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteDoubleArray(array, offset, count); + WriteEndArray(); } } } @@ -1383,10 +1491,9 @@ public unsafe override void WriteArray(string? prefix, XmlDictionaryString local CheckArray(array, offset, count); if (count > 0) { - fixed (double* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.DoubleTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteDoubleArray(array, offset, count); + WriteEndArray(); } } } @@ -1397,10 +1504,9 @@ public unsafe override void WriteArray(string? prefix, string localName, string? CheckArray(array, offset, count); if (count > 0) { - fixed (decimal* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.DecimalTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteDecimalArray(array, offset, count); + WriteEndArray(); } } } @@ -1411,10 +1517,9 @@ public unsafe override void WriteArray(string? prefix, XmlDictionaryString local CheckArray(array, offset, count); if (count > 0) { - fixed (decimal* items = &array[offset]) - { - UnsafeWriteArray(prefix, localName, namespaceUri, XmlBinaryNodeType.DecimalTextWithEndElement, count, (byte*)items, (byte*)&items[count]); - } + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.UnsafeWriteDecimalArray(array, offset, count); + WriteEndArray(); } } } 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 dc124955be3bd8..a7c7827c4b03b0 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.Buffers.Binary; namespace System.Xml { @@ -401,13 +402,8 @@ public unsafe float ReadSingle() { int offset; byte[] buffer = GetBuffer(ValueHandleLength.Single, out offset); - float value; - byte* pb = (byte*)&value; + float value = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset, 4)); 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; } @@ -416,17 +412,8 @@ public unsafe double ReadDouble() { int offset; byte[] buffer = GetBuffer(ValueHandleLength.Double, out offset); - double value; - byte* pb = (byte*)&value; + double value = BinaryPrimitives.ReadDoubleLittleEndian(buffer.AsSpan(offset, 8)); 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; } @@ -436,9 +423,24 @@ public unsafe decimal ReadDecimal() int offset; byte[] buffer = GetBuffer(ValueHandleLength.Decimal, out offset); decimal value; - byte* pb = (byte*)&value; - for (int i = 0; i < sizeof(decimal); i++) - pb[i] = buffer[offset + i]; + if (BitConverter.IsLittleEndian) + { + byte* pb = (byte*)&value; + for (int i = 0; i < sizeof(decimal); i++) + pb[i] = buffer[offset + i]; + } + 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)) + }; + value = new decimal(span); + } Advance(ValueHandleLength.Decimal); return value; } @@ -1035,31 +1037,15 @@ public ulong GetUInt64(int offset) public unsafe float GetSingle(int offset) { - byte[] buffer = _buffer; - float value; - byte* pb = (byte*)&value; + float value = BinaryPrimitives.ReadSingleLittleEndian(_buffer.AsSpan(offset, 4)); 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 unsafe double GetDouble(int offset) { - byte[] buffer = _buffer; - double value; - byte* pb = (byte*)&value; + double value = BinaryPrimitives.ReadDoubleLittleEndian(_buffer.AsSpan(offset, 8)); 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; } @@ -1067,9 +1053,26 @@ public unsafe decimal GetDecimal(int offset) { byte[] buffer = _buffer; decimal value; - byte* pb = (byte*)&value; - for (int i = 0; i < sizeof(decimal); i++) - pb[i] = buffer[offset + i]; + + if (BitConverter.IsLittleEndian) + { + byte* pb = (byte*)&value; + for (int i = 0; i < sizeof(decimal); i++) + pb[i] = buffer[offset + i]; + } + 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)) + }; + value = new decimal(span); + } + return value; } 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 d6c214bbf85807..8ccac5a36c24bd 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,6 +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 b7878a37204498..3fd9aa5f5afd21 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 @@ -28,6 +28,8 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs index cd34fceb0fb4f6..ce13750d56388e 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryReaderTests.cs @@ -1,8 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Xml; using Xunit; @@ -161,6 +163,129 @@ 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 }); + AssertReadContentFromBinary(new DateTime(2022, 8, 26, 12, 34, 56, DateTimeKind.Utc), XmlBinaryNodeType.DateTimeText, + new byte[] { 0x00, 0x18, 0xdf, 0x61, 0x5f, 0x87, 0xda, 0x48 }); + + // 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 }; + float[] floats = new float[] { 1.2345f, 2.3456f }; + double[] doubles = new double[] { 1.2345678901, 2.3456789012 }; + decimal[] decimals = new[] { + new decimal(0x20212223, 0x10111213, 0x01020304, true, scale: 0x1b), + new decimal(0x50515253, 0x40414243, 0x31323334, false, scale: 0x1c) + }; + DateTime[] datetimes = new[] { + new DateTime(2022, 8, 26, 12, 34, 56, DateTimeKind.Utc), + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local) + }; + 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, "floats", null, floats, 0, floats.Length); + writer.WriteArray(null, "doubles", null, doubles, 0, doubles.Length); + writer.WriteArray(null, "decimals", null, decimals, 0, decimals.Length); + writer.WriteArray(null, "datetimes", null, datetimes, 0, datetimes.Length); + 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); + float[] actualFloats = reader.ReadSingleArray("floats", string.Empty); + double[] actualDoubles = reader.ReadDoubleArray("doubles", string.Empty); + decimal[] actualDecimals = reader.ReadDecimalArray("decimals", string.Empty); + DateTime[] actualDateTimes = reader.ReadDateTimeArray("datetimes", string.Empty); + 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(actualFloats, floats); + AssertExtensions.SequenceEqual(actualDoubles, doubles); + AssertExtensions.SequenceEqual(actualDecimals, decimals); + AssertExtensions.SequenceEqual(actualDateTimes, datetimes); + 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(); diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs index 719a06d6e48cf9..3ad4d32400e371 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; @@ -320,6 +321,179 @@ public static void FragmentTest() Assert.False(FragmentHelper.CanFragment(writer)); } + [Fact] + public static void BinaryWriter_PrimitiveTypes() + { + using MemoryStream ms = new(); + using XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(ms); + writer.WriteStartElement("root"); + + AssertBytesWritten(x => x.WriteValue((byte)0x78), XmlBinaryNodeType.Int8Text, new byte[] { 0x78 }); + AssertBytesWritten(x => x.WriteValue((short)0x1234), XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0x12 }); + AssertBytesWritten(x => x.WriteValue(unchecked((short)0xf234)), XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0xf2 }); + AssertBytesWritten(x => x.WriteValue((int)0x12345678), XmlBinaryNodeType.Int32Text, new byte[] { 0x78, 0x56, 0x34, 0x12 }); + AssertBytesWritten(x => x.WriteValue((long)0x0102030412345678), XmlBinaryNodeType.Int64Text, new byte[] { 0x78, 0x56, 0x34, 0x12, 04, 03, 02, 01 }); + + // Integer values should be represented using smalles possible type + AssertBytesWritten(x => x.WriteValue((long)0), XmlBinaryNodeType.ZeroText, Span.Empty); + AssertBytesWritten(x => x.WriteValue((long)1), XmlBinaryNodeType.OneText, Span.Empty); + AssertBytesWritten(x => x.WriteValue((int)0x00000078), XmlBinaryNodeType.Int8Text, new byte[] { 0x78 }); + AssertBytesWritten(x => x.WriteValue(unchecked((int)0xfffffff0)), XmlBinaryNodeType.Int8Text, new byte[] { 0xf0 }); + AssertBytesWritten(x => x.WriteValue((int)0x00001234), XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0x12 }); + AssertBytesWritten(x => x.WriteValue(unchecked((int)0xfffff234)), XmlBinaryNodeType.Int16Text, new byte[] { 0x34, 0xf2 }); + AssertBytesWritten(x => x.WriteValue((long)0x12345678), XmlBinaryNodeType.Int32Text, new byte[] { 0x78, 0x56, 0x34, 0x12 }); + AssertBytesWritten(x => x.WriteValue(unchecked((long)0xfffffffff2345678)), XmlBinaryNodeType.Int32Text, new byte[] { 0x78, 0x56, 0x34, 0xf2 }); + + float f = 1.23456788f; + ReadOnlySpan floatBytes = new byte[] { 0x52, 0x06, 0x9e, 0x3f }; + double d = 1.0 / 3.0; + ReadOnlySpan doubleBytes = new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f }; + Guid guid = new Guid(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + DateTime datetime = new DateTime(2022, 8, 26, 12, 34, 56, DateTimeKind.Utc); + Span datetimeBytes = stackalloc byte[8]; + BinaryPrimitives.WriteInt64LittleEndian(datetimeBytes, datetime.ToBinary()); + + AssertBytesWritten(x => x.WriteValue(f), XmlBinaryNodeType.FloatText, floatBytes); + AssertBytesWritten(x => x.WriteValue(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 }); + AssertBytesWritten(x => x.WriteValue(guid), XmlBinaryNodeType.GuidText, guid.ToByteArray()); + AssertBytesWritten(x => x.WriteValue(new TimeSpan(0x0807060504030201)), XmlBinaryNodeType.TimeSpanText, new byte[] { 01, 02, 03, 04, 05, 06, 07, 08 }); + AssertBytesWritten(x => x.WriteValue(datetime), XmlBinaryNodeType.DateTimeText, datetimeBytes); + + // Double can be represented as float or int as long as no detail is lost + AssertBytesWritten(x => x.WriteValue((double)f), XmlBinaryNodeType.FloatText, floatBytes); + AssertBytesWritten(x => x.WriteValue((double)0x0100), XmlBinaryNodeType.Int16Text, new byte[] { 0x00, 0x01 }); + AssertBytesWritten(x => x.WriteValue(d), XmlBinaryNodeType.DoubleText, doubleBytes); + + + void AssertBytesWritten(Action action, XmlBinaryNodeType nodeType, ReadOnlySpan expected) + { + writer.WriteStartElement("a"); + + // Reset stream so we only compare the actual value written (including end element) + writer.Flush(); + ms.Position = 0; + ms.SetLength(0); + + action(writer); + + writer.Flush(); + ms.TryGetBuffer(out ArraySegment segement); + Assert.Equal(nodeType, (XmlBinaryNodeType)segement[0]); + AssertExtensions.SequenceEqual(expected, segement.AsSpan(1)); + writer.WriteEndElement(); + } + } + + + [Fact] + public static void BinaryWriter_Arrays() + { + using var ms = new MemoryStream(); + using var writer = XmlDictionaryWriter.CreateBinaryWriter(ms); + writer.WriteStartElement("root"); + int offset = 1; + int count = 2; + + bool[] bools = new bool[] { false, true, false, true }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, bools, offset, count), XmlBinaryNodeType.BoolTextWithEndElement, + count, new byte[] { 1, 0 }); + + short[] shorts = new short[] { -1, 0x0102, 0x1122, -1 }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, shorts, offset, count), XmlBinaryNodeType.Int16TextWithEndElement, + count, new byte[] { 2, 1, 0x22, 0x11 }); + + int[] ints = new int[] { -1, 0x01020304, 0x11223344, -1 }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, ints, offset, count), XmlBinaryNodeType.Int32TextWithEndElement, + count, new byte[] { 4, 3, 2, 1, 0x44, 0x33, 0x22, 0x11 }); + + long[] longs = new long[] { -1, 0x0102030405060708, 0x1122334455667788, -1 }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, longs, offset, count), XmlBinaryNodeType.Int64TextWithEndElement, + count, new byte[] { 8, 7, 6, 5, 4, 3, 2, 1, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 }); + + float[] floats = new float[] { -1.0f, 1.23456788f, 1.23456788f, -1.0f }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, floats, offset, count), XmlBinaryNodeType.FloatTextWithEndElement, + count, new byte[] { 0x52, 0x06, 0x9e, 0x3f, 0x52, 0x06, 0x9e, 0x3f }); + + double[] doubles = new double[] { -1.0, 1.0 / 3.0, 1.0 / 3.0, -1.0 }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, doubles, offset, count), XmlBinaryNodeType.DoubleTextWithEndElement, + count, new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f }); + + decimal[] decimals = new[] { + new decimal(0x20212223, 0x10111213, 0x01020304, true, scale: 0x1b), + new decimal(0x50515253, 0x40414243, 0x31323334, false, scale: 0x1c) + }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, decimals, 0, decimals.Length), XmlBinaryNodeType.DecimalTextWithEndElement, + decimals.Length, new byte[] { 0x0, 0x0, 0x1b, 0x80, 0x4, 0x3, 0x2, 0x1, + 0x23, 0x22, 0x21, 0x20, 0x13, 0x12, 0x11, 0x10, + 0x0, 0x0, 0x1c, 0x00, 0x34, 0x33, 0x32, 0x31, + 0x53, 0x52, 0x51, 0x50, 0x43, 0x42, 0x41, 0x40 }); + + DateTime[] datetimes = new[] { + new DateTime(2022, 8, 26, 12, 34, 56, DateTimeKind.Utc), + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local) + }; + Span datetimeBytes = stackalloc byte[8 * datetimes.Length]; + for (int i = 0; i < datetimes.Length; i++) + { + BinaryPrimitives.WriteInt64LittleEndian(datetimeBytes.Slice(8 * i), datetimes[i].ToBinary()); + } + AssertBytesWritten(x => x.WriteArray(null, "a", null, datetimes, 0, datetimes.Length), XmlBinaryNodeType.DateTimeTextWithEndElement, + datetimes.Length, datetimeBytes); + + TimeSpan[] timespans = new[] { new TimeSpan(0x0807060504030201), new TimeSpan(0x1817161514131211) }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, timespans, 0, timespans.Length), XmlBinaryNodeType.TimeSpanTextWithEndElement, + timespans.Length, new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }); + + Guid[] guids = new Guid[] + { + 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 })) + }; + AssertBytesWritten(x => x.WriteArray(null, "a", null, guids, 0, guids.Length), XmlBinaryNodeType.GuidTextWithEndElement, + guids.Length, new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160 }); + + // Write more than 512 bytes in a single call to trigger different writing logic in XmlStreamNodeWriter.WriteBytes + long[] many_longs = Enumerable.Range(0x01020304, 127).Select(i => (long)i | (long)(~i << 32)).ToArray(); + Span many_longBytes = stackalloc byte[8 * many_longs.Length]; + for (int i = 0; i < many_longs.Length; i++) + { + BinaryPrimitives.WriteInt64LittleEndian(many_longBytes.Slice(8 * i), many_longs[i]); + } + AssertBytesWritten(x => x.WriteArray(null, "a", null, many_longs, 0, many_longs.Length), XmlBinaryNodeType.Int64TextWithEndElement, + many_longs.Length, many_longBytes); + + void AssertBytesWritten(Action action, XmlBinaryNodeType nodeType, int count, ReadOnlySpan expected) + { + // Reset stream so we only compare the actual value written (including end element) + writer.Flush(); + ms.Position = 0; + ms.SetLength(0); + + action(writer); + + writer.Flush(); + ms.TryGetBuffer(out ArraySegment segement); + + var actual = segement.AsSpan(); + Assert.Equal(XmlBinaryNodeType.Array, (XmlBinaryNodeType)actual[0]); + Assert.Equal(XmlBinaryNodeType.ShortElement, (XmlBinaryNodeType)actual[1]); + int elementLength = actual[2]; + Assert.InRange(elementLength, 0, 0x8f); // verify count is single byte + Assert.Equal(XmlBinaryNodeType.EndElement, (XmlBinaryNodeType)actual[3 + elementLength]); + + actual = actual.Slice(4 + elementLength); + // nodetype and count + Assert.Equal(nodeType, (XmlBinaryNodeType)actual[0]); + Assert.Equal(checked((sbyte)count), (sbyte)actual[1]); + + AssertExtensions.SequenceEqual(expected, actual.Slice(2)); + } + } + private static bool ReadTest(MemoryStream ms, Encoding encoding, ReaderWriterFactory.ReaderWriterType rwType, byte[] byteArray) { ms.Position = 0;