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 556c38edcdf93c..6c4692733f33e1 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs @@ -6,13 +6,13 @@ // This can help identify element/attribute name/ns that could be written as XmlDictionaryStrings to get better compactness and performance. // #define LOG_NON_DICTIONARY_WRITES -using System.IO; -using System.Text; +using System.Buffers.Binary; using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Serialization; -using System.Globalization; -using System.Collections.Generic; -using System.Buffers.Binary; +using System.Text; namespace System.Xml { @@ -85,59 +85,60 @@ private byte[] GetTextNodeBuffer(int size, out int offset) private void WriteTextNodeWithLength(XmlBinaryNodeType nodeType, int length) { Debug.Assert(nodeType == XmlBinaryNodeType.Chars8Text || nodeType == XmlBinaryNodeType.Bytes8Text || nodeType == XmlBinaryNodeType.UnicodeChars8Text); - int offset; - byte[] buffer = GetTextNodeBuffer(5, out offset); if (length < 256) { - buffer[offset + 0] = (byte)nodeType; - buffer[offset + 1] = (byte)length; - Advance(2); + WriteTextNodeWithInt8(nodeType, unchecked((byte)length)); } else if (length < 65536) { - buffer[offset + 0] = (byte)(nodeType + 1 * 2); // WithEndElements interleave - buffer[offset + 1] = unchecked((byte)length); - length >>= 8; - buffer[offset + 2] = (byte)length; - Advance(3); + WriteTextNodeWithInt16(nodeType + /* WithEndElements interleave */ 1 * 2, unchecked((short)length)); } else { - buffer[offset + 0] = (byte)(nodeType + 2 * 2); // WithEndElements interleave - buffer[offset + 1] = (byte)length; - length >>= 8; - buffer[offset + 2] = (byte)length; - length >>= 8; - buffer[offset + 3] = (byte)length; - length >>= 8; - buffer[offset + 4] = (byte)length; - Advance(5); + WriteTextNodeWithInt32(nodeType + /* WithEndElements interleave */ 2 * 2, length); } } - private void WriteTextNodeWithInt64(XmlBinaryNodeType nodeType, long value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteTextNodeRaw(XmlBinaryNodeType nodeType, T value) + where T : unmanaged { - int offset; - byte[] buffer = GetTextNodeBuffer(9, out offset); - buffer[offset + 0] = (byte)nodeType; - buffer[offset + 1] = (byte)value; - value >>= 8; - buffer[offset + 2] = (byte)value; - value >>= 8; - buffer[offset + 3] = (byte)value; - value >>= 8; - buffer[offset + 4] = (byte)value; - value >>= 8; - buffer[offset + 5] = (byte)value; - value >>= 8; - buffer[offset + 6] = (byte)value; - value >>= 8; - buffer[offset + 7] = (byte)value; - value >>= 8; - buffer[offset + 8] = (byte)value; - Advance(9); + // GetTextNodeBuffer performs bounds checks and ensures returned buffer has size of at least (1 + Unsafe.SizeOf()) + byte[] buffer = GetTextNodeBuffer(1 + Unsafe.SizeOf(), out int offset); + + Debug.Assert(offset >= 0 && offset + 1 + Unsafe.SizeOf() <= buffer.Length, "WriteTextNodeRaw"); + ref byte bytePtr = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(buffer), offset); + + bytePtr = (byte)nodeType; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref bytePtr, 1), value); + Advance(1 + Unsafe.SizeOf()); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteRaw(T value) + where T : unmanaged + { + // GetBuffer performs bounds checks and ensures returned buffer has size of at least (Unsafe.SizeOf()) + byte[] buffer = GetBuffer(Unsafe.SizeOf(), out int offset); + + Debug.Assert(offset >= 0 && offset + Unsafe.SizeOf() <= buffer.Length, "WriteRaw"); + ref byte bytePtr = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(buffer), offset); + Unsafe.WriteUnaligned(ref bytePtr, value); + Advance(Unsafe.SizeOf()); + } + + private void WriteTextNodeWithInt8(XmlBinaryNodeType nodeType, byte value) + => WriteTextNodeRaw(nodeType, value); + + private void WriteTextNodeWithInt16(XmlBinaryNodeType nodeType, short value) + => WriteTextNodeRaw(nodeType, BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness((value))); + + private void WriteTextNodeWithInt32(XmlBinaryNodeType nodeType, int value) + => WriteTextNodeRaw(nodeType, BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness((value))); + + private void WriteTextNodeWithInt64(XmlBinaryNodeType nodeType, long value) + => WriteTextNodeRaw(nodeType, BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness((value))); + public override void WriteDeclaration() { } @@ -459,7 +460,7 @@ public override void WriteBoolText(bool value) public override void WriteInt32Text(int value) { - if (value >= -128 && value < 128) + if (value == (sbyte)value) { if (value == 0) { @@ -471,42 +472,22 @@ public override void WriteInt32Text(int value) } else { - int offset; - byte[] buffer = GetTextNodeBuffer(2, out offset); - buffer[offset + 0] = (byte)XmlBinaryNodeType.Int8Text; - buffer[offset + 1] = (byte)value; - Advance(2); + WriteTextNodeWithInt8(XmlBinaryNodeType.Int8Text, (byte)value); } } - else if (value >= -32768 && value < 32768) + else if (value == (short)value) { - int offset; - byte[] buffer = GetTextNodeBuffer(3, out offset); - buffer[offset + 0] = (byte)XmlBinaryNodeType.Int16Text; - buffer[offset + 1] = (byte)value; - value >>= 8; - buffer[offset + 2] = (byte)value; - Advance(3); + WriteTextNodeWithInt16(XmlBinaryNodeType.Int16Text, (short)value); } else { - int offset; - byte[] buffer = GetTextNodeBuffer(5, out offset); - buffer[offset + 0] = (byte)XmlBinaryNodeType.Int32Text; - buffer[offset + 1] = (byte)value; - value >>= 8; - buffer[offset + 2] = (byte)value; - value >>= 8; - buffer[offset + 3] = (byte)value; - value >>= 8; - buffer[offset + 4] = (byte)value; - Advance(5); + WriteTextNodeWithInt32(XmlBinaryNodeType.Int32Text, value); } } public override void WriteInt64Text(long value) { - if (value >= int.MinValue && value <= int.MaxValue) + if (value == (int)(value)) { WriteInt32Text((int)value); } @@ -529,26 +510,7 @@ public override void WriteUInt64Text(ulong value) } private void WriteInt64(long value) - { - int offset; - byte[] buffer = GetBuffer(8, out offset); - buffer[offset + 0] = (byte)value; - value >>= 8; - buffer[offset + 1] = (byte)value; - value >>= 8; - buffer[offset + 2] = (byte)value; - value >>= 8; - buffer[offset + 3] = (byte)value; - value >>= 8; - buffer[offset + 4] = (byte)value; - value >>= 8; - buffer[offset + 5] = (byte)value; - value >>= 8; - buffer[offset + 6] = (byte)value; - value >>= 8; - buffer[offset + 7] = (byte)value; - Advance(8); - } + => WriteRaw(BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value)); public override void WriteBase64Text(byte[]? trailBytes, int trailByteCount, byte[] base64Buffer, int base64Offset, int base64Count) { @@ -604,6 +566,15 @@ public override void WriteText(XmlDictionaryString value) } public override unsafe void WriteText(string value) + => WriteTextImpl(value); + + public override void WriteText(char[] chars, int offset, int count) + => WriteTextImpl(chars.AsSpan(offset, count)); + + // We need a separate implementation method which is not an override + // so that overload resolution will call string based method instead of ReadOnlySpan + // when called with a string argument + private unsafe void WriteTextImpl(string value) { if (_inAttribute) { @@ -625,19 +596,19 @@ public override unsafe void WriteText(string value) } } - public override unsafe void WriteText(char[] chars, int offset, int count) + private unsafe void WriteTextImpl(ReadOnlySpan chars) { if (_inAttribute) { - _attributeValue.WriteText(new string(chars, offset, count)); + _attributeValue.WriteText(chars.ToString()); } else { - if (count > 0) + if (chars.Length > 0) { - fixed (char* pch = &chars[offset]) + fixed (char* pch = &MemoryMarshal.GetReference(chars)) { - UnsafeWriteText(pch, count); + UnsafeWriteText(pch, chars.Length); } } else @@ -737,62 +708,69 @@ public override void WriteCharEntity(int ch) if (ch > char.MaxValue) { SurrogateChar sch = new SurrogateChar(ch); - char[] chars = new char[2] { sch.HighChar, sch.LowChar, }; - WriteText(chars, 0, 2); + WriteTextImpl(stackalloc char[] { sch.HighChar, sch.LowChar }); } else { - char[] chars = new char[1] { (char)ch }; - WriteText(chars, 0, 1); + char castChar = (char)ch; + WriteTextImpl(new ReadOnlySpan(in castChar)); } } - public override unsafe void WriteFloatText(float f) + public override void WriteFloatText(float f) { - long l; - if (f >= long.MinValue && f <= long.MaxValue && (l = (long)f) == f) + int i; + // Only write as an integer-type if it would save space. A full Int32 value doesn't. + if (f >= short.MinValue && f <= short.MaxValue && (i = (int)f) == f) { - WriteInt64Text(l); + WriteInt32Text(i); } else { - int offset; - byte[] buffer = GetTextNodeBuffer(1 + sizeof(float), out offset); - buffer[offset] = (byte)XmlBinaryNodeType.FloatText; - BinaryPrimitives.WriteSingleLittleEndian(buffer.AsSpan(offset + 1, sizeof(float)), f); - Advance(1 + sizeof(float)); + if (BitConverter.IsLittleEndian) + { + WriteTextNodeRaw(XmlBinaryNodeType.FloatText, f); + } + else + { + Span span = GetTextNodeBuffer(1 + sizeof(float), out int offset).AsSpan(offset, 1 + sizeof(float)); + span[0] = (byte)XmlBinaryNodeType.FloatText; + BinaryPrimitives.WriteSingleLittleEndian(span.Slice(1), f); + Advance(1 + sizeof(float)); + } } } - public override unsafe void WriteDoubleText(double d) + public override void WriteDoubleText(double d) { float f; - if (d >= float.MinValue && d <= float.MaxValue && (f = (float)d) == d) + + // If we can save space by writing as a 32-bit float, do that. + if ((f = (float)d) == d) { WriteFloatText(f); } else { - int offset; - byte[] buffer = GetTextNodeBuffer(1 + sizeof(double), out offset); - buffer[offset] = (byte)XmlBinaryNodeType.DoubleText; - BinaryPrimitives.WriteDoubleLittleEndian(buffer.AsSpan(offset + 1, sizeof(double)), d); - Advance(1 + sizeof(double)); + if (BitConverter.IsLittleEndian) + { + WriteTextNodeRaw(XmlBinaryNodeType.DoubleText, d); + } + else + { + Span span = GetTextNodeBuffer(1 + sizeof(double), out int offset).AsSpan(offset, 1 + sizeof(double)); + span[0] = (byte)XmlBinaryNodeType.DoubleText; + BinaryPrimitives.WriteDoubleLittleEndian(span.Slice(1), d); + Advance(1 + sizeof(double)); + } } } - public override unsafe void WriteDecimalText(decimal d) + public override void WriteDecimalText(decimal d) { - int offset; - byte[] buffer = GetTextNodeBuffer(1 + sizeof(decimal), out offset); - byte* bytes = (byte*)&d; - buffer[offset++] = (byte)XmlBinaryNodeType.DecimalText; if (BitConverter.IsLittleEndian) { - for (int i = 0; i < sizeof(decimal); i++) - { - buffer[offset++] = bytes[i]; - } + WriteTextNodeRaw(XmlBinaryNodeType.DecimalText, d); } else { @@ -800,20 +778,19 @@ public override unsafe void WriteDecimalText(decimal d) 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]); + Span span = GetTextNodeBuffer(1 + sizeof(decimal), out int offset).AsSpan(offset, 1 + sizeof(decimal)); + span[0] = (byte)XmlBinaryNodeType.DecimalText; + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(0 + 1), bits[3]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(4 + 1), bits[2]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(8 + 1), bits[0]); + BinaryPrimitives.WriteInt32LittleEndian(span.Slice(12 + 1), bits[1]); + Advance(1 + sizeof(decimal)); } - - Advance(1 + sizeof(decimal)); } public override void WriteDateTimeText(DateTime dt) - { - WriteTextNodeWithInt64(XmlBinaryNodeType.DateTimeText, dt.ToBinary()); - } + => WriteTextNodeWithInt64(XmlBinaryNodeType.DateTimeText, dt.ToBinary()); + public override void WriteUniqueIdText(UniqueId value) { @@ -833,10 +810,9 @@ public override void WriteUniqueIdText(UniqueId value) public override void WriteGuidText(Guid guid) { - int offset; - byte[] buffer = GetTextNodeBuffer(17, out offset); - buffer[offset] = (byte)XmlBinaryNodeType.GuidText; - Buffer.BlockCopy(guid.ToByteArray(), 0, buffer, offset + 1, 16); + Span span = GetTextNodeBuffer(17, out int offset).AsSpan(offset, 17); + span[0] = (byte)XmlBinaryNodeType.GuidText; + guid.TryWriteBytes(span.Slice(1)); Advance(17); } @@ -875,136 +851,114 @@ private void WriteArrayInfo(XmlBinaryNodeType nodeType, int count) WriteMultiByteInt32(count); } - public unsafe void UnsafeWriteBoolArray(bool[] array, int offset, int count) + public void WriteArray(XmlBinaryNodeType nodeType, int count, ReadOnlySpan bytes) { - WriteArrayInfo(XmlBinaryNodeType.BoolTextWithEndElement, count); - fixed (bool* items = &array[offset]) - { - base.UnsafeWriteBytes((byte*)items, count); - } + WriteArrayInfo(nodeType, count); + base.WriteBytes(bytes); } - public unsafe void UnsafeWriteInt16Array(short[] array, int offset, int count) + public void WriteBoolArray(ReadOnlySpan items) + { + WriteArray(XmlBinaryNodeType.BoolTextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); + } + + public void WriteInt16Array(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.Int16TextWithEndElement, count); if (BitConverter.IsLittleEndian) { - fixed (short* items = &array[offset]) - { - base.UnsafeWriteBytes((byte*)items, sizeof(short) * count); - } + WriteArray(XmlBinaryNodeType.Int16TextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); } else { - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.Int16TextWithEndElement, items.Length); + foreach (short s in items) { - Span span = GetBuffer(sizeof(short), out int bufferOffset).AsSpan(bufferOffset, sizeof(short)); - BinaryPrimitives.WriteInt16LittleEndian(span, array[offset + i]); - Advance(sizeof(short)); + WriteRaw(BinaryPrimitives.ReverseEndianness(s)); } } } - public unsafe void UnsafeWriteInt32Array(int[] array, int offset, int count) + public void WriteInt32Array(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.Int32TextWithEndElement, count); if (BitConverter.IsLittleEndian) { - fixed (int* items = &array[offset]) - { - base.UnsafeWriteBytes((byte*)items, sizeof(int) * count); - } + WriteArray(XmlBinaryNodeType.Int32TextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); } else { - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.Int32TextWithEndElement, items.Length); + foreach (int i in items) { - Span span = GetBuffer(sizeof(int), out int bufferOffset).AsSpan(bufferOffset, sizeof(int)); - BinaryPrimitives.WriteInt32LittleEndian(span, array[offset + i]); - Advance(sizeof(int)); + WriteRaw(BinaryPrimitives.ReverseEndianness(i)); } } } - public unsafe void UnsafeWriteInt64Array(long[] array, int offset, int count) + public void WriteInt64Array(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.Int64TextWithEndElement, count); if (BitConverter.IsLittleEndian) { - fixed (long* items = &array[offset]) - { - base.UnsafeWriteBytes((byte*)items, sizeof(long) * count); - } + WriteArray(XmlBinaryNodeType.Int64TextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); } else { - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.Int64TextWithEndElement, items.Length); + foreach (long l in items) { - Span span = GetBuffer(sizeof(long), out int bufferOffset).AsSpan(bufferOffset, sizeof(long)); - BinaryPrimitives.WriteInt64LittleEndian(span, array[offset + i]); - Advance(sizeof(long)); + WriteRaw(BinaryPrimitives.ReverseEndianness(l)); } } } - public unsafe void UnsafeWriteFloatArray(float[] array, int offset, int count) + public void WriteFloatArray(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.FloatTextWithEndElement, count); if (BitConverter.IsLittleEndian) { - fixed (float* items = &array[offset]) - { - base.UnsafeWriteBytes((byte*)items, sizeof(float) * count); - } + WriteArray(XmlBinaryNodeType.FloatTextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); } else { - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.FloatTextWithEndElement, items.Length); + foreach (float f in items) { Span span = GetBuffer(sizeof(float), out int bufferOffset).AsSpan(bufferOffset, sizeof(float)); - BinaryPrimitives.WriteSingleLittleEndian(span, array[offset + i]); + BinaryPrimitives.WriteSingleLittleEndian(span, f); Advance(sizeof(float)); } } } - public unsafe void UnsafeWriteDoubleArray(double[] array, int offset, int count) + public void WriteDoubleArray(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.DoubleTextWithEndElement, count); if (BitConverter.IsLittleEndian) { - fixed (double* items = &array[offset]) - { - base.UnsafeWriteBytes((byte*)items, sizeof(double) * count); - } + WriteArray(XmlBinaryNodeType.DoubleTextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); } else { - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.DoubleTextWithEndElement, items.Length); + foreach (double d in items) { Span span = GetBuffer(sizeof(double), out int bufferOffset).AsSpan(bufferOffset, sizeof(double)); - BinaryPrimitives.WriteDoubleLittleEndian(span, array[offset + i]); + BinaryPrimitives.WriteDoubleLittleEndian(span, d); Advance(sizeof(double)); } } } - public unsafe void UnsafeWriteDecimalArray(decimal[] array, int offset, int count) + public void WriteDecimalArray(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.DecimalTextWithEndElement, count); if (BitConverter.IsLittleEndian) { - fixed (decimal* items = &array[offset]) - { - base.UnsafeWriteBytes((byte*)items, sizeof(decimal) * count); - } + WriteArray(XmlBinaryNodeType.DecimalTextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); } else { Span bits = stackalloc int[4]; - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.DecimalTextWithEndElement, items.Length); + foreach (ref readonly decimal d in items) { - decimal.TryGetBits(array[offset + i], bits, out int intsWritten); + decimal.TryGetBits(d, bits, out int intsWritten); Debug.Assert(intsWritten == 4); Span span = GetBuffer(16, out int bufferOffset).AsSpan(bufferOffset, 16); @@ -1017,31 +971,40 @@ public unsafe void UnsafeWriteDecimalArray(decimal[] array, int offset, int coun } } - public void WriteDateTimeArray(DateTime[] array, int offset, int count) + public void WriteDateTimeArray(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.DateTimeTextWithEndElement, count); - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.DateTimeTextWithEndElement, items.Length); + foreach (DateTime dateTime in items) { - WriteInt64(array[offset + i].ToBinary()); + WriteInt64(dateTime.ToBinary()); } } - public void WriteGuidArray(Guid[] array, int offset, int count) + public void WriteGuidArray(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.GuidTextWithEndElement, count); - for (int i = 0; i < count; i++) + if (BitConverter.IsLittleEndian) { - byte[] buffer = array[offset + i].ToByteArray(); - WriteBytes(buffer, 0, 16); + WriteArray(XmlBinaryNodeType.GuidTextWithEndElement, items.Length, MemoryMarshal.AsBytes(items)); + } + else + { + + WriteArrayInfo(XmlBinaryNodeType.GuidTextWithEndElement, items.Length); + foreach (ref readonly Guid guid in items) + { + Span bytes = GetBuffer(16, out int bufferOffset).AsSpan(bufferOffset, 16); + guid.TryWriteBytes(bytes); + Advance(16); + } } } - public void WriteTimeSpanArray(TimeSpan[] array, int offset, int count) + public void WriteTimeSpanArray(ReadOnlySpan items) { - WriteArrayInfo(XmlBinaryNodeType.TimeSpanTextWithEndElement, count); - for (int i = 0; i < count; i++) + WriteArrayInfo(XmlBinaryNodeType.TimeSpanTextWithEndElement, items.Length); + foreach (ref readonly TimeSpan timespan in items) { - WriteInt64(array[offset + i].Ticks); + WriteInt64(timespan.Ticks); } } @@ -1057,8 +1020,7 @@ public override void WriteQualifiedName(string prefix, XmlDictionaryString local int key; if (prefix.Length == 1 && char.IsAsciiLetterLower(ch) && TryGetKey(localName, out key)) { - WriteTextNode(XmlBinaryNodeType.QNameDictionaryText); - WriteByte((byte)(ch - 'a')); + WriteTextNodeWithInt8(XmlBinaryNodeType.QNameDictionaryText, (byte)(ch - 'a')); WriteDictionaryString(key); } else @@ -1296,6 +1258,20 @@ private void WriteStartArray(string? prefix, XmlDictionaryString localName, XmlD WriteEndElement(); } + private void WriteArray(string? prefix, string localName, string? namespaceUri, + XmlBinaryNodeType nodeType, int count, ReadOnlySpan bytes) + { + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.WriteArray(nodeType, count, bytes); + } + + private void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, + XmlBinaryNodeType nodeType, int count, ReadOnlySpan bytes) + { + WriteStartArray(prefix, localName, namespaceUri, count); + _writer.WriteArray(nodeType, count, bytes); + } + private static void CheckArray(Array array, int offset, int count) { ArgumentNullException.ThrowIfNull(array); @@ -1308,126 +1284,8 @@ private static void CheckArray(Array array, int offset, int count) throw new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.SizeExceedsRemainingBufferSpace, array.Length - offset)); } - public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, bool[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteBoolArray(array, offset, count); - } - } - } - - public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, bool[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteBoolArray(array, offset, count); - } - } - } - - public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, short[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteInt16Array(array, offset, count); - } - } - } - - public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, short[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteInt16Array(array, offset, count); - } - } - } - - public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, int[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteInt32Array(array, offset, count); - } - } - } - - public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, int[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteInt32Array(array, offset, count); - } - } - } - - public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, long[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteInt64Array(array, offset, count); - } - } - } - - public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, long[] array, int offset, int count) + // Bool + public override void WriteArray(string? prefix, string localName, string? namespaceUri, bool[] array, int offset, int count) { if (Signing) { @@ -1439,12 +1297,13 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteInt64Array(array, offset, count); + _writer.WriteBoolArray(array.AsSpan(offset, count)); } } } - public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, float[] array, int offset, int count) + // Int16 + public override void WriteArray(string? prefix, string localName, string? namespaceUri, short[] array, int offset, int count) { if (Signing) { @@ -1456,12 +1315,13 @@ public override unsafe void WriteArray(string? prefix, string localName, string? if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteFloatArray(array, offset, count); + _writer.WriteInt16Array(array.AsSpan(offset, count)); } } } - public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, float[] array, int offset, int count) + // Int32 + public override void WriteArray(string? prefix, string localName, string? namespaceUri, int[] array, int offset, int count) { if (Signing) { @@ -1473,12 +1333,13 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteFloatArray(array, offset, count); + _writer.WriteInt32Array(array.AsSpan(offset, count)); } } } - public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, double[] array, int offset, int count) + // Int64 + public override void WriteArray(string? prefix, string localName, string? namespaceUri, long[] array, int offset, int count) { if (Signing) { @@ -1490,12 +1351,13 @@ public override unsafe void WriteArray(string? prefix, string localName, string? if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteDoubleArray(array, offset, count); + _writer.WriteInt64Array(array.AsSpan(offset, count)); } } } - public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, double[] array, int offset, int count) + // Float + public override void WriteArray(string? prefix, string localName, string? namespaceUri, float[] array, int offset, int count) { if (Signing) { @@ -1507,12 +1369,13 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteDoubleArray(array, offset, count); + _writer.WriteFloatArray(array.AsSpan(offset, count)); } } } - public override unsafe void WriteArray(string? prefix, string localName, string? namespaceUri, decimal[] array, int offset, int count) + // Double + public override void WriteArray(string? prefix, string localName, string? namespaceUri, double[] array, int offset, int count) { if (Signing) { @@ -1524,12 +1387,13 @@ public override unsafe void WriteArray(string? prefix, string localName, string? if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteDecimalArray(array, offset, count); + _writer.WriteDoubleArray(array.AsSpan(offset, count)); } } } - public override unsafe void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, decimal[] array, int offset, int count) + // Decimal + public override void WriteArray(string? prefix, string localName, string? namespaceUri, decimal[] array, int offset, int count) { if (Signing) { @@ -1541,7 +1405,7 @@ public override unsafe void WriteArray(string? prefix, XmlDictionaryString local if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.UnsafeWriteDecimalArray(array, offset, count); + _writer.WriteDecimalArray(array.AsSpan(offset, count)); } } } @@ -1559,24 +1423,7 @@ public override void WriteArray(string? prefix, string localName, string? namesp if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.WriteDateTimeArray(array, offset, count); - } - } - } - - public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, DateTime[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.WriteDateTimeArray(array, offset, count); + _writer.WriteDateTimeArray(array.AsSpan(offset, count)); } } } @@ -1594,24 +1441,7 @@ public override void WriteArray(string? prefix, string localName, string? namesp if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.WriteGuidArray(array, offset, count); - } - } - } - - public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, Guid[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.WriteGuidArray(array, offset, count); + _writer.WriteGuidArray(array.AsSpan(offset, count)); } } } @@ -1629,24 +1459,7 @@ public override void WriteArray(string? prefix, string localName, string? namesp if (count > 0) { WriteStartArray(prefix, localName, namespaceUri, count); - _writer.WriteTimeSpanArray(array, offset, count); - } - } - } - - public override void WriteArray(string? prefix, XmlDictionaryString localName, XmlDictionaryString? namespaceUri, TimeSpan[] array, int offset, int count) - { - if (Signing) - { - base.WriteArray(prefix, localName, namespaceUri, array, offset, count); - } - else - { - CheckArray(array, offset, count); - if (count > 0) - { - WriteStartArray(prefix, localName, namespaceUri, count); - _writer.WriteTimeSpanArray(array, offset, count); + _writer.WriteTimeSpanArray(array.AsSpan(offset, count)); } } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs index b144c751fe9383..d034435607f6ec 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs @@ -222,22 +222,18 @@ public void WriteBytes(byte[] byteBuffer, int byteOffset, int byteCount) } } - protected unsafe void UnsafeWriteBytes(byte* bytes, int byteCount) + protected void WriteBytes(ReadOnlySpan bytes) { - FlushBuffer(); - byte[] buffer = _buffer; - while (byteCount >= bufferLength) + if (bytes.Length < bufferLength) { - for (int i = 0; i < bufferLength; i++) - buffer[i] = bytes[i]; - OutputStream.Write(buffer, 0, bufferLength); - bytes += bufferLength; - byteCount -= bufferLength; + var buffer = GetBuffer(bytes.Length, out int offset).AsSpan(offset, bytes.Length); + bytes.CopyTo(buffer); + Advance(bytes.Length); } + else { - for (int i = 0; i < byteCount; i++) - buffer[i] = bytes[i]; - OutputStream.Write(buffer, 0, byteCount); + FlushBuffer(); + OutputStream.Write(bytes); } }