diff options
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/Text')
24 files changed, 945 insertions, 637 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs b/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs index e89943a19..217d93467 100644 --- a/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs +++ b/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs @@ -31,7 +31,7 @@ namespace System.Text { } - internal override void SetDefaultFallbacks() + internal sealed override void SetDefaultFallbacks() { // For ASCIIEncoding we just use default replacement fallback this.encoderFallback = EncoderFallback.ReplacementFallback; @@ -59,13 +59,13 @@ namespace System.Text { // Validate input parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - index < count) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -81,11 +81,11 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe int GetByteCount(String chars) + public override unsafe int GetByteCount(string chars) { // Validate input if (chars==null) - throw new ArgumentNullException("chars"); + throw new ArgumentNullException(nameof(chars)); fixed (char* pChars = chars) return GetByteCount(pChars, chars.Length, null); @@ -100,34 +100,42 @@ namespace System.Text { // Validate Parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); // Call it with empty encoder return GetByteCount(chars, count, null); } + public override unsafe int GetByteCount(ReadOnlySpan<char> chars) + { + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + { + return GetByteCount(charsPtr, chars.Length, encoder: null); + } + } + // Parent method is safe. // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding - public override unsafe int GetBytes(String chars, int charIndex, int charCount, + public override unsafe int GetBytes(string chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (chars == null || bytes == null) - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCount); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCount); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); int byteCount = bytes.Length - byteIndex; @@ -154,16 +162,16 @@ namespace System.Text { // Validate parameters if (chars == null || bytes == null) - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); // If nothing to encode return 0 if (charCount == 0) @@ -186,14 +194,23 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetBytes(chars, charCount, bytes, byteCount, null); } + public override unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes) + { + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, encoder: null); + } + } + // Returns the number of characters produced by decoding a range of bytes // in a byte array. // @@ -206,13 +223,13 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input just return 0, fixed doesn't like 0 length arrays if (count == 0) @@ -232,14 +249,22 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); return GetCharCount(bytes, count, null); } + public override unsafe int GetCharCount(ReadOnlySpan<byte> bytes) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + { + return GetCharCount(bytesPtr, bytes.Length, decoder: null); + } + } + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding @@ -250,16 +275,16 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (byteIndex < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if ( bytes.Length - byteIndex < byteCount) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); if (charIndex < 0 || charIndex > chars.Length) - throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -282,14 +307,23 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetChars(bytes, byteCount, chars, charCount, null); } + public override unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, decoder: null); + } + } + // Returns a string containing the decoded representation of a range of // bytes in a byte array. // @@ -298,24 +332,24 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe String GetString(byte[] bytes, int byteIndex, int byteCount) + public override unsafe string GetString(byte[] bytes, int byteIndex, int byteCount) { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (byteIndex < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - byteIndex < byteCount) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // Avoid problems with empty input buffer - if (byteCount == 0) return String.Empty; + if (byteCount == 0) return string.Empty; fixed (byte* pBytes = bytes) - return String.CreateStringFromEncoding( + return string.CreateStringFromEncoding( pBytes + byteIndex, byteCount, this); } @@ -326,7 +360,7 @@ namespace System.Text // GetByteCount // Note: We start by assuming that the output will be the same as count. Having // an encoder or fallback may change that assumption - internal override unsafe int GetByteCount(char* chars, int charCount, EncoderNLS encoder) + internal sealed override unsafe int GetByteCount(char* chars, int charCount, EncoderNLS encoder) { // Just need to ASSERT, this is called by something else internal that checked parameters already Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetByteCount]count is negative"); @@ -348,7 +382,7 @@ namespace System.Text if (encoder != null) { charLeftOver = encoder._charLeftOver; - Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + Debug.Assert(charLeftOver == 0 || char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate"); fallback = encoder.Fallback as EncoderReplacementFallback; @@ -400,7 +434,7 @@ namespace System.Text // We may have a left over character from last time, try and process it. if (charLeftOver > 0) { - Debug.Assert(Char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate"); + Debug.Assert(char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate"); Debug.Assert(encoder != null, "[ASCIIEncoding.GetByteCount]Expected encoder"); // Since left over char was a surrogate, it'll have to be fallen back. @@ -460,8 +494,8 @@ namespace System.Text return byteCount; } - internal override unsafe int GetBytes(char* chars, int charCount, - byte* bytes, int byteCount, EncoderNLS encoder) + internal sealed override unsafe int GetBytes( + char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder) { // Just need to ASSERT, this is called by something else internal that checked parameters already Debug.Assert(bytes != null, "[ASCIIEncoding.GetBytes]bytes is null"); @@ -502,7 +536,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); } - Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + Debug.Assert(charLeftOver == 0 || char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetBytes]leftover character should be high surrogate"); // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert @@ -676,7 +710,7 @@ namespace System.Text } // This is internal and called by something else, - internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) + internal sealed override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) { // Just assert, we're called internally so these should be safe, checked already Debug.Assert(bytes != null, "[ASCIIEncoding.GetCharCount]bytes is null"); @@ -748,8 +782,8 @@ namespace System.Text return charCount; } - internal override unsafe int GetChars(byte* bytes, int byteCount, - char* chars, int charCount, DecoderNLS decoder) + internal sealed override unsafe int GetChars( + byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS decoder) { // Just need to ASSERT, this is called by something else internal that checked parameters already Debug.Assert(bytes != null, "[ASCIIEncoding.GetChars]bytes is null"); diff --git a/src/System.Private.CoreLib/shared/System/Text/Decoder.cs b/src/System.Private.CoreLib/shared/System/Text/Decoder.cs index b827648fc..b4a7575ba 100644 --- a/src/System.Private.CoreLib/shared/System/Text/Decoder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/Decoder.cs @@ -134,7 +134,7 @@ namespace System.Text public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes, bool flush) { - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) { return GetCharCount(bytesPtr, bytes.Length, flush); } @@ -227,8 +227,8 @@ namespace System.Text public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush) { - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) { return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, flush); } @@ -341,8 +341,8 @@ namespace System.Text public virtual unsafe void Convert(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush, out int bytesUsed, out int charsUsed, out bool completed) { - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) { Convert(bytesPtr, bytes.Length, charsPtr, chars.Length, flush, out bytesUsed, out charsUsed, out completed); } diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderBestFitFallback.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderBestFitFallback.cs index 30c817c91..8c62730c2 100644 --- a/src/System.Private.CoreLib/shared/System/Text/DecoderBestFitFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/DecoderBestFitFallback.cs @@ -38,7 +38,7 @@ namespace System.Text } } - public override bool Equals(Object value) + public override bool Equals(object value) { InternalDecoderBestFitFallback that = value as InternalDecoderBestFitFallback; if (that != null) @@ -63,15 +63,15 @@ namespace System.Text private InternalDecoderBestFitFallback _oFallback; // Private object for locking instead of locking on a public type for SQL reliability work. - private static Object s_InternalSyncObject; - private static Object InternalSyncObject + private static object s_InternalSyncObject; + private static object InternalSyncObject { get { if (s_InternalSyncObject == null) { - Object o = new Object(); - Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null); + object o = new object(); + Interlocked.CompareExchange<object>(ref s_InternalSyncObject, o, null); } return s_InternalSyncObject; } diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderExceptionFallback.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderExceptionFallback.cs index 8bfc1f32d..56c004714 100644 --- a/src/System.Private.CoreLib/shared/System/Text/DecoderExceptionFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/DecoderExceptionFallback.cs @@ -29,7 +29,7 @@ namespace System.Text } } - public override bool Equals(Object value) + public override bool Equals(object value) { DecoderExceptionFallback that = value as DecoderExceptionFallback; if (that != null) @@ -112,19 +112,19 @@ namespace System.Text HResult = HResults.COR_E_ARGUMENT; } - public DecoderFallbackException(String message) + public DecoderFallbackException(string message) : base(message) { HResult = HResults.COR_E_ARGUMENT; } - public DecoderFallbackException(String message, Exception innerException) + public DecoderFallbackException(string message, Exception innerException) : base(message, innerException) { HResult = HResults.COR_E_ARGUMENT; } - public DecoderFallbackException(String message, byte[] bytesUnknown, int index) + public DecoderFallbackException(string message, byte[] bytesUnknown, int index) : base(message) { _bytesUnknown = bytesUnknown; diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs index 11b9539b5..fff8ad1d7 100644 --- a/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs @@ -104,9 +104,9 @@ namespace System.Text while ((ch = GetNextChar()) != 0) { // Make sure no mixed up surrogates - if (Char.IsSurrogate(ch)) + if (char.IsSurrogate(ch)) { - if (Char.IsHighSurrogate(ch)) + if (char.IsHighSurrogate(ch)) { // High Surrogate if (bHighSurrogate) @@ -159,9 +159,9 @@ namespace System.Text while ((ch = GetNextChar()) != 0) { // Make sure no mixed up surrogates - if (Char.IsSurrogate(ch)) + if (char.IsSurrogate(ch)) { - if (Char.IsHighSurrogate(ch)) + if (char.IsHighSurrogate(ch)) { // High Surrogate if (bHighSurrogate) diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderReplacementFallback.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderReplacementFallback.cs index 422b80bb2..a97baf05c 100644 --- a/src/System.Private.CoreLib/shared/System/Text/DecoderReplacementFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/DecoderReplacementFallback.cs @@ -9,14 +9,14 @@ namespace System.Text public sealed class DecoderReplacementFallback : DecoderFallback { // Our variables - private String _strDefault; + private string _strDefault; // Construction. Default replacement fallback uses no best fit and ? replacement string public DecoderReplacementFallback() : this("?") { } - public DecoderReplacementFallback(String replacement) + public DecoderReplacementFallback(string replacement) { if (replacement == null) throw new ArgumentNullException(nameof(replacement)); @@ -26,10 +26,10 @@ namespace System.Text for (int i = 0; i < replacement.Length; i++) { // Found a surrogate? - if (Char.IsSurrogate(replacement, i)) + if (char.IsSurrogate(replacement, i)) { // High or Low? - if (Char.IsHighSurrogate(replacement, i)) + if (char.IsHighSurrogate(replacement, i)) { // if already had a high one, stop if (bFoundHigh) @@ -60,7 +60,7 @@ namespace System.Text _strDefault = replacement; } - public String DefaultString + public string DefaultString { get { @@ -82,7 +82,7 @@ namespace System.Text } } - public override bool Equals(Object value) + public override bool Equals(object value) { DecoderReplacementFallback that = value as DecoderReplacementFallback; if (that != null) @@ -103,7 +103,7 @@ namespace System.Text public sealed class DecoderReplacementFallbackBuffer : DecoderFallbackBuffer { // Store our default string - private String _strDefault; + private string _strDefault; private int _fallbackCount = -1; private int _fallbackIndex = -1; diff --git a/src/System.Private.CoreLib/shared/System/Text/Encoder.cs b/src/System.Private.CoreLib/shared/System/Text/Encoder.cs index fb1bdb803..df7d51203 100644 --- a/src/System.Private.CoreLib/shared/System/Text/Encoder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/Encoder.cs @@ -132,7 +132,7 @@ namespace System.Text public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars, bool flush) { - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) { return GetByteCount(charsPtr, chars.Length, flush); } @@ -221,8 +221,8 @@ namespace System.Text public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush) { - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) { return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, flush); } @@ -335,8 +335,8 @@ namespace System.Text public virtual unsafe void Convert(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush, out int charsUsed, out int bytesUsed, out bool completed) { - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) { Convert(charsPtr, chars.Length, bytesPtr, bytes.Length, flush, out charsUsed, out bytesUsed, out completed); } diff --git a/src/System.Private.CoreLib/shared/System/Text/EncoderBestFitFallback.cs b/src/System.Private.CoreLib/shared/System/Text/EncoderBestFitFallback.cs index 7f3be2a7a..4aab3f62a 100644 --- a/src/System.Private.CoreLib/shared/System/Text/EncoderBestFitFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/EncoderBestFitFallback.cs @@ -38,7 +38,7 @@ namespace System.Text } } - public override bool Equals(Object value) + public override bool Equals(object value) { InternalEncoderBestFitFallback that = value as InternalEncoderBestFitFallback; if (that != null) @@ -63,15 +63,15 @@ namespace System.Text private int _iSize; // Private object for locking instead of locking on a public type for SQL reliability work. - private static Object s_InternalSyncObject; - private static Object InternalSyncObject + private static object s_InternalSyncObject; + private static object InternalSyncObject { get { if (s_InternalSyncObject == null) { - Object o = new Object(); - Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null); + object o = new object(); + Interlocked.CompareExchange<object>(ref s_InternalSyncObject, o, null); } return s_InternalSyncObject; } @@ -113,12 +113,12 @@ namespace System.Text public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) { // Double check input surrogate pair - if (!Char.IsHighSurrogate(charUnknownHigh)) + if (!char.IsHighSurrogate(charUnknownHigh)) throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); - if (!Char.IsLowSurrogate(charUnknownLow)) + if (!char.IsLowSurrogate(charUnknownLow)) throw new ArgumentOutOfRangeException(nameof(charUnknownLow), SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); diff --git a/src/System.Private.CoreLib/shared/System/Text/EncoderExceptionFallback.cs b/src/System.Private.CoreLib/shared/System/Text/EncoderExceptionFallback.cs index 66de1940f..92afcf701 100644 --- a/src/System.Private.CoreLib/shared/System/Text/EncoderExceptionFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/EncoderExceptionFallback.cs @@ -28,7 +28,7 @@ namespace System.Text } } - public override bool Equals(Object value) + public override bool Equals(object value) { EncoderExceptionFallback that = value as EncoderExceptionFallback; if (that != null) @@ -57,18 +57,18 @@ namespace System.Text public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) { - if (!Char.IsHighSurrogate(charUnknownHigh)) + if (!char.IsHighSurrogate(charUnknownHigh)) { throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); } - if (!Char.IsLowSurrogate(charUnknownLow)) + if (!char.IsLowSurrogate(charUnknownLow)) { throw new ArgumentOutOfRangeException(nameof(charUnknownLow), SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); } - int iTemp = Char.ConvertToUtf32(charUnknownHigh, charUnknownLow); + int iTemp = char.ConvertToUtf32(charUnknownHigh, charUnknownLow); // Fall back our char throw new EncoderFallbackException( @@ -111,34 +111,34 @@ namespace System.Text HResult = HResults.COR_E_ARGUMENT; } - public EncoderFallbackException(String message) + public EncoderFallbackException(string message) : base(message) { HResult = HResults.COR_E_ARGUMENT; } - public EncoderFallbackException(String message, Exception innerException) + public EncoderFallbackException(string message, Exception innerException) : base(message, innerException) { HResult = HResults.COR_E_ARGUMENT; } internal EncoderFallbackException( - String message, char charUnknown, int index) : base(message) + string message, char charUnknown, int index) : base(message) { _charUnknown = charUnknown; _index = index; } internal EncoderFallbackException( - String message, char charUnknownHigh, char charUnknownLow, int index) : base(message) + string message, char charUnknownHigh, char charUnknownLow, int index) : base(message) { - if (!Char.IsHighSurrogate(charUnknownHigh)) + if (!char.IsHighSurrogate(charUnknownHigh)) { throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); } - if (!Char.IsLowSurrogate(charUnknownLow)) + if (!char.IsLowSurrogate(charUnknownLow)) { throw new ArgumentOutOfRangeException(nameof(CharUnknownLow), SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); diff --git a/src/System.Private.CoreLib/shared/System/Text/EncoderFallback.cs b/src/System.Private.CoreLib/shared/System/Text/EncoderFallback.cs index d860a02a7..f98b15e07 100644 --- a/src/System.Private.CoreLib/shared/System/Text/EncoderFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/EncoderFallback.cs @@ -142,7 +142,7 @@ namespace System.Text int index = (int)(chars - charStart) - 1; // See if it was a high surrogate - if (Char.IsHighSurrogate(ch)) + if (char.IsHighSurrogate(ch)) { // See if there's a low surrogate to go with it if (chars >= this.charEnd) @@ -165,11 +165,11 @@ namespace System.Text { // Might have a low surrogate char cNext = *chars; - if (Char.IsLowSurrogate(cNext)) + if (char.IsLowSurrogate(cNext)) { // If already falling back then fail if (bFallingBack && iRecursionCount++ > iMaxRecursion) - ThrowLastCharRecursive(Char.ConvertToUtf32(ch, cNext)); + ThrowLastCharRecursive(char.ConvertToUtf32(ch, cNext)); // Next is a surrogate, add it as surrogate pair, and increment chars chars++; diff --git a/src/System.Private.CoreLib/shared/System/Text/EncoderReplacementFallback.cs b/src/System.Private.CoreLib/shared/System/Text/EncoderReplacementFallback.cs index a1d0bbcd9..760c280fd 100644 --- a/src/System.Private.CoreLib/shared/System/Text/EncoderReplacementFallback.cs +++ b/src/System.Private.CoreLib/shared/System/Text/EncoderReplacementFallback.cs @@ -11,14 +11,14 @@ namespace System.Text public sealed class EncoderReplacementFallback : EncoderFallback { // Our variables - private String _strDefault; + private string _strDefault; // Construction. Default replacement fallback uses no best fit and ? replacement string public EncoderReplacementFallback() : this("?") { } - public EncoderReplacementFallback(String replacement) + public EncoderReplacementFallback(string replacement) { // Must not be null if (replacement == null) @@ -29,10 +29,10 @@ namespace System.Text for (int i = 0; i < replacement.Length; i++) { // Found a surrogate? - if (Char.IsSurrogate(replacement, i)) + if (char.IsSurrogate(replacement, i)) { // High or Low? - if (Char.IsHighSurrogate(replacement, i)) + if (char.IsHighSurrogate(replacement, i)) { // if already had a high one, stop if (bFoundHigh) @@ -63,7 +63,7 @@ namespace System.Text _strDefault = replacement; } - public String DefaultString + public string DefaultString { get { @@ -85,7 +85,7 @@ namespace System.Text } } - public override bool Equals(Object value) + public override bool Equals(object value) { EncoderReplacementFallback that = value as EncoderReplacementFallback; if (that != null) @@ -106,7 +106,7 @@ namespace System.Text public sealed class EncoderReplacementFallbackBuffer : EncoderFallbackBuffer { // Store our default string - private String _strDefault; + private string _strDefault; private int _fallbackCount = -1; private int _fallbackIndex = -1; @@ -127,7 +127,7 @@ namespace System.Text // If we're recursive we may still have something in our buffer that makes this a surrogate if (char.IsHighSurrogate(charUnknown) && _fallbackCount >= 0 && char.IsLowSurrogate(_strDefault[_fallbackIndex + 1])) - ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknown, _strDefault[_fallbackIndex + 1])); + ThrowLastCharRecursive(char.ConvertToUtf32(charUnknown, _strDefault[_fallbackIndex + 1])); // Nope, just one character ThrowLastCharRecursive(unchecked((int)charUnknown)); @@ -144,18 +144,18 @@ namespace System.Text public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) { // Double check input surrogate pair - if (!Char.IsHighSurrogate(charUnknownHigh)) + if (!char.IsHighSurrogate(charUnknownHigh)) throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); - if (!Char.IsLowSurrogate(charUnknownLow)) + if (!char.IsLowSurrogate(charUnknownLow)) throw new ArgumentOutOfRangeException(nameof(charUnknownLow), SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. if (_fallbackCount >= 1) - ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknownHigh, charUnknownLow)); + ThrowLastCharRecursive(char.ConvertToUtf32(charUnknownHigh, charUnknownLow)); // Go ahead and get our fallback _fallbackCount = _strDefault.Length; diff --git a/src/System.Private.CoreLib/shared/System/Text/Encoding.cs b/src/System.Private.CoreLib/shared/System/Text/Encoding.cs index e469180ce..005f08afd 100644 --- a/src/System.Private.CoreLib/shared/System/Text/Encoding.cs +++ b/src/System.Private.CoreLib/shared/System/Text/Encoding.cs @@ -325,7 +325,7 @@ namespace System.Text // Returns an Encoding object for a given name or a given code page value. // - public static Encoding GetEncoding(String name) + public static Encoding GetEncoding(string name) { Encoding baseEncoding = EncodingProvider.GetEncodingFromProvider(name); if (baseEncoding != null) @@ -342,7 +342,7 @@ namespace System.Text // Returns an Encoding object for a given name or a given code page value. // - public static Encoding GetEncoding(String name, + public static Encoding GetEncoding(string name, EncoderFallback encoderFallback, DecoderFallback decoderFallback) { Encoding baseEncoding = EncodingProvider.GetEncodingFromProvider(name, encoderFallback, decoderFallback); @@ -386,7 +386,7 @@ namespace System.Text // Returns the name for this encoding that can be used with mail agent body tags. // If the encoding may not be used, the string is empty. - public virtual String BodyName + public virtual string BodyName { get { @@ -443,7 +443,7 @@ namespace System.Text } } #else - public virtual String EncodingName + public virtual string EncodingName { get { @@ -454,7 +454,7 @@ namespace System.Text // Returns the name for this encoding that can be used with mail agent header // tags. If the encoding may not be used, the string is empty. - public virtual String HeaderName + public virtual string HeaderName { get { @@ -467,7 +467,7 @@ namespace System.Text } // Returns the IANA preferred name for this encoding. - public virtual String WebName + public virtual string WebName { get { @@ -603,7 +603,7 @@ namespace System.Text } - public virtual Object Clone() + public virtual object Clone() { Encoding newEncoding = (Encoding)this.MemberwiseClone(); @@ -646,7 +646,7 @@ namespace System.Text return GetByteCount(chars, 0, chars.Length); } - public virtual int GetByteCount(String s) + public virtual int GetByteCount(string s) { if (s == null) throw new ArgumentNullException(nameof(s)); @@ -713,7 +713,7 @@ namespace System.Text public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars) { - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) { return GetByteCount(charsPtr, chars.Length); } @@ -767,7 +767,7 @@ namespace System.Text // Returns a byte array containing the encoded representation of the given // string. // - public virtual byte[] GetBytes(String s) + public virtual byte[] GetBytes(string s) { if (s == null) throw new ArgumentNullException(nameof(s), @@ -817,7 +817,7 @@ namespace System.Text } } - public virtual int GetBytes(String s, int charIndex, int charCount, + public virtual int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (s == null) @@ -895,8 +895,8 @@ namespace System.Text public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes) { - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) { return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length); } @@ -945,7 +945,7 @@ namespace System.Text public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes) { - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) { return GetCharCount(bytesPtr, bytes.Length); } @@ -1057,8 +1057,8 @@ namespace System.Text public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars) { - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) - fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) { return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length); } @@ -1082,14 +1082,14 @@ namespace System.Text if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - return String.CreateStringFromEncoding(bytes, byteCount, this); + return string.CreateStringFromEncoding(bytes, byteCount, this); } public unsafe string GetString(ReadOnlySpan<byte> bytes) { - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) { - return GetString(bytesPtr, bytes.Length); + return string.CreateStringFromEncoding(bytesPtr, bytes.Length, this); } } @@ -1184,7 +1184,7 @@ namespace System.Text // Returns a string containing the decoded representation of a given byte // array. // - public virtual String GetString(byte[] bytes) + public virtual string GetString(byte[] bytes) { if (bytes == null) throw new ArgumentNullException(nameof(bytes), @@ -1198,9 +1198,9 @@ namespace System.Text // // Internally we override this for performance // - public virtual String GetString(byte[] bytes, int index, int count) + public virtual string GetString(byte[] bytes, int index, int count) { - return new String(GetChars(bytes, index, count)); + return new string(GetChars(bytes, index, count)); } // Returns an encoding for Unicode format. The returned encoding will be @@ -1241,7 +1241,7 @@ namespace System.Text private static Encoding BigEndianUTF32 => UTF32Encoding.s_bigEndianDefault; - public override bool Equals(Object value) + public override bool Equals(object value) { Encoding that = value as Encoding; if (that != null) @@ -1325,7 +1325,7 @@ namespace System.Text _encoding = encoding; } - public Object GetRealObject(StreamingContext context) + public object GetRealObject(StreamingContext context) { throw new PlatformNotSupportedException(); } @@ -1390,7 +1390,7 @@ namespace System.Text _encoding = encoding; } - public Object GetRealObject(StreamingContext context) + public object GetRealObject(StreamingContext context) { throw new PlatformNotSupportedException(); } diff --git a/src/System.Private.CoreLib/shared/System/Text/EncodingInfo.cs b/src/System.Private.CoreLib/shared/System/Text/EncodingInfo.cs index 99995f759..8e71e58fa 100644 --- a/src/System.Private.CoreLib/shared/System/Text/EncodingInfo.cs +++ b/src/System.Private.CoreLib/shared/System/Text/EncodingInfo.cs @@ -53,7 +53,7 @@ namespace System.Text return Encoding.GetEncoding(iCodePage); } - public override bool Equals(Object value) + public override bool Equals(object value) { EncodingInfo that = value as EncodingInfo; if (that != null) diff --git a/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs b/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs index d5de9e553..e6fa0627d 100644 --- a/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs +++ b/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs @@ -40,13 +40,13 @@ namespace System.Text { // Validate input parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - index < count) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -61,11 +61,11 @@ namespace System.Text // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe int GetByteCount(String s) + public override unsafe int GetByteCount(string s) { // Validate input if (s==null) - throw new ArgumentNullException("s"); + throw new ArgumentNullException(nameof(s)); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -78,10 +78,10 @@ namespace System.Text { // Validate Parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -91,20 +91,20 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding - public override unsafe int GetBytes(String s, int charIndex, int charCount, + public override unsafe int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (s == null || bytes == null) - throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((s == null ? nameof(s) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (s.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + throw new ArgumentOutOfRangeException(nameof(s), SR.ArgumentOutOfRange_IndexCount); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); int byteCount = bytes.Length - byteIndex; @@ -130,16 +130,16 @@ namespace System.Text { // Validate parameters if (chars == null || bytes == null) - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -160,10 +160,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -179,13 +179,13 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input just return 0, fixed doesn't like 0 length arrays if (count == 0) @@ -203,10 +203,10 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); return GetCharCount(bytes, count, null); } @@ -220,16 +220,16 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (byteIndex < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if ( bytes.Length - byteIndex < byteCount) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); if (charIndex < 0 || charIndex > chars.Length) - throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -250,10 +250,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -265,23 +265,23 @@ namespace System.Text // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe String GetString(byte[] bytes, int index, int count) + public override unsafe string GetString(byte[] bytes, int index, int count) { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // Avoid problems with empty input buffer - if (count == 0) return String.Empty; + if (count == 0) return string.Empty; fixed (byte* pBytes = bytes) - return String.CreateStringFromEncoding( + return string.CreateStringFromEncoding( pBytes + index, count, this); } diff --git a/src/System.Private.CoreLib/shared/System/Text/EncodingProvider.cs b/src/System.Private.CoreLib/shared/System/Text/EncodingProvider.cs index ce8c3e020..4d15eea01 100644 --- a/src/System.Private.CoreLib/shared/System/Text/EncodingProvider.cs +++ b/src/System.Private.CoreLib/shared/System/Text/EncodingProvider.cs @@ -130,7 +130,7 @@ namespace System.Text return null; } - private static Object s_InternalSyncObject = new Object(); + private static object s_InternalSyncObject = new object(); private static volatile EncodingProvider[] s_providers; } } diff --git a/src/System.Private.CoreLib/shared/System/Text/Latin1Encoding.cs b/src/System.Private.CoreLib/shared/System/Text/Latin1Encoding.cs index 335eb76e3..736fff5d2 100644 --- a/src/System.Private.CoreLib/shared/System/Text/Latin1Encoding.cs +++ b/src/System.Private.CoreLib/shared/System/Text/Latin1Encoding.cs @@ -11,7 +11,7 @@ namespace System.Text // Latin1Encoding is a simple override to optimize the GetString version of Latin1Encoding. // because of the best fit cases we can't do this when encoding the string, only when decoding // - internal class Latin1Encoding : EncodingNLS + internal sealed class Latin1Encoding : EncodingNLS { // Used by Encoding.Latin1 for lazy initialization // The initialization code will not be run until a static member of the class is referenced @@ -42,7 +42,7 @@ namespace System.Text if (encoder != null) { charLeftOver = encoder._charLeftOver; - Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + Debug.Assert(charLeftOver == 0 || char.IsHighSurrogate(charLeftOver), "[Latin1Encoding.GetByteCount]leftover character should be high surrogate"); fallback = encoder.Fallback as EncoderReplacementFallback; @@ -164,7 +164,7 @@ namespace System.Text { charLeftOver = encoder._charLeftOver; fallback = encoder.Fallback as EncoderReplacementFallback; - Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + Debug.Assert(charLeftOver == 0 || char.IsHighSurrogate(charLeftOver), "[Latin1Encoding.GetBytes]leftover character should be high surrogate"); // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.Debug.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.Debug.cs new file mode 100644 index 000000000..a62c4777a --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.Debug.cs @@ -0,0 +1,37 @@ +// 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.Diagnostics; + +namespace System.Text +{ + public sealed partial class StringBuilder + { + private void ShowChunks(int maxChunksToShow = 10) + { + int count = 0; + StringBuilder head = this, current = this; + while (current != null) + { + if (count < maxChunksToShow) + { + count++; + } + else + { + head = head.m_ChunkPrevious; + } + current = current.m_ChunkPrevious; + } + current = head; + string[] chunks = new string[count]; + for (int i = count; i > 0; i--) + { + chunks[i - 1] = new string(current.m_ChunkChars).Replace('\0', '.'); + current = current.m_ChunkPrevious; + } + Debug.WriteLine('|' + string.Join('|', chunks) + '|'); + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index a7804c399..99021e2dd 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -18,10 +18,10 @@ using System.Collections.Generic; namespace System.Text { // This class represents a mutable string. It is convenient for situations in - // which it is desirable to modify a string, perhaps by removing, replacing, or + // which it is desirable to modify a string, perhaps by removing, replacing, or // inserting characters, without creating a new String subsequent to - // each modification. - // + // each modification. + // // The methods contained within this class do not return a new StringBuilder // object unless specified otherwise. This class may be used in conjunction with the String // class to carry out modifications upon strings. @@ -30,8 +30,8 @@ namespace System.Text public sealed partial class StringBuilder : ISerializable { // A StringBuilder is internally represented as a linked list of blocks each of which holds - // a chunk of the string. It turns out string as a whole can also be represented as just a chunk, - // so that is what we do. + // a chunk of the string. It turns out string as a whole can also be represented as just a chunk, + // so that is what we do. /// <summary> /// The character buffer for this chunk. @@ -73,7 +73,7 @@ namespace System.Text // We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure. // Making the maximum chunk size big means less allocation code called, but also more waste // in unused characters and slower inserts / replaces (since you do need to slide characters over - // within a buffer). + // within a buffer). internal const int MaxChunkSize = 8000; /// <summary> @@ -201,7 +201,7 @@ namespace System.Text int persistedCapacity = 0; string persistedString = null; - int persistedMaxCapacity = Int32.MaxValue; + int persistedMaxCapacity = int.MaxValue; bool capacityPresent = false; // Get the data @@ -353,7 +353,7 @@ namespace System.Text return Capacity; } - public override String ToString() + public override string ToString() { AssertInvariants(); @@ -377,7 +377,7 @@ namespace System.Text int chunkOffset = chunk.m_ChunkOffset; int chunkLength = chunk.m_ChunkLength; - // Check that we will not overrun our boundaries. + // Check that we will not overrun our boundaries. if ((uint)(chunkLength + chunkOffset) <= (uint)result.Length && (uint)chunkLength <= (uint)sourceArray.Length) { fixed (char* sourcePtr = &sourceArray[0]) @@ -462,13 +462,10 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); } - int originalCapacity = Capacity; - if (value == 0 && m_ChunkPrevious == null) { m_ChunkLength = 0; m_ChunkOffset = 0; - Debug.Assert(Capacity >= originalCapacity); return; } @@ -483,22 +480,32 @@ namespace System.Text StringBuilder chunk = FindChunkForIndex(value); if (chunk != this) { - // We crossed a chunk boundary when reducing the Length. We must replace this middle-chunk with a new larger chunk, - // to ensure the original capacity is preserved. - int newLen = originalCapacity - chunk.m_ChunkOffset; - char[] newArray = new char[newLen]; - - Debug.Assert(newLen > chunk.m_ChunkChars.Length, "The new chunk should be larger than the one it is replacing."); - Array.Copy(chunk.m_ChunkChars, 0, newArray, 0, chunk.m_ChunkLength); + // Avoid possible infinite capacity growth. See https://github.com/dotnet/coreclr/pull/16926 + int capacityToPreserve = Math.Min(Capacity, Math.Max(Length * 6 / 5, m_ChunkChars.Length)); + int newLen = capacityToPreserve - chunk.m_ChunkOffset; + if (newLen > chunk.m_ChunkChars.Length) + { + // We crossed a chunk boundary when reducing the Length. We must replace this middle-chunk with a new larger chunk, + // to ensure the capacity we want is preserved. + char[] newArray = new char[newLen]; + Array.Copy(chunk.m_ChunkChars, 0, newArray, 0, chunk.m_ChunkLength); + m_ChunkChars = newArray; + } + else + { + // Special case where the capacity we want to keep corresponds exactly to the size of the content. + // Just take ownership of the array. + Debug.Assert(newLen == chunk.m_ChunkChars.Length, "The new chunk should be larger or equal to the one it is replacing."); + m_ChunkChars = chunk.m_ChunkChars; + } - m_ChunkChars = newArray; m_ChunkPrevious = chunk.m_ChunkPrevious; m_ChunkOffset = chunk.m_ChunkOffset; } m_ChunkLength = value - chunk.m_ChunkOffset; AssertInvariants(); } - Debug.Assert(Capacity >= originalCapacity); + Debug.Assert(Length == value, "Something went wrong setting Length."); } } @@ -551,6 +558,138 @@ namespace System.Text } /// <summary> + /// GetChunks returns ChunkEnumerator that follows the IEnumerable pattern and + /// thus can be used in a C# 'foreach' statements to retreive the data in the StringBuilder + /// as chunks (ReadOnlyMemory) of characters. An example use is: + /// + /// foreach (ReadOnlyMemory<char> chunk in sb.GetChunks()) + /// foreach(char c in chunk.Span) + /// { /* operation on c } + /// + /// It is undefined what happens if the StringBuilder is modified while the chunk + /// enumeration is incomplete. StringBuilder is also not thread-safe, so operating + /// on it with concurrent threads is illegal. Finally the ReadOnlyMemory chunks returned + /// are NOT guarenteed to remain unchanged if the StringBuilder is modified, so do + /// not cache them for later use either. This API's purpose is efficiently extracting + /// the data of a CONSTANT StringBuilder. + /// + /// Creating a ReadOnlySpan from a ReadOnlyMemory (the .Span property) is expensive + /// compared to the fetching of the character, so create a local variable for the SPAN + /// if you need to use it in a nested for statement. For example + /// + /// foreach (ReadOnlyMemory<char> chunk in sb.GetChunks()) + /// { + /// var span = chunk.Span; + /// for(int i = 0; i < span.Length; i++) + /// { /* operation on span[i] */ } + /// } + /// </summary> + public ChunkEnumerator GetChunks() => new ChunkEnumerator(this); + + /// <summary> + /// ChunkEnumerator supports both the IEnumerable and IEnumerator pattern so foreach + /// works (see GetChunks). It needs to be public (so the compiler can use it + /// when building a foreach statement) but users typically don't use it explicitly. + /// (which is why it is a nested type). + /// </summary> + public struct ChunkEnumerator + { + private readonly StringBuilder _firstChunk; // The first Stringbuilder chunk (which is the end of the logical string) + private StringBuilder _currentChunk; // The chunk that this enumerator is currently returning (Current). + private readonly ManyChunkInfo _manyChunks; // Only used for long string builders with many chunks (see constructor) + + /// <summary> + /// Implement IEnumerable.GetEnumerator() to return 'this' as the IEnumerator + /// </summary> + [ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)] // Only here to make foreach work + public ChunkEnumerator GetEnumerator() { return this; } + + /// <summary> + /// Implements the IEnumerator pattern. + /// </summary> + public bool MoveNext() + { + if (_currentChunk == _firstChunk) + return false; + + if (_manyChunks != null) + return _manyChunks.MoveNext(ref _currentChunk); + + StringBuilder next = _firstChunk; + while (next.m_ChunkPrevious != _currentChunk) + next = next.m_ChunkPrevious; + _currentChunk = next; + return true; + } + + /// <summary> + /// Implements the IEnumerator pattern. + /// </summary> + public ReadOnlyMemory<char> Current => new ReadOnlyMemory<char>(_currentChunk.m_ChunkChars, 0, _currentChunk.m_ChunkLength); + + #region private + internal ChunkEnumerator(StringBuilder stringBuilder) + { + Debug.Assert(stringBuilder != null); + _firstChunk = stringBuilder; + _currentChunk = null; // MoveNext will find the last chunk if we do this. + _manyChunks = null; + + // There is a performance-vs-allocation tradeoff. Because the chunks + // are a linked list with each chunk pointing to its PREDECESSOR, walking + // the list FORWARD is not efficient. If there are few chunks (< 8) we + // simply scan from the start each time, and tolerate the N*N behavior. + // However above this size, we allocate an array to hold pointers to all + // the chunks and we can be efficient for large N. + int chunkCount = ChunkCount(stringBuilder); + if (8 < chunkCount) + _manyChunks = new ManyChunkInfo(stringBuilder, chunkCount); + } + + private static int ChunkCount(StringBuilder stringBuilder) + { + int ret = 0; + while (stringBuilder != null) + { + ret++; + stringBuilder = stringBuilder.m_ChunkPrevious; + } + return ret; + } + + /// <summary> + /// Used to hold all the chunks indexes when you have many chunks. + /// </summary> + private class ManyChunkInfo + { + private readonly StringBuilder[] _chunks; // These are in normal order (first chunk first) + private int _chunkPos; + + public bool MoveNext(ref StringBuilder current) + { + int pos = ++_chunkPos; + if (_chunks.Length <= pos) + return false; + current = _chunks[pos]; + return true; + } + + public ManyChunkInfo(StringBuilder stringBuilder, int chunkCount) + { + _chunks = new StringBuilder[chunkCount]; + while (0 <= --chunkCount) + { + Debug.Assert(stringBuilder != null); + _chunks[chunkCount] = stringBuilder; + stringBuilder = stringBuilder.m_ChunkPrevious; + } + _chunkPos = -1; + } + } +#endregion + } + + /// <summary> /// Appends a character 0 or more times to the end of this builder. /// </summary> /// <param name="value">The character to append.</param> @@ -648,7 +787,7 @@ namespace System.Text /// Appends a string to the end of this builder. /// </summary> /// <param name="value">The string to append.</param> - public StringBuilder Append(String value) + public StringBuilder Append(string value) { if (value != null) { @@ -691,7 +830,7 @@ namespace System.Text } // We put this fixed in its own helper to avoid the cost of zero-initing `valueChars` in the - // case we don't actually use it. + // case we don't actually use it. private void AppendHelper(string value) { unsafe @@ -903,7 +1042,7 @@ namespace System.Text /// <param name="index">The index to insert in this builder.</param> /// <param name="value">The string to insert.</param> /// <param name="count">The number of times to insert the string.</param> - public StringBuilder Insert(int index, String value, int count) + public StringBuilder Insert(int index, string value, int count) { if (count < 0) { @@ -921,7 +1060,7 @@ namespace System.Text return this; } - // Ensure we don't insert more chars than we can hold, and we don't + // Ensure we don't insert more chars than we can hold, and we don't // have any integer overflow in our new length. long insertingChars = (long)value.Length * count; if (insertingChars > MaxCapacity - this.Length) @@ -1115,7 +1254,7 @@ namespace System.Text { return AppendJoinCore(&separator, 1, values); } - + private unsafe StringBuilder AppendJoinCore<T>(char* separator, int separatorLength, IEnumerable<T> values) { Debug.Assert(separator != null); @@ -1182,7 +1321,7 @@ namespace System.Text #endregion - public StringBuilder Insert(int index, String value) + public StringBuilder Insert(int index, string value) { if ((uint)index > (uint)Length) { @@ -1292,7 +1431,7 @@ namespace System.Text [CLSCompliant(false)] public StringBuilder Insert(int index, ulong value) => Insert(index, value.ToString(), 1); - public StringBuilder Insert(int index, Object value) => (value == null) ? this : Insert(index, value.ToString(), 1); + public StringBuilder Insert(int index, object value) => (value == null) ? this : Insert(index, value.ToString(), 1); public StringBuilder Insert(int index, ReadOnlySpan<char> value) { @@ -1312,13 +1451,13 @@ namespace System.Text return this; } - public StringBuilder AppendFormat(String format, Object arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0)); + public StringBuilder AppendFormat(string format, object arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0)); - public StringBuilder AppendFormat(String format, Object arg0, Object arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); + public StringBuilder AppendFormat(string format, object arg0, object arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); - public StringBuilder AppendFormat(String format, Object arg0, Object arg1, Object arg2) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); + public StringBuilder AppendFormat(string format, object arg0, object arg1, object arg2) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); - public StringBuilder AppendFormat(String format, params Object[] args) + public StringBuilder AppendFormat(string format, params object[] args) { if (args == null) { @@ -1331,13 +1470,13 @@ namespace System.Text return AppendFormatHelper(null, format, new ParamsArray(args)); } - public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0) => AppendFormatHelper(provider, format, new ParamsArray(arg0)); + public StringBuilder AppendFormat(IFormatProvider provider, string format, object arg0) => AppendFormatHelper(provider, format, new ParamsArray(arg0)); - public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1)); + public StringBuilder AppendFormat(IFormatProvider provider, string format, object arg0, object arg1) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1)); - public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1, Object arg2) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); + public StringBuilder AppendFormat(IFormatProvider provider, string format, object arg0, object arg1, object arg2) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); - public StringBuilder AppendFormat(IFormatProvider provider, String format, params Object[] args) + public StringBuilder AppendFormat(IFormatProvider provider, string format, params object[] args) { if (args == null) { @@ -1359,7 +1498,7 @@ namespace System.Text private const int IndexLimit = 1000000; // Note: 0 <= ArgIndex < IndexLimit private const int WidthLimit = 1000000; // Note: -WidthLimit < ArgAlign < WidthLimit - internal StringBuilder AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args) + internal StringBuilder AppendFormatHelper(IFormatProvider provider, string format, ParamsArray args) { if (format == null) { @@ -1495,8 +1634,8 @@ namespace System.Text // // Start of parsing of optional formatting parameter. // - Object arg = args[index]; - String itemFormat = null; + object arg = args[index]; + string itemFormat = null; ReadOnlySpan<char> itemFormatSpan = default; // used if itemFormat is null // Is current character a colon? which indicates start of formatting parameter. if (ch == ':') @@ -1552,7 +1691,7 @@ namespace System.Text if (startPos != pos) { // There was no brace escaping, extract the item format as a single string - itemFormatSpan = format.AsSpan().Slice(startPos, pos - startPos); + itemFormatSpan = format.AsSpan(startPos, pos - startPos); } } else @@ -1566,7 +1705,7 @@ namespace System.Text if (ch != '}') FormatError(); // Construct the output for this arg hole. pos++; - String s = null; + string s = null; if (cf != null) { if (itemFormatSpan.Length != 0 && itemFormat == null) @@ -1609,7 +1748,7 @@ namespace System.Text } } // Append it to the final output of the Format String. - if (s == null) s = String.Empty; + if (s == null) s = string.Empty; int pad = width - s.Length; if (!leftJustify && pad > 0) Append(' ', pad); Append(s); @@ -1628,7 +1767,7 @@ namespace System.Text /// If <paramref name="newValue"/> is <c>null</c>, instances of <paramref name="oldValue"/> /// are removed from this builder. /// </remarks> - public StringBuilder Replace(String oldValue, String newValue) => Replace(oldValue, newValue, 0, Length); + public StringBuilder Replace(string oldValue, string newValue) => Replace(oldValue, newValue, 0, Length); /// <summary> /// Determines if the contents of this builder are equal to the contents of another builder. @@ -1680,10 +1819,10 @@ namespace System.Text /// <summary> /// Determines if the contents of this builder are equal to the contents of ReadOnlySpan<char>. /// </summary> - /// <param name="value">The ReadOnlySpan{char}.</param> - public bool Equals(ReadOnlySpan<char> value) + /// <param name="span">The ReadOnlySpan{char}.</param> + public bool Equals(ReadOnlySpan<char> span) { - if (value.Length != Length) + if (span.Length != Length) return false; StringBuilder sbChunk = this; @@ -1696,7 +1835,7 @@ namespace System.Text ReadOnlySpan<char> chunk = new ReadOnlySpan<char>(sbChunk.m_ChunkChars, 0, chunk_length); - if (!chunk.Equals(value.Slice(value.Length - offset, chunk_length))) + if (!chunk.EqualsOrdinal(span.Slice(span.Length - offset, chunk_length))) return false; sbChunk = sbChunk.m_ChunkPrevious; @@ -1717,7 +1856,7 @@ namespace System.Text /// If <paramref name="newValue"/> is <c>null</c>, instances of <paramref name="oldValue"/> /// are removed from this builder. /// </remarks> - public StringBuilder Replace(String oldValue, String newValue, int startIndex, int count) + public StringBuilder Replace(string oldValue, string newValue, int startIndex, int count) { int currentLength = Length; if ((uint)startIndex > (uint)currentLength) @@ -1749,7 +1888,7 @@ namespace System.Text int indexInChunk = startIndex - chunk.m_ChunkOffset; while (count > 0) { - // Look for a match in the chunk,indexInChunk pointer + // Look for a match in the chunk,indexInChunk pointer if (StartsWith(chunk, indexInChunk, count, oldValue)) { // Push it on the replacements array (with growth), we will do all replacements in a @@ -1775,13 +1914,13 @@ namespace System.Text if (indexInChunk >= chunk.m_ChunkLength || count == 0) // Have we moved out of the current chunk? { - // Replacing mutates the blocks, so we need to convert to a logical index and back afterwards. + // Replacing mutates the blocks, so we need to convert to a logical index and back afterwards. int index = indexInChunk + chunk.m_ChunkOffset; int indexBeforeAdjustment = index; // See if we accumulated any replacements, if so apply them. ReplaceAllInChunk(replacements, replacementsCount, chunk, oldValue.Length, newValue); - // The replacement has affected the logical index. Adjust it. + // The replacement has affected the logical index. Adjust it. index += ((newValue.Length - oldValue.Length) * replacementsCount); replacementsCount = 0; @@ -1874,7 +2013,7 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(valueCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity); } - // This case is so common we want to optimize for it heavily. + // This case is so common we want to optimize for it heavily. int newIndex = valueCount + m_ChunkLength; if (newIndex <= m_ChunkChars.Length) { @@ -1891,7 +2030,7 @@ namespace System.Text m_ChunkLength = m_ChunkChars.Length; } - // Expand the builder to add another chunk. + // Expand the builder to add another chunk. int restLength = valueCount - firstLength; ExpandByABlock(restLength); Debug.Assert(m_ChunkLength == 0, "A new block was not created."); @@ -1948,16 +2087,16 @@ namespace System.Text { fixed (char* valuePtr = value) { - // calculate the total amount of extra space or space needed for all the replacements. + // calculate the total amount of extra space or space needed for all the replacements. int delta = (value.Length - removeCount) * replacementsCount; StringBuilder targetChunk = sourceChunk; // the target as we copy chars down int targetIndexInChunk = replacements[0]; - // Make the room needed for all the new characters if needed. + // Make the room needed for all the new characters if needed. if (delta > 0) MakeRoom(targetChunk.m_ChunkOffset + targetIndexInChunk, delta, out targetChunk, out targetIndexInChunk, true); - // We made certain that characters after the insertion point are not moved, + // We made certain that characters after the insertion point are not moved, int i = 0; for (;;) { @@ -1974,7 +2113,7 @@ namespace System.Text Debug.Assert(gapStart < sourceChunk.m_ChunkChars.Length, "gap starts at end of buffer. Should not happen"); Debug.Assert(gapStart <= gapEnd, "negative gap size"); Debug.Assert(gapEnd <= sourceChunk.m_ChunkLength, "gap too big"); - if (delta != 0) // can skip the sliding of gaps if source an target string are the same size. + if (delta != 0) // can skip the sliding of gaps if source an target string are the same size. { // Copy the gap data between the current replacement and the next replacement fixed (char* sourcePtr = &sourceChunk.m_ChunkChars[gapStart]) @@ -1987,7 +2126,7 @@ namespace System.Text } } - // Remove extra space if necessary. + // Remove extra space if necessary. if (delta < 0) Remove(targetChunk.m_ChunkOffset + targetIndexInChunk, -delta, out targetChunk, out targetIndexInChunk); } @@ -2044,7 +2183,7 @@ namespace System.Text /// </param> /// <param name="value">The pointer to the start of the character buffer.</param> /// <param name="count">The number of characters in the buffer.</param> - unsafe private void ReplaceInPlaceAtChunk(ref StringBuilder chunk, ref int indexInChunk, char* value, int count) + private unsafe void ReplaceInPlaceAtChunk(ref StringBuilder chunk, ref int indexInChunk, char* value, int count) { if (count != 0) { @@ -2056,7 +2195,7 @@ namespace System.Text int lengthToCopy = Math.Min(lengthInChunk, count); ThreadSafeCopy(value, chunk.m_ChunkChars, indexInChunk, lengthToCopy); - // Advance the index. + // Advance the index. indexInChunk += lengthToCopy; if (indexInChunk >= chunk.m_ChunkLength) { @@ -2374,7 +2513,7 @@ namespace System.Text int endIndex = startIndex + count; - // Find the chunks for the start and end of the block to delete. + // Find the chunks for the start and end of the block to delete. chunk = this; StringBuilder endChunk = null; int endIndexInChunk = 0; @@ -2425,7 +2564,7 @@ namespace System.Text // SafeCritical: We ensure that `endIndexInChunk + copyCount` is within range of `m_ChunkChars`, and // also ensure that `copyTargetIndexInChunk + copyCount` is within the chunk. - // Remove any characters in the end chunk, by sliding the characters down. + // Remove any characters in the end chunk, by sliding the characters down. if (copyTargetIndexInChunk != endIndexInChunk) // Sometimes no move is necessary { ThreadSafeCopy(endChunk.m_ChunkChars, endIndexInChunk, endChunk.m_ChunkChars, copyTargetIndexInChunk, copyCount); diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilderCache.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilderCache.cs new file mode 100644 index 000000000..e699cc27c --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilderCache.cs @@ -0,0 +1,62 @@ +// 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. + +namespace System.Text +{ + /// <summary>Provide a cached reusable instance of stringbuilder per thread.</summary> + internal static class StringBuilderCache + { + // The value 360 was chosen in discussion with performance experts as a compromise between using + // as litle memory per thread as possible and still covering a large part of short-lived + // StringBuilder creations on the startup path of VS designers. + private const int MaxBuilderSize = 360; + private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity + + // WARNING: We allow diagnostic tools to directly inspect this member (t_cachedInstance). + // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. + // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. + // Get in touch with the diagnostics team if you have questions. + [ThreadStatic] + private static StringBuilder t_cachedInstance; + + /// <summary>Get a StringBuilder for the specified capacity.</summary> + /// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks> + public static StringBuilder Acquire(int capacity = DefaultCapacity) + { + if (capacity <= MaxBuilderSize) + { + StringBuilder sb = t_cachedInstance; + if (sb != null) + { + // Avoid stringbuilder block fragmentation by getting a new StringBuilder + // when the requested size is larger than the current capacity + if (capacity <= sb.Capacity) + { + t_cachedInstance = null; + sb.Clear(); + return sb; + } + } + } + return new StringBuilder(capacity); + } + + /// <summary>Place the specified builder in the cache if it is not too big.</summary> + public static void Release(StringBuilder sb) + { + if (sb.Capacity <= MaxBuilderSize) + { + t_cachedInstance = sb; + } + } + + /// <summary>ToString() the stringbuilder, Release it to the cache, and return the resulting string.</summary> + public static string GetStringAndRelease(StringBuilder sb) + { + string result = sb.ToString(); + Release(sb); + return result; + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs b/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs index 7828775ea..86169e6b1 100644 --- a/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs +++ b/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs @@ -103,13 +103,13 @@ namespace System.Text { // Validate input parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - index < count) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -125,11 +125,11 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe int GetByteCount(String s) + public override unsafe int GetByteCount(string s) { // Validate input if (s==null) - throw new ArgumentNullException("s"); + throw new ArgumentNullException(nameof(s)); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -144,10 +144,10 @@ namespace System.Text { // Validate Parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -158,20 +158,20 @@ namespace System.Text // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding - public override unsafe int GetBytes(String s, int charIndex, int charCount, + public override unsafe int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (s == null || bytes == null) - throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((s == null ? nameof(s) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (s.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + throw new ArgumentOutOfRangeException(nameof(s), SR.ArgumentOutOfRange_IndexCount); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); int byteCount = bytes.Length - byteIndex; @@ -198,16 +198,16 @@ namespace System.Text { // Validate parameters if (chars == null || bytes == null) - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -230,10 +230,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -250,13 +250,13 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input just return 0, fixed doesn't like 0 length arrays. if (count == 0) @@ -276,10 +276,10 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); return GetCharCount(bytes, count, null); } @@ -294,16 +294,16 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (byteIndex < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if ( bytes.Length - byteIndex < byteCount) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); if (charIndex < 0 || charIndex > chars.Length) - throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -326,10 +326,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -342,23 +342,23 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe String GetString(byte[] bytes, int index, int count) + public override unsafe string GetString(byte[] bytes, int index, int count) { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // Avoid problems with empty input buffer - if (count == 0) return String.Empty; + if (count == 0) return string.Empty; fixed (byte* pBytes = bytes) - return String.CreateStringFromEncoding( + return string.CreateStringFromEncoding( pBytes + index, count, this); } @@ -417,7 +417,7 @@ namespace System.Text // // In previous char, we encounter a high surrogate, so we are expecting a low surrogate here. // - if (Char.IsLowSurrogate(ch)) + if (char.IsLowSurrogate(ch)) { // They're all legal highSurrogate = '\0'; @@ -447,7 +447,7 @@ namespace System.Text } // Do we have another high surrogate? - if (Char.IsHighSurrogate(ch)) + if (char.IsHighSurrogate(ch)) { // // We'll have a high surrogate to check next time. @@ -457,7 +457,7 @@ namespace System.Text } // Check for illegal characters - if (Char.IsLowSurrogate(ch)) + if (char.IsLowSurrogate(ch)) { // We have a leading low surrogate, do the fallback charsForFallback = chars; @@ -552,7 +552,7 @@ namespace System.Text // // In previous char, we encountered a high surrogate, so we are expecting a low surrogate here. // - if (Char.IsLowSurrogate(ch)) + if (char.IsLowSurrogate(ch)) { // Is it a legal one? uint iTemp = GetSurrogate(highSurrogate, ch); @@ -616,7 +616,7 @@ namespace System.Text } // Do we have another high surrogate?, if so remember it - if (Char.IsHighSurrogate(ch)) + if (char.IsHighSurrogate(ch)) { // // We'll have a high surrogate to check next time. @@ -626,7 +626,7 @@ namespace System.Text } // Check for illegal characters (low surrogate) - if (Char.IsLowSurrogate(ch)) + if (char.IsLowSurrogate(ch)) { // We have a leading low surrogate, do the fallback charsForFallback = chars; @@ -1159,7 +1159,7 @@ namespace System.Text _emitUTF32ByteOrderMark ? (_bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) : Array.Empty<byte>(); - public override bool Equals(Object value) + public override bool Equals(object value) { UTF32Encoding that = value as UTF32Encoding; if (that != null) diff --git a/src/System.Private.CoreLib/shared/System/Text/UTF7Encoding.cs b/src/System.Private.CoreLib/shared/System/Text/UTF7Encoding.cs index 0246c2891..5c51c8107 100644 --- a/src/System.Private.CoreLib/shared/System/Text/UTF7Encoding.cs +++ b/src/System.Private.CoreLib/shared/System/Text/UTF7Encoding.cs @@ -14,17 +14,17 @@ namespace System.Text { public class UTF7Encoding : Encoding { - private const String base64Chars = + private const string base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // 0123456789111111111122222222223333333333444444444455555555556666 // 012345678901234567890123456789012345678901234567890123 // These are the characters that can be directly encoded in UTF7. - private const String directChars = + private const string directChars = "\t\n\r '(),-./0123456789:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // These are the characters that can be optionally directly encoded in UTF7. - private const String optionalChars = + private const string optionalChars = "!\"#$%&*;<=>@[]^_`{|}"; // Used by Encoding.UTF7 for lazy initialization @@ -88,16 +88,16 @@ namespace System.Text } // We go ahead and set this because Encoding expects it, however nothing can fall back in UTF7. - internal override void SetDefaultFallbacks() + internal sealed override void SetDefaultFallbacks() { // UTF7 had an odd decoderFallback behavior, and the Encoder fallback // is irrelevant because we encode surrogates individually and never check for unmatched ones // (so nothing can fallback during encoding) - this.encoderFallback = new EncoderReplacementFallback(String.Empty); + this.encoderFallback = new EncoderReplacementFallback(string.Empty); this.decoderFallback = new DecoderUTF7Fallback(); } - public override bool Equals(Object value) + public override bool Equals(object value) { UTF7Encoding that = value as UTF7Encoding; if (that != null) @@ -133,13 +133,13 @@ namespace System.Text { // Validate input parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - index < count) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -159,7 +159,7 @@ namespace System.Text { // Validate input if (s==null) - throw new ArgumentNullException("s"); + throw new ArgumentNullException(nameof(s)); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -174,10 +174,10 @@ namespace System.Text { // Validate Parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -192,16 +192,16 @@ namespace System.Text byte[] bytes, int byteIndex) { if (s == null || bytes == null) - throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((s == null ? nameof(s) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (s.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + throw new ArgumentOutOfRangeException(nameof(s), SR.ArgumentOutOfRange_IndexCount); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); int byteCount = bytes.Length - byteIndex; @@ -228,16 +228,16 @@ namespace System.Text { // Validate parameters if (chars == null || bytes == null) - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -260,10 +260,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -280,13 +280,13 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input just return 0, fixed doesn't like 0 length arrays. if (count == 0) @@ -306,10 +306,10 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); return GetCharCount(bytes, count, null); } @@ -324,16 +324,16 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (byteIndex < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if ( bytes.Length - byteIndex < byteCount) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); if (charIndex < 0 || charIndex > chars.Length) - throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -356,10 +356,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -372,23 +372,23 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe String GetString(byte[] bytes, int index, int count) + public override unsafe string GetString(byte[] bytes, int index, int count) { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // Avoid problems with empty input buffer - if (count == 0) return String.Empty; + if (count == 0) return string.Empty; fixed (byte* pBytes = bytes) - return String.CreateStringFromEncoding( + return string.CreateStringFromEncoding( pBytes + index, count, this); } @@ -396,7 +396,7 @@ namespace System.Text // End of standard methods copied from EncodingNLS.cs // - internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder) + internal sealed override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder) { Debug.Assert(chars != null, "[UTF7Encoding.GetByteCount]chars!=null"); Debug.Assert(count >= 0, "[UTF7Encoding.GetByteCount]count >=0"); @@ -405,8 +405,8 @@ namespace System.Text return GetBytes(chars, count, null, 0, baseEncoder); } - internal override unsafe int GetBytes(char* chars, int charCount, - byte* bytes, int byteCount, EncoderNLS baseEncoder) + internal sealed override unsafe int GetBytes( + char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS baseEncoder) { Debug.Assert(byteCount >= 0, "[UTF7Encoding.GetBytes]byteCount >=0"); Debug.Assert(chars != null, "[UTF7Encoding.GetBytes]chars!=null"); @@ -545,7 +545,7 @@ namespace System.Text return buffer.Count; } - internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) + internal sealed override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) { Debug.Assert(count >= 0, "[UTF7Encoding.GetCharCount]count >=0"); Debug.Assert(bytes != null, "[UTF7Encoding.GetCharCount]bytes!=null"); @@ -554,8 +554,8 @@ namespace System.Text return GetChars(bytes, count, null, 0, baseDecoder); } - internal override unsafe int GetChars(byte* bytes, int byteCount, - char* chars, int charCount, DecoderNLS baseDecoder) + internal sealed override unsafe int GetChars( + byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder) { Debug.Assert(byteCount >= 0, "[UTF7Encoding.GetChars]byteCount >=0"); Debug.Assert(bytes != null, "[UTF7Encoding.GetChars]bytes!=null"); @@ -873,7 +873,7 @@ namespace System.Text } } - public override bool Equals(Object value) + public override bool Equals(object value) { DecoderUTF7Fallback that = value as DecoderUTF7Fallback; if (that != null) diff --git a/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs b/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs index 67f87c9b0..c9e51f661 100644 --- a/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs +++ b/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs @@ -93,7 +93,7 @@ namespace System.Text SetDefaultFallbacks(); } - internal override void SetDefaultFallbacks() + internal sealed override void SetDefaultFallbacks() { // For UTF-X encodings, we use a replacement fallback with an empty string if (_isThrowException) @@ -130,13 +130,13 @@ namespace System.Text { // Validate input parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - index < count) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -152,7 +152,7 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe int GetByteCount(String chars) + public override unsafe int GetByteCount(string chars) { // Validate input if (chars==null) @@ -171,34 +171,42 @@ namespace System.Text { // Validate Parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); // Call it with empty encoder return GetByteCount(chars, count, null); } + public override unsafe int GetByteCount(ReadOnlySpan<char> chars) + { + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + { + return GetByteCount(charsPtr, chars.Length, baseEncoder: null); + } + } + // Parent method is safe. // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding - public override unsafe int GetBytes(String s, int charIndex, int charCount, + public override unsafe int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (s == null || bytes == null) - throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((s == null ? nameof(s) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (s.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + throw new ArgumentOutOfRangeException(nameof(s), SR.ArgumentOutOfRange_IndexCount); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); int byteCount = bytes.Length - byteIndex; @@ -225,16 +233,16 @@ namespace System.Text { // Validate parameters if (chars == null || bytes == null) - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -257,14 +265,23 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetBytes(chars, charCount, bytes, byteCount, null); } + public override unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes) + { + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, baseEncoder: null); + } + } + // Returns the number of characters produced by decoding a range of bytes // in a byte array. // @@ -277,13 +294,13 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input just return 0, fixed doesn't like 0 length arrays. if (count == 0) @@ -303,14 +320,22 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); return GetCharCount(bytes, count, null); } + public override unsafe int GetCharCount(ReadOnlySpan<byte> bytes) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + { + return GetCharCount(bytesPtr, bytes.Length, baseDecoder: null); + } + } + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding @@ -321,16 +346,16 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (byteIndex < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if ( bytes.Length - byteIndex < byteCount) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); if (charIndex < 0 || charIndex > chars.Length) - throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -353,14 +378,23 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetChars(bytes, byteCount, chars, charCount, null); } + public override unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, baseDecoder: null); + } + } + // Returns a string containing the decoded representation of a range of // bytes in a byte array. // @@ -369,23 +403,23 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe String GetString(byte[] bytes, int index, int count) + public override unsafe string GetString(byte[] bytes, int index, int count) { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // Avoid problems with empty input buffer - if (count == 0) return String.Empty; + if (count == 0) return string.Empty; fixed (byte* pBytes = bytes) - return String.CreateStringFromEncoding( + return string.CreateStringFromEncoding( pBytes + index, count, this); } @@ -395,7 +429,7 @@ namespace System.Text // To simplify maintenance, the structure of GetByteCount and GetBytes should be // kept the same as much as possible - internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder) + internal sealed override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder) { // For fallback we may need a fallback buffer. // We wait to initialize it though in case we don't have any broken input unicode @@ -715,12 +749,15 @@ namespace System.Text break; LongCodeWithMask: -#if BIGENDIAN - // be careful about the sign extension - ch = (int)(((uint)ch) >> 16); -#else // BIGENDIAN - ch = (char)ch; -#endif // BIGENDIAN + if (BitConverter.IsLittleEndian) + { + ch = (char)ch; + } + else + { + // be careful about the sign extension + ch = (int)(((uint)ch) >> 16); + } pSrc++; if (ch <= 0x7F) @@ -783,13 +820,13 @@ namespace System.Text // diffs two char pointers using unsigned arithmetic. The unsigned arithmetic // is good enough for us, and it tends to generate better code than the signed // arithmetic generated by default - unsafe private static int PtrDiff(char* a, char* b) + private static unsafe int PtrDiff(char* a, char* b) { return (int)(((uint)((byte*)a - (byte*)b)) >> 1); } // byte* flavor just for parity - unsafe private static int PtrDiff(byte* a, byte* b) + private static unsafe int PtrDiff(byte* a, byte* b) { return (int)(a - b); } @@ -801,8 +838,8 @@ namespace System.Text // Our workhorse // Note: We ignore mismatched surrogates, unless the exception flag is set in which case we throw - internal override unsafe int GetBytes(char* chars, int charCount, - byte* bytes, int byteCount, EncoderNLS baseEncoder) + internal sealed override unsafe int GetBytes( + char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS baseEncoder) { Debug.Assert(chars != null, "[UTF8Encoding.GetBytes]chars!=null"); Debug.Assert(byteCount >= 0, "[UTF8Encoding.GetBytes]byteCount >=0"); @@ -1142,31 +1179,37 @@ namespace System.Text } // Unfortunately, this is endianess sensitive -#if BIGENDIAN - *pTarget = (byte)(ch>>16); - *(pTarget+1) = (byte)ch; - pSrc += 4; - *(pTarget+2) = (byte)(chc>>16); - *(pTarget+3) = (byte)chc; - pTarget += 4; -#else // BIGENDIAN - *pTarget = (byte)ch; - *(pTarget + 1) = (byte)(ch >> 16); - pSrc += 4; - *(pTarget + 2) = (byte)chc; - *(pTarget + 3) = (byte)(chc >> 16); - pTarget += 4; -#endif // BIGENDIAN + if (BitConverter.IsLittleEndian) + { + *pTarget = (byte)ch; + *(pTarget + 1) = (byte)(ch >> 16); + pSrc += 4; + *(pTarget + 2) = (byte)chc; + *(pTarget + 3) = (byte)(chc >> 16); + pTarget += 4; + } + else + { + *pTarget = (byte)(ch>>16); + *(pTarget+1) = (byte)ch; + pSrc += 4; + *(pTarget+2) = (byte)(chc>>16); + *(pTarget+3) = (byte)chc; + pTarget += 4; + } } continue; LongCodeWithMask: -#if BIGENDIAN - // be careful about the sign extension - ch = (int)(((uint)ch) >> 16); -#else // BIGENDIAN - ch = (char)ch; -#endif // BIGENDIAN + if (BitConverter.IsLittleEndian) + { + ch = (char)ch; + } + else + { + // be careful about the sign extension + ch = (int)(((uint)ch) >> 16); + } pSrc++; if (ch > 0x7F) @@ -1284,7 +1327,7 @@ namespace System.Text // // To simplify maintenance, the structure of GetCharCount and GetChars should be // kept the same as much as possible - internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) + internal sealed override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) { Debug.Assert(count >= 0, "[UTF8Encoding.GetCharCount]count >=0"); Debug.Assert(bytes != null, "[UTF8Encoding.GetCharCount]bytes!=null"); @@ -1564,17 +1607,26 @@ namespace System.Text } break; -#if BIGENDIAN - LongCodeWithMask32: - // be careful about the sign extension - ch = (int)(((uint)ch) >> 16); - LongCodeWithMask16: - ch = (int)(((uint)ch) >> 8); -#else // BIGENDIAN LongCodeWithMask32: + if (BitConverter.IsLittleEndian) + { + ch &= 0xFF; + } + else + { + // be careful about the sign extension + ch = (int)(((uint)ch) >> 16); + } LongCodeWithMask16: - ch &= 0xFF; -#endif // BIGENDIAN + if (BitConverter.IsLittleEndian) + { + ch &= 0xFF; + } + else + { + ch = (int)(((uint)ch) >> 8); + } + pSrc++; if (ch <= 0x7F) { @@ -1713,8 +1765,8 @@ namespace System.Text // // To simplify maintenance, the structure of GetCharCount and GetChars should be // kept the same as much as possible - internal override unsafe int GetChars(byte* bytes, int byteCount, - char* chars, int charCount, DecoderNLS baseDecoder) + internal sealed override unsafe int GetChars( + byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder) { Debug.Assert(chars != null, "[UTF8Encoding.GetChars]chars!=null"); Debug.Assert(byteCount >= 0, "[UTF8Encoding.GetChars]count >=0"); @@ -2048,17 +2100,20 @@ namespace System.Text } // Unfortunately, this is endianess sensitive -#if BIGENDIAN - *pTarget = (char)((ch >> 8) & 0x7F); - pSrc += 2; - *(pTarget+1) = (char)(ch & 0x7F); - pTarget += 2; -#else // BIGENDIAN - *pTarget = (char)(ch & 0x7F); - pSrc += 2; - *(pTarget + 1) = (char)((ch >> 8) & 0x7F); - pTarget += 2; -#endif // BIGENDIAN + if (BitConverter.IsLittleEndian) + { + *pTarget = (char)(ch & 0x7F); + pSrc += 2; + *(pTarget + 1) = (char)((ch >> 8) & 0x7F); + pTarget += 2; + } + else + { + *pTarget = (char)((ch >> 8) & 0x7F); + pSrc += 2; + *(pTarget+1) = (char)(ch & 0x7F); + pTarget += 2; + } } // Run 8 characters at a time! @@ -2072,43 +2127,54 @@ namespace System.Text } // Unfortunately, this is endianess sensitive -#if BIGENDIAN - *pTarget = (char)((ch >> 24) & 0x7F); - *(pTarget+1) = (char)((ch >> 16) & 0x7F); - *(pTarget+2) = (char)((ch >> 8) & 0x7F); - *(pTarget+3) = (char)(ch & 0x7F); - pSrc += 8; - *(pTarget+4) = (char)((chb >> 24) & 0x7F); - *(pTarget+5) = (char)((chb >> 16) & 0x7F); - *(pTarget+6) = (char)((chb >> 8) & 0x7F); - *(pTarget+7) = (char)(chb & 0x7F); - pTarget += 8; -#else // BIGENDIAN - *pTarget = (char)(ch & 0x7F); - *(pTarget + 1) = (char)((ch >> 8) & 0x7F); - *(pTarget + 2) = (char)((ch >> 16) & 0x7F); - *(pTarget + 3) = (char)((ch >> 24) & 0x7F); - pSrc += 8; - *(pTarget + 4) = (char)(chb & 0x7F); - *(pTarget + 5) = (char)((chb >> 8) & 0x7F); - *(pTarget + 6) = (char)((chb >> 16) & 0x7F); - *(pTarget + 7) = (char)((chb >> 24) & 0x7F); - pTarget += 8; -#endif // BIGENDIAN + if (BitConverter.IsLittleEndian) + { + *pTarget = (char)(ch & 0x7F); + *(pTarget + 1) = (char)((ch >> 8) & 0x7F); + *(pTarget + 2) = (char)((ch >> 16) & 0x7F); + *(pTarget + 3) = (char)((ch >> 24) & 0x7F); + pSrc += 8; + *(pTarget + 4) = (char)(chb & 0x7F); + *(pTarget + 5) = (char)((chb >> 8) & 0x7F); + *(pTarget + 6) = (char)((chb >> 16) & 0x7F); + *(pTarget + 7) = (char)((chb >> 24) & 0x7F); + pTarget += 8; + } + else + { + *pTarget = (char)((ch >> 24) & 0x7F); + *(pTarget+1) = (char)((ch >> 16) & 0x7F); + *(pTarget+2) = (char)((ch >> 8) & 0x7F); + *(pTarget+3) = (char)(ch & 0x7F); + pSrc += 8; + *(pTarget+4) = (char)((chb >> 24) & 0x7F); + *(pTarget+5) = (char)((chb >> 16) & 0x7F); + *(pTarget+6) = (char)((chb >> 8) & 0x7F); + *(pTarget+7) = (char)(chb & 0x7F); + pTarget += 8; + } } break; -#if BIGENDIAN - LongCodeWithMask32: - // be careful about the sign extension - ch = (int)(((uint)ch) >> 16); - LongCodeWithMask16: - ch = (int)(((uint)ch) >> 8); -#else // BIGENDIAN LongCodeWithMask32: + if (BitConverter.IsLittleEndian) + { + ch &= 0xFF; + } + else + { + // be careful about the sign extension + ch = (int)(((uint)ch) >> 16); + } LongCodeWithMask16: - ch &= 0xFF; -#endif // BIGENDIAN + if (BitConverter.IsLittleEndian) + { + ch &= 0xFF; + } + else + { + ch = (int)(((uint)ch) >> 8); + } pSrc++; if (ch <= 0x7F) { @@ -2173,7 +2239,7 @@ namespace System.Text // extra byte, we're already planning 2 chars for 2 of these bytes, // but the big loop is testing the target against pStop, so we need - // to subtract 2 more or we risk overrunning the input. Subtract + // to subtract 2 more or we risk overrunning the input. Subtract // one here and one below. pStop--; } @@ -2323,11 +2389,17 @@ namespace System.Text private unsafe int FallbackInvalidByteSequence( byte* pSrc, int ch, DecoderFallbackBuffer fallback) { + // Calling GetBytesUnknown can adjust the pSrc pointer but we need to pass the pointer before the adjustment + // to fallback.InternalFallback. The input pSrc to fallback.InternalFallback will only be used to calculate the + // index inside bytesUnknown and if we pass the adjusted pointer we can end up with negative index values. + // We store the original pSrc in pOriginalSrc and then pass pOriginalSrc to fallback.InternalFallback. + byte* pOriginalSrc = pSrc; + // Get our byte[] byte[] bytesUnknown = GetBytesUnknown(ref pSrc, ch); // Do the actual fallback - int count = fallback.InternalFallback(bytesUnknown, pSrc); + int count = fallback.InternalFallback(bytesUnknown, pOriginalSrc); // # of fallback chars expected. // Note that we only get here for "long" sequences, and have already unreserved @@ -2336,7 +2408,7 @@ namespace System.Text } // Note that some of these bytes may have come from a previous fallback, so we cannot - // just decrement the pointer and use the values we read. In those cases we have + // just decrement the pointer and use the values we read. In those cases we have // to regenerate the original values. private unsafe byte[] GetBytesUnknown(ref byte* pSrc, int ch) { @@ -2481,7 +2553,7 @@ namespace System.Text _emitUTF8Identifier ? s_preamble : Array.Empty<byte>(); - public override bool Equals(Object value) + public override bool Equals(object value) { UTF8Encoding that = value as UTF8Encoding; if (that != null) diff --git a/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs b/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs index 342bf53d6..6a27d2c85 100644 --- a/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs +++ b/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs @@ -6,11 +6,17 @@ // Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused. // +// This define can be used to turn off the fast loops. Useful for finding whether +// the problem is fastloop-specific. +#define FASTLOOP + using System; using System.Globalization; using System.Diagnostics; using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + namespace System.Text { public class UnicodeEncoding : Encoding @@ -23,15 +29,14 @@ namespace System.Text private static readonly byte[] s_bigEndianPreamble = new byte[2] { 0xfe, 0xff }; private static readonly byte[] s_littleEndianPreamble = new byte[2] { 0xff, 0xfe }; - internal bool isThrowException = false; + private bool isThrowException = false; - internal bool bigEndian = false; - internal bool byteOrderMark = true; + private bool bigEndian = false; + private bool byteOrderMark = true; // Unicode version 2.0 character size in bytes public const int CharSize = 2; - public UnicodeEncoding() : this(false, true) { @@ -56,7 +61,7 @@ namespace System.Text SetDefaultFallbacks(); } - internal override void SetDefaultFallbacks() + internal sealed override void SetDefaultFallbacks() { // For UTF-X encodings, we use a replacement fallback with an empty string if (this.isThrowException) @@ -89,13 +94,13 @@ namespace System.Text { // Validate input parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - index < count) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -111,11 +116,11 @@ namespace System.Text // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // parent method is safe - public override unsafe int GetByteCount(String s) + public override unsafe int GetByteCount(string s) { // Validate input if (s==null) - throw new ArgumentNullException("s"); + throw new ArgumentNullException(nameof(s)); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -130,10 +135,10 @@ namespace System.Text { // Validate Parameters if (chars == null) - throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -144,20 +149,20 @@ namespace System.Text // So if you fix this, fix the others. Currently those include: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding - public override unsafe int GetBytes(String s, int charIndex, int charCount, + public override unsafe int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (s == null || bytes == null) - throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((s == null ? nameof(s) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (s.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + throw new ArgumentOutOfRangeException(nameof(s), SR.ArgumentOutOfRange_IndexCount); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); int byteCount = bytes.Length - byteIndex; @@ -184,16 +189,16 @@ namespace System.Text { // Validate parameters if (chars == null || bytes == null) - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array); if (charIndex < 0 || charCount < 0) - throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if (chars.Length - charIndex < charCount) - throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); if (byteIndex < 0 || byteIndex > bytes.Length) - throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -216,10 +221,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -236,13 +241,13 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // If no input just return 0, fixed doesn't like 0 length arrays if (count == 0) @@ -262,10 +267,10 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (count < 0) - throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); return GetCharCount(bytes, count, null); } @@ -280,16 +285,16 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (byteIndex < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); if ( bytes.Length - byteIndex < byteCount) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); if (charIndex < 0 || charIndex > chars.Length) - throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -312,10 +317,10 @@ namespace System.Text { // Validate Parameters if (bytes == null || chars == null) - throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array); if (charCount < 0 || byteCount < 0) - throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -332,19 +337,19 @@ namespace System.Text { // Validate Parameters if (bytes == null) - throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum); if (bytes.Length - index < count) - throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); // Avoid problems with empty input buffer - if (count == 0) return String.Empty; + if (count == 0) return string.Empty; fixed (byte* pBytes = bytes) - return String.CreateStringFromEncoding( + return string.CreateStringFromEncoding( pBytes + index, count, this); } @@ -352,7 +357,7 @@ namespace System.Text // End of standard methods copied from EncodingNLS.cs // - internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) + internal sealed override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) { Debug.Assert(chars != null, "[UnicodeEncoding.GetByteCount]chars!=null"); Debug.Assert(count >= 0, "[UnicodeEncoding.GetByteCount]count >=0"); @@ -372,11 +377,6 @@ namespace System.Text bool wasHereBefore = false; - // Need -1 to check 2 at a time. If we have an even #, longChars will go - // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars - // will go from longEnd - 1 long to longEnd. (Might not get to use this) - ulong* longEnd = (ulong*)(charEnd - 3); - // For fallback we may need a fallback buffer EncoderFallbackBuffer fallbackBuffer = null; char* charsForFallback; @@ -410,19 +410,21 @@ namespace System.Text if (ch == 0) { // No fallback, maybe we can do it fast -#if !NO_FAST_UNICODE_LOOP -#if BIGENDIAN // If endianess is backwards then each pair of bytes would be backwards. - if ( bigEndian && -#else - if (!bigEndian && -#endif // BIGENDIAN - -#if BIT64 // 64 bit CPU needs to be long aligned for this to work. - charLeftOver == 0 && (unchecked((long)chars) & 7) == 0) +#if FASTLOOP + // If endianess is backwards then each pair of bytes would be backwards. + if ( (bigEndian ^ BitConverter.IsLittleEndian) && +#if BIT64 + (unchecked((long)chars) & 7) == 0 && #else - charLeftOver == 0 && (unchecked((int)chars) & 3) == 0) + (unchecked((int)chars) & 3) == 0 && #endif + charLeftOver == 0) { + // Need -1 to check 2 at a time. If we have an even #, longChars will go + // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars + // will go from longEnd - 1 long to longEnd. (Might not get to use this) + ulong* longEnd = (ulong*)(charEnd - 3); + // Need new char* so we can check 4 at a time ulong* longChars = (ulong*)chars; @@ -453,11 +455,8 @@ namespace System.Text // If they happen to be high/low/high/low, we may as well continue. Check the next // bit to see if its set (low) or not (high) in the right pattern -#if BIGENDIAN - if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xd800dc00d800dc00) != 0) -#else - if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xdc00d800dc00d800) != 0) -#endif + if ((0xfc00fc00fc00fc00 & *longChars) != + (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00)) { // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. @@ -480,7 +479,7 @@ namespace System.Text if (chars >= charEnd) break; } -#endif // !NO_FAST_UNICODE_LOOP +#endif // FASTLOOP // No fallback, just get next char ch = *chars; @@ -651,8 +650,8 @@ namespace System.Text return byteCount; } - internal override unsafe int GetBytes(char* chars, int charCount, - byte* bytes, int byteCount, EncoderNLS encoder) + internal sealed override unsafe int GetBytes( + char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder) { Debug.Assert(chars != null, "[UnicodeEncoding.GetBytes]chars!=null"); Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetBytes]byteCount >=0"); @@ -700,17 +699,14 @@ namespace System.Text if (ch == 0) { // No fallback, maybe we can do it fast -#if !NO_FAST_UNICODE_LOOP -#if BIGENDIAN // If endianess is backwards then each pair of bytes would be backwards. - if ( bigEndian && -#else - if (!bigEndian && -#endif // BIGENDIAN -#if BIT64 // 64 bit CPU needs to be long aligned for this to work, 32 bit CPU needs to be 32 bit aligned - (unchecked((long)chars) & 7) == 0 && (unchecked((long)bytes) & 7) == 0 && +#if FASTLOOP + // If endianess is backwards then each pair of bytes would be backwards. + if ( (bigEndian ^ BitConverter.IsLittleEndian) && +#if BIT64 + (unchecked((long)chars) & 7) == 0 && #else - (unchecked((int)chars) & 3) == 0 && (unchecked((int)bytes) & 3) == 0 && -#endif // BIT64 + (unchecked((int)chars) & 3) == 0 && +#endif charLeftOver == 0) { // Need -1 to check 2 at a time. If we have an even #, longChars will go @@ -752,11 +748,8 @@ namespace System.Text // If they happen to be high/low/high/low, we may as well continue. Check the next // bit to see if its set (low) or not (high) in the right pattern -#if BIGENDIAN - if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xd800dc00d800dc00) != 0) -#else - if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xdc00d800dc00d800) != 0) -#endif + if ((0xfc00fc00fc00fc00 & *longChars) != + (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00)) { // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. @@ -771,7 +764,7 @@ namespace System.Text // else all < 0x8000 so we can use them // We can use these 4 chars. - *longBytes = *longChars; + Unsafe.WriteUnaligned<ulong>(longBytes, *longChars); longChars++; longBytes++; } @@ -782,66 +775,7 @@ namespace System.Text if (chars >= charEnd) break; } - // Not aligned, but maybe we can still be somewhat faster - // Also somehow this optimizes the above loop? It seems to cause something above - // to get enregistered, but I haven't figured out how to make that happen without this loop. - else if ((charLeftOver == 0) && -#if BIGENDIAN - bigEndian && -#else - !bigEndian && -#endif // BIGENDIAN - -#if BIT64 - (unchecked((long)chars) & 7) != (unchecked((long)bytes) & 7) && // Only do this if chars & bytes are out of line, otherwise faster loop will be faster next time -#else - (unchecked((int)chars) & 3) != (unchecked((int)bytes) & 3) && // Only do this if chars & bytes are out of line, otherwise faster loop will be faster next time -#endif // BIT64 - (unchecked((int)(bytes)) & 1) == 0) - { - // # to use - long iCount = ((byteEnd - bytes) >> 1 < charEnd - chars) ? - (byteEnd - bytes) >> 1 : charEnd - chars; - - // Need new char* - char* charOut = ((char*)bytes); // a char* for our output - char* tempEnd = chars + iCount - 1; // Our end pointer - - while (chars < tempEnd) - { - if (*chars >= (char)0xd800 && *chars <= (char)0xdfff) - { - // break for fallback for low surrogate - if (*chars >= 0xdc00) - break; - - // break if next one's not a low surrogate (will do fallback) - if (*(chars + 1) < 0xdc00 || *(chars + 1) > 0xdfff) - break; - - // They both exist, use them - } - // If 2nd char is surrogate & this one isn't then only add one - else if (*(chars + 1) >= (char)0xd800 && *(chars + 1) <= 0xdfff) - { - *charOut = *chars; - charOut++; - chars++; - continue; - } - - *charOut = *chars; - *(charOut + 1) = *(chars + 1); - charOut += 2; - chars += 2; - } - - bytes = (byte*)charOut; - - if (chars >= charEnd) - break; - } -#endif // !NO_FAST_UNICODE_LOOP +#endif // FASTLOOP // No fallback, just get next char ch = *chars; @@ -1070,13 +1004,10 @@ namespace System.Text encoder == null || !encoder._throwOnOverflow, "[UnicodeEncoding.GetBytes]Expected empty fallback buffer if not converting"); - // We used to copy it fast, but this doesn't check for surrogates - // System.IO.__UnmanagedMemoryStream.memcpyimpl(bytes, (byte*)chars, usedByteCount); - return (int)(bytes - byteStart); } - internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) + internal sealed override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) { Debug.Assert(bytes != null, "[UnicodeEncoding.GetCharCount]bytes!=null"); Debug.Assert(count >= 0, "[UnicodeEncoding.GetCharCount]count >=0"); @@ -1093,11 +1024,6 @@ namespace System.Text // Start by assuming same # of chars as bytes int charCount = count >> 1; - // Need -1 to check 2 at a time. If we have an even #, longBytes will go - // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longBytes - // will go from longEnd - 1 long to longEnd. (Might not get to use this) - ulong* longEnd = (ulong*)(byteEnd - 7); - // For fallback we may need a fallback buffer DecoderFallbackBuffer fallbackBuffer = null; @@ -1126,19 +1052,20 @@ namespace System.Text { // If we're aligned then maybe we can do it fast // That'll hurt if we're unaligned because we'll always test but never be aligned -#if !NO_FAST_UNICODE_LOOP -#if BIGENDIAN - if (bigEndian && -#else // BIGENDIAN - if (!bigEndian && -#endif // BIGENDIAN -#if BIT64 // win64 has to be long aligned +#if FASTLOOP + if ((bigEndian ^ BitConverter.IsLittleEndian) && +#if BIT64 (unchecked((long)bytes) & 7) == 0 && #else (unchecked((int)bytes) & 3) == 0 && #endif // BIT64 lastByte == -1 && lastChar == 0) { + // Need -1 to check 2 at a time. If we have an even #, longBytes will go + // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longBytes + // will go from longEnd - 1 long to longEnd. (Might not get to use this) + ulong* longEnd = (ulong*)(byteEnd - 7); + // Need new char* so we can check 4 at a time ulong* longBytes = (ulong*)bytes; @@ -1169,11 +1096,8 @@ namespace System.Text // If they happen to be high/low/high/low, we may as well continue. Check the next // bit to see if its set (low) or not (high) in the right pattern -#if BIGENDIAN - if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xd800dc00d800dc00) != 0) -#else - if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xdc00d800dc00d800) != 0) -#endif + if ((0xfc00fc00fc00fc00 & *longBytes) != + (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00)) { // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. @@ -1196,7 +1120,7 @@ namespace System.Text if (bytes >= byteEnd) break; } -#endif // !NO_FAST_UNICODE_LOOP +#endif // FASTLOOP // Get 1st byte if (lastByte < 0) @@ -1410,8 +1334,8 @@ namespace System.Text return charCount; } - internal override unsafe int GetChars(byte* bytes, int byteCount, - char* chars, int charCount, DecoderNLS baseDecoder) + internal sealed override unsafe int GetChars( + byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder) { Debug.Assert(chars != null, "[UnicodeEncoding.GetChars]chars!=null"); Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetChars]byteCount >=0"); @@ -1449,17 +1373,13 @@ namespace System.Text { // If we're aligned then maybe we can do it fast // That'll hurt if we're unaligned because we'll always test but never be aligned -#if !NO_FAST_UNICODE_LOOP -#if BIGENDIAN - if (bigEndian && -#else // BIGENDIAN - if (!bigEndian && -#endif // BIGENDIAN -#if BIT64 // win64 has to be long aligned - (unchecked((long)chars) & 7) == 0 && (unchecked((long)bytes) & 7) == 0 && +#if FASTLOOP + if ((bigEndian ^ BitConverter.IsLittleEndian) && +#if BIT64 + (unchecked((long)chars) & 7) == 0 && #else - (unchecked((int)chars) & 3) == 0 && (unchecked((int)bytes) & 3) == 0 && -#endif // BIT64 + (unchecked((int)chars) & 3) == 0 && +#endif lastByte == -1 && lastChar == 0) { // Need -1 to check 2 at a time. If we have an even #, longChars will go @@ -1501,11 +1421,8 @@ namespace System.Text // If they happen to be high/low/high/low, we may as well continue. Check the next // bit to see if its set (low) or not (high) in the right pattern -#if BIGENDIAN - if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xd800dc00d800dc00) != 0) -#else - if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xdc00d800dc00d800) != 0) -#endif + if ((0xfc00fc00fc00fc00 & *longBytes) != + (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00)) { // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. @@ -1520,7 +1437,7 @@ namespace System.Text // else all < 0x8000 so we can use them // We can use these 4 chars. - *longChars = *longBytes; + Unsafe.WriteUnaligned<ulong>(longChars, *longBytes); longBytes++; longChars++; } @@ -1531,7 +1448,7 @@ namespace System.Text if (bytes >= byteEnd) break; } -#endif // !NO_FAST_UNICODE_LOOP +#endif // FASTLOOP // Get 1st byte if (lastByte < 0) @@ -1840,9 +1757,6 @@ namespace System.Text decoder.lastByte = lastByte; } - // Used to do this the old way - // System.IO.__UnmanagedMemoryStream.memcpyimpl((byte*)chars, bytes, byteCount); - // Shouldn't have anything in fallback buffer for GetChars // (don't have to check _throwOnOverflow for count or chars) Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, @@ -1875,7 +1789,7 @@ namespace System.Text else return new byte[2] { 0xff, 0xfe }; } - return Array.Empty<Byte>(); + return Array.Empty<byte>(); } public override ReadOnlySpan<byte> Preamble => @@ -1928,7 +1842,7 @@ namespace System.Text } - public override bool Equals(Object value) + public override bool Equals(object value) { UnicodeEncoding that = value as UnicodeEncoding; if (that != null) diff --git a/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs index 18d564867..74b5dacf9 100644 --- a/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; namespace System.Text { - internal ref struct ValueStringBuilder + internal ref partial struct ValueStringBuilder { private char[] _arrayToReturnToPool; private Span<char> _chars; @@ -22,11 +22,19 @@ namespace System.Text _pos = 0; } + public ValueStringBuilder(int initialCapacity) + { + _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity); + _chars = _arrayToReturnToPool; + _pos = 0; + } + public int Length { get => _pos; set { + Debug.Assert(value >= 0); Debug.Assert(value <= _chars.Length); _pos = value; } @@ -40,7 +48,30 @@ namespace System.Text Grow(capacity - _chars.Length); } - public ref char GetPinnableReference() => ref MemoryMarshal.GetReference(_chars); + /// <summary> + /// Get a pinnable reference to the builder. + /// Does not ensure there is a null char after <see cref="Length"/> + /// This overload is pattern matched in the C# 7.3+ compiler so you can omit + /// the explicit method call, and write eg "fixed (char* c = builder)" + /// </summary> + public ref char GetPinnableReference() + { + return ref MemoryMarshal.GetReference(_chars); + } + + /// <summary> + /// Get a pinnable reference to the builder. + /// </summary> + /// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param> + public ref char GetPinnableReference(bool terminate) + { + if (terminate) + { + EnsureCapacity(Length + 1); + _chars[Length] = '\0'; + } + return ref MemoryMarshal.GetReference(_chars); + } public ref char this[int index] { @@ -53,12 +84,31 @@ namespace System.Text public override string ToString() { - var s = new string(_chars.Slice(0, _pos)); + var s = _chars.Slice(0, _pos).ToString(); Dispose(); return s; } + /// <summary>Returns the underlying storage of the builder.</summary> + public Span<char> RawChars => _chars; + + /// <summary> + /// Returns a span around the contents of the builder. + /// </summary> + /// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param> + public ReadOnlySpan<char> AsSpan(bool terminate) + { + if (terminate) + { + EnsureCapacity(Length + 1); + _chars[Length] = '\0'; + } + return _chars.Slice(0, _pos); + } + public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos); + public ReadOnlySpan<char> AsSpan(int start) => _chars.Slice(start, _pos - start); + public ReadOnlySpan<char> AsSpan(int start, int length) => _chars.Slice(start, length); public bool TryCopyTo(Span<char> destination, out int charsWritten) { @@ -93,7 +143,7 @@ namespace System.Text public void Append(char c) { int pos = _pos; - if (pos < _chars.Length) + if ((uint)pos < (uint)_chars.Length) { _chars[pos] = c; _pos = pos + 1; @@ -108,7 +158,7 @@ namespace System.Text public void Append(string s) { int pos = _pos; - if (s.Length == 1 && pos < _chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. { _chars[pos] = s[0]; _pos = pos + 1; |