From fb4de17847913786f91c83de3c1daeef1d33df15 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Fri, 8 Mar 2019 12:11:52 +0000 Subject: [PATCH] use rented buffers in stream writes use cached lazy allocated default text stream encoding Commit migrated from https://github.com/dotnet/corefx/commit/8272f7f4f2d9776eb10df0533270a81dfb562715 --- .../src/System/Data/SqlClient/SqlParameter.cs | 20 +- .../src/System/Data/SqlClient/TdsParser.cs | 214 +++++++++++---------- 2 files changed, 131 insertions(+), 103 deletions(-) diff --git a/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs b/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs index 33a2c8f..7ad8805 100644 --- a/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs +++ b/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs @@ -16,6 +16,8 @@ using MSS = Microsoft.SqlServer.Server; using Microsoft.SqlServer.Server; using System.ComponentModel.Design.Serialization; +using System.Text; +using System.Threading; namespace System.Data.SqlClient { @@ -35,12 +37,28 @@ namespace System.Data.SqlClient internal class TextDataFeed : DataFeed { + private static UnicodeEncoding s_defaultEncoding; + internal TextReader _source; internal TextDataFeed(TextReader source) { _source = source; } + + public static UnicodeEncoding DefaultEncoding + { + get + { + UnicodeEncoding encoding = s_defaultEncoding; + if (encoding is null) + { + encoding = new UnicodeEncoding(false, false); ; + Interlocked.CompareExchange(ref s_defaultEncoding, encoding, null); + } + return encoding; + } + } } internal class XmlDataFeed : DataFeed @@ -2039,4 +2057,4 @@ namespace System.Data.SqlClient } } } -} \ No newline at end of file +} diff --git a/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs b/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs index 8d65ba2..d234b2c 100644 --- a/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs +++ b/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs @@ -8897,7 +8897,7 @@ namespace System.Data.SqlClient return null; } - private class TdsOutputStream : Stream + private sealed class TdsOutputStream : Stream { private TdsParser _parser; private TdsParserStateObject _stateObj; @@ -9041,7 +9041,7 @@ namespace System.Data.SqlClient } } - private class ConstrainedTextWriter : TextWriter + private sealed class ConstrainedTextWriter : TextWriter { private TextWriter _next; private int _size; @@ -9172,156 +9172,166 @@ namespace System.Data.SqlClient { preambleToSkip = encoding.GetPreamble(); } - ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, preambleToSkip), encoding), size); - XmlWriterSettings writerSettings = new XmlWriterSettings(); - writerSettings.CloseOutput = false; // don't close the memory stream + writerSettings.CloseOutput = false; // don't close the memory stream writerSettings.ConformanceLevel = ConformanceLevel.Fragment; if (_asyncWrite) { writerSettings.Async = true; } - XmlWriter ww = XmlWriter.Create(writer, writerSettings); - - if (feed._source.ReadState == ReadState.Initial) - { - feed._source.Read(); - } - - while (!feed._source.EOF && !writer.IsComplete) - { - // We are copying nodes from a reader to a writer. This will cause the - // XmlDeclaration to be emitted despite ConformanceLevel.Fragment above. - // Therefore, we filter out the XmlDeclaration while copying. - if (feed._source.NodeType == XmlNodeType.XmlDeclaration) + using (ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, preambleToSkip), encoding), size)) + using (XmlWriter ww = XmlWriter.Create(writer, writerSettings)) + { + if (feed._source.ReadState == ReadState.Initial) { feed._source.Read(); - continue; + } + + while (!feed._source.EOF && !writer.IsComplete) + { + // We are copying nodes from a reader to a writer. This will cause the + // XmlDeclaration to be emitted despite ConformanceLevel.Fragment above. + // Therefore, we filter out the XmlDeclaration while copying. + if (feed._source.NodeType == XmlNodeType.XmlDeclaration) + { + feed._source.Read(); + continue; + } + + if (_asyncWrite) + { + await ww.WriteNodeAsync(feed._source, true).ConfigureAwait(false); + } + else + { + ww.WriteNode(feed._source, true); + } } if (_asyncWrite) { - await ww.WriteNodeAsync(feed._source, true).ConfigureAwait(false); + await ww.FlushAsync().ConfigureAwait(false); } else { - ww.WriteNode(feed._source, true); + ww.Flush(); } } - - if (_asyncWrite) - { - await ww.FlushAsync().ConfigureAwait(false); - } - else - { - ww.Flush(); - } } private async Task WriteTextFeed(TextDataFeed feed, Encoding encoding, bool needBom, TdsParserStateObject stateObj, int size) { Debug.Assert(encoding == null || !needBom); - char[] inBuff = new char[constTextBufferSize]; - - encoding = encoding ?? new UnicodeEncoding(false, false); + char[] inBuff = ArrayPool.Shared.Rent(constTextBufferSize); - ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size); + encoding = encoding ?? TextDataFeed.DefaultEncoding; - if (needBom) + using (ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size)) { - if (_asyncWrite) + if (needBom) { - await writer.WriteAsync((char)TdsEnums.XMLUNICODEBOM).ConfigureAwait(false); + if (_asyncWrite) + { + await writer.WriteAsync((char)TdsEnums.XMLUNICODEBOM).ConfigureAwait(false); + } + else + { + writer.Write((char)TdsEnums.XMLUNICODEBOM); + } } - else + + int nWritten = 0; + do { - writer.Write((char)TdsEnums.XMLUNICODEBOM); - } - } + int nRead = 0; - int nWritten = 0; - do - { - int nRead = 0; + if (_asyncWrite) + { + nRead = await feed._source.ReadBlockAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false); + } + else + { + nRead = feed._source.ReadBlock(inBuff, 0, constTextBufferSize); + } - if (_asyncWrite) - { - nRead = await feed._source.ReadBlockAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false); - } - else - { - nRead = feed._source.ReadBlock(inBuff, 0, constTextBufferSize); - } + if (nRead == 0) + { + break; + } - if (nRead == 0) - { - break; - } + if (_asyncWrite) + { + await writer.WriteAsync(inBuff, 0, nRead).ConfigureAwait(false); + } + else + { + writer.Write(inBuff, 0, nRead); + } + + nWritten += nRead; + } while (!writer.IsComplete); if (_asyncWrite) { - await writer.WriteAsync(inBuff, 0, nRead).ConfigureAwait(false); + await writer.FlushAsync().ConfigureAwait(false); } else { - writer.Write(inBuff, 0, nRead); + writer.Flush(); } - - nWritten += nRead; - } while (!writer.IsComplete); - - if (_asyncWrite) - { - await writer.FlushAsync().ConfigureAwait(false); - } - else - { - writer.Flush(); } + + Array.Clear(inBuff, 0, constTextBufferSize); + ArrayPool.Shared.Return(inBuff, clearArray: false); } private async Task WriteStreamFeed(StreamDataFeed feed, TdsParserStateObject stateObj, int len) { - TdsOutputStream output = new TdsOutputStream(this, stateObj, null); - byte[] buff = new byte[constBinBufferSize]; - int nWritten = 0; - do + byte[] buff = ArrayPool.Shared.Rent(constBinBufferSize); + + using (TdsOutputStream output = new TdsOutputStream(this, stateObj, null)) { - int nRead = 0; - int readSize = constBinBufferSize; - if (len > 0 && nWritten + readSize > len) + int nWritten = 0; + do { - readSize = len - nWritten; - } + int nRead = 0; + int readSize = constBinBufferSize; + if (len > 0 && nWritten + readSize > len) + { + readSize = len - nWritten; + } - Debug.Assert(readSize >= 0); + Debug.Assert(readSize >= 0); - if (_asyncWrite) - { - nRead = await feed._source.ReadAsync(buff, 0, readSize).ConfigureAwait(false); - } - else - { - nRead = feed._source.Read(buff, 0, readSize); - } + if (_asyncWrite) + { + nRead = await feed._source.ReadAsync(buff, 0, readSize).ConfigureAwait(false); + } + else + { + nRead = feed._source.Read(buff, 0, readSize); + } - if (nRead == 0) - { - return; - } + if (nRead == 0) + { + return; + } - if (_asyncWrite) - { - await output.WriteAsync(buff, 0, nRead).ConfigureAwait(false); - } - else - { - output.Write(buff, 0, nRead); - } + if (_asyncWrite) + { + await output.WriteAsync(buff, 0, nRead).ConfigureAwait(false); + } + else + { + output.Write(buff, 0, nRead); + } + + nWritten += nRead; + } while (len <= 0 || nWritten < len); + } - nWritten += nRead; - } while (len <= 0 || nWritten < len); + Array.Clear(buff, 0, constTextBufferSize); + ArrayPool.Shared.Return(buff, clearArray: false); } private Task NullIfCompletedWriteTask(Task task) -- 2.7.4