From 6e6de4e82b2d375d3b5fa5282558429ff6f8cd6a Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Wed, 24 May 2023 00:12:59 +0300 Subject: [PATCH 01/10] Implement span overloads --- .../src/System/Data/SQLTypes/SQLBytes.cs | 161 ++++++++++++------ 1 file changed, 110 insertions(+), 51 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index 581601333c3956..7697e1f8ebcd40 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -290,6 +290,34 @@ public void SetLength(long value) AssertValid(); } + private long ReadInternal(Span buffer, long offset) + { + if (_state == SqlBytesCharsState.Stream) + { + if (_stream!.Position != offset) + _stream.Seek(offset, SeekOrigin.Begin); + return _stream.Read(buffer); + } + + // Adjust count based on data length + int count = Math.Min(buffer.Length, (int)(Length - offset)); + + Span span = _rgbBuf!.AsSpan((int)offset, count); + span.CopyTo(buffer); + + return span.Length; + } + + internal long Read(long offset, Span buffer) + { + if (IsNull) + throw new SqlNullValueException(); + + ArgumentOutOfRangeException.ThrowIfGreaterThan(offset, Length); + ArgumentOutOfRangeException.ThrowIfNegative(offset); + + return ReadInternal(buffer, offset); + } // Read data of specified length from specified offset into a buffer public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) { @@ -308,26 +336,69 @@ public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) ArgumentOutOfRangeException.ThrowIfNegative(count); ArgumentOutOfRangeException.ThrowIfGreaterThan(count, buffer.Length - offsetInBuffer); - // Adjust count based on data length - if (count > Length - offset) - count = (int)(Length - offset); + return ReadInternal(buffer.AsSpan(offsetInBuffer, count), offset); + } - if (count != 0) + private void WriteInternal(long offset, ReadOnlySpan buffer) + { + if (IsNull) { - switch (_state) - { - case SqlBytesCharsState.Stream: - if (_stream!.Position != offset) - _stream.Seek(offset, SeekOrigin.Begin); - count = _stream.Read(buffer, offsetInBuffer, count); - break; + // If NULL and there is buffer inside, we only allow writing from + // offset zero. + // + if (offset != 0) + throw new SqlTypeException(SR.SqlMisc_WriteNonZeroOffsetOnNullMessage); - default: - Array.Copy(_rgbBuf!, offset, buffer, offsetInBuffer, count); - break; - } + // treat as if our current length is zero. + // Note this has to be done after all inputs are validated, so that + // we won't throw exception after this point. + // + _lCurLen = 0; + _state = SqlBytesCharsState.Buffer; + } + else if (offset > _lCurLen) + { + // Don't allow writing from an offset that this larger than current length. + // It would leave uninitialized data in the buffer. + // + throw new SqlTypeException(SR.SqlMisc_WriteOffsetLargerThanLenMessage); + } + + if (buffer.Length != 0) + { + Span span = _rgbBuf.AsSpan((int)offset, buffer.Length); + buffer.CopyTo(span); + + // If the last position that has been written is after + // the current data length, reset the length + if (_lCurLen < offset + buffer.Length) + _lCurLen = offset + buffer.Length; + } + } + + internal void Write(long offset, ReadOnlySpan buffer) + { + if (FStream()) + { + if (_stream!.Position != offset) + _stream.Seek(offset, SeekOrigin.Begin); + _stream.Write(buffer); + } + else + { + if (_rgbBuf == null) + throw new SqlTypeException(SR.SqlMisc_NoBufferMessage); + + ArgumentOutOfRangeException.ThrowIfNegative(offset); + + if (offset > _rgbBuf.Length) + throw new SqlTypeException(SR.SqlMisc_BufferInsufficientMessage); + + if (buffer.Length > _rgbBuf.Length - offset) + throw new SqlTypeException(SR.SqlMisc_BufferInsufficientMessage); + + WriteInternal(offset, buffer); } - return count; } // Write data of specified length into the SqlBytes from specified offset @@ -360,38 +431,7 @@ public void Write(long offset, byte[] buffer, int offsetInBuffer, int count) if (count > _rgbBuf.Length - offset) throw new SqlTypeException(SR.SqlMisc_BufferInsufficientMessage); - if (IsNull) - { - // If NULL and there is buffer inside, we only allow writing from - // offset zero. - // - if (offset != 0) - throw new SqlTypeException(SR.SqlMisc_WriteNonZeroOffsetOnNullMessage); - - // treat as if our current length is zero. - // Note this has to be done after all inputs are validated, so that - // we won't throw exception after this point. - // - _lCurLen = 0; - _state = SqlBytesCharsState.Buffer; - } - else if (offset > _lCurLen) - { - // Don't allow writing from an offset that this larger than current length. - // It would leave uninitialized data in the buffer. - // - throw new SqlTypeException(SR.SqlMisc_WriteOffsetLargerThanLenMessage); - } - - if (count != 0) - { - Array.Copy(buffer, offsetInBuffer, _rgbBuf, offset, count); - - // If the last position that has been written is after - // the current data length, reset the length - if (_lCurLen < offset + count) - _lCurLen = offset + count; - } + WriteInternal(offset, buffer.AsSpan(offsetInBuffer, count)); } AssertValid(); @@ -686,6 +726,13 @@ public override long Seek(long offset, SeekOrigin origin) return _lPosition; } + private int ReadInternal(Span buffer) + { + int bytesRead = (int)_sb.Read(_lPosition, buffer); + _lPosition += bytesRead; + + return bytesRead; + } // The Read/Write/ReadByte/WriteByte simply delegates to SqlBytes public override int Read(byte[] buffer, int offset, int count) { @@ -693,10 +740,14 @@ public override int Read(byte[] buffer, int offset, int count) ValidateBufferArguments(buffer, offset, count); - int iBytesRead = (int)_sb.Read(_lPosition, buffer, offset, count); - _lPosition += iBytesRead; + return ReadInternal(buffer.AsSpan(offset, count)); + } + + public override int Read(Span buffer) + { + CheckIfStreamClosed(); - return iBytesRead; + return ReadInternal(buffer); } public override void Write(byte[] buffer, int offset, int count) @@ -709,6 +760,14 @@ public override void Write(byte[] buffer, int offset, int count) _lPosition += count; } + public override void Write(ReadOnlySpan buffer) + { + CheckIfStreamClosed(); + + _sb.Write(_lPosition, buffer); + _lPosition += buffer.Length; + } + public override int ReadByte() { CheckIfStreamClosed(); From 17a8a9b397786475a2917e6979a665e2b8c0b25f Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Wed, 24 May 2023 01:54:17 +0300 Subject: [PATCH 02/10] Add StreamOnBytes tests --- .../System/Data/SqlTypes/SqlBytesTest.cs | 111 +++++++++++++++++- 1 file changed, 107 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs index d04f0afab5bd46..f48dc6eae4b8f1 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs @@ -148,8 +148,24 @@ public void Read_SuccessTest1() SqlBytes bytes = new SqlBytes(b1); byte[] b2 = new byte[10]; - bytes.Read(0, b2, 0, (int)bytes.Length); + long read = bytes.Read(0, b2, 0, (int)bytes.Length); + Assert.Equal(bytes.Length, read); Assert.Equal(bytes.Value[5], b2[5]); + + Span b2Span = b2.AsSpan(); + b2Span.Clear(); + + Stream stream = bytes.Stream; + read = stream.Read(b2, 0, (int) bytes.Length); + Assert.Equal(bytes.Length, read); + Assert.Equal(bytes.Value[5], b2[5]); + + b2Span.Clear(); + stream.Position = 0; + + read = stream.Read(b2Span.Slice(0, (int) bytes.Length)); + Assert.Equal(bytes.Length, read); + Assert.Equal(bytes.Value[5], b2Span[5]); } [Fact] @@ -160,6 +176,9 @@ public void Read_NullBufferTest() byte[] b2 = null; Assert.Throws(() => bytes.Read(0, b2, 0, 10)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Read(b2, 0, 10)); } [Fact] @@ -170,6 +189,9 @@ public void Read_InvalidCountTest1() byte[] b2 = new byte[5]; Assert.Throws(() => bytes.Read(0, b2, 0, 10)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Read(b2, 0, 10)); } [Fact] @@ -208,7 +230,10 @@ public void Read_NullInstanceValueTest() byte[] b2 = new byte[5]; SqlBytes bytes = new SqlBytes(); - Assert.Throws(() => bytes.Read(0, b2, 8, 4)); + Assert.Throws(() => bytes.Read(0, b2, 0, 1)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Read(b2, 0, 1)); } [Fact] @@ -218,9 +243,28 @@ public void Read_SuccessTest2() SqlBytes bytes = new SqlBytes(b1); byte[] b2 = new byte[10]; - bytes.Read(5, b2, 0, 10); + const long offset = 5; + long read = bytes.Read(offset, b2, 0, 10); + Assert.Equal(b1.Length - offset, read); Assert.Equal(bytes.Value[5], b2[0]); Assert.Equal(bytes.Value[9], b2[4]); + + Span b2Span = b2.AsSpan(); + b2Span.Clear(); + + Stream stream = bytes.Stream; + read = stream.Read(b2, 0, 10); + Assert.Equal(10, read); + Assert.Equal(bytes.Value[5], b2[5]); + Assert.Equal(bytes.Value[9], b2[9]); + + b2Span.Clear(); + stream.Position = 0; + + read = stream.Read(b2Span.Slice(0, 10)); + Assert.Equal(10, read); + Assert.Equal(bytes.Value[5], b2Span[5]); + Assert.Equal(bytes.Value[9], b2Span[9]); } [Fact] @@ -240,6 +284,9 @@ public void Read_NegativeCountTest() byte[] b2 = new byte[5]; Assert.Throws(() => bytes.Read(0, b2, 0, -1)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Read(b2, 0, -1)); } [Fact] @@ -250,6 +297,9 @@ public void Read_InvalidCountTest2() byte[] b2 = new byte[5]; Assert.Throws(() => bytes.Read(0, b2, 3, 4)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Read(b2, 3, 4)); } /* Write Tests */ @@ -262,6 +312,19 @@ public void Write_SuccessTest1() bytes.Write(0, b1, 0, b1.Length); Assert.Equal(bytes.Value[0], b1[0]); + + Span b2Span = b2.AsSpan(); + b2Span.Clear(); + + Stream stream = bytes.Stream; + stream.Write(b1, 0, b1.Length); + Assert.Equal(bytes.Value[0], b1[0]); + + b2Span.Clear(); + stream.Position = 0; + + stream.Write(b1.AsSpan(0, b1.Length)); + Assert.Equal(bytes.Value[0], b1[0]); } [Fact] @@ -292,6 +355,9 @@ public void Write_NegativeOffsetInBufferTest() SqlBytes bytes = new SqlBytes(b2); Assert.Throws(() => bytes.Write(0, b1, -1, b1.Length)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Write(b1, -1, b1.Length)); } [Fact] @@ -300,8 +366,10 @@ public void Write_InvalidOffsetInBufferTest() byte[] b1 = { 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; byte[] b2 = new byte[10]; SqlBytes bytes = new SqlBytes(b2); - Assert.Throws(() => bytes.Write(0, b1, b1.Length + 5, b1.Length)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Write(b1, b1.Length + 5, b1.Length)); } [Fact] @@ -312,6 +380,9 @@ public void Write_InvalidCountTest1() SqlBytes bytes = new SqlBytes(b2); Assert.Throws(() => bytes.Write(0, b1, 0, b1.Length + 5)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Write(b1, 0, b1.Length + 5)); } [Fact] @@ -322,6 +393,13 @@ public void Write_InvalidCountTest2() SqlBytes bytes = new SqlBytes(b2); Assert.Throws(() => bytes.Write(8, b1, 0, b1.Length)); + + Stream stream = bytes.Stream; + const int offsetEmulation = 8; + stream.Write(b1, 0, offsetEmulation); + + Assert.Equal(offsetEmulation, stream.Position); + Assert.Throws(() => stream.Write(b1, 0, b1.Length)); } [Fact] @@ -332,6 +410,9 @@ public void Write_NullBufferTest() SqlBytes bytes = new SqlBytes(b1); Assert.Throws(() => bytes.Write(0, b2, 0, 10)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Write(b2, 0, 10)); } [Fact] @@ -341,6 +422,10 @@ public void Write_NullInstanceValueTest() SqlBytes bytes = new SqlBytes(); Assert.Throws(() => bytes.Write(0, b1, 0, 10)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Write(b1, 0, 10)); + Assert.Throws(() => stream.Write(b1.AsSpan(0, 10))); } [Fact] @@ -350,6 +435,9 @@ public void Write_NullBufferAndInstanceValueTest() SqlBytes bytes = new SqlBytes(); Assert.Throws(() => bytes.Write(0, b1, 0, 10)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Write(b1, 0, 10)); } [Fact] @@ -362,6 +450,18 @@ public void Write_SuccessTest2() bytes.Write(8, b1, 0, 10); Assert.Equal(bytes.Value[8], b1[0]); Assert.Equal(bytes.Value[17], b1[9]); + + Span b2Span = b2.AsSpan(); + b2Span.Clear(); + + const int offsetEmulate = 8; + Stream stream = bytes.Stream; + stream.Write(b1, 0, offsetEmulate); + Assert.Equal(offsetEmulate, stream.Position); + + stream.Write(b1, 0, 10); + Assert.Equal(bytes.Value[8], b1[0]); + Assert.Equal(bytes.Value[17], b1[9]); } [Fact] @@ -372,6 +472,9 @@ public void Write_NegativeCountTest() SqlBytes bytes = new SqlBytes(b2); Assert.Throws(() => bytes.Write(0, b1, 0, -1)); + + Stream stream = bytes.Stream; + Assert.Throws(() => stream.Write(b1, 0, -1)); } [Fact] From 8043e95d7a8330b347378e08315ed922992f21a5 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Wed, 24 May 2023 02:01:52 +0300 Subject: [PATCH 03/10] small tests update --- .../System/Data/SqlTypes/SqlBytesTest.cs | 73 +++++++++++-------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs index f48dc6eae4b8f1..7c51e4e87897ff 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlBytesTest.cs @@ -155,7 +155,7 @@ public void Read_SuccessTest1() Span b2Span = b2.AsSpan(); b2Span.Clear(); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; read = stream.Read(b2, 0, (int) bytes.Length); Assert.Equal(bytes.Length, read); Assert.Equal(bytes.Value[5], b2[5]); @@ -177,7 +177,7 @@ public void Read_NullBufferTest() Assert.Throws(() => bytes.Read(0, b2, 0, 10)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Read(b2, 0, 10)); } @@ -190,7 +190,7 @@ public void Read_InvalidCountTest1() Assert.Throws(() => bytes.Read(0, b2, 0, 10)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Read(b2, 0, 10)); } @@ -232,7 +232,7 @@ public void Read_NullInstanceValueTest() Assert.Throws(() => bytes.Read(0, b2, 0, 1)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Read(b2, 0, 1)); } @@ -245,26 +245,29 @@ public void Read_SuccessTest2() const long offset = 5; long read = bytes.Read(offset, b2, 0, 10); + byte[] currentValue = bytes.Value; Assert.Equal(b1.Length - offset, read); - Assert.Equal(bytes.Value[5], b2[0]); - Assert.Equal(bytes.Value[9], b2[4]); + Assert.Equal(currentValue[5], b2[0]); + Assert.Equal(currentValue[9], b2[4]); Span b2Span = b2.AsSpan(); b2Span.Clear(); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; read = stream.Read(b2, 0, 10); Assert.Equal(10, read); - Assert.Equal(bytes.Value[5], b2[5]); - Assert.Equal(bytes.Value[9], b2[9]); + currentValue = bytes.Value; + Assert.Equal(currentValue[5], b2[5]); + Assert.Equal(currentValue[9], b2[9]); b2Span.Clear(); stream.Position = 0; read = stream.Read(b2Span.Slice(0, 10)); Assert.Equal(10, read); - Assert.Equal(bytes.Value[5], b2Span[5]); - Assert.Equal(bytes.Value[9], b2Span[9]); + currentValue = bytes.Value; + Assert.Equal(currentValue[5], b2Span[5]); + Assert.Equal(currentValue[9], b2Span[9]); } [Fact] @@ -285,7 +288,7 @@ public void Read_NegativeCountTest() Assert.Throws(() => bytes.Read(0, b2, 0, -1)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Read(b2, 0, -1)); } @@ -298,7 +301,7 @@ public void Read_InvalidCountTest2() Assert.Throws(() => bytes.Read(0, b2, 3, 4)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Read(b2, 3, 4)); } @@ -316,7 +319,7 @@ public void Write_SuccessTest1() Span b2Span = b2.AsSpan(); b2Span.Clear(); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; stream.Write(b1, 0, b1.Length); Assert.Equal(bytes.Value[0], b1[0]); @@ -356,7 +359,7 @@ public void Write_NegativeOffsetInBufferTest() Assert.Throws(() => bytes.Write(0, b1, -1, b1.Length)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Write(b1, -1, b1.Length)); } @@ -368,7 +371,7 @@ public void Write_InvalidOffsetInBufferTest() SqlBytes bytes = new SqlBytes(b2); Assert.Throws(() => bytes.Write(0, b1, b1.Length + 5, b1.Length)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Write(b1, b1.Length + 5, b1.Length)); } @@ -381,7 +384,7 @@ public void Write_InvalidCountTest1() Assert.Throws(() => bytes.Write(0, b1, 0, b1.Length + 5)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Write(b1, 0, b1.Length + 5)); } @@ -394,11 +397,10 @@ public void Write_InvalidCountTest2() Assert.Throws(() => bytes.Write(8, b1, 0, b1.Length)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; const int offsetEmulation = 8; - stream.Write(b1, 0, offsetEmulation); + stream.Position = offsetEmulation; - Assert.Equal(offsetEmulation, stream.Position); Assert.Throws(() => stream.Write(b1, 0, b1.Length)); } @@ -411,7 +413,7 @@ public void Write_NullBufferTest() Assert.Throws(() => bytes.Write(0, b2, 0, 10)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Write(b2, 0, 10)); } @@ -423,7 +425,7 @@ public void Write_NullInstanceValueTest() Assert.Throws(() => bytes.Write(0, b1, 0, 10)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Write(b1, 0, 10)); Assert.Throws(() => stream.Write(b1.AsSpan(0, 10))); } @@ -436,7 +438,7 @@ public void Write_NullBufferAndInstanceValueTest() Assert.Throws(() => bytes.Write(0, b1, 0, 10)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Write(b1, 0, 10)); } @@ -448,20 +450,29 @@ public void Write_SuccessTest2() SqlBytes bytes = new SqlBytes(b2); bytes.Write(8, b1, 0, 10); - Assert.Equal(bytes.Value[8], b1[0]); - Assert.Equal(bytes.Value[17], b1[9]); + byte[] currentValue = bytes.Value; + Assert.Equal(currentValue[8], b1[0]); + Assert.Equal(currentValue[17], b1[9]); Span b2Span = b2.AsSpan(); b2Span.Clear(); const int offsetEmulate = 8; - Stream stream = bytes.Stream; - stream.Write(b1, 0, offsetEmulate); - Assert.Equal(offsetEmulate, stream.Position); + using Stream stream = bytes.Stream; + stream.Position = offsetEmulate; stream.Write(b1, 0, 10); - Assert.Equal(bytes.Value[8], b1[0]); - Assert.Equal(bytes.Value[17], b1[9]); + currentValue = bytes.Value; + Assert.Equal(currentValue[8], b1[0]); + Assert.Equal(currentValue[17], b1[9]); + + b2Span.Clear(); + stream.Position = offsetEmulate; + + stream.Write(b1.AsSpan(0, 10)); + currentValue = bytes.Value; + Assert.Equal(currentValue[8], b1[0]); + Assert.Equal(currentValue[17], b1[9]); } [Fact] @@ -473,7 +484,7 @@ public void Write_NegativeCountTest() Assert.Throws(() => bytes.Write(0, b1, 0, -1)); - Stream stream = bytes.Stream; + using Stream stream = bytes.Stream; Assert.Throws(() => stream.Write(b1, 0, -1)); } From fbe8e62b95bdc41fa448bb9e48bc911d9f92e760 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Wed, 24 May 2023 02:38:33 +0300 Subject: [PATCH 04/10] Reroute single byte read/write to span version --- .../System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index 7697e1f8ebcd40..d2fd477fba777c 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -202,7 +202,7 @@ public byte this[long offset] _rgbWorkBuf ??= new byte[1]; - Read(offset, _rgbWorkBuf, 0, 1); + Read(offset, _rgbWorkBuf.AsSpan(0, 1)); return _rgbWorkBuf[0]; } set @@ -210,7 +210,7 @@ public byte this[long offset] _rgbWorkBuf ??= new byte[1]; _rgbWorkBuf[0] = value; - Write(offset, _rgbWorkBuf, 0, 1); + Write(offset, _rgbWorkBuf.AsSpan(0, 1)); } } From cece5b2e3d835a04e534c1de7bbf945f0b5c7927 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Fri, 26 May 2023 20:25:16 +0300 Subject: [PATCH 05/10] Remove _rgbWorkBuf --- .../src/System/Data/SQLTypes/SQLBytes.cs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index d2fd477fba777c..adb980f7815d7b 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -47,8 +47,6 @@ public sealed class SqlBytes : INullable, IXmlSerializable, ISerializable internal Stream? _stream; private SqlBytesCharsState _state; - private byte[]? _rgbWorkBuf; // A 1-byte work buffer. - // The max data length that we support at this time. private const long x_lMaxLen = int.MaxValue; @@ -80,8 +78,6 @@ public SqlBytes(byte[]? buffer) _lCurLen = _rgbBuf.Length; } - _rgbWorkBuf = null; - AssertValid(); } @@ -98,8 +94,6 @@ public SqlBytes(Stream? s) _stream = s; _state = (s == null) ? SqlBytesCharsState.Null : SqlBytesCharsState.Stream; - _rgbWorkBuf = null; - AssertValid(); } @@ -200,17 +194,14 @@ public byte this[long offset] ArgumentOutOfRangeException.ThrowIfNegative(offset); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, Length); - _rgbWorkBuf ??= new byte[1]; + byte b = 0; - Read(offset, _rgbWorkBuf.AsSpan(0, 1)); - return _rgbWorkBuf[0]; + Read(offset, new Span(ref b)); + return b; } set { - _rgbWorkBuf ??= new byte[1]; - - _rgbWorkBuf[0] = value; - Write(offset, _rgbWorkBuf.AsSpan(0, 1)); + Write(offset, new ReadOnlySpan(in value)); } } @@ -476,7 +467,6 @@ private void AssertValid() Debug.Assert(FStream() || (_rgbBuf != null && _lCurLen <= _rgbBuf.Length)); Debug.Assert(!FStream() || (_lCurLen == x_lNull)); } - Debug.Assert(_rgbWorkBuf == null || _rgbWorkBuf.Length == 1); } // Copy the data from the Stream to the array buffer. From d6e3c28bc0f5dc8c03ffcaabd79cd24d17c67247 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Fri, 26 May 2023 20:40:58 +0300 Subject: [PATCH 06/10] Add WriteInternal --- .../src/System/Data/SQLTypes/SQLBytes.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index adb980f7815d7b..f1cf12c207d1c0 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -740,22 +740,26 @@ public override int Read(Span buffer) return ReadInternal(buffer); } + private void WriteInternal(ReadOnlySpan buffer) + { + _sb.Write(_lPosition, buffer); + _lPosition += buffer.Length; + } + public override void Write(byte[] buffer, int offset, int count) { CheckIfStreamClosed(); ValidateBufferArguments(buffer, offset, count); - _sb.Write(_lPosition, buffer, offset, count); - _lPosition += count; + WriteInternal(buffer); } public override void Write(ReadOnlySpan buffer) { CheckIfStreamClosed(); - _sb.Write(_lPosition, buffer); - _lPosition += buffer.Length; + WriteInternal(buffer); } public override int ReadByte() From 19c9e51d12ad36fa34f487e1f0cbdd7de79c3202 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Fri, 26 May 2023 21:19:42 +0300 Subject: [PATCH 07/10] Internal -> NoValidation --- .../src/System/Data/SQLTypes/SQLBytes.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index f1cf12c207d1c0..10a10e176a3630 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -281,7 +281,7 @@ public void SetLength(long value) AssertValid(); } - private long ReadInternal(Span buffer, long offset) + private long ReadNoValidation(Span buffer, long offset) { if (_state == SqlBytesCharsState.Stream) { @@ -307,7 +307,7 @@ internal long Read(long offset, Span buffer) ArgumentOutOfRangeException.ThrowIfGreaterThan(offset, Length); ArgumentOutOfRangeException.ThrowIfNegative(offset); - return ReadInternal(buffer, offset); + return ReadNoValidation(buffer, offset); } // Read data of specified length from specified offset into a buffer public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) @@ -327,10 +327,10 @@ public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) ArgumentOutOfRangeException.ThrowIfNegative(count); ArgumentOutOfRangeException.ThrowIfGreaterThan(count, buffer.Length - offsetInBuffer); - return ReadInternal(buffer.AsSpan(offsetInBuffer, count), offset); + return ReadNoValidation(buffer.AsSpan(offsetInBuffer, count), offset); } - private void WriteInternal(long offset, ReadOnlySpan buffer) + private void WriteNoValidation(long offset, ReadOnlySpan buffer) { if (IsNull) { @@ -388,7 +388,7 @@ internal void Write(long offset, ReadOnlySpan buffer) if (buffer.Length > _rgbBuf.Length - offset) throw new SqlTypeException(SR.SqlMisc_BufferInsufficientMessage); - WriteInternal(offset, buffer); + WriteNoValidation(offset, buffer); } } @@ -422,7 +422,7 @@ public void Write(long offset, byte[] buffer, int offsetInBuffer, int count) if (count > _rgbBuf.Length - offset) throw new SqlTypeException(SR.SqlMisc_BufferInsufficientMessage); - WriteInternal(offset, buffer.AsSpan(offsetInBuffer, count)); + WriteNoValidation(offset, buffer.AsSpan(offsetInBuffer, count)); } AssertValid(); @@ -716,7 +716,7 @@ public override long Seek(long offset, SeekOrigin origin) return _lPosition; } - private int ReadInternal(Span buffer) + private int ReadNoValidation(Span buffer) { int bytesRead = (int)_sb.Read(_lPosition, buffer); _lPosition += bytesRead; @@ -730,17 +730,17 @@ public override int Read(byte[] buffer, int offset, int count) ValidateBufferArguments(buffer, offset, count); - return ReadInternal(buffer.AsSpan(offset, count)); + return ReadNoValidation(buffer.AsSpan(offset, count)); } public override int Read(Span buffer) { CheckIfStreamClosed(); - return ReadInternal(buffer); + return ReadNoValidation(buffer); } - private void WriteInternal(ReadOnlySpan buffer) + private void WriteNoValidation(ReadOnlySpan buffer) { _sb.Write(_lPosition, buffer); _lPosition += buffer.Length; @@ -752,14 +752,14 @@ public override void Write(byte[] buffer, int offset, int count) ValidateBufferArguments(buffer, offset, count); - WriteInternal(buffer); + WriteNoValidation(buffer); } public override void Write(ReadOnlySpan buffer) { CheckIfStreamClosed(); - WriteInternal(buffer); + WriteNoValidation(buffer); } public override int ReadByte() From b143fb8f3a7dbe485566ae106be1d4e5ea181001 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Fri, 26 May 2023 22:10:32 +0300 Subject: [PATCH 08/10] Swap arguments --- .../System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index 10a10e176a3630..7b8e2f6fac8e60 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -281,7 +281,7 @@ public void SetLength(long value) AssertValid(); } - private long ReadNoValidation(Span buffer, long offset) + private long ReadNoValidation(long offset, Span buffer) { if (_state == SqlBytesCharsState.Stream) { @@ -307,7 +307,7 @@ internal long Read(long offset, Span buffer) ArgumentOutOfRangeException.ThrowIfGreaterThan(offset, Length); ArgumentOutOfRangeException.ThrowIfNegative(offset); - return ReadNoValidation(buffer, offset); + return ReadNoValidation(offset, buffer); } // Read data of specified length from specified offset into a buffer public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) @@ -327,7 +327,7 @@ public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) ArgumentOutOfRangeException.ThrowIfNegative(count); ArgumentOutOfRangeException.ThrowIfGreaterThan(count, buffer.Length - offsetInBuffer); - return ReadNoValidation(buffer.AsSpan(offsetInBuffer, count), offset); + return ReadNoValidation(offset, buffer.AsSpan(offsetInBuffer, count)); } private void WriteNoValidation(long offset, ReadOnlySpan buffer) From 12b65403957313ec3f19a2fcd08c1ecef66f2086 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Fri, 26 May 2023 22:18:09 +0300 Subject: [PATCH 09/10] Swap methods --- .../src/System/Data/SQLTypes/SQLBytes.cs | 97 ++++++++++--------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index 7b8e2f6fac8e60..702f95e257f52a 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -281,24 +281,6 @@ public void SetLength(long value) AssertValid(); } - private long ReadNoValidation(long offset, Span buffer) - { - if (_state == SqlBytesCharsState.Stream) - { - if (_stream!.Position != offset) - _stream.Seek(offset, SeekOrigin.Begin); - return _stream.Read(buffer); - } - - // Adjust count based on data length - int count = Math.Min(buffer.Length, (int)(Length - offset)); - - Span span = _rgbBuf!.AsSpan((int)offset, count); - span.CopyTo(buffer); - - return span.Length; - } - internal long Read(long offset, Span buffer) { if (IsNull) @@ -309,6 +291,7 @@ internal long Read(long offset, Span buffer) return ReadNoValidation(offset, buffer); } + // Read data of specified length from specified offset into a buffer public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) { @@ -330,41 +313,22 @@ public long Read(long offset, byte[] buffer, int offsetInBuffer, int count) return ReadNoValidation(offset, buffer.AsSpan(offsetInBuffer, count)); } - private void WriteNoValidation(long offset, ReadOnlySpan buffer) + private long ReadNoValidation(long offset, Span buffer) { - if (IsNull) - { - // If NULL and there is buffer inside, we only allow writing from - // offset zero. - // - if (offset != 0) - throw new SqlTypeException(SR.SqlMisc_WriteNonZeroOffsetOnNullMessage); - - // treat as if our current length is zero. - // Note this has to be done after all inputs are validated, so that - // we won't throw exception after this point. - // - _lCurLen = 0; - _state = SqlBytesCharsState.Buffer; - } - else if (offset > _lCurLen) + if (_state == SqlBytesCharsState.Stream) { - // Don't allow writing from an offset that this larger than current length. - // It would leave uninitialized data in the buffer. - // - throw new SqlTypeException(SR.SqlMisc_WriteOffsetLargerThanLenMessage); + if (_stream!.Position != offset) + _stream.Seek(offset, SeekOrigin.Begin); + return _stream.Read(buffer); } - if (buffer.Length != 0) - { - Span span = _rgbBuf.AsSpan((int)offset, buffer.Length); - buffer.CopyTo(span); + // Adjust count based on data length + int count = Math.Min(buffer.Length, (int)(Length - offset)); - // If the last position that has been written is after - // the current data length, reset the length - if (_lCurLen < offset + buffer.Length) - _lCurLen = offset + buffer.Length; - } + Span span = _rgbBuf!.AsSpan((int)offset, count); + span.CopyTo(buffer); + + return span.Length; } internal void Write(long offset, ReadOnlySpan buffer) @@ -428,6 +392,43 @@ public void Write(long offset, byte[] buffer, int offsetInBuffer, int count) AssertValid(); } + private void WriteNoValidation(long offset, ReadOnlySpan buffer) + { + if (IsNull) + { + // If NULL and there is buffer inside, we only allow writing from + // offset zero. + // + if (offset != 0) + throw new SqlTypeException(SR.SqlMisc_WriteNonZeroOffsetOnNullMessage); + + // treat as if our current length is zero. + // Note this has to be done after all inputs are validated, so that + // we won't throw exception after this point. + // + _lCurLen = 0; + _state = SqlBytesCharsState.Buffer; + } + else if (offset > _lCurLen) + { + // Don't allow writing from an offset that this larger than current length. + // It would leave uninitialized data in the buffer. + // + throw new SqlTypeException(SR.SqlMisc_WriteOffsetLargerThanLenMessage); + } + + if (buffer.Length != 0) + { + Span span = _rgbBuf.AsSpan((int)offset, buffer.Length); + buffer.CopyTo(span); + + // If the last position that has been written is after + // the current data length, reset the length + if (_lCurLen < offset + buffer.Length) + _lCurLen = offset + buffer.Length; + } + } + public SqlBinary ToSqlBinary() { return IsNull ? SqlBinary.Null : new SqlBinary(Value); From 970bff5e495bcc1ee70ec56608173f289c9db0b5 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Fri, 26 May 2023 22:34:21 +0300 Subject: [PATCH 10/10] One more method swap --- .../src/System/Data/SQLTypes/SQLBytes.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs index 702f95e257f52a..5980bc966e7e6f 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBytes.cs @@ -717,12 +717,11 @@ public override long Seek(long offset, SeekOrigin origin) return _lPosition; } - private int ReadNoValidation(Span buffer) + public override int Read(Span buffer) { - int bytesRead = (int)_sb.Read(_lPosition, buffer); - _lPosition += bytesRead; + CheckIfStreamClosed(); - return bytesRead; + return ReadNoValidation(buffer); } // The Read/Write/ReadByte/WriteByte simply delegates to SqlBytes public override int Read(byte[] buffer, int offset, int count) @@ -734,17 +733,19 @@ public override int Read(byte[] buffer, int offset, int count) return ReadNoValidation(buffer.AsSpan(offset, count)); } - public override int Read(Span buffer) + private int ReadNoValidation(Span buffer) { - CheckIfStreamClosed(); + int bytesRead = (int)_sb.Read(_lPosition, buffer); + _lPosition += bytesRead; - return ReadNoValidation(buffer); + return bytesRead; } - private void WriteNoValidation(ReadOnlySpan buffer) + public override void Write(ReadOnlySpan buffer) { - _sb.Write(_lPosition, buffer); - _lPosition += buffer.Length; + CheckIfStreamClosed(); + + WriteNoValidation(buffer); } public override void Write(byte[] buffer, int offset, int count) @@ -756,11 +757,10 @@ public override void Write(byte[] buffer, int offset, int count) WriteNoValidation(buffer); } - public override void Write(ReadOnlySpan buffer) + private void WriteNoValidation(ReadOnlySpan buffer) { - CheckIfStreamClosed(); - - WriteNoValidation(buffer); + _sb.Write(_lPosition, buffer); + _lPosition += buffer.Length; } public override int ReadByte()