- convert ValueStringBuilder.cs to file-scoped namespaces
- add rule in .editorconfig
This commit is contained in:
Levan Revazashvili 2025-09-02 19:34:17 +04:00 • committed by GitHub
commit 4855a3668c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 234 additions and 234 deletions

View file

@ -3,6 +3,7 @@ root = true
# C# files # C# files
[*.cs] [*.cs]
csharp_style_namespace_declarations=file_scoped:warning
# Compiler # Compiler
dotnet_diagnostic.CS1998.severity = suggestion # CS1998: Missing awaits dotnet_diagnostic.CS1998.severity = suggestion # CS1998: Missing awaits

View file

@ -8,310 +8,309 @@ using System.Runtime.InteropServices;
#nullable enable #nullable enable
namespace System.Text namespace System.Text;
internal ref partial struct ValueStringBuilder
{ {
internal ref partial struct ValueStringBuilder private char[]? _arrayToReturnToPool;
private Span<char> _chars;
private int _pos;
public ValueStringBuilder(Span<char> initialBuffer)
{ {
private char[]? _arrayToReturnToPool; _arrayToReturnToPool = null;
private Span<char> _chars; _chars = initialBuffer;
private int _pos; _pos = 0;
}
public ValueStringBuilder(Span<char> initialBuffer) public ValueStringBuilder(int initialCapacity)
{
_arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity);
_chars = _arrayToReturnToPool;
_pos = 0;
}
public int Length
{
get => _pos;
set
{ {
_arrayToReturnToPool = null; Debug.Assert(value >= 0);
_chars = initialBuffer; Debug.Assert(value <= _chars.Length);
_pos = 0; _pos = value;
} }
}
public ValueStringBuilder(int initialCapacity) public int Capacity => _chars.Length;
public void EnsureCapacity(int capacity)
{
// This is not expected to be called this with negative capacity
Debug.Assert(capacity >= 0);
// If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception.
if ((uint)capacity > (uint)_chars.Length)
Grow(capacity - _pos);
}
/// <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)
{ {
_arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity); EnsureCapacity(Length + 1);
_chars = _arrayToReturnToPool; _chars[Length] = '\0';
_pos = 0;
} }
return ref MemoryMarshal.GetReference(_chars);
}
public int Length public ref char this[int index]
{
get
{ {
get => _pos; Debug.Assert(index < _pos);
set return ref _chars[index];
{
Debug.Assert(value >= 0);
Debug.Assert(value <= _chars.Length);
_pos = value;
}
} }
}
public int Capacity => _chars.Length; public override string ToString()
{
string s = _chars.Slice(0, _pos).ToString();
Dispose();
return s;
}
public void EnsureCapacity(int capacity) /// <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)
{ {
// This is not expected to be called this with negative capacity EnsureCapacity(Length + 1);
Debug.Assert(capacity >= 0); _chars[Length] = '\0';
// If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception.
if ((uint)capacity > (uint)_chars.Length)
Grow(capacity - _pos);
} }
return _chars.Slice(0, _pos);
}
/// <summary> public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos);
/// Get a pinnable reference to the builder. public ReadOnlySpan<char> AsSpan(int start) => _chars.Slice(start, _pos - start);
/// Does not ensure there is a null char after <see cref="Length"/> public ReadOnlySpan<char> AsSpan(int start, int length) => _chars.Slice(start, 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> public bool TryCopyTo(Span<char> destination, out int charsWritten)
/// Get a pinnable reference to the builder. {
/// </summary> if (_chars.Slice(0, _pos).TryCopyTo(destination))
/// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param>
public ref char GetPinnableReference(bool terminate)
{ {
if (terminate) charsWritten = _pos;
{
EnsureCapacity(Length + 1);
_chars[Length] = '\0';
}
return ref MemoryMarshal.GetReference(_chars);
}
public ref char this[int index]
{
get
{
Debug.Assert(index < _pos);
return ref _chars[index];
}
}
public override string ToString()
{
string s = _chars.Slice(0, _pos).ToString();
Dispose(); Dispose();
return s; return true;
}
else
{
charsWritten = 0;
Dispose();
return false;
}
}
public void Insert(int index, char value, int count)
{
if (_pos > _chars.Length - count)
{
Grow(count);
} }
/// <summary>Returns the underlying storage of the builder.</summary> int remaining = _pos - index;
public Span<char> RawChars => _chars; _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
_chars.Slice(index, count).Fill(value);
_pos += count;
}
/// <summary> public void Insert(int index, string? s)
/// Returns a span around the contents of the builder. {
/// </summary> if (s == null)
/// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param>
public ReadOnlySpan<char> AsSpan(bool terminate)
{ {
if (terminate) return;
{
EnsureCapacity(Length + 1);
_chars[Length] = '\0';
}
return _chars.Slice(0, _pos);
} }
public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos); int count = s.Length;
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) if (_pos > (_chars.Length - count))
{ {
if (_chars.Slice(0, _pos).TryCopyTo(destination)) Grow(count);
{
charsWritten = _pos;
Dispose();
return true;
}
else
{
charsWritten = 0;
Dispose();
return false;
}
} }
public void Insert(int index, char value, int count) int remaining = _pos - index;
{ _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
if (_pos > _chars.Length - count) s
{
Grow(count);
}
int remaining = _pos - index;
_chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
_chars.Slice(index, count).Fill(value);
_pos += count;
}
public void Insert(int index, string? s)
{
if (s == null)
{
return;
}
int count = s.Length;
if (_pos > (_chars.Length - count))
{
Grow(count);
}
int remaining = _pos - index;
_chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
s
#if !NET #if !NET
.AsSpan() .AsSpan()
#endif #endif
.CopyTo(_chars.Slice(index)); .CopyTo(_chars.Slice(index));
_pos += count; _pos += count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Append(char c)
{
int pos = _pos;
Span<char> chars = _chars;
if ((uint)pos < (uint)chars.Length)
{
chars[pos] = c;
_pos = pos + 1;
}
else
{
GrowAndAppend(c);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Append(string? s)
{
if (s == null)
{
return;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] int pos = _pos;
public void Append(char c) if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
{ {
int pos = _pos; _chars[pos] = s[0];
Span<char> chars = _chars; _pos = pos + 1;
if ((uint)pos < (uint)chars.Length) }
{ else
chars[pos] = c; {
_pos = pos + 1; AppendSlow(s);
} }
else }
{
GrowAndAppend(c); private void AppendSlow(string s)
} {
int pos = _pos;
if (pos > _chars.Length - s.Length)
{
Grow(s.Length);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] s
public void Append(string? s)
{
if (s == null)
{
return;
}
int pos = _pos;
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;
}
else
{
AppendSlow(s);
}
}
private void AppendSlow(string s)
{
int pos = _pos;
if (pos > _chars.Length - s.Length)
{
Grow(s.Length);
}
s
#if !NET #if !NET
.AsSpan() .AsSpan()
#endif #endif
.CopyTo(_chars.Slice(pos)); .CopyTo(_chars.Slice(pos));
_pos += s.Length; _pos += s.Length;
}
public void Append(char c, int count)
{
if (_pos > _chars.Length - count)
{
Grow(count);
} }
public void Append(char c, int count) Span<char> dst = _chars.Slice(_pos, count);
for (int i = 0; i < dst.Length; i++)
{ {
if (_pos > _chars.Length - count) dst[i] = c;
{ }
Grow(count); _pos += count;
} }
Span<char> dst = _chars.Slice(_pos, count); public void Append(scoped ReadOnlySpan<char> value)
for (int i = 0; i < dst.Length; i++) {
{ int pos = _pos;
dst[i] = c; if (pos > _chars.Length - value.Length)
} {
_pos += count; Grow(value.Length);
} }
public void Append(scoped ReadOnlySpan<char> value) value.CopyTo(_chars.Slice(_pos));
{ _pos += value.Length;
int pos = _pos; }
if (pos > _chars.Length - value.Length)
{
Grow(value.Length);
}
value.CopyTo(_chars.Slice(_pos)); [MethodImpl(MethodImplOptions.AggressiveInlining)]
_pos += value.Length; public Span<char> AppendSpan(int length)
{
int origPos = _pos;
if (origPos > _chars.Length - length)
{
Grow(length);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] _pos = origPos + length;
public Span<char> AppendSpan(int length) return _chars.Slice(origPos, length);
{ }
int origPos = _pos;
if (origPos > _chars.Length - length)
{
Grow(length);
}
_pos = origPos + length; [MethodImpl(MethodImplOptions.NoInlining)]
return _chars.Slice(origPos, length); private void GrowAndAppend(char c)
{
Grow(1);
Append(c);
}
/// <summary>
/// Resize the internal buffer either by doubling current buffer size or
/// by adding <paramref name="additionalCapacityBeyondPos"/> to
/// <see cref="_pos"/> whichever is greater.
/// </summary>
/// <param name="additionalCapacityBeyondPos">
/// Number of chars requested beyond current position.
/// </param>
[MethodImpl(MethodImplOptions.NoInlining)]
private void Grow(int additionalCapacityBeyondPos)
{
Debug.Assert(additionalCapacityBeyondPos > 0);
Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed.");
const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength
// Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try
// to double the size if possible, bounding the doubling to not go beyond the max array length.
int newCapacity = (int)Math.Max(
(uint)(_pos + additionalCapacityBeyondPos),
Math.Min((uint)_chars.Length * 2, ArrayMaxLength));
// Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative.
// This could also go negative if the actual required length wraps around.
char[] poolArray = ArrayPool<char>.Shared.Rent(newCapacity);
_chars.Slice(0, _pos).CopyTo(poolArray);
char[]? toReturn = _arrayToReturnToPool;
_chars = _arrayToReturnToPool = poolArray;
if (toReturn != null)
{
ArrayPool<char>.Shared.Return(toReturn);
} }
}
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void GrowAndAppend(char c) public void Dispose()
{
char[]? toReturn = _arrayToReturnToPool;
this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
if (toReturn != null)
{ {
Grow(1); ArrayPool<char>.Shared.Return(toReturn);
Append(c);
}
/// <summary>
/// Resize the internal buffer either by doubling current buffer size or
/// by adding <paramref name="additionalCapacityBeyondPos"/> to
/// <see cref="_pos"/> whichever is greater.
/// </summary>
/// <param name="additionalCapacityBeyondPos">
/// Number of chars requested beyond current position.
/// </param>
[MethodImpl(MethodImplOptions.NoInlining)]
private void Grow(int additionalCapacityBeyondPos)
{
Debug.Assert(additionalCapacityBeyondPos > 0);
Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed.");
const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength
// Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try
// to double the size if possible, bounding the doubling to not go beyond the max array length.
int newCapacity = (int)Math.Max(
(uint)(_pos + additionalCapacityBeyondPos),
Math.Min((uint)_chars.Length * 2, ArrayMaxLength));
// Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative.
// This could also go negative if the actual required length wraps around.
char[] poolArray = ArrayPool<char>.Shared.Rent(newCapacity);
_chars.Slice(0, _pos).CopyTo(poolArray);
char[]? toReturn = _arrayToReturnToPool;
_chars = _arrayToReturnToPool = poolArray;
if (toReturn != null)
{
ArrayPool<char>.Shared.Return(toReturn);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
char[]? toReturn = _arrayToReturnToPool;
this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
if (toReturn != null)
{
ArrayPool<char>.Shared.Return(toReturn);
}
} }
} }
} }