diff --git a/scripts/install-dotnet.ps1 b/scripts/install-dotnet.ps1 index 15cfb72fcca..92f1e58ea8c 100644 --- a/scripts/install-dotnet.ps1 +++ b/scripts/install-dotnet.ps1 @@ -253,7 +253,7 @@ function Get-Download-Link([string]$AzureFeed, [string]$Channel, [string]$Specif $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip" } else { - $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-dev-$SpecificVersion-win-$CLIArchitecture.zip" + $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip" } Say-Verbose "Constructed primary payload URL: $PayloadURL" diff --git a/scripts/install-dotnet.sh b/scripts/install-dotnet.sh index 99857d7d726..15108013d8d 100755 --- a/scripts/install-dotnet.sh +++ b/scripts/install-dotnet.sh @@ -400,7 +400,7 @@ construct_download_link() { if [ "$shared_runtime" = true ]; then download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_version-$osname-$normalized_architecture.tar.gz" else - download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$specific_version-$osname-$normalized_architecture.tar.gz" + download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_version-$osname-$normalized_architecture.tar.gz" fi echo "$download_link" diff --git a/src/System.Collections.Generic.MultiValueDictionary/System/Collections/Generic/MultiValueDictionary.cs b/src/System.Collections.Generic.MultiValueDictionary/System/Collections/Generic/MultiValueDictionary.cs index c459f8aa097..800a28d8544 100644 --- a/src/System.Collections.Generic.MultiValueDictionary/System/Collections/Generic/MultiValueDictionary.cs +++ b/src/System.Collections.Generic.MultiValueDictionary/System/Collections/Generic/MultiValueDictionary.cs @@ -951,7 +951,7 @@ public bool MoveNext() } else if (enumerator.MoveNext()) { - current = new KeyValuePair>(enumerator.Current.Key, (IReadOnlyCollection)enumerator.Current.Value); + current = new KeyValuePair>(enumerator.Current.Key, enumerator.Current.Value); state = EnumerationState.During; return true; } diff --git a/src/System.IO.Compression.Brotli/Resources/BrotliEx.Designer.cs b/src/System.IO.Compression.Brotli/Resources/BrotliEx.Designer.cs index 71486d0f038..dd7271e7315 100644 --- a/src/System.IO.Compression.Brotli/Resources/BrotliEx.Designer.cs +++ b/src/System.IO.Compression.Brotli/Resources/BrotliEx.Designer.cs @@ -97,6 +97,15 @@ internal static string InvalidArgument { } } + /// + /// Looks up a localized string similar to Mode change is not permitted . + /// + internal static string InvalidModeChange { + get { + return ResourceManager.GetString("InvalidModeChange", resourceCulture); + } + } + /// /// Looks up a localized string similar to Quality and WindowSize is ambitious for Decompress mode. /// diff --git a/src/System.IO.Compression.Brotli/Resources/BrotliEx.resx b/src/System.IO.Compression.Brotli/Resources/BrotliEx.resx index 6026a452418..d7cdc32491a 100644 --- a/src/System.IO.Compression.Brotli/Resources/BrotliEx.resx +++ b/src/System.IO.Compression.Brotli/Resources/BrotliEx.resx @@ -159,4 +159,7 @@ Error WriteTimeout exceeded + + Mode change is not permitted + \ No newline at end of file diff --git a/src/System.IO.Compression.Brotli/System/IO/Compression/Brotli.cs b/src/System.IO.Compression.Brotli/System/IO/Compression/Brotli.cs index 3ae06a26c44..8e3c48f1816 100644 --- a/src/System.IO.Compression.Brotli/System/IO/Compression/Brotli.cs +++ b/src/System.IO.Compression.Brotli/System/IO/Compression/Brotli.cs @@ -4,15 +4,116 @@ #if BIT64 using nuint = System.UInt64; #else -using nuint = System.UInt32; + using nuint = System.UInt32; #endif namespace System.IO.Compression { public static class Brotli { - const int DefaultQuality = 11; - const int DefaultWindowSize = 24; + private const int MinWindowBits = 10; + private const int MaxWindowBits = 24; + private const int MinQuality = 0; + private const int MaxQuality = 11; + + public struct State : IDisposable + { + internal IntPtr BrotliNativeState { get; private set; } + internal BrotliDecoderResult LastDecoderResult; + public bool CompressMode { get; private set; } + + public void Dispose() + { + if (CompressMode) + { + BrotliNative.BrotliEncoderDestroyInstance(BrotliNativeState); + } + else + { + BrotliNative.BrotliDecoderDestroyInstance(BrotliNativeState); + } + } + + internal void InitializeDecoder() + { + BrotliNativeState = BrotliNative.BrotliDecoderCreateInstance(); + LastDecoderResult = BrotliDecoderResult.NeedsMoreInput; + if (BrotliNativeState == IntPtr.Zero) + { + throw new System.Exception(BrotliEx.DecoderInstanceCreate); + } + CompressMode = false; + } + + internal void InitializeEncoder() + { + + BrotliNativeState = BrotliNative.BrotliEncoderCreateInstance(); + if (BrotliNativeState == IntPtr.Zero) + { + throw new System.Exception(BrotliEx.EncoderInstanceCreate); + } + CompressMode = true; + } + + public void SetQuality(uint quality) + { + if (BrotliNativeState == IntPtr.Zero) + { + InitializeEncoder(); + } + if (quality > MaxQuality) + { + throw new ArgumentException(BrotliEx.WrongQuality); + } + BrotliNative.BrotliEncoderSetParameter(BrotliNativeState, BrotliEncoderParameter.Quality, quality); + } + + public void SetQuality() + { + SetQuality(MaxQuality); + } + + public void SetWindow(uint window) + { + if (BrotliNativeState == IntPtr.Zero) + { + InitializeEncoder(); + } + if (window - MinWindowBits > MaxWindowBits - MinWindowBits) + { + throw new ArgumentException(BrotliEx.WrongWindowSize); + } + BrotliNative.BrotliEncoderSetParameter(BrotliNativeState, BrotliEncoderParameter.LGWin, window); + } + + public void SetWindow() + { + SetWindow(MaxWindowBits); + } + } + + internal static void EnsureInitialized(ref State state, bool compress) + { + if (state.BrotliNativeState != IntPtr.Zero) + { + if (state.CompressMode != compress) + { + throw new System.Exception((BrotliEx.InvalidModeChange)); + } + return; + } + if (compress) + { + state.SetQuality(); + state.SetWindow(); + } + else + { + state.InitializeDecoder(); + state.LastDecoderResult = BrotliDecoderResult.NeedsMoreInput; + } + } public static int GetMaximumCompressedSize(int inputSize) { @@ -40,16 +141,14 @@ private static TransformationStatus GetTransformationStatusFromBrotliDecoderResu return TransformationStatus.InvalidData; } - public static TransformationStatus Compress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, CompressionLevel quality = (CompressionLevel)DefaultQuality, int windowSize = DefaultWindowSize, BrotliEncoderMode encMode = BrotliEncoderMode.Generic) + public static TransformationStatus FlushEncoder(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, ref State state, bool isFinished = true) { - return Compress(source, destination, out bytesConsumed, out bytesWritten, GetQualityFromCompressionLevel(quality), windowSize, encMode); - } - - internal static TransformationStatus Compress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, int quality = DefaultQuality, int windowSize = DefaultWindowSize, BrotliEncoderMode encMode = BrotliEncoderMode.Generic) - { - if (quality > DefaultQuality || quality < 0) throw new System.ArgumentOutOfRangeException(BrotliEx.WrongQuality); - if (windowSize > DefaultWindowSize || windowSize <= 0) throw new System.ArgumentOutOfRangeException(BrotliEx.WrongWindowSize); - bytesConsumed = bytesWritten = 0; + EnsureInitialized(ref state, true); + BrotliEncoderOperation operation = isFinished ? BrotliEncoderOperation.Finish : BrotliEncoderOperation.Flush; + bytesWritten = destination.Length; + bytesConsumed = 0; + if (state.BrotliNativeState == IntPtr.Zero) return TransformationStatus.InvalidData; + if (BrotliNative.BrotliEncoderIsFinished(state.BrotliNativeState)) return TransformationStatus.Done; unsafe { IntPtr bufIn, bufOut; @@ -58,22 +157,64 @@ internal static TransformationStatus Compress(ReadOnlySpan source, Span 0) + { + if (BrotliNative.BrotliEncoderIsFinished(state.BrotliNativeState)) return TransformationStatus.Done; + else return TransformationStatus.DestinationTooSmall; + } + } + return TransformationStatus.Done; + } + + public static TransformationStatus Compress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, ref State state) + { + EnsureInitialized(ref state, true); + bytesWritten = destination.Length; + bytesConsumed = source.Length; + unsafe + { + IntPtr bufIn, bufOut; + while (bytesConsumed > 0) + { + fixed (byte* inBytes = &source.DangerousGetPinnableReference()) + fixed (byte* outBytes = &destination.DangerousGetPinnableReference()) + { + bufIn = new IntPtr(inBytes); + bufOut = new IntPtr(outBytes); + nuint availableOutput = (nuint)bytesWritten; + nuint consumed = (nuint)bytesConsumed; + if (!BrotliNative.BrotliEncoderCompressStream(state.BrotliNativeState, BrotliEncoderOperation.Process, ref consumed, ref bufIn, ref availableOutput, ref bufOut, out nuint totalOut)) + { + return TransformationStatus.InvalidData; + }; + bytesConsumed = (int)consumed; + bytesWritten = destination.Length - (int)availableOutput; + if (availableOutput != (nuint)destination.Length) + { + return TransformationStatus.DestinationTooSmall; + } + } } + return TransformationStatus.Done; } } - public static TransformationStatus Decompress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten) + public static TransformationStatus Decompress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, ref State state) { - bytesConsumed = bytesWritten = 0; + EnsureInitialized(ref state, false); + bytesConsumed = source.Length; + bytesWritten = destination.Length; + if (BrotliNative.BrotliDecoderIsFinished(state.BrotliNativeState)) return TransformationStatus.Done; unsafe { IntPtr bufIn, bufOut; @@ -82,17 +223,34 @@ public static TransformationStatus Decompress(ReadOnlySpan source, Span - /// Generic - Default (Compressor does not know anything in advance properties of the input) - /// Text - For UTF-8 formatted text input - /// Font - Mode used in WOFF 2.0 - /// - public enum BrotliEncoderMode - { - Generic, - Text, - Font - }; - /// /// This class provides declaration for constants and PInvokes as well as some basic tools for exposing the /// native Brotli library to managed code. @@ -78,12 +66,6 @@ internal class BrotliNative #region Encoder - public static bool BrotliEncoderCompress(int quality, int windowSize, BrotliEncoderMode mode, nuint inputSize, - IntPtr inputBuffer, ref nuint encodedSize, IntPtr encodedBuffer) - { - return Interop.Brotli.BrotliEncoderCompress(quality, windowSize, mode, inputSize, inputBuffer, ref encodedSize, encodedBuffer); - } - public static IntPtr BrotliEncoderCreateInstance() { return Interop.Brotli.BrotliEncoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); @@ -126,11 +108,6 @@ public static UInt32 BrotliEncoderVersion() #region Decoder - public static BrotliDecoderResult BrotliDecoderDecompress(ref nuint availableIn, IntPtr nextIn, ref nuint availableOut, IntPtr nextOut) - { - return Interop.Brotli.BrotliDecoderDecompress(ref availableIn, nextIn, ref availableOut, nextOut); - } - public static IntPtr BrotliDecoderCreateInstance() { return Interop.Brotli.BrotliDecoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); diff --git a/src/System.IO.Compression.Brotli/System/IO/Compression/BrotliStream.cs b/src/System.IO.Compression.Brotli/System/IO/Compression/BrotliStream.cs index ddbf1987179..56f03c9b416 100644 --- a/src/System.IO.Compression.Brotli/System/IO/Compression/BrotliStream.cs +++ b/src/System.IO.Compression.Brotli/System/IO/Compression/BrotliStream.cs @@ -1,34 +1,34 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Buffers; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; using System.IO.Compression.Resources; -using System.Runtime.InteropServices; #if BIT64 using nuint = System.UInt64; #else -using nuint = System.UInt32; + using nuint = System.UInt32; #endif namespace System.IO.Compression { public partial class BrotliStream : Stream { - private const int DefaultBufferSize = (1 << 16) - 1; + private const int DefaultBufferSize = (1 << 16) - 16; //65520 private int _bufferSize; private Stream _stream; private CompressionMode _mode; - private nuint _availableOutput; - private IntPtr _nextOutput = IntPtr.Zero; - private nuint _availableInput; - private IntPtr _nextInput = IntPtr.Zero; - private IntPtr _bufferInput; - private IntPtr _bufferOutput; + private int _availableOutput; + private int _availableInput; + private byte[] _buffer; private bool _leaveOpen; private int totalWrote; - private int _readOffset = 0; - Decoder _decoder; - Encoder _encoder; + private Brotli.State _state; + private TransformationStatus _transformationResult; public override bool CanTimeout => true; @@ -38,26 +38,18 @@ public partial class BrotliStream : Stream public BrotliStream(Stream baseStream, CompressionMode mode, bool leaveOpen, int bufferSize, CompressionLevel quality) : this(baseStream, mode, leaveOpen, bufferSize) { - if (_mode == CompressionMode.Decompress) - { - throw new System.IO.IOException(BrotliEx.QualityAndWinSize); - } - else + if (_mode == CompressionMode.Compress) { - _encoder.SetQuality((uint)Brotli.GetQualityFromCompressionLevel(quality)); + _state.SetQuality((uint)Brotli.GetQualityFromCompressionLevel(quality)); } } public BrotliStream(Stream baseStream, CompressionMode mode, bool leaveOpen, int bufferSize, CompressionLevel quality, uint windowSize) : this(baseStream, mode, leaveOpen, bufferSize) { - if (_mode == CompressionMode.Decompress) - { - throw new System.IO.IOException(BrotliEx.QualityAndWinSize); - } - else + if (_mode == CompressionMode.Compress) { - _encoder.SetQuality((uint)Brotli.GetQualityFromCompressionLevel(quality)); - _encoder.SetWindow(windowSize); + _state.SetQuality((uint)Brotli.GetQualityFromCompressionLevel(quality)); + _state.SetWindow(windowSize); } } @@ -67,27 +59,24 @@ public BrotliStream(Stream baseStream, CompressionMode mode, bool leaveOpen = fa { throw new ArgumentNullException("baseStream"); } + _bufferSize = bufferSize; _mode = mode; _stream = baseStream; _leaveOpen = leaveOpen; + _state = new Brotli.State(); if (_mode == CompressionMode.Compress) { - _encoder = new Encoder(); - _encoder.SetQuality(); - _encoder.SetWindow(); + _state.SetQuality(); + _state.SetWindow(); WriteTimeout = 0; } else { - _decoder = new Decoder(); ReadTimeout = 0; } - _bufferSize = bufferSize; - _bufferInput = Marshal.AllocHGlobal(_bufferSize); - _bufferOutput = Marshal.AllocHGlobal(_bufferSize); - _nextInput = _bufferInput; - _nextOutput = _bufferOutput; - _availableOutput = (nuint)_bufferSize; + _buffer = new byte[_bufferSize]; + _transformationResult = TransformationStatus.NeedMoreSourceData; + _availableOutput = _bufferSize; } public override bool CanRead @@ -134,26 +123,19 @@ public override long Position protected virtual void FlushEncoder(bool finished) { - if (_encoder.State == IntPtr.Zero) return; - if (BrotliNative.BrotliEncoderIsFinished(_encoder.State)) return; - BrotliEncoderOperation op = finished ? BrotliEncoderOperation.Finish : BrotliEncoderOperation.Flush; - nuint totalOut = 0; - while (true) + if (_state.BrotliNativeState == IntPtr.Zero) return; + if (BrotliNative.BrotliEncoderIsFinished(_state.BrotliNativeState)) return; + TransformationStatus flushStatus = TransformationStatus.DestinationTooSmall; + while (flushStatus == TransformationStatus.DestinationTooSmall) { - if (!BrotliNative.BrotliEncoderCompressStream(_encoder.State, op, ref _availableInput, ref _nextInput, ref _availableOutput, ref _nextOutput, out totalOut)) - throw new System.IO.IOException(BrotliEx.unableEncode); - var extraData = (nuint)_availableOutput != (nuint)_bufferSize; - if (extraData) + flushStatus = Brotli.FlushEncoder(Array.Empty(), _buffer, out _availableInput, out _availableOutput, ref _state, finished); + _stream.Write(_buffer, 0, _availableOutput); + _availableOutput = _bufferSize; + + if (BrotliNative.BrotliEncoderIsFinished(_state.BrotliNativeState)) { - var bytesWrote = (int)((nuint)_bufferSize - (nuint)_availableOutput); - Byte[] buf = new Byte[bytesWrote]; - Marshal.Copy(_bufferOutput, buf, 0, bytesWrote); - _stream.Write(buf, 0, bytesWrote); - _availableOutput = (nuint)_bufferSize; - _nextOutput = _bufferOutput; + break; } - if (BrotliNative.BrotliEncoderIsFinished(_encoder.State)) break; - if (!extraData) break; } } @@ -165,28 +147,14 @@ protected override void Dispose(bool disposing) } try { - if (_bufferInput != IntPtr.Zero) Marshal.FreeHGlobal(_bufferInput); - if (_bufferOutput != IntPtr.Zero) Marshal.FreeHGlobal(_bufferOutput); - _bufferInput = IntPtr.Zero; - _bufferOutput = IntPtr.Zero; if (disposing && !_leaveOpen) _stream?.Dispose(); } finally { _stream = null; - try - { - _decoder?.Dispose(); - _encoder?.Dispose(); - } - finally - { - _encoder = null; - _decoder = null; - } + _state.Dispose(); base.Dispose(disposing); } - } public override void Flush() @@ -231,82 +199,31 @@ public override int Read(byte[] buffer, int offset, int count) ValidateParameters(buffer, offset, count); EnsureNotDisposed(); DateTime begin = DateTime.Now; - int bytesRead = (int)(_decoder.BufferStream.Length - _readOffset); - nuint totalCount = 0; - bool endOfStream = false; - bool errorDetected = false; - Byte[] buf = new Byte[_bufferSize]; - while (bytesRead < count) + _availableOutput = 0; + TimeSpan ExecutionTime = DateTime.Now - begin; + if (ReadTimeout > 0 && ExecutionTime.TotalMilliseconds >= ReadTimeout) { - TimeSpan ExecutionTime = DateTime.Now - begin; - if (ReadTimeout > 0 && ExecutionTime.TotalMilliseconds >= ReadTimeout) - { - throw new TimeoutException(BrotliEx.TimeoutRead); - } - while (true) + throw new TimeoutException(BrotliEx.TimeoutRead); + } + while (true) + { + if (_transformationResult == TransformationStatus.NeedMoreSourceData) { - if (_decoder.LastDecoderResult == BrotliDecoderResult.NeedsMoreInput) - { - _availableInput = (nuint)_stream.Read(buf, 0, (int)_bufferSize); - _nextInput = _bufferInput; - if ((int)_availableInput <= 0) - { - endOfStream = true; - break; - } - Marshal.Copy(buf, 0, _bufferInput, (int)_availableInput); - } - else if (_decoder.LastDecoderResult == BrotliDecoderResult.NeedsMoreOutput) - { - Marshal.Copy(_bufferOutput, buf, 0, _bufferSize); - _decoder.BufferStream.Write(buf, 0, _bufferSize); - bytesRead += _bufferSize; - _availableOutput = (nuint)_bufferSize; - _nextOutput = _bufferOutput; - } - else + _availableInput = _stream.Read(_buffer, 0, _bufferSize); + if ((int)_availableInput <= 0) { - //Error or OK - endOfStream = true; break; } - _decoder.LastDecoderResult = BrotliNative.BrotliDecoderDecompressStream(_decoder.State, ref _availableInput, ref _nextInput, - ref _availableOutput, ref _nextOutput, out totalCount); - if (bytesRead >= count) break; } - if (endOfStream && !BrotliNative.BrotliDecoderIsFinished(_decoder.State)) + else if (_transformationResult != TransformationStatus.DestinationTooSmall) { - errorDetected = true; + break; } - if (_decoder.LastDecoderResult == BrotliDecoderResult.Error || errorDetected) + _transformationResult = Brotli.Decompress(_buffer, buffer, out _availableInput, out _availableOutput, ref _state); + if (_availableOutput != 0) { - var error = BrotliNative.BrotliDecoderGetErrorCode(_decoder.State); - var text = BrotliNative.BrotliDecoderErrorString(error); - throw new System.IO.IOException(text + BrotliEx.unableDecode); + return _availableOutput; } - if (endOfStream && !BrotliNative.BrotliDecoderIsFinished(_decoder.State) && _decoder.LastDecoderResult == BrotliDecoderResult.NeedsMoreInput) - { - throw new System.IO.IOException(BrotliEx.FinishDecompress); - } - if (endOfStream && _nextOutput != _bufferOutput) - { - int remainBytes = (int)(_nextOutput.ToInt64() - _bufferOutput.ToInt64()); - bytesRead += remainBytes; - Marshal.Copy(_bufferOutput, buf, 0, remainBytes); - _decoder.BufferStream.Write(buf, 0, remainBytes); - _nextOutput = _bufferOutput; - } - if (endOfStream) break; - } - if (_decoder.BufferStream.Length - _readOffset >= count || endOfStream) - { - _decoder.BufferStream.Seek(_readOffset, SeekOrigin.Begin); - var bytesToRead = (int)(_decoder.BufferStream.Length - _readOffset); - if (bytesToRead > count) bytesToRead = count; - _decoder.BufferStream.Read(buffer, offset, bytesToRead); - _decoder.RemoveBytes(_readOffset + bytesToRead); - _readOffset = 0; - return bytesToRead; } return 0; } @@ -330,7 +247,6 @@ public override void Write(byte[] buffer, int offset, int count) if (_mode != CompressionMode.Compress) totalWrote += count; DateTime begin = DateTime.Now; - nuint totalOut = 0; int bytesRemain = count; int currentOffset = offset; int copyLen; @@ -342,27 +258,19 @@ public override void Write(byte[] buffer, int offset, int count) throw new TimeoutException(BrotliEx.TimeoutWrite); } copyLen = bytesRemain > _bufferSize ? _bufferSize : bytesRemain; - Marshal.Copy(buffer, currentOffset, _bufferInput, copyLen); - bytesRemain -= copyLen; - currentOffset += copyLen; - _availableInput = (nuint)copyLen; - _nextInput = _bufferInput; - while ((int)_availableInput > 0) + Span bufferInput = new Span(buffer, currentOffset, copyLen); + _transformationResult = TransformationStatus.DestinationTooSmall; + _transformationResult = Brotli.Compress(bufferInput, _buffer, out _availableInput, out _availableOutput, ref _state); + if (_transformationResult == TransformationStatus.InvalidData) { - if (!BrotliNative.BrotliEncoderCompressStream(_encoder.State, BrotliEncoderOperation.Process, ref _availableInput, ref _nextInput, ref _availableOutput, - ref _nextOutput, out totalOut)) throw new System.IO.IOException(BrotliEx.unableEncode); - - if (_availableOutput != (nuint)_bufferSize) - { - var bytesWrote = (int)((nuint)_bufferSize - _availableOutput); - Byte[] buf = new Byte[bytesWrote]; - Marshal.Copy(_bufferOutput, buf, 0, bytesWrote); - _stream.Write(buf, 0, bytesWrote); - _availableOutput = (nuint)_bufferSize; - _nextOutput = _bufferOutput; - } + throw new System.Exception(BrotliEx.unableEncode); + } + if (_transformationResult == TransformationStatus.DestinationTooSmall) + { + _stream.Write(_buffer, 0, _availableOutput); } - if (BrotliNative.BrotliEncoderIsFinished(_encoder.State)) break; + bytesRemain -= copyLen; + currentOffset += copyLen; } } } diff --git a/src/System.IO.Compression.Brotli/System/Interop/Interop.Brotli.cs b/src/System.IO.Compression.Brotli/System/Interop/Interop.Brotli.cs index cee29ccfdd7..77ef5581290 100644 --- a/src/System.IO.Compression.Brotli/System/Interop/Interop.Brotli.cs +++ b/src/System.IO.Compression.Brotli/System/Interop/Interop.Brotli.cs @@ -20,10 +20,6 @@ internal static partial class Brotli #region Encoder - [DllImport(LibNameEncoder, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool BrotliEncoderCompress(int quality, int lgwin, BrotliEncoderMode mode, nuint input_size, - IntPtr input_buffer, ref nuint encoded_size, IntPtr encoded_buffer); - [DllImport(LibNameEncoder, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr BrotliEncoderCreateInstance(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque); @@ -51,10 +47,6 @@ internal static extern bool BrotliEncoderCompressStream( #region Decoder - [DllImport(LibNameDecoder, CallingConvention = CallingConvention.Cdecl)] - internal static extern BrotliDecoderResult BrotliDecoderDecompress(ref nuint availableIn, IntPtr nextIn, - ref nuint availableOut, IntPtr nextOut); - [DllImport(LibNameDecoder, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr BrotliDecoderCreateInstance(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque); diff --git a/src/System.IO.Pipelines.Compression/Interop/Interop.zlib.Windows.cs b/src/System.IO.Pipelines.Compression/Interop/Interop.zlib.Windows.cs index 7106c8e1864..1a5790bfd54 100644 --- a/src/System.IO.Pipelines.Compression/Interop/Interop.zlib.Windows.cs +++ b/src/System.IO.Pipelines.Compression/Interop/Interop.zlib.Windows.cs @@ -60,7 +60,7 @@ internal static unsafe ZLibNative.ErrorCode DeflateInit2_( byte* pBytes = (byte*) streamBytes; return (ZLibNative.ErrorCode) - deflateInit2_(pBytes, (int) level, (int) method, (int) windowBits, (int) memLevel, + deflateInit2_(pBytes, (int) level, (int) method, windowBits, memLevel, (int) strategy, versionString, sizeof(ZLibNative.ZStream)); } } @@ -100,7 +100,7 @@ internal static unsafe ZLibNative.ErrorCode InflateInit2_( byte* pBytes = (byte*) streamBytes; return (ZLibNative.ErrorCode) - inflateInit2_(pBytes, (int) windowBits, versionString, sizeof(ZLibNative.ZStream)); + inflateInit2_(pBytes, windowBits, versionString, sizeof(ZLibNative.ZStream)); } } diff --git a/src/System.IO.Pipelines.Compression/ZLibNative.cs b/src/System.IO.Pipelines.Compression/ZLibNative.cs index ea36e04fe32..9afd612dfda 100644 --- a/src/System.IO.Pipelines.Compression/ZLibNative.cs +++ b/src/System.IO.Pipelines.Compression/ZLibNative.cs @@ -19,7 +19,7 @@ internal static partial class ZLibNative // This is the NULL pointer for using with ZLib pointers; // we prefer it to IntPtr.Zero to mimic the definition of Z_NULL in zlib.h: - internal static readonly IntPtr ZNullPtr = (IntPtr)((Int32)0); + internal static readonly IntPtr ZNullPtr = (IntPtr)0; public enum FlushCode : int { diff --git a/src/System.Text.Primitives/System/Text/Encoding/SymbolTable.Symbol.cs b/src/System.Text.Primitives/System/Text/Encoding/SymbolTable.Symbol.cs index 0511c8bb59c..7e6e317469c 100644 --- a/src/System.Text.Primitives/System/Text/Encoding/SymbolTable.Symbol.cs +++ b/src/System.Text.Primitives/System/Text/Encoding/SymbolTable.Symbol.cs @@ -5,6 +5,7 @@ namespace System.Text { public partial class SymbolTable { + // Do not change the specific enum values without careful consideration of the impacts to the parsers. public enum Symbol : ushort { D0 = (ushort)0, D1 = (ushort)1, diff --git a/src/System.Text.Primitives/System/Text/Parsing/InvariantSigned.cs b/src/System.Text.Primitives/System/Text/Parsing/InvariantSigned.cs index 8e7ccaaa06c..14cbb0e4fe3 100644 --- a/src/System.Text.Primitives/System/Text/Parsing/InvariantSigned.cs +++ b/src/System.Text.Primitives/System/Text/Parsing/InvariantSigned.cs @@ -896,176 +896,122 @@ public unsafe static bool TryParseInt32(byte* text, int length, out int value, o public static bool TryParseInt32(ReadOnlySpan text, out int value) { - if (text.Length < 1) - { - value = default; - return false; - } - - int indexOfFirstDigit = 0; - int sign = 1; - if (text[0] == '-') - { - indexOfFirstDigit = 1; - sign = -1; - } - else if (text[0] == '+') - { - indexOfFirstDigit = 1; - } - - int overflowLength = Int32OverflowLength + indexOfFirstDigit; - - // Parse the first digit separately. If invalid here, we need to return false. - int firstDigit = text[indexOfFirstDigit] - 48; // '0' - if (firstDigit < 0 || firstDigit > 9) - { - value = default; - return false; - } - int parsedValue = firstDigit; - - if (text.Length < overflowLength) - { - // Length is less than Int32OverflowLength; overflow is not possible - for (int index = indexOfFirstDigit + 1; index < text.Length; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - value = parsedValue * sign; - return true; - } - parsedValue = parsedValue * 10 + nextDigit; - } - } - else - { - // Length is greater than Int32OverflowLength; overflow is only possible after Int32OverflowLength - // digits. There may be no overflow after Int32OverflowLength if there are leading zeroes. - for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - value = parsedValue * sign; - return true; - } - parsedValue = parsedValue * 10 + nextDigit; - } - for (int index = overflowLength - 1; index < text.Length; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - value = parsedValue * sign; - return true; - } - // If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow. - // if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. - bool positive = sign > 0; - bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7); - if (parsedValue > int.MaxValue / 10 || parsedValue == int.MaxValue / 10 && nextDigitTooLarge) - { - value = default; - return false; - } - parsedValue = parsedValue * 10 + nextDigit; - } - } - - value = parsedValue * sign; - return true; + return TryParseInt32(text, out value, out int bytesConsumed); } public static bool TryParseInt32(ReadOnlySpan text, out int value, out int bytesConsumed) { - if (text.Length < 1) - { - bytesConsumed = 0; - value = default; - return false; - } + if (text.Length < 1) goto FalseExit; - int indexOfFirstDigit = 0; int sign = 1; - if (text[0] == '-') + int index = 0; + int num = text[index]; + if (num == '-') { - indexOfFirstDigit = 1; sign = -1; - } - else if (text[0] == '+') - { - indexOfFirstDigit = 1; - } - - int overflowLength = Int32OverflowLength + indexOfFirstDigit; - - // Parse the first digit separately. If invalid here, we need to return false. - int firstDigit = text[indexOfFirstDigit] - 48; // '0' - if (firstDigit < 0 || firstDigit > 9) - { - bytesConsumed = 0; - value = default; - return false; - } - int parsedValue = firstDigit; - - if (text.Length < overflowLength) - { - // Length is less than Int32OverflowLength; overflow is not possible - for (int index = indexOfFirstDigit + 1; index < text.Length; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - bytesConsumed = index; - value = parsedValue * sign; - return true; - } - parsedValue = parsedValue * 10 + nextDigit; - } - } - else - { - // Length is greater than Int32OverflowLength; overflow is only possible after Int32OverflowLength - // digits. There may be no overflow after Int32OverflowLength if there are leading zeroes. - for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - bytesConsumed = index; - value = parsedValue * sign; - return true; - } - parsedValue = parsedValue * 10 + nextDigit; - } - for (int index = overflowLength - 1; index < text.Length; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - bytesConsumed = index; - value = parsedValue * sign; - return true; - } - // If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow. - // if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. - bool positive = sign > 0; - bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7); - if (parsedValue > int.MaxValue / 10 || parsedValue == int.MaxValue / 10 && nextDigitTooLarge) - { - bytesConsumed = 0; - value = default; - return false; - } - parsedValue = parsedValue * 10 + nextDigit; - } - } - - bytesConsumed = text.Length; - value = parsedValue * sign; + index++; + if ((uint)index >= (uint)text.Length) goto FalseExit; + num = text[index]; + } + else if (num == '+') + { + index++; + if ((uint)index >= (uint)text.Length) goto FalseExit; + num = text[index]; + } + + int answer = 0; + + if (IsDigit(num)) + { + if (num == '0') + { + do + { + index++; + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + } while (num == '0'); + if (!IsDigit(num)) goto Done; + } + + answer = num - '0'; + index++; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + index++; + answer = 10 * answer + num - '0'; + + // Potential overflow + if ((uint)index >= (uint)text.Length) goto Done; + num = text[index]; + if (!IsDigit(num)) goto Done; + if (answer > Int32.MaxValue / 10 + 1) goto FalseExit; // Overflow + answer = answer * 10 + num - '0'; + + // if sign < 0, (-1 * sign + 1) / 2 = 1 + // else, (-1 * sign + 1) / 2 = 0 + if ((uint)answer > (uint)Int32.MaxValue + (-1 * sign + 1) / 2) goto FalseExit; // Overflow + index++; + if ((uint)index >= (uint)text.Length) goto Done; + if (!IsDigit(text[index])) goto Done; + + // Guaranteed overflow + goto FalseExit; + } + + FalseExit: + bytesConsumed = default; + value = default; + return false; + + Done: + bytesConsumed = index; + value = answer * sign; return true; } @@ -2310,9 +2256,15 @@ public unsafe static bool TryParseInt32(char* text, int length, out int value, o } public static bool TryParseInt32(ReadOnlySpan text, out int value) + { + return TryParseInt32(text, out value, out int charsConsumed); + } + + public static bool TryParseInt32(ReadOnlySpan text, out int value, out int charsConsumed) { if (text.Length < 1) { + charsConsumed = 0; value = default; return false; } @@ -2329,91 +2281,13 @@ public static bool TryParseInt32(ReadOnlySpan text, out int value) indexOfFirstDigit = 1; } - int overflowLength = Int32OverflowLength + indexOfFirstDigit; - - // Parse the first digit separately. If invalid here, we need to return false. - int firstDigit = text[indexOfFirstDigit] - 48; // '0' - if (firstDigit < 0 || firstDigit > 9) - { - value = default; - return false; - } - int parsedValue = firstDigit; - - if (text.Length < overflowLength) - { - // Length is less than Int32OverflowLength; overflow is not possible - for (int index = indexOfFirstDigit + 1; index < text.Length; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - value = parsedValue * sign; - return true; - } - parsedValue = parsedValue * 10 + nextDigit; - } - } - else - { - // Length is greater than Int32OverflowLength; overflow is only possible after Int32OverflowLength - // digits. There may be no overflow after Int32OverflowLength if there are leading zeroes. - for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - value = parsedValue * sign; - return true; - } - parsedValue = parsedValue * 10 + nextDigit; - } - for (int index = overflowLength - 1; index < text.Length; index++) - { - int nextDigit = text[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - value = parsedValue * sign; - return true; - } - // If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow. - // if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. - bool positive = sign > 0; - bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7); - if (parsedValue > int.MaxValue / 10 || parsedValue == int.MaxValue / 10 && nextDigitTooLarge) - { - value = default; - return false; - } - parsedValue = parsedValue * 10 + nextDigit; - } - } - - value = parsedValue * sign; - return true; - } - - public static bool TryParseInt32(ReadOnlySpan text, out int value, out int charsConsumed) - { - if (text.Length < 1) + if (indexOfFirstDigit >= text.Length) { charsConsumed = 0; value = default; return false; } - int indexOfFirstDigit = 0; - int sign = 1; - if (text[0] == '-') - { - indexOfFirstDigit = 1; - sign = -1; - } - else if (text[0] == '+') - { - indexOfFirstDigit = 1; - } - int overflowLength = Int32OverflowLength + indexOfFirstDigit; // Parse the first digit separately. If invalid here, we need to return false. diff --git a/src/System.Text.Primitives/System/Text/Parsing/Signed.cs b/src/System.Text.Primitives/System/Text/Parsing/Signed.cs index 71e39aab3a8..67040c462cf 100644 --- a/src/System.Text.Primitives/System/Text/Parsing/Signed.cs +++ b/src/System.Text.Primitives/System/Text/Parsing/Signed.cs @@ -1,10 +1,69 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Runtime.CompilerServices; + namespace System.Text { public static partial class PrimitiveParser { + #region Helpers + + private const sbyte maxValueSbyteDiv10 = sbyte.MaxValue / 10; + private const short maxValueShortDiv10 = short.MaxValue / 10; + private const int maxValueIntDiv10 = int.MaxValue / 10; + private const long maxValueLongDiv10 = long.MaxValue / 10; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsDigit(int i) + { + return (uint)(i - '0') <= ('9' - '0'); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsValid(SymbolTable.Symbol symbol) + { + return symbol <= SymbolTable.Symbol.D9; + } + + // If parsedValue > (sbyte.MaxValue / 10), any more appended digits will cause overflow. + // if parsedValue == (sbyte.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool WillOverFlow(sbyte value, int nextDigit, int sign) + { + bool nextDigitTooLarge = nextDigit > 8 || (sign > 0 && nextDigit > 7); + return (value > maxValueSbyteDiv10 || (value == maxValueSbyteDiv10 && nextDigitTooLarge)); + } + + // If parsedValue > (short.MaxValue / 10), any more appended digits will cause overflow. + // if parsedValue == (short.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool WillOverFlow(short value, int nextDigit, int sign) + { + bool nextDigitTooLarge = nextDigit > 8 || (sign > 0 && nextDigit > 7); + return (value > maxValueShortDiv10 || (value == maxValueShortDiv10 && nextDigitTooLarge)); + } + + // If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow. + // if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool WillOverFlow(int value, int nextDigit, int sign) + { + bool nextDigitTooLarge = nextDigit > 8 || (sign > 0 && nextDigit > 7); + return (value > maxValueIntDiv10 || (value == maxValueIntDiv10 && nextDigitTooLarge)); + } + + // If parsedValue > (long.MaxValue / 10), any more appended digits will cause overflow. + // if parsedValue == (long.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool WillOverFlow(long value, int nextDigit, int sign) + { + bool nextDigitTooLarge = nextDigit > 8 || (sign > 0 && nextDigit > 7); + return (value > maxValueLongDiv10 || (value == maxValueLongDiv10 && nextDigitTooLarge)); + } + + #endregion + public static bool TryParseSByte(ReadOnlySpan text, out sbyte value, out int bytesConsumed, TextFormat format = default, SymbolTable symbolTable = null) { symbolTable = symbolTable ?? SymbolTable.InvariantUtf8; @@ -119,7 +178,6 @@ public static bool TryParseSByte(ReadOnlySpan text, out sbyte value, out i return true; } - public static bool TryParseInt16(ReadOnlySpan text, out short value, out int bytesConsumed, TextFormat format = default, SymbolTable symbolTable = null) { symbolTable = symbolTable ?? SymbolTable.InvariantUtf8; @@ -234,122 +292,125 @@ public static bool TryParseInt16(ReadOnlySpan text, out short value, out i return true; } - public static bool TryParseInt32(ReadOnlySpan text, out int value, out int bytesConsumed, TextFormat format = default, SymbolTable symbolTable = null) { + bool isDefault = format.IsDefault; symbolTable = symbolTable ?? SymbolTable.InvariantUtf8; - if (!format.IsDefault && format.HasPrecision) + if (!isDefault && format.HasPrecision) { throw new NotImplementedException("Format with precision not supported."); } + bool isHex = format.IsHexadecimal; + if (symbolTable == SymbolTable.InvariantUtf8) { - if (format.IsHexadecimal) - { - return InvariantUtf8.Hex.TryParseInt32(text, out value, out bytesConsumed); - } - else - { - return InvariantUtf8.TryParseInt32(text, out value, out bytesConsumed); - } + return isHex ? InvariantUtf8.Hex.TryParseInt32(text, out value, out bytesConsumed) : + InvariantUtf8.TryParseInt32(text, out value, out bytesConsumed); } else if (symbolTable == SymbolTable.InvariantUtf16) { + /*return isHex ? InvariantUtf16.Hex.TryParseInt32(text, out value, out bytesConsumed) : + InvariantUtf16.TryParseInt32(text, out value, out bytesConsumed);*/ ReadOnlySpan utf16Text = text.NonPortableCast(); - int charsConsumed; - bool result; - if (format.IsHexadecimal) - { - result = InvariantUtf16.Hex.TryParseInt32(utf16Text, out value, out charsConsumed); - } - else - { - result = InvariantUtf16.TryParseInt32(utf16Text, out value, out charsConsumed); - } + bool result = isHex ? InvariantUtf16.Hex.TryParseInt32(utf16Text, out value, out int charsConsumed) : + InvariantUtf16.TryParseInt32(utf16Text, out value, out charsConsumed); bytesConsumed = charsConsumed * sizeof(char); return result; } - if (format.IsHexadecimal) + if (isHex) { throw new NotImplementedException("The only supported encodings for hexadecimal parsing are InvariantUtf8 and InvariantUtf16."); } - if (!(format.IsDefault || format.Symbol == 'G' || format.Symbol == 'g')) + if (!(isDefault || format.Symbol == 'G' || format.Symbol == 'g')) { throw new NotImplementedException(String.Format("Format '{0}' not supported.", format.Symbol)); } - SymbolTable.Symbol nextSymbol; - int thisSymbolConsumed; - if (!symbolTable.TryParse(text, out nextSymbol, out thisSymbolConsumed)) - { - value = default; - bytesConsumed = 0; - return false; - } + int textLength = text.Length; + if (textLength < 1) goto FalseExit; - int sign = 1; - if (nextSymbol == SymbolTable.Symbol.MinusSign) - { - sign = -1; - } + if (!symbolTable.TryParse(text, out SymbolTable.Symbol symbol, out int consumed)) goto FalseExit; - int signConsumed = 0; - if (nextSymbol == SymbolTable.Symbol.PlusSign || nextSymbol == SymbolTable.Symbol.MinusSign) + sbyte sign = 1; + int index = 0; + if (symbol == SymbolTable.Symbol.MinusSign) { - signConsumed = thisSymbolConsumed; - if (!symbolTable.TryParse(text.Slice(signConsumed), out nextSymbol, out thisSymbolConsumed)) - { - value = default; - bytesConsumed = 0; - return false; - } + sign = -1; + index += consumed; + if (index >= textLength) goto FalseExit; + if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed)) goto FalseExit; } - - if (nextSymbol > SymbolTable.Symbol.D9) + else if (symbol == SymbolTable.Symbol.PlusSign) { - value = default; - bytesConsumed = 0; - return false; + index += consumed; + if (index >= textLength) goto FalseExit; + if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed)) goto FalseExit; } - int parsedValue = (int)nextSymbol; - int index = signConsumed + thisSymbolConsumed; - - while (index < text.Length) + int answer = 0; + if (IsValid(symbol)) { - bool success = symbolTable.TryParse(text.Slice(index), out nextSymbol, out thisSymbolConsumed); - if (!success || nextSymbol > SymbolTable.Symbol.D9) + int numBytes = consumed; + if (symbol == SymbolTable.Symbol.D0) { - bytesConsumed = index; - value = (int)(parsedValue * sign); - return true; + do + { + index += consumed; + if (index >= textLength) goto Done; + if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed)) goto Done; + } while (symbol == SymbolTable.Symbol.D0); + if (!IsValid(symbol)) goto Done; } - // If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow. - // if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. - bool positive = sign > 0; - bool nextDigitTooLarge = nextSymbol > SymbolTable.Symbol.D8 || (positive && nextSymbol > SymbolTable.Symbol.D7); - if (parsedValue > int.MaxValue / 10 || (parsedValue == int.MaxValue / 10 && nextDigitTooLarge)) + int firstNonZeroDigitIndex = index; + if (textLength - firstNonZeroDigitIndex < Int32OverflowLength * numBytes) { - bytesConsumed = 0; - value = default; - return false; + do + { + answer = answer * 10 + (int)symbol; + index += consumed; + if (index >= textLength) goto Done; + if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed)) goto Done; + } while (IsValid(symbol)); } - - index += thisSymbolConsumed; - parsedValue = parsedValue * 10 + (int)nextSymbol; + else + { + do + { + answer = answer * 10 + (int)symbol; + index += consumed; + if (index - firstNonZeroDigitIndex == (Int32OverflowLength - 1) * numBytes) + { + if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed)) goto Done; + if (IsValid(symbol)) + { + if (WillOverFlow(answer, (int)symbol, sign)) goto FalseExit; + answer = answer * 10 + (int)symbol; + index += consumed; + } + goto Done; + } + if (!symbolTable.TryParse(text.Slice(index), out symbol, out consumed)) goto Done; + } while (IsValid(symbol)); + } + goto Done; } - bytesConsumed = text.Length; - value = (int)(parsedValue * sign); + FalseExit: + bytesConsumed = 0; + value = 0; + return false; + + Done: + bytesConsumed = index; + value = answer * sign; return true; } - public static bool TryParseInt64(ReadOnlySpan text, out long value, out int bytesConsumed, TextFormat format = default, SymbolTable symbolTable = null) { symbolTable = symbolTable ?? SymbolTable.InvariantUtf8; diff --git a/src/System.Text.Primitives/System/Text/TextFormat.cs b/src/System.Text.Primitives/System/Text/TextFormat.cs index 29b5e32d764..dfba90f6299 100644 --- a/src/System.Text.Primitives/System/Text/TextFormat.cs +++ b/src/System.Text.Primitives/System/Text/TextFormat.cs @@ -33,15 +33,16 @@ public TextFormat(char symbol, byte precision = NoPrecision) public static TextFormat Parse(ReadOnlySpan format) { - if (format.Length == 0) + int formatLength = format.Length; + if (formatLength == 0) { return default; } uint precision = NoPrecision; - if (format.Length > 1) + if (formatLength > 1) { - var span = format.Slice(1, format.Length - 1); + var span = format.Slice(1, formatLength - 1); if (!PrimitiveParser.InvariantUtf16.TryParseUInt32(span, out precision)) { @@ -69,18 +70,18 @@ public static TextFormat Parse(string format) return Parse(format.AsSpan()); } - public bool IsHexadecimal => Symbol == 'X' || Symbol == 'x'; + public bool IsHexadecimal => _format == 'X' || _format == 'x'; - public bool HasPrecision => Precision != NoPrecision; + public bool HasPrecision => _precision != NoPrecision; public bool IsDefault => _format == 0 && _precision == 0; public override string ToString() { - return string.Format("{0}:{1}", Symbol, Precision); + return string.Format("{0}:{1}", _format, _precision); } - public static bool operator==(TextFormat left, TextFormat right) => left.Equals(right); + public static bool operator ==(TextFormat left, TextFormat right) => left.Equals(right); public static bool operator !=(TextFormat left, TextFormat right) => !left.Equals(right); [EditorBrowsable(EditorBrowsableState.Never)] @@ -93,13 +94,13 @@ public override bool Equals(object obj) [EditorBrowsable(EditorBrowsableState.Never)] public bool Equals(TextFormat other) { - return Symbol.Equals(other.Symbol) && Precision.Equals(other.Precision); + return _format == other._format && _precision == other._precision; } [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() { - return CombineHashCodes(Symbol.GetHashCode(), Precision.GetHashCode()); + return CombineHashCodes(_format.GetHashCode(), _precision.GetHashCode()); } static int CombineHashCodes(int h1, int h2) diff --git a/tests/System.Buffers.Primitives.Tests/UsageScenarioTests.cs b/tests/System.Buffers.Primitives.Tests/UsageScenarioTests.cs index 5983a98543d..46342061da7 100644 --- a/tests/System.Buffers.Primitives.Tests/UsageScenarioTests.cs +++ b/tests/System.Buffers.Primitives.Tests/UsageScenarioTests.cs @@ -148,22 +148,22 @@ public void CtorReadOnlySpanOverByteArrayValidCasesWithPropertiesAndBasicOperati new byte[] { 1, 2, 7, 7, 5, 6 }, 2, 3)] // copy one byte from the beginning at the end of other array [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 0, 1, new byte[] { 7, 7, 7, 7, 7, 7 }, 6, 0)] // copy two bytes from the beginning at 5th element [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 0, 2, new byte[] { 7, 7, 7, 7, 7, 7 }, 5, 1)] // copy one byte from the beginning at the end of other array [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 5, 1, new byte[] { 7, 7, 7, 7, 7, 7 }, 6, 0)] // copy two bytes from the beginning at 5th element [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 4, 2, new byte[] { 7, 7, 7, 7, 7, 7 }, 5, 1)] public void SpanOfByteCopyToAnotherSpanOfByteTwoDifferentBuffersValidCases(byte[] expected, byte[] a, int aidx, int acount, byte[] b, int bidx, int bcount) @@ -259,22 +259,22 @@ public void SpanOfByteCopyToAnotherSpanOfByteTwoDifferentBuffersValidCases(byte[ new byte[] { 1, 2, 7, 7, 5, 6 }, 2, 3)] // copy one byte from the beginning at the end of other array [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 0, 1, new byte[] { 7, 7, 7, 7, 7, 7 }, 6, 0)] // copy two bytes from the beginning at 5th element [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 0, 2, new byte[] { 7, 7, 7, 7, 7, 7 }, 5, 1)] // copy one byte from the beginning at the end of other array [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 5, 1, new byte[] { 7, 7, 7, 7, 7, 7 }, 6, 0)] // copy two bytes from the beginning at 5th element [InlineData( - (byte[])null, + null, new byte[] { 7, 7, 7, 7, 7, 7 }, 4, 2, new byte[] { 7, 7, 7, 7, 7, 7 }, 5, 1)] public void ReadOnlySpanOfByteCopyToAnotherSpanOfByteTwoDifferentBuffersValidCases(byte[] expected, byte[] a, int aidx, int acount, byte[] b, int bidx, int bcount) diff --git a/tests/System.Collections.Generic.MultiValueDictionary.Tests/Generic/MultiValueDictionary/MVD.Tests.cs b/tests/System.Collections.Generic.MultiValueDictionary.Tests/Generic/MultiValueDictionary/MVD.Tests.cs index 1083205c246..acea4d7f255 100644 --- a/tests/System.Collections.Generic.MultiValueDictionary.Tests/Generic/MultiValueDictionary/MVD.Tests.cs +++ b/tests/System.Collections.Generic.MultiValueDictionary.Tests/Generic/MultiValueDictionary/MVD.Tests.cs @@ -353,7 +353,7 @@ public void MVD_Constructor4_NullComparerInvalidCapacity() public void MVD_Constructor4_InvalidTCollection() { Assert.Throws(() => MultiValueDictionary.Create>(1, (IEqualityComparer)null)); - Assert.Throws(() => MultiValueDictionary.Create>(1, (IEqualityComparer)null, () => new DummyReadOnlyCollection())); + Assert.Throws(() => MultiValueDictionary.Create>(1, null, () => new DummyReadOnlyCollection())); } #endregion diff --git a/tests/System.IO.Compression.Tests/BrotliPerfomanceTests.cs b/tests/System.IO.Compression.Tests/BrotliPerformanceTests.cs similarity index 92% rename from tests/System.IO.Compression.Tests/BrotliPerfomanceTests.cs rename to tests/System.IO.Compression.Tests/BrotliPerformanceTests.cs index 36293b781d4..e328c24f581 100644 --- a/tests/System.IO.Compression.Tests/BrotliPerfomanceTests.cs +++ b/tests/System.IO.Compression.Tests/BrotliPerformanceTests.cs @@ -6,7 +6,7 @@ namespace System.IO.Compression.Tests { - public class BrotliPerfomanceTests + public class BrotliPerformanceTests { private const int Iter = 1000; @@ -71,9 +71,16 @@ public void Decompress(Util.CompressionType type) byte[] data = File.ReadAllBytes(testFilePath); var bytes = new byte[bufferSize]; foreach (var iteration in Benchmark.Iterations) + { using (iteration.StartMeasurement()) + { for (int i = 0; i < Benchmark.InnerIterationCount; i++) - Brotli.Decompress(data, bytes, out int consumed, out int written); + { + Brotli.State state = new Brotli.State(); + Brotli.Decompress(data, bytes, out int consumed, out int written, ref state); + } + } + } File.Delete(testFilePath); } @@ -90,7 +97,8 @@ public void Compress(Util.CompressionType type) byte[] compressed = new byte[bytes.Length]; using (iteration.StartMeasurement()) { - Brotli.Compress(bytes, compressed, out int consumed, out int writen); + Brotli.State state = new Brotli.State(); + Brotli.Compress(bytes, compressed, out int consumed, out int writen, ref state); } } } diff --git a/tests/System.IO.Compression.Tests/BrotliPrimitivesTests.cs b/tests/System.IO.Compression.Tests/BrotliPrimitivesTests.cs index 45e1fa0ab59..a6cce082957 100644 --- a/tests/System.IO.Compression.Tests/BrotliPrimitivesTests.cs +++ b/tests/System.IO.Compression.Tests/BrotliPrimitivesTests.cs @@ -10,16 +10,6 @@ public class BrotliTests { static string brTestFile(string fileName) => Path.Combine("BrotliTestData", fileName); - [Theory] - [InlineData(25, 1)] - [InlineData(-1, 1)] - [InlineData(24, 0)] - [InlineData(24, 12)] - public void TestMethodCompressEx(CompressionLevel quality, int lgWinSize) - { - Assert.Throws(() => Brotli.Compress(new byte[1], new byte[1], out int consumed, out int written, quality, lgWinSize)); - } - [Theory(Skip = "Fails in VS - System.BadImageFormatException : An attempt was made to load a program with an incorrect format.")] [InlineData(1)] [InlineData(5)] @@ -29,19 +19,31 @@ public void RoundtripCompressDecompress(int totalSize) { byte[] data = new byte[totalSize]; new Random(42).NextBytes(data); - Span compressed = new byte[Brotli.GetMaximumCompressedSize(totalSize)]; - TransformationStatus result = Brotli.Compress(data, compressed, out int consumed, out int written); + byte[] compressed = new byte[Brotli.GetMaximumCompressedSize(totalSize)]; + Assert.NotEqual(compressed.Length, 0); + Brotli.State state = new Brotli.State(); + TransformationStatus result = Brotli.Compress(data, compressed, out int consumed, out int written, ref state); + while (consumed != 0 || result != TransformationStatus.Done) + { + result = Brotli.Compress(data, compressed, out consumed, out written, ref state); + } + byte[] flush = new byte[0]; + result = Brotli.FlushEncoder(flush, compressed, out consumed, out written, ref state); Assert.Equal(TransformationStatus.Done, result); - Assert.Equal(totalSize, consumed); - compressed = compressed.Slice(0, written); - ValidateCompressedData(compressed, data); + Assert.Equal(consumed, 0); + byte[] resultCompressed = new byte[written]; + Array.Copy(compressed, resultCompressed, written); + ValidateCompressedData(resultCompressed, data); } - private void ValidateCompressedData(Span data, byte[] expected) + private void ValidateCompressedData(byte[] data, byte[] expected) { byte[] decompressed = new byte[expected.Length]; - TransformationStatus result = Brotli.Decompress(data, decompressed, out int consumed, out int written); + Brotli.State state = new Brotli.State(); + TransformationStatus result = Brotli.Decompress(data, decompressed, out int consumed, out int written, ref state); Assert.Equal(TransformationStatus.Done, result); + Assert.Equal(expected.Length, written); + Assert.Equal(consumed, 0); Assert.Equal(expected, decompressed); } diff --git a/tests/System.IO.Compression.Tests/BrotliStreamTests.cs b/tests/System.IO.Compression.Tests/BrotliStreamTests.cs index 29ca824d693..5df15c0cbb5 100644 --- a/tests/System.IO.Compression.Tests/BrotliStreamTests.cs +++ b/tests/System.IO.Compression.Tests/BrotliStreamTests.cs @@ -214,19 +214,20 @@ public void RoundtripCompressDecompress(int chunkSize, int totalSize) { compressor.Write(data, i, chunkSize); } + compressor.Dispose(); } compressed.Position = 0; - ValidateCompressedData(chunkSize, compressed, data); + ValidateCompressedData(chunkSize, compressed.ToArray(), data); compressed.Dispose(); } - private void ValidateCompressedData(int chunkSize, MemoryStream compressed, byte[] expected) + private void ValidateCompressedData(int chunkSize, byte[] compressedData, byte[] expected) { + MemoryStream compressed = new MemoryStream(compressedData); using (MemoryStream decompressed = new MemoryStream()) using (var decompressor = new BrotliStream(compressed, CompressionMode.Decompress, true)) { - decompressor.CopyTo(decompressed, chunkSize); - + decompressor.CopyTo(decompressed, expected.Length); Assert.Equal(expected, decompressed.ToArray()); } } diff --git a/tests/System.Text.Formatting.Tests/PerformanceTests.cs b/tests/System.Text.Formatting.Tests/PerformanceTests.cs index bc94e66c3ce..b9b120eeed1 100644 --- a/tests/System.Text.Formatting.Tests/PerformanceTests.cs +++ b/tests/System.Text.Formatting.Tests/PerformanceTests.cs @@ -279,4 +279,3 @@ static byte[] GetBytesUtf16(string text) } } } - diff --git a/tests/System.Text.Formatting.Tests/PrimitiveFormattingTests.cs b/tests/System.Text.Formatting.Tests/PrimitiveFormattingTests.cs index ed54e56643d..18b4e98f9d5 100644 --- a/tests/System.Text.Formatting.Tests/PrimitiveFormattingTests.cs +++ b/tests/System.Text.Formatting.Tests/PrimitiveFormattingTests.cs @@ -140,9 +140,9 @@ public void Int64BasicTests() CheckInt64(long.MaxValue, "x", "7fffffffffffffff", formatter); CheckInt64(long.MinValue, "X", "8000000000000000", formatter); - CheckInt64(-10, "X", "FFFFFFFFFFFFFFF6", formatter); - CheckInt64(-1, "X", "FFFFFFFFFFFFFFFF", formatter); - CheckInt64(0, "X", "0", formatter); + CheckInt64(-10, "X", "FFFFFFFFFFFFFFF6", formatter); + CheckInt64(-1, "X", "FFFFFFFFFFFFFFFF", formatter); + CheckInt64(0, "X", "0", formatter); CheckInt64(1, "X", "1", formatter); CheckInt64(10, "X", "A", formatter); CheckInt64(long.MaxValue, "X", "7FFFFFFFFFFFFFFF", formatter); @@ -345,7 +345,8 @@ public void Int32ToStreamUtf8() var buffer = new byte[1024]; MemoryStream stream = new MemoryStream(buffer); - using(var writer = new StreamFormatter(stream, SymbolTable.InvariantUtf8, pool)) { + using (var writer = new StreamFormatter(stream, SymbolTable.InvariantUtf8, pool)) + { writer.Append(100); writer.Append(-100); writer.Append('h'); @@ -360,7 +361,8 @@ public void FormatString() var buffer = new byte[1024]; MemoryStream stream = new MemoryStream(buffer); - using(var utf8Writer = new StreamFormatter(stream, SymbolTable.InvariantUtf8, pool)) { + using (var utf8Writer = new StreamFormatter(stream, SymbolTable.InvariantUtf8, pool)) + { utf8Writer.Append("Hello"); utf8Writer.Append(" "); utf8Writer.Append("World!"); @@ -371,7 +373,8 @@ public void FormatString() } stream.Position = 0; - using(var utf16Writer = new StreamFormatter(stream, SymbolTable.InvariantUtf16, pool)) { + using (var utf16Writer = new StreamFormatter(stream, SymbolTable.InvariantUtf16, pool)) + { utf16Writer.Append("Hello"); utf16Writer.Append(" "); utf16Writer.Append("World!"); @@ -390,7 +393,8 @@ public void FormatLongStringToUtf8() string data = new string('#', length); formatter.Append(data); Assert.Equal(length, formatter.CommitedByteCount); - for(int i=0; i(utf8ByteArray); + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for(int i = 0; i < Benchmark.InnerIterationCount; i++) + { + PrimitiveParser.InvariantUtf8.TryParseInt32(utf8ByteSpan, out int value); + TestHelper.DoNotIgnore(value, 0); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + private static void PrimitiveParserByteSpanToInt32_VariableLength() + { + int textLength = s_Int32TextArray.Length; + byte[][] utf8ByteArray = (byte[][])Array.CreateInstance(typeof(byte[]), textLength); + for (var i = 0; i < textLength; i++) + { + utf8ByteArray[i] = Text.Encoding.UTF8.GetBytes(s_Int32TextArray[i]); + } + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + ReadOnlySpan utf8ByteSpan = utf8ByteArray[i % textLength]; + PrimitiveParser.InvariantUtf8.TryParseInt32(utf8ByteSpan, out int value); + TestHelper.DoNotIgnore(value, 0); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("107374182")] // standard parse + [InlineData("2147483647")] // max value + [InlineData("0")] + [InlineData("-2147483648")] // min value + [InlineData("214748364")] + [InlineData("2")] + [InlineData("21474836")] + [InlineData("-21474")] + [InlineData("21474")] + [InlineData("-21")] + [InlineData("-2")] + [InlineData("214")] + [InlineData("-21474836")] + [InlineData("-214748364")] + [InlineData("2147")] + [InlineData("-2147")] + [InlineData("-214748")] + [InlineData("-2147483")] + [InlineData("214748")] + [InlineData("21")] + [InlineData("2147483")] + [InlineData("-214")] + [InlineData("+21474")] + [InlineData("+21")] + [InlineData("+2")] + [InlineData("+21474836")] + [InlineData("+214748364")] + [InlineData("+2147")] + [InlineData("+214748")] + [InlineData("+2147483")] + [InlineData("+2147483647")] + [InlineData("+214")] + [InlineData("000000000000000000001235abcdfg")] + [InlineData("214748364abcdefghijklmnop")] + [InlineData("2abcdefghijklmnop")] + [InlineData("21474836abcdefghijklmnop")] + [InlineData("-21474abcdefghijklmnop")] + [InlineData("21474abcdefghijklmnop")] + [InlineData("-21abcdefghijklmnop")] + [InlineData("-2abcdefghijklmnop")] + [InlineData("214abcdefghijklmnop")] + [InlineData("-21474836abcdefghijklmnop")] + [InlineData("-214748364abcdefghijklmnop")] + [InlineData("2147abcdefghijklmnop")] + [InlineData("-2147abcdefghijklmnop")] + [InlineData("-214748abcdefghijklmnop")] + [InlineData("-2147483abcdefghijklmnop")] + [InlineData("214748abcdefghijklmnop")] + [InlineData("21abcdefghijklmnop")] + [InlineData("2147483abcdefghijklmnop")] + [InlineData("-214abcdefghijklmnop")] + [InlineData("+21474abcdefghijklmnop")] + [InlineData("+21abcdefghijklmnop")] + [InlineData("+2abcdefghijklmnop")] + [InlineData("+21474836abcdefghijklmnop")] + [InlineData("+214748364abcdefghijklmnop")] + [InlineData("+2147abcdefghijklmnop")] + [InlineData("+214748abcdefghijklmnop")] + [InlineData("+2147483abcdefghijklmnop")] + [InlineData("+2147483647abcdefghijklmnop")] + [InlineData("+214abcdefghijklmnop")] + private static void PrimitiveParserByteSpanToInt32_BytesConsumed(string text) + { + byte[] utf8ByteArray = Text.Encoding.UTF8.GetBytes(text); + var utf8ByteSpan = new ReadOnlySpan(utf8ByteArray); + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + PrimitiveParser.InvariantUtf8.TryParseInt32(utf8ByteSpan, out int value, out int bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + private static void PrimitiveParserByteSpanToInt32_BytesConsumed_VariableLength() + { + int textLength = s_Int32TextArray.Length; + byte[][] utf8ByteArray = (byte[][])Array.CreateInstance(typeof(byte[]), textLength); + for (var i = 0; i < textLength; i++) + { + utf8ByteArray[i] = Text.Encoding.UTF8.GetBytes(s_Int32TextArray[i]); + } + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + ReadOnlySpan utf8ByteSpan = utf8ByteArray[i % textLength]; + PrimitiveParser.InvariantUtf8.TryParseInt32(utf8ByteSpan, out int value, out int bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("107374182")] // standard parse + [InlineData("2147483647")] // max value + [InlineData("0")] + [InlineData("-2147483648")] // min value + [InlineData("214748364")] + [InlineData("2")] + [InlineData("21474836")] + [InlineData("-21474")] + [InlineData("21474")] + [InlineData("-21")] + [InlineData("-2")] + [InlineData("214")] + [InlineData("-21474836")] + [InlineData("-214748364")] + [InlineData("2147")] + [InlineData("-2147")] + [InlineData("-214748")] + [InlineData("-2147483")] + [InlineData("214748")] + [InlineData("21")] + [InlineData("2147483")] + [InlineData("-214")] + [InlineData("+21474")] + [InlineData("+21")] + [InlineData("+2")] + [InlineData("+21474836")] + [InlineData("+214748364")] + [InlineData("+2147")] + [InlineData("+214748")] + [InlineData("+2147483")] + [InlineData("+2147483647")] + [InlineData("+214")] + [InlineData("000000000000000000001235abcdfg")] + [InlineData("214748364abcdefghijklmnop")] + [InlineData("2abcdefghijklmnop")] + [InlineData("21474836abcdefghijklmnop")] + [InlineData("-21474abcdefghijklmnop")] + [InlineData("21474abcdefghijklmnop")] + [InlineData("-21abcdefghijklmnop")] + [InlineData("-2abcdefghijklmnop")] + [InlineData("214abcdefghijklmnop")] + [InlineData("-21474836abcdefghijklmnop")] + [InlineData("-214748364abcdefghijklmnop")] + [InlineData("2147abcdefghijklmnop")] + [InlineData("-2147abcdefghijklmnop")] + [InlineData("-214748abcdefghijklmnop")] + [InlineData("-2147483abcdefghijklmnop")] + [InlineData("214748abcdefghijklmnop")] + [InlineData("21abcdefghijklmnop")] + [InlineData("2147483abcdefghijklmnop")] + [InlineData("-214abcdefghijklmnop")] + [InlineData("+21474abcdefghijklmnop")] + [InlineData("+21abcdefghijklmnop")] + [InlineData("+2abcdefghijklmnop")] + [InlineData("+21474836abcdefghijklmnop")] + [InlineData("+214748364abcdefghijklmnop")] + [InlineData("+2147abcdefghijklmnop")] + [InlineData("+214748abcdefghijklmnop")] + [InlineData("+2147483abcdefghijklmnop")] + [InlineData("+2147483647abcdefghijklmnop")] + [InlineData("+214abcdefghijklmnop")] + private static void PrimitiveParserByteSpanToInt32_BytesConsumed_Baseline(string text) + { + byte[] utf8ByteArray = Text.Encoding.UTF8.GetBytes(text); + var utf8ByteSpan = new ReadOnlySpan(utf8ByteArray); + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + int.TryParse(text, out int value); + TestHelper.DoNotIgnore(value, 0); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + private static void PrimitiveParserByteSpanToInt32_BytesConsumed_VariableLength_Baseline() + { + int textLength = s_Int32TextArray.Length; + byte[][] utf8ByteArray = (byte[][])Array.CreateInstance(typeof(byte[]), textLength); + for (var i = 0; i < textLength; i++) + { + utf8ByteArray[i] = Text.Encoding.UTF8.GetBytes(s_Int32TextArray[i]); + } + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + int.TryParse(s_Int32TextArray[i % textLength], out int value); + TestHelper.DoNotIgnore(value, 0); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("๑๐๗๓๗๔๑๘๒")] + [InlineData("๒๑๔๗๔๘๓๖๔๗")] + [InlineData("๐")] + [InlineData("ลบ๒๑๔๗๔๘๓๖๔๘")] + [InlineData("๒๑๔๗๔๘๓๖๔")] + [InlineData("๒")] + [InlineData("๒๑๔๗๔๘๓๖")] + [InlineData("ลบ๒๑๔๗๔")] + [InlineData("๒๑๔๗๔")] + [InlineData("ลบ๒๑")] + [InlineData("ลบ๒")] + [InlineData("๒๑๔")] + [InlineData("ลบ๒๑๔๗๔๘๓๖")] + [InlineData("ลบ๒๑๔๗๔๘๓๖๔")] + [InlineData("๒๑๔๗")] + [InlineData("ลบ๒๑๔๗")] + [InlineData("ลบ๒๑๔๗๔๘")] + [InlineData("ลบ๒๑๔๗๔๘๓")] + [InlineData("๒๑๔๗๔๘")] + [InlineData("๒๑")] + [InlineData("๒๑๔๗๔๘๓")] + [InlineData("ลบ๒๑๔")] + [InlineData("+๒๑๔๗๔")] + [InlineData("+๒๑")] + [InlineData("+๒")] + [InlineData("+๒๑๔๗๔๘๓๖")] + [InlineData("+๒๑๔๗๔๘๓๖๔")] + [InlineData("+๒๑๔๗")] + [InlineData("+๒๑๔๗๔๘")] + [InlineData("+๒๑๔๗๔๘๓")] + [InlineData("+๒๑๔๗๔๘๓๖๔๗")] + [InlineData("+๒๑๔")] + [InlineData("๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๑๒๓๕abcdfg")] + [InlineData("๒๑๔๗๔๘๓๖๔abcdefghijklmnop")] + [InlineData("๒abcdefghijklmnop")] + [InlineData("๒๑๔๗๔๘๓๖abcdefghijklmnop")] + [InlineData("ลบ๒๑๔๗๔abcdefghijklmnop")] + [InlineData("๒๑๔๗๔abcdefghijklmnop")] + [InlineData("ลบ๒๑abcdefghijklmnop")] + [InlineData("ลบ๒abcdefghijklmnop")] + [InlineData("๒๑๔abcdefghijklmnop")] + [InlineData("ลบ๒๑๔๗๔๘๓๖abcdefghijklmnop")] + [InlineData("ลบ๒๑๔๗๔๘๓๖๔abcdefghijklmnop")] + [InlineData("๒๑๔๗abcdefghijklmnop")] + [InlineData("ลบ๒๑๔๗abcdefghijklmnop")] + [InlineData("ลบ๒๑๔๗๔๘abcdefghijklmnop")] + [InlineData("ลบ๒๑๔๗๔๘๓abcdefghijklmnop")] + [InlineData("๒๑๔๗๔๘abcdefghijklmnop")] + [InlineData("๒๑abcdefghijklmnop")] + [InlineData("๒๑๔๗๔๘๓abcdefghijklmnop")] + [InlineData("ลบ๒๑๔abcdefghijklmnop")] + [InlineData("+๒๑๔๗๔abcdefghijklmnop")] + [InlineData("+๒๑abcdefghijklmnop")] + [InlineData("+๒abcdefghijklmnop")] + [InlineData("+๒๑๔๗๔๘๓๖abcdefghijklmnop")] + [InlineData("+๒๑๔๗๔๘๓๖๔abcdefghijklmnop")] + [InlineData("+๒๑๔๗abcdefghijklmnop")] + [InlineData("+๒๑๔๗๔๘abcdefghijklmnop")] + [InlineData("+๒๑๔๗๔๘๓abcdefghijklmnop")] + [InlineData("+๒๑๔๗๔๘๓๖๔๗abcdefghijklmnop")] + [InlineData("+๒๑๔abcdefghijklmnop")] + public unsafe void ParseInt32Thai(string text) + { + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + PrimitiveParser.TryParseInt32(utf8Span, out int value, out int bytesConsumed, 'G', TestHelper.ThaiTable); + TestHelper.DoNotIgnore(value, bytesConsumed); + } + } + } + } + } +} diff --git a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserIntegerTests.cs b/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserIntegerTests.cs index 585d2a5bbd0..fe6026c6325 100644 --- a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserIntegerTests.cs +++ b/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserIntegerTests.cs @@ -2,51 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Runtime.CompilerServices; + using Xunit; namespace System.Text.Primitives.Tests { public partial class PrimitiveParserTests { - private byte[] UtfEncode(string s, bool utf16) - { - if (utf16) - return Text.Encoding.Unicode.GetBytes(s); - else - return Text.Encoding.UTF8.GetBytes(s); - } - - static byte[][] s_thaiUtf8DigitsAndSymbols = new byte[][] - { - new byte[] { 0xe0, 0xb9, 0x90 }, new byte[] { 0xe0, 0xb9, 0x91 }, new byte[] { 0xe0, 0xb9, 0x92 }, - new byte[] { 0xe0, 0xb9, 0x93 }, new byte[] { 0xe0, 0xb9, 0x94 }, new byte[] { 0xe0, 0xb9, 0x95 }, new byte[] { 0xe0, 0xb9, 0x96 }, - new byte[] { 0xe0, 0xb9, 0x97 }, new byte[] { 0xe0, 0xb9, 0x98 }, new byte[] { 0xe0, 0xb9, 0x99 }, new byte[] { 0xE0, 0xB8, 0x88, 0xE0, 0xB8, 0x94 }, null, - new byte[] { 0xE0, 0xB8, 0xAA, 0xE0, 0xB8, 0xB4, 0xE0, 0xB9, 0x88, 0xE0, 0xB8, 0x87, 0xE0, 0xB8, 0x97, 0xE0, 0xB8, 0xB5, 0xE0, 0xB9, 0x88, 0xE0, 0xB9, 0x83, - 0xE0, 0xB8, 0xAB, 0xE0, 0xB8, 0x8D, 0xE0, 0xB9, 0x88, 0xE0, 0xB9, 0x82, 0xE0, 0xB8, 0x95, 0xE0, 0xB9, 0x80, 0xE0, 0xB8, 0xAB, 0xE0, 0xB8, 0xA5, 0xE0, - 0xB8, 0xB7, 0xE0, 0xB8, 0xAD, 0xE0, 0xB9, 0x80, 0xE0, 0xB8, 0x81, 0xE0, 0xB8, 0xB4, 0xE0, 0xB8, 0x99 }, - new byte[] { 0xE0, 0xB8, 0xA5, 0xE0, 0xB8, 0x9A }, new byte[] { 43 }, new byte[] { 0xE0, 0xB9, 0x84, 0xE0, 0xB8, 0xA1, 0xE0, 0xB9, 0x88, 0xE0, 0xB9, - 0x83, 0xE0, 0xB8, 0x8A, 0xE0, 0xB9, 0x88, 0xE0, 0xB8, 0x95, 0xE0, 0xB8, 0xB1, 0xE0, 0xB8, 0xA7, 0xE0, 0xB9, 0x80, 0xE0, 0xB8, 0xA5, 0xE0, 0xB8, 0x82 }, - new byte[] { 69 }, new byte[] { 101 }, - }; - - public class ThaiSymbolTable : SymbolTable - { - public ThaiSymbolTable() : base(s_thaiUtf8DigitsAndSymbols) {} - - public override bool TryEncode(byte utf8, Span destination, out int bytesWritten) - => SymbolTable.InvariantUtf8.TryEncode(utf8, destination, out bytesWritten); - - public override bool TryEncode(ReadOnlySpan utf8, Span destination, out int bytesConsumed, out int bytesWritten) - => SymbolTable.InvariantUtf8.TryEncode(utf8, destination, out bytesConsumed, out bytesWritten); - - public override bool TryParse(ReadOnlySpan source, out byte utf8, out int bytesConsumed) - => SymbolTable.InvariantUtf8.TryParse(source, out utf8, out bytesConsumed); - - public override bool TryParse(ReadOnlySpan source, Span utf8, out int bytesConsumed, out int bytesWritten) - => SymbolTable.InvariantUtf8.TryParse(source, utf8, out bytesConsumed, out bytesWritten); - } - - static SymbolTable s_thaiTable = new ThaiSymbolTable(); #region byte @@ -62,8 +25,8 @@ public unsafe void ParseByteDec(string text, bool expectSuccess, byte expectedVa { byte parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -140,8 +103,8 @@ public unsafe void ParseByteHex(string text, bool expectSuccess, Byte expectedVa { byte parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -218,8 +181,8 @@ public unsafe void ParseUInt16Dec(string text, bool expectSuccess, ushort expect { ushort parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -296,8 +259,8 @@ public unsafe void ParseUInt16Hex(string text, bool expectSuccess, UInt16 expect { ushort parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -374,8 +337,8 @@ public unsafe void ParseUInt32Dec(string text, bool expectSuccess, uint expected { uint parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -452,8 +415,8 @@ public unsafe void ParseUInt32Hex(string text, bool expectSuccess, UInt32 expect { uint parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -530,8 +493,8 @@ public unsafe void ParseUInt64Dec(string text, bool expectSuccess, ulong expecte { ulong parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -608,8 +571,8 @@ public unsafe void ParseUInt64Hex(string text, bool expectSuccess, UInt64 expect { ulong parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -689,8 +652,8 @@ public unsafe void ParseSByteDec(string text, bool expectSuccess, sbyte expected { sbyte parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -771,10 +734,10 @@ public unsafe void ParseSByteThai(string text, bool expectSuccess, int index, sb { sbyte parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); bool result; - result = PrimitiveParser.TryParseSByte(utf8Span.Slice(index), out parsedValue, out consumed, 'G', s_thaiTable); + result = PrimitiveParser.TryParseSByte(utf8Span.Slice(index), out parsedValue, out consumed, 'G', TestHelper.ThaiTable); Assert.Equal(expectSuccess, result); Assert.Equal(expectedValue, parsedValue); @@ -794,8 +757,8 @@ public unsafe void ParseSByteHex(string text, bool expectSuccess, sbyte expected { sbyte parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -874,8 +837,8 @@ public unsafe void ParseInt16Dec(string text, bool expectSuccess, short expected { short parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -956,10 +919,10 @@ public unsafe void ParseInt16Thai(string text, bool expectSuccess, int index, sh { short parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); bool result; - result = PrimitiveParser.TryParseInt16(utf8Span.Slice(index), out parsedValue, out consumed, 'G', s_thaiTable); + result = PrimitiveParser.TryParseInt16(utf8Span.Slice(index), out parsedValue, out consumed, 'G', TestHelper.ThaiTable); Assert.Equal(expectSuccess, result); Assert.Equal(expectedValue, parsedValue); @@ -979,8 +942,8 @@ public unsafe void ParseInt16Hex(string text, bool expectSuccess, short expected { short parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -1046,21 +1009,60 @@ public unsafe void ParseInt16Hex(string text, bool expectSuccess, short expected #region int [Theory] + [InlineData("a1", false, 0, 0)] + [InlineData("1", true, 1, 1)] + [InlineData("-1", true, -1, 2)] + [InlineData("11", true, 11, 2)] + [InlineData("-11", true, -11, 3)] + [InlineData("00a0", true, 0, 2)] + [InlineData("00a", true, 0, 2)] [InlineData("111", true, 111, 3)] [InlineData("492206507abcdefg", true, 492206507, 9)] [InlineData("2147483647", true, 2147483647, 10)] // max [InlineData("-2147483648", true, -2147483648, 11)] // min [InlineData("-A", false, 0, 0)] // invalid character after a sign [InlineData("I am 1", false, 0, 0)] // invalid character test - [InlineData(" !", false, 0, 0)] // invalid character test w/ char < '0' + [InlineData("123!", true, 123, 3)] // invalid character test w/ char < '0' // TODO: Fix test case elsewhere [InlineData("2147483648", false, 0, 0)] // positive overflow test [InlineData("-2147483649", false, 0, 0)] // negative overflow test + [InlineData("0", true, 0, 1)] + [InlineData("+1", true, 1, 2)] + [InlineData("+2147483647", true, 2147483647, 11)] + [InlineData("as3gf31t`2c", false, 0, 0)] + [InlineData("agbagbagb5", false, 0, 0)] + [InlineData("1faag", true, 1, 1)] + [InlineData("-1sdg", true, -1, 2)] + [InlineData("-afsagsag4", false, 0, 0)] + [InlineData("+a", false, 0, 0)] + [InlineData("-000012345abcdefg1", true, -12345, 10)] + [InlineData("+000012345abcdefg1", true, 12345, 10)] + [InlineData("000012345abcdefg1", true, 12345, 9)] + [InlineData("0000001234145abcdefg1", true, 1234145, 13)] + [InlineData("00000000000000abcdefghijklmnop", true, 0, 14)] + [InlineData("000000a", true, 0, 6)] + [InlineData("00000000000000!", true, 0, 14)] + [InlineData("00000000000000", true, 0, 14)] + [InlineData("1147483648", true, 1147483648, 10)] + [InlineData("-1147483649", true, -1147483649, 11)] + [InlineData("12345!6", true, 12345, 5)] + [InlineData("12345!abc", true, 12345, 5)] + [InlineData("!!", false, 0, 0)] + [InlineData("+", false, 0, 0)] + [InlineData("-", false, 0, 0)] + [InlineData("", false, 0, 0)] + [InlineData("5", true, 5, 1)] + [InlineData("^", false, 0, 0)] + [InlineData("41474836482145", false, 0, 0)] + [InlineData("02147483647", true, 2147483647, 11)] // max + [InlineData("-02147483648", true, -2147483648, 12)] // min + [InlineData("02147483648", false, 0, 0)] // positive overflow test + [InlineData("-02147483649", false, 0, 0)] // negative overflow test public unsafe void ParseInt32Dec(string text, bool expectSuccess, int expectedValue, int expectedConsumed) { int parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -1126,31 +1128,288 @@ public unsafe void ParseInt32Dec(string text, bool expectSuccess, int expectedVa } [Theory] - [InlineData("๑๑๑", true, 0, 111, 9)] - [InlineData("เรื่องเหลวไหล๒๗", true, 39, 27, 6)] - [InlineData("๕๖กขฃคฅฆง", true, 0, 56, 6)] - [InlineData("ที่เล็กที่สุดของประเภทนี้คือลบ๑๒๘.", true, 84, -128, 15)] - [InlineData("ปล่อยให้พวกเขา ลบ๒๘ กินเค้ก", true, 43, -28, 12)] - [InlineData("๑๒๗", true, 0, 127, 9)] // max - [InlineData("ลบ๑๒๘", true, 0, -128, 15)] // min - [InlineData("ลบA", false, 0, 0, 0)] // invalid character after a sign - [InlineData("I am ๑", false, 0, 0, 0)] // invalid character test - [InlineData(" !", false, 0, 0, 0)] // invalid character test w/ char < '0' - [InlineData("ลป๑", false, 0, 0, 0)] // - public unsafe void ParseInt32Thai(string text, bool expectSuccess, int index, int expectedValue, int expectedConsumed) + [InlineData("2", true, 2, 1)] + [InlineData("21", true, 21, 2)] + [InlineData("214", true, 214, 3)] + [InlineData("2147", true, 2147, 4)] + [InlineData("21474", true, 21474, 5)] + [InlineData("214748", true, 214748, 6)] + [InlineData("2147483", true, 2147483, 7)] + [InlineData("21474836", true, 21474836, 8)] + [InlineData("214748364", true, 214748364, 9)] + [InlineData("2147483647", true, 2147483647, 10)] + [InlineData("+2", true, 2, 2)] + [InlineData("+21", true, 21, 3)] + [InlineData("+214", true, 214, 4)] + [InlineData("+2147", true, 2147, 5)] + [InlineData("+21474", true, 21474, 6)] + [InlineData("+214748", true, 214748, 7)] + [InlineData("+2147483", true, 2147483, 8)] + [InlineData("+21474836", true, 21474836, 9)] + [InlineData("+214748364", true, 214748364, 10)] + [InlineData("+2147483647", true, 2147483647, 11)] + [InlineData("-2", true, -2, 2)] + [InlineData("-21", true, -21, 3)] + [InlineData("-214", true, -214, 4)] + [InlineData("-2147", true, -2147, 5)] + [InlineData("-21474", true, -21474, 6)] + [InlineData("-214748", true, -214748, 7)] + [InlineData("-2147483", true, -2147483, 8)] + [InlineData("-21474836", true, -21474836, 9)] + [InlineData("-214748364", true, -214748364, 10)] + [InlineData("-2147483647", true, -2147483647, 11)] + private void ParseInt32VariableLength(string text, bool expectSuccess, int expectedValue, int expectedConsumed) { - int parsedValue; - int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - bool result; + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + bool result = PrimitiveParser.InvariantUtf8.TryParseInt32(utf8Span, out int parsedValue, out int consumed); + Assert.Equal(expectSuccess, result); + Assert.Equal(expectedValue, parsedValue); + Assert.Equal(expectedConsumed, consumed); + } + + [Theory] + [InlineData("3147483647")] + [InlineData("4147483647")] + [InlineData("5147483647")] + [InlineData("6147483647")] + [InlineData("7147483647")] + [InlineData("8147483647")] + [InlineData("9147483647")] + [InlineData("2147483648")] + [InlineData("3147483648")] + [InlineData("4147483648")] + [InlineData("5147483648")] + [InlineData("6147483648")] + [InlineData("7147483648")] + [InlineData("8147483648")] + [InlineData("9147483648")] + [InlineData("11474836471")] + [InlineData("21474836471")] + [InlineData("31474836471")] + [InlineData("41474836471")] + [InlineData("51474836471")] + [InlineData("61474836471")] + [InlineData("71474836471")] + [InlineData("81474836471")] + [InlineData("91474836471")] + [InlineData("11474836481")] + [InlineData("21474836481")] + [InlineData("31474836481")] + [InlineData("41474836481")] + [InlineData("51474836481")] + [InlineData("61474836481")] + [InlineData("71474836481")] + [InlineData("81474836481")] + [InlineData("91474836481")] + private void ParseInt32VariableOverflowTests(string text) + { + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + bool result = PrimitiveParser.InvariantUtf8.TryParseInt32(utf8Span, out int parsedValue, out int consumed); + Assert.Equal(false, result); + Assert.Equal(0, parsedValue); + Assert.Equal(0, consumed); + } + + [Theory] + [InlineData("0", true, 0, int.MaxValue)] + [InlineData("2", true, 2, int.MaxValue)] + [InlineData("21", true, 21, int.MaxValue)] + [InlineData("+2", true, 2, int.MaxValue)] + [InlineData("-2", true, -2, int.MaxValue)] + [InlineData("2147483647", true, 2147483647, int.MaxValue)] // max + [InlineData("-2147483648", true, -2147483648, int.MaxValue)] // min + [InlineData("2147483648", false, 0, 0)] // positive overflow test + [InlineData("-2147483649", false, 0, 0)] // negative overflow test + [InlineData("12345abcdefg1", true, 12345, int.MaxValue - 8)] + [InlineData("1234145abcdefg1", true, 1234145, int.MaxValue - 8)] + [InlineData("abcdefghijklmnop1", true, 0, int.MaxValue - 17)] + [InlineData("1147483648", true, 1147483648, int.MaxValue)] + [InlineData("-1147483649", true, -1147483649, int.MaxValue)] + public unsafe void ParseInt32OverflowCheck(string text, bool expectSuccess, int expectedValue, int expectedConsumed) + { + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + + const int TwoGiB = int.MaxValue; + + unsafe + { + if (!TestHelper.TryAllocNative((IntPtr)TwoGiB, out IntPtr memBlock)) + return; // It's not implausible to believe that a 2gb allocation will fail - if so, skip this test to avoid unnecessary test flakiness. + + try + { + ref byte memory = ref Unsafe.AsRef(memBlock.ToPointer()); + var span = new Span(memBlock.ToPointer(), TwoGiB); + span.Fill(48); + + byte sign = utf8Span[0]; + if (sign == '-' || sign == '+') + { + span[0] = sign; + utf8Span = utf8Span.Slice(1); + } + utf8Span.CopyTo(span.Slice(TwoGiB - utf8Span.Length)); + + bool result = PrimitiveParser.InvariantUtf8.TryParseInt32(span, out int parsedValue, out int consumed); + Assert.Equal(expectSuccess, result); + Assert.Equal(expectedValue, parsedValue); + Assert.Equal(expectedConsumed, consumed); + } + finally + { + TestHelper.ReleaseNative(ref memBlock); + } + } + } - result = PrimitiveParser.TryParseInt32(utf8Span.Slice(index), out parsedValue, out consumed, 'G', s_thaiTable); + [Theory] + [InlineData("๑๑๑", true, 111, 9)] + [InlineData("๕๖กขฃคฅฆง", true, 56, 6)] + [InlineData("๑๒๗", true, 127, 9)] // max + [InlineData("ลบ๑๒๘", true, -128, 15)] // min + [InlineData("ลบA", false, 0, 0)] // invalid character after a sign + [InlineData("I am ๑", false, 0, 0)] // invalid character test + [InlineData(" !", false, 0, 0)] // invalid character test w/ char < '0' + [InlineData("ลป๑", false, 0, 0)] + [InlineData("๑๐๗๓๗๔๑๘๒", true, 107374182, 9 * 3)] + [InlineData("๒๑๔๗๔๘๓๖๔๗", true, 2147483647, 10 * 3)] + [InlineData("๐๒๑๔๗๔๘๓๖๔๗", true, 2147483647, 11 * 3)] + [InlineData("๐", true, 0, 1 * 3)] + [InlineData("ลบ๒๑๔๗๔๘๓๖๔๘", true, -2147483648, 10 * 3 + 6)] + [InlineData("ลบ๐๒๑๔๗๔๘๓๖๔๘", true, -2147483648, 11 * 3 + 6)] + [InlineData("๒๑๔๗๔๘๓๖๔", true, 214748364, 9 * 3)] + [InlineData("๒", true, 2, 1 * 3)] + [InlineData("๒๑๔๗๔๘๓๖", true, 21474836, 8 * 3)] + [InlineData("ลบ๒๑๔๗๔", true, -21474, 5 * 3 + 6)] + [InlineData("๒๑๔๗๔", true, 21474, 5 * 3)] + [InlineData("ลบ๒๑", true, -21, 2 * 3 + 6)] + [InlineData("ลบ๒", true, -2, 1 * 3 + 6)] + [InlineData("๒๑๔", true, 214, 3 * 3)] + [InlineData("ลบ๒๑๔๗๔๘๓๖", true, -21474836, 8 * 3 + 6)] + [InlineData("ลบ๒๑๔๗๔๘๓๖๔", true, -214748364, 9 * 3 + 6)] + [InlineData("๒๑๔๗", true, 2147, 4 * 3)] + [InlineData("ลบ๒๑๔๗", true, -2147, 4 * 3 + 6)] + [InlineData("ลบ๒๑๔๗๔๘", true, -214748, 6 * 3 + 6)] + [InlineData("ลบ๒๑๔๗๔๘๓", true, -2147483, 7 * 3 + 6)] + [InlineData("๒๑๔๗๔๘", true, 214748, 6 * 3)] + [InlineData("๒๑", true, 21, 2 * 3)] + [InlineData("๒๑๔๗๔๘๓", true, 2147483, 7 * 3)] + [InlineData("ลบ๒๑๔", true, -214, 3 * 3 + 6)] + [InlineData("+๒๑๔๗๔", true, 21474, 5 * 3 + 1)] + [InlineData("+๒๑", true, 21, 2 * 3 + 1)] + [InlineData("+๒", true, 2, 1 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓๖", true, 21474836, 8 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓๖๔", true, 214748364, 9 * 3 + 1)] + [InlineData("+๒๑๔๗", true, 2147, 4 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘", true, 214748, 6 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓", true, 2147483, 7 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓๖๔๗", true, 2147483647, 10 * 3 + 1)] + [InlineData("+๒๑๔", true, 214, 3 * 3 + 1)] + [InlineData("๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๑๒๓๕abcdfg", true, 1235, 24 * 3)] + [InlineData("๒๑๔๗๔๘๓๖๔abcdefghijklmnop", true, 214748364, 9 * 3)] + [InlineData("๒abcdefghijklmnop", true, 2, 1 * 3)] + [InlineData("๒๑๔๗๔๘๓๖abcdefghijklmnop", true, 21474836, 8 * 3)] + [InlineData("ลบ๒๑๔๗๔abcdefghijklmnop", true, -21474, 5 * 3 + 6)] + [InlineData("๒๑๔๗๔abcdefghijklmnop", true, 21474, 5 * 3)] + [InlineData("ลบ๒๑abcdefghijklmnop", true, -21, 2 * 3 + 6)] + [InlineData("ลบ๒abcdefghijklmnop", true, -2, 1 * 3 + 6)] + [InlineData("๒๑๔abcdefghijklmnop", true, 214, 3 * 3)] + [InlineData("ลบ๒๑๔๗๔๘๓๖abcdefghijklmnop", true, -21474836, 8 * 3 + 6)] + [InlineData("ลบ๒๑๔๗๔๘๓๖๔abcdefghijklmnop", true, -214748364, 9 * 3 + 6)] + [InlineData("๒๑๔๗abcdefghijklmnop", true, 2147, 4 * 3)] + [InlineData("ลบ๒๑๔๗abcdefghijklmnop", true, -2147, 4 * 3 + 6)] + [InlineData("ลบ๒๑๔๗๔๘abcdefghijklmnop", true, -214748, 6 * 3 + 6)] + [InlineData("ลบ๒๑๔๗๔๘๓abcdefghijklmnop", true, -2147483, 7 * 3 + 6)] + [InlineData("๒๑๔๗๔๘abcdefghijklmnop", true, 214748, 6 * 3)] + [InlineData("๒๑abcdefghijklmnop", true, 21, 2 * 3)] + [InlineData("๒๑๔๗๔๘๓abcdefghijklmnop", true, 2147483, 7 * 3)] + [InlineData("ลบ๒๑๔abcdefghijklmnop", true, -214, 3 * 3 + 6)] + [InlineData("+๒๑๔๗๔abcdefghijklmnop", true, 21474, 5 * 3 + 1)] + [InlineData("+๒๑abcdefghijklmnop", true, 21, 2 * 3 + 1)] + [InlineData("+๒abcdefghijklmnop", true, 2, 1 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓๖abcdefghijklmnop", true, 21474836, 8 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓๖๔abcdefghijklmnop", true, 214748364, 9 * 3 + 1)] + [InlineData("+๒๑๔๗abcdefghijklmnop", true, 2147, 4 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘abcdefghijklmnop", true, 214748, 6 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓abcdefghijklmnop", true, 2147483, 7 * 3 + 1)] + [InlineData("+๒๑๔๗๔๘๓๖๔๗abcdefghijklmnop", true, 2147483647, 10 * 3 + 1)] + [InlineData("+๒๑๔abcdefghijklmnop", true, 214, 3 * 3 + 1)] + [InlineData("๐๐a๐", true, 0, 6)] + [InlineData("๐๐a", true, 0, 6)] + [InlineData("", false, 0, 0)] + [InlineData("+", false, 0, 0)] + [InlineData("ลบ", false, 0, 0)] + public unsafe void ParseInt32Thai(string text, bool expectSuccess, int expectedValue, int expectedConsumed) + { + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + bool result = PrimitiveParser.TryParseInt32(utf8Span, out int parsedValue, out int consumed, 'G', TestHelper.ThaiTable); Assert.Equal(expectSuccess, result); Assert.Equal(expectedValue, parsedValue); Assert.Equal(expectedConsumed, consumed); } + //[Theory] // TODO: Test is too slow, only enable for "outerloop" + [InlineData("๐", true, 0, int.MaxValue)] + [InlineData("๒", true, 2, int.MaxValue)] + [InlineData("๒๑", true, 21, int.MaxValue)] + [InlineData("+๒", true, 2, int.MaxValue)] + [InlineData("ลบ๒", true, -2, int.MaxValue)] + [InlineData("๒๑๔๗๔๘๓๖๔๗", true, 2147483647, int.MaxValue)] // max + [InlineData("ลบ๒๑๔๗๔๘๓๖๔๘", true, -2147483648, int.MaxValue)] // min + [InlineData("๒๑๔๗๔๘๓๖๔๘", false, 0, 0)] // positive overflow test + [InlineData("ลบ๒๑๔๗๔๘๓๖๔๙", false, 0, 0)] // negative overflow test + [InlineData("๑๒๓๔๕abcdefg๑", true, 12345, int.MaxValue - 8)] + [InlineData("๑๒๓๔๑๔๕abcdefg๑", true, 1234145, int.MaxValue - 8)] + [InlineData("abcdefghijklmnop๑", true, 0, int.MaxValue - 17)] + public unsafe void ParseInt32ThaiOverflowCheck(string text, bool expectSuccess, int expectedValue, int expectedConsumed) + { + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + + const int TwoGiB = int.MaxValue; + + unsafe + { + if (!TestHelper.TryAllocNative((IntPtr)TwoGiB, out IntPtr memBlock)) + return; // It's not implausible to believe that a 2gb allocation will fail - if so, skip this test to avoid unnecessary test flakiness. + + try + { + ref byte memory = ref Unsafe.AsRef(memBlock.ToPointer()); + var span = new Span(memBlock.ToPointer(), TwoGiB); + for (int i = 0; i < TwoGiB / 3; i ++) + { + span[i * 3] = 0xe0; + span[i * 3 + 1] = 0xb9; + span[i * 3 + 2] = 0x90; + } + + byte sign = utf8Span[0]; + Span minusSpan = new byte[] { sign, 0xb8, 0xa5, 0xe0, 0xb8, 0x9a }; + if (sign == '+') + { + span[0] = sign; + utf8Span = utf8Span.Slice(1); + } + else if (span.StartsWith(minusSpan)) + { + utf8Span = utf8Span.Slice(6); + } + + utf8Span.CopyTo(span.Slice(TwoGiB - utf8Span.Length)); + + bool result = PrimitiveParser.TryParseInt32(span, out int parsedValue, out int consumed, 'G', TestHelper.ThaiTable); + Assert.Equal(expectSuccess, result); + Assert.Equal(expectedValue, parsedValue); + Assert.Equal(expectedConsumed, consumed); + } + finally + { + TestHelper.ReleaseNative(ref memBlock); + } + } + } + [Theory] [InlineData("1f", true, 0x1f, 2)] [InlineData("7F34098ghijzl", true, 0x7F34098, 7)] @@ -1164,8 +1423,8 @@ public unsafe void ParseInt32Hex(string text, bool expectSuccess, int expectedVa { int parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -1244,8 +1503,8 @@ public unsafe void ParseInt64Dec(string text, bool expectSuccess, long expectedV { long parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); @@ -1321,15 +1580,15 @@ public unsafe void ParseInt64Dec(string text, bool expectSuccess, long expectedV [InlineData("ลบA", false, 0, 0, 0)] // invalid character after a sign [InlineData("I am ๑", false, 0, 0, 0)] // invalid character test [InlineData(" !", false, 0, 0, 0)] // invalid character test w/ char < '0' - [InlineData("ลป๑", false, 0, 0, 0)] // + [InlineData("ลป๑", false, 0, 0, 0)] public unsafe void ParseInt64Thai(string text, bool expectSuccess, int index, long expectedValue, int expectedConsumed) { long parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); bool result; - result = PrimitiveParser.TryParseInt64(utf8Span.Slice(index), out parsedValue, out consumed, 'G', s_thaiTable); + result = PrimitiveParser.TryParseInt64(utf8Span.Slice(index), out parsedValue, out consumed, 'G', TestHelper.ThaiTable); Assert.Equal(expectSuccess, result); Assert.Equal(expectedValue, parsedValue); @@ -1349,8 +1608,8 @@ public unsafe void ParseInt64Hex(string text, bool expectSuccess, long expectedV { long parsedValue; int consumed; - ReadOnlySpan utf8Span = UtfEncode(text, false); - ReadOnlySpan utf16ByteSpan = UtfEncode(text, true); + ReadOnlySpan utf8Span = TestHelper.UtfEncode(text, false); + ReadOnlySpan utf16ByteSpan = TestHelper.UtfEncode(text, true); ReadOnlySpan utf16CharSpan = utf16ByteSpan.NonPortableCast(); byte[] textBytes = utf8Span.ToArray(); char[] textChars = utf16CharSpan.ToArray(); diff --git a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserPerfTests.cs b/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserPerfTests.cs deleted file mode 100644 index 5d2202fef61..00000000000 --- a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserPerfTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -namespace System.Text.Primitives.Tests -{ - public partial class PrimitiveParserPerfTests - { - private const int LoadIterations = 30000; - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void DoNotIgnore(uint value, int consumed) - { - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void DoNotIgnore(ulong value, int consumed) - { - } - - private static void PrintTestName(string testString, [CallerMemberName] string testName = "") - { - if (testString != null) - { - Console.WriteLine("{0} called with test string \"{1}\".", testName, testString); - } - else - { - Console.WriteLine("{0} called with no test string.", testName); - } - } - } -} diff --git a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt32PerfTests.cs b/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt32PerfTests.cs index e3f5dd6c941..a49950f0a7e 100644 --- a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt32PerfTests.cs +++ b/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt32PerfTests.cs @@ -47,11 +47,11 @@ private static void BaselineSimpleByteStarToUInt32(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; uint.TryParse(text, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -64,11 +64,11 @@ private static void BaselineSimpleByteStarToUInt32_VariableLength() { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; uint.TryParse(s_UInt32TextArray[i % 10], out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -84,11 +84,11 @@ private static void BaselineByteStarToUInt32(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; uint.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -101,11 +101,11 @@ private static void BaselineByteStarToUInt32_VariableLength() { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; uint.TryParse(s_UInt32TextArray[i % 10], NumberStyles.None, CultureInfo.InvariantCulture, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -121,11 +121,11 @@ private static void BaselineByteStarToUInt32Hex(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; uint.TryParse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -138,11 +138,11 @@ private static void BaselineByteStarToUInt32Hex_VariableLength() { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; uint.TryParse(s_UInt32TextArrayHex[i % 8], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -162,11 +162,11 @@ private unsafe static void PrimitiveParserByteStarToUInt32(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteStar, length, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -186,14 +186,14 @@ private unsafe static void PrimitiveParserByteStarToUInt32_VariableLength() { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { byte[] utf8ByteArray = byteArrayList[i % 10]; fixed (byte* utf8ByteStar = utf8ByteArray) { uint value; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteStar, utf8ByteArray.Length, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -214,12 +214,12 @@ private unsafe static void PrimitiveParserByteStarToUInt32_BytesConsumed(string { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteStar, length, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -239,7 +239,7 @@ private unsafe static void PrimitiveParserByteStarToUInt32_BytesConsumed_Variabl { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { byte[] utf8ByteArray = byteArrayList[i % 10]; fixed (byte* utf8ByteStar = utf8ByteArray) @@ -247,7 +247,7 @@ private unsafe static void PrimitiveParserByteStarToUInt32_BytesConsumed_Variabl uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteStar, utf8ByteArray.Length, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -266,11 +266,11 @@ private unsafe static void PrimitiveParserByteSpanToUInt32(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteSpan, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -290,12 +290,12 @@ private unsafe static void PrimitiveParserByteSpanToUInt32_VariableLength() { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ReadOnlySpan utf8ByteSpan = utf8ByteArray[i % textLength]; uint value; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteSpan, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -313,12 +313,12 @@ private unsafe static void PrimitiveParserByteSpanToUInt32_BytesConsumed(string { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteSpan, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -337,13 +337,13 @@ private unsafe static void PrimitiveParserByteSpanToUInt32_BytesConsumed_Variabl { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ReadOnlySpan utf8ByteSpan = utf8ByteArray[i % textLength]; uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.TryParseUInt32(utf8ByteSpan, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -363,11 +363,11 @@ private unsafe static void PrimitiveParserByteStarToUInt32Hex(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteStar, length, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -387,14 +387,14 @@ private unsafe static void PrimitiveParserByteStarToUInt32Hex_VariableLength() { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { byte[] utf8ByteArray = byteArrayList[i % 8]; fixed (byte* utf8ByteStar = utf8ByteArray) { uint value; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteStar, utf8ByteArray.Length, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -415,12 +415,12 @@ private unsafe static void PrimitiveParserByteStarToUInt32Hex_BytesConsumed(stri { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteStar, length, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -440,7 +440,7 @@ private unsafe static void PrimitiveParserByteStarToUInt32Hex_BytesConsumed_Vari { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { byte[] utf8ByteArray = byteArrayList[i % 8]; fixed (byte* utf8ByteStar = utf8ByteArray) @@ -448,7 +448,7 @@ private unsafe static void PrimitiveParserByteStarToUInt32Hex_BytesConsumed_Vari uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteStar, utf8ByteArray.Length, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -467,11 +467,11 @@ private unsafe static void PrimitiveParserByteSpanToUInt32Hex(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteSpan, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -490,12 +490,12 @@ private unsafe static void PrimitiveParserByteSpanToUInt32Hex_VariableLength() { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ReadOnlySpan utf8ByteSpan = utf8ByteArray[i % textLength]; uint value; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteSpan, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -513,12 +513,12 @@ private unsafe static void PrimitiveParserByteSpanToUInt32Hex_BytesConsumed(stri { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteSpan, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -537,13 +537,13 @@ private unsafe static void PrimitiveParserByteSpanToUInt32Hex_BytesConsumed_Vari { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ReadOnlySpan utf8ByteSpan = utf8ByteArray[i % textLength]; uint value; int bytesConsumed; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt32(utf8ByteSpan, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } diff --git a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt64PerfTests.cs b/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt64PerfTests.cs index e5e93a51114..04c3a9d2661 100644 --- a/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt64PerfTests.cs +++ b/tests/System.Text.Primitives.Tests/Parsing/PrimitiveParserUInt64PerfTests.cs @@ -20,11 +20,11 @@ private static void BaselineSimpleByteStarToUInt64(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; ulong.TryParse(text, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -40,11 +40,11 @@ private static void BaselineByteStarToUInt64(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -60,11 +60,11 @@ private static void BaselineByteStarToUInt64Hex(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; ulong.TryParse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -84,11 +84,11 @@ private unsafe static void PrimitiveParserByteStarToUInt64(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; PrimitiveParser.InvariantUtf8.TryParseUInt64(utf8ByteStar, length, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -109,12 +109,12 @@ private unsafe static void PrimitiveParserByteStarToUInt64_BytesConsumed(string { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; int bytesConsumed; PrimitiveParser.InvariantUtf8.TryParseUInt64(utf8ByteStar, length, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -133,11 +133,11 @@ private unsafe static void PrimitiveParserByteSpanToUInt64(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; PrimitiveParser.InvariantUtf8.TryParseUInt64(utf8ByteSpan, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -155,12 +155,12 @@ private unsafe static void PrimitiveParserByteSpanToUInt64_BytesConsumed(string { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; int bytesConsumed; PrimitiveParser.InvariantUtf8.TryParseUInt64(utf8ByteSpan, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -180,11 +180,11 @@ private unsafe static void PrimitiveParserByteStarToUInt64Hex(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt64(utf8ByteStar, length, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -205,12 +205,12 @@ private unsafe static void PrimitiveParserByteStarToUInt64Hex_BytesConsumed(stri { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; int bytesConsumed; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt64(utf8ByteStar, length, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } @@ -229,11 +229,11 @@ private unsafe static void PrimitiveParserByteSpanToUInt64Hex(string text) { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt64(utf8ByteSpan, out value); - DoNotIgnore(value, 0); + TestHelper.DoNotIgnore(value, 0); } } } @@ -251,12 +251,12 @@ private unsafe static void PrimitiveParserByteSpanToUInt64Hex_BytesConsumed(stri { using (iteration.StartMeasurement()) { - for (int i = 0; i < LoadIterations; i++) + for (int i = 0; i < TestHelper.LoadIterations; i++) { ulong value; int bytesConsumed; PrimitiveParser.InvariantUtf8.Hex.TryParseUInt64(utf8ByteSpan, out value, out bytesConsumed); - DoNotIgnore(value, bytesConsumed); + TestHelper.DoNotIgnore(value, bytesConsumed); } } } diff --git a/tests/System.Text.Primitives.Tests/TestHelper.cs b/tests/System.Text.Primitives.Tests/TestHelper.cs index 37f12cc6e42..d53e504247b 100644 --- a/tests/System.Text.Primitives.Tests/TestHelper.cs +++ b/tests/System.Text.Primitives.Tests/TestHelper.cs @@ -1,14 +1,46 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Buffers; using Xunit; +using System.Runtime.InteropServices; +using System.Threading; +using System.Runtime.CompilerServices; namespace System.Text.Primitives.Tests { public static class TestHelper { + public const int LoadIterations = 30000; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoNotIgnore(uint value, int consumed) + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoNotIgnore(ulong value, int consumed) + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoNotIgnore(int value, int consumed) + { + } + + public static void PrintTestName(string testString, [CallerMemberName] string testName = "") + { + if (testString != null) + { + Console.WriteLine("{0} called with test string \"{1}\".", testName, testString); + } + else + { + Console.WriteLine("{0} called with no test string.", testName); + } + } + public static string SpanToString(Span span, SymbolTable symbolTable = null) { if (symbolTable == null || symbolTable == SymbolTable.InvariantUtf8) @@ -25,5 +57,85 @@ public static string SpanToString(Span span, SymbolTable symbolTable = nul throw new NotSupportedException(); } + + // Borrowed from https://github.com/dotnet/corefx/blob/master/src/System.Memory/tests/AllocationHelper.cs + + private static readonly Mutex MemoryLock = new Mutex(); + private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(120); + + public static bool TryAllocNative(IntPtr size, out IntPtr memory) + { + memory = IntPtr.Zero; + + if (!MemoryLock.WaitOne(WaitTimeout)) + return false; + + try + { + memory = Marshal.AllocHGlobal(size); + } + catch (OutOfMemoryException) + { + memory = IntPtr.Zero; + MemoryLock.ReleaseMutex(); + } + + return memory != IntPtr.Zero; + } + + public static void ReleaseNative(ref IntPtr memory) + { + try + { + Marshal.FreeHGlobal(memory); + memory = IntPtr.Zero; + } + finally + { + MemoryLock.ReleaseMutex(); + } + } + + public static byte[] UtfEncode(string s, bool utf16) + { + if (utf16) + return Text.Encoding.Unicode.GetBytes(s); + else + return Text.Encoding.UTF8.GetBytes(s); + } + + // TODO: Fix Thai + symbol and adjust tests. + // Change from new byte[] { 43 }, i.e. '+' to new byte[] { 0xE0, 0xB8, 0x9A, 0xE0, 0xB8, 0xA7, 0xE0, 0xB8, 0x81 }, i.e. 'บวก' + static byte[][] s_thaiUtf8DigitsAndSymbols = new byte[][] + { + new byte[] { 0xe0, 0xb9, 0x90 }, new byte[] { 0xe0, 0xb9, 0x91 }, new byte[] { 0xe0, 0xb9, 0x92 }, + new byte[] { 0xe0, 0xb9, 0x93 }, new byte[] { 0xe0, 0xb9, 0x94 }, new byte[] { 0xe0, 0xb9, 0x95 }, new byte[] { 0xe0, 0xb9, 0x96 }, + new byte[] { 0xe0, 0xb9, 0x97 }, new byte[] { 0xe0, 0xb9, 0x98 }, new byte[] { 0xe0, 0xb9, 0x99 }, new byte[] { 0xE0, 0xB8, 0x88, 0xE0, 0xB8, 0x94 }, null, + new byte[] { 0xE0, 0xB8, 0xAA, 0xE0, 0xB8, 0xB4, 0xE0, 0xB9, 0x88, 0xE0, 0xB8, 0x87, 0xE0, 0xB8, 0x97, 0xE0, 0xB8, 0xB5, 0xE0, 0xB9, 0x88, 0xE0, 0xB9, 0x83, + 0xE0, 0xB8, 0xAB, 0xE0, 0xB8, 0x8D, 0xE0, 0xB9, 0x88, 0xE0, 0xB9, 0x82, 0xE0, 0xB8, 0x95, 0xE0, 0xB9, 0x80, 0xE0, 0xB8, 0xAB, 0xE0, 0xB8, 0xA5, 0xE0, + 0xB8, 0xB7, 0xE0, 0xB8, 0xAD, 0xE0, 0xB9, 0x80, 0xE0, 0xB8, 0x81, 0xE0, 0xB8, 0xB4, 0xE0, 0xB8, 0x99 }, + new byte[] { 0xE0, 0xB8, 0xA5, 0xE0, 0xB8, 0x9A }, new byte[] { 43 }, new byte[] { 0xE0, 0xB9, 0x84, 0xE0, 0xB8, 0xA1, 0xE0, 0xB9, 0x88, 0xE0, 0xB9, + 0x83, 0xE0, 0xB8, 0x8A, 0xE0, 0xB9, 0x88, 0xE0, 0xB8, 0x95, 0xE0, 0xB8, 0xB1, 0xE0, 0xB8, 0xA7, 0xE0, 0xB9, 0x80, 0xE0, 0xB8, 0xA5, 0xE0, 0xB8, 0x82 }, + new byte[] { 69 }, new byte[] { 101 }, + }; + + public class ThaiSymbolTable : SymbolTable + { + public ThaiSymbolTable() : base(s_thaiUtf8DigitsAndSymbols) { } + + public override bool TryEncode(byte utf8, Span destination, out int bytesWritten) + => SymbolTable.InvariantUtf8.TryEncode(utf8, destination, out bytesWritten); + + public override bool TryEncode(ReadOnlySpan utf8, Span destination, out int bytesConsumed, out int bytesWritten) + => SymbolTable.InvariantUtf8.TryEncode(utf8, destination, out bytesConsumed, out bytesWritten); + + public override bool TryParse(ReadOnlySpan source, out byte utf8, out int bytesConsumed) + => SymbolTable.InvariantUtf8.TryParse(source, out utf8, out bytesConsumed); + + public override bool TryParse(ReadOnlySpan source, Span utf8, out int bytesConsumed, out int bytesWritten) + => SymbolTable.InvariantUtf8.TryParse(source, utf8, out bytesConsumed, out bytesWritten); + } + + public static SymbolTable ThaiTable = new ThaiSymbolTable(); } } diff --git a/tests/System.Threading.Tasks.Channels.Tests/CaseBuilderTests.cs b/tests/System.Threading.Tasks.Channels.Tests/CaseBuilderTests.cs index 62d3cce21d6..abfac1a5eb1 100644 --- a/tests/System.Threading.Tasks.Channels.Tests/CaseBuilderTests.cs +++ b/tests/System.Threading.Tasks.Channels.Tests/CaseBuilderTests.cs @@ -20,9 +20,9 @@ public void CaseRead_Sync_InvalidArguments_ThrowsArgumentException() public void CaseRead_Async_InvalidArguments_ThrowsArgumentException() { CaseBuilder cb = Channel.CaseRead(Channel.CreateUnbounded(), i => { }); - Assert.Throws("channel", () => cb.CaseRead(null, (Func)null)); + Assert.Throws("channel", () => cb.CaseRead(null, null)); Assert.Throws("channel", () => cb.CaseRead(null, i => Task.CompletedTask)); - Assert.Throws("func", () => cb.CaseRead(Channel.CreateUnbounded(), (Func)null)); + Assert.Throws("func", () => cb.CaseRead(Channel.CreateUnbounded(), null)); } [Fact] @@ -30,7 +30,7 @@ public void CaseWrite_Sync_InvalidArguments_ThrowsArgumentException() { CaseBuilder cb = Channel.CaseRead(Channel.CreateUnbounded(), i => { }); Assert.Throws("channel", () => cb.CaseWrite(null, 0, (Action)null)); - Assert.Throws("channel", () => cb.CaseWrite(null, 0, (Action)delegate { })); + Assert.Throws("channel", () => cb.CaseWrite(null, 0, delegate { })); Assert.Throws("action", () => cb.CaseWrite(Channel.CreateUnbounded(), 0, (Action)null)); } @@ -38,9 +38,9 @@ public void CaseWrite_Sync_InvalidArguments_ThrowsArgumentException() public void CaseWrite_Async_InvalidArguments_ThrowsArgumentException() { CaseBuilder cb = Channel.CaseRead(Channel.CreateUnbounded(), i => { }); - Assert.Throws("channel", () => cb.CaseWrite(null, 0, (Func)null)); + Assert.Throws("channel", () => cb.CaseWrite(null, 0, null)); Assert.Throws("channel", () => cb.CaseWrite(null, 0, delegate { return Task.CompletedTask; })); - Assert.Throws("func", () => cb.CaseWrite(Channel.CreateUnbounded(), 0, (Func)null)); + Assert.Throws("func", () => cb.CaseWrite(Channel.CreateUnbounded(), 0, null)); } [Fact] @@ -53,7 +53,7 @@ public void CaseDefault_Sync_InvalidAction_ThrowsException() public void CaseDefault_Async_InvalidAction_ThrowsException() { CaseBuilder builder1 = Channel.CaseRead(Channel.CreateUnbounded(), i => Task.CompletedTask); - Assert.Throws(() => builder1.CaseDefault((Func)null)); + Assert.Throws(() => builder1.CaseDefault(null)); } [Fact] diff --git a/tests/System.Threading.Tasks.Channels.Tests/ChannelTests.cs b/tests/System.Threading.Tasks.Channels.Tests/ChannelTests.cs index dd6033c76d4..b63f84bb647 100644 --- a/tests/System.Threading.Tasks.Channels.Tests/ChannelTests.cs +++ b/tests/System.Threading.Tasks.Channels.Tests/ChannelTests.cs @@ -82,25 +82,25 @@ public void CaseRead_Sync_InvalidArguments_ThrowsArgumentException() [Fact] public void CaseRead_Async_InvalidArguments_ThrowsArgumentException() { - Assert.Throws("channel", () => Channel.CaseRead(null, (Func)null)); + Assert.Throws("channel", () => Channel.CaseRead(null, null)); Assert.Throws("channel", () => Channel.CaseRead(null, i => Task.CompletedTask)); - Assert.Throws("func", () => Channel.CaseRead(Channel.CreateUnbounded().In, (Func)null)); + Assert.Throws("func", () => Channel.CaseRead(Channel.CreateUnbounded().In, null)); } [Fact] public void CaseWrite_Sync_InvalidArguments_ThrowsArgumentException() { Assert.Throws("channel", () => Channel.CaseWrite(null, 0, (Action)null)); - Assert.Throws("channel", () => Channel.CaseWrite(null, 0, (Action)delegate { })); + Assert.Throws("channel", () => Channel.CaseWrite(null, 0, delegate { })); Assert.Throws("action", () => Channel.CaseWrite(Channel.CreateUnbounded().Out, 0, (Action)null)); } [Fact] public void CaseWrite_Async_InvalidArguments_ThrowsArgumentException() { - Assert.Throws("channel", () => Channel.CaseWrite(null, 0, (Func)null)); + Assert.Throws("channel", () => Channel.CaseWrite(null, 0, null)); Assert.Throws("channel", () => Channel.CaseWrite(null, 0, delegate { return Task.CompletedTask; })); - Assert.Throws("func", () => Channel.CaseWrite(Channel.CreateUnbounded().Out, 0, (Func)null)); + Assert.Throws("func", () => Channel.CaseWrite(Channel.CreateUnbounded().Out, 0, null)); } [Fact] diff --git a/tests/System.Threading.Tasks.Channels.Tests/DebuggerAttributes.cs b/tests/System.Threading.Tasks.Channels.Tests/DebuggerAttributes.cs index f19f23c58d6..fe939dd2ee8 100644 --- a/tests/System.Threading.Tasks.Channels.Tests/DebuggerAttributes.cs +++ b/tests/System.Threading.Tasks.Channels.Tests/DebuggerAttributes.cs @@ -27,7 +27,7 @@ internal static void ValidateDebuggerTypeProxyProperties(object obj) throw new InvalidOperationException( string.Format("Expected one DebuggerTypeProxyAttribute on {0}.", obj)); } - var cad = (CustomAttributeData)attrs[0]; + var cad = attrs[0]; // Get the proxy type. As written, this only works if the proxy and the target type // have the same generic parameters, e.g. Dictionary and Proxy. @@ -63,7 +63,7 @@ internal static void ValidateDebuggerDisplayReferences(object obj) throw new InvalidOperationException( string.Format("Expected one DebuggerDisplayAttribute on {0}.", obj)); } - var cad = (CustomAttributeData)attrs[0]; + var cad = attrs[0]; // Get the text of the DebuggerDisplayAttribute string attrText = (string)cad.ConstructorArguments[0].Value;