From 551efb8f965ef25ce108d96a8ca40b4c25384461 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 22:40:05 +0000 Subject: [PATCH 1/3] Initial plan From 5d218d20401b6baf05d9680b5be7e07236703035 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 23:09:02 +0000 Subject: [PATCH 2/3] Optimize XmlSerializer primitive raw writes with span-based raw writer path Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/29c71057-04f8-4c4e-9c5f-5442fe10a352 Co-authored-by: StephenMolloy <19562826+StephenMolloy@users.noreply.github.com> --- .../Xml/Core/XmlEncodedRawTextWriter.cs | 12 +++++++++ .../Core/XmlRawTextWriterGenerator.ttinclude | 12 +++++++++ .../src/System/Xml/Core/XmlRawWriter.cs | 6 +++++ .../System/Xml/Core/XmlUtf8RawTextWriter.cs | 10 ++++++++ .../System/Xml/Core/XmlWellFormedWriter.cs | 25 +++++++++++++++++++ .../Serialization/XmlSerializationWriter.cs | 9 ++++++- .../XmlSerializerTests.RuntimeOnly.cs | 15 +++++++++++ 7 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs index b74253784340ba..0d0ba35f4e79fb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs @@ -703,6 +703,18 @@ public override unsafe void WriteRaw(string data) _textPos = _bufPos; } + internal override unsafe void WriteRaw(ReadOnlySpan value) + { + if (_trackTextContent && _inTextContent) { ChangeTextContentMark(false); } + + fixed (char* pSrcBegin = value) + { + WriteRawWithCharChecking(pSrcBegin, pSrcBegin + value.Length); + } + + _textPos = _bufPos; + } + // Flush all bytes in the buffer to output and close the output stream or writer. public override void Close() { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude index 3c0837cbc6dcdb..610d58ba71e039 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude @@ -730,6 +730,18 @@ namespace System.Xml _textPos = _bufPos; } + internal override unsafe void WriteRaw(ReadOnlySpan value) + {<# +#><#= SetTextContentMark(3, false) #> + + fixed (char* pSrcBegin = value) + { + WriteRawWithCharChecking(pSrcBegin, pSrcBegin + value.Length); + } + + _textPos = _bufPos; + } + // Flush all bytes in the buffer to output and close the output stream or writer. public override void Close() { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriter.cs index 5f53bb2dd75f35..516e6409b4f763 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriter.cs @@ -179,6 +179,12 @@ public override void WriteRaw(string data) WriteString(data); } + // Forward call to WriteString(string). + internal virtual void WriteRaw(ReadOnlySpan value) + { + WriteString(value.ToString()); + } + // Override in order to handle Xml simple typed values and to pass resolver for QName values public override void WriteValue(object value) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs index d5d3c300c99f1f..78a3c5967dbe7a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs @@ -612,6 +612,16 @@ public override unsafe void WriteRaw(string data) _textPos = _bufPos; } + internal override unsafe void WriteRaw(ReadOnlySpan value) + { + fixed (char* pSrcBegin = value) + { + WriteRawWithCharChecking(pSrcBegin, pSrcBegin + value.Length); + } + + _textPos = _bufPos; + } + // Flush all bytes in the buffer to output and close the output stream or writer. public override void Close() { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs index ce2c6690ea3e73..5901d6e1111bce 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs @@ -1131,6 +1131,31 @@ public override void WriteRaw(string data) } } + internal void WriteRaw(ReadOnlySpan data) + { + try + { + AdvanceState(Token.RawData); + if (SaveAttrValue) + { + _attrValueCache!.WriteRaw(data.ToString()); + } + else if (_rawWriter is not null) + { + _rawWriter.WriteRaw(data); + } + else + { + _writer.WriteRaw(data.ToString()); + } + } + catch + { + _currentState = State.Error; + throw; + } + } + public override void WriteBase64(byte[] buffer, int index, int count) { try diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs index befc8d8b4b2cd3..6e51aa30652aab 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs @@ -463,7 +463,14 @@ protected void WriteTypedPrimitive(string? name, string? ns, object o, bool xsiT Debug.Assert(!span.Slice(0, charsWritten).ContainsAny(escapeChars), "Primitive value contains illegal xml char."); #endif //all the primitive types except string and XmlQualifiedName writes to the buffer - _w.WriteRaw(_primitivesBuffer, 0, charsWritten); + if (_w is XmlWellFormedWriter wellFormedWriter) + { + wellFormedWriter.WriteRaw(_primitivesBuffer.AsSpan(0, charsWritten)); + } + else + { + _w.WriteRaw(_primitivesBuffer, 0, charsWritten); + } } else { diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs index 5e57f747f5ce65..f7ae5fc82098e7 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs @@ -126,6 +126,21 @@ public static void Xml_DecimalAsRoot() } } + [Fact] + public static void Xml_DecimalArrayAsRoot() + { + decimal[] values = new decimal[] { (decimal)-1.2, (decimal)0, (decimal)2.3, decimal.MinValue, decimal.MaxValue }; + string serialized = Serialize(values, string.Empty, skipStringCompare: true); + + Assert.Contains("-1.2", serialized); + Assert.Contains("0", serialized); + Assert.Contains("2.3", serialized); + Assert.Contains($"{decimal.MinValue.ToString(CultureInfo.InvariantCulture)}", serialized); + Assert.Contains($"{decimal.MaxValue.ToString(CultureInfo.InvariantCulture)}", serialized); + + Assert.Equal(values, SerializeAndDeserialize(values, string.Empty, skipStringCompare: true)); + } + [Fact] public static void Xml_DoubleAsRoot() { From c0512d25db2495176cc375f3ff33bc6206282394 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 23:30:44 +0000 Subject: [PATCH 3/3] XmlSerializer: route formatted primitive output through span-based raw writer fast path Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/29c71057-04f8-4c4e-9c5f-5442fe10a352 Co-authored-by: StephenMolloy <19562826+StephenMolloy@users.noreply.github.com> --- .../src/System/Xml/Core/XmlEncodedRawTextWriter.cs | 6 ++++++ .../src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude | 6 ++++++ .../src/System/Xml/Core/XmlUtf8RawTextWriter.cs | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs index 0d0ba35f4e79fb..efaed2c39d2311 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs @@ -2137,6 +2137,12 @@ public override void WriteRaw(string data) base.WriteRaw(data); } + internal override void WriteRaw(ReadOnlySpan value) + { + _mixedContent = true; + base.WriteRaw(value); + } + public override void WriteBase64(byte[] buffer, int index, int count) { _mixedContent = true; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude index 610d58ba71e039..639112dfc68b38 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude @@ -2193,6 +2193,12 @@ namespace System.Xml base.WriteRaw(data); } + internal override void WriteRaw(ReadOnlySpan value) + { + _mixedContent = true; + base.WriteRaw(value); + } + public override void WriteBase64(byte[] buffer, int index, int count) { _mixedContent = true; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs index 78a3c5967dbe7a..d0e7a1c49a03d0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs @@ -1995,6 +1995,12 @@ public override void WriteRaw(string data) base.WriteRaw(data); } + internal override void WriteRaw(ReadOnlySpan value) + { + _mixedContent = true; + base.WriteRaw(value); + } + public override void WriteBase64(byte[] buffer, int index, int count) { _mixedContent = true;